Show More
@@ -67,7 +67,6 b'' | |||||
67 | "<%= dirs.js.src %>/rhodecode/utils/ie.js", |
|
67 | "<%= dirs.js.src %>/rhodecode/utils/ie.js", | |
68 | "<%= dirs.js.src %>/rhodecode/utils/os.js", |
|
68 | "<%= dirs.js.src %>/rhodecode/utils/os.js", | |
69 | "<%= dirs.js.src %>/rhodecode/utils/topics.js", |
|
69 | "<%= dirs.js.src %>/rhodecode/utils/topics.js", | |
70 | "<%= dirs.js.src %>/rhodecode/widgets/multiselect.js", |
|
|||
71 | "<%= dirs.js.src %>/rhodecode/init.js", |
|
70 | "<%= dirs.js.src %>/rhodecode/init.js", | |
72 | "<%= dirs.js.src %>/rhodecode/codemirror.js", |
|
71 | "<%= dirs.js.src %>/rhodecode/codemirror.js", | |
73 | "<%= dirs.js.src %>/rhodecode/comments.js", |
|
72 | "<%= dirs.js.src %>/rhodecode/comments.js", |
@@ -305,7 +305,7 b' def make_map(config):' | |||||
305 | m.connect('delete_user', '/users/{user_id}', |
|
305 | m.connect('delete_user', '/users/{user_id}', | |
306 | action='delete', conditions={'method': ['DELETE']}) |
|
306 | action='delete', conditions={'method': ['DELETE']}) | |
307 | m.connect('edit_user', '/users/{user_id}/edit', |
|
307 | m.connect('edit_user', '/users/{user_id}/edit', | |
308 | action='edit', conditions={'method': ['GET']}) |
|
308 | action='edit', conditions={'method': ['GET']}, jsroute=True) | |
309 | m.connect('user', '/users/{user_id}', |
|
309 | m.connect('user', '/users/{user_id}', | |
310 | action='show', conditions={'method': ['GET']}) |
|
310 | action='show', conditions={'method': ['GET']}) | |
311 | m.connect('force_password_reset_user', '/users/{user_id}/password_reset', |
|
311 | m.connect('force_password_reset_user', '/users/{user_id}/password_reset', | |
@@ -389,7 +389,7 b' def make_map(config):' | |||||
389 |
|
389 | |||
390 | m.connect('edit_user_group_members', |
|
390 | m.connect('edit_user_group_members', | |
391 | '/user_groups/{user_group_id}/edit/members', jsroute=True, |
|
391 | '/user_groups/{user_group_id}/edit/members', jsroute=True, | |
392 |
action=' |
|
392 | action='user_group_members', conditions={'method': ['GET']}) | |
393 |
|
393 | |||
394 | # ADMIN PERMISSIONS ROUTES |
|
394 | # ADMIN PERMISSIONS ROUTES | |
395 | with rmap.submapper(path_prefix=ADMIN_PREFIX, |
|
395 | with rmap.submapper(path_prefix=ADMIN_PREFIX, |
@@ -25,6 +25,7 b' User Groups crud controller for pylons' | |||||
25 | import logging |
|
25 | import logging | |
26 | import formencode |
|
26 | import formencode | |
27 |
|
27 | |||
|
28 | import peppercorn | |||
28 | from formencode import htmlfill |
|
29 | from formencode import htmlfill | |
29 | from pylons import request, tmpl_context as c, url, config |
|
30 | from pylons import request, tmpl_context as c, url, config | |
30 | from pylons.controllers.util import redirect |
|
31 | from pylons.controllers.util import redirect | |
@@ -40,7 +41,7 b' from rhodecode.lib.utils import jsonify,' | |||||
40 | from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int |
|
41 | from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int | |
41 | from rhodecode.lib.auth import ( |
|
42 | from rhodecode.lib.auth import ( | |
42 | LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator, |
|
43 | LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator, | |
43 | HasPermissionAnyDecorator) |
|
44 | HasPermissionAnyDecorator, XHRRequired) | |
44 | from rhodecode.lib.base import BaseController, render |
|
45 | from rhodecode.lib.base import BaseController, render | |
45 | from rhodecode.model.permission import PermissionModel |
|
46 | from rhodecode.model.permission import PermissionModel | |
46 | from rhodecode.model.scm import UserGroupList |
|
47 | from rhodecode.model.scm import UserGroupList | |
@@ -69,13 +70,8 b' class UserGroupsController(BaseControlle' | |||||
69 | def __load_data(self, user_group_id): |
|
70 | def __load_data(self, user_group_id): | |
70 | c.group_members_obj = [x.user for x in c.user_group.members] |
|
71 | c.group_members_obj = [x.user for x in c.user_group.members] | |
71 | c.group_members_obj.sort(key=lambda u: u.username.lower()) |
|
72 | c.group_members_obj.sort(key=lambda u: u.username.lower()) | |
72 |
|
||||
73 | c.group_members = [(x.user_id, x.username) for x in c.group_members_obj] |
|
73 | c.group_members = [(x.user_id, x.username) for x in c.group_members_obj] | |
74 |
|
74 | |||
75 | c.available_members = [(x.user_id, x.username) |
|
|||
76 | for x in User.query().all()] |
|
|||
77 | c.available_members.sort(key=lambda u: u[1].lower()) |
|
|||
78 |
|
||||
79 | def __load_defaults(self, user_group_id): |
|
75 | def __load_defaults(self, user_group_id): | |
80 | """ |
|
76 | """ | |
81 | Load defaults settings for edit, and update |
|
77 | Load defaults settings for edit, and update | |
@@ -207,20 +203,21 b' class UserGroupsController(BaseControlle' | |||||
207 | c.active = 'settings' |
|
203 | c.active = 'settings' | |
208 | self.__load_data(user_group_id) |
|
204 | self.__load_data(user_group_id) | |
209 |
|
205 | |||
210 | available_members = [safe_unicode(x[0]) for x in c.available_members] |
|
|||
211 |
|
||||
212 | users_group_form = UserGroupForm( |
|
206 | users_group_form = UserGroupForm( | |
213 | edit=True, old_data=c.user_group.get_dict(), |
|
207 | edit=True, old_data=c.user_group.get_dict(), allow_disabled=True)() | |
214 | available_members=available_members, allow_disabled=True)() |
|
|||
215 |
|
208 | |||
216 | try: |
|
209 | try: | |
217 | form_result = users_group_form.to_python(request.POST) |
|
210 | form_result = users_group_form.to_python(request.POST) | |
|
211 | pstruct = peppercorn.parse(request.POST.items()) | |||
|
212 | form_result['users_group_members'] = pstruct['user_group_members'] | |||
|
213 | ||||
218 | UserGroupModel().update(c.user_group, form_result) |
|
214 | UserGroupModel().update(c.user_group, form_result) | |
219 | gr = form_result['users_group_name'] |
|
215 | updated_user_group = form_result['users_group_name'] | |
220 | action_logger(c.rhodecode_user, |
|
216 | action_logger(c.rhodecode_user, | |
221 | 'admin_updated_users_group:%s' % gr, |
|
217 | 'admin_updated_users_group:%s' % updated_user_group, | |
222 | None, self.ip_addr, self.sa) |
|
218 | None, self.ip_addr, self.sa) | |
223 |
h.flash(_('Updated user group %s') % gr, |
|
219 | h.flash(_('Updated user group %s') % updated_user_group, | |
|
220 | category='success') | |||
224 | Session().commit() |
|
221 | Session().commit() | |
225 | except formencode.Invalid as errors: |
|
222 | except formencode.Invalid as errors: | |
226 | defaults = errors.value |
|
223 | defaults = errors.value | |
@@ -462,19 +459,29 b' class UserGroupsController(BaseControlle' | |||||
462 | return render('admin/user_groups/user_group_edit.html') |
|
459 | return render('admin/user_groups/user_group_edit.html') | |
463 |
|
460 | |||
464 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') |
|
461 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') | |
465 | def edit_members(self, user_group_id): |
|
462 | @XHRRequired() | |
|
463 | @jsonify | |||
|
464 | def user_group_members(self, user_group_id): | |||
466 | user_group_id = safe_int(user_group_id) |
|
465 | user_group_id = safe_int(user_group_id) | |
467 |
|
|
466 | user_group = UserGroup.get_or_404(user_group_id) | |
468 | c.active = 'members' |
|
467 | group_members_obj = sorted((x.user for x in user_group.members), | |
469 | c.group_members_obj = sorted((x.user for x in c.user_group.members), |
|
468 | key=lambda u: u.username.lower()) | |
470 | key=lambda u: u.username.lower()) |
|
|||
471 |
|
469 | |||
472 | group_members = [(x.user_id, x.username) for x in c.group_members_obj] |
|
470 | group_members = [ | |
|
471 | { | |||
|
472 | 'id': user.user_id, | |||
|
473 | 'first_name': user.name, | |||
|
474 | 'last_name': user.lastname, | |||
|
475 | 'username': user.username, | |||
|
476 | 'icon_link': h.gravatar_url(user.email, 30), | |||
|
477 | 'value_display': h.person(user.email), | |||
|
478 | 'value': user.username, | |||
|
479 | 'value_type': 'user', | |||
|
480 | 'active': user.active, | |||
|
481 | } | |||
|
482 | for user in group_members_obj | |||
|
483 | ] | |||
473 |
|
484 | |||
474 | if request.is_xhr: |
|
485 | return { | |
475 | return jsonify(lambda *a, **k: { |
|
486 | 'members': group_members | |
476 | 'members': group_members |
|
487 | } | |
477 | }) |
|
|||
478 |
|
||||
479 | c.group_members = group_members |
|
|||
480 | return render('admin/user_groups/user_group_edit.html') |
|
@@ -143,10 +143,8 b' def UserForm(edit=False, available_langu' | |||||
143 | return _UserForm |
|
143 | return _UserForm | |
144 |
|
144 | |||
145 |
|
145 | |||
146 |
def UserGroupForm(edit=False, old_data=None, a |
|
146 | def UserGroupForm(edit=False, old_data=None, allow_disabled=False): | |
147 | allow_disabled=False): |
|
|||
148 | old_data = old_data or {} |
|
147 | old_data = old_data or {} | |
149 | available_members = available_members or [] |
|
|||
150 |
|
148 | |||
151 | class _UserGroupForm(formencode.Schema): |
|
149 | class _UserGroupForm(formencode.Schema): | |
152 | allow_extra_fields = True |
|
150 | allow_extra_fields = True | |
@@ -162,10 +160,6 b' def UserGroupForm(edit=False, old_data=N' | |||||
162 | users_group_active = v.StringBoolean(if_missing=False) |
|
160 | users_group_active = v.StringBoolean(if_missing=False) | |
163 |
|
161 | |||
164 | if edit: |
|
162 | if edit: | |
165 | users_group_members = v.OneOf( |
|
|||
166 | available_members, hideList=False, testValueList=True, |
|
|||
167 | if_missing=None, not_empty=False |
|
|||
168 | ) |
|
|||
169 | # this is user group owner |
|
163 | # this is user group owner | |
170 | user = All( |
|
164 | user = All( | |
171 | v.UnicodeString(not_empty=True), |
|
165 | v.UnicodeString(not_empty=True), |
@@ -189,18 +189,15 b' class UserGroupModel(BaseModel):' | |||||
189 | self._log_user_changes('removed from', user_group, removed) |
|
189 | self._log_user_changes('removed from', user_group, removed) | |
190 |
|
190 | |||
191 | def _clean_members_data(self, members_data): |
|
191 | def _clean_members_data(self, members_data): | |
192 | # TODO: anderson: this should be in the form validation but I couldn't |
|
|||
193 | # make it work there as it conflicts with the other validator |
|
|||
194 | if not members_data: |
|
192 | if not members_data: | |
195 | members_data = [] |
|
193 | members_data = [] | |
196 |
|
194 | |||
197 | if isinstance(members_data, basestring): |
|
195 | members = [] | |
198 |
|
|
196 | for user in members_data: | |
199 | else: |
|
197 | uid = int(user['member_user_id']) | |
200 | new_members = members_data |
|
198 | if uid not in members and user['type'] in ['new', 'existing']: | |
201 |
|
199 | members.append(uid) | ||
202 | new_members = [int(uid) for uid in new_members] |
|
200 | return members | |
203 | return new_members |
|
|||
204 |
|
201 | |||
205 | def update(self, user_group, form_data): |
|
202 | def update(self, user_group, form_data): | |
206 | user_group = self._get_user_group(user_group) |
|
203 | user_group = self._get_user_group(user_group) |
@@ -1370,7 +1370,27 b' table.integrations {' | |||||
1370 | margin-right: .5em; |
|
1370 | margin-right: .5em; | |
1371 | margin-left: 3px; |
|
1371 | margin-left: 3px; | |
1372 | } |
|
1372 | } | |
|
1373 | ||||
|
1374 | .to-delete { | |||
|
1375 | .user { | |||
|
1376 | text-decoration: line-through; | |||
|
1377 | } | |||
|
1378 | } | |||
1373 | } |
|
1379 | } | |
|
1380 | ||||
|
1381 | // new entry in group_members | |||
|
1382 | .td-author-new-entry { | |||
|
1383 | background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3); | |||
|
1384 | } | |||
|
1385 | ||||
|
1386 | .usergroup_member_remove { | |||
|
1387 | width: 16px; | |||
|
1388 | margin-bottom: 10px; | |||
|
1389 | padding: 0; | |||
|
1390 | color: black !important; | |||
|
1391 | cursor: pointer; | |||
|
1392 | } | |||
|
1393 | ||||
1374 | .reviewer_ac .ac-input { |
|
1394 | .reviewer_ac .ac-input { | |
1375 | width: 92%; |
|
1395 | width: 92%; | |
1376 | margin-bottom: 1em; |
|
1396 | margin-bottom: 1em; |
@@ -16,6 +16,7 b' function registerRCRoutes() {' | |||||
16 | pyroutes.register('user_autocomplete_data', '/_users', []); |
|
16 | pyroutes.register('user_autocomplete_data', '/_users', []); | |
17 | pyroutes.register('user_group_autocomplete_data', '/_user_groups', []); |
|
17 | pyroutes.register('user_group_autocomplete_data', '/_user_groups', []); | |
18 | pyroutes.register('new_repo', '/_admin/create_repository', []); |
|
18 | pyroutes.register('new_repo', '/_admin/create_repository', []); | |
|
19 | pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']); | |||
19 | pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']); |
|
20 | pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']); | |
20 | pyroutes.register('gists', '/_admin/gists', []); |
|
21 | pyroutes.register('gists', '/_admin/gists', []); | |
21 | pyroutes.register('new_gist', '/_admin/gists/new', []); |
|
22 | pyroutes.register('new_gist', '/_admin/gists/new', []); |
@@ -35,7 +35,6 b'' | |||||
35 | <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_group_advanced', user_group_id=c.user_group.users_group_id)}">${_('Advanced')}</a></li> |
|
35 | <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_group_advanced', user_group_id=c.user_group.users_group_id)}">${_('Advanced')}</a></li> | |
36 | <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_group_global_perms', user_group_id=c.user_group.users_group_id)}">${_('Global permissions')}</a></li> |
|
36 | <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_group_global_perms', user_group_id=c.user_group.users_group_id)}">${_('Global permissions')}</a></li> | |
37 | <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_group_perms_summary', user_group_id=c.user_group.users_group_id)}">${_('Permissions summary')}</a></li> |
|
37 | <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_group_perms_summary', user_group_id=c.user_group.users_group_id)}">${_('Permissions summary')}</a></li> | |
38 | <li class="${'active' if c.active=='members' else ''}"><a href="${h.url('edit_user_group_members', user_group_id=c.user_group.users_group_id)}">${_('Members')}</a></li> |
|
|||
39 | </ul> |
|
38 | </ul> | |
40 | </div> |
|
39 | </div> | |
41 |
|
40 |
@@ -54,40 +54,53 b'' | |||||
54 | ${h.checkbox('users_group_active',value=True)} |
|
54 | ${h.checkbox('users_group_active',value=True)} | |
55 | </div> |
|
55 | </div> | |
56 | </div> |
|
56 | </div> | |
57 | <div class="field"> |
|
57 | ||
58 |
|
|
58 | <div class="field"> | |
59 | <label for="users_group_active">${_('Search')}:</label> |
|
59 | <div class="label label-checkbox"> | |
60 | ${h.text('from_user_group', |
|
60 | <label for="users_group_active">${_('Add members')}:</label> | |
61 | placeholder="user/usergroup", |
|
61 | </div> | |
62 |
|
|
62 | <div class="input"> | |
|
63 | ${h.text('user_group_add_members', placeholder="user/usergroup", class_="medium")} | |||
63 | </div> |
|
64 | </div> | |
64 | <div class="select side-by-side-selector"> |
|
65 | </div> | |
65 | <div class="left-group"> |
|
66 | ||
66 | <label class="text"><strong>${_('Chosen group members')}</strong></label> |
|
67 | <input type="hidden" name="__start__" value="user_group_members:sequence"/> | |
67 | ${h.select('users_group_members',[x[0] for x in c.group_members],c.group_members,multiple=True,size=8,)} |
|
68 | <table id="group_members_placeholder" class="rctable group_members"> | |
68 | <div class="btn" id="remove_all_elements" > |
|
69 | <tr> | |
69 |
|
|
70 | <th>${_('Username')}</th> | |
70 | <i class="icon-chevron-right"></i> |
|
71 | <th>${_('Action')}</th> | |
71 |
|
|
72 | </tr> | |
72 | </div> |
|
73 | ||
73 |
|
|
74 | % if c.group_members_obj: | |
74 | <i id="add_element" class="icon-chevron-left"></i> |
|
75 | % for user in c.group_members_obj: | |
75 |
|
|
76 | <tr> | |
76 |
|
|
77 | <td id="member_user_${user.user_id}" class="td-author"> | |
77 |
< |
|
78 | <div class="group_member"> | |
78 | <div class="right-group"> |
|
79 | ${base.gravatar(user.email, 16)} | |
79 | <label class="text" >${_('Available users')} |
|
80 | <span class="username user">${h.link_to(h.person(user), h.url( 'edit_user',user_id=user.user_id))}</span> | |
80 | </label> |
|
81 | <input type="hidden" name="__start__" value="member:mapping"> | |
81 | ${h.select('available_members',[],c.available_members,multiple=True,size=8,)} |
|
82 | <input type="hidden" name="member_user_id" value="${user.user_id}"> | |
82 | <div class="btn" id="add_all_elements" > |
|
83 | <input type="hidden" name="type" value="existing" id="member_${user.user_id}"> | |
83 | <i class="icon-chevron-left"></i>${_('Add all elements')} |
|
84 | <input type="hidden" name="__end__" value="member:mapping"> | |
84 |
|
|
85 | </div> | |
85 |
</d |
|
86 | </td> | |
86 |
< |
|
87 | <td class=""> | |
87 | </div> |
|
88 | <div class="usergroup_member_remove action_button" onclick="removeUserGroupMember(${user.user_id}, true)" style="visibility: visible;"> | |
88 |
< |
|
89 | <i class="icon-remove-sign"></i> | |
89 | ${h.submit('Save',_('Save'),class_="btn")} |
|
90 | </div> | |
90 |
</d |
|
91 | </td> | |
|
92 | </tr> | |||
|
93 | % endfor | |||
|
94 | ||||
|
95 | % else: | |||
|
96 | <tr><td colspan="2">${_('No members yet')}</td></tr> | |||
|
97 | % endif | |||
|
98 | </table> | |||
|
99 | <input type="hidden" name="__end__" value="user_group_members:sequence"/> | |||
|
100 | ||||
|
101 | <div class="buttons"> | |||
|
102 | ${h.submit('Save',_('Save'),class_="btn")} | |||
|
103 | </div> | |||
91 | </div> |
|
104 | </div> | |
92 | </div> |
|
105 | </div> | |
93 | ${h.end_form()} |
|
106 | ${h.end_form()} | |
@@ -95,15 +108,18 b'' | |||||
95 | </div> |
|
108 | </div> | |
96 | <script> |
|
109 | <script> | |
97 | $(document).ready(function(){ |
|
110 | $(document).ready(function(){ | |
98 | MultiSelectWidget('users_group_members','available_members','edit_users_group'); |
|
|||
99 |
|
||||
100 | $("#group_parent_id").select2({ |
|
111 | $("#group_parent_id").select2({ | |
101 | 'containerCssClass': "drop-menu", |
|
112 | 'containerCssClass': "drop-menu", | |
102 | 'dropdownCssClass': "drop-menu-dropdown", |
|
113 | 'dropdownCssClass': "drop-menu-dropdown", | |
103 | 'dropdownAutoWidth': true |
|
114 | 'dropdownAutoWidth': true | |
104 | }); |
|
115 | }); | |
105 |
|
116 | |||
106 | $('#from_user_group').autocomplete({ |
|
117 | removeUserGroupMember = function(userId){ | |
|
118 | $('#member_'+userId).val('remove'); | |||
|
119 | $('#member_user_'+userId).addClass('to-delete'); | |||
|
120 | }; | |||
|
121 | ||||
|
122 | $('#user_group_add_members').autocomplete({ | |||
107 | serviceUrl: pyroutes.url('user_autocomplete_data'), |
|
123 | serviceUrl: pyroutes.url('user_autocomplete_data'), | |
108 | minChars:2, |
|
124 | minChars:2, | |
109 | maxHeight:400, |
|
125 | maxHeight:400, | |
@@ -115,9 +131,37 b'' | |||||
115 | lookupFilter: autocompleteFilterResult, |
|
131 | lookupFilter: autocompleteFilterResult, | |
116 | onSelect: function(element, suggestion){ |
|
132 | onSelect: function(element, suggestion){ | |
117 |
|
133 | |||
118 |
function |
|
134 | function addMember(user, fromUserGroup) { | |
119 | $('#available_members').val(uids); |
|
135 | var gravatar = user.icon_link; | |
120 |
|
|
136 | var username = user.value_display; | |
|
137 | var userLink = pyroutes.url('edit_user', {"user_id": user.id}); | |||
|
138 | var uid = user.id; | |||
|
139 | ||||
|
140 | if (fromUserGroup) { | |||
|
141 | username = username +" "+ _gettext('(from usergroup {0})'.format(fromUserGroup)) | |||
|
142 | } | |||
|
143 | ||||
|
144 | var elem = $( | |||
|
145 | ('<tr>'+ | |||
|
146 | '<td id="member_user_{6}" class="td-author td-author-new-entry">'+ | |||
|
147 | '<div class="group_member">'+ | |||
|
148 | '<img class="gravatar" src="{0}" height="16" width="16">'+ | |||
|
149 | '<span class="username user"><a href="{1}">{2}</a></span>'+ | |||
|
150 | '<input type="hidden" name="__start__" value="member:mapping">'+ | |||
|
151 | '<input type="hidden" name="member_user_id" value="{3}">'+ | |||
|
152 | '<input type="hidden" name="type" value="new" id="member_{4}">'+ | |||
|
153 | '<input type="hidden" name="__end__" value="member:mapping">'+ | |||
|
154 | '</div>'+ | |||
|
155 | '</td>'+ | |||
|
156 | '<td class="td-author-new-entry">'+ | |||
|
157 | '<div class="usergroup_member_remove action_button" onclick="removeUserGroupMember({5}, true)" style="visibility: visible;">'+ | |||
|
158 | '<i class="icon-remove-sign"></i>'+ | |||
|
159 | '</div>'+ | |||
|
160 | '</td>'+ | |||
|
161 | '</tr>').format(gravatar, userLink, username, | |||
|
162 | uid, uid, uid, uid) | |||
|
163 | ); | |||
|
164 | $('#group_members_placeholder').append(elem) | |||
121 | } |
|
165 | } | |
122 |
|
166 | |||
123 | if (suggestion.value_type == 'user_group') { |
|
167 | if (suggestion.value_type == 'user_group') { | |
@@ -125,20 +169,18 b'' | |||||
125 | pyroutes.url('edit_user_group_members', |
|
169 | pyroutes.url('edit_user_group_members', | |
126 | {'user_group_id': suggestion.id}), |
|
170 | {'user_group_id': suggestion.id}), | |
127 | function(data) { |
|
171 | function(data) { | |
128 | var uids = []; |
|
|||
129 | $.each(data.members, function(idx, user) { |
|
172 | $.each(data.members, function(idx, user) { | |
130 |
|
|
173 | addMember(user, suggestion.value) | |
131 | username = user[1]; |
|
|||
132 | uids.push(userid.toString()); |
|
|||
133 | }); |
|
174 | }); | |
134 | preSelectUserIds(uids) |
|
|||
135 | } |
|
175 | } | |
136 | ); |
|
176 | ); | |
137 | } else if (suggestion.value_type == 'user') { |
|
177 | } else if (suggestion.value_type == 'user') { | |
138 |
|
|
178 | addMember(suggestion, null); | |
139 | } |
|
179 | } | |
140 | } |
|
180 | } | |
141 | }); |
|
181 | }); | |
|
182 | ||||
|
183 | ||||
142 | UsersAutoComplete('user', '${c.rhodecode_user.user_id}'); |
|
184 | UsersAutoComplete('user', '${c.rhodecode_user.user_id}'); | |
143 | }) |
|
185 | }) | |
144 | </script> |
|
186 | </script> |
@@ -545,12 +545,6 b'' | |||||
545 | </div> |
|
545 | </div> | |
546 | </div> |
|
546 | </div> | |
547 |
|
547 | |||
548 | <script> |
|
|||
549 | $(document).ready(function(){ |
|
|||
550 | MultiSelectWidget('users_group_members','available_members','edit_users_group'); |
|
|||
551 | }) |
|
|||
552 | </script> |
|
|||
553 |
|
||||
554 | </div> |
|
548 | </div> | |
555 |
|
549 | |||
556 | <div class='field'> |
|
550 | <div class='field'> | |
@@ -595,12 +589,6 b'' | |||||
595 | </div> |
|
589 | </div> | |
596 | </div> |
|
590 | </div> | |
597 |
|
591 | |||
598 | <script> |
|
|||
599 | $(document).ready(function(){ |
|
|||
600 | MultiSelectWidget('users_group_members2','available_members','edit_users_group'); |
|
|||
601 | }) |
|
|||
602 | </script> |
|
|||
603 |
|
||||
604 | </div> |
|
592 | </div> | |
605 |
|
593 | |||
606 | <div class='field'> |
|
594 | <div class='field'> |
@@ -36,7 +36,7 b' class TestAdminUsersGroupsController(Tes' | |||||
36 | def test_index(self): |
|
36 | def test_index(self): | |
37 | self.log_user() |
|
37 | self.log_user() | |
38 | response = self.app.get(url('users_groups')) |
|
38 | response = self.app.get(url('users_groups')) | |
39 | response.status_int == 200 |
|
39 | assert response.status_int == 200 | |
40 |
|
40 | |||
41 | def test_create(self): |
|
41 | def test_create(self): | |
42 | self.log_user() |
|
42 | self.log_user() | |
@@ -148,19 +148,21 b' class TestAdminUsersGroupsController(Tes' | |||||
148 |
|
148 | |||
149 | fixture.destroy_user_group(users_group_name) |
|
149 | fixture.destroy_user_group(users_group_name) | |
150 |
|
150 | |||
151 | def test_edit(self): |
|
151 | def test_edit_autocomplete(self): | |
152 | self.log_user() |
|
152 | self.log_user() | |
153 | ug = fixture.create_user_group(TEST_USER_GROUP, skip_if_exists=True) |
|
153 | ug = fixture.create_user_group(TEST_USER_GROUP, skip_if_exists=True) | |
154 | response = self.app.get( |
|
154 | response = self.app.get( | |
155 | url('edit_users_group', user_group_id=ug.users_group_id)) |
|
155 | url('edit_users_group', user_group_id=ug.users_group_id)) | |
156 | fixture.destroy_user_group(TEST_USER_GROUP) |
|
156 | fixture.destroy_user_group(TEST_USER_GROUP) | |
157 |
|
157 | |||
158 | def test_edit_user_group_members(self): |
|
158 | def test_edit_user_group_autocomplete_members(self, xhr_header): | |
159 | self.log_user() |
|
159 | self.log_user() | |
160 | ug = fixture.create_user_group(TEST_USER_GROUP, skip_if_exists=True) |
|
160 | ug = fixture.create_user_group(TEST_USER_GROUP, skip_if_exists=True) | |
161 | response = self.app.get( |
|
161 | response = self.app.get( | |
162 |
url('edit_user_group_members', user_group_id=ug.users_group_id) |
|
162 | url('edit_user_group_members', user_group_id=ug.users_group_id), | |
163 | response.mustcontain('No members yet') |
|
163 | extra_environ=xhr_header) | |
|
164 | ||||
|
165 | assert response.body == '{"members": []}' | |||
164 | fixture.destroy_user_group(TEST_USER_GROUP) |
|
166 | fixture.destroy_user_group(TEST_USER_GROUP) | |
165 |
|
167 | |||
166 | def test_usergroup_escape(self): |
|
168 | def test_usergroup_escape(self): | |
@@ -181,7 +183,7 b' class TestAdminUsersGroupsController(Tes' | |||||
181 | 'csrf_token': self.csrf_token |
|
183 | 'csrf_token': self.csrf_token | |
182 | } |
|
184 | } | |
183 |
|
185 | |||
184 |
|
|
186 | self.app.post(url('users_groups'), data) | |
185 | response = self.app.get(url('users_groups')) |
|
187 | response = self.app.get(url('users_groups')) | |
186 |
|
188 | |||
187 | response.mustcontain( |
|
189 | response.mustcontain( | |
@@ -190,3 +192,42 b' class TestAdminUsersGroupsController(Tes' | |||||
190 | response.mustcontain( |
|
192 | response.mustcontain( | |
191 | '<img src="/image2" onload="' |
|
193 | '<img src="/image2" onload="' | |
192 | 'alert('Hello, World!');">') |
|
194 | 'alert('Hello, World!');">') | |
|
195 | ||||
|
196 | def test_update_members_from_user_ids(self, user_regular): | |||
|
197 | uid = user_regular.user_id | |||
|
198 | username = user_regular.username | |||
|
199 | self.log_user() | |||
|
200 | ||||
|
201 | user_group = fixture.create_user_group('test_gr_ids') | |||
|
202 | assert user_group.members == [] | |||
|
203 | assert user_group.user != user_regular | |||
|
204 | expected_active_state = not user_group.users_group_active | |||
|
205 | ||||
|
206 | form_data = [ | |||
|
207 | ('csrf_token', self.csrf_token), | |||
|
208 | ('_method', 'put'), | |||
|
209 | ('user', username), | |||
|
210 | ('users_group_name', 'changed_name'), | |||
|
211 | ('users_group_active', expected_active_state), | |||
|
212 | ('user_group_description', 'changed_description'), | |||
|
213 | ||||
|
214 | ('__start__', 'user_group_members:sequence'), | |||
|
215 | ('__start__', 'member:mapping'), | |||
|
216 | ('member_user_id', uid), | |||
|
217 | ('type', 'existing'), | |||
|
218 | ('__end__', 'member:mapping'), | |||
|
219 | ('__end__', 'user_group_members:sequence'), | |||
|
220 | ] | |||
|
221 | ugid = user_group.users_group_id | |||
|
222 | self.app.post(url('update_users_group', user_group_id=ugid), form_data) | |||
|
223 | ||||
|
224 | user_group = UserGroup.get(ugid) | |||
|
225 | assert user_group | |||
|
226 | ||||
|
227 | assert user_group.members[0].user_id == uid | |||
|
228 | assert user_group.user_id == uid | |||
|
229 | assert 'changed_name' in user_group.users_group_name | |||
|
230 | assert 'changed_description' in user_group.user_group_description | |||
|
231 | assert user_group.users_group_active == expected_active_state | |||
|
232 | ||||
|
233 | fixture.destroy_user_group(user_group) |
@@ -113,40 +113,20 b' def test_add_and_remove_user_from_group(' | |||||
113 | assert user_group.members == [] |
|
113 | assert user_group.members == [] | |
114 |
|
114 | |||
115 |
|
115 | |||
116 | @pytest.mark.parametrize( |
|
116 | @pytest.mark.parametrize('data, expected', [ | |
117 | 'data, expected', [ |
|
117 | ([], []), | |
118 | ("1", [1]), (["1", "2"], [1, 2]) |
|
118 | ([{"member_user_id": 1, "type": "new"}], [1]), | |
119 | ] |
|
119 | ([{"member_user_id": 1, "type": "new"}, | |
120 | ) |
|
120 | {"member_user_id": 1, "type": "existing"}], [1]), | |
|
121 | ([{"member_user_id": 1, "type": "new"}, | |||
|
122 | {"member_user_id": 2, "type": "new"}, | |||
|
123 | {"member_user_id": 3, "type": "remove"}], [1, 2]) | |||
|
124 | ]) | |||
121 | def test_clean_members_data(data, expected): |
|
125 | def test_clean_members_data(data, expected): | |
122 | cleaned = UserGroupModel()._clean_members_data(data) |
|
126 | cleaned = UserGroupModel()._clean_members_data(data) | |
123 | assert cleaned == expected |
|
127 | assert cleaned == expected | |
124 |
|
128 | |||
125 |
|
129 | |||
126 | def test_update_members_from_user_ids(user_regular, user_util): |
|
|||
127 | user_group = user_util.create_user_group() |
|
|||
128 | assert user_group.members == [] |
|
|||
129 | assert user_group.user != user_regular |
|
|||
130 | expected_active_state = not user_group.users_group_active |
|
|||
131 |
|
||||
132 | form_data = { |
|
|||
133 | 'users_group_members': str(user_regular.user_id), |
|
|||
134 | 'user': str(user_regular.username), |
|
|||
135 | 'users_group_name': 'changed_name', |
|
|||
136 | 'users_group_active': expected_active_state, |
|
|||
137 | 'user_group_description': 'changed_description' |
|
|||
138 | } |
|
|||
139 |
|
||||
140 | UserGroupModel().update(user_group, form_data) |
|
|||
141 | assert user_group.members[0].user_id == user_regular.user_id |
|
|||
142 | assert user_group.user_id == user_regular.user_id |
|
|||
143 | assert 'changed_name' in user_group.users_group_name |
|
|||
144 | assert 'changed_description' in user_group.user_group_description |
|
|||
145 | assert user_group.users_group_active == expected_active_state |
|
|||
146 | # Ignore changes on the test |
|
|||
147 | Session().rollback() |
|
|||
148 |
|
||||
149 |
|
||||
150 | def _create_test_members(): |
|
130 | def _create_test_members(): | |
151 | members = [] |
|
131 | members = [] | |
152 | for member_number in range(3): |
|
132 | for member_number in range(3): |
@@ -38,6 +38,7 b' import requests' | |||||
38 | from webtest.app import TestApp |
|
38 | from webtest.app import TestApp | |
39 |
|
39 | |||
40 | import rhodecode |
|
40 | import rhodecode | |
|
41 | from rhodecode.lib.utils2 import AttributeDict | |||
41 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
42 | from rhodecode.model.changeset_status import ChangesetStatusModel | |
42 | from rhodecode.model.comment import ChangesetCommentsModel |
|
43 | from rhodecode.model.comment import ChangesetCommentsModel | |
43 | from rhodecode.model.db import ( |
|
44 | from rhodecode.model.db import ( |
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