From e949c2a6f75a4074b8b96dbc7778a8f4ab471453 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Tue, 30 Jul 2019 13:30:52 +0200 Subject: [PATCH 1/8] Add dummy convert folder to shared folder --- www/common/proxy-manager.js | 8 ++++++++ www/common/userObject.js | 14 ++++++++++++++ www/drive/inner.js | 36 +++++++++++++++++++++++++++++------- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 1257f3ad4..d35df4e68 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -1071,6 +1071,13 @@ define([ } return Env.user.userObject.hasSubfolder(el, trashRoot); }; + var hasSubSharedFolder = function (Env, el, trashRoot) { + if (Env.folders[el]) { + var uo = Env.folders[el].userObject; + return uo.hasSubSharedFolder(uo.find[uo.ROOT]); + } + return Env.user.userObject.hasSubSharedFolder(el, trashRoot); + }; var hasFile = function (Env, el, trashRoot) { if (Env.folders[el]) { var uo = Env.folders[el].userObject; @@ -1140,6 +1147,7 @@ define([ isInTrashRoot: callWithEnv(isInTrashRoot), comparePath: callWithEnv(comparePath), hasSubfolder: callWithEnv(hasSubfolder), + hasSubSharedFolder: callWithEnv(hasSubSharedFolder), hasFile: callWithEnv(hasFile), // Data user: Env.user, diff --git a/www/common/userObject.js b/www/common/userObject.js index 39b55484a..be770232b 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -156,6 +156,20 @@ define([ } }; + exp.hasSubSharedFolder = function (folder) { + for (var el in folder) { + if (isSharedFolder(folder[el])) { + return true; + } + else if (isFolder(folder[el])) { + if (exp.hasSubSharedFolder(folder[el])) { + return true; + } + } + } + return false; + } + // Get data from AllFiles (Cryptpad_RECENTPADS) var getFileData = exp.getFileData = function (file) { if (!file) { return; } diff --git a/www/drive/inner.js b/www/drive/inner.js index fd7723bca..1694564f9 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -996,6 +996,7 @@ define([ // Can't rename or delete root elements hide.push('delete'); hide.push('rename'); + hide.push('share'); hide.push('color'); } if (!$element.is('.cp-app-drive-element-owned')) { @@ -1022,8 +1023,9 @@ define([ } } else if ($element.is('.cp-app-drive-element-sharedf')) { if (containsFolder) { - // More than 1 folder selected: cannot create a new subfolder + // More than 1 shared folder selected: cannot create a new subfolder hide.push('newfolder'); + hide.push('share'); hide.push('expandall'); hide.push('collapseall'); } @@ -1036,13 +1038,13 @@ define([ if (containsFolder) { // More than 1 folder selected: cannot create a new subfolder hide.push('newfolder'); + hide.push('share'); hide.push('expandall'); hide.push('collapseall'); } containsFolder = true; hide.push('openro'); hide.push('properties'); - hide.push('share'); hide.push('hashtag'); } // If we're in the trash, hide restore and properties for non-root elements @@ -3568,7 +3570,7 @@ define([ var parsed, modal; var friends = common.getFriends(); - if (manager.isSharedFolder(el)) { + if (manager.isSharedFolder(el)) { // Shared Folder data = manager.getSharedFolderData(el); parsed = Hash.parsePadUrl(data.href); modal = UIElements.createSFShareModal({ @@ -3582,7 +3584,27 @@ define([ editHash: parsed.hash } }); - } else { + UI.openCustomModal(modal, { + wide: Object.keys(friends).length !== 0 + }); + } else if (manager.isFolder(el)) { // Folder + // if folder already contains SF + if (manager.isInSharedFolder(paths[0].path)) { + UI.alert(Messages.convertFolderToSF_SFParent || "Sharing this folder can't be done because it already in a Shared Folder. Please, move this folder elsewhere in order to continue", undefined, true); + } + // if folder is inside SF + else if (manager.hasSubSharedFolder(el)) { + UI.alert(Messages.convertFolderToSF_SFChildren || "Sharing this folder can't be done because it already contains one ore more Shared Folders. Please, remove those from this folder in order to continue.", undefined, true); + } + // if folder does not contains SF + else { + UI.confirm(Messages.convertFolderToSF_confirm || "In order to be shared, this folder must be converted into a shared folder. Proceed ?", function(res) { + if (!res) { return; } + if (paths[0].path.length <= 1) { return; } // if root + // convert folder to Shared Folder + }); + } + } else { // File data = manager.getFileData(el); parsed = Hash.parsePadUrl(data.href); var roParsed = Hash.parsePadUrl(data.roHref); @@ -3608,10 +3630,10 @@ define([ modal = padType === 'file' ? UIElements.createFileShareModal(padData) : UIElements.createShareModal(padData); modal = UI.dialog.tabs(modal); + UI.openCustomModal(modal, { + wide: Object.keys(friends).length !== 0 + }); } - UI.openCustomModal(modal, { - wide: Object.keys(friends).length !== 0 - }); } else if ($(this).hasClass('cp-app-drive-context-newfolder')) { if (paths.length !== 1) { return; } From 4a93d1829a9dc5f08418753dab52b02168c37ecc Mon Sep 17 00:00:00 2001 From: ClemDee Date: Tue, 30 Jul 2019 16:37:35 +0200 Subject: [PATCH 2/8] Fix isFolderEmpty function in userObject --- www/common/userObject.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/common/userObject.js b/www/common/userObject.js index be770232b..eed9fc19c 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -109,7 +109,9 @@ define([ }; exp.isFolderEmpty = function (element) { if (!isFolder(element)) { return false; } - return Object.keys(element).length === 0; + if (Object.keys(element).length === 0) { return true; } + if (Object.keys(element).length === 1 && isFolderData(element[Object.keys(element)[0]])) { return true; } + return false; }; exp.hasSubfolder = function (element, trashRoot) { From 1cddd99baa9d098c27c0d71ec918ea2744617563 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Tue, 30 Jul 2019 16:39:28 +0200 Subject: [PATCH 3/8] Convert folder into shared folders now working --- www/common/proxy-manager.js | 67 +++++++++++++++++++++++++++++++++++++ www/common/userObject.js | 4 +-- www/drive/inner.js | 2 +- 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index d35df4e68..7b79bcb14 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -461,6 +461,62 @@ define([ cb(id); }); }; + + // convert a folder to a Shared Folder + var _convertFolderToSharedFolder = function (Env, data, cb) { + var path = data.path; + var folderElement = Env.user.userObject.find(path); + if (data.path.length <= 1) { return; } + if (_isInSharedFolder(Env, path)) { return; } + if (Env.user.userObject.hasSubSharedFolder(folderElement)) { return; } + var parentPath = path.slice(0, -1); + var parentFolder = Env.user.userObject.find(parentPath); + var folderName = path[path.length - 1]; + var SFId; + var sharedFolderElement; + nThen(function (waitFor) { + // create shared folder + _addSharedFolder(Env, { + path: parentPath, + name: folderName, + owned: true, + password: '', + }, waitFor(function (id) { SFId = id; })); + }).nThen(function (waitFor) { + // move everything from folder to SF + if (!SFId) { return void cb(); } + var paths = []; + for (var el in folderElement) { + if (Env.user.userObject.isFolder(folderElement[el]) || Env.user.userObject.isFile(folderElement[el])) { + paths.push(path.concat(el)); + } + } + var SFKey = Object.keys(parentFolder).find(function (el) { + return parentFolder[el] === SFId; + }); + var newPath = parentPath.concat(SFKey).concat(UserObject.ROOT); + sharedFolderElement = Env.user.proxy[UserObject.SHARED_FOLDERS][SFId]; + _move(Env, { + paths: paths, + newPath: newPath, + copy: false, + }, waitFor()); + }).nThen(function (waitFor) { + // migrate metadata + var metadata = Env.user.userObject.getFolderData(folderElement); + for (var key in metadata) { + if (key === "metadata") { continue; } + sharedFolderElement[key] = metadata[key]; + } + }).nThen(function (waitFor) { + // remove folder + Env.user.userObject.delete([path], waitFor()); + }).nThen(function () { + // call callback + cb(); + }); + } + // Delete permanently some pads or folders var _delete = function (Env, data, cb) { data = data || {}; @@ -598,6 +654,8 @@ define([ _addFolder(Env, data, cb); break; case 'addSharedFolder': _addSharedFolder(Env, data, cb); break; + case 'convertFolderToSharedFolder': + _convertFolderToSharedFolder(Env, data, cb); break; case 'delete': _delete(Env, data, cb); break; case 'emptyTrash': @@ -914,6 +972,14 @@ define([ } }, cb); }; + var convertFolderToSharedFolderInner = function (Env, path, cb) { + return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", { + cmd: "convertFolderToSharedFolder", + data: { + path: path + } + }, cb); + }; var deleteInner = function (Env, paths, cb) { return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", { cmd: "delete", @@ -1117,6 +1183,7 @@ define([ emptyTrash: callWithEnv(emptyTrashInner), addFolder: callWithEnv(addFolderInner), addSharedFolder: callWithEnv(addSharedFolderInner), + convertFolderToSharedFolder: callWithEnv(convertFolderToSharedFolderInner), delete: callWithEnv(deleteInner), restore: callWithEnv(restoreInner), setFolderData: callWithEnv(setFolderDataInner), diff --git a/www/common/userObject.js b/www/common/userObject.js index eed9fc19c..9bf3487d6 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -158,13 +158,13 @@ define([ } }; - exp.hasSubSharedFolder = function (folder) { + var hasSubSharedFolder = exp.hasSubSharedFolder = function (folder) { for (var el in folder) { if (isSharedFolder(folder[el])) { return true; } else if (isFolder(folder[el])) { - if (exp.hasSubSharedFolder(folder[el])) { + if (hasSubSharedFolder(folder[el])) { return true; } } diff --git a/www/drive/inner.js b/www/drive/inner.js index 1694564f9..456e91efb 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -3601,7 +3601,7 @@ define([ UI.confirm(Messages.convertFolderToSF_confirm || "In order to be shared, this folder must be converted into a shared folder. Proceed ?", function(res) { if (!res) { return; } if (paths[0].path.length <= 1) { return; } // if root - // convert folder to Shared Folder + manager.convertFolderToSharedFolder(paths[0].path, refresh); }); } } else { // File From 27502c61b15af9479440782b90af1b5c261b34d0 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Tue, 30 Jul 2019 17:25:38 +0200 Subject: [PATCH 4/8] Fix lint issues --- www/common/proxy-manager.js | 4 ++-- www/common/userObject.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 7b79bcb14..f75fd0aa7 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -501,7 +501,7 @@ define([ newPath: newPath, copy: false, }, waitFor()); - }).nThen(function (waitFor) { + }).nThen(function () { // migrate metadata var metadata = Env.user.userObject.getFolderData(folderElement); for (var key in metadata) { @@ -515,7 +515,7 @@ define([ // call callback cb(); }); - } + }; // Delete permanently some pads or folders var _delete = function (Env, data, cb) { diff --git a/www/common/userObject.js b/www/common/userObject.js index 9bf3487d6..109928e81 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -170,7 +170,7 @@ define([ } } return false; - } + }; // Get data from AllFiles (Cryptpad_RECENTPADS) var getFileData = exp.getFileData = function (file) { From f2063f37484bedcd0fcc28dc389b93497d3d61be Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 16 Aug 2019 13:56:25 +0200 Subject: [PATCH 5/8] Remove translation fallbacks --- www/drive/inner.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index 456e91efb..7898e62d1 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -3590,15 +3590,15 @@ define([ } else if (manager.isFolder(el)) { // Folder // if folder already contains SF if (manager.isInSharedFolder(paths[0].path)) { - UI.alert(Messages.convertFolderToSF_SFParent || "Sharing this folder can't be done because it already in a Shared Folder. Please, move this folder elsewhere in order to continue", undefined, true); + UI.alert(Messages.convertFolderToSF_SFParent, undefined, true); } // if folder is inside SF else if (manager.hasSubSharedFolder(el)) { - UI.alert(Messages.convertFolderToSF_SFChildren || "Sharing this folder can't be done because it already contains one ore more Shared Folders. Please, remove those from this folder in order to continue.", undefined, true); + UI.alert(Messages.convertFolderToSF_SFChildren, undefined, true); } // if folder does not contains SF else { - UI.confirm(Messages.convertFolderToSF_confirm || "In order to be shared, this folder must be converted into a shared folder. Proceed ?", function(res) { + UI.confirm(Messages.convertFolderToSF_confirm, function(res) { if (!res) { return; } if (paths[0].path.length <= 1) { return; } // if root manager.convertFolderToSharedFolder(paths[0].path, refresh); From 58a3f76464c890671ae175e57b36bac30d3eb919 Mon Sep 17 00:00:00 2001 From: ClemDee Date: Fri, 16 Aug 2019 14:35:07 +0200 Subject: [PATCH 6/8] Fix two comments --- www/drive/inner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/drive/inner.js b/www/drive/inner.js index 7898e62d1..71dd1e4d3 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -3588,11 +3588,11 @@ define([ wide: Object.keys(friends).length !== 0 }); } else if (manager.isFolder(el)) { // Folder - // if folder already contains SF + // if folder is inside SF if (manager.isInSharedFolder(paths[0].path)) { UI.alert(Messages.convertFolderToSF_SFParent, undefined, true); } - // if folder is inside SF + // if folder already contains SF else if (manager.hasSubSharedFolder(el)) { UI.alert(Messages.convertFolderToSF_SFChildren, undefined, true); } From 7d583975b886d171af462ddb01aba4a4ce9f0356 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 19 Aug 2019 11:56:52 +0200 Subject: [PATCH 7/8] fix a few things, add comments for others --- www/common/proxy-manager.js | 68 +++++++++++++++++++++++++------------ www/common/userObject.js | 2 ++ www/drive/inner.js | 16 ++++----- 3 files changed, 55 insertions(+), 31 deletions(-) diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index f75fd0aa7..0e4baa799 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -466,36 +466,61 @@ define([ var _convertFolderToSharedFolder = function (Env, data, cb) { var path = data.path; var folderElement = Env.user.userObject.find(path); - if (data.path.length <= 1) { return; } - if (_isInSharedFolder(Env, path)) { return; } - if (Env.user.userObject.hasSubSharedFolder(folderElement)) { return; } + // don't try to convert top-level elements (trash, root, etc) to shared-folders + // TODO also validate that you're in root (not templates, etc) + if (data.path.length <= 1) { + // XXX call back with error and abort nThen + return; + } + if (_isInSharedFolder(Env, path)) { + // XXX call back with error and abort nThen + return; + } + if (Env.user.userObject.hasSubSharedFolder(folderElement)) { + // XXX call back with error and abort nThen + return; + } var parentPath = path.slice(0, -1); var parentFolder = Env.user.userObject.find(parentPath); var folderName = path[path.length - 1]; var SFId; - var sharedFolderElement; nThen(function (waitFor) { // create shared folder _addSharedFolder(Env, { path: parentPath, name: folderName, - owned: true, - password: '', - }, waitFor(function (id) { SFId = id; })); + owned: true, // FIXME hardcoded preference + password: '', // FIXME hardcoded preference + }, waitFor(function (id) { + // _addSharedFolder can be an id or an error + if (typeof(id) === 'object' && id && id.error) { + // XXX FIXME handle error + } else { + SFId = id; + } + })); }).nThen(function (waitFor) { // move everything from folder to SF - if (!SFId) { return void cb(); } + if (!SFId) { + // XXX FIXME callback does not indicate that there is an error + // XXX FIXME does not abort nThen chain + return void cb(); + } var paths = []; for (var el in folderElement) { if (Env.user.userObject.isFolder(folderElement[el]) || Env.user.userObject.isFile(folderElement[el])) { paths.push(path.concat(el)); } } - var SFKey = Object.keys(parentFolder).find(function (el) { - return parentFolder[el] === SFId; + var SFKey; + // this is basically Array.find, except it works in IE + Object.keys(parentFolder).some(function (el) { + if (parentFolder[el] === SFId) { + SFKey = el; + return true; + } }); var newPath = parentPath.concat(SFKey).concat(UserObject.ROOT); - sharedFolderElement = Env.user.proxy[UserObject.SHARED_FOLDERS][SFId]; _move(Env, { paths: paths, newPath: newPath, @@ -503,17 +528,20 @@ define([ }, waitFor()); }).nThen(function () { // migrate metadata + var sharedFolderElement = Env.user.proxy[UserObject.SHARED_FOLDERS][SFId]; var metadata = Env.user.userObject.getFolderData(folderElement); for (var key in metadata) { + // it shouldn't be possible to have nested metadata + // but this is a reasonable sanity check if (key === "metadata") { continue; } + // copy the metadata from the original folder to the new shared folder sharedFolderElement[key] = metadata[key]; } - }).nThen(function (waitFor) { + // remove folder - Env.user.userObject.delete([path], waitFor()); - }).nThen(function () { - // call callback - cb(); + Env.user.userObject.delete([path], function () { + cb(); + }); }); }; @@ -1137,12 +1165,8 @@ define([ } return Env.user.userObject.hasSubfolder(el, trashRoot); }; - var hasSubSharedFolder = function (Env, el, trashRoot) { - if (Env.folders[el]) { - var uo = Env.folders[el].userObject; - return uo.hasSubSharedFolder(uo.find[uo.ROOT]); - } - return Env.user.userObject.hasSubSharedFolder(el, trashRoot); + var hasSubSharedFolder = function (Env, el) { + return Env.user.userObject.hasSubSharedFolder(el); }; var hasFile = function (Env, el, trashRoot) { if (Env.folders[el]) { diff --git a/www/common/userObject.js b/www/common/userObject.js index 109928e81..cea2a72c0 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -109,7 +109,9 @@ define([ }; exp.isFolderEmpty = function (element) { if (!isFolder(element)) { return false; } + // if the folder contains nothing, it's empty if (Object.keys(element).length === 0) { return true; } + // or if it contains one thing and that thing is metadata if (Object.keys(element).length === 1 && isFolderData(element[Object.keys(element)[0]])) { return true; } return false; }; diff --git a/www/drive/inner.js b/www/drive/inner.js index 71dd1e4d3..5b96643a9 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -993,7 +993,7 @@ define([ hide.push('collapseall'); } if (path.length === 1) { - // Can't rename or delete root elements + // Can't rename, share, delete, or change the color of root elements hide.push('delete'); hide.push('rename'); hide.push('share'); @@ -1023,9 +1023,8 @@ define([ } } else if ($element.is('.cp-app-drive-element-sharedf')) { if (containsFolder) { - // More than 1 shared folder selected: cannot create a new subfolder + // More than 1 folder selected: cannot create a new subfolder hide.push('newfolder'); - hide.push('share'); hide.push('expandall'); hide.push('collapseall'); } @@ -1038,7 +1037,6 @@ define([ if (containsFolder) { // More than 1 folder selected: cannot create a new subfolder hide.push('newfolder'); - hide.push('share'); hide.push('expandall'); hide.push('collapseall'); } @@ -3570,7 +3568,7 @@ define([ var parsed, modal; var friends = common.getFriends(); - if (manager.isSharedFolder(el)) { // Shared Folder + if (manager.isSharedFolder(el)) { data = manager.getSharedFolderData(el); parsed = Hash.parsePadUrl(data.href); modal = UIElements.createSFShareModal({ @@ -3584,21 +3582,21 @@ define([ editHash: parsed.hash } }); - UI.openCustomModal(modal, { + return void UI.openCustomModal(modal, { wide: Object.keys(friends).length !== 0 }); } else if (manager.isFolder(el)) { // Folder // if folder is inside SF if (manager.isInSharedFolder(paths[0].path)) { - UI.alert(Messages.convertFolderToSF_SFParent, undefined, true); + return void UI.alert(Messages.convertFolderToSF_SFParent); } // if folder already contains SF else if (manager.hasSubSharedFolder(el)) { - UI.alert(Messages.convertFolderToSF_SFChildren, undefined, true); + return void UI.alert(Messages.convertFolderToSF_SFChildren); } // if folder does not contains SF else { - UI.confirm(Messages.convertFolderToSF_confirm, function(res) { + return void UI.confirm(Messages.convertFolderToSF_confirm, function(res) { if (!res) { return; } if (paths[0].path.length <= 1) { return; } // if root manager.convertFolderToSharedFolder(paths[0].path, refresh); From a368948fb64407f939976dc80a8c1eda1b7220c2 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 19 Aug 2019 12:06:06 +0200 Subject: [PATCH 8/8] handle errors when converting folders to shared folders --- www/common/proxy-manager.js | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 0e4baa799..c372d4aea 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -469,16 +469,19 @@ define([ // don't try to convert top-level elements (trash, root, etc) to shared-folders // TODO also validate that you're in root (not templates, etc) if (data.path.length <= 1) { - // XXX call back with error and abort nThen - return; + return void cb({ + error: 'E_INVAL_PATH', + }); } if (_isInSharedFolder(Env, path)) { - // XXX call back with error and abort nThen - return; + return void cb({ + error: 'E_INVAL_NESTING', + }); } if (Env.user.userObject.hasSubSharedFolder(folderElement)) { - // XXX call back with error and abort nThen - return; + return void cb({ + error: 'E_INVAL_NESTING', + }); } var parentPath = path.slice(0, -1); var parentFolder = Env.user.userObject.find(parentPath); @@ -489,12 +492,13 @@ define([ _addSharedFolder(Env, { path: parentPath, name: folderName, - owned: true, // FIXME hardcoded preference - password: '', // FIXME hardcoded preference + owned: true, // XXX FIXME hardcoded preference + password: '', // XXX FIXME hardcoded preference }, waitFor(function (id) { // _addSharedFolder can be an id or an error if (typeof(id) === 'object' && id && id.error) { - // XXX FIXME handle error + waitFor.abort(); + return void cb(id); } else { SFId = id; } @@ -502,9 +506,10 @@ define([ }).nThen(function (waitFor) { // move everything from folder to SF if (!SFId) { - // XXX FIXME callback does not indicate that there is an error - // XXX FIXME does not abort nThen chain - return void cb(); + waitFor.abort(); + return void cb({ + error: 'E_NO_ID' + }); } var paths = []; for (var el in folderElement) { @@ -520,6 +525,13 @@ define([ return true; } }); + + if (!SFKey) { + waitFor.abort(); + return void cb({ + error: 'E_NO_KEY' + }); + } var newPath = parentPath.concat(SFKey).concat(UserObject.ROOT); _move(Env, { paths: paths,