Show More
@@ -0,0 +1,76 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | # Copyright (C) 2010-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 | ||
|
21 | import pytest | |
|
22 | ||
|
23 | from rhodecode.apps._base import ADMIN_PREFIX | |
|
24 | from rhodecode.model.db import User, UserEmailMap, Repository, UserFollowing | |
|
25 | from rhodecode.tests import ( | |
|
26 | TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL, | |
|
27 | assert_session_flash) | |
|
28 | from rhodecode.tests.fixture import Fixture | |
|
29 | ||
|
30 | fixture = Fixture() | |
|
31 | ||
|
32 | ||
|
33 | def route_path(name, **kwargs): | |
|
34 | return { | |
|
35 | 'my_account_repos': | |
|
36 | ADMIN_PREFIX + '/my_account/repos', | |
|
37 | 'my_account_watched': | |
|
38 | ADMIN_PREFIX + '/my_account/watched', | |
|
39 | 'my_account_perms': | |
|
40 | ADMIN_PREFIX + '/my_account/perms', | |
|
41 | 'my_account_notifications': | |
|
42 | ADMIN_PREFIX + '/my_account/notifications', | |
|
43 | }[name].format(**kwargs) | |
|
44 | ||
|
45 | ||
|
46 | class TestMyAccountSimpleViews(TestController): | |
|
47 | ||
|
48 | def test_my_account_my_repos(self, autologin_user): | |
|
49 | response = self.app.get(route_path('my_account_repos')) | |
|
50 | repos = Repository.query().filter( | |
|
51 | Repository.user == User.get_by_username( | |
|
52 | TEST_USER_ADMIN_LOGIN)).all() | |
|
53 | for repo in repos: | |
|
54 | response.mustcontain('"name_raw": "%s"' % repo.repo_name) | |
|
55 | ||
|
56 | def test_my_account_my_watched(self, autologin_user): | |
|
57 | response = self.app.get(route_path('my_account_watched')) | |
|
58 | ||
|
59 | repos = UserFollowing.query().filter( | |
|
60 | UserFollowing.user == User.get_by_username( | |
|
61 | TEST_USER_ADMIN_LOGIN)).all() | |
|
62 | for repo in repos: | |
|
63 | response.mustcontain( | |
|
64 | '"name_raw": "%s"' % repo.follows_repository.repo_name) | |
|
65 | ||
|
66 | def test_my_account_perms(self, autologin_user): | |
|
67 | response = self.app.get(route_path('my_account_perms')) | |
|
68 | assert_response = response.assert_response() | |
|
69 | assert assert_response.get_elements('.perm_tag.none') | |
|
70 | assert assert_response.get_elements('.perm_tag.read') | |
|
71 | assert assert_response.get_elements('.perm_tag.write') | |
|
72 | assert assert_response.get_elements('.perm_tag.admin') | |
|
73 | ||
|
74 | def test_my_account_notifications(self, autologin_user): | |
|
75 | response = self.app.get(route_path('my_account_notifications')) | |
|
76 | response.mustcontain('Test flash message') |
@@ -56,6 +56,26 b' def includeme(config):' | |||
|
56 | 56 | name='my_account_emails_delete', |
|
57 | 57 | pattern=ADMIN_PREFIX + '/my_account/emails/delete') |
|
58 | 58 | |
|
59 | config.add_route( | |
|
60 | name='my_account_repos', | |
|
61 | pattern=ADMIN_PREFIX + '/my_account/repos') | |
|
62 | ||
|
63 | config.add_route( | |
|
64 | name='my_account_watched', | |
|
65 | pattern=ADMIN_PREFIX + '/my_account/watched') | |
|
66 | ||
|
67 | config.add_route( | |
|
68 | name='my_account_perms', | |
|
69 | pattern=ADMIN_PREFIX + '/my_account/perms') | |
|
70 | ||
|
71 | config.add_route( | |
|
72 | name='my_account_notifications', | |
|
73 | pattern=ADMIN_PREFIX + '/my_account/notifications') | |
|
74 | ||
|
75 | config.add_route( | |
|
76 | name='my_account_notifications_toggle_visibility', | |
|
77 | pattern=ADMIN_PREFIX + '/my_account/toggle_visibility') | |
|
78 | ||
|
59 | 79 | # channelstream test |
|
60 | 80 | config.add_route( |
|
61 | 81 | name='my_account_notifications_test_channelstream', |
@@ -28,14 +28,18 b' from pyramid.view import view_config' | |||
|
28 | 28 | from rhodecode.apps._base import BaseAppView |
|
29 | 29 | from rhodecode import forms |
|
30 | 30 | from rhodecode.lib import helpers as h |
|
31 | from rhodecode.lib.ext_json import json | |
|
31 | 32 | from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired |
|
32 | 33 | from rhodecode.lib.channelstream import channelstream_request, \ |
|
33 | 34 | ChannelstreamException |
|
34 | 35 | from rhodecode.lib.utils2 import safe_int, md5 |
|
35 | 36 | from rhodecode.model.auth_token import AuthTokenModel |
|
36 |
from rhodecode.model.db import |
|
|
37 | from rhodecode.model.db import ( | |
|
38 | Repository, PullRequest, UserEmailMap, User, UserFollowing, joinedload) | |
|
37 | 39 | from rhodecode.model.meta import Session |
|
40 | from rhodecode.model.scm import RepoList | |
|
38 | 41 | from rhodecode.model.user import UserModel |
|
42 | from rhodecode.model.repo import RepoModel | |
|
39 | 43 | from rhodecode.model.validation_schema.schemas import user_schema |
|
40 | 44 | |
|
41 | 45 | log = logging.getLogger(__name__) |
@@ -290,3 +294,85 b' class MyAccountView(BaseAppView):' | |||
|
290 | 294 | return {"response": 'ERROR: {}'.format(e.__class__.__name__)} |
|
291 | 295 | return {"response": 'Channelstream data sent. ' |
|
292 | 296 | 'You should see a new live message now.'} |
|
297 | ||
|
298 | def _load_my_repos_data(self, watched=False): | |
|
299 | if watched: | |
|
300 | admin = False | |
|
301 | follows_repos = Session().query(UserFollowing)\ | |
|
302 | .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\ | |
|
303 | .options(joinedload(UserFollowing.follows_repository))\ | |
|
304 | .all() | |
|
305 | repo_list = [x.follows_repository for x in follows_repos] | |
|
306 | else: | |
|
307 | admin = True | |
|
308 | repo_list = Repository.get_all_repos( | |
|
309 | user_id=self._rhodecode_user.user_id) | |
|
310 | repo_list = RepoList(repo_list, perm_set=[ | |
|
311 | 'repository.read', 'repository.write', 'repository.admin']) | |
|
312 | ||
|
313 | repos_data = RepoModel().get_repos_as_dict( | |
|
314 | repo_list=repo_list, admin=admin) | |
|
315 | # json used to render the grid | |
|
316 | return json.dumps(repos_data) | |
|
317 | ||
|
318 | @LoginRequired() | |
|
319 | @NotAnonymous() | |
|
320 | @view_config( | |
|
321 | route_name='my_account_repos', request_method='GET', | |
|
322 | renderer='rhodecode:templates/admin/my_account/my_account.mako') | |
|
323 | def my_account_repos(self): | |
|
324 | c = self.load_default_context() | |
|
325 | c.active = 'repos' | |
|
326 | ||
|
327 | # json used to render the grid | |
|
328 | c.data = self._load_my_repos_data() | |
|
329 | return self._get_template_context(c) | |
|
330 | ||
|
331 | @LoginRequired() | |
|
332 | @NotAnonymous() | |
|
333 | @view_config( | |
|
334 | route_name='my_account_watched', request_method='GET', | |
|
335 | renderer='rhodecode:templates/admin/my_account/my_account.mako') | |
|
336 | def my_account_watched(self): | |
|
337 | c = self.load_default_context() | |
|
338 | c.active = 'watched' | |
|
339 | ||
|
340 | # json used to render the grid | |
|
341 | c.data = self._load_my_repos_data(watched=True) | |
|
342 | return self._get_template_context(c) | |
|
343 | ||
|
344 | @LoginRequired() | |
|
345 | @NotAnonymous() | |
|
346 | @view_config( | |
|
347 | route_name='my_account_perms', request_method='GET', | |
|
348 | renderer='rhodecode:templates/admin/my_account/my_account.mako') | |
|
349 | def my_account_perms(self): | |
|
350 | c = self.load_default_context() | |
|
351 | c.active = 'perms' | |
|
352 | ||
|
353 | c.perm_user = c.auth_user | |
|
354 | return self._get_template_context(c) | |
|
355 | ||
|
356 | @LoginRequired() | |
|
357 | @NotAnonymous() | |
|
358 | @view_config( | |
|
359 | route_name='my_account_notifications', request_method='GET', | |
|
360 | renderer='rhodecode:templates/admin/my_account/my_account.mako') | |
|
361 | def my_notifications(self): | |
|
362 | c = self.load_default_context() | |
|
363 | c.active = 'notifications' | |
|
364 | ||
|
365 | return self._get_template_context(c) | |
|
366 | ||
|
367 | @LoginRequired() | |
|
368 | @NotAnonymous() | |
|
369 | @CSRFRequired() | |
|
370 | @view_config( | |
|
371 | route_name='my_account_notifications_toggle_visibility', | |
|
372 | request_method='POST', renderer='json_ext') | |
|
373 | def my_notifications_toggle_visibility(self): | |
|
374 | user = self._rhodecode_db_user | |
|
375 | new_status = not user.user_data.get('notification_status', True) | |
|
376 | user.update_userdata(notification_status=new_status) | |
|
377 | Session().commit() | |
|
378 | return user.user_data['notification_status'] No newline at end of file |
@@ -485,26 +485,9 b' def make_map(config):' | |||
|
485 | 485 | m.connect('my_account_password', '/my_account/password', |
|
486 | 486 | action='my_account_password', conditions={'method': ['GET']}) |
|
487 | 487 | |
|
488 | m.connect('my_account_repos', '/my_account/repos', | |
|
489 | action='my_account_repos', conditions={'method': ['GET']}) | |
|
490 | ||
|
491 | m.connect('my_account_watched', '/my_account/watched', | |
|
492 | action='my_account_watched', conditions={'method': ['GET']}) | |
|
493 | ||
|
494 | 488 | m.connect('my_account_pullrequests', '/my_account/pull_requests', |
|
495 | 489 | action='my_account_pullrequests', conditions={'method': ['GET']}) |
|
496 | 490 | |
|
497 | m.connect('my_account_perms', '/my_account/perms', | |
|
498 | action='my_account_perms', conditions={'method': ['GET']}) | |
|
499 | ||
|
500 | m.connect('my_account_notifications', '/my_account/notifications', | |
|
501 | action='my_notifications', | |
|
502 | conditions={'method': ['GET']}) | |
|
503 | m.connect('my_account_notifications_toggle_visibility', | |
|
504 | '/my_account/toggle_visibility', | |
|
505 | action='my_notifications_toggle_visibility', | |
|
506 | conditions={'method': ['POST']}) | |
|
507 | ||
|
508 | 491 | # NOTIFICATION REST ROUTES |
|
509 | 492 | with rmap.submapper(path_prefix=ADMIN_PREFIX, |
|
510 | 493 | controller='admin/notifications') as m: |
@@ -29,26 +29,22 b' import formencode' | |||
|
29 | 29 | from formencode import htmlfill |
|
30 | 30 | from pyramid.httpexceptions import HTTPFound |
|
31 | 31 | |
|
32 |
from pylons import request, tmpl_context as c |
|
|
32 | from pylons import request, tmpl_context as c | |
|
33 | 33 | from pylons.controllers.util import redirect |
|
34 | 34 | from pylons.i18n.translation import _ |
|
35 | from sqlalchemy.orm import joinedload | |
|
36 | 35 | |
|
37 | 36 | from rhodecode.lib import helpers as h |
|
38 | 37 | from rhodecode.lib import auth |
|
39 | 38 | from rhodecode.lib.auth import ( |
|
40 | 39 | LoginRequired, NotAnonymous, AuthUser) |
|
41 | 40 | from rhodecode.lib.base import BaseController, render |
|
42 | from rhodecode.lib.utils import jsonify | |
|
43 | 41 | from rhodecode.lib.utils2 import safe_int, str2bool |
|
44 | 42 | from rhodecode.lib.ext_json import json |
|
45 | 43 | |
|
46 | 44 | from rhodecode.model.db import ( |
|
47 | 45 | Repository, PullRequest, UserEmailMap, User, UserFollowing) |
|
48 | 46 | from rhodecode.model.forms import UserForm |
|
49 | from rhodecode.model.scm import RepoList | |
|
50 | 47 | from rhodecode.model.user import UserModel |
|
51 | from rhodecode.model.repo import RepoModel | |
|
52 | 48 | from rhodecode.model.meta import Session |
|
53 | 49 | from rhodecode.model.pull_request import PullRequestModel |
|
54 | 50 | from rhodecode.model.comment import CommentsModel |
@@ -78,26 +74,6 b' class MyAccountController(BaseController' | |||
|
78 | 74 | c.auth_user = AuthUser( |
|
79 | 75 | user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr) |
|
80 | 76 | |
|
81 | def _load_my_repos_data(self, watched=False): | |
|
82 | if watched: | |
|
83 | admin = False | |
|
84 | follows_repos = Session().query(UserFollowing)\ | |
|
85 | .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\ | |
|
86 | .options(joinedload(UserFollowing.follows_repository))\ | |
|
87 | .all() | |
|
88 | repo_list = [x.follows_repository for x in follows_repos] | |
|
89 | else: | |
|
90 | admin = True | |
|
91 | repo_list = Repository.get_all_repos( | |
|
92 | user_id=c.rhodecode_user.user_id) | |
|
93 | repo_list = RepoList(repo_list, perm_set=[ | |
|
94 | 'repository.read', 'repository.write', 'repository.admin']) | |
|
95 | ||
|
96 | repos_data = RepoModel().get_repos_as_dict( | |
|
97 | repo_list=repo_list, admin=admin) | |
|
98 | # json used to render the grid | |
|
99 | return json.dumps(repos_data) | |
|
100 | ||
|
101 | 77 | @auth.CSRFRequired() |
|
102 | 78 | def my_account_update(self): |
|
103 | 79 | """ |
@@ -177,29 +153,6 b' class MyAccountController(BaseController' | |||
|
177 | 153 | force_defaults=False |
|
178 | 154 | ) |
|
179 | 155 | |
|
180 | def my_account_repos(self): | |
|
181 | c.active = 'repos' | |
|
182 | self.__load_data() | |
|
183 | ||
|
184 | # json used to render the grid | |
|
185 | c.data = self._load_my_repos_data() | |
|
186 | return render('admin/my_account/my_account.mako') | |
|
187 | ||
|
188 | def my_account_watched(self): | |
|
189 | c.active = 'watched' | |
|
190 | self.__load_data() | |
|
191 | ||
|
192 | # json used to render the grid | |
|
193 | c.data = self._load_my_repos_data(watched=True) | |
|
194 | return render('admin/my_account/my_account.mako') | |
|
195 | ||
|
196 | def my_account_perms(self): | |
|
197 | c.active = 'perms' | |
|
198 | self.__load_data() | |
|
199 | c.perm_user = c.auth_user | |
|
200 | ||
|
201 | return render('admin/my_account/my_account.mako') | |
|
202 | ||
|
203 | 156 | def _extract_ordering(self, request): |
|
204 | 157 | column_index = safe_int(request.GET.get('order[0][column]')) |
|
205 | 158 | order_dir = request.GET.get('order[0][dir]', 'desc') |
@@ -280,15 +233,4 b' class MyAccountController(BaseController' | |||
|
280 | 233 | else: |
|
281 | 234 | return json.dumps(data) |
|
282 | 235 | |
|
283 | def my_notifications(self): | |
|
284 | c.active = 'notifications' | |
|
285 | return render('admin/my_account/my_account.mako') | |
|
286 | 236 | |
|
287 | @auth.CSRFRequired() | |
|
288 | @jsonify | |
|
289 | def my_notifications_toggle_visibility(self): | |
|
290 | user = c.rhodecode_user.get_instance() | |
|
291 | new_status = not user.user_data.get('notification_status', True) | |
|
292 | user.update_userdata(notification_status=new_status) | |
|
293 | Session().commit() | |
|
294 | return user.user_data['notification_status'] |
@@ -134,6 +134,14 b' function registerRCRoutes() {' | |||
|
134 | 134 | pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []); |
|
135 | 135 | pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []); |
|
136 | 136 | pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []); |
|
137 | pyroutes.register('my_account_emails', '/_admin/my_account/emails', []); | |
|
138 | pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []); | |
|
139 | pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []); | |
|
140 | pyroutes.register('my_account_repos', '/_admin/my_account/repos', []); | |
|
141 | pyroutes.register('my_account_watched', '/_admin/my_account/watched', []); | |
|
142 | pyroutes.register('my_account_perms', '/_admin/my_account/perms', []); | |
|
143 | pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []); | |
|
144 | pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []); | |
|
137 | 145 | pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []); |
|
138 | 146 | pyroutes.register('apiv2', '/_admin/api', []); |
|
139 | 147 | } |
@@ -35,11 +35,11 b'' | |||
|
35 | 35 | <li class="${'active' if c.active=='oauth' else ''}"><a href="${my_account_oauth_url}">${_('OAuth Identities')}</a></li> |
|
36 | 36 | % endif |
|
37 | 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. |
|
|
39 |
<li class="${'active' if c.active=='watched' else ''}"><a href="${h. |
|
|
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> | |
|
40 | 40 | <li class="${'active' if c.active=='pullrequests' else ''}"><a href="${h.url('my_account_pullrequests')}">${_('Pull Requests')}</a></li> |
|
41 |
<li class="${'active' if c.active=='perms' else ''}"><a href="${h. |
|
|
42 |
<li class="${'active' if c.active=='my_notifications' else ''}"><a href="${h. |
|
|
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> | |
|
43 | 43 | </ul> |
|
44 | 44 | </div> |
|
45 | 45 |
@@ -1,7 +1,7 b'' | |||
|
1 | 1 | <template is="dom-bind" id="notificationsPage"> |
|
2 | 2 | <iron-ajax id="toggleNotifications" |
|
3 | 3 | method="post" |
|
4 |
url="${ |
|
|
4 | url="${h.route_path('my_account_notifications_toggle_visibility')}" | |
|
5 | 5 | content-type="application/json" |
|
6 | 6 | loading="{{changeNotificationsLoading}}" |
|
7 | 7 | on-response="handleNotifications" |
@@ -57,26 +57,6 b' class TestMyAccountController(TestContro' | |||
|
57 | 57 | |
|
58 | 58 | response.mustcontain('value="test_admin') |
|
59 | 59 | |
|
60 | def test_my_account_my_repos(self): | |
|
61 | self.log_user() | |
|
62 | response = self.app.get(url('my_account_repos')) | |
|
63 | repos = Repository.query().filter( | |
|
64 | Repository.user == User.get_by_username( | |
|
65 | TEST_USER_ADMIN_LOGIN)).all() | |
|
66 | for repo in repos: | |
|
67 | response.mustcontain('"name_raw": "%s"' % repo.repo_name) | |
|
68 | ||
|
69 | def test_my_account_my_watched(self): | |
|
70 | self.log_user() | |
|
71 | response = self.app.get(url('my_account_watched')) | |
|
72 | ||
|
73 | repos = UserFollowing.query().filter( | |
|
74 | UserFollowing.user == User.get_by_username( | |
|
75 | TEST_USER_ADMIN_LOGIN)).all() | |
|
76 | for repo in repos: | |
|
77 | response.mustcontain( | |
|
78 | '"name_raw": "%s"' % repo.follows_repository.repo_name) | |
|
79 | ||
|
80 | 60 | @pytest.mark.backends("git", "hg") |
|
81 | 61 | def test_my_account_my_pullrequests(self, pr_util): |
|
82 | 62 | self.log_user() |
@@ -89,8 +69,6 b' class TestMyAccountController(TestContro' | |||
|
89 | 69 | response.mustcontain('"name_raw": %s' % pr.pull_request_id) |
|
90 | 70 | response.mustcontain('TestMyAccountPR') |
|
91 | 71 | |
|
92 | ||
|
93 | ||
|
94 | 72 | @pytest.mark.parametrize( |
|
95 | 73 | "name, attrs", [ |
|
96 | 74 | ('firstname', {'firstname': 'new_username'}), |
General Comments 0
You need to be logged in to leave comments.
Login now