New support: migration from the legacy system

This commit is contained in:
yflory 2024-02-29 18:36:17 +01:00
parent de825af47a
commit d0d3496904
5 changed files with 223 additions and 105 deletions

View file

@ -253,6 +253,9 @@ define([
sidebar.openCategory = name => {
$(`.cp-sidebarlayout-category[data-category="${name}"]`).click();
};
sidebar.deleteCategory = name => {
$(`.cp-sidebarlayout-category[data-category="${name}"]`).remove();
};
sidebar.disableItem = (key) => {
$(items[key]).remove();

View file

@ -18,7 +18,7 @@ define([
var TYPES = [
'notifications',
'supportadmin',
//'supportadmin',
'support',
'supportteam',
'broadcast'
@ -397,6 +397,7 @@ proxy.mailboxes = {
}
msg.ctime = time || 0;
box.content[hash] = msg;
if (opts.dump) { return; }
showMessage(ctx, type, message, null, function (obj) {
if (!obj || !obj.msg || !notify) { return; }
Notify.system(undefined, obj.msg);
@ -450,7 +451,7 @@ proxy.mailboxes = {
});
box.ready = true;
// Continue
onReady();
onReady(box.content);
};
box.cpNf = CpNetflux.start(cfg);
};

View file

@ -165,7 +165,8 @@ define([
author: data.name,
supportKey: supportKey, // Store current support key
lastAdmin: true,
authorKey: data.curvePublic
authorKey: data.curvePublic,
notifications: data.notifications // Ticket created as admin, add user chan
};
}).nThen((waitFor) => {
if (isAdmin) { return; }
@ -588,6 +589,77 @@ define([
});
};
var clearLegacy = function (ctx, data, cId, cb) {
let proxy = ctx.store.proxy;
ctx.store.mailbox.close('supportadmin', function () {
delete proxy.mailboxes.supportadmin;
ctx.Store.onSync(null, function () {
cb({done: true});
});
});
};
var dumpLegacy = function (ctx, data, cId, cb) {
let proxy = ctx.store.proxy;
let _legacy = Util.find(proxy, ['mailboxes', 'supportadmin']);
if (!_legacy) { return void cb({error: 'ENOENT'}); }
let legacy = Util.clone(_legacy);
legacy.lastKnownHash = undefined;
legacy.viewed = [];
ctx.store.mailbox.open('supportadmin', legacy, function (contentByHash) {
ctx.store.mailbox.close('supportadmin', function () {});
cb(contentByHash);
}, true, { // Opts
dump: true
});
};
var getLegacy = function (ctx, data, cId, cb) {
let proxy = ctx.store.proxy;
let legacy = Util.find(proxy, ['mailboxes', 'supportadmin']);
if (!legacy) { return void cb({error: 'ENOENT'}); }
ctx.store.mailbox.open('supportadmin', legacy, function (contentByHash) {
ctx.store.mailbox.close('supportadmin', function () {});
cb(contentByHash);
}, true, { // Opts
dump: true
});
};
var restoreLegacy = function (ctx, data, cId, cb) {
let proxy = ctx.store.proxy;
let legacy = Util.find(proxy, ['mailboxes', 'supportadmin']);
if (!legacy) { return void cb({error: 'ENOENT'}); }
if (!ctx.adminRdyEvt) { return void cb({ error: 'EFORBIDDEN' }); }
let messages = data.messages;
let hashes = data.hashes;
let first = messages[0];
if (!first) { return void cb({error: 'EINVAL'}); }
ctx.adminRdyEvt.reg(() => {
let ticketData = {
name: Util.find(first, ['sender', 'name']),
notifications: Util.find(first, ['sender', 'notifications']),
curvePublic: Util.find(first, ['sender', 'curvePublic']),
channel: Hash.createChannelId(),
title: first.title,
time: first.time,
ticket: {
legacy: true,
title: first.title,
sender: first.sender,
messages: messages
}
};
makeTicket(ctx, ticketData, true, obj => {
if (obj && obj.error) { return void cb(obj); }
hashes.forEach(hash => {
legacy.viewed.push(hash);
});
ctx.Store.onSync(null, function () {
cb({done: true});
});
});
});
};
// Mailbox events
var notifyClient = function (ctx, admin, type, channel) {
@ -1115,6 +1187,18 @@ define([
if (cmd === 'MOVE_TICKET_ADMIN') {
return void moveTicketAdmin(ctx, data, clientId, cb);
}
if (cmd === 'GET_LEGACY') {
return void getLegacy(ctx, data, clientId, cb);
}
if (cmd === 'DUMP_LEGACY') {
return void dumpLegacy(ctx, data, clientId, cb);
}
if (cmd === 'CLEAR_LEGACY') {
return void clearLegacy(ctx, data, clientId, cb);
}
if (cmd === 'RESTORE_LEGACY') {
return void restoreLegacy(ctx, data, clientId, cb);
}
// Admin commands
if (cmd === 'GET_PRIVATE_KEY') {
return void getAdminKey(ctx, data, clientId, cb);

View file

@ -18,6 +18,8 @@ define([
'/common/inner/sidebar-layout.js',
'/support/ui.js',
'/components/file-saver/FileSaver.min.js',
'css!/components/components-font-awesome/css/font-awesome.min.css',
'less!/moderation/app-moderation.less',
], function (
@ -38,6 +40,7 @@ define([
)
{
var APP = {};
var saveAs = window.saveAs;
var common;
var sframeChan;
@ -131,13 +134,20 @@ define([
}
var $ticket = $(ticket);
obj.forEach(function (msg) {
if (!data.notifications) {
// Only add notifications channel if this is coming from the other user
if (!data.notifications && msg.sender.drive) {
data.notifications = Util.find(msg, ['sender', 'notifications']);
}
if (msg.close) {
$ticket.addClass('cp-support-list-closed');
return $ticket.append(APP.support.makeCloseMessage(msg));
}
if (msg.legacy && msg.messages) {
msg.messages.forEach(c => {
$ticket.append(APP.support.makeMessage(c));
});
return;
}
$ticket.append(APP.support.makeMessage(msg));
});
if (!open.includes(channel)) { open.push(channel); }
@ -386,62 +396,137 @@ define([
// XXX
Messages.support_legacyTitle = "View old support data";
Messages.support_legacyHint = "View tickets from the legacy system. You'll be able to recreate thse tickets on the new support system.";
Messages.support_legacyButton = "Start";
Messages.support_legacyButton = "Get active";
Messages.support_legacyDump = "Export all";
Messages.support_legacyClear = "Delete from my account";
sidebar.addItem('legacy', cb => {
if (!APP.privateKey) { return void cb(false); }
let start = blocks.button('primary', 'fa-paper-plane', Messages.support_legacyButton);
let dump = blocks.button('secondary', 'fa-database', Messages.support_legacyDump);
let clean = blocks.button('danger', 'fa-trash-o', Messages.support_legacyClear);
let content = h('div.cp-support-container');
let nav = blocks.nav([start, clean]);
let nav = blocks.nav([start, dump, clean]);
UI.confirmButton(clean, { classes: 'btn-danger' }, function () {
// XXX TODO remove my supportadmin mailbox
console.error('NOT IMPLEMENTED');
let sortLegacyTickets = contentByHash => {
let all = {};
Object.keys(contentByHash).forEach(key => {
let data = contentByHash[key];
let content = data.content;
let id = content.id;
content.hash = key;
if (data.ctime) { content.time = data.ctime; }
if (content.sender && content.sender.curvePublic !== data.author) { return; }
all[id] = all[id] || [];
all[id].push(content);
all[id].sort((c1, c2) => {
return c1.time - c2.time;
});
});
// sort
let sorted = Object.keys(all).sort((t1, t2) => {
let a = t1[0];
let b = t2[0];
return (a.time || 0) - (b.time || 0);
});
return sorted.map(id => {
return all[id];
});
};
UI.confirmButton(dump, { classes: 'btn-secondary' }, function () {
APP.module.execCommand('DUMP_LEGACY', {}, contentByHash => {
// group by ticket id
let sorted = sortLegacyTickets(contentByHash);
let dump = '';
sorted.forEach((t,i) => {
if (!Array.isArray(t) || !t.length) { return; }
let first = t[0];
if (i) { dump += '\n\n'; }
dump += `================================
================================
ID: #${first.id}
Title: ${first.title}
User: ${first.sender.name}
Date: ${new Date(first.time).toISOString()}`;
t.forEach(msg => {
if (!msg.message) {
dump += `
--------------------------------
CLOSED: ${new Date(msg.time).toISOString()}`;
return;
}
dump += `
--------------------------------
From: ${msg.sender.name}
Date: ${new Date(msg.time).toISOString()}
---
${msg.message}
---
Attachments:${JSON.stringify(msg.attachments, 0, 2)}`;
});
});
saveAs(new Blob([dump], {type: 'text/plain'}), "cryptpad-support-dump.txt");
});
});
UI.confirmButton(clean, { classes: 'btn-danger' }, function () {
APP.module.execCommand('CLEAR_LEGACY', {}, () => {
delete APP.privateKey;
sidebar.deleteCategory('legacy');
sidebar.openCategory('open');
});
});
let run = () => {
let $div = $(content);
$div.empty();
common.mailbox.subscribe(['supportadmin'], {
onMessage: function (data) {
/*
Get ID of the ticket
If we already have a div for this ID
Push the message to the end of the ticket
If it's a new ticket ID
Make a new div for this ID
*/
var msg = data.content.msg;
var hash = data.content.hash;
var content = msg.content;
console.error(content, msg, hash);
var id = content.id;
var $ticket = $div.find('.cp-support-list-ticket[data-id="'+id+'"]');
APP.module.execCommand('GET_LEGACY', {}, contentByHash => {
// group by ticket id
let sorted = sortLegacyTickets(contentByHash);
sorted.forEach(ticket => {
if (!Array.isArray(ticket) || !ticket.length) { return; }
ticket.forEach(content => {
var id = content.id;
var $ticket = $div.find('.cp-support-list-ticket[data-id="'+id+'"]');
if (msg.type === 'CLOSE') {
// A ticket has been closed by the admins...
if (!$ticket.length) { return; }
$ticket.addClass('cp-support-list-closed');
$ticket.append(APP.support.makeCloseMessage(content, hash));
return;
}
if (msg.type !== 'TICKET') { return; }
$ticket.removeClass('cp-support-list-closed');
if (!content.message) {
// A ticket has been closed by the admins...
if (!$ticket.length) { return; }
$ticket.hide();
$ticket.append(APP.support.makeCloseMessage(content));
return;
}
$ticket.show();
if (!$ticket.length) {
$ticket = APP.support.makeTicket({id, content});
$div.append($ticket);
}
$ticket.append(APP.support.makeMessage(content));
$ticket.find('.cp-support-showdata').attr('onclick', `showData();`);
$ticket.find('.cp-support-showdata button').attr('onclick', `copyData();`);
}
const onMove = function () {
let hashes = [];
let messages = [];
ticket.forEach(content => {
hashes.push(content.hash);
let clone = Util.clone(content);
delete clone.hash;
messages.push(clone);
});
APP.module.execCommand('RESTORE_LEGACY', {
messages, hashes
}, obj => {
if (obj && obj.error) {
console.error(obj.error);
return void UI.warn(Messages.error);
}
});
};
if (!$ticket.length) {
$ticket = APP.support.makeTicket({id, content, onMove});
$div.append($ticket);
}
$ticket.append(APP.support.makeMessage(content));
});
});
});
};
Util.onClickEnter($(start), run);
let div = blocks.form([content], nav);
cb(div);
});

View file

@ -47,7 +47,6 @@ define([
var categories = {
'tickets': [ // Msg.support_cat_tickets
'cp-support-list',
'cp-support-listnew',
],
'new': [ // Msg.support_cat_new
'cp-support-subscribe',
@ -92,71 +91,11 @@ define([
return $div;
};
// List existing (open?) tickets
create['list'] = function () {
var key = 'list';
var $div = makeBlock(key); // Msg.support_listHint, .support_listTitle
$div.addClass('cp-support-container');
var hashesById = {};
// Register to the "support" mailbox
common.mailbox.subscribe(['support'], {
onMessage: function (data) {
/*
Get ID of the ticket
If we already have a div for this ID
Push the message to the end of the ticket
If it's a new ticket ID
Make a new div for this ID
*/
var msg = data.content.msg;
var hash = data.content.hash;
var content = msg.content;
var id = content.id;
var $ticket = $div.find('.cp-support-list-ticket[data-id="'+id+'"]');
hashesById[id] = hashesById[id] || [];
if (hashesById[id].indexOf(hash) === -1) {
hashesById[id].push(data);
}
if (msg.type === 'CLOSE') {
// A ticket has been closed by the admins...
if (!$ticket.length) { return; }
$ticket.addClass('cp-support-list-closed');
$ticket.append(APP.support.makeCloseMessage(content, hash));
return;
}
if (msg.type !== 'TICKET') { return; }
$ticket.removeClass('cp-support-list-closed');
if (!$ticket.length) {
$ticket = APP.support.makeTicket($div, content, function () {
var error = false;
hashesById[id].forEach(function (d) {
common.mailbox.dismiss(d, function (err) {
if (err) {
error = true;
console.error(err);
}
});
});
if (!error) { $ticket.remove(); }
});
}
$ticket.append(APP.support.makeMessage(content, hash));
}
});
return $div;
};
var events = {
'UPDATE_TICKET': Util.mkEvent()
};
create['listnew'] = function () {
var key = 'listnew';
create['list'] = function () {
var key = 'list';
var $div = makeBlock(key); // Msg.support_listHint, .support_listTitle
var list = h('div.cp-support-container');
var $list = $(list);
@ -226,6 +165,12 @@ define([
$ticket.addClass('cp-support-list-closed');
return $ticket.append(APP.support.makeCloseMessage(msg));
}
if (msg.legacy && msg.messages) {
msg.messages.forEach(c => {
$ticket.append(APP.support.makeMessage(c));
});
return;
}
$ticket.append(APP.support.makeMessage(msg));
});