Add UI for changing the bot owners.

Add neccesary UI in #administration and #settings for
changing the bot owner. The bot owner select control
is rendered dynamically in order to avoid performance
issues in case of large number of users.

Fixes: #2719.
This commit is contained in:
Harshit Bansal 2017-02-25 13:19:12 +05:30 committed by Tim Abbott
parent 8b11deedb3
commit 1948cb6a89
10 changed files with 88 additions and 23 deletions

View File

@ -547,12 +547,12 @@ run(function (override, capture, args) {
event = event_fixtures.realm_bot__update; event = event_fixtures.realm_bot__update;
override('bot_data', 'update', capture(['email', 'bot'])); override('bot_data', 'update', capture(['email', 'bot']));
override('admin', 'update_user_full_name', capture(['update_user_id', 'name'])); override('admin', 'update_user_data', capture(['update_user_id', 'update_bot_data']));
dispatch(event); dispatch(event);
assert_same(args.email, event.bot.email); assert_same(args.email, event.bot.email);
assert_same(args.bot, event.bot); assert_same(args.bot, event.bot);
assert_same(args.update_user_id, event.bot.user_id); assert_same(args.update_user_id, event.bot.user_id);
assert_same(args.name, event.bot.full_name); assert_same(args.update_bot_data, event.bot);
}); });

View File

@ -354,6 +354,25 @@ function render(template_name, args) {
assert.equal(img.attr('src'), '/hamlet/avatar/url'); assert.equal(img.attr('src'), '/hamlet/avatar/url');
}()); }());
(function bot_owner_select() {
var args = {
users_list: [
{
email: "hamlet@zulip.com",
api_key: "123456ABCD",
full_name: "Hamlet",
avatar_url: "/hamlet/avatar/url",
},
],
};
var html = render('bot_owner_select', args);
global.write_handlebars_output("bot_owner_select", html);
var option = $(html).find("option:last");
assert.equal(option.val(), "hamlet@zulip.com");
assert.equal(option.text(), "Hamlet");
}());
(function compose_invite_users() { (function compose_invite_users() {
var args = { var args = {
email: 'hamlet@zulip.com', email: 'hamlet@zulip.com',

View File

@ -12,7 +12,7 @@ set_global('activity', {
redraw: function () {}, redraw: function () {},
}); });
set_global('admin', { set_global('admin', {
update_user_full_name: function () {}, update_user_data: function () {},
show_or_hide_menu_item: function () {}, show_or_hide_menu_item: function () {},
}); });
set_global('page_params', { set_global('page_params', {
@ -79,6 +79,3 @@ initialize();
assert.equal(full_name, 'Me V2'); assert.equal(full_name, 'Me V2');
}()); }());

View File

@ -30,19 +30,28 @@ function get_email_for_user_row(row) {
return email; return email;
} }
exports.update_user_full_name = function (user_id, new_full_name) { exports.update_user_data = function (user_id, new_data) {
if (!meta.loaded) { if (!meta.loaded) {
return; return;
} }
var user_info = get_user_info(user_id); var user_info = get_user_info(user_id);
var user_row = user_info.user_row; var user_row = user_info.user_row;
var form_row = user_info.form_row; var form_row = user_info.form_row;
// Update the full name in the table if (new_data.full_name !== undefined) {
user_row.find(".user_name").text(new_full_name); // Update the full name in the table
form_row.find("input[name='full_name']").val(new_full_name); user_row.find(".user_name").text(new_data.full_name);
form_row.find("input[name='full_name']").val(new_data.full_name);
}
if (new_data.owner !== undefined) {
// Update the bot owner in the table
user_row.find(".owner").text(new_data.owner);
}
// Remove the bot owner select control.
form_row.find(".edit_bot_owner_container select").remove();
// Hide name change form // Hide name change form
form_row.hide(); form_row.hide();
@ -58,10 +67,6 @@ function failed_listing_streams(xhr) {
ui.report_error(i18n.t("Error listing streams"), xhr, $("#administration-status")); ui.report_error(i18n.t("Error listing streams"), xhr, $("#administration-status"));
} }
function failed_changing_name(xhr) {
ui.report_error(i18n.t("Error changing name"), xhr, $("#administration-status"));
}
function populate_users(realm_people_data) { function populate_users(realm_people_data) {
var users_table = $("#admin_users_table"); var users_table = $("#admin_users_table");
var deactivated_users_table = $("#admin_deactivated_users_table"); var deactivated_users_table = $("#admin_deactivated_users_table");
@ -791,6 +796,9 @@ function _setup_page() {
}); });
$(".admin_user_table, .admin_bot_table").on("click", ".open-user-form", function (e) { $(".admin_user_table, .admin_bot_table").on("click", ".open-user-form", function (e) {
var users_list = people.get_realm_persons().filter(function (person) {
return !person.is_bot;
});
var user_id = $(e.currentTarget).attr("data-user-id"); var user_id = $(e.currentTarget).attr("data-user-id");
var user_info = get_user_info(user_id); var user_info = get_user_info(user_id);
var user_row = user_info.user_row; var user_row = user_info.user_row;
@ -798,19 +806,25 @@ function _setup_page() {
var reset_button = form_row.find(".reset_edit_user"); var reset_button = form_row.find(".reset_edit_user");
var submit_button = form_row.find(".submit_name_changes"); var submit_button = form_row.find(".submit_name_changes");
var full_name = form_row.find("input[name='full_name']"); var full_name = form_row.find("input[name='full_name']");
var owner_select = $(templates.render("bot_owner_select", {users_list: users_list}));
var admin_status = $('#administration-status').expectOne(); var admin_status = $('#administration-status').expectOne();
var person = people.get_person_from_user_id(user_id); var person = people.get_person_from_user_id(user_id);
if (!person) { if (!person) {
return; return;
} }
// Dynamically add the owner select control in order to
// avoid performance issues in case of large number of users.
owner_select.val(bot_data.get(person.email).owner || "");
form_row.find(".edit_bot_owner_container").append(owner_select);
// Show user form. // Show user form.
user_row.hide(); user_row.hide();
form_row.show(); form_row.show();
reset_button.on("click", function () { reset_button.on("click", function () {
owner_select.remove();
form_row.hide(); form_row.hide();
user_row.show(); user_row.show();
}); });
@ -819,18 +833,24 @@ function _setup_page() {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
var url = "/json/users/" + encodeURIComponent(person.email); var url = "/json/bots/" + encodeURIComponent(person.email);
var data = { var data = {
full_name: JSON.stringify(full_name.val()), full_name: full_name.val(),
}; };
if (owner_select.val() !== undefined && owner_select.val() !== "") {
data.bot_owner = owner_select.val();
}
channel.patch({ channel.patch({
url: url, url: url,
data: data, data: data,
success: function () { success: function () {
ui.report_success(i18n.t('Name successfully updated!'), admin_status); ui.report_success(i18n.t('Updated successfully!'), admin_status);
},
error: function () {
ui.report_error(i18n.t('Update failed!'), admin_status);
}, },
error: failed_changing_name,
}); });
}); });
}); });

View File

@ -96,8 +96,11 @@ function dispatch_normal_event(event) {
} else if (event.op === 'remove') { } else if (event.op === 'remove') {
bot_data.remove(event.bot.email); bot_data.remove(event.bot.email);
} else if (event.op === 'update') { } else if (event.op === 'update') {
if (_.has(event.bot, 'owner_id')) {
event.bot.owner = people.get_person_from_user_id(event.bot.owner_id).email;
}
bot_data.update(event.bot.email, event.bot); bot_data.update(event.bot.email, event.bot);
admin.update_user_full_name(event.bot.user_id, event.bot.full_name); admin.update_user_data(event.bot.user_id, event.bot);
} }
break; break;

View File

@ -719,15 +719,21 @@ function _setup_page() {
var image_version = 0; var image_version = 0;
$("#bots_list").on("click", "button.open_edit_bot_form", function (e) { $("#bots_list").on("click", "button.open_edit_bot_form", function (e) {
var users_list = people.get_realm_persons().filter(function (person) {
return !person.is_bot;
});
var li = $(e.currentTarget).closest('li'); var li = $(e.currentTarget).closest('li');
var edit_div = li.find('div.edit_bot'); var edit_div = li.find('div.edit_bot');
var form = li.find('.edit_bot_form'); var form = li.find('.edit_bot_form');
var image = li.find(".image"); var image = li.find(".image");
var bot_info = li.find(".bot_info"); var bot_info = li.find(".bot_info");
var reset_edit_bot = li.find(".reset_edit_bot"); var reset_edit_bot = li.find(".reset_edit_bot");
var owner_select = $(templates.render("bot_owner_select", {users_list:users_list}));
var old_full_name = bot_info.find(".name").text(); var old_full_name = bot_info.find(".name").text();
var old_owner = bot_data.get(bot_info.find(".email .value").text()).owner;
form.find(".edit_bot_name").attr('value', old_full_name); form.find(".edit_bot_name").attr('value', old_full_name);
form.find(".edit-bot-owner .controls").append(owner_select);
form.find(".edit-bot-owner select").val(old_owner);
image.hide(); image.hide();
bot_info.hide(); bot_info.hide();
@ -744,6 +750,7 @@ function _setup_page() {
reset_edit_bot.click(function (event) { reset_edit_bot.click(function (event) {
form.find(".edit_bot_name").val(old_full_name); form.find(".edit_bot_name").val(old_full_name);
owner_select.remove();
show_row_again(); show_row_again();
$(this).off(event); $(this).off(event);
}); });
@ -758,6 +765,7 @@ function _setup_page() {
submitHandler: function () { submitHandler: function () {
var email = form.data('email'); var email = form.data('email');
var full_name = form.find('.edit_bot_name').val(); var full_name = form.find('.edit_bot_name').val();
var bot_owner = form.find('.edit-bot-owner select').val();
var file_input = li.find('.edit_bot_avatar_file_input'); var file_input = li.find('.edit_bot_avatar_file_input');
var default_sending_stream = form.find('.edit_bot_default_sending_stream').val(); var default_sending_stream = form.find('.edit_bot_default_sending_stream').val();
var default_events_register_stream = form.find('.edit_bot_default_events_register_stream').val(); var default_events_register_stream = form.find('.edit_bot_default_events_register_stream').val();
@ -767,6 +775,7 @@ function _setup_page() {
formData.append('csrfmiddlewaretoken', csrf_token); formData.append('csrfmiddlewaretoken', csrf_token);
formData.append('full_name', full_name); formData.append('full_name', full_name);
formData.append('bot_owner', bot_owner);
add_bot_default_streams_to_form(formData, default_sending_stream, add_bot_default_streams_to_form(formData, default_sending_stream,
default_events_register_stream); default_events_register_stream);
jQuery.each(file_input[0].files, function (i, file) { jQuery.each(file_input[0].files, function (i, file) {
@ -784,6 +793,7 @@ function _setup_page() {
loading.destroy_indicator(spinner); loading.destroy_indicator(spinner);
errors.hide(); errors.hide();
edit_button.show(); edit_button.show();
owner_select.remove();
show_row_again(); show_row_again();
bot_info.find('.name').text(full_name); bot_info.find('.name').text(full_name);
if (data.avatar_url) { if (data.avatar_url) {

View File

@ -33,7 +33,7 @@ exports.update_person = function update(person) {
if (_.has(person, 'full_name')) { if (_.has(person, 'full_name')) {
people.set_full_name(person_obj, person.full_name); people.set_full_name(person_obj, person.full_name);
admin.update_user_full_name(person.user_id, person.full_name); admin.update_user_data(person.user_id, person);
activity.redraw(); activity.redraw();
message_live_update.update_user_full_name(person.user_id, person.full_name); message_live_update.update_user_full_name(person.user_id, person.full_name);
pm_list.update_private_messages(); pm_list.update_private_messages();

View File

@ -52,6 +52,11 @@
<label for="full_name">{{t "Full name" }}</label> <label for="full_name">{{t "Full name" }}</label>
<input type="text" name="full_name" value="{{ full_name }}" /> <input type="text" name="full_name" value="{{ full_name }}" />
</div> </div>
{{#if is_bot}}
<div class="input-group edit_bot_owner_container">
<label for="bot_owner_select">{{t "Owner" }}</label>
</div>
{{/if}}
<div class="input-group"> <div class="input-group">
<input type="button" class="btn btn-primary submit_name_changes" value="{{t 'Save changes' }}" /> <input type="button" class="btn btn-primary submit_name_changes" value="{{t 'Save changes' }}" />
<button type="button" class="btn btn-default reset_edit_user">{{t 'Cancel' }}</button> <button type="button" class="btn btn-default reset_edit_user">{{t 'Cancel' }}</button>

View File

@ -44,6 +44,11 @@
<div><label for="edit_bot_name{{id_suffix}}" generated="true" class="text-error"></label></div> <div><label for="edit_bot_name{{id_suffix}}" generated="true" class="text-error"></label></div>
</div> </div>
</div> </div>
<div class="control-group edit-bot-owner">
<label for="bot_owner_select" class="control-label">{{t "Owner" }}</label>
<div class="controls">
</div>
</div>
<div class="control-group new-bot-ui"> <div class="control-group new-bot-ui">
<label for="edit_bot_default_sending_stream{{id_suffix}}" class="control-label">{{t "Send to stream" }}</label> <label for="edit_bot_default_sending_stream{{id_suffix}}" class="control-label">{{t "Send to stream" }}</label>
<select name="bot_default_sending_stream" class="edit_bot_default_sending_stream" id="edit_bot_default_sending_stream{{id_suffix}}"></select> <select name="bot_default_sending_stream" class="edit_bot_default_sending_stream" id="edit_bot_default_sending_stream{{id_suffix}}"></select>

View File

@ -0,0 +1,6 @@
<select name="bot_owner_select">
<option value=''></option>
{{#each users_list}}
<option value='{{this.email}}'>{{this.full_name}}</option>
{{/each}}
</select>