diff --git a/www/common/onlyoffice/history.js b/www/common/onlyoffice/history.js index 3e7e6b1d8..4c9abb917 100644 --- a/www/common/onlyoffice/history.js +++ b/www/common/onlyoffice/history.js @@ -22,7 +22,7 @@ define([ History.loadHistoryData = (cfg) => { const { sframeChan, mainRtChannel, downloadId, - currentCp, nextCp + currentCp, nextCp, href, password } = cfg; return new Promise((resolve, reject) => { @@ -30,6 +30,7 @@ define([ if (currentCp?.rtChannel) { // Load all messages from currentCp.rtChannel sframeChan.query('Q_GET_FULL_HISTORY', { + href, password, // get secret from other pad (template) channel: currentCp.rtChannel, isDownload: downloadId, full: true @@ -41,11 +42,15 @@ define([ } // Old CP or no CP: use mainRtChannel + if (!mainRtChannel) { + return void reject('EINVAL'); + } // No CP and no nextCp hash if (!currentCp && !nextCp?.hash) { // Load all messages from mainRtChannel sframeChan.query('Q_GET_FULL_HISTORY', { + href, password, // get secret from other pad (template) channel: mainRtChannel, isDownload: downloadId, full: true @@ -63,6 +68,7 @@ define([ if (currentCp?.hash || nextCp?.hash) { sframeChan.query('Q_GET_HISTORY_RANGE', { + href, password, // get secret from other pad (template) channel: mainRtChannel, lastKnownHash: endHash, toHash: startHash, @@ -257,7 +263,7 @@ define([ // Dropdown to select checkpoint (or "major version") const makeDropdown = ($dropdown) => { const all = sortedCp.slice(); - all.unshift(0); + if (mainRtChannel) { all.unshift(0); } const options = all.map((id, idx) => { const cp = hashes[id] || {}; let time = '';; @@ -281,12 +287,17 @@ define([ buttonCls: 'btn btn-default small' }; const dd = UIElements.createDropdown(dropdownConfig); - dd.setValue(cpIdx + 1); + if (mainRtChannel) { dd.setValue(cpIdx + 1); } + else { dd.setValue(cpIdx); } dd.onChange.reg((id, idx) => { loading = true; dd.find('> button').attr('disabled', 'disabled'); - cpIdx = idx - 1; // -1 because we've added "0" to the list + if (!mainRtChannel) { cpIdx = idx; } + else { + cpIdx = idx - 1; // -1 because we've added "0" to the list + } + hideVersion(); loadMessages().then(() => { diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 507e08525..4d0ea51d5 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -3042,7 +3042,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null } }; - var loadTemplate = function (href, pw, parsed) { + var loadTemplate = function (href, password, parsed) { APP.history = true; APP.template = true; var editor = getEditor(); @@ -3058,29 +3058,16 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null var lastIndex = idx[idx.length - 1]; var lastCp = hashes[lastIndex] || {}; - // Current cp or initial hash (invalid hash ==> initial hash) - var toHash = lastCp.hash || 'NONE'; - // Last hash - var fromHash = 'NONE'; - content.mediasSources = medias; - sframeChan.query('Q_GET_HISTORY_RANGE', { - href: href, - password: pw, - channel: _content.channel, - lastKnownHash: fromHash, - toHash: toHash, - }, function (err, data) { - if (err) { return void console.error(err); } - if (!Array.isArray(data.messages)) { return void console.error('Not an array!'); } - - // The first "cp" in history is the empty doc. It doesn't include the first patch - // of the history - var initialCp = !lastCp.hash; - - var messages = (data.messages || []).slice(initialCp ? 0 : 1); - + History.loadHistoryData({ + sframeChan, + href, password, + mainRtChannel: content.channel, + currentCp: lastCp, + nextCp: undefined + }).then(messages => { + if (!Array.isArray(messages)) { return void console.error('Not an array!'); } ooChannel.queue = messages.map(function (obj) { return { hash: obj.serverHash, @@ -3090,6 +3077,8 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null ooChannel.historyLastHash = ooChannel.lastHash; ooChannel.currentIndex = ooChannel.cpIndex; loadCp(lastCp, true); + }).catch(err => { + console.error(err); }); }; @@ -3561,7 +3550,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null Title.updateTitle(Title.defaultTitle); } - if (!content.channel) { + if (!content.channel && !Object.keys(content.hashes).length) { content.channel = Hash.createChannelId(); APP.onLocal(); checkLinkedDocs(); diff --git a/www/common/onlyoffice/trim-history.js b/www/common/onlyoffice/trim-history.js index fcc1ccdca..849642c70 100644 --- a/www/common/onlyoffice/trim-history.js +++ b/www/common/onlyoffice/trim-history.js @@ -15,6 +15,9 @@ const factory = () => { const lastCp = hashes[lastIdx]; if (!lastCp) { return content; } content.content.hashes = hashes = {}; + if (lastCp.rtChannel) { + delete content.content.channel; + } hashes[lastIdx] = lastCp; return content; }; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 4f140fd46..1fbd198d8 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -1443,6 +1443,53 @@ define([ } }, cb); }); + + // History + sframeChan.on('Q_GET_FULL_HISTORY', function (data, cb) { + let nSecret = secret; + if (data.isDownload && ooDownloadData[data.isDownload]) { + var ooData = ooDownloadData[data.isDownload]; + delete ooDownloadData[data.isDownload]; + nSecret = Utils.Hash.getSecrets('sheet', ooData.hash, ooData.password); + } else if (data.href) { + var _parsed = Utils.Hash.parsePadUrl(data.href); + nSecret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, data.password); + } + var crypto = Crypto.createEncryptor(nSecret.keys); + Cryptpad.getFullHistory({ + debug: data?.debug, + full: data?.full, + channel: data.channel || nSecret.channel, + validateKey: nSecret.keys.validateKey + }, function (encryptedMsgs) { + var nt = nThen; + var decryptedMsgs = []; + var total = encryptedMsgs.length; + encryptedMsgs.forEach(function (_msg, i) { + nt = nt(function (waitFor) { + // The 3rd parameter "true" means we're going to skip signature validation. + // We don't need it since the message is already validated serverside by hk + if (typeof(_msg) === "object") { + decryptedMsgs.push({ + author: _msg.author, + serverHash: _msg.serverHash, + time: _msg.time, + msg: crypto.decrypt(_msg.msg, true, true) + }); + } else { + decryptedMsgs.push(crypto.decrypt(_msg, true, true)); + } + setTimeout(waitFor(function () { + sframeChan.event('EV_FULL_HISTORY_STATUS', (i+1)/total); + })); + }).nThen; + }); + nt(function () { + cb(decryptedMsgs); + }); + }); + }); + sframeChan.on('Q_GET_HISTORY_RANGE', function (data, cb) { var nSecret = secret; if (cfg.isDrive) { @@ -1747,49 +1794,6 @@ define([ Cryptpad.anonGetPreviewContent(data, cb); }); - // History - sframeChan.on('Q_GET_FULL_HISTORY', function (data, cb) { - let nSecret = secret; - if (data.isDownload && ooDownloadData[data.isDownload]) { - var ooData = ooDownloadData[data.isDownload]; - delete ooDownloadData[data.isDownload]; - nSecret = Utils.Hash.getSecrets('sheet', ooData.hash, ooData.password); - } - var crypto = Crypto.createEncryptor(nSecret.keys); - Cryptpad.getFullHistory({ - debug: data?.debug, - full: data?.full, - channel: data.channel || nSecret.channel, - validateKey: nSecret.keys.validateKey - }, function (encryptedMsgs) { - var nt = nThen; - var decryptedMsgs = []; - var total = encryptedMsgs.length; - encryptedMsgs.forEach(function (_msg, i) { - nt = nt(function (waitFor) { - // The 3rd parameter "true" means we're going to skip signature validation. - // We don't need it since the message is already validated serverside by hk - if (typeof(_msg) === "object") { - decryptedMsgs.push({ - author: _msg.author, - serverHash: _msg.serverHash, - time: _msg.time, - msg: crypto.decrypt(_msg.msg, true, true) - }); - } else { - decryptedMsgs.push(crypto.decrypt(_msg, true, true)); - } - setTimeout(waitFor(function () { - sframeChan.event('EV_FULL_HISTORY_STATUS', (i+1)/total); - })); - }).nThen; - }); - nt(function () { - cb(decryptedMsgs); - }); - }); - }); - // Store sframeChan.on('Q_DRIVE_GETDELETED', function (data, cb) { Cryptpad.getDeletedPads(data, function (err, obj) { @@ -2093,7 +2097,7 @@ define([ nThen(function (waitFor) { channels.forEach(function (chan) { if (chan === "chainpad") { chan = secret.channel; } - console.error(chan); + if (!chan) { return; } Utils.Cache.clearChannel(chan, waitFor()); }); }).nThen(cb);