diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index e8c0cce4c..4e33b83d0 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -3013,6 +3013,7 @@ define([ // ACCEPT sframeChan.query('Q_SET_PAD_METADATA', { channel: msg.content.channel, + channels: msg.content.channels, command: 'ADD_OWNERS', value: [priv.edPublic] }, function (err, res) { @@ -3062,6 +3063,7 @@ define([ // Remove yourself from the pending owners sframeChan.query('Q_SET_PAD_METADATA', { channel: msg.content.channel, + channels: msg.content.channels, command: 'RM_PENDING_OWNERS', value: [priv.edPublic] }, function (err, res) { @@ -3078,6 +3080,7 @@ define([ // Remove yourself from the pending owners sframeChan.query('Q_SET_PAD_METADATA', { channel: msg.content.channel, + channels: msg.content.channels, command: 'RM_PENDING_OWNERS', value: [priv.edPublic] }, function (err, res) { diff --git a/www/common/inner/access.js b/www/common/inner/access.js index 693b8d236..a1d2c37a1 100644 --- a/www/common/inner/access.js +++ b/www/common/inner/access.js @@ -32,6 +32,12 @@ define([ var teamOwner = data.teamId; var title = opts.title; + var p = priv.propChannels; + var otherChan; + if (p && p.answersChannel) { + otherChan = [p.answersChannel]; + } + opts = opts || {}; var redrawAll = function () {}; @@ -255,6 +261,7 @@ define([ // Send the command sframeChan.query('Q_SET_PAD_METADATA', { channel: channel, + channels: otherChan, command: 'ADD_OWNERS', value: toAddTeams.map(function (obj) { return obj.edPublic; }), teamId: teamOwner @@ -290,6 +297,7 @@ define([ // Send the command sframeChan.query('Q_SET_PAD_METADATA', { channel: channel, + channels: otherChan, command: 'ADD_PENDING_OWNERS', value: toAdd, teamId: teamOwner @@ -310,6 +318,7 @@ define([ // Send the command sframeChan.query('Q_SET_PAD_METADATA', { channel: channel, + channels: otherChan, command: 'ADD_OWNERS', value: [priv.edPublic], teamId: teamOwner @@ -338,6 +347,7 @@ define([ if (!friend) { return; } common.mailbox.sendTo("ADD_OWNER", { channel: channel, + channels: otherChan, href: href, calendar: opts.calendar, password: data.password || priv.password, @@ -417,6 +427,12 @@ define([ var allowed = data.allowed || []; var teamOwner = data.teamId; + var p = priv.propChannels; + var otherChan; + if (p && p.answersChannel) { + otherChan = [p.answersChannel]; + } + var redrawAll = function () {}; var addBtn = h('button.btn.btn-primary.cp-access-add', [h('i.fa.fa-arrow-left'), h('i.fa.fa-arrow-up')]); @@ -495,6 +511,7 @@ define([ // Send the command sframeChan.query('Q_SET_PAD_METADATA', { channel: channel, + channels: otherChan, command: 'RM_ALLOWED', value: [ed], teamId: teamOwner @@ -524,6 +541,7 @@ define([ var val = $checkbox.is(':checked'); sframeChan.query('Q_SET_PAD_METADATA', { channel: channel, + channels: otherChan, command: 'RESTRICT_ACCESS', value: [Boolean(val)], teamId: teamOwner @@ -659,6 +677,7 @@ define([ // Send the command sframeChan.query('Q_SET_PAD_METADATA', { channel: channel, + channels: otherChan, command: 'ADD_ALLOWED', value: toAdd, teamId: teamOwner @@ -987,6 +1006,15 @@ define([ UI.findCancelButton().click(); if (err || (obj && obj.error)) { UI.warn(Messages.error); } }); + + // If this is a form wiht a answer channel, delete it too + var p = priv.propChannels; + if (p.answersChannel) { + sframeChan.query('Q_DELETE_OWNED', { + teamId: typeof(owned) !== "boolean" ? owned : undefined, + channel: p.answersChannel + }, function () {}); + } }); if (!opts.noEditPassword) { $d.append(h('br')); } $d.append(h('div', [ @@ -1020,7 +1048,7 @@ define([ var owned = Modal.isOwned(Env, data); // Request edit access - if (common.isLoggedIn() && ((data.roHref && !data.href) || data.fakeHref) && !owned && !opts.calendar) { + if (common.isLoggedIn() && ((data.roHref && !data.href) || data.fakeHref) && !owned && !opts.calendar && priv.app !== 'form') { var requestButton = h('button.btn.btn-secondary.no-margin.cp-access-margin-right', Messages.requestEdit_button); var requestBlock = h('p', requestButton); @@ -1058,7 +1086,7 @@ define([ var canMute = data.mailbox && owned === true && ( (typeof (data.mailbox) === "string" && data.owners[0] === edPublic) || data.mailbox[edPublic]); - if (owned === true && !opts.calendar) { + if (owned === true && !opts.calendar && priv.app !== 'form') { var cbox = UI.createCheckbox('cp-access-mute', Messages.access_muteRequests, !canMute); var $cbox = $(cbox); var spinner = UI.makeSpinner($cbox); diff --git a/www/common/inner/properties.js b/www/common/inner/properties.js index ddbf017fc..c946ed208 100644 --- a/www/common/inner/properties.js +++ b/www/common/inner/properties.js @@ -24,6 +24,7 @@ define([ if (privateData.propChannels) { var p = privateData.propChannels; data.channel = data.channel || p.channel; + data.answersChannel = data.answersChannel || p.answersChannel; data.rtChannel = data.rtChannel || p.rtChannel; data.lastVersion = data.lastVersion || p.lastVersion; data.lastCpHash = data.lastCpHash || p.lastCpHash; @@ -75,6 +76,7 @@ define([ var bytes = 0; var historyBytes; var chan = [data.channel]; + if (data.answersChannel) { chan.push(data.answersChannel); } if (data.rtChannel) { chan.push(data.rtChannel); } if (data.lastVersion) { chan.push(Hash.hrefToHexChannelId(data.lastVersion)); } diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 0e1e9684e..2d08b52d4 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -2139,11 +2139,23 @@ define([ if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); } if (!data.command) { return void cb({ error: 'EINVAL' }); } var s = getStore(data.teamId); + var otherChannels = data.channels; + delete data.channels; s.rpc.setMetadata(data, function (err, res) { if (err) { return void cb({ error: err }); } if (!Array.isArray(res) || !res.length) { return void cb({}); } cb(res[0]); }); + // If we have other related channels, send the command for them too + if (Array.isArray(otherChannels)) { + otherChannels.forEach(function (chan) { + var _d = Util.clone(data); + _d.channel = chan; + Store.setPadMetadata(clientId, _d, function () { + + }); + }); + } }; // GET_FULL_HISTORY from sframe-common-outer diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 9371b9e8b..526677d4b 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -810,6 +810,7 @@ define([ _findChannels(Env, toUnpin).forEach(function (id) { var data = _getFileData(Env, id); var arr = [data.channel]; + if (data.answersChannel) { arr.push(data.answersChannel); } if (data.rtChannel) { arr.push(data.rtChannel); } if (data.lastVersion) { arr.push(Hash.hrefToHexChannelId(data.lastVersion)); } Array.prototype.push.apply(toKeep, arr); @@ -1176,6 +1177,10 @@ define([ result.push(otherChan); } } + // Pin form answers channels + if (data.answersChannel && result.indexOf(data.answersChannel) === -1) { + result.push(data.answersChannel); + } // Pin onlyoffice realtime patches if (data.rtChannel && result.indexOf(data.rtChannel) === -1) { result.push(data.rtChannel); diff --git a/www/form/inner.js b/www/form/inner.js index bfc7dac3a..f53141842 100644 --- a/www/form/inner.js +++ b/www/form/inner.js @@ -597,6 +597,7 @@ define([ }; var checkIntegrity = function (getter) { + if (!content.order || !content.form) { return; } var changed = false; content.order.forEach(function (uid) { if (!content.form[uid]) { @@ -680,6 +681,8 @@ define([ } } + sframeChan.event('EV_FORM_PIN', {channel: content.answers.channel}); + var $container = $('#cp-app-form-container'); $container.append(makeFormCreator()); diff --git a/www/form/main.js b/www/form/main.js index ca4aea7e6..c02921887 100644 --- a/www/form/main.js +++ b/www/form/main.js @@ -17,6 +17,10 @@ define([ hash = obj.hash; }).nThen(function (/*waitFor*/) { var privateKey, publicKey; + var channels = {}; + var getPropChannels = function () { + return channels; + }; var addData = function (meta, CryptPad, user, Utils) { var keys = Utils.secret && Utils.secret.keys; @@ -45,6 +49,17 @@ define([ }; var addRpc = function (sframeChan, Cryptpad, Utils) { + sframeChan.on('EV_FORM_PIN', function (data) { + channels.answersChannel = data.channel; + Cryptpad.getPadAttribute('answersChannel', function (err, res) { + // If already stored, don't pin it again + if (res && res === data.channel) { return; } + Cryptpad.pinPads([data.channel], function () { + Cryptpad.setPadAttribute('answersChannel', data.channel, function () {}); + }); + }); + + }); sframeChan.on('Q_FORM_FETCH_ANSWERS', function (data, cb) { var myKeys = {}; var CPNetflux; @@ -83,7 +98,7 @@ define([ channel: data.channel, noChainPad: true, validateKey: keys.secondaryValidateKey, - owners: [myKeys.edPublic], // XXX add pad owner + owners: [myKeys.edPublic], crypto: crypto, // XXX Cache }; @@ -162,7 +177,7 @@ define([ var text = JSON.stringify(data.results); var ciphertext = crypto.encrypt(text, box.publicKey); - var hash = ciphertext.slice(0,64); // XXX use this to recover our previous answers + var hash = ciphertext.slice(0,64); Cryptpad.anonRpcMsg("WRITE_PRIVATE_MESSAGE", [ box.channel, ciphertext @@ -190,7 +205,8 @@ define([ hash: hash, href: href, useCreationScreen: true, - messaging: true + messaging: true, + getPropChannels: getPropChannels }); }); });