diff --git a/frontend_tests/node_tests/message_edit.js b/frontend_tests/node_tests/message_edit.js
index 6c87d639f3..31a59f1609 100644
--- a/frontend_tests/node_tests/message_edit.js
+++ b/frontend_tests/node_tests/message_edit.js
@@ -116,9 +116,13 @@ run_test("is_topic_editable", ({override}) => {
message.sent_by_me = false;
page_params.is_admin = true;
- assert.equal(message_edit.is_topic_editable(message), true);
+ override(settings_data, "user_can_edit_topic_of_any_message", () => false);
+ assert.equal(message_edit.is_topic_editable(message), false);
page_params.is_admin = false;
+ message.sent_by_me = false;
+ assert.equal(message_edit.is_topic_editable(message), false);
+
message.topic = "translated: (no topic)";
assert.equal(message_edit.is_topic_editable(message), true);
diff --git a/frontend_tests/node_tests/settings_data.js b/frontend_tests/node_tests/settings_data.js
index 103f4d361b..eb01afba58 100644
--- a/frontend_tests/node_tests/settings_data.js
+++ b/frontend_tests/node_tests/settings_data.js
@@ -248,6 +248,14 @@ test_message_policy(
settings_data.user_can_edit_topic_of_any_message,
);
+run_test("user_can_edit_topic_of_any_message_nobody_case", () => {
+ page_params.is_admin = true;
+ page_params.is_guest = false;
+ page_params.realm_edit_topic_policy =
+ settings_config.edit_topic_policy_values.nobody.code;
+ assert.equal(settings_data.user_can_edit_topic_of_any_message(), false);
+});
+
test_message_policy(
"user_can_delete_own_message",
"realm_delete_own_message_policy",
diff --git a/static/js/message_edit.js b/static/js/message_edit.js
index ec39323211..9cfc9b2365 100644
--- a/static/js/message_edit.js
+++ b/static/js/message_edit.js
@@ -65,10 +65,8 @@ export function is_topic_editable(message, edit_limit_seconds_buffer = 0) {
// If message editing is disabled, so is topic editing.
return false;
}
- // Organization admins and message senders can edit message topics indefinitely.
- if (page_params.is_admin) {
- return true;
- }
+
+ // message senders can edit message topics indefinitely.
if (message.sent_by_me) {
return true;
}
@@ -81,9 +79,10 @@ export function is_topic_editable(message, edit_limit_seconds_buffer = 0) {
return false;
}
- // moderators can edit the topic if edit_topic_policy allows them to do so,
- // irrespective of the topic editing deadline.
- if (page_params.is_moderator) {
+ // Organization admins and moderators can edit message topics indefinitely,
+ // irrespective of the topic editing deadline, if edit_topic_policy allows
+ // them to do so.
+ if (page_params.is_admin || page_params.is_moderator) {
return true;
}
diff --git a/static/js/settings_config.ts b/static/js/settings_config.ts
index 92740c8f3f..9c83200ab4 100644
--- a/static/js/settings_config.ts
+++ b/static/js/settings_config.ts
@@ -290,6 +290,15 @@ export const common_message_policy_values = {
},
};
+export const edit_topic_policy_values = {
+ ...common_message_policy_values,
+ nobody: {
+ order: 6,
+ code: 6,
+ description: $t({defaultMessage: "Nobody"}),
+ },
+};
+
export const time_limit_dropdown_values = [
{
text: $t({defaultMessage: "Any time"}),
diff --git a/static/js/settings_org.js b/static/js/settings_org.js
index fe520f0329..2d5fd73cbf 100644
--- a/static/js/settings_org.js
+++ b/static/js/settings_org.js
@@ -114,6 +114,9 @@ export function get_organization_settings_options() {
options.invite_to_realm_policy_values = get_sorted_options_list(
settings_config.invite_to_realm_policy_values,
);
+ options.edit_topic_policy_values = get_sorted_options_list(
+ settings_config.edit_topic_policy_values,
+ );
return options;
}
diff --git a/static/templates/settings/organization_permissions_admin.hbs b/static/templates/settings/organization_permissions_admin.hbs
index 917217c57f..34951cf050 100644
--- a/static/templates/settings/organization_permissions_admin.hbs
+++ b/static/templates/settings/organization_permissions_admin.hbs
@@ -177,7 +177,7 @@
diff --git a/templates/zerver/api/changelog.md b/templates/zerver/api/changelog.md
index 784d804c40..a8b680b817 100644
--- a/templates/zerver/api/changelog.md
+++ b/templates/zerver/api/changelog.md
@@ -20,6 +20,12 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 7.0
+**Feature level 159**
+
+* [`POST /register`](/api/register-queue), [`GET /events`](/api/get-events),
+ `PATCH /realm`: Nobody added as an option for the realm setting
+ `edit_topic_policy`.
+
Feature levels 157-158 are reserved for future use in 6.x maintenance
releases.
diff --git a/zerver/models.py b/zerver/models.py
index 80f88601d8..3a7384fcf9 100644
--- a/zerver/models.py
+++ b/zerver/models.py
@@ -365,6 +365,15 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
POLICY_NOBODY,
]
+ EDIT_TOPIC_POLICY_TYPES = [
+ POLICY_MEMBERS_ONLY,
+ POLICY_ADMINS_ONLY,
+ POLICY_FULL_MEMBERS_ONLY,
+ POLICY_MODERATORS_ONLY,
+ POLICY_EVERYONE,
+ POLICY_NOBODY,
+ ]
+
DEFAULT_COMMUNITY_TOPIC_EDITING_LIMIT_SECONDS = 259200
# Who in the organization is allowed to add custom emojis.
diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml
index eb237223a3..14e188f7b8 100644
--- a/zerver/openapi/zulip.yaml
+++ b/zerver/openapi/zulip.yaml
@@ -3890,9 +3890,11 @@ paths:
- 3 = [full members][calc-full-member] only
- 4 = moderators only
- 5 = everyone
+ - 6 = nobody
**Changes**: New in Zulip 5.0 (feature level 75), replacing the
- previous `allow_community_topic_editing` boolean.
+ previous `allow_community_topic_editing` boolean. Nobody added
+ as an option in Zulip 7.0 (feature level 159).
[permission-level]: /api/roles-and-permissions#permission-levels
[calc-full-member]: /api/roles-and-permissions#determining-if-a-user-is-a-full-member
@@ -12155,9 +12157,11 @@ paths:
- 3 = [full members][calc-full-member] only
- 4 = moderators only
- 5 = everyone
+ - 6 = nobody
**Changes**: New in Zulip 5.0 (feature level 75), replacing the
- previous `allow_community_topic_editing` boolean.
+ previous `allow_community_topic_editing` boolean. Nobody added as
+ an option in Zulip 7.0 (feature level 159).
[permission-level]: /api/roles-and-permissions#permission-levels
[calc-full-member]: /api/roles-and-permissions#determining-if-a-user-is-a-full-member
diff --git a/zerver/tests/test_message_edit.py b/zerver/tests/test_message_edit.py
index 87621ce598..a0c2988ebc 100644
--- a/zerver/tests/test_message_edit.py
+++ b/zerver/tests/test_message_edit.py
@@ -1153,6 +1153,15 @@ class EditMessageTest(EditMessageTestCase):
)
do_edit_message_assert_success(id_, "E", "iago")
+ # even owners and admins cannot edit the topics of messages
+ set_message_editing_params(True, "unlimited", Realm.POLICY_NOBODY)
+ do_edit_message_assert_error(
+ id_, "H", "You don't have permission to edit this message", "desdemona"
+ )
+ do_edit_message_assert_error(
+ id_, "H", "You don't have permission to edit this message", "iago"
+ )
+
# users cannot edit topics if allow_message_editing is False
set_message_editing_params(False, "unlimited", Realm.POLICY_EVERYONE)
do_edit_message_assert_error(
diff --git a/zerver/tests/test_realm.py b/zerver/tests/test_realm.py
index 4149cfd978..187a8e4dbf 100644
--- a/zerver/tests/test_realm.py
+++ b/zerver/tests/test_realm.py
@@ -1176,7 +1176,7 @@ class RealmAPITest(ZulipTestCase):
move_messages_between_streams_policy=Realm.COMMON_POLICY_TYPES,
add_custom_emoji_policy=Realm.COMMON_POLICY_TYPES,
delete_own_message_policy=Realm.COMMON_MESSAGE_POLICY_TYPES,
- edit_topic_policy=Realm.COMMON_MESSAGE_POLICY_TYPES,
+ edit_topic_policy=Realm.EDIT_TOPIC_POLICY_TYPES,
message_content_edit_limit_seconds=[1000, 1100, 1200],
)
diff --git a/zerver/views/realm.py b/zerver/views/realm.py
index 1375c57f78..54e73be14b 100644
--- a/zerver/views/realm.py
+++ b/zerver/views/realm.py
@@ -78,7 +78,7 @@ def update_realm(
),
allow_message_editing: Optional[bool] = REQ(json_validator=check_bool, default=None),
edit_topic_policy: Optional[int] = REQ(
- json_validator=check_int_in(Realm.COMMON_MESSAGE_POLICY_TYPES), default=None
+ json_validator=check_int_in(Realm.EDIT_TOPIC_POLICY_TYPES), default=None
),
mandatory_topics: Optional[bool] = REQ(json_validator=check_bool, default=None),
message_content_edit_limit_seconds_raw: Optional[Union[int, str]] = REQ(