##// END OF EJS Templates
account: use jsonify decorator
ergo -
r694:ed7b1b2e default
parent child Browse files
Show More
@@ -1,371 +1,373 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2013-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 """
23 23 my account controller for RhodeCode admin
24 24 """
25 25
26 26 import logging
27 27
28 28 import formencode
29 29 from formencode import htmlfill
30 30 from pylons import request, tmpl_context as c, url, session
31 31 from pylons.controllers.util import redirect
32 32 from pylons.i18n.translation import _
33 33 from sqlalchemy.orm import joinedload
34 34
35 35 from rhodecode import forms
36 36 from rhodecode.lib import helpers as h
37 37 from rhodecode.lib import auth
38 38 from rhodecode.lib.auth import (
39 39 LoginRequired, NotAnonymous, AuthUser, generate_auth_token)
40 40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.utils import jsonify
41 42 from rhodecode.lib.utils2 import safe_int, md5
42 43 from rhodecode.lib.ext_json import json
43 44
44 45 from rhodecode.model.validation_schema.schemas import user_schema
45 46 from rhodecode.model.db import (
46 47 Repository, PullRequest, PullRequestReviewers, UserEmailMap, User,
47 48 UserFollowing)
48 49 from rhodecode.model.forms import UserForm
49 50 from rhodecode.model.scm import RepoList
50 51 from rhodecode.model.user import UserModel
51 52 from rhodecode.model.repo import RepoModel
52 53 from rhodecode.model.auth_token import AuthTokenModel
53 54 from rhodecode.model.meta import Session
54 55
55 56 log = logging.getLogger(__name__)
56 57
57 58
58 59 class MyAccountController(BaseController):
59 60 """REST Controller styled on the Atom Publishing Protocol"""
60 61 # To properly map this controller, ensure your config/routing.py
61 62 # file has a resource setup:
62 63 # map.resource('setting', 'settings', controller='admin/settings',
63 64 # path_prefix='/admin', name_prefix='admin_')
64 65
65 66 @LoginRequired()
66 67 @NotAnonymous()
67 68 def __before__(self):
68 69 super(MyAccountController, self).__before__()
69 70
70 71 def __load_data(self):
71 72 c.user = User.get(c.rhodecode_user.user_id)
72 73 if c.user.username == User.DEFAULT_USER:
73 74 h.flash(_("You can't edit this user since it's"
74 75 " crucial for entire application"), category='warning')
75 76 return redirect(url('users'))
76 77
77 78 def _load_my_repos_data(self, watched=False):
78 79 if watched:
79 80 admin = False
80 81 follows_repos = Session().query(UserFollowing)\
81 82 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
82 83 .options(joinedload(UserFollowing.follows_repository))\
83 84 .all()
84 85 repo_list = [x.follows_repository for x in follows_repos]
85 86 else:
86 87 admin = True
87 88 repo_list = Repository.get_all_repos(
88 89 user_id=c.rhodecode_user.user_id)
89 90 repo_list = RepoList(repo_list, perm_set=[
90 91 'repository.read', 'repository.write', 'repository.admin'])
91 92
92 93 repos_data = RepoModel().get_repos_as_dict(
93 94 repo_list=repo_list, admin=admin)
94 95 # json used to render the grid
95 96 return json.dumps(repos_data)
96 97
97 98 @auth.CSRFRequired()
98 99 def my_account_update(self):
99 100 """
100 101 POST /_admin/my_account Updates info of my account
101 102 """
102 103 # url('my_account')
103 104 c.active = 'profile_edit'
104 105 self.__load_data()
105 106 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
106 107 ip_addr=self.ip_addr)
107 108 c.extern_type = c.user.extern_type
108 109 c.extern_name = c.user.extern_name
109 110
110 111 defaults = c.user.get_dict()
111 112 update = False
112 113 _form = UserForm(edit=True,
113 114 old_data={'user_id': c.rhodecode_user.user_id,
114 115 'email': c.rhodecode_user.email})()
115 116 form_result = {}
116 117 try:
117 118 post_data = dict(request.POST)
118 119 post_data['new_password'] = ''
119 120 post_data['password_confirmation'] = ''
120 121 form_result = _form.to_python(post_data)
121 122 # skip updating those attrs for my account
122 123 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
123 124 'new_password', 'password_confirmation']
124 125 # TODO: plugin should define if username can be updated
125 126 if c.extern_type != "rhodecode":
126 127 # forbid updating username for external accounts
127 128 skip_attrs.append('username')
128 129
129 130 UserModel().update_user(
130 131 c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
131 132 h.flash(_('Your account was updated successfully'),
132 133 category='success')
133 134 Session().commit()
134 135 update = True
135 136
136 137 except formencode.Invalid as errors:
137 138 return htmlfill.render(
138 139 render('admin/my_account/my_account.html'),
139 140 defaults=errors.value,
140 141 errors=errors.error_dict or {},
141 142 prefix_error=False,
142 143 encoding="UTF-8",
143 144 force_defaults=False)
144 145 except Exception:
145 146 log.exception("Exception updating user")
146 147 h.flash(_('Error occurred during update of user %s')
147 148 % form_result.get('username'), category='error')
148 149
149 150 if update:
150 151 return redirect('my_account')
151 152
152 153 return htmlfill.render(
153 154 render('admin/my_account/my_account.html'),
154 155 defaults=defaults,
155 156 encoding="UTF-8",
156 157 force_defaults=False
157 158 )
158 159
159 160 def my_account(self):
160 161 """
161 162 GET /_admin/my_account Displays info about my account
162 163 """
163 164 # url('my_account')
164 165 c.active = 'profile'
165 166 self.__load_data()
166 167
167 168 defaults = c.user.get_dict()
168 169 return htmlfill.render(
169 170 render('admin/my_account/my_account.html'),
170 171 defaults=defaults, encoding="UTF-8", force_defaults=False)
171 172
172 173 def my_account_edit(self):
173 174 """
174 175 GET /_admin/my_account/edit Displays edit form of my account
175 176 """
176 177 c.active = 'profile_edit'
177 178 self.__load_data()
178 179 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
179 180 ip_addr=self.ip_addr)
180 181 c.extern_type = c.user.extern_type
181 182 c.extern_name = c.user.extern_name
182 183
183 184 defaults = c.user.get_dict()
184 185 return htmlfill.render(
185 186 render('admin/my_account/my_account.html'),
186 187 defaults=defaults,
187 188 encoding="UTF-8",
188 189 force_defaults=False
189 190 )
190 191
191 192 @auth.CSRFRequired(except_methods=['GET'])
192 193 def my_account_password(self):
193 194 c.active = 'password'
194 195 self.__load_data()
195 196
196 197 schema = user_schema.ChangePasswordSchema().bind(
197 198 username=c.rhodecode_user.username)
198 199
199 200 form = forms.Form(schema,
200 201 buttons=(forms.buttons.save, forms.buttons.reset))
201 202
202 203 if request.method == 'POST':
203 204 controls = request.POST.items()
204 205 try:
205 206 valid_data = form.validate(controls)
206 207 UserModel().update_user(c.rhodecode_user.user_id, **valid_data)
207 208 instance = c.rhodecode_user.get_instance()
208 209 instance.update_userdata(force_password_change=False)
209 210 Session().commit()
210 211 except forms.ValidationFailure as e:
211 212 request.session.flash(
212 213 _('Error occurred during update of user password'),
213 214 queue='error')
214 215 form = e
215 216 except Exception:
216 217 log.exception("Exception updating password")
217 218 request.session.flash(
218 219 _('Error occurred during update of user password'),
219 220 queue='error')
220 221 else:
221 222 session.setdefault('rhodecode_user', {}).update(
222 223 {'password': md5(instance.password)})
223 224 session.save()
224 225 request.session.flash(
225 226 _("Successfully updated password"), queue='success')
226 227 return redirect(url('my_account_password'))
227 228
228 229 c.form = form
229 230 return render('admin/my_account/my_account.html')
230 231
231 232 def my_account_repos(self):
232 233 c.active = 'repos'
233 234 self.__load_data()
234 235
235 236 # json used to render the grid
236 237 c.data = self._load_my_repos_data()
237 238 return render('admin/my_account/my_account.html')
238 239
239 240 def my_account_watched(self):
240 241 c.active = 'watched'
241 242 self.__load_data()
242 243
243 244 # json used to render the grid
244 245 c.data = self._load_my_repos_data(watched=True)
245 246 return render('admin/my_account/my_account.html')
246 247
247 248 def my_account_perms(self):
248 249 c.active = 'perms'
249 250 self.__load_data()
250 251 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
251 252 ip_addr=self.ip_addr)
252 253
253 254 return render('admin/my_account/my_account.html')
254 255
255 256 def my_account_emails(self):
256 257 c.active = 'emails'
257 258 self.__load_data()
258 259
259 260 c.user_email_map = UserEmailMap.query()\
260 261 .filter(UserEmailMap.user == c.user).all()
261 262 return render('admin/my_account/my_account.html')
262 263
263 264 @auth.CSRFRequired()
264 265 def my_account_emails_add(self):
265 266 email = request.POST.get('new_email')
266 267
267 268 try:
268 269 UserModel().add_extra_email(c.rhodecode_user.user_id, email)
269 270 Session().commit()
270 271 h.flash(_("Added new email address `%s` for user account") % email,
271 272 category='success')
272 273 except formencode.Invalid as error:
273 274 msg = error.error_dict['email']
274 275 h.flash(msg, category='error')
275 276 except Exception:
276 277 log.exception("Exception in my_account_emails")
277 278 h.flash(_('An error occurred during email saving'),
278 279 category='error')
279 280 return redirect(url('my_account_emails'))
280 281
281 282 @auth.CSRFRequired()
282 283 def my_account_emails_delete(self):
283 284 email_id = request.POST.get('del_email_id')
284 285 user_model = UserModel()
285 286 user_model.delete_extra_email(c.rhodecode_user.user_id, email_id)
286 287 Session().commit()
287 288 h.flash(_("Removed email address from user account"),
288 289 category='success')
289 290 return redirect(url('my_account_emails'))
290 291
291 292 def my_account_pullrequests(self):
292 293 c.active = 'pullrequests'
293 294 self.__load_data()
294 295 c.show_closed = request.GET.get('pr_show_closed')
295 296
296 297 def _filter(pr):
297 298 s = sorted(pr, key=lambda o: o.created_on, reverse=True)
298 299 if not c.show_closed:
299 300 s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
300 301 return s
301 302
302 303 c.my_pull_requests = _filter(
303 304 PullRequest.query().filter(
304 305 PullRequest.user_id == c.rhodecode_user.user_id).all())
305 306 my_prs = [
306 307 x.pull_request for x in PullRequestReviewers.query().filter(
307 308 PullRequestReviewers.user_id == c.rhodecode_user.user_id).all()]
308 309 c.participate_in_pull_requests = _filter(my_prs)
309 310 return render('admin/my_account/my_account.html')
310 311
311 312 def my_account_auth_tokens(self):
312 313 c.active = 'auth_tokens'
313 314 self.__load_data()
314 315 show_expired = True
315 316 c.lifetime_values = [
316 317 (str(-1), _('forever')),
317 318 (str(5), _('5 minutes')),
318 319 (str(60), _('1 hour')),
319 320 (str(60 * 24), _('1 day')),
320 321 (str(60 * 24 * 30), _('1 month')),
321 322 ]
322 323 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
323 324 c.role_values = [(x, AuthTokenModel.cls._get_role_name(x))
324 325 for x in AuthTokenModel.cls.ROLES]
325 326 c.role_options = [(c.role_values, _("Role"))]
326 327 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
327 328 c.rhodecode_user.user_id, show_expired=show_expired)
328 329 return render('admin/my_account/my_account.html')
329 330
330 331 @auth.CSRFRequired()
331 332 def my_account_auth_tokens_add(self):
332 333 lifetime = safe_int(request.POST.get('lifetime'), -1)
333 334 description = request.POST.get('description')
334 335 role = request.POST.get('role')
335 336 AuthTokenModel().create(c.rhodecode_user.user_id, description, lifetime,
336 337 role)
337 338 Session().commit()
338 339 h.flash(_("Auth token successfully created"), category='success')
339 340 return redirect(url('my_account_auth_tokens'))
340 341
341 342 @auth.CSRFRequired()
342 343 def my_account_auth_tokens_delete(self):
343 344 auth_token = request.POST.get('del_auth_token')
344 345 user_id = c.rhodecode_user.user_id
345 346 if request.POST.get('del_auth_token_builtin'):
346 347 user = User.get(user_id)
347 348 if user:
348 349 user.api_key = generate_auth_token(user.username)
349 350 Session().add(user)
350 351 Session().commit()
351 352 h.flash(_("Auth token successfully reset"), category='success')
352 353 elif auth_token:
353 354 AuthTokenModel().delete(auth_token, c.rhodecode_user.user_id)
354 355 Session().commit()
355 356 h.flash(_("Auth token successfully deleted"), category='success')
356 357
357 358 return redirect(url('my_account_auth_tokens'))
358 359
359 360 def my_notifications(self):
360 361 c.active = 'notifications'
361 362 return render('admin/my_account/my_account.html')
362 363
363 364 @auth.CSRFRequired()
365 @jsonify
364 366 def my_notifications_toggle_visibility(self):
365 367 user = c.rhodecode_user.get_instance()
366 368 user_data = user.user_data
367 369 status = user_data.get('notification_status', False)
368 370 user_data['notification_status'] = not status
369 371 user.user_data = user_data
370 372 Session().commit()
371 return json.dumps(user_data['notification_status'])
373 return user_data['notification_status']
General Comments 0
You need to be logged in to leave comments. Login now