|
|
define([
|
|
|
'jquery',
|
|
|
'/api/config',
|
|
|
'/common/hyperscript.js',
|
|
|
'/common/common-interface.js',
|
|
|
'/common/common-hash.js',
|
|
|
'/common/common-util.js',
|
|
|
'/common/clipboard.js',
|
|
|
'/common/common-ui-elements.js',
|
|
|
'/customize/messages.js',
|
|
|
'/customize/pages.js',
|
|
|
], function ($, ApiConfig, h, UI, Hash, Util, Clipboard, UIElements, Messages, Pages) {
|
|
|
|
|
|
var getDebuggingData = function (ctx, data) {
|
|
|
var common = ctx.common;
|
|
|
var metadataMgr = common.getMetadataMgr();
|
|
|
var privateData = metadataMgr.getPrivateData();
|
|
|
var user = metadataMgr.getUserData();
|
|
|
var teams = privateData.teams || {};
|
|
|
data = data || {};
|
|
|
|
|
|
data.sender = {
|
|
|
name: user.name,
|
|
|
drive: privateData.driveChannel,
|
|
|
channel: privateData.support,
|
|
|
curvePublic: user.curvePublic,
|
|
|
edPublic: privateData.edPublic,
|
|
|
notifications: user.notifications,
|
|
|
};
|
|
|
|
|
|
if (typeof(ctx.pinUsage) === 'object') {
|
|
|
// pass pin.usage, pin.limit, and pin.plan if supplied
|
|
|
data.sender.quota = ctx.pinUsage;
|
|
|
}
|
|
|
|
|
|
if (!ctx.isAdmin) {
|
|
|
data.sender.userAgent = Util.find(window, ['navigator', 'userAgent']);
|
|
|
data.sender.vendor = Util.find(window, ['navigator', 'vendor']);
|
|
|
data.sender.appVersion = Util.find(window, ['navigator', 'appVersion']);
|
|
|
data.sender.screenWidth = Util.find(window, ['screen', 'width']);
|
|
|
data.sender.screenHeight = Util.find(window, ['screen', 'height']);
|
|
|
data.sender.blockLocation = privateData.blockLocation || '';
|
|
|
data.sender.teams = Object.keys(teams).map(function (key) {
|
|
|
var team = teams[key];
|
|
|
if (!team) { return; }
|
|
|
var ret = {};
|
|
|
['channel', 'roster', 'numberPads', 'numberSf', 'edPublic', 'curvePublic', 'owner', 'viewer', 'hasSecondaryKey', 'validKeys'].forEach(function (k) {
|
|
|
ret[k] = team[k];
|
|
|
});
|
|
|
if (ctx.teamsUsage && ctx.teamsUsage[key]) {
|
|
|
ret.quota = ctx.teamsUsage[key];
|
|
|
}
|
|
|
return ret;
|
|
|
}).filter(Boolean);
|
|
|
}
|
|
|
|
|
|
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
|
|
|
});
|
|
|
}
|
|
|
};
|
|
|
|
|
|
//Messages.support_formCategoryError = "Error: category is empty"; // XXX (existing key)
|
|
|
Messages.support_formCategoryError = "Please select a ticket category from the dropdown menu"; // XXX
|
|
|
|
|
|
var sendForm = function (ctx, id, form, dest) {
|
|
|
var $form = $(form);
|
|
|
var $cat = $form.find('.cp-support-form-category');
|
|
|
var $title = $form.find('.cp-support-form-title');
|
|
|
var $content = $form.find('.cp-support-form-msg');
|
|
|
// TODO block submission until pending uploads are complete?
|
|
|
var $attachments = $form.find('.cp-support-attachments');
|
|
|
|
|
|
var category = $cat.val().trim();
|
|
|
if (!category) {
|
|
|
console.log($cat);
|
|
|
return void UI.alert(Messages.support_formCategoryError);
|
|
|
}
|
|
|
|
|
|
var title = $title.val().trim();
|
|
|
if (!title) {
|
|
|
return void UI.alert(Messages.support_formTitleError);
|
|
|
}
|
|
|
var content = $content.val().trim();
|
|
|
if (!content) {
|
|
|
return void UI.alert(Messages.support_formContentError);
|
|
|
}
|
|
|
$cat.val('');
|
|
|
$content.val('');
|
|
|
$title.val('');
|
|
|
|
|
|
var attachments = [];
|
|
|
$attachments.find('> span').each(function (i, el) {
|
|
|
var $el = $(el);
|
|
|
attachments.push({
|
|
|
href: $el.attr('data-href'),
|
|
|
name: $el.attr('data-name')
|
|
|
});
|
|
|
});
|
|
|
$attachments.html('');
|
|
|
|
|
|
send(ctx, id, 'TICKET', {
|
|
|
category: category,
|
|
|
title: title,
|
|
|
attachments: attachments,
|
|
|
message: content,
|
|
|
}, dest);
|
|
|
|
|
|
return true;
|
|
|
};
|
|
|
|
|
|
Messages.support_cat_drives = "Drive or team"; // XXX
|
|
|
Messages.support_cat_document = "Document"; // XXX
|
|
|
Messages.support_cat_abuse = "Report abuse"; // XXX
|
|
|
|
|
|
var makeCategoryDropdown = function (ctx, container, onChange, all) {
|
|
|
var categories = [
|
|
|
'account', // Msg.support_cat_account
|
|
|
'drives', // Msg.support_cat_drives
|
|
|
'document', // Msg.support_cat_document,
|
|
|
Pages.customURLs.terms? 'abuse': undefined, // Msg.support_cat_abuse
|
|
|
'bug', // Msg.support_cat_bug
|
|
|
'other' // Msg.support_cat_other
|
|
|
];
|
|
|
if (all) { categories.push('all'); } // Msg.support_cat_all
|
|
|
|
|
|
categories = categories.map(function (key) {
|
|
|
if (!key) { return; }
|
|
|
return {
|
|
|
tag: 'a',
|
|
|
content: h('span', Messages['support_cat_'+key]),
|
|
|
action: function () {
|
|
|
onChange(key);
|
|
|
}
|
|
|
};
|
|
|
});
|
|
|
var dropdownCfg = {
|
|
|
text: Messages.support_category,
|
|
|
angleDown: 1,
|
|
|
options: categories,
|
|
|
container: $(container),
|
|
|
isSelect: true
|
|
|
};
|
|
|
var $select = UIElements.createDropdown(dropdownCfg);
|
|
|
$select.find('button').addClass('btn');
|
|
|
return $select;
|
|
|
};
|
|
|
|
|
|
Messages.support_warning_prompt = "Please choose the most relevant category for your issue, this helps administrators triage and provides further suggestions for what information to provide"; // XXX
|
|
|
|
|
|
Messages.support_warning_account = "Please note that administrators are not able to reset passwords. If you have lost the credentials to your account but are still logged in, you can <a>migrate your data to a new account</a>"; // XXX
|
|
|
|
|
|
Messages.support_warning_drives = "Note that administrators are not able to identify folders and documents by name. For shared folders, please provide a <a>document identifier</a> (does not provide access to the content)"; // XXX
|
|
|
|
|
|
Messages.support_warning_document = "Please specify which type of document is causing the issue and provide a <a>document identifier</a> (does not provide access to the content)"; // XXX
|
|
|
|
|
|
Messages.support_warning_bug = "Please specify in which browser the issue occurs and if any extensions are installed. Please provide as much detail as possible about the issue and the steps necessary to reproduce it"; // XXX
|
|
|
|
|
|
Messages.support_warning_abuse = "Please report content that violates the <a>Terms of Service</a>. Please provide links to the offending documents or user profiles and describe how they are violating the terms. Any additional information on the context in which you discovered the content or behaviour may help administrators prevent future violations"; // XXX
|
|
|
|
|
|
Messages.support_warning_other = "What is the nature of your query? Please provide as much relevant information as possible to make it easier for us to address your issue quickly"; // XXX
|
|
|
|
|
|
var documentIdDocs = Pages.localizeDocsLink('https://docs.cryptpad.fr/en/user_guide/apps/general.html#properties');
|
|
|
|
|
|
var warningLinks = {
|
|
|
account: documentIdDocs,
|
|
|
document: documentIdDocs,
|
|
|
drives: documentIdDocs,
|
|
|
abuse: Pages.customURLs.terms,
|
|
|
};
|
|
|
|
|
|
var makeForm = function (ctx, cb, title) {
|
|
|
var button;
|
|
|
|
|
|
if (typeof(cb) === "function") {
|
|
|
button = h('button.btn.btn-primary.cp-support-list-send', Messages.contacts_send);
|
|
|
$(button).click(cb);
|
|
|
}
|
|
|
|
|
|
var cancel = title ? h('button.btn.btn-secondary', Messages.cancel) : undefined;
|
|
|
|
|
|
var category = h('input.cp-support-form-category', {
|
|
|
type: 'hidden',
|
|
|
value: ''
|
|
|
});
|
|
|
var catContainer = h('div.cp-dropdown-container' + (title ? '.cp-hidden': ''));
|
|
|
var notice = h('div.alert.alert-info', Messages.support_warning_prompt);
|
|
|
var clickHandler = function (ev) {
|
|
|
ev.preventDefault();
|
|
|
var $link = $(this);
|
|
|
var href = $link.attr('href');
|
|
|
if (!href) { return; }
|
|
|
ctx.common.openUnsafeURL(href);
|
|
|
};
|
|
|
|
|
|
makeCategoryDropdown(ctx, catContainer, function (key) {
|
|
|
$(category).val(key);
|
|
|
console.log(key);
|
|
|
var warning = Messages['support_warning_' + key] || '';
|
|
|
var warningLink = warningLinks[key];
|
|
|
if (!warningLink) {
|
|
|
notice.innerText = warning;
|
|
|
return;
|
|
|
}
|
|
|
notice.innerHTML = '';
|
|
|
var content = UI.setHTML(h('span'), warning);
|
|
|
var link = content.querySelector('a');
|
|
|
if (link) {
|
|
|
link.href = warningLink;
|
|
|
link.onclick = clickHandler;
|
|
|
}
|
|
|
notice.appendChild(content);
|
|
|
});
|
|
|
|
|
|
var attachments, addAttachment;
|
|
|
|
|
|
|
|
|
var content = [
|
|
|
h('hr'),
|
|
|
category,
|
|
|
catContainer,
|
|
|
notice,
|
|
|
//h('br'),
|
|
|
h('input.cp-support-form-title' + (title ? '.cp-hidden' : ''), {
|
|
|
placeholder: Messages.support_formTitle,
|
|
|
type: 'text',
|
|
|
value: title || ''
|
|
|
}),
|
|
|
cb ? undefined : h('br'),
|
|
|
h('textarea.cp-support-form-msg', {
|
|
|
placeholder: Messages.support_formMessage
|
|
|
}),
|
|
|
h('label', Messages.support_attachments),
|
|
|
attachments = h('div.cp-support-attachments'),
|
|
|
addAttachment = h('button.btn', Messages.support_addAttachment),
|
|
|
h('hr'),
|
|
|
button,
|
|
|
cancel
|
|
|
];
|
|
|
|
|
|
$(addAttachment).click(function () {
|
|
|
var $input = $('<input>', {
|
|
|
'type': 'file',
|
|
|
'style': 'display: none;',
|
|
|
'multiple': 'multiple',
|
|
|
'accept': 'image/*'
|
|
|
}).on('change', function (e) {
|
|
|
var files = Util.slice(e.target.files);
|
|
|
files.forEach(function (file) {
|
|
|
var ev = {};
|
|
|
ev.callback = function (data) {
|
|
|
var x, a;
|
|
|
var span = h('span', {
|
|
|
'data-name': data.name,
|
|
|
'data-href': data.url
|
|
|
}, [
|
|
|
x = h('i.fa.fa-times'),
|
|
|
a = h('a', {
|
|
|
href: '#'
|
|
|
}, data.name)
|
|
|
]);
|
|
|
$(x).click(function () {
|
|
|
$(span).remove();
|
|
|
});
|
|
|
$(a).click(function (e) {
|
|
|
e.preventDefault();
|
|
|
ctx.common.openURL(data.url);
|
|
|
});
|
|
|
|
|
|
$(attachments).append(span);
|
|
|
};
|
|
|
// The empty object allows us to bypass the file upload modal
|
|
|
ctx.FM.handleFile(file, ev, {});
|
|
|
});
|
|
|
});
|
|
|
$input.click();
|
|
|
});
|
|
|
|
|
|
var form = h('div.cp-support-form-container', content);
|
|
|
|
|
|
$(cancel).click(function () {
|
|
|
$(form).closest('.cp-support-list-ticket').find('.cp-support-list-actions').css('display', '');
|
|
|
$(form).remove();
|
|
|
});
|
|
|
|
|
|
return form;
|
|
|
};
|
|
|
|
|
|
var makeTicket = function (ctx, $div, content, onHide) {
|
|
|
var common = ctx.common;
|
|
|
var metadataMgr = common.getMetadataMgr();
|
|
|
var privateData = metadataMgr.getPrivateData();
|
|
|
|
|
|
var ticketTitle = content.title + ' (#' + content.id + ')';
|
|
|
var ticketCategory;
|
|
|
var answer = h('button.btn.btn-primary.cp-support-answer', Messages.support_answer);
|
|
|
var close = h('button.btn.btn-danger.cp-support-close', Messages.support_close);
|
|
|
var hide = h('button.btn.btn-danger.cp-support-hide', Messages.support_remove);
|
|
|
|
|
|
var actions = h('div.cp-support-list-actions', [
|
|
|
answer,
|
|
|
close,
|
|
|
hide
|
|
|
]);
|
|
|
|
|
|
var url;
|
|
|
if (ctx.isAdmin) {
|
|
|
ticketCategory = Messages['support_cat_'+(content.category || 'all')] + ' - ';
|
|
|
url = h('button.btn.fa.fa-clipboard');
|
|
|
$(url).click(function (e) {
|
|
|
e.stopPropagation();
|
|
|
var link = privateData.origin + privateData.pathname + '#' + 'support-' + content.id;
|
|
|
var success = Clipboard.copy(link);
|
|
|
if (success) { UI.log(Messages.shareSuccess); }
|
|
|
});
|
|
|
}
|
|
|
|
|
|
var $ticket = $(h('div.cp-support-list-ticket', {
|
|
|
'data-cat': content.category,
|
|
|
'data-id': content.id
|
|
|
}, [
|
|
|
h('h2', [
|
|
|
h('span', [ticketCategory, ticketTitle]),
|
|
|
h('span.cp-support-title-buttons',url)
|
|
|
]),
|
|
|
actions
|
|
|
]));
|
|
|
|
|
|
/*
|
|
|
$(close).click(function () {
|
|
|
send(ctx, content.id, 'CLOSE', {}, content.sender);
|
|
|
});
|
|
|
|
|
|
$(hide).click(function () {
|
|
|
if (typeof(onHide) !== "function") { return; }
|
|
|
onHide();
|
|
|
});
|
|
|
*/
|
|
|
|
|
|
UI.confirmButton(close, {
|
|
|
classes: 'btn-danger'
|
|
|
}, function() {
|
|
|
send(ctx, content.id, 'CLOSE', {}, content.sender);
|
|
|
$(close).hide();
|
|
|
});
|
|
|
UI.confirmButton(hide, {
|
|
|
classes: 'btn-danger'
|
|
|
}, function() {
|
|
|
if (typeof(onHide) !== "function") { return; }
|
|
|
onHide(hide);
|
|
|
});
|
|
|
|
|
|
$(answer).click(function () {
|
|
|
$ticket.find('.cp-support-form-container').remove();
|
|
|
$(actions).hide();
|
|
|
var form = makeForm(ctx, function () {
|
|
|
var sent = sendForm(ctx, content.id, form, content.sender);
|
|
|
if (sent) {
|
|
|
$(actions).css('display', '');
|
|
|
$(form).remove();
|
|
|
}
|
|
|
}, content.title);
|
|
|
$ticket.append(form);
|
|
|
});
|
|
|
|
|
|
$div.append($ticket);
|
|
|
return $ticket;
|
|
|
};
|
|
|
|
|
|
var makeMessage = function (ctx, content, hash) {
|
|
|
var common = ctx.common;
|
|
|
var isAdmin = ctx.isAdmin;
|
|
|
var metadataMgr = common.getMetadataMgr();
|
|
|
var privateData = metadataMgr.getPrivateData();
|
|
|
|
|
|
// 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 fromPremium = Boolean(content.sender.plan || Util.find(content, ['sender', 'quota', 'plan']));
|
|
|
|
|
|
var userData = h('div.cp-support-showdata', [
|
|
|
Messages.support_showData,
|
|
|
h('pre.cp-support-message-data', JSON.stringify(content.sender, 0, 2))
|
|
|
]);
|
|
|
$(userData).click(function () {
|
|
|
$(userData).find('pre').toggle();
|
|
|
}).find('pre').click(function (ev) {
|
|
|
ev.stopPropagation();
|
|
|
});
|
|
|
|
|
|
var attachments = (content.attachments || []).map(function (obj) {
|
|
|
if (!obj || !obj.name || !obj.href) { return; }
|
|
|
// only support files explicitly beginning with /file/ so that users can't link outside of the instance
|
|
|
if (!/^\/file\//.test(obj.href)) { return; }
|
|
|
var a = h('a', {
|
|
|
href: '#'
|
|
|
}, obj.name);
|
|
|
$(a).click(function (e) {
|
|
|
e.preventDefault();
|
|
|
ctx.common.openURL(obj.href);
|
|
|
});
|
|
|
return h('span', [
|
|
|
a
|
|
|
]);
|
|
|
});
|
|
|
|
|
|
var displayed = content.message;
|
|
|
var pre = h('pre.cp-support-message-content');
|
|
|
var $pre = $(pre);
|
|
|
var more;
|
|
|
if (content.message.length >= 2000) {
|
|
|
displayed = content.message.slice(0, 2000) + '...';
|
|
|
var expand = h('button.btn.btn-secondary', Messages.admin_support_open);
|
|
|
var collapse = h('button.btn.btn-secondary', Messages.admin_support_collapse);
|
|
|
var $collapse = $(collapse).hide();
|
|
|
var $expand = $(expand).click(function () {
|
|
|
$pre.text(content.message);
|
|
|
$expand.hide();
|
|
|
$collapse.show();
|
|
|
});
|
|
|
$collapse.click(function () {
|
|
|
$pre.text(displayed);
|
|
|
$collapse.hide();
|
|
|
$expand.show();
|
|
|
});
|
|
|
more = h('div', [expand, collapse]);
|
|
|
}
|
|
|
$pre.text(displayed);
|
|
|
|
|
|
var adminClass = (fromAdmin? '.cp-support-fromadmin': '');
|
|
|
var premiumClass = (fromPremium && !fromAdmin? '.cp-support-frompremium': '');
|
|
|
var name = Util.fixHTML(content.sender.name) || Messages.anonymous;
|
|
|
return h('div.cp-support-list-message' + adminClass + premiumClass, {
|
|
|
'data-hash': hash
|
|
|
}, [
|
|
|
h('div.cp-support-message-from' + (fromMe ? '.cp-support-fromme' : ''), [
|
|
|
UI.setHTML(h('span'), Messages._getKey('support_from', [name])),
|
|
|
h('span.cp-support-message-time', content.time ? new Date(content.time).toLocaleString() : '')
|
|
|
]),
|
|
|
pre,
|
|
|
more,
|
|
|
h('div.cp-support-attachments', attachments),
|
|
|
isAdmin ? userData : undefined,
|
|
|
]);
|
|
|
};
|
|
|
|
|
|
var makeCloseMessage = function (ctx, content, hash) {
|
|
|
var common = ctx.common;
|
|
|
var metadataMgr = common.getMetadataMgr();
|
|
|
var privateData = metadataMgr.getPrivateData();
|
|
|
var fromMe = content.sender && content.sender.edPublic === privateData.edPublic;
|
|
|
|
|
|
var name = Util.fixHTML(content.sender.name) || Messages.anonymous;
|
|
|
return h('div.cp-support-list-message', {
|
|
|
'data-hash': hash
|
|
|
}, [
|
|
|
h('div.cp-support-message-from' + (fromMe ? '.cp-support-fromme' : ''), [
|
|
|
UI.setHTML(h('span'), Messages._getKey('support_from', [name])),
|
|
|
h('span.cp-support-message-time', content.time ? new Date(content.time).toLocaleString() : '')
|
|
|
]),
|
|
|
h('pre.cp-support-message-content', Messages.support_closed)
|
|
|
]);
|
|
|
};
|
|
|
|
|
|
var create = function (common, isAdmin, pinUsage, teamsUsage) {
|
|
|
var ui = {};
|
|
|
var ctx = {
|
|
|
common: common,
|
|
|
isAdmin: isAdmin,
|
|
|
pinUsage: pinUsage || false,
|
|
|
teamsUsage: teamsUsage || false,
|
|
|
adminKeys: Array.isArray(ApiConfig.adminKeys)? ApiConfig.adminKeys.slice(): [],
|
|
|
};
|
|
|
|
|
|
var fmConfig = {
|
|
|
body: $('body'),
|
|
|
noStore: true, // Don't store attachments into our drive
|
|
|
onUploaded: function (ev, data) {
|
|
|
if (ev.callback) {
|
|
|
ev.callback(data);
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
ctx.FM = common.createFileManager(fmConfig);
|
|
|
|
|
|
ui.sendForm = function (id, form, dest) {
|
|
|
return sendForm(ctx, id, form, dest);
|
|
|
};
|
|
|
ui.makeForm = function (cb, title) {
|
|
|
return makeForm(ctx, cb, title);
|
|
|
};
|
|
|
ui.makeCategoryDropdown = function (container, onChange, all) {
|
|
|
return makeCategoryDropdown(ctx, container, onChange, all);
|
|
|
};
|
|
|
ui.makeTicket = function ($div, content, onHide) {
|
|
|
return makeTicket(ctx, $div, content, onHide);
|
|
|
};
|
|
|
ui.makeMessage = function (content, hash) {
|
|
|
return makeMessage(ctx, content, hash);
|
|
|
};
|
|
|
ui.makeCloseMessage = function (content, hash) {
|
|
|
return makeCloseMessage(ctx, content, hash);
|
|
|
};
|
|
|
ui.getDebuggingData = function (data) {
|
|
|
return getDebuggingData(ctx, data);
|
|
|
};
|
|
|
|
|
|
return ui;
|
|
|
};
|
|
|
|
|
|
return {
|
|
|
create: create
|
|
|
};
|
|
|
});
|