Merge branch 'newDrive' into staging

pull/1/head
yflory 8 years ago
commit 7dd9ed543a

@ -4,8 +4,9 @@ define([
'/bower_components/textpatcher/TextPatcher.amd.js', '/bower_components/textpatcher/TextPatcher.amd.js',
'json.sortify', 'json.sortify',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/drive/tests.js',
'/common/test.js' '/common/test.js'
], function ($, Hyperjson, TextPatcher, Sortify, Cryptpad, Test) { ], function ($, Hyperjson, TextPatcher, Sortify, Cryptpad, Drive, Test) {
window.Hyperjson = Hyperjson; window.Hyperjson = Hyperjson;
window.TextPatcher = TextPatcher; window.TextPatcher = TextPatcher;
window.Sortify = Sortify; window.Sortify = Sortify;
@ -206,6 +207,9 @@ define([
return cb(true); return cb(true);
}, "version 2 hash failed to parse correctly"); }, "version 2 hash failed to parse correctly");
Drive.test(assert);
var swap = function (str, dict) { var swap = function (str, dict) {
return str.replace(/\{\{(.*?)\}\}/g, function (all, key) { return str.replace(/\{\{(.*?)\}\}/g, function (all, key) {
return typeof dict[key] !== 'undefined'? dict[key] : all; return typeof dict[key] !== 'undefined'? dict[key] : all;

@ -238,7 +238,8 @@ Version 1
var parsed = parsePadUrl(rHref); var parsed = parsePadUrl(rHref);
if (!parsed.hash) { return false; } if (!parsed.hash) { return false; }
var weaker; var weaker;
recents.some(function (pad) { Object.keys(recents).some(function (id) {
var pad = recents[id];
var p = parsePadUrl(pad.href); var p = parsePadUrl(pad.href);
if (p.type !== parsed.type) { return; } // Not the same type if (p.type !== parsed.type) { return; } // Not the same type
if (p.hash === parsed.hash) { return; } // Same hash, not stronger if (p.hash === parsed.hash) { return; } // Same hash, not stronger
@ -264,7 +265,8 @@ Version 1
var parsed = parsePadUrl(rHref); var parsed = parsePadUrl(rHref);
if (!parsed.hash) { return false; } if (!parsed.hash) { return false; }
var stronger; var stronger;
recents.some(function (pad) { Object.keys(recents).some(function (id) {
var pad = recents[id];
var p = parsePadUrl(pad.href); var p = parsePadUrl(pad.href);
if (p.type !== parsed.type) { return; } // Not the same type if (p.type !== parsed.type) { return; } // Not the same type
if (p.hash === parsed.hash) { return; } // Same hash, not stronger if (p.hash === parsed.hash) { return; } // Same hash, not stronger

@ -42,7 +42,8 @@ define([
common.displayNameKey = 'cryptpad.username'; common.displayNameKey = 'cryptpad.username';
var newPadNameKey = common.newPadNameKey = "newPadName"; var newPadNameKey = common.newPadNameKey = "newPadName";
var newPadPathKey = common.newPadPathKey = "newPadPath"; var newPadPathKey = common.newPadPathKey = "newPadPath";
var storageKey = common.storageKey = 'CryptPad_RECENTPADS'; var oldStorageKey = common.oldStorageKey = 'CryptPad_RECENTPADS';
common.storageKey = 'filesData';
var PINNING_ENABLED = AppConfig.enablePinning; var PINNING_ENABLED = AppConfig.enablePinning;
var store; var store;
@ -327,7 +328,8 @@ define([
}; };
// Remove everything from RecentPads that is not an object and check the objects // Remove everything from RecentPads that is not an object and check the objects
var checkRecentPads = common.checkRecentPads = function (pads) { var checkRecentPads = common.checkRecentPads = function (pads) {
pads.forEach(function (pad, i) { Object.keys(pads).forEach(function (id, i) {
var pad = pads[id];
if (pad && typeof(pad) === 'object') { if (pad && typeof(pad) === 'object') {
var parsedHash = checkObjectData(pad); var parsedHash = checkObjectData(pad);
if (!parsedHash || !parsedHash.type) { if (!parsedHash || !parsedHash.type) {
@ -347,7 +349,7 @@ define([
require(['/customize/store.js'], function(Legacy) { // TODO DEPRECATE_F require(['/customize/store.js'], function(Legacy) { // TODO DEPRECATE_F
Legacy.ready(function (err, legacy) { Legacy.ready(function (err, legacy) {
if (err) { cb(err, null); return; } if (err) { cb(err, null); return; }
legacy.get(storageKey, function (err2, recentPads) { legacy.get(oldStorageKey, function (err2, recentPads) {
if (err2) { cb(err2, null); return; } if (err2) { cb(err2, null); return; }
if (Array.isArray(recentPads)) { if (Array.isArray(recentPads)) {
feedback('MIGRATE_LEGACY_STORE'); feedback('MIGRATE_LEGACY_STORE');
@ -361,23 +363,30 @@ define([
}; };
// Create untitled documents when no name is given // Create untitled documents when no name is given
var getLocaleDate = common.getLocaleDate = function () {
if (window.Intl && window.Intl.DateTimeFormat) {
var options = {weekday: "short", year: "numeric", month: "long", day: "numeric"};
return new window.Intl.DateTimeFormat(undefined, options).format(new Date());
}
return new Date().toString().split(' ').slice(0,4).join(' ');
};
var getDefaultName = common.getDefaultName = function (parsed) { var getDefaultName = common.getDefaultName = function (parsed) {
var type = parsed.type; var type = parsed.type;
var name = (Messages.type)[type] + ' - ' + new Date().toString().split(' ').slice(0,4).join(' '); var name = (Messages.type)[type] + ' - ' + getLocaleDate();
return name; return name;
}; };
var isDefaultName = common.isDefaultName = function (parsed, title) { common.isDefaultName = function (parsed, title) {
var name = getDefaultName(parsed); var name = getDefaultName(parsed);
return title === name; return title === name;
}; };
var makePad = function (href, title) { var makePad = common.makePad = function (href, title) {
var now = +new Date(); var now = +new Date();
return { return {
href: href, href: href,
atime: now, atime: now,
ctime: now, ctime: now,
title: title || window.location.hash.slice(1, 9), title: title || getDefaultName(parsePadUrl(href)),
}; };
}; };
@ -428,8 +437,10 @@ define([
return templates; return templates;
}; };
common.addTemplate = function (data) { common.addTemplate = function (data) {
getStore().pushData(data); getStore().pushData(data, function (e, id) {
getStore().addPad(data.href, ['template']); if (e) { return void console.error("Error while adding a template:", e); } // TODO LIMIT
getStore().addPad(id, ['template']);
});
}; };
common.isTemplate = function (href) { common.isTemplate = function (href) {
@ -472,13 +483,13 @@ define([
// STORAGE // STORAGE
/* fetch and migrate your pad history from the store */ /* fetch and migrate your pad history from the store */
var getRecentPads = common.getRecentPads = function (cb) { var getRecentPads = common.getRecentPads = function (cb) {
getStore().getDrive(storageKey, function (err, recentPads) { getStore().getDrive('filesData', function (err, recentPads) {
if (Array.isArray(recentPads)) { if (typeof(recentPads) === "object") {
checkRecentPads(recentPads); checkRecentPads(recentPads);
cb(void 0, recentPads); cb(void 0, recentPads);
return; return;
} }
cb(void 0, []); cb(void 0, {});
}); });
}; };
@ -502,50 +513,13 @@ define([
// STORAGE // STORAGE
common.forgetPad = function (href, cb) { common.forgetPad = function (href, cb) {
var parsed = parsePadUrl(href);
var callback = function (err) {
if (err) {
cb(err);
return;
}
getStore().keys(function (err, keys) {
if (err) {
cb(err);
return;
}
var toRemove = keys.filter(function (k) {
return k.indexOf(parsed.hash) === 0;
});
if (!toRemove.length) {
cb();
return;
}
getStore().removeBatch(toRemove, function (err, data) {
cb(err, data);
});
});
};
if (typeof(getStore().forgetPad) === "function") { if (typeof(getStore().forgetPad) === "function") {
getStore().forgetPad(common.getRelativeHref(href), callback); getStore().forgetPad(common.getRelativeHref(href), cb);
return;
} }
cb ("store.forgetPad is not a function");
}; };
var updateFileName = function (href, oldName, newName) {
var fo = getStore().getProxy().fo;
var paths = fo.findFileInRoot(href);
paths.forEach(function (path) {
if (path.length !== 2) { return; }
var name = path[1].split('_')[0];
var parsed = parsePadUrl(href);
if (path.length === 2 && name === oldName && isDefaultName(parsed, name)) {
fo.rename(path, newName);
}
});
};
common.setPadTitle = function (name, cb) { common.setPadTitle = function (name, cb) {
var href = window.location.href; var href = window.location.href;
var parsed = parsePadUrl(href); var parsed = parsePadUrl(href);
@ -561,7 +535,8 @@ define([
var updateWeaker = []; var updateWeaker = [];
var contains; var contains;
recent.forEach(function (pad) { Object.keys(recent).forEach(function (id) {
var pad = recent[id];
var p = parsePadUrl(pad.href); var p = parsePadUrl(pad.href);
if (p.type !== parsed.type) { return pad; } if (p.type !== parsed.type) { return pad; }
@ -592,7 +567,6 @@ define([
pad.atime = +new Date(); pad.atime = +new Date();
// set the name // set the name
var old = pad.title;
pad.title = name; pad.title = name;
// If we now have a stronger version of a stored href, replace the weaker one by the strong one // If we now have a stronger version of a stored href, replace the weaker one by the strong one
@ -603,14 +577,13 @@ define([
}); });
} }
pad.href = href; pad.href = href;
updateFileName(href, old, name);
} }
return pad; return pad;
}); });
if (!contains && href) { if (!contains && href) {
var data = makePad(href, name); var data = makePad(href, name);
getStore().pushData(data, function (e) { getStore().pushData(data, function (e, id) {
if (e) { if (e) {
if (e === 'E_OVER_LIMIT') { if (e === 'E_OVER_LIMIT') {
common.alert(Messages.pinLimitNotPinned, null, true); common.alert(Messages.pinLimitNotPinned, null, true);
@ -618,12 +591,14 @@ define([
} }
else { throw new Error("Cannot push this pad to CryptDrive", e); } else { throw new Error("Cannot push this pad to CryptDrive", e); }
} }
getStore().addPad(data, common.initialPath); getStore().addPad(id, common.initialPath);
}); });
} }
if (updateWeaker.length > 0) { if (updateWeaker.length > 0) {
updateWeaker.forEach(function (obj) { updateWeaker.forEach(function (obj) {
getStore().replaceHref(obj.o, obj.n); // If we have a stronger url, and if all the occurences of the weaker were
// in the trash, add remove them from the trash and add the stronger in root
getStore().restoreHref(obj.n);
}); });
} }
cb(err, recent); cb(err, recent);
@ -677,7 +652,9 @@ define([
var userChannel = userParsedHash && userParsedHash.channel; var userChannel = userParsedHash && userParsedHash.channel;
if (!userChannel) { return null; } if (!userChannel) { return null; }
var list = fo.getFiles([fo.FILES_DATA]).map(hrefToHexChannelId) var list = fo.getFiles([fo.FILES_DATA]).map(function (id) {
return hrefToHexChannelId(fo.getFileData(id).href);
})
.filter(function (x) { return x; }); .filter(function (x) { return x; });
list.push(common.base64ToHex(userChannel)); list.push(common.base64ToHex(userChannel));

@ -87,10 +87,7 @@ define([
ret.removeData = filesOp.removeData; ret.removeData = filesOp.removeData;
ret.pushData = filesOp.pushData; ret.pushData = filesOp.pushData;
ret.addPad = filesOp.add;
ret.addPad = function (data, path) {
filesOp.add(data, path);
};
ret.forgetPad = function (href, cb) { ret.forgetPad = function (href, cb) {
filesOp.forget(href); filesOp.forget(href);
@ -123,9 +120,9 @@ define([
return filesOp.getStructure(); return filesOp.getStructure();
}; };
ret.replaceHref = function (o, n) { ret.replace = filesOp.replace;
return filesOp.replace(o, n);
}; ret.restoreHref = filesOp.restoreHref;
ret.changeHandlers = []; ret.changeHandlers = [];
@ -144,76 +141,81 @@ define([
var onReady = function (f, proxy, Cryptpad, exp) { var onReady = function (f, proxy, Cryptpad, exp) {
var fo = exp.fo = FO.init(proxy.drive, { var fo = exp.fo = FO.init(proxy.drive, {
Cryptpad: Cryptpad Cryptpad: Cryptpad,
rt: exp.realtime
}); });
var todo = function () {
fo.fixFiles();
//storeObj = proxy; //storeObj = proxy;
store = initStore(fo, proxy, exp); store = initStore(fo, proxy, exp);
if (typeof(f) === 'function') { if (typeof(f) === 'function') {
f(void 0, store); f(void 0, store);
} }
var requestLogin = function () { var requestLogin = function () {
// log out so that you don't go into an endless loop... // log out so that you don't go into an endless loop...
Cryptpad.logout(); Cryptpad.logout();
// redirect them to log in, and come back when they're done. // redirect them to log in, and come back when they're done.
sessionStorage.redirectTo = window.location.href; sessionStorage.redirectTo = window.location.href;
window.location.href = '/login/'; window.location.href = '/login/';
}; };
var tokenKey = 'loginToken'; var tokenKey = 'loginToken';
if (Cryptpad.isLoggedIn()) { if (Cryptpad.isLoggedIn()) {
/* This isn't truly secure, since anyone who can read the user's object can /* This isn't truly secure, since anyone who can read the user's object can
set their local loginToken to match that in the object. However, it exposes set their local loginToken to match that in the object. However, it exposes
a UI that will work most of the time. */ a UI that will work most of the time. */
// every user object should have a persistent, random number // every user object should have a persistent, random number
if (typeof(proxy.loginToken) !== 'number') { if (typeof(proxy.loginToken) !== 'number') {
proxy[tokenKey] = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER); proxy[tokenKey] = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
} }
// copy User_hash into sessionStorage because cross-domain iframes // copy User_hash into sessionStorage because cross-domain iframes
// on safari replaces localStorage with sessionStorage or something // on safari replaces localStorage with sessionStorage or something
if (sessionStorage) { sessionStorage.setItem('User_hash', localStorage.getItem('User_hash')); } if (sessionStorage) { sessionStorage.setItem('User_hash', localStorage.getItem('User_hash')); }
var localToken = tryParsing(localStorage.getItem(tokenKey)); var localToken = tryParsing(localStorage.getItem(tokenKey));
if (localToken === null) { if (localToken === null) {
// if that number hasn't been set to localStorage, do so. // if that number hasn't been set to localStorage, do so.
localStorage.setItem(tokenKey, proxy.loginToken); localStorage.setItem(tokenKey, proxy.loginToken);
} else if (localToken !== proxy[tokenKey]) { } else if (localToken !== proxy[tokenKey]) {
// if it has been, and the local number doesn't match that in // if it has been, and the local number doesn't match that in
// the user object, request that they reauthenticate. // the user object, request that they reauthenticate.
return void requestLogin(); return void requestLogin();
}
} }
}
if (typeof(proxy.allowUserFeedback) !== 'boolean') {
proxy.allowUserFeedback = true;
}
if (typeof(proxy.uid) !== 'string' || proxy.uid.length !== 32) { if (typeof(proxy.allowUserFeedback) !== 'boolean') {
// even anonymous users should have a persistent, unique-ish id proxy.allowUserFeedback = true;
console.log('generating a persistent identifier'); }
proxy.uid = Cryptpad.createChannelId();
}
// if the user is logged in, but does not have signing keys... if (typeof(proxy.uid) !== 'string' || proxy.uid.length !== 32) {
if (Cryptpad.isLoggedIn() && !Cryptpad.hasSigningKeys(proxy)) { // even anonymous users should have a persistent, unique-ish id
return void requestLogin(); console.log('generating a persistent identifier');
} proxy.uid = Cryptpad.createChannelId();
}
proxy.on('change', [Cryptpad.displayNameKey], function (o, n) { // if the user is logged in, but does not have signing keys...
if (typeof(n) !== "string") { return; } if (Cryptpad.isLoggedIn() && !Cryptpad.hasSigningKeys(proxy)) {
Cryptpad.changeDisplayName(n);
});
proxy.on('change', [tokenKey], function () {
console.log('wut');
var localToken = tryParsing(localStorage.getItem(tokenKey));
if (localToken !== proxy[tokenKey]) {
return void requestLogin(); return void requestLogin();
} }
});
proxy.on('change', [Cryptpad.displayNameKey], function (o, n) {
if (typeof(n) !== "string") { return; }
Cryptpad.changeDisplayName(n);
});
proxy.on('change', [tokenKey], function () {
console.log('wut');
var localToken = tryParsing(localStorage.getItem(tokenKey));
if (localToken !== proxy[tokenKey]) {
return void requestLogin();
}
});
};
fo.migrate(todo);
}; };
var initialized = false; var initialized = false;
@ -263,6 +265,7 @@ define([
var rt = window.rt = Listmap.create(listmapConfig); var rt = window.rt = Listmap.create(listmapConfig);
exp.realtime = rt.realtime;
exp.proxy = rt.proxy; exp.proxy = rt.proxy;
rt.proxy.on('create', function (info) { rt.proxy.on('create', function (info) {
exp.info = info; exp.info = info;
@ -274,9 +277,10 @@ define([
if (!rt.proxy.drive || typeof(rt.proxy.drive) !== 'object') { rt.proxy.drive = {}; } if (!rt.proxy.drive || typeof(rt.proxy.drive) !== 'object') { rt.proxy.drive = {}; }
var drive = rt.proxy.drive; var drive = rt.proxy.drive;
// Creating a new anon drive: import anon pads from localStorage // Creating a new anon drive: import anon pads from localStorage
if (!drive[Cryptpad.storageKey] || !Cryptpad.isArray(drive[Cryptpad.storageKey])) { if ((!drive[Cryptpad.oldStorageKey] || !Cryptpad.isArray(drive[Cryptpad.oldStorageKey]))
&& !drive['filesData']) {
Cryptpad.getLegacyPads(function (err, data) { Cryptpad.getLegacyPads(function (err, data) {
drive[Cryptpad.storageKey] = data; drive[Cryptpad.oldStorageKey] = data;
onReady(f, rt.proxy, Cryptpad, exp); onReady(f, rt.proxy, Cryptpad, exp);
}); });
return; return;

@ -2,8 +2,7 @@ define([
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/cryptget.js', '/common/cryptget.js',
'/common/userObject.js', '/common/userObject.js',
'json.sortify' ], function (Cryptpad, Crypt, FO) {
], function (Cryptpad, Crypt, FO, Sortify) {
var exp = {}; var exp = {};
var getType = function (el) { var getType = function (el) {
@ -22,61 +21,17 @@ define([
return nkey; return nkey;
}; };
var copy = function (el) { var createFromPath = function (proxy, oldFo, path, id) {
if (typeof (el) !== "object") { return el; }
return JSON.parse(JSON.stringify(el));
};
var deduplicate = function (array) {
var a = array.slice();
for(var i=0; i<a.length; i++) {
for(var j=i+1; j<a.length; j++) {
if(a[i] === a[j] || (
typeof(a[i]) === "object" && Sortify(a[i]) === Sortify(a[j]))) {
a.splice(j--, 1);
}
}
}
return a;
};
// Merge obj2 into obj1
// If keepOld is true, obj1 values are kept in case of conflicti
// Not used ATM
var merge = function (obj1, obj2, keepOld) {
if (typeof (obj1) !== "object" || typeof (obj2) !== "object") { return; }
Object.keys(obj2).forEach(function (k) {
//var v = obj2[k];
// If one of them is not an object or if we have a map and a array, don't override, create a new key
if (!obj1[k] || typeof(obj1[k]) !== "object" || typeof(obj2[k]) !== "object" ||
(getType(obj1[k]) !== getType(obj2[k]))) {
// We don't want to override the values in the object (username, preferences)
// These values should be the ones stored in the first object
if (keepOld) { return; }
if (obj1[k] === obj2[k]) { return; }
var nkey = findAvailableKey(obj1, k);
obj1[nkey] = copy(obj2[k]);
return;
}
// Else, they're both maps or both arrays
if (getType(obj1[k]) === "array" && getType(obj2[k]) === "array") {
var c = obj1[k].concat(obj2[k]);
obj1[k] = deduplicate(c);
return;
}
merge(obj1[k], obj2[k], keepOld);
});
};
var createFromPath = function (proxy, oldFo, path, href) {
var root = proxy.drive; var root = proxy.drive;
if (!oldFo.isFile(id)) { return; }
var error = function (msg) { var error = function (msg) {
console.error(msg || "Unable to find that path", path); console.error(msg || "Unable to find that path", path);
}; };
if (oldFo.isInTrashRoot(path)) { if (oldFo.isInTrashRoot(path)) {
href = oldFo.find(path.slice(0,3)); id = oldFo.find(path.slice(0,3));
path.pop(); path.pop();
} }
@ -86,7 +41,7 @@ define([
if (typeof(p) === "string") { if (typeof(p) === "string") {
if (getType(root) !== "object") { root = undefined; error(); return; } if (getType(root) !== "object") { root = undefined; error(); return; }
if (i === path.length - 1) { if (i === path.length - 1) {
root[findAvailableKey(root, p)] = href; root[Cryptpad.createChannelId()] = id;
return; return;
} }
next = getType(path[i+1]); next = getType(path[i+1]);
@ -111,7 +66,7 @@ define([
if (typeof(p) !== "number") { root = undefined; error(); return; } if (typeof(p) !== "number") { root = undefined; error(); return; }
if (getType(root) !== "array") { root = undefined; error(); return; } if (getType(root) !== "array") { root = undefined; error(); return; }
if (i === path.length - 1) { if (i === path.length - 1) {
if (root.indexOf(href) === -1) { root.push(href); } if (root.indexOf(id) === -1) { root.push(id); }
return; return;
} }
next = getType(path[i+1]); next = getType(path[i+1]);
@ -147,51 +102,57 @@ define([
return; return;
} }
if (parsed) { if (parsed) {
//merge(proxy, parsed, true);
var oldFo = FO.init(parsed.drive, { var oldFo = FO.init(parsed.drive, {
Cryptpad: Cryptpad Cryptpad: Cryptpad
}); });
oldFo.fixFiles(); var todo = function () {
var newData = Cryptpad.getStore().getProxy(); oldFo.fixFiles();
var newFo = newData.fo; var newData = Cryptpad.getStore().getProxy();
var newRecentPads = proxy.drive[Cryptpad.storageKey]; var newFo = newData.fo;
var newFiles = newFo.getFiles([newFo.FILES_DATA]); var oldRecentPads = parsed.drive[newFo.FILES_DATA];
var oldFiles = oldFo.getFiles([newFo.FILES_DATA]); var newRecentPads = proxy.drive[newFo.FILES_DATA];
oldFiles.forEach(function (href) { var newFiles = newFo.getFiles([newFo.FILES_DATA]);
// Do not migrate a pad if we already have it, it would create a duplicate in the drive var oldFiles = oldFo.getFiles([newFo.FILES_DATA]);
if (newFiles.indexOf(href) !== -1) { return; } oldFiles.forEach(function (id) {
// If we have a stronger version, do not add the current href var href = oldRecentPads[id].href;
if (Cryptpad.findStronger(href, newRecentPads)) { console.log(href); return; } // Do not migrate a pad if we already have it, it would create a duplicate in the drive
// If we have a weaker version, replace the href by the new one if (newFiles.indexOf(id) !== -1) { return; }
// NOTE: if that weaker version is in the trash, the strong one will be put in unsorted // If we have a stronger version, do not add the current href
var weaker = Cryptpad.findWeaker(href, newRecentPads); if (Cryptpad.findStronger(href, newRecentPads)) { return; }
if (weaker) { // If we have a weaker version, replace the href by the new one
// Update RECENTPADS // NOTE: if that weaker version is in the trash, the strong one will be put in unsorted
newRecentPads.some(function (pad) { var weaker = Cryptpad.findWeaker(href, newRecentPads);
if (pad.href === weaker) { if (weaker) {
pad.href = href; // Update RECENTPADS
return true; newRecentPads.some(function (pad) {
} if (pad.href === weaker) {
pad.href = href;
return true;
}
return;
});
// Update the file in the drive
newFo.replace(weaker, href);
return; return;
}); }
// Update the file in the drive // Here it means we have a new href, so we should add it to the drive at its old location
newFo.replace(weaker, href); var paths = oldFo.findFile(id);
return; if (paths.length === 0) { return; }
} // Add the file data in our array and use the id to add the file
// Here it means we have a new href, so we should add it to the drive at its old location var data = oldFo.getFileData(id);
var paths = oldFo.findFile(href); if (data) {
if (paths.length === 0) { return; } newFo.pushData(data, function (err, id) {
createFromPath(proxy, oldFo, paths[0], href); if (err) { return void console.error("Cannot import file:", data, err); }
// Also, push the file data in our array createFromPath(proxy, oldFo, paths[0], id);
var data = oldFo.getFileData(href); });
if (data) { }
newRecentPads.push(data); });
if (!proxy.FS_hashes || !Array.isArray(proxy.FS_hashes)) {
proxy.FS_hashes = [];
} }
}); proxy.FS_hashes.push(localStorage.FS_hash);
if (!proxy.FS_hashes || !Array.isArray(proxy.FS_hashes)) { };
proxy.FS_hashes = []; oldFo.migrate(todo);
}
proxy.FS_hashes.push(localStorage.FS_hash);
} }
if (typeof(cb) === "function") { cb(); } if (typeof(cb) === "function") { cb(); }
}; };

@ -15,6 +15,7 @@ define([
var Messages = Cryptpad.Messages; var Messages = Cryptpad.Messages;
var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Cryptpad.storageKey; var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Cryptpad.storageKey;
var OLD_FILES_DATA = module.OLD_FILES_DATA = exp.OLD_FILES_DATA = Cryptpad.oldStorageKey;
var NEW_FOLDER_NAME = Messages.fm_newFolder; var NEW_FOLDER_NAME = Messages.fm_newFolder;
var NEW_FILE_NAME = Messages.fm_newFile; var NEW_FILE_NAME = Messages.fm_newFile;
@ -42,7 +43,7 @@ define([
var a = {}; var a = {};
a[ROOT] = {}; a[ROOT] = {};
a[TRASH] = {}; a[TRASH] = {};
a[FILES_DATA] = []; a[FILES_DATA] = {};
a[TEMPLATE] = []; a[TEMPLATE] = [];
return a; return a;
}; };
@ -53,15 +54,19 @@ define([
var compareFiles = function (fileA, fileB) { return fileA === fileB; }; var compareFiles = function (fileA, fileB) { return fileA === fileB; };
var isFile = exp.isFile = function (element) { var isFile = exp.isFile = function (element, allowStr) {
return typeof(element) === "string"; return typeof(element) === "number" ||
((typeof(files[OLD_FILES_DATA]) !== "undefined" || allowStr)
&& typeof(element) === "string");
}; };
exp.isReadOnlyFile = function (element) { exp.isReadOnlyFile = function (element) {
if (!isFile(element)) { return false; } if (!isFile(element)) { return false; }
var parsed = Cryptpad.parsePadUrl(element); var data = exp.getFileData(element);
var parsed = Cryptpad.parsePadUrl(data.href);
if (!parsed) { return false; } if (!parsed) { return false; }
var pHash = parsed.hashData; var pHash = parsed.hashData;
if (!pHash || pHash.type !== "pad") { return; }
return pHash && pHash.mode === 'view'; return pHash && pHash.mode === 'view';
}; };
@ -69,12 +74,12 @@ define([
return typeof(element) === "object"; return typeof(element) === "object";
}; };
exp.isFolderEmpty = function (element) { exp.isFolderEmpty = function (element) {
if (typeof(element) !== "object") { return false; } if (!isFolder(element)) { return false; }
return Object.keys(element).length === 0; return Object.keys(element).length === 0;
}; };
exp.hasSubfolder = function (element, trashRoot) { exp.hasSubfolder = function (element, trashRoot) {
if (typeof(element) !== "object") { return false; } if (!isFolder(element)) { return false; }
var subfolder = 0; var subfolder = 0;
var addSubfolder = function (el) { var addSubfolder = function (el) {
subfolder += isFolder(el.element) ? 1 : 0; subfolder += isFolder(el.element) ? 1 : 0;
@ -92,7 +97,7 @@ define([
}; };
exp.hasFile = function (element, trashRoot) { exp.hasFile = function (element, trashRoot) {
if (typeof(element) !== "object") { return false; } if (!isFolder(element)) { return false; }
var file = 0; var file = 0;
var addFile = function (el) { var addFile = function (el) {
file += isFile(el.element) ? 1 : 0; file += isFile(el.element) ? 1 : 0;
@ -112,30 +117,22 @@ define([
// Get data from AllFiles (Cryptpad_RECENTPADS) // Get data from AllFiles (Cryptpad_RECENTPADS)
var getFileData = exp.getFileData = function (file) { var getFileData = exp.getFileData = function (file) {
if (!file) { return; } if (!file) { return; }
var res; return files[FILES_DATA][file] || {};
files[FILES_DATA].some(function(arr) {
var href = arr.href;
if (href === file) {
res = arr;
return true;
}
return false;
});
return res;
}; };
// Data from filesData // Data from filesData
var getTitle = exp.getTitle = function (href) { var getTitle = exp.getTitle = function (file, type) {
if (workgroup) { debug("No titles in workgroups"); return; } if (workgroup) { debug("No titles in workgroups"); return; }
var data = getFileData(href); var data = getFileData(file);
if (!href || !data) { if (!file || !data || !data.href) {
error("getTitle called with a non-existing href: ", href); error("getTitle called with a non-existing file id: ", file, data);
return; return;
} }
return data.title; if (type === 'title') { return data.title; }
if (type === 'name') { return data.filename; }
return data.filename || data.title || NEW_FILE_NAME;
}; };
// PATHS // PATHS
var comparePath = exp.comparePath = function (a, b) { var comparePath = exp.comparePath = function (a, b) {
@ -245,19 +242,25 @@ define([
} }
return ret; return ret;
}; };
_getFiles[FILES_DATA] = function () { _getFiles[OLD_FILES_DATA] = function () {
var ret = []; var ret = [];
files[FILES_DATA].forEach(function (el) { if (!files[OLD_FILES_DATA]) { return ret; }
files[OLD_FILES_DATA].forEach(function (el) {
if (el.href && ret.indexOf(el.href) === -1) { if (el.href && ret.indexOf(el.href) === -1) {
ret.push(el.href); ret.push(el.href);
} }
}); });
return ret; return ret;
}; };
_getFiles[FILES_DATA] = function () {
var ret = [];
if (!files[FILES_DATA]) { return ret; }
return Object.keys(files[FILES_DATA]).map(Number);
};
var getFiles = exp.getFiles = function (categories) { var getFiles = exp.getFiles = function (categories) {
var ret = []; var ret = [];
if (!categories || !categories.length) { if (!categories || !categories.length) {
categories = [ROOT, 'hrefArray', TRASH, FILES_DATA]; categories = [ROOT, 'hrefArray', TRASH, OLD_FILES_DATA, FILES_DATA];
} }
categories.forEach(function (c) { categories.forEach(function (c) {
if (typeof _getFiles[c] === "function") { if (typeof _getFiles[c] === "function") {
@ -267,8 +270,20 @@ define([
return Cryptpad.deduplicateString(ret); return Cryptpad.deduplicateString(ret);
}; };
var getIdFromHref = exp.getIdFromHref = function (href) {
var result;
getFiles([FILES_DATA]).some(function (id) {
if (files[FILES_DATA][id].href === href) {
result = id;
return true;
}
return;
});
return result;
};
// SEARCH // SEARCH
var _findFileInRoot = function (path, href) { var _findFileInRoot = function (path, file) {
if (!isPathIn(path, [ROOT, TRASH])) { return []; } if (!isPathIn(path, [ROOT, TRASH])) { return []; }
var paths = []; var paths = [];
var root = find(path); var root = find(path);
@ -279,7 +294,7 @@ define([
}; };
if (isFile(root)) { if (isFile(root)) {
if (compareFiles(href, root)) { if (compareFiles(file, root)) {
if (paths.indexOf(path) === -1) { if (paths.indexOf(path) === -1) {
paths.push(path); paths.push(path);
} }
@ -289,24 +304,25 @@ define([
for (var e in root) { for (var e in root) {
var nPath = path.slice(); var nPath = path.slice();
nPath.push(e); nPath.push(e);
_findFileInRoot(nPath, href).forEach(addPaths); _findFileInRoot(nPath, file).forEach(addPaths);
} }
return paths; return paths;
}; };
exp.findFileInRoot = function (href) { exp.findFileInRoot = function (file) {
return _findFileInRoot([ROOT], href); return _findFileInRoot([ROOT], file);
}; };
var _findFileInHrefArray = function (rootName, href) { var _findFileInHrefArray = function (rootName, file) {
if (!files[rootName]) { return []; }
var unsorted = files[rootName].slice(); var unsorted = files[rootName].slice();
var ret = []; var ret = [];
var i = -1; var i = -1;
while ((i = unsorted.indexOf(href, i+1)) !== -1){ while ((i = unsorted.indexOf(file, i+1)) !== -1){
ret.push([rootName, i]); ret.push([rootName, i]);
} }
return ret; return ret;
}; };
var _findFileInTrash = function (path, href) { var _findFileInTrash = function (path, file) {
var root = find(path); var root = find(path);
var paths = []; var paths = [];
var addPaths = function (p) { var addPaths = function (p) {
@ -320,7 +336,7 @@ define([
if (!Array.isArray(arr)) { return; } if (!Array.isArray(arr)) { return; }
var nPath = path.slice(); var nPath = path.slice();
nPath.push(key); nPath.push(key);
_findFileInTrash(nPath, href).forEach(addPaths); _findFileInTrash(nPath, file).forEach(addPaths);
}); });
} }
if (path.length === 2) { if (path.length === 2) {
@ -330,70 +346,44 @@ define([
nPath.push(i); nPath.push(i);
nPath.push('element'); nPath.push('element');
if (isFile(el.element)) { if (isFile(el.element)) {
if (compareFiles(href, el.element)) { if (compareFiles(file, el.element)) {
addPaths(nPath); addPaths(nPath);
} }
return; return;
} }
_findFileInTrash(nPath, href).forEach(addPaths); _findFileInTrash(nPath, file).forEach(addPaths);
}); });
} }
if (path.length >= 4) { if (path.length >= 4) {
_findFileInRoot(path, href).forEach(addPaths); _findFileInRoot(path, file).forEach(addPaths);
} }
return paths; return paths;
}; };
var findFile = exp.findFile = function (href) { var findFile = exp.findFile = function (file) {
var rootpaths = _findFileInRoot([ROOT], href); var rootpaths = _findFileInRoot([ROOT], file);
var templatepaths = _findFileInHrefArray(TEMPLATE, href); var templatepaths = _findFileInHrefArray(TEMPLATE, file);
var trashpaths = _findFileInTrash([TRASH], href); var trashpaths = _findFileInTrash([TRASH], file);
return rootpaths.concat(templatepaths, trashpaths); return rootpaths.concat(templatepaths, trashpaths);
}; };
exp.search = function (value) { exp.search = function (value) {
if (typeof(value) !== "string") { return []; } if (typeof(value) !== "string") { return []; }
var res = []; var res = [];
// Search in ROOT
var findIn = function (root) {
Object.keys(root).forEach(function (k) {
if (isFile(root[k])) {
if (k.toLowerCase().indexOf(value.toLowerCase()) !== -1) {
res.push(root[k]);
}
return;
}
findIn(root[k]);
});
};
findIn(files[ROOT]);
// Search in TRASH
var trash = files[TRASH];
Object.keys(trash).forEach(function (k) {
if (k.toLowerCase().indexOf(value.toLowerCase()) !== -1) {
trash[k].forEach(function (el) {
if (isFile(el.element)) {
res.push(el.element);
}
});
}
trash[k].forEach(function (el) {
if (isFolder(el.element)) {
findIn(el.element);
}
});
});
// Search title // Search title
var allFilesList = files[FILES_DATA].slice(); var allFilesList = files[FILES_DATA];
allFilesList.forEach(function (t) { var lValue = value.toLowerCase();
if (t.title && t.title.toLowerCase().indexOf(value.toLowerCase()) !== -1) { getFiles([FILES_DATA]).forEach(function (id) {
res.push(t.href); var data = allFilesList[id];
if ((data.title && data.title.toLowerCase().indexOf(lValue) !== -1) ||
(data.filename && data.filename.toLowerCase().indexOf(lValue) !== -1)) {
res.push(id);
} }
}); });
// Search Href // Search Href
var href = Cryptpad.getRelativeHref(value); var href = Cryptpad.getRelativeHref(value);
if (href) { if (href) {
res.push(href); var id = getIdFromHref(href);
if (id) { res.push(id); }
} }
res = Cryptpad.deduplicateString(res); res = Cryptpad.deduplicateString(res);
@ -425,27 +415,24 @@ define([
}; };
// FILES DATA // FILES DATA
var pushFileData = exp.pushData = function (data, cb) { exp.pushData = function (data, cb) {
if (typeof cb !== "function") { cb = function () {}; } if (typeof cb !== "function") { cb = function () {}; }
var todo = function () { var todo = function () {
files[FILES_DATA].push(data); var id = Cryptpad.createRandomInteger();
cb(); files[FILES_DATA][id] = data;
cb(null, id);
}; };
if (!Cryptpad.isLoggedIn() || !AppConfig.enablePinning) { return void todo(); } if (!Cryptpad.isLoggedIn() || !AppConfig.enablePinning || config.testMode) {
return void todo();
}
Cryptpad.pinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e) { Cryptpad.pinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
todo(); todo();
}); });
}; };
var spliceFileData = exp.removeData = function (idx) { var spliceFileData = exp.removeData = function (id) {
var data = files[FILES_DATA][idx]; files[FILES_DATA][id] = undefined;
if (typeof data === "object" && Cryptpad.isLoggedIn() && AppConfig.enablePinning) { delete files[FILES_DATA][id];
Cryptpad.unpinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e, hash) {
if (e) { return void logError(e); }
debug('UNPIN', hash);
});
}
files[FILES_DATA].splice(idx, 1);
}; };
// MOVE // MOVE
@ -492,16 +479,10 @@ define([
} }
} }
// Move to root // Move to root
var name; var newName = isFile(element) ?
if (isPathIn(elementPath, ['hrefArray'])) { getAvailableName(newParent, Cryptpad.createChannelId()) :
name = getTitle(element); isInTrashRoot(elementPath) ?
} else if (isInTrashRoot(elementPath)) { elementPath[1] : elementPath.pop();
// Element from the trash root: elementPath = [TRASH, "{dirName}", 0, 'element']
name = elementPath[1];
} else {
name = elementPath[elementPath.length-1];
}
var newName = !isPathIn(elementPath, [ROOT]) ? getAvailableName(newParent, name) : name;
if (typeof(newParent[newName]) !== "undefined") { if (typeof(newParent[newName]) !== "undefined") {
log(Messages.fo_unavailableName); log(Messages.fo_unavailableName);
@ -522,7 +503,7 @@ define([
return; return;
} }
// Try to copy, and if success, remove the element from the old location // Try to copy, and if success, remove the element from the old location
if (copyElement(p, newPath)) { if (copyElement(p.slice(), newPath)) {
toRemove.push(p); toRemove.push(p);
} }
}); });
@ -538,11 +519,10 @@ define([
// ADD // ADD
var add = exp.add = function (data, path) { var add = exp.add = function (id, path) {
if (!Cryptpad.isLoggedIn()) { return; } if (!Cryptpad.isLoggedIn()) { return; }
var data = files[FILES_DATA][id];
if (!data || typeof(data) !== "object") { return; } if (!data || typeof(data) !== "object") { return; }
var href = data.href;
var name = data.title;
var newPath = path, parentEl; var newPath = path, parentEl;
if (path && !Array.isArray(path)) { if (path && !Array.isArray(path)) {
newPath = decodeURIComponent(path).split(','); newPath = decodeURIComponent(path).split(',');
@ -550,43 +530,20 @@ define([
// Add to href array // Add to href array
if (path && isPathIn(newPath, ['hrefArray'])) { if (path && isPathIn(newPath, ['hrefArray'])) {
parentEl = find(newPath); parentEl = find(newPath);
parentEl.push(href); parentEl.push(id);
return; return;
} }
// Add to root if path is ROOT or if no path // Add to root if path is ROOT or if no path
var filesList = getFiles([ROOT, TRASH, 'hrefArray']); var filesList = getFiles([ROOT, TRASH, 'hrefArray']);
if ((path && isPathIn(newPath, [ROOT]) || filesList.indexOf(href) === -1) && name) { if (path && isPathIn(newPath, [ROOT]) || filesList.indexOf(id) === -1) {
parentEl = find(newPath || [ROOT]); parentEl = find(newPath || [ROOT]);
if (parentEl) { if (parentEl) {
var newName = getAvailableName(parentEl, name); var newName = getAvailableName(parentEl, Cryptpad.createChannelId());
parentEl[newName] = href; parentEl[newName] = id;
return; return;
} }
} }
}; };
exp.addFile = function (filePath, name, type, cb) {
var parentEl = findElement(files, filePath);
var fileName = getAvailableName(parentEl, name || NEW_FILE_NAME);
var href = '/' + type + '/#' + Cryptpad.createRandomHash();
pushFileData({
href: href,
title: fileName,
atime: +new Date(),
ctime: +new Date()
}, function (err) {
if (err) {
logError(err);
return void cb(err);
}
parentEl[fileName] = href;
var newPath = filePath.slice();
newPath.push(fileName);
cb(void 0, {
newPath: newPath
});
});
};
exp.addFolder = function (folderPath, name, cb) { exp.addFolder = function (folderPath, name, cb) {
var parentEl = find(folderPath); var parentEl = find(folderPath);
var folderName = getAvailableName(parentEl, name || NEW_FOLDER_NAME); var folderName = getAvailableName(parentEl, name || NEW_FOLDER_NAME);
@ -600,19 +557,15 @@ define([
// FORGET (move with href not path) // FORGET (move with href not path)
exp.forget = function (href) { exp.forget = function (href) {
var id = getIdFromHref(href);
if (!id) { return; }
if (!Cryptpad.isLoggedIn()) { if (!Cryptpad.isLoggedIn()) {
// delete permanently // delete permanently
var data = getFileData(href); exp.removePadAttribute(href);
if (data) { spliceFileData(id);
var i = find([FILES_DATA]).indexOf(data);
if (i !== -1) {
exp.removePadAttribute(href);
spliceFileData(i);
}
}
return; return;
} }
var paths = findFile(href); var paths = findFile(id);
move(paths, [TRASH]); move(paths, [TRASH]);
}; };
@ -634,29 +587,21 @@ define([
}); });
}; };
var checkDeletedFiles = function () { var checkDeletedFiles = function () {
// Nothing in FILES_DATA for workgroups // Nothing in OLD_FILES_DATA for workgroups
if (workgroup || !Cryptpad.isLoggedIn()) { return; } if (workgroup || !Cryptpad.isLoggedIn()) { return; }
var filesList = getFiles([ROOT, 'hrefArray', TRASH]); var filesList = getFiles([ROOT, 'hrefArray', TRASH]);
var toRemove = []; var fData = files[FILES_DATA];
files[FILES_DATA].forEach(function (arr) { getFiles([FILES_DATA]).forEach(function (id) {
var f = arr.href; if (filesList.indexOf(id) === -1) {
if (filesList.indexOf(f) === -1) { removePadAttribute(fData[id].href);
toRemove.push(arr); spliceFileData(id);
}
});
toRemove.forEach(function (f) {
var idx = files[FILES_DATA].indexOf(f);
if (idx !== -1) {
debug("Removing", f, "from filesData");
spliceFileData(idx);
removePadAttribute(f.href);
} }
}); });
}; };
var deleteHrefs = function (hrefs) { var deleteHrefs = function (ids) {
hrefs.forEach(function (obj) { ids.forEach(function (obj) {
var idx = files[obj.root].indexOf(obj.href); var idx = files[obj.root].indexOf(obj.id);
files[obj.root].splice(idx, 1); files[obj.root].splice(idx, 1);
}); });
}; };
@ -673,30 +618,26 @@ define([
var allFilesPaths = paths.filter(function(x) { return isPathIn(x, [FILES_DATA]); }); var allFilesPaths = paths.filter(function(x) { return isPathIn(x, [FILES_DATA]); });
if (!Cryptpad.isLoggedIn()) { if (!Cryptpad.isLoggedIn()) {
var toSplice = [];
allFilesPaths.forEach(function (path) { allFilesPaths.forEach(function (path) {
var el = find(path); var el = find(path);
toSplice.push(el); if (!el) { return; }
}); var id = getIdFromHref(el.href);
toSplice.forEach(function (el) { if (!id) { return; }
var i = find([FILES_DATA]).indexOf(el); spliceFileData(id);
if (i === -1) { return; }
removePadAttribute(el.href); removePadAttribute(el.href);
console.log(el.href);
spliceFileData(i);
}); });
return; return;
} }
var hrefs = []; var ids = [];
hrefPaths.forEach(function (path) { hrefPaths.forEach(function (path) {
var href = find(path); var id = find(path);
hrefs.push({ ids.push({
root: path[0], root: path[0],
href: href id: id
}); });
}); });
deleteHrefs(hrefs); deleteHrefs(ids);
rootPaths.forEach(function (path) { rootPaths.forEach(function (path) {
var parentPath = path.slice(); var parentPath = path.slice();
@ -727,7 +668,7 @@ define([
deleteMultipleTrashRoot(trashRoot); deleteMultipleTrashRoot(trashRoot);
// In some cases, we want to remove pads from a location without removing them from // In some cases, we want to remove pads from a location without removing them from
// FILES_DATA (replaceHref) // OLD_FILES_DATA (replaceHref)
if (!nocheck) { checkDeletedFiles(); } if (!nocheck) { checkDeletedFiles(); }
}; };
exp.delete = function (paths, cb, nocheck) { exp.delete = function (paths, cb, nocheck) {
@ -746,65 +687,69 @@ define([
logError('Renaming `root` is forbidden'); logError('Renaming `root` is forbidden');
return; return;
} }
if (!newName || newName.trim() === "") { return; }
// Copy the element path and remove the last value to have the parent path and the old name // Copy the element path and remove the last value to have the parent path and the old name
var element = find(path); var element = find(path);
var parentPath = path.slice();
var oldName = parentPath.pop(); // Folders
if (oldName === newName) { if (isFolder(element)) {
var parentPath = path.slice();
var oldName = parentPath.pop();
if (!newName || !newName.trim() || oldName === newName) { return; }
var parentEl = find(parentPath);
if (typeof(parentEl[newName]) !== "undefined") {
log(Messages.fo_existingNameError);
return;
}
parentEl[newName] = element;
parentEl[oldName] = undefined;
delete parentEl[oldName];
if (typeof cb === "function") { cb(); }
return; return;
} }
var parentEl = find(parentPath);
if (typeof(parentEl[newName]) !== "undefined") { // Files
log(Messages.fo_existingNameError); var data = files[FILES_DATA][element];
if (!data) { return; }
if (!newName || newName.trim() === "") {
data.filename = undefined;
delete data.filename;
if (typeof cb === "function") { cb(); }
return; return;
} }
parentEl[newName] = element; if (getTitle(element, 'name') === newName) { return; }
parentEl[oldName] = undefined; data.filename = newName;
delete parentEl[oldName];
if (typeof cb === "function") { cb(); } if (typeof cb === "function") { cb(); }
}; };
// REPLACE // REPLACE
var replaceFile = function (path, o, n) {
var root = find(path);
if (isFile(root)) { return; }
for (var e in root) {
if (isFile(root[e])) {
if (compareFiles(o, root[e])) {
root[e] = n;
}
} else {
var nPath = path.slice();
nPath.push(e);
replaceFile(nPath, o, n);
}
}
};
// Replace a href by a stronger one everywhere in the drive (except FILES_DATA)
exp.replace = function (o, n) { exp.replace = function (o, n) {
if (!isFile(o) || !isFile(n)) { return; } var idO = getIdFromHref(o);
var paths = findFile(o); if (!idO || !isFile(idO)) { return; }
var data = 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.
// This is use with setPadTitle when we open a stronger version of a deleted pad
exp.restoreHref = function (href) {
var idO = getIdFromHref(href);
if (!idO || !isFile(idO)) { return; }
var paths = findFile(idO);
// Remove all the occurences in the trash // Remove all the occurences in the trash
// Replace all the occurences not in the trash // If all the occurences are in the trash or no occurence, add the pad to root
// If all the occurences are in the trash or no occurence, add the pad to unsorted
var allInTrash = true; var allInTrash = true;
paths.forEach(function (p) { paths.forEach(function (p) {
if (p[0] === TRASH) { if (p[0] === TRASH) {
exp.delete(p, null, true); // 3rd parameter means skip "checkDeletedFiles" exp.delete(p, null, true); // 3rd parameter means skip "checkDeletedFiles"
return; return;
} else {
allInTrash = false;
var parentPath = p.slice();
var key = parentPath.pop();
var parentEl = find(parentPath);
parentEl[key] = n;
} }
allInTrash = false;
}); });
if (allInTrash) { if (allInTrash) {
add(n); add(idO);
} }
}; };
@ -812,12 +757,104 @@ define([
* INTEGRITY CHECK * INTEGRITY CHECK
*/ */
exp.migrate = function (cb) {
// Make sure unsorted doesn't exist anymore
// Note: Unsorted only works with the old structure where pads are href
// It should be called before the migration code
var fixUnsorted = function () {
if (!files[UNSORTED] || !files[OLD_FILES_DATA]) { return; }
debug("UNSORTED still exists in the object, removing it...");
var us = files[UNSORTED];
if (us.length === 0) {
delete files[UNSORTED];
return;
}
us.forEach(function (el) {
if (typeof el !== "string") {
return;
}
var data = files[OLD_FILES_DATA].filter(function (x) {
return x.href === el;
});
if (data.length === 0) {
files[OLD_FILES_DATA].push({
href: el
});
}
return;
});
delete files[UNSORTED];
};
// mergeDrive...
var migrateToNewFormat = function (todo) {
if (!files[OLD_FILES_DATA]) {
return void todo();
}
try {
debug("Migrating file system...");
files.migrate = 1;
if (exp.rt) { exp.rt.sync(); }
window.setTimeout(function () {
var oldData = files[OLD_FILES_DATA].slice();
if (!files[FILES_DATA]) {
files[FILES_DATA] = {};
}
var newData = files[FILES_DATA];
//var oldFiles = oldData.map(function (o) { return o.href; });
oldData.forEach(function (obj) {
if (!obj || !obj.href) { return; }
var href = obj.href;
var id = Cryptpad.createRandomInteger();
var paths = findFile(href);
var data = obj;
var key = Cryptpad.createChannelId();
if (data) {
newData[id] = data;
} else {
newData[id] = {href: href};
}
paths.forEach(function (p) {
var parentPath = p.slice();
var okey = parentPath.pop(); // get the parent
var parent = find(parentPath);
if (isInTrashRoot(p)) {
parent.element = id;
newData[id].filename = p[1];
return;
}
if (isPathIn(p, ['hrefArray'])) {
parent[okey] = id;
return;
}
// else root or trash (not trashroot)
parent[key] = id;
newData[id].filename = okey;
delete parent[okey];
});
});
files[OLD_FILES_DATA] = undefined;
delete files[OLD_FILES_DATA];
files.migrate = undefined;
delete files.migrate;
console.log('done');
todo();
}, 300);
} catch(e) {
console.error(e);
todo();
}
};
fixUnsorted();
migrateToNewFormat(cb);
};
exp.fixFiles = function () { exp.fixFiles = function () {
// Explore the tree and check that everything is correct: // Explore the tree and check that everything is correct:
// * 'root', 'trash', 'unsorted' and 'filesData' exist and are objects // * 'root', 'trash', 'unsorted' and 'filesData' exist and are objects
// * ROOT: Folders are objects, files are href // * ROOT: Folders are objects, files are href
// * TRASH: Trash root contains only arrays, each element of the array is an object {element:.., path:..} // * TRASH: Trash root contains only arrays, each element of the array is an object {element:.., path:..}
// * FILES_DATA: - Data (title, cdate, adte) are stored in filesData. filesData contains only href keys linking to object with title, cdate, adate. // * OLD_FILES_DATA: - Data (title, cdate, adte) are stored in filesData. filesData contains only href keys linking to object with title, cdate, adate.
// - Dates (adate, cdate) can be parsed/formatted // - Dates (adate, cdate) can be parsed/formatted
// - All files in filesData should be either in 'root', 'trash' or 'unsorted'. If that's not the case, copy the fily to 'unsorted' // - All files in filesData should be either in 'root', 'trash' or 'unsorted'. If that's not the case, copy the fily to 'unsorted'
// * TEMPLATE: Contains only files (href), and does not contains files that are in ROOT // * TEMPLATE: Contains only files (href), and does not contains files that are in ROOT
@ -829,12 +866,23 @@ define([
if (typeof(files[ROOT]) !== "object") { debug("ROOT was not an object"); files[ROOT] = {}; } if (typeof(files[ROOT]) !== "object") { debug("ROOT was not an object"); files[ROOT] = {}; }
var element = elem || files[ROOT]; var element = elem || files[ROOT];
for (var el in element) { for (var el in element) {
if (!isFile(element[el]) && !isFolder(element[el])) { if (!isFile(element[el], true) && !isFolder(element[el])) {
debug("An element in ROOT was not a folder nor a file. ", element[el]); debug("An element in ROOT was not a folder nor a file. ", element[el]);
element[el] = undefined; element[el] = undefined;
delete element[el]; delete element[el];
} else if (isFolder(element[el])) { continue;
}
if (isFolder(element[el])) {
fixRoot(element[el]); fixRoot(element[el]);
continue;
}
if (typeof element[el] === "string") {
// We have an old file (href) which is not in filesData: add it
var id = Cryptpad.createRandomInteger();
var key = Cryptpad.createChannelId();
files[FILES_DATA][id] = {href: element[el], filename: el};
element[key] = id;
delete element[el];
} }
} }
}; };
@ -842,47 +890,38 @@ define([
if (typeof(files[TRASH]) !== "object") { debug("TRASH was not an object"); files[TRASH] = {}; } if (typeof(files[TRASH]) !== "object") { debug("TRASH was not an object"); files[TRASH] = {}; }
var tr = files[TRASH]; var tr = files[TRASH];
var toClean; var toClean;
var addToClean = function (obj, idx) { var addToClean = function (obj, idx, el) {
if (typeof(obj) !== "object") { toClean.push(idx); return; } if (typeof(obj) !== "object") { toClean.push(idx); return; }
if (!isFile(obj.element) && !isFolder(obj.element)) { toClean.push(idx); return; } if (!isFile(obj.element, true) && !isFolder(obj.element)) { toClean.push(idx); return; }
if (!$.isArray(obj.path)) { toClean.push(idx); return; } if (!$.isArray(obj.path)) { toClean.push(idx); return; }
if (typeof obj.element === "string") {
// We have an old file (href) which is not in filesData: add it
var id = Cryptpad.createRandomInteger();
files[FILES_DATA][id] = {href: obj.element, filename: el};
obj.element = id;
}
if (isFolder(obj.element)) { fixRoot(obj.element); }
}; };
for (var el in tr) { for (var el in tr) {
if (!$.isArray(tr[el])) { if (!Array.isArray(tr[el])) {
debug("An element in TRASH root is not an array. ", tr[el]); debug("An element in TRASH root is not an array. ", tr[el]);
tr[el] = undefined; tr[el] = undefined;
delete tr[el]; delete tr[el];
} else if (tr[el].length === 0) {
debug("Empty array in TRASH root. ", tr[el]);
tr[el] = undefined;
delete tr[el];
} else { } else {
toClean = []; toClean = [];
tr[el].forEach(addToClean); for (var j=0; j<tr[el].length; j++) {
addToClean(tr[el][j], j, el);
}
for (var i = toClean.length-1; i>=0; i--) { for (var i = toClean.length-1; i>=0; i--) {
tr[el].splice(toClean[i], 1); tr[el].splice(toClean[i], 1);
} }
} }
} }
}; };
// Make sure unsorted doesn't exist anymore
var fixUnsorted = function () {
if (!files[UNSORTED]) { return; }
debug("UNSORTED still exists in the object, removing it...");
var us = files[UNSORTED];
if (us.length === 0) {
delete files[UNSORTED];
return;
}
var rootFiles = getFiles([ROOT, TEMPLATE]).slice();
var root = find([ROOT]);
us.forEach(function (el) {
if (!isFile(el) || rootFiles.indexOf(el) !== -1) {
return;
}
var data = getFileData(el);
var name = data ? data.title : NEW_FILE_NAME;
var newName = getAvailableName(root, name);
root[newName] = el;
});
delete files[UNSORTED];
};
var fixTemplate = function () { var fixTemplate = function () {
if (!Array.isArray(files[TEMPLATE])) { debug("TEMPLATE was not an array"); files[TEMPLATE] = []; } if (!Array.isArray(files[TEMPLATE])) { debug("TEMPLATE was not an array"); files[TEMPLATE] = []; }
files[TEMPLATE] = Cryptpad.deduplicateString(files[TEMPLATE].slice()); files[TEMPLATE] = Cryptpad.deduplicateString(files[TEMPLATE].slice());
@ -890,51 +929,54 @@ define([
var rootFiles = getFiles([ROOT]).slice(); var rootFiles = getFiles([ROOT]).slice();
var toClean = []; var toClean = [];
us.forEach(function (el, idx) { us.forEach(function (el, idx) {
if (!isFile(el) || rootFiles.indexOf(el) !== -1) { if (!isFile(el, true) || rootFiles.indexOf(el) !== -1) {
toClean.push(idx); toClean.push(idx);
} }
if (typeof el === "string") {
// We have an old file (href) which is not in filesData: add it
var id = Cryptpad.createRandomInteger();
files[FILES_DATA][id] = {href: el};
us[idx] = id;
}
}); });
toClean.forEach(function (idx) { toClean.forEach(function (idx) {
us.splice(idx, 1); us.splice(idx, 1);
}); });
}; };
var fixFilesData = function () { var fixFilesData = function () {
if (!$.isArray(files[FILES_DATA])) { debug("FILES_DATA was not an array"); files[FILES_DATA] = []; } if (typeof files[FILES_DATA] !== "object") { debug("OLD_FILES_DATA was not an object"); files[FILES_DATA] = {}; }
var fd = files[FILES_DATA]; var fd = files[FILES_DATA];
var rootFiles = getFiles([ROOT, TRASH, 'hrefArray']); var rootFiles = getFiles([ROOT, TRASH, 'hrefArray']);
var root = find([ROOT]); var root = find([ROOT]);
var toClean = []; var toClean = [];
fd.forEach(function (el) { for (var id in fd) {
id = Number(id);
var el = fd[id];
if (!el || typeof(el) !== "object") { if (!el || typeof(el) !== "object") {
debug("An element in filesData was not an object.", el); debug("An element in filesData was not an object.", el);
toClean.push(el); toClean.push(id);
return; continue;
} }
if (!el.href) { if (!el.href) {
debug("Rmoving an element in filesData with a missing href.", el); debug("Removing an element in filesData with a missing href.", el);
toClean.push(el); toClean.push(id);
return; continue;
} }
if (Cryptpad.isLoggedIn() && rootFiles.indexOf(el.href) === -1) { if (Cryptpad.isLoggedIn() && rootFiles.indexOf(id) === -1) {
debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", el); debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el);
var name = el.title || NEW_FILE_NAME; var newName = Cryptpad.createChannelId();
var newName = getAvailableName(root, name); root[newName] = id;
root[newName] = el.href; continue;
return;
}
});
toClean.forEach(function (el) {
var idx = fd.indexOf(el);
if (idx !== -1) {
spliceFileData(idx);
} }
}
toClean.forEach(function (id) {
spliceFileData(id);
}); });
}; };
fixRoot(); fixRoot();
fixTrashRoot(); fixTrashRoot();
if (!workgroup) { if (!workgroup) {
fixUnsorted();
fixTemplate(); fixTemplate();
fixFilesData(); fixFilesData();
} }
@ -948,6 +990,5 @@ define([
return exp; return exp;
}; };
return module; return module;
}); });

@ -485,6 +485,7 @@ define([
}; };
var getDate = function (sDate) { var getDate = function (sDate) {
if (!sDate) { return ''; }
var ret = sDate.toString(); var ret = sDate.toString();
try { try {
var date = new Date(sDate); var date = new Date(sDate);
@ -501,12 +502,15 @@ define([
return ret; return ret;
}; };
var openFile = function (fileEl, name) { var openFile = function (el, href) {
if (name) { if (!href) {
sessionStorage[Cryptpad.newPadNameKey] = name; var data = filesOp.getFileData(el);
if (!data || !data.href) {
return void logError("Missing data for the file", el, data);
}
href = data.href;
} }
window.open(fileEl); window.open(href);
delete sessionStorage[Cryptpad.newPadNameKey];
}; };
var refresh = APP.refresh = function () { var refresh = APP.refresh = function () {
@ -529,7 +533,8 @@ define([
$name = $element.find('> .element'); $name = $element.find('> .element');
} }
$name.hide(); $name.hide();
var name = path[path.length - 1]; var el = filesOp.find(path);
var name = filesOp.isFile(el) ? filesOp.getTitle(el) : path[path.length - 1];
var $input = $('<input>', { var $input = $('<input>', {
placeholder: name, placeholder: name,
value: name value: name
@ -934,19 +939,9 @@ define([
}; };
var getElementName = function (path) { var getElementName = function (path) {
// Trash root var file = filesOp.find(path);
if (filesOp.isInTrashRoot(path)) { return path[0]; } if (!file || !filesOp.isFile(file)) { return '???'; }
// Root or trash return filesOp.getTitle(file);
if (filesOp.isPathIn(path, [ROOT, TRASH])) { return path[path.length - 1]; }
// Unsorted or template
if (filesOp.isPathIn(path, ['hrefArray'])) {
var file = filesOp.find(path);
if (filesOp.isFile(file) && filesOp.getTitle(file)) {
return filesOp.getTitle(file);
}
}
// default
return "???";
}; };
// filesOp.moveElements is able to move several paths to a new location, including // filesOp.moveElements is able to move several paths to a new location, including
// the Trash or the "Unsorted files" folder // the Trash or the "Unsorted files" folder
@ -966,8 +961,9 @@ define([
} }
var msg = Messages._getKey('fm_removeSeveralDialog', [paths.length]); var msg = Messages._getKey('fm_removeSeveralDialog', [paths.length]);
if (paths.length === 1) { if (paths.length === 1) {
var path = paths[0]; var path = paths[0].slice();
var name = path[0] === TEMPLATE ? filesOp.getTitle(filesOp.find(path)) : path[path.length - 1]; var el = filesOp.find(path);
var name = filesOp.isFile(el) ? getElementName(path) : path.pop();
msg = Messages._getKey('fm_removeDialog', [name]); msg = Messages._getKey('fm_removeDialog', [name]);
} }
Cryptpad.confirm(msg, function (res) { Cryptpad.confirm(msg, function (res) {
@ -1094,30 +1090,27 @@ define([
// In list mode, display metadata from the filesData object // In list mode, display metadata from the filesData object
// _WORKGROUP_ : Do not display title, atime and ctime columns since we don't have files data // _WORKGROUP_ : Do not display title, atime and ctime columns since we don't have files data
var addFileData = function (element, key, $span, displayTitle) { var addFileData = function (element, $span) {
if (!filesOp.isFile(element)) { return; } if (!filesOp.isFile(element)) { return; }
var data = filesOp.getFileData(element);
if (!data) { return void logError("No data for the file", element); }
var name = filesOp.getTitle(element);
// The element with the class '.name' is underlined when the 'li' is hovered // The element with the class '.name' is underlined when the 'li' is hovered
var $name = $('<span>', {'class': 'name', title: key}).text(key); var $name = $('<span>', {'class': 'name', title: name}).text(name);
$span.html(''); $span.html('');
$span.append($name); $span.append($name);
if (!filesOp.getFileData(element)) { var hrefData = Cryptpad.parsePadUrl(data.href);
return;
}
var hrefData = Cryptpad.parsePadUrl(element);
var data = filesOp.getFileData(element);
var type = Messages.type[hrefData.type] || hrefData.type; var type = Messages.type[hrefData.type] || hrefData.type;
var $title = $('<span>', {'class': 'title listElement', title: data.title}).text(data.title);
var $type = $('<span>', {'class': 'type listElement', title: type}).text(type); var $type = $('<span>', {'class': 'type listElement', title: type}).text(type);
if (hrefData.hashData && hrefData.hashData.mode === 'view') { if (hrefData.hashData && hrefData.hashData.mode === 'view') {
$type.append(' (' + Messages.readonly+ ')'); $type.append(' (' + Messages.readonly+ ')');
} }
var $adate = $('<span>', {'class': 'atime listElement', title: getDate(data.atime)}).text(getDate(data.atime)); var $adate = $('<span>', {'class': 'atime listElement', title: getDate(data.atime)}).text(getDate(data.atime));
var $cdate = $('<span>', {'class': 'ctime listElement', title: getDate(data.ctime)}).text(getDate(data.ctime)); var $cdate = $('<span>', {'class': 'ctime listElement', title: getDate(data.ctime)}).text(getDate(data.ctime));
if (displayTitle && !isWorkgroup()) {
$span.append($title);
}
$span.append($type); $span.append($type);
if (!isWorkgroup()) { if (!isWorkgroup()) {
$span.append($adate).append($cdate); $span.append($adate).append($cdate);
@ -1136,9 +1129,15 @@ define([
$span.append($name).append($subfolders).append($files); $span.append($name).append($subfolders).append($files);
}; };
var getFileIcon = function (href) { var getFileIcon = function (id) {
var $icon = $fileIcon.clone(); var $icon = $fileIcon.clone();
var data = filesOp.getFileData(id);
if (!data) { return $icon; }
var href = data.href;
if (!href) { return $icon; }
if (href.indexOf('/pad/') !== -1) { $icon = $padIcon.clone(); } if (href.indexOf('/pad/') !== -1) { $icon = $padIcon.clone(); }
else if (href.indexOf('/code/') !== -1) { $icon = $codeIcon.clone(); } else if (href.indexOf('/code/') !== -1) { $icon = $codeIcon.clone(); }
else if (href.indexOf('/slide/') !== -1) { $icon = $slideIcon.clone(); } else if (href.indexOf('/slide/') !== -1) { $icon = $slideIcon.clone(); }
@ -1154,7 +1153,7 @@ define([
var isTrash = path[0] === TRASH; var isTrash = path[0] === TRASH;
var newPath = path.slice(); var newPath = path.slice();
var key; var key;
if (isTrash && $.isArray(elPath)) { if (isTrash && Array.isArray(elPath)) {
key = elPath[0]; key = elPath[0];
elPath.forEach(function (k) { newPath.push(k); }); elPath.forEach(function (k) { newPath.push(k); });
} else { } else {
@ -1179,7 +1178,7 @@ define([
if (isFolder) { if (isFolder) {
addFolderData(element, key, $element); addFolderData(element, key, $element);
} else { } else {
addFileData(element, key, $element, true); addFileData(element, $element);
} }
$element.prepend($icon).dblclick(function () { $element.prepend($icon).dblclick(function () {
if (isFolder) { if (isFolder) {
@ -1187,7 +1186,7 @@ define([
return; return;
} }
if (isTrash) { return; } if (isTrash) { return; }
openFile(root[key], key); openFile(root[key]);
}); });
$element.addClass(liClass); $element.addClass(liClass);
$element.data('path', newPath); $element.data('path', newPath);
@ -1252,10 +1251,13 @@ define([
if (APP.mobile()) { if (APP.mobile()) {
return $title; return $title;
} }
var el = path[0] === SEARCH ? undefined : filesOp.find(path);
path = path[0] === SEARCH ? path.slice(0,1) : path; path = path[0] === SEARCH ? path.slice(0,1) : path;
path.forEach(function (p, idx) { path.forEach(function (p, idx) {
if (isTrash && [2,3].indexOf(idx) !== -1) { return; } if (isTrash && [2,3].indexOf(idx) !== -1) { return; }
var name = p;
var $span = $('<span>', {'class': 'element'}); var $span = $('<span>', {'class': 'element'});
if (idx < path.length - 1) { if (idx < path.length - 1) {
if (!noStyle) { if (!noStyle) {
@ -1266,9 +1268,10 @@ define([
module.displayDirectory(path.slice(0, sliceEnd)); module.displayDirectory(path.slice(0, sliceEnd));
}); });
} }
} else if (idx > 0 && filesOp.isFile(el)) {
name = getElementName(path);
} }
var name = p;
if (idx === 0) { name = getPrettyName(p); } if (idx === 0) { name = getPrettyName(p); }
else { $title.append(' > '); } else { $title.append(' > '); }
@ -1414,24 +1417,12 @@ define([
$block.find('a.newFolder').click(function () { $block.find('a.newFolder').click(function () {
filesOp.addFolder(currentPath, null, onCreated); filesOp.addFolder(currentPath, null, onCreated);
}); });
$block.find('a.newdoc').click(function () {
var type = $(this).attr('data-type') || 'pad';
// We can't create a hash for files before uploading the file
if (type === 'file') { // TODO: remove when filename are gone?
sessionStorage[Cryptpad.newPadPathKey] = filesOp.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
window.open('/' + type + '/');
return;
}
var name = Cryptpad.getDefaultName({type: type});
filesOp.addFile(currentPath, name, type, onCreated);
});
} else {
$block.find('a.newdoc').click(function () {
var type = $(this).attr('data-type') || 'pad';
sessionStorage[Cryptpad.newPadPathKey] = filesOp.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
window.open('/' + type + '/');
});
} }
$block.find('a.newdoc').click(function () {
var type = $(this).attr('data-type') || 'pad';
sessionStorage[Cryptpad.newPadPathKey] = filesOp.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
window.open('/' + type + '/');
});
return $block; return $block;
}; };
@ -1521,67 +1512,44 @@ define([
} }
}; };
// _WORKGROUP_ : do not display title, atime and ctime in workgroups since we don't have files data // _WORKGROUP_ : do not display title, atime and ctime in workgroups since we don't have files data
var getFileListHeader = function (displayTitle) { var getFileListHeader = function () {
var $fihElement = $('<li>', {'class': 'file-header header listElement element'}); var $fihElement = $('<li>', {'class': 'file-header header listElement element'});
//var $fihElement = $('<span>', {'class': 'element'}).appendTo($fileHeader); //var $fihElement = $('<span>', {'class': 'element'}).appendTo($fileHeader);
var $fhIcon = $('<span>', {'class': 'icon'}); var $fhIcon = $('<span>', {'class': 'icon'});
var $fhName = $('<span>', {'class': 'name filename clickable'}).text(Messages.fm_fileName).click(onSortByClick); var $fhName = $('<span>', {'class': 'name filename clickable'}).text(Messages.fm_fileName).click(onSortByClick);
var $fhTitle = $('<span>', {'class': 'title clickable'}).text(Messages.fm_title).click(onSortByClick);
var $fhType = $('<span>', {'class': 'type clickable'}).text(Messages.fm_type).click(onSortByClick); var $fhType = $('<span>', {'class': 'type clickable'}).text(Messages.fm_type).click(onSortByClick);
var $fhAdate = $('<span>', {'class': 'atime clickable'}).text(Messages.fm_lastAccess).click(onSortByClick); var $fhAdate = $('<span>', {'class': 'atime clickable'}).text(Messages.fm_lastAccess).click(onSortByClick);
var $fhCdate = $('<span>', {'class': 'ctime clickable'}).text(Messages.fm_creation).click(onSortByClick); var $fhCdate = $('<span>', {'class': 'ctime clickable'}).text(Messages.fm_creation).click(onSortByClick);
// If displayTitle is false, it means the "name" is the title, so do not display the "name" header // If displayTitle is false, it means the "name" is the title, so do not display the "name" header
$fihElement.append($fhIcon); $fihElement.append($fhIcon).append($fhName).append($fhType);
if (displayTitle || isWorkgroup()) {
$fihElement.append($fhName);
} else {
$fhTitle.width('auto');
}
if (!isWorkgroup()) {
$fihElement.append($fhTitle);
}
$fihElement.append($fhType);
if (!isWorkgroup()) { if (!isWorkgroup()) {
$fihElement.append($fhAdate).append($fhCdate); $fihElement.append($fhAdate).append($fhCdate);
} }
addFileSortIcon($fihElement); addFileSortIcon($fihElement);
return $fihElement; return $fihElement;
//return $fileHeader;
}; };
var sortElements = function (folder, path, oldkeys, prop, asc, useHref, useData) { var sortElements = function (folder, path, oldkeys, prop, asc, useId) {
var root = filesOp.find(path); var root = filesOp.find(path);
var test = folder ? filesOp.isFolder : filesOp.isFile; var test = folder ? filesOp.isFolder : filesOp.isFile;
var keys; var keys = oldkeys.filter(function (e) {
if (!useData) { return useId ? test(e) : test(root[e]);
keys = oldkeys.filter(function (e) { });
return useHref ? test(e) : test(root[e]);
});
} else { keys = oldkeys.slice(); }
if (keys.length < 2) { return keys; } if (keys.length < 2) { return keys; }
var mult = asc ? 1 : -1; var mult = asc ? 1 : -1;
var getProp = function (el, prop) { var getProp = function (el, prop) {
if (prop) { if (folder) { return el.toLowerCase(); }
var element = useHref || useData ? el : root[el]; var id = useId ? el : root[el];
var e = useData ? element : filesOp.getFileData(element); var data = filesOp.getFileData(id);
if (!e) { if (!data) { return ''; }
e = { if (prop === 'type') {
href : element, var hrefData = Cryptpad.parsePadUrl(data.href);
title : Messages.fm_noname, return hrefData.type;
atime : 0, }
ctime : 0 if (prop === 'atime' || prop === 'ctime') {
}; return new Date(data[prop]);
}
if (prop === 'type') {
var hrefData = Cryptpad.parsePadUrl(e.href);
return hrefData.type;
}
if (prop === 'atime' || prop === 'ctime') {
return new Date(e[prop]);
}
return e && e.title ? e.title.toLowerCase() : '';
} }
return useData ? el.title.toLowerCase() : el.toLowerCase(); return filesOp.getTitle(id).toLowerCase();
}; };
keys.sort(function(a, b) { keys.sort(function(a, b) {
if (getProp(a, prop) < getProp(b, prop)) { return mult * -1; } if (getProp(a, prop) < getProp(b, prop)) { return mult * -1; }
@ -1591,7 +1559,6 @@ define([
return keys; return keys;
}; };
var sortTrashElements = function (folder, oldkeys, prop, asc) { var sortTrashElements = function (folder, oldkeys, prop, asc) {
//var root = files[TRASH];
var test = folder ? filesOp.isFolder : filesOp.isFile; var test = folder ? filesOp.isFolder : filesOp.isFile;
var keys = oldkeys.filter(function (e) { var keys = oldkeys.filter(function (e) {
return test(e.element); return test(e.element);
@ -1617,7 +1584,6 @@ define([
if (prop === 'atime' || prop === 'ctime') { if (prop === 'atime' || prop === 'ctime') {
return new Date(e[prop]); return new Date(e[prop]);
} }
return e.title.toLowerCase();
} }
return el.name.toLowerCase(); return el.name.toLowerCase();
}; };
@ -1649,27 +1615,27 @@ define([
$container.append($fileHeader); $container.append($fileHeader);
var keys = unsorted; var keys = unsorted;
var sortBy = Cryptpad.getLSAttribute(SORT_FILE_BY); var sortBy = Cryptpad.getLSAttribute(SORT_FILE_BY);
sortBy = sortBy === "" ? sortBy = 'title' : sortBy; sortBy = sortBy === "" ? sortBy = 'name' : sortBy;
var sortedFiles = sortElements(false, [rootName], keys, sortBy, !getSortFileDesc(), true); var sortedFiles = sortElements(false, [rootName], keys, sortBy, !getSortFileDesc(), true);
sortedFiles.forEach(function (href) { sortedFiles.forEach(function (id) {
var file = filesOp.getFileData(href); var file = filesOp.getFileData(id);
if (!file) { if (!file) {
//debug("Unsorted or template returns an element not present in filesData: ", href); //debug("Unsorted or template returns an element not present in filesData: ", href);
file = { title: Messages.fm_noname }; file = { title: Messages.fm_noname };
//return; //return;
} }
var idx = files[rootName].indexOf(href); var idx = files[rootName].indexOf(id);
var $icon = getFileIcon(href); var $icon = getFileIcon(id);
var ro = filesOp.isReadOnlyFile(href); var ro = filesOp.isReadOnlyFile(id);
// ro undefined mens it's an old hash which doesn't support read-only // ro undefined mens it's an old hash which doesn't support read-only
var roClass = typeof(ro) === 'undefined' ? ' noreadonly' : ro ? ' readonly' : ''; var roClass = typeof(ro) === 'undefined' ? ' noreadonly' : ro ? ' readonly' : '';
var $element = $('<li>', { var $element = $('<li>', {
'class': 'file-element element element-row' + roClass, 'class': 'file-element element element-row' + roClass,
draggable: draggable draggable: draggable
}); });
addFileData(href, file.title, $element, false); addFileData(id, $element);
$element.prepend($icon).dblclick(function () { $element.prepend($icon).dblclick(function () {
openFile(href); openFile(id);
}); });
var path = [rootName, idx]; var path = [rootName, idx];
$element.data('path', path); $element.data('path', path);
@ -1691,20 +1657,19 @@ define([
if (allfiles.length === 0) { return; } if (allfiles.length === 0) { return; }
var $fileHeader = getFileListHeader(false); var $fileHeader = getFileListHeader(false);
$container.append($fileHeader); $container.append($fileHeader);
var keys = allfiles; var keys = filesOp.getFiles([FILES_DATA]);
var sortedFiles = sortElements(false, [FILES_DATA], keys, Cryptpad.getLSAttribute(SORT_FILE_BY), !getSortFileDesc(), true);
var sortedFiles = sortElements(false, [FILES_DATA], keys, Cryptpad.getLSAttribute(SORT_FILE_BY), !getSortFileDesc(), false, true); sortedFiles.forEach(function (id) {
sortedFiles.forEach(function (file) { var $icon = getFileIcon(id);
var $icon = getFileIcon(file.href); var ro = filesOp.isReadOnlyFile(id);
var ro = filesOp.isReadOnlyFile(file.href); // ro undefined maens it's an old hash which doesn't support read-only
// ro undefined mens it's an old hash which doesn't support read-only
var roClass = typeof(ro) === 'undefined' ? ' noreadonly' : ro ? ' readonly' : ''; var roClass = typeof(ro) === 'undefined' ? ' noreadonly' : ro ? ' readonly' : '';
var $element = $('<li>', { 'class': 'file-element element element-row' + roClass }); var $element = $('<li>', { 'class': 'file-element element element-row' + roClass });
addFileData(file.href, file.title, $element, false); addFileData(id, $element);
$element.data('path', [FILES_DATA, allfiles.indexOf(file)]); $element.data('path', [FILES_DATA, id]);
$element.data('element', file.href); $element.data('element', id);
$element.prepend($icon).dblclick(function () { $element.prepend($icon).dblclick(function () {
openFile(file.href); openFile(id);
}); });
$element.click(function(e) { $element.click(function(e) {
e.stopPropagation(); e.stopPropagation();
@ -1720,24 +1685,18 @@ define([
var filesList = []; var filesList = [];
var root = files[TRASH]; var root = files[TRASH];
// Elements in the trash are JS arrays (several elements can have the same name) // Elements in the trash are JS arrays (several elements can have the same name)
[true,false].forEach(function (folder) { Object.keys(root).forEach(function (key) {
var testElement = filesOp.isFile; if (!Array.isArray(root[key])) {
if (!folder) { logError("Trash element has a wrong type", root[key]);
testElement = filesOp.isFolder; return;
} }
Object.keys(root).forEach(function (key) { root[key].forEach(function (el, idx) {
if (!$.isArray(root[key])) { if (!filesOp.isFile(el.element) && !filesOp.isFolder(el.element)) { return; }
logError("Trash element has a wrong type", root[key]); var spath = [key, idx, 'element'];
return; filesList.push({
} element: el.element,
root[key].forEach(function (el, idx) { spath: spath,
if (testElement(el.element)) { return; } name: key
var spath = [key, idx, 'element'];
filesList.push({
element: el.element,
spath: spath,
name: key
});
}); });
}); });
}); });
@ -1763,8 +1722,9 @@ define([
var parsed = Cryptpad.parsePadUrl(href); var parsed = Cryptpad.parsePadUrl(href);
var $table = $('<table>'); var $table = $('<table>');
var $icon = $('<td>', {'rowspan': '3', 'class': 'icon'}).append(getFileIcon(href)); var $icon = $('<td>', {'rowspan': '3', 'class': 'icon'}).append(getFileIcon(href));
var $title = $('<td>', {'class': 'col1 title'}).text(r.data.title).click(function () { var $title = $('<td>', {'class': 'col1 title'}).text(r.data.title)
openFile(r.data.href); .click(function () {
openFile(null, r.data.href);
}); });
var $typeName = $('<td>', {'class': 'label2'}).text(Messages.fm_type); var $typeName = $('<td>', {'class': 'label2'}).text(Messages.fm_type);
var $type = $('<td>', {'class': 'col2'}).text(Messages.type[parsed.type] || parsed.type); var $type = $('<td>', {'class': 'col2'}).text(Messages.type[parsed.type] || parsed.type);
@ -2152,29 +2112,12 @@ define([
}; };
var stringifyPath = function (path) { var stringifyPath = function (path) {
if (!$.isArray(path)) { return; } if (!Array.isArray(path)) { return; }
var rootName = function (s) {
var prettyName;
switch (s) {
case ROOT:
prettyName = ROOT_NAME;
break;
case FILES_DATA:
prettyName = FILES_DATA_NAME;
break;
case TRASH:
prettyName = TRASH_NAME;
break;
default:
prettyName = s;
}
return prettyName;
};
var $div = $('<div>'); var $div = $('<div>');
var i = 0; var i = 0;
var space = 10; var space = 10;
path.forEach(function (s) { path.forEach(function (s) {
if (i === 0) { s = rootName(s); } if (i === 0) { s = getPrettyName(s); }
$div.append($('<span>', {'style': 'margin: 0 0 0 ' + i * space + 'px;'}).text(s)); $div.append($('<span>', {'style': 'margin: 0 0 0 ' + i * space + 'px;'}).text(s));
$div.append($('<br>')); $div.append($('<br>'));
i++; i++;
@ -2182,11 +2125,15 @@ define([
return $div.html(); return $div.html();
}; };
var getReadOnlyUrl = APP.getRO = function (href) { var getReadOnlyUrl = APP.getRO = function (id) {
if (!filesOp.isFile(href)) { return; } if (!filesOp.isFile(id)) { return; }
var i = href.indexOf('#') + 1; var data = filesOp.getFileData(id);
var parsed = Cryptpad.parsePadUrl(href); if (!data) { return; }
var base = href.slice(0, i); var parsed = Cryptpad.parsePadUrl(data.href);
if (parsed.hashData.type !== "pad") { return; }
var origin = window.location.origin;
var i = data.href.indexOf('#') + 1;
var base = origin + data.href.slice(0, i);
var hrefsecret = Cryptpad.getSecrets(parsed.type, parsed.hash); var hrefsecret = Cryptpad.getSecrets(parsed.type, parsed.hash);
if (!hrefsecret.keys) { return; } if (!hrefsecret.keys) { return; }
var viewHash = Cryptpad.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys); var viewHash = Cryptpad.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys);
@ -2210,16 +2157,21 @@ define([
var base = window.location.origin; var base = window.location.origin;
var $d = $('<div>'); var $d = $('<div>');
$('<strong>').text(Messages.fc_prop).appendTo($d); $('<strong>').text(Messages.fc_prop).appendTo($d);
var data = filesOp.getFileData(el);
if (!data || !data.href) { return void cb(void 0, $d); }
$('<br>').appendTo($d); $('<br>').appendTo($d);
if (!ro) { if (!ro) {
$('<label>', {'for': 'propLink'}).text(Messages.editShare).appendTo($d); $('<label>', {'for': 'propLink'}).text(Messages.editShare).appendTo($d);
$('<input>', {'id': 'propLink', 'readonly': 'readonly', 'value': base + el}) $('<input>', {'id': 'propLink', 'readonly': 'readonly', 'value': base + data.href})
.click(function () { $(this).select(); }) .click(function () { $(this).select(); })
.appendTo($d); .appendTo($d);
} }
var parsed = Cryptpad.parsePadUrl(el);
var parsed = Cryptpad.parsePadUrl(data.href);
if (parsed.hashData && parsed.hashData.type === 'pad') { if (parsed.hashData && parsed.hashData.type === 'pad') {
var roLink = ro ? base + el : getReadOnlyUrl(base + el); var roLink = ro ? base + data.href : getReadOnlyUrl(el);
if (roLink) { if (roLink) {
$('<label>', {'for': 'propROLink'}).text(Messages.viewShare).appendTo($d); $('<label>', {'for': 'propROLink'}).text(Messages.viewShare).appendTo($d);
$('<input>', {'id': 'propROLink', 'readonly': 'readonly', 'value': roLink}) $('<input>', {'id': 'propROLink', 'readonly': 'readonly', 'value': roLink})
@ -2230,7 +2182,8 @@ define([
if (APP.loggedIn && AppConfig.enablePinning) { if (APP.loggedIn && AppConfig.enablePinning) {
// check the size of this file... // check the size of this file...
Cryptpad.getFileSize(el, function (e, bytes) { console.log(data.href);
Cryptpad.getFileSize(data.href, function (e, bytes) {
if (e) { if (e) {
// there was a problem with the RPC // there was a problem with the RPC
logError(e); logError(e);
@ -2293,7 +2246,7 @@ define([
var el = filesOp.find(p.path); var el = filesOp.find(p.path);
if (filesOp.isFolder(el)) { return; } if (filesOp.isFolder(el)) { return; }
var roUrl = getReadOnlyUrl(el); var roUrl = getReadOnlyUrl(el);
openFile(roUrl, false); openFile(null, roUrl);
}); });
} }
else if ($(this).hasClass('newfolder')) { else if ($(this).hasClass('newfolder')) {
@ -2337,7 +2290,7 @@ define([
if (filesOp.isPathIn(p.path, [FILES_DATA])) { el = el.href; } if (filesOp.isPathIn(p.path, [FILES_DATA])) { el = el.href; }
if (!el || filesOp.isFolder(el)) { return; } if (!el || filesOp.isFolder(el)) { return; }
var roUrl = getReadOnlyUrl(el); var roUrl = getReadOnlyUrl(el);
openFile(roUrl, false); openFile(null, roUrl);
}); });
} }
else if ($(this).hasClass('delete')) { else if ($(this).hasClass('delete')) {
@ -2387,8 +2340,8 @@ define([
} }
else if ($(this).hasClass("newdoc")) { else if ($(this).hasClass("newdoc")) {
var type = $(this).data('type') || 'pad'; var type = $(this).data('type') || 'pad';
var name = Cryptpad.getDefaultName({type: type}); sessionStorage[Cryptpad.newPadPathKey] = filesOp.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
filesOp.addFile(path, name, type, onCreated); window.open('/' + type + '/');
} }
module.hideMenu(); module.hideMenu();
}); });
@ -2550,6 +2503,12 @@ define([
} }
module.resetTree(); module.resetTree();
return false; return false;
}).on('change', ['drive', 'migrate'], function () {
var path = arguments[2];
var value = arguments[1];
if (path[1] === "migrate" && value === 1) {
if (APP.onDisconnect) { APP.onDisconnect(true); }
}
}); });
$iframe.find('#tree').mousedown(function () { $iframe.find('#tree').mousedown(function () {
@ -2588,8 +2547,10 @@ define([
atime: new Date().toISOString(), atime: new Date().toISOString(),
ctime: new Date().toISOString() ctime: new Date().toISOString()
}; };
filesOp.pushData(data); filesOp.pushData(data, function (e, id) {
filesOp.add(data); if (e) { return void console.error("Error while creating the default pad:", e); } // TODO LIMIT?
filesOp.add(id);
});
if (typeof(cb) === "function") { cb(); } if (typeof(cb) === "function") { cb(); }
}); });
delete sessionStorage.createReadme; delete sessionStorage.createReadme;
@ -2777,11 +2738,11 @@ define([
Cryptpad.removeLoadingScreen(); Cryptpad.removeLoadingScreen();
}); });
}; };
var onDisconnect = function () { var onDisconnect = APP.onDisconnect = function (noAlert) {
setEditable(false); setEditable(false);
if (APP.refresh) { APP.refresh(); } if (APP.refresh) { APP.refresh(); }
APP.toolbar.failed(); APP.toolbar.failed();
Cryptpad.alert(Messages.common_connectionLost, undefined, true); if (!noAlert) { Cryptpad.alert(Messages.common_connectionLost, undefined, true); }
}; };
var onReconnect = function (info) { var onReconnect = function (info) {
setEditable(true); setEditable(true);

@ -0,0 +1,382 @@
define([
'/common/cryptpad-common.js',
'/common/userObject.js',
],function (Cryptpad, FO) {
var module = {};
var href1 = "/pad/#/1/edit/a798u+miu2tg5b-QaP9SvA/UIPoGUPewZscBUFhNIi+eBBM/";
var href2 = "/poll/#/1/edit/uFJTXjQUEwV2bl-y3cKVpP/LJ-4qPnpR5iY0HVdwLcnjLsx/";
var href3 = "/code/#/1/view/eRS+YPTTASNqjRbgrznAdQ/2OyNsvfYw7ZwLg6wkJuCaGBzOZvxNLra9n7GN848Zic/";
var href4 = "/slide/#/1/edit/R2bZC1mY9khSsrLCyJT+CA/mlQrCxbTiqQJ4HyUxbFBnmG8/";
var href5 = "/whiteboard/#/1/edit/k8bZC1mY9khSsrLCyJT+CA/moQrCxbTiqQJ4HyUxbFBnmG8/";
var id1 = 1000000000001;
var id2 = 1000000000002;
var id3 = 1000000000003;
var id4 = 1000000000004;
var example = {
"root": {
"Folder": {
"Sub": {}
},
"Folder2": {
"rdmStrFile1": id1
}
},
"template": [id2],
"trash": {
"DeletedF": [{
"path": ["root"],
"element": {}
},{
"path": ["root"],
"element": {
"rdmStrFile3": id3
}
}],
"Title4": [{
"path": ["root", "Folder"],
"element": id4
}]
},
"filesData": {
"1000000000004": {
"atime": 23456783489,
"ctime": 12345678999,
"href": href4,
"title": "Title4"
},
"1000000000003": {
"atime": 23456783456,
"ctime": 12345678901,
"href": href3,
"title": "Title3"
},
"1000000000002": {
"atime": 23456789012,
"ctime": 12345789235,
"href": href2,
"title": "Title2"
},
"1000000000001": {
"atime": 23456789012,
"ctime": 12345789235,
"href": href1,
"title": "Title1",
"filename": "FileName1"
}
}
};
module.test = function (assert) {
var config = {Cryptpad: Cryptpad, workgroup: false, testMode: true};
// MIGRATION FROM HREF TO ID
assert(function (cb) {
console.log('START DRIVE1');
var files = {
"root": {
"Folder": {},
"Folder2": {
"FileName": href1
}
},
"template": [href3],
"trash": {
"DeletedF": [{
"path": ["root"],
"element": {}
}, {
"path": ["root", "Folder"],
"element": href2
}]
},
"CryptPad_RECENTPADS": [{
"atime": 23456783456,
"ctime": 12345678901,
"href": href3,
"title": "pewcode"
}, {
"atime": 23456789012,
"ctime": 12345789235,
"href": href2,
"title": "pewpoll"
}, {
"atime": 23456789012,
"ctime": 12345789235,
"href": href1,
"title": "pewpad"
}]
};
var fo = FO.init(files, config);
var todo = function () {
fo.fixFiles();
if (files['CryptPad_RECENTPADS'] || !files.filesData) {
console.log("DRIVE1: migration from RECENTPADS to filesData failed");
return cb();
}
var fileKey = Object.keys(files.root.Folder2)[0];
if (!fileKey) { return cb(); }
var fileId = files.root.Folder2[fileKey];
var res = typeof fileId === "number"
&& typeof files.filesData[fileId] === "object"
&& files.filesData[fileId].filename === "FileName"
&& typeof files.trash.DeletedF[1].element === "number"
&& typeof files.filesData[files.trash.DeletedF[1].element] === "object"
&& files.filesData[files.trash.DeletedF[1].element].filename === "DeletedF"
&& typeof files.template[0] === "number"
&& typeof files.filesData[files.template[0]] === "object"
&& !files.filesData[files.template[0]].filename;
return cb(res);
};
fo.migrate(todo);
}, "DRIVE1: migration and fixFiles without unsorted");
assert(function (cb) {
console.log('START DRIVE2');
var files = {
"root": {
"Folder": {},
"Folder2": {
"FileName": "/pad/#/1/edit/a798u+miu2tg5b-QaP9SvA/UIPoGUPewZscBUFhNIi+eBBM/"
}
},
"unsorted": ["/code/#/1/edit/R1kZC1mY9khSsrLCyJT+CA/jtQrCxbTiqQJ4HyUxbFBnmG8/"],
"trash": {},
"CryptPad_RECENTPADS": [{
"atime": 23456783456,
"ctime": 12345678901,
"href": "/code/#/1/edit/R1kZC1mY9khSsrLCyJT+CA/jtQrCxbTiqQJ4HyUxbFBnmG8/",
"title": "pewcode"
}, {
"atime": 23456789012,
"ctime": 12345789235,
"href": "/pad/#/1/edit/a798u+miu2tg5b-QaP9SvA/UIPoGUPewZscBUFhNIi+eBBM/",
"title": "pewpad"
}]
};
var fo = FO.init(files, config);
var todo = function () {
fo.fixFiles();
if (files['CryptPad_RECENTPADS'] || !files.filesData) {
console.log("DRIVE2: migration from RECENTPADS to filesData failed");
return cb();
}
if (!files.template) {
console.log("DRIVE2: template is missing");
return cb();
}
if (files.unsorted) {
console.log("DRIVE2: unsorted not removed");
return cb();
}
var fileKey = Object.keys(files.root.Folder2)[0];
var fileKey2 = Object.keys(files.root).filter(function (x) {
return typeof files.root[x] === "number";
})[0];
if (!fileKey || !fileKey2) { return cb(); }
var fileId = files.root.Folder2[fileKey];
var fileId2 = files.root[fileKey2];
var res = typeof fileId === "number"
&& typeof files.filesData[fileId] === "object"
&& files.filesData[fileId].filename === "FileName"
&& typeof fileId2 === "number"
&& typeof files.filesData[fileId2] === "object"
&& !files.filesData[fileId2].filename;
return cb(res);
};
fo.migrate(todo);
}, "DRIVE2: migration and fixFiles with unsorted");
assert(function (cb) {
console.log('START DRIVE3');
var files = {
"root": {
"Folder": {},
"Folder2": {
"FileName": href1
}
},
"template": [href3],
"trash": {
"DeletedF": [{
"path": ["root"],
"element": { "Trash": href4 }
}, {
"path": ["root", "Folder"],
"element": href2
}]
},
"CryptPad_RECENTPADS": []
};
var fo = FO.init(files, config);
var todo = function () {
fo.fixFiles();
if (files['CryptPad_RECENTPADS'] || !files.filesData) {
console.log("DRIVE2: migration from RECENTPADS to filesData failed");
return cb();
}
var fileKey = Object.keys(files.root.Folder2)[0];
var fileKey2 = Object.keys(files.trash.DeletedF[0].element)[0];
if (!fileKey || !fileKey2) { return cb(); }
var fileId = files.root.Folder2[fileKey];
var fileId2 = files.trash.DeletedF[0].element[fileKey2];
var res = typeof fileId === "number"
&& typeof files.filesData[fileId] === "object"
&& files.filesData[fileId].filename === "FileName"
&& files.filesData[fileId].href === href1
&& typeof files.trash.DeletedF[1].element === "number"
&& typeof files.filesData[files.trash.DeletedF[1].element] === "object"
&& files.filesData[files.trash.DeletedF[1].element].filename === "DeletedF"
&& files.filesData[files.trash.DeletedF[1].element].href === href2
&& 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
&& typeof fileId2 === "number"
&& typeof files.filesData[fileId2] === "object"
&& files.filesData[fileId2].filename === "Trash"
&& files.filesData[fileId2].href === href4;
return cb(res);
};
fo.migrate(todo);
}, "DRIVE4: migration and fixFiles with a pad in trash not root");
// userObject Tests
// UTILS
assert(function (cb) {
console.log('START DRIVE utils');
var files = JSON.parse(JSON.stringify(example));
var fo = FO.init(files, config);
fo.fixFiles();
if (fo.isFile({}) || fo.isFile(href1) || !fo.isFile(href1, true) || !fo.isFile(id1)) {
console.log("DRIVE utils: isFile returns an incorrect value");
return cb();
}
if (fo.isReadOnlyFile(id1)) {
console.log("DRIVE utils: isReadOnlyFile returns true for an 'edit' file");
return cb();
}
if (!fo.isReadOnlyFile(id3)) {
console.log("DRIVE utils: isReadOnlyFile returns false for a 'view' file");
return cb();
}
if (!fo.hasSubfolder(files.root.Folder) || fo.hasSubfolder(files.root.Folder2)) {
console.log("DRIVE utils: hasSubfolder returns an incorrect value");
return cb();
}
if (fo.hasFile(files.root.Folder) || !fo.hasFile(files.root.Folder2)) {
console.log("DRIVE utils: hasFile returns an incorrect value");
return cb();
}
if (JSON.stringify(fo.getFileData(id1)) !== JSON.stringify(files.filesData[id1])) {
console.log("DRIVE utils: getFileData returns an incorrect value");
return cb();
}
if (fo.getTitle(id4) !== "Title4" || fo.getTitle(id1) !== "FileName1") {
console.log("DRIVE utils: getTitle returns an incorrect value");
return cb();
}
if (fo.find(["root", "Folder2", "rdmStrFile1"]) !== id1) {
console.log("DRIVE utils: 'find' returns an incorrect value");
return cb();
}
if (fo.getFiles().length !== 4 || fo.getFiles(['trash']).length !== 2) {
console.log("DRIVE utils: getFiles returns an incorrect value");
return cb();
}
if (fo.findFile(id4).length !== 1 || fo.findFile(id4)[0].length !== 4) {
console.log("DRIVE utils: findFile returns an incorrect value");
return cb();
}
if (fo.search('tle2').length !== 1 || fo.search('tle2')[0].data.href !== href2 || fo.search('tle2')[0].paths[0][0] !== 'template') {
console.log("DRIVE utils: search returns an incorrect value");
return cb();
}
return cb(true);
}, "DRIVE utils");
// OPERATIONS
assert(function (cb) {
console.log('START DRIVE operations');
var files = JSON.parse(JSON.stringify(example));
var fo = FO.init(files, config);
fo.fixFiles();
var data = Cryptpad.makePad(href5, 'Title5');
var res;
var id5;
// pushData is synchronous in test mode (no pinning)
fo.pushData(data, function (e, id) {
fo.add(id, ["root", "Folder"]);
id5 = id;
res = JSON.stringify(data) === JSON.stringify(fo.getFileData(id)) &&
fo.getFiles(["root"]).indexOf(id) !== -1;
});
if (!res) {
console.log("DRIVE operations: pushData");
return cb();
}
fo.move([["root", "Folder"], ["template", 0]], ["trash"]);
if (fo.getFiles(["template"]).indexOf(id2) !== -1 ||
fo.getFiles(["trash"]).indexOf(id5) === -1) {
console.log("DRIVE operations: move");
return cb();
}
fo.restore(["trash", "Title2", 0, "element"]);
if (files["template"][0] !== id2 || fo.getFiles(['trash']).indexOf(id2) !== -1) {
console.log("DRIVE operations: restore");
return cb();
}
files["template"] = [];
fo.add(id2, ["template"]);
if (fo.getFiles(['template']).indexOf(id2) === -1) {
console.log("DRIVE operations");
return cb();
}
var path;
fo.addFolder(["root", "Folder2"], "subsub", function (e, o) { path = o.newPath; });
if (!files.root.Folder2.subsub || path.length !== 3) {
console.log("DRIVE operations: add folder");
return cb();
}
fo.forget(href2);
if (files["template"].length !== 0 || fo.getFiles(['trash']).indexOf(id2) === -1) {
console.log("DRIVE operations: forget");
return cb();
}
fo.restore(["trash", "Title2", 0, "element"]);
fo.delete([["root", "Folder2", "subsub"],["template",0]]);
if (files.root.Folder2.subsub || fo.getFiles().indexOf(id2) !== -1) {
console.log("DRIVE operations: delete");
return cb();
}
fo.emptyTrash();
if (JSON.stringify(files.trash) !== "{}" || fo.getFiles().indexOf(id5) !== -1 ||
files.filesData[id5]) {
console.log("DRIVE operations: emptyTrash");
return cb();
}
fo.rename(["root", "Folder2"], "FolderNew");
fo.rename(["root", "FolderNew", "rdmStrFile1"], "NewFileName1");
if (files.root.Folder2 || !files.root.FolderNew ||
fo.getFileData(id1).filename !== "NewFileName1" ||
fo.getTitle(id1) !== "NewFileName1") {
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");
};
return module;
});
Loading…
Cancel
Save