fix(office): fix loading template using linked documents

This commit is contained in:
yflory 2026-07-22 16:53:38 +02:00
parent 176bfe4c4c
commit ff552b968e
4 changed files with 78 additions and 71 deletions

View File

@ -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(() => {

View File

@ -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();

View File

@ -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;
};

View File

@ -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);