Show More
@@ -0,0 +1,27 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2016-2018 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_group_profile', | |||
|
25 | pattern='/_profile_user_group/{user_group_name}') | |||
|
26 | # Scan module for configuration decorators. | |||
|
27 | config.scan('.views', ignore='.tests') |
@@ -0,0 +1,19 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2016-2018 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/ |
@@ -0,0 +1,76 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2010-2018 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 | from rhodecode.model.user_group import UserGroupModel | |||
|
21 | from rhodecode.tests import ( | |||
|
22 | TestController, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |||
|
23 | from rhodecode.tests.fixture import Fixture | |||
|
24 | from rhodecode.tests.utils import AssertResponse | |||
|
25 | ||||
|
26 | fixture = Fixture() | |||
|
27 | ||||
|
28 | ||||
|
29 | def route_path(name, **kwargs): | |||
|
30 | return '/_profile_user_group/{user_group_name}'.format(**kwargs) | |||
|
31 | ||||
|
32 | ||||
|
33 | class TestUsersController(TestController): | |||
|
34 | ||||
|
35 | def test_user_group_profile(self, user_util): | |||
|
36 | self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |||
|
37 | user, usergroup = user_util.create_user_with_group() | |||
|
38 | ||||
|
39 | response = self.app.get(route_path('profile_user_group', user_group_name=usergroup.users_group_name)) | |||
|
40 | response.mustcontain(usergroup.users_group_name) | |||
|
41 | response.mustcontain(user.username) | |||
|
42 | ||||
|
43 | def test_user_can_check_own_group(self, user_util): | |||
|
44 | user = user_util.create_user( | |||
|
45 | TEST_USER_REGULAR_LOGIN, password=TEST_USER_REGULAR_PASS, email='testme@rhodecode.org') | |||
|
46 | self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |||
|
47 | usergroup = user_util.create_user_group(owner=user) | |||
|
48 | response = self.app.get(route_path('profile_user_group', user_group_name=usergroup.users_group_name)) | |||
|
49 | response.mustcontain(usergroup.users_group_name) | |||
|
50 | response.mustcontain(user.username) | |||
|
51 | ||||
|
52 | def test_user_can_not_check_other_group(self, user_util): | |||
|
53 | self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |||
|
54 | user_group = user_util.create_user_group() | |||
|
55 | UserGroupModel().grant_user_permission(user_group, self._get_logged_user(), 'usergroup.none') | |||
|
56 | response = self.app.get(route_path('profile_user_group', user_group_name=user_group.users_group_name), status=404) | |||
|
57 | assert response.status_code == 404 | |||
|
58 | ||||
|
59 | def test_another_user_can_check_if_he_is_in_group(self, user_util): | |||
|
60 | self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |||
|
61 | user = user_util.create_user( | |||
|
62 | 'test-my-user', password='qweqwe', email='testme@rhodecode.org') | |||
|
63 | user_group = user_util.create_user_group() | |||
|
64 | UserGroupModel().add_user_to_group(user_group, user) | |||
|
65 | UserGroupModel().grant_user_permission(user_group, self._get_logged_user(), 'usergroup.read') | |||
|
66 | response = self.app.get(route_path('profile_user_group', user_group_name=user_group.users_group_name)) | |||
|
67 | response.mustcontain(user_group.users_group_name) | |||
|
68 | response.mustcontain(user.username) | |||
|
69 | ||||
|
70 | def test_with_anonymous_user(self, user_util): | |||
|
71 | user = user_util.create_user( | |||
|
72 | 'test-my-user', password='qweqwe', email='testme@rhodecode.org') | |||
|
73 | user_group = user_util.create_user_group() | |||
|
74 | UserGroupModel().add_user_to_group(user_group, user) | |||
|
75 | response = self.app.get(route_path('profile_user_group', user_group_name=user_group.users_group_name), status=302) | |||
|
76 | assert response.status_code == 302 No newline at end of file |
@@ -0,0 +1,53 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2016-2018 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 HasUserGroupPermissionAnyDecorator, LoginRequired, NotAnonymous | |||
|
28 | from rhodecode.model.db import UserGroup, User | |||
|
29 | ||||
|
30 | ||||
|
31 | log = logging.getLogger(__name__) | |||
|
32 | ||||
|
33 | ||||
|
34 | class UserGroupProfileView(BaseAppView): | |||
|
35 | ||||
|
36 | @LoginRequired() | |||
|
37 | @NotAnonymous() | |||
|
38 | @HasUserGroupPermissionAnyDecorator('usergroup.read', 'usergroup.write', 'usergroup.admin',) | |||
|
39 | @view_config( | |||
|
40 | route_name='user_group_profile', request_method='GET', | |||
|
41 | renderer='rhodecode:templates/user_group/user_group.mako') | |||
|
42 | def user_group_profile(self): | |||
|
43 | c = self._get_local_tmpl_context() | |||
|
44 | c.active = 'profile' | |||
|
45 | self.db_user_group_name = self.request.matchdict.get('user_group_name') | |||
|
46 | c.user_group = UserGroup().get_by_group_name(self.db_user_group_name) | |||
|
47 | if not c.user_group: | |||
|
48 | raise HTTPNotFound() | |||
|
49 | group_members_obj = sorted((x.user for x in c.user_group.members), | |||
|
50 | key=lambda u: u.username.lower()) | |||
|
51 | c.group_members = group_members_obj | |||
|
52 | c.anonymous = self._rhodecode_user.username == User.DEFAULT_USER | |||
|
53 | return self._get_template_context(c) |
@@ -0,0 +1,70 b'' | |||||
|
1 | <%namespace name="base" file="/base/base.mako"/> | |||
|
2 | ||||
|
3 | <div class="panel panel-default user-profile"> | |||
|
4 | <div class="panel-heading"> | |||
|
5 | <h3 class="panel-title">${_('User group profile')}</h3> | |||
|
6 | %if h.HasPermissionAny('hg.admin')(): | |||
|
7 | ${h.link_to(_('Edit'), h.route_path('edit_user_group', user_group_id=c.user_group.users_group_id), class_='panel-edit')} | |||
|
8 | %endif | |||
|
9 | </div> | |||
|
10 | ||||
|
11 | <div class="panel-body user-profile-content"> | |||
|
12 | ||||
|
13 | <div class="fieldset"> | |||
|
14 | <div class="left-label"> | |||
|
15 | ${_('Group Name')}: | |||
|
16 | </div> | |||
|
17 | <div class="right-content"> | |||
|
18 | ${c.user_group.users_group_name} | |||
|
19 | </div> | |||
|
20 | </div> | |||
|
21 | <div class="fieldset"> | |||
|
22 | <div class="left-label"> | |||
|
23 | ${_('Owner')}: | |||
|
24 | </div> | |||
|
25 | <div class="group_member"> | |||
|
26 | ${base.gravatar(c.user_group.user.email, 16)} | |||
|
27 | <span class="username user">${h.link_to_user(c.user_group.user)}</span> | |||
|
28 | ||||
|
29 | </div> | |||
|
30 | </div> | |||
|
31 | <div class="fieldset"> | |||
|
32 | <div class="left-label"> | |||
|
33 | ${_('Active')}: | |||
|
34 | </div> | |||
|
35 | <div class="right-content"> | |||
|
36 | ${c.user_group.users_group_active} | |||
|
37 | </div> | |||
|
38 | </div> | |||
|
39 | % if not c.anonymous: | |||
|
40 | <div class="fieldset"> | |||
|
41 | <div class="left-label"> | |||
|
42 | ${_('Members')}: | |||
|
43 | </div> | |||
|
44 | <div class="right-content"> | |||
|
45 | <table id="group_members_placeholder" class="rctable group_members"> | |||
|
46 | <th>${_('Username')}</th> | |||
|
47 | % if c.group_members: | |||
|
48 | % for user in c.group_members: | |||
|
49 | <tr> | |||
|
50 | <td id="member_user_${user.user_id}" class="td-author"> | |||
|
51 | <div class="group_member"> | |||
|
52 | ${base.gravatar(user.email, 16)} | |||
|
53 | <span class="username user">${h.link_to(h.person(user), h.route_path('user_edit',user_id=user.user_id))}</span> | |||
|
54 | <input type="hidden" name="__start__" value="member:mapping"> | |||
|
55 | <input type="hidden" name="member_user_id" value="${user.user_id}"> | |||
|
56 | <input type="hidden" name="type" value="existing" id="member_${user.user_id}"> | |||
|
57 | <input type="hidden" name="__end__" value="member:mapping"> | |||
|
58 | </div> | |||
|
59 | </td> | |||
|
60 | </tr> | |||
|
61 | % endfor | |||
|
62 | % else: | |||
|
63 | <tr><td colspan="2">${_('No members yet')}</td></tr> | |||
|
64 | % endif | |||
|
65 | </table> | |||
|
66 | </div> | |||
|
67 | </div> | |||
|
68 | % endif | |||
|
69 | </div> | |||
|
70 | </div> No newline at end of file |
@@ -0,0 +1,46 b'' | |||||
|
1 | <%inherit file="/base/base.mako"/> | |||
|
2 | ||||
|
3 | <%def name="title()"> | |||
|
4 | ${_('User group')}: ${c.user_group.users_group_name} | |||
|
5 | %if c.rhodecode_name: | |||
|
6 | · ${h.branding(c.rhodecode_name)} | |||
|
7 | %endif | |||
|
8 | </%def> | |||
|
9 | ||||
|
10 | <%def name="breadcrumbs_links()"> | |||
|
11 | ${_('User group')}: ${c.user_group.users_group_name} | |||
|
12 | </%def> | |||
|
13 | ||||
|
14 | <%def name="menu_bar_nav()"> | |||
|
15 | ${self.menu_items(active='my_account')} | |||
|
16 | </%def> | |||
|
17 | ||||
|
18 | <%def name="main()"> | |||
|
19 | <div class="box"> | |||
|
20 | <div class="title"> | |||
|
21 | ${self.breadcrumbs()} | |||
|
22 | </div> | |||
|
23 | ||||
|
24 | <div class="sidebar-col-wrapper scw-small"> | |||
|
25 | ##main | |||
|
26 | <div class="sidebar"> | |||
|
27 | <ul class="nav nav-pills nav-stacked"> | |||
|
28 | <li class="${'active' if c.active=='profile' else ''}"> | |||
|
29 | <a href="${h.route_path('user_group_profile', user_group_name=c.user_group.users_group_name)}">${_('User Group 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 | |||
|
31 | <li class="placeholder"><a href="#" style="visibility: hidden;">placeholder</a></li> | |||
|
32 | <li class="placeholder"><a href="#" style="visibility: hidden;">placeholder</a></li> | |||
|
33 | <li class="placeholder"><a href="#" style="visibility: hidden;">placeholder</a></li> | |||
|
34 | <li class="placeholder"><a href="#" style="visibility: hidden;">placeholder</a></li> | |||
|
35 | <li class="placeholder"><a href="#" style="visibility: hidden;">placeholder</a></li> | |||
|
36 | <li class="placeholder"><a href="#" style="visibility: hidden;">placeholder</a></li> | |||
|
37 | </ul> | |||
|
38 | </div> | |||
|
39 | ||||
|
40 | <div class="main-content-full-width"> | |||
|
41 | <%include file="/user_group/${c.active}.mako"/> | |||
|
42 | </div> | |||
|
43 | </div> | |||
|
44 | </div> | |||
|
45 | ||||
|
46 | </%def> |
@@ -226,6 +226,7 b' def includeme(config):' | |||||
226 | config.include('rhodecode.apps.user_group') |
|
226 | config.include('rhodecode.apps.user_group') | |
227 | config.include('rhodecode.apps.search') |
|
227 | config.include('rhodecode.apps.search') | |
228 | config.include('rhodecode.apps.user_profile') |
|
228 | config.include('rhodecode.apps.user_profile') | |
|
229 | config.include('rhodecode.apps.user_group_profile') | |||
229 | config.include('rhodecode.apps.my_account') |
|
230 | config.include('rhodecode.apps.my_account') | |
230 | config.include('rhodecode.apps.svn_support') |
|
231 | config.include('rhodecode.apps.svn_support') | |
231 | config.include('rhodecode.apps.ssh_support') |
|
232 | config.include('rhodecode.apps.ssh_support') |
@@ -896,6 +896,13 b' def link_to_user(author, length=0, **kwa' | |||||
896 | return escape(display_person) |
|
896 | return escape(display_person) | |
897 |
|
897 | |||
898 |
|
898 | |||
|
899 | def link_to_group(users_group_name, **kwargs): | |||
|
900 | return link_to( | |||
|
901 | escape(users_group_name), | |||
|
902 | route_path('user_group_profile', user_group_name=users_group_name), | |||
|
903 | **kwargs) | |||
|
904 | ||||
|
905 | ||||
899 | def person(author, show_attr="username_and_name"): |
|
906 | def person(author, show_attr="username_and_name"): | |
900 | user = discover_user(author) |
|
907 | user = discover_user(author) | |
901 | if user: |
|
908 | if user: |
@@ -134,13 +134,17 b' def get_user_group_slug(request):' | |||||
134 | elif getattr(request, 'matchdict', None): |
|
134 | elif getattr(request, 'matchdict', None): | |
135 | # pyramid |
|
135 | # pyramid | |
136 | _user_group = request.matchdict.get('user_group_id') |
|
136 | _user_group = request.matchdict.get('user_group_id') | |
137 |
|
137 | _user_group_name = request.matchdict.get('user_group_name') | ||
138 | try: |
|
138 | try: | |
139 | _user_group = UserGroup.get(_user_group) |
|
139 | if _user_group: | |
|
140 | _user_group = UserGroup.get(_user_group) | |||
|
141 | elif _user_group_name: | |||
|
142 | _user_group = UserGroup.get_by_group_name(_user_group_name) | |||
|
143 | ||||
140 | if _user_group: |
|
144 | if _user_group: | |
141 | _user_group = _user_group.users_group_name |
|
145 | _user_group = _user_group.users_group_name | |
142 | except Exception: |
|
146 | except Exception: | |
143 | log.exception('Failed to get user group by id') |
|
147 | log.exception('Failed to get user group by id and name') | |
144 | # catch all failures here |
|
148 | # catch all failures here | |
145 | return None |
|
149 | return None | |
146 |
|
150 |
@@ -98,7 +98,7 b'' | |||||
98 | ${_user_group.users_group_name} |
|
98 | ${_user_group.users_group_name} | |
99 | </a> |
|
99 | </a> | |
100 | %else: |
|
100 | %else: | |
101 | ${_user_group.users_group_name} |
|
101 | ${h.link_to_group(_user_group.users_group_name)} | |
102 | %endif |
|
102 | %endif | |
103 | </td> |
|
103 | </td> | |
104 | <td class="td-action"> |
|
104 | <td class="td-action"> |
@@ -89,7 +89,7 b'' | |||||
89 | ${_user_group.users_group_name} |
|
89 | ${_user_group.users_group_name} | |
90 | </a> |
|
90 | </a> | |
91 | %else: |
|
91 | %else: | |
92 | ${_user_group.users_group_name} |
|
92 | ${h.link_to_group(_user_group.users_group_name)} | |
93 | %endif |
|
93 | %endif | |
94 | </td> |
|
94 | </td> | |
95 | <td class="td-action"> |
|
95 | <td class="td-action"> |
@@ -100,7 +100,7 b'' | |||||
100 | ${_user_group.users_group_name} |
|
100 | ${_user_group.users_group_name} | |
101 | </a> |
|
101 | </a> | |
102 | %else: |
|
102 | %else: | |
103 | ${_user_group.users_group_name} |
|
103 | ${h.link_to_group(_user_group.users_group_name)} | |
104 | %endif |
|
104 | %endif | |
105 | </td> |
|
105 | </td> | |
106 | <td class="td-action"> |
|
106 | <td class="td-action"> |
General Comments 0
You need to be logged in to leave comments.
Login now