Add realm setting to disable message editing.

This is controlled through the admin tab and a new field in the Realms
table.  This mirrors the behavior of the old hardcoded setting
feature_flags.disable_message_editing.  Partially resolves #903.
This commit is contained in:
Rishi Gupta
2016-06-21 12:34:41 -07:00
committed by Tim Abbott
parent 07e7230ae1
commit 43c2f35776
14 changed files with 269 additions and 17 deletions

View File

@@ -185,6 +185,170 @@ casper.then(function () {
});
// TODO: Test stream deletion
// Test turning message editing off and on
// go to home page
casper.then(function () {
casper.click('.global-filter[data-name="home"]');
});
// send two messages
common.then_send_message('stream', {
stream: 'Verona',
subject: 'edits',
content: 'test editing 1'
});
common.then_send_message('stream', {
stream: 'Verona',
subject: 'edits',
content: 'test editing 2'
});
casper.waitForText("test editing 1");
casper.waitForText("test editing 2");
// wait for message to be sent
casper.waitFor(function () {
return casper.evaluate(function () {
return current_msg_list.last().local_id === undefined;
});
});
// edit the last message just sent
casper.then(function () {
casper.evaluate(function () {
var msg = $('#zhome .message_row:last');
msg.find('.info').click();
$('.popover_edit_message').click();
});
});
casper.waitForSelector(".message_edit_content", function () {
casper.evaluate(function () {
var msg = $('#zhome .message_row:last');
msg.find('.message_edit_content').val("test edited");
msg.find('.message_edit_save').click();
});
});
// check that the message was indeed edited
casper.waitWhileVisible("textarea.message_edit_content", function () {
casper.test.assertSelectorHasText(".last_message .message_content", "test edited");
});
// edit the same message, but don't hit save this time
casper.then(function () {
casper.evaluate(function () {
var msg = $('#zhome .message_row:last');
msg.find('.info').click();
$('.popover_edit_message').click();
});
});
casper.waitForSelector(".message_edit_content", function () {
casper.evaluate(function () {
var msg = $('#zhome .message_row:last');
msg.find('.message_edit_content').val("test RE-edited");
});
});
// go to admin page
casper.then(function () {
casper.click('#settings-dropdown');
casper.click('a[href^="#administration"]');
});
// deactivate "allow message editing"
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 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');
});
});
// go back to home page
casper.then(function () {
casper.click('.global-filter[data-name="home"]');
});
// try to save the half-finished edit
casper.waitForSelector('.message_table', function () {
casper.then(function () {
casper.evaluate(function () {
var msg = $('#zhome .message_row:last');
msg.find('.message_edit_save').click();
});
});
});
// make sure we get the right error message, and that the message hasn't actually changed
casper.waitForSelector("div.edit_error", function () {
casper.test.assertSelectorHasText('div.edit_error', 'Error saving edit: Your organization has turned off message editing.');
casper.test.assertSelectorHasText(".last_message .message_content", "test edited");
});
// Check that edit link no longer appears in the popover menu
// TODO: also check that the edit icon no longer appears next to the message
casper.then(function () {
casper.evaluate(function () {
var msg = $('#zhome .message_row:last');
msg.find('.info').click();
});
casper.test.assertDoesntExist('.popover_edit_message');
casper.evaluate(function () {
var msg = $('#zhome .message_row:last');
msg.find('.info').click();
});
});
// go back to admin page, and reactivate "allow message editing"
casper.then(function () {
casper.click('#settings-dropdown');
casper.click('a[href^="#administration"]');
});
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 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 re-activated');
});
});
// go back home, and save our edit
casper.then(function () {
casper.click('.global-filter[data-name="home"]');
});
casper.waitForSelector('.message_table', function () {
casper.then(function () {
casper.evaluate(function () {
var msg = $('#zhome .message_row:last');
msg.find('.message_edit_save').click();
});
});
});
// check that edit went through
casper.waitWhileVisible("textarea.message_edit_content", function () {
casper.test.assertSelectorHasText(".last_message .message_content", "test RE-edited");
});
// check that the edit link reappears in popover menu
// TODO check for edit icon next to message on hover
casper.then(function () {
casper.evaluate(function () {
var msg = $('#zhome .message_row:last');
msg.find('.info').click();
});
casper.test.assertExists('.popover_edit_message');
casper.evaluate(function () {
var msg = $('#zhome .message_row:last');
msg.find('.info').click();
});
});
common.then_log_out();
casper.run(function () {

View File

@@ -142,7 +142,8 @@ function _setup_page() {
realm_restricted_to_domain: page_params.realm_restricted_to_domain,
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_create_stream_by_admins_only: page_params.realm_create_stream_by_admins_only,
realm_allow_message_editing: page_params.realm_allow_message_editing
};
var admin_tab = templates.render('admin_tab', options);
$("#administration").html(admin_tab);
@@ -152,6 +153,7 @@ function _setup_page() {
$("#admin-realm-invite-required-status").expectOne().hide();
$("#admin-realm-invite-by-admins-only-status").expectOne().hide();
$("#admin-realm-create-stream-by-admins-only-status").expectOne().hide();
$("#admin-realm-message-editing-status").expectOne().hide();
$("#admin-emoji-status").expectOne().hide();
$("#admin-emoji-name-status").expectOne().hide();
$("#admin-emoji-url-status").expectOne().hide();
@@ -344,11 +346,13 @@ function _setup_page() {
var invite_required_status = $("#admin-realm-invite-required-status").expectOne();
var invite_by_admins_only_status = $("#admin-realm-invite-by-admins-only-status").expectOne();
var create_stream_by_admins_only_status = $("#admin-realm-create-stream-by-admins-only-status").expectOne();
var message_editing_status = $("#admin-realm-message-editing-status").expectOne();
name_status.hide();
restricted_to_domain_status.hide();
invite_required_status.hide();
invite_by_admins_only_status.hide();
create_stream_by_admins_only_status.hide();
message_editing_status.hide();
e.preventDefault();
e.stopPropagation();
@@ -358,6 +362,7 @@ function _setup_page() {
var new_invite = $("#id_realm_invite_required").prop("checked");
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 url = "/json/realm";
var data = {
@@ -365,7 +370,8 @@ function _setup_page() {
restricted_to_domain: JSON.stringify(new_restricted),
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)
create_stream_by_admins_only: JSON.stringify(new_create_stream_by_admins_only),
allow_message_editing: JSON.stringify(new_allow_message_editing)
};
channel.patch({
@@ -403,6 +409,13 @@ function _setup_page() {
ui.report_success(i18n.t("Any user may now create new streams!"), create_stream_by_admins_only_status);
}
}
if (data.allow_message_editing !== undefined) {
if (data.allow_message_editing) {
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);
}
}
},
error: function (xhr, error) {
ui.report_error(i18n.t("Failed!"), xhr, name_status);

View File

@@ -11,9 +11,6 @@ exports.collect_send_times = false;
exports.use_socket = true;
exports.local_echo = true;
// Permanent realm-specific stuff:
exports.disable_message_editing = _.contains(['mit.edu'], page_params.domain);
// Experimental modification to support much wider message views.
exports.full_width = false;

View File

@@ -104,7 +104,7 @@ exports.process_message_for_recent_private_messages = function process_message_f
function set_topic_edit_properties(message) {
message.always_visible_topic_edit = false;
message.on_hover_topic_edit = false;
if (feature_flags.disable_message_editing) {
if (!page_params.realm_allow_message_editing) {
return;
}

View File

@@ -56,7 +56,7 @@ exports.toggle_actions_popover = function (element, id) {
var elt = $(element);
if (elt.data('popover') === undefined) {
var message = current_msg_list.get(id);
var can_edit = message.sent_by_me && message.local_id === undefined && !feature_flags.disable_message_editing;
var can_edit = message.sent_by_me && message.local_id === undefined && page_params.realm_allow_message_editing;
var can_mute_topic =
message.stream &&
message.subject &&

View File

@@ -95,6 +95,10 @@ function get_events_success(events) {
}
} else if (event.op === 'update' && event.property === 'restricted_to_domain') {
page_params.realm_restricted_to_domain = event.value;
} else if (event.op === 'update_dict' && event.property === 'default') {
$.each(event.data, function (key, value) {
page_params['realm_' + key] = value;
});
}
break;
case 'realm_user':

View File

@@ -118,7 +118,7 @@ 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 && !feature_flags.disable_message_editing) {
if (message && message.sent_by_me && !message.status_message && page_params.realm_allow_message_editing) {
message_row.find('.message_content').find('p:last').append(edit_content_button);
}
current_message_hover = message_row;
@@ -479,7 +479,7 @@ $(function () {
animation: false });
if (feature_flags.disable_message_editing) {
if (!page_params.realm_allow_message_editing) {
$("#edit-message-hotkey-help").hide();
}

View File

@@ -35,6 +35,7 @@
<div class="alert" id="admin-realm-invite-required-status"></div>
<div class="alert" id="admin-realm-invite-by-admins-only-status"></div>
<div class="alert" id="admin-realm-create-stream-by-admins-only-status"></div>
<div class="alert" id="admin-realm-message-editing-status"></div>
<label for="realm_name" class="control-label">{{t "Your organization's name" }}</label>
<div class="controls">
<input type="text" id="id_realm_name" name="realm_name" class="admin-realm-name"
@@ -84,10 +85,22 @@
{{t "Only admins may create streams" }}
</label>
<div class="controls">
<input type="checkbox" id="id_realm_create_stream_by_admins_only" name="realm_create_stream_by_admins_only"
<input type="checkbox" id="id_realm_create_stream_by_admins_only"
name="realm_create_stream_by_admins_only"
{{#if realm_create_stream_by_admins_only}}checked="checked"{{/if}} />
</div>
</div>
<div class="control-group">
<label for="realm_allow_message_editing"
title="{{t 'If checked, users can edit the content and topics of their old messages.' }}"
class="control-label">
{{t "Users can edit old messages" }}
</label>
<div class="controls">
<input type="checkbox" id="id_realm_allow_message_editing" name="realm_allow_message_editing"
{{#if realm_allow_message_editing}}checked="checked"{{/if}} />
</div>
</div>
<div class="controls organization-submission">
<input type="submit" class="btn btn-big btn-primary" value="{{t 'Save changes' }}" />
</div>

View File

@@ -411,6 +411,18 @@ 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
realm.allow_message_editing = allow_message_editing
realm.save(update_fields=['allow_message_editing'])
event = dict(
type="realm",
op="update_dict",
property="default",
data=dict(allow_message_editing=allow_message_editing),
)
send_event(event, active_user_ids(realm))
def do_deactivate_realm(realm):
# type: (Realm) -> None
"""
@@ -2692,6 +2704,7 @@ def fetch_initial_state_data(user_profile, event_types, queue_id):
state['realm_invite_required'] = user_profile.realm.invite_required
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
if want('realm_domain'):
state['realm_domain'] = user_profile.realm.domain
@@ -2812,8 +2825,12 @@ def apply_events(state, events, user_profile):
elif event['type'] == 'default_streams':
state['realm_default_streams'] = event['default_streams']
elif event['type'] == 'realm':
field = 'realm_' + event['property']
state[field] = event['value']
if event['op'] == "update":
field = 'realm_' + event['property']
state[field] = event['value']
elif event['op'] == "update_dict":
for key, value in event['data'].items():
state['realm_' + key] = value
elif event['type'] == "subscription":
if event['op'] in ["add"]:
# Convert the user_profile IDs to emails since that's what register() returns

View 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', '0023_userprofile_default_language'),
]
operations = [
migrations.AddField(
model_name='realm',
name='allow_message_editing',
field=models.BooleanField(default=True),
),
]

View File

@@ -143,6 +143,7 @@ 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
date_created = models.DateTimeField(default=timezone.now) # type: datetime.datetime
notifications_stream = models.ForeignKey('Stream', related_name='+', null=True, blank=True) # type: Optional[Stream]

View File

@@ -39,6 +39,7 @@ from zerver.lib.actions import (
do_set_realm_restricted_to_domain,
do_set_realm_invite_required,
do_set_realm_invite_by_admins_only,
do_set_realm_message_editing,
do_update_message,
do_update_pointer,
do_change_twenty_four_hour_time,
@@ -506,6 +507,21 @@ class EventsRegisterTest(AuthedTestCase):
error = schema_checker('events[0]', events[0])
self.assert_on_error(error)
def test_change_realm_message_edit_settings(self):
# type: () -> None
schema_checker = check_dict([
('type', equals('realm')),
('op', equals('update_dict')),
('property', equals('default')),
('data', check_dict([('allow_message_editing', check_bool)])),
])
# The first False is probably a noop, then we get transitions in both directions.
for allow_message_editing in [False, True, False]:
events = self.do_test(lambda: do_set_realm_message_editing(self.user_profile.realm,
allow_message_editing))
error = schema_checker('events[0]', events[0])
self.assert_on_error(error)
def test_change_is_admin(self):
# type: () -> None
schema_checker = check_dict([

View File

@@ -33,9 +33,10 @@ from zerver.lib.actions import do_change_password, do_change_full_name, do_chang
get_status_dict, do_change_enable_offline_email_notifications, \
do_change_enable_digest_emails, do_set_realm_name, do_set_realm_restricted_to_domain, \
do_set_realm_invite_required, do_set_realm_invite_by_admins_only, \
do_set_realm_create_stream_by_admins_only, get_default_subs, \
user_email_is_unique, do_invite_users, do_refer_friend, compute_mit_user_fullname, \
do_set_muted_topics, clear_followup_emails_queue, do_update_pointer, realm_user_count
do_set_realm_create_stream_by_admins_only, do_set_realm_message_editing, \
get_default_subs, user_email_is_unique, do_invite_users, do_refer_friend, \
compute_mit_user_fullname, do_set_muted_topics, clear_followup_emails_queue, \
do_update_pointer, realm_user_count
from zerver.lib.push_notifications import num_push_devices_for_user
from zerver.forms import RegistrationForm, HomepageForm, RealmCreationForm, ToSForm, \
CreateUserForm, is_inactive, OurAuthenticationForm
@@ -945,6 +946,7 @@ def home(request):
realm_invite_required = register_ret['realm_invite_required'],
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_restricted_to_domain = register_ret['realm_restricted_to_domain'],
enter_sends = user_profile.enter_sends,
left_side_userlist = register_ret['left_side_userlist'],
@@ -1101,8 +1103,9 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
restricted_to_domain=REQ(validator=check_bool, default=None),
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)):
# type: (HttpRequest, UserProfile, Optional[str], Optional[bool], Optional[bool], Optional[bool], Optional[bool]) -> HttpResponse
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
realm = user_profile.realm
data = {} # type: Dict[str, Any]
if name is not None and realm.name != name:
@@ -1120,6 +1123,9 @@ 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)
data['allow_message_editing'] = allow_message_editing
return json_success(data)
@csrf_exempt

View File

@@ -800,6 +800,8 @@ def update_message_backend(request, user_profile,
# type: (HttpRequest, UserProfile, int, Optional[text_type], Optional[str], Optional[text_type]) -> HttpResponse
if subject is None and content is None:
return json_error(_("Nothing to change"))
if not user_profile.realm.allow_message_editing:
return json_error(_("Your organization has turned off message editing."))
do_update_message(user_profile, message_id, subject, propagate_mode, content)
return json_success()