Merge branch 'sharedfolder' into staging
This commit is contained in:
commit
aafcb1bc02
@ -386,6 +386,7 @@ define(function () {
|
|||||||
out.fm_newFolder = "Nouveau dossier";
|
out.fm_newFolder = "Nouveau dossier";
|
||||||
out.fm_newFile = "Nouveau pad";
|
out.fm_newFile = "Nouveau pad";
|
||||||
out.fm_folder = "Dossier";
|
out.fm_folder = "Dossier";
|
||||||
|
out.fm_sharedFolder = "Dossier partagé";
|
||||||
out.fm_folderName = "Nom du dossier";
|
out.fm_folderName = "Nom du dossier";
|
||||||
out.fm_numberOfFolders = "# de dossiers";
|
out.fm_numberOfFolders = "# de dossiers";
|
||||||
out.fm_numberOfFiles = "# de fichiers";
|
out.fm_numberOfFiles = "# de fichiers";
|
||||||
@ -446,6 +447,7 @@ define(function () {
|
|||||||
out.fm_tags_used = "Nombre d'utilisations";
|
out.fm_tags_used = "Nombre d'utilisations";
|
||||||
// File - Context menu
|
// File - Context menu
|
||||||
out.fc_newfolder = "Nouveau dossier";
|
out.fc_newfolder = "Nouveau dossier";
|
||||||
|
out.fc_newsharedfolder = "Nouveau dossier partagé";
|
||||||
out.fc_rename = "Renommer";
|
out.fc_rename = "Renommer";
|
||||||
out.fc_open = "Ouvrir";
|
out.fc_open = "Ouvrir";
|
||||||
out.fc_open_ro = "Ouvrir (lecture seule)";
|
out.fc_open_ro = "Ouvrir (lecture seule)";
|
||||||
@ -453,12 +455,13 @@ define(function () {
|
|||||||
out.fc_delete_owned = "Supprimer du serveur";
|
out.fc_delete_owned = "Supprimer du serveur";
|
||||||
out.fc_restore = "Restaurer";
|
out.fc_restore = "Restaurer";
|
||||||
out.fc_remove = "Supprimer de votre CryptDrive";
|
out.fc_remove = "Supprimer de votre CryptDrive";
|
||||||
|
out.fc_remove_sharedfolder = "Supprimer";
|
||||||
out.fc_empty = "Vider la corbeille";
|
out.fc_empty = "Vider la corbeille";
|
||||||
out.fc_prop = "Propriétés";
|
out.fc_prop = "Propriétés";
|
||||||
out.fc_hashtag = "Mots-clés";
|
out.fc_hashtag = "Mots-clés";
|
||||||
out.fc_sizeInKilobytes = "Taille en kilo-octets";
|
out.fc_sizeInKilobytes = "Taille en kilo-octets";
|
||||||
// fileObject.js (logs)
|
// fileObject.js (logs)
|
||||||
out.fo_moveUnsortedError = "La liste des éléments non triés ne peut pas contenir de dossiers.";
|
out.fo_moveUnsortedError = "La liste des modèles ne peut pas contenir de dossiers.";
|
||||||
out.fo_existingNameError = "Ce nom est déjà utilisé dans ce répertoire. Veuillez en choisir un autre.";
|
out.fo_existingNameError = "Ce nom est déjà utilisé dans ce répertoire. Veuillez en choisir un autre.";
|
||||||
out.fo_moveFolderToChildError = "Vous ne pouvez pas déplacer un dossier dans un de ses descendants";
|
out.fo_moveFolderToChildError = "Vous ne pouvez pas déplacer un dossier dans un de ses descendants";
|
||||||
out.fo_unableToRestore = "Impossible de restaurer ce fichier à son emplacement d'origine. Vous pouvez essayer de le déplacer à un nouvel emplacement.";
|
out.fo_unableToRestore = "Impossible de restaurer ce fichier à son emplacement d'origine. Vous pouvez essayer de le déplacer à un nouvel emplacement.";
|
||||||
|
|||||||
@ -387,6 +387,7 @@ define(function () {
|
|||||||
out.fm_newFolder = "New folder";
|
out.fm_newFolder = "New folder";
|
||||||
out.fm_newFile = "New pad";
|
out.fm_newFile = "New pad";
|
||||||
out.fm_folder = "Folder";
|
out.fm_folder = "Folder";
|
||||||
|
out.fm_sharedFolder = "Shared folder";
|
||||||
out.fm_folderName = "Folder name";
|
out.fm_folderName = "Folder name";
|
||||||
out.fm_numberOfFolders = "# of folders";
|
out.fm_numberOfFolders = "# of folders";
|
||||||
out.fm_numberOfFiles = "# of files";
|
out.fm_numberOfFiles = "# of files";
|
||||||
@ -447,6 +448,7 @@ define(function () {
|
|||||||
out.fm_tags_used = "Number of uses";
|
out.fm_tags_used = "Number of uses";
|
||||||
// File - Context menu
|
// File - Context menu
|
||||||
out.fc_newfolder = "New folder";
|
out.fc_newfolder = "New folder";
|
||||||
|
out.fc_newsharedfolder = "New shared folder";
|
||||||
out.fc_rename = "Rename";
|
out.fc_rename = "Rename";
|
||||||
out.fc_open = "Open";
|
out.fc_open = "Open";
|
||||||
out.fc_open_ro = "Open (read-only)";
|
out.fc_open_ro = "Open (read-only)";
|
||||||
@ -454,12 +456,13 @@ define(function () {
|
|||||||
out.fc_delete_owned = "Delete from the server";
|
out.fc_delete_owned = "Delete from the server";
|
||||||
out.fc_restore = "Restore";
|
out.fc_restore = "Restore";
|
||||||
out.fc_remove = "Remove from your CryptDrive";
|
out.fc_remove = "Remove from your CryptDrive";
|
||||||
|
out.fc_remove_sharedfolder = "Remove";
|
||||||
out.fc_empty = "Empty the trash";
|
out.fc_empty = "Empty the trash";
|
||||||
out.fc_prop = "Properties";
|
out.fc_prop = "Properties";
|
||||||
out.fc_hashtag = "Tags";
|
out.fc_hashtag = "Tags";
|
||||||
out.fc_sizeInKilobytes = "Size in Kilobytes";
|
out.fc_sizeInKilobytes = "Size in Kilobytes";
|
||||||
// fileObject.js (logs)
|
// fileObject.js (logs)
|
||||||
out.fo_moveUnsortedError = "You can't move a folder to the list of unsorted pads";
|
out.fo_moveUnsortedError = "You can't move a folder to the list of templates";
|
||||||
out.fo_existingNameError = "Name already used in that directory. Please choose another one.";
|
out.fo_existingNameError = "Name already used in that directory. Please choose another one.";
|
||||||
out.fo_moveFolderToChildError = "You can't move a folder into one of its descendants";
|
out.fo_moveFolderToChildError = "You can't move a folder into one of its descendants";
|
||||||
out.fo_unableToRestore = "Unable to restore that file to its original location. You can try to move it to a new location.";
|
out.fo_unableToRestore = "Unable to restore that file to its original location. You can try to move it to a new location.";
|
||||||
@ -1209,5 +1212,15 @@ define(function () {
|
|||||||
out.loading_drive_2 = "Updating data format";
|
out.loading_drive_2 = "Updating data format";
|
||||||
out.loading_drive_3 = "Verifying data integrity";
|
out.loading_drive_3 = "Verifying data integrity";
|
||||||
|
|
||||||
|
// Shared folders
|
||||||
|
// XXX
|
||||||
|
out.sharedFolders_forget = "This pad is only stored in a shared folder, you can't move it to the trash. You can use your CryptDrive if you want to delete it from the folder.";
|
||||||
|
out.sharedFolders_duplicate = "Some of the pads you were trying to move were already stored.";
|
||||||
|
out.sharedFolders_create = "Create a shared folder";
|
||||||
|
out.sharedFolders_create_name = "Folder name";
|
||||||
|
out.sharedFolders_create_owned = "Owned folder";
|
||||||
|
out.sharedFolders_create_password = "Folder password";
|
||||||
|
out.sharedFolders_share = "Share this URL with other registered users to give them access to the shared folder. Once they open this URL, the shared folder will be added to the root directory of their CryptDrive.";
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -398,10 +398,16 @@ Version 1
|
|||||||
Hash.findWeaker = function (href, channel, recents) {
|
Hash.findWeaker = function (href, channel, recents) {
|
||||||
var parsed = parsePadUrl(href);
|
var parsed = parsePadUrl(href);
|
||||||
if (!parsed.hash) { return false; }
|
if (!parsed.hash) { return false; }
|
||||||
|
// We can't have a weaker hash if we're already in view mode
|
||||||
|
if (parsed.hashData && parsed.hashData.mode === 'view') { return; }
|
||||||
var weaker;
|
var weaker;
|
||||||
Object.keys(recents).some(function (id) {
|
Object.keys(recents).some(function (id) {
|
||||||
var pad = recents[id];
|
var pad = recents[id];
|
||||||
var p = parsePadUrl(pad.href);
|
if (pad.href || !pad.roHref) {
|
||||||
|
// This pad has an edit link, so it can't be weaker
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var p = parsePadUrl(pad.roHref);
|
||||||
if (p.type !== parsed.type) { return; } // Not the same type
|
if (p.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
|
||||||
if (channel !== pad.channel) { return; } // Not the same channel
|
if (channel !== pad.channel) { return; } // Not the same channel
|
||||||
@ -430,6 +436,10 @@ Version 1
|
|||||||
var stronger;
|
var stronger;
|
||||||
Object.keys(recents).some(function (id) {
|
Object.keys(recents).some(function (id) {
|
||||||
var pad = recents[id];
|
var pad = recents[id];
|
||||||
|
if (!pad.href) {
|
||||||
|
// This pad doesn't have an edit link, so it can't be stronger
|
||||||
|
return;
|
||||||
|
}
|
||||||
var p = parsePadUrl(pad.href);
|
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
|
||||||
|
|||||||
@ -665,7 +665,7 @@ define([
|
|||||||
// Update the current state
|
// Update the current state
|
||||||
loading.driveState = data.state;
|
loading.driveState = data.state;
|
||||||
data.progress = data.progress || 100;
|
data.progress = data.progress || 100;
|
||||||
data.msg = Messages['loading_drive_'+data.state] || '';
|
data.msg = Messages['loading_drive_'+ Math.floor(data.state)] || '';
|
||||||
$progress.html(data.msg);
|
$progress.html(data.msg);
|
||||||
if (data.progress) {
|
if (data.progress) {
|
||||||
$progress.append(h('div.cp-loading-progress-bar', [
|
$progress.append(h('div.cp-loading-progress-bar', [
|
||||||
@ -761,7 +761,7 @@ define([
|
|||||||
UI.getFileIcon = function (data) {
|
UI.getFileIcon = function (data) {
|
||||||
var $icon = UI.getIcon();
|
var $icon = UI.getIcon();
|
||||||
if (!data) { return $icon; }
|
if (!data) { return $icon; }
|
||||||
var href = data.href;
|
var href = data.href || data.roHref;
|
||||||
var type = data.type;
|
var type = data.type;
|
||||||
if (!href && !type) { return $icon; }
|
if (!href && !type) { return $icon; }
|
||||||
|
|
||||||
@ -826,13 +826,13 @@ define([
|
|||||||
var out = false;
|
var out = false;
|
||||||
var xId = $(x).attr('aria-describedby');
|
var xId = $(x).attr('aria-describedby');
|
||||||
if (xId) {
|
if (xId) {
|
||||||
if (xId.indexOf('tippy-tooltip-') === 0) {
|
if (xId.indexOf('tippy-') === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$(x).find('[aria-describedby]').each(function (i, el) {
|
$(x).find('[aria-describedby]').each(function (i, el) {
|
||||||
var id = el.getAttribute('aria-describedby');
|
var id = el.getAttribute('aria-describedby');
|
||||||
if (id.indexOf('tippy-tooltip-') !== 0) { return; }
|
if (id.indexOf('tippy-') !== 0) { return; }
|
||||||
out = true;
|
out = true;
|
||||||
});
|
});
|
||||||
return out;
|
return out;
|
||||||
|
|||||||
@ -73,26 +73,14 @@ define([
|
|||||||
data.password = val;
|
data.password = val;
|
||||||
}));
|
}));
|
||||||
}).nThen(function (waitFor) {
|
}).nThen(function (waitFor) {
|
||||||
common.getPadAttribute('href', waitFor(function (err, val) {
|
|
||||||
var base = common.getMetadataMgr().getPrivateData().origin;
|
var base = common.getMetadataMgr().getPrivateData().origin;
|
||||||
|
common.getPadAttribute('href', waitFor(function (err, val) {
|
||||||
var parsed = Hash.parsePadUrl(val);
|
if (!val) { return; }
|
||||||
if (parsed.hashData.mode === "view") {
|
|
||||||
data.roHref = base + val;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're not in a read-only pad
|
|
||||||
data.href = base + val;
|
data.href = base + val;
|
||||||
|
}));
|
||||||
// Get Read-only href
|
common.getPadAttribute('roHref', waitFor(function (err, val) {
|
||||||
if (parsed.hashData.type !== "pad") { return; }
|
if (!val) { return; }
|
||||||
var i = data.href.indexOf('#') + 1;
|
data.roHref = base + val;
|
||||||
var hBase = data.href.slice(0, i);
|
|
||||||
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
|
||||||
if (!hrefsecret.keys) { return; }
|
|
||||||
var viewHash = Hash.getViewHashFromKeys(hrefsecret);
|
|
||||||
data.roHref = hBase + viewHash;
|
|
||||||
}));
|
}));
|
||||||
common.getPadAttribute('channel', waitFor(function (err, val) {
|
common.getPadAttribute('channel', waitFor(function (err, val) {
|
||||||
data.channel = val;
|
data.channel = val;
|
||||||
@ -162,7 +150,7 @@ define([
|
|||||||
$d.append(password);
|
$d.append(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsed = Hash.parsePadUrl(data.href);
|
var parsed = Hash.parsePadUrl(data.href || data.roHref);
|
||||||
if (owned && parsed.hashData.type === 'pad') {
|
if (owned && parsed.hashData.type === 'pad') {
|
||||||
var sframeChan = common.getSframeChannel();
|
var sframeChan = common.getSframeChannel();
|
||||||
var changePwTitle = Messages.properties_changePassword;
|
var changePwTitle = Messages.properties_changePassword;
|
||||||
@ -191,7 +179,7 @@ define([
|
|||||||
UI.confirm(changePwConfirm, function (yes) {
|
UI.confirm(changePwConfirm, function (yes) {
|
||||||
if (!yes) { return; }
|
if (!yes) { return; }
|
||||||
sframeChan.query("Q_PAD_PASSWORD_CHANGE", {
|
sframeChan.query("Q_PAD_PASSWORD_CHANGE", {
|
||||||
href: data.href,
|
href: data.href || data.roHref,
|
||||||
password: newPass
|
password: newPass
|
||||||
}, function (err, data) {
|
}, function (err, data) {
|
||||||
if (err || data.error) {
|
if (err || data.error) {
|
||||||
@ -203,11 +191,11 @@ define([
|
|||||||
// If we had a password and we removed it, we have to remove the /p/
|
// If we had a password and we removed it, we have to remove the /p/
|
||||||
if (data.warning) {
|
if (data.warning) {
|
||||||
return void UI.alert(Messages.properties_passwordWarning, function () {
|
return void UI.alert(Messages.properties_passwordWarning, function () {
|
||||||
common.gotoURL(hasPassword && newPass ? undefined : data.href);
|
common.gotoURL(hasPassword && newPass ? undefined : (data.href || data.roHref));
|
||||||
}, {force: true});
|
}, {force: true});
|
||||||
}
|
}
|
||||||
return void UI.alert(Messages.properties_passwordSuccess, function () {
|
return void UI.alert(Messages.properties_passwordSuccess, function () {
|
||||||
common.gotoURL(hasPassword && newPass ? undefined : data.href);
|
common.gotoURL(hasPassword && newPass ? undefined : (data.href || data.roHref));
|
||||||
}, {force: true});
|
}, {force: true});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -242,17 +230,21 @@ define([
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.ctime) {
|
||||||
$('<label>', {'for': 'cp-app-prop-ctime'}).text(Messages.fm_creation)
|
$('<label>', {'for': 'cp-app-prop-ctime'}).text(Messages.fm_creation)
|
||||||
.appendTo($d);
|
.appendTo($d);
|
||||||
$d.append(UI.dialog.selectable(new Date(data.ctime).toLocaleString(), {
|
$d.append(UI.dialog.selectable(new Date(data.ctime).toLocaleString(), {
|
||||||
id: 'cp-app-prop-ctime',
|
id: 'cp-app-prop-ctime',
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.atime) {
|
||||||
$('<label>', {'for': 'cp-app-prop-atime'}).text(Messages.fm_lastAccess)
|
$('<label>', {'for': 'cp-app-prop-atime'}).text(Messages.fm_lastAccess)
|
||||||
.appendTo($d);
|
.appendTo($d);
|
||||||
$d.append(UI.dialog.selectable(new Date(data.atime).toLocaleString(), {
|
$d.append(UI.dialog.selectable(new Date(data.atime).toLocaleString(), {
|
||||||
id: 'cp-app-prop-atime',
|
id: 'cp-app-prop-atime',
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
if (common.isLoggedIn() && AppConfig.enablePinning) {
|
if (common.isLoggedIn() && AppConfig.enablePinning) {
|
||||||
// check the size of this file...
|
// check the size of this file...
|
||||||
@ -533,6 +525,35 @@ define([
|
|||||||
}
|
}
|
||||||
return tabs;
|
return tabs;
|
||||||
};
|
};
|
||||||
|
UIElements.createSFShareModal = function (config) {
|
||||||
|
var origin = config.origin;
|
||||||
|
var pathname = config.pathname;
|
||||||
|
var hashes = config.hashes;
|
||||||
|
|
||||||
|
if (!hashes.editHash) { throw new Error("You must provide a valid hash"); }
|
||||||
|
var url = origin + pathname + '#' + hashes.editHash;
|
||||||
|
|
||||||
|
// Share link tab
|
||||||
|
var link = h('div.cp-share-modal', [
|
||||||
|
h('label', Messages.sharedFolders_share),
|
||||||
|
h('br'),
|
||||||
|
UI.dialog.selectable(url, { id: 'cp-share-link-preview', tabindex: 1 })
|
||||||
|
]);
|
||||||
|
var linkButtons = [{
|
||||||
|
name: Messages.cancel,
|
||||||
|
onClick: function () {},
|
||||||
|
keys: [27]
|
||||||
|
}, {
|
||||||
|
className: 'primary',
|
||||||
|
name: Messages.share_linkCopy,
|
||||||
|
onClick: function () {
|
||||||
|
var success = Clipboard.copy(url);
|
||||||
|
if (success) { UI.log(Messages.shareSuccess); }
|
||||||
|
},
|
||||||
|
keys: [13]
|
||||||
|
}];
|
||||||
|
return UI.dialog.customModal(link, {buttons: linkButtons});
|
||||||
|
};
|
||||||
|
|
||||||
UIElements.createButton = function (common, type, rightside, data, callback) {
|
UIElements.createButton = function (common, type, rightside, data, callback) {
|
||||||
var AppConfig = common.getAppConfig();
|
var AppConfig = common.getAppConfig();
|
||||||
@ -695,7 +716,15 @@ define([
|
|||||||
button
|
button
|
||||||
.click(common.prepareFeedback(type))
|
.click(common.prepareFeedback(type))
|
||||||
.click(function() {
|
.click(function() {
|
||||||
var msg = common.isLoggedIn() ? Messages.forgetPrompt : Messages.fm_removePermanentlyDialog;
|
sframeChan.query('Q_IS_ONLY_IN_SHARED_FOLDER', null, function (err, res) {
|
||||||
|
if (err || res.error) { return void console.log(err || res.error); }
|
||||||
|
var msg = Messages.forgetPrompt;
|
||||||
|
if (res) {
|
||||||
|
UI.alert(Messages.sharedFolders_forget);
|
||||||
|
return;
|
||||||
|
} else if (!common.isLoggedIn()) {
|
||||||
|
msg = Messages.fm_removePermanentlyDialog;
|
||||||
|
}
|
||||||
UI.confirm(msg, function (yes) {
|
UI.confirm(msg, function (yes) {
|
||||||
if (!yes) { return; }
|
if (!yes) { return; }
|
||||||
sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) {
|
sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) {
|
||||||
@ -707,6 +736,8 @@ define([
|
|||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'present':
|
case 'present':
|
||||||
|
|||||||
@ -84,6 +84,11 @@ define([
|
|||||||
cb(obj);
|
cb(obj);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
common.getSharedFolder = function (id, cb) {
|
||||||
|
postMessage("GET_SHARED_FOLDER", id, function (obj) {
|
||||||
|
cb(obj);
|
||||||
|
});
|
||||||
|
};
|
||||||
// Settings and ready
|
// Settings and ready
|
||||||
common.mergeAnonDrive = function (cb) {
|
common.mergeAnonDrive = function (cb) {
|
||||||
var data = {
|
var data = {
|
||||||
@ -99,6 +104,25 @@ define([
|
|||||||
common.userObjectCommand = function (data, cb) {
|
common.userObjectCommand = function (data, cb) {
|
||||||
postMessage("DRIVE_USEROBJECT", data, cb);
|
postMessage("DRIVE_USEROBJECT", data, cb);
|
||||||
};
|
};
|
||||||
|
common.restoreDrive = function (data, cb) {
|
||||||
|
postMessage("SET", {
|
||||||
|
key:['drive'],
|
||||||
|
value: data
|
||||||
|
}, function (obj) {
|
||||||
|
cb(obj);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
common.addSharedFolder = function (secret, cb) {
|
||||||
|
postMessage("ADD_SHARED_FOLDER", {
|
||||||
|
path: ['root'],
|
||||||
|
folderData: {
|
||||||
|
href: '/drive/#' + Hash.getEditHashFromKeys(secret),
|
||||||
|
roHref: '/drive/#' + Hash.getViewHashFromKeys(secret),
|
||||||
|
channel: secret.channel,
|
||||||
|
ctime: +new Date()
|
||||||
|
}
|
||||||
|
}, cb);
|
||||||
|
};
|
||||||
common.drive = {};
|
common.drive = {};
|
||||||
common.drive.onLog = Util.mkEvent();
|
common.drive.onLog = Util.mkEvent();
|
||||||
common.drive.onChange = Util.mkEvent();
|
common.drive.onChange = Util.mkEvent();
|
||||||
@ -294,6 +318,13 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
common.isOnlyInSharedFolder = function (data, cb) {
|
||||||
|
postMessage("IS_ONLY_IN_SHARED_FOLDER", data, function (obj) {
|
||||||
|
if (obj && obj.error) { return void cb(obj.error); }
|
||||||
|
cb(null, obj);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
common.setDisplayName = function (value, cb) {
|
common.setDisplayName = function (value, cb) {
|
||||||
postMessage("SET_DISPLAY_NAME", value, cb);
|
postMessage("SET_DISPLAY_NAME", value, cb);
|
||||||
};
|
};
|
||||||
@ -506,7 +537,8 @@ define([
|
|||||||
|
|
||||||
if (common.initialPath) {
|
if (common.initialPath) {
|
||||||
if (!data.path) {
|
if (!data.path) {
|
||||||
data.path = common.initialPath;
|
data.path = Array.isArray(common.initialPath) ? common.initialPath
|
||||||
|
: decodeURIComponent(common.initialPath).split(',');
|
||||||
delete common.initialPath;
|
delete common.initialPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -620,7 +652,7 @@ define([
|
|||||||
if (!parsed.hash) { return void cb({ error: 'EINVAL_HREF' }); }
|
if (!parsed.hash) { return void cb({ error: 'EINVAL_HREF' }); }
|
||||||
|
|
||||||
var warning = false;
|
var warning = false;
|
||||||
var newHash;
|
var newHash, newRoHref;
|
||||||
var oldChannel;
|
var oldChannel;
|
||||||
var newSecret;
|
var newSecret;
|
||||||
|
|
||||||
@ -693,6 +725,11 @@ define([
|
|||||||
common.setPadAttribute('channel', newSecret.channel, waitFor(function (err) {
|
common.setPadAttribute('channel', newSecret.channel, waitFor(function (err) {
|
||||||
if (err) { warning = true; }
|
if (err) { warning = true; }
|
||||||
}), href);
|
}), href);
|
||||||
|
var viewHash = Hash.getViewHashFromKeys(secret);
|
||||||
|
newRoHref = '/' + parsed.type + '/#' + viewHash;
|
||||||
|
common.setPadAttribute('roHref', newRoHref, waitFor(function (err) {
|
||||||
|
if (err) { warning = true; }
|
||||||
|
}), href);
|
||||||
|
|
||||||
if (parsed.hashData.password && newPassword) { return; } // same hash
|
if (parsed.hashData.password && newPassword) { return; } // same hash
|
||||||
common.setPadAttribute('href', newHref, waitFor(function (err) {
|
common.setPadAttribute('href', newHref, waitFor(function (err) {
|
||||||
@ -702,7 +739,8 @@ define([
|
|||||||
cb({
|
cb({
|
||||||
warning: warning,
|
warning: warning,
|
||||||
hash: newHash,
|
hash: newHash,
|
||||||
href: newHref
|
href: newHref,
|
||||||
|
roHref: newRoHref
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,84 +6,6 @@ define([
|
|||||||
], function (Crypt, FO, Hash, Realtime) {
|
], function (Crypt, FO, Hash, Realtime) {
|
||||||
var exp = {};
|
var exp = {};
|
||||||
|
|
||||||
var getType = function (el) {
|
|
||||||
if (el === null) { return "null"; }
|
|
||||||
return Array.isArray(el) ? "array" : typeof(el);
|
|
||||||
};
|
|
||||||
|
|
||||||
var findAvailableKey = function (obj, key) {
|
|
||||||
if (typeof (obj[key]) === "undefined") { return key; }
|
|
||||||
var i = 1;
|
|
||||||
var nkey = key;
|
|
||||||
while (typeof (obj[nkey]) !== "undefined") {
|
|
||||||
nkey = key + '_' + i;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return nkey;
|
|
||||||
};
|
|
||||||
|
|
||||||
var createFromPath = function (proxy, oldFo, path, id) {
|
|
||||||
var root = proxy.drive;
|
|
||||||
|
|
||||||
if (!oldFo.isFile(id)) { return; }
|
|
||||||
|
|
||||||
var error = function (msg) {
|
|
||||||
console.error(msg || "Unable to find that path", path);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (oldFo.isInTrashRoot(path)) {
|
|
||||||
id = oldFo.find(path.slice(0,3));
|
|
||||||
path.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
var next, nextRoot;
|
|
||||||
path.forEach(function (p, i) {
|
|
||||||
if (!root) { return; }
|
|
||||||
if (typeof(p) === "string") {
|
|
||||||
if (getType(root) !== "object") { root = undefined; error(); return; }
|
|
||||||
if (i === path.length - 1) {
|
|
||||||
root[Hash.createChannelId()] = id;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
next = getType(path[i+1]);
|
|
||||||
nextRoot = getType(root[p]);
|
|
||||||
if (nextRoot !== "undefined") {
|
|
||||||
if (next === "string" && nextRoot === "object" || next === "number" && nextRoot === "array") {
|
|
||||||
root = root[p];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
p = findAvailableKey(root, p);
|
|
||||||
}
|
|
||||||
if (next === "number") {
|
|
||||||
root[p] = [];
|
|
||||||
root = root[p];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
root[p] = {};
|
|
||||||
root = root[p];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Path contains a non-string element: it's an array index
|
|
||||||
if (typeof(p) !== "number") { root = undefined; error(); return; }
|
|
||||||
if (getType(root) !== "array") { root = undefined; error(); return; }
|
|
||||||
if (i === path.length - 1) {
|
|
||||||
if (root.indexOf(id) === -1) { root.push(id); }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
next = getType(path[i+1]);
|
|
||||||
if (next === "number") {
|
|
||||||
error('2 consecutives arrays in the user object');
|
|
||||||
root = undefined;
|
|
||||||
//root.push([]);
|
|
||||||
//root = root[root.length - 1];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
root.push({});
|
|
||||||
root = root[root.length - 1];
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exp.anonDriveIntoUser = function (proxyData, fsHash, cb) {
|
exp.anonDriveIntoUser = function (proxyData, fsHash, cb) {
|
||||||
// Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb
|
// Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb
|
||||||
if (!fsHash || !proxyData.loggedIn) {
|
if (!fsHash || !proxyData.loggedIn) {
|
||||||
@ -105,43 +27,38 @@ define([
|
|||||||
if (parsed) {
|
if (parsed) {
|
||||||
var proxy = proxyData.proxy;
|
var proxy = proxyData.proxy;
|
||||||
var oldFo = FO.init(parsed.drive, {
|
var oldFo = FO.init(parsed.drive, {
|
||||||
loggedIn: proxyData.loggedIn,
|
loggedIn: true,
|
||||||
pinPads: function () {} // without pinPads /outer/userObject.js won't be loaded
|
outer: true
|
||||||
});
|
});
|
||||||
var onMigrated = function () {
|
var onMigrated = function () {
|
||||||
oldFo.fixFiles(true);
|
oldFo.fixFiles(true);
|
||||||
var newFo = proxyData.userObject;
|
var manager = proxyData.manager;
|
||||||
var oldRecentPads = parsed.drive[newFo.FILES_DATA];
|
var oldFiles = oldFo.getFiles([oldFo.FILES_DATA]);
|
||||||
var newRecentPads = proxy.drive[newFo.FILES_DATA];
|
|
||||||
var oldFiles = oldFo.getFiles([newFo.FILES_DATA]);
|
|
||||||
var newHrefs = Object.keys(newRecentPads).map(function (id) {
|
|
||||||
return newRecentPads[id].href;
|
|
||||||
});
|
|
||||||
oldFiles.forEach(function (id) {
|
oldFiles.forEach(function (id) {
|
||||||
var href = oldRecentPads[id].href;
|
var data = oldFo.getFileData(id);
|
||||||
// Do not migrate a pad if we already have it, it would create a duplicate in the drive
|
var channel = data.channel;
|
||||||
if (newHrefs.indexOf(href) !== -1) { return; }
|
|
||||||
// If we have a stronger version, do not add the current href
|
var datas = manager.findChannel(channel, true);
|
||||||
if (Hash.findStronger(href, oldRecentPads[id].channel, newRecentPads)) { return; }
|
// Do not migrate a pad if we already have it, it would create a duplicate
|
||||||
// If we have a weaker version, replace the href by the new one
|
// in the drive
|
||||||
// NOTE: if that weaker version is in the trash, the strong one will be put in unsorted
|
if (datas.length !== 0) {
|
||||||
var weaker = Hash.findWeaker(href, oldRecentPads[id].channel, newRecentPads);
|
// We want to merge a read-only pad: it can't be stronger than what
|
||||||
if (weaker) {
|
// we already have so abort
|
||||||
// Update RECENTPADS
|
if (!data.href) { return; }
|
||||||
weaker.href = href;
|
|
||||||
// Update the file in the drive
|
// We want to merge an edit pad: check if we have the same channel
|
||||||
newFo.replace(weaker.href, href);
|
// but read-only and upgrade it in that case
|
||||||
|
datas.forEach(function (pad) {
|
||||||
|
if (!pad.href) { data.href = pad.href; }
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Here it means we have a new href, so we should add it to the drive at its old location
|
// Here it means we have a new href, so we should add it to the drive
|
||||||
var paths = oldFo.findFile(id);
|
|
||||||
if (paths.length === 0) { return; }
|
|
||||||
// Add the file data in our array and use the id to add the file
|
|
||||||
var data = oldFo.getFileData(id);
|
|
||||||
if (data) {
|
if (data) {
|
||||||
newFo.pushData(data, function (err, id) {
|
manager.addPad(null, data, function (err) {
|
||||||
if (err) { return void console.error("Cannot import file:", data, err); }
|
if (err) {
|
||||||
createFromPath(proxy, oldFo, paths[0], id);
|
return void console.error("Cannot import file:", data, err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -123,12 +123,58 @@ define([
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
n.nThen(waitFor());
|
n.nThen(waitFor(function () {
|
||||||
|
Feedback.send('Migrate-6', true);
|
||||||
|
userObject.version = version = 6;
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
if (version < 6) {
|
if (version < 6) {
|
||||||
addChannelId();
|
addChannelId();
|
||||||
Feedback.send('Migrate-6', true);
|
}
|
||||||
userObject.version = version = 6;
|
}).nThen(function (waitFor) {
|
||||||
|
var addRoHref = function () {
|
||||||
|
var data = userObject.drive.filesData;
|
||||||
|
var el, parsed;
|
||||||
|
var n = nThen(function () {});
|
||||||
|
var padsLength = Object.keys(data).length;
|
||||||
|
Object.keys(data).forEach(function (k, i) {
|
||||||
|
n = n.nThen(function (w) {
|
||||||
|
setTimeout(w(function () {
|
||||||
|
el = data[k];
|
||||||
|
if (!el.href || (el.roHref && false)) {
|
||||||
|
// Already migrated
|
||||||
|
return void progress(7, Math.round(100*i/padsLength));
|
||||||
|
}
|
||||||
|
parsed = Hash.parsePadUrl(el.href);
|
||||||
|
if (parsed.hashData.type !== "pad") {
|
||||||
|
// No read-only mode for files
|
||||||
|
return void progress(7, Math.round(100*i/padsLength));
|
||||||
|
}
|
||||||
|
if (parsed.hashData.mode === "view") {
|
||||||
|
// This is a read-only pad in our drive
|
||||||
|
el.roHref = el.href;
|
||||||
|
delete el.href;
|
||||||
|
console.log('Move href to roHref in filesData ', el.roHref);
|
||||||
|
} else {
|
||||||
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||||
|
var hash = Hash.getViewHashFromKeys(secret);
|
||||||
|
if (hash) {
|
||||||
|
// Version 0 won't have a view hash available
|
||||||
|
el.roHref = '/' + parsed.type + '/#' + hash;
|
||||||
|
console.log('Adding missing roHref in filesData ', el.href);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
progress(6, Math.round(100*i/padsLength));
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
n.nThen(waitFor(function () {
|
||||||
|
Feedback.send('Migrate-7', true);
|
||||||
|
userObject.version = version = 7;
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
if (version < 7) {
|
||||||
|
addRoHref();
|
||||||
}
|
}
|
||||||
/*}).nThen(function (waitFor) {
|
/*}).nThen(function (waitFor) {
|
||||||
// Test progress bar in the loading screen
|
// Test progress bar in the loading screen
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
define([
|
define([
|
||||||
'json.sortify',
|
'json.sortify',
|
||||||
'/common/userObject.js',
|
'/common/userObject.js',
|
||||||
|
'/common/proxy-manager.js',
|
||||||
'/common/migrate-user-object.js',
|
'/common/migrate-user-object.js',
|
||||||
'/common/common-hash.js',
|
'/common/common-hash.js',
|
||||||
'/common/common-util.js',
|
'/common/common-util.js',
|
||||||
@ -18,7 +19,7 @@ define([
|
|||||||
'/bower_components/chainpad-listmap/chainpad-listmap.js',
|
'/bower_components/chainpad-listmap/chainpad-listmap.js',
|
||||||
'/bower_components/nthen/index.js',
|
'/bower_components/nthen/index.js',
|
||||||
'/bower_components/saferphore/index.js',
|
'/bower_components/saferphore/index.js',
|
||||||
], function (Sortify, UserObject, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger,
|
], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger,
|
||||||
CpNfWorker, NetConfig, AppConfig,
|
CpNfWorker, NetConfig, AppConfig,
|
||||||
Crypto, ChainPad, Listmap, nThen, Saferphore) {
|
Crypto, ChainPad, Listmap, nThen, Saferphore) {
|
||||||
var Store = {};
|
var Store = {};
|
||||||
@ -27,17 +28,23 @@ define([
|
|||||||
var postMessage = function () {};
|
var postMessage = function () {};
|
||||||
var broadcast = function () {};
|
var broadcast = function () {};
|
||||||
var sendDriveEvent = function () {};
|
var sendDriveEvent = function () {};
|
||||||
|
var registerProxyEvents = function () {};
|
||||||
|
|
||||||
var storeHash;
|
var storeHash;
|
||||||
|
|
||||||
var store = window.CryptPad_AsyncStore = {};
|
var store = window.CryptPad_AsyncStore = {};
|
||||||
|
|
||||||
|
|
||||||
var onSync = function (cb) {
|
var onSync = function (cb) {
|
||||||
Realtime.whenRealtimeSyncs(store.realtime, cb);
|
nThen(function (waitFor) {
|
||||||
|
Realtime.whenRealtimeSyncs(store.realtime, waitFor());
|
||||||
|
if (store.sharedFolders) {
|
||||||
|
for (var k in store.sharedFolders) {
|
||||||
|
Realtime.whenRealtimeSyncs(store.sharedFolders[k].realtime, waitFor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).nThen(function () { cb(); });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Store.get = function (clientId, key, cb) {
|
Store.get = function (clientId, key, cb) {
|
||||||
cb(Util.find(store.proxy, key));
|
cb(Util.find(store.proxy, key));
|
||||||
};
|
};
|
||||||
@ -55,6 +62,13 @@ define([
|
|||||||
onSync(cb);
|
onSync(cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Store.getSharedFolder = function (clientId, id, cb) {
|
||||||
|
if (store.manager.folders[id]) {
|
||||||
|
return void cb(store.manager.folders[id].proxy);
|
||||||
|
}
|
||||||
|
cb({});
|
||||||
|
};
|
||||||
|
|
||||||
Store.hasSigningKeys = function () {
|
Store.hasSigningKeys = function () {
|
||||||
if (!store.proxy) { return; }
|
if (!store.proxy) { return; }
|
||||||
return typeof(store.proxy.edPrivate) === 'string' &&
|
return typeof(store.proxy.edPrivate) === 'string' &&
|
||||||
@ -78,17 +92,10 @@ define([
|
|||||||
if (!userChannel) { return null; }
|
if (!userChannel) { return null; }
|
||||||
|
|
||||||
// Get the list of pads' channel ID in your drive
|
// Get the list of pads' channel ID in your drive
|
||||||
// This list is filtered so that it doesn't include pad owned by other users (you should
|
// This list is filtered so that it doesn't include pad owned by other users
|
||||||
// not pin these pads)
|
// It now includes channels from shared folders
|
||||||
var files = store.userObject.getFiles([store.userObject.FILES_DATA]);
|
|
||||||
var edPublic = store.proxy.edPublic;
|
var edPublic = store.proxy.edPublic;
|
||||||
var list = files.map(function (id) {
|
var list = store.manager.getChannelsList(edPublic, 'pin');
|
||||||
var d = store.userObject.getFileData(id);
|
|
||||||
if (d.owners && d.owners.length && edPublic &&
|
|
||||||
d.owners.indexOf(edPublic) === -1) { return; }
|
|
||||||
return d.channel;
|
|
||||||
})
|
|
||||||
.filter(function (x) { return x; });
|
|
||||||
|
|
||||||
// Get the avatar
|
// Get the avatar
|
||||||
var profile = store.proxy.profile;
|
var profile = store.proxy.profile;
|
||||||
@ -111,19 +118,8 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var getExpirableChannelList = function () {
|
var getExpirableChannelList = function () {
|
||||||
var list = [];
|
|
||||||
store.userObject.getFiles([store.userObject.FILES_DATA]).forEach(function (id) {
|
|
||||||
var data = store.userObject.getFileData(id);
|
|
||||||
var edPublic = store.proxy.edPublic;
|
var edPublic = store.proxy.edPublic;
|
||||||
|
return store.manager.getChannelsList(edPublic, 'expirable');
|
||||||
// Push channels owned by someone else or channel that should have expired
|
|
||||||
// because of the expiration time
|
|
||||||
if ((data.owners && data.owners.length && data.owners.indexOf(edPublic) === -1) ||
|
|
||||||
(data.expire && data.expire < (+new Date()))) {
|
|
||||||
list.push(data.channel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return list;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var getCanonicalChannelList = function (expirable) {
|
var getCanonicalChannelList = function (expirable) {
|
||||||
@ -380,7 +376,7 @@ define([
|
|||||||
|
|
||||||
Store.getDeletedPads = function (clientId, data, cb) {
|
Store.getDeletedPads = function (clientId, data, cb) {
|
||||||
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
|
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
|
||||||
var list = getCanonicalChannelList(true);
|
var list = (data && data.list) || getCanonicalChannelList(true);
|
||||||
if (!Array.isArray(list)) {
|
if (!Array.isArray(list)) {
|
||||||
return void cb({error: 'INVALID_FILE_LIST'});
|
return void cb({error: 'INVALID_FILE_LIST'});
|
||||||
}
|
}
|
||||||
@ -435,10 +431,11 @@ define([
|
|||||||
cb(JSON.parse(JSON.stringify(metadata)));
|
cb(JSON.parse(JSON.stringify(metadata)));
|
||||||
};
|
};
|
||||||
|
|
||||||
var makePad = function (href, title) {
|
var makePad = function (href, roHref, title) {
|
||||||
var now = +new Date();
|
var now = +new Date();
|
||||||
return {
|
return {
|
||||||
href: href,
|
href: href,
|
||||||
|
roHref: roHref,
|
||||||
atime: now,
|
atime: now,
|
||||||
ctime: now,
|
ctime: now,
|
||||||
title: title || Hash.getDefaultName(Hash.parsePadUrl(href)),
|
title: title || Hash.getDefaultName(Hash.parsePadUrl(href)),
|
||||||
@ -446,16 +443,21 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
Store.addPad = function (clientId, data, cb) {
|
Store.addPad = function (clientId, data, cb) {
|
||||||
if (!data.href) { return void cb({error:'NO_HREF'}); }
|
if (!data.href && !data.roHref) { return void cb({error:'NO_HREF'}); }
|
||||||
var pad = makePad(data.href, data.title);
|
if (!data.roHref) {
|
||||||
|
var parsed = Hash.parsePadUrl(data.href);
|
||||||
|
if (parsed.hashData.type === "pad") {
|
||||||
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
||||||
|
data.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var pad = makePad(data.href, data.roHref, data.title);
|
||||||
if (data.owners) { pad.owners = data.owners; }
|
if (data.owners) { pad.owners = data.owners; }
|
||||||
if (data.expire) { pad.expire = data.expire; }
|
if (data.expire) { pad.expire = data.expire; }
|
||||||
if (data.password) { pad.password = data.password; }
|
if (data.password) { pad.password = data.password; }
|
||||||
if (data.channel) { pad.channel = data.channel; }
|
if (data.channel) { pad.channel = data.channel; }
|
||||||
store.userObject.pushData(pad, function (e, id) {
|
store.manager.addPad(data.path, pad, function (e) {
|
||||||
if (e) { return void cb({error: "Error while adding a template:"+ e}); }
|
if (e) { return void cb({error: "Error while adding the pad:"+ e}); }
|
||||||
var path = data.path || ['root'];
|
|
||||||
store.userObject.add(id, path);
|
|
||||||
sendDriveEvent('DRIVE_CHANGE', {
|
sendDriveEvent('DRIVE_CHANGE', {
|
||||||
path: ['drive', UserObject.FILES_DATA]
|
path: ['drive', UserObject.FILES_DATA]
|
||||||
}, clientId);
|
}, clientId);
|
||||||
@ -464,17 +466,8 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var getOwnedPads = function () {
|
var getOwnedPads = function () {
|
||||||
var list = [];
|
|
||||||
store.userObject.getFiles([store.userObject.FILES_DATA]).forEach(function (id) {
|
|
||||||
var data = store.userObject.getFileData(id);
|
|
||||||
var edPublic = store.proxy.edPublic;
|
var edPublic = store.proxy.edPublic;
|
||||||
|
var list = store.manager.getChannelsList(edPublic, 'owned');
|
||||||
// Push channels owned by someone else or channel that should have expired
|
|
||||||
// because of the expiration time
|
|
||||||
if (data.owners && data.owners.length === 1 && data.owners.indexOf(edPublic) !== -1) {
|
|
||||||
list.push(data.channel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (store.proxy.todo) {
|
if (store.proxy.todo) {
|
||||||
// No password for todo
|
// No password for todo
|
||||||
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
|
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
|
||||||
@ -596,33 +589,6 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var getAttributeObject = function (attr) {
|
|
||||||
if (typeof attr === "string") {
|
|
||||||
console.error('DEPRECATED: use setAttribute with an array, not a string');
|
|
||||||
return {
|
|
||||||
path: ['settings'],
|
|
||||||
obj: store.proxy.settings,
|
|
||||||
key: attr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!Array.isArray(attr)) { return void console.error("Attribute must be string or array"); }
|
|
||||||
if (attr.length === 0) { return void console.error("Attribute can't be empty"); }
|
|
||||||
var obj = store.proxy.settings;
|
|
||||||
attr.forEach(function (el, i) {
|
|
||||||
if (i === attr.length-1) { return; }
|
|
||||||
if (!obj[el]) {
|
|
||||||
obj[el] = {};
|
|
||||||
}
|
|
||||||
else if (typeof obj[el] !== "object") { return void console.error("Wrong attribute"); }
|
|
||||||
obj = obj[el];
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
path: ['settings'].concat(attr),
|
|
||||||
obj: obj,
|
|
||||||
key: attr[attr.length-1]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set the display name (username) in the proxy
|
// Set the display name (username) in the proxy
|
||||||
Store.setDisplayName = function (clientId, value, cb) {
|
Store.setDisplayName = function (clientId, value, cb) {
|
||||||
store.proxy[Constants.displayNameKey] = value;
|
store.proxy[Constants.displayNameKey] = value;
|
||||||
@ -651,7 +617,7 @@ define([
|
|||||||
* - value (String)
|
* - value (String)
|
||||||
*/
|
*/
|
||||||
Store.setPadAttribute = function (clientId, data, cb) {
|
Store.setPadAttribute = function (clientId, data, cb) {
|
||||||
store.userObject.setPadAttribute(data.href, data.attr, data.value, function () {
|
store.manager.setPadAttribute(data, function () {
|
||||||
sendDriveEvent('DRIVE_CHANGE', {
|
sendDriveEvent('DRIVE_CHANGE', {
|
||||||
path: ['drive', UserObject.FILES_DATA]
|
path: ['drive', UserObject.FILES_DATA]
|
||||||
}, clientId);
|
}, clientId);
|
||||||
@ -659,11 +625,38 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
Store.getPadAttribute = function (clientId, data, cb) {
|
Store.getPadAttribute = function (clientId, data, cb) {
|
||||||
store.userObject.getPadAttribute(data.href, data.attr, function (err, val) {
|
store.manager.getPadAttribute(data, function (err, val) {
|
||||||
if (err) { return void cb({error: err}); }
|
if (err) { return void cb({error: err}); }
|
||||||
cb(val);
|
cb(val);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var getAttributeObject = function (attr) {
|
||||||
|
if (typeof attr === "string") {
|
||||||
|
console.error('DEPRECATED: use setAttribute with an array, not a string');
|
||||||
|
return {
|
||||||
|
path: ['settings'],
|
||||||
|
obj: store.proxy.settings,
|
||||||
|
key: attr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!Array.isArray(attr)) { return void console.error("Attribute must be string or array"); }
|
||||||
|
if (attr.length === 0) { return void console.error("Attribute can't be empty"); }
|
||||||
|
var obj = store.proxy.settings;
|
||||||
|
attr.forEach(function (el, i) {
|
||||||
|
if (i === attr.length-1) { return; }
|
||||||
|
if (!obj[el]) {
|
||||||
|
obj[el] = {};
|
||||||
|
}
|
||||||
|
else if (typeof obj[el] !== "object") { return void console.error("Wrong attribute"); }
|
||||||
|
obj = obj[el];
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
path: ['settings'].concat(attr),
|
||||||
|
obj: obj,
|
||||||
|
key: attr[attr.length-1]
|
||||||
|
};
|
||||||
|
};
|
||||||
Store.setAttribute = function (clientId, data, cb) {
|
Store.setAttribute = function (clientId, data, cb) {
|
||||||
try {
|
try {
|
||||||
var object = getAttributeObject(data.attr);
|
var object = getAttributeObject(data.attr);
|
||||||
@ -681,11 +674,12 @@ define([
|
|||||||
|
|
||||||
// Tags
|
// Tags
|
||||||
Store.listAllTags = function (clientId, data, cb) {
|
Store.listAllTags = function (clientId, data, cb) {
|
||||||
cb(store.userObject.getTagsList());
|
cb(store.manager.getTagsList());
|
||||||
};
|
};
|
||||||
|
|
||||||
// Templates
|
// Templates
|
||||||
Store.getTemplates = function (clientId, data, cb) {
|
Store.getTemplates = function (clientId, data, cb) {
|
||||||
|
// No templates in shared folders: we don't need the manager here
|
||||||
var templateFiles = store.userObject.getFiles(['template']);
|
var templateFiles = store.userObject.getFiles(['template']);
|
||||||
var res = [];
|
var res = [];
|
||||||
templateFiles.forEach(function (f) {
|
templateFiles.forEach(function (f) {
|
||||||
@ -695,6 +689,7 @@ define([
|
|||||||
cb(res);
|
cb(res);
|
||||||
};
|
};
|
||||||
Store.incrementTemplateUse = function (clientId, href) {
|
Store.incrementTemplateUse = function (clientId, href) {
|
||||||
|
// No templates in shared folders: we don't need the manager here
|
||||||
store.userObject.getPadAttribute(href, 'used', function (err, data) {
|
store.userObject.getPadAttribute(href, 'used', function (err, data) {
|
||||||
// This is a not critical function, abort in case of error to make sure we won't
|
// This is a not critical function, abort in case of error to make sure we won't
|
||||||
// create any issue with the user object or the async store
|
// create any issue with the user object or the async store
|
||||||
@ -705,6 +700,17 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Pads
|
// Pads
|
||||||
|
Store.isOnlyInSharedFolder = function (clientId, channel, cb) {
|
||||||
|
var res = store.manager.findChannel(channel);
|
||||||
|
|
||||||
|
// A pad is only in a shared worker if:
|
||||||
|
// 1. this pad is in at least one proxy
|
||||||
|
// 2. no proxy containing this pad is the main drive
|
||||||
|
return cb (res.length && !res.some(function (obj) {
|
||||||
|
// Main drive doesn't have an fId (folder ID)
|
||||||
|
return !obj.fId;
|
||||||
|
}));
|
||||||
|
};
|
||||||
Store.moveToTrash = function (clientId, data, cb) {
|
Store.moveToTrash = function (clientId, data, cb) {
|
||||||
var href = Hash.getRelativeHref(data.href);
|
var href = Hash.getRelativeHref(data.href);
|
||||||
store.userObject.forget(href);
|
store.userObject.forget(href);
|
||||||
@ -734,47 +740,10 @@ define([
|
|||||||
expire = +channelData.data.expire || undefined;
|
expire = +channelData.data.expire || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
|
var datas = store.manager.findChannel(channel);
|
||||||
var isStronger;
|
var contains = datas.length !== 0;
|
||||||
|
datas.forEach(function (obj) {
|
||||||
// If we don't find the new channel in our existing pads, we'll have to add the pads
|
var pad = obj.data;
|
||||||
// to filesData
|
|
||||||
var contains;
|
|
||||||
|
|
||||||
// Update all pads that use the same channel but with a weaker hash
|
|
||||||
// Edit > Edit (present) > View > View (present)
|
|
||||||
for (var id in allPads) {
|
|
||||||
var pad = allPads[id];
|
|
||||||
if (!pad.href) { continue; }
|
|
||||||
|
|
||||||
var p2 = Hash.parsePadUrl(pad.href);
|
|
||||||
var h2 = p2.hashData;
|
|
||||||
|
|
||||||
// Different types, proceed to the next one
|
|
||||||
// No hash data: corrupted pad?
|
|
||||||
if (p.type !== p2.type || !h2) { continue; }
|
|
||||||
// Different channel: continue
|
|
||||||
if (pad.channel !== channel) { continue; }
|
|
||||||
|
|
||||||
var shouldUpdate = p.hash.replace(/\/$/, '') === p2.hash.replace(/\/$/, '');
|
|
||||||
|
|
||||||
// If the hash is different but represents the same channel, check if weaker or stronger
|
|
||||||
if (!shouldUpdate && h.version !== 0) {
|
|
||||||
// We had view & now we have edit, update
|
|
||||||
if (h2.mode === 'view' && h.mode === 'edit') { shouldUpdate = true; }
|
|
||||||
// Same mode and we had present URL, update
|
|
||||||
else if (h.mode === h2.mode && h2.present) { shouldUpdate = true; }
|
|
||||||
// If we're here it means we have a weaker URL:
|
|
||||||
// update the date but keep the existing hash
|
|
||||||
else {
|
|
||||||
pad.atime = +new Date();
|
|
||||||
contains = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldUpdate) {
|
|
||||||
contains = true;
|
|
||||||
pad.atime = +new Date();
|
pad.atime = +new Date();
|
||||||
pad.title = title;
|
pad.title = title;
|
||||||
if (owners || h.type !== "file") {
|
if (owners || h.type !== "file") {
|
||||||
@ -783,23 +752,27 @@ define([
|
|||||||
pad.owners = owners;
|
pad.owners = owners;
|
||||||
}
|
}
|
||||||
pad.expire = expire;
|
pad.expire = expire;
|
||||||
|
if (h.mode === 'view') { return; }
|
||||||
|
|
||||||
// If the href is different, it means we have a stronger one
|
// If we only have rohref, it means we have a stronger href
|
||||||
if (href !== pad.href) { isStronger = true; }
|
if (!pad.href) {
|
||||||
pad.href = href;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isStronger) {
|
|
||||||
// If we have a stronger url, remove the possible weaker from the trash.
|
// If we have a stronger url, remove the possible weaker from the trash.
|
||||||
// If all of the weaker ones were in the trash, add the stronger to ROOT
|
// If all of the weaker ones were in the trash, add the stronger to ROOT
|
||||||
store.userObject.restoreHref(href);
|
obj.userObject.restoreHref(href);
|
||||||
}
|
}
|
||||||
|
pad.href = href;
|
||||||
|
});
|
||||||
|
|
||||||
// Add the pad if it does not exist in our drive
|
// Add the pad if it does not exist in our drive
|
||||||
if (!contains) {
|
if (!contains) {
|
||||||
|
var roHref;
|
||||||
|
if (h.mode === "view") {
|
||||||
|
roHref = href;
|
||||||
|
href = undefined;
|
||||||
|
}
|
||||||
Store.addPad(clientId, {
|
Store.addPad(clientId, {
|
||||||
href: href,
|
href: href,
|
||||||
|
roHref: roHref,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
title: title,
|
title: title,
|
||||||
owners: owners,
|
owners: owners,
|
||||||
@ -819,7 +792,6 @@ define([
|
|||||||
// Filepicker app
|
// Filepicker app
|
||||||
Store.getSecureFilesList = function (clientId, query, cb) {
|
Store.getSecureFilesList = function (clientId, query, cb) {
|
||||||
var list = {};
|
var list = {};
|
||||||
var hashes = [];
|
|
||||||
var types = query.types;
|
var types = query.types;
|
||||||
var where = query.where;
|
var where = query.where;
|
||||||
var filter = query.filter || {};
|
var filter = query.filter || {};
|
||||||
@ -834,19 +806,19 @@ define([
|
|||||||
}
|
}
|
||||||
return filtered;
|
return filtered;
|
||||||
};
|
};
|
||||||
store.userObject.getFiles(where).forEach(function (id) {
|
store.manager.getSecureFilesList(where).forEach(function (obj) {
|
||||||
var data = store.userObject.getFileData(id);
|
var data = obj.data;
|
||||||
var parsed = Hash.parsePadUrl(data.href);
|
var id = obj.id;
|
||||||
|
var parsed = Hash.parsePadUrl(data.href || data.roHref);
|
||||||
if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) &&
|
if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) &&
|
||||||
hashes.indexOf(parsed.hash) === -1 &&
|
|
||||||
!isFiltered(parsed.type, data)) {
|
!isFiltered(parsed.type, data)) {
|
||||||
hashes.push(parsed.hash);
|
|
||||||
list[id] = data;
|
list[id] = data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cb(list);
|
cb(list);
|
||||||
};
|
};
|
||||||
Store.getPadData = function (clientId, id, cb) {
|
Store.getPadData = function (clientId, id, cb) {
|
||||||
|
// FIXME: this is only used for templates at the moment, so we don't need the manager
|
||||||
cb(store.userObject.getFileData(id));
|
cb(store.userObject.getFileData(id));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1192,37 +1164,60 @@ define([
|
|||||||
}]));
|
}]));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// SHARED FOLDERS
|
||||||
|
var loadSharedFolder = function (id, data, cb) {
|
||||||
|
var parsed = Hash.parsePadUrl(data.href);
|
||||||
|
var secret = Hash.getSecrets('drive', parsed.hash, data.password);
|
||||||
|
var owners = data.owners;
|
||||||
|
var listmapConfig = {
|
||||||
|
data: {},
|
||||||
|
websocketURL: NetConfig.getWebsocketURL(),
|
||||||
|
channel: secret.channel,
|
||||||
|
readOnly: false,
|
||||||
|
validateKey: secret.keys.validateKey || undefined,
|
||||||
|
crypto: Crypto.createEncryptor(secret.keys),
|
||||||
|
userName: 'sharedFolder',
|
||||||
|
logLevel: 1,
|
||||||
|
ChainPad: ChainPad,
|
||||||
|
classic: true,
|
||||||
|
owners: owners
|
||||||
|
};
|
||||||
|
var rt = Listmap.create(listmapConfig);
|
||||||
|
store.sharedFolders[id] = rt;
|
||||||
|
rt.proxy.on('ready', function (info) {
|
||||||
|
store.manager.addProxy(id, rt.proxy, info.leave);
|
||||||
|
cb(rt, info.metadata);
|
||||||
|
});
|
||||||
|
if (store.driveEvents) {
|
||||||
|
registerProxyEvents(rt.proxy, id);
|
||||||
|
}
|
||||||
|
return rt;
|
||||||
|
};
|
||||||
|
Store.addSharedFolder = function (clientId, data, cb) {
|
||||||
|
Store.userObjectCommand(clientId, {
|
||||||
|
cmd: 'addSharedFolder',
|
||||||
|
data: data
|
||||||
|
}, cb);
|
||||||
|
};
|
||||||
|
|
||||||
// Drive
|
// Drive
|
||||||
Store.userObjectCommand = function (clientId, cmdData, cb) {
|
Store.userObjectCommand = function (clientId, cmdData, cb) {
|
||||||
if (!cmdData || !cmdData.cmd) { return; }
|
if (!cmdData || !cmdData.cmd) { return; }
|
||||||
var data = cmdData.data;
|
//var data = cmdData.data;
|
||||||
var cb2 = function (data2) {
|
var cb2 = function (data2) {
|
||||||
var paths = data.paths || [data.path] || [];
|
//var paths = data.paths || [data.path] || [];
|
||||||
paths = paths.concat(data.newPath || []);
|
//paths = paths.concat(data.newPath ? [data.newPath] : []);
|
||||||
paths.forEach(function (p) {
|
//paths.forEach(function (p) {
|
||||||
sendDriveEvent('DRIVE_CHANGE', {
|
sendDriveEvent('DRIVE_CHANGE', {
|
||||||
//path: ['drive', UserObject.FILES_DATA]
|
path: ['drive', UserObject.FILES_DATA]
|
||||||
path: ['drive'].concat(p)
|
//path: ['drive'].concat(p)
|
||||||
}, clientId);
|
}, clientId);
|
||||||
});
|
//});
|
||||||
|
onSync(function () {
|
||||||
cb(data2);
|
cb(data2);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
switch (cmdData.cmd) {
|
store.manager.command(cmdData, cb2);
|
||||||
case 'move':
|
|
||||||
store.userObject.move(data.paths, data.newPath, cb2); break;
|
|
||||||
case 'restore':
|
|
||||||
store.userObject.restore(data.path, cb2); break;
|
|
||||||
case 'addFolder':
|
|
||||||
store.userObject.addFolder(data.path, data.name, cb2); break;
|
|
||||||
case 'delete':
|
|
||||||
store.userObject.delete(data.paths, cb2, data.nocheck, data.isOwnPadRemoved); break;
|
|
||||||
case 'emptyTrash':
|
|
||||||
store.userObject.emptyTrash(cb2); break;
|
|
||||||
case 'rename':
|
|
||||||
store.userObject.rename(data.path, data.newName, cb2); break;
|
|
||||||
default:
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Clients management
|
// Clients management
|
||||||
@ -1259,32 +1254,41 @@ define([
|
|||||||
|
|
||||||
// Special events
|
// Special events
|
||||||
|
|
||||||
var driveEventInit = false;
|
|
||||||
sendDriveEvent = function (q, data, sender) {
|
sendDriveEvent = function (q, data, sender) {
|
||||||
driveEventClients.forEach(function (cId) {
|
driveEventClients.forEach(function (cId) {
|
||||||
if (cId === sender) { return; }
|
if (cId === sender) { return; }
|
||||||
postMessage(cId, q, data);
|
postMessage(cId, q, data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
Store._subscribeToDrive = function (clientId) {
|
registerProxyEvents = function (proxy, fId) {
|
||||||
if (driveEventClients.indexOf(clientId) === -1) {
|
proxy.on('change', [], function (o, n, p) {
|
||||||
driveEventClients.push(clientId);
|
|
||||||
}
|
|
||||||
if (!driveEventInit) {
|
|
||||||
store.proxy.on('change', [], function (o, n, p) {
|
|
||||||
sendDriveEvent('DRIVE_CHANGE', {
|
sendDriveEvent('DRIVE_CHANGE', {
|
||||||
|
id: fId,
|
||||||
old: o,
|
old: o,
|
||||||
new: n,
|
new: n,
|
||||||
path: p
|
path: p
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
store.proxy.on('remove', [], function (o, p) {
|
proxy.on('remove', [], function (o, p) {
|
||||||
sendDriveEvent(clientId, 'DRIVE_REMOVE', {
|
sendDriveEvent('DRIVE_REMOVE', {
|
||||||
|
id: fId,
|
||||||
old: o,
|
old: o,
|
||||||
path: p
|
path: p
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
driveEventInit = true;
|
};
|
||||||
|
|
||||||
|
Store._subscribeToDrive = function (clientId) {
|
||||||
|
if (driveEventClients.indexOf(clientId) === -1) {
|
||||||
|
driveEventClients.push(clientId);
|
||||||
|
}
|
||||||
|
if (!store.driveEvents) {
|
||||||
|
store.driveEvents = true;
|
||||||
|
registerProxyEvents(store.proxy);
|
||||||
|
Object.keys(store.manager.folders).forEach(function (fId) {
|
||||||
|
var proxy = store.manager.folders[fId].proxy;
|
||||||
|
registerProxyEvents(proxy, fId);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1340,12 +1344,60 @@ define([
|
|||||||
/////////////////////// Init /////////////////////////////////////
|
/////////////////////// Init /////////////////////////////////////
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
var loadSharedFolders = function (waitFor) {
|
||||||
|
store.sharedFolders = {};
|
||||||
|
var shared = Util.find(store.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {};
|
||||||
|
// Check if any of our shared folder is expired or deleted by its owner.
|
||||||
|
// If we don't check now, Listmap will create an empty proxy if it no longer exists on
|
||||||
|
// the server.
|
||||||
|
nThen(function (waitFor) {
|
||||||
|
var edPublic = store.proxy.edPublic;
|
||||||
|
var checkExpired = Object.keys(shared).filter(function (fId) {
|
||||||
|
var d = shared[fId];
|
||||||
|
return (Array.isArray(d.owners) && d.owners.length &&
|
||||||
|
(!edPublic || d.owners.indexOf(edPublic) === -1))
|
||||||
|
|| (d.expire && d.expire < (+new Date()));
|
||||||
|
}).map(function (fId) {
|
||||||
|
return shared[fId].channel;
|
||||||
|
});
|
||||||
|
Store.getDeletedPads(null, {list: checkExpired}, waitFor(function (chans) {
|
||||||
|
if (chans && chans.error) { return void console.error(chans.error); }
|
||||||
|
if (!Array.isArray(chans) || !chans.length) { return; }
|
||||||
|
var toDelete = [];
|
||||||
|
Object.keys(shared).forEach(function (fId) {
|
||||||
|
if (chans.indexOf(shared[fId].channel) !== -1
|
||||||
|
&& toDelete.indexOf(fId) === -1) {
|
||||||
|
toDelete.push(fId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toDelete.forEach(function (fId) {
|
||||||
|
var paths = store.userObject.findFile(Number(fId));
|
||||||
|
store.userObject.delete(paths, waitFor(), true);
|
||||||
|
delete shared[fId];
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
Object.keys(shared).forEach(function (id) {
|
||||||
|
var sf = shared[id];
|
||||||
|
loadSharedFolder(id, sf, waitFor());
|
||||||
|
});
|
||||||
|
}).nThen(waitFor());
|
||||||
|
};
|
||||||
|
|
||||||
var onReady = function (clientId, returned, cb) {
|
var onReady = function (clientId, returned, cb) {
|
||||||
var proxy = store.proxy;
|
var proxy = store.proxy;
|
||||||
var userObject = store.userObject = UserObject.init(proxy.drive, {
|
var unpin = function (data, cb) {
|
||||||
pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
|
if (!store.loggedIn) { return void cb(); }
|
||||||
unpinPads: function (data, cb) { Store.unpinPads(null, data, cb); },
|
Store.unpinPads(null, data, cb);
|
||||||
removeOwnedChannel: function (data, cb) { Store.removeOwnedChannel(null, data, cb); },
|
};
|
||||||
|
var pin = function (data, cb) {
|
||||||
|
if (!store.loggedIn) { return void cb(); }
|
||||||
|
Store.pinPads(null, data, cb);
|
||||||
|
};
|
||||||
|
var manager = store.manager = ProxyManager.create(proxy.drive, proxy.edPublic,
|
||||||
|
pin, unpin, loadSharedFolder, {
|
||||||
|
outer: true,
|
||||||
|
removeOwnedChannel: function (data, cb) { Store.removeOwnedChannel('', data, cb); },
|
||||||
edPublic: store.proxy.edPublic,
|
edPublic: store.proxy.edPublic,
|
||||||
loggedIn: store.loggedIn,
|
loggedIn: store.loggedIn,
|
||||||
log: function (msg) {
|
log: function (msg) {
|
||||||
@ -1353,6 +1405,7 @@ define([
|
|||||||
sendDriveEvent("DRIVE_LOG", msg);
|
sendDriveEvent("DRIVE_LOG", msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
var userObject = store.userObject = manager.user.userObject;
|
||||||
nThen(function (waitFor) {
|
nThen(function (waitFor) {
|
||||||
postMessage(clientId, 'LOADING_DRIVE', {
|
postMessage(clientId, 'LOADING_DRIVE', {
|
||||||
state: 2
|
state: 2
|
||||||
@ -1361,16 +1414,18 @@ define([
|
|||||||
}).nThen(function (waitFor) {
|
}).nThen(function (waitFor) {
|
||||||
Migrate(proxy, waitFor(), function (version, progress) {
|
Migrate(proxy, waitFor(), function (version, progress) {
|
||||||
postMessage(clientId, 'LOADING_DRIVE', {
|
postMessage(clientId, 'LOADING_DRIVE', {
|
||||||
state: 2,
|
state: (2 + (version / 10)),
|
||||||
progress: progress
|
progress: progress
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).nThen(function () {
|
Store.initAnonRpc(null, null, waitFor());
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
postMessage(clientId, 'LOADING_DRIVE', {
|
postMessage(clientId, 'LOADING_DRIVE', {
|
||||||
state: 3
|
state: 3
|
||||||
});
|
});
|
||||||
userObject.fixFiles();
|
userObject.fixFiles();
|
||||||
|
loadSharedFolders(waitFor);
|
||||||
|
}).nThen(function () {
|
||||||
var requestLogin = function () {
|
var requestLogin = function () {
|
||||||
broadcast([], "REQUEST_LOGIN");
|
broadcast([], "REQUEST_LOGIN");
|
||||||
};
|
};
|
||||||
|
|||||||
@ -42,6 +42,7 @@ define([
|
|||||||
MOVE_TO_TRASH: Store.moveToTrash,
|
MOVE_TO_TRASH: Store.moveToTrash,
|
||||||
RESET_DRIVE: Store.resetDrive,
|
RESET_DRIVE: Store.resetDrive,
|
||||||
GET_METADATA: Store.getMetadata,
|
GET_METADATA: Store.getMetadata,
|
||||||
|
IS_ONLY_IN_SHARED_FOLDER: Store.isOnlyInSharedFolder,
|
||||||
SET_DISPLAY_NAME: Store.setDisplayName,
|
SET_DISPLAY_NAME: Store.setDisplayName,
|
||||||
SET_PAD_ATTRIBUTE: Store.setPadAttribute,
|
SET_PAD_ATTRIBUTE: Store.setPadAttribute,
|
||||||
GET_PAD_ATTRIBUTE: Store.getPadAttribute,
|
GET_PAD_ATTRIBUTE: Store.getPadAttribute,
|
||||||
@ -53,6 +54,8 @@ define([
|
|||||||
GET_PAD_DATA: Store.getPadData,
|
GET_PAD_DATA: Store.getPadData,
|
||||||
GET_STRONGER_HASH: Store.getStrongerHash,
|
GET_STRONGER_HASH: Store.getStrongerHash,
|
||||||
INCREMENT_TEMPLATE_USE: Store.incrementTemplateUse,
|
INCREMENT_TEMPLATE_USE: Store.incrementTemplateUse,
|
||||||
|
GET_SHARED_FOLDER: Store.getSharedFolder,
|
||||||
|
ADD_SHARED_FOLDER: Store.addSharedFolder,
|
||||||
// Messaging
|
// Messaging
|
||||||
INVITE_FROM_USERLIST: Store.inviteFromUserlist,
|
INVITE_FROM_USERLIST: Store.inviteFromUserlist,
|
||||||
ADD_DIRECT_MESSAGE_HANDLERS: Store.addDirectMessageHandlers,
|
ADD_DIRECT_MESSAGE_HANDLERS: Store.addDirectMessageHandlers,
|
||||||
|
|||||||
@ -14,15 +14,11 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.init = function (config, exp, files) {
|
module.init = function (config, exp, files) {
|
||||||
var unpinPads = config.unpinPads || function () {
|
|
||||||
console.error("unpinPads was not provided");
|
|
||||||
};
|
|
||||||
var pinPads = config.pinPads;
|
|
||||||
var removeOwnedChannel = config.removeOwnedChannel || function () {
|
var removeOwnedChannel = config.removeOwnedChannel || function () {
|
||||||
console.error("removeOwnedChannel was not provided");
|
console.error("removeOwnedChannel was not provided");
|
||||||
};
|
};
|
||||||
var loggedIn = config.loggedIn;
|
var loggedIn = config.loggedIn;
|
||||||
var workgroup = config.workgroup;
|
var sharedFolder = config.sharedFolder;
|
||||||
var edPublic = config.edPublic;
|
var edPublic = config.edPublic;
|
||||||
|
|
||||||
var ROOT = exp.ROOT;
|
var ROOT = exp.ROOT;
|
||||||
@ -31,6 +27,7 @@ define([
|
|||||||
var UNSORTED = exp.UNSORTED;
|
var UNSORTED = exp.UNSORTED;
|
||||||
var TRASH = exp.TRASH;
|
var TRASH = exp.TRASH;
|
||||||
var TEMPLATE = exp.TEMPLATE;
|
var TEMPLATE = exp.TEMPLATE;
|
||||||
|
var SHARED_FOLDERS = exp.SHARED_FOLDERS;
|
||||||
|
|
||||||
var debug = exp.debug;
|
var debug = exp.debug;
|
||||||
|
|
||||||
@ -50,35 +47,31 @@ define([
|
|||||||
var data = exp.getFileData(id);
|
var data = exp.getFileData(id);
|
||||||
cb(null, clone(data[attr]));
|
cb(null, clone(data[attr]));
|
||||||
};
|
};
|
||||||
var removePadAttribute = exp.removePadAttribute = function (f) {
|
|
||||||
if (typeof(f) !== 'string') {
|
|
||||||
console.error("Can't find pad attribute for an undefined pad");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Object.keys(files).forEach(function (key) {
|
|
||||||
var hash = f.indexOf('#') !== -1 ? f.slice(f.indexOf('#') + 1) : null;
|
|
||||||
if (hash && key.indexOf(hash) === 0) {
|
|
||||||
exp.debug("Deleting pad attribute in the realtime object");
|
|
||||||
delete files[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exp.pushData = function (data, cb) {
|
exp.pushData = function (data, cb) {
|
||||||
if (typeof cb !== "function") { cb = function () {}; }
|
if (typeof cb !== "function") { cb = function () {}; }
|
||||||
var todo = function () {
|
|
||||||
var id = Util.createRandomInteger();
|
var id = Util.createRandomInteger();
|
||||||
files[FILES_DATA][id] = data;
|
files[FILES_DATA][id] = data;
|
||||||
cb(null, id);
|
cb(null, id);
|
||||||
};
|
};
|
||||||
if (!loggedIn || !AppConfig.enablePinning || config.testMode) {
|
|
||||||
return void todo();
|
exp.pushSharedFolder = function (data, cb) {
|
||||||
|
if (typeof cb !== "function") { cb = function () {}; }
|
||||||
|
|
||||||
|
// Check if we already have this shared folder in our drive
|
||||||
|
if (Object.keys(files[SHARED_FOLDERS]).some(function (k) {
|
||||||
|
return files[SHARED_FOLDERS][k].channel === data.channel;
|
||||||
|
})) {
|
||||||
|
return void cb ('EEXISTS');
|
||||||
}
|
}
|
||||||
if (!pinPads) { return; }
|
|
||||||
pinPads([data.channel], function (obj) {
|
// Add the folder
|
||||||
if (obj && obj.error) { return void cb(obj.error); }
|
if (!loggedIn || !AppConfig.enablePinning || config.testMode) {
|
||||||
todo();
|
return void cb("EAUTH");
|
||||||
});
|
}
|
||||||
|
var id = Util.createRandomInteger();
|
||||||
|
files[SHARED_FOLDERS][id] = data;
|
||||||
|
cb(null, id);
|
||||||
};
|
};
|
||||||
|
|
||||||
// FILES DATA
|
// FILES DATA
|
||||||
@ -89,19 +82,20 @@ define([
|
|||||||
// Find files in FILES_DATA that are not anymore in the drive, and remove them from
|
// Find files in FILES_DATA that are not anymore in the drive, and remove them from
|
||||||
// FILES_DATA. If there are owned pads, remove them from server too, unless the flag tells
|
// FILES_DATA. If there are owned pads, remove them from server too, unless the flag tells
|
||||||
// us they're already removed
|
// us they're already removed
|
||||||
exp.checkDeletedFiles = function (isOwnPadRemoved) {
|
exp.checkDeletedFiles = function (isOwnPadRemoved, cb) {
|
||||||
// Nothing in FILES_DATA for workgroups
|
if (!loggedIn && !config.testMode) { return void cb(); }
|
||||||
if (workgroup || (!loggedIn && !config.testMode)) { return; }
|
|
||||||
|
|
||||||
var filesList = exp.getFiles([ROOT, 'hrefArray', TRASH]);
|
var filesList = exp.getFiles([ROOT, 'hrefArray', TRASH]);
|
||||||
var toClean = [];
|
var toClean = [];
|
||||||
exp.getFiles([FILES_DATA]).forEach(function (id) {
|
var ownedRemoved = [];
|
||||||
|
exp.getFiles([FILES_DATA, SHARED_FOLDERS]).forEach(function (id) {
|
||||||
if (filesList.indexOf(id) === -1) {
|
if (filesList.indexOf(id) === -1) {
|
||||||
var fd = exp.getFileData(id);
|
var fd = exp.isSharedFolder(id) ? files[SHARED_FOLDERS][id] : exp.getFileData(id);
|
||||||
var channelId = fd.channel;
|
var channelId = fd.channel;
|
||||||
// If trying to remove an owned pad, remove it from server also
|
// If trying to remove an owned pad, remove it from server also
|
||||||
if (!isOwnPadRemoved &&
|
if (!isOwnPadRemoved && !sharedFolder &&
|
||||||
fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
|
fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
|
||||||
|
if (channelId) { ownedRemoved.push(channelId); }
|
||||||
removeOwnedChannel(channelId, function (obj) {
|
removeOwnedChannel(channelId, function (obj) {
|
||||||
if (obj && obj.error) {
|
if (obj && obj.error) {
|
||||||
// If the error is that the file is already removed, nothing to
|
// If the error is that the file is already removed, nothing to
|
||||||
@ -116,14 +110,15 @@ define([
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (channelId) { toClean.push(channelId); }
|
if (channelId) { toClean.push(channelId); }
|
||||||
|
if (exp.isSharedFolder(id)) {
|
||||||
|
delete files[SHARED_FOLDERS][id];
|
||||||
|
} else {
|
||||||
spliceFileData(id);
|
spliceFileData(id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (!toClean.length) { return; }
|
if (!toClean.length) { return void cb(); }
|
||||||
unpinPads(toClean, function (response) {
|
cb(null, toClean, ownedRemoved);
|
||||||
if (response && response.error) { return console.error(response.error); }
|
|
||||||
// console.error(response);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
var deleteHrefs = function (ids) {
|
var deleteHrefs = function (ids) {
|
||||||
ids.forEach(function (obj) {
|
ids.forEach(function (obj) {
|
||||||
@ -137,7 +132,7 @@ define([
|
|||||||
files[TRASH][obj.name].splice(idx, 1);
|
files[TRASH][obj.name].splice(idx, 1);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
exp.deleteMultiplePermanently = function (paths, nocheck, isOwnPadRemoved) {
|
exp.deleteMultiplePermanently = function (paths, nocheck, isOwnPadRemoved, cb) {
|
||||||
var hrefPaths = paths.filter(function(x) { return exp.isPathIn(x, ['hrefArray']); });
|
var hrefPaths = paths.filter(function(x) { return exp.isPathIn(x, ['hrefArray']); });
|
||||||
var rootPaths = paths.filter(function(x) { return exp.isPathIn(x, [ROOT]); });
|
var rootPaths = paths.filter(function(x) { return exp.isPathIn(x, [ROOT]); });
|
||||||
var trashPaths = paths.filter(function(x) { return exp.isPathIn(x, [TRASH]); });
|
var trashPaths = paths.filter(function(x) { return exp.isPathIn(x, [TRASH]); });
|
||||||
@ -145,14 +140,11 @@ define([
|
|||||||
|
|
||||||
if (!loggedIn && !config.testMode) {
|
if (!loggedIn && !config.testMode) {
|
||||||
allFilesPaths.forEach(function (path) {
|
allFilesPaths.forEach(function (path) {
|
||||||
var el = exp.find(path);
|
var id = path[1];
|
||||||
if (!el) { return; }
|
|
||||||
var id = exp.getIdFromHref(el.href);
|
|
||||||
if (!id) { return; }
|
if (!id) { return; }
|
||||||
spliceFileData(id);
|
spliceFileData(id);
|
||||||
removePadAttribute(el.href);
|
|
||||||
});
|
});
|
||||||
return;
|
return void cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
var ids = [];
|
var ids = [];
|
||||||
@ -192,11 +184,68 @@ 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
|
||||||
// OLD_FILES_DATA (replaceHref)
|
// FILES_DATA (replaceHref)
|
||||||
if (!nocheck) { exp.checkDeletedFiles(isOwnPadRemoved); }
|
if (!nocheck) { exp.checkDeletedFiles(isOwnPadRemoved, cb); }
|
||||||
|
else { cb(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Move
|
// Move
|
||||||
|
|
||||||
|
// From another drive
|
||||||
|
exp.copyFromOtherDrive = function (path, element, data) {
|
||||||
|
// Copy files data
|
||||||
|
// We have to remove pads that are already in the current proxy to make sure
|
||||||
|
// we won't create duplicates
|
||||||
|
|
||||||
|
var toRemove = [];
|
||||||
|
Object.keys(data).forEach(function (id) {
|
||||||
|
id = Number(id);
|
||||||
|
// Find and maybe update existing pads with the same channel id
|
||||||
|
var d = data[id];
|
||||||
|
var found = false;
|
||||||
|
for (var i in files[FILES_DATA]) {
|
||||||
|
if (files[FILES_DATA][i].channel === d.channel) {
|
||||||
|
// Update href?
|
||||||
|
if (!files[FILES_DATA][i].href) { files[FILES_DATA][i].href = d.href; }
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
toRemove.push(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
files[FILES_DATA][id] = data[id];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove existing pads from the "element" variable
|
||||||
|
if (exp.isFile(element) && toRemove.indexOf(element) !== -1) {
|
||||||
|
exp.log(Messages.sharedFolders_duplicate);
|
||||||
|
return;
|
||||||
|
} else if (exp.isFolder(element)) {
|
||||||
|
var _removeExisting = function (root) {
|
||||||
|
for (var k in root) {
|
||||||
|
if (exp.isFile(root[k])) {
|
||||||
|
if (toRemove.indexOf(root[k]) !== -1) {
|
||||||
|
exp.log(Messages.sharedFolders_duplicate);
|
||||||
|
delete root[k];
|
||||||
|
}
|
||||||
|
} else if (exp.isFolder(root[k])) {
|
||||||
|
_removeExisting(root[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_removeExisting(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Copy file or folder
|
||||||
|
var newParent = exp.find(path);
|
||||||
|
var newName = exp.getAvailableName(newParent, Hash.createChannelId());
|
||||||
|
newParent[newName] = element;
|
||||||
|
};
|
||||||
|
|
||||||
|
// From the same drive
|
||||||
var pushToTrash = function (name, element, path) {
|
var pushToTrash = function (name, element, path) {
|
||||||
var trash = files[TRASH];
|
var trash = files[TRASH];
|
||||||
if (typeof(trash[name]) === "undefined") { trash[name] = []; }
|
if (typeof(trash[name]) === "undefined") { trash[name] = []; }
|
||||||
@ -259,7 +308,6 @@ define([
|
|||||||
if (!id) { return; }
|
if (!id) { return; }
|
||||||
if (!loggedIn && !config.testMode) {
|
if (!loggedIn && !config.testMode) {
|
||||||
// delete permanently
|
// delete permanently
|
||||||
exp.removePadAttribute(href);
|
|
||||||
spliceFileData(id);
|
spliceFileData(id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -268,14 +316,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
// REPLACE
|
// REPLACE
|
||||||
exp.replace = function (o, n) {
|
// If all the occurences of an href are in the trash, remove them and add the file in root.
|
||||||
var idO = exp.getIdFromHref(o);
|
|
||||||
if (!idO || !exp.isFile(idO)) { return; }
|
|
||||||
var data = exp.getFileData(idO);
|
|
||||||
if (!data) { return; }
|
|
||||||
data.href = n;
|
|
||||||
};
|
|
||||||
// If all the occurences of an href are in the trash, remvoe them and add the file in root.
|
|
||||||
// This is use with setPadTitle when we open a stronger version of a deleted pad
|
// This is use with setPadTitle when we open a stronger version of a deleted pad
|
||||||
exp.restoreHref = function (href) {
|
exp.restoreHref = function (href) {
|
||||||
var idO = exp.getIdFromHref(href);
|
var idO = exp.getIdFromHref(href);
|
||||||
@ -300,9 +341,9 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
exp.add = function (id, path) {
|
exp.add = function (id, path) {
|
||||||
// TODO WW
|
|
||||||
if (!loggedIn && !config.testMode) { return; }
|
if (!loggedIn && !config.testMode) { return; }
|
||||||
var data = files[FILES_DATA][id];
|
id = Number(id);
|
||||||
|
var data = files[FILES_DATA][id] || files[SHARED_FOLDERS][id];
|
||||||
if (!data || typeof(data) !== "object") { return; }
|
if (!data || typeof(data) !== "object") { return; }
|
||||||
var newPath = path, parentEl;
|
var newPath = path, parentEl;
|
||||||
if (path && !Array.isArray(path)) {
|
if (path && !Array.isArray(path)) {
|
||||||
@ -406,7 +447,6 @@ define([
|
|||||||
});
|
});
|
||||||
delete files[OLD_FILES_DATA];
|
delete files[OLD_FILES_DATA];
|
||||||
delete files.migrate;
|
delete files.migrate;
|
||||||
console.log('done');
|
|
||||||
todo();
|
todo();
|
||||||
};
|
};
|
||||||
if (exp.rt) {
|
if (exp.rt) {
|
||||||
@ -473,6 +513,7 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
var fixTrashRoot = function () {
|
var fixTrashRoot = function () {
|
||||||
|
if (sharedFolder) { return; }
|
||||||
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;
|
||||||
@ -516,6 +557,7 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
var fixTemplate = function () {
|
var fixTemplate = function () {
|
||||||
|
if (sharedFolder) { return; }
|
||||||
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] = Util.deduplicateString(files[TEMPLATE].slice());
|
files[TEMPLATE] = Util.deduplicateString(files[TEMPLATE].slice());
|
||||||
var us = files[TEMPLATE];
|
var us = files[TEMPLATE];
|
||||||
@ -547,7 +589,7 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
var fixFilesData = function () {
|
var fixFilesData = function () {
|
||||||
if (typeof files[FILES_DATA] !== "object") { debug("OLD_FILES_DATA was not an object"); files[FILES_DATA] = {}; }
|
if (typeof files[FILES_DATA] !== "object") { debug("FILES_DATA was not an object"); files[FILES_DATA] = {}; }
|
||||||
var fd = files[FILES_DATA];
|
var fd = files[FILES_DATA];
|
||||||
var rootFiles = exp.getFiles([ROOT, TRASH, 'hrefArray']);
|
var rootFiles = exp.getFiles([ROOT, TRASH, 'hrefArray']);
|
||||||
var root = exp.find([ROOT]);
|
var root = exp.find([ROOT]);
|
||||||
@ -563,13 +605,15 @@ define([
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Clean missing href
|
// Clean missing href
|
||||||
if (!el.href) {
|
if (!el.href && !el.roHref) {
|
||||||
debug("Removing an element in filesData with a missing href.", el);
|
debug("Removing an element in filesData with a missing href.", el);
|
||||||
toClean.push(id);
|
toClean.push(id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsed = Hash.parsePadUrl(el.href);
|
var parsed = Hash.parsePadUrl(el.href || el.roHref);
|
||||||
|
var secret;
|
||||||
|
|
||||||
// Clean invalid hash
|
// Clean invalid hash
|
||||||
if (!parsed.hash) {
|
if (!parsed.hash) {
|
||||||
debug("Removing an element in filesData with a invalid href.", el);
|
debug("Removing an element in filesData with a invalid href.", el);
|
||||||
@ -583,6 +627,23 @@ define([
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have an edit link, check the view link
|
||||||
|
if (el.href && parsed.hashData.type === "pad") {
|
||||||
|
if (parsed.hashData.mode === "view") {
|
||||||
|
el.roHref = el.href;
|
||||||
|
delete el.href;
|
||||||
|
} else if (!el.roHref) {
|
||||||
|
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||||
|
el.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
|
||||||
|
} else {
|
||||||
|
var parsed2 = Hash.parsePadUrl(el.roHref);
|
||||||
|
if (!parsed2.hash || !parsed2.type) {
|
||||||
|
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||||
|
el.roHref = '/' + parsed.type + '/#' + Hash.getViewHashFromKeys(secret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fix href
|
// Fix href
|
||||||
if (/^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
|
if (/^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
|
||||||
// Fix creation time
|
// Fix creation time
|
||||||
@ -592,9 +653,12 @@ define([
|
|||||||
// Fix channel
|
// Fix channel
|
||||||
if (!el.channel) {
|
if (!el.channel) {
|
||||||
try {
|
try {
|
||||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
if (!secret) {
|
||||||
|
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||||
|
}
|
||||||
el.channel = secret.channel;
|
el.channel = secret.channel;
|
||||||
console.log('Adding missing channel in filesData ', el.channel);
|
console.log(el);
|
||||||
|
debug('Adding missing channel in filesData ', el.channel);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
@ -611,6 +675,21 @@ define([
|
|||||||
spliceFileData(id);
|
spliceFileData(id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
var fixSharedFolders = function () {
|
||||||
|
if (sharedFolder) { return; }
|
||||||
|
if (typeof(files[SHARED_FOLDERS]) !== "object") { debug("SHARED_FOLDER was not an object"); files[SHARED_FOLDERS] = {}; }
|
||||||
|
var sf = files[SHARED_FOLDERS];
|
||||||
|
var rootFiles = exp.getFiles([ROOT]);
|
||||||
|
var root = exp.find([ROOT]);
|
||||||
|
for (var id in sf) {
|
||||||
|
id = Number(id);
|
||||||
|
if (rootFiles.indexOf(id) === -1) {
|
||||||
|
console.log('missing' + id);
|
||||||
|
var newName = Hash.createChannelId();
|
||||||
|
root[newName] = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var fixDrive = function () {
|
var fixDrive = function () {
|
||||||
Object.keys(files).forEach(function (key) {
|
Object.keys(files).forEach(function (key) {
|
||||||
@ -618,12 +697,11 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fixSharedFolders();
|
||||||
fixRoot();
|
fixRoot();
|
||||||
fixTrashRoot();
|
fixTrashRoot();
|
||||||
if (!workgroup) {
|
|
||||||
fixTemplate();
|
fixTemplate();
|
||||||
fixFilesData();
|
fixFilesData();
|
||||||
}
|
|
||||||
fixDrive();
|
fixDrive();
|
||||||
|
|
||||||
if (JSON.stringify(files) !== before) {
|
if (JSON.stringify(files) !== before) {
|
||||||
|
|||||||
982
www/common/proxy-manager.js
Normal file
982
www/common/proxy-manager.js
Normal file
@ -0,0 +1,982 @@
|
|||||||
|
define([
|
||||||
|
'/common/userObject.js',
|
||||||
|
'/common/common-util.js',
|
||||||
|
'/common/common-hash.js',
|
||||||
|
'/customize/messages.js',
|
||||||
|
'/bower_components/nthen/index.js',
|
||||||
|
], function (UserObject, Util, Hash, Messages, nThen) {
|
||||||
|
|
||||||
|
|
||||||
|
var getConfig = function (Env) {
|
||||||
|
var cfg = {};
|
||||||
|
for (var k in Env.cfg) { cfg[k] = Env.cfg[k]; }
|
||||||
|
return cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add a shared folder to the list
|
||||||
|
var addProxy = function (Env, id, proxy, leave) {
|
||||||
|
var cfg = getConfig(Env);
|
||||||
|
cfg.sharedFolder = true;
|
||||||
|
cfg.id = id;
|
||||||
|
var userObject = UserObject.init(proxy, cfg);
|
||||||
|
if (userObject.fixFiles) {
|
||||||
|
// Only in outer
|
||||||
|
userObject.fixFiles();
|
||||||
|
}
|
||||||
|
Env.folders[id] = {
|
||||||
|
proxy: proxy,
|
||||||
|
userObject: userObject,
|
||||||
|
leave: leave
|
||||||
|
};
|
||||||
|
return userObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Remove a shared folder from the list
|
||||||
|
var removeProxy = function (Env, id) {
|
||||||
|
var f = Env.folders[id];
|
||||||
|
if (!f) { return; }
|
||||||
|
f.leave();
|
||||||
|
delete Env.folders[id];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Tools
|
||||||
|
*/
|
||||||
|
|
||||||
|
var _getUserObjects = function (Env) {
|
||||||
|
var userObjects = [Env.user.userObject];
|
||||||
|
var foldersUO = Object.keys(Env.folders).map(function (k) {
|
||||||
|
return Env.folders[k].userObject;
|
||||||
|
});
|
||||||
|
Array.prototype.push.apply(userObjects, foldersUO);
|
||||||
|
return userObjects;
|
||||||
|
};
|
||||||
|
|
||||||
|
var _getUserObjectFromId = function (Env, id) {
|
||||||
|
var userObjects = _getUserObjects(Env);
|
||||||
|
var userObject = Env.user.userObject;
|
||||||
|
userObjects.some(function (uo) {
|
||||||
|
if (Object.keys(uo.getFileData(id)).length) {
|
||||||
|
userObject = uo;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return userObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
var _getUserObjectPath = function (Env, uo) {
|
||||||
|
var fId = Number(uo.id);
|
||||||
|
if (!fId) { return; }
|
||||||
|
var fPath = Env.user.userObject.findFile(fId)[0];
|
||||||
|
return fPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return files data objects associated to a channel for setPadTitle
|
||||||
|
// All occurences are returned, in drive or shared folders
|
||||||
|
var findChannel = function (Env, channel) {
|
||||||
|
var ret = [];
|
||||||
|
Env.user.userObject.findChannels([channel]).forEach(function (id) {
|
||||||
|
ret.push({
|
||||||
|
data: Env.user.userObject.getFileData(id),
|
||||||
|
userObject: Env.user.userObject
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Object.keys(Env.folders).forEach(function (fId) {
|
||||||
|
Env.folders[fId].userObject.findChannels([channel]).forEach(function (id) {
|
||||||
|
ret.push({
|
||||||
|
fId: fId,
|
||||||
|
data: Env.folders[fId].userObject.getFileData(id),
|
||||||
|
userObject: Env.folders[fId].userObject
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
// Return files data objects associated to a given href for setPadAttribute...
|
||||||
|
var findHref = function (Env, href) {
|
||||||
|
var ret = [];
|
||||||
|
var id = Env.user.userObject.getIdFromHref(href);
|
||||||
|
if (id) {
|
||||||
|
ret.push({
|
||||||
|
data: Env.user.userObject.getFileData(id),
|
||||||
|
userObject: Env.user.userObject
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Object.keys(Env.folders).forEach(function (fId) {
|
||||||
|
var id = Env.folders[fId].userObject.getIdFromHref(href);
|
||||||
|
if (!id) { return; }
|
||||||
|
ret.push({
|
||||||
|
fId: fId,
|
||||||
|
data: Env.folders[fId].userObject.getFileData(id),
|
||||||
|
userObject: Env.folders[fId].userObject
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
// Return paths linked to a file ID
|
||||||
|
var findFile = function (Env, id) {
|
||||||
|
var ret = [];
|
||||||
|
var userObjects = _getUserObjects(Env);
|
||||||
|
userObjects.forEach(function (uo) {
|
||||||
|
var fPath = _getUserObjectPath(Env, uo);
|
||||||
|
var results = uo.findFile(id);
|
||||||
|
if (fPath) {
|
||||||
|
// This is a shared folder, we have to fix the paths in the results
|
||||||
|
results.forEach(function (p) {
|
||||||
|
Array.prototype.unshift.apply(p, fPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Push the results from this proxy
|
||||||
|
Array.prototype.push.apply(ret, results);
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns file IDs corresponding to the provided channels
|
||||||
|
var _findChannels = function (Env, channels, onlyMain) {
|
||||||
|
if (onlyMain) {
|
||||||
|
return Env.user.userObject.findChannels(channels);
|
||||||
|
}
|
||||||
|
var ret = [];
|
||||||
|
var userObjects = _getUserObjects(Env);
|
||||||
|
userObjects.forEach(function (uo) {
|
||||||
|
var results = uo.findChannels(channels);
|
||||||
|
Array.prototype.push.apply(ret, results);
|
||||||
|
});
|
||||||
|
ret = Util.deduplicateString(ret);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
var _getFileData = function (Env, id) {
|
||||||
|
var userObjects = _getUserObjects(Env);
|
||||||
|
var data = {};
|
||||||
|
userObjects.some(function (uo) {
|
||||||
|
data = uo.getFileData(id);
|
||||||
|
if (Object.keys(data).length) { return true; }
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Transform an absolute path into a path relative to the correct shared folder
|
||||||
|
var _resolvePath = function (Env, path) {
|
||||||
|
var res = {
|
||||||
|
id: null,
|
||||||
|
userObject: Env.user.userObject,
|
||||||
|
path: path
|
||||||
|
};
|
||||||
|
if (!Array.isArray(path) || path.length <= 1) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
var current;
|
||||||
|
var uo = Env.user.userObject;
|
||||||
|
// We don't need to check the last element of the path because we only need to split it
|
||||||
|
// when the path contains an element inside the shared folder
|
||||||
|
for (var i=2; i<path.length; i++) {
|
||||||
|
current = uo.find(path.slice(0,i));
|
||||||
|
if (uo.isSharedFolder(current)) {
|
||||||
|
res = {
|
||||||
|
id: current,
|
||||||
|
userObject: Env.folders[current].userObject,
|
||||||
|
path: path.slice(i)
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
var _resolvePaths = function (Env, paths) {
|
||||||
|
var main = [];
|
||||||
|
var folders = {};
|
||||||
|
paths.forEach(function (path) {
|
||||||
|
var r = _resolvePath(Env, path);
|
||||||
|
if (r.id) {
|
||||||
|
if (!folders[r.id]) {
|
||||||
|
folders[r.id] = [r.path];
|
||||||
|
} else {
|
||||||
|
folders[r.id].push(r.path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
main.push(r.path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
main: main,
|
||||||
|
folders: folders
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get a copy of the elements located in the given paths, with their files data
|
||||||
|
var _getCopyFromPaths = function (paths, userObject) {
|
||||||
|
var data = [];
|
||||||
|
paths.forEach(function (path) {
|
||||||
|
var el = userObject.find(path);
|
||||||
|
var files = [];
|
||||||
|
|
||||||
|
// Get the files ID from the current path (file or folder)
|
||||||
|
if (userObject.isFile(el)) {
|
||||||
|
files.push(el);
|
||||||
|
} else {
|
||||||
|
userObject.getFilesRecursively(el, files);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the shared folder from this list of files ID
|
||||||
|
files.filter(function (f) { return !userObject.isSharedFolder(f); });
|
||||||
|
// Deduplicate
|
||||||
|
files = Util.deduplicateString(files);
|
||||||
|
|
||||||
|
// Get the files data associated to these files
|
||||||
|
var filesData = {};
|
||||||
|
files.forEach(function (f) {
|
||||||
|
filesData[f] = userObject.getFileData(f);
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO RO
|
||||||
|
// Encrypt or decrypt edit link here
|
||||||
|
// filesData.forEach(function (d) { d.href = encrypt(d.href); });
|
||||||
|
|
||||||
|
|
||||||
|
data.push({
|
||||||
|
el: el,
|
||||||
|
data: filesData
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Drive RPC
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Move files or folders in the drive
|
||||||
|
var _move = function (Env, data, cb) {
|
||||||
|
var resolved = _resolvePaths(Env, data.paths);
|
||||||
|
var newResolved = _resolvePath(Env, data.newPath);
|
||||||
|
|
||||||
|
if (!newResolved.userObject.isFolder(newResolved.path)) { return void cb(); }
|
||||||
|
|
||||||
|
nThen(function (waitFor) {
|
||||||
|
if (resolved.main.length) {
|
||||||
|
// Move from the main drive
|
||||||
|
if (!newResolved.id) {
|
||||||
|
// Move from the main drive to the main drive
|
||||||
|
Env.user.userObject.move(resolved.main, newResolved.path, waitFor());
|
||||||
|
} else {
|
||||||
|
// Move from the main drive to a shared folder
|
||||||
|
|
||||||
|
// Copy the elements to the new location
|
||||||
|
var toCopy = _getCopyFromPaths(resolved.main, Env.user.userObject);
|
||||||
|
var newUserObject = newResolved.userObject;
|
||||||
|
var ownedPads = [];
|
||||||
|
toCopy.forEach(function (obj) {
|
||||||
|
newUserObject.copyFromOtherDrive(newResolved.path, obj.el, obj.data);
|
||||||
|
var _owned = Object.keys(obj.data).filter(function (id) {
|
||||||
|
var owners = obj.data[id].owners;
|
||||||
|
return Array.isArray(owners) && owners.indexOf(Env.edPublic) !== -1;
|
||||||
|
});
|
||||||
|
Array.prototype.push.apply(ownedPads, _owned);
|
||||||
|
});
|
||||||
|
|
||||||
|
var rootPath = resolved.main[0].slice();
|
||||||
|
rootPath.pop();
|
||||||
|
ownedPads = Util.deduplicateString(ownedPads);
|
||||||
|
ownedPads.forEach(function (id) {
|
||||||
|
Env.user.userObject.add(Number(id), rootPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove the elements from the old location (without unpinning)
|
||||||
|
Env.user.userObject.delete(resolved.main, waitFor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var folderIds = Object.keys(resolved.folders);
|
||||||
|
if (folderIds.length) {
|
||||||
|
// Move from a shared folder
|
||||||
|
folderIds.forEach(function (fIdStr) {
|
||||||
|
var fId = Number(fIdStr);
|
||||||
|
var paths = resolved.folders[fId];
|
||||||
|
if (newResolved.id === fId) {
|
||||||
|
// Move to the same shared folder
|
||||||
|
newResolved.userObject.move(paths, newResolved.path, waitFor());
|
||||||
|
} else {
|
||||||
|
// Move to a different shared folder or to main drive
|
||||||
|
var uoFrom = Env.folders[fId].userObject;
|
||||||
|
var uoTo = newResolved.userObject;
|
||||||
|
|
||||||
|
// Copy the elements to the new location
|
||||||
|
var toCopy = _getCopyFromPaths(paths, uoFrom);
|
||||||
|
toCopy.forEach(function (obj) {
|
||||||
|
uoTo.copyFromOtherDrive(newResolved.path, obj.el, obj.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove the elements from the old location (without unpinning)
|
||||||
|
uoFrom.delete(paths, waitFor());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).nThen(function () {
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// Restore from the trash (main drive only)
|
||||||
|
var _restore = function (Env, data, cb) {
|
||||||
|
var userObject = Env.user.userObject;
|
||||||
|
data = data || {};
|
||||||
|
userObject.restore(data.path, cb);
|
||||||
|
};
|
||||||
|
// Add a folder/subfolder
|
||||||
|
var _addFolder = function (Env, data, cb) {
|
||||||
|
data = data || {};
|
||||||
|
var resolved = _resolvePath(Env, data.path);
|
||||||
|
if (!resolved || !resolved.userObject) { return void cb({error: 'E_NOTFOUND'}); }
|
||||||
|
resolved.userObject.addFolder(resolved.path, data.name, function (obj) {
|
||||||
|
// The result is the relative path of the new folder. We have to make it absolute.
|
||||||
|
if (obj.newPath && resolved.id) {
|
||||||
|
var fPath = _getUserObjectPath(Env, resolved.userObject);
|
||||||
|
if (fPath) {
|
||||||
|
// This is a shared folder, we have to fix the paths in the search results
|
||||||
|
Array.prototype.unshift.apply(obj.newPath, fPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb(obj);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// Add a folder/subfolder
|
||||||
|
var _addSharedFolder = function (Env, data, cb) {
|
||||||
|
data = data || {};
|
||||||
|
var resolved = _resolvePath(Env, data.path);
|
||||||
|
if (!resolved || !resolved.userObject) { return void cb({error: 'E_NOTFOUND'}); }
|
||||||
|
if (resolved.id) { return void cb({error: 'EINVAL'}); }
|
||||||
|
if (!Env.pinPads) { return void cb({error: 'EAUTH'}); }
|
||||||
|
|
||||||
|
var folderData = data.folderData || {};
|
||||||
|
|
||||||
|
var id;
|
||||||
|
nThen(function () {
|
||||||
|
// Check if it is an imported folder or a folder creation
|
||||||
|
if (data.folderData) { return; }
|
||||||
|
|
||||||
|
// Folder creation
|
||||||
|
var hash = Hash.createRandomHash('drive');
|
||||||
|
var href = '/drive/#' + hash;
|
||||||
|
var secret = Hash.getSecrets('drive', hash);
|
||||||
|
folderData = {
|
||||||
|
href: href,
|
||||||
|
roHref: '/drive/#' + Hash.getViewHashFromKeys(secret),
|
||||||
|
channel: secret.channel,
|
||||||
|
ctime: +new Date()
|
||||||
|
};
|
||||||
|
if (data.password) { folderData.password = data.password; }
|
||||||
|
if (data.owned) { folderData.owners = [Env.edPublic]; }
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
Env.pinPads([folderData.channel], waitFor());
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
// 1. add the shared folder to our list of shared folders
|
||||||
|
Env.user.userObject.pushSharedFolder(folderData, waitFor(function (err, folderId) {
|
||||||
|
if (err) {
|
||||||
|
waitFor.abort();
|
||||||
|
return void cb(err);
|
||||||
|
}
|
||||||
|
id = folderId;
|
||||||
|
}));
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
// 2a. add the shared folder to the path in our drive
|
||||||
|
Env.user.userObject.add(id, resolved.path);
|
||||||
|
|
||||||
|
// 2b. load the proxy
|
||||||
|
Env.loadSharedFolder(id, folderData, waitFor(function (rt, metadata) {
|
||||||
|
if (!rt.proxy.metadata) { // Creating a new shared folder
|
||||||
|
rt.proxy.metadata = { title: data.name || Messages.fm_newFolder };
|
||||||
|
}
|
||||||
|
// If we're importing a folder, check its serverside metadata
|
||||||
|
if (data.folderData && metadata) {
|
||||||
|
var fData = Env.user.proxy[UserObject.SHARED_FOLDERS][id];
|
||||||
|
if (metadata.owners) { fData.owners = metadata.owners; }
|
||||||
|
if (metadata.expire) { fData.expire = +metadata.expire; }
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}).nThen(function () {
|
||||||
|
cb(id);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// Delete permanently some pads or folders
|
||||||
|
var _delete = function (Env, data, cb) {
|
||||||
|
data = data || {};
|
||||||
|
var resolved = _resolvePaths(Env, data.paths);
|
||||||
|
if (!resolved.main.length && !Object.keys(resolved.folders).length) {
|
||||||
|
return void cb({error: 'E_NOTFOUND'});
|
||||||
|
}
|
||||||
|
|
||||||
|
var toUnpin = [];
|
||||||
|
var ownedRemoved;
|
||||||
|
nThen(function (waitFor) {
|
||||||
|
// Delete paths from the main drive and get the list of pads to unpin
|
||||||
|
// We also get the list of owned pads that were removed
|
||||||
|
if (resolved.main.length) {
|
||||||
|
Env.user.userObject.delete(resolved.main, waitFor(function (err, _toUnpin, _ownedRemoved) {
|
||||||
|
if (!Env.unpinPads || !_toUnpin) { return; }
|
||||||
|
Array.prototype.push.apply(toUnpin, _toUnpin);
|
||||||
|
ownedRemoved = _ownedRemoved;
|
||||||
|
}), data.nocheck, data.isOwnPadRemoved);
|
||||||
|
}
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
// Check if removed owned pads are duplicated is some shared folders
|
||||||
|
// If that's the case, we have to remove them from the shared folders too
|
||||||
|
// We can do that by adding their paths to the list of pads to remove from shared folders
|
||||||
|
if (ownedRemoved) {
|
||||||
|
var ids = _findChannels(Env, ownedRemoved);
|
||||||
|
ids.forEach(function (id) {
|
||||||
|
var paths = findFile(Env, id);
|
||||||
|
var _resolved = _resolvePaths(Env, paths);
|
||||||
|
Object.keys(_resolved.folders).forEach(function (fId) {
|
||||||
|
if (resolved.folders[fId]) {
|
||||||
|
Array.prototype.push.apply(resolved.folders[fId], _resolved.folders[fId]);
|
||||||
|
} else {
|
||||||
|
resolved.folders[fId] = _resolved.folders[fId];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Delete paths from the shared folders
|
||||||
|
Object.keys(resolved.folders).forEach(function (id) {
|
||||||
|
Env.folders[id].userObject.delete(resolved.folders[id], waitFor(function (err, _toUnpin) {
|
||||||
|
if (!Env.unpinPads || !_toUnpin) { return; }
|
||||||
|
Array.prototype.push.apply(toUnpin, _toUnpin);
|
||||||
|
}), data.nocheck, data.isOwnPadRemoved);
|
||||||
|
});
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
if (!Env.unpinPads) { return; }
|
||||||
|
|
||||||
|
// Deleted channels
|
||||||
|
toUnpin = Util.deduplicateString(toUnpin);
|
||||||
|
// Deleted channels that are still in another proxy
|
||||||
|
var toKeep = _findChannels(Env, toUnpin).map(function (id) {
|
||||||
|
return _getFileData(Env, id).channel;
|
||||||
|
});
|
||||||
|
// Compute the unpin list and unpin
|
||||||
|
var unpinList = [];
|
||||||
|
toUnpin.forEach(function (chan) {
|
||||||
|
if (toKeep.indexOf(chan) === -1) {
|
||||||
|
unpinList.push(chan);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Env.unpinPads(unpinList, waitFor(function (response) {
|
||||||
|
if (response && response.error) { return console.error(response.error); }
|
||||||
|
}));
|
||||||
|
}).nThen(function () {
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// Empty the trash (main drive only)
|
||||||
|
var _emptyTrash = function (Env, data, cb) {
|
||||||
|
Env.user.userObject.emptyTrash(cb);
|
||||||
|
};
|
||||||
|
// Rename files or folders
|
||||||
|
var _rename = function (Env, data, cb) {
|
||||||
|
data = data || {};
|
||||||
|
var resolved = _resolvePath(Env, data.path);
|
||||||
|
if (!resolved || !resolved.userObject) { return void cb({error: 'E_NOTFOUND'}); }
|
||||||
|
if (!resolved.id) {
|
||||||
|
var el = Env.user.userObject.find(resolved.path);
|
||||||
|
if (Env.user.userObject.isSharedFolder(el) && Env.folders[el]) {
|
||||||
|
Env.folders[el].proxy.metadata.title = data.newName;
|
||||||
|
return void cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolved.userObject.rename(resolved.path, data.newName, cb);
|
||||||
|
};
|
||||||
|
var onCommand = function (Env, cmdData, cb) {
|
||||||
|
var cmd = cmdData.cmd;
|
||||||
|
var data = cmdData.data || {};
|
||||||
|
switch (cmd) {
|
||||||
|
case 'move':
|
||||||
|
_move(Env, data, cb); break;
|
||||||
|
case 'restore':
|
||||||
|
_restore(Env, data, cb); break;
|
||||||
|
case 'addFolder':
|
||||||
|
_addFolder(Env, data, cb); break;
|
||||||
|
case 'addSharedFolder':
|
||||||
|
_addSharedFolder(Env, data, cb); break;
|
||||||
|
case 'delete':
|
||||||
|
_delete(Env, data, cb); break;
|
||||||
|
case 'emptyTrash':
|
||||||
|
_emptyTrash(Env, data, cb); break;
|
||||||
|
case 'rename':
|
||||||
|
_rename(Env, data, cb); break;
|
||||||
|
default:
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the value everywhere the given pad is stored (main and shared folders)
|
||||||
|
var setPadAttribute = function (Env, data, cb) {
|
||||||
|
cb = cb || function () {};
|
||||||
|
var datas = findHref(Env, data.href);
|
||||||
|
var nt = nThen;
|
||||||
|
datas.forEach(function (d) {
|
||||||
|
nt = nt(function (waitFor) {
|
||||||
|
d.userObject.setPadAttribute(data.href, data.attr, data.value, waitFor());
|
||||||
|
}).nThen;
|
||||||
|
});
|
||||||
|
nt(function () { cb(); });
|
||||||
|
};
|
||||||
|
// Get pad attribute must return only one value, even if the pad is stored in multiple places
|
||||||
|
// (main or shared folders)
|
||||||
|
// We're going to return the value with the most recent atime. The attributes may have been
|
||||||
|
// updated in a shared folder by another user, so the most recent one is more likely to be the
|
||||||
|
// correct one.
|
||||||
|
var getPadAttribute = function (Env, data, cb) {
|
||||||
|
cb = cb || function () {};
|
||||||
|
var datas = findHref(Env, data.href);
|
||||||
|
var nt = nThen;
|
||||||
|
var res = {};
|
||||||
|
datas.forEach(function (d) {
|
||||||
|
nt = nt(function (waitFor) {
|
||||||
|
var atime, value;
|
||||||
|
var w = waitFor();
|
||||||
|
nThen(function (waitFor2) {
|
||||||
|
d.userObject.getPadAttribute(data.href, 'atime', waitFor2(function (err, v) {
|
||||||
|
atime = v;
|
||||||
|
}));
|
||||||
|
d.userObject.getPadAttribute(data.href, data.attr, waitFor2(function (err, v) {
|
||||||
|
value = v;
|
||||||
|
}));
|
||||||
|
}).nThen(function () {
|
||||||
|
if (!res.value || res.atime < atime) {
|
||||||
|
res.atime = atime;
|
||||||
|
res.value = value;
|
||||||
|
}
|
||||||
|
w();
|
||||||
|
});
|
||||||
|
}).nThen;
|
||||||
|
});
|
||||||
|
nt(function () { cb(null, res.value); });
|
||||||
|
};
|
||||||
|
|
||||||
|
var getTagsList = function (Env) {
|
||||||
|
var list = [];
|
||||||
|
var userObjects = _getUserObjects(Env);
|
||||||
|
userObjects.forEach(function (uo) {
|
||||||
|
Array.prototype.push.apply(list, uo.getTagsList());
|
||||||
|
});
|
||||||
|
list = Util.deduplicateString(list);
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getSecureFilesList = function (Env, where) {
|
||||||
|
var userObjects = _getUserObjects(Env);
|
||||||
|
var list = [];
|
||||||
|
var channels = [];
|
||||||
|
userObjects.forEach(function (uo) {
|
||||||
|
var toPush = uo.getFiles(where).map(function (id) {
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
data: uo.getFileData(id)
|
||||||
|
};
|
||||||
|
}).filter(function (d) {
|
||||||
|
if (channels.indexOf(d.data.channel) === -1) {
|
||||||
|
channels.push(d.data.channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Array.prototype.push.apply(list, toPush);
|
||||||
|
});
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Store
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Get the list of channels filtered by a type (expirable channels, owned channels, pin list)
|
||||||
|
var getChannelsList = function (Env, edPublic, type) {
|
||||||
|
//if (!edPublic) { return; }
|
||||||
|
var result = [];
|
||||||
|
var addChannel = function (userObject) {
|
||||||
|
if (type === 'expirable') {
|
||||||
|
return function (fileId) {
|
||||||
|
var data = userObject.getFileData(fileId);
|
||||||
|
// Don't push duplicates
|
||||||
|
if (result.indexOf(data.channel) !== -1) { return; }
|
||||||
|
// Return pads owned by someone else or expired by time
|
||||||
|
if ((data.owners && data.owners.length && (!edPublic || data.owners.indexOf(edPublic) === -1))
|
||||||
|
|| (data.expire && data.expire < (+new Date()))) {
|
||||||
|
result.push(data.channel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (type === 'owned') {
|
||||||
|
return function (fileId) {
|
||||||
|
var data = userObject.getFileData(fileId);
|
||||||
|
// Don't push duplicates
|
||||||
|
if (result.indexOf(data.channel) !== -1) { return; }
|
||||||
|
// Return owned pads
|
||||||
|
if (Array.isArray(data.owners) && data.owners.length &&
|
||||||
|
data.owners.indexOf(edPublic) !== -1) {
|
||||||
|
result.push(data.channel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (type === "pin") {
|
||||||
|
return function (fileId) {
|
||||||
|
var data = userObject.getFileData(fileId);
|
||||||
|
// Don't pin pads owned by someone else
|
||||||
|
if (Array.isArray(data.owners) && data.owners.length &&
|
||||||
|
data.owners.indexOf(edPublic) === -1) { return; }
|
||||||
|
// Don't push duplicates
|
||||||
|
if (result.indexOf(data.channel) === -1) {
|
||||||
|
result.push(data.channel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type === 'owned' && !edPublic) { return result; }
|
||||||
|
if (type === 'pin' && !edPublic) { return result; }
|
||||||
|
|
||||||
|
// Get the list of user objects
|
||||||
|
var userObjects = _getUserObjects(Env);
|
||||||
|
|
||||||
|
userObjects.forEach(function (uo) {
|
||||||
|
var files = uo.getFiles([UserObject.FILES_DATA]);
|
||||||
|
files.forEach(addChannel(uo));
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOTE: expirable shared folder should be added here if we ever decide to enable them
|
||||||
|
if (type === "owned") {
|
||||||
|
var sfOwned = Object.keys(Env.user.proxy[UserObject.SHARED_FOLDERS]).filter(function (fId) {
|
||||||
|
var owners = Env.user.proxy[UserObject.SHARED_FOLDERS][fId].owners;
|
||||||
|
if (Array.isArray(owners) && owners.length &&
|
||||||
|
owners.indexOf(edPublic) !== -1) { return true; }
|
||||||
|
}).map(function (fId) {
|
||||||
|
return Env.user.proxy[UserObject.SHARED_FOLDERS][fId].channel;
|
||||||
|
});
|
||||||
|
Array.prototype.push.apply(result, sfOwned);
|
||||||
|
}
|
||||||
|
if (type === "pin") {
|
||||||
|
var sfChannels = Object.keys(Env.folders).map(function (fId) {
|
||||||
|
return Env.user.proxy[UserObject.SHARED_FOLDERS][fId].channel;
|
||||||
|
});
|
||||||
|
Array.prototype.push.apply(result, sfChannels);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
var addPad = function (Env, path, pad, cb) {
|
||||||
|
var uo = Env.user.userObject;
|
||||||
|
var p = ['root'];
|
||||||
|
if (path) {
|
||||||
|
var resolved = _resolvePath(Env, path);
|
||||||
|
uo = resolved.userObject;
|
||||||
|
p = resolved.path;
|
||||||
|
}
|
||||||
|
var todo = function () {
|
||||||
|
uo.pushData(pad, function (e, id) {
|
||||||
|
if (e) { return void cb(e); }
|
||||||
|
uo.add(id, p);
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (!Env.pinPads) { return void todo(); }
|
||||||
|
Env.pinPads([pad.channel], function (obj) {
|
||||||
|
if (obj && obj.error) { return void cb(obj.error); }
|
||||||
|
todo();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var create = function (proxy, edPublic, pinPads, unpinPads, loadSf, uoConfig) {
|
||||||
|
var Env = {
|
||||||
|
pinPads: pinPads,
|
||||||
|
unpinPads: unpinPads,
|
||||||
|
loadSharedFolder: loadSf,
|
||||||
|
cfg: uoConfig,
|
||||||
|
edPublic: edPublic,
|
||||||
|
user: {
|
||||||
|
proxy: proxy,
|
||||||
|
userObject: UserObject.init(proxy, uoConfig)
|
||||||
|
},
|
||||||
|
folders: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
var callWithEnv = function (f) {
|
||||||
|
return function () {
|
||||||
|
[].unshift.call(arguments, Env);
|
||||||
|
return f.apply(null, arguments);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Manager
|
||||||
|
addProxy: callWithEnv(addProxy),
|
||||||
|
removeProxy: callWithEnv(removeProxy),
|
||||||
|
// Drive
|
||||||
|
command: callWithEnv(onCommand),
|
||||||
|
getPadAttribute: callWithEnv(getPadAttribute),
|
||||||
|
setPadAttribute: callWithEnv(setPadAttribute),
|
||||||
|
getTagsList: callWithEnv(getTagsList),
|
||||||
|
getSecureFilesList: callWithEnv(getSecureFilesList),
|
||||||
|
// Store
|
||||||
|
getChannelsList: callWithEnv(getChannelsList),
|
||||||
|
addPad: callWithEnv(addPad),
|
||||||
|
// Tools
|
||||||
|
findChannel: callWithEnv(findChannel),
|
||||||
|
findHref: callWithEnv(findHref),
|
||||||
|
user: Env.user,
|
||||||
|
folders: Env.folders
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/* =============================================================================
|
||||||
|
* =============================================================================
|
||||||
|
* Inner only
|
||||||
|
* =============================================================================
|
||||||
|
* ============================================================================= */
|
||||||
|
|
||||||
|
var renameInner = function (Env, path, newName, cb) {
|
||||||
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||||
|
cmd: "rename",
|
||||||
|
data: {
|
||||||
|
path: path,
|
||||||
|
newName: newName
|
||||||
|
}
|
||||||
|
}, cb);
|
||||||
|
};
|
||||||
|
var moveInner = function (Env, paths, newPath, cb) {
|
||||||
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||||
|
cmd: "move",
|
||||||
|
data: {
|
||||||
|
paths: paths,
|
||||||
|
newPath: newPath
|
||||||
|
}
|
||||||
|
}, cb);
|
||||||
|
};
|
||||||
|
var emptyTrashInner = function (Env, cb) {
|
||||||
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||||
|
cmd: "emptyTrash"
|
||||||
|
}, cb);
|
||||||
|
};
|
||||||
|
var addFolderInner = function (Env, path, name, cb) {
|
||||||
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||||
|
cmd: "addFolder",
|
||||||
|
data: {
|
||||||
|
path: path,
|
||||||
|
name: name
|
||||||
|
}
|
||||||
|
}, cb);
|
||||||
|
};
|
||||||
|
var addSharedFolderInner = function (Env, path, data, cb) {
|
||||||
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||||
|
cmd: "addSharedFolder",
|
||||||
|
data: {
|
||||||
|
path: path,
|
||||||
|
name: data.name,
|
||||||
|
owned: data.owned,
|
||||||
|
password: data.password
|
||||||
|
}
|
||||||
|
}, cb);
|
||||||
|
};
|
||||||
|
var deleteInner = function (Env, paths, cb, nocheck, isOwnPadRemoved, noUnpin) {
|
||||||
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||||
|
cmd: "delete",
|
||||||
|
data: {
|
||||||
|
paths: paths,
|
||||||
|
nocheck: nocheck,
|
||||||
|
noUnpin: noUnpin,
|
||||||
|
isOwnPadRemoved: isOwnPadRemoved
|
||||||
|
}
|
||||||
|
}, cb);
|
||||||
|
};
|
||||||
|
var restoreInner = function (Env, path, cb) {
|
||||||
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||||
|
cmd: "restore",
|
||||||
|
data: {
|
||||||
|
path: path
|
||||||
|
}
|
||||||
|
}, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Tools */
|
||||||
|
|
||||||
|
var findChannels = _findChannels;
|
||||||
|
var getFileData = _getFileData;
|
||||||
|
var getUserObjectPath = _getUserObjectPath;
|
||||||
|
|
||||||
|
var find = function (Env, path) {
|
||||||
|
var resolved = _resolvePath(Env, path);
|
||||||
|
return resolved.userObject.find(resolved.path);
|
||||||
|
};
|
||||||
|
|
||||||
|
var getTitle = function (Env, id, type) {
|
||||||
|
var uo = _getUserObjectFromId(Env, id);
|
||||||
|
return uo.getTitle(id, type);
|
||||||
|
};
|
||||||
|
|
||||||
|
var isReadOnlyFile = function (Env, id) {
|
||||||
|
var uo = _getUserObjectFromId(Env, id);
|
||||||
|
return uo.isReadOnlyFile(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
var getFiles = function (Env, categories) {
|
||||||
|
var files = [];
|
||||||
|
var userObjects = _getUserObjects(Env);
|
||||||
|
userObjects.forEach(function (uo) {
|
||||||
|
Array.prototype.push.apply(files, uo.getFiles(categories));
|
||||||
|
});
|
||||||
|
files = Util.deduplicateString(files);
|
||||||
|
return files;
|
||||||
|
};
|
||||||
|
|
||||||
|
var search = function (Env, value) {
|
||||||
|
var ret = [];
|
||||||
|
var userObjects = _getUserObjects(Env);
|
||||||
|
userObjects.forEach(function (uo) {
|
||||||
|
var fPath = _getUserObjectPath(Env, uo);
|
||||||
|
var results = uo.search(value);
|
||||||
|
if (!results.length) { return; }
|
||||||
|
if (fPath) {
|
||||||
|
// This is a shared folder, we have to fix the paths in the search results
|
||||||
|
results.forEach(function (r) {
|
||||||
|
r.paths.forEach(function (p) {
|
||||||
|
Array.prototype.unshift.apply(p, fPath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Push the results from this proxy
|
||||||
|
Array.prototype.push.apply(ret, results);
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getRecentPads = function (Env) {
|
||||||
|
return Env.user.userObject.getRecentPads();
|
||||||
|
};
|
||||||
|
var getOwnedPads = function (Env, edPublic) {
|
||||||
|
return Env.user.userObject.getOwnedPads(edPublic);
|
||||||
|
};
|
||||||
|
|
||||||
|
var getSharedFolderData = function (Env, id) {
|
||||||
|
if (!Env.folders[id]) { return; }
|
||||||
|
var obj = Env.folders[id].proxy.metadata || {};
|
||||||
|
for (var k in Env.user.proxy[UserObject.SHARED_FOLDERS][id] || {}) {
|
||||||
|
obj[k] = Env.user.proxy[UserObject.SHARED_FOLDERS][id][k];
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
var isInSharedFolder = function (Env, path) {
|
||||||
|
var resolved = _resolvePath(Env, path);
|
||||||
|
return typeof resolved.id === "number" ? resolved.id : false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Generic: doesn't need access to a proxy */
|
||||||
|
var isFile = function (Env, el, allowStr) {
|
||||||
|
return Env.user.userObject.isFile(el, allowStr);
|
||||||
|
};
|
||||||
|
var isFolder = function (Env, el) {
|
||||||
|
return Env.user.userObject.isFolder(el);
|
||||||
|
};
|
||||||
|
var isSharedFolder = function (Env, el) {
|
||||||
|
return Env.user.userObject.isSharedFolder(el);
|
||||||
|
};
|
||||||
|
var isFolderEmpty = function (Env, el) {
|
||||||
|
if (Env.folders[el]) {
|
||||||
|
var uo = Env.folders[el].userObject;
|
||||||
|
return uo.isFolderEmpty(uo.find[uo.ROOT]);
|
||||||
|
}
|
||||||
|
return Env.user.userObject.isFolderEmpty(el);
|
||||||
|
};
|
||||||
|
var isPathIn = function (Env, path, categories) {
|
||||||
|
return Env.user.userObject.isPathIn(path, categories);
|
||||||
|
};
|
||||||
|
var isSubpath = function (Env, path, parentPath) {
|
||||||
|
return Env.user.userObject.isSubpath(path, parentPath);
|
||||||
|
};
|
||||||
|
var isInTrashRoot = function (Env, path) {
|
||||||
|
return Env.user.userObject.isInTrashRoot(path);
|
||||||
|
};
|
||||||
|
var comparePath = function (Env, a, b) {
|
||||||
|
return Env.user.userObject.comparePath(a, b);
|
||||||
|
};
|
||||||
|
var hasSubfolder = function (Env, el, trashRoot) {
|
||||||
|
if (Env.folders[el]) {
|
||||||
|
var uo = Env.folders[el].userObject;
|
||||||
|
return uo.hasSubfolder(uo.find[uo.ROOT]);
|
||||||
|
}
|
||||||
|
return Env.user.userObject.hasSubfolder(el, trashRoot);
|
||||||
|
};
|
||||||
|
var hasFile = function (Env, el, trashRoot) {
|
||||||
|
if (Env.folders[el]) {
|
||||||
|
var uo = Env.folders[el].userObject;
|
||||||
|
return uo.hasFile(uo.find[uo.ROOT]);
|
||||||
|
}
|
||||||
|
return Env.user.userObject.hasFile(el, trashRoot);
|
||||||
|
};
|
||||||
|
|
||||||
|
var createInner = function (proxy, sframeChan, uoConfig) {
|
||||||
|
var Env = {
|
||||||
|
cfg: uoConfig,
|
||||||
|
sframeChan: sframeChan,
|
||||||
|
user: {
|
||||||
|
proxy: proxy,
|
||||||
|
userObject: UserObject.init(proxy, uoConfig)
|
||||||
|
},
|
||||||
|
folders: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
var callWithEnv = function (f) {
|
||||||
|
return function () {
|
||||||
|
[].unshift.call(arguments, Env);
|
||||||
|
return f.apply(null, arguments);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Manager
|
||||||
|
addProxy: callWithEnv(addProxy),
|
||||||
|
removeProxy: callWithEnv(removeProxy),
|
||||||
|
// Drive RPC commands
|
||||||
|
rename: callWithEnv(renameInner),
|
||||||
|
move: callWithEnv(moveInner),
|
||||||
|
emptyTrash: callWithEnv(emptyTrashInner),
|
||||||
|
addFolder: callWithEnv(addFolderInner),
|
||||||
|
addSharedFolder: callWithEnv(addSharedFolderInner),
|
||||||
|
delete: callWithEnv(deleteInner),
|
||||||
|
restore: callWithEnv(restoreInner),
|
||||||
|
// Tools
|
||||||
|
getFileData: callWithEnv(getFileData),
|
||||||
|
find: callWithEnv(find),
|
||||||
|
getTitle: callWithEnv(getTitle),
|
||||||
|
isReadOnlyFile: callWithEnv(isReadOnlyFile),
|
||||||
|
getFiles: callWithEnv(getFiles),
|
||||||
|
search: callWithEnv(search),
|
||||||
|
getRecentPads: callWithEnv(getRecentPads),
|
||||||
|
getOwnedPads: callWithEnv(getOwnedPads),
|
||||||
|
getTagsList: callWithEnv(getTagsList),
|
||||||
|
findFile: callWithEnv(findFile),
|
||||||
|
findChannels: callWithEnv(findChannels),
|
||||||
|
getSharedFolderData: callWithEnv(getSharedFolderData),
|
||||||
|
isInSharedFolder: callWithEnv(isInSharedFolder),
|
||||||
|
getUserObjectPath: callWithEnv(getUserObjectPath),
|
||||||
|
// Generic
|
||||||
|
isFile: callWithEnv(isFile),
|
||||||
|
isFolder: callWithEnv(isFolder),
|
||||||
|
isSharedFolder: callWithEnv(isSharedFolder),
|
||||||
|
isFolderEmpty: callWithEnv(isFolderEmpty),
|
||||||
|
isPathIn: callWithEnv(isPathIn),
|
||||||
|
isSubpath: callWithEnv(isSubpath),
|
||||||
|
isInTrashRoot: callWithEnv(isInTrashRoot),
|
||||||
|
comparePath: callWithEnv(comparePath),
|
||||||
|
hasSubfolder: callWithEnv(hasSubfolder),
|
||||||
|
hasFile: callWithEnv(hasFile),
|
||||||
|
// Data
|
||||||
|
user: Env.user,
|
||||||
|
folders: Env.folders
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
create: create,
|
||||||
|
createInner: createInner
|
||||||
|
};
|
||||||
|
});
|
||||||
@ -266,7 +266,7 @@ define([
|
|||||||
isDeleted: isNewFile && window.location.hash.length > 0,
|
isDeleted: isNewFile && window.location.hash.length > 0,
|
||||||
forceCreationScreen: forceCreationScreen,
|
forceCreationScreen: forceCreationScreen,
|
||||||
password: password,
|
password: password,
|
||||||
channel: secret.channel
|
channel: secret.channel,
|
||||||
};
|
};
|
||||||
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
|
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
|
||||||
|
|
||||||
@ -484,6 +484,12 @@ define([
|
|||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sframeChan.on('Q_IS_ONLY_IN_SHARED_FOLDER', function (data, cb) {
|
||||||
|
Cryptpad.isOnlyInSharedFolder(secret.channel, function (err, t) {
|
||||||
|
if (err) { return void cb({error: err}); }
|
||||||
|
cb(t);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Present mode URL
|
// Present mode URL
|
||||||
sframeChan.on('Q_PRESENT_URL_GET_VALUE', function (data, cb) {
|
sframeChan.on('Q_PRESENT_URL_GET_VALUE', function (data, cb) {
|
||||||
|
|||||||
@ -114,6 +114,10 @@ define({
|
|||||||
'Q_GET_PAD_ATTRIBUTE': true,
|
'Q_GET_PAD_ATTRIBUTE': true,
|
||||||
'Q_SET_PAD_ATTRIBUTE': true,
|
'Q_SET_PAD_ATTRIBUTE': true,
|
||||||
|
|
||||||
|
// Check if a pad is only in a shared folder or (also) in the main drive.
|
||||||
|
// This allows us to change the behavior of some buttons (trash icon...)
|
||||||
|
'Q_IS_ONLY_IN_SHARED_FOLDER': true,
|
||||||
|
|
||||||
// Open/close the File picker (sent from the iframe to the outside)
|
// Open/close the File picker (sent from the iframe to the outside)
|
||||||
'EV_FILE_PICKER_OPEN': true,
|
'EV_FILE_PICKER_OPEN': true,
|
||||||
'EV_FILE_PICKER_CLOSE': true,
|
'EV_FILE_PICKER_CLOSE': true,
|
||||||
@ -206,6 +210,7 @@ define({
|
|||||||
// Inner drive needs to send command and receive updates from the async store
|
// Inner drive needs to send command and receive updates from the async store
|
||||||
'Q_DRIVE_USEROBJECT': true,
|
'Q_DRIVE_USEROBJECT': true,
|
||||||
'Q_DRIVE_GETOBJECT': true,
|
'Q_DRIVE_GETOBJECT': true,
|
||||||
|
'Q_DRIVE_RESTORE': true,
|
||||||
// Get the pads deleted from the server by other users to remove them from the drive
|
// Get the pads deleted from the server by other users to remove them from the drive
|
||||||
'Q_DRIVE_GETDELETED': true,
|
'Q_DRIVE_GETDELETED': true,
|
||||||
// Store's userObject need to send log messages to inner to display them in the UI
|
// Store's userObject need to send log messages to inner to display them in the UI
|
||||||
|
|||||||
@ -13,10 +13,10 @@ define([
|
|||||||
var UNSORTED = module.UNSORTED = "unsorted";
|
var UNSORTED = module.UNSORTED = "unsorted";
|
||||||
var TRASH = module.TRASH = "trash";
|
var TRASH = module.TRASH = "trash";
|
||||||
var TEMPLATE = module.TEMPLATE = "template";
|
var TEMPLATE = module.TEMPLATE = "template";
|
||||||
|
var SHARED_FOLDERS = module.SHARED_FOLDERS = "sharedFolders";
|
||||||
|
|
||||||
module.init = function (files, config) {
|
module.init = function (files, config) {
|
||||||
var exp = {};
|
var exp = {};
|
||||||
var pinPads = config.pinPads;
|
|
||||||
var sframeChan = config.sframeChan;
|
var sframeChan = config.sframeChan;
|
||||||
|
|
||||||
var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Constants.storageKey;
|
var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Constants.storageKey;
|
||||||
@ -28,14 +28,21 @@ define([
|
|||||||
exp.UNSORTED = UNSORTED;
|
exp.UNSORTED = UNSORTED;
|
||||||
exp.TRASH = TRASH;
|
exp.TRASH = TRASH;
|
||||||
exp.TEMPLATE = TEMPLATE;
|
exp.TEMPLATE = TEMPLATE;
|
||||||
|
exp.SHARED_FOLDERS = SHARED_FOLDERS;
|
||||||
|
|
||||||
|
var sharedFolder = exp.sharedFolder = config.sharedFolder;
|
||||||
|
exp.id = config.id;
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
var logging = function () {
|
var logging = function () {
|
||||||
console.log.apply(console, arguments);
|
console.log.apply(console, arguments);
|
||||||
};
|
};
|
||||||
var log = config.log || logging;
|
var log = exp.log = config.log || logging;
|
||||||
var logError = config.logError || logging;
|
var logError = config.logError || logging;
|
||||||
var debug = exp.debug = config.debug || logging;
|
var debug = exp.debug = config.debug || logging;
|
||||||
|
|
||||||
|
exp.fixFiles = function () {}; // Overriden by OuterFO
|
||||||
|
|
||||||
var error = exp.error = function() {
|
var error = exp.error = function() {
|
||||||
if (sframeChan) {
|
if (sframeChan) {
|
||||||
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
||||||
@ -46,12 +53,10 @@ define([
|
|||||||
exp.fixFiles();
|
exp.fixFiles();
|
||||||
}
|
}
|
||||||
console.error.apply(console, arguments);
|
console.error.apply(console, arguments);
|
||||||
|
exp.fixFiles();
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: workgroup
|
if (config.outer) {
|
||||||
var workgroup = config.workgroup;
|
|
||||||
|
|
||||||
if (pinPads) {
|
|
||||||
// Extend "exp" with methods used only outside of the iframe (requires access to store)
|
// Extend "exp" with methods used only outside of the iframe (requires access to store)
|
||||||
OuterFO.init(config, exp, files);
|
OuterFO.init(config, exp, files);
|
||||||
}
|
}
|
||||||
@ -76,7 +81,12 @@ define([
|
|||||||
|
|
||||||
var compareFiles = function (fileA, fileB) { return fileA === fileB; };
|
var compareFiles = function (fileA, fileB) { return fileA === fileB; };
|
||||||
|
|
||||||
|
var isSharedFolder = exp.isSharedFolder = function (element) {
|
||||||
|
if (sharedFolder) { return false; } // No recursive shared folders
|
||||||
|
return Boolean(files[SHARED_FOLDERS][element]);
|
||||||
|
};
|
||||||
var isFile = exp.isFile = function (element, allowStr) {
|
var isFile = exp.isFile = function (element, allowStr) {
|
||||||
|
if (isSharedFolder(element)) { return false; }
|
||||||
return typeof(element) === "number" ||
|
return typeof(element) === "number" ||
|
||||||
((typeof(files[OLD_FILES_DATA]) !== "undefined" || allowStr)
|
((typeof(files[OLD_FILES_DATA]) !== "undefined" || allowStr)
|
||||||
&& typeof(element) === "string");
|
&& typeof(element) === "string");
|
||||||
@ -85,15 +95,11 @@ define([
|
|||||||
exp.isReadOnlyFile = function (element) {
|
exp.isReadOnlyFile = function (element) {
|
||||||
if (!isFile(element)) { return false; }
|
if (!isFile(element)) { return false; }
|
||||||
var data = exp.getFileData(element);
|
var data = exp.getFileData(element);
|
||||||
var parsed = Hash.parsePadUrl(data.href);
|
return Boolean(data.roHref && !data.href);
|
||||||
if (!parsed) { return false; }
|
|
||||||
var pHash = parsed.hashData;
|
|
||||||
if (!pHash || pHash.type !== "pad") { return; }
|
|
||||||
return pHash && pHash.mode === 'view';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var isFolder = exp.isFolder = function (element) {
|
var isFolder = exp.isFolder = function (element) {
|
||||||
return typeof(element) === "object";
|
return typeof(element) === "object" || isSharedFolder(element);
|
||||||
};
|
};
|
||||||
exp.isFolderEmpty = function (element) {
|
exp.isFolderEmpty = function (element) {
|
||||||
if (!isFolder(element)) { return false; }
|
if (!isFolder(element)) { return false; }
|
||||||
@ -144,9 +150,11 @@ define([
|
|||||||
|
|
||||||
// Data from filesData
|
// Data from filesData
|
||||||
var getTitle = exp.getTitle = function (file, type) {
|
var getTitle = exp.getTitle = function (file, type) {
|
||||||
if (workgroup) { debug("No titles in workgroups"); return; }
|
if (isSharedFolder(file)) {
|
||||||
|
return '??';
|
||||||
|
}
|
||||||
var data = getFileData(file);
|
var data = getFileData(file);
|
||||||
if (!file || !data || !data.href) {
|
if (!file || !data || !(data.href || data.roHref)) {
|
||||||
error("getTitle called with a non-existing file id: ", file, data);
|
error("getTitle called with a non-existing file id: ", file, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -216,14 +224,16 @@ define([
|
|||||||
|
|
||||||
// GET FILES
|
// GET FILES
|
||||||
|
|
||||||
var getFilesRecursively = function (root, arr) {
|
var getFilesRecursively = exp.getFilesRecursively = function (root, arr) {
|
||||||
|
arr = arr || [];
|
||||||
for (var e in root) {
|
for (var e in root) {
|
||||||
if (isFile(root[e])) {
|
if (isFile(root[e]) || isSharedFolder(root[e])) {
|
||||||
if(arr.indexOf(root[e]) === -1) { arr.push(root[e]); }
|
if(arr.indexOf(root[e]) === -1) { arr.push(root[e]); }
|
||||||
} else {
|
} else {
|
||||||
getFilesRecursively(root[e], arr);
|
getFilesRecursively(root[e], arr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return arr;
|
||||||
};
|
};
|
||||||
var _getFiles = {};
|
var _getFiles = {};
|
||||||
_getFiles['array'] = function (cat) {
|
_getFiles['array'] = function (cat) {
|
||||||
@ -235,6 +245,7 @@ define([
|
|||||||
});
|
});
|
||||||
_getFiles['hrefArray'] = function () {
|
_getFiles['hrefArray'] = function () {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
|
if (sharedFolder) { return ret; }
|
||||||
getHrefArray().forEach(function (c) {
|
getHrefArray().forEach(function (c) {
|
||||||
ret = ret.concat(_getFiles[c]());
|
ret = ret.concat(_getFiles[c]());
|
||||||
});
|
});
|
||||||
@ -279,10 +290,15 @@ define([
|
|||||||
if (!files[FILES_DATA]) { return ret; }
|
if (!files[FILES_DATA]) { return ret; }
|
||||||
return Object.keys(files[FILES_DATA]).map(Number);
|
return Object.keys(files[FILES_DATA]).map(Number);
|
||||||
};
|
};
|
||||||
|
_getFiles[SHARED_FOLDERS] = function () {
|
||||||
|
var ret = [];
|
||||||
|
if (!files[SHARED_FOLDERS]) { return ret; }
|
||||||
|
return Object.keys(files[SHARED_FOLDERS]).map(Number);
|
||||||
|
};
|
||||||
var getFiles = exp.getFiles = function (categories) {
|
var getFiles = exp.getFiles = function (categories) {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
if (!categories || !categories.length) {
|
if (!categories || !categories.length) {
|
||||||
categories = [ROOT, 'hrefArray', TRASH, OLD_FILES_DATA, FILES_DATA];
|
categories = [ROOT, 'hrefArray', TRASH, OLD_FILES_DATA, FILES_DATA, SHARED_FOLDERS];
|
||||||
}
|
}
|
||||||
categories.forEach(function (c) {
|
categories.forEach(function (c) {
|
||||||
if (typeof _getFiles[c] === "function") {
|
if (typeof _getFiles[c] === "function") {
|
||||||
@ -295,11 +311,11 @@ define([
|
|||||||
var getIdFromHref = exp.getIdFromHref = function (href) {
|
var getIdFromHref = exp.getIdFromHref = function (href) {
|
||||||
var result;
|
var result;
|
||||||
getFiles([FILES_DATA]).some(function (id) {
|
getFiles([FILES_DATA]).some(function (id) {
|
||||||
if (files[FILES_DATA][id].href === href) {
|
if (files[FILES_DATA][id].href === href ||
|
||||||
|
files[FILES_DATA][id].roHref === href) {
|
||||||
result = id;
|
result = id;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
@ -315,7 +331,7 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isFile(root)) {
|
if (isFile(root) || isSharedFolder(root)) {
|
||||||
if (compareFiles(file, root)) {
|
if (compareFiles(file, root)) {
|
||||||
if (paths.indexOf(path) === -1) {
|
if (paths.indexOf(path) === -1) {
|
||||||
paths.push(path);
|
paths.push(path);
|
||||||
@ -335,6 +351,7 @@ define([
|
|||||||
return _findFileInRoot([ROOT], file);
|
return _findFileInRoot([ROOT], file);
|
||||||
};
|
};
|
||||||
var _findFileInHrefArray = function (rootName, file) {
|
var _findFileInHrefArray = function (rootName, file) {
|
||||||
|
if (sharedFolder) { return []; }
|
||||||
if (!files[rootName]) { return []; }
|
if (!files[rootName]) { return []; }
|
||||||
var unsorted = files[rootName].slice();
|
var unsorted = files[rootName].slice();
|
||||||
var ret = [];
|
var ret = [];
|
||||||
@ -345,6 +362,7 @@ define([
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
var _findFileInTrash = function (path, file) {
|
var _findFileInTrash = function (path, file) {
|
||||||
|
if (sharedFolder) { return []; }
|
||||||
var root = find(path);
|
var root = find(path);
|
||||||
var paths = [];
|
var paths = [];
|
||||||
var addPaths = function (p) {
|
var addPaths = function (p) {
|
||||||
@ -572,8 +590,9 @@ define([
|
|||||||
}
|
}
|
||||||
}, cb);
|
}, cb);
|
||||||
}
|
}
|
||||||
exp.deleteMultiplePermanently(paths, nocheck, isOwnPadRemoved);
|
cb = cb || function () {};
|
||||||
if (typeof cb === "function") { cb(); }
|
exp.deleteMultiplePermanently(paths, nocheck, isOwnPadRemoved, cb);
|
||||||
|
//if (typeof cb === "function") { cb(); }
|
||||||
};
|
};
|
||||||
exp.emptyTrash = function (cb) {
|
exp.emptyTrash = function (cb) {
|
||||||
if (sframeChan) {
|
if (sframeChan) {
|
||||||
@ -605,7 +624,7 @@ define([
|
|||||||
var element = find(path);
|
var element = find(path);
|
||||||
|
|
||||||
// Folders
|
// Folders
|
||||||
if (isFolder(element)) {
|
if (isFolder(element) && !isSharedFolder(element)) {
|
||||||
var parentPath = path.slice();
|
var parentPath = path.slice();
|
||||||
var oldName = parentPath.pop();
|
var oldName = parentPath.pop();
|
||||||
if (!newName || !newName.trim() || oldName === newName) { return; }
|
if (!newName || !newName.trim() || oldName === newName) { return; }
|
||||||
@ -620,8 +639,13 @@ define([
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Files
|
// Files or Shared folder
|
||||||
var data = files[FILES_DATA][element];
|
var data;
|
||||||
|
if (isSharedFolder(element)) {
|
||||||
|
data = files[SHARED_FOLDERS][element];
|
||||||
|
} else {
|
||||||
|
data = files[FILES_DATA][element];
|
||||||
|
}
|
||||||
if (!data) { return; }
|
if (!data) { return; }
|
||||||
if (!newName || newName.trim() === "") {
|
if (!newName || newName.trim() === "") {
|
||||||
delete data.filename;
|
delete data.filename;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -37,10 +37,19 @@ define([
|
|||||||
window.addEventListener('message', onMsg);
|
window.addEventListener('message', onMsg);
|
||||||
}).nThen(function (/*waitFor*/) {
|
}).nThen(function (/*waitFor*/) {
|
||||||
var getSecrets = function (Cryptpad, Utils, cb) {
|
var getSecrets = function (Cryptpad, Utils, cb) {
|
||||||
var hash = window.location.hash.slice(1) || Utils.LocalStore.getUserHash() ||
|
var hash = window.location.hash.slice(1);
|
||||||
Utils.LocalStore.getFSHash();
|
var secret = Utils.Hash.getSecrets('drive', hash);
|
||||||
|
if (hash) {
|
||||||
|
// Add a shared folder!
|
||||||
|
// XXX password?
|
||||||
|
Cryptpad.addSharedFolder(secret, function () {
|
||||||
|
window.location.hash = "";
|
||||||
|
cb(null, secret);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
// No password for drive
|
// No password for drive
|
||||||
cb(null, Utils.Hash.getSecrets('drive', hash));
|
cb(null, secret);
|
||||||
};
|
};
|
||||||
var addRpc = function (sframeChan, Cryptpad, Utils) {
|
var addRpc = function (sframeChan, Cryptpad, Utils) {
|
||||||
sframeChan.on('EV_BURN_ANON_DRIVE', function () {
|
sframeChan.on('EV_BURN_ANON_DRIVE', function () {
|
||||||
@ -52,7 +61,16 @@ define([
|
|||||||
sframeChan.on('Q_DRIVE_USEROBJECT', function (data, cb) {
|
sframeChan.on('Q_DRIVE_USEROBJECT', function (data, cb) {
|
||||||
Cryptpad.userObjectCommand(data, cb);
|
Cryptpad.userObjectCommand(data, cb);
|
||||||
});
|
});
|
||||||
|
sframeChan.on('Q_DRIVE_RESTORE', function (data, cb) {
|
||||||
|
Cryptpad.restoreDrive(data, cb);
|
||||||
|
});
|
||||||
sframeChan.on('Q_DRIVE_GETOBJECT', function (data, cb) {
|
sframeChan.on('Q_DRIVE_GETOBJECT', function (data, cb) {
|
||||||
|
if (data && data.sharedFolder) {
|
||||||
|
Cryptpad.getSharedFolder(data.sharedFolder, function (obj) {
|
||||||
|
cb(obj);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
Cryptpad.getUserObject(function (obj) {
|
Cryptpad.getUserObject(function (obj) {
|
||||||
cb(obj);
|
cb(obj);
|
||||||
});
|
});
|
||||||
@ -82,6 +100,7 @@ define([
|
|||||||
SFCommonO.start({
|
SFCommonO.start({
|
||||||
getSecrets: getSecrets,
|
getSecrets: getSecrets,
|
||||||
noHash: true,
|
noHash: true,
|
||||||
|
noRealtime: true,
|
||||||
driveEvents: true,
|
driveEvents: true,
|
||||||
addRpc: addRpc
|
addRpc: addRpc
|
||||||
});
|
});
|
||||||
|
|||||||
@ -237,7 +237,8 @@ define([
|
|||||||
&& typeof files.template[0] === "number"
|
&& typeof files.template[0] === "number"
|
||||||
&& typeof files.filesData[files.template[0]] === "object"
|
&& typeof files.filesData[files.template[0]] === "object"
|
||||||
&& !files.filesData[files.template[0]].filename
|
&& !files.filesData[files.template[0]].filename
|
||||||
&& files.filesData[files.template[0]].href === href3
|
&& !files.filesData[files.template[0]].href
|
||||||
|
&& files.filesData[files.template[0]].roHref === href3
|
||||||
&& typeof fileId2 === "number"
|
&& typeof fileId2 === "number"
|
||||||
&& typeof files.filesData[fileId2] === "object"
|
&& typeof files.filesData[fileId2] === "object"
|
||||||
&& files.filesData[fileId2].filename === "Trash"
|
&& files.filesData[fileId2].filename === "Trash"
|
||||||
@ -392,11 +393,6 @@ define([
|
|||||||
console.log("DRIVE operations: rename");
|
console.log("DRIVE operations: rename");
|
||||||
return cb();
|
return cb();
|
||||||
}
|
}
|
||||||
fo.replace(href1, href2);
|
|
||||||
if (fo.getFileData(id1).href !== href2) {
|
|
||||||
console.log("DRIVE operations: replace");
|
|
||||||
return cb();
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(true);
|
cb(true);
|
||||||
}, "DRIVE operations");
|
}, "DRIVE operations");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user