mirror of
https://github.com/zulip/zulip.git
synced 2025-11-10 17:07:07 +00:00
Add warning for @all / @everyone.
* The warning contains a count of the number of people in the stream. * An error appears if the warning is ignored and the user tries to send the message anyway. * The message cannot be sent until the warning is acknowledged or @all / @everyone is removed. * This only applies to stream messages and not private messages. Fixes #853.
This commit is contained in:
85
frontend_tests/casper_tests/12-mention.js
Normal file
85
frontend_tests/casper_tests/12-mention.js
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
var common = require('../casper_lib/common.js').common;
|
||||||
|
|
||||||
|
function enter_mention_in_composer(str, item) {
|
||||||
|
casper.then(function () {
|
||||||
|
casper.test.info('Start typing @all');
|
||||||
|
casper.evaluate(function (str, item) {
|
||||||
|
// Set the value and then send a bogus keyup event to trigger
|
||||||
|
// the typeahead.
|
||||||
|
$('#new_message_content')
|
||||||
|
.focus()
|
||||||
|
.val(str)
|
||||||
|
.trigger($.Event('keyup', { which: 0 }));
|
||||||
|
|
||||||
|
// Trigger the typeahead.
|
||||||
|
// Reaching into the guts of Bootstrap Typeahead like this is not
|
||||||
|
// great, but I found it very hard to do it any other way.
|
||||||
|
var tah = $('#new_message_content').data().typeahead;
|
||||||
|
tah.mouseenter({
|
||||||
|
currentTarget: $('.typeahead:visible li:contains("'+item+'")')[0]
|
||||||
|
});
|
||||||
|
tah.select();
|
||||||
|
}, {str: str, item: item});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
common.start_and_log_in();
|
||||||
|
casper.verbonse = true;
|
||||||
|
|
||||||
|
casper.waitForSelector('#new_message_content', function () {
|
||||||
|
casper.test.info('compose box visible');
|
||||||
|
casper.page.sendEvent('keypress', "c"); // brings up the compose box
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.then(function () {
|
||||||
|
casper.fill('form[action^="/json/messages"]', {
|
||||||
|
stream: 'Verona',
|
||||||
|
subject: 'Test mention all'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
enter_mention_in_composer('@all', 'all');
|
||||||
|
|
||||||
|
casper.waitForText("Are you sure you want to message all", function () {
|
||||||
|
casper.test.info('Warning message appears when mentioning @all');
|
||||||
|
casper.test.assertSelectorHasText('.compose-all-everyone', 'Are you sure you want to message all');
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.then( function () {
|
||||||
|
casper.test.info('Click Send Button');
|
||||||
|
casper.click('#compose-send-button');
|
||||||
|
});
|
||||||
|
casper.waitForText("Please remove @all", function () {
|
||||||
|
casper.test.info('Error message appears when attempting to send a message without acknowledging the @all mention warning');
|
||||||
|
casper.test.assertSelectorHasText('#error-msg', "Please remove @all / @everyone or acknowledge that you will be spamming everyone!");
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.waitForSelector('.compose-all-everyone-confirm', function () {
|
||||||
|
casper.click('.compose-all-everyone-confirm');
|
||||||
|
}, function () {
|
||||||
|
casper.test.error('Could not click confirm button.');
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.waitWhileVisible('.compose-all-everyone-confirm', function () {
|
||||||
|
casper.test.info('Check that error messages are gone.');
|
||||||
|
casper.test.assertNotVisible('.compose-all-everyone-msg');
|
||||||
|
casper.test.assertNotVisible('#error-msg');
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.then( function () {
|
||||||
|
casper.test.info('Click Send Button');
|
||||||
|
casper.click('#compose-send-button');
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.then( function () {
|
||||||
|
common.expected_messages('zhome', ['Verona > Test mention all'],
|
||||||
|
["<p><span class=\"user-mention user-mention-me\" data-user-email=\"*\">@all</span> </p>"]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
common.then_log_out();
|
||||||
|
|
||||||
|
casper.run(function () {
|
||||||
|
casper.test.done();
|
||||||
|
});
|
||||||
@@ -190,6 +190,19 @@ function render(template_name, args) {
|
|||||||
assert.equal(button.text(), "Subscribe");
|
assert.equal(button.text(), "Subscribe");
|
||||||
}());
|
}());
|
||||||
|
|
||||||
|
(function compose_all_everyone() {
|
||||||
|
var args = {
|
||||||
|
count: '101',
|
||||||
|
name: 'all'
|
||||||
|
};
|
||||||
|
var html = render('compose_all_everyone', args);
|
||||||
|
global.write_test_output("compose_all_everyone.handlebars", html);
|
||||||
|
var button = $(html).find("button:first");
|
||||||
|
assert.equal(button.text(), "YES");
|
||||||
|
var error_msg = $(html).find('span.compose-all-everyone-msg').text().trim();
|
||||||
|
assert.equal(error_msg, "Are you sure you want to message all 101 people in this stream?");
|
||||||
|
}());
|
||||||
|
|
||||||
(function compose_notification() {
|
(function compose_notification() {
|
||||||
var args = {
|
var args = {
|
||||||
"note": "You sent a message to a muted topic.",
|
"note": "You sent a message to a muted topic.",
|
||||||
|
|||||||
@@ -2,6 +2,14 @@ var compose = (function () {
|
|||||||
|
|
||||||
var exports = {};
|
var exports = {};
|
||||||
var is_composing_message = false;
|
var is_composing_message = false;
|
||||||
|
|
||||||
|
// Track the state of the @all warning. The user must acknowledge that they are spamming the entire stream
|
||||||
|
// before the warning will go away. If they try to send before explicitly dismissing the warning, they will
|
||||||
|
// get an error message too.
|
||||||
|
// undefined: no @all/@everyone in message; false: user typed @all/@everyone; true: user clicked YES
|
||||||
|
var user_acknowledged_all_everyone;
|
||||||
|
var all_everyone_re = /(@\*{2}(all|everyone)\*{2})|(@(all|everyone))/;
|
||||||
|
|
||||||
var message_snapshot;
|
var message_snapshot;
|
||||||
var empty_subject_placeholder = "(no topic)";
|
var empty_subject_placeholder = "(no topic)";
|
||||||
|
|
||||||
@@ -85,6 +93,28 @@ function show_box(tabname, focus_area, opts) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function show_all_everyone_warnings() {
|
||||||
|
var current_stream = stream_data.get_sub(compose.stream_name());
|
||||||
|
var stream_count = current_stream.subscribers.num_items();
|
||||||
|
|
||||||
|
var all_everyone_template = templates.render("compose_all_everyone", {count: stream_count});
|
||||||
|
var error_area_all_everyone = $("#compose-all-everyone");
|
||||||
|
|
||||||
|
// only show one error for any number of @all or @everyone mentions
|
||||||
|
if (!error_area_all_everyone.is(':visible')) {
|
||||||
|
error_area_all_everyone.append(all_everyone_template);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_area_all_everyone.show();
|
||||||
|
user_acknowledged_all_everyone = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear_all_everyone_warnings() {
|
||||||
|
$("#compose-all-everyone").hide();
|
||||||
|
$("#compose-all-everyone").empty();
|
||||||
|
$("#send-status").hide();
|
||||||
|
}
|
||||||
|
|
||||||
function clear_invites() {
|
function clear_invites() {
|
||||||
$("#compose_invite_users").hide();
|
$("#compose_invite_users").hide();
|
||||||
$("#compose_invite_users").empty();
|
$("#compose_invite_users").empty();
|
||||||
@@ -93,6 +123,8 @@ function clear_invites() {
|
|||||||
function clear_box() {
|
function clear_box() {
|
||||||
exports.snapshot_message();
|
exports.snapshot_message();
|
||||||
clear_invites();
|
clear_invites();
|
||||||
|
clear_all_everyone_warnings();
|
||||||
|
user_acknowledged_all_everyone = undefined;
|
||||||
$("#compose").find('input[type=text], textarea').val('');
|
$("#compose").find('input[type=text], textarea').val('');
|
||||||
autosize_textarea();
|
autosize_textarea();
|
||||||
$("#send-status").hide(0);
|
$("#send-status").hide(0);
|
||||||
@@ -342,6 +374,11 @@ exports.restore_message = function () {
|
|||||||
clear_message_snapshot();
|
clear_message_snapshot();
|
||||||
compose_fade.clear_compose();
|
compose_fade.clear_compose();
|
||||||
compose.start(snapshot_copy.type, snapshot_copy);
|
compose.start(snapshot_copy.type, snapshot_copy);
|
||||||
|
|
||||||
|
if (snapshot_copy.content !== undefined &&
|
||||||
|
all_everyone_re.test(snapshot_copy.content)) {
|
||||||
|
show_all_everyone_warnings();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function compose_error(error_text, bad_input) {
|
function compose_error(error_text, bad_input) {
|
||||||
@@ -696,6 +733,23 @@ function validate_stream_message() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if @all or @everyone is in the message
|
||||||
|
if (all_everyone_re.test(exports.message_content())) {
|
||||||
|
if (user_acknowledged_all_everyone === undefined ||
|
||||||
|
user_acknowledged_all_everyone === false) {
|
||||||
|
// user has not seen a warning message yet if undefined
|
||||||
|
show_all_everyone_warnings();
|
||||||
|
// user has not acknowledge the warning message yet
|
||||||
|
compose_error("Please remove @all / @everyone or acknowledge that you will be spamming everyone!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the message no longer contains @all or @everyone
|
||||||
|
clear_all_everyone_warnings();
|
||||||
|
}
|
||||||
|
// at this point, the user has either acknowledged the warning or removed @all / @everyone
|
||||||
|
user_acknowledged_all_everyone = undefined;
|
||||||
|
|
||||||
var response;
|
var response;
|
||||||
|
|
||||||
if (!stream_data.is_subscribed(stream_name)) {
|
if (!stream_data.is_subscribed(stream_name)) {
|
||||||
@@ -789,6 +843,13 @@ $(function () {
|
|||||||
|
|
||||||
if (data !== undefined && data.mentioned !== undefined) {
|
if (data !== undefined && data.mentioned !== undefined) {
|
||||||
var email = data.mentioned.email;
|
var email = data.mentioned.email;
|
||||||
|
|
||||||
|
// warn if @all or @everyone is mentioned
|
||||||
|
if (data.mentioned.full_name === 'all' || data.mentioned.full_name === 'everyone') {
|
||||||
|
show_all_everyone_warnings();
|
||||||
|
return; // don't check if @all or @everyone is subscribed to a stream
|
||||||
|
}
|
||||||
|
|
||||||
if (compose_fade.would_receive_message(email) === false) {
|
if (compose_fade.would_receive_message(email) === false) {
|
||||||
var new_row = templates.render("compose-invite-users", {email: email,
|
var new_row = templates.render("compose-invite-users", {email: email,
|
||||||
name: data.mentioned.full_name});
|
name: data.mentioned.full_name});
|
||||||
@@ -805,6 +866,16 @@ $(function () {
|
|||||||
error_area.show();
|
error_area.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#compose-all-everyone").on('click', '.compose-all-everyone-confirm', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
$(event.target).parents('.compose-all-everyone').remove();
|
||||||
|
user_acknowledged_all_everyone = true;
|
||||||
|
clear_all_everyone_warnings();
|
||||||
|
$('#new_message_content').focus().select();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#compose_invite_users").on('click', '.compose_invite_link', function (event) {
|
$("#compose_invite_users").on('click', '.compose_invite_link', function (event) {
|
||||||
|
|||||||
@@ -272,13 +272,8 @@ exports.content_typeahead_selected = function (item) {
|
|||||||
} else if (this.completing === 'mention') {
|
} else if (this.completing === 'mention') {
|
||||||
beginning = (beginning.substring(0, beginning.length - this.token.length-1)
|
beginning = (beginning.substring(0, beginning.length - this.token.length-1)
|
||||||
+ '@**' + item.full_name + '** ');
|
+ '@**' + item.full_name + '** ');
|
||||||
|
|
||||||
// We insert a special `all` item to the autocompleter above
|
|
||||||
// Don't consider it a user mention
|
|
||||||
if (item.email !== 'all' && item.email !== "everyone") {
|
|
||||||
$(document).trigger('usermention_completed.zulip', {mentioned: item});
|
$(document).trigger('usermention_completed.zulip', {mentioned: item});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Keep the cursor after the newly inserted text, as Bootstrap will call textbox.change() to overwrite the text
|
// Keep the cursor after the newly inserted text, as Bootstrap will call textbox.change() to overwrite the text
|
||||||
// in the textbox.
|
// in the textbox.
|
||||||
|
|||||||
@@ -1607,6 +1607,7 @@ blockquote p {
|
|||||||
width: 10px;
|
width: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.compose-all-everyone-controls,
|
||||||
.compose_invite_user_controls {
|
.compose_invite_user_controls {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|||||||
8
static/templates/compose_all_everyone.handlebars
Normal file
8
static/templates/compose_all_everyone.handlebars
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<div class="compose-all-everyone">
|
||||||
|
<span class="compose-all-everyone-msg">
|
||||||
|
{{#tr this}}Are you sure you want to message all <strong>__count__</strong> people in this stream?{{/tr}}
|
||||||
|
</span>
|
||||||
|
<span class="compose-all-everyone-controls">
|
||||||
|
<button type="button" class="compose-all-everyone-confirm">{{t "YES" }}</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
@@ -25,9 +25,9 @@
|
|||||||
<span class="send-status-close">×</span>
|
<span class="send-status-close">×</span>
|
||||||
<span id="error-msg"></span>
|
<span id="error-msg"></span>
|
||||||
</div>
|
</div>
|
||||||
<div id="compose_invite_users" class="alert" style="display: none"></div>
|
<div id="compose_invite_users" class="alert home-error-bar"></div>
|
||||||
<div id="out-of-view-notification" class="notification-alert">
|
<div id="compose-all-everyone" class="alert home-error-bar"></div>
|
||||||
</div>
|
<div id="out-of-view-notification" class="notification-alert"></div>
|
||||||
<div class="composition-area">
|
<div class="composition-area">
|
||||||
<button type="button" class="close" id='compose_close'>×</button>
|
<button type="button" class="close" id='compose_close'>×</button>
|
||||||
<form id="send_message_form" action="/json/messages" method="post">
|
<form id="send_message_form" action="/json/messages" method="post">
|
||||||
|
|||||||
Reference in New Issue
Block a user