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 | 132 | response.mustcontain(version_string) |
|
133 | 133 | if state is False: |
|
134 | 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 | 28 | name='my_account_profile', |
|
29 | 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 | 40 | config.add_route( |
|
32 | 41 | name='my_account_password', |
|
33 | 42 | pattern=ADMIN_PREFIX + '/my_account/password') |
@@ -36,6 +45,7 b' def includeme(config):' | |||
|
36 | 45 | name='my_account_password_update', |
|
37 | 46 | pattern=ADMIN_PREFIX + '/my_account/password') |
|
38 | 47 | |
|
48 | # my account tokens | |
|
39 | 49 | config.add_route( |
|
40 | 50 | name='my_account_auth_tokens', |
|
41 | 51 | pattern=ADMIN_PREFIX + '/my_account/auth_tokens') |
@@ -46,6 +56,7 b' def includeme(config):' | |||
|
46 | 56 | name='my_account_auth_tokens_delete', |
|
47 | 57 | pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete') |
|
48 | 58 | |
|
59 | # my account emails | |
|
49 | 60 | config.add_route( |
|
50 | 61 | name='my_account_emails', |
|
51 | 62 | pattern=ADMIN_PREFIX + '/my_account/emails') |
@@ -76,6 +87,14 b' def includeme(config):' | |||
|
76 | 87 | name='my_account_notifications_toggle_visibility', |
|
77 | 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 | 98 | # channelstream test |
|
80 | 99 | config.add_route( |
|
81 | 100 | name='my_account_notifications_test_channelstream', |
@@ -24,8 +24,10 b' import datetime' | |||
|
24 | 24 | import formencode |
|
25 | 25 | from pyramid.httpexceptions import HTTPFound |
|
26 | 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 | 31 | from rhodecode import forms |
|
30 | 32 | from rhodecode.lib import helpers as h |
|
31 | 33 | from rhodecode.lib import audit_logger |
@@ -33,11 +35,16 b' from rhodecode.lib.ext_json import json' | |||
|
33 | 35 | from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired |
|
34 | 36 | from rhodecode.lib.channelstream import channelstream_request, \ |
|
35 | 37 | ChannelstreamException |
|
36 |
from rhodecode.lib.utils |
|
|
38 | from rhodecode.lib.utils import PartialRenderer | |
|
39 | from rhodecode.lib.utils2 import safe_int, md5, str2bool | |
|
37 | 40 | from rhodecode.model.auth_token import AuthTokenModel |
|
41 | from rhodecode.model.comment import CommentsModel | |
|
38 | 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 | 46 | from rhodecode.model.meta import Session |
|
47 | from rhodecode.model.pull_request import PullRequestModel | |
|
41 | 48 | from rhodecode.model.scm import RepoList |
|
42 | 49 | from rhodecode.model.user import UserModel |
|
43 | 50 | from rhodecode.model.repo import RepoModel |
@@ -46,7 +53,7 b' from rhodecode.model.validation_schema.s' | |||
|
46 | 53 | log = logging.getLogger(__name__) |
|
47 | 54 | |
|
48 | 55 | |
|
49 | class MyAccountView(BaseAppView): | |
|
56 | class MyAccountView(BaseAppView, DataGridAppView): | |
|
50 | 57 | ALLOW_SCOPED_TOKENS = False |
|
51 | 58 | """ |
|
52 | 59 | This view has alternative version inside EE, if modified please take a look |
@@ -396,4 +403,182 b' class MyAccountView(BaseAppView):' | |||
|
396 | 403 | new_status = not user.user_data.get('notification_status', True) |
|
397 | 404 | user.update_userdata(notification_status=new_status) |
|
398 | 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 | 462 | with rmap.submapper(path_prefix=ADMIN_PREFIX, |
|
463 | 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 | 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 | 467 | m.connect('my_account_password', '/my_account/password', |
|
473 | 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 | 470 | # NOTIFICATION REST ROUTES |
|
479 | 471 | with rmap.submapper(path_prefix=ADMIN_PREFIX, |
|
480 | 472 | controller='admin/notifications') as m: |
@@ -136,6 +136,8 b' function registerRCRoutes() {' | |||
|
136 | 136 | pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']); |
|
137 | 137 | pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']); |
|
138 | 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 | 141 | pyroutes.register('my_account_password', '/_admin/my_account/password', []); |
|
140 | 142 | pyroutes.register('my_account_password_update', '/_admin/my_account/password', []); |
|
141 | 143 | pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []); |
@@ -149,6 +151,8 b' function registerRCRoutes() {' | |||
|
149 | 151 | pyroutes.register('my_account_perms', '/_admin/my_account/perms', []); |
|
150 | 152 | pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []); |
|
151 | 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 | 156 | pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []); |
|
153 | 157 | pyroutes.register('gists_show', '/_admin/gists', []); |
|
154 | 158 | pyroutes.register('gists_new', '/_admin/gists/new', []); |
@@ -37,7 +37,7 b'' | |||
|
37 | 37 | <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li> |
|
38 | 38 | <li class="${'active' if c.active=='repos' else ''}"><a href="${h.route_path('my_account_repos')}">${_('Repositories')}</a></li> |
|
39 | 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. |
|
|
40 | <li class="${'active' if c.active=='pullrequests' else ''}"><a href="${h.route_path('my_account_pullrequests')}">${_('Pull Requests')}</a></li> | |
|
41 | 41 | <li class="${'active' if c.active=='perms' else ''}"><a href="${h.route_path('my_account_perms')}">${_('Permissions')}</a></li> |
|
42 | 42 | <li class="${'active' if c.active=='my_notifications' else ''}"><a href="${h.route_path('my_account_notifications')}">${_('Live Notifications')}</a></li> |
|
43 | 43 | </ul> |
@@ -2,7 +2,7 b'' | |||
|
2 | 2 | <div class="panel panel-default user-profile"> |
|
3 | 3 | <div class="panel-heading"> |
|
4 | 4 | <h3 class="panel-title">${_('My Profile')}</h3> |
|
5 |
<a href="${ |
|
|
5 | <a href="${h.route_path('my_account_edit')}" class="panel-edit">${_('Edit')}</a> | |
|
6 | 6 | </div> |
|
7 | 7 | |
|
8 | 8 | <div class="panel-body"> |
@@ -6,7 +6,7 b'' | |||
|
6 | 6 | </div> |
|
7 | 7 | |
|
8 | 8 | <div class="panel-body"> |
|
9 |
${h.secure_form( |
|
|
9 | ${h.secure_form(h.route_path('my_account_update'), class_='form', method='POST')} | |
|
10 | 10 | <% readonly = None %> |
|
11 | 11 | <% disabled = "" %> |
|
12 | 12 |
@@ -2,7 +2,7 b'' | |||
|
2 | 2 | |
|
3 | 3 | <div class="panel panel-default"> |
|
4 | 4 | <div class="panel-body"> |
|
5 |
%if c. |
|
|
5 | %if c.closed: | |
|
6 | 6 | ${h.checkbox('show_closed',checked="checked", label=_('Show Closed Pull Requests'))} |
|
7 | 7 | %else: |
|
8 | 8 | ${h.checkbox('show_closed',label=_('Show Closed Pull Requests'))} |
@@ -12,25 +12,41 b'' | |||
|
12 | 12 | |
|
13 | 13 | <div class="panel panel-default"> |
|
14 | 14 | <div class="panel-heading"> |
|
15 |
<h3 class="panel-title">${_('Pull Requests You Participate In')} |
|
|
15 | <h3 class="panel-title">${_('Pull Requests You Participate In')}</h3> | |
|
16 | 16 | </div> |
|
17 | <div class="panel-body"> | |
|
18 |
<table id="pull_request_list_table |
|
|
17 | <div class="panel-body panel-body-min-height"> | |
|
18 | <table id="pull_request_list_table" class="display"></table> | |
|
19 | 19 | </div> |
|
20 | 20 | </div> |
|
21 | 21 | |
|
22 | <script> | |
|
22 | <script type="text/javascript"> | |
|
23 | $(document).ready(function() { | |
|
24 | ||
|
23 | 25 | $('#show_closed').on('click', function(e){ |
|
24 | 26 | if($(this).is(":checked")){ |
|
25 |
window.location = "${h. |
|
|
27 | window.location = "${h.route_path('my_account_pullrequests', _query={'pr_show_closed':1})}"; | |
|
26 | 28 | } |
|
27 | 29 | else{ |
|
28 |
window.location = "${h. |
|
|
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 | 50 | { data: {"_": "status", |
|
35 | 51 | "sort": "status"}, title: "", className: "td-status", orderable: false}, |
|
36 | 52 | { data: {"_": "target_repo", |
@@ -45,21 +61,10 b'' | |||
|
45 | 61 | "sort": "comments_raw"}, title: "", className: "td-comments", orderable: false}, |
|
46 | 62 | { data: {"_": "updated_on", |
|
47 | 63 | "sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" } |
|
48 |
] |
|
|
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, | |
|
64 | ], | |
|
61 | 65 | language: { |
|
62 | 66 | paginate: DEFAULT_GRID_PAGINATION, |
|
67 | sProcessing: _gettext('loading...'), | |
|
63 | 68 | emptyTable: _gettext("There are currently no open pull requests requiring your participation.") |
|
64 | 69 | }, |
|
65 | 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 | 90 | </script> |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now