Add basic support for versioned link

pull/1/head
yflory 5 years ago
parent ba854e88c0
commit a798873230

@ -171,7 +171,7 @@ Version 1
return true; return true;
} }
}); });
return k; return k ? Crypto.b64AddSlashes(k) : '';
}; };
var getOwnerKey = function (hashArr) { var getOwnerKey = function (hashArr) {
var k; var k;
@ -227,6 +227,9 @@ Version 1
if (opts.embed) { hash += 'embed/'; } if (opts.embed) { hash += 'embed/'; }
if (opts.present) { hash += 'present/'; } if (opts.present) { hash += 'present/'; }
var versionHash = typeof(opts.versionHash) !== "undefined" ? opts.versionHash : parsed.versionHash; var versionHash = typeof(opts.versionHash) !== "undefined" ? opts.versionHash : parsed.versionHash;
if (versionHash) {
hash += 'hash=' + Crypto.b64RemoveSlashes(versionHash) + '/';
}
return hash; return hash;
}; };

@ -329,6 +329,9 @@ define([
} }
// warning about sharing links // warning about sharing links
// when sharing a version hash, there is a similar warning and we want
// to avoid alert fatigue
if (!opts.versionHash) {
var localStore = window.cryptpadStore; var localStore = window.cryptpadStore;
var dismissButton = h('span.fa.fa-times'); var dismissButton = h('span.fa.fa-times');
var shareLinkWarning = h('div.alert.alert-warning.dismissable', var shareLinkWarning = h('div.alert.alert-warning.dismissable',
@ -349,6 +352,7 @@ define([
}); });
}); });
}
// Burn after reading // Burn after reading
if (opts.barAlert) { linkContent.push(opts.barAlert.cloneNode(true)); } if (opts.barAlert) { linkContent.push(opts.barAlert.cloneNode(true)); }
@ -460,7 +464,8 @@ define([
var pathname = opts.pathname; var pathname = opts.pathname;
var parsed = Hash.parsePadUrl(pathname); var parsed = Hash.parsePadUrl(pathname);
var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1; var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1;
var canBAR = parsed.type !== 'drive'; var versionHash = hashes.viewHash && opts.versionHash;
var canBAR = parsed.type !== 'drive' && !versionHash;
var burnAfterReading = (hashes.viewHash && canBAR) ? var burnAfterReading = (hashes.viewHash && canBAR) ?
UI.createRadio('accessRights', 'cp-share-bar', Messages.burnAfterReading_linkBurnAfterReading, false, { UI.createRadio('accessRights', 'cp-share-bar', Messages.burnAfterReading_linkBurnAfterReading, false, {
@ -519,6 +524,12 @@ define([
var embed = val.embed; var embed = val.embed;
var present = val.present !== undefined ? val.present : Util.isChecked($rights.find('#cp-share-present')); var present = val.present !== undefined ? val.present : Util.isChecked($rights.find('#cp-share-present'));
var burnAfterReading = Util.isChecked($rights.find('#cp-share-bar')); var burnAfterReading = Util.isChecked($rights.find('#cp-share-bar'));
if (versionHash) {
edit = false;
embed = false;
present = false;
burnAfterReading = false;
}
if (burnAfterReading && !opts.burnAfterReadingUrl) { if (burnAfterReading && !opts.burnAfterReadingUrl) {
if (cb) { // Called from the contacts tab, "share" button if (cb) { // Called from the contacts tab, "share" button
var barHref = origin + pathname + '#' + (hashes.viewHash || hashes.editHash); var barHref = origin + pathname + '#' + (hashes.viewHash || hashes.editHash);
@ -533,7 +544,7 @@ define([
var href = burnAfterReading ? opts.burnAfterReadingUrl var href = burnAfterReading ? opts.burnAfterReadingUrl
: (origin + pathname + '#' + hash); : (origin + pathname + '#' + hash);
var parsed = Hash.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
return origin + parsed.getUrl({embed: embed, present: present}); return origin + parsed.getUrl({embed: embed, present: present, versionHash: versionHash});
}; };
opts.getEmbedValue = function () { opts.getEmbedValue = function () {
var url = opts.getLinkValue({ var url = opts.getLinkValue({
@ -543,7 +554,11 @@ define([
}; };
// disable edit share options if you don't have edit rights // disable edit share options if you don't have edit rights
if (!hashes.editHash) { if (versionHash) {
$rights.find('#cp-share-editable-false').attr('checked', true);
$rights.find('#cp-share-present').removeAttr('checked').attr('disabled', true);
$rights.find('#cp-share-editable-true').removeAttr('checked').attr('disabled', true);
} else if (!hashes.editHash) {
$rights.find('#cp-share-editable-false').attr('checked', true); $rights.find('#cp-share-editable-false').attr('checked', true);
$rights.find('#cp-share-editable-true').removeAttr('checked').attr('disabled', true); $rights.find('#cp-share-editable-true').removeAttr('checked').attr('disabled', true);
} else if (!hashes.viewHash) { } else if (!hashes.viewHash) {
@ -582,7 +597,9 @@ define([
// Set default values // Set default values
common.getAttribute(['general', 'share'], function (err, val) { common.getAttribute(['general', 'share'], function (err, val) {
val = val || {}; val = val || {};
if (val.present && canPresent) { if (versionHash) {
$rights.find('#cp-share-editable-false').prop('checked', true);
} else if (val.present && canPresent) {
$rights.find('#cp-share-editable-false').prop('checked', false); $rights.find('#cp-share-editable-false').prop('checked', false);
$rights.find('#cp-share-editable-true').prop('checked', false); $rights.find('#cp-share-editable-true').prop('checked', false);
$rights.find('#cp-share-present').prop('checked', true); $rights.find('#cp-share-present').prop('checked', true);
@ -671,9 +688,22 @@ define([
onHide: resetTab onHide: resetTab
}]; }];
Modal.getModal(common, opts, tabs, function (err, modal) { Modal.getModal(common, opts, tabs, function (err, modal) {
$(modal).find('.cp-bar').hide(); // Hide the burn-after-reading option by default
var $modal = $(modal);
$modal.find('.cp-bar').hide();
// Prepend the "rights" radio selection // Prepend the "rights" radio selection
$(modal).find('.alertify-tabs-titles').after($rights); $modal.find('.alertify-tabs-titles').after($rights);
// Add the versionHash warning if needed
if (opts.versionHash) {
Messages.share_versionHash = "You're going to share the selected history version of the document in read-only mode. This will also <b>give view access</b> to the recipients."; // XXX
$rights.after(h('div.alert.alert-warning', [
h('i.fa.fa-history'),
UI.setHTML(h('span'), Messages.share_versionHash)
]));
}
// callback // callback
cb(err, modal); cb(err, modal);
}); });

@ -1455,7 +1455,47 @@ define([
var channels = Store.channels = store.channels = {}; var channels = Store.channels = store.channels = {};
var getVersionHash = function (clientId, data) {
var validateKey;
var fakeNetflux = Hash.createChannelId();
nThen(function (waitFor) {
Store.getPadMetadata(null, {
channel: data.channel
}, function (md) {
// XXX not needed? we don't need to validate messages coming from history keeper
validateKey = md.validateKey;
});
}).nThen(function () {
Store.getHistoryRange(clientId, {
cpCount: 1,
channel: data.channel,
lastKnownHash: data.versionHash
}, function (obj) {
if (obj && obj.error) {
postMessage(clientId, "PAD_ERROR", obj.error);
return;
}
postMessage(clientId, "PAD_CONNECT", {
myID: fakeNetflux,
id: data.channel,
members: [fakeNetflux]
});
(obj.messages || []).forEach(function (data) {
postMessage(clientId, "PAD_MESSAGE", {
msg: data.msg,
user: fakeNetflux.slice(0,16), // fake history keeper to avoid validate
validateKey: validateKey
});
});
postMessage(clientId, "PAD_READY");
});
});
};
Store.joinPad = function (clientId, data) { Store.joinPad = function (clientId, data) {
if (data.versionHash) {
return void getVersionHash(clientId, data);
}
var isNew = typeof channels[data.channel] === "undefined"; var isNew = typeof channels[data.channel] === "undefined";
var channel = channels[data.channel] = channels[data.channel] || { var channel = channels[data.channel] = channels[data.channel] || {
queue: [], queue: [],
@ -2046,7 +2086,7 @@ define([
network.on('message', onMsg); network.on('message', onMsg);
network.sendto(hk, JSON.stringify(['GET_HISTORY_RANGE', data.channel, { network.sendto(hk, JSON.stringify(['GET_HISTORY_RANGE', data.channel, {
from: data.lastKnownHash, from: data.lastKnownHash,
cpCount: 2, cpCount: data.cpCount || 2,
txid: txid txid: txid
}])); }]));
}; };

@ -28,10 +28,13 @@ define([], function () {
var padRpc = conf.padRpc; var padRpc = conf.padRpc;
var sframeChan = conf.sframeChan; var sframeChan = conf.sframeChan;
var metadata= conf.metadata || {}; var metadata= conf.metadata || {};
var versionHash = conf.versionHash;
var validateKey = metadata.validateKey; var validateKey = metadata.validateKey;
var onConnect = conf.onConnect || function () { }; var onConnect = conf.onConnect || function () { };
conf = undefined; conf = undefined;
if (versionHash) { readOnly = true; }
padRpc.onReadyEvent.reg(function () { padRpc.onReadyEvent.reg(function () {
sframeChan.event('EV_RT_READY', null); sframeChan.event('EV_RT_READY', null);
}); });
@ -132,6 +135,7 @@ define([], function () {
padRpc.joinPad({ padRpc.joinPad({
channel: channel || null, channel: channel || null,
readOnly: readOnly, readOnly: readOnly,
versionHash: versionHash,
metadata: metadata metadata: metadata
}); });
}; };

@ -445,6 +445,7 @@ define([
feedbackAllowed: Utils.Feedback.state, feedbackAllowed: Utils.Feedback.state,
isPresent: parsed.hashData && parsed.hashData.present, isPresent: parsed.hashData && parsed.hashData.present,
isEmbed: parsed.hashData && parsed.hashData.embed, isEmbed: parsed.hashData && parsed.hashData.embed,
isHistoryVersion: parsed.hashData && parsed.hashData.versionHash,
accounts: { accounts: {
donateURL: Cryptpad.donateURL, donateURL: Cryptpad.donateURL,
upgradeURL: Cryptpad.upgradeURL upgradeURL: Cryptpad.upgradeURL
@ -1499,6 +1500,7 @@ define([
var cpNfCfg = { var cpNfCfg = {
sframeChan: sframeChan, sframeChan: sframeChan,
channel: secret.channel, channel: secret.channel,
versionHash: parsed.hashData && parsed.hashData.versionHash,
padRpc: Cryptpad.padRpc, padRpc: Cryptpad.padRpc,
validateKey: secret.keys.validateKey || undefined, validateKey: secret.keys.validateKey || undefined,
isNewHash: isNewHash, isNewHash: isNewHash,

Loading…
Cancel
Save