mirror of
https://github.com/zulip/zulip.git
synced 2025-11-07 15:33:30 +00:00
Add realm setting to time-limit editing of message content.
This is controlled through the admin tab and a new field in the Realms table. Notes: * The admin tab setting takes a value in minutes, whereas the backend stores it in seconds. * This setting is unused when allow_message_editing is false. * There is some generosity in how the limit is enforced. For instance, if the user sees the hovering edit button, we ensure they have at least 5 seconds to click it, and if the user gets to the message edit form, we ensure they have at least 10 seconds to make the edit, by relaxing the limit. * This commit also includes a countdown timer in the message edit form. Resolves #903.
This commit is contained in:
@@ -312,7 +312,7 @@ casper.waitForSelector('input[type="checkbox"][id="id_realm_allow_message_editin
|
||||
casper.click('input[type="checkbox"][id="id_realm_allow_message_editing"]');
|
||||
casper.click('form.admin-realm-form input.btn');
|
||||
casper.waitUntilVisible('#admin-realm-message-editing-status', function () {
|
||||
casper.test.assertSelectorHasText('#admin-realm-message-editing-status', 'Users can now edit the content and topics of all their past messages!');
|
||||
casper.test.assertSelectorHasText('#admin-realm-message-editing-status', 'Users can now edit topics for all their messages, and the content of messages which are less than 10 minutes old.');
|
||||
casper.test.assertEval(function () {
|
||||
return document.querySelector('input[type="checkbox"][id="id_realm_allow_message_editing"]').checked;
|
||||
}, 'Allow message editing Setting re-activated');
|
||||
@@ -353,6 +353,85 @@ casper.then(function () {
|
||||
});
|
||||
});
|
||||
|
||||
// go back to admin page
|
||||
casper.then(function () {
|
||||
casper.test.info('Administration page');
|
||||
casper.click('a[href^="#administration"]');
|
||||
casper.test.assertUrlMatch(/^http:\/\/[^\/]+\/#administration/, 'URL suggests we are on administration page');
|
||||
casper.test.assertExists('#administration.tab-pane.active', 'Administration page is active');
|
||||
});
|
||||
|
||||
casper.waitForSelector('form.admin-realm-form input.btn', function () {
|
||||
// deactivate message editing
|
||||
casper.waitForSelector('input[type="checkbox"][id="id_realm_allow_message_editing"]', function () {
|
||||
casper.evaluate(function () {
|
||||
$('input[type="text"][id="id_realm_message_content_edit_limit_minutes"]').val('4');
|
||||
});
|
||||
casper.click('input[type="checkbox"][id="id_realm_allow_message_editing"]');
|
||||
casper.click('form.admin-realm-form input.btn');
|
||||
casper.waitUntilVisible('#admin-realm-message-editing-status', function () {
|
||||
casper.test.assertSelectorHasText('#admin-realm-message-editing-status', 'Users can no longer edit their past messages!');
|
||||
casper.test.assertEval(function () {
|
||||
return !(document.querySelector('input[type="checkbox"][id="id_realm_allow_message_editing"]').checked);
|
||||
}, 'Allow message editing Setting de-activated');
|
||||
casper.test.assertEval(function () {
|
||||
return $('input[type="text"][id="id_realm_message_content_edit_limit_minutes"]').val() === '4';
|
||||
}, 'Message content edit limit now 4');
|
||||
});
|
||||
});
|
||||
|
||||
// allow message editing again, and check that the old edit limit is still there
|
||||
casper.waitForSelector('input[type="checkbox"][id="id_realm_allow_message_editing"]', function () {
|
||||
casper.click('input[type="checkbox"][id="id_realm_allow_message_editing"]');
|
||||
casper.click('form.admin-realm-form input.btn');
|
||||
casper.waitUntilVisible('#admin-realm-message-editing-status', function () {
|
||||
casper.test.assertSelectorHasText('#admin-realm-message-editing-status', 'Users can now edit topics for all their messages, and the content of messages which are less than 4 minutes old.');
|
||||
casper.test.assertEval(function () {
|
||||
return document.querySelector('input[type="checkbox"][id="id_realm_allow_message_editing"]').checked;
|
||||
}, 'Allow message editing Setting activated');
|
||||
casper.test.assertEval(function () {
|
||||
return $('input[type="text"][id="id_realm_message_content_edit_limit_minutes"]').val() === '4';
|
||||
}, 'Message content edit limit still 4');
|
||||
});
|
||||
});
|
||||
|
||||
// allow arbitrary message editing
|
||||
casper.waitForSelector('input[type="checkbox"][id="id_realm_allow_message_editing"]', function () {
|
||||
casper.evaluate(function () {
|
||||
$('input[type="text"][id="id_realm_message_content_edit_limit_minutes"]').val('0');
|
||||
});
|
||||
casper.click('form.admin-realm-form input.btn');
|
||||
casper.waitUntilVisible('#admin-realm-message-editing-status', function () {
|
||||
casper.test.assertSelectorHasText('#admin-realm-message-editing-status', 'Users can now edit the content and topics of all their past messages!');
|
||||
casper.test.assertEval(function () {
|
||||
return document.querySelector('input[type="checkbox"][id="id_realm_allow_message_editing"]').checked;
|
||||
}, 'Allow message editing Setting still activated');
|
||||
casper.test.assertEval(function () {
|
||||
return $('input[type="text"][id="id_realm_message_content_edit_limit_minutes"]').val() === '0';
|
||||
}, 'Message content edit limit is 0');
|
||||
});
|
||||
});
|
||||
|
||||
// disallow message editing, with illegal edit limit value. should be fixed by admin.js
|
||||
casper.waitForSelector('input[type="checkbox"][id="id_realm_allow_message_editing"]', function () {
|
||||
casper.evaluate(function () {
|
||||
$('input[type="text"][id="id_realm_message_content_edit_limit_minutes"]').val('moo');
|
||||
});
|
||||
casper.click('input[type="checkbox"][id="id_realm_allow_message_editing"]');
|
||||
casper.click('form.admin-realm-form input.btn');
|
||||
casper.waitUntilVisible('#admin-realm-message-editing-status', function () {
|
||||
casper.test.assertSelectorHasText('#admin-realm-message-editing-status', 'Users can no longer edit their past messages!');
|
||||
casper.test.assertEval(function () {
|
||||
return !(document.querySelector('input[type="checkbox"][id="id_realm_allow_message_editing"]').checked);
|
||||
}, 'Allow message editing Setting de-activated');
|
||||
casper.test.assertEval(function () {
|
||||
return $('input[type="text"][id="id_realm_message_content_edit_limit_minutes"]').val() === '10';
|
||||
}, 'Message content edit limit has been reset to its default');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
common.then_log_out();
|
||||
|
||||
casper.run(function () {
|
||||
|
||||
@@ -143,7 +143,8 @@ function _setup_page() {
|
||||
realm_invite_required: page_params.realm_invite_required,
|
||||
realm_invite_by_admins_only: page_params.realm_invite_by_admins_only,
|
||||
realm_create_stream_by_admins_only: page_params.realm_create_stream_by_admins_only,
|
||||
realm_allow_message_editing: page_params.realm_allow_message_editing
|
||||
realm_allow_message_editing: page_params.realm_allow_message_editing,
|
||||
realm_message_content_edit_limit_minutes: Math.ceil(page_params.realm_message_content_edit_limit_seconds / 60)
|
||||
};
|
||||
var admin_tab = templates.render('admin_tab', options);
|
||||
$("#administration").html(admin_tab);
|
||||
@@ -340,6 +341,16 @@ function _setup_page() {
|
||||
}
|
||||
});
|
||||
|
||||
$("#id_realm_allow_message_editing").change(function () {
|
||||
if (this.checked) {
|
||||
$("#id_realm_message_content_edit_limit_minutes").removeAttr("disabled");
|
||||
$("#id_realm_message_content_edit_limit_minutes_label").removeClass("control-label-disabled");
|
||||
} else {
|
||||
$("#id_realm_message_content_edit_limit_minutes").attr("disabled", true);
|
||||
$("#id_realm_message_content_edit_limit_minutes_label").addClass("control-label-disabled");
|
||||
}
|
||||
});
|
||||
|
||||
$(".administration").on("submit", "form.admin-realm-form", function (e) {
|
||||
var name_status = $("#admin-realm-name-status").expectOne();
|
||||
var restricted_to_domain_status = $("#admin-realm-restricted-to-domain-status").expectOne();
|
||||
@@ -363,6 +374,19 @@ function _setup_page() {
|
||||
var new_invite_by_admins_only = $("#id_realm_invite_by_admins_only").prop("checked");
|
||||
var new_create_stream_by_admins_only = $("#id_realm_create_stream_by_admins_only").prop("checked");
|
||||
var new_allow_message_editing = $("#id_realm_allow_message_editing").prop("checked");
|
||||
var new_message_content_edit_limit_minutes = $("#id_realm_message_content_edit_limit_minutes").val();
|
||||
|
||||
// If allow_message_editing is unchecked, message_content_edit_limit_minutes
|
||||
// is irrelevant. Hence if allow_message_editing is unchecked, and
|
||||
// message_content_edit_limit_minutes is poorly formed, we set the latter to
|
||||
// a default value to prevent the server from returning an error.
|
||||
if (!new_allow_message_editing) {
|
||||
if ((parseInt(new_message_content_edit_limit_minutes, 10).toString() !==
|
||||
new_message_content_edit_limit_minutes) ||
|
||||
new_message_content_edit_limit_minutes < 0) {
|
||||
new_message_content_edit_limit_minutes = 10; // Realm.DEFAULT_MESSAGE_CONTENT_EDIT_LIMIT_SECONDS / 60
|
||||
}
|
||||
}
|
||||
|
||||
var url = "/json/realm";
|
||||
var data = {
|
||||
@@ -371,7 +395,8 @@ function _setup_page() {
|
||||
invite_required: JSON.stringify(new_invite),
|
||||
invite_by_admins_only: JSON.stringify(new_invite_by_admins_only),
|
||||
create_stream_by_admins_only: JSON.stringify(new_create_stream_by_admins_only),
|
||||
allow_message_editing: JSON.stringify(new_allow_message_editing)
|
||||
allow_message_editing: JSON.stringify(new_allow_message_editing),
|
||||
message_content_edit_limit_seconds: JSON.stringify(parseInt(new_message_content_edit_limit_minutes, 10) * 60)
|
||||
};
|
||||
|
||||
channel.patch({
|
||||
@@ -410,11 +435,23 @@ function _setup_page() {
|
||||
}
|
||||
}
|
||||
if (response_data.allow_message_editing !== undefined) {
|
||||
// We expect message_content_edit_limit_seconds was sent in the
|
||||
// response as well
|
||||
var data_message_content_edit_limit_minutes = Math.ceil(response_data.message_content_edit_limit_seconds / 60);
|
||||
if (response_data.allow_message_editing) {
|
||||
if (response_data.message_content_edit_limit_seconds > 0) {
|
||||
ui.report_success(i18n.t("Users can now edit topics for all their messages, and the content of messages which are less than __num_minutes__ minutes old.",
|
||||
{'num_minutes' : data_message_content_edit_limit_minutes}),
|
||||
message_editing_status);
|
||||
} else {
|
||||
ui.report_success(i18n.t("Users can now edit the content and topics of all their past messages!"), message_editing_status);
|
||||
}
|
||||
} else {
|
||||
ui.report_success(i18n.t("Users can no longer edit their past messages!"), message_editing_status);
|
||||
}
|
||||
// message_content_edit_limit_seconds could have been changed earlier
|
||||
// in this function, so update the field just in case
|
||||
$("#id_realm_message_content_edit_limit_minutes").val(data_message_content_edit_limit_minutes);
|
||||
}
|
||||
},
|
||||
error: function (xhr, error) {
|
||||
|
||||
@@ -86,6 +86,17 @@ function handle_edit_keydown(from_topic_edited_only, e) {
|
||||
}
|
||||
}
|
||||
|
||||
function timer_text(seconds_left) {
|
||||
var minutes = Math.floor(seconds_left / 60);
|
||||
var seconds = seconds_left % 60;
|
||||
if (minutes >= 1) {
|
||||
return i18n.t("__minutes__ min to edit", {'minutes': minutes.toString()});
|
||||
} else if (seconds_left >= 10) {
|
||||
return i18n.t("__seconds__ sec to edit", {'seconds': (seconds - seconds % 5).toString()});
|
||||
}
|
||||
return i18n.t("__seconds__ sec to edit", {'seconds': seconds.toString()});
|
||||
}
|
||||
|
||||
function edit_message (row, raw_content) {
|
||||
var content_top = row.find('.message_content')[0]
|
||||
.getBoundingClientRect().top;
|
||||
@@ -95,7 +106,8 @@ function edit_message (row, raw_content) {
|
||||
var form = $(templates.render('message_edit_form',
|
||||
{is_stream: message.is_stream,
|
||||
topic: message.subject,
|
||||
content: raw_content}));
|
||||
content: raw_content,
|
||||
minutes_to_edit: Math.floor(page_params.realm_message_content_edit_limit_seconds / 60)}));
|
||||
|
||||
var edit_obj = {form: form, raw_content: raw_content};
|
||||
var original_topic = message.subject;
|
||||
@@ -104,8 +116,69 @@ function edit_message (row, raw_content) {
|
||||
|
||||
form.keydown(_.partial(handle_edit_keydown, false));
|
||||
|
||||
// We potentially got to this function by clicking a button that implied the
|
||||
// user would be able to edit their message. Give a little bit of buffer in
|
||||
// case the button has been around for a bit, e.g. we show the
|
||||
// edit_content_button (hovering pencil icon) as long as the user would have
|
||||
// been able to click it at the time the mouse entered the message_row. Also
|
||||
// a buffer in case their computer is slow, or stalled for a second, etc
|
||||
// If you change this number also change edit_limit_buffer in
|
||||
// zerver.views.messages.update_message_backend
|
||||
var seconds_left_buffer = 5;
|
||||
|
||||
var now = new XDate();
|
||||
var seconds_left = page_params.realm_message_content_edit_limit_seconds +
|
||||
now.diffSeconds(message.timestamp * 1000);
|
||||
var can_edit_content = (page_params.realm_message_content_edit_limit_seconds === 0) ||
|
||||
(seconds_left + seconds_left_buffer > 0);
|
||||
if (!can_edit_content) {
|
||||
row.find('textarea.message_edit_content').attr("disabled","disabled");
|
||||
}
|
||||
|
||||
// If we allow editing at all, give them at least 10 seconds to do it.
|
||||
// If you change this number also change edit_limit_buffer in
|
||||
// zerver.views.messages.update_message_backend
|
||||
var min_seconds_to_edit = 10;
|
||||
seconds_left = Math.floor(Math.max(seconds_left, min_seconds_to_edit));
|
||||
|
||||
// Add a visual timer if appropriate
|
||||
if (can_edit_content && page_params.realm_message_content_edit_limit_seconds > 0) {
|
||||
row.find('.message-edit-timer-control-group').show();
|
||||
$('#message_edit_tooltip').tooltip({ animation: false, placement: 'left',
|
||||
template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner message-edit-tooltip-inner"></div></div>'});
|
||||
// I believe these need to be defined outside the countdown_timer, since
|
||||
// row just refers to something like the currently selected message, and
|
||||
// can change out from under us
|
||||
var message_content_row = row.find('textarea.message_edit_content');
|
||||
var message_topic_row, message_topic_propagate_row;
|
||||
if (message.is_stream) {
|
||||
message_topic_row = row.find('input.message_edit_topic');
|
||||
message_topic_propagate_row = row.find('select.message_edit_topic_propagate');
|
||||
}
|
||||
var message_save_row = row.find('button.message_edit_save');
|
||||
var timer_row = row.find('.message_edit_countdown_timer');
|
||||
// Do this right away, rather than waiting for the timer to do its first update,
|
||||
// since otherwise there is a noticeable lag
|
||||
timer_row.text(timer_text(seconds_left));
|
||||
var countdown_timer = setInterval(function () {
|
||||
if (--seconds_left <= 0) {
|
||||
clearInterval(countdown_timer);
|
||||
message_content_row.attr("disabled","disabled");
|
||||
if (message.is_stream) {
|
||||
message_topic_row.attr("disabled","disabled");
|
||||
message_topic_propagate_row.hide();
|
||||
}
|
||||
message_save_row.addClass("disabled");
|
||||
timer_row.text(i18n.t("Time's up!"));
|
||||
} else {
|
||||
timer_row.text(timer_text(seconds_left));
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
currently_editing_messages[message.id] = edit_obj;
|
||||
if (message.type === 'stream' && message.subject === compose.empty_subject_placeholder()) {
|
||||
if ((message.type === 'stream' && message.subject === compose.empty_subject_placeholder()) ||
|
||||
!can_edit_content) {
|
||||
edit_row.find(".message_edit_topic").focus();
|
||||
} else {
|
||||
edit_row.find(".message_edit_content").focus();
|
||||
@@ -128,6 +201,7 @@ function edit_message (row, raw_content) {
|
||||
}
|
||||
|
||||
composebox_typeahead.initialize_compose_typeahead("#message_edit_content", {emoji: true});
|
||||
|
||||
}
|
||||
|
||||
function start_edit_maintaining_scroll(row, content) {
|
||||
|
||||
@@ -118,7 +118,12 @@ function message_hover(message_row) {
|
||||
message = current_msg_list.get(rows.id(message_row));
|
||||
message_unhover();
|
||||
message_row.addClass('message_hovered');
|
||||
if (message && message.sent_by_me && !message.status_message && page_params.realm_allow_message_editing) {
|
||||
var now = new XDate();
|
||||
if (message && message.sent_by_me && !message.status_message &&
|
||||
page_params.realm_allow_message_editing &&
|
||||
(page_params.realm_message_content_edit_limit_seconds === 0 ||
|
||||
page_params.realm_message_content_edit_limit_seconds + now.diffSeconds(message.timestamp * 1000) > 0))
|
||||
{
|
||||
message_row.find('.message_content').find('p:last').append(edit_content_button);
|
||||
}
|
||||
current_message_hover = message_row;
|
||||
|
||||
@@ -345,6 +345,11 @@ form.admin-realm .control-label {
|
||||
color: #d3d3d3;
|
||||
}
|
||||
|
||||
.admin-realm-message-content-edit-limit-minutes {
|
||||
width: 5ch;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#settings-status {
|
||||
text-align: center;
|
||||
width: 50%;
|
||||
|
||||
@@ -321,6 +321,7 @@ a:hover code {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#message_edit_tooltip,
|
||||
#streams_inline_cog,
|
||||
#streams_filter_icon {
|
||||
float: right;
|
||||
@@ -331,11 +332,19 @@ a:hover code {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
#message_edit_tooltip:hover,
|
||||
#streams_inline_cog:hover,
|
||||
#streams_filter_icon:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.message-edit-tooltip-inner {
|
||||
width: 200px;
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
top: -18px;
|
||||
}
|
||||
|
||||
#streams_header a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
@@ -1406,6 +1415,22 @@ div.focused_table {
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.message_edit_countdown_timer {
|
||||
text-align: right;
|
||||
display: inline;
|
||||
color: #a1a1a1;
|
||||
}
|
||||
|
||||
.message_edit_tooltip {
|
||||
display: inline;
|
||||
color: #a1a1a1;
|
||||
}
|
||||
|
||||
.message-edit-timer-control-group {
|
||||
float: right;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.topic_edit {
|
||||
display: none;
|
||||
line-height: 22px;
|
||||
|
||||
@@ -101,6 +101,21 @@
|
||||
{{#if realm_allow_message_editing}}checked="checked"{{/if}} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label for="realm_message_content_edit_limit_minutes"
|
||||
id="id_realm_message_content_edit_limit_minutes_label"
|
||||
title="{{t 'If non-zero, users can edit their message for this many minutes after it is sent. If zero, users can edit all their past messages.' }}"
|
||||
class="control-label{{#unless realm_allow_message_editing}} control-label-disabled{{/unless}}">
|
||||
{{t 'Message edit limit in minutes (0 for no limit)' }}
|
||||
</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="id_realm_message_content_edit_limit_minutes"
|
||||
name="realm_message_content_edit_limit_minutes"
|
||||
class="admin-realm-message-content-edit-limit-minutes"
|
||||
value="{{ realm_message_content_edit_limit_minutes }}"
|
||||
{{#unless realm_allow_message_editing}}disabled="disabled"{{/unless}} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls organization-submission">
|
||||
<input type="submit" class="btn btn-big btn-primary" value="{{t 'Save changes' }}" />
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls edit-controls">
|
||||
<textarea class="message_edit_content" id="message_edit_content">{{content}}</textarea>
|
||||
@@ -23,6 +24,12 @@
|
||||
<div class="controls edit-controls">
|
||||
<button type="button" class="message_edit_save btn btn-primary btn-small">{{t "Save" }}</button>
|
||||
<button type="button" class="message_edit_cancel btn btn-default btn-small">{{t "Cancel" }}</button>
|
||||
<div class="message-edit-timer-control-group">
|
||||
<span class="message_edit_countdown_timer"></span>
|
||||
<span><i id="message_edit_tooltip" class="message_edit_tooltip icon-vector-question-sign" data-toggle="tooltip"
|
||||
title="Message content can only be edited for {{minutes_to_edit}} minutes after it is sent."></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-error edit_error hide"></div>
|
||||
|
||||
@@ -412,15 +412,17 @@ def do_set_realm_create_stream_by_admins_only(realm, create_stream_by_admins_onl
|
||||
)
|
||||
send_event(event, active_user_ids(realm))
|
||||
|
||||
def do_set_realm_message_editing(realm, allow_message_editing):
|
||||
# type: (Realm, bool) -> None
|
||||
def do_set_realm_message_editing(realm, allow_message_editing, message_content_edit_limit_seconds):
|
||||
# type: (Realm, bool, int) -> None
|
||||
realm.allow_message_editing = allow_message_editing
|
||||
realm.save(update_fields=['allow_message_editing'])
|
||||
realm.message_content_edit_limit_seconds = message_content_edit_limit_seconds
|
||||
realm.save(update_fields=['allow_message_editing', 'message_content_edit_limit_seconds'])
|
||||
event = dict(
|
||||
type="realm",
|
||||
op="update_dict",
|
||||
property="default",
|
||||
data=dict(allow_message_editing=allow_message_editing),
|
||||
data=dict(allow_message_editing=allow_message_editing,
|
||||
message_content_edit_limit_seconds=message_content_edit_limit_seconds),
|
||||
)
|
||||
send_event(event, active_user_ids(realm))
|
||||
|
||||
@@ -2388,7 +2390,7 @@ def do_update_message(user_profile, message_id, subject, propagate_mode, content
|
||||
edit_history_event = {}
|
||||
changed_messages = [message]
|
||||
|
||||
# You can only edit a message if:
|
||||
# You only have permission to edit a message if:
|
||||
# 1. You sent it, OR:
|
||||
# 2. This is a topic-only edit for a (no topic) message, OR:
|
||||
# 3. This is a topic-only edit and you are an admin.
|
||||
@@ -2400,6 +2402,20 @@ def do_update_message(user_profile, message_id, subject, propagate_mode, content
|
||||
else:
|
||||
raise JsonableError(_("You don't have permission to edit this message"))
|
||||
|
||||
# We already check for realm.allow_message_editing in
|
||||
# zerver.views.messages.update_message_backend
|
||||
# If there is a change to the content, we also need to check it hasn't been
|
||||
# too long
|
||||
|
||||
# Allow an extra 20 seconds since we potentially allow editing 15 seconds
|
||||
# past the limit, and in case there are network issues, etc. The 15 comes
|
||||
# from (min_seconds_to_edit + seconds_left_buffer) in message_edit.js; if
|
||||
# you change this value also change those two parameters in message_edit.js.
|
||||
edit_limit_buffer = 20
|
||||
if content is not None and user_profile.realm.message_content_edit_limit_seconds > 0 and \
|
||||
(now() - message.pub_date) > datetime.timedelta(seconds=user_profile.realm.message_content_edit_limit_seconds + edit_limit_buffer):
|
||||
raise JsonableError(_("The time limit for editing this message has past"))
|
||||
|
||||
# Set first_rendered_content to be the oldest version of the
|
||||
# rendered content recorded; which is the current version if the
|
||||
# content hasn't been edited before. Note that because one could
|
||||
@@ -2708,6 +2724,7 @@ def fetch_initial_state_data(user_profile, event_types, queue_id):
|
||||
state['realm_invite_by_admins_only'] = user_profile.realm.invite_by_admins_only
|
||||
state['realm_create_stream_by_admins_only'] = user_profile.realm.create_stream_by_admins_only
|
||||
state['realm_allow_message_editing'] = user_profile.realm.allow_message_editing
|
||||
state['realm_message_content_edit_limit_seconds'] = user_profile.realm.message_content_edit_limit_seconds
|
||||
|
||||
if want('realm_domain'):
|
||||
state['realm_domain'] = user_profile.realm.domain
|
||||
|
||||
19
zerver/migrations/0025_realm_message_content_edit_limit.py
Normal file
19
zerver/migrations/0025_realm_message_content_edit_limit.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('zerver', '0024_realm_allow_message_editing'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='realm',
|
||||
name='message_content_edit_limit_seconds',
|
||||
field=models.IntegerField(default=600),
|
||||
),
|
||||
]
|
||||
@@ -143,7 +143,10 @@ class Realm(ModelReprMixin, models.Model):
|
||||
mandatory_topics = models.BooleanField(default=False) # type: bool
|
||||
show_digest_email = models.BooleanField(default=True) # type: bool
|
||||
name_changes_disabled = models.BooleanField(default=False) # type: bool
|
||||
|
||||
allow_message_editing = models.BooleanField(default=True) # type: bool
|
||||
DEFAULT_MESSAGE_CONTENT_EDIT_LIMIT_SECONDS = 600 # if changed, also change in admin.js
|
||||
message_content_edit_limit_seconds = models.IntegerField(default=DEFAULT_MESSAGE_CONTENT_EDIT_LIMIT_SECONDS) # type: int
|
||||
|
||||
date_created = models.DateTimeField(default=timezone.now) # type: datetime.datetime
|
||||
notifications_stream = models.ForeignKey('Stream', related_name='+', null=True, blank=True) # type: Optional[Stream]
|
||||
|
||||
@@ -512,12 +512,16 @@ class EventsRegisterTest(AuthedTestCase):
|
||||
('type', equals('realm')),
|
||||
('op', equals('update_dict')),
|
||||
('property', equals('default')),
|
||||
('data', check_dict([('allow_message_editing', check_bool)])),
|
||||
('data', check_dict([('allow_message_editing', check_bool),
|
||||
('message_content_edit_limit_seconds', check_int)])),
|
||||
])
|
||||
# The first False is probably a noop, then we get transitions in both directions.
|
||||
for allow_message_editing in [False, True, False]:
|
||||
# Test every transition among the four possibilities {T,F} x {0, non-0}
|
||||
for (allow_message_editing, message_content_edit_limit_seconds) in \
|
||||
((True, 0), (False, 0), (True, 0), (False, 1234), (True, 0), (True, 1234), (True, 0),
|
||||
(False, 0), (False, 1234), (False, 0), (True, 1234), (False, 0),
|
||||
(True, 1234), (True, 600), (False, 600), (False, 1234), (True, 600)):
|
||||
events = self.do_test(lambda: do_set_realm_message_editing(self.user_profile.realm,
|
||||
allow_message_editing))
|
||||
allow_message_editing, message_content_edit_limit_seconds))
|
||||
error = schema_checker('events[0]', events[0])
|
||||
self.assert_on_error(error)
|
||||
|
||||
|
||||
@@ -779,6 +779,72 @@ class EditMessageTest(AuthedTestCase):
|
||||
})
|
||||
self.assert_json_error(result, "Content can't be empty")
|
||||
|
||||
def test_edit_message_content_limit(self):
|
||||
def set_message_editing_params(allow_message_editing,
|
||||
message_content_edit_limit_seconds):
|
||||
result = self.client_patch("/json/realm", {
|
||||
'allow_message_editing': ujson.dumps(allow_message_editing),
|
||||
'message_content_edit_limit_seconds': message_content_edit_limit_seconds
|
||||
})
|
||||
self.assert_json_success(result)
|
||||
|
||||
def do_edit_message_assert_success(id_, unique_str, topic_only = False):
|
||||
new_subject = 'subject' + unique_str
|
||||
new_content = 'content' + unique_str
|
||||
params_dict = { 'message_id': id_, 'subject': new_subject }
|
||||
if not topic_only:
|
||||
params_dict['content'] = new_content
|
||||
result = self.client.post("/json/update_message", params_dict)
|
||||
self.assert_json_success(result)
|
||||
if topic_only:
|
||||
self.check_message(id_, subject=new_subject)
|
||||
else:
|
||||
self.check_message(id_, subject=new_subject, content=new_content)
|
||||
|
||||
def do_edit_message_assert_error(id_, unique_str, error, topic_only = False):
|
||||
message = Message.objects.get(id=id_)
|
||||
old_subject = message.subject
|
||||
old_content = message.content
|
||||
new_subject = 'subject' + unique_str
|
||||
new_content = 'content' + unique_str
|
||||
params_dict = { 'message_id': id_, 'subject': new_subject }
|
||||
if not topic_only:
|
||||
params_dict['content'] = new_content
|
||||
result = self.client.post("/json/update_message", params_dict)
|
||||
message = Message.objects.get(id=id_)
|
||||
self.assert_json_error(result, error)
|
||||
self.check_message(id_, subject=old_subject, content=old_content)
|
||||
|
||||
self.login("iago@zulip.com")
|
||||
# send a message in the past
|
||||
id_ = self.send_message("iago@zulip.com", "Scotland", Recipient.STREAM,
|
||||
content="content", subject="subject")
|
||||
message = Message.objects.get(id=id_)
|
||||
message.pub_date = message.pub_date - datetime.timedelta(seconds=180)
|
||||
message.save()
|
||||
|
||||
# test the various possible message editing settings
|
||||
# high enough time limit, all edits allowed
|
||||
set_message_editing_params(True, 240)
|
||||
do_edit_message_assert_success(id_, 'A')
|
||||
|
||||
# out of time, only topic editing allowed
|
||||
set_message_editing_params(True, 120)
|
||||
do_edit_message_assert_success(id_, 'B', True)
|
||||
do_edit_message_assert_error(id_, 'C', "The time limit for editing this message has past")
|
||||
|
||||
# infinite time, all edits allowed
|
||||
set_message_editing_params(True, 0)
|
||||
do_edit_message_assert_success(id_, 'D')
|
||||
|
||||
# without allow_message_editing, nothing is allowed
|
||||
set_message_editing_params(False, 240)
|
||||
do_edit_message_assert_error(id_, 'E', "Your organization has turned off message editing.", True)
|
||||
set_message_editing_params(False, 120)
|
||||
do_edit_message_assert_error(id_, 'F', "Your organization has turned off message editing.", True)
|
||||
set_message_editing_params(False, 0)
|
||||
do_edit_message_assert_error(id_, 'G', "Your organization has turned off message editing.", True)
|
||||
|
||||
def test_propagate_topic_forward(self):
|
||||
self.login("hamlet@zulip.com")
|
||||
id1 = self.send_message("hamlet@zulip.com", "Scotland", Recipient.STREAM,
|
||||
@@ -982,4 +1048,3 @@ class CheckMessageTest(AuthedTestCase):
|
||||
new_count = message_stream_count(parent)
|
||||
self.assertEqual(new_count, old_count + 1)
|
||||
self.assertEqual(ret['message'].sender.email, 'othello-bot@zulip.com')
|
||||
|
||||
|
||||
@@ -946,6 +946,7 @@ def home(request):
|
||||
realm_invite_by_admins_only = register_ret['realm_invite_by_admins_only'],
|
||||
realm_create_stream_by_admins_only = register_ret['realm_create_stream_by_admins_only'],
|
||||
realm_allow_message_editing = register_ret['realm_allow_message_editing'],
|
||||
realm_message_content_edit_limit_seconds = register_ret['realm_message_content_edit_limit_seconds'],
|
||||
realm_restricted_to_domain = register_ret['realm_restricted_to_domain'],
|
||||
enter_sends = user_profile.enter_sends,
|
||||
left_side_userlist = register_ret['left_side_userlist'],
|
||||
@@ -1103,8 +1104,9 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
|
||||
invite_required=REQ(validator=check_bool, default=None),
|
||||
invite_by_admins_only=REQ(validator=check_bool, default=None),
|
||||
create_stream_by_admins_only=REQ(validator=check_bool, default=None),
|
||||
allow_message_editing=REQ(validator=check_bool, default=None)):
|
||||
# type: (HttpRequest, UserProfile, Optional[str], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool]) -> HttpResponse
|
||||
allow_message_editing=REQ(validator=check_bool, default=None),
|
||||
message_content_edit_limit_seconds=REQ(converter=to_non_negative_int, default=None)):
|
||||
# type: (HttpRequest, UserProfile, Optional[str], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[int]) -> HttpResponse
|
||||
realm = user_profile.realm
|
||||
data = {} # type: Dict[str, Any]
|
||||
if name is not None and realm.name != name:
|
||||
@@ -1122,9 +1124,15 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
|
||||
if create_stream_by_admins_only is not None and realm.create_stream_by_admins_only != create_stream_by_admins_only:
|
||||
do_set_realm_create_stream_by_admins_only(realm, create_stream_by_admins_only)
|
||||
data['create_stream_by_admins_only'] = create_stream_by_admins_only
|
||||
if allow_message_editing is not None and realm.allow_message_editing != allow_message_editing:
|
||||
do_set_realm_message_editing(realm, allow_message_editing)
|
||||
if (allow_message_editing is not None and realm.allow_message_editing != allow_message_editing) or \
|
||||
(message_content_edit_limit_seconds is not None and realm.message_content_edit_limit_seconds != message_content_edit_limit_seconds):
|
||||
if allow_message_editing is None:
|
||||
allow_message_editing = realm.allow_message_editing
|
||||
if message_content_edit_limit_seconds is None:
|
||||
message_content_edit_limit_seconds = realm.message_content_edit_limit_seconds
|
||||
do_set_realm_message_editing(realm, allow_message_editing, message_content_edit_limit_seconds)
|
||||
data['allow_message_editing'] = allow_message_editing
|
||||
data['message_content_edit_limit_seconds'] = message_content_edit_limit_seconds
|
||||
return json_success(data)
|
||||
|
||||
@csrf_exempt
|
||||
|
||||
Reference in New Issue
Block a user