diff --git a/frontend_tests/node_tests/message_edit.js b/frontend_tests/node_tests/message_edit.js index fd7459881e..ffd8e73cb8 100644 --- a/frontend_tests/node_tests/message_edit.js +++ b/frontend_tests/node_tests/message_edit.js @@ -134,6 +134,50 @@ run_test("is_topic_editable", ({override}) => { assert.equal(message_edit.is_topic_editable(message), true); }); +run_test("is_stream_editable", ({override}) => { + const now = new Date(); + const current_timestamp = now / 1000; + + const message = { + sent_by_me: true, + locally_echoed: true, + type: "stream", + }; + page_params.realm_allow_message_editing = true; + override(settings_data, "user_can_move_messages_between_streams", () => true); + page_params.is_admin = true; + + assert.equal(message_edit.is_stream_editable(message), false); + + message.locally_echoed = false; + message.failed_request = true; + assert.equal(message_edit.is_stream_editable(message), false); + + message.failed_request = false; + assert.equal(message_edit.is_stream_editable(message), true); + + page_params.sent_by_me = false; + assert.equal(message_edit.is_stream_editable(message), true); + + override(settings_data, "user_can_move_messages_between_streams", () => false); + assert.equal(message_edit.is_stream_editable(message), false); + + page_params.is_admin = false; + assert.equal(message_edit.is_stream_editable(message), false); + + page_params.realm_move_messages_between_streams_limit_seconds = 259200; + message.timestamp = current_timestamp - 60; + + override(settings_data, "user_can_move_messages_between_streams", () => true); + assert.equal(message_edit.is_stream_editable(message), true); + + message.timestamp = current_timestamp - 600000; + assert.equal(message_edit.is_stream_editable(message), false); + + page_params.is_moderator = true; + assert.equal(message_edit.is_stream_editable(message), true); +}); + run_test("get_deletability", ({override}) => { page_params.is_admin = true; override(settings_data, "user_can_delete_own_message", () => false); diff --git a/static/js/message_edit.js b/static/js/message_edit.js index 4c6d83e07c..a1a31c9480 100644 --- a/static/js/message_edit.js +++ b/static/js/message_edit.js @@ -179,16 +179,41 @@ export function get_deletability(message) { return false; } -export function can_move_message(message) { - if (!message.is_stream) { - return false; - } - +export function is_stream_editable(message, edit_limit_seconds_buffer = 0) { if (!is_message_editable_ignoring_permissions(message)) { return false; } - return is_topic_editable(message) || settings_data.user_can_move_messages_between_streams(); + if (message.type !== "stream") { + return false; + } + + if (!settings_data.user_can_move_messages_between_streams()) { + return false; + } + + // Organization admins and moderators can edit stream indefinitely, + // irrespective of the stream editing deadline, if + // move_messages_between_streams_policy allows them to do so. + if (page_params.is_admin || page_params.is_moderator) { + return true; + } + + if (page_params.realm_move_messages_between_streams_limit_seconds === null) { + // This means no time limit for editing streams. + return true; + } + + return ( + page_params.realm_move_messages_between_streams_limit_seconds + + edit_limit_seconds_buffer + + (message.timestamp - Date.now() / 1000) > + 0 + ); +} + +export function can_move_message(message) { + return is_topic_editable(message) || is_stream_editable(message); } export function stream_and_topic_exist_in_edit_history(message, stream_id, topic) { diff --git a/static/js/stream_popover.js b/static/js/stream_popover.js index 7ded9be566..dfe83cb956 100644 --- a/static/js/stream_popover.js +++ b/static/js/stream_popover.js @@ -412,6 +412,14 @@ export function build_move_topic_to_stream_popover(current_stream_id, topic_name from_message_actions_popover: message !== undefined, }; + // When the modal is opened for moving the whole topic from left sidebar, + // we do not have any message object and so we disable the stream input + // based on the move_messages_between_streams_policy setting. In other + // cases message row, message object is available and thus we check + // the time-based permissions as well in the below if block to enable or + // disable the stream input. + let disable_stream_input = !settings_data.user_can_move_messages_between_streams(); + let modal_heading = $t_html({defaultMessage: "Move topic"}); if (message !== undefined) { modal_heading = $t_html({defaultMessage: "Move messages"}); @@ -423,6 +431,7 @@ export function build_move_topic_to_stream_popover(current_stream_id, topic_name // Though, this will be changed soon as we are going to make topic // edit permission independent of message. args.disable_topic_input = !message_edit.is_topic_editable(message); + disable_stream_input = !message_edit.is_stream_editable(message); } function get_params_from_form() { @@ -563,10 +572,7 @@ export function build_move_topic_to_stream_popover(current_stream_id, topic_name stream_widget.setup(); - $("#select_stream_widget .dropdown-toggle").prop( - "disabled", - !settings_data.user_can_move_messages_between_streams(), - ); + $("#select_stream_widget .dropdown-toggle").prop("disabled", disable_stream_input); update_submit_button_disabled_state(stream_widget.value()); $("#move_topic_modal .inline_topic_edit").on("input", () => { update_submit_button_disabled_state(stream_widget.value());