##// END OF EJS Templates
Public user group profile Task #5326
Bartłomiej Wołyńczyk -
r2638:01feb8aa default
parent child Browse files
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 &middot; ${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