##// END OF EJS Templates
bookmarks: Add extra checks for bogus data in bookmarks repo/repo group ids passed.
marcink -
r3751:c36477cc new-ui
parent child Browse files
Show More
@@ -1,743 +1,743 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 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 import logging
22 22 import datetime
23 23 import string
24 24
25 25 import formencode
26 26 import formencode.htmlfill
27 27 import peppercorn
28 28 from pyramid.httpexceptions import HTTPFound
29 29 from pyramid.view import view_config
30 30
31 31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 32 from rhodecode import forms
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.lib import audit_logger
35 35 from rhodecode.lib.ext_json import json
36 36 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired, \
37 37 HasRepoPermissionAny, HasRepoGroupPermissionAny
38 38 from rhodecode.lib.channelstream import (
39 39 channelstream_request, ChannelstreamException)
40 40 from rhodecode.lib.utils2 import safe_int, md5, str2bool
41 41 from rhodecode.model.auth_token import AuthTokenModel
42 42 from rhodecode.model.comment import CommentsModel
43 43 from rhodecode.model.db import (
44 44 IntegrityError, joinedload,
45 45 Repository, UserEmailMap, UserApiKeys, UserFollowing,
46 46 PullRequest, UserBookmark, RepoGroup)
47 47 from rhodecode.model.meta import Session
48 48 from rhodecode.model.pull_request import PullRequestModel
49 49 from rhodecode.model.scm import RepoList
50 50 from rhodecode.model.user import UserModel
51 51 from rhodecode.model.repo import RepoModel
52 52 from rhodecode.model.user_group import UserGroupModel
53 53 from rhodecode.model.validation_schema.schemas import user_schema
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57
58 58 class MyAccountView(BaseAppView, DataGridAppView):
59 59 ALLOW_SCOPED_TOKENS = False
60 60 """
61 61 This view has alternative version inside EE, if modified please take a look
62 62 in there as well.
63 63 """
64 64
65 65 def load_default_context(self):
66 66 c = self._get_local_tmpl_context()
67 67 c.user = c.auth_user.get_instance()
68 68 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
69 69
70 70 return c
71 71
72 72 @LoginRequired()
73 73 @NotAnonymous()
74 74 @view_config(
75 75 route_name='my_account_profile', request_method='GET',
76 76 renderer='rhodecode:templates/admin/my_account/my_account.mako')
77 77 def my_account_profile(self):
78 78 c = self.load_default_context()
79 79 c.active = 'profile'
80 80 return self._get_template_context(c)
81 81
82 82 @LoginRequired()
83 83 @NotAnonymous()
84 84 @view_config(
85 85 route_name='my_account_password', request_method='GET',
86 86 renderer='rhodecode:templates/admin/my_account/my_account.mako')
87 87 def my_account_password(self):
88 88 c = self.load_default_context()
89 89 c.active = 'password'
90 90 c.extern_type = c.user.extern_type
91 91
92 92 schema = user_schema.ChangePasswordSchema().bind(
93 93 username=c.user.username)
94 94
95 95 form = forms.Form(
96 96 schema,
97 97 action=h.route_path('my_account_password_update'),
98 98 buttons=(forms.buttons.save, forms.buttons.reset))
99 99
100 100 c.form = form
101 101 return self._get_template_context(c)
102 102
103 103 @LoginRequired()
104 104 @NotAnonymous()
105 105 @CSRFRequired()
106 106 @view_config(
107 107 route_name='my_account_password_update', request_method='POST',
108 108 renderer='rhodecode:templates/admin/my_account/my_account.mako')
109 109 def my_account_password_update(self):
110 110 _ = self.request.translate
111 111 c = self.load_default_context()
112 112 c.active = 'password'
113 113 c.extern_type = c.user.extern_type
114 114
115 115 schema = user_schema.ChangePasswordSchema().bind(
116 116 username=c.user.username)
117 117
118 118 form = forms.Form(
119 119 schema, buttons=(forms.buttons.save, forms.buttons.reset))
120 120
121 121 if c.extern_type != 'rhodecode':
122 122 raise HTTPFound(self.request.route_path('my_account_password'))
123 123
124 124 controls = self.request.POST.items()
125 125 try:
126 126 valid_data = form.validate(controls)
127 127 UserModel().update_user(c.user.user_id, **valid_data)
128 128 c.user.update_userdata(force_password_change=False)
129 129 Session().commit()
130 130 except forms.ValidationFailure as e:
131 131 c.form = e
132 132 return self._get_template_context(c)
133 133
134 134 except Exception:
135 135 log.exception("Exception updating password")
136 136 h.flash(_('Error occurred during update of user password'),
137 137 category='error')
138 138 else:
139 139 instance = c.auth_user.get_instance()
140 140 self.session.setdefault('rhodecode_user', {}).update(
141 141 {'password': md5(instance.password)})
142 142 self.session.save()
143 143 h.flash(_("Successfully updated password"), category='success')
144 144
145 145 raise HTTPFound(self.request.route_path('my_account_password'))
146 146
147 147 @LoginRequired()
148 148 @NotAnonymous()
149 149 @view_config(
150 150 route_name='my_account_auth_tokens', request_method='GET',
151 151 renderer='rhodecode:templates/admin/my_account/my_account.mako')
152 152 def my_account_auth_tokens(self):
153 153 _ = self.request.translate
154 154
155 155 c = self.load_default_context()
156 156 c.active = 'auth_tokens'
157 157 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
158 158 c.role_values = [
159 159 (x, AuthTokenModel.cls._get_role_name(x))
160 160 for x in AuthTokenModel.cls.ROLES]
161 161 c.role_options = [(c.role_values, _("Role"))]
162 162 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
163 163 c.user.user_id, show_expired=True)
164 164 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
165 165 return self._get_template_context(c)
166 166
167 167 def maybe_attach_token_scope(self, token):
168 168 # implemented in EE edition
169 169 pass
170 170
171 171 @LoginRequired()
172 172 @NotAnonymous()
173 173 @CSRFRequired()
174 174 @view_config(
175 175 route_name='my_account_auth_tokens_add', request_method='POST',)
176 176 def my_account_auth_tokens_add(self):
177 177 _ = self.request.translate
178 178 c = self.load_default_context()
179 179
180 180 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
181 181 description = self.request.POST.get('description')
182 182 role = self.request.POST.get('role')
183 183
184 184 token = UserModel().add_auth_token(
185 185 user=c.user.user_id,
186 186 lifetime_minutes=lifetime, role=role, description=description,
187 187 scope_callback=self.maybe_attach_token_scope)
188 188 token_data = token.get_api_data()
189 189
190 190 audit_logger.store_web(
191 191 'user.edit.token.add', action_data={
192 192 'data': {'token': token_data, 'user': 'self'}},
193 193 user=self._rhodecode_user, )
194 194 Session().commit()
195 195
196 196 h.flash(_("Auth token successfully created"), category='success')
197 197 return HTTPFound(h.route_path('my_account_auth_tokens'))
198 198
199 199 @LoginRequired()
200 200 @NotAnonymous()
201 201 @CSRFRequired()
202 202 @view_config(
203 203 route_name='my_account_auth_tokens_delete', request_method='POST')
204 204 def my_account_auth_tokens_delete(self):
205 205 _ = self.request.translate
206 206 c = self.load_default_context()
207 207
208 208 del_auth_token = self.request.POST.get('del_auth_token')
209 209
210 210 if del_auth_token:
211 211 token = UserApiKeys.get_or_404(del_auth_token)
212 212 token_data = token.get_api_data()
213 213
214 214 AuthTokenModel().delete(del_auth_token, c.user.user_id)
215 215 audit_logger.store_web(
216 216 'user.edit.token.delete', action_data={
217 217 'data': {'token': token_data, 'user': 'self'}},
218 218 user=self._rhodecode_user,)
219 219 Session().commit()
220 220 h.flash(_("Auth token successfully deleted"), category='success')
221 221
222 222 return HTTPFound(h.route_path('my_account_auth_tokens'))
223 223
224 224 @LoginRequired()
225 225 @NotAnonymous()
226 226 @view_config(
227 227 route_name='my_account_emails', request_method='GET',
228 228 renderer='rhodecode:templates/admin/my_account/my_account.mako')
229 229 def my_account_emails(self):
230 230 _ = self.request.translate
231 231
232 232 c = self.load_default_context()
233 233 c.active = 'emails'
234 234
235 235 c.user_email_map = UserEmailMap.query()\
236 236 .filter(UserEmailMap.user == c.user).all()
237 237
238 238 schema = user_schema.AddEmailSchema().bind(
239 239 username=c.user.username, user_emails=c.user.emails)
240 240
241 241 form = forms.RcForm(schema,
242 242 action=h.route_path('my_account_emails_add'),
243 243 buttons=(forms.buttons.save, forms.buttons.reset))
244 244
245 245 c.form = form
246 246 return self._get_template_context(c)
247 247
248 248 @LoginRequired()
249 249 @NotAnonymous()
250 250 @CSRFRequired()
251 251 @view_config(
252 252 route_name='my_account_emails_add', request_method='POST',
253 253 renderer='rhodecode:templates/admin/my_account/my_account.mako')
254 254 def my_account_emails_add(self):
255 255 _ = self.request.translate
256 256 c = self.load_default_context()
257 257 c.active = 'emails'
258 258
259 259 schema = user_schema.AddEmailSchema().bind(
260 260 username=c.user.username, user_emails=c.user.emails)
261 261
262 262 form = forms.RcForm(
263 263 schema, action=h.route_path('my_account_emails_add'),
264 264 buttons=(forms.buttons.save, forms.buttons.reset))
265 265
266 266 controls = self.request.POST.items()
267 267 try:
268 268 valid_data = form.validate(controls)
269 269 UserModel().add_extra_email(c.user.user_id, valid_data['email'])
270 270 audit_logger.store_web(
271 271 'user.edit.email.add', action_data={
272 272 'data': {'email': valid_data['email'], 'user': 'self'}},
273 273 user=self._rhodecode_user,)
274 274 Session().commit()
275 275 except formencode.Invalid as error:
276 276 h.flash(h.escape(error.error_dict['email']), category='error')
277 277 except forms.ValidationFailure as e:
278 278 c.user_email_map = UserEmailMap.query() \
279 279 .filter(UserEmailMap.user == c.user).all()
280 280 c.form = e
281 281 return self._get_template_context(c)
282 282 except Exception:
283 283 log.exception("Exception adding email")
284 284 h.flash(_('Error occurred during adding email'),
285 285 category='error')
286 286 else:
287 287 h.flash(_("Successfully added email"), category='success')
288 288
289 289 raise HTTPFound(self.request.route_path('my_account_emails'))
290 290
291 291 @LoginRequired()
292 292 @NotAnonymous()
293 293 @CSRFRequired()
294 294 @view_config(
295 295 route_name='my_account_emails_delete', request_method='POST')
296 296 def my_account_emails_delete(self):
297 297 _ = self.request.translate
298 298 c = self.load_default_context()
299 299
300 300 del_email_id = self.request.POST.get('del_email_id')
301 301 if del_email_id:
302 302 email = UserEmailMap.get_or_404(del_email_id).email
303 303 UserModel().delete_extra_email(c.user.user_id, del_email_id)
304 304 audit_logger.store_web(
305 305 'user.edit.email.delete', action_data={
306 306 'data': {'email': email, 'user': 'self'}},
307 307 user=self._rhodecode_user,)
308 308 Session().commit()
309 309 h.flash(_("Email successfully deleted"),
310 310 category='success')
311 311 return HTTPFound(h.route_path('my_account_emails'))
312 312
313 313 @LoginRequired()
314 314 @NotAnonymous()
315 315 @CSRFRequired()
316 316 @view_config(
317 317 route_name='my_account_notifications_test_channelstream',
318 318 request_method='POST', renderer='json_ext')
319 319 def my_account_notifications_test_channelstream(self):
320 320 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
321 321 self._rhodecode_user.username, datetime.datetime.now())
322 322 payload = {
323 323 # 'channel': 'broadcast',
324 324 'type': 'message',
325 325 'timestamp': datetime.datetime.utcnow(),
326 326 'user': 'system',
327 327 'pm_users': [self._rhodecode_user.username],
328 328 'message': {
329 329 'message': message,
330 330 'level': 'info',
331 331 'topic': '/notifications'
332 332 }
333 333 }
334 334
335 335 registry = self.request.registry
336 336 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
337 337 channelstream_config = rhodecode_plugins.get('channelstream', {})
338 338
339 339 try:
340 340 channelstream_request(channelstream_config, [payload], '/message')
341 341 except ChannelstreamException as e:
342 342 log.exception('Failed to send channelstream data')
343 343 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
344 344 return {"response": 'Channelstream data sent. '
345 345 'You should see a new live message now.'}
346 346
347 347 def _load_my_repos_data(self, watched=False):
348 348 if watched:
349 349 admin = False
350 350 follows_repos = Session().query(UserFollowing)\
351 351 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
352 352 .options(joinedload(UserFollowing.follows_repository))\
353 353 .all()
354 354 repo_list = [x.follows_repository for x in follows_repos]
355 355 else:
356 356 admin = True
357 357 repo_list = Repository.get_all_repos(
358 358 user_id=self._rhodecode_user.user_id)
359 359 repo_list = RepoList(repo_list, perm_set=[
360 360 'repository.read', 'repository.write', 'repository.admin'])
361 361
362 362 repos_data = RepoModel().get_repos_as_dict(
363 363 repo_list=repo_list, admin=admin, short_name=False)
364 364 # json used to render the grid
365 365 return json.dumps(repos_data)
366 366
367 367 @LoginRequired()
368 368 @NotAnonymous()
369 369 @view_config(
370 370 route_name='my_account_repos', request_method='GET',
371 371 renderer='rhodecode:templates/admin/my_account/my_account.mako')
372 372 def my_account_repos(self):
373 373 c = self.load_default_context()
374 374 c.active = 'repos'
375 375
376 376 # json used to render the grid
377 377 c.data = self._load_my_repos_data()
378 378 return self._get_template_context(c)
379 379
380 380 @LoginRequired()
381 381 @NotAnonymous()
382 382 @view_config(
383 383 route_name='my_account_watched', request_method='GET',
384 384 renderer='rhodecode:templates/admin/my_account/my_account.mako')
385 385 def my_account_watched(self):
386 386 c = self.load_default_context()
387 387 c.active = 'watched'
388 388
389 389 # json used to render the grid
390 390 c.data = self._load_my_repos_data(watched=True)
391 391 return self._get_template_context(c)
392 392
393 393 @LoginRequired()
394 394 @NotAnonymous()
395 395 @view_config(
396 396 route_name='my_account_bookmarks', request_method='GET',
397 397 renderer='rhodecode:templates/admin/my_account/my_account.mako')
398 398 def my_account_bookmarks(self):
399 399 c = self.load_default_context()
400 400 c.active = 'bookmarks'
401 401 return self._get_template_context(c)
402 402
403 403 def _process_entry(self, entry, user_id):
404 404 position = safe_int(entry.get('position'))
405 405 if position is None:
406 406 return
407 407
408 408 # check if this is an existing entry
409 409 is_new = False
410 410 db_entry = UserBookmark().get_by_position_for_user(position, user_id)
411 411
412 412 if db_entry and str2bool(entry.get('remove')):
413 413 log.debug('Marked bookmark %s for deletion', db_entry)
414 414 Session().delete(db_entry)
415 415 return
416 416
417 417 if not db_entry:
418 418 # new
419 419 db_entry = UserBookmark()
420 420 is_new = True
421 421
422 422 should_save = False
423 423 default_redirect_url = ''
424 424
425 425 # save repo
426 if entry.get('bookmark_repo'):
426 if entry.get('bookmark_repo') and safe_int(entry.get('bookmark_repo')):
427 427 repo = Repository.get(entry['bookmark_repo'])
428 428 perm_check = HasRepoPermissionAny(
429 429 'repository.read', 'repository.write', 'repository.admin')
430 430 if repo and perm_check(repo_name=repo.repo_name):
431 431 db_entry.repository = repo
432 432 should_save = True
433 433 default_redirect_url = '${repo_url}'
434 434 # save repo group
435 elif entry.get('bookmark_repo_group'):
435 elif entry.get('bookmark_repo_group') and safe_int(entry.get('bookmark_repo_group')):
436 436 repo_group = RepoGroup.get(entry['bookmark_repo_group'])
437 437 perm_check = HasRepoGroupPermissionAny(
438 438 'group.read', 'group.write', 'group.admin')
439 439
440 440 if repo_group and perm_check(group_name=repo_group.group_name):
441 441 db_entry.repository_group = repo_group
442 442 should_save = True
443 443 default_redirect_url = '${repo_group_url}'
444 444 # save generic info
445 445 elif entry.get('title') and entry.get('redirect_url'):
446 446 should_save = True
447 447
448 448 if should_save:
449 449 log.debug('Saving bookmark %s, new:%s', db_entry, is_new)
450 450 # mark user and position
451 451 db_entry.user_id = user_id
452 452 db_entry.position = position
453 453 db_entry.title = entry.get('title')
454 454 db_entry.redirect_url = entry.get('redirect_url') or default_redirect_url
455 455
456 456 Session().add(db_entry)
457 457
458 458 @LoginRequired()
459 459 @NotAnonymous()
460 460 @CSRFRequired()
461 461 @view_config(
462 462 route_name='my_account_bookmarks_update', request_method='POST')
463 463 def my_account_bookmarks_update(self):
464 464 _ = self.request.translate
465 465 c = self.load_default_context()
466 466 c.active = 'bookmarks'
467 467
468 468 controls = peppercorn.parse(self.request.POST.items())
469 469 user_id = c.user.user_id
470 470
471 471 try:
472 472 for entry in controls.get('bookmarks', []):
473 473 self._process_entry(entry, user_id)
474 474
475 475 Session().commit()
476 476 h.flash(_("Update Bookmarks"), category='success')
477 477 except IntegrityError:
478 478 h.flash(_("Failed to update bookmarks. "
479 479 "Make sure an unique position is used"), category='error')
480 480
481 481 return HTTPFound(h.route_path('my_account_bookmarks'))
482 482
483 483 @LoginRequired()
484 484 @NotAnonymous()
485 485 @view_config(
486 486 route_name='my_account_goto_bookmark', request_method='GET',
487 487 renderer='rhodecode:templates/admin/my_account/my_account.mako')
488 488 def my_account_goto_bookmark(self):
489 489
490 490 bookmark_id = self.request.matchdict['bookmark_id']
491 491 user_bookmark = UserBookmark().query()\
492 492 .filter(UserBookmark.user_id == self.request.user.user_id) \
493 493 .filter(UserBookmark.position == bookmark_id).scalar()
494 494
495 495 redirect_url = h.route_path('my_account_bookmarks')
496 496 if not user_bookmark:
497 497 raise HTTPFound(redirect_url)
498 498
499 499 # repository set
500 500 if user_bookmark.repository:
501 501 repo_name = user_bookmark.repository.repo_name
502 502 base_redirect_url = h.route_path(
503 503 'repo_summary', repo_name=repo_name)
504 504 if user_bookmark.redirect_url and \
505 505 '${repo_url}' in user_bookmark.redirect_url:
506 506 redirect_url = string.Template(user_bookmark.redirect_url)\
507 507 .safe_substitute({'repo_url': base_redirect_url})
508 508 else:
509 509 redirect_url = base_redirect_url
510 510 # repository group set
511 511 elif user_bookmark.repository_group:
512 512 repo_group_name = user_bookmark.repository_group.group_name
513 513 base_redirect_url = h.route_path(
514 514 'repo_group_home', repo_group_name=repo_group_name)
515 515 if user_bookmark.redirect_url and \
516 516 '${repo_group_url}' in user_bookmark.redirect_url:
517 517 redirect_url = string.Template(user_bookmark.redirect_url)\
518 518 .safe_substitute({'repo_group_url': base_redirect_url})
519 519 else:
520 520 redirect_url = base_redirect_url
521 521 # custom URL set
522 522 elif user_bookmark.redirect_url:
523 523 server_url = h.route_url('home').rstrip('/')
524 524 redirect_url = string.Template(user_bookmark.redirect_url) \
525 525 .safe_substitute({'server_url': server_url})
526 526
527 527 log.debug('Redirecting bookmark %s to %s', user_bookmark, redirect_url)
528 528 raise HTTPFound(redirect_url)
529 529
530 530 @LoginRequired()
531 531 @NotAnonymous()
532 532 @view_config(
533 533 route_name='my_account_perms', request_method='GET',
534 534 renderer='rhodecode:templates/admin/my_account/my_account.mako')
535 535 def my_account_perms(self):
536 536 c = self.load_default_context()
537 537 c.active = 'perms'
538 538
539 539 c.perm_user = c.auth_user
540 540 return self._get_template_context(c)
541 541
542 542 @LoginRequired()
543 543 @NotAnonymous()
544 544 @view_config(
545 545 route_name='my_account_notifications', request_method='GET',
546 546 renderer='rhodecode:templates/admin/my_account/my_account.mako')
547 547 def my_notifications(self):
548 548 c = self.load_default_context()
549 549 c.active = 'notifications'
550 550
551 551 return self._get_template_context(c)
552 552
553 553 @LoginRequired()
554 554 @NotAnonymous()
555 555 @CSRFRequired()
556 556 @view_config(
557 557 route_name='my_account_notifications_toggle_visibility',
558 558 request_method='POST', renderer='json_ext')
559 559 def my_notifications_toggle_visibility(self):
560 560 user = self._rhodecode_db_user
561 561 new_status = not user.user_data.get('notification_status', True)
562 562 user.update_userdata(notification_status=new_status)
563 563 Session().commit()
564 564 return user.user_data['notification_status']
565 565
566 566 @LoginRequired()
567 567 @NotAnonymous()
568 568 @view_config(
569 569 route_name='my_account_edit',
570 570 request_method='GET',
571 571 renderer='rhodecode:templates/admin/my_account/my_account.mako')
572 572 def my_account_edit(self):
573 573 c = self.load_default_context()
574 574 c.active = 'profile_edit'
575 575 c.extern_type = c.user.extern_type
576 576 c.extern_name = c.user.extern_name
577 577
578 578 schema = user_schema.UserProfileSchema().bind(
579 579 username=c.user.username, user_emails=c.user.emails)
580 580 appstruct = {
581 581 'username': c.user.username,
582 582 'email': c.user.email,
583 583 'firstname': c.user.firstname,
584 584 'lastname': c.user.lastname,
585 585 }
586 586 c.form = forms.RcForm(
587 587 schema, appstruct=appstruct,
588 588 action=h.route_path('my_account_update'),
589 589 buttons=(forms.buttons.save, forms.buttons.reset))
590 590
591 591 return self._get_template_context(c)
592 592
593 593 @LoginRequired()
594 594 @NotAnonymous()
595 595 @CSRFRequired()
596 596 @view_config(
597 597 route_name='my_account_update',
598 598 request_method='POST',
599 599 renderer='rhodecode:templates/admin/my_account/my_account.mako')
600 600 def my_account_update(self):
601 601 _ = self.request.translate
602 602 c = self.load_default_context()
603 603 c.active = 'profile_edit'
604 604 c.perm_user = c.auth_user
605 605 c.extern_type = c.user.extern_type
606 606 c.extern_name = c.user.extern_name
607 607
608 608 schema = user_schema.UserProfileSchema().bind(
609 609 username=c.user.username, user_emails=c.user.emails)
610 610 form = forms.RcForm(
611 611 schema, buttons=(forms.buttons.save, forms.buttons.reset))
612 612
613 613 controls = self.request.POST.items()
614 614 try:
615 615 valid_data = form.validate(controls)
616 616 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
617 617 'new_password', 'password_confirmation']
618 618 if c.extern_type != "rhodecode":
619 619 # forbid updating username for external accounts
620 620 skip_attrs.append('username')
621 621 old_email = c.user.email
622 622 UserModel().update_user(
623 623 self._rhodecode_user.user_id, skip_attrs=skip_attrs,
624 624 **valid_data)
625 625 if old_email != valid_data['email']:
626 626 old = UserEmailMap.query() \
627 627 .filter(UserEmailMap.user == c.user).filter(UserEmailMap.email == valid_data['email']).first()
628 628 old.email = old_email
629 629 h.flash(_('Your account was updated successfully'), category='success')
630 630 Session().commit()
631 631 except forms.ValidationFailure as e:
632 632 c.form = e
633 633 return self._get_template_context(c)
634 634 except Exception:
635 635 log.exception("Exception updating user")
636 636 h.flash(_('Error occurred during update of user'),
637 637 category='error')
638 638 raise HTTPFound(h.route_path('my_account_profile'))
639 639
640 640 def _get_pull_requests_list(self, statuses):
641 641 draw, start, limit = self._extract_chunk(self.request)
642 642 search_q, order_by, order_dir = self._extract_ordering(self.request)
643 643 _render = self.request.get_partial_renderer(
644 644 'rhodecode:templates/data_table/_dt_elements.mako')
645 645
646 646 pull_requests = PullRequestModel().get_im_participating_in(
647 647 user_id=self._rhodecode_user.user_id,
648 648 statuses=statuses,
649 649 offset=start, length=limit, order_by=order_by,
650 650 order_dir=order_dir)
651 651
652 652 pull_requests_total_count = PullRequestModel().count_im_participating_in(
653 653 user_id=self._rhodecode_user.user_id, statuses=statuses)
654 654
655 655 data = []
656 656 comments_model = CommentsModel()
657 657 for pr in pull_requests:
658 658 repo_id = pr.target_repo_id
659 659 comments = comments_model.get_all_comments(
660 660 repo_id, pull_request=pr)
661 661 owned = pr.user_id == self._rhodecode_user.user_id
662 662
663 663 data.append({
664 664 'target_repo': _render('pullrequest_target_repo',
665 665 pr.target_repo.repo_name),
666 666 'name': _render('pullrequest_name',
667 667 pr.pull_request_id, pr.target_repo.repo_name,
668 668 short=True),
669 669 'name_raw': pr.pull_request_id,
670 670 'status': _render('pullrequest_status',
671 671 pr.calculated_review_status()),
672 672 'title': _render(
673 673 'pullrequest_title', pr.title, pr.description),
674 674 'description': h.escape(pr.description),
675 675 'updated_on': _render('pullrequest_updated_on',
676 676 h.datetime_to_time(pr.updated_on)),
677 677 'updated_on_raw': h.datetime_to_time(pr.updated_on),
678 678 'created_on': _render('pullrequest_updated_on',
679 679 h.datetime_to_time(pr.created_on)),
680 680 'created_on_raw': h.datetime_to_time(pr.created_on),
681 681 'author': _render('pullrequest_author',
682 682 pr.author.full_contact, ),
683 683 'author_raw': pr.author.full_name,
684 684 'comments': _render('pullrequest_comments', len(comments)),
685 685 'comments_raw': len(comments),
686 686 'closed': pr.is_closed(),
687 687 'owned': owned
688 688 })
689 689
690 690 # json used to render the grid
691 691 data = ({
692 692 'draw': draw,
693 693 'data': data,
694 694 'recordsTotal': pull_requests_total_count,
695 695 'recordsFiltered': pull_requests_total_count,
696 696 })
697 697 return data
698 698
699 699 @LoginRequired()
700 700 @NotAnonymous()
701 701 @view_config(
702 702 route_name='my_account_pullrequests',
703 703 request_method='GET',
704 704 renderer='rhodecode:templates/admin/my_account/my_account.mako')
705 705 def my_account_pullrequests(self):
706 706 c = self.load_default_context()
707 707 c.active = 'pullrequests'
708 708 req_get = self.request.GET
709 709
710 710 c.closed = str2bool(req_get.get('pr_show_closed'))
711 711
712 712 return self._get_template_context(c)
713 713
714 714 @LoginRequired()
715 715 @NotAnonymous()
716 716 @view_config(
717 717 route_name='my_account_pullrequests_data',
718 718 request_method='GET', renderer='json_ext')
719 719 def my_account_pullrequests_data(self):
720 720 self.load_default_context()
721 721 req_get = self.request.GET
722 722 closed = str2bool(req_get.get('closed'))
723 723
724 724 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
725 725 if closed:
726 726 statuses += [PullRequest.STATUS_CLOSED]
727 727
728 728 data = self._get_pull_requests_list(statuses=statuses)
729 729 return data
730 730
731 731 @LoginRequired()
732 732 @NotAnonymous()
733 733 @view_config(
734 734 route_name='my_account_user_group_membership',
735 735 request_method='GET',
736 736 renderer='rhodecode:templates/admin/my_account/my_account.mako')
737 737 def my_account_user_group_membership(self):
738 738 c = self.load_default_context()
739 739 c.active = 'user_group_membership'
740 740 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
741 741 for group in self._rhodecode_db_user.group_member]
742 742 c.user_groups = json.dumps(groups)
743 743 return self._get_template_context(c)
General Comments 0
You need to be logged in to leave comments. Login now