mirror of https://github.com/xwiki-labs/cryptpad
New support: answer anonymously for admins
This commit is contained in:
parent
62052929e6
commit
06d9201bbd
|
@ -44,6 +44,14 @@
|
|||
.avatar_main(30px);
|
||||
padding: 0 5px;
|
||||
}
|
||||
.cp-avatar-image {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
img {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
.cp-notification-content {
|
||||
flex: 1;
|
||||
align-items: stretch;
|
||||
|
|
|
@ -497,7 +497,7 @@ define([
|
|||
};
|
||||
content.handler = function () {
|
||||
let url = msg.isAdmin ? '/support/#tickets' : `/moderation/#support-${content.channel}`;
|
||||
common.openURL(msg.url);
|
||||
common.openURL(url);
|
||||
defaultDismiss(common, data)();
|
||||
};
|
||||
if (!content.archived) {
|
||||
|
|
|
@ -212,6 +212,9 @@ define([
|
|||
title: data.ticket.title,
|
||||
isClose: data.ticket.close,
|
||||
channel: data.channel,
|
||||
user: Util.find(data.ticket, ['sender', 'curvePublic']) ? undefined : {
|
||||
supportTeam: true
|
||||
}
|
||||
}, {
|
||||
channel: notifChannel,
|
||||
curvePublic: notifKey
|
||||
|
|
|
@ -87,6 +87,8 @@ define([
|
|||
e.stopPropagation();
|
||||
Common.openURL(Hash.hashToHref(userData.profile, 'profile'));
|
||||
});
|
||||
} else if (userData && userData.supportTeam) {
|
||||
avatar = h('span.cp-avatar-image', h('img', { src:'/customize/CryptPad_logo.svg' }));
|
||||
}
|
||||
var order = -Math.floor((Util.find(data, ['content', 'msg', 'ctime']) || 0) / 1000);
|
||||
const tabIndexValue = undefined;//data.content.isDismissible ? undefined : '0';
|
||||
|
|
|
@ -17,6 +17,6 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
|||
</head>
|
||||
<body class="cp-app-moderation">
|
||||
<div id="cp-toolbar" class="cp-toolbar-container"></div>
|
||||
<div id="cp-content-container"></div>
|
||||
<div id="cp-sidebarlayout-container"></div>
|
||||
</body>
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ define([
|
|||
'jquery',
|
||||
'/api/config',
|
||||
'/customize/application_config.js',
|
||||
'/components/chainpad-crypto/crypto.js',
|
||||
'/common/toolbar.js',
|
||||
'/components/nthen/index.js',
|
||||
'/common/sframe-common.js',
|
||||
|
@ -16,20 +15,15 @@ define([
|
|||
'/common/common-ui-elements.js',
|
||||
'/common/common-util.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-signing-keys.js',
|
||||
'/common/inner/sidebar-layout.js',
|
||||
'/support/ui.js',
|
||||
'/common/clipboard.js',
|
||||
'json.sortify',
|
||||
|
||||
'css!/lib/datepicker/flatpickr.min.css',
|
||||
'css!/components/bootstrap/dist/css/bootstrap.min.css',
|
||||
'css!/components/components-font-awesome/css/font-awesome.min.css',
|
||||
'less!/moderation/app-moderation.less',
|
||||
], function (
|
||||
$,
|
||||
ApiConfig,
|
||||
AppConfig,
|
||||
Crypto,
|
||||
Toolbar,
|
||||
nThen,
|
||||
SFCommon,
|
||||
|
@ -39,38 +33,33 @@ define([
|
|||
UIElements,
|
||||
Util,
|
||||
Hash,
|
||||
Keys,
|
||||
Sidebar,
|
||||
Support,
|
||||
Clipboard,
|
||||
Sortify
|
||||
)
|
||||
{
|
||||
var APP = {
|
||||
'instanceStatus': {}
|
||||
};
|
||||
var APP = {};
|
||||
|
||||
var Nacl = window.nacl;
|
||||
var common;
|
||||
var sFrameChan;
|
||||
var sframeChan;
|
||||
var events = {
|
||||
'NEW_TICKET': Util.mkEvent(),
|
||||
'UPDATE_TICKET': Util.mkEvent()
|
||||
};
|
||||
|
||||
var andThen = function (linkedTicket) {
|
||||
var $body = $('#cp-content-container');
|
||||
// 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";
|
||||
|
||||
var button = h('button.btn.btn-primary', 'refresh'); // XXX
|
||||
$body.append(h('div', button));
|
||||
|
||||
$body.append(h('h1'), 'ACTIVE');
|
||||
var $containerActive = $(h('div.cp-support-container')).appendTo($body);
|
||||
$body.append(h('h1'), 'PENDING');
|
||||
var $containerPending = $(h('div.cp-support-container')).appendTo($body);
|
||||
$body.append(h('h1'), 'CLOSED');
|
||||
var $containerClosed = $(h('div.cp-support-container')).appendTo($body);
|
||||
Messages.support_privacyTitle = "Answer anonymously";
|
||||
Messages.support_privacyHint = "Check this option to reply as 'The Support Team' instead of your own usenrame";
|
||||
|
||||
var andThen = function (common, $container, linkedTicket) {
|
||||
const sidebar = Sidebar.create(common, 'support', $container);
|
||||
const blocks = sidebar.blocks;
|
||||
|
||||
// Support panel functions
|
||||
let open = [];
|
||||
let refresh = ($container, type) => {
|
||||
APP.module.execCommand('LIST_TICKETS_ADMIN', {
|
||||
|
@ -201,18 +190,66 @@ define([
|
|||
console.log(type, tickets);
|
||||
});
|
||||
};
|
||||
|
||||
let activeContainer, pendingContainer, closedContainer;
|
||||
var refreshAll = function () {
|
||||
refresh($containerActive, 'active');
|
||||
refresh($containerPending, 'pending');
|
||||
refresh($containerClosed, 'closed');
|
||||
console.error('refresh');
|
||||
refresh($(activeContainer), 'active');
|
||||
refresh($(pendingContainer), 'pending');
|
||||
refresh($(closedContainer), 'closed');
|
||||
};
|
||||
|
||||
// Make sidebar layout
|
||||
const categories = {
|
||||
'open': {
|
||||
icon: undefined,
|
||||
content: [
|
||||
'privacy',
|
||||
'active-list',
|
||||
'pending-list',
|
||||
]
|
||||
},
|
||||
'closed': {
|
||||
icon: undefined,
|
||||
content: [
|
||||
'closed-list'
|
||||
]
|
||||
},
|
||||
'refresh': {
|
||||
icon: undefined,
|
||||
action: refreshAll
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
sidebar.addCheckboxItem({
|
||||
key: 'privacy',
|
||||
getState: () => false,
|
||||
query: (val, setState) => {
|
||||
APP.support.setAnonymous(val);
|
||||
setState(val);
|
||||
}
|
||||
});
|
||||
sidebar.addItem('active-list', cb => {
|
||||
let div = activeContainer = h('div.cp-support-container'); // XXX block
|
||||
cb(div);
|
||||
});
|
||||
sidebar.addItem('pending-list', cb => {
|
||||
let div = pendingContainer = h('div.cp-support-container'); // XXX block
|
||||
cb(div);
|
||||
});
|
||||
sidebar.addItem('closed-list', cb => {
|
||||
let div = closedContainer = h('div.cp-support-container'); // XXX block
|
||||
cb(div);
|
||||
}, { noTitle: true, noHint: true });
|
||||
|
||||
let _refresh = Util.throttle(refreshAll, 500);
|
||||
events.NEW_TICKET.reg(_refresh);
|
||||
events.UPDATE_TICKET.reg(_refresh);
|
||||
|
||||
Util.onClickEnter($(button), refreshAll); // XXX temp button?
|
||||
refreshAll();
|
||||
|
||||
sidebar.makeLeftside(categories);
|
||||
};
|
||||
|
||||
var createToolbar = function () {
|
||||
|
@ -232,10 +269,10 @@ define([
|
|||
$(waitFor(UI.addLoadingScreen));
|
||||
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
|
||||
}).nThen(function (waitFor) {
|
||||
APP.$container = $('#cp-content-container');
|
||||
APP.$container = $('#cp-sidebarlayout-container');
|
||||
APP.$toolbar = $('#cp-toolbar');
|
||||
sFrameChan = common.getSframeChannel();
|
||||
sFrameChan.onReady(waitFor());
|
||||
sframeChan = common.getSframeChannel();
|
||||
sframeChan.onReady(waitFor());
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
createToolbar();
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
|
@ -266,7 +303,7 @@ define([
|
|||
active = active.split('-')[0];
|
||||
}
|
||||
|
||||
andThen(linkedTicket);
|
||||
andThen(common, APP.$container, linkedTicket);
|
||||
UI.removeLoadingScreen();
|
||||
|
||||
});
|
||||
|
|
|
@ -15,6 +15,9 @@ define([
|
|||
'/customize/pages.js',
|
||||
], function ($, ApiConfig, h, UI, Hash, Util, Clipboard, UIElements, Messages, Pages) {
|
||||
|
||||
Messages.support_team = "The Support Team"; // XXX
|
||||
Messages.support_answerAs = "Answer as <b>{0}</b>"; // XXX
|
||||
|
||||
var getDebuggingData = function (ctx, data) {
|
||||
var common = ctx.common;
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
|
@ -34,6 +37,14 @@ define([
|
|||
notifications: user.notifications,
|
||||
};
|
||||
|
||||
if (ctx.isAdmin && ctx.anonymous) {
|
||||
data.sender = {
|
||||
name: Messages.support_team,
|
||||
accountName: 'support'
|
||||
// XXX send edPublic? or keep it private
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof(ctx.pinUsage) === 'object') {
|
||||
// pass pin.usage, pin.limit, and pin.plan if supplied
|
||||
data.sender.quota = ctx.pinUsage;
|
||||
|
@ -63,45 +74,6 @@ define([
|
|||
return data;
|
||||
};
|
||||
|
||||
var send = function (ctx, id, type, data, dest) {
|
||||
var common = ctx.common;
|
||||
var supportKey = ApiConfig.supportMailbox;
|
||||
var supportChannel = Hash.getChannelIdFromKey(supportKey);
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var user = metadataMgr.getUserData();
|
||||
var privateData = metadataMgr.getPrivateData();
|
||||
|
||||
data = getDebuggingData(ctx, data);
|
||||
|
||||
data.id = id;
|
||||
data.time = +new Date();
|
||||
|
||||
if (!ctx.isAdmin) {
|
||||
// "dest" is the recipient that is not the admin support mailbox.
|
||||
// In the support page, make sure dest is always ourselves.
|
||||
dest.channel = privateData.support;
|
||||
dest.curvePublic = user.curvePublic;
|
||||
}
|
||||
|
||||
// Send the message to the admin mailbox and to the user mailbox
|
||||
common.mailbox.sendTo(type, data, {
|
||||
channel: supportChannel,
|
||||
curvePublic: supportKey
|
||||
});
|
||||
common.mailbox.sendTo(type, data, {
|
||||
channel: dest.channel,
|
||||
curvePublic: dest.curvePublic
|
||||
});
|
||||
|
||||
if (ctx.isAdmin) {
|
||||
common.mailbox.sendTo('SUPPORT_MESSAGE', {}, {
|
||||
channel: dest.notifications,
|
||||
curvePublic: dest.curvePublic
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var getFormData = function (ctx, form) {
|
||||
var $form = $(form);
|
||||
var $cat = $form.find('.cp-support-form-category');
|
||||
|
@ -140,10 +112,6 @@ define([
|
|||
message: content,
|
||||
});
|
||||
};
|
||||
var sendForm = function (ctx, id, form, dest) {
|
||||
send(ctx, id, 'TICKET', getFormData(ctx, form), dest);
|
||||
return true;
|
||||
};
|
||||
|
||||
var makeCategoryDropdown = function (ctx, container, onChange, all) {
|
||||
var categories = [
|
||||
|
@ -187,6 +155,14 @@ define([
|
|||
abuse: Pages.customURLs.terms,
|
||||
};
|
||||
|
||||
var getAnswerAs = function (ctx) {
|
||||
var common = ctx.common;
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var user = metadataMgr.getUserData();
|
||||
var answerName = Util.fixHTML(ctx.anonymous ? Messages.support_team : user.name);
|
||||
return Messages._getKey('support_answerAs', [answerName]);
|
||||
};
|
||||
|
||||
var makeForm = function (ctx, oldData, cb, title, hideNotice) {
|
||||
var button;
|
||||
|
||||
|
@ -239,6 +215,8 @@ define([
|
|||
var attachments, addAttachment;
|
||||
|
||||
|
||||
var answerAs = UI.setHTML(h('span.cp-support-answeras'), getAnswerAs(ctx));
|
||||
|
||||
var content = [
|
||||
h('hr'),
|
||||
category,
|
||||
|
@ -254,12 +232,14 @@ define([
|
|||
h('textarea.cp-support-form-msg', {
|
||||
placeholder: Messages.support_formMessage
|
||||
}, (oldData && oldData.message) || ''),
|
||||
h('br'),
|
||||
h('label', Messages.support_attachments),
|
||||
attachments = h('div.cp-support-attachments'),
|
||||
addAttachment = h('button.btn', Messages.support_addAttachment),
|
||||
h('hr'),
|
||||
button,
|
||||
cancel
|
||||
cancel,
|
||||
ctx.isAdmin? answerAs : undefined
|
||||
];
|
||||
|
||||
var _addAttachment = (name, href) => {
|
||||
|
@ -440,7 +420,8 @@ define([
|
|||
// Check content.sender to see if it comes from us or from an admin
|
||||
var senderKey = content.sender && content.sender.edPublic;
|
||||
var fromMe = senderKey === privateData.edPublic;
|
||||
var fromAdmin = ctx.adminKeys.indexOf(senderKey) !== -1;
|
||||
var fromAdmin = ctx.adminKeys.indexOf(senderKey) !== -1
|
||||
|| (!senderKey && content.sender.accountName === 'support'); // XXX anon key?
|
||||
var fromPremium = Boolean(content.sender.plan || Util.find(content, ['sender', 'quota', 'plan']));
|
||||
|
||||
var userData = h('div.cp-support-showdata', [
|
||||
|
@ -550,6 +531,10 @@ define([
|
|||
};
|
||||
ctx.FM = common.createFileManager(fmConfig);
|
||||
|
||||
ui.setAnonymous = function (val) {
|
||||
ctx.anonymous = ctx.isAdmin && val;
|
||||
$('.cp-support-answeras').html(getAnswerAs(ctx));
|
||||
};
|
||||
ui.getFormData = function (form) {
|
||||
return getFormData(ctx, form);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue