Show More
@@ -0,0 +1,53 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 | ||||
|
21 | import logging | |||
|
22 | from pylons import tmpl_context as c | |||
|
23 | ||||
|
24 | from rhodecode.lib.utils2 import StrictAttributeDict | |||
|
25 | ||||
|
26 | log = logging.getLogger(__name__) | |||
|
27 | ||||
|
28 | ||||
|
29 | class TemplateArgs(StrictAttributeDict): | |||
|
30 | pass | |||
|
31 | ||||
|
32 | ||||
|
33 | class BaseAppView(object): | |||
|
34 | ||||
|
35 | def __init__(self, context, request): | |||
|
36 | self.request = request | |||
|
37 | self.context = context | |||
|
38 | self.session = request.session | |||
|
39 | self._rhodecode_user = request.user | |||
|
40 | ||||
|
41 | def _get_local_tmpl_context(self): | |||
|
42 | return TemplateArgs() | |||
|
43 | ||||
|
44 | def _get_template_context(self, tmpl_args): | |||
|
45 | ||||
|
46 | for k, v in tmpl_args.items(): | |||
|
47 | setattr(c, k, v) | |||
|
48 | ||||
|
49 | return { | |||
|
50 | 'defaults': {}, | |||
|
51 | 'errors': {}, | |||
|
52 | } | |||
|
53 |
@@ -0,0 +1,28 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 | ||||
|
21 | ||||
|
22 | def includeme(config): | |||
|
23 | config.add_route( | |||
|
24 | name='user_profile', | |||
|
25 | pattern='/_profiles/{username}') | |||
|
26 | ||||
|
27 | # Scan module for configuration decorators. | |||
|
28 | config.scan() |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 |
@@ -0,0 +1,75 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.model.db import User | |||
|
24 | from rhodecode.tests import ( | |||
|
25 | TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS, | |||
|
26 | TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |||
|
27 | from rhodecode.tests.fixture import Fixture | |||
|
28 | from rhodecode.tests.utils import AssertResponse | |||
|
29 | ||||
|
30 | fixture = Fixture() | |||
|
31 | ||||
|
32 | ||||
|
33 | def route_path(name, **kwargs): | |||
|
34 | return '/_profiles/{username}'.format(**kwargs) | |||
|
35 | ||||
|
36 | ||||
|
37 | class TestUsersController(TestController): | |||
|
38 | ||||
|
39 | def test_user_profile(self, user_util): | |||
|
40 | edit_link_css = '.user-profile .panel-edit' | |||
|
41 | self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |||
|
42 | user = user_util.create_user( | |||
|
43 | 'test-my-user', password='qweqwe', email='testme@rhodecode.org') | |||
|
44 | username = user.username | |||
|
45 | ||||
|
46 | response = self.app.get(route_path('user_profile', username=username)) | |||
|
47 | response.mustcontain('testme') | |||
|
48 | response.mustcontain('testme@rhodecode.org') | |||
|
49 | assert_response = AssertResponse(response) | |||
|
50 | assert_response.no_element_exists(edit_link_css) | |||
|
51 | ||||
|
52 | # edit should be available to superadmin users | |||
|
53 | self.logout_user() | |||
|
54 | self.log_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS) | |||
|
55 | response = self.app.get(route_path('user_profile', username=username)) | |||
|
56 | assert_response = AssertResponse(response) | |||
|
57 | assert_response.element_contains(edit_link_css, 'Edit') | |||
|
58 | ||||
|
59 | def test_user_profile_not_available(self, user_util): | |||
|
60 | user = user_util.create_user() | |||
|
61 | username = user.username | |||
|
62 | ||||
|
63 | # not logged in, redirect | |||
|
64 | self.app.get(route_path('user_profile', username=username), status=302) | |||
|
65 | ||||
|
66 | self.log_user() | |||
|
67 | # after log-in show | |||
|
68 | self.app.get(route_path('user_profile', username=username), status=200) | |||
|
69 | ||||
|
70 | # default user, not allowed to show it | |||
|
71 | self.app.get( | |||
|
72 | route_path('user_profile', username=User.DEFAULT_USER), status=404) | |||
|
73 | ||||
|
74 | # actual 404 | |||
|
75 | self.app.get(route_path('user_profile', username='unknown'), status=404) |
@@ -0,0 +1,53 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 | ||||
|
21 | import logging | |||
|
22 | ||||
|
23 | from pyramid.httpexceptions import HTTPNotFound | |||
|
24 | from pyramid.view import view_config | |||
|
25 | ||||
|
26 | from rhodecode.apps._base import BaseAppView | |||
|
27 | from rhodecode.lib.auth import LoginRequired, NotAnonymous | |||
|
28 | ||||
|
29 | from rhodecode.model.db import User | |||
|
30 | from rhodecode.model.user import UserModel | |||
|
31 | ||||
|
32 | log = logging.getLogger(__name__) | |||
|
33 | ||||
|
34 | ||||
|
35 | class UserProfileView(BaseAppView): | |||
|
36 | ||||
|
37 | @LoginRequired() | |||
|
38 | @NotAnonymous() | |||
|
39 | @view_config( | |||
|
40 | route_name='user_profile', request_method='GET', | |||
|
41 | renderer='rhodecode:templates/users/user.mako') | |||
|
42 | def login(self): | |||
|
43 | # register local template context | |||
|
44 | c = self._get_local_tmpl_context() | |||
|
45 | c.active = 'user_profile' | |||
|
46 | ||||
|
47 | username = self.request.matchdict.get('username') | |||
|
48 | ||||
|
49 | c.user = UserModel().get_by_username(username) | |||
|
50 | if not c.user or c.user.username == User.DEFAULT_USER: | |||
|
51 | raise HTTPNotFound() | |||
|
52 | ||||
|
53 | return self._get_template_context(c) |
@@ -281,7 +281,11 b' def includeme(config):' | |||||
281 | config.include('rhodecode.admin') |
|
281 | config.include('rhodecode.admin') | |
282 | config.include('rhodecode.authentication') |
|
282 | config.include('rhodecode.authentication') | |
283 | config.include('rhodecode.integrations') |
|
283 | config.include('rhodecode.integrations') | |
|
284 | ||||
|
285 | # apps | |||
284 | config.include('rhodecode.apps.login') |
|
286 | config.include('rhodecode.apps.login') | |
|
287 | config.include('rhodecode.apps.user_profile') | |||
|
288 | ||||
285 | config.include('rhodecode.tweens') |
|
289 | config.include('rhodecode.tweens') | |
286 | config.include('rhodecode.api') |
|
290 | config.include('rhodecode.api') | |
287 | config.include('rhodecode.svn_support') |
|
291 | config.include('rhodecode.svn_support') |
@@ -92,7 +92,7 b' class JSRoutesMapper(Mapper):' | |||||
92 | def _extract_route_information(self, route): |
|
92 | def _extract_route_information(self, route): | |
93 | """ |
|
93 | """ | |
94 | Convert a route into tuple(name, path, args), eg: |
|
94 | Convert a route into tuple(name, path, args), eg: | |
95 |
('user |
|
95 | ('show_user', '/profile/%(username)s', ['username']) | |
96 | """ |
|
96 | """ | |
97 | routepath = route.routepath |
|
97 | routepath = route.routepath | |
98 | def replace(matchobj): |
|
98 | def replace(matchobj): | |
@@ -198,10 +198,6 b' def make_map(config):' | |||||
198 | rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home', |
|
198 | rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home', | |
199 | action='user_group_autocomplete_data', jsroute=True) |
|
199 | action='user_group_autocomplete_data', jsroute=True) | |
200 |
|
200 | |||
201 | rmap.connect( |
|
|||
202 | 'user_profile', '/_profiles/{username}', controller='users', |
|
|||
203 | action='user_profile') |
|
|||
204 |
|
||||
205 | # TODO: johbo: Static links, to be replaced by our redirection mechanism |
|
201 | # TODO: johbo: Static links, to be replaced by our redirection mechanism | |
206 | rmap.connect('rst_help', |
|
202 | rmap.connect('rst_help', | |
207 | 'http://docutils.sourceforge.net/docs/user/rst/quickref.html', |
|
203 | 'http://docutils.sourceforge.net/docs/user/rst/quickref.html', |
@@ -871,7 +871,7 b' def link_to_user(author, length=0, **kwa' | |||||
871 | if user: |
|
871 | if user: | |
872 | return link_to( |
|
872 | return link_to( | |
873 | escape(display_person), |
|
873 | escape(display_person), | |
874 |
|
|
874 | route_path('user_profile', username=user.username), | |
875 | **kwargs) |
|
875 | **kwargs) | |
876 | else: |
|
876 | else: | |
877 | return escape(display_person) |
|
877 | return escape(display_person) |
@@ -12,16 +12,16 b' A new user `${user.username}` has regist' | |||||
12 | - Username: ${user.username} |
|
12 | - Username: ${user.username} | |
13 | - Full Name: ${user.firstname} ${user.lastname} |
|
13 | - Full Name: ${user.firstname} ${user.lastname} | |
14 | - Email: ${user.email} |
|
14 | - Email: ${user.email} | |
15 |
- Profile link: ${h. |
|
15 | - Profile link: ${h.route_path('user_profile', username=user.username, qualified=True)} | |
16 |
|
16 | |||
17 | ${self.plaintext_footer()} |
|
17 | ${self.plaintext_footer()} | |
18 | </%def> |
|
18 | </%def> | |
19 |
|
19 | |||
20 | ## BODY GOES BELOW |
|
20 | ## BODY GOES BELOW | |
21 | <table style="text-align:left;vertical-align:middle;"> |
|
21 | <table style="text-align:left;vertical-align:middle;"> | |
22 |
<tr><td colspan="2" style="width:100%;padding-bottom:15px;border-bottom:1px solid #dbd9da;"><h4><a href="${h. |
|
22 | <tr><td colspan="2" style="width:100%;padding-bottom:15px;border-bottom:1px solid #dbd9da;"><h4><a href="${h.route_path('user_profile', username=user.username, qualified=True)}" style="color:#427cc9;text-decoration:none;cursor:pointer">${_('New user %(user)s has registered on %(date)s') % {'user': user.username, 'date': h.format_date(date)}}</a></h4></td></tr> | |
23 | <tr><td style="padding-right:20px;padding-top:20px;">${_('Username')}</td><td style="line-height:1;padding-top:20px;"><img style="margin-bottom:-5px;text-align:left;border:1px solid #dbd9da" src="${h.gravatar_url(user.email, 16)}" height="16" width="16"> ${user.username}</td></tr> |
|
23 | <tr><td style="padding-right:20px;padding-top:20px;">${_('Username')}</td><td style="line-height:1;padding-top:20px;"><img style="margin-bottom:-5px;text-align:left;border:1px solid #dbd9da" src="${h.gravatar_url(user.email, 16)}" height="16" width="16"> ${user.username}</td></tr> | |
24 | <tr><td style="padding-right:20px;">${_('Full Name')}</td><td>${user.firstname} ${user.lastname}</td></tr> |
|
24 | <tr><td style="padding-right:20px;">${_('Full Name')}</td><td>${user.firstname} ${user.lastname}</td></tr> | |
25 | <tr><td style="padding-right:20px;">${_('Email')}</td><td>${user.email}</td></tr> |
|
25 | <tr><td style="padding-right:20px;">${_('Email')}</td><td>${user.email}</td></tr> | |
26 |
<tr><td style="padding-right:20px;">${_('Profile')}</td><td><a href="${h. |
|
26 | <tr><td style="padding-right:20px;">${_('Profile')}</td><td><a href="${h.route_path('user_profile', username=user.username, qualified=True)}">${h.route_path('user_profile', username=user.username, qualified=True)}</a></td></tr> | |
27 | </table> No newline at end of file |
|
27 | </table> |
@@ -12,7 +12,7 b'' | |||||
12 | </%def> |
|
12 | </%def> | |
13 |
|
13 | |||
14 | <%def name="menu_bar_nav()"> |
|
14 | <%def name="menu_bar_nav()"> | |
15 |
${self.menu_items(active=' |
|
15 | ${self.menu_items(active='my_account')} | |
16 | </%def> |
|
16 | </%def> | |
17 |
|
17 | |||
18 | <%def name="main()"> |
|
18 | <%def name="main()"> | |
@@ -26,7 +26,7 b'' | |||||
26 | <div class="sidebar"> |
|
26 | <div class="sidebar"> | |
27 | <ul class="nav nav-pills nav-stacked"> |
|
27 | <ul class="nav nav-pills nav-stacked"> | |
28 | <li class="${'active' if c.active=='user_profile' else ''}"> |
|
28 | <li class="${'active' if c.active=='user_profile' else ''}"> | |
29 |
<a href="${h. |
|
29 | <a href="${h.route_path('user_profile', username=c.user.username)}">${_('Profile')}</a></li> | |
30 | ## These placeholders are here only for styling purposes. For every new item added to the list, you should remove one placeholder |
|
30 | ## These placeholders are here only for styling purposes. For every new item added to the list, you should remove one placeholder | |
31 | <li class="placeholder"><a href="#" style="visibility: hidden;">placeholder</a></li> |
|
31 | <li class="placeholder"><a href="#" style="visibility: hidden;">placeholder</a></li> | |
32 | <li class="placeholder"><a href="#" style="visibility: hidden;">placeholder</a></li> |
|
32 | <li class="placeholder"><a href="#" style="visibility: hidden;">placeholder</a></li> |
@@ -276,7 +276,7 b' class TestGistsController(TestController' | |||||
276 | assert_response = AssertResponse(response) |
|
276 | assert_response = AssertResponse(response) | |
277 | assert_response.element_equals_to( |
|
277 | assert_response.element_equals_to( | |
278 | 'div.rc-user span.user', |
|
278 | 'div.rc-user span.user', | |
279 | '<span class="user"> %s</span>' % h.link_to_user('test_admin')) |
|
279 | '<a href="/_profiles/test_admin">test_admin</a></span>') | |
280 |
|
280 | |||
281 | response.mustcontain('gist-desc') |
|
281 | response.mustcontain('gist-desc') | |
282 |
|
282 | |||
@@ -299,7 +299,7 b' class TestGistsController(TestController' | |||||
299 | assert_response = AssertResponse(response) |
|
299 | assert_response = AssertResponse(response) | |
300 | assert_response.element_equals_to( |
|
300 | assert_response.element_equals_to( | |
301 | 'div.rc-user span.user', |
|
301 | 'div.rc-user span.user', | |
302 | '<span class="user"> %s</span>' % h.link_to_user('test_admin')) |
|
302 | '<a href="/_profiles/test_admin">test_admin</a></span>') | |
303 | response.mustcontain('gist-desc') |
|
303 | response.mustcontain('gist-desc') | |
304 |
|
304 | |||
305 | def test_show_as_raw(self, create_gist): |
|
305 | def test_show_as_raw(self, create_gist): |
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