##// END OF EJS Templates
admin-users: add view for user groups managment...
Bartłomiej Wołyńczyk -
r1556:9ac012a6 default
parent child Browse files
Show More
@@ -23,7 +23,7 b' import pytest'
23 23
24 24 from rhodecode.model.user import UserModel
25 25 from rhodecode.model.user_group import UserGroupModel
26 from rhodecode.tests import TEST_USER_REGULAR_LOGIN
26 from rhodecode.tests import TEST_USER_ADMIN_EMAIL
27 27 from rhodecode.api.tests.utils import (
28 28 build_data, api_call, assert_error, assert_ok, crash, jsonify)
29 29
@@ -33,7 +33,8 b' class TestUpdateUserGroup(object):'
33 33 @pytest.mark.parametrize("changing_attr, updates", [
34 34 ('group_name', {'group_name': 'new_group_name'}),
35 35 ('group_name', {'group_name': 'test_group_for_update'}),
36 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
36 # ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
37 ('owner_email', {'owner_email': TEST_USER_ADMIN_EMAIL}),
37 38 ('active', {'active': False}),
38 39 ('active', {'active': True})
39 40 ])
@@ -59,7 +60,8 b' class TestUpdateUserGroup(object):'
59 60 # TODO: mikhail: decide if we need to test against the commented params
60 61 # ('group_name', {'group_name': 'new_group_name'}),
61 62 # ('group_name', {'group_name': 'test_group_for_update'}),
62 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
63 # ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
64 ('owner_email', {'owner_email': TEST_USER_ADMIN_EMAIL}),
63 65 ('active', {'active': False}),
64 66 ('active', {'active': True})
65 67 ])
@@ -70,6 +70,15 b' def admin_routes(config):'
70 70 name='edit_user_auth_tokens_delete',
71 71 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete')
72 72
73 # user groups management
74 config.add_route(
75 name='edit_user_groups_management',
76 pattern='/users/{user_id:\d+}/edit/groups_management')
77
78 config.add_route(
79 name='edit_user_groups_management_updates',
80 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates')
81
73 82
74 83 def includeme(config):
75 84 settings = config.get_settings()
@@ -22,6 +22,7 b' import logging'
22 22
23 23 from pyramid.httpexceptions import HTTPFound
24 24 from pyramid.view import view_config
25 from rhodecode_tools.lib.ext_json import json
25 26
26 27 from rhodecode.apps._base import BaseAppView
27 28 from rhodecode.lib.auth import (
@@ -30,6 +31,7 b' from rhodecode.lib import helpers as h'
30 31 from rhodecode.lib.utils import PartialRenderer
31 32 from rhodecode.lib.utils2 import safe_int, safe_unicode
32 33 from rhodecode.model.auth_token import AuthTokenModel
34 from rhodecode.model.user_group import UserGroupModel
33 35 from rhodecode.model.db import User, or_
34 36 from rhodecode.model.meta import Session
35 37
@@ -235,3 +237,49 b' class AdminUsersView(BaseAppView):'
235 237 h.flash(_("Auth token successfully deleted"), category='success')
236 238
237 239 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
240
241
242 @LoginRequired()
243 @HasPermissionAllDecorator('hg.admin')
244 @view_config(
245 route_name='edit_user_groups_management', request_method='GET',
246 renderer='rhodecode:templates/admin/users/user_edit.mako')
247 def groups_management(self):
248 c = self.load_default_context()
249
250 user_id = self.request.matchdict.get('user_id')
251 c.user = User.get_or_404(user_id, pyramid_exc=True)
252 c.data = c.user.group_member
253 self._redirect_for_default_user(c.user.username)
254 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) for group in c.user.group_member]
255 c.groups = json.dumps(groups)
256 c.active = 'groups'
257
258 return self._get_template_context(c)
259
260
261 @LoginRequired()
262 @HasPermissionAllDecorator('hg.admin')
263 @view_config(
264 route_name='edit_user_groups_management_updates', request_method='POST')
265 def groups_management_updates(self):
266 _ = self.request.translate
267 c = self.load_default_context()
268
269 user_id = self.request.matchdict.get('user_id')
270 c.user = User.get_or_404(user_id, pyramid_exc=True)
271 self._redirect_for_default_user(c.user.username)
272
273 users_groups = set(self.request.POST.getall('users_group_id'))
274 users_groups_model = []
275
276 for ugid in users_groups:
277 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
278 user_group_model = UserGroupModel()
279 user_group_model.change_groups(c.user, users_groups_model)
280
281 Session().commit()
282 c.active = 'user_groups_management'
283 h.flash(_("Groups successfully changed"), category='success')
284
285 return HTTPFound(h.route_path('edit_user_groups_management', user_id=user_id))
@@ -1261,14 +1261,15 b' class UserGroup(Base, BaseModel):'
1261 1261
1262 1262 """
1263 1263 user_group = self
1264
1265 1264 data = {
1266 1265 'users_group_id': user_group.users_group_id,
1267 1266 'group_name': user_group.users_group_name,
1268 1267 'group_description': user_group.user_group_description,
1269 1268 'active': user_group.users_group_active,
1270 1269 'owner': user_group.user.username,
1270 'owner_email': user_group.user.email,
1271 1271 }
1272
1272 1273 if with_group_members:
1273 1274 users = []
1274 1275 for user in user_group.members:
@@ -197,6 +197,7 b' class RepoModel(BaseModel):'
197 197 return _users
198 198
199 199 def get_user_groups(self, name_contains=None, limit=20, only_active=True):
200
200 201 # TODO: mikhail: move this method to the UserGroupModel.
201 202 query = self.sa.query(UserGroup)
202 203 if only_active:
@@ -223,6 +224,12 b' class RepoModel(BaseModel):'
223 224 'value_display': 'Group: %s (%d members)' % (
224 225 group.users_group_name, len(group.members),),
225 226 'value': group.users_group_name,
227 'description': group.user_group_description,
228 'owner': group.user.username,
229
230 'owner_icon': h.gravatar_url(group.user.email, 30),
231 'value_display_owner': h.person(group.user.email),
232
226 233 'value_type': 'user_group',
227 234 'active': group.users_group_active,
228 235 }
@@ -512,3 +512,49 b' class UserGroupModel(BaseModel):'
512 512 else:
513 513 log.debug('Skipping addition to group %s since it is '
514 514 'not managed by auth plugins' % gr)
515
516
517 def change_groups(self, user, groups):
518 """
519 This method changes user group assignment
520 :param user: User
521 :param groups: array of UserGroupModel
522 :return:
523 """
524 user = self._get_user(user)
525 log.debug('Changing user(%s) assignment to groups(%s)', user, groups)
526 current_groups = user.group_member
527 current_groups = [x.users_group for x in current_groups]
528
529 # calculate from what groups user should be removed/add
530 groups = set(groups)
531 current_groups = set(current_groups)
532
533 groups_to_remove = current_groups - groups
534 groups_to_add = groups - current_groups
535
536 for gr in groups_to_remove:
537 log.debug('Removing user %s from user group %s', user.username, gr.users_group_name)
538 self.remove_user_from_group(gr.users_group_name, user.username)
539 for gr in groups_to_add:
540 log.debug('Adding user %s to user group %s', user.username, gr.users_group_name)
541 UserGroupModel().add_user_to_group(gr.users_group_name, user.username)
542
543 @staticmethod
544 def get_user_groups_as_dict(user_group):
545 import rhodecode.lib.helpers as h
546
547 data = {
548 'users_group_id': user_group.users_group_id,
549 'group_name': user_group.users_group_name,
550 'group_description': user_group.user_group_description,
551 'active': user_group.users_group_active,
552 "owner": user_group.user.username,
553 'owner_icon': h.gravatar_url(user_group.user.email, 30),
554 "owner_data": {'owner': user_group.user.username, 'owner_icon': h.gravatar_url(user_group.user.email, 30)}
555 }
556 return data
557
558
559
560
@@ -37,6 +37,11 b''
37 37 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
38 38 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
39 39 <li class="${'active' if c.active=='ips' else ''}"><a href="${h.url('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
40
41 <li class="${'active' if c.active=='groups' else ''}">
42 <a href="${h.route_path('edit_user_groups_management', user_id=c.user.user_id)}">${_('User Groups Management')}</a>
43 </li>
44
40 45 </ul>
41 46 </div>
42 47
@@ -1,75 +1,85 b''
1 1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
3 2
4 <%def name="title()">
5 ${_('User groups administration')}
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
9 </%def>
10
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_group_count">0</span> ${_('user groups')}
14 </%def>
15 3
16 <%def name="menu_bar_nav()">
17 ${self.menu_items(active='admin')}
18 </%def>
19
20 <%def name="main()">
21 <div class="box">
22
23 <div class="title">
24 ${self.breadcrumbs()}
25 <ul class="links">
26 %if h.HasPermissionAny('hg.admin', 'hg.usergroup.create.true')():
27 <li>
28 <a href="${h.url('new_users_group')}" class="btn btn-small btn-success">${_(u'Add User Group')}</a>
29 </li>
30 %endif
31 </ul>
4 <div class="panel panel-default">
5 <div class="panel-heading">
6 <h3 class="panel-title">${_('User groups administration')}</h3>
7 </div>
8 <div class="panel-body">
9 <div class="field">
10 <div class="label label-checkbox">
11 <label for="users_group_active">${_('Add user to group')}:</label>
12 </div>
13 <div class="input">
14 ${h.text('add_user_to_group', placeholder="user group name", class_="medium")}
32 15 </div>
33 16
17 </div>
18
19 <div class="groups_management">
20 ${h.secure_form(h.route_path('edit_user_groups_management_updates', user_id=c.user.user_id), method='post')}
34 21 <div id="repos_list_wrap">
35 22 <table id="user_group_list_table" class="display"></table>
36 23 </div>
37
24 <div class="buttons">
25 ${h.submit('save',_('Save'),class_="btn")}
26 </div>
27 ${h.end_form()}
28 </div>
29 </div>
38 30 </div>
39 31 <script>
32 var api;
40 33 $(document).ready(function() {
41 34
42 35 var get_datatable_count = function(){
43 var api = $('#user_group_list_table').dataTable().api();
44 36 $('#user_group_count').text(api.page.info().recordsDisplay);
45 37 };
46 38
47 // user list
39 $('#user_group_list_table').on('click', 'a.editor_remove', function (e) {
40 e.preventDefault();
41 var row = api.row($(this).closest('tr'));
42 row.remove().draw();
43 } );
44
48 45 $('#user_group_list_table').DataTable({
49 data: ${c.data|n},
46 data: ${c.groups|n},
50 47 dom: 'rtp',
51 48 pageLength: ${c.visual.admin_grid_items},
52 49 order: [[ 0, "asc" ]],
53 50 columns: [
54 51 { data: {"_": "group_name",
55 "sort": "group_name_raw"}, title: "${_('Name')}", className: "td-componentname" },
56 { data: {"_": "desc",
57 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
58 { data: {"_": "members",
59 "sort": "members",
60 "type": Number}, title: "${_('Members')}", className: "td-number" },
52 "sort": "group_name"}, title: "${_('Name')}", className: "td-componentname," ,
53 render: function (data,type,full,meta)
54 {return '<div><i class="icon-group" title="User group">'+data+'</i></div>'}},
55
56 { data: {"_": "group_description",
57 "sort": "group_description"}, title: "${_('Description')}", className: "td-description" },
58 { data: {"_": "users_group_id"}, className: "td-user",
59 render: function (data,type,full,meta)
60 {return '<input type="hidden" name="users_group_id" value="'+data+'">'}},
61 61 { data: {"_": "active",
62 62 "sort": "active"}, title: "${_('Active')}", className: "td-active", className: "td-number"},
63 { data: {"_": "owner",
64 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
65 { data: {"_": "action",
66 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
63 { data: {"_": "owner_data"}, title: "${_('Owner')}", className: "td-user",
64 render: function (data,type,full,meta)
65 {return '<div class="rc-user tooltip">'+
66 '<img class="gravatar" src="'+ data.owner_icon +'" height="16" width="16">'+
67 data.owner +'</div>'
68 }
69 },
70 { data: null,
71 title: "${_('Action')}",
72 className: "td-action",
73 defaultContent: '<a href="" class="btn btn-link btn-danger">Delete</a>'
74 },
67 75 ],
68 76 language: {
69 77 paginate: DEFAULT_GRID_PAGINATION,
70 78 emptyTable: _gettext("No user groups available yet.")
71 79 },
72 80 "initComplete": function( settings, json ) {
81 var data_grid = $('#user_group_list_table').dataTable();
82 api = data_grid.api();
73 83 get_datatable_count();
74 84 }
75 85 });
@@ -93,6 +103,43 b''
93 103
94 104 });
95 105
106 $('#language').select2({
107 'containerCssClass': "drop-menu",
108 'dropdownCssClass': "drop-menu-dropdown",
109 'dropdownAutoWidth': true
110 });
111
112
113
114 $(document).ready(function(){
115 $("#group_parent_id").select2({
116 'containerCssClass': "drop-menu",
117 'dropdownCssClass': "drop-menu-dropdown",
118 'dropdownAutoWidth': true
119 });
120
121 $('#add_user_to_group').autocomplete({
122 serviceUrl: pyroutes.url('user_group_autocomplete_data'),
123 minChars:2,
124 maxHeight:400,
125 width:300,
126 deferRequestBy: 300, //miliseconds
127 showNoSuggestionNotice: true,
128 params: { user_groups:true },
129 formatResult: autocompleteFormatResult,
130 lookupFilter: autocompleteFilterResult,
131 onSelect: function(element, suggestion){
132 var owner = {owner_icon: suggestion.owner_icon, owner:suggestion.owner};
133 api.row.add(
134 {"active": suggestion.active,
135 "owner_data": owner,
136 "users_group_id": suggestion.id,
137 "group_description": suggestion.description,
138 "group_name": suggestion.value}).draw();
139 }
140 });
141 })
142
96 143 </script>
97 144
98 </%def>
145
@@ -260,6 +260,7 b' class TestGetUserGroups(object):'
260 260 user_util.create_user_group(users_group_active=True))
261 261
262 262 group_filter = created_groups[-1].users_group_name[-2:]
263 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
263 264 with self._patch_user_group_list():
264 265 groups = RepoModel().get_user_groups(group_filter)
265 266
@@ -275,7 +276,7 b' class TestGetUserGroups(object):'
275 276 for i in range(3):
276 277 created_groups.append(
277 278 user_util.create_user_group(users_group_active=True))
278
279 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
279 280 with self._patch_user_group_list():
280 281 groups = RepoModel().get_user_groups('test_returns')
281 282
@@ -287,7 +288,7 b' class TestGetUserGroups(object):'
287 288 for i in range(4):
288 289 is_active = i % 2 == 0
289 290 user_util.create_user_group(users_group_active=is_active)
290
291 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
291 292 with self._patch_user_group_list():
292 293 groups = RepoModel().get_user_groups()
293 294 expected = ('id', 'icon_link', 'value_display', 'value', 'value_type')
General Comments 0
You need to be logged in to leave comments. Login now