diff --git a/customize.dist/src/less2/include/modals-ui-elements.less b/customize.dist/src/less2/include/modals-ui-elements.less index 62f815b21..855142a28 100644 --- a/customize.dist/src/less2/include/modals-ui-elements.less +++ b/customize.dist/src/less2/include/modals-ui-elements.less @@ -81,6 +81,9 @@ } } + .cp-access-margin-right { + margin-right: 5px !important; + } // teams invite modal .cp-teams-invite-block { diff --git a/www/common/inner/access.js b/www/common/inner/access.js index 0b55b8476..4473b8f30 100644 --- a/www/common/inner/access.js +++ b/www/common/inner/access.js @@ -634,7 +634,7 @@ define([ var priv = common.getMetadataMgr().getPrivateData(); var edPublic = priv.edPublic; var owned = false; - if (data.owners && data.owners.length) { + if (Array.isArray(data.owners) && data.owners.length) { if (data.owners.indexOf(edPublic) !== -1) { owned = true; } else { @@ -898,7 +898,7 @@ define([ // Request edit access if (data.roHref && !data.href) { - var requestButton = h('button.btn.btn-secondary.no-margin', + var requestButton = h('button.btn.btn-secondary.no-margin.cp-access-margin-right', Messages.requestEdit_button); var requestBlock = h('p', requestButton); var $requestBlock = $(requestBlock).hide(); @@ -930,6 +930,49 @@ define([ }); } + // XXX access_muteRequests + Messages.access_muteRequests = "Mute access requests for this pad"; // XXX + + // Mute access requests + var priv = common.getMetadataMgr().getPrivateData(); + var edPublic = priv.edPublic; + var owned = isOwned(common, data); + var canMute = data.mailbox && owned === true && ( + (typeof (data.mailbox) === "string" && data.owners[0] === edPublic) || + data.mailbox[edPublic]); + if (owned === true) { + var cbox = UI.createCheckbox('cp-access-mute', Messages.access_muteRequests, !canMute); + var $cbox = $(cbox); + var spinner = UI.makeSpinner($cbox); + var $checkbox = $cbox.find('input').on('change', function () { + if (spinner.getState()) { + $checkbox.prop('checked', !$checkbox.prop('checked')); + return; + } + spinner.spin(); + var val = $checkbox.is(':checked'); + sframeChan.query('Q_UPDATE_MAILBOX', { + metadata: data, + add: !val + }, function (err, res) { + err = err || (res && res.error); + if (err) { + spinner.hide(); + var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden + : Messages.error; + return void UI.warn(text); + } + spinner.done(); + UI.log(Messages.saved); + }); + }); + $cbox.find('.cp-checkmark-label').addClass('cp-access-margin-right'); + $cbox.find('.cp-checkmark-mark') + .after(h('span.fa.fa-bell-slash.cp-access-margin-right')); + content.push(h('p', cbox)); + } + + // Allow list var state = data.restricted ? Messages.allow_enabled : Messages.allow_disabled; content.push(h('label', Messages._getKey('allow_label', [state]))); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 56415dab6..10e915520 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1632,6 +1632,7 @@ define([ // If the owner was not is the pad metadata, check if it is a friend. // We'll contact the first owner for whom we know the mailbox + /* // XXX check mailbox in our contacts is not compatible with the new "mute pad" feature if (!owner && Array.isArray(owners)) { var friends = store.proxy.friends || {}; // If we have friends, check if an owner is one of them (with a mailbox) @@ -1648,6 +1649,7 @@ define([ }); } } + */ // If send is true, send the request to the owner. if (owner) { diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 9fce81594..4d2cd08f1 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1278,7 +1278,7 @@ define([ if (metadata) { return void todo(metadata); } Cryptpad.getPadMetadata({ - channel: secret.channel + channel: _secret.channel }, waitFor(function (obj) { obj = obj || {}; if (obj.error) { return; } @@ -1287,18 +1287,93 @@ define([ }).nThen(function () { // If we are just checking (send === false) and there is a mailbox field, cb state true // If there is no mailbox, we'll have to check if an owner is a friend in the worker + /* // XXX if (owner && !send) { return void cb({state: true}); } + */ + if (!send) { return void cb({state: Boolean(owner)}); } + Cryptpad.padRpc.requestAccess({ send: send, - channel: secret.channel, + channel: _secret.channel, owner: owner, owners: owners }, cb); }); }); + // Add or remove our mailbox from the list if we're an owner + sframeChan.on('Q_UPDATE_MAILBOX', function (data, cb) { + var metadata = data.metadata; + var add = data.add; + var _secret = secret; + if (metadata && (metadata.href || metadata.roHref)) { + var _parsed = Utils.Hash.parsePadUrl(metadata.href || metadata.roHref); + _secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password); + } + var crypto = Crypto.createEncryptor(_secret.keys); + nThen(function (waitFor) { + // If we already have metadata, use it, otherwise, try to get it + if (metadata) { return; } + + Cryptpad.getPadMetadata({ + channel: secret.channel + }, waitFor(function (obj) { + obj = obj || {}; + if (obj.error) { + waitFor.abort(); + return void cb(obj); + } + metadata = obj; + })); + }).nThen(function () { + // Get and maybe migrate the existing mailbox object + var owners = metadata.owners; + if (!Array.isArray(owners) || owners.indexOf(edPublic) === -1) { + waitFor.abort(); + return void cb({ error: 'INSUFFICIENT_PERMISSIONS' }); + } + + // Remove a mailbox + if (!add) { + // Old format: this is the mailbox of the first owner + if (typeof (metadata.mailbox) === "string" && metadata.mailbox) { + // Not our mailbox? abort + if (owners[0] !== edPublic) { + return void cb({ error: 'INSUFFICIENT_PERMISSIONS' }); + } + // Remove it + return void Cryptpad.setPadMetadata({ + channel: _secret.channel, + command: 'RM_MAILBOX', + value: [] + }, cb); + } else if (metadata.mailbox) { // New format + return void Cryptpad.setPadMetadata({ + channel: _secret.channel, + command: 'RM_MAILBOX', + value: [edPublic] + }, cb); + } + return void cb({ + error: 'NO_MAILBOX' + }); + } + // Add a mailbox + var toAdd = {}; + toAdd[edPublic] = crypto.encrypt(JSON.stringify({ + notifications: notifications, + curvePublic: curvePublic + })); + Cryptpad.setPadMetadata({ + channel: _secret.channel, + command: 'ADD_MAILBOX', + value: toAdd + }, cb); + }); + }); + sframeChan.on('EV_BURN_PAD', function (channel) { if (!burnAfterReading) { return; } Cryptpad.burnPad({