mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-14 11:05:41 +05:00
feat(storage): support propChannels and otherPadAttrs with linked docs
This commit is contained in:
parent
f4562a269d
commit
0820868e16
@ -889,7 +889,7 @@ const factory = (Util, Hash, Realtime, Feedback) => {
|
||||
// toClean.push(id);
|
||||
}
|
||||
|
||||
if (!el.v && ['sheet', 'doc', 'presentation'].includes(parsed.type) && !el.rtChannel) {
|
||||
if (!el.linked && ['sheet', 'doc', 'presentation'].includes(parsed.type) && !el.rtChannel) {
|
||||
missingRtChannel[el.channel] = el;
|
||||
}
|
||||
|
||||
|
||||
@ -737,7 +737,7 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
});
|
||||
|
||||
if (['doc', 'sheet', 'presentation'].includes(parsed.type)) {
|
||||
if (!pad.rtChannel) {
|
||||
if (!pad.rtChannel && !pad.linked) {
|
||||
return getRtChannelFromPad(pad, (err, rtChannel) => {
|
||||
const key = 'ADDPAD_NO_RT_CHANNEL' ;
|
||||
if (!err) {
|
||||
|
||||
@ -122,8 +122,7 @@ const factory = (Feedback) => {
|
||||
metadata: {
|
||||
//forcePlaceholder: true,
|
||||
validateKey: obj.validateKey,
|
||||
owners: obj.owners,
|
||||
expire: obj.expire
|
||||
linked: obj.padChan
|
||||
}
|
||||
};
|
||||
var msg = ['GET_HISTORY', wc.id, cfg];
|
||||
@ -217,25 +216,6 @@ const factory = (Feedback) => {
|
||||
});
|
||||
};
|
||||
|
||||
var updateHash = function (ctx, data, clientId, cb) {
|
||||
var c = ctx.clients[clientId];
|
||||
if (!c) { return void cb({ error: 'NOT_IN_CHANNEL' }); }
|
||||
var chan = ctx.channels[c.channel];
|
||||
if (!chan) { return void cb({ error: 'INVALID_CHANNEL' }); }
|
||||
var hash = data;
|
||||
var index = -1;
|
||||
chan.history.some(function (msg, idx) {
|
||||
if (msg.slice(0,64) === hash) {
|
||||
index = idx + 1;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (index !== -1) {
|
||||
chan.history = chan.history.slice(index);
|
||||
}
|
||||
cb();
|
||||
};
|
||||
|
||||
var sendMessage = function (ctx, data, clientId, cb) {
|
||||
var c = ctx.clients[clientId];
|
||||
if (!c) { return void cb({ error: 'NOT_IN_CHANNEL' }); }
|
||||
@ -355,9 +335,6 @@ const factory = (Feedback) => {
|
||||
if (cmd === 'SEND_MESSAGE') {
|
||||
return void sendMessage(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'UPDATE_HASH') {
|
||||
return void updateHash(ctx, data, clientId, cb);
|
||||
}
|
||||
if (cmd === 'OPEN_CHANNEL') {
|
||||
return void openChannel(ctx, data, clientId, cb);
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ define([
|
||||
if (redraw) { Env.evRedrawAll.fire(redraw); }
|
||||
}));
|
||||
};
|
||||
Modal.getOtherChans = (priv, opts) => { // XXX TODO
|
||||
Modal.getOtherChans = (priv, opts) => {
|
||||
// "attributes" contains the additional channels that the other user
|
||||
// has to store (rtChannel, answersChannels, lastVersion, lastCpHash)
|
||||
// - opts.attributes when access modal is created from the drive
|
||||
|
||||
@ -338,6 +338,12 @@ define([
|
||||
return a-b;
|
||||
});
|
||||
};
|
||||
const getLastCpId = (old) => {
|
||||
const hashes = old ? oldHashes : content.hashes;
|
||||
if (!hashes || !Object.keys(hashes).length) { return 0; }
|
||||
const allIdx = sortCpIndex(hashes);
|
||||
return allIdx[allIdx.length - 1];
|
||||
};
|
||||
var getLastCp = function (old, i) {
|
||||
var hashes = old ? oldHashes : content.hashes;
|
||||
if (!hashes || !Object.keys(hashes).length) { return {}; }
|
||||
@ -491,7 +497,6 @@ define([
|
||||
_content.hashes = {};
|
||||
_content.hashes[1] = {
|
||||
file: data.url,
|
||||
index: 0,
|
||||
rtChannel: Hash.createChannelId(),
|
||||
version: OOCurrentVersion.currentVersionNumber
|
||||
};
|
||||
@ -548,8 +553,6 @@ define([
|
||||
var i = current + 1;
|
||||
var cpData = content.hashes[i] = {
|
||||
file: data.url,
|
||||
hash: ev.hash,
|
||||
index: ev.index,
|
||||
rtChannel: Hash.createChannelId(),
|
||||
version: OOCurrentVersion.currentVersionNumber
|
||||
};
|
||||
@ -582,12 +585,6 @@ define([
|
||||
});
|
||||
|
||||
});
|
||||
sframeChan.query('Q_OO_COMMAND', {
|
||||
cmd: 'UPDATE_HASH',
|
||||
data: ev.hash
|
||||
}, function (err, obj) {
|
||||
if (err || (obj && obj.error)) { console.error(err || obj.error); }
|
||||
});
|
||||
};
|
||||
|
||||
var fmConfig = {
|
||||
@ -595,10 +592,8 @@ define([
|
||||
noStore: true,
|
||||
body: $('body'),
|
||||
onUploaded: function (ev, data) {
|
||||
if (!data || !data.url) { return; }
|
||||
sframeChan.query('Q_OO_SAVE', data, function (err) {
|
||||
onUploaded(ev, data, err);
|
||||
});
|
||||
if (!data?.url) { return; }
|
||||
onUploaded(ev, data);
|
||||
},
|
||||
onError: function (err) {
|
||||
onUploaded(null, null, err);
|
||||
@ -654,8 +649,6 @@ define([
|
||||
var file = getFileType();
|
||||
blob.name = title || (metadataMgr.getMetadataLazy().title || file.doc) + '.' + file.type;
|
||||
var data = {
|
||||
//hash: (APP.history || APP.template) ? ooChannel.historyLastHash : ooChannel.lastHash,
|
||||
index: 0, //(APP.history || APP.template) ? ooChannel.currentIndex : ooChannel.cpIndex,
|
||||
time: new Date()
|
||||
};
|
||||
fixSheets();
|
||||
@ -679,10 +672,9 @@ define([
|
||||
if (APP.cantCheckpoint) { return; } // TOO_LARGE
|
||||
|
||||
var locked = content.saveLock;
|
||||
var lastCp = getLastCp();
|
||||
|
||||
var currentIdx = ooChannel.cpIndex;
|
||||
var needCp = force || (currentIdx - (lastCp.index || 0)) > FORCE_CHECKPOINT_INTERVAL;
|
||||
var needCp = force || currentIdx > FORCE_CHECKPOINT_INTERVAL;
|
||||
|
||||
if (!needCp) { return; }
|
||||
|
||||
@ -719,10 +711,7 @@ define([
|
||||
content.saveLock = myOOId;
|
||||
APP.onLocal();
|
||||
APP.realtime.onSettle(function () {
|
||||
onUploaded({
|
||||
hash: ooChannel.lastHash,
|
||||
index: ooChannel.cpIndex
|
||||
}, {
|
||||
onUploaded({}, {
|
||||
url: getLastCp().file,
|
||||
});
|
||||
});
|
||||
@ -768,7 +757,7 @@ define([
|
||||
if (!lastCp || !lastCp.file) {
|
||||
return void reject('EEMPTY');
|
||||
}
|
||||
ooChannel.cpIndex = lastCp.index || 0;
|
||||
ooChannel.cpIndex = 0;
|
||||
ooChannel.lastHash = lastCp.hash;
|
||||
var parsed = Hash.parsePadUrl(lastCp.file);
|
||||
var secret = Hash.getSecrets('file', parsed.hash);
|
||||
@ -813,17 +802,16 @@ define([
|
||||
var openVersionHash = function (version) {
|
||||
readOnly = true;
|
||||
var hashes = content.hashes || {};
|
||||
var sortedCp = Object.keys(hashes).map(Number).sort(function (a, b) {
|
||||
return hashes[a].index - hashes[b].index;
|
||||
});
|
||||
var sortedCp = sortCpIndex(hashes);
|
||||
var s = version.split('.');
|
||||
var v = parseInt(s[1]);
|
||||
if (s.length !== 2) { return UI.errorLoadingScreen(Messages.error); }
|
||||
|
||||
var major = Number(s[0]);
|
||||
var cpId = sortedCp[major - 1];
|
||||
var nextCpId = sortedCp[major];
|
||||
var cp = hashes[cpId] || {};
|
||||
const cpId = Number(s[0]);
|
||||
const cp = hashes[cpId] || {};
|
||||
|
||||
const cpIdx = sortedCp.indexOf(cpId);
|
||||
const nextCpId = sortedCp[cpidx + 1];
|
||||
|
||||
var minor = Number(s[1]) + 1;
|
||||
if (APP.isDownload) { minor = undefined; }
|
||||
@ -831,6 +819,8 @@ define([
|
||||
var toHash = cp.hash || 'NONE';
|
||||
var fromHash = nextCpId ? hashes[nextCpId].hash : 'NONE';
|
||||
|
||||
// XXX XXX HISTORY
|
||||
// if (cp.file && !cp.hash) {} // new format, full history...
|
||||
sframeChan.query('Q_GET_HISTORY_RANGE', {
|
||||
channel: content.channel,
|
||||
lastKnownHash: fromHash,
|
||||
@ -878,6 +868,7 @@ define([
|
||||
|
||||
loadLastDocument(cp)
|
||||
.then(({blob, fileType}) => {
|
||||
// XXX HISTORY minor.... ?
|
||||
ooChannel.queue = messages.slice(1, minor+1);
|
||||
resetData(blob, fileType, cp);
|
||||
UI.removeLoadingScreen();
|
||||
@ -2856,8 +2847,6 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
});
|
||||
};
|
||||
var data = {
|
||||
hash: ooChannel.lastHash,
|
||||
index: ooChannel.cpIndex,
|
||||
callback: uploadedCallback
|
||||
};
|
||||
APP.FM.handleFile(blob, data);
|
||||
@ -3315,8 +3304,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
time = obj.time;
|
||||
} else {
|
||||
var major = Object.keys(content.hashes).length;
|
||||
var cpIndex = getLastCp().index || 0;
|
||||
var minor = ooChannel.cpIndex - cpIndex;
|
||||
var minor = ooChannel.cpIndex;
|
||||
hash = major+'.'+minor;
|
||||
time = +new Date();
|
||||
}
|
||||
@ -3544,11 +3532,17 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
throw new Error(errorText);
|
||||
}
|
||||
content = hjson.content || content;
|
||||
|
||||
// Support old checkpoints
|
||||
var newLatest = getLastCp();
|
||||
sframeChan.query('Q_OO_SAVE', {
|
||||
hash: newLatest.hash,
|
||||
url: newLatest.file
|
||||
}, function () { });
|
||||
if (newLatest.hash) {
|
||||
sframeChan.query('Q_OO_SAVE', {
|
||||
channel: content.channel,
|
||||
hash: newLatest.hash,
|
||||
url: newLatest.file
|
||||
}, function () { });
|
||||
}
|
||||
|
||||
newDoc = !content.hashes || Object.keys(content.hashes).length === 0;
|
||||
checkLinkedDocs();
|
||||
} else if (!privateData.isNewFile) {
|
||||
@ -4037,19 +4031,14 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
|
||||
var editor = getEditor();
|
||||
if (content.hashes) {
|
||||
var latest = getLastCp(true);
|
||||
var newLatest = getLastCp();
|
||||
if (newLatest.index > latest.index || (newLatest.index && !latest.index)) {
|
||||
var oldLastCp = getLastCpId(true);
|
||||
var newLastCp = getLastCpId();
|
||||
if (newLastCp > oldLastCp) {
|
||||
ooChannel.queue = [];
|
||||
ooChannel.ready = false;
|
||||
var reload = function () {
|
||||
// New checkpoint
|
||||
sframeChan.query('Q_OO_SAVE', {
|
||||
hash: newLatest.hash,
|
||||
url: newLatest.file
|
||||
}, function () {
|
||||
checkNewCheckpoint();
|
||||
});
|
||||
checkNewCheckpoint();
|
||||
};
|
||||
var editing = editor.asc_isDocumentModified ? editor.asc_isDocumentModified() : editor.isDocumentModify;
|
||||
if (editing) {
|
||||
|
||||
@ -38,65 +38,42 @@ define([
|
||||
obj.ooVersionHash = version;
|
||||
obj.ooForceVersion = localStorage.CryptPad_ooVersion || "";
|
||||
};
|
||||
var channels = {};
|
||||
const channels = {};
|
||||
var getPropChannels = function () {
|
||||
return channels;
|
||||
};
|
||||
var addRpc = function (sframeChan, Cryptpad, Utils) {
|
||||
Cryptpad.otherPadAttrs = channels;
|
||||
|
||||
sframeChan.on('Q_OO_SAVE', function (data, cb) {
|
||||
// Legacy, we keep lastCpHash to compute history size
|
||||
// from drive
|
||||
// XXX server can find the last checkpoint
|
||||
Cryptpad.setPadAttribute('lastVersion', data.url, cb);
|
||||
// Only called onReady when loading legacy checkpoints
|
||||
channels.rtChannel = data.channel;
|
||||
channels.lastVersion = data.url;
|
||||
Cryptpad.setPadAttribute('lastCpHash', data.hash, cb);
|
||||
if (data.hash) { channels.lastCpHash = data.hash; }
|
||||
Cryptpad.setPadAttribute('lastCpHash', data.hash, cb);
|
||||
Cryptpad.setPadAttribute('lastVersion', data.url, cb);
|
||||
});
|
||||
|
||||
sframeChan.on('Q_OO_OPENCHANNEL', function (data, cb) {
|
||||
const other = Cryptpad.otherPadAttrs = {
|
||||
rtChannel: data.channel
|
||||
};
|
||||
if (channels.lastVersion) {
|
||||
other.lastVersion = channels.lastVersion;
|
||||
}
|
||||
other.lastCpHash = channels.lastCpHash;
|
||||
channels.rtChannel = data.channel;
|
||||
|
||||
// XXX mark as migrated? and clean existing data
|
||||
if (channels?.lastVersion && !channels?.lastCpHash) {
|
||||
Cryptpad.setPadAttribute('v', 2, function () {});
|
||||
// If we don't have "channels" values, it means we're not
|
||||
// loading an "old" checkpoint so we can clean attributes
|
||||
if (!channels?.lastVersion) {
|
||||
channels.linked = true;
|
||||
Cryptpad.setPadAttribute('linked', true, () => {});
|
||||
Cryptpad.setPadAttribute('rtChannel', void 0, () => {});
|
||||
Cryptpad.setPadAttribute('lastVersion', void 0, () => {});
|
||||
Cryptpad.setPadAttribute('lastCpHash', void 0, () => {});
|
||||
}
|
||||
|
||||
var owners, expire;
|
||||
nThen(function (waitFor) {
|
||||
if (Utils.rtConfig) {
|
||||
owners = Utils.Util.find(Utils.rtConfig, ['metadata', 'owners']);
|
||||
expire = Utils.Util.find(Utils.rtConfig, ['metadata', 'expire']);
|
||||
return;
|
||||
Cryptpad.onlyoffice.execCommand({
|
||||
cmd: 'OPEN_CHANNEL',
|
||||
data: {
|
||||
channel: data.channel,
|
||||
lastCpHash: data.lastCpHash,
|
||||
padChan: Utils.secret.channel, // metadata inherited from this pad
|
||||
validateKey: Utils.secret.keys.validateKey
|
||||
}
|
||||
Cryptpad.getPadAttribute('owners', waitFor(function (err, res) {
|
||||
owners = res;
|
||||
}));
|
||||
Cryptpad.getPadAttribute('expire', waitFor(function (err, res) {
|
||||
expire = res;
|
||||
}));
|
||||
}).nThen(function () {
|
||||
Cryptpad.onlyoffice.execCommand({
|
||||
cmd: 'OPEN_CHANNEL',
|
||||
data: {
|
||||
owners: owners,
|
||||
expire: expire,
|
||||
channel: data.channel,
|
||||
lastCpHash: data.lastCpHash,
|
||||
padChan: Utils.secret.channel,
|
||||
validateKey: Utils.secret.keys.validateKey
|
||||
}
|
||||
}, cb);
|
||||
});
|
||||
}, cb);
|
||||
});
|
||||
sframeChan.on('EV_OO_PIN_IMAGES', function (list) {
|
||||
Cryptpad.getPadAttribute('ooImages', function (err, res) {
|
||||
|
||||
2
www/common/worker.bundle.min.js
vendored
2
www/common/worker.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user