Group add/remove bulk view
This commit is contained in:
parent
20dc306106
commit
bbb8a84f5c
@ -263,6 +263,21 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User, UserTi
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters groups of all given users. Either adds or removes the given groups.
|
||||
*
|
||||
* @param users Affected users
|
||||
* @param action add or remove the groups
|
||||
* @param groupIds All group ids to add or remove
|
||||
*/
|
||||
public async bulkAlterGroups(users: ViewUser[], action: 'add' | 'remove', groupIds: number[]): Promise<void> {
|
||||
await this.httpService.post('/rest/users/user/bulk_alter_groups/', {
|
||||
user_ids: users.map(user => user.id),
|
||||
action: action,
|
||||
group_ids: groupIds
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends invitation emails to all given users. Returns a prepared string to show the user.
|
||||
* This string should always be shown, becuase even in success cases, some users may not get
|
||||
|
@ -290,8 +290,7 @@
|
||||
<div *ngIf="user.groups && user.groups.length > 0">
|
||||
<h4 translate>Groups</h4>
|
||||
<span *ngFor="let group of user.groups; let last = last">
|
||||
{{ group.getTitle() | translate }}
|
||||
<span *ngIf="!last">, </span>
|
||||
{{ group.getTitle() | translate }}<span *ngIf="!last">, </span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -302,18 +302,8 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
||||
const choices = [_('add group(s)'), _('remove group(s)')];
|
||||
const selectedChoice = await this.choiceService.open(content, this.groupRepo.getViewModelList(), true, choices);
|
||||
if (selectedChoice) {
|
||||
for (const user of this.selectedRows) {
|
||||
const newGroups = [...user.groups_id];
|
||||
(selectedChoice.items as number[]).forEach(newChoice => {
|
||||
const idx = newGroups.indexOf(newChoice);
|
||||
if (idx < 0 && selectedChoice.action === choices[0]) {
|
||||
newGroups.push(newChoice);
|
||||
} else if (idx >= 0 && selectedChoice.action === choices[1]) {
|
||||
newGroups.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
await this.repo.update({ groups_id: newGroups }, user);
|
||||
}
|
||||
const action = selectedChoice.action === choices[0] ? 'add' : 'remove';
|
||||
await this.repo.bulkAlterGroups(this.selectedRows, action, selectedChoice.items as number[]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,7 @@ class UserViewSet(ModelViewSet):
|
||||
"bulk_generate_passwords",
|
||||
"bulk_reset_passwords_to_default",
|
||||
"bulk_set_state",
|
||||
"bulk_alter_groups",
|
||||
"bulk_delete",
|
||||
"mass_import",
|
||||
"mass_invite_email",
|
||||
@ -246,6 +247,40 @@ class UserViewSet(ModelViewSet):
|
||||
|
||||
return Response()
|
||||
|
||||
@list_route(methods=["post"])
|
||||
def bulk_alter_groups(self, request):
|
||||
"""
|
||||
Adds or removes groups from given users. The request user is excluded.
|
||||
Expected data:
|
||||
{
|
||||
user_ids: <list of ids>,
|
||||
action: "add" | "remove",
|
||||
group_ids: <list of ids>
|
||||
}
|
||||
"""
|
||||
user_ids = request.data.get("user_ids")
|
||||
self.assert_list_of_ints(user_ids)
|
||||
group_ids = request.data.get("group_ids")
|
||||
self.assert_list_of_ints(group_ids, ids_name="groups_id")
|
||||
|
||||
action = request.data.get("action")
|
||||
if action not in ("add", "remove"):
|
||||
raise ValidationError({"detail": "The action must be add or remove"})
|
||||
|
||||
users = User.objects.exclude(pk=request.user.id).filter(pk__in=user_ids)
|
||||
groups = list(Group.objects.filter(pk__in=group_ids))
|
||||
|
||||
for user in users:
|
||||
if action == "add":
|
||||
user.groups.add(*groups)
|
||||
else:
|
||||
user.groups.remove(*groups)
|
||||
# Maybe some group assignments have changed. Better delete the restricted user cache
|
||||
async_to_sync(element_cache.del_user)(user.pk)
|
||||
|
||||
inform_changed_data(users)
|
||||
return Response()
|
||||
|
||||
@list_route(methods=["post"])
|
||||
def bulk_delete(self, request):
|
||||
"""
|
||||
@ -357,13 +392,13 @@ class UserViewSet(ModelViewSet):
|
||||
{"count": len(success_users), "no_email_ids": user_pks_without_email}
|
||||
)
|
||||
|
||||
def assert_list_of_ints(self, ids):
|
||||
def assert_list_of_ints(self, ids, ids_name="user_ids"):
|
||||
""" Asserts, that ids is a list of ints. Raises a ValidationError, if not. """
|
||||
if not isinstance(ids, list):
|
||||
raise ValidationError({"detail": "user_ids must be a list"})
|
||||
raise ValidationError({"detail": f"{ids_name} must be a list"})
|
||||
for id in ids:
|
||||
if not isinstance(id, int):
|
||||
raise ValidationError({"detail": "every id must be a int"})
|
||||
raise ValidationError({"detail": "Every id must be a int"})
|
||||
|
||||
|
||||
class GroupViewSetMetadata(SimpleMetadata):
|
||||
|
@ -9,7 +9,12 @@ from openslides.users.models import Group, PersonalNote, User
|
||||
from openslides.utils.autoupdate import inform_changed_data
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
from ...common_groups import GROUP_DEFAULT_PK, GROUP_DELEGATE_PK, GROUP_STAFF_PK
|
||||
from ...common_groups import (
|
||||
GROUP_ADMIN_PK,
|
||||
GROUP_DEFAULT_PK,
|
||||
GROUP_DELEGATE_PK,
|
||||
GROUP_STAFF_PK,
|
||||
)
|
||||
from ..helpers import count_queries
|
||||
|
||||
|
||||
@ -421,6 +426,80 @@ class UserBulkSetState(TestCase):
|
||||
self.assertTrue(User.objects.get().is_committee)
|
||||
|
||||
|
||||
class UserBulkAlterGroups(TestCase):
|
||||
"""
|
||||
Tests altering groups of users.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
self.client.login(username="admin", password="admin")
|
||||
self.admin = User.objects.get()
|
||||
self.user = User.objects.create(username="Test name apfj31fa0ovmc8cqc8e8")
|
||||
|
||||
def test_add(self):
|
||||
self.assertEqual(self.user.groups.count(), 0)
|
||||
response = self.client.post(
|
||||
reverse("user-bulk-alter-groups"),
|
||||
{
|
||||
"user_ids": [self.user.pk],
|
||||
"action": "add",
|
||||
"group_ids": [GROUP_DELEGATE_PK, GROUP_STAFF_PK],
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(self.user.groups.count(), 2)
|
||||
self.assertTrue(self.user.groups.filter(pk=GROUP_DELEGATE_PK).exists())
|
||||
self.assertTrue(self.user.groups.filter(pk=GROUP_STAFF_PK).exists())
|
||||
|
||||
def test_remove(self):
|
||||
groups = Group.objects.filter(
|
||||
pk__in=[GROUP_DEFAULT_PK, GROUP_DELEGATE_PK, GROUP_STAFF_PK]
|
||||
)
|
||||
self.user.groups.set(groups)
|
||||
response = self.client.post(
|
||||
reverse("user-bulk-alter-groups"),
|
||||
{
|
||||
"user_ids": [self.user.pk],
|
||||
"action": "remove",
|
||||
"group_ids": [GROUP_DEFAULT_PK, GROUP_STAFF_PK],
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(self.user.groups.count(), 1)
|
||||
self.assertTrue(self.user.groups.filter(pk=GROUP_DELEGATE_PK).exists())
|
||||
|
||||
def test_no_request_user(self):
|
||||
self.assertEqual(self.admin.groups.count(), 1)
|
||||
self.assertEqual(self.admin.groups.get().pk, GROUP_ADMIN_PK)
|
||||
response = self.client.post(
|
||||
reverse("user-bulk-alter-groups"),
|
||||
{
|
||||
"user_ids": [self.admin.pk],
|
||||
"action": "add",
|
||||
"group_ids": [GROUP_DELEGATE_PK],
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(self.admin.groups.count(), 1)
|
||||
self.assertEqual(self.admin.groups.get().pk, GROUP_ADMIN_PK)
|
||||
|
||||
def test_invalid_action(self):
|
||||
response = self.client.post(
|
||||
reverse("user-bulk-alter-groups"),
|
||||
{
|
||||
"user_ids": [self.admin.pk],
|
||||
"action": "invalid",
|
||||
"group_ids": [GROUP_DELEGATE_PK],
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class UserMassImport(TestCase):
|
||||
"""
|
||||
Tests mass import of users.
|
||||
|
Loading…
Reference in New Issue
Block a user