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 @@
  • ${_('Permissions summary')}
  • ${_('Emails')}
  • ${_('Ip Whitelist')}
  • + +
  • + ${_('User Groups Management')} +
  • + diff --git a/rhodecode/templates/admin/user_groups/user_groups.mako b/rhodecode/templates/admin/users/user_edit_groups.mako copy from rhodecode/templates/admin/user_groups/user_groups.mako copy to rhodecode/templates/admin/users/user_edit_groups.mako --- a/rhodecode/templates/admin/user_groups/user_groups.mako +++ b/rhodecode/templates/admin/users/user_edit_groups.mako @@ -1,82 +1,92 @@ ## -*- coding: utf-8 -*- -<%inherit file="/base/base.mako"/> -<%def name="title()"> - ${_('User groups administration')} - %if c.rhodecode_name: - · ${h.branding(c.rhodecode_name)} - %endif - - -<%def name="breadcrumbs_links()"> - - ${h.link_to(_('Admin'),h.url('admin_home'))} » 0 ${_('user groups')} - - -<%def name="menu_bar_nav()"> - ${self.menu_items(active='admin')} - -<%def name="main()"> -
    +
    +
    +

    ${_('User groups administration')}

    +
    +
    +
    +
    + +
    +
    + ${h.text('add_user_to_group', placeholder="user group name", class_="medium")} +
    -
    - ${self.breadcrumbs()} - -
    +
    -
    -
    -
    - +
    + ${h.secure_form(h.route_path('edit_user_groups_management_updates', user_id=c.user.user_id), method='post')} +
    +
    +
    +
    + ${h.submit('save',_('Save'),class_="btn")} +
    + ${h.end_form()} +
    +
    - + diff --git a/rhodecode/tests/models/test_repos.py b/rhodecode/tests/models/test_repos.py --- a/rhodecode/tests/models/test_repos.py +++ b/rhodecode/tests/models/test_repos.py @@ -260,8 +260,9 @@ class TestGetUserGroups(object): user_util.create_user_group(users_group_active=True)) group_filter = created_groups[-1].users_group_name[-2:] - with self._patch_user_group_list(): - groups = RepoModel().get_user_groups(group_filter) + with mock.patch('rhodecode.lib.helpers.gravatar_url'): + with self._patch_user_group_list(): + groups = RepoModel().get_user_groups(group_filter) fake_groups = [ u for u in groups if u['value'].startswith('test_returns')] @@ -275,9 +276,9 @@ class TestGetUserGroups(object): for i in range(3): created_groups.append( user_util.create_user_group(users_group_active=True)) - - with self._patch_user_group_list(): - groups = RepoModel().get_user_groups('test_returns') + with mock.patch('rhodecode.lib.helpers.gravatar_url'): + with self._patch_user_group_list(): + groups = RepoModel().get_user_groups('test_returns') fake_groups = [ u for u in groups if u['value'].startswith('test_returns')] @@ -287,9 +288,9 @@ class TestGetUserGroups(object): for i in range(4): is_active = i % 2 == 0 user_util.create_user_group(users_group_active=is_active) - - with self._patch_user_group_list(): - groups = RepoModel().get_user_groups() + with mock.patch('rhodecode.lib.helpers.gravatar_url'): + with self._patch_user_group_list(): + groups = RepoModel().get_user_groups() expected = ('id', 'icon_link', 'value_display', 'value', 'value_type') for group in groups: assert group['value_type'] is 'user_group'