From 92672c1339690318077663e9a120f1fb602b0841 Mon Sep 17 00:00:00 2001 From: Vector73 Date: Tue, 17 Jun 2025 03:20:55 +0000 Subject: [PATCH] settings: Add `can_set_topics_policy_group` realm setting. Added `can_set_topics_policy_group` realm setting to control permission to change per-channel `topics_policy`. --- api_docs/unmerged.d/ZF-ec92b4.md | 5 +++ web/src/group_permission_settings.ts | 1 + web/src/server_events_dispatch.js | 1 + web/src/settings_components.ts | 3 ++ web/src/settings_config.ts | 7 ++++ web/src/settings_data.ts | 8 +++++ web/src/settings_org.ts | 1 + web/src/state_data.ts | 1 + .../organization_permissions_admin.hbs | 4 +++ web/tests/settings_data.test.cjs | 5 +++ zerver/lib/event_types.py | 1 + .../0714_realm_can_set_topics_policy_group.py | 23 ++++++++++++ ...t_value_for_can_set_topics_policy_group.py | 35 +++++++++++++++++++ ...alter_realm_can_set_topics_policy_group.py | 22 ++++++++++++ zerver/models/realms.py | 12 +++++++ zerver/openapi/zulip.yaml | 28 +++++++++++++++ zerver/tests/test_home.py | 1 + zerver/views/realm.py | 1 + 18 files changed, 159 insertions(+) create mode 100644 api_docs/unmerged.d/ZF-ec92b4.md create mode 100644 zerver/migrations/0714_realm_can_set_topics_policy_group.py create mode 100644 zerver/migrations/0715_set_default_value_for_can_set_topics_policy_group.py create mode 100644 zerver/migrations/0716_alter_realm_can_set_topics_policy_group.py diff --git a/api_docs/unmerged.d/ZF-ec92b4.md b/api_docs/unmerged.d/ZF-ec92b4.md new file mode 100644 index 0000000000..380b8e8801 --- /dev/null +++ b/api_docs/unmerged.d/ZF-ec92b4.md @@ -0,0 +1,5 @@ +* `PATCH /realm`, [`POST /register`](/api/register-queue), + [`GET /events`](/api/get-events): Added `can_set_topics_policy_group` + realm setting, which is a [group-setting value](/api/group-setting-values) + describing the set of users with permission to change per-channel `topics_policy` + setting. diff --git a/web/src/group_permission_settings.ts b/web/src/group_permission_settings.ts index b8ee23605a..af8ddbc724 100644 --- a/web/src/group_permission_settings.ts +++ b/web/src/group_permission_settings.ts @@ -60,6 +60,7 @@ export const realm_group_setting_name_schema = z.enum([ "can_move_messages_between_channels_group", "can_move_messages_between_topics_group", "can_resolve_topics_group", + "can_set_topics_policy_group", "can_summarize_topics_group", "create_multiuse_invite_group", "direct_message_initiator_group", diff --git a/web/src/server_events_dispatch.js b/web/src/server_events_dispatch.js index fef70912fc..17ab6c3cf7 100644 --- a/web/src/server_events_dispatch.js +++ b/web/src/server_events_dispatch.js @@ -248,6 +248,7 @@ export function dispatch_normal_event(event) { can_move_messages_between_channels_group: noop, can_move_messages_between_topics_group: noop, can_resolve_topics_group: noop, + can_set_topics_policy_group: noop, can_summarize_topics_group: noop, create_multiuse_invite_group: noop, default_code_block_language: noop, diff --git a/web/src/settings_components.ts b/web/src/settings_components.ts index cf7e47c3c1..bc5c04922f 100644 --- a/web/src/settings_components.ts +++ b/web/src/settings_components.ts @@ -842,6 +842,7 @@ export function check_realm_settings_property_changed(elem: HTMLElement): boolea case "realm_can_move_messages_between_channels_group": case "realm_can_move_messages_between_topics_group": case "realm_can_resolve_topics_group": + case "realm_can_set_topics_policy_group": case "realm_can_summarize_topics_group": case "realm_create_multiuse_invite_group": case "realm_direct_message_initiator_group": @@ -1104,6 +1105,7 @@ export function populate_data_for_realm_settings_request( "can_move_messages_between_channels_group", "can_move_messages_between_topics_group", "can_resolve_topics_group", + "can_set_topics_policy_group", "can_summarize_topics_group", "create_multiuse_invite_group", "direct_message_initiator_group", @@ -1579,6 +1581,7 @@ export const group_setting_widget_map = new Map(also requires being a channel administrator)", + }), + ), }, stream: { can_add_subscribers_group: $t({defaultMessage: "Who can subscribe anyone to this channel"}), @@ -761,6 +767,7 @@ export const realm_group_permission_settings: { "can_create_private_channel_group", "can_add_subscribers_group", "can_mention_many_users_group", + "can_set_topics_policy_group", ], }, { diff --git a/web/src/settings_data.ts b/web/src/settings_data.ts index 9bc7b1b77f..c93195a3e9 100644 --- a/web/src/settings_data.ts +++ b/web/src/settings_data.ts @@ -92,6 +92,14 @@ export function user_can_create_multiuse_invite(): boolean { ); } +export function user_can_set_topics_policy(): boolean { + return user_has_permission_for_group_setting( + realm.realm_can_set_topics_policy_group, + "can_set_topics_policy_group", + "realm", + ); +} + export function user_can_summarize_topics(): boolean { if (!realm.server_can_summarize_topics) { return false; diff --git a/web/src/settings_org.ts b/web/src/settings_org.ts index b36d8cac12..35a29e475b 100644 --- a/web/src/settings_org.ts +++ b/web/src/settings_org.ts @@ -521,6 +521,7 @@ export function discard_realm_property_element_changes(elem: HTMLElement): void case "realm_can_move_messages_between_channels_group": case "realm_can_move_messages_between_topics_group": case "realm_can_resolve_topics_group": + case "realm_can_set_topics_policy_group": case "realm_can_summarize_topics_group": case "realm_create_multiuse_invite_group": case "realm_direct_message_initiator_group": diff --git a/web/src/state_data.ts b/web/src/state_data.ts index 73bfe59e8c..c2f67c4809 100644 --- a/web/src/state_data.ts +++ b/web/src/state_data.ts @@ -321,6 +321,7 @@ export const realm_schema = z.object({ realm_can_move_messages_between_channels_group: group_setting_value_schema, realm_can_move_messages_between_topics_group: group_setting_value_schema, realm_can_resolve_topics_group: group_setting_value_schema, + realm_can_set_topics_policy_group: group_setting_value_schema, realm_can_summarize_topics_group: group_setting_value_schema, realm_create_multiuse_invite_group: group_setting_value_schema, realm_date_created: z.number(), diff --git a/web/templates/settings/organization_permissions_admin.hbs b/web/templates/settings/organization_permissions_admin.hbs index 267f3fc9cf..692fec42c0 100644 --- a/web/templates/settings/organization_permissions_admin.hbs +++ b/web/templates/settings/organization_permissions_admin.hbs @@ -99,6 +99,10 @@ setting_name="realm_can_mention_many_users_group" label=group_setting_labels.can_mention_many_users_group help_link="/help/restrict-wildcard-mentions"}} + + {{> group_setting_value_pill_input + setting_name="realm_can_set_topics_policy_group" + label=group_setting_labels.can_set_topics_policy_group}} diff --git a/web/tests/settings_data.test.cjs b/web/tests/settings_data.test.cjs index a4bbfeb09b..ea591abb4d 100644 --- a/web/tests/settings_data.test.cjs +++ b/web/tests/settings_data.test.cjs @@ -205,6 +205,11 @@ test_realm_group_settings( settings_data.can_subscribe_others_to_all_accessible_streams, ); +test_realm_group_settings( + "realm_can_set_topics_policy_group", + settings_data.user_can_set_topics_policy, +); + test_realm_group_settings( "realm_can_delete_any_message_group", settings_data.user_can_delete_any_message, diff --git a/zerver/lib/event_types.py b/zerver/lib/event_types.py index 97b1c5ecf8..52d07fe6f1 100644 --- a/zerver/lib/event_types.py +++ b/zerver/lib/event_types.py @@ -592,6 +592,7 @@ class GroupSettingUpdateData(GroupSettingUpdateDataCore): can_move_messages_between_channels_group: int | UserGroupMembersDict | None = None can_move_messages_between_topics_group: int | UserGroupMembersDict | None = None can_resolve_topics_group: int | UserGroupMembersDict | None = None + can_set_topics_policy_group: int | UserGroupMembersDict | None = None can_summarize_topics_group: int | UserGroupMembersDict | None = None direct_message_initiator_group: int | UserGroupMembersDict | None = None direct_message_permission_group: int | UserGroupMembersDict | None = None diff --git a/zerver/migrations/0714_realm_can_set_topics_policy_group.py b/zerver/migrations/0714_realm_can_set_topics_policy_group.py new file mode 100644 index 0000000000..a6bfcd2dc0 --- /dev/null +++ b/zerver/migrations/0714_realm_can_set_topics_policy_group.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.7 on 2025-03-29 11:55 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("zerver", "0713_remove_realm_mandatory_topics"), + ] + + operations = [ + migrations.AddField( + model_name="realm", + name="can_set_topics_policy_group", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.RESTRICT, + related_name="+", + to="zerver.usergroup", + ), + ), + ] diff --git a/zerver/migrations/0715_set_default_value_for_can_set_topics_policy_group.py b/zerver/migrations/0715_set_default_value_for_can_set_topics_policy_group.py new file mode 100644 index 0000000000..cf0a032748 --- /dev/null +++ b/zerver/migrations/0715_set_default_value_for_can_set_topics_policy_group.py @@ -0,0 +1,35 @@ +from django.db import migrations +from django.db.backends.base.schema import BaseDatabaseSchemaEditor +from django.db.migrations.state import StateApps +from django.db.models import OuterRef + + +def set_default_value_for_can_set_topics_policy_group( + apps: StateApps, schema_editor: BaseDatabaseSchemaEditor +) -> None: + Realm = apps.get_model("zerver", "Realm") + NamedUserGroup = apps.get_model("zerver", "NamedUserGroup") + + MEMBERS_GROUP_NAME = "role:members" + + Realm.objects.filter(can_set_topics_policy_group=None).update( + can_set_topics_policy_group=NamedUserGroup.objects.filter( + name=MEMBERS_GROUP_NAME, realm=OuterRef("id"), is_system_group=True + ).values("pk") + ) + + +class Migration(migrations.Migration): + atomic = False + + dependencies = [ + ("zerver", "0714_realm_can_set_topics_policy_group"), + ] + + operations = [ + migrations.RunPython( + set_default_value_for_can_set_topics_policy_group, + elidable=True, + reverse_code=migrations.RunPython.noop, + ) + ] diff --git a/zerver/migrations/0716_alter_realm_can_set_topics_policy_group.py b/zerver/migrations/0716_alter_realm_can_set_topics_policy_group.py new file mode 100644 index 0000000000..7e6c31c165 --- /dev/null +++ b/zerver/migrations/0716_alter_realm_can_set_topics_policy_group.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.7 on 2025-03-29 12:00 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("zerver", "0715_set_default_value_for_can_set_topics_policy_group"), + ] + + operations = [ + migrations.AlterField( + model_name="realm", + name="can_set_topics_policy_group", + field=models.ForeignKey( + on_delete=django.db.models.deletion.RESTRICT, + related_name="+", + to="zerver.usergroup", + ), + ), + ] diff --git a/zerver/models/realms.py b/zerver/models/realms.py index 9a088c7eac..e04416cc2f 100644 --- a/zerver/models/realms.py +++ b/zerver/models/realms.py @@ -390,6 +390,11 @@ class Realm(models.Model): "UserGroup", on_delete=models.RESTRICT, related_name="+" ) + # UserGroup which is allowed to set per-channel `topics_policy` setting. + can_set_topics_policy_group = models.ForeignKey( + "UserGroup", on_delete=models.RESTRICT, related_name="+" + ) + WILDCARD_MENTION_POLICY_TYPES = [field.value for field in WildcardMentionPolicyEnum] # Threshold in days for new users to create streams, and potentially take @@ -867,6 +872,13 @@ class Realm(models.Model): allow_everyone_group=True, default_group_name=SystemGroups.EVERYONE, ), + can_set_topics_policy_group=GroupPermissionSetting( + require_system_group=False, + allow_internet_group=False, + allow_nobody_group=True, + allow_everyone_group=True, + default_group_name=SystemGroups.MEMBERS, + ), can_summarize_topics_group=GroupPermissionSetting( require_system_group=False, allow_internet_group=False, diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml index 78ce7607b7..cd7c37f9a6 100644 --- a/zerver/openapi/zulip.yaml +++ b/zerver/openapi/zulip.yaml @@ -4769,6 +4769,19 @@ paths: setting controlled this permission; `true` corresponded to `Everyone`, and `false` to `Admins`. - $ref: "#/components/schemas/GroupSettingValue" + can_set_topics_policy_group: + allOf: + - description: | + A [group-setting value](/api/group-setting-values) defining the set of + users who have permission to change per-channel `topics_policy` setting. Note that + the user must be a member of both this group and the `can_administer_channel_group` + of the channel whose `topics_policy` they want to change. + + Organization administrators can always change the `topics_policy` setting of + every channel. + + **Changes**: New in Zulip 10.0 (feature level ZF-ec92b4). + - $ref: "#/components/schemas/GroupSettingValue" can_invite_users_group: allOf: - description: | @@ -17821,6 +17834,21 @@ paths: setting controlled this permission; `true` corresponded to `Everyone`, and `false` to `Admins`. - $ref: "#/components/schemas/GroupSettingValue" + realm_can_set_topics_policy_group: + allOf: + - description: | + Present if `realm` is present in `fetch_event_types`. + + A [group-setting value](/api/group-setting-values) defining the set of + users who have permission to change per-channel `topics_policy` setting. Note that + the user must be a member of both this group and the `can_administer_channel_group` + of the channel whose `topics_policy` they want to change. + + Organization administrators can always change the `topics_policy` setting of + every channel. + + **Changes**: New in Zulip 10.0 (feature level ZF-ec92b4). + - $ref: "#/components/schemas/GroupSettingValue" realm_can_invite_users_group: allOf: - description: | diff --git a/zerver/tests/test_home.py b/zerver/tests/test_home.py index 9f431f299b..6f02961509 100644 --- a/zerver/tests/test_home.py +++ b/zerver/tests/test_home.py @@ -142,6 +142,7 @@ class HomeTest(ZulipTestCase): "realm_can_move_messages_between_channels_group", "realm_can_move_messages_between_topics_group", "realm_can_resolve_topics_group", + "realm_can_set_topics_policy_group", "realm_can_summarize_topics_group", "realm_create_multiuse_invite_group", "realm_create_private_stream_policy", diff --git a/zerver/views/realm.py b/zerver/views/realm.py index c43694fe84..474d789141 100644 --- a/zerver/views/realm.py +++ b/zerver/views/realm.py @@ -167,6 +167,7 @@ def update_realm( can_move_messages_between_channels_group: Json[GroupSettingChangeRequest] | None = None, can_move_messages_between_topics_group: Json[GroupSettingChangeRequest] | None = None, can_resolve_topics_group: Json[GroupSettingChangeRequest] | None = None, + can_set_topics_policy_group: Json[GroupSettingChangeRequest] | None = None, can_summarize_topics_group: Json[GroupSettingChangeRequest] | None = None, direct_message_initiator_group: Json[GroupSettingChangeRequest] | None = None, direct_message_permission_group: Json[GroupSettingChangeRequest] | None = None,