From 834e12fcba68900ea98db452ec2db1d7453b0f68 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 6 Sep 2019 15:45:56 +0200 Subject: [PATCH] Teams: async-store refactoring --- customize.dist/src/less2/include/drive.less | 8 +- www/common/cryptpad-common.js | 3 +- www/common/outer/async-store.js | 248 ++++++++++++-------- www/common/outer/sharedfolder.js | 2 +- www/common/outer/team.js | 4 + www/common/outer/userObject.js | 3 +- www/drive/main.js | 2 +- www/team/main.js | 13 +- 8 files changed, 172 insertions(+), 111 deletions(-) diff --git a/customize.dist/src/less2/include/drive.less b/customize.dist/src/less2/include/drive.less index 4dde139d5..3866a09e5 100644 --- a/customize.dist/src/less2/include/drive.less +++ b/customize.dist/src/less2/include/drive.less @@ -270,8 +270,6 @@ margin-left: -5px; padding-left: 5px; } - & > span.cp-app-drive-element-row:not(.cp-app-drive-element-selected):not(.cp-app-drive-element-active):hover { - } } } span.cp-app-drive-element { @@ -360,14 +358,14 @@ z-index: 10; cursor: default; &:before { - position:relative; + position: relative; top: -1px; } } .cp-app-drive-tree-docs { .cp-app-drive-tree-root > .cp-app-drive-element-row > .cp-app-drive-icon-expcol { position: relative; - top:0; + top: 0; left: -10px; } .cp-app-drive-tree-root > .cp-app-drive-element-row > .cp-app-drive-icon-folder { @@ -587,7 +585,7 @@ top: 3px; right: 3px; .fa, .cptools { - margin:0; + margin: 0; font-size: 18px; } } diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 940f5bc2a..4e2132821 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -157,8 +157,9 @@ define([ timeout: 5 * 60 * 1000 }); }; - common.addSharedFolder = function (secret, cb) { + common.addSharedFolder = function (teamId, secret, cb) { postMessage("ADD_SHARED_FOLDER", { + teamId: teamId, path: ['root'], folderData: { href: '/drive/#' + Hash.getEditHashFromKeys(secret), diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 7d04d23f4..a0088dd5e 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -59,18 +59,7 @@ define([ return; } }; - var getProxy = function (teamId) { - var s = getStore(teamId); - if (!s) { return; } - return s.proxy; - }; - var getManager = function (teamId) { - var s = getStore(teamId); - if (!s) { return; } - return s.manager; - }; - // XXX search for all calls var onSync = function (teamId, cb) { var s = getStore(teamId); if (!s) { return void cb({ error: 'ENOTFOUND' }); } @@ -335,7 +324,7 @@ define([ }; Store.uploadComplete = function (clientId, data, cb) { - var s = getStore(teamId); + var s = getStore(data.teamId); if (!s) { return void cb({ error: 'ENOTFOUND' }); } if (!s.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (data.owned) { @@ -354,7 +343,7 @@ define([ }; Store.uploadStatus = function (clientId, data, cb) { - var s = getStore(teamId); + var s = getStore(data.teamId); if (!s) { return void cb({ error: 'ENOTFOUND' }); } if (!s.rpc) { return void cb({error: 'RPC_NOT_READY'}); } s.rpc.uploadStatus(data.size, function (err, res) { @@ -364,7 +353,7 @@ define([ }; Store.uploadCancel = function (clientId, data, cb) { - var s = getStore(teamId); + var s = getStore(data.teamId); if (!s) { return void cb({ error: 'ENOTFOUND' }); } if (!s.rpc) { return void cb({error: 'RPC_NOT_READY'}); } s.rpc.uploadCancel(data.size, function (err, res) { @@ -374,7 +363,7 @@ define([ }; Store.uploadChunk = function (clientId, data, cb) { - var s = getStore(teamId); + var s = getStore(data.teamId); if (!s) { return void cb({ error: 'ENOTFOUND' }); } if (!s.rpc) { return void cb({error: 'RPC_NOT_READY'}); } s.rpc.send.unauthenticated('UPLOAD', data.chunk, function (e, msg) { @@ -603,7 +592,7 @@ define([ send('DRIVE_CHANGE', { path: ['drive', UserObject.FILES_DATA] }, clientId); - onSync(teamId, cb); + onSync(data.teamId, cb); }); }; @@ -776,20 +765,22 @@ define([ return stores; }; Store.setPadAttribute = function (clientId, data, cb) { - getAllStores.forEach(function (s) { - s.manager.setPadAttribute(data, function () { - var send = s.id ? s.sendEvent : sendDriveEvent; - send('DRIVE_CHANGE', { - path: ['drive', UserObject.FILES_DATA] - }, clientId); - onSync(s.id, cb); + nThen(function (waitFor) { + getAllStores().forEach(function (s) { + s.manager.setPadAttribute(data, waitFor(function () { + var send = s.id ? s.sendEvent : sendDriveEvent; + send('DRIVE_CHANGE', { + path: ['drive', UserObject.FILES_DATA] + }, clientId); + onSync(s.id, waitFor()); + })); }); - }); + }).nThen(cb); // cb when all managers are synced }; Store.getPadAttribute = function (clientId, data, cb) { var res = {}; nThen(function (waitFor) { - getAllStores.forEach(function (s) { + getAllStores().forEach(function (s) { s.manager.getPadAttribute(data, waitFor(function (err, val) { if (err) { return; } if (!res.value || res.atime < val.atime) { @@ -850,7 +841,7 @@ define([ // Tags Store.listAllTags = function (clientId, data, cb) { var tags = {}; - getAllStores.forEach(function (s) { + getAllStores().forEach(function (s) { var l = s.manager.getTagsList(); Object.keys(l).forEach(function (tag) { tags[tag] = (tags[tag] || 0) + l[tag]; @@ -866,12 +857,12 @@ define([ // No templates in shared folders: we don't need the manager here var res = []; var channels = []; - getAllStores.forEach(function (s) { + getAllStores().forEach(function (s) { var templateFiles = s.userObject.getFiles(['template']); templateFiles.forEach(function (f) { var data = s.userObject.getFileData(f); // Don't push duplicates - if (channels.indexOf(data.channel) !== -1) { return } + if (channels.indexOf(data.channel) !== -1) { return; } channels.push(data.channel); // Puhs a copy of the data res.push(JSON.parse(JSON.stringify(data))); @@ -881,35 +872,60 @@ define([ }; 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) { - // 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 - if (err) { return; } - var used = typeof data === "number" ? ++data : 1; - store.userObject.setPadAttribute(href, 'used', used); + getAllStores().forEach(function (s) { + s.userObject.getPadAttribute(href, 'used', function (err, data) { + // 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 + if (err) { return; } + var used = typeof data === "number" ? ++data : 1; + s.userObject.setPadAttribute(href, 'used', used); + }); }); }; // Pads Store.isOnlyInSharedFolder = function (clientId, channel, cb) { - var res = store.manager.findChannel(channel); + var res = false; + + // The main drive doesn't have an fId (folder ID) + var isInMainDrive = function (obj) { return !obj.fId; }; + + // A pad is only in a shared folder if it is in one of our managers + // AND if if it not in any "main" drive + getAllStores().some(function (s) { + var _res = s.manager.findChannel(channel); + + // Not in this manager: go the the next one + if (!_res.length) { return; } + + // if the pad is in the main drive, cb(false) + if (_res.some(isInMainDrive)) { + res = false; + return true; + } + + // Otherwise the pad is only in a shared folder in this manager: + // set the result to true and check the next manager + res = true; + }); - // 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; - })); + return cb (res); }; Store.moveToTrash = function (clientId, data, cb) { var href = Hash.getRelativeHref(data.href); - store.userObject.forget(href); - sendDriveEvent('DRIVE_CHANGE', { - path: ['drive', UserObject.FILES_DATA] - }, clientId); - onSync(cb); + nThen(function (waitFor) { + getAllStores().forEach(function (s) { + var deleted = s.userObject.forget(href); + if (!deleted) { return; } + var send = s.id ? s.sendEvent : sendDriveEvent; + send('DRIVE_CHANGE', { + path: ['drive', UserObject.FILES_DATA] + }, clientId); + onSync(s.id, waitFor()); + }); + }).nThen(cb); }; + // XXX Teams. encrypted href... Store.setPadTitle = function (clientId, data, cb) { var title = data.title; var href = data.href; @@ -935,9 +951,19 @@ define([ expire = +channelData.data.expire || undefined; } - var datas = store.manager.findChannel(channel); - var contains = datas.length !== 0; - datas.forEach(function (obj) { + // Check if the pad is stored in any managers. + // If it is stored, update its data, otherwise ask the user where to store it + var allData = []; + var sendTo = []; + getAllStores().forEach(function (s) { + var res = s.manager.findChannel(channel); + if (res.length) { + sendTo.push(s.id); + } + Array.prototype.push.apply(allData, res); + }); + var contains = allData.length !== 0; + allData.forEach(function (obj) { var pad = obj.data; pad.atime = +new Date(); pad.title = title; @@ -961,6 +987,7 @@ define([ // Add the pad if it does not exist in our drive if (!contains) { var autoStore = Util.find(store.proxy, ['settings', 'general', 'autostore']); + // XXX owned by one of our teams? var ownedByMe = Array.isArray(owners) && owners.indexOf(store.proxy.edPublic) !== -1; if (autoStore !== 1 && !data.forceSave && !data.path && !ownedByMe) { // send event to inner to display the corner popup @@ -975,6 +1002,7 @@ define([ href = undefined; } Store.addPad(clientId, { + teamId: data.teamId, href: href, roHref: roHref, channel: channel, @@ -990,19 +1018,30 @@ define([ }); return; } - } else { - sendDriveEvent('DRIVE_CHANGE', { + } + + sendTo.forEach(function (teamId) { + var send = teamId ? getStore(teamId).sendEvent : sendDriveEvent; + send('DRIVE_CHANGE', { path: ['drive', UserObject.FILES_DATA] }, clientId); - // Let inner know that dropped files shouldn't trigger the popup - postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", { - stored: true + }); + // Let inner know that dropped files shouldn't trigger the popup + postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", { + stored: true + }); + nThen(function (waitFor) { + sendTo.forEach(function (teamId) { + onSync(teamId, waitFor()); }); - } - onSync(cb); + }).nThen(cb); }; // Filepicker app + + // Get a map of file data corresponding to the given filters. + // The keys used in the map represent the ID of the "files" + // TODO maybe we shouldn't mix results from teams and from the user? Store.getSecureFilesList = function (clientId, query, cb) { var list = {}; var types = query.types; @@ -1019,20 +1058,31 @@ define([ } return filtered; }; - store.manager.getSecureFilesList(where).forEach(function (obj) { - var data = obj.data; - var id = obj.id; - var parsed = Hash.parsePadUrl(data.href || data.roHref); - if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) && - !isFiltered(parsed.type, data)) { - list[id] = data; - } + var channels = []; + getAllStores().forEach(function (s) { + s.manager.getSecureFilesList(where).forEach(function (obj) { + var data = obj.data; + if (channels.indexOf(data.channel) !== -1) { return; } + var id = obj.id; + var parsed = Hash.parsePadUrl(data.href || data.roHref); + if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) && + !isFiltered(parsed.type, data)) { + list[id] = data; + } + }); }); cb(list); }; + // Get the first pad we can find in any of our managers and return its file data 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)); + var res = {}; + getAllStores().some(function (s) { + var d = s.userObject.getFileData(id); + if (!d.roHref && !d.href) { return; } + res = d; + return true; + }); + cb(res); }; @@ -1109,16 +1159,22 @@ define([ }; // Get hashes for the share button + // If we can find a stronger hash Store.getStrongerHash = function (clientId, data, cb) { - var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {}; - - // If we have a stronger version in drive, add it and add a redirect button - var stronger = Hash.findStronger(data.href, data.channel, allPads); - if (stronger) { - var parsed2 = Hash.parsePadUrl(stronger.href); - return void cb(parsed2.hash); + var found = getAllStores().some(function (s) { + var allPads = Util.find(s.proxy, ['drive', 'filesData']) || {}; + + // If we have a stronger version in drive, add it and add a redirect button + var stronger = Hash.findStronger(data.href, data.channel, allPads); + if (stronger) { + var parsed2 = Hash.parsePadUrl(stronger.href); + cb(parsed2.hash); + return true; + } + }); + if (!found) { + cb(); } - cb(); }; // Universal @@ -1318,12 +1374,14 @@ define([ }, onMetadataUpdate: function (metadata) { channel.data = metadata || {}; - var allData = store.manager.findChannel(data.channel); - allData.forEach(function (obj) { - obj.data.owners = metadata.owners; - if (metadata.expire) { - obj.data.expire = +metadata.expire; - } + getAllStores().forEach(function (s) { + var allData = s.manager.findChannel(data.channel); + allData.forEach(function (obj) { + obj.data.owners = metadata.owners; + if (metadata.expire) { + obj.data.expire = +metadata.expire; + } + }); }); channel.bcast("PAD_METADATA", metadata); }, @@ -1460,6 +1518,7 @@ define([ var href, title; + // XXX TEAMOWNER if (!res.some(function (obj) { if (obj.data && Array.isArray(obj.data.owners) && obj.data.owners.indexOf(edPublic) !== -1 && @@ -1509,6 +1568,10 @@ define([ Store.setPadMetadata = function (clientId, data, cb) { if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); } if (!data.command) { return void cb({ error: 'EINVAL' }); } + // XXX TEAMOWNER + // If owned by a team, we should use the team rpc here + // We'll need common-ui-elements to tell us the "owners" value or we can + // call getPadMetadata first store.rpc.setMetadata(data, function (err, res) { if (err) { return void cb({ error: err }); } if (!Array.isArray(res) || !res.length) { return void cb({}); } @@ -1638,8 +1701,10 @@ define([ Store.loadSharedFolder(null, data.id, data.data, cb); }; Store.addSharedFolder = function (clientId, data, cb) { - store.manager.addSharedFolder(data, function (id) { - sendDriveEvent('DRIVE_CHANGE', { + var s = getStore(data.teamId); + s.manager.addSharedFolder(data, function (id) { + var send = data.teamId ? s.sendEvent : sendDriveEvent; + send('DRIVE_CHANGE', { path: ['drive', UserObject.FILES_DATA] }, clientId); cb(id); @@ -1650,20 +1715,17 @@ define([ Store.userObjectCommand = function (clientId, cmdData, cb) { if (!cmdData || !cmdData.cmd) { return; } //var data = cmdData.data; + var s = getStore(cmdData.teamId); var cb2 = function (data2) { - //var paths = data.paths || [data.path] || []; - //paths = paths.concat(data.newPath ? [data.newPath] : []); - //paths.forEach(function (p) { - sendDriveEvent('DRIVE_CHANGE', { - path: ['drive', UserObject.FILES_DATA] - //path: ['drive'].concat(p) - }, clientId); - //}); - onSync(function () { + var send = cmdData.teamId ? s.sendEvent : sendDriveEvent; + send('DRIVE_CHANGE', { + path: ['drive', UserObject.FILES_DATA] + }, clientId); + onSync(cmdData.teamId, function () { cb(data2); }); }; - store.manager.command(cmdData, cb2); + s.manager.command(cmdData, cb2); }; // Clients management diff --git a/www/common/outer/sharedfolder.js b/www/common/outer/sharedfolder.js index a628cc74e..b6ef17f7c 100644 --- a/www/common/outer/sharedfolder.js +++ b/www/common/outer/sharedfolder.js @@ -83,7 +83,7 @@ define([ }).nThen(function (waitFor) { Object.keys(shared).forEach(function (id) { var sf = shared[id]; - var rt = SF.load({ + SF.load({ network: network, store: store }, id, sf, waitFor()); diff --git a/www/common/outer/team.js b/www/common/outer/team.js index 9ce0e570f..86551c4eb 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -36,6 +36,7 @@ define([ // load manager with userObject // team.manager =... team.userObject = .... // load shared folders + // register event for these folders // ~resetPins for the team? // getPinLimit ctx.teams[id] = team; @@ -63,6 +64,9 @@ define([ lm.proxy.on('create', function () { }).on('ready', function () { var sendEvent = function (type, data, sender) { + type = type; + data = data; + sender = sender; // XXX emit UPDATE event to the inner iframe // don't send the event back to the sender // types are DRIVE_CHANGE, DRIVE_REMOVE and DRIVE_LOG diff --git a/www/common/outer/userObject.js b/www/common/outer/userObject.js index 4f235366a..c1ca650fd 100644 --- a/www/common/outer/userObject.js +++ b/www/common/outer/userObject.js @@ -317,10 +317,11 @@ define([ if (!loggedIn && !config.testMode) { // delete permanently spliceFileData(id); - return; + return true; } var paths = exp.findFile(id); exp.move(paths, [TRASH]); + return true; }; // REPLACE diff --git a/www/drive/main.js b/www/drive/main.js index 38e111883..273f6b153 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -40,7 +40,7 @@ define([ var hash = window.location.hash.slice(1); if (hash && Utils.LocalStore.isLoggedIn()) { // Add a shared folder! - Cryptpad.addSharedFolder(secret, function (id) { + Cryptpad.addSharedFolder(null, secret, function (id) { window.CryptPad_newSharedFolder = id; cb(); }); diff --git a/www/team/main.js b/www/team/main.js index 42e561812..b63c906d6 100644 --- a/www/team/main.js +++ b/www/team/main.js @@ -41,7 +41,7 @@ define([ var hash = window.location.hash.slice(1); if (hash && Utils.LocalStore.isLoggedIn()) { // Add a shared folder! - Cryptpad.addSharedFolder(secret, function (id) { + Cryptpad.addSharedFolder(teamId, secret, function (id) { window.CryptPad_newSharedFolder = id; cb(); }); @@ -58,13 +58,8 @@ define([ cb(); }; var addRpc = function (sframeChan, Cryptpad, Utils) { - sframeChan.on('EV_BURN_ANON_DRIVE', function () { - if (Utils.LocalStore.isLoggedIn()) { return; } - Utils.LocalStore.setFSHash(''); - Utils.LocalStore.clearThumbnail(); - window.location.reload(); - }); sframeChan.on('Q_DRIVE_USEROBJECT', function (data, cb) { + data.teamId = teamId; Cryptpad.userObjectCommand(data, cb); }); // XXX no drive restore in teams? you could restore old keys... @@ -115,9 +110,9 @@ define([ afterSecrets: afterSecrets, noHash: true, noRealtime: true, - driveEvents: true, + //driveEvents: true, addRpc: addRpc, - isDrive: true, + isDrive: true, // Used for history... }); }); });