Add new support initialization to refactored admin page

This commit is contained in:
yflory 2024-03-19 15:36:57 +01:00
parent eb746731cc
commit 7ddd8cd468
6 changed files with 305 additions and 32 deletions

View File

@ -2621,18 +2621,18 @@ Example
};
// XXX
Messages.admin_supportNewHint = "Create or update the current support keys.";
Messages.admin_supportNewTitle = "Initialize support";
Messages.admin_supportNewEnabled = "Modern support system is enabled.";
Messages.admin_supportNewDisabled = "Modern support system is disabled.";
Messages.admin_supportNewInit = "Initialize support page on this instance";
Messages.admin_supportNewDelete = "Disable support";
Messages.admin_supportNewConfirm = "Are you sure? This will remove access to all current moderators and delete all existing tickets.";
Messages.admin_supportSetupHint = "Create or update the current support keys.";
Messages.admin_supportSetupTitle = "Initialize support";
Messages.admin_supportEnabled = "Modern support system is enabled.";
Messages.admin_supportDisabled = "Modern support system is disabled.";
Messages.admin_supportInit = "Initialize support page on this instance";
Messages.admin_supportDelete = "Disable support";
Messages.admin_supportConfirm = "Are you sure? This will remove access to all current moderators and delete all existing tickets.";
Messages.admin_supportMembers = "Current support team";
Messages.admin_supportAdd = "Add a contact to the support team";
Messages.admin_supportRotateNotify = "Warning: new keys have been generated but an unenexpected error prevented the system to send them to the moderators. You may have to remove and re-add all the other moderators";
create['support-new'] = function () {
const $block = makeBlock('support-new'); // Msg.admin_supportNewHint, .admin_supportNewTitle
create['support-setup'] = function () {
const $block = makeBlock('support-setup'); // Msg.admin_supportNewHint, .admin_supportNewTitle
const $div = $(h('div')).appendTo($block);
let supportKey = ApiConfig.supportMailboxKey;
let edPublic = common.getMetadataMgr().getPrivateData().edPublic; // My edPublic
@ -2642,9 +2642,9 @@ Example
const state = h('div');
const $state = $(state).appendTo($div);
const button = h('button.btn.btn-primary', Messages.admin_supportNewInit);
const button = h('button.btn.btn-primary', Messages.admin_supportInit);
const $button = $(button).appendTo($div);
const delButton = h('button.btn.btn-danger', Messages.admin_supportNewDelete);
const delButton = h('button.btn.btn-danger', Messages.admin_supportDelete);
const $delButton = $(delButton).appendTo($div).hide();
const spinner = UI.makeSpinner($div);
@ -2655,24 +2655,19 @@ Example
$delButton.show();
return $state.append([
h('i.fa.fa-check'),
h('span', Messages.admin_supportNewEnabled)
h('span', Messages.admin_supportEnabled)
]);
}
$button.show();
$state.append([
h('i.fa.fa-times'),
h('span', Messages.admin_supportNewDisabled)
h('span', Messages.admin_supportDisabled)
]);
};
setState();
// XXX TODO add/remove access
// XXX when removing access, send new private keys to everybody who should keep access AND move chainpad doc to the new one
// ==> we'll need to delete the old chainpad doc using admin commands
Util.onClickEnter($delButton, function () {
UI.confirm(Messages.admin_supportNewConfirm, function (yes) {
UI.confirm(Messages.admin_supportConfirm, function (yes) {
if (!yes) { return; }
// Send the decree, don't delete data locally, we just want to remove
// the support UI for the clients

View File

@ -215,6 +215,7 @@ define(function() {
teams: 'fa-users',
admin: 'fa-gears',
settings: 'fa-gear',
moderation: 'fa-comments-o',
profile: 'fa-user-circle',
support: 'fa-life-ring',
accounts: 'fa-ticket'

View File

@ -2037,7 +2037,7 @@ define([
options.push({ tag: 'hr' });
// Add administration panel link if the user is an admin
if (priv.edPublic && Array.isArray(Config.adminKeys) && Config.adminKeys.indexOf(priv.edPublic) !== -1) {
if (priv.edPublic && Array.isArray(Config.adminKeys) && Config.adminKeys.includes(priv.edPublic)) {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-admin fa fa-cogs'},
@ -2051,6 +2051,19 @@ define([
},
});
}
// Add moderation panel link if the user is a moderator and support is enabled
if (priv.edPublic && Config.supportMailboxKey && Array.isArray(Config.moderatorKeys) && Config.moderatorKeys.includes(priv.edPublic)) {
Messages.moderationPage = "Support panel"; // XXX already in moderation/inner.js
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-admin fa fa-comments-o'},
content: h('span', Messages.moderationPage || 'Support panel'),
action: function () {
Common.openURL(origin+'/moderation/');
return true;
},
});
}
options.push({
tag: 'a',
attributes: {

View File

@ -44,14 +44,15 @@ define([
]);
};
blocks.icon = (icon) => {
let prefix = icon.slice(0, icon.indexOf('-'));
let cls = `.${prefix}.${icon}`;
return h(`i${cls}`);
};
blocks.button = (type, icon, text) => {
type = type || 'primary';
if (icon && icon.indexOf('-') !== -1) {
let prefix = icon.slice(0, icon.indexOf('-'));
icon = `${prefix} ${icon}`;
}
return h(`button.btn.btn-${type}`, [
icon ? h('i', { 'class': icon }) : undefined,
icon ? blocks.icon(icon) : undefined,
h('span', text)
]);
};
@ -81,7 +82,9 @@ define([
return h('span', value);
};
blocks.block = (content, className) => {
return h('div', { class: className }, content);
let attr = {};
if (className) { attr.class = className; }
return h('div', attr, content);
};
blocks.paragraph = (content) => {
return h('p', content);

View File

@ -54,6 +54,8 @@ define([
};
// XXX
Messages.moderationPage = "Support panel"; // XXX
Messages.support_pending = "Pending tickets:";
Messages.support_pending_tag = "Pending";
Messages.support_active_tag = "Active";
@ -954,7 +956,7 @@ Attachments:${JSON.stringify(msg.attachments, 0, 2)}`;
displayed: displayed,
sfCommon: common,
$container: APP.$toolbar,
pageTitle: Messages.supportPage,
pageTitle: Messages.moderationPage,
metadataMgr: common.getMetadataMgr(),
};
APP.toolbar = Toolbar.create(configTb);
@ -977,7 +979,7 @@ Attachments:${JSON.stringify(msg.attachments, 0, 2)}`;
createToolbar();
var metadataMgr = common.getMetadataMgr();
var privateData = metadataMgr.getPrivateData();
common.setTabTitle(Messages.supportPage);
common.setTabTitle(Messages.moderationPage);
if (!ApiConfig.supportMailboxKey) {
return void UI.errorLoadingScreen(Messages.support_disabledTitle);

View File

@ -111,9 +111,7 @@ define([
'support' : {
icon : 'fa fa-life-ring',
content : [
'support-list',
'support-init',
'support-priv',
'support-setup',
]
},
'broadcast' : {
@ -2574,6 +2572,266 @@ define([
};
// XXX DUPLICATE FROM ADMIN/INNER.JS
Messages.admin_supportSetupHint = "Create or update the current support keys.";
Messages.admin_supportSetupTitle = "Initialize support";
Messages.admin_supportEnabled = "Modern support system is enabled.";
Messages.admin_supportDisabled = "Modern support system is disabled.";
Messages.admin_supportInit = "Initialize support page on this instance";
Messages.admin_supportDelete = "Disable support";
Messages.admin_supportConfirm = "Are you sure? This will remove access to all current moderators and delete all existing tickets.";
Messages.admin_supportMembers = "Current support team";
Messages.admin_supportAdd = "Add a contact to the support team";
Messages.admin_supportRotateNotify = "Warning: new keys have been generated but an unenexpected error prevented the system to send them to the moderators. You may have to remove and re-add all the other moderators";
sidebar.addItem('support-setup', function (cb) {
let supportKey = ApiConfig.supportMailboxKey;
let edPublic = common.getMetadataMgr().getPrivateData().edPublic; // My edPublic
let refresh = function () {};
let content = blocks.block();
const $div = $(content);
const redraw = function (moderatorsData) {
$div.empty();
const state = blocks.block();
const $state = $(state).appendTo($div);
const button = blocks.button('primary', '', Messages.admin_supportInit);
const $button = $(button).appendTo($div);
const delButton = blocks.button('danger', '', Messages.admin_supportDelete);
const $delButton = $(delButton).appendTo($div).hide();
const spinner = UI.makeSpinner($div);
const setState = function () {
$state.html('');
if (supportKey) {
$button.hide();
$delButton.show();
return $state.append([
blocks.icon('fa-check'),
blocks.inline(Messages.admin_supportEnabled)
]);
}
$button.show();
$state.append([
blocks.icon('fa-times'),
blocks.inline(Messages.admin_supportDisabled)
]);
};
setState();
Util.onClickEnter($delButton, function () {
UI.confirm(Messages.admin_supportConfirm, function (yes) {
if (!yes) { return; }
// Send the decree, don't delete data locally, we just want to remove
// the support UI for the clients
spinner.spin();
$delButton.attr('disabled', 'disabled');
APP.supportModule.execCommand('DISABLE_SUPPORT', {}, (obj) => {
$delButton.removeAttr('disabled');
if (obj && obj.error) {
UI.warn(Messages.error);
console.error(obj.error);
return void spinner.hide();
}
spinner.done();
UI.log(Messages.saved);
supportKey = undefined;
refresh();
});
});
});
const getMyData = () => {
let metadataMgr = common.getMetadataMgr();
let priv = metadataMgr.getPrivateData();
let user = metadataMgr.getUserData();
return {
name: user.name,
edPublic: priv.edPublic,
curvePublic: user.curvePublic,
mailbox: user.notifications,
profile: user.profile
};
};
const getContactData = (curve) => {
if (!curve) { return getMyData(); }
let friends = common.getFriends(true);
let f = friends[curve];
return {
name: f.displayName,
edPublic: f.edPublic,
curvePublic: f.curvePublic,
mailbox: f.notifications,
profile: f.profile
};
};
const generateKey = function () {
if (supportKey && !moderatorsData[edPublic]) {
UI.alert("A support key already exists. You must be a moderator to generate a new one or delete the existing support data.");
return;
}
spinner.spin();
$button.attr('disabled', 'disabled');
const onError = (waitFor, err, res) => {
if (waitFor) { waitFor.abort(); }
console.error(err, res);
spinner.hide();
UI.warn(Messages.error);
};
nThen((waitFor) => {
// Add myself to moderator role if not already there
let me = getContactData();
if (moderatorsData[me.edPublic]) { return; }
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADD_MODERATOR',
data: me
}, waitFor((e, response) => {
console.error(e, response);
}));
}).nThen((waitFor) => {
// Copy chainpad doc and pin
APP.supportModule.execCommand('ROTATE_KEYS', {}, waitFor((obj) => {
$button.removeAttr('disabled');
if (obj && obj.error) { return void onError(waitFor, obj.error); }
if (obj && obj.success && obj.noNotify) {
UI.alert(Messages.admin_supportRotateNotify);
onError(waitFor, 'NOTIFY_ERROR', obj);
return;
}
spinner.done();
UI.log(Messages.saved);
}));
}).nThen(() => {
refresh();
});
};
const addModerator = (curve, _cb) => {
let cb = Util.mkAsync(_cb);
let userData = getContactData(curve);
if (!userData) { return void cb('INVALID_USER'); }
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADD_MODERATOR',
data: userData
}, (e, response) => {
if (e || (response && response.error)) {
return void cb(e || response.error);
}
// User added to support team in database, send them the keys
APP.supportModule.execCommand('ADD_MODERATOR', userData, cb);
});
};
const removeModerator = (ed) => {
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'REMOVE_MODERATOR',
data: ed
}, (e, response) => {
if (e || (response && response.error)) {
console.error(e || response.error);
return void UI.warn(Messages.error);
}
generateKey();
});
};
Util.onClickEnter($button, function () {
generateKey();
});
const drawModerators = () => {
if (!supportKey) { return; }
const members = {};
const friends = Util.clone(common.getFriends(false));
Object.keys(moderatorsData).forEach((ed) => {
let m = moderatorsData[ed];
members[m.curvePublic] = {
displayName: m.name,
edPublic: m.edPublic,
profile: m.profile,
curvePublic: m.curvePublic,
notificatons: m.mailbox
};
});
Object.keys(friends).forEach((curve) => {
if (members[curve]) { delete friends[curve]; }
});
let currentList = UIElements.getUserGrid(Messages.admin_supportMembers, {
common: common,
list: true,
large: true,
noSelect: true,
data: members,
remove: (el) => {
let ed = $(el).attr('data-ed');
removeModerator(ed);
}
});
let contactsGrid = UIElements.getUserGrid(Messages.admin_supportAdd, {
common: common,
list: true,
large: true,
data: friends
}, function () {});
let addBtn = blocks.button('primary', '', Messages.tag_add);
Util.onClickEnter($(addBtn), () => {
var $sel = $(contactsGrid.div).find('.cp-usergrid-user.cp-selected');
nThen((waitFor) => {
$sel.each((i, el) => {
let curve = $(el).attr('data-curve');
if (!curve) {
console.error('Missing data on selected user', el);
return void UI.warn(Messages.error);
}
addModerator(curve, waitFor());
});
}).nThen(() => {
refresh();
});
});
// Only moderators can add new moderators
if (!moderatorsData[edPublic]) {
contactsGrid.div = undefined;
addBtn = undefined;
}
const list = blocks.block([
currentList.div,
contactsGrid.div,
blocks.nav([addBtn])
]);
$div.append(list);
};
drawModerators();
};
refresh = () => {
let moderators;
nThen((waitFor) => {
APP.supportModule.execCommand('GET_PRIVATE_KEY', {}, waitFor((obj) => {
supportKey = obj && obj.curvePublic;
}));
}).nThen((waitFor) => {
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'GET_MODERATORS',
data: {}
}, waitFor((e, response) => {
if (e || response.error) {
console.error(e || response.error);
UI.warn(Messages.error);
return;
}
moderators = response[0];
}));
}).nThen(() => {
redraw(moderators);
});
};
refresh();
cb(content);
});
sidebar.addItem('maintenance', function(cb){
var form = blocks.form([]);
var refresh = getApi(function (Broadcast) {
@ -3377,10 +3635,11 @@ define([
// Add toolbar
createToolbar();
APP.supportModule = common.makeUniversal('support');
// Content
andThen(common, APP.$container);
common.setTabTitle(Messages.settings_title);
UI.removeLoadingScreen();
});
});