mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 12:03:46 +00:00 
			
		
		
		
	| @@ -20,6 +20,13 @@ format used by the Zulip server that they are interacting with. | ||||
|  | ||||
| ## Changes in Zulip 10.0 | ||||
|  | ||||
| **Feature level 281** | ||||
|  | ||||
| * [`GET /events`](/api/get-events), [`POST /register`](/api/register-queue): | ||||
|   Added a new realm setting `realm_can_delete_any_message_group` which is a | ||||
|   [group-setting value](/api/group-setting-values) describing the set of | ||||
|   users with permission to delete any message in the organization. | ||||
|  | ||||
| **Feature level 280** | ||||
|  | ||||
| * `PATCH /realm`, [`POST /register`](/api/register-queue), | ||||
|   | ||||
| @@ -157,7 +157,7 @@ IGNORED_PHRASES = [ | ||||
|     # Used in message-move-time-limit setting label | ||||
|     r"does not apply to moderators and administrators", | ||||
|     # Used in message-delete-time-limit setting label | ||||
|     r"does not apply to administrators", | ||||
|     r"does not apply to users who can delete any message", | ||||
|     # Used as indicator with names for guest users. | ||||
|     r"guest", | ||||
|     # Used in pills for deactivated users. | ||||
|   | ||||
| @@ -34,7 +34,7 @@ DESKTOP_WARNING_VERSION = "5.9.3" | ||||
| # new level means in api_docs/changelog.md, as well as "**Changes**" | ||||
| # entries in the endpoint's documentation in `zulip.yaml`. | ||||
|  | ||||
| API_FEATURE_LEVEL = 280  # Last bumped for can_create_web_public_channel_group | ||||
| API_FEATURE_LEVEL = 281  # Last bumped for realm_can_delete_any_message_group | ||||
|  | ||||
|  | ||||
| # Bump the minor PROVISION_VERSION to indicate that folks should provision | ||||
|   | ||||
| @@ -183,6 +183,7 @@ export function build_page() { | ||||
|         can_invite_users_by_email: settings_data.user_can_invite_users_by_email(), | ||||
|         realm_invite_required: realm.realm_invite_required, | ||||
|         policy_values: settings_config.common_policy_values, | ||||
|         realm_can_delete_any_message_group: realm.realm_can_delete_any_message_group, | ||||
|         realm_delete_own_message_policy: realm.realm_delete_own_message_policy, | ||||
|         DELETE_OWN_MESSAGE_POLICY_ADMINS_ONLY: | ||||
|             settings_config.common_message_policy_values.by_admins_only.code, | ||||
|   | ||||
| @@ -178,7 +178,7 @@ export function is_message_sent_by_my_bot(message) { | ||||
| } | ||||
|  | ||||
| export function get_deletability(message) { | ||||
|     if (current_user.is_admin) { | ||||
|     if (settings_data.user_can_delete_any_message()) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -204,6 +204,7 @@ export function dispatch_normal_event(event) { | ||||
|                 user_group_edit_policy: noop, | ||||
|                 avatar_changes_disabled: settings_account.update_avatar_change_display, | ||||
|                 bot_creation_policy: settings_bots.update_bot_permissions_ui, | ||||
|                 can_delete_any_message_group: noop, | ||||
|                 create_multiuse_invite_group: noop, | ||||
|                 invite_to_stream_policy: noop, | ||||
|                 default_code_block_language: noop, | ||||
|   | ||||
| @@ -479,6 +479,7 @@ const dropdown_widget_map = new Map<string, DropdownWidget | null>([ | ||||
|     ["realm_can_create_public_channel_group", null], | ||||
|     ["realm_can_create_private_channel_group", null], | ||||
|     ["realm_can_create_web_public_channel_group", null], | ||||
|     ["realm_can_delete_any_message_group", null], | ||||
|     ["realm_direct_message_initiator_group", null], | ||||
|     ["realm_direct_message_permission_group", null], | ||||
| ]); | ||||
| @@ -794,6 +795,7 @@ export function check_realm_settings_property_changed(elem: HTMLElement): boolea | ||||
|         case "realm_can_create_public_channel_group": | ||||
|         case "realm_can_create_private_channel_group": | ||||
|         case "realm_can_create_web_public_channel_group": | ||||
|         case "realm_can_delete_any_message_group": | ||||
|         case "realm_direct_message_initiator_group": | ||||
|         case "realm_direct_message_permission_group": | ||||
|             proposed_val = get_dropdown_list_widget_setting_value($elem); | ||||
| @@ -990,6 +992,7 @@ export function populate_data_for_realm_settings_request( | ||||
|                     "can_create_private_channel_group", | ||||
|                     "can_create_public_channel_group", | ||||
|                     "can_create_web_public_channel_group", | ||||
|                     "can_delete_any_message_group", | ||||
|                     "direct_message_initiator_group", | ||||
|                     "direct_message_permission_group", | ||||
|                 ]); | ||||
|   | ||||
| @@ -193,6 +193,16 @@ export function user_can_move_messages_to_another_topic(): boolean { | ||||
|     return user_has_permission(realm.realm_edit_topic_policy); | ||||
| } | ||||
|  | ||||
| export function user_can_delete_any_message(): boolean { | ||||
|     if (page_params.is_spectator) { | ||||
|         return false; | ||||
|     } | ||||
|     return user_groups.is_user_in_group( | ||||
|         realm.realm_can_delete_any_message_group, | ||||
|         current_user.user_id, | ||||
|     ); | ||||
| } | ||||
|  | ||||
| export function user_can_delete_own_message(): boolean { | ||||
|     return user_has_permission(realm.realm_delete_own_message_policy); | ||||
| } | ||||
|   | ||||
| @@ -261,36 +261,68 @@ function set_msg_move_limit_setting(property_name) { | ||||
|     enable_or_disable_related_message_move_time_limit_setting(property_name, disable_setting); | ||||
| } | ||||
|  | ||||
| function message_delete_limit_setting_enabled(setting_value) { | ||||
| function message_delete_limit_setting_enabled() { | ||||
|     // This function is used to check whether the time-limit setting | ||||
|     // should be enabled. The setting is disabled when delete_own_message_policy | ||||
|     // is set to 'admins only' as admins can delete messages irrespective of | ||||
|     // time limit. | ||||
|     if (setting_value === settings_config.common_message_policy_values.by_admins_only.code) { | ||||
|     // should be enabled. The setting is disabled when every user | ||||
|     // who is allowed to delete their own messages is also allowed | ||||
|     // to delete any message in the organization. | ||||
|     const realm_delete_own_message_policy = Number.parseInt( | ||||
|         $("#id_realm_delete_own_message_policy").val(), | ||||
|         10, | ||||
|     ); | ||||
|     const realm_can_delete_any_message_group_id = | ||||
|         settings_components.get_dropdown_list_widget_setting_value( | ||||
|             $("#id_realm_can_delete_any_message_group"), | ||||
|         ); | ||||
|     const realm_can_delete_any_message_group_name = user_groups.get_user_group_from_id( | ||||
|         realm_can_delete_any_message_group_id, | ||||
|     ).name; | ||||
|     const common_message_policy_values = settings_config.common_message_policy_values; | ||||
|  | ||||
|     if (realm_delete_own_message_policy === common_message_policy_values.by_admins_only.code) { | ||||
|         return false; | ||||
|     } | ||||
|     if (realm_can_delete_any_message_group_name === "role:administrators") { | ||||
|         return true; | ||||
|     } | ||||
|     if (realm_delete_own_message_policy === common_message_policy_values.by_moderators_only.code) { | ||||
|         return false; | ||||
|     } | ||||
|     if (realm_can_delete_any_message_group_name === "role:moderators") { | ||||
|         return true; | ||||
|     } | ||||
|     if (realm_delete_own_message_policy === common_message_policy_values.by_full_members.code) { | ||||
|         return false; | ||||
|     } | ||||
|     if (realm_can_delete_any_message_group_name === "role:fullmembers") { | ||||
|         return true; | ||||
|     } | ||||
|     if (realm_delete_own_message_policy === common_message_policy_values.by_members.code) { | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| function set_delete_own_message_policy_dropdown(setting_value) { | ||||
|     $("#id_realm_delete_own_message_policy").val(setting_value); | ||||
| function check_disable_message_delete_limit_setting_dropdown() { | ||||
|     settings_ui.disable_sub_setting_onchange( | ||||
|         message_delete_limit_setting_enabled(setting_value), | ||||
|         message_delete_limit_setting_enabled(), | ||||
|         "id_realm_message_content_delete_limit_seconds", | ||||
|         true, | ||||
|     ); | ||||
|     const limit_setting_dropdown_value = settings_components.get_time_limit_dropdown_setting_value( | ||||
|         "realm_message_content_delete_limit_seconds", | ||||
|     ); | ||||
|     if (limit_setting_dropdown_value === "custom_period") { | ||||
|     if ($("#id_realm_message_content_delete_limit_minutes").length) { | ||||
|         settings_ui.disable_sub_setting_onchange( | ||||
|             message_delete_limit_setting_enabled(setting_value), | ||||
|             message_delete_limit_setting_enabled(), | ||||
|             "id_realm_message_content_delete_limit_minutes", | ||||
|             true, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function set_delete_own_message_policy_dropdown(setting_value) { | ||||
|     $("#id_realm_delete_own_message_policy").val(setting_value); | ||||
|     check_disable_message_delete_limit_setting_dropdown(); | ||||
| } | ||||
|  | ||||
| function set_msg_delete_limit_dropdown() { | ||||
|     settings_components.set_time_limit_setting("realm_message_content_delete_limit_seconds"); | ||||
| } | ||||
| @@ -453,6 +485,9 @@ function update_dependent_subsettings(property_name) { | ||||
|         case "realm_allow_message_editing": | ||||
|             update_message_edit_sub_settings(realm.realm_allow_message_editing); | ||||
|             break; | ||||
|         case "realm_can_delete_any_message_group": | ||||
|             check_disable_message_delete_limit_setting_dropdown(); | ||||
|             break; | ||||
|         case "realm_delete_own_message_policy": | ||||
|             set_delete_own_message_policy_dropdown(realm.realm_delete_own_message_policy); | ||||
|             break; | ||||
| @@ -506,6 +541,7 @@ export function discard_realm_property_element_changes(elem) { | ||||
|         case "realm_can_create_public_channel_group": | ||||
|         case "realm_can_create_private_channel_group": | ||||
|         case "realm_can_create_web_public_channel_group": | ||||
|         case "realm_can_delete_any_message_group": | ||||
|             settings_components.set_dropdown_list_widget_setting_value( | ||||
|                 property_name, | ||||
|                 property_value, | ||||
| @@ -825,6 +861,8 @@ export function set_up_dropdown_widget_for_realm_group_settings() { | ||||
|         if (setting_name === "direct_message_permission_group") { | ||||
|             dropdown_list_item_click_callback = | ||||
|                 check_disable_direct_message_initiator_group_dropdown; | ||||
|         } else if (setting_name === "can_delete_any_message_group") { | ||||
|             dropdown_list_item_click_callback = check_disable_message_delete_limit_setting_dropdown; | ||||
|         } | ||||
|         set_up_dropdown_widget( | ||||
|             "realm_" + setting_name, | ||||
|   | ||||
| @@ -267,6 +267,7 @@ const realm_schema = z.object({ | ||||
|     realm_can_create_public_channel_group: z.number(), | ||||
|     realm_can_create_private_channel_group: z.number(), | ||||
|     realm_can_create_web_public_channel_group: z.number(), | ||||
|     realm_can_delete_any_message_group: z.number(), | ||||
|     realm_create_multiuse_invite_group: z.number(), | ||||
|     realm_create_private_stream_policy: z.number(), | ||||
|     realm_date_created: z.number(), | ||||
|   | ||||
| @@ -236,9 +236,11 @@ | ||||
|                 {{> settings_save_discard_widget section_name="msg-deletion" }} | ||||
|             </div> | ||||
|             <div class="inline-block organization-settings-parent"> | ||||
|                 <label for="org-msg-deletion" class="inline-block"> | ||||
|                     {{t "Administrators can delete any message." }} | ||||
|                 </label> | ||||
|                 {{> ../dropdown_widget_with_label | ||||
|                   widget_name="realm_can_delete_any_message_group" | ||||
|                   label=(t 'Who can delete any message') | ||||
|                   value_type="number" }} | ||||
|  | ||||
|                 <div class="input-group"> | ||||
|                     <label for="realm_delete_own_message_policy" class="settings-field-label"> | ||||
|                         {{t "Who can delete their own messages" }} | ||||
| @@ -250,7 +252,7 @@ | ||||
|  | ||||
|                 <div class="input-group time-limit-setting"> | ||||
|                     <label for="realm_message_content_delete_limit_seconds" class="settings-field-label"> | ||||
|                         {{t "Time limit for deleting messages" }} <i>({{t "does not apply to administrators" }})</i> | ||||
|                         {{t "Time limit for deleting messages" }} <i>({{t "does not apply to users who can delete any message" }})</i> | ||||
|                     </label> | ||||
|                     <select name="realm_message_content_delete_limit_seconds" id="id_realm_message_content_delete_limit_seconds" class="prop-element bootstrap-focus-style settings_select" data-setting-widget-type="time-limit"> | ||||
|                         {{#each msg_delete_limit_dropdown_values}} | ||||
|   | ||||
| @@ -181,6 +181,7 @@ run_test("is_stream_editable", ({override}) => { | ||||
|  | ||||
| run_test("get_deletability", ({override}) => { | ||||
|     current_user.is_admin = true; | ||||
|     override(settings_data, "user_can_delete_any_message", () => true); | ||||
|     override(settings_data, "user_can_delete_own_message", () => false); | ||||
|     realm.realm_message_content_delete_limit_seconds = null; | ||||
|     const test_user = { | ||||
| @@ -204,11 +205,11 @@ run_test("get_deletability", ({override}) => { | ||||
|         sender_id: 1, | ||||
|     }; | ||||
|  | ||||
|     // Admin can always delete any message | ||||
|     // User can delete any message | ||||
|     assert.equal(message_edit.get_deletability(message), true); | ||||
|  | ||||
|     // Non-admin can't delete message sent by others | ||||
|     current_user.is_admin = false; | ||||
|     override(settings_data, "user_can_delete_any_message", () => false); | ||||
|     // User can't delete message sent by others | ||||
|     assert.equal(message_edit.get_deletability(message), false); | ||||
|  | ||||
|     // Locally echoed messages are not deletable | ||||
|   | ||||
| @@ -14,6 +14,7 @@ const message_lists = zrequire("message_lists"); | ||||
| const popover_menus_data = zrequire("popover_menus_data"); | ||||
| const people = zrequire("people"); | ||||
| const compose_state = zrequire("compose_state"); | ||||
| const user_groups = zrequire("user_groups"); | ||||
|  | ||||
| const noop = function () {}; | ||||
|  | ||||
| @@ -70,6 +71,15 @@ const me = { | ||||
|     is_guest: false, | ||||
| }; | ||||
|  | ||||
| const everyone = { | ||||
|     name: "role:everyone", | ||||
|     id: 2, | ||||
|     members: new Set([999, 1000, 2000]), | ||||
|     is_system_group: true, | ||||
|     direct_subgroup_ids: new Set([]), | ||||
| }; | ||||
| user_groups.initialize({realm_user_groups: [everyone]}); | ||||
|  | ||||
| // Helper functions: | ||||
| function add_initialize_users() { | ||||
|     // Initialize people | ||||
| @@ -139,7 +149,7 @@ function test(label, f) { | ||||
| test("my_message_all_actions", () => { | ||||
|     // Set page parameters. | ||||
|     set_page_params_no_edit_restrictions(); | ||||
|  | ||||
|     realm.realm_can_delete_any_message_group = everyone.id; | ||||
|     // Get message with maximum permissions available | ||||
|     // Initialize message list | ||||
|     const list = init_message_list(); | ||||
| @@ -191,7 +201,7 @@ test("my_message_all_actions", () => { | ||||
| test("not_my_message_view_actions", () => { | ||||
|     set_page_params_no_edit_restrictions(); | ||||
|     // Get message that is only viewable | ||||
|  | ||||
|     realm.realm_can_delete_any_message_group = everyone.id; | ||||
|     const list = init_message_list(); | ||||
|     message_lists.set_current(list); | ||||
|  | ||||
| @@ -229,7 +239,7 @@ test("not_my_message_view_actions", () => { | ||||
|  | ||||
| test("not_my_message_view_source_and_move", () => { | ||||
|     set_page_params_no_edit_restrictions(); | ||||
|  | ||||
|     realm.realm_can_delete_any_message_group = everyone.id; | ||||
|     // Get message that is movable with viewable source | ||||
|  | ||||
|     const list = init_message_list(); | ||||
|   | ||||
| @@ -227,6 +227,11 @@ run_test("user_can_move_messages_between_streams_nobody_case", () => { | ||||
|     assert.equal(settings_data.user_can_move_messages_between_streams(), false); | ||||
| }); | ||||
|  | ||||
| test_realm_group_settings( | ||||
|     "realm_can_delete_any_message_group", | ||||
|     settings_data.user_can_delete_any_message, | ||||
| ); | ||||
|  | ||||
| test_message_policy( | ||||
|     "user_can_delete_own_message", | ||||
|     "realm_delete_own_message_policy", | ||||
|   | ||||
| @@ -505,6 +505,7 @@ function test_discard_changes_button(discard_changes) { | ||||
| } | ||||
|  | ||||
| test("set_up", ({override, override_rewire}) => { | ||||
|     override_rewire(settings_org, "check_disable_message_delete_limit_setting_dropdown", noop); | ||||
|     realm.realm_available_video_chat_providers = { | ||||
|         jitsi_meet: { | ||||
|             id: 1, | ||||
|   | ||||
| @@ -1050,6 +1050,7 @@ group_setting_update_data_type = DictType( | ||||
|         ("can_create_public_channel_group", group_setting_type), | ||||
|         ("can_create_private_channel_group", group_setting_type), | ||||
|         ("can_create_web_public_channel_group", group_setting_type), | ||||
|         ("can_delete_any_message_group", group_setting_type), | ||||
|         ("direct_message_initiator_group", group_setting_type), | ||||
|         ("direct_message_permission_group", group_setting_type), | ||||
|     ], | ||||
|   | ||||
							
								
								
									
										23
									
								
								zerver/migrations/0565_realm_can_delete_any_message_group.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								zerver/migrations/0565_realm_can_delete_any_message_group.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| # Generated by Django 5.0.6 on 2024-07-18 15:05 | ||||
|  | ||||
| import django.db.models.deletion | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|     dependencies = [ | ||||
|         ("zerver", "0564_purge_nagios_messages"), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name="realm", | ||||
|             name="can_delete_any_message_group", | ||||
|             field=models.ForeignKey( | ||||
|                 null=True, | ||||
|                 on_delete=django.db.models.deletion.RESTRICT, | ||||
|                 related_name="+", | ||||
|                 to="zerver.usergroup", | ||||
|             ), | ||||
|         ), | ||||
|     ] | ||||
| @@ -0,0 +1,39 @@ | ||||
| # Generated by Django 4.2.1 on 2023-06-12 10:47 | ||||
|  | ||||
| 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_delete_any_message_group( | ||||
|     apps: StateApps, schema_editor: BaseDatabaseSchemaEditor | ||||
| ) -> None: | ||||
|     Realm = apps.get_model("zerver", "Realm") | ||||
|     NamedUserGroup = apps.get_model("zerver", "NamedUserGroup") | ||||
|  | ||||
|     ADMINISTRATORS_GROUP_NAME = "role:administrators" | ||||
|  | ||||
|     Realm.objects.filter( | ||||
|         can_delete_any_message_group=None, | ||||
|     ).update( | ||||
|         can_delete_any_message_group=NamedUserGroup.objects.filter( | ||||
|             name=ADMINISTRATORS_GROUP_NAME, realm=OuterRef("id"), is_system_group=True | ||||
|         ).values("pk") | ||||
|     ) | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|     atomic = False | ||||
|  | ||||
|     dependencies = [ | ||||
|         ("zerver", "0565_realm_can_delete_any_message_group"), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.RunPython( | ||||
|             set_default_value_for_can_delete_any_message_group, | ||||
|             elidable=True, | ||||
|             reverse_code=migrations.RunPython.noop, | ||||
|         ) | ||||
|     ] | ||||
| @@ -0,0 +1,22 @@ | ||||
| # Generated by Django 5.0.6 on 2024-07-18 15:06 | ||||
|  | ||||
| import django.db.models.deletion | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|     dependencies = [ | ||||
|         ("zerver", "0566_set_default_for_can_delete_any_message_group"), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name="realm", | ||||
|             name="can_delete_any_message_group", | ||||
|             field=models.ForeignKey( | ||||
|                 on_delete=django.db.models.deletion.RESTRICT, | ||||
|                 related_name="+", | ||||
|                 to="zerver.usergroup", | ||||
|             ), | ||||
|         ), | ||||
|     ] | ||||
| @@ -308,6 +308,11 @@ class Realm(models.Model):  # type: ignore[django-manager-missing] # django-stub | ||||
|         "UserGroup", on_delete=models.RESTRICT, related_name="+" | ||||
|     ) | ||||
|  | ||||
|     # Who in the organization is allowed to delete any message. | ||||
|     can_delete_any_message_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( | ||||
|         default=CommonMessagePolicyEnum.EVERYONE | ||||
| @@ -723,6 +728,15 @@ class Realm(models.Model):  # type: ignore[django-manager-missing] # django-stub | ||||
|             default_group_name=SystemGroups.MEMBERS, | ||||
|             id_field_name="can_create_private_channel_group_id", | ||||
|         ), | ||||
|         can_delete_any_message_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.ADMINISTRATORS, | ||||
|             id_field_name="can_delete_any_message_group_id", | ||||
|         ), | ||||
|         direct_message_initiator_group=GroupPermissionSetting( | ||||
|             require_system_group=False, | ||||
|             allow_internet_group=False, | ||||
| @@ -762,6 +776,7 @@ class Realm(models.Model):  # type: ignore[django-manager-missing] # django-stub | ||||
|         "can_create_private_channel_group", | ||||
|         "can_create_public_channel_group", | ||||
|         "can_create_web_public_channel_group", | ||||
|         "can_delete_any_message_group", | ||||
|         "direct_message_initiator_group", | ||||
|         "direct_message_permission_group", | ||||
|     ] | ||||
| @@ -1131,6 +1146,8 @@ def get_realm_with_settings(realm_id: int) -> Realm: | ||||
|         "can_create_private_channel_group__named_user_group", | ||||
|         "can_create_web_public_channel_group", | ||||
|         "can_create_web_public_channel_group__named_user_group", | ||||
|         "can_delete_any_message_group", | ||||
|         "can_delete_any_message_group__named_user_group", | ||||
|         "direct_message_initiator_group", | ||||
|         "direct_message_initiator_group__named_user_group", | ||||
|         "direct_message_permission_group", | ||||
|   | ||||
| @@ -781,6 +781,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): | ||||
|             "can_create_private_channel_group", | ||||
|             "can_create_public_channel_group", | ||||
|             "can_create_web_public_channel_group", | ||||
|             "can_delete_any_message_group", | ||||
|             "create_multiuse_invite_group", | ||||
|             "delete_own_message_policy", | ||||
|             "direct_message_initiator_group", | ||||
| @@ -868,6 +869,9 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): | ||||
|     def can_add_custom_emoji(self) -> bool: | ||||
|         return self.has_permission("add_custom_emoji_policy") | ||||
|  | ||||
|     def can_delete_any_message(self) -> bool: | ||||
|         return self.has_permission("can_delete_any_message_group") | ||||
|  | ||||
|     def can_delete_own_message(self) -> bool: | ||||
|         return self.has_permission("delete_own_message_policy") | ||||
|  | ||||
|   | ||||
| @@ -4299,6 +4299,15 @@ paths: | ||||
|                                             **Changes**: New in Zulip 10.0 (feature level 280). Previously | ||||
|                                             `realm_create_web_public_stream_policy` field used to control | ||||
|                                             the permission to create web-public channels. | ||||
|                                     can_delete_any_message_group: | ||||
|                                       allOf: | ||||
|                                         - description: | | ||||
|                                             A [group-setting value](/api/group-setting-values) defining the set of | ||||
|                                             users who have permission to delete any message in the organization. | ||||
| 
 | ||||
|                                             **Changes**: New in Zulip 10.0 (feature level 281). Previously, this | ||||
|                                             permission was limited to administrators only and was uneditable. | ||||
|                                         - $ref: "#/components/schemas/GroupSettingValue" | ||||
|                                     default_code_block_language: | ||||
|                                       type: string | ||||
|                                       description: | | ||||
| @@ -15530,6 +15539,17 @@ paths: | ||||
| 
 | ||||
|                           Whether this organization is configured to allow users to access | ||||
|                           [message edit history](/help/view-a-messages-edit-history). | ||||
|                       realm_can_delete_any_message_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 delete any message in the organization. | ||||
| 
 | ||||
|                               **Changes**: New in Zulip 10.0 (feature level 281). Previously, this | ||||
|                               permission was limited to administrators only and was uneditable. | ||||
|                           - $ref: "#/components/schemas/GroupSettingValue" | ||||
|                       realm_delete_own_message_policy: | ||||
|                         type: integer | ||||
|                         description: | | ||||
|   | ||||
| @@ -133,6 +133,7 @@ class HomeTest(ZulipTestCase): | ||||
|         "realm_can_create_private_channel_group", | ||||
|         "realm_can_create_public_channel_group", | ||||
|         "realm_can_create_web_public_channel_group", | ||||
|         "realm_can_delete_any_message_group", | ||||
|         "realm_create_multiuse_invite_group", | ||||
|         "realm_create_private_stream_policy", | ||||
|         "realm_create_public_stream_policy", | ||||
|   | ||||
| @@ -7,9 +7,13 @@ from django.db import IntegrityError | ||||
| from django.utils.timezone import now as timezone_now | ||||
|  | ||||
| from zerver.actions.message_delete import do_delete_messages | ||||
| from zerver.actions.realm_settings import do_set_realm_property | ||||
| from zerver.actions.realm_settings import ( | ||||
|     do_change_realm_permission_group_setting, | ||||
|     do_set_realm_property, | ||||
| ) | ||||
| from zerver.lib.test_classes import ZulipTestCase | ||||
| from zerver.models import Message, UserProfile | ||||
| from zerver.models import Message, NamedUserGroup, UserProfile | ||||
| from zerver.models.groups import SystemGroups | ||||
| from zerver.models.realms import CommonMessagePolicyEnum, get_realm | ||||
| from zerver.models.streams import get_stream | ||||
|  | ||||
| @@ -29,12 +33,17 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|  | ||||
|     def test_delete_message_by_user(self) -> None: | ||||
|         def set_message_deleting_params( | ||||
|             delete_own_message_policy: int, message_content_delete_limit_seconds: int | str | ||||
|             can_delete_any_message_group: NamedUserGroup, | ||||
|             delete_own_message_policy: int, | ||||
|             message_content_delete_limit_seconds: int | str, | ||||
|         ) -> None: | ||||
|             self.login("iago") | ||||
|             result = self.client_patch( | ||||
|                 "/json/realm", | ||||
|                 { | ||||
|                     "can_delete_any_message_group": orjson.dumps( | ||||
|                         {"new": can_delete_any_message_group.id} | ||||
|                     ).decode(), | ||||
|                     "delete_own_message_policy": orjson.dumps(delete_own_message_policy).decode(), | ||||
|                     "message_content_delete_limit_seconds": orjson.dumps( | ||||
|                         message_content_delete_limit_seconds | ||||
| @@ -48,7 +57,12 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|             result = self.client_delete(f"/json/messages/{msg_id}") | ||||
|             return result | ||||
|  | ||||
|         def test_delete_message_by_owner(msg_id: int) -> "TestHttpResponse": | ||||
|         def test_delete_message_by_moderator(msg_id: int) -> "TestHttpResponse": | ||||
|             self.login("shiva") | ||||
|             result = self.client_delete(f"/json/messages/{msg_id}") | ||||
|             return result | ||||
|  | ||||
|         def test_delete_message_by_sender(msg_id: int) -> "TestHttpResponse": | ||||
|             self.login("hamlet") | ||||
|             result = self.client_delete(f"/json/messages/{msg_id}") | ||||
|             return result | ||||
| @@ -58,13 +72,27 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|             result = self.client_delete(f"/json/messages/{msg_id}") | ||||
|             return result | ||||
|  | ||||
|         realm = get_realm("zulip") | ||||
|  | ||||
|         administrators_system_group = NamedUserGroup.objects.get( | ||||
|             name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True | ||||
|         ) | ||||
|         members_system_group = NamedUserGroup.objects.get( | ||||
|             name=SystemGroups.MEMBERS, realm=realm, is_system_group=True | ||||
|         ) | ||||
|         moderators_system_group = NamedUserGroup.objects.get( | ||||
|             name=SystemGroups.MODERATORS, realm=realm, is_system_group=True | ||||
|         ) | ||||
|  | ||||
|         # Test if message deleting is not allowed(default). | ||||
|         set_message_deleting_params(CommonMessagePolicyEnum.ADMINS_ONLY, "unlimited") | ||||
|         set_message_deleting_params( | ||||
|             administrators_system_group, CommonMessagePolicyEnum.ADMINS_ONLY, "unlimited" | ||||
|         ) | ||||
|         hamlet = self.example_user("hamlet") | ||||
|         self.login_user(hamlet) | ||||
|         msg_id = self.send_stream_message(hamlet, "Denmark") | ||||
|  | ||||
|         result = test_delete_message_by_owner(msg_id=msg_id) | ||||
|         result = test_delete_message_by_sender(msg_id=msg_id) | ||||
|         self.assert_json_error(result, "You don't have permission to delete this message") | ||||
|  | ||||
|         result = test_delete_message_by_other_user(msg_id=msg_id) | ||||
| @@ -75,7 +103,9 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|  | ||||
|         # Test if message deleting is allowed. | ||||
|         # Test if time limit is None(no limit). | ||||
|         set_message_deleting_params(CommonMessagePolicyEnum.EVERYONE, "unlimited") | ||||
|         set_message_deleting_params( | ||||
|             administrators_system_group, CommonMessagePolicyEnum.EVERYONE, "unlimited" | ||||
|         ) | ||||
|         msg_id = self.send_stream_message(hamlet, "Denmark") | ||||
|         message = Message.objects.get(id=msg_id) | ||||
|         message.date_sent -= timedelta(seconds=600) | ||||
| @@ -84,11 +114,13 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|         result = test_delete_message_by_other_user(msg_id=msg_id) | ||||
|         self.assert_json_error(result, "You don't have permission to delete this message") | ||||
|  | ||||
|         result = test_delete_message_by_owner(msg_id=msg_id) | ||||
|         result = test_delete_message_by_sender(msg_id=msg_id) | ||||
|         self.assert_json_success(result) | ||||
|  | ||||
|         # Test if time limit is non-zero. | ||||
|         set_message_deleting_params(CommonMessagePolicyEnum.EVERYONE, 240) | ||||
|         set_message_deleting_params( | ||||
|             administrators_system_group, CommonMessagePolicyEnum.EVERYONE, 240 | ||||
|         ) | ||||
|         msg_id_1 = self.send_stream_message(hamlet, "Denmark") | ||||
|         message = Message.objects.get(id=msg_id_1) | ||||
|         message.date_sent -= timedelta(seconds=120) | ||||
| @@ -102,9 +134,9 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|         result = test_delete_message_by_other_user(msg_id=msg_id_1) | ||||
|         self.assert_json_error(result, "You don't have permission to delete this message") | ||||
|  | ||||
|         result = test_delete_message_by_owner(msg_id=msg_id_1) | ||||
|         result = test_delete_message_by_sender(msg_id=msg_id_1) | ||||
|         self.assert_json_success(result) | ||||
|         result = test_delete_message_by_owner(msg_id=msg_id_2) | ||||
|         result = test_delete_message_by_sender(msg_id=msg_id_2) | ||||
|         self.assert_json_error(result, "The time limit for deleting this message has passed") | ||||
|  | ||||
|         # No limit for admin. | ||||
| @@ -113,11 +145,53 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|  | ||||
|         # Test multiple delete requests with no latency issues | ||||
|         msg_id = self.send_stream_message(hamlet, "Denmark") | ||||
|         result = test_delete_message_by_owner(msg_id=msg_id) | ||||
|         result = test_delete_message_by_sender(msg_id=msg_id) | ||||
|         self.assert_json_success(result) | ||||
|         result = test_delete_message_by_owner(msg_id=msg_id) | ||||
|         result = test_delete_message_by_sender(msg_id=msg_id) | ||||
|         self.assert_json_error(result, "Invalid message(s)") | ||||
|  | ||||
|         # Test if message deletion is allowed when every member can delete any message. | ||||
|         set_message_deleting_params( | ||||
|             members_system_group, CommonMessagePolicyEnum.ADMINS_ONLY, "unlimited" | ||||
|         ) | ||||
|         msg_id_1 = self.send_stream_message(hamlet, "Denmark") | ||||
|         msg_id_2 = self.send_stream_message(hamlet, "Denmark") | ||||
|         msg_id_3 = self.send_stream_message(hamlet, "Denmark") | ||||
|  | ||||
|         result = test_delete_message_by_other_user(msg_id=msg_id_1) | ||||
|         self.assert_json_success(result) | ||||
|  | ||||
|         result = test_delete_message_by_sender(msg_id=msg_id_2) | ||||
|         self.assert_json_success(result) | ||||
|  | ||||
|         result = test_delete_message_by_admin(msg_id=msg_id_3) | ||||
|         self.assert_json_success(result) | ||||
|  | ||||
|         # Test if there is no time limit to delete messages for users who can delete | ||||
|         # any message. | ||||
|         set_message_deleting_params(moderators_system_group, CommonMessagePolicyEnum.EVERYONE, 240) | ||||
|         msg_id_1 = self.send_stream_message(hamlet, "Denmark") | ||||
|         message = Message.objects.get(id=msg_id_1) | ||||
|         message.date_sent -= timedelta(seconds=120) | ||||
|         message.save() | ||||
|  | ||||
|         msg_id_2 = self.send_stream_message(hamlet, "Denmark") | ||||
|         message = Message.objects.get(id=msg_id_2) | ||||
|         message.date_sent -= timedelta(seconds=360) | ||||
|         message.save() | ||||
|  | ||||
|         result = test_delete_message_by_other_user(msg_id=msg_id_1) | ||||
|         self.assert_json_error(result, "You don't have permission to delete this message") | ||||
|  | ||||
|         result = test_delete_message_by_sender(msg_id=msg_id_1) | ||||
|         self.assert_json_success(result) | ||||
|  | ||||
|         result = test_delete_message_by_sender(msg_id=msg_id_2) | ||||
|         self.assert_json_error(result, "The time limit for deleting this message has passed") | ||||
|  | ||||
|         result = test_delete_message_by_moderator(msg_id=msg_id_2) | ||||
|         self.assert_json_success(result) | ||||
|  | ||||
|         # Test handling of 500 error caused by multiple delete requests due to latency. | ||||
|         # see issue #11219. | ||||
|         with ( | ||||
| @@ -126,24 +200,30 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|             mock.patch("zerver.views.message_edit.access_message", return_value=(None, None)), | ||||
|         ): | ||||
|             m.side_effect = IntegrityError() | ||||
|             result = test_delete_message_by_owner(msg_id=msg_id) | ||||
|             result = test_delete_message_by_sender(msg_id=msg_id) | ||||
|             self.assert_json_error(result, "Message already deleted") | ||||
|             m.side_effect = Message.DoesNotExist() | ||||
|             result = test_delete_message_by_owner(msg_id=msg_id) | ||||
|             result = test_delete_message_by_sender(msg_id=msg_id) | ||||
|             self.assert_json_error(result, "Message already deleted") | ||||
|  | ||||
|     def test_delete_message_sent_by_bots(self) -> None: | ||||
|         iago = self.example_user("iago") | ||||
|         shiva = self.example_user("shiva") | ||||
|         hamlet = self.example_user("hamlet") | ||||
|         cordelia = self.example_user("cordelia") | ||||
|  | ||||
|         def set_message_deleting_params( | ||||
|             delete_own_message_policy: int, message_content_delete_limit_seconds: int | str | ||||
|             can_delete_any_message_group: NamedUserGroup, | ||||
|             delete_own_message_policy: int, | ||||
|             message_content_delete_limit_seconds: int | str, | ||||
|         ) -> None: | ||||
|             result = self.api_patch( | ||||
|                 iago, | ||||
|                 "/api/v1/realm", | ||||
|                 { | ||||
|                     "can_delete_any_message_group": orjson.dumps( | ||||
|                         {"new": can_delete_any_message_group.id} | ||||
|                     ).decode(), | ||||
|                     "delete_own_message_policy": orjson.dumps(delete_own_message_policy).decode(), | ||||
|                     "message_content_delete_limit_seconds": orjson.dumps( | ||||
|                         message_content_delete_limit_seconds | ||||
| @@ -156,6 +236,10 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|             result = self.api_delete(iago, f"/api/v1/messages/{msg_id}") | ||||
|             return result | ||||
|  | ||||
|         def test_delete_message_by_moderator(msg_id: int) -> "TestHttpResponse": | ||||
|             result = self.api_delete(shiva, f"/api/v1/messages/{msg_id}") | ||||
|             return result | ||||
|  | ||||
|         def test_delete_message_by_bot_owner(msg_id: int) -> "TestHttpResponse": | ||||
|             result = self.api_delete(hamlet, f"/api/v1/messages/{msg_id}") | ||||
|             return result | ||||
| @@ -164,23 +248,41 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|             result = self.api_delete(cordelia, f"/api/v1/messages/{msg_id}") | ||||
|             return result | ||||
|  | ||||
|         set_message_deleting_params(CommonMessagePolicyEnum.ADMINS_ONLY, "unlimited") | ||||
|         realm = get_realm("zulip") | ||||
|  | ||||
|         administrators_system_group = NamedUserGroup.objects.get( | ||||
|             name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True | ||||
|         ) | ||||
|         moderators_system_group = NamedUserGroup.objects.get( | ||||
|             name=SystemGroups.MODERATORS, realm=realm, is_system_group=True | ||||
|         ) | ||||
|  | ||||
|         set_message_deleting_params( | ||||
|             moderators_system_group, CommonMessagePolicyEnum.ADMINS_ONLY, "unlimited" | ||||
|         ) | ||||
|  | ||||
|         hamlet = self.example_user("hamlet") | ||||
|         test_bot = self.create_test_bot("test-bot", hamlet) | ||||
|         msg_id = self.send_stream_message(test_bot, "Denmark") | ||||
|         msg_id_1 = self.send_stream_message(test_bot, "Denmark") | ||||
|         msg_id_2 = self.send_stream_message(test_bot, "Denmark") | ||||
|  | ||||
|         result = test_delete_message_by_other_user(msg_id) | ||||
|         result = test_delete_message_by_other_user(msg_id_1) | ||||
|         self.assert_json_error(result, "You don't have permission to delete this message") | ||||
|  | ||||
|         result = test_delete_message_by_bot_owner(msg_id) | ||||
|         result = test_delete_message_by_bot_owner(msg_id_1) | ||||
|         self.assert_json_error(result, "You don't have permission to delete this message") | ||||
|  | ||||
|         result = test_delete_message_by_admin(msg_id) | ||||
|         # Admins and moderators can delete any message. | ||||
|         result = test_delete_message_by_moderator(msg_id_1) | ||||
|         self.assert_json_success(result) | ||||
|  | ||||
|         result = test_delete_message_by_admin(msg_id_2) | ||||
|         self.assert_json_success(result) | ||||
|  | ||||
|         msg_id = self.send_stream_message(test_bot, "Denmark") | ||||
|         set_message_deleting_params(CommonMessagePolicyEnum.EVERYONE, "unlimited") | ||||
|         set_message_deleting_params( | ||||
|             administrators_system_group, CommonMessagePolicyEnum.EVERYONE, "unlimited" | ||||
|         ) | ||||
|  | ||||
|         result = test_delete_message_by_other_user(msg_id) | ||||
|         self.assert_json_error(result, "You don't have permission to delete this message") | ||||
| @@ -189,7 +291,9 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|         self.assert_json_success(result) | ||||
|  | ||||
|         msg_id = self.send_stream_message(test_bot, "Denmark") | ||||
|         set_message_deleting_params(CommonMessagePolicyEnum.EVERYONE, 600) | ||||
|         set_message_deleting_params( | ||||
|             administrators_system_group, CommonMessagePolicyEnum.EVERYONE, 600 | ||||
|         ) | ||||
|  | ||||
|         message = Message.objects.get(id=msg_id) | ||||
|         message.date_sent = timezone_now() - timedelta(seconds=700) | ||||
| @@ -206,12 +310,16 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|  | ||||
|         # Check that the bot can also delete the messages sent by them | ||||
|         # depending on the realm permissions for message deletion. | ||||
|         set_message_deleting_params(CommonMessagePolicyEnum.ADMINS_ONLY, 600) | ||||
|         set_message_deleting_params( | ||||
|             administrators_system_group, CommonMessagePolicyEnum.ADMINS_ONLY, 600 | ||||
|         ) | ||||
|         msg_id = self.send_stream_message(test_bot, "Denmark") | ||||
|         result = self.api_delete(test_bot, f"/api/v1/messages/{msg_id}") | ||||
|         self.assert_json_error(result, "You don't have permission to delete this message") | ||||
|  | ||||
|         set_message_deleting_params(CommonMessagePolicyEnum.EVERYONE, 600) | ||||
|         set_message_deleting_params( | ||||
|             administrators_system_group, CommonMessagePolicyEnum.EVERYONE, 600 | ||||
|         ) | ||||
|         message = Message.objects.get(id=msg_id) | ||||
|         message.date_sent = timezone_now() - timedelta(seconds=700) | ||||
|         message.save() | ||||
| @@ -224,6 +332,84 @@ class DeleteMessageTest(ZulipTestCase): | ||||
|         result = self.api_delete(test_bot, f"/api/v1/messages/{msg_id}") | ||||
|         self.assert_json_success(result) | ||||
|  | ||||
|     def test_delete_message_according_to_can_delete_any_message_group(self) -> None: | ||||
|         def check_delete_message_by_sender(sender_name: str, error_msg: str | None = None) -> None: | ||||
|             sender = self.example_user(sender_name) | ||||
|             msg_id = self.send_stream_message(sender, "Verona") | ||||
|             self.login_user(sender) | ||||
|             result = self.client_delete(f"/json/messages/{msg_id}") | ||||
|             if error_msg is None: | ||||
|                 self.assert_json_success(result) | ||||
|             else: | ||||
|                 self.assert_json_error(result, error_msg) | ||||
|  | ||||
|         def check_delete_message_by_other_user( | ||||
|             sender_name: str, other_user_name: str, error_msg: str | None = None | ||||
|         ) -> None: | ||||
|             sender = self.example_user(sender_name) | ||||
|             other_user = self.example_user(other_user_name) | ||||
|             msg_id = self.send_stream_message(sender, "Verona") | ||||
|             self.login_user(other_user) | ||||
|             result = self.client_delete(f"/json/messages/{msg_id}") | ||||
|             if error_msg is None: | ||||
|                 self.assert_json_success(result) | ||||
|             else: | ||||
|                 self.assert_json_error(result, error_msg) | ||||
|  | ||||
|         realm = get_realm("zulip") | ||||
|  | ||||
|         administrators_system_group = NamedUserGroup.objects.get( | ||||
|             name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True | ||||
|         ) | ||||
|         moderators_system_group = NamedUserGroup.objects.get( | ||||
|             name=SystemGroups.MODERATORS, realm=realm, is_system_group=True | ||||
|         ) | ||||
|  | ||||
|         do_change_realm_permission_group_setting( | ||||
|             realm, | ||||
|             "can_delete_any_message_group", | ||||
|             administrators_system_group, | ||||
|             acting_user=None, | ||||
|         ) | ||||
|         do_set_realm_property( | ||||
|             realm, | ||||
|             "delete_own_message_policy", | ||||
|             CommonMessagePolicyEnum.EVERYONE, | ||||
|             acting_user=None, | ||||
|         ) | ||||
|  | ||||
|         # Only admins can delete any message. Everyone else can only delete their | ||||
|         # own message. | ||||
|         check_delete_message_by_sender("shiva") | ||||
|         check_delete_message_by_other_user( | ||||
|             "hamlet", "shiva", "You don't have permission to delete this message" | ||||
|         ) | ||||
|         check_delete_message_by_other_user("hamlet", "iago") | ||||
|  | ||||
|         do_change_realm_permission_group_setting( | ||||
|             realm, | ||||
|             "can_delete_any_message_group", | ||||
|             moderators_system_group, | ||||
|             acting_user=None, | ||||
|         ) | ||||
|         do_set_realm_property( | ||||
|             realm, | ||||
|             "delete_own_message_policy", | ||||
|             CommonMessagePolicyEnum.ADMINS_ONLY, | ||||
|             acting_user=None, | ||||
|         ) | ||||
|  | ||||
|         # Admins and moderators can delete any message. No one else can delete any | ||||
|         # message. | ||||
|         check_delete_message_by_sender( | ||||
|             "cordelia", "You don't have permission to delete this message" | ||||
|         ) | ||||
|         check_delete_message_by_sender("shiva") | ||||
|         check_delete_message_by_other_user("iago", "shiva") | ||||
|         check_delete_message_by_other_user( | ||||
|             "hamlet", "cordelia", "You don't have permission to delete this message" | ||||
|         ) | ||||
|  | ||||
|     def test_delete_message_according_to_delete_own_message_policy(self) -> None: | ||||
|         def check_delete_message_by_sender(sender_name: str, error_msg: str | None = None) -> None: | ||||
|             sender = self.example_user(sender_name) | ||||
|   | ||||
| @@ -148,8 +148,7 @@ def update_message_backend( | ||||
|  | ||||
|  | ||||
| def validate_can_delete_message(user_profile: UserProfile, message: Message) -> None: | ||||
|     if user_profile.is_realm_admin: | ||||
|         # Admin can delete any message, any time. | ||||
|     if user_profile.can_delete_any_message(): | ||||
|         return | ||||
|     if message.sender != user_profile and message.sender.bot_owner_id != user_profile.id: | ||||
|         # Users can only delete messages sent by them or by their bots. | ||||
|   | ||||
| @@ -113,6 +113,7 @@ def update_realm( | ||||
|     inline_image_preview: Json[bool] | None = None, | ||||
|     inline_url_embed_preview: Json[bool] | None = None, | ||||
|     add_custom_emoji_policy: Json[CommonPolicyEnum] | None = None, | ||||
|     can_delete_any_message_group: Json[GroupSettingChangeRequest] | None = None, | ||||
|     delete_own_message_policy: Json[CommonMessagePolicyEnum] | None = None, | ||||
|     message_content_delete_limit_seconds_raw: Annotated[ | ||||
|         Json[int | str] | None, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user