diff --git a/api_docs/changelog.md b/api_docs/changelog.md index c9def3ca36..23c6600115 100644 --- a/api_docs/changelog.md +++ b/api_docs/changelog.md @@ -20,6 +20,13 @@ format used by the Zulip server that they are interacting with. ## Changes in Zulip 9.0 +**Feature level 266** + +* `PATCH /realm`, [`POST /register`](/api/register-queue), + [`GET /events`](/api/get-events): Added `can_create_private_channel_group` + realm setting, which is a [group-setting value](/api/group-setting-values) + describing the set of users with permission to create private channels. + **Feature level 265** * [`GET /messages`](/api/get-messages), diff --git a/zerver/lib/event_schema.py b/zerver/lib/event_schema.py index 4bd5e3bc93..2531110609 100644 --- a/zerver/lib/event_schema.py +++ b/zerver/lib/event_schema.py @@ -1048,6 +1048,7 @@ group_setting_update_data_type = DictType( ("create_multiuse_invite_group", int), ("can_access_all_users_group", int), ("can_create_public_channel_group", group_setting_type), + ("can_create_private_channel_group", group_setting_type), ], ) diff --git a/zerver/migrations/0537_realm_can_create_private_channel_group.py b/zerver/migrations/0537_realm_can_create_private_channel_group.py new file mode 100644 index 0000000000..456b881e62 --- /dev/null +++ b/zerver/migrations/0537_realm_can_create_private_channel_group.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.6 on 2024-06-17 09:20 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("zerver", "0536_add_message_type"), + ] + + operations = [ + migrations.AddField( + model_name="realm", + name="can_create_private_channel_group", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.RESTRICT, + related_name="+", + to="zerver.usergroup", + ), + ), + ] diff --git a/zerver/migrations/0538_set_can_create_private_channel_group.py b/zerver/migrations/0538_set_can_create_private_channel_group.py new file mode 100644 index 0000000000..9f4dc7d7bb --- /dev/null +++ b/zerver/migrations/0538_set_can_create_private_channel_group.py @@ -0,0 +1,61 @@ +# Generated by Django 5.0.6 on 2024-06-17 09:20 + +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_can_create_private_channel_group_for_existing_realms( + apps: StateApps, schema_editor: BaseDatabaseSchemaEditor +) -> None: + Realm = apps.get_model("zerver", "Realm") + NamedUserGroup = apps.get_model("zerver", "NamedUserGroup") + + MEMBERS_ONLY = 1 + ADMINS_ONLY = 2 + FULL_MEMBERS_ONLY = 3 + MODERATORS_ONLY = 4 + + Realm.objects.filter( + can_create_private_channel_group=None, create_private_stream_policy=MEMBERS_ONLY + ).update( + can_create_private_channel_group=NamedUserGroup.objects.filter( + name="role:members", realm=OuterRef("id"), is_system_group=True + ).values("pk") + ) + Realm.objects.filter( + can_create_private_channel_group=None, create_private_stream_policy=ADMINS_ONLY + ).update( + can_create_private_channel_group=NamedUserGroup.objects.filter( + name="role:administrators", realm=OuterRef("id"), is_system_group=True + ).values("pk") + ) + Realm.objects.filter( + can_create_private_channel_group=None, create_private_stream_policy=FULL_MEMBERS_ONLY + ).update( + can_create_private_channel_group=NamedUserGroup.objects.filter( + name="role:fullmembers", realm=OuterRef("id"), is_system_group=True + ).values("pk") + ) + Realm.objects.filter( + can_create_private_channel_group=None, create_private_stream_policy=MODERATORS_ONLY + ).update( + can_create_private_channel_group=NamedUserGroup.objects.filter( + name="role:moderators", realm=OuterRef("id"), is_system_group=True + ).values("pk") + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("zerver", "0537_realm_can_create_private_channel_group"), + ] + + operations = [ + migrations.RunPython( + set_can_create_private_channel_group_for_existing_realms, + elidable=True, + reverse_code=migrations.RunPython.noop, + ), + ] diff --git a/zerver/migrations/0539_alter_realm_can_create_private_channel_group.py b/zerver/migrations/0539_alter_realm_can_create_private_channel_group.py new file mode 100644 index 0000000000..db87dc6425 --- /dev/null +++ b/zerver/migrations/0539_alter_realm_can_create_private_channel_group.py @@ -0,0 +1,22 @@ +# Generated by Django 5.0.6 on 2024-06-17 09:24 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("zerver", "0538_set_can_create_private_channel_group"), + ] + + operations = [ + migrations.AlterField( + model_name="realm", + name="can_create_private_channel_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 7ee7a3cf5a..eb0aed089e 100644 --- a/zerver/models/realms.py +++ b/zerver/models/realms.py @@ -313,6 +313,9 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub can_create_public_channel_group = models.ForeignKey( "UserGroup", on_delete=models.RESTRICT, related_name="+" ) + can_create_private_channel_group = models.ForeignKey( + "UserGroup", on_delete=models.RESTRICT, related_name="+" + ) # Who in the organization is allowed to delete messages they themselves sent. delete_own_message_policy = models.PositiveSmallIntegerField( @@ -716,9 +719,19 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub default_group_name=SystemGroups.MEMBERS, id_field_name="can_create_public_channel_group_id", ), + can_create_private_channel_group=GroupPermissionSetting( + require_system_group=False, + allow_internet_group=False, + allow_owners_group=False, + allow_nobody_group=False, + allow_everyone_group=False, + default_group_name=SystemGroups.MEMBERS, + id_field_name="can_create_private_channel_group_id", + ), ) REALM_PERMISSION_GROUP_SETTINGS_WITH_NEW_API_FORMAT = [ + "can_create_private_channel_group", "can_create_public_channel_group", ] @@ -1081,6 +1094,8 @@ def get_realm_with_settings(realm_id: int) -> Realm: "can_access_all_users_group__named_user_group", "can_create_public_channel_group", "can_create_public_channel_group__named_user_group", + "can_create_private_channel_group", + "can_create_private_channel_group__named_user_group", ).get(id=realm_id) diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml index 8f2860365b..82481c9a89 100644 --- a/zerver/openapi/zulip.yaml +++ b/zerver/openapi/zulip.yaml @@ -4245,6 +4245,20 @@ paths: **Changes**: New in Zulip 9.0 (feature level 264). Previously `realm_create_public_stream_policy` field used to control the permission to create public channels. + can_create_private_channel_group: + allOf: + - $ref: "#/components/schemas/GroupSettingValue" + - description: | + A [group-setting value](/api/group-setting-values) defining + the set of users who have permission to create private + channels in this organization. + + This setting cannot be set to `"role:internet"`, `"role:everyone"`, + `"role:owners"` and `"role:nobody"` system groups. + + **Changes**: New in Zulip 9.0 (feature level 266). Previously + `realm_create_private_stream_policy` field used to control the + permission to create private channels. create_private_stream_policy: type: integer description: | @@ -15250,6 +15264,20 @@ paths: **Changes**: New in Zulip 9.0 (feature level 264). Previously `realm_create_public_stream_policy` field used to control the permission to create public channels. + realm_can_create_private_channel_group: + allOf: + - $ref: "#/components/schemas/GroupSettingValue" + - description: | + A [group-setting value](/api/group-setting-values) defining + the set of users who have permission to create private + channels in this organization. + + This setting cannot be set to `"role:internet"`, `"role:everyone"`, + `"role:owners"` and `"role:nobody"` system groups. + + **Changes**: New in Zulip 9.0 (feature level 266). Previously + `realm_create_private_stream_policy` field used to control the + permission to create private channels. realm_create_public_stream_policy: type: integer deprecated: true diff --git a/zerver/tests/test_home.py b/zerver/tests/test_home.py index 3bed3f358c..2e6767c367 100644 --- a/zerver/tests/test_home.py +++ b/zerver/tests/test_home.py @@ -126,6 +126,7 @@ class HomeTest(ZulipTestCase): "realm_bot_domain", "realm_bots", "realm_can_access_all_users_group", + "realm_can_create_private_channel_group", "realm_can_create_public_channel_group", "realm_create_multiuse_invite_group", "realm_create_private_stream_policy", diff --git a/zerver/tests/test_realm.py b/zerver/tests/test_realm.py index 29d46c6c5f..4758b0c839 100644 --- a/zerver/tests/test_realm.py +++ b/zerver/tests/test_realm.py @@ -1594,7 +1594,7 @@ class RealmAPITest(ZulipTestCase): realm = get_realm("zulip") othello = self.example_user("othello") hamlet = self.example_user("hamlet") - leadership_group = check_add_user_group(realm, "leadership", [hamlet], acting_user=None) + leadership_group = NamedUserGroup.objects.get(name="leadership", realm=realm) moderators_group = NamedUserGroup.objects.get( name=SystemGroups.MODERATORS, realm=realm, is_system_group=True @@ -1829,6 +1829,9 @@ class RealmAPITest(ZulipTestCase): with self.subTest(property=prop): self.do_test_realm_permission_group_setting_update_api(prop) + check_add_user_group( + get_realm("zulip"), "leadership", [self.example_user("hamlet")], acting_user=None + ) for prop in Realm.REALM_PERMISSION_GROUP_SETTINGS_WITH_NEW_API_FORMAT: with self.subTest(property=prop): self.do_test_realm_permission_group_setting_update_api_with_anonymous_groups(prop) diff --git a/zerver/views/realm.py b/zerver/views/realm.py index f2eadba6cd..22df2ed76b 100644 --- a/zerver/views/realm.py +++ b/zerver/views/realm.py @@ -148,6 +148,7 @@ def update_realm( message_content_allowed_in_email_notifications: Optional[Json[bool]] = None, bot_creation_policy: Optional[Json[BotCreationPolicyEnum]] = None, can_create_public_channel_group: Optional[Json[GroupSettingChangeRequest]] = None, + can_create_private_channel_group: Optional[Json[GroupSettingChangeRequest]] = None, create_private_stream_policy: Optional[Json[CommonPolicyEnum]] = None, create_web_public_stream_policy: Optional[Json[CreateWebPublicStreamPolicyEnum]] = None, invite_to_stream_policy: Optional[Json[CommonPolicyEnum]] = None,