mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 21:43:21 +00:00
We now ignore question edits if the sender of the submessage is not the message author. The webapp UI prevents folks from editing the question for somebody else's poll, but a determined person could use our low level API to do it. We will add safeguards on the server side for this, but this change is sufficient to protect the webapp (and mobile when they upgrade the library).
203 lines
5.8 KiB
JavaScript
203 lines
5.8 KiB
JavaScript
import $ from "jquery";
|
|
|
|
import {PollData} from "../shared/js/poll_data";
|
|
import render_widgets_poll_widget from "../templates/widgets/poll_widget.hbs";
|
|
import render_widgets_poll_widget_results from "../templates/widgets/poll_widget_results.hbs";
|
|
|
|
import * as blueslip from "./blueslip";
|
|
import * as people from "./people";
|
|
|
|
export function activate({
|
|
elem,
|
|
callback,
|
|
extra_data: {question = "", options = []} = {},
|
|
message,
|
|
}) {
|
|
const is_my_poll = people.is_my_user_id(message.sender_id);
|
|
const poll_data = new PollData({
|
|
message_sender_id: message.sender_id,
|
|
current_user_id: people.my_current_user_id(),
|
|
is_my_poll,
|
|
question,
|
|
options,
|
|
comma_separated_names: people.get_full_names_for_poll_option,
|
|
report_error_function: blueslip.warn,
|
|
});
|
|
|
|
function update_edit_controls() {
|
|
const has_question = elem.find("input.poll-question").val().trim() !== "";
|
|
elem.find("button.poll-question-check").toggle(has_question);
|
|
}
|
|
|
|
function render_question() {
|
|
const question = poll_data.get_question();
|
|
const input_mode = poll_data.get_input_mode();
|
|
const can_edit = is_my_poll && !input_mode;
|
|
const has_question = question.trim() !== "";
|
|
const can_vote = has_question;
|
|
const waiting = !is_my_poll && !has_question;
|
|
const author_help = is_my_poll && !has_question;
|
|
|
|
elem.find(".poll-question-header").toggle(!input_mode);
|
|
elem.find(".poll-question-header").text(question);
|
|
elem.find(".poll-edit-question").toggle(can_edit);
|
|
update_edit_controls();
|
|
|
|
elem.find(".poll-question-bar").toggle(input_mode);
|
|
elem.find(".poll-option-bar").toggle(can_vote);
|
|
|
|
elem.find(".poll-please-wait").toggle(waiting);
|
|
|
|
elem.find(".poll-author-help").toggle(author_help);
|
|
}
|
|
|
|
function start_editing() {
|
|
poll_data.set_input_mode();
|
|
|
|
const question = poll_data.get_question();
|
|
elem.find("input.poll-question").val(question);
|
|
render_question();
|
|
elem.find("input.poll-question").trigger("focus");
|
|
}
|
|
|
|
function abort_edit() {
|
|
poll_data.clear_input_mode();
|
|
render_question();
|
|
}
|
|
|
|
function submit_question() {
|
|
const poll_question_input = elem.find("input.poll-question");
|
|
let new_question = poll_question_input.val().trim();
|
|
const old_question = poll_data.get_question();
|
|
|
|
// We should disable the button for blank questions,
|
|
// so this is just defensive code.
|
|
if (new_question.trim() === "") {
|
|
new_question = old_question;
|
|
}
|
|
|
|
// Optimistically set the question locally.
|
|
poll_data.set_question(new_question);
|
|
render_question();
|
|
|
|
// If there were no actual edits, we can exit now.
|
|
if (new_question === old_question) {
|
|
return;
|
|
}
|
|
|
|
// Broadcast the new question to our peers.
|
|
const data = poll_data.handle.question.outbound(new_question);
|
|
callback(data);
|
|
}
|
|
|
|
function submit_option() {
|
|
const poll_option_input = elem.find("input.poll-option");
|
|
const option = poll_option_input.val().trim();
|
|
const options = poll_data.get_widget_data().options;
|
|
|
|
if (poll_data.is_option_present(options, option)) {
|
|
return;
|
|
}
|
|
|
|
if (option === "") {
|
|
return;
|
|
}
|
|
|
|
poll_option_input.val("").trigger("focus");
|
|
|
|
const data = poll_data.handle.new_option.outbound(option);
|
|
callback(data);
|
|
}
|
|
|
|
function submit_vote(key) {
|
|
const data = poll_data.handle.vote.outbound(key);
|
|
callback(data);
|
|
}
|
|
|
|
function build_widget() {
|
|
const html = render_widgets_poll_widget();
|
|
elem.html(html);
|
|
|
|
elem.find("input.poll-question").on("keyup", (e) => {
|
|
e.stopPropagation();
|
|
update_edit_controls();
|
|
});
|
|
|
|
elem.find("input.poll-question").on("keydown", (e) => {
|
|
e.stopPropagation();
|
|
|
|
if (e.key === "Enter") {
|
|
submit_question();
|
|
return;
|
|
}
|
|
|
|
if (e.key === "Escape") {
|
|
abort_edit();
|
|
return;
|
|
}
|
|
});
|
|
|
|
elem.find(".poll-edit-question").on("click", (e) => {
|
|
e.stopPropagation();
|
|
start_editing();
|
|
});
|
|
|
|
elem.find("button.poll-question-check").on("click", (e) => {
|
|
e.stopPropagation();
|
|
submit_question();
|
|
});
|
|
|
|
elem.find("button.poll-question-remove").on("click", (e) => {
|
|
e.stopPropagation();
|
|
abort_edit();
|
|
});
|
|
|
|
elem.find("button.poll-option").on("click", (e) => {
|
|
e.stopPropagation();
|
|
submit_option();
|
|
});
|
|
|
|
elem.find("input.poll-option").on("keydown", (e) => {
|
|
e.stopPropagation();
|
|
|
|
if (e.key === "Enter") {
|
|
submit_option();
|
|
return;
|
|
}
|
|
|
|
if (e.key === "Escape") {
|
|
$("input.poll-option").val("");
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
|
|
function render_results() {
|
|
const widget_data = poll_data.get_widget_data();
|
|
|
|
const html = render_widgets_poll_widget_results(widget_data);
|
|
elem.find("ul.poll-widget").html(html);
|
|
|
|
elem.find("button.poll-vote")
|
|
.off("click")
|
|
.on("click", (e) => {
|
|
e.stopPropagation();
|
|
const key = $(e.target).attr("data-key");
|
|
submit_vote(key);
|
|
});
|
|
}
|
|
|
|
elem.handle_events = function (events) {
|
|
for (const event of events) {
|
|
poll_data.handle_event(event.sender_id, event.data);
|
|
}
|
|
|
|
render_question();
|
|
render_results();
|
|
};
|
|
|
|
build_widget();
|
|
render_question();
|
|
render_results();
|
|
}
|