##// END OF EJS Templates
bookmarks: cache fetching of bookmarks since this is quite expensive query to make with joinedload on repos/repo groups.
marcink -
r4143:d9dc1c76 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,762 +1,764 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 c.bookmark_items = UserBookmark.get_bookmarks_for_user(
402 self._rhodecode_db_user.user_id, cache=False)
401 403 return self._get_template_context(c)
402 404
403 405 def _process_bookmark_entry(self, entry, user_id):
404 406 position = safe_int(entry.get('position'))
405 407 cur_position = safe_int(entry.get('cur_position'))
406 if position is None or cur_position is None:
408 if position is None:
407 409 return
408 410
409 411 # check if this is an existing entry
410 412 is_new = False
411 413 db_entry = UserBookmark().get_by_position_for_user(cur_position, user_id)
412 414
413 415 if db_entry and str2bool(entry.get('remove')):
414 416 log.debug('Marked bookmark %s for deletion', db_entry)
415 417 Session().delete(db_entry)
416 418 return
417 419
418 420 if not db_entry:
419 421 # new
420 422 db_entry = UserBookmark()
421 423 is_new = True
422 424
423 425 should_save = False
424 426 default_redirect_url = ''
425 427
426 428 # save repo
427 429 if entry.get('bookmark_repo') and safe_int(entry.get('bookmark_repo')):
428 430 repo = Repository.get(entry['bookmark_repo'])
429 431 perm_check = HasRepoPermissionAny(
430 432 'repository.read', 'repository.write', 'repository.admin')
431 433 if repo and perm_check(repo_name=repo.repo_name):
432 434 db_entry.repository = repo
433 435 should_save = True
434 436 default_redirect_url = '${repo_url}'
435 437 # save repo group
436 438 elif entry.get('bookmark_repo_group') and safe_int(entry.get('bookmark_repo_group')):
437 439 repo_group = RepoGroup.get(entry['bookmark_repo_group'])
438 440 perm_check = HasRepoGroupPermissionAny(
439 441 'group.read', 'group.write', 'group.admin')
440 442
441 443 if repo_group and perm_check(group_name=repo_group.group_name):
442 444 db_entry.repository_group = repo_group
443 445 should_save = True
444 446 default_redirect_url = '${repo_group_url}'
445 447 # save generic info
446 448 elif entry.get('title') and entry.get('redirect_url'):
447 449 should_save = True
448 450
449 451 if should_save:
450 452 # mark user and position
451 453 db_entry.user_id = user_id
452 454 db_entry.position = position
453 455 db_entry.title = entry.get('title')
454 456 db_entry.redirect_url = entry.get('redirect_url') or default_redirect_url
455 457 log.debug('Saving bookmark %s, new:%s', db_entry, is_new)
456 458
457 459 Session().add(db_entry)
458 460
459 461 @LoginRequired()
460 462 @NotAnonymous()
461 463 @CSRFRequired()
462 464 @view_config(
463 465 route_name='my_account_bookmarks_update', request_method='POST')
464 466 def my_account_bookmarks_update(self):
465 467 _ = self.request.translate
466 468 c = self.load_default_context()
467 469 c.active = 'bookmarks'
468 470
469 471 controls = peppercorn.parse(self.request.POST.items())
470 472 user_id = c.user.user_id
471 473
472 474 # validate positions
473 475 positions = {}
474 476 for entry in controls.get('bookmarks', []):
475 477 position = safe_int(entry['position'])
476 478 if position is None:
477 479 continue
478 480
479 481 if position in positions:
480 482 h.flash(_("Position {} is defined twice. "
481 483 "Please correct this error.").format(position), category='error')
482 484 return HTTPFound(h.route_path('my_account_bookmarks'))
483 485
484 486 entry['position'] = position
485 487 entry['cur_position'] = safe_int(entry.get('cur_position'))
486 488 positions[position] = entry
487 489
488 490 try:
489 491 for entry in positions.values():
490 492 self._process_bookmark_entry(entry, user_id)
491 493
492 494 Session().commit()
493 495 h.flash(_("Update Bookmarks"), category='success')
494 496 except IntegrityError:
495 497 h.flash(_("Failed to update bookmarks. "
496 498 "Make sure an unique position is used."), category='error')
497 499
498 500 return HTTPFound(h.route_path('my_account_bookmarks'))
499 501
500 502 @LoginRequired()
501 503 @NotAnonymous()
502 504 @view_config(
503 505 route_name='my_account_goto_bookmark', request_method='GET',
504 506 renderer='rhodecode:templates/admin/my_account/my_account.mako')
505 507 def my_account_goto_bookmark(self):
506 508
507 509 bookmark_id = self.request.matchdict['bookmark_id']
508 510 user_bookmark = UserBookmark().query()\
509 511 .filter(UserBookmark.user_id == self.request.user.user_id) \
510 512 .filter(UserBookmark.position == bookmark_id).scalar()
511 513
512 514 redirect_url = h.route_path('my_account_bookmarks')
513 515 if not user_bookmark:
514 516 raise HTTPFound(redirect_url)
515 517
516 518 # repository set
517 519 if user_bookmark.repository:
518 520 repo_name = user_bookmark.repository.repo_name
519 521 base_redirect_url = h.route_path(
520 522 'repo_summary', repo_name=repo_name)
521 523 if user_bookmark.redirect_url and \
522 524 '${repo_url}' in user_bookmark.redirect_url:
523 525 redirect_url = string.Template(user_bookmark.redirect_url)\
524 526 .safe_substitute({'repo_url': base_redirect_url})
525 527 else:
526 528 redirect_url = base_redirect_url
527 529 # repository group set
528 530 elif user_bookmark.repository_group:
529 531 repo_group_name = user_bookmark.repository_group.group_name
530 532 base_redirect_url = h.route_path(
531 533 'repo_group_home', repo_group_name=repo_group_name)
532 534 if user_bookmark.redirect_url and \
533 535 '${repo_group_url}' in user_bookmark.redirect_url:
534 536 redirect_url = string.Template(user_bookmark.redirect_url)\
535 537 .safe_substitute({'repo_group_url': base_redirect_url})
536 538 else:
537 539 redirect_url = base_redirect_url
538 540 # custom URL set
539 541 elif user_bookmark.redirect_url:
540 542 server_url = h.route_url('home').rstrip('/')
541 543 redirect_url = string.Template(user_bookmark.redirect_url) \
542 544 .safe_substitute({'server_url': server_url})
543 545
544 546 log.debug('Redirecting bookmark %s to %s', user_bookmark, redirect_url)
545 547 raise HTTPFound(redirect_url)
546 548
547 549 @LoginRequired()
548 550 @NotAnonymous()
549 551 @view_config(
550 552 route_name='my_account_perms', request_method='GET',
551 553 renderer='rhodecode:templates/admin/my_account/my_account.mako')
552 554 def my_account_perms(self):
553 555 c = self.load_default_context()
554 556 c.active = 'perms'
555 557
556 558 c.perm_user = c.auth_user
557 559 return self._get_template_context(c)
558 560
559 561 @LoginRequired()
560 562 @NotAnonymous()
561 563 @view_config(
562 564 route_name='my_account_notifications', request_method='GET',
563 565 renderer='rhodecode:templates/admin/my_account/my_account.mako')
564 566 def my_notifications(self):
565 567 c = self.load_default_context()
566 568 c.active = 'notifications'
567 569
568 570 return self._get_template_context(c)
569 571
570 572 @LoginRequired()
571 573 @NotAnonymous()
572 574 @CSRFRequired()
573 575 @view_config(
574 576 route_name='my_account_notifications_toggle_visibility',
575 577 request_method='POST', renderer='json_ext')
576 578 def my_notifications_toggle_visibility(self):
577 579 user = self._rhodecode_db_user
578 580 new_status = not user.user_data.get('notification_status', True)
579 581 user.update_userdata(notification_status=new_status)
580 582 Session().commit()
581 583 return user.user_data['notification_status']
582 584
583 585 @LoginRequired()
584 586 @NotAnonymous()
585 587 @view_config(
586 588 route_name='my_account_edit',
587 589 request_method='GET',
588 590 renderer='rhodecode:templates/admin/my_account/my_account.mako')
589 591 def my_account_edit(self):
590 592 c = self.load_default_context()
591 593 c.active = 'profile_edit'
592 594 c.extern_type = c.user.extern_type
593 595 c.extern_name = c.user.extern_name
594 596
595 597 schema = user_schema.UserProfileSchema().bind(
596 598 username=c.user.username, user_emails=c.user.emails)
597 599 appstruct = {
598 600 'username': c.user.username,
599 601 'email': c.user.email,
600 602 'firstname': c.user.firstname,
601 603 'lastname': c.user.lastname,
602 604 'description': c.user.description,
603 605 }
604 606 c.form = forms.RcForm(
605 607 schema, appstruct=appstruct,
606 608 action=h.route_path('my_account_update'),
607 609 buttons=(forms.buttons.save, forms.buttons.reset))
608 610
609 611 return self._get_template_context(c)
610 612
611 613 @LoginRequired()
612 614 @NotAnonymous()
613 615 @CSRFRequired()
614 616 @view_config(
615 617 route_name='my_account_update',
616 618 request_method='POST',
617 619 renderer='rhodecode:templates/admin/my_account/my_account.mako')
618 620 def my_account_update(self):
619 621 _ = self.request.translate
620 622 c = self.load_default_context()
621 623 c.active = 'profile_edit'
622 624 c.perm_user = c.auth_user
623 625 c.extern_type = c.user.extern_type
624 626 c.extern_name = c.user.extern_name
625 627
626 628 schema = user_schema.UserProfileSchema().bind(
627 629 username=c.user.username, user_emails=c.user.emails)
628 630 form = forms.RcForm(
629 631 schema, buttons=(forms.buttons.save, forms.buttons.reset))
630 632
631 633 controls = self.request.POST.items()
632 634 try:
633 635 valid_data = form.validate(controls)
634 636 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
635 637 'new_password', 'password_confirmation']
636 638 if c.extern_type != "rhodecode":
637 639 # forbid updating username for external accounts
638 640 skip_attrs.append('username')
639 641 old_email = c.user.email
640 642 UserModel().update_user(
641 643 self._rhodecode_user.user_id, skip_attrs=skip_attrs,
642 644 **valid_data)
643 645 if old_email != valid_data['email']:
644 646 old = UserEmailMap.query() \
645 647 .filter(UserEmailMap.user == c.user).filter(UserEmailMap.email == valid_data['email']).first()
646 648 old.email = old_email
647 649 h.flash(_('Your account was updated successfully'), category='success')
648 650 Session().commit()
649 651 except forms.ValidationFailure as e:
650 652 c.form = e
651 653 return self._get_template_context(c)
652 654 except Exception:
653 655 log.exception("Exception updating user")
654 656 h.flash(_('Error occurred during update of user'),
655 657 category='error')
656 658 raise HTTPFound(h.route_path('my_account_profile'))
657 659
658 660 def _get_pull_requests_list(self, statuses):
659 661 draw, start, limit = self._extract_chunk(self.request)
660 662 search_q, order_by, order_dir = self._extract_ordering(self.request)
661 663 _render = self.request.get_partial_renderer(
662 664 'rhodecode:templates/data_table/_dt_elements.mako')
663 665
664 666 pull_requests = PullRequestModel().get_im_participating_in(
665 667 user_id=self._rhodecode_user.user_id,
666 668 statuses=statuses,
667 669 offset=start, length=limit, order_by=order_by,
668 670 order_dir=order_dir)
669 671
670 672 pull_requests_total_count = PullRequestModel().count_im_participating_in(
671 673 user_id=self._rhodecode_user.user_id, statuses=statuses)
672 674
673 675 data = []
674 676 comments_model = CommentsModel()
675 677 for pr in pull_requests:
676 678 repo_id = pr.target_repo_id
677 679 comments = comments_model.get_all_comments(
678 680 repo_id, pull_request=pr)
679 681 owned = pr.user_id == self._rhodecode_user.user_id
680 682
681 683 data.append({
682 684 'target_repo': _render('pullrequest_target_repo',
683 685 pr.target_repo.repo_name),
684 686 'name': _render('pullrequest_name',
685 687 pr.pull_request_id, pr.pull_request_state,
686 688 pr.work_in_progress, pr.target_repo.repo_name,
687 689 short=True),
688 690 'name_raw': pr.pull_request_id,
689 691 'status': _render('pullrequest_status',
690 692 pr.calculated_review_status()),
691 693 'title': _render('pullrequest_title', pr.title, pr.description),
692 694 'description': h.escape(pr.description),
693 695 'updated_on': _render('pullrequest_updated_on',
694 696 h.datetime_to_time(pr.updated_on)),
695 697 'updated_on_raw': h.datetime_to_time(pr.updated_on),
696 698 'created_on': _render('pullrequest_updated_on',
697 699 h.datetime_to_time(pr.created_on)),
698 700 'created_on_raw': h.datetime_to_time(pr.created_on),
699 701 'state': pr.pull_request_state,
700 702 'author': _render('pullrequest_author',
701 703 pr.author.full_contact, ),
702 704 'author_raw': pr.author.full_name,
703 705 'comments': _render('pullrequest_comments', len(comments)),
704 706 'comments_raw': len(comments),
705 707 'closed': pr.is_closed(),
706 708 'owned': owned
707 709 })
708 710
709 711 # json used to render the grid
710 712 data = ({
711 713 'draw': draw,
712 714 'data': data,
713 715 'recordsTotal': pull_requests_total_count,
714 716 'recordsFiltered': pull_requests_total_count,
715 717 })
716 718 return data
717 719
718 720 @LoginRequired()
719 721 @NotAnonymous()
720 722 @view_config(
721 723 route_name='my_account_pullrequests',
722 724 request_method='GET',
723 725 renderer='rhodecode:templates/admin/my_account/my_account.mako')
724 726 def my_account_pullrequests(self):
725 727 c = self.load_default_context()
726 728 c.active = 'pullrequests'
727 729 req_get = self.request.GET
728 730
729 731 c.closed = str2bool(req_get.get('pr_show_closed'))
730 732
731 733 return self._get_template_context(c)
732 734
733 735 @LoginRequired()
734 736 @NotAnonymous()
735 737 @view_config(
736 738 route_name='my_account_pullrequests_data',
737 739 request_method='GET', renderer='json_ext')
738 740 def my_account_pullrequests_data(self):
739 741 self.load_default_context()
740 742 req_get = self.request.GET
741 743 closed = str2bool(req_get.get('closed'))
742 744
743 745 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
744 746 if closed:
745 747 statuses += [PullRequest.STATUS_CLOSED]
746 748
747 749 data = self._get_pull_requests_list(statuses=statuses)
748 750 return data
749 751
750 752 @LoginRequired()
751 753 @NotAnonymous()
752 754 @view_config(
753 755 route_name='my_account_user_group_membership',
754 756 request_method='GET',
755 757 renderer='rhodecode:templates/admin/my_account/my_account.mako')
756 758 def my_account_user_group_membership(self):
757 759 c = self.load_default_context()
758 760 c.active = 'user_group_membership'
759 761 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
760 762 for group in self._rhodecode_db_user.group_member]
761 763 c.user_groups = json.dumps(groups)
762 764 return self._get_template_context(c)
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now