2024-02-12 21:30:33 +08:00
|
|
|
|
// SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
|
|
|
|
|
//
|
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
|
|
|
|
|
|
define([
|
|
|
|
|
'jquery',
|
|
|
|
|
'/api/config',
|
|
|
|
|
'/customize/application_config.js',
|
|
|
|
|
'/common/toolbar.js',
|
|
|
|
|
'/components/nthen/index.js',
|
|
|
|
|
'/common/sframe-common.js',
|
|
|
|
|
'/common/hyperscript.js',
|
|
|
|
|
'/customize/messages.js',
|
|
|
|
|
'/common/common-interface.js',
|
|
|
|
|
'/common/common-ui-elements.js',
|
|
|
|
|
'/common/common-util.js',
|
|
|
|
|
'/common/common-hash.js',
|
2024-02-22 00:31:07 +08:00
|
|
|
|
'/common/inner/sidebar-layout.js',
|
2024-02-12 21:30:33 +08:00
|
|
|
|
'/support/ui.js',
|
|
|
|
|
|
|
|
|
|
'css!/components/components-font-awesome/css/font-awesome.min.css',
|
|
|
|
|
'less!/moderation/app-moderation.less',
|
|
|
|
|
], function (
|
|
|
|
|
$,
|
|
|
|
|
ApiConfig,
|
|
|
|
|
AppConfig,
|
|
|
|
|
Toolbar,
|
|
|
|
|
nThen,
|
|
|
|
|
SFCommon,
|
|
|
|
|
h,
|
|
|
|
|
Messages,
|
|
|
|
|
UI,
|
|
|
|
|
UIElements,
|
|
|
|
|
Util,
|
|
|
|
|
Hash,
|
2024-02-22 00:31:07 +08:00
|
|
|
|
Sidebar,
|
2024-02-28 00:17:45 +08:00
|
|
|
|
Support
|
2024-02-12 21:30:33 +08:00
|
|
|
|
)
|
|
|
|
|
{
|
2024-02-22 00:31:07 +08:00
|
|
|
|
var APP = {};
|
2024-02-12 21:30:33 +08:00
|
|
|
|
|
|
|
|
|
var common;
|
2024-02-22 00:31:07 +08:00
|
|
|
|
var sframeChan;
|
2024-02-20 21:00:09 +08:00
|
|
|
|
var events = {
|
|
|
|
|
'NEW_TICKET': Util.mkEvent(),
|
2024-02-27 02:07:39 +08:00
|
|
|
|
'UPDATE_TICKET': Util.mkEvent(),
|
|
|
|
|
'UPDATE_RIGHTS': Util.mkEvent()
|
2024-02-20 21:00:09 +08:00
|
|
|
|
};
|
2024-02-12 21:30:33 +08:00
|
|
|
|
|
2024-02-22 00:31:07 +08:00
|
|
|
|
// XXX
|
|
|
|
|
Messages.support_activeListTitle = "Active tickets";
|
|
|
|
|
Messages.support_activeListHint = "List of tickets that are in an active state";
|
|
|
|
|
Messages.support_pendingListTitle = "Pending tickets";
|
|
|
|
|
Messages.support_pendingListHint = "List of tickets that may not be updated for a while but should not be closed";
|
2024-02-21 00:50:41 +08:00
|
|
|
|
|
2024-02-22 00:31:07 +08:00
|
|
|
|
Messages.support_privacyTitle = "Answer anonymously";
|
2024-02-22 01:52:12 +08:00
|
|
|
|
Messages.support_privacyHint = "Check this option to reply as 'The Support Team' instead of your own username";
|
|
|
|
|
|
|
|
|
|
Messages.support_notificationsTitle = "Disable notifications";
|
|
|
|
|
Messages.support_notificationsHint = "Check this option to disable notifications on new or updated ticket";
|
2024-02-16 01:37:08 +08:00
|
|
|
|
|
2024-02-22 00:31:07 +08:00
|
|
|
|
var andThen = function (common, $container, linkedTicket) {
|
|
|
|
|
const sidebar = Sidebar.create(common, 'support', $container);
|
2024-02-16 01:37:08 +08:00
|
|
|
|
|
2024-02-22 00:31:07 +08:00
|
|
|
|
// Support panel functions
|
2024-02-20 21:00:09 +08:00
|
|
|
|
let open = [];
|
2024-02-28 00:17:45 +08:00
|
|
|
|
let refreshAll = function () {};
|
2024-02-21 00:50:41 +08:00
|
|
|
|
let refresh = ($container, type) => {
|
|
|
|
|
APP.module.execCommand('LIST_TICKETS_ADMIN', {
|
|
|
|
|
type: type
|
|
|
|
|
}, (tickets) => {
|
2024-02-27 02:07:39 +08:00
|
|
|
|
if (tickets.error) {
|
|
|
|
|
if (tickets.error === 'EFORBIDDEN') {
|
|
|
|
|
return void UI.errorLoadingScreen(Messages.admin_authError || '403 Forbidden');
|
|
|
|
|
}
|
|
|
|
|
return void UI.errorLoadingScreen(tickets.error);
|
|
|
|
|
}
|
|
|
|
|
UI.removeLoadingScreen();
|
|
|
|
|
|
2024-02-20 21:00:09 +08:00
|
|
|
|
let activeForms = {};
|
|
|
|
|
$container.find('.cp-support-form-container').each((i, el) => {
|
|
|
|
|
let id = $(el).attr('data-id');
|
|
|
|
|
if (!id) { return; }
|
|
|
|
|
activeForms[id] = el;
|
|
|
|
|
});
|
2024-02-16 01:37:08 +08:00
|
|
|
|
$container.empty();
|
|
|
|
|
var col1 = h('div.cp-support-column', h('h1', [
|
|
|
|
|
h('span', Messages.admin_support_premium),
|
|
|
|
|
h('span.cp-support-count'),
|
|
|
|
|
]));
|
|
|
|
|
var col2 = h('div.cp-support-column', h('h1', [
|
|
|
|
|
h('span', Messages.admin_support_normal),
|
|
|
|
|
h('span.cp-support-count'),
|
|
|
|
|
]));
|
|
|
|
|
var col3 = h('div.cp-support-column', h('h1', [
|
|
|
|
|
h('span', Messages.admin_support_answered),
|
|
|
|
|
h('span.cp-support-count'),
|
|
|
|
|
]));
|
2024-02-21 00:50:41 +08:00
|
|
|
|
var col4 = h('div.cp-support-column', h('h1', [
|
|
|
|
|
h('span', Messages.admin_support_closed),
|
|
|
|
|
h('span.cp-support-count'),
|
|
|
|
|
]));
|
|
|
|
|
if (type === 'closed') {
|
|
|
|
|
// Only one column
|
|
|
|
|
col1 = col2 = col3 = col4;
|
|
|
|
|
}
|
2024-02-16 01:37:08 +08:00
|
|
|
|
$container.append([col1, col2, col3]);
|
|
|
|
|
var sortTicket = function (c1, c2) {
|
|
|
|
|
return tickets[c2].time - tickets[c1].time;
|
|
|
|
|
};
|
2024-02-12 21:30:33 +08:00
|
|
|
|
|
2024-02-20 21:00:09 +08:00
|
|
|
|
const onShow = function (ticket, channel, data, done) {
|
2024-02-16 01:37:08 +08:00
|
|
|
|
APP.module.execCommand('LOAD_TICKET_ADMIN', {
|
|
|
|
|
channel: channel,
|
2024-02-27 23:52:30 +08:00
|
|
|
|
curvePublic: data.authorKey,
|
|
|
|
|
supportKey: data.supportKey
|
2024-02-16 01:37:08 +08:00
|
|
|
|
}, function (obj) {
|
|
|
|
|
if (!Array.isArray(obj)) {
|
|
|
|
|
console.error(obj && obj.error);
|
2024-02-20 21:00:09 +08:00
|
|
|
|
done();
|
2024-02-16 01:37:08 +08:00
|
|
|
|
return void UI.warn(Messages.error);
|
|
|
|
|
}
|
2024-02-21 00:50:41 +08:00
|
|
|
|
var $ticket = $(ticket);
|
2024-02-16 01:37:08 +08:00
|
|
|
|
obj.forEach(function (msg) {
|
2024-02-19 22:20:50 +08:00
|
|
|
|
if (!data.notifications) {
|
|
|
|
|
data.notifications = Util.find(msg, ['sender', 'notifications']);
|
|
|
|
|
}
|
2024-02-21 00:50:41 +08:00
|
|
|
|
if (msg.close) {
|
|
|
|
|
$ticket.addClass('cp-support-list-closed');
|
|
|
|
|
return $ticket.append(APP.support.makeCloseMessage(msg));
|
|
|
|
|
}
|
|
|
|
|
$ticket.append(APP.support.makeMessage(msg));
|
2024-02-16 01:37:08 +08:00
|
|
|
|
});
|
2024-02-20 21:00:09 +08:00
|
|
|
|
if (!open.includes(channel)) { open.push(channel); }
|
|
|
|
|
done();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
const onHide = function (ticket, channel, data, done) {
|
|
|
|
|
$(ticket).find('.cp-support-list-message').remove();
|
|
|
|
|
open = open.filter((chan) => {
|
|
|
|
|
return chan !== channel;
|
2024-02-16 01:37:08 +08:00
|
|
|
|
});
|
2024-02-20 21:00:09 +08:00
|
|
|
|
done();
|
2024-02-16 01:37:08 +08:00
|
|
|
|
};
|
2024-02-28 00:17:45 +08:00
|
|
|
|
const onReply = function (ticket, channel, data, form) {
|
2024-02-16 01:37:08 +08:00
|
|
|
|
var formData = APP.support.getFormData(form);
|
|
|
|
|
APP.module.execCommand('REPLY_TICKET_ADMIN', {
|
|
|
|
|
channel: channel,
|
|
|
|
|
curvePublic: data.authorKey,
|
2024-02-19 22:20:50 +08:00
|
|
|
|
notifChannel: data.notifications,
|
2024-02-27 23:52:30 +08:00
|
|
|
|
supportKey: data.supportKey,
|
2024-02-16 01:37:08 +08:00
|
|
|
|
ticket: formData
|
|
|
|
|
}, function (obj) {
|
|
|
|
|
if (obj && obj.error) {
|
|
|
|
|
console.error(obj && obj.error);
|
|
|
|
|
return void UI.warn(Messages.error);
|
|
|
|
|
}
|
|
|
|
|
$(ticket).find('.cp-support-list-message').remove();
|
2024-02-20 21:00:09 +08:00
|
|
|
|
$(ticket).find('.cp-support-form-container').remove();
|
2024-02-21 00:50:41 +08:00
|
|
|
|
refresh($container, type);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
const onClose = function (ticket, channel, data) {
|
|
|
|
|
APP.module.execCommand('CLOSE_TICKET_ADMIN', {
|
|
|
|
|
channel: channel,
|
|
|
|
|
curvePublic: data.authorKey,
|
|
|
|
|
notifChannel: data.notifications,
|
2024-02-27 23:52:30 +08:00
|
|
|
|
supportKey: data.supportKey,
|
2024-02-21 00:50:41 +08:00
|
|
|
|
ticket: APP.support.getDebuggingData({
|
|
|
|
|
close: true
|
|
|
|
|
})
|
|
|
|
|
}, function (obj) {
|
|
|
|
|
if (obj && obj.error) {
|
|
|
|
|
console.error(obj && obj.error);
|
|
|
|
|
return void UI.warn(Messages.error);
|
|
|
|
|
}
|
|
|
|
|
refreshAll();
|
2024-02-16 01:37:08 +08:00
|
|
|
|
});
|
|
|
|
|
};
|
2024-02-12 21:30:33 +08:00
|
|
|
|
|
2024-02-16 01:37:08 +08:00
|
|
|
|
Object.keys(tickets).sort(sortTicket).forEach(function (channel) {
|
|
|
|
|
var d = tickets[channel];
|
2024-02-20 21:00:09 +08:00
|
|
|
|
var ticket = APP.support.makeTicket({
|
|
|
|
|
id: channel,
|
|
|
|
|
content: d,
|
|
|
|
|
form: activeForms[channel],
|
|
|
|
|
onShow, onHide, onClose, onReply
|
|
|
|
|
});
|
|
|
|
|
|
2024-02-16 01:37:08 +08:00
|
|
|
|
var container;
|
|
|
|
|
if (d.lastAdmin) { container = col3; }
|
|
|
|
|
else if (d.premium) { container = col1; }
|
|
|
|
|
else { container = col2; }
|
|
|
|
|
$(container).append(ticket);
|
2024-02-20 21:00:09 +08:00
|
|
|
|
|
|
|
|
|
if (open.includes(channel)) { return void ticket.open(); }
|
|
|
|
|
if (linkedTicket === channel) {
|
|
|
|
|
linkedTicket = undefined;
|
|
|
|
|
ticket.open();
|
|
|
|
|
ticket.scrollIntoView();
|
|
|
|
|
}
|
2024-02-16 01:37:08 +08:00
|
|
|
|
});
|
2024-02-20 21:00:09 +08:00
|
|
|
|
open = [];
|
2024-02-21 00:50:41 +08:00
|
|
|
|
console.log(type, tickets);
|
2024-02-16 01:37:08 +08:00
|
|
|
|
});
|
|
|
|
|
};
|
2024-02-22 00:31:07 +08:00
|
|
|
|
|
|
|
|
|
let activeContainer, pendingContainer, closedContainer;
|
2024-02-28 00:17:45 +08:00
|
|
|
|
refreshAll = function () {
|
2024-02-22 00:31:07 +08:00
|
|
|
|
refresh($(activeContainer), 'active');
|
|
|
|
|
refresh($(pendingContainer), 'pending');
|
|
|
|
|
refresh($(closedContainer), 'closed');
|
2024-02-21 00:50:41 +08:00
|
|
|
|
};
|
2024-02-22 01:52:12 +08:00
|
|
|
|
let _refresh = Util.throttle(refreshAll, 500);
|
|
|
|
|
events.NEW_TICKET.reg(_refresh);
|
|
|
|
|
events.UPDATE_TICKET.reg(_refresh);
|
2024-02-27 02:07:39 +08:00
|
|
|
|
events.UPDATE_RIGHTS.reg(_refresh);
|
2024-02-21 00:50:41 +08:00
|
|
|
|
|
2024-02-22 00:31:07 +08:00
|
|
|
|
// Make sidebar layout
|
|
|
|
|
const categories = {
|
|
|
|
|
'open': {
|
|
|
|
|
icon: undefined,
|
|
|
|
|
content: [
|
|
|
|
|
'privacy',
|
|
|
|
|
'active-list',
|
|
|
|
|
'pending-list',
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
'closed': {
|
|
|
|
|
icon: undefined,
|
|
|
|
|
content: [
|
|
|
|
|
'closed-list'
|
|
|
|
|
]
|
|
|
|
|
},
|
2024-02-22 01:52:12 +08:00
|
|
|
|
'settings': {
|
|
|
|
|
icon: undefined,
|
|
|
|
|
content: [
|
|
|
|
|
'notifications'
|
|
|
|
|
]
|
|
|
|
|
},
|
2024-02-22 00:31:07 +08:00
|
|
|
|
'refresh': {
|
|
|
|
|
icon: undefined,
|
2024-02-22 01:52:12 +08:00
|
|
|
|
onClick: refreshAll
|
2024-02-22 00:31:07 +08:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sidebar.addCheckboxItem({
|
|
|
|
|
key: 'privacy',
|
|
|
|
|
getState: () => false,
|
|
|
|
|
query: (val, setState) => {
|
|
|
|
|
APP.support.setAnonymous(val);
|
|
|
|
|
setState(val);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
sidebar.addItem('active-list', cb => {
|
2024-02-28 00:17:45 +08:00
|
|
|
|
activeContainer = h('div.cp-support-container'); // XXX block
|
|
|
|
|
cb(activeContainer);
|
2024-02-22 00:31:07 +08:00
|
|
|
|
});
|
|
|
|
|
sidebar.addItem('pending-list', cb => {
|
2024-02-28 00:17:45 +08:00
|
|
|
|
pendingContainer = h('div.cp-support-container');
|
|
|
|
|
cb(pendingContainer);
|
2024-02-22 00:31:07 +08:00
|
|
|
|
});
|
|
|
|
|
sidebar.addItem('closed-list', cb => {
|
2024-02-28 00:17:45 +08:00
|
|
|
|
closedContainer = h('div.cp-support-container');
|
|
|
|
|
cb(closedContainer);
|
2024-02-22 00:31:07 +08:00
|
|
|
|
}, { noTitle: true, noHint: true });
|
2024-02-21 00:50:41 +08:00
|
|
|
|
refreshAll();
|
2024-02-22 00:31:07 +08:00
|
|
|
|
|
2024-02-22 01:52:12 +08:00
|
|
|
|
sidebar.addCheckboxItem({
|
|
|
|
|
key: 'notifications',
|
|
|
|
|
getState: () => APP.disableSupportNotif,
|
|
|
|
|
query: (val, setState) => {
|
|
|
|
|
common.setAttribute(['general', 'disableSupportNotif'], val, function (err) {
|
|
|
|
|
if (err) { val = APP.disableSupportNotif; }
|
|
|
|
|
APP.disableSupportNotif = val;
|
|
|
|
|
setState(val);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2024-02-22 00:31:07 +08:00
|
|
|
|
sidebar.makeLeftside(categories);
|
2024-02-12 21:30:33 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var createToolbar = function () {
|
|
|
|
|
var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle', 'notifications'];
|
|
|
|
|
var configTb = {
|
|
|
|
|
displayed: displayed,
|
|
|
|
|
sfCommon: common,
|
|
|
|
|
$container: APP.$toolbar,
|
|
|
|
|
pageTitle: Messages.supportPage,
|
|
|
|
|
metadataMgr: common.getMetadataMgr(),
|
|
|
|
|
};
|
|
|
|
|
APP.toolbar = Toolbar.create(configTb);
|
|
|
|
|
APP.toolbar.$rightside.hide();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
nThen(function (waitFor) {
|
|
|
|
|
$(waitFor(UI.addLoadingScreen));
|
|
|
|
|
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
|
|
|
|
|
}).nThen(function (waitFor) {
|
2024-02-22 00:31:07 +08:00
|
|
|
|
APP.$container = $('#cp-sidebarlayout-container');
|
2024-02-12 21:30:33 +08:00
|
|
|
|
APP.$toolbar = $('#cp-toolbar');
|
2024-02-22 00:31:07 +08:00
|
|
|
|
sframeChan = common.getSframeChannel();
|
|
|
|
|
sframeChan.onReady(waitFor());
|
2024-02-22 01:52:12 +08:00
|
|
|
|
}).nThen(function (waitFor) {
|
|
|
|
|
common.getAttribute(['general', 'disableSupportNotif'], waitFor(function (err, value) {
|
|
|
|
|
APP.disableSupportNotif = !!value;
|
|
|
|
|
}));
|
2024-02-12 21:30:33 +08:00
|
|
|
|
}).nThen(function (/*waitFor*/) {
|
|
|
|
|
createToolbar();
|
|
|
|
|
var metadataMgr = common.getMetadataMgr();
|
|
|
|
|
var privateData = metadataMgr.getPrivateData();
|
|
|
|
|
common.setTabTitle(Messages.supportPage);
|
|
|
|
|
|
2024-02-27 23:52:30 +08:00
|
|
|
|
if (!ApiConfig.supportMailboxKey) {
|
|
|
|
|
return void UI.errorLoadingScreen(Messages.support_disabledTitle);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-12 21:30:33 +08:00
|
|
|
|
APP.privateKey = privateData.supportPrivateKey;
|
|
|
|
|
APP.origin = privateData.origin;
|
|
|
|
|
APP.readOnly = privateData.readOnly;
|
2024-02-20 21:00:09 +08:00
|
|
|
|
APP.module = common.makeUniversal('support', {
|
|
|
|
|
onEvent: (obj) => {
|
|
|
|
|
let cmd = obj.ev;
|
|
|
|
|
let data = obj.data;
|
|
|
|
|
if (!events[cmd]) { return; }
|
|
|
|
|
events[cmd].fire(data);
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-02-12 21:30:33 +08:00
|
|
|
|
APP.support = Support.create(common, true);
|
|
|
|
|
|
2024-02-20 21:00:09 +08:00
|
|
|
|
let active = privateData.category || 'active';
|
|
|
|
|
let linkedTicket;
|
|
|
|
|
if (active.indexOf('-') !== -1) {
|
|
|
|
|
linkedTicket = active.split('-')[1];
|
|
|
|
|
active = active.split('-')[0];
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-22 00:31:07 +08:00
|
|
|
|
andThen(common, APP.$container, linkedTicket);
|
2024-02-12 21:30:33 +08:00
|
|
|
|
UI.removeLoadingScreen();
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
});
|