diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 39aac9b54..a775638e8 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1622,6 +1622,7 @@ define([ }; if (!proxy.settings) { proxy.settings = {}; } var manager = store.manager = ProxyManager.create(proxy.drive, { + onSync: onSync, edPublic: proxy.edPublic, pin: pin, unpin: unpin, diff --git a/www/common/outer/userObject.js b/www/common/outer/userObject.js index 12771fd92..e67dd5f01 100644 --- a/www/common/outer/userObject.js +++ b/www/common/outer/userObject.js @@ -375,6 +375,19 @@ define([ } }; + exp.setFolderData = function (path, key, value, cb) { + var folder = exp.find(path); + if (!exp.isFolder(folder) || exp.isSharedFolder(folder)) { return; } + if (!exp.hasFolderData(folder)) { + var hashKey = "000" + Hash.createChannelId().slice(0, -3); + folder[hashKey] = { + metadata: true + }; + } + exp.getFolderData(folder)[key] = value; + cb(); + }; + /** * INTEGRITY CHECK */ @@ -493,7 +506,16 @@ define([ var fixRoot = function (elem) { if (typeof(files[ROOT]) !== "object") { debug("ROOT was not an object"); files[ROOT] = {}; } var element = elem || files[ROOT]; + var nbMetadataFolders = 0; for (var el in element) { + if (exp.isFolderData(element[el])) { + if (nbMetadataFolders !== 0) { + debug("Multiple metadata files in folder"); + delete element[el]; + } + nbMetadataFolders++; + continue; + } if (!exp.isFile(element[el], true) && !exp.isFolder(element[el])) { debug("An element in ROOT was not a folder nor a file. ", element[el]); delete element[el]; diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index a933da776..017a403bb 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -567,6 +567,22 @@ define([ } resolved.userObject.rename(resolved.path, data.newName, cb); }; + var _setFolderData = 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.user.proxy[UserObject.SHARED_FOLDERS][el][data.key] = data.value; + return void Env.onSync(cb); + } + } + resolved.userObject.setFolderData(resolved.path, data.key, data.value, function () { + Env.onSync(cb); + }); + + }; var onCommand = function (Env, cmdData, cb) { var cmd = cmdData.cmd; var data = cmdData.data || {}; @@ -585,6 +601,8 @@ define([ _emptyTrash(Env, data, cb); break; case 'rename': _rename(Env, data, cb); break; + case 'setFolderData': + _setFolderData(Env, data, cb); break; default: cb(); } @@ -799,6 +817,7 @@ define([ var Env = { pinPads: data.pin, unpinPads: data.unpin, + onSync: data.onSync, loadSharedFolder: data.loadSharedFolder, cfg: uoConfig, edPublic: data.edPublic, @@ -905,6 +924,12 @@ define([ } }, cb); }; + var setFolderDataInner = function (Env, data, cb) { + return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", { + cmd: "setFolderData", + data: data + }, cb); + }; /* Tools */ @@ -989,6 +1014,19 @@ define([ return obj; }; + var getFolderData = function (Env, path) { + var resolved = _resolvePath(Env, path); + if (!resolved || !resolved.userObject) { return {}; } + if (!resolved.id) { + var el = Env.user.userObject.find(resolved.path); + if (Env.user.userObject.isSharedFolder(el)) { + return getSharedFolderData(Env, el); + } + } + var folder = resolved.userObject.find(resolved.path); + return resolved.userObject.getFolderData(folder); + }; + var isInSharedFolder = _isInSharedFolder; /* Generic: doesn't need access to a proxy */ @@ -1068,6 +1106,7 @@ define([ addSharedFolder: callWithEnv(addSharedFolderInner), delete: callWithEnv(deleteInner), restore: callWithEnv(restoreInner), + setFolderData: callWithEnv(setFolderDataInner), // Tools getFileData: callWithEnv(getFileData), find: callWithEnv(find), @@ -1081,6 +1120,7 @@ define([ findFile: callWithEnv(findFile), findChannels: callWithEnv(findChannels), getSharedFolderData: callWithEnv(getSharedFolderData), + getFolderData: callWithEnv(getFolderData), isInSharedFolder: callWithEnv(isInSharedFolder), getUserObjectPath: callWithEnv(getUserObjectPath), isDuplicateOwned: callWithEnv(isDuplicateOwned), diff --git a/www/common/userObject.js b/www/common/userObject.js index 1a2229d40..5cf532438 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -91,6 +91,9 @@ define([ ((typeof(files[OLD_FILES_DATA]) !== "undefined" || allowStr) && typeof(element) === "string"); }; + var isFolderData = exp.isFolderData = function (element) { + return typeof(element) === "object" && element.metadata === true; + }; exp.isReadOnlyFile = function (element) { if (!isFile(element)) { return false; } @@ -101,6 +104,7 @@ define([ }; var isFolder = exp.isFolder = function (element) { + if (isFolderData(element)) { return false; } return typeof(element) === "object" || isSharedFolder(element); }; exp.isFolderEmpty = function (element) { @@ -144,12 +148,29 @@ define([ return file; }; + exp.hasFolderData = function (folder) { + for (var el in folder) { + if(isFolderData(folder[el])) { + return true; + } + } + }; + // Get data from AllFiles (Cryptpad_RECENTPADS) var getFileData = exp.getFileData = function (file) { if (!file) { return; } return files[FILES_DATA][file] || {}; }; + exp.getFolderData = function (folder) { + for (var el in folder) { + if(isFolderData(folder[el])) { + return folder[el]; + } + } + return {}; + }; + // Data from filesData var getTitle = exp.getTitle = function (file, type) { if (isSharedFolder(file)) { @@ -231,7 +252,7 @@ define([ for (var e in root) { if (isFile(root[e]) || isSharedFolder(root[e])) { if(arr.indexOf(root[e]) === -1) { arr.push(root[e]); } - } else { + } else if (!isFolderData(root[e])) { getFilesRecursively(root[e], arr); } } diff --git a/www/drive/inner.js b/www/drive/inner.js index 645519691..398bc12de 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -835,15 +835,19 @@ define([ jscolorL.show(); }; - var getFolderColor = function ($element) { - if ($element.length === 0) { return; } - return $element.find(".cp-app-drive-icon-folder").css("color") || "#000"; + var getFolderColor = function (path) { + if (path.length === 0) { return; } + return manager.getFolderData(path).color || "#000"; }; - var setFolderColor = function ($element, color) { + var setFolderColor = function ($element, path, color) { if ($element.length === 0) { return; } $element.find(".cp-app-drive-icon-folder").css("color", color); - // TODO : save color in folder metadata + manager.setFolderData({ + path: path, + key: "color", + value: color + }, function () {}); }; @@ -877,6 +881,7 @@ define([ // Can't rename or delete root elements hide.push('delete'); hide.push('rename'); + hide.push('color'); } if (!$element.is('.cp-app-drive-element-owned')) { hide.push('deleteowned'); @@ -1578,9 +1583,11 @@ define([ if (isSharedFolder) { liClass = 'cp-app-drive-element-folder cp-app-drive-element'; $icon = $sharedFolderIcon.clone(); + $icon.css("color", getFolderColor(path.concat(elPath))); } else if (isFolder) { liClass = 'cp-app-drive-element-folder cp-app-drive-element'; $icon = manager.isFolderEmpty(root[key]) ? $folderEmptyIcon.clone() : $folderIcon.clone(); + $icon.css("color", getFolderColor(path.concat(elPath))); } var $element = $('