Show More
@@ -0,0 +1,93 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 | |||
|
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_emails': | |||
|
36 | ADMIN_PREFIX + '/my_account/emails', | |||
|
37 | 'my_account_emails_add': | |||
|
38 | ADMIN_PREFIX + '/my_account/emails/new', | |||
|
39 | 'my_account_emails_delete': | |||
|
40 | ADMIN_PREFIX + '/my_account/emails/delete', | |||
|
41 | }[name].format(**kwargs) | |||
|
42 | ||||
|
43 | ||||
|
44 | class TestMyAccountEmails(TestController): | |||
|
45 | def test_my_account_my_emails(self): | |||
|
46 | self.log_user() | |||
|
47 | response = self.app.get(route_path('my_account_emails')) | |||
|
48 | response.mustcontain('No additional emails specified') | |||
|
49 | ||||
|
50 | def test_my_account_my_emails_add_existing_email(self): | |||
|
51 | self.log_user() | |||
|
52 | response = self.app.get(route_path('my_account_emails')) | |||
|
53 | response.mustcontain('No additional emails specified') | |||
|
54 | response = self.app.post(route_path('my_account_emails_add'), | |||
|
55 | {'new_email': TEST_USER_REGULAR_EMAIL, | |||
|
56 | 'csrf_token': self.csrf_token}) | |||
|
57 | assert_session_flash(response, 'This e-mail address is already taken') | |||
|
58 | ||||
|
59 | def test_my_account_my_emails_add_mising_email_in_form(self): | |||
|
60 | self.log_user() | |||
|
61 | response = self.app.get(route_path('my_account_emails')) | |||
|
62 | response.mustcontain('No additional emails specified') | |||
|
63 | response = self.app.post(route_path('my_account_emails_add'), | |||
|
64 | {'csrf_token': self.csrf_token}) | |||
|
65 | assert_session_flash(response, 'Please enter an email address') | |||
|
66 | ||||
|
67 | def test_my_account_my_emails_add_remove(self): | |||
|
68 | self.log_user() | |||
|
69 | response = self.app.get(route_path('my_account_emails')) | |||
|
70 | response.mustcontain('No additional emails specified') | |||
|
71 | ||||
|
72 | response = self.app.post(route_path('my_account_emails_add'), | |||
|
73 | {'new_email': 'foo@barz.com', | |||
|
74 | 'csrf_token': self.csrf_token}) | |||
|
75 | ||||
|
76 | response = self.app.get(route_path('my_account_emails')) | |||
|
77 | ||||
|
78 | email_id = UserEmailMap.query().filter( | |||
|
79 | UserEmailMap.user == User.get_by_username( | |||
|
80 | TEST_USER_ADMIN_LOGIN)).filter( | |||
|
81 | UserEmailMap.email == 'foo@barz.com').one().email_id | |||
|
82 | ||||
|
83 | response.mustcontain('foo@barz.com') | |||
|
84 | response.mustcontain('<input id="del_email_id" name="del_email_id" ' | |||
|
85 | 'type="hidden" value="%s" />' % email_id) | |||
|
86 | ||||
|
87 | response = self.app.post( | |||
|
88 | route_path('my_account_emails_delete'), { | |||
|
89 | 'del_email_id': email_id, | |||
|
90 | 'csrf_token': self.csrf_token}) | |||
|
91 | assert_session_flash(response, 'Email successfully deleted') | |||
|
92 | response = self.app.get(route_path('my_account_emails')) | |||
|
93 | response.mustcontain('No additional emails specified') |
@@ -46,6 +46,16 b' def includeme(config):' | |||||
46 | name='my_account_auth_tokens_delete', |
|
46 | name='my_account_auth_tokens_delete', | |
47 | pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete') |
|
47 | pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete') | |
48 |
|
48 | |||
|
49 | config.add_route( | |||
|
50 | name='my_account_emails', | |||
|
51 | pattern=ADMIN_PREFIX + '/my_account/emails') | |||
|
52 | config.add_route( | |||
|
53 | name='my_account_emails_add', | |||
|
54 | pattern=ADMIN_PREFIX + '/my_account/emails/new') | |||
|
55 | config.add_route( | |||
|
56 | name='my_account_emails_delete', | |||
|
57 | pattern=ADMIN_PREFIX + '/my_account/emails/delete') | |||
|
58 | ||||
49 | # channelstream test |
|
59 | # channelstream test | |
50 | config.add_route( |
|
60 | config.add_route( | |
51 | name='my_account_notifications_test_channelstream', |
|
61 | name='my_account_notifications_test_channelstream', |
@@ -21,6 +21,7 b'' | |||||
21 | import logging |
|
21 | import logging | |
22 | import datetime |
|
22 | import datetime | |
23 |
|
23 | |||
|
24 | import formencode | |||
24 | from pyramid.httpexceptions import HTTPFound |
|
25 | from pyramid.httpexceptions import HTTPFound | |
25 | from pyramid.view import view_config |
|
26 | from pyramid.view import view_config | |
26 |
|
27 | |||
@@ -32,6 +33,7 b' from rhodecode.lib.channelstream import ' | |||||
32 | ChannelstreamException |
|
33 | ChannelstreamException | |
33 | from rhodecode.lib.utils2 import safe_int, md5 |
|
34 | from rhodecode.lib.utils2 import safe_int, md5 | |
34 | from rhodecode.model.auth_token import AuthTokenModel |
|
35 | from rhodecode.model.auth_token import AuthTokenModel | |
|
36 | from rhodecode.model.db import UserEmailMap | |||
35 | from rhodecode.model.meta import Session |
|
37 | from rhodecode.model.meta import Session | |
36 | from rhodecode.model.user import UserModel |
|
38 | from rhodecode.model.user import UserModel | |
37 | from rhodecode.model.validation_schema.schemas import user_schema |
|
39 | from rhodecode.model.validation_schema.schemas import user_schema | |
@@ -161,7 +163,7 b' class MyAccountView(BaseAppView):' | |||||
161 | @NotAnonymous() |
|
163 | @NotAnonymous() | |
162 | @CSRFRequired() |
|
164 | @CSRFRequired() | |
163 | @view_config( |
|
165 | @view_config( | |
164 | route_name='my_account_auth_tokens_add', request_method='POST') |
|
166 | route_name='my_account_auth_tokens_add', request_method='POST',) | |
165 | def my_account_auth_tokens_add(self): |
|
167 | def my_account_auth_tokens_add(self): | |
166 | _ = self.request.translate |
|
168 | _ = self.request.translate | |
167 | c = self.load_default_context() |
|
169 | c = self.load_default_context() | |
@@ -198,6 +200,65 b' class MyAccountView(BaseAppView):' | |||||
198 |
|
200 | |||
199 | @LoginRequired() |
|
201 | @LoginRequired() | |
200 | @NotAnonymous() |
|
202 | @NotAnonymous() | |
|
203 | @view_config( | |||
|
204 | route_name='my_account_emails', request_method='GET', | |||
|
205 | renderer='rhodecode:templates/admin/my_account/my_account.mako') | |||
|
206 | def my_account_emails(self): | |||
|
207 | _ = self.request.translate | |||
|
208 | ||||
|
209 | c = self.load_default_context() | |||
|
210 | c.active = 'emails' | |||
|
211 | ||||
|
212 | c.user_email_map = UserEmailMap.query()\ | |||
|
213 | .filter(UserEmailMap.user == c.user).all() | |||
|
214 | return self._get_template_context(c) | |||
|
215 | ||||
|
216 | @LoginRequired() | |||
|
217 | @NotAnonymous() | |||
|
218 | @CSRFRequired() | |||
|
219 | @view_config( | |||
|
220 | route_name='my_account_emails_add', request_method='POST') | |||
|
221 | def my_account_emails_add(self): | |||
|
222 | _ = self.request.translate | |||
|
223 | c = self.load_default_context() | |||
|
224 | ||||
|
225 | email = self.request.POST.get('new_email') | |||
|
226 | ||||
|
227 | try: | |||
|
228 | UserModel().add_extra_email(c.user.user_id, email) | |||
|
229 | Session().commit() | |||
|
230 | h.flash(_("Added new email address `%s` for user account") % email, | |||
|
231 | category='success') | |||
|
232 | except formencode.Invalid as error: | |||
|
233 | msg = error.error_dict['email'] | |||
|
234 | h.flash(msg, category='error') | |||
|
235 | except Exception: | |||
|
236 | log.exception("Exception in my_account_emails") | |||
|
237 | h.flash(_('An error occurred during email saving'), | |||
|
238 | category='error') | |||
|
239 | return HTTPFound(h.route_path('my_account_emails')) | |||
|
240 | ||||
|
241 | @LoginRequired() | |||
|
242 | @NotAnonymous() | |||
|
243 | @CSRFRequired() | |||
|
244 | @view_config( | |||
|
245 | route_name='my_account_emails_delete', request_method='POST') | |||
|
246 | def my_account_emails_delete(self): | |||
|
247 | _ = self.request.translate | |||
|
248 | c = self.load_default_context() | |||
|
249 | ||||
|
250 | del_email_id = self.request.POST.get('del_email_id') | |||
|
251 | if del_email_id: | |||
|
252 | ||||
|
253 | UserModel().delete_extra_email( | |||
|
254 | c.user.user_id, del_email_id) | |||
|
255 | Session().commit() | |||
|
256 | h.flash(_("Email successfully deleted"), | |||
|
257 | category='success') | |||
|
258 | return HTTPFound(h.route_path('my_account_emails')) | |||
|
259 | ||||
|
260 | @LoginRequired() | |||
|
261 | @NotAnonymous() | |||
201 | @CSRFRequired() |
|
262 | @CSRFRequired() | |
202 | @view_config( |
|
263 | @view_config( | |
203 | route_name='my_account_notifications_test_channelstream', |
|
264 | route_name='my_account_notifications_test_channelstream', |
@@ -497,13 +497,6 b' def make_map(config):' | |||||
497 | m.connect('my_account_perms', '/my_account/perms', |
|
497 | m.connect('my_account_perms', '/my_account/perms', | |
498 | action='my_account_perms', conditions={'method': ['GET']}) |
|
498 | action='my_account_perms', conditions={'method': ['GET']}) | |
499 |
|
499 | |||
500 | m.connect('my_account_emails', '/my_account/emails', |
|
|||
501 | action='my_account_emails', conditions={'method': ['GET']}) |
|
|||
502 | m.connect('my_account_emails', '/my_account/emails', |
|
|||
503 | action='my_account_emails_add', conditions={'method': ['POST']}) |
|
|||
504 | m.connect('my_account_emails', '/my_account/emails', |
|
|||
505 | action='my_account_emails_delete', conditions={'method': ['DELETE']}) |
|
|||
506 |
|
||||
507 | m.connect('my_account_notifications', '/my_account/notifications', |
|
500 | m.connect('my_account_notifications', '/my_account/notifications', | |
508 | action='my_notifications', |
|
501 | action='my_notifications', | |
509 | conditions={'method': ['GET']}) |
|
502 | conditions={'method': ['GET']}) |
@@ -200,42 +200,6 b' class MyAccountController(BaseController' | |||||
200 |
|
200 | |||
201 | return render('admin/my_account/my_account.mako') |
|
201 | return render('admin/my_account/my_account.mako') | |
202 |
|
202 | |||
203 | def my_account_emails(self): |
|
|||
204 | c.active = 'emails' |
|
|||
205 | self.__load_data() |
|
|||
206 |
|
||||
207 | c.user_email_map = UserEmailMap.query()\ |
|
|||
208 | .filter(UserEmailMap.user == c.user).all() |
|
|||
209 | return render('admin/my_account/my_account.mako') |
|
|||
210 |
|
||||
211 | @auth.CSRFRequired() |
|
|||
212 | def my_account_emails_add(self): |
|
|||
213 | email = request.POST.get('new_email') |
|
|||
214 |
|
||||
215 | try: |
|
|||
216 | UserModel().add_extra_email(c.rhodecode_user.user_id, email) |
|
|||
217 | Session().commit() |
|
|||
218 | h.flash(_("Added new email address `%s` for user account") % email, |
|
|||
219 | category='success') |
|
|||
220 | except formencode.Invalid as error: |
|
|||
221 | msg = error.error_dict['email'] |
|
|||
222 | h.flash(msg, category='error') |
|
|||
223 | except Exception: |
|
|||
224 | log.exception("Exception in my_account_emails") |
|
|||
225 | h.flash(_('An error occurred during email saving'), |
|
|||
226 | category='error') |
|
|||
227 | return redirect(url('my_account_emails')) |
|
|||
228 |
|
||||
229 | @auth.CSRFRequired() |
|
|||
230 | def my_account_emails_delete(self): |
|
|||
231 | email_id = request.POST.get('del_email_id') |
|
|||
232 | user_model = UserModel() |
|
|||
233 | user_model.delete_extra_email(c.rhodecode_user.user_id, email_id) |
|
|||
234 | Session().commit() |
|
|||
235 | h.flash(_("Removed email address from user account"), |
|
|||
236 | category='success') |
|
|||
237 | return redirect(url('my_account_emails')) |
|
|||
238 |
|
||||
239 | def _extract_ordering(self, request): |
|
203 | def _extract_ordering(self, request): | |
240 | column_index = safe_int(request.GET.get('order[0][column]')) |
|
204 | column_index = safe_int(request.GET.get('order[0][column]')) | |
241 | order_dir = request.GET.get('order[0][dir]', 'desc') |
|
205 | order_dir = request.GET.get('order[0][dir]', 'desc') |
@@ -34,7 +34,7 b'' | |||||
34 | % if my_account_oauth_url: |
|
34 | % if my_account_oauth_url: | |
35 | <li class="${'active' if c.active=='oauth' else ''}"><a href="${my_account_oauth_url}">${_('OAuth Identities')}</a></li> |
|
35 | <li class="${'active' if c.active=='oauth' else ''}"><a href="${my_account_oauth_url}">${_('OAuth Identities')}</a></li> | |
36 | % endif |
|
36 | % endif | |
37 |
<li class="${'active' if c.active=='emails' else ''}"><a href="${h. |
|
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.url('my_account_repos')}">${_('Repositories')}</a></li> |
|
38 | <li class="${'active' if c.active=='repos' else ''}"><a href="${h.url('my_account_repos')}">${_('Repositories')}</a></li> | |
39 | <li class="${'active' if c.active=='watched' else ''}"><a href="${h.url('my_account_watched')}">${_('Watched')}</a></li> |
|
39 | <li class="${'active' if c.active=='watched' else ''}"><a href="${h.url('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.url('my_account_pullrequests')}">${_('Pull Requests')}</a></li> |
@@ -25,10 +25,10 b'' | |||||
25 | <span class="user email">${em.email}</span> |
|
25 | <span class="user email">${em.email}</span> | |
26 | </td> |
|
26 | </td> | |
27 | <td class="td-action"> |
|
27 | <td class="td-action"> | |
28 |
${h.secure_form( |
|
28 | ${h.secure_form(h.route_path('my_account_emails_delete'), method='POST')} | |
29 | ${h.hidden('del_email_id',em.email_id)} |
|
29 | ${h.hidden('del_email_id',em.email_id)} | |
30 |
<button class="btn btn-link btn-danger" type="submit" id="remove_email_%s |
|
30 | <button class="btn btn-link btn-danger" type="submit" id="${'remove_email_%s'.format(em.email_id)}" | |
31 |
onclick="return confirm('${_('Confirm to delete this email: |
|
31 | onclick="return confirm('${_('Confirm to delete this email: {}').format(em.email)}');"> | |
32 | ${_('Delete')} |
|
32 | ${_('Delete')} | |
33 | </button> |
|
33 | </button> | |
34 | ${h.end_form()} |
|
34 | ${h.end_form()} | |
@@ -48,7 +48,7 b'' | |||||
48 | </div> |
|
48 | </div> | |
49 |
|
49 | |||
50 | <div> |
|
50 | <div> | |
51 |
${h.secure_form( |
|
51 | ${h.secure_form(h.route_path('my_account_emails_add'), method='POST')} | |
52 | <div class="form"> |
|
52 | <div class="form"> | |
53 | <!-- fields --> |
|
53 | <!-- fields --> | |
54 | <div class="fields"> |
|
54 | <div class="fields"> |
@@ -23,8 +23,7 b' import pytest' | |||||
23 | from rhodecode.lib import helpers as h |
|
23 | from rhodecode.lib import helpers as h | |
24 | from rhodecode.model.db import User, UserFollowing, Repository |
|
24 | from rhodecode.model.db import User, UserFollowing, Repository | |
25 | from rhodecode.tests import ( |
|
25 | from rhodecode.tests import ( | |
26 |
TestController, url, TEST_USER_ADMIN_LOGIN, |
|
26 | TestController, url, TEST_USER_ADMIN_LOGIN, assert_session_flash) | |
27 | assert_session_flash) |
|
|||
28 | from rhodecode.tests.fixture import Fixture |
|
27 | from rhodecode.tests.fixture import Fixture | |
29 | from rhodecode.tests.utils import AssertResponse |
|
28 | from rhodecode.tests.utils import AssertResponse | |
30 |
|
29 | |||
@@ -90,56 +89,7 b' class TestMyAccountController(TestContro' | |||||
90 | response.mustcontain('"name_raw": %s' % pr.pull_request_id) |
|
89 | response.mustcontain('"name_raw": %s' % pr.pull_request_id) | |
91 | response.mustcontain('TestMyAccountPR') |
|
90 | response.mustcontain('TestMyAccountPR') | |
92 |
|
91 | |||
93 | def test_my_account_my_emails(self): |
|
|||
94 | self.log_user() |
|
|||
95 | response = self.app.get(url('my_account_emails')) |
|
|||
96 | response.mustcontain('No additional emails specified') |
|
|||
97 |
|
92 | |||
98 | def test_my_account_my_emails_add_existing_email(self): |
|
|||
99 | self.log_user() |
|
|||
100 | response = self.app.get(url('my_account_emails')) |
|
|||
101 | response.mustcontain('No additional emails specified') |
|
|||
102 | response = self.app.post(url('my_account_emails'), |
|
|||
103 | {'new_email': TEST_USER_REGULAR_EMAIL, |
|
|||
104 | 'csrf_token': self.csrf_token}) |
|
|||
105 | assert_session_flash(response, 'This e-mail address is already taken') |
|
|||
106 |
|
||||
107 | def test_my_account_my_emails_add_mising_email_in_form(self): |
|
|||
108 | self.log_user() |
|
|||
109 | response = self.app.get(url('my_account_emails')) |
|
|||
110 | response.mustcontain('No additional emails specified') |
|
|||
111 | response = self.app.post(url('my_account_emails'), |
|
|||
112 | {'csrf_token': self.csrf_token}) |
|
|||
113 | assert_session_flash(response, 'Please enter an email address') |
|
|||
114 |
|
||||
115 | def test_my_account_my_emails_add_remove(self): |
|
|||
116 | self.log_user() |
|
|||
117 | response = self.app.get(url('my_account_emails')) |
|
|||
118 | response.mustcontain('No additional emails specified') |
|
|||
119 |
|
||||
120 | response = self.app.post(url('my_account_emails'), |
|
|||
121 | {'new_email': 'foo@barz.com', |
|
|||
122 | 'csrf_token': self.csrf_token}) |
|
|||
123 |
|
||||
124 | response = self.app.get(url('my_account_emails')) |
|
|||
125 |
|
||||
126 | from rhodecode.model.db import UserEmailMap |
|
|||
127 | email_id = UserEmailMap.query().filter( |
|
|||
128 | UserEmailMap.user == User.get_by_username( |
|
|||
129 | TEST_USER_ADMIN_LOGIN)).filter( |
|
|||
130 | UserEmailMap.email == 'foo@barz.com').one().email_id |
|
|||
131 |
|
||||
132 | response.mustcontain('foo@barz.com') |
|
|||
133 | response.mustcontain('<input id="del_email_id" name="del_email_id" ' |
|
|||
134 | 'type="hidden" value="%s" />' % email_id) |
|
|||
135 |
|
||||
136 | response = self.app.post( |
|
|||
137 | url('my_account_emails'), { |
|
|||
138 | 'del_email_id': email_id, '_method': 'delete', |
|
|||
139 | 'csrf_token': self.csrf_token}) |
|
|||
140 | assert_session_flash(response, 'Removed email address from user account') |
|
|||
141 | response = self.app.get(url('my_account_emails')) |
|
|||
142 | response.mustcontain('No additional emails specified') |
|
|||
143 |
|
93 | |||
144 | @pytest.mark.parametrize( |
|
94 | @pytest.mark.parametrize( | |
145 | "name, attrs", [ |
|
95 | "name, attrs", [ |
General Comments 0
You need to be logged in to leave comments.
Login now