diff --git a/rhodecode/apps/my_account/__init__.py b/rhodecode/apps/my_account/__init__.py --- a/rhodecode/apps/my_account/__init__.py +++ b/rhodecode/apps/my_account/__init__.py @@ -46,6 +46,16 @@ def includeme(config): name='my_account_auth_tokens_delete', pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete') + config.add_route( + name='my_account_emails', + pattern=ADMIN_PREFIX + '/my_account/emails') + config.add_route( + name='my_account_emails_add', + pattern=ADMIN_PREFIX + '/my_account/emails/new') + config.add_route( + name='my_account_emails_delete', + pattern=ADMIN_PREFIX + '/my_account/emails/delete') + # channelstream test config.add_route( name='my_account_notifications_test_channelstream', diff --git a/rhodecode/apps/my_account/tests/test_my_account_emails.py b/rhodecode/apps/my_account/tests/test_my_account_emails.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/my_account/tests/test_my_account_emails.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2010-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + +import pytest + +from rhodecode.apps._base import ADMIN_PREFIX +from rhodecode.model.db import User, UserEmailMap +from rhodecode.tests import ( + TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL, + assert_session_flash) +from rhodecode.tests.fixture import Fixture + +fixture = Fixture() + + +def route_path(name, **kwargs): + return { + 'my_account_emails': + ADMIN_PREFIX + '/my_account/emails', + 'my_account_emails_add': + ADMIN_PREFIX + '/my_account/emails/new', + 'my_account_emails_delete': + ADMIN_PREFIX + '/my_account/emails/delete', + }[name].format(**kwargs) + + +class TestMyAccountEmails(TestController): + def test_my_account_my_emails(self): + self.log_user() + response = self.app.get(route_path('my_account_emails')) + response.mustcontain('No additional emails specified') + + def test_my_account_my_emails_add_existing_email(self): + self.log_user() + response = self.app.get(route_path('my_account_emails')) + response.mustcontain('No additional emails specified') + response = self.app.post(route_path('my_account_emails_add'), + {'new_email': TEST_USER_REGULAR_EMAIL, + 'csrf_token': self.csrf_token}) + assert_session_flash(response, 'This e-mail address is already taken') + + def test_my_account_my_emails_add_mising_email_in_form(self): + self.log_user() + response = self.app.get(route_path('my_account_emails')) + response.mustcontain('No additional emails specified') + response = self.app.post(route_path('my_account_emails_add'), + {'csrf_token': self.csrf_token}) + assert_session_flash(response, 'Please enter an email address') + + def test_my_account_my_emails_add_remove(self): + self.log_user() + response = self.app.get(route_path('my_account_emails')) + response.mustcontain('No additional emails specified') + + response = self.app.post(route_path('my_account_emails_add'), + {'new_email': 'foo@barz.com', + 'csrf_token': self.csrf_token}) + + response = self.app.get(route_path('my_account_emails')) + + email_id = UserEmailMap.query().filter( + UserEmailMap.user == User.get_by_username( + TEST_USER_ADMIN_LOGIN)).filter( + UserEmailMap.email == 'foo@barz.com').one().email_id + + response.mustcontain('foo@barz.com') + response.mustcontain('' % email_id) + + response = self.app.post( + route_path('my_account_emails_delete'), { + 'del_email_id': email_id, + 'csrf_token': self.csrf_token}) + assert_session_flash(response, 'Email successfully deleted') + response = self.app.get(route_path('my_account_emails')) + response.mustcontain('No additional emails specified') diff --git a/rhodecode/apps/my_account/views.py b/rhodecode/apps/my_account/views.py --- a/rhodecode/apps/my_account/views.py +++ b/rhodecode/apps/my_account/views.py @@ -21,6 +21,7 @@ import logging import datetime +import formencode from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config @@ -32,6 +33,7 @@ from rhodecode.lib.channelstream import ChannelstreamException from rhodecode.lib.utils2 import safe_int, md5 from rhodecode.model.auth_token import AuthTokenModel +from rhodecode.model.db import UserEmailMap from rhodecode.model.meta import Session from rhodecode.model.user import UserModel from rhodecode.model.validation_schema.schemas import user_schema @@ -161,7 +163,7 @@ class MyAccountView(BaseAppView): @NotAnonymous() @CSRFRequired() @view_config( - route_name='my_account_auth_tokens_add', request_method='POST') + route_name='my_account_auth_tokens_add', request_method='POST',) def my_account_auth_tokens_add(self): _ = self.request.translate c = self.load_default_context() @@ -198,6 +200,65 @@ class MyAccountView(BaseAppView): @LoginRequired() @NotAnonymous() + @view_config( + route_name='my_account_emails', request_method='GET', + renderer='rhodecode:templates/admin/my_account/my_account.mako') + def my_account_emails(self): + _ = self.request.translate + + c = self.load_default_context() + c.active = 'emails' + + c.user_email_map = UserEmailMap.query()\ + .filter(UserEmailMap.user == c.user).all() + return self._get_template_context(c) + + @LoginRequired() + @NotAnonymous() + @CSRFRequired() + @view_config( + route_name='my_account_emails_add', request_method='POST') + def my_account_emails_add(self): + _ = self.request.translate + c = self.load_default_context() + + email = self.request.POST.get('new_email') + + try: + UserModel().add_extra_email(c.user.user_id, email) + Session().commit() + h.flash(_("Added new email address `%s` for user account") % email, + category='success') + except formencode.Invalid as error: + msg = error.error_dict['email'] + h.flash(msg, category='error') + except Exception: + log.exception("Exception in my_account_emails") + h.flash(_('An error occurred during email saving'), + category='error') + return HTTPFound(h.route_path('my_account_emails')) + + @LoginRequired() + @NotAnonymous() + @CSRFRequired() + @view_config( + route_name='my_account_emails_delete', request_method='POST') + def my_account_emails_delete(self): + _ = self.request.translate + c = self.load_default_context() + + del_email_id = self.request.POST.get('del_email_id') + if del_email_id: + + UserModel().delete_extra_email( + c.user.user_id, del_email_id) + Session().commit() + h.flash(_("Email successfully deleted"), + category='success') + return HTTPFound(h.route_path('my_account_emails')) + + @LoginRequired() + @NotAnonymous() @CSRFRequired() @view_config( route_name='my_account_notifications_test_channelstream', diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -497,13 +497,6 @@ def make_map(config): m.connect('my_account_perms', '/my_account/perms', action='my_account_perms', conditions={'method': ['GET']}) - m.connect('my_account_emails', '/my_account/emails', - action='my_account_emails', conditions={'method': ['GET']}) - m.connect('my_account_emails', '/my_account/emails', - action='my_account_emails_add', conditions={'method': ['POST']}) - m.connect('my_account_emails', '/my_account/emails', - action='my_account_emails_delete', conditions={'method': ['DELETE']}) - m.connect('my_account_notifications', '/my_account/notifications', action='my_notifications', conditions={'method': ['GET']}) diff --git a/rhodecode/controllers/admin/my_account.py b/rhodecode/controllers/admin/my_account.py --- a/rhodecode/controllers/admin/my_account.py +++ b/rhodecode/controllers/admin/my_account.py @@ -200,42 +200,6 @@ class MyAccountController(BaseController return render('admin/my_account/my_account.mako') - def my_account_emails(self): - c.active = 'emails' - self.__load_data() - - c.user_email_map = UserEmailMap.query()\ - .filter(UserEmailMap.user == c.user).all() - return render('admin/my_account/my_account.mako') - - @auth.CSRFRequired() - def my_account_emails_add(self): - email = request.POST.get('new_email') - - try: - UserModel().add_extra_email(c.rhodecode_user.user_id, email) - Session().commit() - h.flash(_("Added new email address `%s` for user account") % email, - category='success') - except formencode.Invalid as error: - msg = error.error_dict['email'] - h.flash(msg, category='error') - except Exception: - log.exception("Exception in my_account_emails") - h.flash(_('An error occurred during email saving'), - category='error') - return redirect(url('my_account_emails')) - - @auth.CSRFRequired() - def my_account_emails_delete(self): - email_id = request.POST.get('del_email_id') - user_model = UserModel() - user_model.delete_extra_email(c.rhodecode_user.user_id, email_id) - Session().commit() - h.flash(_("Removed email address from user account"), - category='success') - return redirect(url('my_account_emails')) - def _extract_ordering(self, request): column_index = safe_int(request.GET.get('order[0][column]')) order_dir = request.GET.get('order[0][dir]', 'desc') diff --git a/rhodecode/templates/admin/my_account/my_account.mako b/rhodecode/templates/admin/my_account/my_account.mako --- a/rhodecode/templates/admin/my_account/my_account.mako +++ b/rhodecode/templates/admin/my_account/my_account.mako @@ -34,7 +34,7 @@ % if my_account_oauth_url:
  • ${_('OAuth Identities')}
  • % endif -
  • ${_('Emails')}
  • +
  • ${_('Emails')}
  • ${_('Repositories')}
  • ${_('Watched')}
  • ${_('Pull Requests')}
  • diff --git a/rhodecode/templates/admin/my_account/my_account_emails.mako b/rhodecode/templates/admin/my_account/my_account_emails.mako --- a/rhodecode/templates/admin/my_account/my_account_emails.mako +++ b/rhodecode/templates/admin/my_account/my_account_emails.mako @@ -25,10 +25,10 @@ ${em.email} - ${h.secure_form(url('my_account_emails'),method='delete')} + ${h.secure_form(h.route_path('my_account_emails_delete'), method='POST')} ${h.hidden('del_email_id',em.email_id)} - ${h.end_form()} @@ -48,7 +48,7 @@
    - ${h.secure_form(url('my_account_emails'), method='post')} + ${h.secure_form(h.route_path('my_account_emails_add'), method='POST')}
    diff --git a/rhodecode/tests/functional/test_admin_my_account.py b/rhodecode/tests/functional/test_admin_my_account.py --- a/rhodecode/tests/functional/test_admin_my_account.py +++ b/rhodecode/tests/functional/test_admin_my_account.py @@ -23,8 +23,7 @@ import pytest from rhodecode.lib import helpers as h from rhodecode.model.db import User, UserFollowing, Repository from rhodecode.tests import ( - TestController, url, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL, - assert_session_flash) + TestController, url, TEST_USER_ADMIN_LOGIN, assert_session_flash) from rhodecode.tests.fixture import Fixture from rhodecode.tests.utils import AssertResponse @@ -90,56 +89,7 @@ class TestMyAccountController(TestContro response.mustcontain('"name_raw": %s' % pr.pull_request_id) response.mustcontain('TestMyAccountPR') - def test_my_account_my_emails(self): - self.log_user() - response = self.app.get(url('my_account_emails')) - response.mustcontain('No additional emails specified') - def test_my_account_my_emails_add_existing_email(self): - self.log_user() - response = self.app.get(url('my_account_emails')) - response.mustcontain('No additional emails specified') - response = self.app.post(url('my_account_emails'), - {'new_email': TEST_USER_REGULAR_EMAIL, - 'csrf_token': self.csrf_token}) - assert_session_flash(response, 'This e-mail address is already taken') - - def test_my_account_my_emails_add_mising_email_in_form(self): - self.log_user() - response = self.app.get(url('my_account_emails')) - response.mustcontain('No additional emails specified') - response = self.app.post(url('my_account_emails'), - {'csrf_token': self.csrf_token}) - assert_session_flash(response, 'Please enter an email address') - - def test_my_account_my_emails_add_remove(self): - self.log_user() - response = self.app.get(url('my_account_emails')) - response.mustcontain('No additional emails specified') - - response = self.app.post(url('my_account_emails'), - {'new_email': 'foo@barz.com', - 'csrf_token': self.csrf_token}) - - response = self.app.get(url('my_account_emails')) - - from rhodecode.model.db import UserEmailMap - email_id = UserEmailMap.query().filter( - UserEmailMap.user == User.get_by_username( - TEST_USER_ADMIN_LOGIN)).filter( - UserEmailMap.email == 'foo@barz.com').one().email_id - - response.mustcontain('foo@barz.com') - response.mustcontain('' % email_id) - - response = self.app.post( - url('my_account_emails'), { - 'del_email_id': email_id, '_method': 'delete', - 'csrf_token': self.csrf_token}) - assert_session_flash(response, 'Removed email address from user account') - response = self.app.get(url('my_account_emails')) - response.mustcontain('No additional emails specified') @pytest.mark.parametrize( "name, attrs", [