Merge branch 'master' into fix-#254

This commit is contained in:
Marcel
2018-08-28 17:11:20 +02:00
committed by GitHub
118 changed files with 7083 additions and 4984 deletions

View File

@@ -1,16 +1,14 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) "../../customize/src/less2/include/framework.less";
@import (reference) "../../customize/src/less2/include/browser.less";
@import (reference) "../../customize/src/less2/include/markdown.less";
@import (reference) "../../customize/src/less2/include/framework.less";
.framework_main(
@bg-color: @colortheme_code-bg,
@warn-color: @colortheme_code-warn,
@color: @colortheme_code-color
);
// body
&.cp-app-code {
.framework_main(
@bg-color: @colortheme_code-bg,
@warn-color: @colortheme_code-warn,
@color: @colortheme_code-color
);
display: flex;
flex-flow: column;
max-height: 100%;

View File

@@ -37,6 +37,8 @@ define([
'cm/addon/fold/comment-fold',
'cm/addon/display/placeholder',
'less!/code/app-code.less'
], function (
$,
DiffMd,

View File

@@ -4,9 +4,11 @@ const define = (x:any, y:any) => {};
const require = define;
*/
define([
'/api/config'
], function (Config) { /*::});module.exports = (function() {
'/api/config',
'/bower_components/nthen/index.js'
], function (Config, nThen) { /*::});module.exports = (function() {
const Config = (undefined:any);
const nThen = (undefined:any);
*/
var module = { exports: {} };
@@ -100,6 +102,10 @@ define([
require(['/bower_components/less/dist/less.min.js'], function (Less) {
if (lessEngine) { return void cb(lessEngine); }
lessEngine = Less;
Less.functions.functionRegistry.add('LessLoader_currentFile', function () {
return new Less.tree.UnicodeDescriptor('"' +
fixURL(this.currentFileInfo.filename) + '"');
});
var doXHR = lessEngine.FileManager.prototype.doXHR;
lessEngine.FileManager.prototype.doXHR = function (url, type, callback, errback) {
url = fixURL(url);
@@ -133,24 +139,38 @@ define([
});
};
module.exports.load = function (url /*:string*/, cb /*:()=>void*/) {
var btime = +new Date();
var loadSubmodulesAndInject = function (css, url, cb, stack) {
inject(css, url);
nThen(function (w) {
css.replace(/\-\-LessLoader_require\:\s*"([^"]*)"\s*;/g, function (all, u) {
u = u.replace(/\?.*$/, '');
module.exports.load(u, w(), stack);
return '';
});
}).nThen(function () { cb(); });
};
module.exports.load = function (url /*:string*/, cb /*:()=>void*/, stack /*:?Array<string>*/) {
var btime = stack ? null : +new Date();
stack = stack || [];
if (stack.indexOf(url) > -1) { return void cb(); }
var timeout = setTimeout(function () { console.log('failed', url); }, 10000);
var done = function () {
console.log("Compiling [" + url + "] took " + (+new Date() - btime) + "ms");
clearTimeout(timeout);
if (btime) {
console.log("Compiling [" + url + "] took " + (+new Date() - btime) + "ms");
}
cb();
};
stack.push(url);
cacheGet(url, function (css) {
if (css) {
inject(css, url);
return void done();
}
if (css) { return void loadSubmodulesAndInject(css, url, done, stack); }
console.log('CACHE MISS ' + url);
((/\.less([\?\#].*)?$/.test(url)) ? loadLess : loadCSS)(url, function (err, css) {
if (!css) { return void console.error(err); }
var output = fixAllURLs(css, url);
cachePut(url, output);
inject(output, url);
done();
loadSubmodulesAndInject(output, url, done, stack);
});
});
};

View File

@@ -126,5 +126,11 @@ define(function() {
// Warning: This is an experimental feature. It will be enabled by default once we're sure it's stable.
config.disableWorkers = true;
// Shared folder are in a beta-test state. They are likely to disappear from a user's drive
// spontaneously, resulting in the deletion of the entire folder's content.
// We highly recommend to keep them disabled until they are stable enough to be enabled
// by default by the CryptPad developers.
config.disableSharedFolders = true;
return config;
});

View File

@@ -398,10 +398,16 @@ Version 1
Hash.findWeaker = function (href, channel, recents) {
var parsed = parsePadUrl(href);
if (!parsed.hash) { return false; }
// We can't have a weaker hash if we're already in view mode
if (parsed.hashData && parsed.hashData.mode === 'view') { return; }
var weaker;
Object.keys(recents).some(function (id) {
var pad = recents[id];
var p = parsePadUrl(pad.href);
if (pad.href || !pad.roHref) {
// This pad has an edit link, so it can't be weaker
return;
}
var p = parsePadUrl(pad.roHref);
if (p.type !== parsed.type) { return; } // Not the same type
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
if (channel !== pad.channel) { return; } // Not the same channel
@@ -430,6 +436,10 @@ Version 1
var stronger;
Object.keys(recents).some(function (id) {
var pad = recents[id];
if (!pad.href) {
// This pad doesn't have an edit link, so it can't be stronger
return;
}
var p = parsePadUrl(pad.href);
if (p.type !== parsed.type) { return; } // Not the same type
if (p.hash === parsed.hash) { return; } // Same hash, not stronger

View File

@@ -1,3 +1,12 @@
if (!document.querySelector("#alertifyCSS")) {
// Prevent alertify from injecting CSS, we create our own in alertify.less.
// see: https://github.com/alertifyjs/alertify.js/blob/v1.0.11/src/js/alertify.js#L414
var head = document.getElementsByTagName("head")[0];
var css = document.createElement("span");
css.id = "alertifyCSS";
css.setAttribute('data-but-why', 'see: common-interface.js');
head.insertBefore(css, head.firstChild);
}
define([
'jquery',
'/customize/messages.js',
@@ -665,7 +674,7 @@ define([
// Update the current state
loading.driveState = data.state;
data.progress = data.progress || 100;
data.msg = Messages['loading_drive_'+data.state] || '';
data.msg = Messages['loading_drive_'+ Math.floor(data.state)] || '';
$progress.html(data.msg);
if (data.progress) {
$progress.append(h('div.cp-loading-progress-bar', [
@@ -761,7 +770,7 @@ define([
UI.getFileIcon = function (data) {
var $icon = UI.getIcon();
if (!data) { return $icon; }
var href = data.href;
var href = data.href || data.roHref;
var type = data.type;
if (!href && !type) { return $icon; }
@@ -826,13 +835,13 @@ define([
var out = false;
var xId = $(x).attr('aria-describedby');
if (xId) {
if (xId.indexOf('tippy-tooltip-') === 0) {
if (xId.indexOf('tippy-') === 0) {
return true;
}
}
$(x).find('[aria-describedby]').each(function (i, el) {
var id = el.getAttribute('aria-describedby');
if (id.indexOf('tippy-tooltip-') !== 0) { return; }
if (id.indexOf('tippy-') !== 0) { return; }
out = true;
});
return out;

View File

@@ -13,6 +13,7 @@ define([
'/customize/messages.js',
'/customize/application_config.js',
'/bower_components/nthen/index.js',
'css!/customize/fonts/cptools/style.css'
], function ($, Config, Util, Hash, Language, UI, Constants, Feedback, h, MediaTag, Clipboard,
Messages, AppConfig, NThen) {
var UIElements = {};
@@ -73,26 +74,14 @@ define([
data.password = val;
}));
}).nThen(function (waitFor) {
var base = common.getMetadataMgr().getPrivateData().origin;
common.getPadAttribute('href', waitFor(function (err, val) {
var base = common.getMetadataMgr().getPrivateData().origin;
var parsed = Hash.parsePadUrl(val);
if (parsed.hashData.mode === "view") {
data.roHref = base + val;
return;
}
// We're not in a read-only pad
if (!val) { return; }
data.href = base + val;
// Get Read-only href
if (parsed.hashData.type !== "pad") { return; }
var i = data.href.indexOf('#') + 1;
var hBase = data.href.slice(0, i);
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
if (!hrefsecret.keys) { return; }
var viewHash = Hash.getViewHashFromKeys(hrefsecret);
data.roHref = hBase + viewHash;
}));
common.getPadAttribute('roHref', waitFor(function (err, val) {
if (!val) { return; }
data.roHref = base + val;
}));
common.getPadAttribute('channel', waitFor(function (err, val) {
data.channel = val;
@@ -137,82 +126,86 @@ define([
id: 'cp-app-prop-owners',
}));
var expire = Messages.creation_expireFalse;
if (data.expire && typeof (data.expire) === "number") {
expire = new Date(data.expire).toLocaleString();
}
$('<label>', {'for': 'cp-app-prop-expire'}).text(Messages.creation_expiration)
.appendTo($d);
$d.append(UI.dialog.selectable(expire, {
id: 'cp-app-prop-expire',
}));
var hasPassword = data.password;
if (hasPassword) {
$('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
.appendTo($d);
var password = UI.passwordInput({
id: 'cp-app-prop-password',
readonly: 'readonly'
});
var $pwInput = $(password).find('.cp-password-input');
$pwInput.val(data.password).click(function () {
$pwInput[0].select();
});
$d.append(password);
}
var parsed = Hash.parsePadUrl(data.href);
if (owned && parsed.hashData.type === 'pad') {
var sframeChan = common.getSframeChannel();
var changePwTitle = Messages.properties_changePassword;
var changePwConfirm = Messages.properties_confirmChange;
if (!hasPassword) {
changePwTitle = Messages.properties_addPassword;
changePwConfirm = Messages.properties_confirmNew;
if (!data.noExpiration) {
var expire = Messages.creation_expireFalse;
if (data.expire && typeof (data.expire) === "number") {
expire = new Date(data.expire).toLocaleString();
}
$('<label>', {'for': 'cp-app-prop-change-password'})
.text(changePwTitle).appendTo($d);
var newPassword = UI.passwordInput({
id: 'cp-app-prop-change-password',
style: 'flex: 1;'
});
var passwordOk = h('button', Messages.properties_changePasswordButton);
var changePass = h('span.cp-password-container', [
newPassword,
passwordOk
]);
$(passwordOk).click(function () {
var newPass = $(newPassword).find('input').val();
if (data.password === newPass ||
(!data.password && !newPass)) {
return void UI.alert(Messages.properties_passwordSame);
$('<label>', {'for': 'cp-app-prop-expire'}).text(Messages.creation_expiration)
.appendTo($d);
$d.append(UI.dialog.selectable(expire, {
id: 'cp-app-prop-expire',
}));
}
if (!data.noPassword) {
var hasPassword = data.password;
if (hasPassword) {
$('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
.appendTo($d);
var password = UI.passwordInput({
id: 'cp-app-prop-password',
readonly: 'readonly'
});
var $pwInput = $(password).find('.cp-password-input');
$pwInput.val(data.password).click(function () {
$pwInput[0].select();
});
$d.append(password);
}
var parsed = Hash.parsePadUrl(data.href || data.roHref);
if (owned && parsed.hashData.type === 'pad') {
var sframeChan = common.getSframeChannel();
var changePwTitle = Messages.properties_changePassword;
var changePwConfirm = Messages.properties_confirmChange;
if (!hasPassword) {
changePwTitle = Messages.properties_addPassword;
changePwConfirm = Messages.properties_confirmNew;
}
UI.confirm(changePwConfirm, function (yes) {
if (!yes) { return; }
sframeChan.query("Q_PAD_PASSWORD_CHANGE", {
href: data.href,
password: newPass
}, function (err, data) {
if (err || data.error) {
return void UI.alert(Messages.properties_passwordError);
}
UI.findOKButton().click();
// If we didn't have a password, we have to add the /p/
// If we had a password and we changed it to a new one, we just have to reload
// If we had a password and we removed it, we have to remove the /p/
if (data.warning) {
return void UI.alert(Messages.properties_passwordWarning, function () {
common.gotoURL(hasPassword && newPass ? undefined : data.href);
}, {force: true});
}
return void UI.alert(Messages.properties_passwordSuccess, function () {
common.gotoURL(hasPassword && newPass ? undefined : data.href);
}, {force: true});
$('<label>', {'for': 'cp-app-prop-change-password'})
.text(changePwTitle).appendTo($d);
var newPassword = UI.passwordInput({
id: 'cp-app-prop-change-password',
style: 'flex: 1;'
});
var passwordOk = h('button', Messages.properties_changePasswordButton);
var changePass = h('span.cp-password-container', [
newPassword,
passwordOk
]);
$(passwordOk).click(function () {
var newPass = $(newPassword).find('input').val();
if (data.password === newPass ||
(!data.password && !newPass)) {
return void UI.alert(Messages.properties_passwordSame);
}
UI.confirm(changePwConfirm, function (yes) {
if (!yes) { return; }
sframeChan.query("Q_PAD_PASSWORD_CHANGE", {
href: data.href || data.roHref,
password: newPass
}, function (err, data) {
if (err || data.error) {
return void UI.alert(Messages.properties_passwordError);
}
UI.findOKButton().click();
// If we didn't have a password, we have to add the /p/
// If we had a password and we changed it to a new one, we just have to reload
// If we had a password and we removed it, we have to remove the /p/
if (data.warning) {
return void UI.alert(Messages.properties_passwordWarning, function () {
common.gotoURL(hasPassword && newPass ? undefined : (data.href || data.roHref));
}, {force: true});
}
return void UI.alert(Messages.properties_passwordSuccess, function () {
common.gotoURL(hasPassword && newPass ? undefined : (data.href || data.roHref));
}, {force: true});
});
});
});
});
$d.append(changePass);
$d.append(changePass);
}
}
cb(void 0, $d);
@@ -242,17 +235,21 @@ define([
}));
}
$('<label>', {'for': 'cp-app-prop-ctime'}).text(Messages.fm_creation)
.appendTo($d);
$d.append(UI.dialog.selectable(new Date(data.ctime).toLocaleString(), {
id: 'cp-app-prop-ctime',
}));
if (data.ctime) {
$('<label>', {'for': 'cp-app-prop-ctime'}).text(Messages.fm_creation)
.appendTo($d);
$d.append(UI.dialog.selectable(new Date(data.ctime).toLocaleString(), {
id: 'cp-app-prop-ctime',
}));
}
$('<label>', {'for': 'cp-app-prop-atime'}).text(Messages.fm_lastAccess)
.appendTo($d);
$d.append(UI.dialog.selectable(new Date(data.atime).toLocaleString(), {
id: 'cp-app-prop-atime',
}));
if (data.atime) {
$('<label>', {'for': 'cp-app-prop-atime'}).text(Messages.fm_lastAccess)
.appendTo($d);
$d.append(UI.dialog.selectable(new Date(data.atime).toLocaleString(), {
id: 'cp-app-prop-atime',
}));
}
if (common.isLoggedIn() && AppConfig.enablePinning) {
// check the size of this file...
@@ -463,6 +460,7 @@ define([
var pathname = config.pathname;
var hashes = config.hashes;
var common = config.common;
var fileData = config.fileData;
if (!hashes.fileHash) { throw new Error("You must provide a file hash"); }
var url = origin + pathname + '#' + hashes.fileHash;
@@ -498,7 +496,7 @@ define([
UI.dialog.selectable(common.getMediatagScript()),
h('p', Messages.fileEmbedTag),
h('br'),
UI.dialog.selectable(common.getMediatagFromHref(url)),
UI.dialog.selectable(common.getMediatagFromHref(fileData)),
]);
var embedButtons = [{
name: Messages.cancel,
@@ -508,7 +506,7 @@ define([
className: 'primary',
name: Messages.share_mediatagCopy,
onClick: function () {
var v = common.getMediatagFromHref(url);
var v = common.getMediatagFromHref(fileData);
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess); }
},
@@ -533,6 +531,35 @@ define([
}
return tabs;
};
UIElements.createSFShareModal = function (config) {
var origin = config.origin;
var pathname = config.pathname;
var hashes = config.hashes;
if (!hashes.editHash) { throw new Error("You must provide a valid hash"); }
var url = origin + pathname + '#' + hashes.editHash;
// Share link tab
var link = h('div.cp-share-modal', [
h('label', Messages.sharedFolders_share),
h('br'),
UI.dialog.selectable(url, { id: 'cp-share-link-preview', tabindex: 1 })
]);
var linkButtons = [{
name: Messages.cancel,
onClick: function () {},
keys: [27]
}, {
className: 'primary',
name: Messages.share_linkCopy,
onClick: function () {
var success = Clipboard.copy(url);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: [13]
}];
return UI.dialog.customModal(link, {buttons: linkButtons});
};
UIElements.createButton = function (common, type, rightside, data, callback) {
var AppConfig = common.getAppConfig();
@@ -695,17 +722,27 @@ define([
button
.click(common.prepareFeedback(type))
.click(function() {
var msg = common.isLoggedIn() ? Messages.forgetPrompt : Messages.fm_removePermanentlyDialog;
UI.confirm(msg, function (yes) {
if (!yes) { return; }
sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) {
if (err) { return void callback(err); }
var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted;
var msg = common.fixLinks($('<div>').html(cMsg));
UI.alert(msg);
callback();
sframeChan.query('Q_IS_ONLY_IN_SHARED_FOLDER', null, function (err, res) {
if (err || res.error) { return void console.log(err || res.error); }
var msg = Messages.forgetPrompt;
if (res) {
UI.alert(Messages.sharedFolders_forget);
return;
} else if (!common.isLoggedIn()) {
msg = Messages.fm_removePermanentlyDialog;
}
UI.confirm(msg, function (yes) {
if (!yes) { return; }
sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) {
if (err) { return void callback(err); }
var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted;
var msg = common.fixLinks($('<div>').html(cMsg));
UI.alert(msg);
callback();
return;
});
});
});
});
break;
@@ -2010,12 +2047,14 @@ define([
allData.unshift({
name: Messages.creation_newTemplate,
id: -1,
icon: h('span.fa.fa-bookmark')
//icon: h('span.fa.fa-bookmark')
icon: h('span.cptools.cptools-new-template')
});
allData.unshift({
name: Messages.creation_noTemplate,
id: 0,
icon: h('span.fa.fa-file')
//icon: h('span.fa.fa-file')
icon: UI.getFileIcon({type: type})
});
var redraw = function (index) {
if (index < 0) { i = 0; }
@@ -2031,7 +2070,9 @@ define([
}).appendTo($container);
$span.data('id', obj.id);
if (idx === selected) { $span.addClass('cp-creation-template-selected'); }
$span.append(obj.icon || UI.getFileIcon({type: type}));
if (!obj.thumbnail) {
$span.append(obj.icon || h('span.cptools.cptools-template'));
}
$('<span>', {'class': 'cp-creation-template-element-name'}).text(name)
.appendTo($span);
$span.click(function () {

View File

@@ -84,6 +84,11 @@ define([
cb(obj);
});
};
common.getSharedFolder = function (id, cb) {
postMessage("GET_SHARED_FOLDER", id, function (obj) {
cb(obj);
});
};
// Settings and ready
common.mergeAnonDrive = function (cb) {
var data = {
@@ -99,6 +104,27 @@ define([
common.userObjectCommand = function (data, cb) {
postMessage("DRIVE_USEROBJECT", data, cb);
};
common.restoreDrive = function (data, cb) {
postMessage("SET", {
key:['drive'],
value: data
}, function (obj) {
cb(obj);
}, {
timeout: 5 * 60 * 1000
});
};
common.addSharedFolder = function (secret, cb) {
postMessage("ADD_SHARED_FOLDER", {
path: ['root'],
folderData: {
href: '/drive/#' + Hash.getEditHashFromKeys(secret),
roHref: '/drive/#' + Hash.getViewHashFromKeys(secret),
channel: secret.channel,
ctime: +new Date()
}
}, cb);
};
common.drive = {};
common.drive.onLog = Util.mkEvent();
common.drive.onChange = Util.mkEvent();
@@ -294,6 +320,13 @@ define([
});
};
common.isOnlyInSharedFolder = function (data, cb) {
postMessage("IS_ONLY_IN_SHARED_FOLDER", data, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(null, obj);
});
};
common.setDisplayName = function (value, cb) {
postMessage("SET_DISPLAY_NAME", value, cb);
};
@@ -506,7 +539,8 @@ define([
if (common.initialPath) {
if (!data.path) {
data.path = common.initialPath;
data.path = Array.isArray(common.initialPath) ? common.initialPath
: decodeURIComponent(common.initialPath).split(',');
delete common.initialPath;
}
}
@@ -620,7 +654,7 @@ define([
if (!parsed.hash) { return void cb({ error: 'EINVAL_HREF' }); }
var warning = false;
var newHash;
var newHash, newRoHref;
var oldChannel;
var newSecret;
@@ -693,6 +727,11 @@ define([
common.setPadAttribute('channel', newSecret.channel, waitFor(function (err) {
if (err) { warning = true; }
}), href);
var viewHash = Hash.getViewHashFromKeys(newSecret);
newRoHref = '/' + parsed.type + '/#' + viewHash;
common.setPadAttribute('roHref', newRoHref, waitFor(function (err) {
if (err) { warning = true; }
}), href);
if (parsed.hashData.password && newPassword) { return; } // same hash
common.setPadAttribute('href', newHref, waitFor(function (err) {
@@ -702,7 +741,8 @@ define([
cb({
warning: warning,
hash: newHash,
href: newHref
href: newHref,
roHref: newRoHref
});
});
};
@@ -1290,12 +1330,12 @@ define([
});
});
postMessage = function (cmd, data, cb) {
postMessage = function (cmd, data, cb, opts) {
cb = cb || function () {};
chan.query(cmd, data, function (err, data) {
if (err) { return void cb ({error: err}); }
cb(data);
});
}, opts);
};
console.log('Posting CONNECT');

View File

@@ -1,8 +1,9 @@
@import (once) '../customize/src/less2/include/colortheme-all.less';
@import '../customize/src/less2/include/modal.less';
@import (reference) '../customize/src/less2/include/colortheme-all.less';
@import (reference) '../customize/src/less2/include/modal.less';
.fileDialog_main () {
#fileDialog {
.modal_main();
display: none;
.cp-modal {
.fileContainer {

View File

@@ -6,84 +6,6 @@ define([
], function (Crypt, FO, Hash, Realtime) {
var exp = {};
var getType = function (el) {
if (el === null) { return "null"; }
return Array.isArray(el) ? "array" : typeof(el);
};
var findAvailableKey = function (obj, key) {
if (typeof (obj[key]) === "undefined") { return key; }
var i = 1;
var nkey = key;
while (typeof (obj[nkey]) !== "undefined") {
nkey = key + '_' + i;
i++;
}
return nkey;
};
var createFromPath = function (proxy, oldFo, path, id) {
var root = proxy.drive;
if (!oldFo.isFile(id)) { return; }
var error = function (msg) {
console.error(msg || "Unable to find that path", path);
};
if (oldFo.isInTrashRoot(path)) {
id = oldFo.find(path.slice(0,3));
path.pop();
}
var next, nextRoot;
path.forEach(function (p, i) {
if (!root) { return; }
if (typeof(p) === "string") {
if (getType(root) !== "object") { root = undefined; error(); return; }
if (i === path.length - 1) {
root[Hash.createChannelId()] = id;
return;
}
next = getType(path[i+1]);
nextRoot = getType(root[p]);
if (nextRoot !== "undefined") {
if (next === "string" && nextRoot === "object" || next === "number" && nextRoot === "array") {
root = root[p];
return;
}
p = findAvailableKey(root, p);
}
if (next === "number") {
root[p] = [];
root = root[p];
return;
}
root[p] = {};
root = root[p];
return;
}
// Path contains a non-string element: it's an array index
if (typeof(p) !== "number") { root = undefined; error(); return; }
if (getType(root) !== "array") { root = undefined; error(); return; }
if (i === path.length - 1) {
if (root.indexOf(id) === -1) { root.push(id); }
return;
}
next = getType(path[i+1]);
if (next === "number") {
error('2 consecutives arrays in the user object');
root = undefined;
//root.push([]);
//root = root[root.length - 1];
return;
}
root.push({});
root = root[root.length - 1];
return;
});
};
exp.anonDriveIntoUser = function (proxyData, fsHash, cb) {
// Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb
if (!fsHash || !proxyData.loggedIn) {
@@ -105,43 +27,38 @@ define([
if (parsed) {
var proxy = proxyData.proxy;
var oldFo = FO.init(parsed.drive, {
loggedIn: proxyData.loggedIn,
pinPads: function () {} // without pinPads /outer/userObject.js won't be loaded
loggedIn: true,
outer: true
});
var onMigrated = function () {
oldFo.fixFiles(true);
var newFo = proxyData.userObject;
var oldRecentPads = parsed.drive[newFo.FILES_DATA];
var newRecentPads = proxy.drive[newFo.FILES_DATA];
var oldFiles = oldFo.getFiles([newFo.FILES_DATA]);
var newHrefs = Object.keys(newRecentPads).map(function (id) {
return newRecentPads[id].href;
});
var manager = proxyData.manager;
var oldFiles = oldFo.getFiles([oldFo.FILES_DATA]);
oldFiles.forEach(function (id) {
var href = oldRecentPads[id].href;
// Do not migrate a pad if we already have it, it would create a duplicate in the drive
if (newHrefs.indexOf(href) !== -1) { return; }
// If we have a stronger version, do not add the current href
if (Hash.findStronger(href, oldRecentPads[id].channel, newRecentPads)) { return; }
// If we have a weaker version, replace the href by the new one
// NOTE: if that weaker version is in the trash, the strong one will be put in unsorted
var weaker = Hash.findWeaker(href, oldRecentPads[id].channel, newRecentPads);
if (weaker) {
// Update RECENTPADS
weaker.href = href;
// Update the file in the drive
newFo.replace(weaker.href, href);
var data = oldFo.getFileData(id);
var channel = data.channel;
var datas = manager.findChannel(channel, true);
// Do not migrate a pad if we already have it, it would create a duplicate
// in the drive
if (datas.length !== 0) {
// We want to merge a read-only pad: it can't be stronger than what
// we already have so abort
if (!data.href) { return; }
// We want to merge an edit pad: check if we have the same channel
// but read-only and upgrade it in that case
datas.forEach(function (pad) {
if (!pad.href) { data.href = pad.href; }
});
return;
}
// Here it means we have a new href, so we should add it to the drive at its old location
var paths = oldFo.findFile(id);
if (paths.length === 0) { return; }
// Add the file data in our array and use the id to add the file
var data = oldFo.getFileData(id);
// Here it means we have a new href, so we should add it to the drive
if (data) {
newFo.pushData(data, function (err, id) {
if (err) { return void console.error("Cannot import file:", data, err); }
createFromPath(proxy, oldFo, paths[0], id);
manager.addPad(null, data, function (err) {
if (err) {
return void console.error("Cannot import file:", data, err);
}
});
}
});
@@ -153,7 +70,12 @@ define([
Realtime.whenRealtimeSyncs(proxyData.realtime, cb);
}
};
oldFo.migrate(onMigrated);
if (oldFo && typeof(oldFo.migrate) === 'function') {
oldFo.migrate(onMigrated);
} else {
console.log('oldFo.migrate is not a function');
onMigrated();
}
return;
}
if (typeof(cb) === "function") { cb(); }

View File

@@ -123,12 +123,58 @@ define([
}));
});
});
n.nThen(waitFor());
n.nThen(waitFor(function () {
Feedback.send('Migrate-6', true);
userObject.version = version = 6;
}));
};
if (version < 6) {
addChannelId();
Feedback.send('Migrate-6', true);
userObject.version = version = 6;
}
}).nThen(function (waitFor) {
var addRoHref = function () {
var data = userObject.drive.filesData;
var el, parsed;
var n = nThen(function () {});
var padsLength = Object.keys(data).length;
Object.keys(data).forEach(function (k, i) {
n = n.nThen(function (w) {
setTimeout(w(function () {
el = data[k];
if (!el.href || (el.roHref && false)) {
// Already migrated
return void progress(7, Math.round(100*i/padsLength));
}
parsed = Hash.parsePadUrl(el.href);
if (parsed.hashData.type !== "pad") {
// No read-only mode for files
return void progress(7, Math.round(100*i/padsLength));
}
if (parsed.hashData.mode === "view") {
// This is a read-only pad in our drive
el.roHref = el.href;
delete el.href;
console.log('Move href to roHref in filesData ', el.roHref);
} else {
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
var hash = Hash.getViewHashFromKeys(secret);
if (hash) {
// Version 0 won't have a view hash available
el.roHref = '/' + parsed.type + '/#' + hash;
console.log('Adding missing roHref in filesData ', el.href);
}
}
progress(6, Math.round(100*i/padsLength));
}));
});
});
n.nThen(waitFor(function () {
Feedback.send('Migrate-7', true);
userObject.version = version = 7;
}));
};
if (version < 7) {
addRoHref();
}
/*}).nThen(function (waitFor) {
// Test progress bar in the loading screen

View File

@@ -1,6 +1,7 @@
define([
'json.sortify',
'/common/userObject.js',
'/common/proxy-manager.js',
'/common/migrate-user-object.js',
'/common/common-hash.js',
'/common/common-util.js',
@@ -18,7 +19,7 @@ define([
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/nthen/index.js',
'/bower_components/saferphore/index.js',
], function (Sortify, UserObject, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger,
], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger,
CpNfWorker, NetConfig, AppConfig,
Crypto, ChainPad, Listmap, nThen, Saferphore) {
var Store = {};
@@ -27,17 +28,23 @@ define([
var postMessage = function () {};
var broadcast = function () {};
var sendDriveEvent = function () {};
var registerProxyEvents = function () {};
var storeHash;
var store = window.CryptPad_AsyncStore = {};
var onSync = function (cb) {
Realtime.whenRealtimeSyncs(store.realtime, cb);
nThen(function (waitFor) {
Realtime.whenRealtimeSyncs(store.realtime, waitFor());
if (store.sharedFolders) {
for (var k in store.sharedFolders) {
Realtime.whenRealtimeSyncs(store.sharedFolders[k].realtime, waitFor());
}
}
}).nThen(function () { cb(); });
};
Store.get = function (clientId, key, cb) {
cb(Util.find(store.proxy, key));
};
@@ -55,6 +62,13 @@ define([
onSync(cb);
};
Store.getSharedFolder = function (clientId, id, cb) {
if (store.manager.folders[id]) {
return void cb(store.manager.folders[id].proxy);
}
cb({});
};
Store.hasSigningKeys = function () {
if (!store.proxy) { return; }
return typeof(store.proxy.edPrivate) === 'string' &&
@@ -78,17 +92,9 @@ define([
if (!userChannel) { return null; }
// Get the list of pads' channel ID in your drive
// This list is filtered so that it doesn't include pad owned by other users (you should
// not pin these pads)
var files = store.userObject.getFiles([store.userObject.FILES_DATA]);
var edPublic = store.proxy.edPublic;
var list = files.map(function (id) {
var d = store.userObject.getFileData(id);
if (d.owners && d.owners.length && edPublic &&
d.owners.indexOf(edPublic) === -1) { return; }
return d.channel;
})
.filter(function (x) { return x; });
// This list is filtered so that it doesn't include pad owned by other users
// It now includes channels from shared folders
var list = store.manager.getChannelsList('pin');
// Get the avatar
var profile = store.proxy.profile;
@@ -111,19 +117,7 @@ define([
};
var getExpirableChannelList = function () {
var list = [];
store.userObject.getFiles([store.userObject.FILES_DATA]).forEach(function (id) {
var data = store.userObject.getFileData(id);
var edPublic = store.proxy.edPublic;
// Push channels owned by someone else or channel that should have expired
// because of the expiration time
if ((data.owners && data.owners.length && data.owners.indexOf(edPublic) === -1) ||
(data.expire && data.expire < (+new Date()))) {
list.push(data.channel);
}
});
return list;
return store.manager.getChannelsList('expirable');
};
var getCanonicalChannelList = function (expirable) {
@@ -380,7 +374,7 @@ define([
Store.getDeletedPads = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var list = getCanonicalChannelList(true);
var list = (data && data.list) || getCanonicalChannelList(true);
if (!Array.isArray(list)) {
return void cb({error: 'INVALID_FILE_LIST'});
}
@@ -435,10 +429,11 @@ define([
cb(JSON.parse(JSON.stringify(metadata)));
};
var makePad = function (href, title) {
var makePad = function (href, roHref, title) {
var now = +new Date();
return {
href: href,
roHref: roHref,
atime: now,
ctime: now,
title: title || Hash.getDefaultName(Hash.parsePadUrl(href)),
@@ -446,16 +441,21 @@ define([
};
Store.addPad = function (clientId, data, cb) {
if (!data.href) { return void cb({error:'NO_HREF'}); }
var pad = makePad(data.href, data.title);
if (!data.href && !data.roHref) { return void cb({error:'NO_HREF'}); }
if (!data.roHref) {
var parsed = Hash.parsePadUrl(data.href);
if (parsed.hashData.type === "pad") {
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
data.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
}
}
var pad = makePad(data.href, data.roHref, data.title);
if (data.owners) { pad.owners = data.owners; }
if (data.expire) { pad.expire = data.expire; }
if (data.password) { pad.password = data.password; }
if (data.channel) { pad.channel = data.channel; }
store.userObject.pushData(pad, function (e, id) {
if (e) { return void cb({error: "Error while adding a template:"+ e}); }
var path = data.path || ['root'];
store.userObject.add(id, path);
store.manager.addPad(data.path, pad, function (e) {
if (e) { return void cb({error: "Error while adding the pad:"+ e}); }
sendDriveEvent('DRIVE_CHANGE', {
path: ['drive', UserObject.FILES_DATA]
}, clientId);
@@ -464,17 +464,7 @@ define([
};
var getOwnedPads = function () {
var list = [];
store.userObject.getFiles([store.userObject.FILES_DATA]).forEach(function (id) {
var data = store.userObject.getFileData(id);
var edPublic = store.proxy.edPublic;
// Push channels owned by someone else or channel that should have expired
// because of the expiration time
if (data.owners && data.owners.length === 1 && data.owners.indexOf(edPublic) !== -1) {
list.push(data.channel);
}
});
var list = store.manager.getChannelsList('owned');
if (store.proxy.todo) {
// No password for todo
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
@@ -596,33 +586,6 @@ define([
});
};
var getAttributeObject = function (attr) {
if (typeof attr === "string") {
console.error('DEPRECATED: use setAttribute with an array, not a string');
return {
path: ['settings'],
obj: store.proxy.settings,
key: attr
};
}
if (!Array.isArray(attr)) { return void console.error("Attribute must be string or array"); }
if (attr.length === 0) { return void console.error("Attribute can't be empty"); }
var obj = store.proxy.settings;
attr.forEach(function (el, i) {
if (i === attr.length-1) { return; }
if (!obj[el]) {
obj[el] = {};
}
else if (typeof obj[el] !== "object") { return void console.error("Wrong attribute"); }
obj = obj[el];
});
return {
path: ['settings'].concat(attr),
obj: obj,
key: attr[attr.length-1]
};
};
// Set the display name (username) in the proxy
Store.setDisplayName = function (clientId, value, cb) {
store.proxy[Constants.displayNameKey] = value;
@@ -651,7 +614,7 @@ define([
* - value (String)
*/
Store.setPadAttribute = function (clientId, data, cb) {
store.userObject.setPadAttribute(data.href, data.attr, data.value, function () {
store.manager.setPadAttribute(data, function () {
sendDriveEvent('DRIVE_CHANGE', {
path: ['drive', UserObject.FILES_DATA]
}, clientId);
@@ -659,11 +622,38 @@ define([
});
};
Store.getPadAttribute = function (clientId, data, cb) {
store.userObject.getPadAttribute(data.href, data.attr, function (err, val) {
store.manager.getPadAttribute(data, function (err, val) {
if (err) { return void cb({error: err}); }
cb(val);
});
};
var getAttributeObject = function (attr) {
if (typeof attr === "string") {
console.error('DEPRECATED: use setAttribute with an array, not a string');
return {
path: ['settings'],
obj: store.proxy.settings,
key: attr
};
}
if (!Array.isArray(attr)) { return void console.error("Attribute must be string or array"); }
if (attr.length === 0) { return void console.error("Attribute can't be empty"); }
var obj = store.proxy.settings;
attr.forEach(function (el, i) {
if (i === attr.length-1) { return; }
if (!obj[el]) {
obj[el] = {};
}
else if (typeof obj[el] !== "object") { return void console.error("Wrong attribute"); }
obj = obj[el];
});
return {
path: ['settings'].concat(attr),
obj: obj,
key: attr[attr.length-1]
};
};
Store.setAttribute = function (clientId, data, cb) {
try {
var object = getAttributeObject(data.attr);
@@ -681,11 +671,12 @@ define([
// Tags
Store.listAllTags = function (clientId, data, cb) {
cb(store.userObject.getTagsList());
cb(store.manager.getTagsList());
};
// Templates
Store.getTemplates = function (clientId, data, cb) {
// No templates in shared folders: we don't need the manager here
var templateFiles = store.userObject.getFiles(['template']);
var res = [];
templateFiles.forEach(function (f) {
@@ -695,6 +686,7 @@ define([
cb(res);
};
Store.incrementTemplateUse = function (clientId, href) {
// No templates in shared folders: we don't need the manager here
store.userObject.getPadAttribute(href, 'used', function (err, data) {
// This is a not critical function, abort in case of error to make sure we won't
// create any issue with the user object or the async store
@@ -705,6 +697,17 @@ define([
};
// Pads
Store.isOnlyInSharedFolder = function (clientId, channel, cb) {
var res = store.manager.findChannel(channel);
// A pad is only in a shared worker if:
// 1. this pad is in at least one proxy
// 2. no proxy containing this pad is the main drive
return cb (res.length && !res.some(function (obj) {
// Main drive doesn't have an fId (folder ID)
return !obj.fId;
}));
};
Store.moveToTrash = function (clientId, data, cb) {
var href = Hash.getRelativeHref(data.href);
store.userObject.forget(href);
@@ -728,78 +731,48 @@ define([
if (channelData && channelData.wc && channel === channelData.wc.id) {
owners = channelData.data.owners || undefined;
}
if (data.owners) {
owners = data.owners;
}
var expire;
if (channelData && channelData.wc && channel === channelData.wc.id) {
expire = +channelData.data.expire || undefined;
}
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
var isStronger;
// If we don't find the new channel in our existing pads, we'll have to add the pads
// to filesData
var contains;
// Update all pads that use the same channel but with a weaker hash
// Edit > Edit (present) > View > View (present)
for (var id in allPads) {
var pad = allPads[id];
if (!pad.href) { continue; }
var p2 = Hash.parsePadUrl(pad.href);
var h2 = p2.hashData;
// Different types, proceed to the next one
// No hash data: corrupted pad?
if (p.type !== p2.type || !h2) { continue; }
// Different channel: continue
if (pad.channel !== channel) { continue; }
var shouldUpdate = p.hash.replace(/\/$/, '') === p2.hash.replace(/\/$/, '');
// If the hash is different but represents the same channel, check if weaker or stronger
if (!shouldUpdate && h.version !== 0) {
// We had view & now we have edit, update
if (h2.mode === 'view' && h.mode === 'edit') { shouldUpdate = true; }
// Same mode and we had present URL, update
else if (h.mode === h2.mode && h2.present) { shouldUpdate = true; }
// If we're here it means we have a weaker URL:
// update the date but keep the existing hash
else {
pad.atime = +new Date();
contains = true;
continue;
}
var datas = store.manager.findChannel(channel);
var contains = datas.length !== 0;
datas.forEach(function (obj) {
var pad = obj.data;
pad.atime = +new Date();
pad.title = title;
if (owners || h.type !== "file") {
// OWNED_FILES
// Never remove owner for files
pad.owners = owners;
}
pad.expire = expire;
if (h.mode === 'view') { return; }
if (shouldUpdate) {
contains = true;
pad.atime = +new Date();
pad.title = title;
if (owners || h.type !== "file") {
// OWNED_FILES
// Never remove owner for files
pad.owners = owners;
}
pad.expire = expire;
// If the href is different, it means we have a stronger one
if (href !== pad.href) { isStronger = true; }
pad.href = href;
// If we only have rohref, it means we have a stronger href
if (!pad.href) {
// If we have a stronger url, remove the possible weaker from the trash.
// If all of the weaker ones were in the trash, add the stronger to ROOT
obj.userObject.restoreHref(href);
}
}
if (isStronger) {
// If we have a stronger url, remove the possible weaker from the trash.
// If all of the weaker ones were in the trash, add the stronger to ROOT
store.userObject.restoreHref(href);
}
pad.href = href;
});
// Add the pad if it does not exist in our drive
if (!contains) {
var roHref;
if (h.mode === "view") {
roHref = href;
href = undefined;
}
Store.addPad(clientId, {
href: href,
roHref: roHref,
channel: channel,
title: title,
owners: owners,
@@ -819,7 +792,6 @@ define([
// Filepicker app
Store.getSecureFilesList = function (clientId, query, cb) {
var list = {};
var hashes = [];
var types = query.types;
var where = query.where;
var filter = query.filter || {};
@@ -834,19 +806,19 @@ define([
}
return filtered;
};
store.userObject.getFiles(where).forEach(function (id) {
var data = store.userObject.getFileData(id);
var parsed = Hash.parsePadUrl(data.href);
store.manager.getSecureFilesList(where).forEach(function (obj) {
var data = obj.data;
var id = obj.id;
var parsed = Hash.parsePadUrl(data.href || data.roHref);
if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) &&
hashes.indexOf(parsed.hash) === -1 &&
!isFiltered(parsed.type, data)) {
hashes.push(parsed.hash);
list[id] = data;
}
});
cb(list);
};
Store.getPadData = function (clientId, id, cb) {
// FIXME: this is only used for templates at the moment, so we don't need the manager
cb(store.userObject.getFileData(id));
};
@@ -1192,37 +1164,60 @@ define([
}]));
};
// SHARED FOLDERS
var loadSharedFolder = function (id, data, cb) {
var parsed = Hash.parsePadUrl(data.href);
var secret = Hash.getSecrets('drive', parsed.hash, data.password);
var owners = data.owners;
var listmapConfig = {
data: {},
websocketURL: NetConfig.getWebsocketURL(),
channel: secret.channel,
readOnly: false,
validateKey: secret.keys.validateKey || undefined,
crypto: Crypto.createEncryptor(secret.keys),
userName: 'sharedFolder',
logLevel: 1,
ChainPad: ChainPad,
classic: true,
owners: owners
};
var rt = Listmap.create(listmapConfig);
store.sharedFolders[id] = rt;
rt.proxy.on('ready', function (info) {
store.manager.addProxy(id, rt.proxy, info.leave);
cb(rt, info.metadata);
});
if (store.driveEvents) {
registerProxyEvents(rt.proxy, id);
}
return rt;
};
Store.addSharedFolder = function (clientId, data, cb) {
Store.userObjectCommand(clientId, {
cmd: 'addSharedFolder',
data: data
}, cb);
};
// Drive
Store.userObjectCommand = function (clientId, cmdData, cb) {
if (!cmdData || !cmdData.cmd) { return; }
var data = cmdData.data;
//var data = cmdData.data;
var cb2 = function (data2) {
var paths = data.paths || [data.path] || [];
paths = paths.concat(data.newPath || []);
paths.forEach(function (p) {
//var paths = data.paths || [data.path] || [];
//paths = paths.concat(data.newPath ? [data.newPath] : []);
//paths.forEach(function (p) {
sendDriveEvent('DRIVE_CHANGE', {
//path: ['drive', UserObject.FILES_DATA]
path: ['drive'].concat(p)
path: ['drive', UserObject.FILES_DATA]
//path: ['drive'].concat(p)
}, clientId);
//});
onSync(function () {
cb(data2);
});
cb(data2);
};
switch (cmdData.cmd) {
case 'move':
store.userObject.move(data.paths, data.newPath, cb2); break;
case 'restore':
store.userObject.restore(data.path, cb2); break;
case 'addFolder':
store.userObject.addFolder(data.path, data.name, cb2); break;
case 'delete':
store.userObject.delete(data.paths, cb2, data.nocheck, data.isOwnPadRemoved); break;
case 'emptyTrash':
store.userObject.emptyTrash(cb2); break;
case 'rename':
store.userObject.rename(data.path, data.newName, cb2); break;
default:
cb();
}
store.manager.command(cmdData, cb2);
};
// Clients management
@@ -1259,32 +1254,41 @@ define([
// Special events
var driveEventInit = false;
sendDriveEvent = function (q, data, sender) {
driveEventClients.forEach(function (cId) {
if (cId === sender) { return; }
postMessage(cId, q, data);
});
};
registerProxyEvents = function (proxy, fId) {
proxy.on('change', [], function (o, n, p) {
sendDriveEvent('DRIVE_CHANGE', {
id: fId,
old: o,
new: n,
path: p
});
});
proxy.on('remove', [], function (o, p) {
sendDriveEvent('DRIVE_REMOVE', {
id: fId,
old: o,
path: p
});
});
};
Store._subscribeToDrive = function (clientId) {
if (driveEventClients.indexOf(clientId) === -1) {
driveEventClients.push(clientId);
}
if (!driveEventInit) {
store.proxy.on('change', [], function (o, n, p) {
sendDriveEvent('DRIVE_CHANGE', {
old: o,
new: n,
path: p
});
if (!store.driveEvents) {
store.driveEvents = true;
registerProxyEvents(store.proxy);
Object.keys(store.manager.folders).forEach(function (fId) {
var proxy = store.manager.folders[fId].proxy;
registerProxyEvents(proxy, fId);
});
store.proxy.on('remove', [], function (o, p) {
sendDriveEvent(clientId, 'DRIVE_REMOVE', {
old: o,
path: p
});
});
driveEventInit = true;
}
};
@@ -1340,12 +1344,60 @@ define([
/////////////////////// Init /////////////////////////////////////
//////////////////////////////////////////////////////////////////
var loadSharedFolders = function (waitFor) {
store.sharedFolders = {};
var shared = Util.find(store.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {};
// Check if any of our shared folder is expired or deleted by its owner.
// If we don't check now, Listmap will create an empty proxy if it no longer exists on
// the server.
nThen(function (waitFor) {
var edPublic = store.proxy.edPublic;
var checkExpired = Object.keys(shared).filter(function (fId) {
var d = shared[fId];
return (Array.isArray(d.owners) && d.owners.length &&
(!edPublic || d.owners.indexOf(edPublic) === -1))
|| (d.expire && d.expire < (+new Date()));
}).map(function (fId) {
return shared[fId].channel;
});
Store.getDeletedPads(null, {list: checkExpired}, waitFor(function (chans) {
if (chans && chans.error) { return void console.error(chans.error); }
if (!Array.isArray(chans) || !chans.length) { return; }
var toDelete = [];
Object.keys(shared).forEach(function (fId) {
if (chans.indexOf(shared[fId].channel) !== -1
&& toDelete.indexOf(fId) === -1) {
toDelete.push(fId);
}
});
toDelete.forEach(function (fId) {
var paths = store.userObject.findFile(Number(fId));
store.userObject.delete(paths, waitFor(), true);
delete shared[fId];
});
}));
}).nThen(function (waitFor) {
Object.keys(shared).forEach(function (id) {
var sf = shared[id];
loadSharedFolder(id, sf, waitFor());
});
}).nThen(waitFor());
};
var onReady = function (clientId, returned, cb) {
var proxy = store.proxy;
var userObject = store.userObject = UserObject.init(proxy.drive, {
pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
unpinPads: function (data, cb) { Store.unpinPads(null, data, cb); },
removeOwnedChannel: function (data, cb) { Store.removeOwnedChannel(null, data, cb); },
var unpin = function (data, cb) {
if (!store.loggedIn) { return void cb(); }
Store.unpinPads(null, data, cb);
};
var pin = function (data, cb) {
if (!store.loggedIn) { return void cb(); }
Store.pinPads(null, data, cb);
};
var manager = store.manager = ProxyManager.create(proxy.drive, proxy.edPublic,
pin, unpin, loadSharedFolder, {
outer: true,
removeOwnedChannel: function (data, cb) { Store.removeOwnedChannel('', data, cb); },
edPublic: store.proxy.edPublic,
loggedIn: store.loggedIn,
log: function (msg) {
@@ -1353,6 +1405,7 @@ define([
sendDriveEvent("DRIVE_LOG", msg);
}
});
var userObject = store.userObject = manager.user.userObject;
nThen(function (waitFor) {
postMessage(clientId, 'LOADING_DRIVE', {
state: 2
@@ -1361,16 +1414,18 @@ define([
}).nThen(function (waitFor) {
Migrate(proxy, waitFor(), function (version, progress) {
postMessage(clientId, 'LOADING_DRIVE', {
state: 2,
state: (2 + (version / 10)),
progress: progress
});
});
}).nThen(function () {
Store.initAnonRpc(null, null, waitFor());
}).nThen(function (waitFor) {
postMessage(clientId, 'LOADING_DRIVE', {
state: 3
});
userObject.fixFiles();
loadSharedFolders(waitFor);
}).nThen(function () {
var requestLogin = function () {
broadcast([], "REQUEST_LOGIN");
};

View File

@@ -19,127 +19,130 @@ var debug = function (msg) { console.log(msg); };
var init = function (client, cb) {
debug('SW INIT');
require([
'/common/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require(['/api/config?cb=' + (+new Date()).toString(16)], function (ApiConfig) {
if (ApiConfig.requireConf) { require.config(ApiConfig.requireConf); }
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
debug('SW Required ressources loaded');
var msgEv = Util.mkEvent();
'/common/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
debug('SW Required ressources loaded');
var msgEv = Util.mkEvent();
if (!self.Rpc) {
self.Rpc = SRpc();
}
var Rpc = self.Rpc;
if (!self.Rpc) {
self.Rpc = SRpc();
}
var Rpc = self.Rpc;
var postToClient = function (data) {
postMsg(client, data);
};
Channel.create(msgEv, postToClient, function (chan) {
debug('SW Channel created');
var postToClient = function (data) {
postMsg(client, data);
};
Channel.create(msgEv, postToClient, function (chan) {
debug('SW Channel created');
var clientId = client.id;
self.tabs[clientId].chan = chan;
Object.keys(Rpc.queries).forEach(function (q) {
if (q === 'CONNECT') { return; }
if (q === 'JOIN_PAD') { return; }
if (q === 'SEND_PAD_MSG') { return; }
chan.on(q, function (data, cb) {
try {
Rpc.queries[q](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query ' + q);
console.error(e);
console.log(data);
}
if (q === "DISCONNECT") {
console.log('Deleting existing store!');
delete self.Rpc;
delete self.store;
}
});
});
chan.on('CONNECT', function (cfg, cb) {
debug('SW Connect callback');
if (self.store) {
debug('Store already exists!');
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
return void cb(self.store);
}
debug('Loading new async store');
// One-time initialization (init async-store)
cfg.query = function (cId, cmd, data, cb) {
cb = cb || function () {};
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
var clientId = client.id;
self.tabs[clientId].chan = chan;
Object.keys(Rpc.queries).forEach(function (q) {
if (q === 'CONNECT') { return; }
if (q === 'JOIN_PAD') { return; }
if (q === 'SEND_PAD_MSG') { return; }
chan.on(q, function (data, cb) {
try {
Rpc.queries[q](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query ' + q);
console.error(e);
console.log(data);
}
if (q === "DISCONNECT") {
console.log('Deleting existing store!');
delete self.Rpc;
delete self.store;
}
});
};
cfg.broadcast = function (excludes, cmd, data, cb) {
cb = cb || function () {};
Object.keys(self.tabs).forEach(function (cId) {
if (excludes.indexOf(cId) !== -1) { return; }
});
chan.on('CONNECT', function (cfg, cb) {
debug('SW Connect callback');
if (self.store) {
debug('Store already exists!');
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
return void cb(self.store);
}
debug('Loading new async store');
// One-time initialization (init async-store)
cfg.query = function (cId, cmd, data, cb) {
cb = cb || function () {};
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
cfg.broadcast = function (excludes, cmd, data, cb) {
cb = cb || function () {};
Object.keys(self.tabs).forEach(function (cId) {
if (excludes.indexOf(cId) !== -1) { return; }
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
});
};
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
if (data && data.state === "ALREADY_INIT") {
return void cb(data.returned);
}
self.store = data;
cb(data);
});
};
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
if (data && data.state === "ALREADY_INIT") {
return void cb(data.returned);
}
self.store = data;
cb(data);
});
});
chan.on('JOIN_PAD', function (data, cb) {
self.tabs[clientId].channelId = data.channel;
try {
Rpc.queries['JOIN_PAD'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query JOIN_PAD');
console.error(e);
console.log(data);
}
});
chan.on('SEND_PAD_MSG', function (msg, cb) {
var data = {
msg: msg,
channel: self.tabs[clientId].channelId
};
try {
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query SEND_PAD_MSG');
console.error(e);
console.log(data);
}
});
cb();
}, true);
chan.on('JOIN_PAD', function (data, cb) {
self.tabs[clientId].channelId = data.channel;
try {
Rpc.queries['JOIN_PAD'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query JOIN_PAD');
console.error(e);
console.log(data);
}
});
chan.on('SEND_PAD_MSG', function (msg, cb) {
var data = {
msg: msg,
channel: self.tabs[clientId].channelId
};
try {
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query SEND_PAD_MSG');
console.error(e);
console.log(data);
}
});
cb();
}, true);
self.tabs[client.id].msgEv = msgEv;
self.tabs[client.id].msgEv = msgEv;
self.tabs[client.id].close = function () {
Rpc._removeClient(client.id);
};
self.tabs[client.id].close = function () {
Rpc._removeClient(client.id);
};
});
});
});
};

View File

@@ -19,128 +19,131 @@ var debug = function (msg) { console.log(msg); };
var init = function (client, cb) {
debug('SharedW INIT');
require([
'/common/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require(['/api/config?cb=' + (+new Date()).toString(16)], function (ApiConfig) {
if (ApiConfig.requireConf) { require.config(ApiConfig.requireConf); }
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
debug('SharedW Required ressources loaded');
var msgEv = Util.mkEvent();
'/common/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
debug('SharedW Required ressources loaded');
var msgEv = Util.mkEvent();
if (!self.Rpc) {
self.Rpc = SRpc();
}
var Rpc = self.Rpc;
if (!self.Rpc) {
self.Rpc = SRpc();
}
var Rpc = self.Rpc;
var postToClient = function (data) {
postMsg(client, data);
};
Channel.create(msgEv, postToClient, function (chan) {
debug('SharedW Channel created');
var postToClient = function (data) {
postMsg(client, data);
};
Channel.create(msgEv, postToClient, function (chan) {
debug('SharedW Channel created');
var clientId = client.id;
client.chan = chan;
Object.keys(Rpc.queries).forEach(function (q) {
if (q === 'CONNECT') { return; }
if (q === 'JOIN_PAD') { return; }
if (q === 'SEND_PAD_MSG') { return; }
chan.on(q, function (data, cb) {
try {
Rpc.queries[q](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query ' + q);
console.error(e);
console.log(data);
}
if (q === "DISCONNECT") {
console.log('Deleting existing store!');
delete self.Rpc;
delete self.store;
}
});
});
chan.on('CONNECT', function (cfg, cb) {
debug('SharedW connecting to store...');
if (self.store) {
debug('Store already exists!');
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
return void cb(self.store);
}
debug('Loading new async store');
// One-time initialization (init async-store)
cfg.query = function (cId, cmd, data, cb) {
cb = cb || function () {};
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
var clientId = client.id;
client.chan = chan;
Object.keys(Rpc.queries).forEach(function (q) {
if (q === 'CONNECT') { return; }
if (q === 'JOIN_PAD') { return; }
if (q === 'SEND_PAD_MSG') { return; }
chan.on(q, function (data, cb) {
try {
Rpc.queries[q](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query ' + q);
console.error(e);
console.log(data);
}
if (q === "DISCONNECT") {
console.log('Deleting existing store!');
delete self.Rpc;
delete self.store;
}
});
};
cfg.broadcast = function (excludes, cmd, data, cb) {
cb = cb || function () {};
Object.keys(self.tabs).forEach(function (cId) {
if (excludes.indexOf(cId) !== -1) { return; }
});
chan.on('CONNECT', function (cfg, cb) {
debug('SharedW connecting to store...');
if (self.store) {
debug('Store already exists!');
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
return void cb(self.store);
}
debug('Loading new async store');
// One-time initialization (init async-store)
cfg.query = function (cId, cmd, data, cb) {
cb = cb || function () {};
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
cfg.broadcast = function (excludes, cmd, data, cb) {
cb = cb || function () {};
Object.keys(self.tabs).forEach(function (cId) {
if (excludes.indexOf(cId) !== -1) { return; }
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
});
};
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
if (data && data.state === "ALREADY_INIT") {
self.store = data.returned;
return void cb(data.returned);
}
self.store = data;
cb(data);
});
};
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
if (data && data.state === "ALREADY_INIT") {
self.store = data.returned;
return void cb(data.returned);
}
self.store = data;
cb(data);
});
});
chan.on('JOIN_PAD', function (data, cb) {
client.channelId = data.channel;
try {
Rpc.queries['JOIN_PAD'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query JOIN_PAD');
console.error(e);
console.log(data);
}
});
chan.on('SEND_PAD_MSG', function (msg, cb) {
var data = {
msg: msg,
channel: client.channelId
};
try {
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query SEND_PAD_MSG');
console.error(e);
console.log(data);
}
});
cb();
}, true);
chan.on('JOIN_PAD', function (data, cb) {
client.channelId = data.channel;
try {
Rpc.queries['JOIN_PAD'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query JOIN_PAD');
console.error(e);
console.log(data);
}
});
chan.on('SEND_PAD_MSG', function (msg, cb) {
var data = {
msg: msg,
channel: client.channelId
};
try {
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query SEND_PAD_MSG');
console.error(e);
console.log(data);
}
});
cb();
}, true);
client.msgEv = msgEv;
client.msgEv = msgEv;
client.close = function () {
Rpc._removeClient(client.id);
};
client.close = function () {
Rpc._removeClient(client.id);
};
});
});
});
};

View File

@@ -42,6 +42,7 @@ define([
MOVE_TO_TRASH: Store.moveToTrash,
RESET_DRIVE: Store.resetDrive,
GET_METADATA: Store.getMetadata,
IS_ONLY_IN_SHARED_FOLDER: Store.isOnlyInSharedFolder,
SET_DISPLAY_NAME: Store.setDisplayName,
SET_PAD_ATTRIBUTE: Store.setPadAttribute,
GET_PAD_ATTRIBUTE: Store.getPadAttribute,
@@ -53,6 +54,8 @@ define([
GET_PAD_DATA: Store.getPadData,
GET_STRONGER_HASH: Store.getStrongerHash,
INCREMENT_TEMPLATE_USE: Store.incrementTemplateUse,
GET_SHARED_FOLDER: Store.getSharedFolder,
ADD_SHARED_FOLDER: Store.addSharedFolder,
// Messaging
INVITE_FROM_USERLIST: Store.inviteFromUserlist,
ADD_DIRECT_MESSAGE_HANDLERS: Store.addDirectMessageHandlers,

View File

@@ -92,7 +92,8 @@ define([
href: href,
path: path,
password: password,
channel: id
channel: id,
owners: metadata.owners
};
common.setPadTitle(data, function (err) {
if (err) { return void console.error(err); }

View File

@@ -14,15 +14,11 @@ define([
};
module.init = function (config, exp, files) {
var unpinPads = config.unpinPads || function () {
console.error("unpinPads was not provided");
};
var pinPads = config.pinPads;
var removeOwnedChannel = config.removeOwnedChannel || function () {
console.error("removeOwnedChannel was not provided");
};
var loggedIn = config.loggedIn;
var workgroup = config.workgroup;
var sharedFolder = config.sharedFolder;
var edPublic = config.edPublic;
var ROOT = exp.ROOT;
@@ -31,6 +27,7 @@ define([
var UNSORTED = exp.UNSORTED;
var TRASH = exp.TRASH;
var TEMPLATE = exp.TEMPLATE;
var SHARED_FOLDERS = exp.SHARED_FOLDERS;
var debug = exp.debug;
@@ -50,35 +47,31 @@ define([
var data = exp.getFileData(id);
cb(null, clone(data[attr]));
};
var removePadAttribute = exp.removePadAttribute = function (f) {
if (typeof(f) !== 'string') {
console.error("Can't find pad attribute for an undefined pad");
return;
}
Object.keys(files).forEach(function (key) {
var hash = f.indexOf('#') !== -1 ? f.slice(f.indexOf('#') + 1) : null;
if (hash && key.indexOf(hash) === 0) {
exp.debug("Deleting pad attribute in the realtime object");
delete files[key];
}
});
};
exp.pushData = function (data, cb) {
if (typeof cb !== "function") { cb = function () {}; }
var todo = function () {
var id = Util.createRandomInteger();
files[FILES_DATA][id] = data;
cb(null, id);
};
if (!loggedIn || !AppConfig.enablePinning || config.testMode) {
return void todo();
var id = Util.createRandomInteger();
files[FILES_DATA][id] = data;
cb(null, id);
};
exp.pushSharedFolder = function (data, cb) {
if (typeof cb !== "function") { cb = function () {}; }
// Check if we already have this shared folder in our drive
if (Object.keys(files[SHARED_FOLDERS]).some(function (k) {
return files[SHARED_FOLDERS][k].channel === data.channel;
})) {
return void cb ('EEXISTS');
}
if (!pinPads) { return; }
pinPads([data.channel], function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
todo();
});
// Add the folder
if (!loggedIn || !AppConfig.enablePinning || config.testMode) {
return void cb("EAUTH");
}
var id = Util.createRandomInteger();
files[SHARED_FOLDERS][id] = data;
cb(null, id);
};
// FILES DATA
@@ -87,21 +80,21 @@ define([
};
// Find files in FILES_DATA that are not anymore in the drive, and remove them from
// FILES_DATA. If there are owned pads, remove them from server too, unless the flag tells
// us they're already removed
exp.checkDeletedFiles = function (isOwnPadRemoved) {
// Nothing in FILES_DATA for workgroups
if (workgroup || (!loggedIn && !config.testMode)) { return; }
// FILES_DATA. If there are owned pads, remove them from server too.
exp.checkDeletedFiles = function (cb) {
if (!loggedIn && !config.testMode) { return void cb(); }
var filesList = exp.getFiles([ROOT, 'hrefArray', TRASH]);
var toClean = [];
exp.getFiles([FILES_DATA]).forEach(function (id) {
var ownedRemoved = [];
exp.getFiles([FILES_DATA, SHARED_FOLDERS]).forEach(function (id) {
if (filesList.indexOf(id) === -1) {
var fd = exp.getFileData(id);
var fd = exp.isSharedFolder(id) ? files[SHARED_FOLDERS][id] : exp.getFileData(id);
var channelId = fd.channel;
// If trying to remove an owned pad, remove it from server also
if (!isOwnPadRemoved &&
fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
if (!sharedFolder && fd.owners && fd.owners.indexOf(edPublic) !== -1
&& channelId) {
if (channelId) { ownedRemoved.push(channelId); }
removeOwnedChannel(channelId, function (obj) {
if (obj && obj.error) {
// If the error is that the file is already removed, nothing to
@@ -116,14 +109,15 @@ define([
});
}
if (channelId) { toClean.push(channelId); }
spliceFileData(id);
if (exp.isSharedFolder(id)) {
delete files[SHARED_FOLDERS][id];
} else {
spliceFileData(id);
}
}
});
if (!toClean.length) { return; }
unpinPads(toClean, function (response) {
if (response && response.error) { return console.error(response.error); }
// console.error(response);
});
if (!toClean.length) { return void cb(); }
cb(null, toClean, ownedRemoved);
};
var deleteHrefs = function (ids) {
ids.forEach(function (obj) {
@@ -137,7 +131,7 @@ define([
files[TRASH][obj.name].splice(idx, 1);
});
};
exp.deleteMultiplePermanently = function (paths, nocheck, isOwnPadRemoved) {
exp.deleteMultiplePermanently = function (paths, nocheck, cb) {
var hrefPaths = paths.filter(function(x) { return exp.isPathIn(x, ['hrefArray']); });
var rootPaths = paths.filter(function(x) { return exp.isPathIn(x, [ROOT]); });
var trashPaths = paths.filter(function(x) { return exp.isPathIn(x, [TRASH]); });
@@ -145,14 +139,11 @@ define([
if (!loggedIn && !config.testMode) {
allFilesPaths.forEach(function (path) {
var el = exp.find(path);
if (!el) { return; }
var id = exp.getIdFromHref(el.href);
var id = path[1];
if (!id) { return; }
spliceFileData(id);
removePadAttribute(el.href);
});
return;
return void cb();
}
var ids = [];
@@ -192,11 +183,69 @@ define([
deleteMultipleTrashRoot(trashRoot);
// In some cases, we want to remove pads from a location without removing them from
// OLD_FILES_DATA (replaceHref)
if (!nocheck) { exp.checkDeletedFiles(isOwnPadRemoved); }
// FILES_DATA (replaceHref)
if (!nocheck) { exp.checkDeletedFiles(cb); }
else { cb(); }
};
// Move
// From another drive
exp.copyFromOtherDrive = function (path, element, data, key) {
// Copy files data
// We have to remove pads that are already in the current proxy to make sure
// we won't create duplicates
var toRemove = [];
Object.keys(data).forEach(function (id) {
id = Number(id);
// Find and maybe update existing pads with the same channel id
var d = data[id];
var found = false;
for (var i in files[FILES_DATA]) {
if (files[FILES_DATA][i].channel === d.channel) {
// Update href?
if (!files[FILES_DATA][i].href) { files[FILES_DATA][i].href = d.href; }
found = true;
break;
}
}
if (found) {
toRemove.push(id);
return;
}
files[FILES_DATA][id] = data[id];
});
// Remove existing pads from the "element" variable
if (exp.isFile(element) && toRemove.indexOf(element) !== -1) {
exp.log(Messages.sharedFolders_duplicate);
return;
} else if (exp.isFolder(element)) {
var _removeExisting = function (root) {
for (var k in root) {
if (exp.isFile(root[k])) {
if (toRemove.indexOf(root[k]) !== -1) {
exp.log(Messages.sharedFolders_duplicate);
delete root[k];
}
} else if (exp.isFolder(root[k])) {
_removeExisting(root[k]);
}
}
};
_removeExisting(element);
}
// Copy file or folder
var newParent = exp.find(path);
var tempName = exp.isFile(element) ? Hash.createChannelId() : key;
var newName = exp.getAvailableName(newParent, tempName);
newParent[newName] = element;
};
// From the same drive
var pushToTrash = function (name, element, path) {
var trash = files[TRASH];
if (typeof(trash[name]) === "undefined") { trash[name] = []; }
@@ -259,7 +308,6 @@ define([
if (!id) { return; }
if (!loggedIn && !config.testMode) {
// delete permanently
exp.removePadAttribute(href);
spliceFileData(id);
return;
}
@@ -268,14 +316,7 @@ define([
};
// REPLACE
exp.replace = function (o, n) {
var idO = exp.getIdFromHref(o);
if (!idO || !exp.isFile(idO)) { return; }
var data = exp.getFileData(idO);
if (!data) { return; }
data.href = n;
};
// If all the occurences of an href are in the trash, remvoe them and add the file in root.
// If all the occurences of an href are in the trash, remove them and add the file in root.
// This is use with setPadTitle when we open a stronger version of a deleted pad
exp.restoreHref = function (href) {
var idO = exp.getIdFromHref(href);
@@ -300,9 +341,9 @@ define([
};
exp.add = function (id, path) {
// TODO WW
if (!loggedIn && !config.testMode) { return; }
var data = files[FILES_DATA][id];
id = Number(id);
var data = files[FILES_DATA][id] || files[SHARED_FOLDERS][id];
if (!data || typeof(data) !== "object") { return; }
var newPath = path, parentEl;
if (path && !Array.isArray(path)) {
@@ -406,7 +447,6 @@ define([
});
delete files[OLD_FILES_DATA];
delete files.migrate;
console.log('done');
todo();
};
if (exp.rt) {
@@ -473,6 +513,7 @@ define([
}
};
var fixTrashRoot = function () {
if (sharedFolder) { return; }
if (typeof(files[TRASH]) !== "object") { debug("TRASH was not an object"); files[TRASH] = {}; }
var tr = files[TRASH];
var toClean;
@@ -516,6 +557,7 @@ define([
}
};
var fixTemplate = function () {
if (sharedFolder) { return; }
if (!Array.isArray(files[TEMPLATE])) { debug("TEMPLATE was not an array"); files[TEMPLATE] = []; }
files[TEMPLATE] = Util.deduplicateString(files[TEMPLATE].slice());
var us = files[TEMPLATE];
@@ -547,7 +589,7 @@ define([
});
};
var fixFilesData = function () {
if (typeof files[FILES_DATA] !== "object") { debug("OLD_FILES_DATA was not an object"); files[FILES_DATA] = {}; }
if (typeof files[FILES_DATA] !== "object") { debug("FILES_DATA was not an object"); files[FILES_DATA] = {}; }
var fd = files[FILES_DATA];
var rootFiles = exp.getFiles([ROOT, TRASH, 'hrefArray']);
var root = exp.find([ROOT]);
@@ -563,33 +605,45 @@ define([
continue;
}
// Clean missing href
var parsed;
if (el.href) {
if (!el.href) {
debug("Removing an element in filesData with a missing href.", el);
toClean.push(id);
continue;
}
parsed = Hash.parsePadUrl(el.href);
// Clean invalid hash
if (!parsed.hash) {
debug("Removing an element in filesData with a invalid href.", el);
toClean.push(id);
continue;
}
// Clean invalid type
if (!parsed.type) {
debug("Removing an element in filesData with a invalid type.", el);
toClean.push(id);
continue;
}
} else if (!el.roHref) {
if (!el.href && !el.roHref) {
debug("Removing an element in filesData with a missing href.", el);
toClean.push(id);
continue;
}
var parsed = Hash.parsePadUrl(el.href || el.roHref);
var secret;
// Clean invalid hash
if (!parsed.hash) {
debug("Removing an element in filesData with a invalid href.", el);
toClean.push(id);
continue;
}
// Clean invalid type
if (!parsed.type) {
debug("Removing an element in filesData with a invalid type.", el);
toClean.push(id);
continue;
}
// If we have an edit link, check the view link
if (el.href && parsed.hashData.type === "pad") {
if (parsed.hashData.mode === "view") {
el.roHref = el.href;
delete el.href;
} else if (!el.roHref) {
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
el.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
} else {
var parsed2 = Hash.parsePadUrl(el.roHref);
if (!parsed2.hash || !parsed2.type) {
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
el.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
}
}
}
// Fix href
if (el.href && /^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
// Fix creation time
@@ -597,11 +651,14 @@ define([
// Fix title
if (!el.title) { el.title = Hash.getDefaultName(parsed); }
// Fix channel
if (el.href && !el.channel) {
if (!el.channel) {
try {
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
if (!secret) {
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
}
el.channel = secret.channel;
console.log('Adding missing channel in filesData ', el.channel);
console.log(el);
debug('Adding missing channel in filesData ', el.channel);
} catch (e) {
console.error(e);
}
@@ -618,6 +675,21 @@ define([
spliceFileData(id);
});
};
var fixSharedFolders = function () {
if (sharedFolder) { return; }
if (typeof(files[SHARED_FOLDERS]) !== "object") { debug("SHARED_FOLDER was not an object"); files[SHARED_FOLDERS] = {}; }
var sf = files[SHARED_FOLDERS];
var rootFiles = exp.getFiles([ROOT]);
var root = exp.find([ROOT]);
for (var id in sf) {
id = Number(id);
if (rootFiles.indexOf(id) === -1) {
console.log('missing' + id);
var newName = Hash.createChannelId();
root[newName] = id;
}
}
};
var fixDrive = function () {
Object.keys(files).forEach(function (key) {
@@ -627,11 +699,10 @@ define([
fixRoot();
fixTrashRoot();
if (!workgroup) {
fixTemplate();
fixFilesData();
}
fixTemplate();
fixFilesData();
fixDrive();
fixSharedFolders();
if (JSON.stringify(files) !== before) {
debug("Your file system was corrupted. It has been cleaned so that the pads you visit can be stored safely");

View File

@@ -7,94 +7,97 @@ localStorage = {
getItem: function (k) { return localStorage[k]; }
};
require([
'/common/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require(['/api/config?cb=' + (+new Date()).toString(16)], function (ApiConfig) {
if (ApiConfig.requireConf) { require.config(ApiConfig.requireConf); }
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
var msgEv = Util.mkEvent();
'/common/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
var msgEv = Util.mkEvent();
var Rpc = SRpc();
var Rpc = SRpc();
Channel.create(msgEv, postMessage, function (chan) {
var clientId = '1';
Object.keys(Rpc.queries).forEach(function (q) {
if (q === 'CONNECT') { return; }
if (q === 'JOIN_PAD') { return; }
if (q === 'SEND_PAD_MSG') { return; }
chan.on(q, function (data, cb) {
Channel.create(msgEv, postMessage, function (chan) {
var clientId = '1';
Object.keys(Rpc.queries).forEach(function (q) {
if (q === 'CONNECT') { return; }
if (q === 'JOIN_PAD') { return; }
if (q === 'SEND_PAD_MSG') { return; }
chan.on(q, function (data, cb) {
try {
Rpc.queries[q](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query ' + q);
console.error(e);
console.log(data);
}
});
});
chan.on('CONNECT', function (cfg, cb) {
// load Store here, with cfg, and pass a "query" (chan.query)
// cId is a clientId used in ServiceWorker or SharedWorker
cfg.query = function (cId, cmd, data, cb) {
cb = cb || function () {};
chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
cfg.broadcast = function (excludes, cmd, data, cb) {
cb = cb || function () {};
if (excludes.indexOf(clientId) !== -1) { return; }
chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
if (data && data.state === "ALREADY_INIT") {
return void cb(data);
}
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
cb(data);
});
});
var chanId;
chan.on('JOIN_PAD', function (data, cb) {
chanId = data.channel;
try {
Rpc.queries[q](clientId, data, cb);
Rpc.queries['JOIN_PAD'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query ' + q);
console.error('Error in webworker when executing query JOIN_PAD');
console.error(e);
console.log(data);
}
});
});
chan.on('CONNECT', function (cfg, cb) {
// load Store here, with cfg, and pass a "query" (chan.query)
// cId is a clientId used in ServiceWorker or SharedWorker
cfg.query = function (cId, cmd, data, cb) {
cb = cb || function () {};
chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
cfg.broadcast = function (excludes, cmd, data, cb) {
cb = cb || function () {};
if (excludes.indexOf(clientId) !== -1) { return; }
chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
if (data && data.state === "ALREADY_INIT") {
return void cb(data);
chan.on('SEND_PAD_MSG', function (msg, cb) {
var data = {
msg: msg,
channel: chanId
};
try {
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query SEND_PAD_MSG');
console.error(e);
console.log(data);
}
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
cb(data);
});
});
var chanId;
chan.on('JOIN_PAD', function (data, cb) {
chanId = data.channel;
try {
Rpc.queries['JOIN_PAD'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query JOIN_PAD');
console.error(e);
console.log(data);
}
});
chan.on('SEND_PAD_MSG', function (msg, cb) {
var data = {
msg: msg,
channel: chanId
};
try {
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query SEND_PAD_MSG');
console.error(e);
console.log(data);
}
});
}, true);
}, true);
onmessage = function (e) {
msgEv.fire(e);
};
onmessage = function (e) {
msgEv.fire(e);
};
});
});
});

View File

@@ -32,12 +32,14 @@ define([
var chan = {};
// Send a query. channel.query('Q_SOMETHING', { args: "whatever" }, function (reply) { ... });
chan.query = function (q, content, cb) {
chan.query = function (q, content, cb, opts) {
var txid = mkTxid();
opts = opts || {};
var to = opts.timeout || 30000;
var timeout = setTimeout(function () {
delete queries[txid];
//console.log("Timeout making query " + q);
}, 30000);
}, to);
queries[txid] = function (data, msg) {
clearTimeout(timeout);
delete queries[txid];

1018
www/common/proxy-manager.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,6 @@ define([
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,
Hyperjson,

View File

@@ -21,16 +21,18 @@ define([
var chan = {};
// Send a query. channel.query('Q_SOMETHING', { args: "whatever" }, function (reply) { ... });
chan.query = function (q, content, cb) {
chan.query = function (q, content, cb, opts) {
if (!otherWindow) { throw new Error('not yet initialized'); }
if (!SFrameProtocol[q]) {
throw new Error('please only make queries are defined in sframe-protocol.js');
}
opts = opts || {};
var txid = mkTxid();
var to = opts.timeout || 30000;
var timeout = setTimeout(function () {
delete queries[txid];
console.log("Timeout making query " + q);
}, 30000);
}, to);
queries[txid] = function (data, msg) {
clearTimeout(timeout);
delete queries[txid];

View File

@@ -266,8 +266,12 @@ define([
isDeleted: isNewFile && window.location.hash.length > 0,
forceCreationScreen: forceCreationScreen,
password: password,
channel: secret.channel
channel: secret.channel,
enableSF: localStorage.CryptPad_SF === "1" // TODO to remove when enabled by default
};
if (window.CryptPad_newSharedFolder) {
additionalPriv.newSharedFolder = window.CryptPad_newSharedFolder;
}
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
if (cfg.addData) {
@@ -421,10 +425,19 @@ define([
});
});
sframeChan.on('Q_GET_HISTORY_RANGE', function (data, cb) {
var crypto = Crypto.createEncryptor(secret.keys);
var nSecret = secret;
if (cfg.isDrive) {
var hash = Utils.LocalStore.getUserHash() || Utils.LocalStore.getFSHash();
if (hash) {
nSecret = Utils.Hash.getSecrets('drive', hash);
}
}
var channel = nSecret.channel;
var validate = nSecret.keys.validateKey;
var crypto = Crypto.createEncryptor(nSecret.keys);
Cryptpad.getHistoryRange({
channel: secret.channel,
validateKey: secret.keys.validateKey,
channel: channel,
validateKey: validate,
lastKnownHash: data.lastKnownHash
}, function (data) {
cb({
@@ -484,6 +497,12 @@ define([
cb();
});
sframeChan.on('Q_IS_ONLY_IN_SHARED_FOLDER', function (data, cb) {
Cryptpad.isOnlyInSharedFolder(secret.channel, function (err, t) {
if (err) { return void cb({error: err}); }
cb(t);
});
});
// Present mode URL
sframeChan.on('Q_PRESENT_URL_GET_VALUE', function (data, cb) {

View File

@@ -112,9 +112,14 @@ define([
var origin = ctx.metadataMgr.getPrivateData().origin;
return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>';
};
funcs.getMediatagFromHref = function () {
funcs.getMediatagFromHref = function (obj) {
var data = ctx.metadataMgr.getPrivateData();
var secret = Hash.getSecrets('file', data.availableHashes.fileHash, data.password);
var secret;
if (obj) {
secret = Hash.getSecrets('file', obj.hash, obj.password);
} else {
secret = Hash.getSecrets('file', data.availableHashes.fileHash, data.password);
}
if (secret.keys && secret.channel) {
var key = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
var hexFileName = secret.channel;

View File

@@ -114,6 +114,10 @@ define({
'Q_GET_PAD_ATTRIBUTE': true,
'Q_SET_PAD_ATTRIBUTE': true,
// Check if a pad is only in a shared folder or (also) in the main drive.
// This allows us to change the behavior of some buttons (trash icon...)
'Q_IS_ONLY_IN_SHARED_FOLDER': true,
// Open/close the File picker (sent from the iframe to the outside)
'EV_FILE_PICKER_OPEN': true,
'EV_FILE_PICKER_CLOSE': true,
@@ -206,6 +210,7 @@ define({
// Inner drive needs to send command and receive updates from the async store
'Q_DRIVE_USEROBJECT': true,
'Q_DRIVE_GETOBJECT': true,
'Q_DRIVE_RESTORE': true,
// Get the pads deleted from the server by other users to remove them from the drive
'Q_DRIVE_GETDELETED': true,
// Store's userObject need to send log messages to inner to display them in the UI

View File

@@ -13,10 +13,10 @@ define([
var UNSORTED = module.UNSORTED = "unsorted";
var TRASH = module.TRASH = "trash";
var TEMPLATE = module.TEMPLATE = "template";
var SHARED_FOLDERS = module.SHARED_FOLDERS = "sharedFolders";
module.init = function (files, config) {
var exp = {};
var pinPads = config.pinPads;
var sframeChan = config.sframeChan;
var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Constants.storageKey;
@@ -28,14 +28,21 @@ define([
exp.UNSORTED = UNSORTED;
exp.TRASH = TRASH;
exp.TEMPLATE = TEMPLATE;
exp.SHARED_FOLDERS = SHARED_FOLDERS;
var sharedFolder = exp.sharedFolder = config.sharedFolder;
exp.id = config.id;
// Logging
var logging = function () {
console.log.apply(console, arguments);
};
var log = config.log || logging;
var log = exp.log = config.log || logging;
var logError = config.logError || logging;
var debug = exp.debug = config.debug || logging;
exp.fixFiles = function () {}; // Overriden by OuterFO
var error = exp.error = function() {
if (sframeChan) {
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
@@ -46,12 +53,10 @@ define([
exp.fixFiles();
}
console.error.apply(console, arguments);
exp.fixFiles();
};
// TODO: workgroup
var workgroup = config.workgroup;
if (pinPads) {
if (config.outer) {
// Extend "exp" with methods used only outside of the iframe (requires access to store)
OuterFO.init(config, exp, files);
}
@@ -76,7 +81,12 @@ define([
var compareFiles = function (fileA, fileB) { return fileA === fileB; };
var isSharedFolder = exp.isSharedFolder = function (element) {
if (sharedFolder) { return false; } // No recursive shared folders
return Boolean(files[SHARED_FOLDERS] && files[SHARED_FOLDERS][element]);
};
var isFile = exp.isFile = function (element, allowStr) {
if (isSharedFolder(element)) { return false; }
return typeof(element) === "number" ||
((typeof(files[OLD_FILES_DATA]) !== "undefined" || allowStr)
&& typeof(element) === "string");
@@ -85,15 +95,11 @@ define([
exp.isReadOnlyFile = function (element) {
if (!isFile(element)) { return false; }
var data = exp.getFileData(element);
var parsed = Hash.parsePadUrl(data.href);
if (!parsed) { return false; }
var pHash = parsed.hashData;
if (!pHash || pHash.type !== "pad") { return; }
return pHash && pHash.mode === 'view';
return Boolean(data.roHref && !data.href);
};
var isFolder = exp.isFolder = function (element) {
return typeof(element) === "object";
return typeof(element) === "object" || isSharedFolder(element);
};
exp.isFolderEmpty = function (element) {
if (!isFolder(element)) { return false; }
@@ -144,9 +150,11 @@ define([
// Data from filesData
var getTitle = exp.getTitle = function (file, type) {
if (workgroup) { debug("No titles in workgroups"); return; }
if (isSharedFolder(file)) {
return '??';
}
var data = getFileData(file);
if (!file || !data || !data.href) {
if (!file || !data || !(data.href || data.roHref)) {
error("getTitle called with a non-existing file id: ", file, data);
return;
}
@@ -216,14 +224,16 @@ define([
// GET FILES
var getFilesRecursively = function (root, arr) {
var getFilesRecursively = exp.getFilesRecursively = function (root, arr) {
arr = arr || [];
for (var e in root) {
if (isFile(root[e])) {
if (isFile(root[e]) || isSharedFolder(root[e])) {
if(arr.indexOf(root[e]) === -1) { arr.push(root[e]); }
} else {
getFilesRecursively(root[e], arr);
}
}
return arr;
};
var _getFiles = {};
_getFiles['array'] = function (cat) {
@@ -235,6 +245,7 @@ define([
});
_getFiles['hrefArray'] = function () {
var ret = [];
if (sharedFolder) { return ret; }
getHrefArray().forEach(function (c) {
ret = ret.concat(_getFiles[c]());
});
@@ -249,7 +260,7 @@ define([
var root = files[TRASH];
var ret = [];
var addFiles = function (el) {
if (isFile(el.element)) {
if (isFile(el.element) || isSharedFolder(el.element)) {
if(ret.indexOf(el.element) === -1) { ret.push(el.element); }
} else {
getFilesRecursively(el.element, ret);
@@ -279,10 +290,15 @@ define([
if (!files[FILES_DATA]) { return ret; }
return Object.keys(files[FILES_DATA]).map(Number);
};
_getFiles[SHARED_FOLDERS] = function () {
var ret = [];
if (!files[SHARED_FOLDERS]) { return ret; }
return Object.keys(files[SHARED_FOLDERS]).map(Number);
};
var getFiles = exp.getFiles = function (categories) {
var ret = [];
if (!categories || !categories.length) {
categories = [ROOT, 'hrefArray', TRASH, OLD_FILES_DATA, FILES_DATA];
categories = [ROOT, 'hrefArray', TRASH, OLD_FILES_DATA, FILES_DATA, SHARED_FOLDERS];
}
categories.forEach(function (c) {
if (typeof _getFiles[c] === "function") {
@@ -295,11 +311,11 @@ define([
var getIdFromHref = exp.getIdFromHref = function (href) {
var result;
getFiles([FILES_DATA]).some(function (id) {
if (files[FILES_DATA][id].href === href) {
if (files[FILES_DATA][id].href === href ||
files[FILES_DATA][id].roHref === href) {
result = id;
return true;
}
return;
});
return result;
};
@@ -315,7 +331,7 @@ define([
}
};
if (isFile(root)) {
if (isFile(root) || isSharedFolder(root)) {
if (compareFiles(file, root)) {
if (paths.indexOf(path) === -1) {
paths.push(path);
@@ -335,6 +351,7 @@ define([
return _findFileInRoot([ROOT], file);
};
var _findFileInHrefArray = function (rootName, file) {
if (sharedFolder) { return []; }
if (!files[rootName]) { return []; }
var unsorted = files[rootName].slice();
var ret = [];
@@ -345,6 +362,7 @@ define([
return ret;
};
var _findFileInTrash = function (path, file) {
if (sharedFolder) { return []; }
var root = find(path);
var paths = [];
var addPaths = function (p) {
@@ -561,29 +579,29 @@ define([
// DELETE
// Permanently delete multiple files at once using a list of paths
// NOTE: We have to be careful when removing elements from arrays (trash root, unsorted or template)
exp.delete = function (paths, cb, nocheck, isOwnPadRemoved) {
exp.delete = function (paths, cb, nocheck) {
if (sframeChan) {
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
cmd: "delete",
data: {
paths: paths,
nocheck: nocheck,
isOwnPadRemoved: isOwnPadRemoved
}
}, cb);
}
exp.deleteMultiplePermanently(paths, nocheck, isOwnPadRemoved);
if (typeof cb === "function") { cb(); }
cb = cb || function () {};
exp.deleteMultiplePermanently(paths, nocheck, cb);
//if (typeof cb === "function") { cb(); }
};
exp.emptyTrash = function (cb) {
cb = cb || function () {};
if (sframeChan) {
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
cmd: "emptyTrash"
}, cb);
}
files[TRASH] = {};
exp.checkDeletedFiles();
if(cb) { cb(); }
exp.checkDeletedFiles(cb);
};
// RENAME
@@ -605,7 +623,7 @@ define([
var element = find(path);
// Folders
if (isFolder(element)) {
if (isFolder(element) && !isSharedFolder(element)) {
var parentPath = path.slice();
var oldName = parentPath.pop();
if (!newName || !newName.trim() || oldName === newName) { return; }
@@ -620,8 +638,13 @@ define([
return;
}
// Files
var data = files[FILES_DATA][element];
// Files or Shared folder
var data;
if (isSharedFolder(element)) {
data = files[SHARED_FOLDERS][element];
} else {
data = files[FILES_DATA][element];
}
if (!data) { return; }
if (!newName || newName.trim() === "") {
delete data.filename;

View File

@@ -1,16 +1,14 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/avatar.less';
@import (once) '../../customize/src/less2/include/framework.less';
.framework_min_main(
@bg-color: @colortheme_friends-bg,
@warn-color: @colortheme_friends-warn,
@color: @colortheme_friends-color
);
@import (reference) '../../customize/src/less2/include/avatar.less';
@import (reference) '../../customize/src/less2/include/framework.less';
// body
&.cp-app-contacts {
.framework_min_main(
@bg-color: @colortheme_friends-bg,
@warn-color: @colortheme_friends-warn,
@color: @colortheme_friends-color
);
@keyframes example {
0% {
background: rgba(0,0,0,0.1);

View File

@@ -12,7 +12,7 @@ define([
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'less!/contacts/app-contacts.less',
], function (
$,
Crypto,

View File

@@ -247,7 +247,7 @@ define([
// insert a newline if they're holding either
var val = this.value;
var start = this.selectionState;
var start = this.selectionStart;
var end = this.selectionEnd;
if (![start,end].some(function (x) {

View File

@@ -1,14 +1,11 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/tools.less';
@import (once) '../../customize/src/less2/include/tokenfield.less';
@import (once) '../../customize/src/less2/include/framework.less';
.tokenfield_main();
.framework_min_main();
@import (reference) '../../customize/src/less2/include/tokenfield.less';
@import (reference) '../../customize/src/less2/include/framework.less';
// body
&.cp-app-debug {
.tokenfield_main();
.framework_min_main();
display: flex;
flex-flow: column;
height: 100%;

View File

@@ -16,7 +16,7 @@ define([
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'less!/debug/app-debug.less',
], function (
$,
Crypto,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -37,10 +37,25 @@ define([
window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) {
var getSecrets = function (Cryptpad, Utils, cb) {
var hash = window.location.hash.slice(1) || Utils.LocalStore.getUserHash() ||
Utils.LocalStore.getFSHash();
var hash = window.location.hash.slice(1);
var secret = Utils.Hash.getSecrets('drive', hash);
if (hash) {
// Add a shared folder!
// XXX password?
Cryptpad.addSharedFolder(secret, function (id) {
window.CryptPad_newSharedFolder = id;
// Update the hash in the address bar
var ohc = window.onhashchange;
window.onhashchange = function () {};
window.location.hash = "";
window.onhashchange = ohc;
ohc({reset:true});
cb(null, secret);
});
return;
}
// No password for drive
cb(null, Utils.Hash.getSecrets('drive', hash));
cb(null, secret);
};
var addRpc = function (sframeChan, Cryptpad, Utils) {
sframeChan.on('EV_BURN_ANON_DRIVE', function () {
@@ -52,7 +67,16 @@ define([
sframeChan.on('Q_DRIVE_USEROBJECT', function (data, cb) {
Cryptpad.userObjectCommand(data, cb);
});
sframeChan.on('Q_DRIVE_RESTORE', function (data, cb) {
Cryptpad.restoreDrive(data, cb);
});
sframeChan.on('Q_DRIVE_GETOBJECT', function (data, cb) {
if (data && data.sharedFolder) {
Cryptpad.getSharedFolder(data.sharedFolder, function (obj) {
cb(obj);
});
return;
}
Cryptpad.getUserObject(function (obj) {
cb(obj);
});
@@ -82,8 +106,10 @@ define([
SFCommonO.start({
getSecrets: getSecrets,
noHash: true,
noRealtime: true,
driveEvents: true,
addRpc: addRpc
addRpc: addRpc,
isDrive: true,
});
});
});

View File

@@ -70,7 +70,7 @@ define([
module.test = function (assert) {
var config = {
pinPads: Cryptpad.pinPads,
outer: true,
workgroup: false,
testMode: true,
loggedIn: false
@@ -237,7 +237,8 @@ define([
&& typeof files.template[0] === "number"
&& typeof files.filesData[files.template[0]] === "object"
&& !files.filesData[files.template[0]].filename
&& files.filesData[files.template[0]].href === href3
&& !files.filesData[files.template[0]].href
&& files.filesData[files.template[0]].roHref === href3
&& typeof fileId2 === "number"
&& typeof files.filesData[fileId2] === "object"
&& files.filesData[fileId2].filename === "Trash"
@@ -392,11 +393,6 @@ define([
console.log("DRIVE operations: rename");
return cb();
}
fo.replace(href1, href2);
if (fo.getFileData(id1).href !== href2) {
console.log("DRIVE operations: replace");
return cb();
}
cb(true);
}, "DRIVE operations");

View File

@@ -1,151 +1,152 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/tokenfield.less';
@import (once) '../../customize/src/less2/include/framework.less';
@import (reference) '../../customize/src/less2/include/tokenfield.less';
@import (reference) '../../customize/src/less2/include/framework.less';
.framework_min_main(
@bg-color: @colortheme_file-bg,
@warn-color: @colortheme_file-warn,
@color: @colortheme_file-color
);
.tokenfield_main();
&.cp-app-file {
@button-border: 2px;
.framework_min_main(
@bg-color: @colortheme_file-bg,
@warn-color: @colortheme_file-warn,
@color: @colortheme_file-color
);
.tokenfield_main();
// body
display: flex;
flex-flow: column;
@button-border: 2px;
#cp-app-file-content {
flex: 1;
// body
display: flex;
justify-content: center;
align-items: center;
flex-flow: column;
min-height: 0;
}
#cp-app-file-content.ready {
//background: url('/customize/bg3.jpg') no-repeat center center;
background-size: cover;
background-position: center;
}
#cp-app-file-upfile, #cp-app-file-dlfile {
display: block;
height: 100%;
width: 100%;
border: @button-border solid black;
}
.cp-app-file-input {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
media-tag {
img {
max-width: 100%;
max-height: ~"calc(100vh - 96px)";
}
}
#cp-app-file-upload-form, #cp-app-file-download-form {
padding: 0px;
margin: 0px;
position: relative;
width: 50vh;
height: 50vh;
display: block;
margin: 50px auto;
max-width: 80vw;
label {
line-height: ~"calc(50vh - 20px)";
text-align: center;
position: relative;
padding: 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
height: 50vh;
box-sizing: border-box;
}
}
#cp-app-file-download-form {
label {
display: flex;
justify-content: center;
align-items: center;
white-space: normal;
word-wrap: break-word;
span {
width: 50vh;
max-width: 80vw;
text-align: center;
line-height: 1.5em;
}
}
}
.cp-app-file-hovering {
background-color: rgba(255, 0, 115, 0.5) !important;
}
.cp-app-file-block {
display: block;
}
.cp-app-file-hidden {
display: none;
}
.cp-app-file-input + label {
//border: 2px solid black;
//background-color: rgba(50, 50, 50, .10);
display: block;
}
.cp-app-file-input:focus + label,
.cp-app-file-input + label:hover {
//background-color: rgba(50, 50, 50, 0.30);
}
#cp-app-file-dlprogress {
position: absolute;
top: 0;
left: 0;
height: 100%;
transition: width 200ms;
width: 0%;
max-width: 100%;
max-height: 100%;
background-color: rgba(255, 0, 115, 0.75);
z-index: 10000;
display: block;
}
#cp-app-file-download-view {
flex: 1;
display: flex;
min-height: 0;
align-items: center;
justify-content: center;
flex-flow: column;
media-tag {
#cp-app-file-content {
flex: 1;
min-height: 0;
max-width: 100vw;
display: flex;
align-items: center;
justify-content: center;
&> * {
max-height: 100%;
align-items: center;
flex-flow: column;
min-height: 0;
}
#cp-app-file-content.ready {
//background: url('/customize/bg3.jpg') no-repeat center center;
background-size: cover;
background-position: center;
}
#cp-app-file-upfile, #cp-app-file-dlfile {
display: block;
height: 100%;
width: 100%;
border: @button-border solid black;
}
.cp-app-file-input {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
media-tag {
img {
max-width: 100%;
max-height: ~"calc(100vh - 96px)";
}
}
}
#cp-app-file-upload-form, #cp-app-file-download-form {
padding: 0px;
margin: 0px;
position: relative;
width: 50vh;
height: 50vh;
display: block;
margin: 50px auto;
max-width: 80vw;
label {
line-height: ~"calc(50vh - 20px)";
text-align: center;
position: relative;
padding: 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
height: 50vh;
box-sizing: border-box;
}
}
#cp-app-file-download-form {
label {
display: flex;
justify-content: center;
align-items: center;
white-space: normal;
word-wrap: break-word;
span {
width: 50vh;
max-width: 80vw;
text-align: center;
line-height: 1.5em;
}
}
}
.cp-app-file-hovering {
background-color: rgba(255, 0, 115, 0.5) !important;
}
.cp-app-file-block {
display: block;
}
.cp-app-file-hidden {
display: none;
}
.cp-app-file-input + label {
//border: 2px solid black;
//background-color: rgba(50, 50, 50, .10);
display: block;
}
.cp-app-file-input:focus + label,
.cp-app-file-input + label:hover {
//background-color: rgba(50, 50, 50, 0.30);
}
#cp-app-file-dlprogress {
position: absolute;
top: 0;
left: 0;
height: 100%;
transition: width 200ms;
width: 0%;
max-width: 100%;
max-height: 100%;
background-color: rgba(255, 0, 115, 0.75);
z-index: 10000;
display: block;
}
#cp-app-file-download-view {
flex: 1;
display: flex;
min-height: 0;
align-items: center;
justify-content: center;
flex-flow: column;
media-tag {
flex: 1;
min-height: 0;
max-width: 100vw;
display: flex;
align-items: center;
justify-content: center;
&> * {
max-height: 100%;
max-width: 100%;
}
}
}
}

View File

@@ -36,7 +36,7 @@ define([
var increment = function (N) {
var l = N.length;
while (l-- > 1) {
/* jshint probably suspects this is unsafe because we lack types
/* our linter suspects this is unsafe because we lack types
but as long as this is only used on nonces, it should be safe */
if (N[l] !== 255) { return void N[l]++; } // jshint ignore:line
if (l === 0) { throw new Error('E_NONCE_TOO_LARGE'); }

View File

@@ -17,7 +17,7 @@ define([
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'less!/file/app-file.less',
], function (
$,

View File

@@ -1,83 +1,85 @@
@import (once) '../../customize/src/less2/include/colortheme-all.less';
@import (once) '../../customize/src/less2/include/modal.less';
@import (once) '../../customize/src/less2/include/icon-colors.less';
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/tippy.less';
@import (once) '../../customize/src/less2/include/checkmark.less';
@import (once) '../../customize/src/less2/include/password-input.less';
@import (reference) '../../customize/src/less2/include/colortheme-all.less';
@import (reference) '../../customize/src/less2/include/modal.less';
@import (reference) '../../customize/src/less2/include/icon-colors.less';
@import (reference) '../../customize/src/less2/include/fileupload.less';
@import (reference) '../../customize/src/less2/include/alertify.less';
@import (reference) '../../customize/src/less2/include/tippy.less';
@import (reference) '../../customize/src/less2/include/checkmark.less';
@import (reference) '../../customize/src/less2/include/password-input.less';
.iconColors_main();
.fileupload_main();
.alertify_main();
.tippy_main();
.checkmark_main(20px);
.password_main();
&.cp-app-filepicker {
.iconColors_main();
.fileupload_main();
.alertify_main();
.tippy_main();
.checkmark_main(20px);
.password_main();
.modal_main();
#cp-filepicker-dialog {
display: none;
.cp-modal {
.cp-filepicker-content {
display: flex;
flex-wrap: wrap;
justify-content: center;
overflow-y: auto;
}
.cp-filepicker-content-element {
@darker: darken(@colortheme_modal-fg, 30%);
width: 125px;
//min-width: 200px;
//height: 1em;
padding: 10px;
margin: 5px;
display: inline-flex;
flex-flow: column;
box-sizing: content-box;
text-align: left;
line-height: 1em;
cursor: pointer;
background-color: @colortheme_modal-bg;
box-shadow: 2px 2px 5px #000;
color: @darker;
transition: all 0.1s;
&:hover {
color: @colortheme_modal-fg;
#cp-filepicker-dialog {
display: none;
.cp-modal {
.cp-filepicker-content {
display: flex;
flex-wrap: wrap;
justify-content: center;
overflow-y: auto;
}
align-items: center;
.cp-filepicker-content-element {
@darker: darken(@colortheme_modal-fg, 30%);
img {
max-width: 100px;
max-height: 100px;
background: #fff;
}
width: 125px;
//min-width: 200px;
//height: 1em;
padding: 10px;
margin: 5px;
.cp-filepicker-content-element-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 20px;
line-height: 20px;
margin-top: 5px;
max-width: 100%;
}
.fa {
display: inline-flex;
flex-flow: column;
box-sizing: content-box;
text-align: left;
line-height: 1em;
cursor: pointer;
width: 100px;
height: 100px;
font-size: 70px;
text-align: center;
line-height: 100px;
background-color: @colortheme_modal-bg;
box-shadow: 2px 2px 5px #000;
color: @darker;
transition: all 0.1s;
&:hover {
color: @colortheme_modal-fg;
}
align-items: center;
img {
max-width: 100px;
max-height: 100px;
background: #fff;
}
.cp-filepicker-content-element-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 20px;
line-height: 20px;
margin-top: 5px;
max-width: 100%;
}
.fa {
cursor: pointer;
width: 100px;
height: 100px;
font-size: 70px;
text-align: center;
line-height: 100px;
}
}
}
}
}
}

View File

@@ -12,7 +12,7 @@ define([
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'less!/filepicker/app-filepicker.less',
], function (
$,
Crypto,

View File

@@ -1,13 +1,15 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/framework.less";
@import (once) "../../customize/src/less2/include/tools.less";
.framework_main( @bg-color: @colortheme_kanban-bg,
@warn-color: @colortheme_kanban-warn,
@color: @colortheme_kanban-color);
@import (reference) "../../customize/src/less2/include/browser.less";
@import (reference) "../../customize/src/less2/include/framework.less";
@import (reference) "../../customize/src/less2/include/tools.less";
// body
&.cp-app-kanban {
.framework_main(
@bg-color: @colortheme_kanban-bg,
@warn-color: @colortheme_kanban-warn,
@color: @colortheme_kanban-color
);
display: flex;
flex-flow: column;
max-height: 100%;

View File

@@ -12,6 +12,8 @@ define([
'/kanban/jkanban.js',
'/kanban/jscolor.js',
'css!/kanban/jkanban.css',
'less!/kanban/app-kanban.less'
], function (
$,
Sortify,

View File

@@ -1,15 +1,12 @@
@import (once) "../../customize/src/less2/include/framework.less";
@import (reference) "../../customize/src/less2/include/framework.less";
.framework_main(
@bg-color: @colortheme_pad-bg,
@warn-color: @colortheme_pad-warn,
@color: @colortheme_pad-color
);
.alertify_main();
body.cp-app-pad {
.framework_main(
@bg-color: @colortheme_pad-bg,
@warn-color: @colortheme_pad-warn,
@color: @colortheme_pad-color
);
// body
&.cp-app-pad {
.tokenfield_main();
#cke_1_top {
overflow: visible;
padding: 0px;
@@ -46,17 +43,16 @@
}
}
}
.cke_wysiwyg_frame {
min-width: 60%;
}
@media print {
#cke_1_top {
display:none !important;
.cke_wysiwyg_frame {
min-width: 60%;
}
&.cp-app-pad .cp-toolbar-userlist-drawer {
display:none;
@media print {
#cke_1_top {
display:none !important;
}
&.cp-app-pad .cp-toolbar-userlist-drawer {
display:none;
}
}
}
}

40
www/pad/disable-base64.js Normal file
View File

@@ -0,0 +1,40 @@
( function() {
CKEDITOR.plugins.add('blockbase64', {
init: function (editor) {
var replaceImgText = function (html) {
var ret = html.replace( /<img[^>]*src="data:image\/(bmp|dds|gif|jpg|jpeg|png|psd|pspimage|tga|thm|tif|tiff|yuv|ai|eps|ps|svg);base64,.*?"[^>]*>/gi,
function () {
console.error("Direct image paste is not allowed.");
return '';
});
return ret;
};
/*var chkImg = function () {
// don't execute code if the editor is readOnly
if (editor.readOnly) {
return;
}
setTimeout( function() {
editor.document.$.body.innerHTML = replaceImgText(editor.document.$.body.innerHTML);
},100);
};
editor.on('contentDom', function () {
// For Firefox
editor.document.on('drop', chkImg);
// For IE
editor.document.getBody().on('drop', chkImg);
});*/
editor.on('paste', function(e) {
var html = e.data.dataValue;
if (!html) { return; }
e.data.dataValue = replaceImgText(html);
});
}
});
})();

View File

@@ -32,12 +32,13 @@ define([
'/common/common-util.js',
'/bower_components/chainpad/chainpad.dist.js',
'/customize/application_config.js',
'/common/test.js',
'/bower_components/diff-dom/diffDOM.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'less!/pad/app-pad.less'
], function (
$,
Hyperjson,
@@ -52,7 +53,9 @@ define([
Hash,
Util,
ChainPad,
AppConfig)
AppConfig,
Test
)
{
var DiffDom = window.diffDOM;
@@ -160,7 +163,14 @@ define([
];
var getHTML = function (inner) {
return ('<!DOCTYPE html>\n' + '<html>\n' + inner.innerHTML);
return ('<!DOCTYPE html>\n' + '<html>\n' +
' <head><meta charset="utf-8"></head>\n <body>' +
inner.innerHTML.replace(/<img[^>]*class="cke_anchor"[^>]*data-cke-realelement="([^"]*)"[^>]*>/g,
function(match,realElt){
//console.log("returning realElt \"" + unescape(realElt)+ "\".");
return decodeURIComponent(realElt); }) +
' </body>\n</html>'
);
};
var CKEDITOR_CHECK_INTERVAL = 100;
@@ -730,6 +740,7 @@ define([
'.cke_body_width body > *:first-child { margin-top: 0; }';
Ckeditor.addCss(newCss);
Ckeditor.plugins.addExternal('mediatag','/pad/', 'mediatag-plugin.js');
Ckeditor.plugins.addExternal('blockbase64','/pad/', 'disable-base64.js');
module.ckeditor = editor = Ckeditor.replace('editor1', {
customConfig: '/customize/ckeditor-config.js',
});
@@ -753,6 +764,79 @@ define([
}).nThen(waitFor());
}).nThen(function (/*waitFor*/) {
function launchAnchorTest(test) {
// -------- anchor test: make sure the exported anchor contains <a name="..."> -------
console.log('---- anchor test: make sure the exported anchor contains <a name="..."> -----.');
function tryAndTestExport() {
console.log("Starting tryAndTestExport.");
editor.on( 'dialogShow', function( evt ) {
console.log("Anchor dialog detected.");
var dialog = evt.data;
$(dialog.parts.contents.$).find("input").val('xx-' + Math.round(Math.random()*1000));
dialog.click(window.CKEDITOR.dialog.okButton(editor).id);
} );
var existingText = editor.getData();
editor.insertText("A bit of text");
console.log("Launching anchor command.");
editor.execCommand(editor.ui.get('Anchor').command);
console.log("Anchor command launched.");
var waitH = window.setInterval(function() {
console.log("Waited 2s for the dialog to appear");
var anchors = window.CKEDITOR.plugins["link"].getEditorAnchors(editor);
if(!anchors || anchors.length===0) {
test.fail("No anchors found. Please adjust document");
} else {
console.log(anchors.length + " anchors found.");
var exported = getHTML(window.inner);
console.log("Obtained exported: " + exported);
var allFound = true;
for(var i=0; i<anchors.length; i++) {
var anchor = anchors[i];
console.log("Anchor " + anchor.name);
var expected = "<a id=\"" + anchor.id + "\" name=\"" + anchor.name + "\" ";
var found = exported.indexOf(expected)>=0;
console.log("Found " + expected + " " + found + ".");
allFound = allFound && found;
}
console.log("Cleaning up.");
if(allFound) {
// clean-up
editor.execCommand('undo');
editor.execCommand('undo');
var nint = window.setInterval(function(){
console.log("Waiting for undo to yield same result.");
if(existingText === editor.getData()) {
window.clearInterval(nint);
test.pass();
}
}, 500);
} else
{
test.fail("Not all expected a elements found for document at " + window.top.location + ".");
}
}
window.clearInterval(waitH);
},2000);
}
var intervalHandle = window.setInterval(function() {
if(editor.status==="ready") {
window.clearInterval(intervalHandle);
console.log("Editor is ready.");
tryAndTestExport();
} else {
console.log("Waiting for editor to be ready.");
}
}, 100);
}
Test(function(test) {
launchAnchorTest(test);
});
andThen2(editor, Ckeditor, framework);
});
};

View File

@@ -1,12 +1,3 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview The Image plugin.
*/
( function() {
CKEDITOR.plugins.add( 'mediatag', {

File diff suppressed because it is too large Load Diff

View File

@@ -26,7 +26,7 @@ define([
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'less!/poll/app-poll.less',
], function (
$,
Toolbar,

View File

@@ -1,19 +1,15 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/framework.less';
@import (reference) '../../customize/src/less2/include/framework.less';
@import (reference) '../../customize/src/less2/include/sidebar-layout.less';
@import (once) '../../customize/src/less2/include/avatar.less';
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
.framework_min_main(
@bg-color: @colortheme_profile-bg,
@warn-color: @colortheme_profile-warn,
@color: @colortheme_profile-color
);
.sidebar-layout_main();
// body
&.cp-app-profile {
.framework_min_main(
@bg-color: @colortheme_profile-bg,
@warn-color: @colortheme_profile-warn,
@color: @colortheme_profile-color
);
.sidebar-layout_main();
display: flex;
flex-flow: column;
#cp-app-profile-header {

View File

@@ -20,7 +20,7 @@ define([
'css!/bower_components/codemirror/addon/fold/foldgutter.css',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'less!/profile/app-profile.less',
'/bower_components/croppie/croppie.min.js',
'css!/bower_components/croppie/croppie.css',
], function (

View File

@@ -1,21 +1,18 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
@import (once) "../../customize/src/less2/include/limit-bar.less";
@import (once) "../../customize/src/less2/include/creation.less";
@import (once) '../../customize/src/less2/include/framework.less';
@import (reference) '../../customize/src/less2/include/sidebar-layout.less';
@import (reference) "../../customize/src/less2/include/limit-bar.less";
@import (reference) "../../customize/src/less2/include/creation.less";
@import (reference) '../../customize/src/less2/include/framework.less';
.framework_min_main(
@bg-color: @colortheme_settings-bg,
@warn-color: @colortheme_settings-warn,
@color: @colortheme_settings-color
);
.sidebar-layout_main();
.limit-bar_main();
.creation_main();
// body
&.cp-app-settings {
.framework_min_main(
@bg-color: @colortheme_settings-bg,
@warn-color: @colortheme_settings-warn,
@color: @colortheme_settings-color
);
.sidebar-layout_main();
.limit-bar_main();
.creation_main();
display: flex;
flex-flow: column;
font: @colortheme_app-font;

View File

@@ -16,7 +16,7 @@ define([
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'less!/settings/app-settings.less',
], function (
$,
Toolbar,
@@ -428,6 +428,10 @@ define([
return;
}
if (oldPassword === newPassword) {
return void UI.alert(Messages.settings_changePasswordNewPasswordSameAsOld);
}
UI.confirm(Messages.settings_changePasswordConfirm,
function (yes) {
if (!yes) { return; }

View File

@@ -1,417 +1,420 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) "../../customize/src/less2/include/mediatag.less";
@import (once) "../../customize/src/less2/include/framework.less";
@import (reference) "../../customize/src/less2/include/markdown.less";
@import (reference) "../../customize/src/less2/include/mediatag.less";
@import (reference) "../../customize/src/less2/include/framework.less";
.mediatag_base();
.framework_main(
@bg-color: @colortheme_slide-bg,
@warn-color: @colortheme_slide-warn,
@color: @colortheme_slide-color
);
&.cp-app-slide {
// body
font-size: unset;
display: flex;
flex-flow: column;
.mediatag_base();
@slide-default-bg: #000;
.size (@n) {
// font-size: @n * 1vmin;
// line-height: @n * 1.1vmin;
font-size: @n * 10%;
// line-height: @n * 11%;
line-height: 110%;
}
.framework_main(
@bg-color: @colortheme_slide-bg,
@warn-color: @colortheme_slide-warn,
@color: @colortheme_slide-color
);
h1 { font-size: 40px; }
h2 { font-size: 37px; }
h3 { font-size: 34px; }
h4 { font-size: 31px; }
h5 { font-size: 27px; }
h6 { font-size: 24px; }
#cp-app-slide-editor-container {
display: inline-flex;
flex-flow: column;
height: 100%;
min-height: 100%;
width: 50%;
min-width: 20%;
max-width: 80%;
overflow: hidden;
max-width: 100%;
flex: 1;
}
.CodeMirror {
flex: 1;
width: 100%;
font-size: initial;
}
.CodeMirror-focused .cm-matchhighlight {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
background-position: bottom;
background-repeat: repeat-x;
}
#cp-app-slide-colorpicker {
display: block;
}
#cme_toolbox {
z-index: 10000;
}
#cp-app-slide-editor {
flex: 1;
// body
font-size: unset;
display: flex;
flex-flow: row;
height: 100%;
overflow: hidden;
.CodeMirror {
resize: none;
//width: 100vw;
flex-flow: column;
@slide-default-bg: #000;
.size (@n) {
// font-size: @n * 1vmin;
// line-height: @n * 1.1vmin;
font-size: @n * 10%;
// line-height: @n * 11%;
line-height: 110%;
}
&.cp-app-slide-preview {
h1 { font-size: 40px; }
h2 { font-size: 37px; }
h3 { font-size: 34px; }
h4 { font-size: 31px; }
h5 { font-size: 27px; }
h6 { font-size: 24px; }
#cp-app-slide-editor-container {
display: inline-flex;
flex-flow: column;
height: 100%;
min-height: 100%;
width: 50%;
min-width: 20%;
max-width: 80%;
overflow: hidden;
max-width: 100%;
flex: 1;
}
.CodeMirror {
flex: 1;
width: 100%;
font-size: initial;
}
.CodeMirror-focused .cm-matchhighlight {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
background-position: bottom;
background-repeat: repeat-x;
}
#cp-app-slide-colorpicker {
display: block;
}
#cme_toolbox {
z-index: 10000;
}
#cp-app-slide-editor {
flex: 1;
display: flex;
flex-flow: row;
height: 100%;
overflow: hidden;
.CodeMirror {
//resize: horizontal;
//width: 50vw;
resize: none;
//width: 100vw;
}
&.cp-app-slide-preview {
.CodeMirror {
//resize: horizontal;
//width: 50vw;
}
}
}
}
.cp-app-slide-shown {
.cp-app-slide-container {
.cp-app-slide-shown {
.cp-app-slide-container {
position: relative;
&> media-tag {
position: absolute;
top:0; right: 0; bottom: 0; left: 0;
z-index: -1;
&> img {
width: 100vw;
height: 56.25vw; // height:width ratio = 9/16 = .5625
max-height: 100vh;
max-width: 177.78vh; // 16/9 = 1.778
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
}
}
}
.cp-app-slide-preview {
.cp-app-slide-viewer {
width: 50vw;
overflow: hidden;
div#cp-app-slide-modal:not(.cp-app-slide-shown) {
position: relative;
top: auto;
left: auto;
width: auto;
display: block;
height: 100%;
#cp-app-slide-modal-content {
.cp-app-slide-container {
position: relative;
&> media-tag {
position: absolute;
top:0; right: 0; bottom: 0; left: 0;
z-index: -1;
&> img {
width: 50vw;
height: 28.125vw;
max-height: ~"calc(100vh - 96px)";
max-width: ~"calc(16 / 9 * (100vh - 96px))";
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
}
}
.cp-app-slide-container {
width: 100%;
}
.cp-app-slide-frame {
width: 50vw;
height: 28.125vw; // height:width ratio = 9/16 = .5625
max-height: ~"calc(100vh - 96px)";
max-width: ~"calc(16 / 9 * (100vh - 96px))";
//max-height: 100vh;
//max-width: 177.78vh; // 16/9 = 1.778
}
}
#cp-app-slide-modal-exit {
visibility: hidden;
}
}
}
.CodeMirror {
flex: 1;
}
}
/* Slide position (print mode) */
@ratio:0.9;
@media print {
#cp-app-slide-editor-container {
display: none;
}
}
#cp-app-slide-print {
position: relative;
&> media-tag {
display: none;
font-size: @ratio*11.25vw;
.cp-app-slide-frame {
display: flex !important;
flex-flow: column;
padding: 0.5em;
margin: auto;
border: 1px solid black;
height: @ratio*56.25vw;
width: @ratio*100vw;
page-break-after: always;
position: relative;
box-sizing: border-box;
overflow: hidden;
li {
min-width: @ratio*50vw;
}
h1 {
padding-top: 0;
}
}
.cp-app-slide-container {
position: relative;
&> media-tag {
position: absolute;
top:0; right: 0; bottom: 0; left: 0;
z-index: -1;
&> img {
width: 90vw;
height: 50.625vw;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
}
}
.cp-app-slide-container {
width: 90vw;
height: 100vh;
margin: 0vh 5vw !important;
display: flex;
&:last-child {
height: ~"calc(100vh - 2px)";
}
}
}
/* Slide position (present mode) */
div#cp-app-slide-modal {
display: none;
background-color: black;
color: white;
.cp-app-slide-isempty {
display: flex;
align-items: center;
justify-content: center;
img {
max-width: 100%;
max-height: 100%;
}
}
/* Navigation buttons */
.cp-app-slide-modal-button {
position: absolute;
top:0; right: 0; bottom: 0; left: 0;
z-index: -1;
&> img {
cursor: pointer;
font-size: 30px;
opacity: 0.6;
display: none;
z-index: 9001;
}
.cp-app-slide-modal-button:hover {
opacity: 1;
display: block !important;
}
#cp-app-slide-modal-exit {
left: 20px;
top: 20px;
}
#cp-app-slide-modal-left {
left: 6vw;
bottom: 10vh;
}
#cp-app-slide-modal-right {
right: 6vw;
bottom: 10vh;
}
&.cp-app-slide-shown {
display: block;
position: fixed;
top: 0px;
left: 0px;
z-index: 1000000; // one order of magnitude higher than alertify
height: 100vh;
width: 100%;
}
#cp-app-slide-modal-content {
font-size: 20vh;
position: relative;
height: 100%;
overflow: visible;
white-space: nowrap;
.cp-app-slide-frame {
overflow: hidden;
display: flex;
flex-flow: column !important;
box-sizing: border-box;
border: 1px solid;
white-space: normal;
vertical-align: middle;
padding: 0.5em;
width: 100vw;
height: 56.25vw; // height:width ratio = 9/16 = .5625
max-height: 100vh;
max-width: 177.78vh; // 16/9 = 1.778
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
}
}
}
.cp-app-slide-preview {
.cp-app-slide-viewer {
width: 50vw;
overflow: hidden;
div#cp-app-slide-modal:not(.cp-app-slide-shown) {
position: relative;
top: auto;
left: auto;
width: auto;
display: block;
height: 100%;
#cp-app-slide-modal-content {
.cp-app-slide-container {
display: inline-flex;
height: 100%; width: 100vw;
text-align: center;
vertical-align: top;
}
&.cp-app-slide-transition {
.cp-app-slide-container {
position: relative;
&> media-tag {
position: absolute;
top:0; right: 0; bottom: 0; left: 0;
z-index: -1;
&> img {
width: 50vw;
height: 28.125vw;
max-height: ~"calc(100vh - 96px)";
max-width: ~"calc(16 / 9 * (100vh - 96px))";
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
}
}
.cp-app-slide-container {
width: 100%;
}
.cp-app-slide-frame {
width: 50vw;
height: 28.125vw; // height:width ratio = 9/16 = .5625
max-height: ~"calc(100vh - 96px)";
max-width: ~"calc(16 / 9 * (100vh - 96px))";
//max-height: 100vh;
//max-width: 177.78vh; // 16/9 = 1.778
transition: margin-left 1s;
}
}
#cp-app-slide-modal-exit {
visibility: hidden;
media-tag button {
max-height: none;
}
}
}
.CodeMirror {
flex: 1;
}
}
/* Slide position (print mode) */
@ratio:0.9;
@media print {
#cp-app-slide-editor-container {
display: none;
}
}
#cp-app-slide-print {
position: relative;
display: none;
font-size: @ratio*11.25vw;
.cp-app-slide-frame {
display: flex !important;
flex-flow: column;
padding: 0.5em;
margin: auto;
border: 1px solid black;
height: @ratio*56.25vw;
width: @ratio*100vw;
page-break-after: always;
position: relative;
box-sizing: border-box;
overflow: hidden;
li {
min-width: @ratio*50vw;
}
h1 {
padding-top: 0;
}
}
.cp-app-slide-container {
position: relative;
&> media-tag {
position: absolute;
top:0; right: 0; bottom: 0; left: 0;
z-index: -1;
&> img {
width: 90vw;
height: 50.625vw;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
}
}
.cp-app-slide-container {
width: 90vw;
height: 100vh;
margin: 0vh 5vw !important;
display: flex;
&:last-child {
height: ~"calc(100vh - 2px)";
}
}
}
/* Slide position (present mode) */
div#cp-app-slide-modal {
display: none;
background-color: black;
color: white;
.cp-app-slide-isempty {
display: flex;
align-items: center;
justify-content: center;
img {
max-width: 100%;
max-height: 100%;
}
}
/* Navigation buttons */
.cp-app-slide-modal-button {
position: absolute;
cursor: pointer;
font-size: 30px;
opacity: 0.6;
display: none;
z-index: 9001;
}
.cp-app-slide-modal-button:hover {
opacity: 1;
display: block !important;
}
#cp-app-slide-modal-exit {
left: 20px;
top: 20px;
}
#cp-app-slide-modal-left {
left: 6vw;
bottom: 10vh;
}
#cp-app-slide-modal-right {
right: 6vw;
bottom: 10vh;
}
&.cp-app-slide-shown {
display: block;
position: fixed;
top: 0px;
left: 0px;
z-index: 1000000; // one order of magnitude higher than alertify
height: 100vh;
width: 100%;
height: 100vh;
display: none;
background-color: @slide-default-bg;
}
#cp-app-slide-modal-content {
font-size: 20vh;
position: relative;
height: 100%;
overflow: visible;
white-space: nowrap;
/* Slide content */
div#cp-app-slide-modal #cp-app-slide-modal-content, #cp-app-slide-print {
.cp-app-slide-frame {
overflow: hidden;
display: flex;
flex-flow: column !important;
box-sizing: border-box;
border: 1px solid;
white-space: normal;
vertical-align: middle;
padding: 0.5em;
width: 100vw;
height: 56.25vw; // height:width ratio = 9/16 = .5625
max-height: 100vh;
max-width: 177.78vh; // 16/9 = 1.778
margin: auto;
}
.cp-app-slide-container {
display: inline-flex;
height: 100%; width: 100vw;
text-align: center;
vertical-align: top;
}
&.cp-app-slide-transition {
.cp-app-slide-container {
transition: margin-left 1s;
}
}
media-tag button {
max-height: none;
}
}
box-sizing: border-box;
z-index: 9001;
position: fixed;
top: 0px;
left: 0px;
width: 100%;
height: 100vh;
display: none;
background-color: @slide-default-bg;
}
/* Slide content */
div#cp-app-slide-modal #cp-app-slide-modal-content, #cp-app-slide-print {
.cp-app-slide-frame {
* {
.size(2.75);
* {
font-size: 1em;
line-height: 1em;
.size(2.75);
* {
font-size: 1em;
line-height: 1em;
}
}
}
ul, ol {
ul, ol {
margin: 0;
ul, ol {
margin: 0;
}
}
}
h1 { .size(5); }
h2 { .size(4.2); }
h3 { .size(3.6); }
h4 { .size (3); }
h5 { .size(2.2); }
h6 { .size(1.6); }
h1 { .size(5); }
h2 { .size(4.2); }
h3 { .size(3.6); }
h4 { .size (3); }
h5 { .size(2.2); }
h6 { .size(1.6); }
h1, h2, h3, h4, h5, h6 {
color: inherit;
text-align: center;
padding-top: 0;
margin-bottom: 0.5em;
}
.markdown_preformatted-code;
ul, ol {
min-width: 50%;
max-width: 100%;
display: table;
margin: 0 auto;
padding-left: 0.3em;
}
// fixes image overflowing
media-tag {
height: 100%;
flex-flow: row;
justify-content: center;
& + * {
margin-top: 1rem;
h1, h2, h3, h4, h5, h6 {
color: inherit;
text-align: center;
padding-top: 0;
margin-bottom: 0.5em;
}
img { flex: unset; }
}
img {
.markdown_preformatted-code;
ul, ol {
min-width: 50%;
max-width: 100%;
display: table;
margin: 0 auto;
padding-left: 0.3em;
}
// fixes image overflowing
media-tag {
height: 100%;
flex-flow: row;
justify-content: center;
& + * {
margin-top: 1rem;
}
img { flex: unset; }
}
img {
position: relative;
min-width: 1%;
margin: auto;
}
.cp-app-slide-number {
position: absolute;
right: 5vh;
bottom: 5vh;
.size(1);
}
.cp-app-slide-date {
position: absolute;
left: 5vh;
bottom: 5vh;
.size(1);
}
.cp-app-slide-title {
position: absolute;
bottom: 5vh;
left: 0px; right: 0px;
text-align: center;
.size(1);
}
text-align: left;
position: relative;
min-width: 1%;
margin: auto;
}
.cp-app-slide-number {
position: absolute;
right: 5vh;
bottom: 5vh;
.size(1);
}
.cp-app-slide-date {
position: absolute;
left: 5vh;
bottom: 5vh;
.size(1);
}
.cp-app-slide-title {
position: absolute;
bottom: 5vh;
left: 0px; right: 0px;
text-align: center;
.size(1);
}
text-align: left;
position: relative;
}
}
.cp-app-slide-frame * {
max-width: 100%;
max-height: 100%;
}
.cp-app-slide-frame * {
max-width: 100%;
max-height: 100%;
}
p {
padding: 0;
margin: 0;
min-height:0;
min-width:0;
}
p {
padding: 0;
margin: 0;
min-height:0;
min-width:0;
}
pre.cp-slide-css-error {
color: white;
}
pre.cp-slide-css-error {
color: white;
}
}

View File

@@ -15,7 +15,7 @@ define([
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'less!/slide/app-slide.less',
'css!cm/lib/codemirror.css',
'css!cm/addon/dialog/dialog.css',

View File

@@ -1,16 +1,13 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/avatar.less';
@import (once) '../../customize/src/less2/include/framework.less';
@import (reference) '../../customize/src/less2/include/framework.less';
.framework_min_main(
@bg-color: @colortheme_todo-bg,
@warn-color: @colortheme_todo-warn,
@color: @colortheme_todo-color
);
// body
&.cp-app-todo {
.framework_min_main(
@bg-color: @colortheme_todo-bg,
@warn-color: @colortheme_todo-warn,
@color: @colortheme_todo-color
);
display: flex;
flex-flow: column;

View File

@@ -13,7 +13,7 @@ define([
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'less!/todo/app-todo.less',
], function (
$,
Crypto,

View File

@@ -1,16 +1,14 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/tools.less';
@import (once) "../../customize/src/less2/include/framework.less";
@import (reference) '../../customize/src/less2/include/tools.less';
@import (reference) "../../customize/src/less2/include/framework.less";
.framework_main(
@bg-color: @colortheme_whiteboard-bg,
@warn-color: @colortheme_whiteboard-warn,
@color: @colortheme_whiteboard-color
);
// body
&.cp-app-whiteboard {
.framework_main(
@bg-color: @colortheme_whiteboard-bg,
@warn-color: @colortheme_whiteboard-warn,
@color: @colortheme_whiteboard-color
);
display: flex;
flex-flow: column;
height: 100%;

View File

@@ -15,6 +15,7 @@ define([
'/bower_components/chainpad/chainpad.dist.js',
'/bower_components/secure-fabric.js/dist/fabric.min.js',
'less!/whiteboard/app-whiteboard.less'
], function (
$,
Sortify,

View File

@@ -1,12 +1,9 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/avatar.less';
@import (once) '../../customize/src/less2/include/framework.less';
@import (reference) "../../customize/src/less2/include/colortheme-all.less";
@import (reference) '../../customize/src/less2/include/framework.less';
.framework_min_main();
// body
&.cp-app-worker {
.framework_min_main();
display: flex;
flex-flow: column;

View File

@@ -9,7 +9,7 @@ define([
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'less!/worker/app-worker.less',
], function (
$,
Toolbar,