From 578042154ec38cb19b3280f08ba39f498cfff9a5 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 13 Mar 2020 17:39:16 +0100 Subject: [PATCH 01/29] Better reconnect after invalid lastKnownHash --- lib/hk-util.js | 4 +- www/common/outer/async-store.js | 23 +++++----- www/common/sframe-app-framework.js | 8 ++++ www/common/sframe-chainpad-netflux-inner.js | 49 +++++++++++++-------- www/common/toolbar3.js | 9 ++++ www/poll/inner.js | 5 +++ 6 files changed, 66 insertions(+), 32 deletions(-) diff --git a/lib/hk-util.js b/lib/hk-util.js index 3f0aed360..3a904367a 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -424,7 +424,7 @@ const storeMessage = function (Env, channel, msg, isCp, optionalMessageHash) { * it has a side-effect of filling the index cache if it's empty 1. if you provided a lastKnownHash and that message does not exist in the history: * either the client has made a mistake or the history they knew about no longer exists - * call back with EINVAL + * call back with EUNKNOWN 2. if you did not provide a lastKnownHash * and there are fewer than two checkpoints: * return 0 (read from the start of the file) @@ -460,7 +460,7 @@ const getHistoryOffset = (Env, channelName, lastKnownHash, cb) => { // QUESTION: does this mean mailboxes are causing the server to store too much stuff in memory? if (lastKnownHash && typeof(lkh) !== "number") { waitFor.abort(); - return void cb(new Error('EINVAL')); + return void cb(new Error('EUNKNOWN')); } // Since last 2 checkpoints diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 497babd22..13bdf26a1 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1499,6 +1499,15 @@ define([ return; } + var onError = function (err) { + console.warn(err); // XXX DEBUG + + channel.bcast("PAD_ERROR", err); + + // If this is a DELETED, EXPIRED or RESTRICTED pad, leave the channel + if (["EDELETED", "EEXPIRED", "ERESTRICTED"].indexOf(err.type) === -1) { return; } + Store.leavePad(null, data, function () {}); + }; var conf = { onReady: function (pad) { var padData = pad.metadata || {}; @@ -1522,18 +1531,8 @@ define([ onLeave: function (m) { channel.bcast("PAD_LEAVE", m); }, - onError: function (err) { - console.error(err); // XXX DEBUG - - channel.bcast("PAD_ERROR", err); - Store.leavePad(null, data, function () {}); - }, - onChannelError: function (err) { - console.warn(err); // XXX DEBUG - - channel.bcast("PAD_ERROR", err); - Store.leavePad(null, data, function () {}); - }, + onError: onError, + onChannelError: onError, onRejected: function (allowed, _cb) { var cb = Util.once(Util.mkAsync(_cb)); diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index f4567ee70..aa1d6f10f 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -325,6 +325,11 @@ define([ UI.updateLoadingProgress({ state: -1 }, false); + if (toolbar) { + // Check if we have a new chainpad instance + toolbar.resetChainpad(cpNfInner.chainpad); + } + var newPad = false; if (newContentStr === '') { newPad = true; } @@ -364,6 +369,9 @@ define([ }).nThen(function () { stateChange(STATE.READY); firstConnection = false; + + oldContent = undefined; + if (!readOnly) { onLocal(); } evOnReady.fire(newPad); diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index 65feb67da..a55463bb7 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -49,24 +49,29 @@ define([ config = undefined; var evPatchSent = Util.mkEvent(); + var chainpad; - var chainpad = ChainPad.create({ - userName: userName, - initialState: initialState, - patchTransformer: patchTransformer, - validateContent: validateContent, - avgSyncMilliseconds: avgSyncMilliseconds, - logLevel: logLevel - }); - chainpad.onMessage(function(message, cb) { - sframeChan.query('Q_RT_MESSAGE', message, function (err) { - if (!err) { evPatchSent.fire(); } - cb(err); + var makeChainPad = function () { + var _chainpad = ChainPad.create({ + userName: userName, + initialState: initialState, + patchTransformer: patchTransformer, + validateContent: validateContent, + avgSyncMilliseconds: avgSyncMilliseconds, + logLevel: logLevel }); - }); - chainpad.onPatch(function () { - onRemote({ realtime: chainpad }); - }); + _chainpad.onMessage(function(message, cb) { + sframeChan.query('Q_RT_MESSAGE', message, function (err) { + if (!err) { evPatchSent.fire(); } + cb(err); + }); + }); + _chainpad.onPatch(function () { + onRemote({ realtime: chainpad }); + }); + return _chainpad; + }; + chainpad = makeChainPad(); var myID; var isReady = false; @@ -96,6 +101,11 @@ define([ sframeChan.on('EV_RT_ERROR', function (err) { isReady = false; chainpad.abort(); + if (err.type === 'EUNKNOWN') { // XXX + // Recoverable error: make a new chainpad + chainpad = makeChainPad(); + return; + } onError(err); }); sframeChan.on('EV_RT_CONNECT', function (content) { @@ -149,15 +159,18 @@ define([ }); }; - return Object.freeze({ + var cpNfInner = { getMyID: function () { return myID; }, metadataMgr: metadataMgr, whenRealtimeSyncs: whenRealtimeSyncs, onInfiniteSpinner: evInfiniteSpinner.reg, onPatchSent: evPatchSent.reg, offPatchSent: evPatchSent.unreg, - chainpad: chainpad, + }; + cpNfInner.__defineGetter__("chainpad", function () { + return chainpad; }); + return Object.freeze(cpNfInner); }; return Object.freeze(module.exports); }); diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js index ef42fcaea..2473a0517 100644 --- a/www/common/toolbar3.js +++ b/www/common/toolbar3.js @@ -1331,6 +1331,15 @@ MessengerUI, Messages) { showColors = true; }; + // If we had to create a new chainpad instance, reset the one used in the toolbar + toolbar.resetChainpad = function (chainpad) { + if (config.realtime !== chainpad) { + config.realtime = chainpad; + config.realtime.onPatch(ks(toolbar, config)); + config.realtime.onMessage(ks(toolbar, config, true)); + } + }; + // On log out, remove permanently the realtime elements of the toolbar Common.onLogout(function () { failed(); diff --git a/www/poll/inner.js b/www/poll/inner.js index 6c8e77482..38ed1b79c 100644 --- a/www/poll/inner.js +++ b/www/poll/inner.js @@ -794,6 +794,11 @@ define([ var userDoc = JSON.stringify(proxy); if (userDoc === "" || userDoc === "{}") { isNew = true; } + if (APP.toolbar && APP.rt.cpCnInner) { + // Check if we have a new chainpad instance + APP.toolbar.resetChainpad(APP.rt.cpCnInner.chainpad); + } + if (!isNew) { if (proxy.info) { // Migration From 27c12911823d98a4788834808230669361a30044 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 16 Mar 2020 11:19:04 +0100 Subject: [PATCH 02/29] Fix duplicate text bug on reconnect or ACK timeout --- www/common/cryptpad-common.js | 3 ++- www/common/outer/worker-channel.js | 11 +++++++---- www/common/sframe-chainpad-netflux-inner.js | 3 ++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 565a67c42..ef9fc7473 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -879,7 +879,8 @@ define([ postMessage("LEAVE_PAD", data, cb); }; pad.sendPadMsg = function (data, cb) { - postMessage("SEND_PAD_MSG", data, cb); + // -1 ==> no timeout, we may receive the callback only when we reconnect + postMessage("SEND_PAD_MSG", data, cb, { timeout: -1 }); }; pad.onReadyEvent = Util.mkEvent(); pad.onMessageEvent = Util.mkEvent(); diff --git a/www/common/outer/worker-channel.js b/www/common/outer/worker-channel.js index 42b91b802..545ce435b 100644 --- a/www/common/outer/worker-channel.js +++ b/www/common/outer/worker-channel.js @@ -45,10 +45,13 @@ define([ var txid = mkTxid(); opts = opts || {}; var to = opts.timeout || 30000; - var timeout = setTimeout(function () { - delete queries[txid]; - cb('TIMEOUT'); - }, to); + var timeout; + if (to > 0) { + timeout = setTimeout(function () { + delete queries[txid]; + cb('TIMEOUT'); + }, to); + } acks[txid] = function (err) { clearTimeout(timeout); delete acks[txid]; diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index 65feb67da..c786f7009 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -59,10 +59,11 @@ define([ logLevel: logLevel }); chainpad.onMessage(function(message, cb) { + // -1 ==> no timeout, we may receive the callback only when we reconnect sframeChan.query('Q_RT_MESSAGE', message, function (err) { if (!err) { evPatchSent.fire(); } cb(err); - }); + }, { timeout: -1 }); }); chainpad.onPatch(function () { onRemote({ realtime: chainpad }); From d69ad7c0e6d84c46c1febeae4439ee93043bd672 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 16 Mar 2020 12:06:18 +0100 Subject: [PATCH 03/29] Send the error to chainpad when a patch is not sent --- www/common/outer/async-store.js | 5 ++++- www/common/sframe-chainpad-netflux-inner.js | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 497babd22..bb30014d1 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1599,7 +1599,10 @@ define([ onConnect: function (wc, sendMessage) { channel.sendMessage = function (msg, cId, cb) { // Send to server - sendMessage(msg, function () { + sendMessage(msg, function (err) { + if (err) { + return void cb({ error: err }); + } // Broadcast to other tabs channel.pushHistory(CpNetflux.removeCp(msg), /^cp\|/.test(msg)); channel.bcast("PAD_MESSAGE", { diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index c786f7009..f7d0ada00 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -60,7 +60,8 @@ define([ }); chainpad.onMessage(function(message, cb) { // -1 ==> no timeout, we may receive the callback only when we reconnect - sframeChan.query('Q_RT_MESSAGE', message, function (err) { + sframeChan.query('Q_RT_MESSAGE', message, function (_err, obj) { + var err = _err || (obj && obj.error); if (!err) { evPatchSent.fire(); } cb(err); }, { timeout: -1 }); From dcfd9c5a7385df313f963aaf0f7b03cae88361af Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Mar 2020 16:33:45 +0100 Subject: [PATCH 04/29] Smaller palette in whiteboard --- www/whiteboard/app-whiteboard.less | 15 ++++++++++----- www/whiteboard/inner.js | 6 +++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/www/whiteboard/app-whiteboard.less b/www/whiteboard/app-whiteboard.less index 0ac575df6..7bfef2b42 100644 --- a/www/whiteboard/app-whiteboard.less +++ b/www/whiteboard/app-whiteboard.less @@ -123,23 +123,28 @@ .middle; z-index: 100; background: white; - display: flex; justify-content: space-between; + display: flex; + flex-shrink: 1; + flex-wrap: wrap; + min-width: 0; + max-width: 300px; padding: 10px; span.cp-app-whiteboard-palette-color { - height: 4vw; - width: 4vw; + height: 30px; + width: 30px; display: block; margin: 5px; - border: 1px solid black; + border: 1px solid #bbb; vertical-align: top; border-radius: 50%; + cursor: pointer; transition: transform 0.1s; &:hover { - transform: scale(1.2); + transform: scale(1.1); } } } diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js index 77057f505..cd87512b8 100644 --- a/www/whiteboard/inner.js +++ b/www/whiteboard/inner.js @@ -511,13 +511,13 @@ define([ }), h('span#cp-app-whiteboard-opacity-val', '100%') ]), - h('span.cp-app-whiteboard-selected.cp-app-whiteboard-unselectable', [ + h('div.cp-app-whiteboard-selected.cp-app-whiteboard-unselectable', [ h('img', { title: Messages.canvas_currentBrush }) - ]) + ]), + UI.setHTML(h('div#cp-app-whiteboard-colors'), ' '), ]), - UI.setHTML(h('div#cp-app-whiteboard-colors'), ' '), h('div#cp-app-whiteboard-cursors', { style: { display: 'none', From f780087ac77e0a9af5a05735fffab60ac585fc04 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Mar 2020 17:23:29 +0100 Subject: [PATCH 05/29] Better UI for whiteboard controls --- www/whiteboard/app-whiteboard.less | 67 +++++++++++++-------- www/whiteboard/inner.js | 96 ++++++++++++++++++++---------- 2 files changed, 106 insertions(+), 57 deletions(-) diff --git a/www/whiteboard/app-whiteboard.less b/www/whiteboard/app-whiteboard.less index f035c6efa..a4237cbdf 100644 --- a/www/whiteboard/app-whiteboard.less +++ b/www/whiteboard/app-whiteboard.less @@ -1,5 +1,6 @@ @import (reference) '../../customize/src/less2/include/tools.less'; @import (reference) "../../customize/src/less2/include/framework.less"; +@import (reference) "../../customize/src/less2/include/buttons.less"; &.cp-app-whiteboard { @@ -36,6 +37,7 @@ flex: 1; display: flex; overflow: auto; + flex-flow: column; } // created in the html @@ -74,6 +76,8 @@ padding: 10px; + .buttons_main(); + & > * + * { margin: 0; margin-left: 1em; @@ -82,10 +86,27 @@ #cp-app-whiteboard-width, #cp-app-whiteboard-opacity { .middle; } - #cp-app-whiteboard-clear, #cp-app-whiteboard-delete, #cp-app-whiteboard-toggledraw { + + #cp-app-whiteboard-clear { display: inline; vertical-align: middle; } + #cp-app-whiteboard-delete { + min-width: 40px; + } + .cp-whiteboard-type { + button { + min-width: 40px; + text-align: center; + &:not(:first-child) { + margin-left: -1px; + } + &.btn-primary:hover { + cursor: default; + } + } + } + .cp-app-whiteboard-selected { display: flex; align-items: center; @@ -96,27 +117,30 @@ height: 100px; } - .cp-app-whiteboard-range-group { + .cp-whiteboard-brush { display: flex; flex-direction: column; - position: relative; - - input[type="range"] { - background-color: inherit; - } - - & > span { - cursor: default; - position: absolute; - top: 0; - right: 0; - } - } - .cp-app-whiteboard-range-group:first-of-type { margin-left: 2em; - } - .cp-app-whiteboard-range-group:last-of-type { margin-right: 1em; + .cp-app-whiteboard-range-group { + display: flex; + align-items: center; + position: relative; + + label { + margin-bottom: 0; + margin-right: 5px; + } + + input[type="range"] { + background-color: inherit; + margin-right: 5px; + } + + & > span { + cursor: default; + } + } } } @@ -125,14 +149,9 @@ .middle; z-index: 100; background: white; - justify-content: space-between; display: flex; - flex-shrink: 1; flex-wrap: wrap; - min-width: 0; - max-width: 300px; - - padding: 10px; + max-width: 320px; span.cp-app-whiteboard-palette-color { height: 30px; diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js index 5083864b3..313e32b2d 100644 --- a/www/whiteboard/inner.js +++ b/www/whiteboard/inner.js @@ -50,7 +50,9 @@ define([ var $widthLabel = $('label[for="cp-app-whiteboard-width"]'); var $opacity = $('#cp-app-whiteboard-opacity'); var $opacityLabel = $('label[for="cp-app-whiteboard-opacity"]'); - var $toggle = $('#cp-app-whiteboard-toggledraw'); + var $type = $('.cp-whiteboard-type'); + var $brush = $('.cp-whiteboard-type .brush'); + var $move = $('.cp-whiteboard-type .move'); var $deleteButton = $('#cp-app-whiteboard-delete'); var metadataMgr = framework._.cpNfInner.metadataMgr; @@ -97,7 +99,7 @@ define([ var updateBrushWidth = function () { var val = $width.val(); canvas.freeDrawingBrush.width = Number(val); - $widthLabel.text(Messages._getKey("canvas_widthLabel", [val])); + $widthLabel.text(Messages._getKey("canvas_widthLabel", [''])); $('#cp-app-whiteboard-width-val').text(val + 'px'); createCursor(); }; @@ -108,7 +110,7 @@ define([ var val = $opacity.val(); brush.opacity = Number(val); canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity); - $opacityLabel.text(Messages._getKey("canvas_opacityLabel", [val])); + $opacityLabel.text(Messages._getKey("canvas_opacityLabel", [''])); $('#cp-app-whiteboard-opacity-val').text((Number(val) * 100) + '%'); createCursor(); }; @@ -116,17 +118,27 @@ define([ $opacity.on('change', updateBrushOpacity); APP.draw = true; - var toggleDrawMode = function () { + $brush.click(function () { + if (APP.draw) { return; } canvas.deactivateAll().renderAll(); - APP.draw = !APP.draw; + APP.draw = true; canvas.isDrawingMode = APP.draw; - $toggle.text(APP.draw ? Messages.canvas_disable : Messages.canvas_enable); - if (APP.draw) { $deleteButton.hide(); } - else { $deleteButton.show(); } - }; - $toggle.click(toggleDrawMode); + $type.find('button').removeClass('btn-primary'); + $brush.addClass('btn-primary'); + $deleteButton.prop('disabled', 'disabled'); + }); + $move.click(function () { + if (!APP.draw) { return; } + canvas.deactivateAll().renderAll(); + APP.draw = false; + canvas.isDrawingMode = APP.draw; + $type.find('button').removeClass('btn-primary'); + $move.addClass('btn-primary'); + $deleteButton.prop('disabled', ''); + }); var deleteSelection = function () { + if (APP.draw) { return; } if (canvas.getActiveObject()) { canvas.getActiveObject().remove(); } @@ -211,6 +223,7 @@ define([ if (first || Sortify(palette) !== Sortify(newPalette)) { palette = newPalette; $colors.html(''); + $colors.css('width', (palette.length * 20)+'px'); palette.forEach(addColorToPalette); first = false; } @@ -494,6 +507,9 @@ define([ framework.start(); }; + Messages.canvas_brush = "Brush"; // XXX + Messages.canvas_select = "Select"; // XXX + var initialContent = function () { return [ h('div#cp-toolbar.cp-toolbar-container'), @@ -509,34 +525,48 @@ define([ } }, [ h('button#cp-app-whiteboard-clear.btn.btn-danger', Messages.canvas_clear), ' ', + h('div.cp-whiteboard-type', [ + h('button.brush.fa.fa-paint-brush.btn-primary', {title: Messages.canvas_brush}), + h('button.move.fa.fa-arrows', {title: Messages.canvas_select}), + ]), + h('button.fa.fa-trash#cp-app-whiteboard-delete', { + disabled: 'disabled', + title: Messages.canvas_delete + }), + /* + h('button#cp-app-whiteboard-toggledraw.btn.btn-secondary', Messages.canvas_disable), h('button#cp-app-whiteboard-toggledraw.btn.btn-secondary', Messages.canvas_disable), h('button#cp-app-whiteboard-delete.btn.btn-secondary', { style: { display: 'none', } - }, Messages.canvas_delete), - h('div.cp-app-whiteboard-range-group', [ - h('label', { - 'for': 'cp-app-whiteboard-width' - }, Messages.canvas_width), - h('input#cp-app-whiteboard-width', { - type: 'range', - min: "1", - max: "100" - }), - h('span#cp-app-whiteboard-width-val', '5px') - ]), - h('div.cp-app-whiteboard-range-group', [ - h('label', { - 'for': 'cp-app-whiteboard-opacity', - }, Messages.canvas_opacity), - h('input#cp-app-whiteboard-opacity', { - type: 'range', - min: "0.1", - max: "1", - step: "0.1" - }), - h('span#cp-app-whiteboard-opacity-val', '100%') + }, Messages.canvas_delete),*/ + h('div.cp-whiteboard-brush', [ + h('div.cp-app-whiteboard-range-group', [ + h('label', { + 'for': 'cp-app-whiteboard-width' + }, Messages.canvas_width), + h('input#cp-app-whiteboard-width', { + type: 'range', + value: "20", + min: "1", + max: "100" + }), + h('span#cp-app-whiteboard-width-val', '5px') + ]), + h('div.cp-app-whiteboard-range-group', [ + h('label', { + 'for': 'cp-app-whiteboard-opacity', + }, Messages.canvas_opacity), + h('input#cp-app-whiteboard-opacity', { + type: 'range', + value: "1", + min: "0.1", + max: "1", + step: "0.1" + }), + h('span#cp-app-whiteboard-opacity-val', '100%') + ]), ]), h('div.cp-app-whiteboard-selected.cp-app-whiteboard-unselectable', [ h('img', { From d7b28767112f9e15b2416aa2f28c19081e856bdf Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 25 Mar 2020 17:37:52 +0100 Subject: [PATCH 06/29] Fix UI for brush settings in whiteboard --- www/whiteboard/app-whiteboard.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/www/whiteboard/app-whiteboard.less b/www/whiteboard/app-whiteboard.less index a4237cbdf..210da403b 100644 --- a/www/whiteboard/app-whiteboard.less +++ b/www/whiteboard/app-whiteboard.less @@ -130,14 +130,18 @@ label { margin-bottom: 0; margin-right: 5px; + flex: 1; } input[type="range"] { background-color: inherit; margin-right: 5px; + width: 150px; + padding: 0; } & > span { + width: 50px; cursor: default; } } From 01cdac21cc9fcb17bfc9af319de54519c427851a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Benqu=C3=A9?= Date: Wed, 25 Mar 2020 17:07:31 +0000 Subject: [PATCH 07/29] remove // XXX related to Whiteboard keys --- www/whiteboard/inner.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js index 313e32b2d..650fc1251 100644 --- a/www/whiteboard/inner.js +++ b/www/whiteboard/inner.js @@ -507,8 +507,6 @@ define([ framework.start(); }; - Messages.canvas_brush = "Brush"; // XXX - Messages.canvas_select = "Select"; // XXX var initialContent = function () { return [ From ed722f7385c2fee2007bce739c5514914b63d918 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 26 Mar 2020 13:12:41 +0100 Subject: [PATCH 08/29] Fix properties and access modal being opened multiple times at once --- www/common/common-ui-elements.js | 859 +------------------------------ www/common/drive-ui.js | 4 +- www/common/inner/access.js | 234 ++------- www/common/inner/common-modal.js | 156 ++++++ www/common/inner/properties.js | 181 +++++++ 5 files changed, 396 insertions(+), 1038 deletions(-) create mode 100644 www/common/inner/common-modal.js create mode 100644 www/common/inner/properties.js diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index fefbba6e1..880c4c354 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -99,857 +99,6 @@ define([ }); }; }; -/* - var getPropertiesData = function (common, cb) { - var data = {}; - NThen(function (waitFor) { - var base = common.getMetadataMgr().getPrivateData().origin; - common.getPadAttribute('', waitFor(function (err, val) { - if (err || !val) { - waitFor.abort(); - return void cb(err || 'EEMPTY'); - } - if (!val.fileType) { - delete val.owners; - delete val.expire; - } - Util.extend(data, val); - if (data.href) { data.href = base + data.href; } - if (data.roHref) { data.roHref = base + data.roHref; } - })); - common.getPadMetadata(null, waitFor(function (obj) { - if (obj && obj.error) { return; } - data.owners = obj.owners; - data.expire = obj.expire; - data.pending_owners = obj.pending_owners; - })); - }).nThen(function () { - cb(void 0, data); - }); - }; -*/ - var getPropertiesData = function (common, opts, cb) { - opts = opts || {}; - var data = {}; - NThen(function (waitFor) { - var base = common.getMetadataMgr().getPrivateData().origin; - common.getPadAttribute('', waitFor(function (err, val) { - if (err || !val) { - waitFor.abort(); - return void cb(err || 'EEMPTY'); - } - if (!val.fileType) { - delete val.owners; - delete val.expire; - } - Util.extend(data, val); - if (data.href) { data.href = base + data.href; } - if (data.roHref) { data.roHref = base + data.roHref; } - }), opts.href); - - // If this is a file, don't try to look for metadata - if (opts.channel && opts.channel.length > 34) { return; } - common.getPadMetadata({ - channel: opts.channel // optional, fallback to current pad - }, waitFor(function (obj) { - if (obj && obj.error) { return; } - data.owners = obj.owners; - data.expire = obj.expire; - data.pending_owners = obj.pending_owners; - })); - }).nThen(function () { - cb(void 0, data); - }); - }; - - -/* - var createOwnerModal = function (common, data) { - var friends = common.getFriends(true); - var sframeChan = common.getSframeChannel(); - var priv = common.getMetadataMgr().getPrivateData(); - var user = common.getMetadataMgr().getUserData(); - var edPublic = priv.edPublic; - var channel = data.channel; - var owners = data.owners || []; - var pending_owners = data.pending_owners || []; - var teams = priv.teams; - var teamOwner = data.teamId; - - var redrawAll = function () {}; - - var div1 = h('div.cp-usergrid-user.cp-share-column.cp-ownership'); - var div2 = h('div.cp-usergrid-user.cp-share-column.cp-ownership'); - var $div1 = $(div1); - var $div2 = $(div2); - - // Remove owner column - var drawRemove = function (pending) { - var _owners = {}; - var o = (pending ? pending_owners : owners) || []; - o.forEach(function (ed) { - var f; - Object.keys(friends).some(function (c) { - if (friends[c].edPublic === ed) { - f = friends[c]; - return true; - } - }); - Object.keys(teams).some(function (id) { - if (teams[id].edPublic === ed) { - f = teams[id]; - f.teamId = id; - } - }); - if (ed === edPublic) { - f = f || user; - if (f.name) { f.edPublic = edPublic; } - } - _owners[ed] = f || { - displayName: Messages._getKey('owner_unknownUser', [ed]), - edPublic: ed, - }; - }); - var msg = pending ? Messages.owner_removePendingText - : Messages.owner_removeText; - var removeCol = UIElements.getUserGrid(msg, { - common: common, - large: true, - data: _owners, - noFilter: true - }, function () { - }); - var $div = $(removeCol.div); - // When clicking on the remove button, we check the selected users. - // If you try to remove yourself, we'll display an additional warning message - var btnMsg = pending ? Messages.owner_removePendingButton : Messages.owner_removeButton; - var removeButton = h('button.no-margin', btnMsg); - $(removeButton).click(function () { - // Check selection - var $sel = $div.find('.cp-usergrid-user.cp-selected'); - var sel = $sel.toArray(); - if (!sel.length) { return; } - var me = false; - var toRemove = sel.map(function (el) { - var ed = $(el).attr('data-ed'); - if (!ed) { return; } - if (teamOwner && teams[teamOwner] && teams[teamOwner].edPublic === ed) { me = true; } - if (ed === edPublic && !teamOwner) { me = true; } - return ed; - }).filter(function (x) { return x; }); - NThen(function (waitFor) { - var msg = me ? Messages.owner_removeMeConfirm : Messages.owner_removeConfirm; - UI.confirm(msg, waitFor(function (yes) { - if (!yes) { - waitFor.abort(); - return; - } - })); - }).nThen(function (waitFor) { - // Send the command - sframeChan.query('Q_SET_PAD_METADATA', { - channel: channel, - command: pending ? 'RM_PENDING_OWNERS' : 'RM_OWNERS', - value: toRemove, - teamId: teamOwner - }, waitFor(function (err, res) { - err = err || (res && res.error); - if (err) { - waitFor.abort(); - redrawAll(); - var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden - : Messages.error; - return void UI.warn(text); - } - UI.log(Messages.saved); - })); - }).nThen(function (waitFor) { - sel.forEach(function (el) { - var curve = $(el).attr('data-curve'); - var friend = curve === user.curvePublic ? user : friends[curve]; - if (!friend) { return; } - common.mailbox.sendTo("RM_OWNER", { - channel: channel, - title: data.title, - pending: pending - }, { - channel: friend.notifications, - curvePublic: friend.curvePublic - }, waitFor()); - }); - }).nThen(function () { - redrawAll(); - }); - }); - $div.append(h('p', removeButton)); - return $div; - }; - - // Add owners column - var drawAdd = function () { - var $div = $(h('div.cp-share-column')); - var _friends = JSON.parse(JSON.stringify(friends)); - Object.keys(_friends).forEach(function (curve) { - if (owners.indexOf(_friends[curve].edPublic) !== -1 || - pending_owners.indexOf(_friends[curve].edPublic) !== -1 || - !_friends[curve].notifications) { - delete _friends[curve]; - } - }); - var addCol = UIElements.getUserGrid(Messages.owner_addText, { - common: common, - large: true, - data: _friends - }, function () { - //console.log(arguments); - }); - $div.append(addCol.div); - - var teamsData = Util.tryParse(JSON.stringify(priv.teams)) || {}; - Object.keys(teamsData).forEach(function (id) { - var t = teamsData[id]; - t.teamId = id; - if (owners.indexOf(t.edPublic) !== -1 || pending_owners.indexOf(t.edPublic) !== -1) { - delete teamsData[id]; - } - }); - var teamsList = UIElements.getUserGrid(Messages.owner_addTeamText, { - common: common, - large: true, - noFilter: true, - data: teamsData - }, function () {}); - $div.append(teamsList.div); - - // When clicking on the add button, we get the selected users. - var addButton = h('button.no-margin', Messages.owner_addButton); - $(addButton).click(function () { - // Check selection - var $sel = $div.find('.cp-usergrid-user.cp-selected'); - var sel = $sel.toArray(); - if (!sel.length) { return; } - var toAdd = sel.map(function (el) { - var curve = $(el).attr('data-curve'); - // If the pad is woned by a team, we can transfer ownership to ourselves - if (curve === user.curvePublic && teamOwner) { return priv.edPublic; } - var friend = friends[curve]; - if (!friend) { return; } - return friend.edPublic; - }).filter(function (x) { return x; }); - var toAddTeams = sel.map(function (el) { - var team = teamsData[$(el).attr('data-teamid')]; - if (!team || !team.edPublic) { return; } - return { - edPublic: team.edPublic, - id: $(el).attr('data-teamid') - }; - }).filter(function (x) { return x; }); - - NThen(function (waitFor) { - var msg = Messages.owner_addConfirm; - UI.confirm(msg, waitFor(function (yes) { - if (!yes) { - waitFor.abort(); - return; - } - })); - }).nThen(function (waitFor) { - // Add one of our teams as an owner - if (toAddTeams.length) { - // Send the command - sframeChan.query('Q_SET_PAD_METADATA', { - channel: channel, - command: 'ADD_OWNERS', - value: toAddTeams.map(function (obj) { return obj.edPublic; }), - teamId: teamOwner - }, waitFor(function (err, res) { - err = err || (res && res.error); - if (err) { - waitFor.abort(); - redrawAll(); - var text = err === "INSUFFICIENT_PERMISSIONS" ? - Messages.fm_forbidden : Messages.error; - return void UI.warn(text); - } - var isTemplate = priv.isTemplate || data.isTemplate; - toAddTeams.forEach(function (obj) { - sframeChan.query('Q_STORE_IN_TEAM', { - href: data.href || data.rohref, - password: data.password, - path: isTemplate ? ['template'] : undefined, - title: data.title || '', - teamId: obj.id - }, waitFor(function (err) { - if (err) { return void console.error(err); } - })); - }); - })); - } - }).nThen(function (waitFor) { - // Offer ownership to a friend - if (toAdd.length) { - // Send the command - sframeChan.query('Q_SET_PAD_METADATA', { - channel: channel, - command: 'ADD_PENDING_OWNERS', - value: toAdd, - teamId: teamOwner - }, waitFor(function (err, res) { - err = err || (res && res.error); - if (err) { - waitFor.abort(); - redrawAll(); - var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden - : Messages.error; - return void UI.warn(text); - } - })); - } - }).nThen(function (waitFor) { - sel.forEach(function (el) { - var curve = $(el).attr('data-curve'); - var friend = curve === user.curvePublic ? user : friends[curve]; - if (!friend) { return; } - common.mailbox.sendTo("ADD_OWNER", { - channel: channel, - href: data.href, - password: data.password, - title: data.title - }, { - channel: friend.notifications, - curvePublic: friend.curvePublic - }, waitFor()); - }); - }).nThen(function () { - redrawAll(); - UI.log(Messages.saved); - }); - }); - $div.append(h('p', addButton)); - return $div; - }; - - redrawAll = function (md) { - var todo = function (obj) { - if (obj && obj.error) { return; } - owners = obj.owners || []; - pending_owners = obj.pending_owners || []; - $div1.empty(); - $div2.empty(); - $div1.append(drawRemove(false)).append(drawRemove(true)); - $div2.append(drawAdd()); - }; - - if (md) { return void todo(md); } - common.getPadMetadata({ - channel: data.channel - }, todo); - }; - - $div1.append(drawRemove(false)).append(drawRemove(true)); - $div2.append(drawAdd()); - - var handler = sframeChan.on('EV_RT_METADATA', function (md) { - if (!$div1.length) { - return void handler.stop(); - } - owners = md.owners || []; - pending_owners = md.pending_owners || []; - redrawAll(md); - }); - - // Create modal - var link = h('div.cp-share-columns', [ - div1, - div2 - // drawRemove()[0], - //drawAdd()[0] - ]); - var linkButtons = [{ - className: 'cancel', - name: Messages.filePicker_close, - onClick: function () {}, - keys: [27] - }]; - return UI.dialog.customModal(link, {buttons: linkButtons}); - }; -*/ -/* - var getRightsProperties = function (common, data, cb) { - var $div = $('
'); - if (!data) { return void cb(void 0, $div); } - - var draw = function () { - var $d = $('
'); - var priv = common.getMetadataMgr().getPrivateData(); - var user = common.getMetadataMgr().getUserData(); - var edPublic = priv.edPublic; - var owned = false; - var _owners = {}; - if (data.owners && data.owners.length) { - if (data.owners.indexOf(edPublic) !== -1) { - owned = true; - } else { - Object.keys(priv.teams || {}).some(function (id) { - var team = priv.teams[id] || {}; - if (team.viewer) { return; } - if (data.owners.indexOf(team.edPublic) === -1) { return; } - owned = Number(id); - return true; - }); - } - var strangers = 0; - data.owners.forEach(function (ed) { - // If a friend is an owner, add their name to the list - // otherwise, increment the list of strangers - - // Our edPublic? print "Yourself" - if (ed === edPublic) { - _owners[ed] = { - selected: true, - name: user.name, - avatar: user.avatar - }; - return; - } - // One of our teams? print the team name - if (Object.keys(priv.teams || {}).some(function (id) { - var team = priv.teams[id] || {}; - if (team.edPublic !== ed) { return; } - _owners[ed] = { - name: team.name, - avatar: team.avatar - }; - return true; - })) { - return; - } - // One of our friends? print the friend name - if (Object.keys(priv.friends || {}).some(function (c) { - var friend = priv.friends[c] || {}; - if (friend.edPublic !== ed || c === 'me') { return; } - _owners[friend.edPublic] = { - name: friend.displayName, - avatar: friend.avatar - }; - return true; - })) { - return; - } - // Otherwise it's a stranger - strangers++; - }); - if (strangers) { - _owners['stangers'] = { - name: Messages._getKey('properties_unknownUser', [strangers]), - }; - } - } - var _ownersGrid = UIElements.getUserGrid(Messages.creation_owners, { - common: common, - noSelect: true, - data: _owners, - large: true - }, function () {}); - if (_ownersGrid && Object.keys(_owners).length) { - $d.append(_ownersGrid.div); - } else { - $d.append([ - h('label', Messages.creation_owners), - ]); - $d.append(UI.dialog.selectable(Messages.creation_noOwner, { - id: 'cp-app-prop-owners', - })); - - } - - var parsed; - if (data.href || data.roHref) { - parsed = Hash.parsePadUrl(data.href || data.roHref); - } - if (owned && parsed.hashData.type === 'pad') { - var manageOwners = h('button.no-margin', Messages.owner_openModalButton); - $(manageOwners).click(function () { - data.teamId = typeof(owned) !== "boolean" ? owned : undefined; - var modal = createOwnerModal(common, data); - UI.openCustomModal(modal, { - wide: true, - }); - }); - $d.append(h('p', manageOwners)); - } - - if (!data.noExpiration) { - var expire = Messages.creation_expireFalse; - if (data.expire && typeof (data.expire) === "number") { - expire = new Date(data.expire).toLocaleString(); - } - $('