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 _ownedByMe = function (Env, owners) { return Array.isArray(owners) && owners.indexOf(Env.edPublic) !== -1; }; var _ownedByOther = function (Env, owners) { return Array.isArray(owners) && owners.length && (!Env.edPublic || owners.indexOf(Env.edPublic) === -1); }; 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 }; }; // Check if a given path is resolved to a shared folder or to the main drive var _isInSharedFolder = function (Env, path) { var resolved = _resolvePath(Env, path); return typeof resolved.id === "number" ? resolved.id : false; }; // Get the owned files in the main drive that are also duplicated in shared folders var _isDuplicateOwned = function (Env, path, id) { if (path && _isInSharedFolder(Env, path)) { return; } var data = _getFileData(Env, id || Env.user.userObject.find(path)); if (!data) { return; } if (!_ownedByMe(Env, data.owners)) { return; } var channel = data.channel; if (!channel) { return; } var foldersUO = Object.keys(Env.folders).map(function (k) { return Env.folders[k].userObject; }); return foldersUO.some(function (uo) { return uo.findChannels([channel]).length; }); }; // Get a copy of the elements located in the given paths, with their files data // Note: This function is only called to move files from a proxy to another var _getCopyFromPaths = function (Env, paths, userObject) { var data = []; var toNotRemove = []; paths.forEach(function (path, idx) { var el = userObject.find(path); var files = []; var key = path[path.length - 1]; // Get the files ID from the current path (file or folder) if (userObject.isFile(el)) { files.push(el); } else if (userObject.isSharedFolder(el)) { files.push(el); var obj = Env.folders[el].proxy.metadata || {}; if (obj) { key = obj.title; } } else { userObject.getFilesRecursively(el, files); } // If the element is a folder and it contains a shared folder, abort! // We don't want nested shared folders! if (files.some(function (f) { return userObject.isSharedFolder(f); })) { if (Env.cfg && Env.cfg.log) { Env.cfg.log(Messages._getKey('fm_moveNestedSF', [key])); } toNotRemove.unshift(idx); return; } // 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, key: key }); }); // Remove from the "paths" array the elements that we don't want to move toNotRemove.forEach(function (idx) { paths.splice(idx, 1); }); 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); // NOTE: we can only copy when moving from one drive to another. We don't want // duplicates in the same drive var copy = data.copy; 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(Env, resolved.main, Env.user.userObject); var newUserObject = newResolved.userObject; var ownedPads = []; toCopy.forEach(function (obj) { newUserObject.copyFromOtherDrive(newResolved.path, obj.el, obj.data, obj.key); var _owned = Object.keys(obj.data).filter(function (id) { var owners = obj.data[id].owners; return _ownedByMe(Env, owners); }); Array.prototype.push.apply(ownedPads, _owned); }); if (copy) { return; } if (resolved.main.length) { 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(Env, paths, uoFrom); toCopy.forEach(function (obj) { uoTo.copyFromOtherDrive(newResolved.path, obj.el, obj.data, obj.key); }); if (copy) { return; } // 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 shared folder 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', data.password); var secret = Hash.getSecrets('drive', hash, data.password); var hashes = Hash.getHashes(secret); folderData = { href: '/drive/#' + hashes.editHash, roHref: '/drive/#' + hashes.viewHash, 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) { var uo = Env.user.userObject; if (Util.find(Env.settings, ['drive', 'hideDuplicate'])) { // If we hide duplicate owned pads in our drive, we have // to make sure we're not deleting a hidden own file // from inside a folder we're trying to delete resolved.main.forEach(function (p) { var el = uo.find(p); if (uo.isFile(el) || uo.isSharedFolder(el)) { return; } var arr = []; uo.getFilesRecursively(el, arr); arr.forEach(function (id) { if (_isDuplicateOwned(Env, null, id)) { Env.user.userObject.add(Number(id), [UserObject.ROOT]); } }); }); } uo.delete(resolved.main, waitFor(function (err, _toUnpin, _ownedRemoved) { ownedRemoved = _ownedRemoved; if (!Env.unpinPads || !_toUnpin) { return; } Array.prototype.push.apply(toUnpin, _toUnpin); })); } }).nThen(function (waitFor) { // Check if removed owned pads are duplicated in 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); })); }); }).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).forEach(function (id) { var data = _getFileData(Env, id); var arr = [data.channel]; if (data.rtChannel) { arr.push(data.rtChannel); } if (data.lastVersion) { arr.push(Hash.hrefToHexChannelId(data.lastVersion)); } Array.prototype.push.apply(toKeep, arr); }); // 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 () {}; if (!data.attr || !data.attr.trim()) { return void cb("E_INVAL_ATTR"); } var sfId = Env.user.userObject.getSFIdFromHref(data.href); if (sfId) { Env.user.proxy[UserObject.SHARED_FOLDERS][sfId][data.attr] = data.value; } 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 sfId = Env.user.userObject.getSFIdFromHref(data.href); if (sfId) { return void cb(null, Env.user.proxy[UserObject.SHARED_FOLDERS][sfId][data.attr]); } 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) { var l = uo.getTagsList(); Object.keys(l).forEach(function (t) { list[t] = list[t] ? (list[t] + l[t]) : l[t]; }); }); 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, type) { 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 (_ownedByOther(Env, data.owners) || (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 (_ownedByMe(Env, data.owners)) { result.push(data.channel); } }; } if (type === "pin") { return function (fileId) { var data = userObject.getFileData(fileId); // Don't pin pads owned by someone else if (_ownedByOther(Env, data.owners)) { return; } // Don't push duplicates if (data.lastVersion) { var otherChan = Hash.hrefToHexChannelId(data.lastVersion); if (result.indexOf(otherChan) === -1) { result.push(otherChan); } } if (data.rtChannel && result.indexOf(data.rtChannel) === -1) { result.push(data.rtChannel); } if (result.indexOf(data.channel) === -1) { result.push(data.channel); } }; } }; if (type === 'owned' && !Env.edPublic) { return result; } if (type === 'pin' && !Env.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 (_ownedByMe(Env, owners)) { 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 () { var error; nThen(function (waitFor) { uo.pushData(pad, waitFor(function (e, id) { if (e) { error = e; return; } uo.add(id, p); })); if (uo.id && _ownedByMe(Env, pad.owners)) { // Creating an owned pad in a shared folder: // We must add a copy in the user's personnal drive Env.user.userObject.pushData(pad, waitFor(function (e, id) { if (e) { error = e; return; } Env.user.userObject.add(id, ['root']); })); } }).nThen(function () { cb(error); }); }; 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, data, uoConfig) { var Env = { pinPads: data.pin, unpinPads: data.unpin, loadSharedFolder: data.loadSharedFolder, cfg: uoConfig, edPublic: data.edPublic, settings: data.settings, 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, copy) { return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", { cmd: "move", data: { paths: paths, newPath: newPath, copy: copy } }, cb); }; var emptyTrashInner = function (Env, cb) { return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", { cmd: "emptyTrash", data: null }, 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) { return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", { cmd: "delete", data: { paths: paths, } }, 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, fId) { if (fId) { return Env.folders[fId].userObject.find(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.inSharedFolder = true; 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) { var files = []; var userObjects = _getUserObjects(Env); userObjects.forEach(function (uo) { var data = uo.getFiles([UserObject.FILES_DATA]).map(function (id) { return [Number(id), uo.getFileData(id)]; }); Array.prototype.push.apply(files, data); }); var sorted = files.filter(function (a) { return a[1].atime; }) .sort(function (a,b) { return b[1].atime - a[1].atime; }); return sorted; //return Env.user.userObject.getRecentPads(); }; var getOwnedPads = function (Env) { return Env.user.userObject.getOwnedPads(Env.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 = _isInSharedFolder; /* 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 isDuplicateOwned = _isDuplicateOwned; var createInner = function (proxy, sframeChan, edPublic, uoConfig) { var Env = { cfg: uoConfig, sframeChan: sframeChan, 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 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), isDuplicateOwned: callWithEnv(isDuplicateOwned), // 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 }; });