Merge branch 'staging' into sfAllow

pull/1/head
yflory 5 years ago
commit bf8c43e9af

@ -1,3 +1,32 @@
# Thylacine's revenge (3.19.1)
Our upcoming 3.20.0 release is planned for July 7th, 2020, but we are once again releasing a minor version featuring some nice bug fixes and usability improvements which are ready to be deployed now. In case you missed [our announcement](https://social.weho.st/@cryptpad/104360490068671089) we are phasing out our usage of the `master` and basing our releases on the `main` branch. For best results we recommend explicitly checking out code by its tag.
New features:
* We've spent a little time making support tickets a little bit easier for both users and admins.
* Users can now label their tickets with a set of predefined categories, making it easier for admins to sort through related reports.
* Users and admins can both attach encrypted uploads to their messages, making it easier to demonstrate a problem with an image, video, or other example file.
* Teams now take advantage of the same "mailbox" functionality that powers user accounts' notification center. Team members with the "viewer" role can now use this feature to share documents with their team using the "share menu" as they already can with other users. Anyone with the ability to add a document to the team's drive will then be able to receive the notification and add the document to the team's drive for them. Going forward we'll use this functionality to implement more behaviour to make teams function more like shared user accounts.
* The "pad creation screen" which is displayed to registered users when they first create a pad will no longer remember the settings used when they last created a pad. While this behaviour was intended to streamline the process of creating documents, in practice it led to some user's documents getting deleted because they didn't realize they were set to automatically expire. If you prefer not to use the defaults (owned, non-expiring) then you'll have to click a few more times to create a document, but we think that's a worthwhile tradeoff to avoid data loss.
Bug fixes:
* Hitting _ctrl-A_ in the drive used to select lots of the page's elements which had no business being selected. Now it will select the contents of the directory currently being displayed.
* Due to some complications in OnlyOffice (which we use for spreadsheets) remote updates made to a sheet were not displayed for users who had opened the document in "view mode". We still don't have the means to apply these remote changes in real-time, but we now prompt users to click a button to refresh the editor (not the full page) to display the latest document state.
* A recent update set the text color of the team chat input to 'white', matching the input's background and making the text unreadable. We patched it to make it black text on a white background.
* We're slowly working on improving keyboard shortcuts for a variety of actions. This time around we fixed a bug that prevented "ESC" from closing an open "tag prompt" interface.
* We noticed that the zip file constructed in the browser when you downloaded a subtree of a shared folder in your drive contained the correct directory structure but did not contain the files that were supposed to be there. This has been fixed.
* Finally, we've tweaked our styles to use more specific CSS selectors to prevent a variety of styles from being accidentally applied to the wrong elements. This should make the platform a little easier to maintain and help us improve the visual consistency of a variety of elements on different pages.
To update from 3.19.0 to 3.19.1:
1. Stop your server
2. Get the latest code with `git checkout 3.19.1`
3. Restart your server
If you're updating from anything other than 3.19.0 you may need other clientside dependencies (available with `bower update` and `npm i`).
# Thylacine release (3.19.0)
## Goals

@ -62,7 +62,7 @@ define([
var imprintUrl = AppConfig.imprint && (typeof(AppConfig.imprint) === "boolean" ?
'/imprint.html' : AppConfig.imprint);
Pages.versionString = "CryptPad v3.19.0 (Thylacine)";
Pages.versionString = "CryptPad v3.19.1 (Thylacine's revenge)";
// used for the about menu
Pages.imprintLink = AppConfig.imprint ? footLink(imprintUrl, 'imprint') : undefined;

@ -3,7 +3,6 @@
@import (reference) "./variables.less";
@import (reference) "./avatar.less";
@import (reference) "./tools.less";
@import (reference) "./buttons.less";
.alertify_main() {
--LessLoader_require: LessLoader_currentFile();
@ -272,7 +271,6 @@
}
}
.buttons_main();
input:not(.form-control), textarea {
margin-bottom: 15px;
}

@ -1,7 +1,6 @@
@import (reference) "./colortheme-all.less";
@import (reference) "./variables.less";
@import (reference) "./avatar.less";
@import (reference) "./buttons.less";
@import (reference) "./tools.less";
.comments_main() {
@ -22,8 +21,6 @@
}
.buttons_main();
.cp-comment-reply {
margin-left: 30px;
}

@ -1,7 +1,7 @@
@import (reference) "./colortheme-all.less";
@import (reference) "./variables.less";
.buttons_main() {
.forms_main() {
@alertify-fore: @colortheme_modal-fg;
@alertify-btn-fg: @alertify-fore;
@alertify-light-bg: fade(@alertify-fore, 25%);
@ -30,7 +30,7 @@
div.cp-alertify-type {
display: flex;
input {
margin: 0;
margin: 0 !important;
flex: 1;
min-width: 0;
}
@ -83,7 +83,7 @@
margin-bottom: 3px !important;
}
button:not(.pure-button):not(.md-button):not(.mdl-button) {
button.btn {
background-color: @colortheme_alertify-cancel;
box-sizing: border-box;

@ -16,6 +16,7 @@
@import (reference) "./cursor.less";
@import (reference) "./usergrid.less";
@import (reference) "./mentions.less";
@import (reference) "./forms.less";
@import (reference) "./modals-ui-elements.less";
.framework_main(@bg-color, @warn-color, @color) {
@ -48,6 +49,7 @@
);
.cursor_main();
.usergrid_main();
.forms_main();
.mentions_main();
.creation_main(
@bg-color: @bg-color,
@ -84,6 +86,7 @@
.checkmark_main(20px);
.password_main();
.usergrid_main();
.forms_main();
font: @colortheme_app-font;
}

@ -1,8 +1,5 @@
@import (reference) "./colortheme-all.less";
@import (reference) "./variables.less";
@import (reference) './buttons.less';
.modal_base() {
font-family: @colortheme_font;
@ -39,8 +36,6 @@
background-color: @colortheme_modal-dim;
.cp-modal {
.buttons_main();
background-color: @colortheme_modal-bg;
color: @colortheme_modal-fg;
box-shadow: @variables_shadow;

@ -1,6 +1,5 @@
@import (reference) "/customize/src/less2/include/colortheme-all.less";
@import (reference) "/customize/src/less2/include/leftside-menu.less";
@import (reference) "/customize/src/less2/include/buttons.less";
@import (reference) "/customize/src/less2/include/browser.less";
@sidebar_button-width: 400px;
@ -98,7 +97,6 @@
}
}
margin-bottom: 20px;
.buttons_main();
}
[type="text"], [type="password"], button {
vertical-align: middle;

@ -4,6 +4,9 @@
@msg-bg: #eee;
@fromme-bg: #ddd;
.cp-support-form-container {
div {
margin-bottom: 10px;
}
[type="text"] {
width: @sidebar_button-width;
margin-bottom: 10px;
@ -15,6 +18,18 @@
height: 300px;
}
}
.cp-support-attachments {
display: flex;
.fa {
cursor: pointer;
margin-right: 10px;
}
&> span {
border: 1px solid #ddd;
margin-right: 5px;
padding: 10px;
}
}
.cp-support-container {
.cp-support-list-ticket {
display: flex;

@ -84,7 +84,6 @@
width: 100%;
min-width: 100% !important;
&:focus {
border-color: transparent;
outline: 0;
box-shadow: none;
}

@ -2,9 +2,11 @@
@import (reference) "../include/colortheme-all.less";
@import (reference) "../include/alertify.less";
@import (reference) "../include/checkmark.less";
@import (reference) "../include/forms.less";
&.cp-page-register {
.infopages_main();
.forms_main();
.alertify_main();
.checkmark_main(20px);

2
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "cryptpad",
"version": "3.19.0",
"version": "3.19.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server",
"version": "3.19.0",
"version": "3.19.1",
"license": "AGPL-3.0+",
"repository": {
"type": "git",

@ -185,6 +185,20 @@ define([
var $container = makeBlock('support-list');
var $div = $(h('div.cp-support-container')).appendTo($container);
var catContainer = h('div.cp-dropdown-container');
$div.append(catContainer);
var category = 'all';
var $drop = APP.support.makeCategoryDropdown(catContainer, function (key) {
category = key;
if (key === 'all') {
$div.find('.cp-support-list-ticket').show();
return;
}
$div.find('.cp-support-list-ticket').hide();
$div.find('.cp-support-list-ticket[data-cat="'+key+'"]').show();
}, true);
$drop.setValue('all');
var metadataMgr = common.getMetadataMgr();
var privateData = metadataMgr.getPrivateData();
var cat = privateData.category || '';
@ -277,6 +291,9 @@ define([
UI.alert(Messages.error);
});
});
if (category !== 'all' && $ticket.attr('data-cat') !== category) {
$ticket.hide();
}
}
$ticket.append(APP.support.makeMessage(content, hash));
reorder();

@ -145,12 +145,12 @@ define([
};
dialog.okButton = function (content, classString) {
var sel = typeof(classString) === 'string'? 'button.ok.' + classString:'button.ok.primary';
var sel = typeof(classString) === 'string'? 'button.ok.' + classString:'button.btn.ok.primary';
return h(sel, { tabindex: '2', }, content || Messages.okButton);
};
dialog.cancelButton = function (content, classString) {
var sel = typeof(classString) === 'string'? 'button.' + classString:'button.cancel';
var sel = typeof(classString) === 'string'? 'button.' + classString:'button.btn.cancel';
return h(sel, { tabindex: '1'}, content || Messages.cancelButton);
};
@ -356,11 +356,15 @@ define([
var $cancel = findCancelButton(tagger).click(function (e) {
close(null, e);
});
listener = listenForKeys(function () {
$ok.click();
}, function () {
$cancel.click();
}, tagger);
$(tagger).on('keydown', function (e) {
if (e.which === 27) {
$cancel.click();
return;
}
if (e.which === 13) {
$ok.click();
}
});
$(tagger).on('click submit', function (e) {
e.stopPropagation();
@ -392,6 +396,7 @@ define([
buttons.forEach(function (b) {
if (!b.name || !b.onClick) { return; }
var button = h('button', { tabindex: '1', 'class': b.className || '' }, b.name);
button.classList.add('btn');
var todo = function () {
var noClose = b.onClick();
if (noClose) { return; }
@ -642,9 +647,6 @@ define([
var $ok = $(ok).click(function (ev) { close(true, ev); });
var $cancel = $(cancel).click(function (ev) { close(false, ev); });
if (opt.cancelClass) { $cancel.addClass(opt.cancelClass); }
if (opt.okClass) { $ok.addClass(opt.okClass); }
listener = listenForKeys(function () {
$ok.click();
}, function () {

@ -313,26 +313,30 @@ define([
var $friends = $div.find('.cp-usergrid-user.cp-selected');
$friends.each(function (i, el) {
var curve = $(el).attr('data-curve');
// Check if the selected element is a friend or a team
if (curve) { // Friend
if (!curve || !friends[curve]) { return; }
var friend = friends[curve];
if (!friend.notifications || !friend.curvePublic) { return; }
common.mailbox.sendTo("SHARE_PAD", {
href: href,
password: config.password,
isTemplate: config.isTemplate,
name: myName,
title: title
}, {
channel: friend.notifications,
curvePublic: friend.curvePublic
});
return;
}
// Team
var ed = $(el).attr('data-ed');
var friend = curve && friends[curve];
var team = teams[ed];
// If the selected element is a friend or a team without edit right,
// send a notification
var mailbox = friend || ((team && team.viewer) ? team : undefined);
if (mailbox) { // Friend
if (friends[curve] && !mailbox.notifications) { return; }
if (mailbox.notifications && mailbox.curvePublic) {
common.mailbox.sendTo("SHARE_PAD", {
href: href,
password: config.password,
isTemplate: config.isTemplate,
name: myName,
title: title
}, {
viewed: team && team.id,
channel: mailbox.notifications,
curvePublic: mailbox.curvePublic
});
return;
}
}
// If it's a team with edit right, add the pad directly
if (!team) { return; }
sframeChan.query('Q_STORE_IN_TEAM', {
href: href,
@ -450,10 +454,11 @@ define([
// config.teamId only exists when we're trying to share a pad from a team drive
// In this case, we don't want to share the pad with the current team
if (config.teamId && config.teamId === id) { return; }
if (!teamsData[id].hasSecondaryKey) { return; }
var t = teamsData[id];
teams[t.edPublic] = {
notifications: true,
viewer: !teamsData[id].hasSecondaryKey,
notifications: t.notifications,
curvePublic: t.curvePublic,
displayName: t.name,
edPublic: t.edPublic,
avatar: t.avatar,
@ -2150,6 +2155,11 @@ define([
'class': 'fa fa-caret-down',
}).prependTo($button);
}
if (config.angleDown) {
$('<span>', {
'class': 'fa fa-angle-down',
}).prependTo($button);
}
// Menu
var $innerblock = $('<div>', {'class': 'cp-dropdown-content'});
@ -2203,7 +2213,9 @@ define([
if (config.isSelect && value) {
var $val = $innerblock.find('[data-value="'+value+'"]');
setActive($val);
$innerblock.scrollTop($val.position().top + $innerblock.scrollTop());
try {
$innerblock.scrollTop($val.position().top + $innerblock.scrollTop());
} catch (e) {}
}
if (config.feedback) { Feedback.send(config.feedback); }
};
@ -2849,6 +2861,7 @@ define([
});
};
/*
UIElements.setExpirationValue = function (val, $expire) {
if (val && typeof (val) === "number") {
$expire.find('#cp-creation-expire').attr('checked', true).trigger('change');
@ -2872,6 +2885,7 @@ define([
$expire.find('#cp-creation-expire-false').attr('checked', true);
}
};
*/
UIElements.getPadCreationScreen = function (common, cfg, appCfg, cb) {
appCfg = appCfg || {};
if (!common.isLoggedIn()) { return void cb(); }
@ -3184,10 +3198,12 @@ define([
// Initial values
/*
if (!cfg.owned && typeof cfg.owned !== "undefined") {
$creation.find('#cp-creation-owned').prop('checked', false);
}
UIElements.setExpirationValue(cfg.expire, $creation);
*/
// Create the pad
var getFormValues = function () {
@ -3537,6 +3553,11 @@ define([
link
]);
var dismiss = function () {
common.mailbox.dismiss(data, function (err) {
console.log(err);
});
};
var answer = function (yes) {
common.mailbox.sendTo("ADD_OWNER_ANSWER", {
channel: msg.content.channel,
@ -3548,9 +3569,7 @@ define([
channel: msg.content.user.notifications,
curvePublic: msg.content.user.curvePublic
});
common.mailbox.dismiss(data, function (err) {
console.log(err);
});
dismiss();
};
var todo = function (yes) {
@ -3565,6 +3584,8 @@ define([
if (err) {
var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden
: Messages.error;
console.error(err);
dismiss();
return void UI.warn(text);
}
UI.log(Messages.saved);

@ -945,6 +945,7 @@ define([
// Ctrl+A select all
if (e.which === 65 && (e.ctrlKey || (e.metaKey && APP.isMac))) {
e.preventDefault();
$content.find('.cp-app-drive-element:not(.cp-app-drive-element-selected)')
.each(function (idx, element) {
selectElement($(element));
@ -4204,7 +4205,8 @@ define([
if (!manager.isSharedFolder(el)) {
name = path.path[path.path.length - 1];
folderEl = el;
downloadFolder(folderEl, name);
var sfId = manager.isInSharedFolder(path.path);
downloadFolder(folderEl, name, sfId);
}
// shared folder
else {

@ -804,7 +804,7 @@ define([
id: 'cp-app-prop-change-password',
style: 'flex: 1;'
});
var passwordOk = h('button', Messages.properties_changePasswordButton);
var passwordOk = h('button.btn', Messages.properties_changePasswordButton);
var changePass = h('span.cp-password-change-container', [
newPassword,
passwordOk

@ -499,7 +499,7 @@ define([
}, 500);
progress(0, 0);*/
}).nThen(function () {
Realtime.whenRealtimeSyncs(store.realtime, Util.bake(cb));
Realtime.whenRealtimeSyncs(store.realtime, Util.mkAsync(Util.bake(cb)));
});
};
});

@ -83,7 +83,6 @@ define([
};
// Share pad
handlers['SHARE_PAD'] = function(common, data) {
var content = data.content;
var msg = content.msg;
@ -91,10 +90,22 @@ define([
var key = type === 'drive' ? 'notification_folderShared' :
(type === 'file' ? 'notification_fileShared' :
'notification_padShared');
var teamNotification = /^team-/.test(data.type) && Number(data.type.slice(5));
var teamName = '';
if (teamNotification) {
var privateData = common.getMetadataMgr().getPrivateData();
var teamsData = Util.tryParse(JSON.stringify(privateData.teams)) || {};
var team = teamsData[teamNotification];
if (!team || !team.name) { return; }
key += "Team";
teamName = Util.fixHTML(team.name);
}
var name = Util.fixHTML(msg.content.name) || Messages.anonymous;
var title = Util.fixHTML(msg.content.title);
content.getFormatText = function() {
return Messages._getKey(key, [name, title]);
return Messages._getKey(key, [name, title, teamName]);
};
content.handler = function() {
var todo = function() {
@ -105,6 +116,9 @@ define([
if (msg.content.isTemplate) {
common.sessionStorage.put(Constants.newPadPathKey, ['template'], waitFor());
}
if (teamNotification) {
common.sessionStorage.put(Constants.newPadTeamKey, teamNotification, waitFor());
}
common.sessionStorage.put('newPadPassword', msg.content.password || '', waitFor());
}).nThen(function() {
todo();

@ -56,6 +56,7 @@ define([
var DISPLAY_RESTORE_BUTTON = false;
var NEW_VERSION = 2;
var PENDING_TIMEOUT = 30000;
var READ_ONLY_REFRESH_DATA_DELAY = 15000;
var debug = function (x) {
if (!window.CP_DEV_MODE) { return; }
@ -122,7 +123,6 @@ define([
if (!state && !readOnly) {
$('#cp-app-oo-editor').append(h('div#cp-app-oo-offline'));
}
debug(state);
};
var deleteOffline = function () {
@ -506,6 +506,87 @@ define([
}, to);
};
var loadInitDocument = function (type, useNewDefault) {
var newText;
switch (type) {
case 'sheet' :
newText = EmptyCell(useNewDefault);
break;
case 'oodoc':
newText = EmptyDoc();
break;
case 'ooslide':
newText = EmptySlide();
break;
default:
newText = '';
}
return new Blob([newText], {type: 'text/plain'});
};
var loadLastDocument = function (lastCp, onCpError, cb) {
ooChannel.cpIndex = lastCp.index || 0;
var parsed = Hash.parsePadUrl(lastCp.file);
var secret = Hash.getSecrets('file', parsed.hash);
if (!secret || !secret.channel) { return; }
var hexFileName = secret.channel;
var fileHost = privateData.fileHost || privateData.origin;
var src = fileHost + Hash.getBlobPathFromHex(hexFileName);
var key = secret.keys && secret.keys.cryptKey;
var xhr = new XMLHttpRequest();
xhr.open('GET', src, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
if (/^4/.test('' + this.status)) {
onCpError();
return void console.error('XHR error', this.status);
}
var arrayBuffer = xhr.response;
if (arrayBuffer) {
var u8 = new Uint8Array(arrayBuffer);
FileCrypto.decrypt(u8, key, function (err, decrypted) {
if (err) { return void console.error(err); }
var blob = new Blob([decrypted.content], {type: 'plain/text'});
if (cb) {
return cb(blob, getFileType());
}
startOO(blob, getFileType());
});
}
};
xhr.onerror = function () {
onCpError();
};
xhr.send(null);
};
var refreshReadOnly = function () {
var cancel = h('button.cp-corner-cancel', Messages.cancel);
var reload = h('button.cp-corner-primary', [
h('i.fa.fa-refresh'),
Messages.oo_refresh
]);
var actions = h('div', [cancel, reload]);
var m = UI.cornerPopup(Messages.oo_refreshText, actions, '');
$(reload).click(function () {
ooChannel.ready = false;
var lastCp = getLastCp();
loadLastDocument(lastCp, function () {
var file = getFileType();
var type = common.getMetadataMgr().getPrivateData().ooType;
var blob = loadInitDocument(type, true);
resetData(blob, file);
}, function (blob, file) {
resetData(blob, file);
});
delete APP.refreshPopup;
m.delete();
});
$(cancel).click(function () {
delete APP.refreshPopup;
m.delete();
});
};
var openRtChannel = function (cb) {
if (rtChannel.ready) { return void cb(); }
@ -529,6 +610,18 @@ define([
break;
case 'MESSAGE':
if (ooChannel.ready) {
// In read-only mode, push the message to the queue and prompt
// the user to refresh OO (without reloading the page)
if (readOnly) {
ooChannel.queue.push(obj.data);
if (APP.refreshPopup) { return; }
APP.refreshPopup = true;
// Don't "spam" the user instantly and no more than
// 1 popup every 15s
setTimeout(refreshReadOnly, READ_ONLY_REFRESH_DATA_DELAY);
return;
}
ooChannel.send(obj.data.msg);
ooChannel.lastHash = obj.data.hash;
ooChannel.cpIndex++;
@ -986,8 +1079,10 @@ define([
ooChannel.queue.forEach(function (data) {
ooChannel.send(data.msg);
});
var last = ooChannel.queue.pop();
if (last) { ooChannel.lastHash = last.hash; }
if (!readOnly) {
var last = ooChannel.queue.pop();
if (last) { ooChannel.lastHash = last.hash; }
}
ooChannel.cpIndex += ooChannel.queue.length;
// Apply existing locks
deleteOfflineLocks();
@ -1018,7 +1113,7 @@ define([
UI.openCustomModal(UI.dialog.customModal(div, {buttons: []}));
setTimeout(function () {
makeCheckpoint(true);
}, 1000);
}, 5000);
}
}
}
@ -1028,6 +1123,9 @@ define([
if (ifr) { ifr.remove(); }
};
APP.UploadImageFiles = function (files, type, id, jwt, cb) {
cb('NO');
};
APP.AddImage = function(cb1, cb2) {
APP.AddImageSuccessCallback = cb1;
APP.AddImageErrorCallback = cb2;
@ -1447,41 +1545,6 @@ define([
}, 100);
};
var loadLastDocument = function (lastCp, onCpError, cb) {
ooChannel.cpIndex = lastCp.index || 0;
var parsed = Hash.parsePadUrl(lastCp.file);
var secret = Hash.getSecrets('file', parsed.hash);
if (!secret || !secret.channel) { return; }
var hexFileName = secret.channel;
var fileHost = privateData.fileHost || privateData.origin;
var src = fileHost + Hash.getBlobPathFromHex(hexFileName);
var key = secret.keys && secret.keys.cryptKey;
var xhr = new XMLHttpRequest();
xhr.open('GET', src, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
if (/^4/.test('' + this.status)) {
onCpError();
return void console.error('XHR error', this.status);
}
var arrayBuffer = xhr.response;
if (arrayBuffer) {
var u8 = new Uint8Array(arrayBuffer);
FileCrypto.decrypt(u8, key, function (err, decrypted) {
if (err) { return void console.error(err); }
var blob = new Blob([decrypted.content], {type: 'plain/text'});
if (cb) {
return cb(blob, getFileType());
}
startOO(blob, getFileType());
});
}
};
xhr.onerror = function () {
onCpError();
};
xhr.send(null);
};
var loadDocument = function (noCp, useNewDefault, i) {
if (ooLoaded) { return; }
var type = common.getMetadataMgr().getPrivateData().ooType;
@ -1511,7 +1574,7 @@ define([
default:
newText = '';
}
var blob = new Blob([newText], {type: 'text/plain'});
var blob = loadInitDocument(type, useNewDefault);
startOO(blob, file);
};
@ -1788,10 +1851,10 @@ define([
var latest = getLastCp(true);
var newLatest = getLastCp();
if (newLatest.index > latest.index) {
ooChannel.queue = [];
var hasDrawings = checkDrawings();
if (hasDrawings) {
ooChannel.ready = false;
ooChannel.queue = [];
}
// New checkpoint
sframeChan.query('Q_OO_SAVE', {

@ -624,10 +624,10 @@ url+'" method="POST" enctype="multipart/form-data"><input id="apiiuFile" name="a
ValidateUploadImage(e.target.files);if(c_oAscServerError.NoError!=nError){callbackOld(mapAscServerErrorToAscError(nError));return}}callbackOld(Asc.c_oAscError.ID.No);fileSubmit.click()}}if(AscBrowser.isOpera)setTimeout(function(){fileName.click()},0);else fileName.click()}function InitDragAndDrop(oHtmlElement,callback){if("undefined"!=typeof FileReader&&null!=oHtmlElement){oHtmlElement["ondragover"]=function(e){e.preventDefault();e.dataTransfer.dropEffect=CanDropFiles(e)?"copy":"none";if(e.dataTransfer.dropEffect==
"copy"){var editor=window["Asc"]["editor"]?window["Asc"]["editor"]:window.editor;editor.beginInlineDropTarget(e)}return false};oHtmlElement["ondrop"]=function(e){e.preventDefault();var files=e.dataTransfer.files;var nError=ValidateUploadImage(files);var editor=window["Asc"]["editor"]?window["Asc"]["editor"]:window.editor;editor.endInlineDropTarget(e);if(nError==c_oAscServerError.UploadCountFiles){try{var htmlValue=e.dataTransfer.getData("text/html");if(htmlValue&&!AscCommon.AscBrowser.isIE){var index=
htmlValue.indexOf("StartHTML");var indexHtml=htmlValue.indexOf("<html");if(-1==indexHtml)indexHtml=htmlValue.indexOf("<HTML");if(index>0&&indexHtml>0&&index<indexHtml)htmlValue=htmlValue.substr(indexHtml);editor["pluginMethod_PasteHtml"](htmlValue);return}}catch(err){}try{var textValue=e.dataTransfer.getData("text/plain");if(textValue){editor["pluginMethod_PasteText"](textValue);return}}catch(err$11){}try{var textValue=e.dataTransfer.getData("Text");if(textValue){editor["pluginMethod_PasteText"](textValue);
return}}catch(err$12){}}callback(mapAscServerErrorToAscError(nError),files)}}}function UploadImageFiles(files,documentId,documentUserId,jwt,callback){window.parent.APP.UploadImageFiles(files,documentId,documentUserId,jwt,callback);return;if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);var file=aFiles.pop();var aResultUrls=
[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push(urls[i]);break}if(aFiles.length===0)callback(Asc.c_oAscError.ID.No,aResultUrls);else{file=aFiles.pop();var xhr=new XMLHttpRequest;url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);
xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}}else callback(Asc.c_oAscError.ID.UplImageFileCount)};var xhr=new XMLHttpRequest;xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}else callback(Asc.c_oAscError.ID.UplImageFileCount)}function UploadImageUrls(files,documentId,documentUserId,
jwt,callback){if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);var file=aFiles.pop();var aResultUrls=[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push({path:i,
return}}catch(err$12){}}callback(mapAscServerErrorToAscError(nError),files)}}}function UploadImageFiles(files,documentId,documentUserId,jwt,callback){window.parent.APP.UploadImageFiles(files,documentId,documentUserId,jwt,function(err,urls){callback(err||Asc.c_oAscError.ID.No,urls)});return;if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);
var file=aFiles.pop();var aResultUrls=[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push(urls[i]);break}if(aFiles.length===0)callback(Asc.c_oAscError.ID.No,aResultUrls);else{file=aFiles.pop();var xhr=new XMLHttpRequest;url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+=
"?token="+encodeURIComponent(jwt);xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}}else callback(Asc.c_oAscError.ID.UplImageFileCount)};var xhr=new XMLHttpRequest;xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}else callback(Asc.c_oAscError.ID.UplImageFileCount)}function UploadImageUrls(files,
documentId,documentUserId,jwt,callback){if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);var file=aFiles.pop();var aResultUrls=[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push({path:i,
url:urls[i]});break}if(aFiles.length===0)callback(aResultUrls);else{file=aFiles.pop();var xhr=new XMLHttpRequest;url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}}else callback([])};var xhr=new XMLHttpRequest;xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",
file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}else callback(Asc.c_oAscError.ID.UplImageFileCount)}function ValidateUploadImage(files){var nRes=c_oAscServerError.NoError;if(files.length>0)for(var i=0,length=files.length;i<length;i++){var file=files[i];var sName=file.fileName||file.name;if(sName){var bSupported=false;var ext=GetFileExtension(sName);if(null!==ext)for(var j=0,length2=c_oAscImageUploadProp.SupportedFormats.length;j<length2;j++)if(c_oAscImageUploadProp.SupportedFormats[j]==
ext){bSupported=true;break}if(false==bSupported)nRes=c_oAscServerError.UploadExtension}if(Asc.c_oAscError.ID.No==nRes){var nSize=file.fileSize||file.size;if(nSize&&c_oAscImageUploadProp.MaxFileSize<nSize)nRes=c_oAscServerError.UploadContentLength}if(c_oAscServerError.NoError!=nRes)break}else nRes=c_oAscServerError.UploadCountFiles;return nRes}function CanDropFiles(event){var editor=window["Asc"]["editor"]?window["Asc"]["editor"]:window.editor;if(!editor.isEnabledDropTarget())return false;var bRes=

@ -9413,11 +9413,11 @@ newCDocument;drawingObject.graphicObject.graphicObject.Set_Parent(newCDocument);
node,isIntoShape);else window["AscCommon"].g_specialPasteHelper.Paste_Process_End()};worksheet.objectRender.controller.checkSelectedObjectsAndCallback2(callback);return}if(copyPasteUseBinary){this.activeRange=worksheet.model.selectionRange.getLast().clone();binaryResult=this._pasteFromBinaryClassHtml(worksheet,node,isIntoShape);if(binaryResult===true)return;else if(binaryResult!==false&&binaryResult!=undefined)node=binaryResult}this.activeRange=worksheet.model.selectionRange.getLast().clone();var callBackAfterLoadImages=
function(){History.TurnOff();var oTempDrawingDocument=worksheet.model.DrawingDocument;var newCDocument=new CDocument(oTempDrawingDocument,false);newCDocument.bFromDocument=true;newCDocument.Content[0].bFromDocument=true;newCDocument.theme=window["Asc"]["editor"].wbModel.theme;var oOldLogicDocument=oTempDrawingDocument.m_oLogicDocument;oTempDrawingDocument.m_oLogicDocument=newCDocument;var oOldEditor=undefined;if("undefined"!==typeof editor)oOldEditor=editor;editor={WordControl:oTempDrawingDocument,
isDocumentEditor:true};var oPasteProcessor=new AscCommon.PasteProcessor({WordControl:{m_oLogicDocument:newCDocument},FontLoader:{}},false,false);oPasteProcessor._Prepeare_recursive(node,true,true);if(window["AscCommon"].g_specialPasteHelper.specialPasteStart&&window["AscCommon"].g_specialPasteHelper.specialPasteData.aContent)oPasteProcessor.aContent=window["AscCommon"].g_specialPasteHelper.specialPasteData.aContent;else{oPasteProcessor._Execute(node,{},true,true,false);window["AscCommon"].g_specialPasteHelper.specialPasteData.aContent=
oPasteProcessor.aContent}editor=oOldEditor;oTempDrawingDocument.m_oLogicDocument=oOldLogicDocument;History.TurnOn();var oPasteFromBinaryWord=new pasteFromBinaryWord(t,worksheet);oPasteFromBinaryWord._paste(worksheet,{content:oPasteProcessor.aContent})};var aImagesToDownload=this._getImageFromHtml(node,true);var specialPasteProps=window["AscCommon"].g_specialPasteHelper.specialPasteProps;if(aImagesToDownload!==null&&(!specialPasteProps||specialPasteProps&&specialPasteProps.images)){var api=Asc["editor"];
AscCommon.sendImgUrls(api,aImagesToDownload,function(data){for(var i=0,length=Math.min(data.length,aImagesToDownload.length);i<length;++i){var elem=data[i];var sFrom=aImagesToDownload[i];if(null!=elem.url)t.oImages[sFrom]=AscCommon.g_oDocumentUrls.imagePath2Local(elem.path);else t.oImages[sFrom]=sFrom}t.alreadyLoadImagesOnServer=true;callBackAfterLoadImages()},true)}else callBackAfterLoadImages()},_pasteFromBinaryClassHtml:function(worksheet,node,isIntoShape){var base64=null,base64FromWord=null,base64FromPresentation=
null,t=this;var returnBinary=this._getClassBinaryFromHtml(node);base64=returnBinary.base64;base64FromWord=returnBinary.base64FromWord;base64FromPresentation=returnBinary.base64FromPresentation;var result=false;if(base64!=null)result=this._pasteFromBinaryExcel(worksheet,base64,isIntoShape);else if(base64FromWord)result=this._pasteFromBinaryWord(worksheet,base64FromWord,isIntoShape);else if(base64FromPresentation)result=this._pasteFromBinaryPresentation(worksheet,base64FromPresentation,isIntoShape);
return result},_getClassBinaryFromHtml:function(node){var base64=null,base64FromWord=null,base64FromPresentation=null;var classNode=AscCommon.searchBinaryClass(node);if(classNode!=null){var cL=classNode.split(" ");for(var i=0;i<cL.length;i++)if(cL[i].indexOf("xslData;")>-1)base64=cL[i].split("xslData;")[1];else if(cL[i].indexOf("docData;")>-1)base64FromWord=cL[i].split("docData;")[1];else if(cL[i].indexOf("pptData;")>-1)base64FromPresentation=cL[i].split("pptData;")[1]}return{base64:base64,base64FromWord:base64FromWord,
base64FromPresentation:base64FromPresentation}},_getImageFromHtml:function(html,isGetUrlsArray){var res=null;if(html){var allImages=html.getElementsByTagName("img");if(allImages&&allImages.length)if(isGetUrlsArray){var arrayImages=[];for(var i=0;i<allImages.length;i++)arrayImages[i]=allImages[i].src;res=arrayImages}else res=allImages}return res},_getBinaryColor:function(c){var bin,m,x,type,r,g,b,a,s;var reColor=/^\s*(?:#?([0-9a-f]{6})|#?([0-9a-f]{3})|rgba?\s*\(\s*((?:\d*\.?\d+)(?:\s*,\s*(?:\d*\.?\d+)){2,3})\s*\))\s*$/i;
oPasteProcessor.aContent}editor=oOldEditor;oTempDrawingDocument.m_oLogicDocument=oOldLogicDocument;History.TurnOn();var oPasteFromBinaryWord=new pasteFromBinaryWord(t,worksheet);oPasteFromBinaryWord._paste(worksheet,{content:oPasteProcessor.aContent})};var aImagesToDownload=this._getImageFromHtml(node,true);if(aImagesToDownload)return;var specialPasteProps=window["AscCommon"].g_specialPasteHelper.specialPasteProps;if(aImagesToDownload!==null&&(!specialPasteProps||specialPasteProps&&specialPasteProps.images)){var api=
Asc["editor"];AscCommon.sendImgUrls(api,aImagesToDownload,function(data){for(var i=0,length=Math.min(data.length,aImagesToDownload.length);i<length;++i){var elem=data[i];var sFrom=aImagesToDownload[i];if(null!=elem.url)t.oImages[sFrom]=AscCommon.g_oDocumentUrls.imagePath2Local(elem.path);else t.oImages[sFrom]=sFrom}t.alreadyLoadImagesOnServer=true;callBackAfterLoadImages()},true)}else callBackAfterLoadImages()},_pasteFromBinaryClassHtml:function(worksheet,node,isIntoShape){var base64=null,base64FromWord=
null,base64FromPresentation=null,t=this;var returnBinary=this._getClassBinaryFromHtml(node);base64=returnBinary.base64;base64FromWord=returnBinary.base64FromWord;base64FromPresentation=returnBinary.base64FromPresentation;var result=false;if(base64!=null)result=this._pasteFromBinaryExcel(worksheet,base64,isIntoShape);else if(base64FromWord)result=this._pasteFromBinaryWord(worksheet,base64FromWord,isIntoShape);else if(base64FromPresentation)result=this._pasteFromBinaryPresentation(worksheet,base64FromPresentation,
isIntoShape);return result},_getClassBinaryFromHtml:function(node){var base64=null,base64FromWord=null,base64FromPresentation=null;var classNode=AscCommon.searchBinaryClass(node);if(classNode!=null){var cL=classNode.split(" ");for(var i=0;i<cL.length;i++)if(cL[i].indexOf("xslData;")>-1)base64=cL[i].split("xslData;")[1];else if(cL[i].indexOf("docData;")>-1)base64FromWord=cL[i].split("docData;")[1];else if(cL[i].indexOf("pptData;")>-1)base64FromPresentation=cL[i].split("pptData;")[1]}return{base64:base64,
base64FromWord:base64FromWord,base64FromPresentation:base64FromPresentation}},_getImageFromHtml:function(html,isGetUrlsArray){var res=null;if(html){var allImages=html.getElementsByTagName("img");if(allImages&&allImages.length)if(isGetUrlsArray){var arrayImages=[];for(var i=0;i<allImages.length;i++)arrayImages[i]=allImages[i].src;res=arrayImages}else res=allImages}return res},_getBinaryColor:function(c){var bin,m,x,type,r,g,b,a,s;var reColor=/^\s*(?:#?([0-9a-f]{6})|#?([0-9a-f]{3})|rgba?\s*\(\s*((?:\d*\.?\d+)(?:\s*,\s*(?:\d*\.?\d+)){2,3})\s*\))\s*$/i;
if(typeof c==="number")bin=c;else{m=reColor.exec(c);if(!m)return null;if(m[1]){x=[m[1].slice(0,2),m[1].slice(2,4),m[1].slice(4)];type=1}else if(m[2]){x=[m[2].slice(0,1),m[2].slice(1,2),m[2].slice(2)];type=0}else{x=m[3].split(/\s*,\s*/i);type=x.length===3?2:3}r=parseInt(type!==0?x[0]:x[0]+x[0],type<2?16:10);g=parseInt(type!==0?x[1]:x[1]+x[1],type<2?16:10);b=parseInt(type!==0?x[2]:x[2]+x[2],type<2?16:10);a=type===3?Math.round(parseFloat(x[3])*100)*.01:1;bin=r<<16|g<<8|b}return bin},_checkMaxTextLength:function(aResult,
r,c){var result=false;var isChange=false;var currentCellData=aResult.content[r][c];if(currentCellData&&currentCellData.content){currentCellData=currentCellData.content;for(var i=0;i<currentCellData.length;i++)if(currentCellData[i]&&currentCellData[i].text&&currentCellData[i].text.length>c_oAscMaxCellOrCommentLength){isChange=true;var text=currentCellData[i].text;var format=currentCellData[i].format;var lengthOfText=text.length;var iterCount=Math.ceil(lengthOfText/c_oAscMaxCellOrCommentLength);var splitText;
for(var j=0;j<iterCount;j++){splitText=text.substr(c_oAscMaxCellOrCommentLength*j,c_oAscMaxCellOrCommentLength*(j+1));if(!aResult.content[r])aResult.content[r]=[];if(!aResult.content[r][c])aResult.content[r][c]=[];if(!aResult.content[r][c].content)aResult.content[r][c].content=[];aResult.content[r][c].content[0]={format:format,text:splitText};if(iterCount!==j+1)r++}}if(isChange)result={aResult:aResult,r:r,c:c}}return result},_getBorderStyleName:function(borderStyle,borderWidth){var res=null;var nBorderWidth=

@ -622,10 +622,10 @@ url+'" method="POST" enctype="multipart/form-data"><input id="apiiuFile" name="a
ValidateUploadImage(e.target.files);if(c_oAscServerError.NoError!=nError){callbackOld(mapAscServerErrorToAscError(nError));return}}callbackOld(Asc.c_oAscError.ID.No);fileSubmit.click()}}if(AscBrowser.isOpera)setTimeout(function(){fileName.click()},0);else fileName.click()}function InitDragAndDrop(oHtmlElement,callback){if("undefined"!=typeof FileReader&&null!=oHtmlElement){oHtmlElement["ondragover"]=function(e){e.preventDefault();e.dataTransfer.dropEffect=CanDropFiles(e)?"copy":"none";if(e.dataTransfer.dropEffect==
"copy"){var editor=window["Asc"]["editor"]?window["Asc"]["editor"]:window.editor;editor.beginInlineDropTarget(e)}return false};oHtmlElement["ondrop"]=function(e){e.preventDefault();var files=e.dataTransfer.files;var nError=ValidateUploadImage(files);var editor=window["Asc"]["editor"]?window["Asc"]["editor"]:window.editor;editor.endInlineDropTarget(e);if(nError==c_oAscServerError.UploadCountFiles){try{var htmlValue=e.dataTransfer.getData("text/html");if(htmlValue&&!AscCommon.AscBrowser.isIE){var index=
htmlValue.indexOf("StartHTML");var indexHtml=htmlValue.indexOf("<html");if(-1==indexHtml)indexHtml=htmlValue.indexOf("<HTML");if(index>0&&indexHtml>0&&index<indexHtml)htmlValue=htmlValue.substr(indexHtml);editor["pluginMethod_PasteHtml"](htmlValue);return}}catch(err){}try{var textValue=e.dataTransfer.getData("text/plain");if(textValue){editor["pluginMethod_PasteText"](textValue);return}}catch(err$16){}try{var textValue=e.dataTransfer.getData("Text");if(textValue){editor["pluginMethod_PasteText"](textValue);
return}}catch(err$17){}}callback(mapAscServerErrorToAscError(nError),files)}}}function UploadImageFiles(files,documentId,documentUserId,jwt,callback){window.parent.APP.UploadImageFiles(files,documentId,documentUserId,jwt,callback);return;if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);var file=aFiles.pop();var aResultUrls=
[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push(urls[i]);break}if(aFiles.length===0)callback(Asc.c_oAscError.ID.No,aResultUrls);else{file=aFiles.pop();var xhr=new XMLHttpRequest;url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);
xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}}else callback(Asc.c_oAscError.ID.UplImageFileCount)};var xhr=new XMLHttpRequest;xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}else callback(Asc.c_oAscError.ID.UplImageFileCount)}function UploadImageUrls(files,documentId,documentUserId,
jwt,callback){if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);var file=aFiles.pop();var aResultUrls=[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push({path:i,
return}}catch(err$17){}}callback(mapAscServerErrorToAscError(nError),files)}}}function UploadImageFiles(files,documentId,documentUserId,jwt,callback){window.parent.APP.UploadImageFiles(files,documentId,documentUserId,jwt,function(err,urls){callback(err||Asc.c_oAscError.ID.No,urls)});return;if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);
var file=aFiles.pop();var aResultUrls=[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push(urls[i]);break}if(aFiles.length===0)callback(Asc.c_oAscError.ID.No,aResultUrls);else{file=aFiles.pop();var xhr=new XMLHttpRequest;url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+=
"?token="+encodeURIComponent(jwt);xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}}else callback(Asc.c_oAscError.ID.UplImageFileCount)};var xhr=new XMLHttpRequest;xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}else callback(Asc.c_oAscError.ID.UplImageFileCount)}function UploadImageUrls(files,
documentId,documentUserId,jwt,callback){if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);var file=aFiles.pop();var aResultUrls=[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push({path:i,
url:urls[i]});break}if(aFiles.length===0)callback(aResultUrls);else{file=aFiles.pop();var xhr=new XMLHttpRequest;url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}}else callback([])};var xhr=new XMLHttpRequest;xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",
file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}else callback(Asc.c_oAscError.ID.UplImageFileCount)}function ValidateUploadImage(files){var nRes=c_oAscServerError.NoError;if(files.length>0)for(var i=0,length=files.length;i<length;i++){var file=files[i];var sName=file.fileName||file.name;if(sName){var bSupported=false;var ext=GetFileExtension(sName);if(null!==ext)for(var j=0,length2=c_oAscImageUploadProp.SupportedFormats.length;j<length2;j++)if(c_oAscImageUploadProp.SupportedFormats[j]==
ext){bSupported=true;break}if(false==bSupported)nRes=c_oAscServerError.UploadExtension}if(Asc.c_oAscError.ID.No==nRes){var nSize=file.fileSize||file.size;if(nSize&&c_oAscImageUploadProp.MaxFileSize<nSize)nRes=c_oAscServerError.UploadContentLength}if(c_oAscServerError.NoError!=nRes)break}else nRes=c_oAscServerError.UploadCountFiles;return nRes}function CanDropFiles(event){var editor=window["Asc"]["editor"]?window["Asc"]["editor"]:window.editor;if(!editor.isEnabledDropTarget())return false;var bRes=

@ -622,10 +622,10 @@ url+'" method="POST" enctype="multipart/form-data"><input id="apiiuFile" name="a
ValidateUploadImage(e.target.files);if(c_oAscServerError.NoError!=nError){callbackOld(mapAscServerErrorToAscError(nError));return}}callbackOld(Asc.c_oAscError.ID.No);fileSubmit.click()}}if(AscBrowser.isOpera)setTimeout(function(){fileName.click()},0);else fileName.click()}function InitDragAndDrop(oHtmlElement,callback){if("undefined"!=typeof FileReader&&null!=oHtmlElement){oHtmlElement["ondragover"]=function(e){e.preventDefault();e.dataTransfer.dropEffect=CanDropFiles(e)?"copy":"none";if(e.dataTransfer.dropEffect==
"copy"){var editor=window["Asc"]["editor"]?window["Asc"]["editor"]:window.editor;editor.beginInlineDropTarget(e)}return false};oHtmlElement["ondrop"]=function(e){e.preventDefault();var files=e.dataTransfer.files;var nError=ValidateUploadImage(files);var editor=window["Asc"]["editor"]?window["Asc"]["editor"]:window.editor;editor.endInlineDropTarget(e);if(nError==c_oAscServerError.UploadCountFiles){try{var htmlValue=e.dataTransfer.getData("text/html");if(htmlValue&&!AscCommon.AscBrowser.isIE){var index=
htmlValue.indexOf("StartHTML");var indexHtml=htmlValue.indexOf("<html");if(-1==indexHtml)indexHtml=htmlValue.indexOf("<HTML");if(index>0&&indexHtml>0&&index<indexHtml)htmlValue=htmlValue.substr(indexHtml);editor["pluginMethod_PasteHtml"](htmlValue);return}}catch(err){}try{var textValue=e.dataTransfer.getData("text/plain");if(textValue){editor["pluginMethod_PasteText"](textValue);return}}catch(err$1){}try{var textValue=e.dataTransfer.getData("Text");if(textValue){editor["pluginMethod_PasteText"](textValue);
return}}catch(err$2){}}callback(mapAscServerErrorToAscError(nError),files)}}}function UploadImageFiles(files,documentId,documentUserId,jwt,callback){window.parent.APP.UploadImageFiles(files,documentId,documentUserId,jwt,callback);return;if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);var file=aFiles.pop();var aResultUrls=
[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push(urls[i]);break}if(aFiles.length===0)callback(Asc.c_oAscError.ID.No,aResultUrls);else{file=aFiles.pop();var xhr=new XMLHttpRequest;url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);
xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}}else callback(Asc.c_oAscError.ID.UplImageFileCount)};var xhr=new XMLHttpRequest;xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}else callback(Asc.c_oAscError.ID.UplImageFileCount)}function UploadImageUrls(files,documentId,documentUserId,
jwt,callback){if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);var file=aFiles.pop();var aResultUrls=[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push({path:i,
return}}catch(err$2){}}callback(mapAscServerErrorToAscError(nError),files)}}}function UploadImageFiles(files,documentId,documentUserId,jwt,callback){window.parent.APP.UploadImageFiles(files,documentId,documentUserId,jwt,function(err,urls){callback(err||Asc.c_oAscError.ID.No,urls)});return;if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);
var file=aFiles.pop();var aResultUrls=[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push(urls[i]);break}if(aFiles.length===0)callback(Asc.c_oAscError.ID.No,aResultUrls);else{file=aFiles.pop();var xhr=new XMLHttpRequest;url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+=
"?token="+encodeURIComponent(jwt);xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}}else callback(Asc.c_oAscError.ID.UplImageFileCount)};var xhr=new XMLHttpRequest;xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}else callback(Asc.c_oAscError.ID.UplImageFileCount)}function UploadImageUrls(files,
documentId,documentUserId,jwt,callback){if(files.length>0){var url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);var aFiles=[];for(var i=files.length-1;i>-1;--i)aFiles.push(files[i]);var file=aFiles.pop();var aResultUrls=[];var fOnReadyChnageState=function(){if(4==this.readyState)if(this.status==200||this.status==1223){var urls=JSON.parse(this.responseText);g_oDocumentUrls.addUrls(urls);for(var i in urls)if(urls.hasOwnProperty(i)){aResultUrls.push({path:i,
url:urls[i]});break}if(aFiles.length===0)callback(aResultUrls);else{file=aFiles.pop();var xhr=new XMLHttpRequest;url=sUploadServiceLocalUrl+"/"+documentId+"/"+documentUserId+"/"+g_oDocumentUrls.getMaxIndex();if(jwt)url+="?token="+encodeURIComponent(jwt);xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}}else callback([])};var xhr=new XMLHttpRequest;xhr.open("POST",url,true);xhr.setRequestHeader("Content-Type",
file.type||"application/octet-stream");xhr.onreadystatechange=fOnReadyChnageState;xhr.send(file)}else callback(Asc.c_oAscError.ID.UplImageFileCount)}function ValidateUploadImage(files){var nRes=c_oAscServerError.NoError;if(files.length>0)for(var i=0,length=files.length;i<length;i++){var file=files[i];var sName=file.fileName||file.name;if(sName){var bSupported=false;var ext=GetFileExtension(sName);if(null!==ext)for(var j=0,length2=c_oAscImageUploadProp.SupportedFormats.length;j<length2;j++)if(c_oAscImageUploadProp.SupportedFormats[j]==
ext){bSupported=true;break}if(false==bSupported)nRes=c_oAscServerError.UploadExtension}if(Asc.c_oAscError.ID.No==nRes){var nSize=file.fileSize||file.size;if(nSize&&c_oAscImageUploadProp.MaxFileSize<nSize)nRes=c_oAscServerError.UploadContentLength}if(c_oAscServerError.NoError!=nRes)break}else nRes=c_oAscServerError.UploadCountFiles;return nRes}function CanDropFiles(event){var editor=window["Asc"]["editor"]?window["Asc"]["editor"]:window.editor;if(!editor.isEnabledDropTarget())return false;var bRes=

@ -109,6 +109,17 @@ proxy.mailboxes = {
});
var ciphertext = crypto.encrypt(text, user.curvePublic);
// If we've sent this message to one of our teams' mailbox, we may want to "dismiss" it
// automatically
if (user.viewed) {
var team = Util.find(ctx, ['store', 'proxy', 'teams', user.viewed]);
if (team) {
var hash = ciphertext.slice(0,64);
var viewed = Util.find(team, ['keys', 'mailbox', 'viewed']);
if (Array.isArray(viewed)) { viewed.push(hash); }
}
}
anonRpc.send("WRITE_PRIVATE_MESSAGE", [
user.channel,
ciphertext
@ -126,10 +137,9 @@ proxy.mailboxes = {
var dismiss = function (ctx, data, cId, cb) {
var type = data.type;
var hash = data.hash;
var m = Util.find(ctx, ['store', 'proxy', 'mailboxes', type]);
if (!m) { return void cb({error: 'NOT_FOUND'}); }
var box = ctx.boxes[type];
if (!box) { return void cb({error: 'NOT_LOADED'}); }
var m = box.data || {};
// If the hash in in our history, get the index from the history:
// - if the index is 0, we can change our lastKnownHash
@ -191,7 +201,15 @@ proxy.mailboxes = {
};
var openChannel = function (ctx, type, m, onReady) {
var leaveChannel = function (ctx, type, cb) {
var box = ctx.boxes[type];
if (!box) { return void cb(); }
if (!box.cpNf || typeof(box.cpNf.stop) !== "function") { return void cb('EINVAL'); }
box.cpNf.stop();
delete ctx.boxes[type];
};
var openChannel = function (ctx, type, m, onReady, opts) {
opts = opts || {};
var box = ctx.boxes[type] = {
channel: m.channel,
type: type,
@ -210,7 +228,8 @@ proxy.mailboxes = {
console.error(e);
}
box.queue.push(msg);
}
},
data: m
};
if (!Crypto.Mailbox) {
return void console.error("chainpad-crypto is outdated and doesn't support mailboxes.");
@ -224,7 +243,7 @@ proxy.mailboxes = {
channel: m.channel,
noChainPad: true,
crypto: crypto,
owners: [ctx.store.proxy.edPublic],
owners: opts.owners || [ctx.store.proxy.edPublic],
lastKnownHash: m.lastKnownHash
};
cfg.onConnectionChange = function () {}; // Allow reconnections in chainpad-netflux
@ -346,7 +365,7 @@ proxy.mailboxes = {
// Continue
onReady();
};
CpNetflux.start(cfg);
box.cpNf = CpNetflux.start(cfg);
};
var initializeHistory = function (ctx) {
@ -467,6 +486,19 @@ proxy.mailboxes = {
}
});
Object.keys(store.proxy.teams || {}).forEach(function (teamId) {
var team = store.proxy.teams[teamId];
if (!team) { return; }
var teamMailbox = team.keys.mailbox || {};
if (!teamMailbox.channel) { return; }
var opts = {
owners: [Util.find(team, ['keys', 'drive', 'edPublic'])]
};
openChannel(ctx, 'team-'+teamId, teamMailbox, function () {
//console.log('Mailbox team', teamId);
}, opts);
});
mailbox.post = function (box, type, content) {
var b = ctx.boxes[box];
if (!b) { return; }
@ -477,9 +509,12 @@ proxy.mailboxes = {
});
};
mailbox.open = function (key, m, cb) {
if (TYPES.indexOf(key) === -1) { return; }
openChannel(ctx, key, m, cb);
mailbox.open = function (key, m, cb, team, opts) {
if (TYPES.indexOf(key) === -1 && !team) { return; }
openChannel(ctx, key, m, cb, opts);
};
mailbox.close = function (key, cb) {
leaveChannel(ctx, key, cb);
};
mailbox.dismiss = function (data, cb) {

@ -798,7 +798,6 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) {
return void console.error(err);
}
metadata = ref.internal.metadata = (data && data[0]) || undefined;
console.log("TEAM_METADATA", metadata);
});
}).nThen(function (w) {
if (!config.keys.teamEdPublic && metadata && metadata.validateKey) {

@ -126,6 +126,9 @@ define([
delete ctx.store.proxy.teams[teamId];
ctx.emit('LEAVE_TEAM', teamId, team.clients);
ctx.updateMetadata();
ctx.store.mailbox.close('team-'+teamId, function () {
// Close team mailbox
});
};
var getTeamChannelList = function (ctx, id) {
@ -172,6 +175,23 @@ define([
Pinpad.create(ctx.store.network, data, function (e, call) {
if (e) { return void cb(e); }
team.rpc = call;
team.pin = function (data, cb) {
if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { console.error('expected a callback'); }
team.rpc.pin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
team.unpin = function (data, cb) {
if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { console.error('expected a callback'); }
team.rpc.unpin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
cb();
});
});
@ -242,27 +262,7 @@ define([
nThen(function (waitFor) {
// Init Team RPC
if (!keys.drive.edPrivate) { return; }
initRpc(ctx, team, keys.drive, waitFor(function (err) {
if (err) { return; }
team.pin = function (data, cb) {
if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { console.error('expected a callback'); }
team.rpc.pin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
team.unpin = function (data, cb) {
if (!team.rpc) { return void cb({error: 'TEAM_RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { console.error('expected a callback'); }
team.rpc.unpin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
}));
initRpc(ctx, team, keys.drive, waitFor(function () {}));
}).nThen(function () {
// Create the proxy manager
var loadSharedFolder = function (id, data, cb, isNew) {
@ -471,6 +471,16 @@ define([
// Make sure we have not been kicked from the roster
var state = roster.getState();
var me = Util.find(ctx, ['store', 'proxy', 'curvePublic']);
// XXX FIXME roster history temporarily corrupted, don't leave the team
if (!state.members || !Object.keys(state.members).length) {
lm.stop();
roster.stop();
lm.proxy = {};
cb({error: 'EINVAL'});
waitFor.abort();
console.error(JSON.stringify(state));
return;
}
if (!state.members[me]) {
lm.stop();
roster.stop();
@ -494,6 +504,8 @@ define([
var roHash = Hash.getViewHashFromKeys(secret);
var keyPair = Nacl.sign.keyPair(); // keyPair.secretKey , keyPair.publicKey
var curvePair = Nacl.box.keyPair();
var rosterSeed = Crypto.Team.createSeed();
var rosterKeys = Crypto.Team.deriveMemberKeys(rosterSeed, {
curvePublic: ctx.store.proxy.curvePublic,
@ -585,6 +597,14 @@ define([
proxy.on('ready', function () {
// Store keys in our drive
var keys = {
mailbox: {
channel: Hash.createChannelId(),
viewed: [],
keys: {
curvePrivate: Nacl.util.encodeBase64(curvePair.secretKey),
curvePublic: Nacl.util.encodeBase64(curvePair.publicKey)
}
},
drive: {
edPrivate: Nacl.util.encodeBase64(keyPair.secretKey),
edPublic: Nacl.util.encodeBase64(keyPair.publicKey)
@ -601,7 +621,7 @@ define([
view: rosterKeys.viewKeyStr,
}
};
ctx.store.proxy.teams[id] = {
var t = ctx.store.proxy.teams[id] = {
owner: true,
channel: secret.channel,
hash: hash,
@ -618,6 +638,11 @@ define([
onReady(ctx, id, lm, roster, keys, cId, function () {
Feedback.send('TEAM_CREATION');
ctx.store.mailbox.open('team-'+id, t.keys.mailbox, function () {
// Team mailbox loaded
}, true, {
owners: t.keys.drive.edPublic
});
ctx.updateMetadata();
cb();
});
@ -720,6 +745,11 @@ define([
team.rpc.removePins(waitFor(function (err) {
if (err) { console.error(err); }
}));
// Delete the mailbox
var mailboxChan = Util.find(teamData, ['keys', 'mailbox', 'channel']);
team.rpc.removeOwnedChannel(mailboxChan, waitFor(function (err) {
if (err) { console.error(err); }
}));
// Delete the roster
var rosterChan = Util.find(teamData, ['keys', 'roster', 'channel']);
ctx.store.rpc.removeOwnedChannel(rosterChan, waitFor(function (err) {
@ -750,6 +780,12 @@ define([
ctx.onReadyHandlers[id] = [];
openChannel(ctx, team, id, function (obj) {
if (!(obj && obj.error)) { console.debug('Team joined:' + id); }
var t = ctx.store.proxy.teams[id];
ctx.store.mailbox.open('team-'+id, t.keys.mailbox, function () {
// Team mailbox loaded
}, true, {
owners: t.keys.drive.edPublic
});
ctx.updateMetadata();
cb(obj);
});
@ -1093,6 +1129,7 @@ define([
teamData.hash = data.hash;
teamData.keys.drive.edPrivate = data.keys.drive.edPrivate;
teamData.keys.chat.edit = data.keys.chat.edit;
initRpc(ctx, team, teamData.keys.drive, function () {});
var secret = Hash.getSecrets('team', data.hash, teamData.password);
team.secondaryKey = secret && secret.keys.secondaryKey;
@ -1101,6 +1138,9 @@ define([
delete teamData.keys.drive.edPrivate;
delete teamData.keys.chat.edit;
delete team.secondaryKey;
if (team.rpc && team.rpc.destroy) {
team.rpc.destroy();
}
}
updateMyRights(ctx, teamId, data.hash);
@ -1566,6 +1606,25 @@ define([
});
};
var deriveMailbox = function (team) {
if (!team) { return; }
if (team.keys && team.keys.mailbox) { return team.keys.mailbox; }
var strSeed = Util.find(team, ['keys', 'roster', 'edit']);
if (!strSeed) { return; }
var hash = Nacl.hash(Nacl.util.decodeUTF8(strSeed));
var seed = hash.slice(0,32);
var mailboxChannel = Util.uint8ArrayToHex(hash.slice(32,48));
var curvePair = Nacl.box.keyPair.fromSecretKey(seed);
return {
channel: mailboxChannel,
viewed: [],
keys: {
curvePrivate: Nacl.util.encodeBase64(curvePair.secretKey),
curvePublic: Nacl.util.encodeBase64(curvePair.publicKey)
}
};
};
Team.init = function (cfg, waitFor, emit) {
var team = {};
var store = cfg.store;
@ -1595,6 +1654,9 @@ define([
Object.keys(teams).forEach(function (id) {
ctx.onReadyHandlers[id] = [];
if (!Util.find(teams, [id, 'keys', 'mailbox'])) {
teams[id].keys.mailbox = deriveMailbox(teams[id]);
}
openChannel(ctx, teams[id], id, waitFor(function (err) {
if (err) { return void console.error(err); }
console.debug('Team '+id+' ready');
@ -1609,12 +1671,15 @@ define([
var safe = false;
if (['drive', 'teams', 'settings'].indexOf(app) !== -1) { safe = true; }
Object.keys(teams).forEach(function (id) {
if (!ctx.teams[id]) { return; }
t[id] = {
owner: teams[id].owner,
name: teams[id].metadata.name,
edPublic: Util.find(teams[id], ['keys', 'drive', 'edPublic']),
avatar: Util.find(teams[id], ['metadata', 'avatar']),
viewer: !Util.find(teams[id], ['keys', 'drive', 'edPrivate']),
notifications: Util.find(teams[id], ['keys', 'mailbox', 'channel']),
curvePublic: Util.find(teams[id], ['keys', 'mailbox', 'keys', 'curvePublic']),
};
if (safe && ctx.teams[id]) {
@ -1663,6 +1728,15 @@ define([
team.removeClient = function (clientId) {
removeClient(ctx, clientId);
};
var listTeams = function (cb) {
var t = Util.clone(teams);
Object.keys(t).forEach(function (id) {
// If failure to load the team, don't send it
if (ctx.teams[id]) { return; }
t[id].error = true;
});
cb(t);
};
team.execCommand = function (clientId, obj, cb) {
if (ctx.store.offline) {
return void cb({ error: 'OFFLINE' });
@ -1675,7 +1749,7 @@ define([
return void subscribe(ctx, data, clientId, cb);
}
if (cmd === 'LIST_TEAMS') {
return void cb(store.proxy.teams);
return void listTeams(cb);
}
if (cmd === 'OPEN_TEAM_CHAT') {
return void openTeamChat(ctx, data, clientId, cb);

@ -490,6 +490,7 @@ define([
}, {
typeInput: $select[0]
});
$select.find('button').addClass('btn');
});
toolbar.$drawer.append($export);
};

@ -105,11 +105,14 @@ define([
});
// Call the onMessage handlers
var isNotification = function (type) {
return type === "notifications" || /^team-/.test(type);
};
var pushMessage = function (data, handler) {
var todo = function (f) {
try {
var el;
if (data.type === 'notifications') {
if (isNotification(data.type)) {
Notifications.add(Common, data);
el = createElement(data);
}
@ -129,7 +132,7 @@ define([
onViewedHandlers.forEach(function (f) {
try {
f(data);
if (data.type === 'notifications') {
if (isNotification(data.type)) {
Notifications.remove(Common, data);
}
} catch (e) {
@ -141,7 +144,6 @@ define([
var onMessage = function (data, cb) {
// data = { type: 'type', content: {msg: 'msg', hash: 'hash'} }
console.debug(data.type, data.content);
pushMessage(data);
if (data.content && typeof (data.content.getFormatText) === "function") {
var text = $('<div>').html(data.content.getFormatText()).text();
@ -173,20 +175,23 @@ define([
execCommand('SUBSCRIBE', null, function () {});
subscribed = true;
}
var teams = types.indexOf('team') !== -1;
if (typeof(cfg.onViewed) === "function") {
onViewedHandlers.push(function (data) {
if (types.indexOf(data.type) === -1) { return; }
var type = data.type;
if (types.indexOf(type) === -1 && !(teams && /^team-/.test(type))) { return; }
cfg.onViewed(data);
});
}
if (typeof(cfg.onMessage) === "function") {
onMessageHandlers.push(function (data, el) {
if (types.indexOf(data.type) === -1) { return; }
var type = data.type;
if (types.indexOf(type) === -1 && !(teams && /^team-/.test(type))) { return; }
cfg.onMessage(data, el);
});
}
Object.keys(history).forEach(function (type) {
if (types.indexOf(type) === -1) { return; }
if (types.indexOf(type) === -1 && !(teams && /^team-/.test(type))) { return; }
history[type].forEach(function (data) {
pushMessage({
type: type,

@ -1025,7 +1025,7 @@ MessengerUI, Messages) {
$button.addClass('fa-bell');
};
Common.mailbox.subscribe(['notifications'], {
Common.mailbox.subscribe(['notifications', 'team'], {
onMessage: function (data, el) {
if (el) {
$(div).prepend(el);

@ -148,7 +148,6 @@
"or": "o",
"tags_title": "Etiquetes (només vostres)",
"tags_add": "Actualitza les etiquetes d'aquesta pàgina",
"tags_searchHint": "Inicieu una cerca amb # al vostre CryptDrive per trobar els vostres documents etiquetats.",
"tags_notShared": "Les vostres etiquetes no es comparteixen amb altres persones usuàries",
"tags_duplicate": "Etiquetes duplicades: {0}",
"tags_noentry": "No podeu etiquetar un document esborrat!",

@ -21,7 +21,7 @@
"button_newslide": "Neue Präsentation",
"button_newwhiteboard": "Neues Whiteboard",
"button_newkanban": "Neues Kanban",
"common_connectionLost": "<b>Die Verbindung zum Server ist abgebrochen</b><br>Du verwendest jetzt das Dokument schreibgeschützt, bis die Verbindung wieder funktioniert.",
"common_connectionLost": "<b>Die Verbindung zum Server ist abgebrochen</b><br>Du kannst das Dokument nicht bearbeiten, bis die Verbindung wieder funktioniert.",
"websocketError": "Verbindung zum Websocket fehlgeschlagen...",
"typeError": "Dieses Dokument ist nicht mit der ausgewählten Anwendung kompatibel",
"onLogout": "Du bist ausgeloggt. {0}Klicke hier{1}, um dich wieder einzuloggen,<br>oder drücke <em>Escape</em>, um dein Pad schreibgeschützt zu benutzen.",
@ -146,7 +146,6 @@
"or": "oder",
"tags_title": "Tags (nur für dich)",
"tags_add": "Tags der ausgewählten Pads bearbeiten",
"tags_searchHint": "Beginne die Suche in deinem CryptDrive mit #, um getaggte Dokumente zu finden.",
"tags_notShared": "Deine Tags werden nicht mit anderen Benutzern geteilt",
"tags_duplicate": "Doppeltes Tag: {0}",
"tags_noentry": "Du kannst keine Tags zu einem gelöschten Pad hinzufügen!",
@ -1088,7 +1087,7 @@
"support_disabledTitle": "Support ist nicht aktiviert",
"support_disabledHint": "Diese CryptPad-Instanz wurde noch nicht für die Verwendung eines Support-Formulars konfiguriert.",
"support_cat_new": "Neues Ticket",
"support_formTitle": "Titel des Tickets",
"support_formTitle": "Neues Ticket",
"support_formHint": "Mit diesem Formular kann ein neues Support-Ticket eröffnet werden. Es erlaubt die sichere Kontaktaufnahme mit den Administratoren zur Lösung von Problemen oder Beantwortung von Fragen. Bitte eröffne kein neues Ticket, wenn du bereits ein offenes Ticket bezüglich des gleichen Problems hast. Verwende stattdessen die Antworten-Schaltfläche, um weitere Informationen hinzuzufügen.",
"support_formButton": "Absenden",
"support_formTitleError": "Fehler: Titel ist leer",
@ -1124,7 +1123,7 @@
"properties_unknownUser": "{0} unbekannte(r) Benutzer",
"fm_morePads": "Mehr",
"fc_openInCode": "Im Code-Editor öffnen",
"uploadFolder_modal_title": "Optionen für Ordnerupload",
"uploadFolder_modal_title": "Optionen für Hochladen des Ordners",
"uploadFolder_modal_filesPassword": "Passwort für Dateien",
"uploadFolder_modal_owner": "Eigene Dateien",
"uploadFolder_modal_forceSave": "Dateien im CryptDrive speichern",
@ -1182,7 +1181,7 @@
"team_rosterPromote": "Befördern",
"team_rosterDemote": "Degradieren",
"team_rosterKick": "Aus dem Team entfernen",
"team_inviteButton": "Kontakte einladen",
"team_inviteButton": "Mitglieder einladen",
"team_leaveButton": "Dieses Team verlassen",
"team_leaveConfirm": "Wenn du dieses Team verlässt, verlierst du den Zugriff auf das dazugehörige CryptDrive, den Chatverlauf und andere Inhalte. Bist du sicher?",
"team_owner": "Eigentümer",
@ -1382,5 +1381,18 @@
"settings_safeLinkDefault": "Sichere Links sind nun standardmäßig aktiviert. Bitte verwende zum Kopieren von Links das Menü <i class=\"fa fa-shhare-alt\"></i> <b>Teilen</b> und nicht die Adressleiste des Browsers.",
"info_imprintFlavour": "<a>Rechtliche Informationen über die Administratoren dieses Servers</a>.",
"info_privacyFlavour": "Unsere <a>Datenschutzerklärung</a> beschreibt, wie wir deine Daten verarbeiten.",
"user_about": "Über CryptPad"
"user_about": "Über CryptPad",
"support_cat_all": "Alle",
"support_cat_other": "Anderes",
"support_cat_account": "Benutzerkonto",
"support_cat_data": "Datenverlust",
"notification_folderSharedTeam": "{0} hat einen Ordner mit dem Team {2} geteilt: <b>{1}</b>",
"notification_fileSharedTeam": "{0} hat eine Datei mit dem Team {2} geteilt: <b>{1}</b>",
"notification_padSharedTeam": "{0} hat ein Pad mit dem Team {2} geteilt: <b>{1}</b>",
"support_addAttachment": "Anhang hinzufügen",
"support_attachments": "Anhänge",
"support_cat_bug": "Fehlerbericht",
"oo_refresh": "Neu laden",
"support_category": "Wähle eine Kategorie",
"oo_refreshText": "Dieses Dokument wurde aktualisiert"
}

@ -119,7 +119,6 @@
"or": "ή",
"tags_title": "Ετικέτες (για εσάς μόνο)",
"tags_add": "Ενημερώστε τις ετικέτες αυτής της σελίδας",
"tags_searchHint": "Ξεκινήστε μια αναζήτηση με το σύμβολο # στο CryptDrive σας για να βρείτε pads με ετικέτες.",
"tags_notShared": "Οι ετικέτες σας δεν μοιράζονται με άλλους χρήστες",
"tags_duplicate": "Διπλή ετικέτα: {0}",
"tags_noentry": "Δεν μπορείτε να βάλετε ετικέτα σε διεγραμένο pad!",

@ -472,7 +472,6 @@
"printBackgroundRemove": "Eliminar este fondo de pantalla",
"tags_title": "Etiquetas (sólo para tí)",
"tags_add": "Actualizar las etiquetas de esta página",
"tags_searchHint": "Comenzar una búsqueda con # en tú CryptDrive para encontrar las notas etiquetadas.",
"tags_notShared": "Tus etiquetas no están compartidas con otros usuarios",
"tags_duplicate": "Duplicar etiquetas:{0}",
"tags_noentry": "No puedes etiquetar una nota eliminada!",

@ -151,7 +151,6 @@
"or": "tai",
"tags_title": "Tunnisteet (vain sinulle)",
"tags_add": "Päivitä sivun tunnisteet",
"tags_searchHint": "Aloita hakusi CryptDrivessa #-merkillä löytääksesi tunnisteita sisältävät padit.",
"tags_notShared": "Tunnisteitasi ei jaeta muiden käyttäjien kanssa",
"tags_duplicate": "Kaksinkertainen tunniste: {0}",
"tags_noentry": "Et voi lisätä tunnistetta poistettuun padiin!",

@ -148,7 +148,6 @@
"or": "ou",
"tags_title": "Mots-clés du pad (pour vous uniquement)",
"tags_add": "Modifier les mots-clés de la sélection",
"tags_searchHint": "Commencez une recherche par # dans votre CryptDrive pour retrouver vos pads par mot-clé.",
"tags_notShared": "Vos mots-clés ne sont pas partagés avec les autres utilisateurs",
"tags_duplicate": "Mot-clé déjà présent : {0}",
"tags_noentry": "Vous ne pouvez pas ajouter de mots-clés à un pad supprimé !",
@ -1087,7 +1086,7 @@
"support_disabledTitle": "Le support n'est pas activé",
"support_disabledHint": "Cette instance de CryptPad n'est pas encore configurée pour utiliser le formulaire de support.",
"support_cat_new": "Nouveau ticket",
"support_formTitle": "Titre du ticket",
"support_formTitle": "Nouveau Ticket",
"support_formButton": "Envoyer",
"support_formTitleError": "Erreur : le titre est vide",
"support_formContentError": "Erreur : le contenu est vide",
@ -1165,7 +1164,7 @@
"team_inviteModalButton": "Inviter",
"team_pcsSelectLabel": "Sauver dans",
"team_pcsSelectHelp": "Créer un pad dans le drive d'une équipe rend cette équipe propriétaire du pad si l'option est cochée.",
"team_invitedToTeam": "{0} vous à inviter à rejoindre l'équipe : <b>{1}</b>",
"team_invitedToTeam": "{0} vous a invité à rejoindre l'équipe : <b>{1}</b>",
"team_kickedFromTeam": "{0} vous a exclu de l'équipe : <b>{1}</b>",
"team_acceptInvitation": "{0} a accepté votre offre de rejoindre l'équipe : <b>{1}</b>",
"team_declineInvitation": "{0} a refusé votre offre de rejoindre l'équipe : <b>{1}</b>",
@ -1184,7 +1183,7 @@
"team_rosterPromote": "Promouvoir",
"team_rosterDemote": "Rétrograder",
"team_rosterKick": "Expulser de l'équipe",
"team_inviteButton": "Inviter des contacts",
"team_inviteButton": "Inviter des membres",
"team_leaveButton": "Quitter cette équipe",
"team_leaveConfirm": "Si vous quittez cette équipe, vous perdrez l'accès à son CryptDrive, son chat et les autres contenus. Êtes-vous sûr ?",
"team_owner": "Propriétaires",
@ -1382,5 +1381,18 @@
"support_languagesPreamble": "L'équipe de support parle les langues suivantes :",
"info_privacyFlavour": "<a>Description de la confidentialité</a> de vos données.",
"user_about": "À propos de CryptPad",
"info_imprintFlavour": "<a>Informations légales sur les administateurs de cette instance</a>."
"info_imprintFlavour": "<a>Informations légales sur les administateurs de cette instance</a>.",
"support_cat_all": "Tout",
"support_cat_other": "Autre",
"support_cat_bug": "Rapport de bug",
"support_cat_data": "Perte de données",
"support_cat_account": "Compte utilisateur",
"notification_folderSharedTeam": "{0} a partagé un dossier avec l'équipe {2} : <b>{1}</b>",
"notification_fileSharedTeam": "{0} a partagé un fichier avec l'équipe {2} : <b>{1}</b>",
"notification_padSharedTeam": "{0} a partagé un pad avec l'équipe {2} : <b>{1}</b>",
"support_addAttachment": "Ajouter une pièce jointe",
"support_attachments": "Pièces jointes",
"oo_refreshText": "Ce document a été mis à jour",
"oo_refresh": "Recharger",
"support_category": "Choisir une catégorie"
}

@ -148,7 +148,6 @@
"or": "o",
"tags_title": "Tag (mostrati solo a te)",
"tags_add": "Aggiorna i tag di questa pagina",
"tags_searchHint": "Inizia una ricerca con # nel tuo CryptDrive per trovare i pad taggati.",
"tags_notShared": "I tuoi tag non sono condivisi con altri utenti",
"tags_duplicate": "Duplica tag: {0}",
"tags_noentry": "Non puoi taggare un pad eliminato!",

@ -151,7 +151,6 @@
"or": "or",
"tags_title": "Tags (for you only)",
"tags_add": "Update the tags for selected pads",
"tags_searchHint": "Start a search with # in your CryptDrive to find your tagged pads.",
"tags_notShared": "Your tags are not shared with other users",
"tags_duplicate": "Duplicate tag: {0}",
"tags_noentry": "You can't tag a deleted pad!",
@ -1109,7 +1108,7 @@
"support_disabledTitle": "Support is not enabled",
"support_disabledHint": "This CryptPad instance is not yet configured to use a support form.",
"support_cat_new": "New ticket",
"support_formTitle": "Ticket title",
"support_formTitle": "New Ticket",
"support_formHint": "This form can be used to create a new support ticket. Use it to contact the administrators to solve issues or ask any question in a secure way. Please don't create a new ticket if you already have an open ticket about the same issue, but use the reply button to provide more information.",
"support_formButton": "Send",
"support_formTitleError": "Error: title is empty",
@ -1188,7 +1187,7 @@
"team_rosterPromote": "Promote",
"team_rosterDemote": "Demote",
"team_rosterKick": "Kick from the team",
"team_inviteButton": "Invite contacts",
"team_inviteButton": "Invite members",
"team_leaveButton": "Leave this team",
"team_leaveConfirm": "If you leave this team you will lose access to its CryptDrive, chat history, and other contents. Are you sure?",
"team_owner": "Owners",
@ -1382,5 +1381,18 @@
"settings_safeLinkDefault": "Safe Links are now turned on by default. Please use the <i class=\"fa fa-shhare-alt\"></i> <b>Share</b> menu to copy links rather than your browser's address bar.",
"info_imprintFlavour": "<a>Legal information about the administrators of this instance</a>.",
"user_about": "About CryptPad",
"info_privacyFlavour": "Our <a>privacy policy</a> describes how we treat your data."
"info_privacyFlavour": "Our <a>privacy policy</a> describes how we treat your data.",
"support_cat_account": "User account",
"support_cat_data": "Loss of content",
"support_cat_bug": "Bug report",
"support_cat_other": "Other",
"support_cat_all": "All",
"support_attachments": "Attachments",
"support_addAttachment": "Add attachment",
"notification_padSharedTeam": "{0} has shared a pad with the team {2}: <b>{1}</b>",
"notification_fileSharedTeam": "{0} has shared a file with the team {2}: <b>{1}</b>",
"notification_folderSharedTeam": "{0} has shared a folder with the team {2}: <b>{1}</b>",
"oo_refresh": "Refresh",
"oo_refreshText": "This document has been updated",
"support_category": "Choose a category"
}

@ -140,7 +140,6 @@
"or": "eller",
"tags_title": "Tags (kun for ditt bruk)",
"tags_add": "Oppdater tags for denne sida",
"tags_searchHint": "Søk med # i CryptDriven din for å finne pads med slike tags.",
"tags_notShared": "Dine tags deles ikke med andre brukerer",
"tags_duplicate": "Dupliser tag:{0}",
"tags_noentry": "Du kan ikke tagge en sletta pad!",

@ -74,7 +74,6 @@
"tags_noentry": "U kunt een verwijderde werkomgeving niet markeren!",
"tags_duplicate": "Gedupliceerde markering: {0}",
"tags_notShared": "Uw markeringen worden niet gedeeld met andere gebruikers",
"tags_searchHint": "Begin een zoekopdracht met # in uw CryptDrive om gemarkeerde werkomgevingen te vinden.",
"tags_add": "Werk de markeringen van deze pagina bij",
"tags_title": "Markeringen (alleen voor u)",
"or": "of",

@ -392,7 +392,6 @@
"or": "",
"tags_title": "",
"tags_add": "",
"tags_searchHint": "",
"tags_notShared": "",
"tags_duplicate": "",
"tags_noentry": "",

@ -382,7 +382,6 @@
"or": "sau",
"tags_title": "Etichete (doar pentru tine)",
"tags_add": "Updatează etichetele acestei pagini",
"tags_searchHint": "Începe o căutare cu # în CryptDrive-ul tău pentru a găsi pad-uri etichetate",
"tags_notShared": "Etichetele tale nu sunt împărțite cu alți utilizatori",
"tags_duplicate": "Duplică eticheta: {0}",
"tags_noentry": "Nu poți eticheta un pad șters",

@ -140,7 +140,6 @@
"or": "или",
"tags_title": "Теги (только для вас)",
"tags_add": "Обновить теги страницы",
"tags_searchHint": "Начните поиск в вашем CryptDrive при помощи # чтобы найти пэды с тегами.",
"tags_notShared": "Ваши теги не разделяются с другими пользователями",
"button_newsheet": "Новый Лист",
"newButtonTitle": "создать новую запись",

@ -38,7 +38,6 @@
"tags_noentry": "Du kan inte tagga ett raderat dokument!",
"tags_duplicate": "Duplicera tagg: {0}",
"tags_notShared": "Dina taggar är inte delade med andra användare",
"tags_searchHint": "Påbörja en sökning med # i din CryptDrive för att hitta dina taggade dokument.",
"tags_add": "Uppdatera taggar för denna sida",
"tags_title": "Taggar (endast för dig)",
"or": "eller",

@ -1,7 +1,6 @@
@import (reference) '../../customize/src/less2/include/framework.less';
@import (reference) '../../customize/src/less2/include/messenger.less';
@import (reference) '../../customize/src/less2/include/avatar.less';
@import (reference) '../../customize/src/less2/include/buttons.less';
// body
&.cp-app-contacts {
@ -19,9 +18,6 @@
display: flex; // We need this to remove a 3px border at the bottom of the toolbar
}
.cp-app-contacts-friends {
.buttons_main();
}
.cp-contacts-muted-table {
.avatar_main(50px);
.cp-contacts-muted-user {
@ -33,8 +29,6 @@
}
}
.messenger_main();
}

@ -3,7 +3,6 @@
@import (reference) "../../customize/src/less2/include/tools.less";
@import (reference) "../../customize/src/less2/include/markdown.less";
@import (reference) "../../customize/src/less2/include/avatar.less";
@import (reference) "../../customize/src/less2/include/buttons.less";
// body
&.cp-app-kanban {
@ -142,6 +141,8 @@
.kanban-edit-item {
padding: 5px;
border: 0;
background: transparent;
align-self: flex-start;
}
@ -310,7 +311,6 @@
position: relative;
min-height: 50px;
.cp-kanban-filterTags {
.buttons_main();
display: inline-flex;
align-items: center;
flex: 1;
@ -346,7 +346,7 @@
margin-left: 10px;
display: flex;
flex-wrap: wrap;
&:not(:empty) {
&:not(.cp-empty) {
margin-top: -5px;
}
em {

@ -503,7 +503,7 @@ define([
$container.find('.kanban-item').each(function (i, el) {
var itemId = $(el).attr('data-eid');
$('<button>', {
'class': 'kanban-edit-item btn btn-default fa fa-pencil',
'class': 'kanban-edit-item fa fa-pencil',
'alt': Messages.kanban_editCard,
}).click(function (e) {
getItemEditModal(framework, kanban, itemId);
@ -513,7 +513,7 @@ define([
$container.find('.kanban-board').each(function (i, el) {
var itemId = $(el).attr('data-id');
$('<button>', {
'class': 'kanban-edit-item btn btn-default fa fa-pencil',
'class': 'kanban-edit-item fa fa-pencil',
'alt': Messages.kanban_editBoard,
}).click(function (e) {
getBoardEditModal(framework, kanban, itemId);
@ -913,7 +913,9 @@ define([
var redrawList = function (allTags) {
if (!Array.isArray(allTags)) { return; }
$list.empty();
$list.removeClass('cp-empty');
if (!allTags.length) {
$list.addClass('cp-empty');
$list.append(h('em', Messages.kanban_noTags));
return;
}

@ -1,6 +1,5 @@
@import (reference) "../../customize/src/less2/include/framework.less";
@import (reference) "../../customize/src/less2/include/comments.less";
@import (reference) "../../customize/src/less2/include/buttons.less";
body.cp-app-pad {
.framework_main(
@ -71,7 +70,6 @@ body.cp-app-pad {
order: 1;
}
div.cp-comment-bubble {
.buttons_main();
position: relative;
order: 2;
button {

@ -115,8 +115,11 @@ define([
}, {
ok: Messages.register_writtenPassword,
cancel: Messages.register_cancel,
cancelClass: 'safe',
okClass: 'danger',
/* If we're certain that we aren't using these "*Class" APIs
anywhere else then we can deprecate them and make this a
custom modal in common-interface (or here). */
cancelClass: 'btn.btn-safe',
okClass: 'btn.btn-danger',
reverseOrder: true,
done: function ($dialog) {
$dialog.find('> div').addClass('half');

@ -8,6 +8,7 @@
@import (reference) '../../customize/src/less2/include/password-input.less';
@import (reference) '../../customize/src/less2/include/modals-ui-elements.less';
@import (reference) '../../customize/src/less2/include/usergrid.less';
@import (reference) '../../customize/src/less2/include/forms.less';
&.cp-app-secureiframe {
.modals-ui-elements_main();
@ -19,6 +20,7 @@
.password_main();
.modal_main();
.usergrid_main();
.forms_main();
#cp-filepicker-dialog {
display: none;

@ -494,8 +494,8 @@ define([
}, {
ok: Messages.register_writtenPassword,
cancel: Messages.register_cancel,
cancelClass: 'safe',
okClass: 'danger',
cancelClass: 'btn.btn-safe',
okClass: 'btn.btn-danger',
reverseOrder: true,
done: function($dialog) {
$dialog.find('> div').addClass('half');

@ -18,6 +18,15 @@
display: flex;
flex-flow: column;
.cp-support-form-attachments {
.fa {
cursor: pointer;
}
&> span {
padding: 10px;
}
}
.cp-support-language-list {
.cp-support-language {
margin-left: 5px;

@ -165,8 +165,6 @@ define([
var form = APP.support.makeForm();
$div.find('button').before(form);
var id = Util.uid();
$div.find('button').click(function () {
@ -182,6 +180,7 @@ define([
$('.cp-sidebarlayout-category[data-category="tickets"]').click();
}
});
$div.find('button').before(form);
return $div;
};

@ -6,8 +6,9 @@ define([
'/common/common-hash.js',
'/common/common-util.js',
'/common/clipboard.js',
'/common/common-ui-elements.js',
'/customize/messages.js',
], function ($, ApiConfig, h, UI, Hash, Util, Clipboard, Messages) {
], function ($, ApiConfig, h, UI, Hash, Util, Clipboard, UIElements, Messages) {
var send = function (ctx, id, type, data, dest) {
var common = ctx.common;
@ -61,8 +62,23 @@ define([
};
var sendForm = function (ctx, id, form, dest) {
var $title = $(form).find('.cp-support-form-title');
var $content = $(form).find('.cp-support-form-msg');
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();
/*
// || ($form.closest('.cp-support-list-ticket').data('cat') || "").trim();
// Messages.support_formCategoryError = "Error: category is empty"; // TODO ensure this is translated before use
if (!category) {
console.log($cat);
return void UI.alert(Messages.support_formCategoryError);
}
*/
var title = $title.val().trim();
if (!title) {
@ -72,18 +88,55 @@ define([
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;
};
var makeForm = function (cb, title) {
var makeCategoryDropdown = function (ctx, container, onChange, all) {
var categories = ['account', 'data', 'bug', 'other'];
if (all) { categories.push('all'); }
categories = categories.map(function (key) {
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;
};
var makeForm = function (ctx, cb, title) {
var button;
if (typeof(cb) === "function") {
@ -93,8 +146,22 @@ define([
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': ''));
makeCategoryDropdown(ctx, catContainer, function (key) {
$(category).val(key);
});
var attachments, addAttachment;
var content = [
h('hr'),
category,
catContainer,
h('br'),
h('input.cp-support-form-title' + (title ? '.cp-hidden' : ''), {
placeholder: Messages.support_formTitle,
type: 'text',
@ -104,11 +171,52 @@ define([
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 () {
@ -125,6 +233,7 @@ define([
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);
@ -137,6 +246,7 @@ define([
var url;
if (ctx.isAdmin) {
ticketCategory = Messages['support_cat_'+(content.category || 'all')] + ' - ';
url = h('button.btn.btn-primary.fa.fa-clipboard');
$(url).click(function () {
var link = privateData.origin + privateData.pathname + '#' + 'support-' + content.id;
@ -146,9 +256,10 @@ define([
}
var $ticket = $(h('div.cp-support-list-ticket', {
'data-cat': content.category,
'data-id': content.id
}, [
h('h2', [ticketTitle, url]),
h('h2', [ticketCategory, ticketTitle, url]),
actions
]));
@ -179,7 +290,7 @@ define([
$(answer).click(function () {
$ticket.find('.cp-support-form-container').remove();
$(actions).hide();
var form = makeForm(function () {
var form = makeForm(ctx, function () {
var sent = sendForm(ctx, content.id, form, content.sender);
if (sent) {
$(actions).show();
@ -215,6 +326,22 @@ define([
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 adminClass = (fromAdmin? '.cp-support-fromadmin': '');
var premiumClass = (fromPremium && !fromAdmin? '.cp-support-frompremium': '');
var name = Util.fixHTML(content.sender.name) || Messages.anonymous;
@ -226,6 +353,7 @@ define([
h('span.cp-support-message-time', content.time ? new Date(content.time).toLocaleString() : '')
]),
h('pre.cp-support-message-content', content.message),
h('div.cp-support-attachments', attachments),
isAdmin ? userData : undefined,
]);
};
@ -257,10 +385,25 @@ define([
adminKeys: Array.isArray(ApiConfig.adminKeys)? ApiConfig.adminKeys.slice(): [],
};
var fmConfig = {
body: $('body'),
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 = makeForm;
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);
};

@ -45,7 +45,7 @@
.cp-app-contacts-input {
textarea {
border: 0px;
color: white;
color: @cryptpad_text_col;
}
}
}

@ -422,6 +422,10 @@ define([
]));
common.displayAvatar($(avatar), team.metadata.avatar, team.metadata.name);
$(btn).click(function () {
if (team.error) {
UI.warn(Messages.error); // XXX better error message - roster bug, can't load the team for now
return;
}
openTeam(common, id, team);
});
});

@ -1,6 +1,5 @@
@import (reference) '../../customize/src/less2/include/tools.less';
@import (reference) "../../customize/src/less2/include/framework.less";
@import (reference) "../../customize/src/less2/include/buttons.less";
&.cp-app-whiteboard {
@ -74,8 +73,6 @@
padding: 10px;
.buttons_main();
& > * + * {
margin: 0;
margin-left: 1em;

Loading…
Cancel
Save