user_groups: Add support to set can_mention_group during creation.

This commit adds API support to set can_mention_group while
creating a user group.

Fixes a part of #25927.
This commit is contained in:
Sahil Batra 2023-06-14 20:18:58 +05:30 committed by Tim Abbott
parent e6accb0ad9
commit 4bea6ffaa8
6 changed files with 154 additions and 7 deletions

View File

@ -25,6 +25,9 @@ format used by the Zulip server that they are interacting with.
* [`GET /events`](/api/get-events), [`POST /register`](/api/register-queue), * [`GET /events`](/api/get-events), [`POST /register`](/api/register-queue),
[`GET /user_groups`](/api/get-user-groups): Add `can_mention_group_id` to [`GET /user_groups`](/api/get-user-groups): Add `can_mention_group_id` to
user group objects. user group objects.
* [`POST /user_groups/create`](/api/create-user-group): Added `can_mention_group_id`
parameter to support setting the user group whose members can mention the new user
group.
**Feature level 190** **Feature level 190**

View File

@ -1,5 +1,5 @@
import datetime import datetime
from typing import Dict, List, Optional, Sequence, TypedDict from typing import Dict, List, Mapping, Optional, Sequence, TypedDict
import django.db.utils import django.db.utils
from django.db import transaction from django.db import transaction
@ -37,14 +37,20 @@ def create_user_group_in_database(
*, *,
acting_user: Optional[UserProfile], acting_user: Optional[UserProfile],
description: str = "", description: str = "",
group_settings_map: Mapping[str, UserGroup] = {},
is_system_group: bool = False, is_system_group: bool = False,
) -> UserGroup: ) -> UserGroup:
user_group = UserGroup( user_group = UserGroup(
name=name, realm=realm, description=description, is_system_group=is_system_group name=name, realm=realm, description=description, is_system_group=is_system_group
) )
system_groups_name_dict = get_role_based_system_groups_dict(realm)
user_group = set_defaults_for_group_settings(user_group, system_groups_name_dict)
for setting_name, setting_value in group_settings_map.items():
setattr(user_group, setting_name, setting_value)
system_groups_name_dict = get_role_based_system_groups_dict(realm)
user_group = set_defaults_for_group_settings(
user_group, group_settings_map, system_groups_name_dict
)
user_group.save() user_group.save()
UserGroupMembership.objects.bulk_create( UserGroupMembership.objects.bulk_create(
@ -150,12 +156,18 @@ def check_add_user_group(
name: str, name: str,
initial_members: List[UserProfile], initial_members: List[UserProfile],
description: str = "", description: str = "",
group_settings_map: Mapping[str, UserGroup] = {},
*, *,
acting_user: Optional[UserProfile], acting_user: Optional[UserProfile],
) -> UserGroup: ) -> UserGroup:
try: try:
user_group = create_user_group_in_database( user_group = create_user_group_in_database(
name, initial_members, realm, description=description, acting_user=acting_user name,
initial_members,
realm,
description=description,
group_settings_map=group_settings_map,
acting_user=acting_user,
) )
do_send_create_user_group_event(user_group, initial_members) do_send_create_user_group_event(user_group, initial_members)
return user_group return user_group

View File

@ -1,4 +1,4 @@
from typing import Dict, Iterable, List, Sequence, TypedDict from typing import Dict, Iterable, List, Mapping, Sequence, TypedDict
from django.db import transaction from django.db import transaction
from django.db.models import F, QuerySet from django.db.models import F, QuerySet
@ -239,9 +239,15 @@ def get_role_based_system_groups_dict(realm: Realm) -> Dict[str, UserGroup]:
def set_defaults_for_group_settings( def set_defaults_for_group_settings(
user_group: UserGroup, user_group: UserGroup,
group_settings_map: Mapping[str, UserGroup],
system_groups_name_dict: Dict[str, UserGroup], system_groups_name_dict: Dict[str, UserGroup],
) -> UserGroup: ) -> UserGroup:
for setting_name, permission_config in UserGroup.GROUP_PERMISSION_SETTINGS.items(): for setting_name, permission_config in UserGroup.GROUP_PERMISSION_SETTINGS.items():
if setting_name in group_settings_map:
# We skip the settings for which a value is passed
# in user group creation API request.
continue
if user_group.is_system_group and permission_config.default_for_system_groups is not None: if user_group.is_system_group and permission_config.default_for_system_groups is not None:
default_group_name = permission_config.default_for_system_groups default_group_name = permission_config.default_for_system_groups
else: else:
@ -315,7 +321,7 @@ def create_system_user_groups_for_realm(realm: Realm) -> Dict[int, UserGroup]:
groups_with_updated_settings = [] groups_with_updated_settings = []
system_groups_name_dict = get_role_based_system_groups_dict(realm) system_groups_name_dict = get_role_based_system_groups_dict(realm)
for group in system_user_groups_list: for group in system_user_groups_list:
user_group = set_defaults_for_group_settings(group, system_groups_name_dict) user_group = set_defaults_for_group_settings(group, {}, system_groups_name_dict)
groups_with_updated_settings.append(group) groups_with_updated_settings.append(group)
UserGroup.objects.bulk_update(groups_with_updated_settings, ["can_mention_group"]) UserGroup.objects.bulk_update(groups_with_updated_settings, ["can_mention_group"])

View File

@ -16164,6 +16164,21 @@ paths:
type: integer type: integer
example: [1, 2, 3, 4] example: [1, 2, 3, 4]
required: true required: true
- name: can_mention_group_id
in: query
description: |
ID of the user group whose members are allowed to mention the new
user group.
This setting cannot be set to `"@role:internet"` and `"@role:owners"`
system groups.
**Changes**: New in Zulip 8.0 (feature level 191). Previously, groups
could be mentioned if and only if they were not system groups.
schema:
type: integer
example: 11
required: false
responses: responses:
"200": "200":
$ref: "#/components/responses/SimpleSuccess" $ref: "#/components/responses/SimpleSuccess"

View File

@ -267,6 +267,88 @@ class UserGroupAPITestCase(UserGroupTestCase):
self.assert_json_error(result, "User group 'support' already exists.") self.assert_json_error(result, "User group 'support' already exists.")
self.assert_length(UserGroup.objects.filter(realm=hamlet.realm), 10) self.assert_length(UserGroup.objects.filter(realm=hamlet.realm), 10)
def test_can_mention_group_setting_during_user_group_creation(self) -> None:
self.login("hamlet")
hamlet = self.example_user("hamlet")
leadership_group = check_add_user_group(
hamlet.realm, "leadership", [hamlet], acting_user=None
)
moderators_group = UserGroup.objects.get(
name="@role:moderators", realm=hamlet.realm, is_system_group=True
)
params = {
"name": "support",
"members": orjson.dumps([hamlet.id]).decode(),
"description": "Support team",
"can_mention_group_id": orjson.dumps(moderators_group.id).decode(),
}
result = self.client_post("/json/user_groups/create", info=params)
self.assert_json_success(result)
support_group = UserGroup.objects.get(name="support", realm=hamlet.realm)
self.assertEqual(support_group.can_mention_group, moderators_group)
params = {
"name": "test",
"members": orjson.dumps([hamlet.id]).decode(),
"description": "Test group",
"can_mention_group_id": orjson.dumps(leadership_group.id).decode(),
}
result = self.client_post("/json/user_groups/create", info=params)
self.assert_json_success(result)
test_group = UserGroup.objects.get(name="test", realm=hamlet.realm)
self.assertEqual(test_group.can_mention_group, leadership_group)
nobody_group = UserGroup.objects.get(
name="@role:nobody", realm=hamlet.realm, is_system_group=True
)
params = {
"name": "marketing",
"members": orjson.dumps([hamlet.id]).decode(),
"description": "Marketing team",
"can_mention_group_id": orjson.dumps(nobody_group.id).decode(),
}
result = self.client_post("/json/user_groups/create", info=params)
self.assert_json_success(result)
marketing_group = UserGroup.objects.get(name="marketing", realm=hamlet.realm)
self.assertEqual(marketing_group.can_mention_group, nobody_group)
internet_group = UserGroup.objects.get(
name="@role:internet", realm=hamlet.realm, is_system_group=True
)
params = {
"name": "frontend",
"members": orjson.dumps([hamlet.id]).decode(),
"description": "Frontend team",
"can_mention_group_id": orjson.dumps(internet_group.id).decode(),
}
result = self.client_post("/json/user_groups/create", info=params)
self.assert_json_error(
result, "'can_mention_group' setting cannot be set to '@role:internet' group."
)
owners_group = UserGroup.objects.get(
name="@role:owners", realm=hamlet.realm, is_system_group=True
)
params = {
"name": "frontend",
"members": orjson.dumps([hamlet.id]).decode(),
"description": "Frontend team",
"can_mention_group_id": orjson.dumps(owners_group.id).decode(),
}
result = self.client_post("/json/user_groups/create", info=params)
self.assert_json_error(
result, "'can_mention_group' setting cannot be set to '@role:owners' group."
)
params = {
"name": "frontend",
"members": orjson.dumps([hamlet.id]).decode(),
"description": "Frontend team",
"can_mention_group_id": orjson.dumps(1111).decode(),
}
result = self.client_post("/json/user_groups/create", info=params)
self.assert_json_error(result, "Invalid user group")
def test_user_group_get(self) -> None: def test_user_group_get(self) -> None:
# Test success # Test success
user_profile = self.example_user("hamlet") user_profile = self.example_user("hamlet")

View File

@ -23,6 +23,7 @@ from zerver.lib.request import REQ, has_request_variables
from zerver.lib.response import json_success from zerver.lib.response import json_success
from zerver.lib.user_groups import ( from zerver.lib.user_groups import (
access_user_group_by_id, access_user_group_by_id,
access_user_group_for_setting,
access_user_groups_as_potential_subgroups, access_user_groups_as_potential_subgroups,
get_direct_memberships_of_users, get_direct_memberships_of_users,
get_recursive_subgroups_for_groups, get_recursive_subgroups_for_groups,
@ -46,10 +47,38 @@ def add_user_group(
name: str = REQ(), name: str = REQ(),
members: Sequence[int] = REQ(json_validator=check_list(check_int), default=[]), members: Sequence[int] = REQ(json_validator=check_list(check_int), default=[]),
description: str = REQ(), description: str = REQ(),
can_mention_group_id: Optional[int] = REQ(json_validator=check_int, default=None),
) -> HttpResponse: ) -> HttpResponse:
user_profiles = user_ids_to_users(members, user_profile.realm) user_profiles = user_ids_to_users(members, user_profile.realm)
group_settings_map = {}
request_settings_dict = locals()
for setting_name, permission_config in UserGroup.GROUP_PERMISSION_SETTINGS.items():
setting_group_id_name = setting_name + "_id"
if setting_group_id_name not in request_settings_dict: # nocoverage
continue
if request_settings_dict[setting_group_id_name] is not None:
setting_value_group_id = request_settings_dict[setting_group_id_name]
setting_value_group = access_user_group_for_setting(
setting_value_group_id,
user_profile,
setting_name=setting_name,
require_system_group=permission_config.require_system_group,
allow_internet_group=permission_config.allow_internet_group,
allow_owners_group=permission_config.allow_owners_group,
allow_nobody_group=permission_config.allow_nobody_group,
)
group_settings_map[setting_name] = setting_value_group
check_add_user_group( check_add_user_group(
user_profile.realm, name, user_profiles, description, acting_user=user_profile user_profile.realm,
name,
user_profiles,
description,
group_settings_map=group_settings_map,
acting_user=user_profile,
) )
return json_success(request) return json_success(request)