mirror of https://github.com/zulip/zulip.git
parent
dc938768ca
commit
80969a62e6
|
@ -20,6 +20,13 @@ format used by the Zulip server that they are interacting with.
|
||||||
|
|
||||||
## Changes in Zulip 10.0
|
## 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**
|
**Feature level 280**
|
||||||
|
|
||||||
* `PATCH /realm`, [`POST /register`](/api/register-queue),
|
* `PATCH /realm`, [`POST /register`](/api/register-queue),
|
||||||
|
|
|
@ -157,7 +157,7 @@ IGNORED_PHRASES = [
|
||||||
# Used in message-move-time-limit setting label
|
# Used in message-move-time-limit setting label
|
||||||
r"does not apply to moderators and administrators",
|
r"does not apply to moderators and administrators",
|
||||||
# Used in message-delete-time-limit setting label
|
# 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.
|
# Used as indicator with names for guest users.
|
||||||
r"guest",
|
r"guest",
|
||||||
# Used in pills for deactivated users.
|
# 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**"
|
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
||||||
# entries in the endpoint's documentation in `zulip.yaml`.
|
# 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
|
# 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(),
|
can_invite_users_by_email: settings_data.user_can_invite_users_by_email(),
|
||||||
realm_invite_required: realm.realm_invite_required,
|
realm_invite_required: realm.realm_invite_required,
|
||||||
policy_values: settings_config.common_policy_values,
|
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,
|
realm_delete_own_message_policy: realm.realm_delete_own_message_policy,
|
||||||
DELETE_OWN_MESSAGE_POLICY_ADMINS_ONLY:
|
DELETE_OWN_MESSAGE_POLICY_ADMINS_ONLY:
|
||||||
settings_config.common_message_policy_values.by_admins_only.code,
|
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) {
|
export function get_deletability(message) {
|
||||||
if (current_user.is_admin) {
|
if (settings_data.user_can_delete_any_message()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,6 +204,7 @@ export function dispatch_normal_event(event) {
|
||||||
user_group_edit_policy: noop,
|
user_group_edit_policy: noop,
|
||||||
avatar_changes_disabled: settings_account.update_avatar_change_display,
|
avatar_changes_disabled: settings_account.update_avatar_change_display,
|
||||||
bot_creation_policy: settings_bots.update_bot_permissions_ui,
|
bot_creation_policy: settings_bots.update_bot_permissions_ui,
|
||||||
|
can_delete_any_message_group: noop,
|
||||||
create_multiuse_invite_group: noop,
|
create_multiuse_invite_group: noop,
|
||||||
invite_to_stream_policy: noop,
|
invite_to_stream_policy: noop,
|
||||||
default_code_block_language: 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_public_channel_group", null],
|
||||||
["realm_can_create_private_channel_group", null],
|
["realm_can_create_private_channel_group", null],
|
||||||
["realm_can_create_web_public_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_initiator_group", null],
|
||||||
["realm_direct_message_permission_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_public_channel_group":
|
||||||
case "realm_can_create_private_channel_group":
|
case "realm_can_create_private_channel_group":
|
||||||
case "realm_can_create_web_public_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_initiator_group":
|
||||||
case "realm_direct_message_permission_group":
|
case "realm_direct_message_permission_group":
|
||||||
proposed_val = get_dropdown_list_widget_setting_value($elem);
|
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_private_channel_group",
|
||||||
"can_create_public_channel_group",
|
"can_create_public_channel_group",
|
||||||
"can_create_web_public_channel_group",
|
"can_create_web_public_channel_group",
|
||||||
|
"can_delete_any_message_group",
|
||||||
"direct_message_initiator_group",
|
"direct_message_initiator_group",
|
||||||
"direct_message_permission_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);
|
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 {
|
export function user_can_delete_own_message(): boolean {
|
||||||
return user_has_permission(realm.realm_delete_own_message_policy);
|
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);
|
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
|
// This function is used to check whether the time-limit setting
|
||||||
// should be enabled. The setting is disabled when delete_own_message_policy
|
// should be enabled. The setting is disabled when every user
|
||||||
// is set to 'admins only' as admins can delete messages irrespective of
|
// who is allowed to delete their own messages is also allowed
|
||||||
// time limit.
|
// to delete any message in the organization.
|
||||||
if (setting_value === settings_config.common_message_policy_values.by_admins_only.code) {
|
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 false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_delete_own_message_policy_dropdown(setting_value) {
|
function check_disable_message_delete_limit_setting_dropdown() {
|
||||||
$("#id_realm_delete_own_message_policy").val(setting_value);
|
|
||||||
settings_ui.disable_sub_setting_onchange(
|
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",
|
"id_realm_message_content_delete_limit_seconds",
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
const limit_setting_dropdown_value = settings_components.get_time_limit_dropdown_setting_value(
|
if ($("#id_realm_message_content_delete_limit_minutes").length) {
|
||||||
"realm_message_content_delete_limit_seconds",
|
|
||||||
);
|
|
||||||
if (limit_setting_dropdown_value === "custom_period") {
|
|
||||||
settings_ui.disable_sub_setting_onchange(
|
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",
|
"id_realm_message_content_delete_limit_minutes",
|
||||||
true,
|
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() {
|
function set_msg_delete_limit_dropdown() {
|
||||||
settings_components.set_time_limit_setting("realm_message_content_delete_limit_seconds");
|
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":
|
case "realm_allow_message_editing":
|
||||||
update_message_edit_sub_settings(realm.realm_allow_message_editing);
|
update_message_edit_sub_settings(realm.realm_allow_message_editing);
|
||||||
break;
|
break;
|
||||||
|
case "realm_can_delete_any_message_group":
|
||||||
|
check_disable_message_delete_limit_setting_dropdown();
|
||||||
|
break;
|
||||||
case "realm_delete_own_message_policy":
|
case "realm_delete_own_message_policy":
|
||||||
set_delete_own_message_policy_dropdown(realm.realm_delete_own_message_policy);
|
set_delete_own_message_policy_dropdown(realm.realm_delete_own_message_policy);
|
||||||
break;
|
break;
|
||||||
|
@ -506,6 +541,7 @@ export function discard_realm_property_element_changes(elem) {
|
||||||
case "realm_can_create_public_channel_group":
|
case "realm_can_create_public_channel_group":
|
||||||
case "realm_can_create_private_channel_group":
|
case "realm_can_create_private_channel_group":
|
||||||
case "realm_can_create_web_public_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(
|
settings_components.set_dropdown_list_widget_setting_value(
|
||||||
property_name,
|
property_name,
|
||||||
property_value,
|
property_value,
|
||||||
|
@ -825,6 +861,8 @@ export function set_up_dropdown_widget_for_realm_group_settings() {
|
||||||
if (setting_name === "direct_message_permission_group") {
|
if (setting_name === "direct_message_permission_group") {
|
||||||
dropdown_list_item_click_callback =
|
dropdown_list_item_click_callback =
|
||||||
check_disable_direct_message_initiator_group_dropdown;
|
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(
|
set_up_dropdown_widget(
|
||||||
"realm_" + setting_name,
|
"realm_" + setting_name,
|
||||||
|
|
|
@ -267,6 +267,7 @@ const realm_schema = z.object({
|
||||||
realm_can_create_public_channel_group: z.number(),
|
realm_can_create_public_channel_group: z.number(),
|
||||||
realm_can_create_private_channel_group: z.number(),
|
realm_can_create_private_channel_group: z.number(),
|
||||||
realm_can_create_web_public_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_multiuse_invite_group: z.number(),
|
||||||
realm_create_private_stream_policy: z.number(),
|
realm_create_private_stream_policy: z.number(),
|
||||||
realm_date_created: z.number(),
|
realm_date_created: z.number(),
|
||||||
|
|
|
@ -236,9 +236,11 @@
|
||||||
{{> settings_save_discard_widget section_name="msg-deletion" }}
|
{{> settings_save_discard_widget section_name="msg-deletion" }}
|
||||||
</div>
|
</div>
|
||||||
<div class="inline-block organization-settings-parent">
|
<div class="inline-block organization-settings-parent">
|
||||||
<label for="org-msg-deletion" class="inline-block">
|
{{> ../dropdown_widget_with_label
|
||||||
{{t "Administrators can delete any message." }}
|
widget_name="realm_can_delete_any_message_group"
|
||||||
</label>
|
label=(t 'Who can delete any message')
|
||||||
|
value_type="number" }}
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label for="realm_delete_own_message_policy" class="settings-field-label">
|
<label for="realm_delete_own_message_policy" class="settings-field-label">
|
||||||
{{t "Who can delete their own messages" }}
|
{{t "Who can delete their own messages" }}
|
||||||
|
@ -250,7 +252,7 @@
|
||||||
|
|
||||||
<div class="input-group time-limit-setting">
|
<div class="input-group time-limit-setting">
|
||||||
<label for="realm_message_content_delete_limit_seconds" class="settings-field-label">
|
<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>
|
</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">
|
<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}}
|
{{#each msg_delete_limit_dropdown_values}}
|
||||||
|
|
|
@ -181,6 +181,7 @@ run_test("is_stream_editable", ({override}) => {
|
||||||
|
|
||||||
run_test("get_deletability", ({override}) => {
|
run_test("get_deletability", ({override}) => {
|
||||||
current_user.is_admin = true;
|
current_user.is_admin = true;
|
||||||
|
override(settings_data, "user_can_delete_any_message", () => true);
|
||||||
override(settings_data, "user_can_delete_own_message", () => false);
|
override(settings_data, "user_can_delete_own_message", () => false);
|
||||||
realm.realm_message_content_delete_limit_seconds = null;
|
realm.realm_message_content_delete_limit_seconds = null;
|
||||||
const test_user = {
|
const test_user = {
|
||||||
|
@ -204,11 +205,11 @@ run_test("get_deletability", ({override}) => {
|
||||||
sender_id: 1,
|
sender_id: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Admin can always delete any message
|
// User can delete any message
|
||||||
assert.equal(message_edit.get_deletability(message), true);
|
assert.equal(message_edit.get_deletability(message), true);
|
||||||
|
|
||||||
// Non-admin can't delete message sent by others
|
override(settings_data, "user_can_delete_any_message", () => false);
|
||||||
current_user.is_admin = false;
|
// User can't delete message sent by others
|
||||||
assert.equal(message_edit.get_deletability(message), false);
|
assert.equal(message_edit.get_deletability(message), false);
|
||||||
|
|
||||||
// Locally echoed messages are not deletable
|
// 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 popover_menus_data = zrequire("popover_menus_data");
|
||||||
const people = zrequire("people");
|
const people = zrequire("people");
|
||||||
const compose_state = zrequire("compose_state");
|
const compose_state = zrequire("compose_state");
|
||||||
|
const user_groups = zrequire("user_groups");
|
||||||
|
|
||||||
const noop = function () {};
|
const noop = function () {};
|
||||||
|
|
||||||
|
@ -70,6 +71,15 @@ const me = {
|
||||||
is_guest: false,
|
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:
|
// Helper functions:
|
||||||
function add_initialize_users() {
|
function add_initialize_users() {
|
||||||
// Initialize people
|
// Initialize people
|
||||||
|
@ -139,7 +149,7 @@ function test(label, f) {
|
||||||
test("my_message_all_actions", () => {
|
test("my_message_all_actions", () => {
|
||||||
// Set page parameters.
|
// Set page parameters.
|
||||||
set_page_params_no_edit_restrictions();
|
set_page_params_no_edit_restrictions();
|
||||||
|
realm.realm_can_delete_any_message_group = everyone.id;
|
||||||
// Get message with maximum permissions available
|
// Get message with maximum permissions available
|
||||||
// Initialize message list
|
// Initialize message list
|
||||||
const list = init_message_list();
|
const list = init_message_list();
|
||||||
|
@ -191,7 +201,7 @@ test("my_message_all_actions", () => {
|
||||||
test("not_my_message_view_actions", () => {
|
test("not_my_message_view_actions", () => {
|
||||||
set_page_params_no_edit_restrictions();
|
set_page_params_no_edit_restrictions();
|
||||||
// Get message that is only viewable
|
// Get message that is only viewable
|
||||||
|
realm.realm_can_delete_any_message_group = everyone.id;
|
||||||
const list = init_message_list();
|
const list = init_message_list();
|
||||||
message_lists.set_current(list);
|
message_lists.set_current(list);
|
||||||
|
|
||||||
|
@ -229,7 +239,7 @@ test("not_my_message_view_actions", () => {
|
||||||
|
|
||||||
test("not_my_message_view_source_and_move", () => {
|
test("not_my_message_view_source_and_move", () => {
|
||||||
set_page_params_no_edit_restrictions();
|
set_page_params_no_edit_restrictions();
|
||||||
|
realm.realm_can_delete_any_message_group = everyone.id;
|
||||||
// Get message that is movable with viewable source
|
// Get message that is movable with viewable source
|
||||||
|
|
||||||
const list = init_message_list();
|
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);
|
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(
|
test_message_policy(
|
||||||
"user_can_delete_own_message",
|
"user_can_delete_own_message",
|
||||||
"realm_delete_own_message_policy",
|
"realm_delete_own_message_policy",
|
||||||
|
|
|
@ -505,6 +505,7 @@ function test_discard_changes_button(discard_changes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
test("set_up", ({override, override_rewire}) => {
|
test("set_up", ({override, override_rewire}) => {
|
||||||
|
override_rewire(settings_org, "check_disable_message_delete_limit_setting_dropdown", noop);
|
||||||
realm.realm_available_video_chat_providers = {
|
realm.realm_available_video_chat_providers = {
|
||||||
jitsi_meet: {
|
jitsi_meet: {
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|
|
@ -1050,6 +1050,7 @@ group_setting_update_data_type = DictType(
|
||||||
("can_create_public_channel_group", group_setting_type),
|
("can_create_public_channel_group", group_setting_type),
|
||||||
("can_create_private_channel_group", group_setting_type),
|
("can_create_private_channel_group", group_setting_type),
|
||||||
("can_create_web_public_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_initiator_group", group_setting_type),
|
||||||
("direct_message_permission_group", group_setting_type),
|
("direct_message_permission_group", group_setting_type),
|
||||||
],
|
],
|
||||||
|
|
|
@ -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="+"
|
"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.
|
# Who in the organization is allowed to delete messages they themselves sent.
|
||||||
delete_own_message_policy = models.PositiveSmallIntegerField(
|
delete_own_message_policy = models.PositiveSmallIntegerField(
|
||||||
default=CommonMessagePolicyEnum.EVERYONE
|
default=CommonMessagePolicyEnum.EVERYONE
|
||||||
|
@ -723,6 +728,15 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
|
||||||
default_group_name=SystemGroups.MEMBERS,
|
default_group_name=SystemGroups.MEMBERS,
|
||||||
id_field_name="can_create_private_channel_group_id",
|
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(
|
direct_message_initiator_group=GroupPermissionSetting(
|
||||||
require_system_group=False,
|
require_system_group=False,
|
||||||
allow_internet_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_private_channel_group",
|
||||||
"can_create_public_channel_group",
|
"can_create_public_channel_group",
|
||||||
"can_create_web_public_channel_group",
|
"can_create_web_public_channel_group",
|
||||||
|
"can_delete_any_message_group",
|
||||||
"direct_message_initiator_group",
|
"direct_message_initiator_group",
|
||||||
"direct_message_permission_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_private_channel_group__named_user_group",
|
||||||
"can_create_web_public_channel_group",
|
"can_create_web_public_channel_group",
|
||||||
"can_create_web_public_channel_group__named_user_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",
|
||||||
"direct_message_initiator_group__named_user_group",
|
"direct_message_initiator_group__named_user_group",
|
||||||
"direct_message_permission_group",
|
"direct_message_permission_group",
|
||||||
|
|
|
@ -781,6 +781,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings):
|
||||||
"can_create_private_channel_group",
|
"can_create_private_channel_group",
|
||||||
"can_create_public_channel_group",
|
"can_create_public_channel_group",
|
||||||
"can_create_web_public_channel_group",
|
"can_create_web_public_channel_group",
|
||||||
|
"can_delete_any_message_group",
|
||||||
"create_multiuse_invite_group",
|
"create_multiuse_invite_group",
|
||||||
"delete_own_message_policy",
|
"delete_own_message_policy",
|
||||||
"direct_message_initiator_group",
|
"direct_message_initiator_group",
|
||||||
|
@ -868,6 +869,9 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings):
|
||||||
def can_add_custom_emoji(self) -> bool:
|
def can_add_custom_emoji(self) -> bool:
|
||||||
return self.has_permission("add_custom_emoji_policy")
|
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:
|
def can_delete_own_message(self) -> bool:
|
||||||
return self.has_permission("delete_own_message_policy")
|
return self.has_permission("delete_own_message_policy")
|
||||||
|
|
||||||
|
|
|
@ -4299,6 +4299,15 @@ paths:
|
||||||
**Changes**: New in Zulip 10.0 (feature level 280). Previously
|
**Changes**: New in Zulip 10.0 (feature level 280). Previously
|
||||||
`realm_create_web_public_stream_policy` field used to control
|
`realm_create_web_public_stream_policy` field used to control
|
||||||
the permission to create web-public channels.
|
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:
|
default_code_block_language:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
|
@ -15530,6 +15539,17 @@ paths:
|
||||||
|
|
||||||
Whether this organization is configured to allow users to access
|
Whether this organization is configured to allow users to access
|
||||||
[message edit history](/help/view-a-messages-edit-history).
|
[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:
|
realm_delete_own_message_policy:
|
||||||
type: integer
|
type: integer
|
||||||
description: |
|
description: |
|
||||||
|
|
|
@ -133,6 +133,7 @@ class HomeTest(ZulipTestCase):
|
||||||
"realm_can_create_private_channel_group",
|
"realm_can_create_private_channel_group",
|
||||||
"realm_can_create_public_channel_group",
|
"realm_can_create_public_channel_group",
|
||||||
"realm_can_create_web_public_channel_group",
|
"realm_can_create_web_public_channel_group",
|
||||||
|
"realm_can_delete_any_message_group",
|
||||||
"realm_create_multiuse_invite_group",
|
"realm_create_multiuse_invite_group",
|
||||||
"realm_create_private_stream_policy",
|
"realm_create_private_stream_policy",
|
||||||
"realm_create_public_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 django.utils.timezone import now as timezone_now
|
||||||
|
|
||||||
from zerver.actions.message_delete import do_delete_messages
|
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.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.realms import CommonMessagePolicyEnum, get_realm
|
||||||
from zerver.models.streams import get_stream
|
from zerver.models.streams import get_stream
|
||||||
|
|
||||||
|
@ -29,12 +33,17 @@ class DeleteMessageTest(ZulipTestCase):
|
||||||
|
|
||||||
def test_delete_message_by_user(self) -> None:
|
def test_delete_message_by_user(self) -> None:
|
||||||
def set_message_deleting_params(
|
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:
|
) -> None:
|
||||||
self.login("iago")
|
self.login("iago")
|
||||||
result = self.client_patch(
|
result = self.client_patch(
|
||||||
"/json/realm",
|
"/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(),
|
"delete_own_message_policy": orjson.dumps(delete_own_message_policy).decode(),
|
||||||
"message_content_delete_limit_seconds": orjson.dumps(
|
"message_content_delete_limit_seconds": orjson.dumps(
|
||||||
message_content_delete_limit_seconds
|
message_content_delete_limit_seconds
|
||||||
|
@ -48,7 +57,12 @@ class DeleteMessageTest(ZulipTestCase):
|
||||||
result = self.client_delete(f"/json/messages/{msg_id}")
|
result = self.client_delete(f"/json/messages/{msg_id}")
|
||||||
return result
|
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")
|
self.login("hamlet")
|
||||||
result = self.client_delete(f"/json/messages/{msg_id}")
|
result = self.client_delete(f"/json/messages/{msg_id}")
|
||||||
return result
|
return result
|
||||||
|
@ -58,13 +72,27 @@ class DeleteMessageTest(ZulipTestCase):
|
||||||
result = self.client_delete(f"/json/messages/{msg_id}")
|
result = self.client_delete(f"/json/messages/{msg_id}")
|
||||||
return result
|
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).
|
# 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")
|
hamlet = self.example_user("hamlet")
|
||||||
self.login_user(hamlet)
|
self.login_user(hamlet)
|
||||||
msg_id = self.send_stream_message(hamlet, "Denmark")
|
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")
|
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)
|
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 message deleting is allowed.
|
||||||
# Test if time limit is None(no limit).
|
# 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")
|
msg_id = self.send_stream_message(hamlet, "Denmark")
|
||||||
message = Message.objects.get(id=msg_id)
|
message = Message.objects.get(id=msg_id)
|
||||||
message.date_sent -= timedelta(seconds=600)
|
message.date_sent -= timedelta(seconds=600)
|
||||||
|
@ -84,11 +114,13 @@ class DeleteMessageTest(ZulipTestCase):
|
||||||
result = test_delete_message_by_other_user(msg_id=msg_id)
|
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")
|
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)
|
self.assert_json_success(result)
|
||||||
|
|
||||||
# Test if time limit is non-zero.
|
# 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")
|
msg_id_1 = self.send_stream_message(hamlet, "Denmark")
|
||||||
message = Message.objects.get(id=msg_id_1)
|
message = Message.objects.get(id=msg_id_1)
|
||||||
message.date_sent -= timedelta(seconds=120)
|
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)
|
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")
|
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)
|
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")
|
self.assert_json_error(result, "The time limit for deleting this message has passed")
|
||||||
|
|
||||||
# No limit for admin.
|
# No limit for admin.
|
||||||
|
@ -113,11 +145,53 @@ class DeleteMessageTest(ZulipTestCase):
|
||||||
|
|
||||||
# Test multiple delete requests with no latency issues
|
# Test multiple delete requests with no latency issues
|
||||||
msg_id = self.send_stream_message(hamlet, "Denmark")
|
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)
|
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)")
|
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.
|
# Test handling of 500 error caused by multiple delete requests due to latency.
|
||||||
# see issue #11219.
|
# see issue #11219.
|
||||||
with (
|
with (
|
||||||
|
@ -126,24 +200,30 @@ class DeleteMessageTest(ZulipTestCase):
|
||||||
mock.patch("zerver.views.message_edit.access_message", return_value=(None, None)),
|
mock.patch("zerver.views.message_edit.access_message", return_value=(None, None)),
|
||||||
):
|
):
|
||||||
m.side_effect = IntegrityError()
|
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")
|
self.assert_json_error(result, "Message already deleted")
|
||||||
m.side_effect = Message.DoesNotExist()
|
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")
|
self.assert_json_error(result, "Message already deleted")
|
||||||
|
|
||||||
def test_delete_message_sent_by_bots(self) -> None:
|
def test_delete_message_sent_by_bots(self) -> None:
|
||||||
iago = self.example_user("iago")
|
iago = self.example_user("iago")
|
||||||
|
shiva = self.example_user("shiva")
|
||||||
hamlet = self.example_user("hamlet")
|
hamlet = self.example_user("hamlet")
|
||||||
cordelia = self.example_user("cordelia")
|
cordelia = self.example_user("cordelia")
|
||||||
|
|
||||||
def set_message_deleting_params(
|
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:
|
) -> None:
|
||||||
result = self.api_patch(
|
result = self.api_patch(
|
||||||
iago,
|
iago,
|
||||||
"/api/v1/realm",
|
"/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(),
|
"delete_own_message_policy": orjson.dumps(delete_own_message_policy).decode(),
|
||||||
"message_content_delete_limit_seconds": orjson.dumps(
|
"message_content_delete_limit_seconds": orjson.dumps(
|
||||||
message_content_delete_limit_seconds
|
message_content_delete_limit_seconds
|
||||||
|
@ -156,6 +236,10 @@ class DeleteMessageTest(ZulipTestCase):
|
||||||
result = self.api_delete(iago, f"/api/v1/messages/{msg_id}")
|
result = self.api_delete(iago, f"/api/v1/messages/{msg_id}")
|
||||||
return result
|
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":
|
def test_delete_message_by_bot_owner(msg_id: int) -> "TestHttpResponse":
|
||||||
result = self.api_delete(hamlet, f"/api/v1/messages/{msg_id}")
|
result = self.api_delete(hamlet, f"/api/v1/messages/{msg_id}")
|
||||||
return result
|
return result
|
||||||
|
@ -164,23 +248,41 @@ class DeleteMessageTest(ZulipTestCase):
|
||||||
result = self.api_delete(cordelia, f"/api/v1/messages/{msg_id}")
|
result = self.api_delete(cordelia, f"/api/v1/messages/{msg_id}")
|
||||||
return result
|
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")
|
hamlet = self.example_user("hamlet")
|
||||||
test_bot = self.create_test_bot("test-bot", 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")
|
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")
|
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)
|
self.assert_json_success(result)
|
||||||
|
|
||||||
msg_id = self.send_stream_message(test_bot, "Denmark")
|
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)
|
result = test_delete_message_by_other_user(msg_id)
|
||||||
self.assert_json_error(result, "You don't have permission to delete this message")
|
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)
|
self.assert_json_success(result)
|
||||||
|
|
||||||
msg_id = self.send_stream_message(test_bot, "Denmark")
|
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 = Message.objects.get(id=msg_id)
|
||||||
message.date_sent = timezone_now() - timedelta(seconds=700)
|
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
|
# Check that the bot can also delete the messages sent by them
|
||||||
# depending on the realm permissions for message deletion.
|
# 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")
|
msg_id = self.send_stream_message(test_bot, "Denmark")
|
||||||
result = self.api_delete(test_bot, f"/api/v1/messages/{msg_id}")
|
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")
|
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 = Message.objects.get(id=msg_id)
|
||||||
message.date_sent = timezone_now() - timedelta(seconds=700)
|
message.date_sent = timezone_now() - timedelta(seconds=700)
|
||||||
message.save()
|
message.save()
|
||||||
|
@ -224,6 +332,84 @@ class DeleteMessageTest(ZulipTestCase):
|
||||||
result = self.api_delete(test_bot, f"/api/v1/messages/{msg_id}")
|
result = self.api_delete(test_bot, f"/api/v1/messages/{msg_id}")
|
||||||
self.assert_json_success(result)
|
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 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:
|
def check_delete_message_by_sender(sender_name: str, error_msg: str | None = None) -> None:
|
||||||
sender = self.example_user(sender_name)
|
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:
|
def validate_can_delete_message(user_profile: UserProfile, message: Message) -> None:
|
||||||
if user_profile.is_realm_admin:
|
if user_profile.can_delete_any_message():
|
||||||
# Admin can delete any message, any time.
|
|
||||||
return
|
return
|
||||||
if message.sender != user_profile and message.sender.bot_owner_id != user_profile.id:
|
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.
|
# 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_image_preview: Json[bool] | None = None,
|
||||||
inline_url_embed_preview: Json[bool] | None = None,
|
inline_url_embed_preview: Json[bool] | None = None,
|
||||||
add_custom_emoji_policy: Json[CommonPolicyEnum] | 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,
|
delete_own_message_policy: Json[CommonMessagePolicyEnum] | None = None,
|
||||||
message_content_delete_limit_seconds_raw: Annotated[
|
message_content_delete_limit_seconds_raw: Annotated[
|
||||||
Json[int | str] | None,
|
Json[int | str] | None,
|
||||||
|
|
Loading…
Reference in New Issue