Merge branch 'staging' into oo
This commit is contained in:
@@ -42,6 +42,11 @@ define(function() {
|
||||
'#800080', // purple
|
||||
];
|
||||
|
||||
// Background color in the apps with centered content:
|
||||
// - file app in view mode
|
||||
// - rich text app when editor's width reduced in settings
|
||||
config.appBackgroundColor = '#666';
|
||||
|
||||
// Set enableTemplates to false to remove the button allowing users to save a pad as a template
|
||||
// and remove the template category in CryptDrive
|
||||
config.enableTemplates = true;
|
||||
|
||||
@@ -37,6 +37,15 @@ define([
|
||||
window.alert("CryptPad needs localStorage to work, try a different browser");
|
||||
};
|
||||
|
||||
window.onerror = function (e) {
|
||||
if (/requirejs\.org/.test(e)) {
|
||||
console.log();
|
||||
console.error("Require.js threw a Script Error. This probably means you're missing a dependency for CryptPad.\nIt is recommended that the admin of this server runs `bower install && bower update` to get the latest code, then modify their cache version.\nBest of luck,\nThe CryptPad Developers");
|
||||
return void console.log();
|
||||
}
|
||||
throw e;
|
||||
};
|
||||
|
||||
try {
|
||||
var test_key = 'localStorage_test';
|
||||
var testval = Math.random().toString();
|
||||
|
||||
@@ -6,7 +6,7 @@ define([
|
||||
], function (Util, Messages, Crypto) {
|
||||
var Nacl = window.nacl;
|
||||
|
||||
var Hash = {};
|
||||
var Hash = window.CryptPad_Hash = {};
|
||||
|
||||
var uint8ArrayToHex = Util.uint8ArrayToHex;
|
||||
var hexToBase64 = Util.hexToBase64;
|
||||
@@ -176,7 +176,7 @@ Version 1
|
||||
secret.keys = Crypto.createEditCryptor();
|
||||
secret.key = Crypto.createEditCryptor().editKeyStr;
|
||||
};
|
||||
if (!secretHash && !/#/.test(window.location.href)) {
|
||||
if (!secretHash && !window.location.hash) { //!/#/.test(window.location.href)) {
|
||||
generate();
|
||||
return secret;
|
||||
} else {
|
||||
@@ -300,6 +300,8 @@ Version 1
|
||||
var rHref = href || getRelativeHref(window.location.href);
|
||||
var parsed = parsePadUrl(rHref);
|
||||
if (!parsed.hash) { return false; }
|
||||
// We can't have a stronger hash if we're already in edit mode
|
||||
if (parsed.hashData && parsed.hashData.mode === 'edit') { return; }
|
||||
var stronger;
|
||||
Object.keys(recents).some(function (id) {
|
||||
var pad = recents[id];
|
||||
|
||||
@@ -111,12 +111,14 @@ define([
|
||||
return input;
|
||||
};
|
||||
|
||||
dialog.okButton = function (content) {
|
||||
return h('button.ok.primary', { tabindex: '2', }, content || Messages.okButton);
|
||||
dialog.okButton = function (content, classString) {
|
||||
var sel = typeof(classString) === 'string'? 'button.ok.' + classString:'button.ok.primary';
|
||||
return h(sel, { tabindex: '2', }, content || Messages.okButton);
|
||||
};
|
||||
|
||||
dialog.cancelButton = function (content) {
|
||||
return h('button.cancel', { tabindex: '1'}, content || Messages.cancelButton);
|
||||
dialog.cancelButton = function (content, classString) {
|
||||
var sel = typeof(classString) === 'string'? 'button.' + classString:'button.cancel';
|
||||
return h(sel, { tabindex: '1'}, content || Messages.cancelButton);
|
||||
};
|
||||
|
||||
dialog.message = function (text) {
|
||||
@@ -315,11 +317,11 @@ define([
|
||||
message = dialog.message(msg);
|
||||
}
|
||||
|
||||
var close = Util.once(function (el) {
|
||||
var close = function (el) {
|
||||
var $el = $(el).fadeOut(150, function () {
|
||||
$el.remove();
|
||||
$el.detach();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var navs = [];
|
||||
opt.buttons.forEach(function (b) {
|
||||
@@ -327,7 +329,7 @@ define([
|
||||
var button = h('button', { tabindex: '1', 'class': b.className || '' }, b.name);
|
||||
$(button).click(function () {
|
||||
b.onClick();
|
||||
close($(this).parents('.alertify').first());
|
||||
close($(button).parents('.alertify').first());
|
||||
});
|
||||
if (b.keys && b.keys.length) { $(button).attr('data-keys', JSON.stringify(b.keys)); }
|
||||
navs.push(button);
|
||||
@@ -464,8 +466,8 @@ define([
|
||||
message = dialog.message(msg);
|
||||
}
|
||||
|
||||
var ok = dialog.okButton(opt.ok);
|
||||
var cancel = dialog.cancelButton(opt.cancel);
|
||||
var ok = dialog.okButton(opt.ok, opt.okClass);
|
||||
var cancel = dialog.cancelButton(opt.cancel, opt.cancelClass);
|
||||
|
||||
var frame = dialog.frame([
|
||||
message,
|
||||
@@ -551,6 +553,7 @@ define([
|
||||
var $loading, $container;
|
||||
if ($('#' + LOADING).length) {
|
||||
$loading = $('#' + LOADING); //.show();
|
||||
$loading.css('display', '');
|
||||
$loading.removeClass('cp-loading-hidden');
|
||||
if (loadingText) {
|
||||
$('#' + LOADING).find('p').text(loadingText);
|
||||
@@ -598,11 +601,20 @@ define([
|
||||
}, 3750);
|
||||
// jquery.fadeout can get stuck
|
||||
};
|
||||
UI.errorLoadingScreen = function (error, transparent) {
|
||||
if (!$('#' + LOADING).is(':visible')) { UI.addLoadingScreen({hideTips: true}); }
|
||||
UI.errorLoadingScreen = function (error, transparent, exitable) {
|
||||
if (!$('#' + LOADING).is(':visible') || $('#' + LOADING).hasClass('cp-loading-hidden')) {
|
||||
UI.addLoadingScreen({hideTips: true});
|
||||
}
|
||||
$('.cp-loading-spinner-container').hide();
|
||||
$('#cp-loading-tip').remove();
|
||||
if (transparent) { $('#' + LOADING).css('opacity', 0.8); }
|
||||
$('#' + LOADING).find('p').html(error || Messages.error);
|
||||
if (exitable) {
|
||||
$(window).focus();
|
||||
$(window).keydown(function (e) {
|
||||
if (e.which === 27) { $('#' + LOADING).hide(); }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var $defaultIcon = $('<span>', {"class": "fa fa-file-text-o"});
|
||||
|
||||
@@ -375,8 +375,8 @@ define([
|
||||
}
|
||||
if (val.embed) { $(link).find('#cp-share-embed').attr('checked', true); }
|
||||
if (val.present) { $(link).find('#cp-share-present').attr('checked', true); }
|
||||
UI.openCustomModal(UI.dialog.tabs(tabs));
|
||||
});
|
||||
return tabs;
|
||||
};
|
||||
UIElements.createFileShareModal = function (config) {
|
||||
var origin = config.origin;
|
||||
@@ -451,7 +451,7 @@ define([
|
||||
pathname: pathname
|
||||
});
|
||||
}
|
||||
UI.openCustomModal(UI.dialog.tabs(tabs));
|
||||
return tabs;
|
||||
};
|
||||
|
||||
UIElements.createButton = function (common, type, rightside, data, callback) {
|
||||
@@ -1655,8 +1655,6 @@ define([
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var type = metadataMgr.getMetadataLazy().type;
|
||||
|
||||
// XXX check text for pad creation screen + translate it in French
|
||||
|
||||
var $body = $('body');
|
||||
var $creationContainer = $('<div>', { id: 'cp-creation-container' }).appendTo($body);
|
||||
var $creation = $('<div>', { id: 'cp-creation' }).appendTo($creationContainer);
|
||||
@@ -1687,7 +1685,10 @@ define([
|
||||
Messages.creation_ownedTitle,
|
||||
createHelper(Messages.creation_owned1 + '\n' + Messages.creation_owned2)
|
||||
]),
|
||||
setHTML(h('p'), Messages.creation_owned1 + '<br>' + Messages.creation_owned2),
|
||||
h('div.cp-creation-help-container', [
|
||||
setHTML(h('p'), Messages.creation_owned1),
|
||||
setHTML(h('p'), Messages.creation_owned2)
|
||||
]),
|
||||
h('input#cp-creation-owned-true.cp-creation-owned-value', {
|
||||
type: 'radio',
|
||||
name: 'cp-creation-owned',
|
||||
@@ -1715,7 +1716,10 @@ define([
|
||||
Messages.creation_expireTitle,
|
||||
createHelper(Messages.creation_expire1, Messages.creation_expire2)
|
||||
]),
|
||||
setHTML(h('p'), Messages.creation_expire1 + '<br>' + Messages.creation_expire2),
|
||||
h('div.cp-creation-help-container', [
|
||||
setHTML(h('p'), Messages.creation_expire1),
|
||||
setHTML(h('p'), Messages.creation_expire2)
|
||||
]),
|
||||
h('input#cp-creation-expire-false.cp-creation-expire-value', {
|
||||
type: 'radio',
|
||||
name: 'cp-creation-expire',
|
||||
@@ -1816,6 +1820,32 @@ define([
|
||||
$button.click(function () {
|
||||
create();
|
||||
});
|
||||
|
||||
// Settings button
|
||||
var origin = common.getMetadataMgr().getPrivateData().origin;
|
||||
$(h('div.cp-creation-settings', h('a', {
|
||||
href: origin + '/settings/#creation',
|
||||
target: '_blank'
|
||||
}, Messages.creation_settings))).appendTo($creation);
|
||||
};
|
||||
|
||||
UIElements.onServerError = function (common, err, toolbar, cb) {
|
||||
if (["EDELETED", "EEXPIRED"].indexOf(err.type) === -1) { return; }
|
||||
var msg = err.type;
|
||||
if (err.type === 'EEXPIRED') {
|
||||
msg = Messages.expiredError;
|
||||
if (err.loaded) {
|
||||
msg += Messages.expiredErrorCopy;
|
||||
}
|
||||
} else if (err.type === 'EDELETED') {
|
||||
msg = Messages.deletedError;
|
||||
if (err.loaded) {
|
||||
msg += Messages.expiredErrorCopy;
|
||||
}
|
||||
}
|
||||
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
|
||||
UI.errorLoadingScreen(msg, true, true);
|
||||
(cb || function () {})();
|
||||
};
|
||||
|
||||
return UIElements;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(function (window) {
|
||||
define([], function () {
|
||||
var Util = {};
|
||||
var Util = window.CryptPad_Util = {};
|
||||
|
||||
// If once is true, after the event has been fired, any further handlers which are
|
||||
// registered will fire immediately, and this type of event cannot be fired twice.
|
||||
|
||||
@@ -195,6 +195,16 @@ define([
|
||||
common.clearOwnedChannel = function (channel, cb) {
|
||||
postMessage("CLEAR_OWNED_CHANNEL", channel, cb);
|
||||
};
|
||||
common.removeOwnedChannel = function (channel, cb) {
|
||||
postMessage("REMOVE_OWNED_CHANNEL", channel, cb);
|
||||
};
|
||||
|
||||
common.getDeletedPads = function (cb) {
|
||||
postMessage("GET_DELETED_PADS", null, function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj.error); }
|
||||
cb(null, obj);
|
||||
});
|
||||
};
|
||||
|
||||
common.uploadComplete = function (cb) {
|
||||
postMessage("UPLOAD_COMPLETE", null, function (obj) {
|
||||
@@ -528,6 +538,7 @@ define([
|
||||
pad.onJoinEvent = Util.mkEvent();
|
||||
pad.onLeaveEvent = Util.mkEvent();
|
||||
pad.onDisconnectEvent = Util.mkEvent();
|
||||
pad.onErrorEvent = Util.mkEvent();
|
||||
|
||||
common.getFullHistory = function (data, cb) {
|
||||
postMessage("GET_FULL_HISTORY", data, cb);
|
||||
@@ -550,6 +561,11 @@ define([
|
||||
return void cb(null, hashes);
|
||||
}
|
||||
|
||||
if (hashes.editHash) {
|
||||
// no need to find stronger if we already have edit hash
|
||||
return void cb(null, hashes);
|
||||
}
|
||||
|
||||
postMessage("GET_STRONGER_HASH", {
|
||||
href: window.location.href
|
||||
}, function (hash) {
|
||||
@@ -664,6 +680,9 @@ define([
|
||||
case 'PAD_DISCONNECT': {
|
||||
common.padRpc.onDisconnectEvent.fire(data); break;
|
||||
}
|
||||
case 'PAD_ERROR': {
|
||||
common.padRpc.onErrorEvent.fire(data); break;
|
||||
}
|
||||
// Drive
|
||||
case 'DRIVE_LOG': {
|
||||
common.drive.onLog.fire(data); break;
|
||||
|
||||
@@ -115,6 +115,12 @@ define(['json.sortify'], function (Sortify) {
|
||||
if (!meta.user) { return; }
|
||||
change(true);
|
||||
});
|
||||
sframeChan.on('EV_RT_ERROR', function (err) {
|
||||
if (err.type !== 'EEXPIRED' && err.type !== 'EDELETED') { return; }
|
||||
members = [];
|
||||
if (!meta.user) { return; }
|
||||
change(true);
|
||||
});
|
||||
|
||||
return Object.freeze({
|
||||
updateMetadata: function (m) {
|
||||
|
||||
@@ -94,9 +94,27 @@ define([
|
||||
return list;
|
||||
};
|
||||
|
||||
var getCanonicalChannelList = function () {
|
||||
return Util.deduplicateString(getUserChannelList()).sort();
|
||||
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(Hash.hrefToHexChannelId(data.href));
|
||||
}
|
||||
});
|
||||
return list;
|
||||
};
|
||||
|
||||
var getCanonicalChannelList = function (expirable) {
|
||||
var list = expirable ? getExpirableChannelList() : getUserChannelList();
|
||||
return Util.deduplicateString(list).sort();
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
/////////////////////// RPC //////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
@@ -172,6 +190,34 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
Store.removeOwnedChannel = function (data, cb) {
|
||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
store.rpc.removeOwnedChannel(data, function (err) {
|
||||
cb({error:err});
|
||||
});
|
||||
};
|
||||
|
||||
var arePinsSynced = function (cb) {
|
||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
|
||||
var list = getCanonicalChannelList(false);
|
||||
var local = Hash.hashChannelList(list);
|
||||
store.rpc.getServerHash(function (e, hash) {
|
||||
if (e) { return void cb(e); }
|
||||
cb(null, hash === local);
|
||||
});
|
||||
};
|
||||
|
||||
var resetPins = function (cb) {
|
||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
|
||||
var list = getCanonicalChannelList(false);
|
||||
store.rpc.reset(list, function (e, hash) {
|
||||
if (e) { return void cb(e); }
|
||||
cb(null, hash);
|
||||
});
|
||||
};
|
||||
|
||||
Store.uploadComplete = function (data, cb) {
|
||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
store.rpc.uploadComplete(function (err, res) {
|
||||
@@ -196,27 +242,6 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
var arePinsSynced = function (cb) {
|
||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
|
||||
var list = getCanonicalChannelList();
|
||||
var local = Hash.hashChannelList(list);
|
||||
store.rpc.getServerHash(function (e, hash) {
|
||||
if (e) { return void cb(e); }
|
||||
cb(null, hash === local);
|
||||
});
|
||||
};
|
||||
|
||||
var resetPins = function (cb) {
|
||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
|
||||
var list = getCanonicalChannelList();
|
||||
store.rpc.reset(list, function (e, hash) {
|
||||
if (e) { return void cb(e); }
|
||||
cb(null, hash);
|
||||
});
|
||||
};
|
||||
|
||||
Store.uploadChunk = function (data, cb) {
|
||||
store.rpc.send.unauthenticated('UPLOAD', data.chunk, function (e, msg) {
|
||||
cb({
|
||||
@@ -309,6 +334,23 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
Store.getDeletedPads = function (data, cb) {
|
||||
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
|
||||
var list = getCanonicalChannelList(true);
|
||||
if (!Array.isArray(list)) {
|
||||
return void cb({error: 'INVALID_FILE_LIST'});
|
||||
}
|
||||
|
||||
store.anon_rpc.send('GET_DELETED_PADS', list, function (e, res) {
|
||||
if (e) { return void cb({error: e}); }
|
||||
if (res && res.length && Array.isArray(res[0])) {
|
||||
cb(res[0]);
|
||||
} else {
|
||||
cb({error: 'UNEXPECTED_RESPONSE'});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Store.initAnonRpc = function (data, cb) {
|
||||
require([
|
||||
'/common/rpc.js',
|
||||
@@ -321,8 +363,6 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
/////////////////////// Store ////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
@@ -579,6 +619,8 @@ define([
|
||||
contains = true;
|
||||
pad.atime = +new Date();
|
||||
pad.title = title;
|
||||
pad.owners = owners;
|
||||
pad.expire = expire;
|
||||
|
||||
// If the href is different, it means we have a stronger one
|
||||
if (href !== pad.href) { isStronger = true; }
|
||||
@@ -773,6 +815,9 @@ define([
|
||||
onDisconnect: function () {
|
||||
postMessage("PAD_DISCONNECT");
|
||||
}, // post EV_PAD_DISCONNECT
|
||||
onError: function (err) {
|
||||
postMessage("PAD_ERROR", err);
|
||||
}, // post EV_PAD_ERROR
|
||||
channel: data.channel,
|
||||
validateKey: data.validateKey,
|
||||
owners: data.owners,
|
||||
@@ -852,7 +897,7 @@ define([
|
||||
case 'addFolder':
|
||||
store.userObject.addFolder(data.path, data.name, cb); break;
|
||||
case 'delete':
|
||||
store.userObject.delete(data.paths, cb, data.nocheck); break;
|
||||
store.userObject.delete(data.paths, cb, data.nocheck, data.isOwnPadRemoved); break;
|
||||
case 'emptyTrash':
|
||||
store.userObject.emptyTrash(cb); break;
|
||||
case 'rename':
|
||||
@@ -871,6 +916,8 @@ define([
|
||||
var userObject = store.userObject = UserObject.init(proxy.drive, {
|
||||
pinPads: Store.pinPads,
|
||||
unpinPads: Store.unpinPads,
|
||||
removeOwnedChannel: Store.removeOwnedChannel,
|
||||
edPublic: store.proxy.edPublic,
|
||||
loggedIn: store.loggedIn,
|
||||
log: function (msg) {
|
||||
postMessage("DRIVE_LOG", msg);
|
||||
|
||||
@@ -33,6 +33,7 @@ define([], function () {
|
||||
var onLeave = conf.onLeave;
|
||||
var onReady = conf.onReady;
|
||||
var onDisconnect = conf.onDisconnect;
|
||||
var onError = conf.onError;
|
||||
var owners = conf.owners;
|
||||
var password = conf.password;
|
||||
var expire = conf.expire;
|
||||
@@ -44,6 +45,17 @@ define([], function () {
|
||||
|
||||
var messageFromOuter = function () {};
|
||||
|
||||
var error = function (err, wc) {
|
||||
if (onError) {
|
||||
onError({
|
||||
type: err,
|
||||
loaded: !initializing
|
||||
});
|
||||
if (wc && (err === "EEXPIRED" || err === "EDELETED")) { wc.leave(); }
|
||||
}
|
||||
else { console.error(err); }
|
||||
};
|
||||
|
||||
var onRdy = function (padData) {
|
||||
// Trigger onReady only if not ready yet. This is important because the history keeper sends a direct
|
||||
// message through "network" when it is synced, and it triggers onReady for each channel joined.
|
||||
@@ -57,27 +69,11 @@ define([], function () {
|
||||
// shim between chainpad and netflux
|
||||
var msgIn = function (peerId, msg) {
|
||||
return msg.replace(/^cp\|/, '');
|
||||
|
||||
/*try {
|
||||
var decryptedMsg = Crypto.decrypt(msg, validateKey);
|
||||
return decryptedMsg;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return msg;
|
||||
}*/
|
||||
};
|
||||
|
||||
var msgOut = function (msg) {
|
||||
if (readOnly) { return; }
|
||||
return msg;
|
||||
/*try {
|
||||
var cmsg = Crypto.encrypt(msg);
|
||||
if (msg.indexOf('[4') === 0) { cmsg = 'cp|' + cmsg; }
|
||||
return cmsg;
|
||||
} catch (err) {
|
||||
console.log(msg);
|
||||
throw err;
|
||||
}*/
|
||||
};
|
||||
|
||||
var onMsg = function(peer, msg, wc, network, direct) {
|
||||
@@ -93,7 +89,9 @@ define([], function () {
|
||||
if (parsed.channel === wc.id && !validateKey) {
|
||||
validateKey = parsed.validateKey;
|
||||
}
|
||||
padData = parsed;
|
||||
if (parsed.channel === wc.id) {
|
||||
padData = parsed;
|
||||
}
|
||||
// We have to return even if it is not the current channel:
|
||||
// we don't want to continue with other channels messages here
|
||||
return;
|
||||
@@ -110,11 +108,17 @@ define([], function () {
|
||||
if (peer === hk) {
|
||||
// if the peer is the 'history keeper', extract their message
|
||||
var parsed1 = JSON.parse(msg);
|
||||
// First check if it is an error message (EXPIRED/DELETED)
|
||||
if (parsed1.channel === wc.id && parsed1.error) {
|
||||
return void error(parsed1.error, wc);
|
||||
}
|
||||
|
||||
msg = parsed1[4];
|
||||
// Check that this is a message for our channel
|
||||
if (parsed1[3] !== wc.id) { return; }
|
||||
}
|
||||
|
||||
|
||||
lastKnownHash = msg.slice(0,64);
|
||||
var message = msgIn(peer, msg);
|
||||
|
||||
@@ -191,7 +195,12 @@ define([], function () {
|
||||
};
|
||||
var msg = ['GET_HISTORY', wc.id, cfg];
|
||||
// Add the validateKey if we are the channel creator and we have a validateKey
|
||||
if (hk) { network.sendto(hk, JSON.stringify(msg)); }
|
||||
if (hk) {
|
||||
network.sendto(hk, JSON.stringify(msg)).then(function () {
|
||||
}, function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
onRdy();
|
||||
}
|
||||
@@ -218,8 +227,8 @@ define([], function () {
|
||||
// join the netflux network, promise to handle opening of the channel
|
||||
network.join(channel || null).then(function(wc) {
|
||||
onOpen(wc, network, firstConnection);
|
||||
}, function(error) {
|
||||
console.error(error);
|
||||
}, function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ define([
|
||||
case 'CLEAR_OWNED_CHANNEL': {
|
||||
Store.clearOwnedChannel(data, cb); break;
|
||||
}
|
||||
case 'REMOVE_OWNED_CHANNEL': {
|
||||
Store.removeOwnedChannel(data, cb); break;
|
||||
}
|
||||
case 'UPLOAD_CHUNK': {
|
||||
Store.uploadChunk(data, cb); break;
|
||||
}
|
||||
@@ -49,6 +52,9 @@ define([
|
||||
case 'UNPIN_PADS': {
|
||||
Store.unpinPads(data, cb); break;
|
||||
}
|
||||
case 'GET_DELETED_PADS': {
|
||||
Store.getDeletedPads(data, cb); break;
|
||||
}
|
||||
case 'GET_PINNED_USAGE': {
|
||||
Store.getPinnedUsage(data, cb); break;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@ define([
|
||||
'/common/common-util.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-realtime.js',
|
||||
'/common/common-feedback.js',
|
||||
'/customize/messages.js'
|
||||
], function (AppConfig, Util, Hash, Realtime, Messages) {
|
||||
], function (AppConfig, Util, Hash, Realtime, Feedback, Messages) {
|
||||
var module = {};
|
||||
|
||||
var clone = function (o) {
|
||||
@@ -17,8 +18,12 @@ define([
|
||||
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 edPublic = config.edPublic;
|
||||
|
||||
var ROOT = exp.ROOT;
|
||||
var FILES_DATA = exp.FILES_DATA;
|
||||
@@ -81,8 +86,11 @@ define([
|
||||
delete files[FILES_DATA][id];
|
||||
};
|
||||
|
||||
exp.checkDeletedFiles = function () {
|
||||
// Nothing in OLD_FILES_DATA for workgroups
|
||||
// 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; }
|
||||
|
||||
var filesList = exp.getFiles([ROOT, 'hrefArray', TRASH]);
|
||||
@@ -90,9 +98,20 @@ define([
|
||||
exp.getFiles([FILES_DATA]).forEach(function (id) {
|
||||
if (filesList.indexOf(id) === -1) {
|
||||
var fd = exp.getFileData(id);
|
||||
if (fd && fd.href) {
|
||||
toClean.push(Hash.hrefToHexChannelId(fd.href));
|
||||
var channelId = fd && fd.href && Hash.hrefToHexChannelId(fd.href);
|
||||
// If trying to remove an owned pad, remove it from server also
|
||||
if (!isOwnPadRemoved &&
|
||||
fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
|
||||
removeOwnedChannel(channelId, function (obj) {
|
||||
if (obj && obj.error) {
|
||||
console.error(obj.error);
|
||||
// RPC may not be responding
|
||||
// Send a report that can be handled manually
|
||||
Feedback.send('ERROR_DELETING_OWNED_PAD=' + channelId, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (channelId) { toClean.push(channelId); }
|
||||
spliceFileData(id);
|
||||
}
|
||||
});
|
||||
@@ -114,7 +133,7 @@ define([
|
||||
files[TRASH][obj.name].splice(idx, 1);
|
||||
});
|
||||
};
|
||||
exp.deleteMultiplePermanently = function (paths, nocheck) {
|
||||
exp.deleteMultiplePermanently = function (paths, nocheck, isOwnPadRemoved) {
|
||||
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]); });
|
||||
@@ -170,7 +189,7 @@ define([
|
||||
|
||||
// In some cases, we want to remove pads from a location without removing them from
|
||||
// OLD_FILES_DATA (replaceHref)
|
||||
if (!nocheck) { exp.checkDeletedFiles(); }
|
||||
if (!nocheck) { exp.checkDeletedFiles(isOwnPadRemoved); }
|
||||
};
|
||||
|
||||
// Move
|
||||
|
||||
@@ -145,7 +145,22 @@ define([
|
||||
if (response && response.length) {
|
||||
cb(void 0, response[0]);
|
||||
} else {
|
||||
cb('INVALID_RESPONSE');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exp.removeOwnedChannel = function (channel, cb) {
|
||||
if (typeof(channel) !== 'string' || channel.length !== 32) {
|
||||
// can't use this on files because files can't be owned...
|
||||
return void cb('INVALID_ARGUMENTS');
|
||||
}
|
||||
rpc.send('REMOVE_OWNED_CHANNEL', channel, function (e, response) {
|
||||
if (e) { return void cb(e); }
|
||||
if (response && response.length && response[0] === "OK") {
|
||||
cb();
|
||||
} else {
|
||||
cb('INVALID_RESPONSE');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -102,7 +102,7 @@ types of messages:
|
||||
}
|
||||
|
||||
// HACK to hide messages from the anon rpc
|
||||
if (parsed.length !== 4) {
|
||||
if (parsed.length !== 4 && parsed[1] !== 'ERROR') {
|
||||
console.log(parsed);
|
||||
console.error("received message [%s] for txid[%s] with no callback", msg, txid);
|
||||
}
|
||||
@@ -217,6 +217,15 @@ types of messages:
|
||||
});
|
||||
});
|
||||
|
||||
if (network.onHistoryKeeperChange) {
|
||||
network.onHistoryKeeperChange(function () {
|
||||
send('COOKIE', "", function (e) {
|
||||
if (e) { return void cb(e); }
|
||||
ctx.connected = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
send('COOKIE', "", function (e) {
|
||||
if (e) { return void cb(e); }
|
||||
// callback to provide 'send' method to whatever needs it
|
||||
|
||||
@@ -7,6 +7,7 @@ define([
|
||||
'/common/sframe-common.js',
|
||||
'/customize/messages.js',
|
||||
'/common/common-util.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-interface.js',
|
||||
'/common/common-thumbnail.js',
|
||||
'/common/common-feedback.js',
|
||||
@@ -27,6 +28,7 @@ define([
|
||||
SFCommon,
|
||||
Messages,
|
||||
Util,
|
||||
Hash,
|
||||
UI,
|
||||
Thumb,
|
||||
Feedback,
|
||||
@@ -41,6 +43,7 @@ define([
|
||||
var STATE = Object.freeze({
|
||||
DISCONNECTED: 'DISCONNECTED',
|
||||
FORGOTTEN: 'FORGOTTEN',
|
||||
DELETED: 'DELETED',
|
||||
INFINITE_SPINNER: 'INFINITE_SPINNER',
|
||||
INITIALIZING: 'INITIALIZING',
|
||||
HISTORY_MODE: 'HISTORY_MODE',
|
||||
@@ -84,6 +87,7 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
var textContentGetter;
|
||||
var titleRecommender = function () { return false; };
|
||||
var contentGetter = function () { return UNINITIALIZED; };
|
||||
var normalize0 = function (x) { return x; };
|
||||
@@ -116,8 +120,9 @@ define([
|
||||
|
||||
var stateChange = function (newState) {
|
||||
var wasEditable = (state === STATE.READY);
|
||||
if (state === STATE.DELETED) { return; }
|
||||
if (state === STATE.INFINITE_SPINNER && newState !== STATE.READY) { return; }
|
||||
if (newState === STATE.INFINITE_SPINNER) {
|
||||
if (newState === STATE.INFINITE_SPINNER || newState === STATE.DELETED) {
|
||||
state = newState;
|
||||
} else if (state === STATE.DISCONNECTED && newState !== STATE.INITIALIZING) {
|
||||
throw new Error("Cannot transition from DISCONNECTED to " + newState);
|
||||
@@ -146,6 +151,10 @@ define([
|
||||
evStart.reg(function () { toolbar.forgotten(); });
|
||||
break;
|
||||
}
|
||||
case STATE.DELETED: {
|
||||
evStart.reg(function () { toolbar.deleted(); });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
if (wasEditable !== (state === STATE.READY)) {
|
||||
@@ -254,6 +263,7 @@ define([
|
||||
|
||||
var onReady = function () {
|
||||
var newContentStr = cpNfInner.chainpad.getUserDoc();
|
||||
if (state === STATE.DELETED) { return; }
|
||||
|
||||
var newPad = false;
|
||||
if (newContentStr === '') { newPad = true; }
|
||||
@@ -287,11 +297,17 @@ define([
|
||||
UI.removeLoadingScreen(emitResize);
|
||||
|
||||
var privateDat = cpNfInner.metadataMgr.getPrivateData();
|
||||
var hash = privateDat.availableHashes.editHash ||
|
||||
privateDat.availableHashes.viewHash;
|
||||
var href = privateDat.pathname + '#' + hash;
|
||||
if (AppConfig.textAnalyzer && textContentGetter) {
|
||||
var channelId = Hash.hrefToHexChannelId(href);
|
||||
AppConfig.textAnalyzer(textContentGetter, channelId);
|
||||
}
|
||||
|
||||
if (options.thumbnail && privateDat.thumbnails) {
|
||||
var hash = privateDat.availableHashes.editHash ||
|
||||
privateDat.availableHashes.viewHash;
|
||||
if (hash) {
|
||||
options.thumbnail.href = privateDat.pathname + '#' + hash;
|
||||
options.thumbnail.href = href;
|
||||
options.thumbnail.getContent = function () {
|
||||
if (!cpNfInner.chainpad) { return; }
|
||||
return cpNfInner.chainpad.getUserDoc();
|
||||
@@ -307,6 +323,7 @@ define([
|
||||
}
|
||||
};
|
||||
var onConnectionChange = function (info) {
|
||||
if (state === STATE.DELETED) { return; }
|
||||
stateChange(info.state ? STATE.INITIALIZING : STATE.DISCONNECTED);
|
||||
if (info.state) {
|
||||
UI.findOKButton().click();
|
||||
@@ -315,6 +332,12 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
var onError = function (err) {
|
||||
common.onServerError(err, toolbar, function () {
|
||||
stateChange(STATE.DELETED);
|
||||
});
|
||||
};
|
||||
|
||||
var setFileExporter = function (extension, fe, async) {
|
||||
var $export = common.createButton('export', true, {}, function () {
|
||||
var ext = (typeof(extension) === 'function') ? extension() : extension;
|
||||
@@ -407,7 +430,9 @@ define([
|
||||
var priv = common.getMetadataMgr().getPrivateData();
|
||||
if (priv.isNewFile) {
|
||||
var c = (priv.settings.general && priv.settings.general.creation) || {};
|
||||
if (c.skip && !priv.forceCreationScreen) { return void common.createPad(c, waitFor()); }
|
||||
if (c.skip && !priv.forceCreationScreen) {
|
||||
return void common.createPad(c, waitFor());
|
||||
}
|
||||
common.getPadCreationScreen(c, waitFor());
|
||||
}
|
||||
}).nThen(function (waitFor) {
|
||||
@@ -432,7 +457,8 @@ define([
|
||||
onLocal: onLocal,
|
||||
onInit: function () { stateChange(STATE.INITIALIZING); },
|
||||
onReady: function () { evStart.reg(onReady); },
|
||||
onConnectionChange: onConnectionChange
|
||||
onConnectionChange: onConnectionChange,
|
||||
onError: onError
|
||||
});
|
||||
|
||||
var privReady = Util.once(waitFor());
|
||||
@@ -448,6 +474,7 @@ define([
|
||||
var infiniteSpinnerModal = false;
|
||||
window.setInterval(function () {
|
||||
if (state === STATE.DISCONNECTED) { return; }
|
||||
if (state === STATE.DELETED) { return; }
|
||||
var l;
|
||||
try {
|
||||
l = cpNfInner.chainpad.getLag();
|
||||
@@ -567,6 +594,10 @@ define([
|
||||
// in the pad when requested by the framework.
|
||||
setContentGetter: function (cg) { contentGetter = cg; },
|
||||
|
||||
// Set a text content supplier, this is a function which will give a text
|
||||
// representation of the pad content if a text analyzer is configured
|
||||
setTextContentGetter: function (tcg) { textContentGetter = tcg; },
|
||||
|
||||
// Inform the framework that the content of the pad has been changed locally.
|
||||
localChange: onLocal,
|
||||
|
||||
|
||||
@@ -36,5 +36,14 @@ define([
|
||||
// This test is completed in common-interface.js
|
||||
Test(function (t) { Test.__ASYNC_BLOCKER__ = t; });
|
||||
|
||||
window.onerror = function (e) {
|
||||
if (/requirejs\.org/.test(e)) {
|
||||
console.log();
|
||||
console.error("Require.js threw a Script Error. This probably means you're missing a dependency for CryptPad.\nIt is recommended that the admin of this server runs `bower install && bower update` to get the latest code, then modify their cache version.\nBest of luck,\nThe CryptPad Developers");
|
||||
return void console.log();
|
||||
}
|
||||
throw e;
|
||||
};
|
||||
|
||||
require([document.querySelector('script[data-bootload]').getAttribute('data-bootload')]);
|
||||
});
|
||||
|
||||
@@ -34,6 +34,7 @@ define([
|
||||
var onLocal = config.onLocal || function () { };
|
||||
var setMyID = config.setMyID || function () { };
|
||||
var onReady = config.onReady || function () { };
|
||||
var onError = config.onError || function () { };
|
||||
var userName = config.userName;
|
||||
var initialState = config.initialState;
|
||||
if (config.transformFunction) { throw new Error("transformFunction is nolonger allowed"); }
|
||||
@@ -83,6 +84,11 @@ define([
|
||||
chainpad.abort();
|
||||
onConnectionChange({ state: false });
|
||||
});
|
||||
sframeChan.on('EV_RT_ERROR', function (err) {
|
||||
isReady = false;
|
||||
chainpad.abort();
|
||||
onError(err);
|
||||
});
|
||||
sframeChan.on('EV_RT_CONNECT', function (content) {
|
||||
//content.members.forEach(userList.onJoin);
|
||||
isReady = false;
|
||||
|
||||
@@ -102,6 +102,10 @@ define([], function () {
|
||||
sframeChan.event('EV_RT_DISCONNECT');
|
||||
});
|
||||
|
||||
padRpc.onErrorEvent.reg(function (err) {
|
||||
sframeChan.event('EV_RT_ERROR', err);
|
||||
});
|
||||
|
||||
// join the netflux network, promise to handle opening of the channel
|
||||
padRpc.joinPad({
|
||||
channel: channel || null,
|
||||
|
||||
@@ -44,12 +44,12 @@ define([
|
||||
|
||||
History.readOnly = common.getMetadataMgr().getPrivateData().readOnly;
|
||||
|
||||
var to = window.setTimeout(function () {
|
||||
/*var to = window.setTimeout(function () {
|
||||
cb('[GET_FULL_HISTORY_TIMEOUT]');
|
||||
}, 30000);
|
||||
}, 30000);*/
|
||||
|
||||
common.getFullHistory(realtime, function () {
|
||||
window.clearTimeout(to);
|
||||
//window.clearTimeout(to);
|
||||
cb(null, realtime);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -132,10 +132,12 @@ define([
|
||||
// Check if the pad exists on server
|
||||
if (!window.location.hash) { isNewFile = true; return; }
|
||||
|
||||
Cryptpad.isNewChannel(window.location.href, waitFor(function (e, isNew) {
|
||||
if (e) { return console.error(e); }
|
||||
isNewFile = Boolean(isNew);
|
||||
}));
|
||||
if (realtime) {
|
||||
Cryptpad.isNewChannel(window.location.href, waitFor(function (e, isNew) {
|
||||
if (e) { return console.error(e); }
|
||||
isNewFile = Boolean(isNew);
|
||||
}));
|
||||
}
|
||||
}).nThen(function () {
|
||||
var readOnly = secret.keys && !secret.keys.editKeyStr;
|
||||
var isNewHash = true;
|
||||
@@ -501,6 +503,9 @@ define([
|
||||
sframeChan.on('Q_CONTACTS_CLEAR_OWNED_CHANNEL', function (channel, cb) {
|
||||
Cryptpad.clearOwnedChannel(channel, cb);
|
||||
});
|
||||
sframeChan.on('Q_REMOVE_OWNED_CHANNEL', function (channel, cb) {
|
||||
Cryptpad.removeOwnedChannel(channel, cb);
|
||||
});
|
||||
|
||||
if (cfg.addRpc) {
|
||||
cfg.addRpc(sframeChan, Cryptpad, Utils);
|
||||
|
||||
@@ -92,6 +92,7 @@ define([
|
||||
funcs.createMarkdownToolbar = callWithCommon(UIElements.createMarkdownToolbar);
|
||||
funcs.getPadCreationScreen = callWithCommon(UIElements.getPadCreationScreen);
|
||||
funcs.createNewPadModal = callWithCommon(UIElements.createNewPadModal);
|
||||
funcs.onServerError = callWithCommon(UIElements.onServerError);
|
||||
|
||||
// Thumb
|
||||
funcs.displayThumbnail = callWithCommon(Thumb.displayThumbnail);
|
||||
@@ -392,15 +393,12 @@ define([
|
||||
UI.log(data.logText);
|
||||
});
|
||||
|
||||
ctx.metadataMgr.onChange(function () {
|
||||
try {
|
||||
var feedback = ctx.metadataMgr.getPrivateData().feedbackAllowed;
|
||||
Feedback.init(feedback);
|
||||
} catch (e) { Feedback.init(false); }
|
||||
});
|
||||
|
||||
ctx.metadataMgr.onReady(waitFor());
|
||||
}).nThen(function () {
|
||||
try {
|
||||
var feedback = ctx.metadataMgr.getPrivateData().feedbackAllowed;
|
||||
Feedback.init(feedback);
|
||||
} catch (e) { Feedback.init(false); }
|
||||
ctx.sframeChan.ready();
|
||||
cb(funcs);
|
||||
});
|
||||
|
||||
@@ -31,6 +31,8 @@ define({
|
||||
'EV_RT_CONNECT': true,
|
||||
// Called after the history is finished synchronizing, no arguments.
|
||||
'EV_RT_READY': true,
|
||||
// Called when the server returns an error in a pad (EEXPIRED, EDELETED).
|
||||
'EV_RT_ERROR': true,
|
||||
// Called from both outside and inside, argument is a (string) chainpad message.
|
||||
'Q_RT_MESSAGE': true,
|
||||
|
||||
@@ -201,12 +203,17 @@ define({
|
||||
// Inner drive needs to send command and receive updates from the async store
|
||||
'Q_DRIVE_USEROBJECT': true,
|
||||
'Q_DRIVE_GETOBJECT': 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
|
||||
'EV_DRIVE_LOG': true,
|
||||
// Refresh the drive when the drive has changed ('change' or 'remove' events)
|
||||
'EV_DRIVE_CHANGE': true,
|
||||
'EV_DRIVE_REMOVE': true,
|
||||
|
||||
// Remove an owned pad from the server
|
||||
'Q_REMOVE_OWNED_CHANNEL': true,
|
||||
|
||||
// Notifications about connection and disconnection from the network
|
||||
'EV_NETWORK_DISCONNECT': true,
|
||||
'EV_NETWORK_RECONNECT': true,
|
||||
|
||||
@@ -444,13 +444,14 @@ define([
|
||||
'class': 'fa fa-share-alt cp-toolbar-share-button',
|
||||
title: Messages.shareButton
|
||||
});
|
||||
var modal = UIElements.createShareModal({
|
||||
origin: origin,
|
||||
pathname: pathname,
|
||||
hashes: hashes,
|
||||
common: Common
|
||||
});
|
||||
$shareBlock.click(function () {
|
||||
UIElements.createShareModal({
|
||||
origin: origin,
|
||||
pathname: pathname,
|
||||
hashes: hashes,
|
||||
common: Common
|
||||
});
|
||||
UI.openCustomModal(UI.dialog.tabs(modal));
|
||||
});
|
||||
|
||||
toolbar.$leftside.append($shareBlock);
|
||||
@@ -472,13 +473,14 @@ define([
|
||||
'class': 'fa fa-share-alt cp-toolbar-share-button',
|
||||
title: Messages.shareButton
|
||||
});
|
||||
var modal = UIElements.createFileShareModal({
|
||||
origin: origin,
|
||||
pathname: pathname,
|
||||
hashes: hashes,
|
||||
common: Common
|
||||
});
|
||||
$shareBlock.click(function () {
|
||||
UIElements.createFileShareModal({
|
||||
origin: origin,
|
||||
pathname: pathname,
|
||||
hashes: hashes,
|
||||
common: Common
|
||||
});
|
||||
UI.openCustomModal(UI.dialog.tabs(modal));
|
||||
});
|
||||
|
||||
toolbar.$leftside.append($shareBlock);
|
||||
@@ -707,6 +709,7 @@ define([
|
||||
typing = 1;
|
||||
$spin.text(Messages.typing);
|
||||
$spin.interval = window.setInterval(function () {
|
||||
if (toolbar.isErrorState) { return; }
|
||||
var dots = Array(typing+1).join('.');
|
||||
$spin.text(Messages.typing + dots);
|
||||
typing++;
|
||||
@@ -716,6 +719,7 @@ define([
|
||||
var onSynced = function () {
|
||||
if ($spin.timeout) { clearTimeout($spin.timeout); }
|
||||
$spin.timeout = setTimeout(function () {
|
||||
if (toolbar.isErrorState) { return; }
|
||||
window.clearInterval($spin.interval);
|
||||
typing = -1;
|
||||
$spin.text(Messages.saved);
|
||||
@@ -1092,6 +1096,16 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
// When the pad is deleted from the server
|
||||
toolbar.deleted = function (/*userId*/) {
|
||||
toolbar.isErrorState = true;
|
||||
toolbar.connected = false;
|
||||
updateUserList(toolbar, config);
|
||||
if (toolbar.spinner) {
|
||||
toolbar.spinner.text(Messages.deletedFromServer);
|
||||
}
|
||||
};
|
||||
|
||||
// On log out, remove permanently the realtime elements of the toolbar
|
||||
Common.onLogout(function () {
|
||||
failed();
|
||||
|
||||
@@ -380,6 +380,18 @@ define([
|
||||
var trashpaths = _findFileInTrash([TRASH], file);
|
||||
return rootpaths.concat(templatepaths, trashpaths);
|
||||
};
|
||||
|
||||
// Get drive ids of files from their channel ids
|
||||
exp.findChannels = function (channels) {
|
||||
var allFilesList = files[FILES_DATA];
|
||||
var channels64 = channels.slice().map(Util.hexToBase64);
|
||||
return getFiles([FILES_DATA]).filter(function (k) {
|
||||
var data = allFilesList[k];
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
return parsed.hashData && channels64.indexOf(parsed.hashData.channel) !== -1;
|
||||
});
|
||||
};
|
||||
|
||||
exp.search = function (value) {
|
||||
if (typeof(value) !== "string") { return []; }
|
||||
value = value.trim();
|
||||
@@ -544,17 +556,18 @@ 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) {
|
||||
exp.delete = function (paths, cb, nocheck, isOwnPadRemoved) {
|
||||
if (sframeChan) {
|
||||
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||
cmd: "delete",
|
||||
data: {
|
||||
paths: paths,
|
||||
nocheck: nocheck
|
||||
nocheck: nocheck,
|
||||
isOwnPadRemoved: isOwnPadRemoved
|
||||
}
|
||||
}, cb);
|
||||
}
|
||||
exp.deleteMultiplePermanently(paths, nocheck);
|
||||
exp.deleteMultiplePermanently(paths, nocheck, isOwnPadRemoved);
|
||||
if (typeof cb === "function") { cb(); }
|
||||
};
|
||||
exp.emptyTrash = function (cb) {
|
||||
|
||||
Reference in New Issue
Block a user