diff --git a/rhodecode/api/tests/test_update_user_group.py b/rhodecode/api/tests/test_update_user_group.py --- a/rhodecode/api/tests/test_update_user_group.py +++ b/rhodecode/api/tests/test_update_user_group.py @@ -23,7 +23,7 @@ import pytest from rhodecode.model.user import UserModel from rhodecode.model.user_group import UserGroupModel -from rhodecode.tests import TEST_USER_REGULAR_LOGIN +from rhodecode.tests import TEST_USER_ADMIN_EMAIL from rhodecode.api.tests.utils import ( build_data, api_call, assert_error, assert_ok, crash, jsonify) @@ -33,7 +33,8 @@ class TestUpdateUserGroup(object): @pytest.mark.parametrize("changing_attr, updates", [ ('group_name', {'group_name': 'new_group_name'}), ('group_name', {'group_name': 'test_group_for_update'}), - ('owner', {'owner': TEST_USER_REGULAR_LOGIN}), + # ('owner', {'owner': TEST_USER_REGULAR_LOGIN}), + ('owner_email', {'owner_email': TEST_USER_ADMIN_EMAIL}), ('active', {'active': False}), ('active', {'active': True}) ]) @@ -59,7 +60,8 @@ class TestUpdateUserGroup(object): # TODO: mikhail: decide if we need to test against the commented params # ('group_name', {'group_name': 'new_group_name'}), # ('group_name', {'group_name': 'test_group_for_update'}), - ('owner', {'owner': TEST_USER_REGULAR_LOGIN}), + # ('owner', {'owner': TEST_USER_REGULAR_LOGIN}), + ('owner_email', {'owner_email': TEST_USER_ADMIN_EMAIL}), ('active', {'active': False}), ('active', {'active': True}) ]) diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py --- a/rhodecode/apps/admin/__init__.py +++ b/rhodecode/apps/admin/__init__.py @@ -70,6 +70,15 @@ def admin_routes(config): name='edit_user_auth_tokens_delete', pattern='/users/{user_id:\d+}/edit/auth_tokens/delete') + # user groups management + config.add_route( + name='edit_user_groups_management', + pattern='/users/{user_id:\d+}/edit/groups_management') + + config.add_route( + name='edit_user_groups_management_updates', + pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates') + def includeme(config): settings = config.get_settings() diff --git a/rhodecode/apps/admin/views/users.py b/rhodecode/apps/admin/views/users.py --- a/rhodecode/apps/admin/views/users.py +++ b/rhodecode/apps/admin/views/users.py @@ -22,6 +22,7 @@ import logging from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config +from rhodecode_tools.lib.ext_json import json from rhodecode.apps._base import BaseAppView from rhodecode.lib.auth import ( @@ -30,6 +31,7 @@ from rhodecode.lib import helpers as h from rhodecode.lib.utils import PartialRenderer from rhodecode.lib.utils2 import safe_int, safe_unicode from rhodecode.model.auth_token import AuthTokenModel +from rhodecode.model.user_group import UserGroupModel from rhodecode.model.db import User, or_ from rhodecode.model.meta import Session @@ -235,3 +237,49 @@ class AdminUsersView(BaseAppView): h.flash(_("Auth token successfully deleted"), category='success') return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id)) + + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @view_config( + route_name='edit_user_groups_management', request_method='GET', + renderer='rhodecode:templates/admin/users/user_edit.mako') + def groups_management(self): + c = self.load_default_context() + + user_id = self.request.matchdict.get('user_id') + c.user = User.get_or_404(user_id, pyramid_exc=True) + c.data = c.user.group_member + self._redirect_for_default_user(c.user.username) + groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) for group in c.user.group_member] + c.groups = json.dumps(groups) + c.active = 'groups' + + return self._get_template_context(c) + + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @view_config( + route_name='edit_user_groups_management_updates', request_method='POST') + def groups_management_updates(self): + _ = self.request.translate + c = self.load_default_context() + + user_id = self.request.matchdict.get('user_id') + c.user = User.get_or_404(user_id, pyramid_exc=True) + self._redirect_for_default_user(c.user.username) + + users_groups = set(self.request.POST.getall('users_group_id')) + users_groups_model = [] + + for ugid in users_groups: + users_groups_model.append(UserGroupModel().get_group(safe_int(ugid))) + user_group_model = UserGroupModel() + user_group_model.change_groups(c.user, users_groups_model) + + Session().commit() + c.active = 'user_groups_management' + h.flash(_("Groups successfully changed"), category='success') + + return HTTPFound(h.route_path('edit_user_groups_management', user_id=user_id)) diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -1261,14 +1261,15 @@ class UserGroup(Base, BaseModel): """ user_group = self - data = { 'users_group_id': user_group.users_group_id, 'group_name': user_group.users_group_name, 'group_description': user_group.user_group_description, 'active': user_group.users_group_active, 'owner': user_group.user.username, + 'owner_email': user_group.user.email, } + if with_group_members: users = [] for user in user_group.members: diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py --- a/rhodecode/model/repo.py +++ b/rhodecode/model/repo.py @@ -197,6 +197,7 @@ class RepoModel(BaseModel): return _users def get_user_groups(self, name_contains=None, limit=20, only_active=True): + # TODO: mikhail: move this method to the UserGroupModel. query = self.sa.query(UserGroup) if only_active: @@ -223,6 +224,12 @@ class RepoModel(BaseModel): 'value_display': 'Group: %s (%d members)' % ( group.users_group_name, len(group.members),), 'value': group.users_group_name, + 'description': group.user_group_description, + 'owner': group.user.username, + + 'owner_icon': h.gravatar_url(group.user.email, 30), + 'value_display_owner': h.person(group.user.email), + 'value_type': 'user_group', 'active': group.users_group_active, } diff --git a/rhodecode/model/user_group.py b/rhodecode/model/user_group.py --- a/rhodecode/model/user_group.py +++ b/rhodecode/model/user_group.py @@ -512,3 +512,49 @@ class UserGroupModel(BaseModel): else: log.debug('Skipping addition to group %s since it is ' 'not managed by auth plugins' % gr) + + + def change_groups(self, user, groups): + """ + This method changes user group assignment + :param user: User + :param groups: array of UserGroupModel + :return: + """ + user = self._get_user(user) + log.debug('Changing user(%s) assignment to groups(%s)', user, groups) + current_groups = user.group_member + current_groups = [x.users_group for x in current_groups] + + # calculate from what groups user should be removed/add + groups = set(groups) + current_groups = set(current_groups) + + groups_to_remove = current_groups - groups + groups_to_add = groups - current_groups + + for gr in groups_to_remove: + log.debug('Removing user %s from user group %s', user.username, gr.users_group_name) + self.remove_user_from_group(gr.users_group_name, user.username) + for gr in groups_to_add: + log.debug('Adding user %s to user group %s', user.username, gr.users_group_name) + UserGroupModel().add_user_to_group(gr.users_group_name, user.username) + + @staticmethod + def get_user_groups_as_dict(user_group): + import rhodecode.lib.helpers as h + + data = { + 'users_group_id': user_group.users_group_id, + 'group_name': user_group.users_group_name, + 'group_description': user_group.user_group_description, + 'active': user_group.users_group_active, + "owner": user_group.user.username, + 'owner_icon': h.gravatar_url(user_group.user.email, 30), + "owner_data": {'owner': user_group.user.username, 'owner_icon': h.gravatar_url(user_group.user.email, 30)} + } + return data + + + + diff --git a/rhodecode/templates/admin/users/user_edit.mako b/rhodecode/templates/admin/users/user_edit.mako --- a/rhodecode/templates/admin/users/user_edit.mako +++ b/rhodecode/templates/admin/users/user_edit.mako @@ -37,6 +37,11 @@