##// END OF EJS Templates
my-account: migrated left over controller functions into pyramid views....
dan -
r1892:89ddfad2 default
parent child Browse files
Show More
@@ -0,0 +1,205 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 # -*- coding: utf-8 -*-
21
22 # Copyright (C) 2016-2017 RhodeCode GmbH
23 #
24 # This program is free software: you can redistribute it and/or modify
25 # it under the terms of the GNU Affero General Public License, version 3
26 # (only), as published by the Free Software Foundation.
27 #
28 # This program is distributed in the hope that it will be useful,
29 # but WITHOUT ANY WARRANTY; without even the implied warranty of
30 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 # GNU General Public License for more details.
32 #
33 # You should have received a copy of the GNU Affero General Public License
34 # along with this program. If not, see <http://www.gnu.org/licenses/>.
35 #
36 # This program is dual-licensed. If you wish to learn more about the
37 # RhodeCode Enterprise Edition, including its added features, Support services,
38 # and proprietary license terms, please see https://rhodecode.com/licenses/
39
40 import pytest
41
42 from rhodecode.model.db import User
43 from rhodecode.tests import TestController, assert_session_flash
44 from rhodecode.lib import helpers as h
45
46
47 def route_path(name, params=None, **kwargs):
48 import urllib
49 from rhodecode.apps._base import ADMIN_PREFIX
50
51 base_url = {
52 'my_account_edit': ADMIN_PREFIX + '/my_account/edit',
53 'my_account_update': ADMIN_PREFIX + '/my_account/update',
54 'my_account_pullrequests': ADMIN_PREFIX + '/my_account/pull_requests',
55 'my_account_pullrequests_data': ADMIN_PREFIX + '/my_account/pull_requests/data',
56 }[name].format(**kwargs)
57
58 if params:
59 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
60 return base_url
61
62
63 class TestMyAccountEdit(TestController):
64
65 def test_my_account_edit(self):
66 self.log_user()
67 response = self.app.get(route_path('my_account_edit'))
68
69 response.mustcontain('value="test_admin')
70
71 @pytest.mark.backends("git", "hg")
72 def test_my_account_my_pullrequests(self, pr_util):
73 self.log_user()
74 response = self.app.get(route_path('my_account_pullrequests'))
75 response.mustcontain('There are currently no open pull '
76 'requests requiring your participation.')
77
78 @pytest.mark.backends("git", "hg")
79 def test_my_account_my_pullrequests_data(self, pr_util, xhr_header):
80 self.log_user()
81 response = self.app.get(route_path('my_account_pullrequests_data'),
82 extra_environ=xhr_header)
83 assert response.json == {
84 u'data': [], u'draw': None,
85 u'recordsFiltered': 0, u'recordsTotal': 0}
86
87 pr = pr_util.create_pull_request(title='TestMyAccountPR')
88 expected = {
89 'author_raw': 'RhodeCode Admin',
90 'name_raw': pr.pull_request_id
91 }
92 response = self.app.get(route_path('my_account_pullrequests_data'),
93 extra_environ=xhr_header)
94 assert response.json['recordsTotal'] == 1
95 assert response.json['data'][0]['author_raw'] == expected['author_raw']
96
97 assert response.json['data'][0]['author_raw'] == expected['author_raw']
98 assert response.json['data'][0]['name_raw'] == expected['name_raw']
99
100 @pytest.mark.parametrize(
101 "name, attrs", [
102 ('firstname', {'firstname': 'new_username'}),
103 ('lastname', {'lastname': 'new_username'}),
104 ('admin', {'admin': True}),
105 ('admin', {'admin': False}),
106 ('extern_type', {'extern_type': 'ldap'}),
107 ('extern_type', {'extern_type': None}),
108 # ('extern_name', {'extern_name': 'test'}),
109 # ('extern_name', {'extern_name': None}),
110 ('active', {'active': False}),
111 ('active', {'active': True}),
112 ('email', {'email': 'some@email.com'}),
113 ])
114 def test_my_account_update(self, name, attrs, user_util):
115 usr = user_util.create_user(password='qweqwe')
116 params = usr.get_api_data() # current user data
117 user_id = usr.user_id
118 self.log_user(
119 username=usr.username, password='qweqwe')
120
121 params.update({'password_confirmation': ''})
122 params.update({'new_password': ''})
123 params.update({'extern_type': 'rhodecode'})
124 params.update({'extern_name': 'rhodecode'})
125 params.update({'csrf_token': self.csrf_token})
126
127 params.update(attrs)
128 # my account page cannot set language param yet, only for admins
129 del params['language']
130 response = self.app.post(route_path('my_account_update'), params)
131
132 assert_session_flash(
133 response, 'Your account was updated successfully')
134
135 del params['csrf_token']
136
137 updated_user = User.get(user_id)
138 updated_params = updated_user.get_api_data()
139 updated_params.update({'password_confirmation': ''})
140 updated_params.update({'new_password': ''})
141
142 params['last_login'] = updated_params['last_login']
143 params['last_activity'] = updated_params['last_activity']
144 # my account page cannot set language param yet, only for admins
145 # but we get this info from API anyway
146 params['language'] = updated_params['language']
147
148 if name == 'email':
149 params['emails'] = [attrs['email']]
150 if name == 'extern_type':
151 # cannot update this via form, expected value is original one
152 params['extern_type'] = "rhodecode"
153 if name == 'extern_name':
154 # cannot update this via form, expected value is original one
155 params['extern_name'] = str(user_id)
156 if name == 'active':
157 # my account cannot deactivate account
158 params['active'] = True
159 if name == 'admin':
160 # my account cannot make you an admin !
161 params['admin'] = False
162
163 assert params == updated_params
164
165 def test_my_account_update_err_email_exists(self):
166 self.log_user()
167
168 new_email = 'test_regular@mail.com' # already existing email
169 params = {
170 'username': 'test_admin',
171 'new_password': 'test12',
172 'password_confirmation': 'test122',
173 'firstname': 'NewName',
174 'lastname': 'NewLastname',
175 'email': new_email,
176 'csrf_token': self.csrf_token,
177 }
178
179 response = self.app.post(route_path('my_account_update'),
180 params=params)
181
182 response.mustcontain('This e-mail address is already taken')
183
184 def test_my_account_update_bad_email_address(self):
185 self.log_user('test_regular2', 'test12')
186
187 new_email = 'newmail.pl'
188 params = {
189 'username': 'test_admin',
190 'new_password': 'test12',
191 'password_confirmation': 'test122',
192 'firstname': 'NewName',
193 'lastname': 'NewLastname',
194 'email': new_email,
195 'csrf_token': self.csrf_token,
196 }
197 response = self.app.post(route_path('my_account_update'),
198 params=params)
199
200 response.mustcontain('An email address must contain a single @')
201 from rhodecode.model import validators
202 msg = validators.ValidUsername(
203 edit=False, old_data={})._messages['username_exists']
204 msg = h.html_escape(msg % {'username': 'test_admin'})
205 response.mustcontain(u"%s" % msg)
@@ -132,3 +132,9 b' class TestHomeController(TestController)'
132 response.mustcontain(version_string)
132 response.mustcontain(version_string)
133 if state is False:
133 if state is False:
134 response.mustcontain(no=[version_string])
134 response.mustcontain(no=[version_string])
135
136 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
137 response = self.app.get(route_path('home'))
138 assert_response = response.assert_response()
139 element = assert_response.get_element('.logout #csrf_token')
140 assert element.value == csrf_token
@@ -28,6 +28,15 b' def includeme(config):'
28 name='my_account_profile',
28 name='my_account_profile',
29 pattern=ADMIN_PREFIX + '/my_account/profile')
29 pattern=ADMIN_PREFIX + '/my_account/profile')
30
30
31 # my account edit details
32 config.add_route(
33 name='my_account_edit',
34 pattern=ADMIN_PREFIX + '/my_account/edit')
35 config.add_route(
36 name='my_account_update',
37 pattern=ADMIN_PREFIX + '/my_account/update')
38
39 # my account password
31 config.add_route(
40 config.add_route(
32 name='my_account_password',
41 name='my_account_password',
33 pattern=ADMIN_PREFIX + '/my_account/password')
42 pattern=ADMIN_PREFIX + '/my_account/password')
@@ -36,6 +45,7 b' def includeme(config):'
36 name='my_account_password_update',
45 name='my_account_password_update',
37 pattern=ADMIN_PREFIX + '/my_account/password')
46 pattern=ADMIN_PREFIX + '/my_account/password')
38
47
48 # my account tokens
39 config.add_route(
49 config.add_route(
40 name='my_account_auth_tokens',
50 name='my_account_auth_tokens',
41 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
51 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
@@ -46,6 +56,7 b' def includeme(config):'
46 name='my_account_auth_tokens_delete',
56 name='my_account_auth_tokens_delete',
47 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete')
57 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete')
48
58
59 # my account emails
49 config.add_route(
60 config.add_route(
50 name='my_account_emails',
61 name='my_account_emails',
51 pattern=ADMIN_PREFIX + '/my_account/emails')
62 pattern=ADMIN_PREFIX + '/my_account/emails')
@@ -76,6 +87,14 b' def includeme(config):'
76 name='my_account_notifications_toggle_visibility',
87 name='my_account_notifications_toggle_visibility',
77 pattern=ADMIN_PREFIX + '/my_account/toggle_visibility')
88 pattern=ADMIN_PREFIX + '/my_account/toggle_visibility')
78
89
90 # my account pull requests
91 config.add_route(
92 name='my_account_pullrequests',
93 pattern=ADMIN_PREFIX + '/my_account/pull_requests')
94 config.add_route(
95 name='my_account_pullrequests_data',
96 pattern=ADMIN_PREFIX + '/my_account/pull_requests/data')
97
79 # channelstream test
98 # channelstream test
80 config.add_route(
99 config.add_route(
81 name='my_account_notifications_test_channelstream',
100 name='my_account_notifications_test_channelstream',
@@ -24,8 +24,10 b' import datetime'
24 import formencode
24 import formencode
25 from pyramid.httpexceptions import HTTPFound
25 from pyramid.httpexceptions import HTTPFound
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27 from pyramid.renderers import render
28 from pyramid.response import Response
27
29
28 from rhodecode.apps._base import BaseAppView
30 from rhodecode.apps._base import BaseAppView, DataGridAppView
29 from rhodecode import forms
31 from rhodecode import forms
30 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
31 from rhodecode.lib import audit_logger
33 from rhodecode.lib import audit_logger
@@ -33,11 +35,16 b' from rhodecode.lib.ext_json import json'
33 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
35 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
34 from rhodecode.lib.channelstream import channelstream_request, \
36 from rhodecode.lib.channelstream import channelstream_request, \
35 ChannelstreamException
37 ChannelstreamException
36 from rhodecode.lib.utils2 import safe_int, md5
38 from rhodecode.lib.utils import PartialRenderer
39 from rhodecode.lib.utils2 import safe_int, md5, str2bool
37 from rhodecode.model.auth_token import AuthTokenModel
40 from rhodecode.model.auth_token import AuthTokenModel
41 from rhodecode.model.comment import CommentsModel
38 from rhodecode.model.db import (
42 from rhodecode.model.db import (
39 Repository, UserEmailMap, UserApiKeys, UserFollowing, joinedload)
43 Repository, UserEmailMap, UserApiKeys, UserFollowing, joinedload,
44 PullRequest)
45 from rhodecode.model.forms import UserForm
40 from rhodecode.model.meta import Session
46 from rhodecode.model.meta import Session
47 from rhodecode.model.pull_request import PullRequestModel
41 from rhodecode.model.scm import RepoList
48 from rhodecode.model.scm import RepoList
42 from rhodecode.model.user import UserModel
49 from rhodecode.model.user import UserModel
43 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
@@ -46,7 +53,7 b' from rhodecode.model.validation_schema.s'
46 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
47
54
48
55
49 class MyAccountView(BaseAppView):
56 class MyAccountView(BaseAppView, DataGridAppView):
50 ALLOW_SCOPED_TOKENS = False
57 ALLOW_SCOPED_TOKENS = False
51 """
58 """
52 This view has alternative version inside EE, if modified please take a look
59 This view has alternative version inside EE, if modified please take a look
@@ -396,4 +403,182 b' class MyAccountView(BaseAppView):'
396 new_status = not user.user_data.get('notification_status', True)
403 new_status = not user.user_data.get('notification_status', True)
397 user.update_userdata(notification_status=new_status)
404 user.update_userdata(notification_status=new_status)
398 Session().commit()
405 Session().commit()
399 return user.user_data['notification_status'] No newline at end of file
406 return user.user_data['notification_status']
407
408 @LoginRequired()
409 @NotAnonymous()
410 @view_config(
411 route_name='my_account_edit',
412 request_method='GET',
413 renderer='rhodecode:templates/admin/my_account/my_account.mako')
414 def my_account_edit(self):
415 c = self.load_default_context()
416 c.active = 'profile_edit'
417
418 c.perm_user = c.auth_user
419 c.extern_type = c.user.extern_type
420 c.extern_name = c.user.extern_name
421
422 defaults = c.user.get_dict()
423
424 data = render('rhodecode:templates/admin/my_account/my_account.mako',
425 self._get_template_context(c), self.request)
426 html = formencode.htmlfill.render(
427 data,
428 defaults=defaults,
429 encoding="UTF-8",
430 force_defaults=False
431 )
432 return Response(html)
433
434 @LoginRequired()
435 @NotAnonymous()
436 @CSRFRequired()
437 @view_config(
438 route_name='my_account_update',
439 request_method='POST',
440 renderer='rhodecode:templates/admin/my_account/my_account.mako')
441 def my_account_update(self):
442 _ = self.request.translate
443 c = self.load_default_context()
444 c.active = 'profile_edit'
445
446 c.perm_user = c.auth_user
447 c.extern_type = c.user.extern_type
448 c.extern_name = c.user.extern_name
449
450 _form = UserForm(edit=True,
451 old_data={'user_id': self._rhodecode_user.user_id,
452 'email': self._rhodecode_user.email})()
453 form_result = {}
454 try:
455 post_data = dict(self.request.POST)
456 post_data['new_password'] = ''
457 post_data['password_confirmation'] = ''
458 form_result = _form.to_python(post_data)
459 # skip updating those attrs for my account
460 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
461 'new_password', 'password_confirmation']
462 # TODO: plugin should define if username can be updated
463 if c.extern_type != "rhodecode":
464 # forbid updating username for external accounts
465 skip_attrs.append('username')
466
467 UserModel().update_user(
468 self._rhodecode_user.user_id, skip_attrs=skip_attrs,
469 **form_result)
470 h.flash(_('Your account was updated successfully'),
471 category='success')
472 Session().commit()
473
474 except formencode.Invalid as errors:
475 data = render(
476 'rhodecode:templates/admin/my_account/my_account.mako',
477 self._get_template_context(c), self.request)
478
479 html = formencode.htmlfill.render(
480 data,
481 defaults=errors.value,
482 errors=errors.error_dict or {},
483 prefix_error=False,
484 encoding="UTF-8",
485 force_defaults=False)
486 return Response(html)
487
488 except Exception:
489 log.exception("Exception updating user")
490 h.flash(_('Error occurred during update of user %s')
491 % form_result.get('username'), category='error')
492 raise HTTPFound(h.route_path('my_account_profile'))
493
494 raise HTTPFound(h.route_path('my_account_profile'))
495
496 def _get_pull_requests_list(self, statuses):
497 draw, start, limit = self._extract_chunk(self.request)
498 search_q, order_by, order_dir = self._extract_ordering(self.request)
499 _render = PartialRenderer('data_table/_dt_elements.mako')
500
501 pull_requests = PullRequestModel().get_im_participating_in(
502 user_id=self._rhodecode_user.user_id,
503 statuses=statuses,
504 offset=start, length=limit, order_by=order_by,
505 order_dir=order_dir)
506
507 pull_requests_total_count = PullRequestModel().count_im_participating_in(
508 user_id=self._rhodecode_user.user_id, statuses=statuses)
509
510 data = []
511 comments_model = CommentsModel()
512 for pr in pull_requests:
513 repo_id = pr.target_repo_id
514 comments = comments_model.get_all_comments(
515 repo_id, pull_request=pr)
516 owned = pr.user_id == self._rhodecode_user.user_id
517
518 data.append({
519 'target_repo': _render('pullrequest_target_repo',
520 pr.target_repo.repo_name),
521 'name': _render('pullrequest_name',
522 pr.pull_request_id, pr.target_repo.repo_name,
523 short=True),
524 'name_raw': pr.pull_request_id,
525 'status': _render('pullrequest_status',
526 pr.calculated_review_status()),
527 'title': _render(
528 'pullrequest_title', pr.title, pr.description),
529 'description': h.escape(pr.description),
530 'updated_on': _render('pullrequest_updated_on',
531 h.datetime_to_time(pr.updated_on)),
532 'updated_on_raw': h.datetime_to_time(pr.updated_on),
533 'created_on': _render('pullrequest_updated_on',
534 h.datetime_to_time(pr.created_on)),
535 'created_on_raw': h.datetime_to_time(pr.created_on),
536 'author': _render('pullrequest_author',
537 pr.author.full_contact, ),
538 'author_raw': pr.author.full_name,
539 'comments': _render('pullrequest_comments', len(comments)),
540 'comments_raw': len(comments),
541 'closed': pr.is_closed(),
542 'owned': owned
543 })
544
545 # json used to render the grid
546 data = ({
547 'draw': draw,
548 'data': data,
549 'recordsTotal': pull_requests_total_count,
550 'recordsFiltered': pull_requests_total_count,
551 })
552 return data
553
554 @LoginRequired()
555 @NotAnonymous()
556 @view_config(
557 route_name='my_account_pullrequests',
558 request_method='GET',
559 renderer='rhodecode:templates/admin/my_account/my_account.mako')
560 def my_account_pullrequests(self):
561 c = self.load_default_context()
562 c.active = 'pullrequests'
563 req_get = self.request.GET
564
565 c.closed = str2bool(req_get.get('pr_show_closed'))
566
567 return self._get_template_context(c)
568
569 @LoginRequired()
570 @NotAnonymous()
571 @view_config(
572 route_name='my_account_pullrequests_data',
573 request_method='GET', renderer='json_ext')
574 def my_account_pullrequests_data(self):
575 req_get = self.request.GET
576 closed = str2bool(req_get.get('closed'))
577
578 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
579 if closed:
580 statuses += [PullRequest.STATUS_CLOSED]
581
582 data = self._get_pull_requests_list(statuses=statuses)
583 return data
584
@@ -462,19 +462,11 b' def make_map(config):'
462 with rmap.submapper(path_prefix=ADMIN_PREFIX,
462 with rmap.submapper(path_prefix=ADMIN_PREFIX,
463 controller='admin/my_account') as m:
463 controller='admin/my_account') as m:
464
464
465 m.connect('my_account_edit', '/my_account/edit',
466 action='my_account_edit', conditions={'method': ['GET']})
467 m.connect('my_account', '/my_account/update',
468 action='my_account_update', conditions={'method': ['POST']})
469
470 # NOTE(marcink): this needs to be kept for password force flag to be
465 # NOTE(marcink): this needs to be kept for password force flag to be
471 # handler, remove after migration to pyramid
466 # handled in pylons controllers, remove after full migration to pyramid
472 m.connect('my_account_password', '/my_account/password',
467 m.connect('my_account_password', '/my_account/password',
473 action='my_account_password', conditions={'method': ['GET']})
468 action='my_account_password', conditions={'method': ['GET']})
474
469
475 m.connect('my_account_pullrequests', '/my_account/pull_requests',
476 action='my_account_pullrequests', conditions={'method': ['GET']})
477
478 # NOTIFICATION REST ROUTES
470 # NOTIFICATION REST ROUTES
479 with rmap.submapper(path_prefix=ADMIN_PREFIX,
471 with rmap.submapper(path_prefix=ADMIN_PREFIX,
480 controller='admin/notifications') as m:
472 controller='admin/notifications') as m:
@@ -136,6 +136,8 b' function registerRCRoutes() {'
136 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
136 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
137 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
137 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
138 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
138 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
139 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
140 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
139 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
141 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
140 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
142 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
141 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
143 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
@@ -149,6 +151,8 b' function registerRCRoutes() {'
149 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
151 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
150 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
152 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
151 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
153 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
154 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
155 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
152 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
156 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
153 pyroutes.register('gists_show', '/_admin/gists', []);
157 pyroutes.register('gists_show', '/_admin/gists', []);
154 pyroutes.register('gists_new', '/_admin/gists/new', []);
158 pyroutes.register('gists_new', '/_admin/gists/new', []);
@@ -37,7 +37,7 b''
37 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li>
37 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li>
38 <li class="${'active' if c.active=='repos' else ''}"><a href="${h.route_path('my_account_repos')}">${_('Repositories')}</a></li>
38 <li class="${'active' if c.active=='repos' else ''}"><a href="${h.route_path('my_account_repos')}">${_('Repositories')}</a></li>
39 <li class="${'active' if c.active=='watched' else ''}"><a href="${h.route_path('my_account_watched')}">${_('Watched')}</a></li>
39 <li class="${'active' if c.active=='watched' else ''}"><a href="${h.route_path('my_account_watched')}">${_('Watched')}</a></li>
40 <li class="${'active' if c.active=='pullrequests' else ''}"><a href="${h.url('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
40 <li class="${'active' if c.active=='pullrequests' else ''}"><a href="${h.route_path('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
41 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.route_path('my_account_perms')}">${_('Permissions')}</a></li>
41 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.route_path('my_account_perms')}">${_('Permissions')}</a></li>
42 <li class="${'active' if c.active=='my_notifications' else ''}"><a href="${h.route_path('my_account_notifications')}">${_('Live Notifications')}</a></li>
42 <li class="${'active' if c.active=='my_notifications' else ''}"><a href="${h.route_path('my_account_notifications')}">${_('Live Notifications')}</a></li>
43 </ul>
43 </ul>
@@ -2,7 +2,7 b''
2 <div class="panel panel-default user-profile">
2 <div class="panel panel-default user-profile">
3 <div class="panel-heading">
3 <div class="panel-heading">
4 <h3 class="panel-title">${_('My Profile')}</h3>
4 <h3 class="panel-title">${_('My Profile')}</h3>
5 <a href="${url('my_account_edit')}" class="panel-edit">${_('Edit')}</a>
5 <a href="${h.route_path('my_account_edit')}" class="panel-edit">${_('Edit')}</a>
6 </div>
6 </div>
7
7
8 <div class="panel-body">
8 <div class="panel-body">
@@ -6,7 +6,7 b''
6 </div>
6 </div>
7
7
8 <div class="panel-body">
8 <div class="panel-body">
9 ${h.secure_form(url('my_account'), method='post', class_='form')}
9 ${h.secure_form(h.route_path('my_account_update'), class_='form', method='POST')}
10 <% readonly = None %>
10 <% readonly = None %>
11 <% disabled = "" %>
11 <% disabled = "" %>
12
12
@@ -2,7 +2,7 b''
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-body">
4 <div class="panel-body">
5 %if c.show_closed:
5 %if c.closed:
6 ${h.checkbox('show_closed',checked="checked", label=_('Show Closed Pull Requests'))}
6 ${h.checkbox('show_closed',checked="checked", label=_('Show Closed Pull Requests'))}
7 %else:
7 %else:
8 ${h.checkbox('show_closed',label=_('Show Closed Pull Requests'))}
8 ${h.checkbox('show_closed',label=_('Show Closed Pull Requests'))}
@@ -12,25 +12,41 b''
12
12
13 <div class="panel panel-default">
13 <div class="panel panel-default">
14 <div class="panel-heading">
14 <div class="panel-heading">
15 <h3 class="panel-title">${_('Pull Requests You Participate In')}: ${c.records_total_participate}</h3>
15 <h3 class="panel-title">${_('Pull Requests You Participate In')}</h3>
16 </div>
16 </div>
17 <div class="panel-body">
17 <div class="panel-body panel-body-min-height">
18 <table id="pull_request_list_table_participate" class="display"></table>
18 <table id="pull_request_list_table" class="display"></table>
19 </div>
19 </div>
20 </div>
20 </div>
21
21
22 <script>
22 <script type="text/javascript">
23 $(document).ready(function() {
24
23 $('#show_closed').on('click', function(e){
25 $('#show_closed').on('click', function(e){
24 if($(this).is(":checked")){
26 if($(this).is(":checked")){
25 window.location = "${h.url('my_account_pullrequests', pr_show_closed=1)}";
27 window.location = "${h.route_path('my_account_pullrequests', _query={'pr_show_closed':1})}";
26 }
28 }
27 else{
29 else{
28 window.location = "${h.url('my_account_pullrequests')}";
30 window.location = "${h.route_path('my_account_pullrequests')}";
29 }
31 }
30 });
32 });
31 $(document).ready(function() {
33
34 var $pullRequestListTable = $('#pull_request_list_table');
32
35
33 var columnsDefs = [
36 // participating object list
37 $pullRequestListTable.DataTable({
38 processing: true,
39 serverSide: true,
40 ajax: {
41 "url": "${h.route_path('my_account_pullrequests_data')}",
42 "data": function (d) {
43 d.closed = "${c.closed}";
44 }
45 },
46 dom: 'rtp',
47 pageLength: ${c.visual.dashboard_items},
48 order: [[ 2, "desc" ]],
49 columns: [
34 { data: {"_": "status",
50 { data: {"_": "status",
35 "sort": "status"}, title: "", className: "td-status", orderable: false},
51 "sort": "status"}, title: "", className: "td-status", orderable: false},
36 { data: {"_": "target_repo",
52 { data: {"_": "target_repo",
@@ -45,21 +61,10 b''
45 "sort": "comments_raw"}, title: "", className: "td-comments", orderable: false},
61 "sort": "comments_raw"}, title: "", className: "td-comments", orderable: false},
46 { data: {"_": "updated_on",
62 { data: {"_": "updated_on",
47 "sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" }
63 "sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" }
48 ];
64 ],
49
50 // participating object list
51 $('#pull_request_list_table_participate').DataTable({
52 data: ${c.data_participate|n},
53 processing: true,
54 serverSide: true,
55 deferLoading: ${c.records_total_participate},
56 ajax: "",
57 dom: 'tp',
58 pageLength: ${c.visual.dashboard_items},
59 order: [[ 2, "desc" ]],
60 columns: columnsDefs,
61 language: {
65 language: {
62 paginate: DEFAULT_GRID_PAGINATION,
66 paginate: DEFAULT_GRID_PAGINATION,
67 sProcessing: _gettext('loading...'),
63 emptyTable: _gettext("There are currently no open pull requests requiring your participation.")
68 emptyTable: _gettext("There are currently no open pull requests requiring your participation.")
64 },
69 },
65 "drawCallback": function( settings, json ) {
70 "drawCallback": function( settings, json ) {
@@ -74,5 +79,12 b''
74 }
79 }
75 }
80 }
76 });
81 });
82 $pullRequestListTable.on('xhr.dt', function(e, settings, json, xhr){
83 $pullRequestListTable.css('opacity', 1);
84 });
85
86 $pullRequestListTable.on('preXhr.dt', function(e, settings, data){
87 $pullRequestListTable.css('opacity', 0.3);
88 });
77 });
89 });
78 </script>
90 </script>
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now