From 3940b9c4be40402d50ed7de0fce49a2cea75a2cd Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 15 May 2024 11:17:25 +0200 Subject: [PATCH 01/23] Fix checkpoint issues with integrated OO apps --- www/common/onlyoffice/inner.js | 14 +++++++++----- www/common/outer/async-store.js | 32 ++++++++++++++++++++++++++------ www/cryptpad-api.js | 2 ++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 9faa7afc2..5184833c7 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -392,6 +392,10 @@ define([ }; var onUploaded = function (ev, data, err) { + if (!ev && err) { + console.error(err); + return void UI.warn(Messages.error); + } if (ev.newTemplate) { if (err) { console.error(err); @@ -549,7 +553,7 @@ define([ var saveToServer = function (blob, title) { if (APP.cantCheckpoint) { return; } // TOO_LARGE - var text = getContent(); + var text = !blob && getContent(); if (!text && !blob) { setEditable(false, true); sframeChan.query('Q_CLEAR_CACHE_CHANNELS', [ @@ -3154,9 +3158,9 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null return void UI.errorLoadingScreen(Messages.error); } var blob = new Blob([bin], {type: 'text/plain'}); - var file = getFileType(); - resetData(blob, file); - //saveToServer(blob, title); + //var file = getFileType(); + //resetData(blob, file); + saveToServer(blob, title); Title.updateTitle(title); UI.removeLoadingScreen(); }); @@ -3205,7 +3209,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null cb(); }); }); - if (privateData.initialState) { + if (privateData.initialState && (!content || !content.hashes)) { var blob = privateData.initialState; let title = `document.${cfg.fileType}`; console.error(blob, title); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index cd93675fe..9ba3853e0 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -467,6 +467,19 @@ define([ }); }; + var initTempRpc = (clientId, cb) => { + if (store.rpc) { return void cb(store.rpc); } + var kp = Crypto.Nacl.sign.keyPair(); + var keys = { + edPublic: Crypto.Nacl.util.encodeBase64(kp.publicKey), + edPrivate: Crypto.Nacl.util.encodeBase64(kp.secretKey) + }; + Pinpad.create(store.network, keys, function (e, call) { + if (e) { return void cb({error: e}); } + store.rpc = call; + cb(call); + }); + }; var initRpc = function (clientId, data, cb) { if (!store.loggedIn) { return cb(); } if (store.rpc) { return void cb(account); } @@ -3149,7 +3162,7 @@ define([ // If we load CryptPad for the first time from an existing pad, don't create a // drive automatically. - var onNoDrive = function (clientId, cb) { + var onNoDrive = function (clientId, cb, initRpc) { var andThen = function () { // To be able to use all the features inside the pad, we need to // initialize the chat (messenger) and the cursor modules. @@ -3160,9 +3173,16 @@ define([ store.messenger = store.modules['messenger']; // And now we're ready - initAnonRpc(null, null, function () { - cb({}); - }); + let getAnon = () => { + initAnonRpc(null, null, function () { + cb({}); + }); + }; + + if (initRpc) { + return initTempRpc(clientId, getAnon); + } + getAnon(); }; // We need an anonymous RPC to be able to check if the pad exists and to get @@ -3245,7 +3265,7 @@ define([ // First tab, no user hash, no anon hash and this app doesn't need a drive // ==> don't create a drive // Or "neverDrive" (integration into another platform?) - // ==> don't create a drive + // ==> don't create a drive BUT create temp RPC (we may need to upload) if (data.neverDrive || (data.noDrive && !data.userHash && !data.anonHash)) { return void onNoDrive(clientId, function (obj) { if (obj && obj.error) { @@ -3259,7 +3279,7 @@ define([ } Feedback.send("NO_DRIVE", true); callback(obj); - }); + }, !!data.neverDrive); } initialized = true; diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index e73ef40bd..cdbf7f8dd 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -188,6 +188,7 @@ chan.on('ON_DOWNLOADAS', blob => { let url = URL.createObjectURL(blob); + if (!config.events.onDownloadAs) { return; } config.events.onDownloadAs({ data: { fileType: config.document && config.document.fileType, @@ -198,6 +199,7 @@ chan.on('SAVE', function (data, cb) { blob = data; + if (!config.events.onSave) { return void cb(); } config.events.onSave(data, cb); }); chan.on('RELOAD', function () { From 154b3e66c84d3611e628e3925928282b4d2bd693 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 15 May 2024 11:37:03 +0200 Subject: [PATCH 02/23] Get username and lang from API config --- customize.dist/messages.js | 7 +++---- www/common/onlyoffice/inner.js | 8 ++++++-- www/common/sframe-app-outer.js | 4 +++- www/cryptpad-api.js | 3 ++- www/integration/main.js | 10 ++++++++-- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/customize.dist/messages.js b/customize.dist/messages.js index 579dcd31e..0fb495bb2 100755 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -35,11 +35,10 @@ var getStoredLanguage = function () { return localStorage && localStorage.getIte var getBrowserLanguage = function () { return navigator.language || navigator.userLanguage || ''; }; var getLanguage = Messages._getLanguage = function () { if (window.cryptpadLanguage) { return window.cryptpadLanguage; } - try { - if (getStoredLanguage()) { return getStoredLanguage(); } - } catch (e) { console.log(e); } var l = getBrowserLanguage(); - // Edge returns 'fr-FR' --> transform it to 'fr' and check again + try { + l = getStoredLanguage() || getBrowserLanguage(); + } catch (e) { console.log(e); } return map[l] ? l : (map[l.split('-')[0]] ? l.split('-')[0] : (map[l.split('_')[0]] ? l.split('_')[0] : 'en')); diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 5184833c7..dfa949eb6 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1671,6 +1671,10 @@ define([ var lang = (window.cryptpadLanguage || navigator.language || navigator.userLanguage || '').slice(0,2); + let username = Util.find(privateData, ['integrationConfig', 'user', 'name']) + || metadataMgr.getUserData().name + || Messages.anonymous; + // Config APP.ooconfig = { "document": { @@ -1694,8 +1698,8 @@ define([ }, "user": { "id": String(myOOId), //"c0c3bf82-20d7-4663-bf6d-7fa39c598b1d", - "firstname": metadataMgr.getUserData().name || Messages.anonymous, - "name": metadataMgr.getUserData().name || Messages.anonymous, + "firstname": username, + "name": username }, "mode": "edit", "lang": lang diff --git a/www/common/sframe-app-outer.js b/www/common/sframe-app-outer.js index b1210796f..b634f51b4 100644 --- a/www/common/sframe-app-outer.js +++ b/www/common/sframe-app-outer.js @@ -17,7 +17,9 @@ define([ nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { - var obj = SFCommonO.initIframe(waitFor, true, integration.pathname); + let lang = integration && integration.config && integration.config.editorConfig + && integration.config.editorConfig.lang; + var obj = SFCommonO.initIframe(waitFor, true, integration.pathname, lang); href = obj.href; hash = obj.hash; if (isIntegration) { diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index cdbf7f8dd..d7d2ac7e2 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -116,7 +116,8 @@ application: config.documentType, document: blob, ext: config.document.fileType, - autosave: config.autosave || 10 + autosave: config.autosave || 10, + editorConfig: config.editorConfig || {} }, function (obj) { if (obj && obj.error) { reject(obj.error); return console.error(obj.error); } resolve({}); diff --git a/www/integration/main.js b/www/integration/main.js index fe3ffd010..a09592d3b 100644 --- a/www/integration/main.js +++ b/www/integration/main.js @@ -155,7 +155,12 @@ define([ chan.on('START', function (data) { console.warn('INNER START', data); var href = Hash.hashToHref(data.key, data.application); - console.error(Hash.hrefToHexChannelId(href)); + + if (data.editorConfig.lang) { + var LS_LANG = "CRYPTPAD_LANG"; + localStorage.setItem(LS_LANG, data.editorConfig.lang); + } + window.CP_integration_outer = { pathname: `/${data.application}/`, hash: data.key, @@ -163,7 +168,8 @@ define([ initialState: data.document, config: { fileType: data.ext, - autosave: data.autosave + autosave: data.autosave, + user: data.editorConfig.user }, utils: { onReady: onReady, From 112a222f79f7bf82db96404b1ce73844789494cc Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 21 May 2024 18:05:03 +0200 Subject: [PATCH 03/23] Fix initial OO content issue with API --- www/common/onlyoffice/inner.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index dfa949eb6..9ffbfd47b 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -3213,10 +3213,9 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null cb(); }); }); - if (privateData.initialState && (!content || !content.hashes)) { + if (privateData.initialState && (!content || !content.hashes || !Object.keys(content.hashes).length)) { var blob = privateData.initialState; let title = `document.${cfg.fileType}`; - console.error(blob, title); return convertImportBlob(blob, title); } } From 97a806353e911efb3e1c3bc1ce8dca3c33c5d0aa Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 24 Jun 2024 16:18:27 +0200 Subject: [PATCH 04/23] Fix issue with application names --- www/cryptpad-api.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index d7d2ac7e2..c72f41f65 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -251,16 +251,22 @@ cryptpadURL = getInstanceURL(); } + config.events = config.events || {}; + // OnlyOffice shim let url = config.document.url; if (/^http:\/\/localhost\/cache\/files\//.test(url)) { url = url.replace(/(http:\/\/localhost\/cache\/files\/)/, getInstanceURL() + 'ooapi/'); } config.document.url = url; - if (config.documentType === "spreadsheet") { + if (config.documentType === "spreadsheet" || config.documentType === "cell") { config.documentType = "sheet"; } - if (config.documentType === "text") { + + if (config.documentType === "slide") { + config.documentType = "presentation"; + } + if (config.documentType === "word" || config.documentType === "text") { config.documentType = "doc"; } @@ -311,8 +317,8 @@ iframe.setAttribute('name', 'frameEditor'); iframe.setAttribute('align', 'top'); iframe.setAttribute("src", url); - iframe.setAttribute("width", config.width); - iframe.setAttribute("height", config.height); + iframe.setAttribute("width", config.width || '100%'); + iframe.setAttribute("height", config.height || '100%'); if (config.editorConfig) { // OnlyOffice container.replaceWith(iframe); container = iframe; From b62ff674ef0088a0adae75856e84c13f41af446b Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 6 Sep 2024 11:31:09 +0200 Subject: [PATCH 05/23] API: download document from our server --- www/cryptpad-api.js | 6 +++ www/integration/main.js | 90 +++++++++++++++++++++++++++++------------ 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index e73ef40bd..f4a10d147 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -18,7 +18,9 @@ var scripts = document.getElementsByTagName('script'); for (var i = scripts.length - 1; i >= 0; i--) { var match = scripts[i].src.match(/(.*)web-apps\/apps\/api\/documents\/api.js/i); + var match2 = scripts[i].src.match(/(.*)\/cryptpad-api.js/i); if (match) { return match[1]; } + else if (match2) { return match2[1]; } } }; @@ -114,6 +116,7 @@ chan.send('START', { key: key, application: config.documentType, + url: config.document.url, document: blob, ext: config.document.fileType, autosave: config.autosave || 10 @@ -130,6 +133,9 @@ blob = config.document.blob; return start(); } + return start(); + // XXX use server only when not zero knowledge? i.e. no save handler? + // XXX or when error with client? getBlob(function (err, _blob) { if (err) { reject(err); return console.error(err); } _blob.name = `document.${config.document.fileType}`; diff --git a/www/integration/main.js b/www/integration/main.js index fe3ffd010..eb137c039 100644 --- a/www/integration/main.js +++ b/www/integration/main.js @@ -3,9 +3,10 @@ // SPDX-License-Identifier: AGPL-3.0-or-later define([ + '/api/config', '/common/sframe-common-outer.js', '/common/common-hash.js', -], function (SCO, Hash) { +], function (Config, SCO, Hash) { var getTxid = function () { return Math.random().toString(16).replace('0.', ''); @@ -152,36 +153,73 @@ define([ chan.send('ON_DOWNLOADAS', blob); }; - chan.on('START', function (data) { + + let getInstanceURL = function () { + return Config.httpUnsafeOrigin; + }; + let getBlobServer = function (documentURL, cb) { + let xhr = new XMLHttpRequest(); + let data = encodeURIComponent(documentURL); + let url = getInstanceURL() + '/ooapidl?url=' + data; + console.log(url); + xhr.open('GET', url, true); + xhr.responseType = 'blob'; + //xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.onload = function () { + if (this.status === 200) { + var blob = this.response; + // myBlob is now the blob that the object URL pointed to. + cb(null, blob); + } else { + cb(this.status); + } + }; + xhr.onerror = function (e) { + cb(e.message); + }; + xhr.send(); + }; + chan.on('START', function (data, cb) { console.warn('INNER START', data); var href = Hash.hashToHref(data.key, data.application); console.error(Hash.hrefToHexChannelId(href)); - window.CP_integration_outer = { - pathname: `/${data.application}/`, - hash: data.key, - href: href, - initialState: data.document, - config: { - fileType: data.ext, - autosave: data.autosave - }, - utils: { - onReady: onReady, - onDownloadAs, - setDownloadAs, - save: save, - reload: reload, - onHasUnsavedChanges: onHasUnsavedChanges, - onInsertImage: onInsertImage + let startApp = function (blob) { + window.CP_integration_outer = { + pathname: `/${data.application}/`, + hash: data.key, + href: href, + initialState: blob, + config: { + fileType: data.ext, + autosave: data.autosave + }, + utils: { + onReady: onReady, + onDownloadAs, + setDownloadAs, + save: save, + reload: reload, + onHasUnsavedChanges: onHasUnsavedChanges, + onInsertImage: onInsertImage + } + }; + let path = "/common/sframe-app-outer.js"; + if (['sheet', 'doc', 'presentation'].includes(data.application)) { + path = '/common/onlyoffice/main.js'; } + require([path], function () { + console.warn('SAO REQUIRED'); + delete window.CP_integration_outer; + cb(); + }); }; - let path = "/common/sframe-app-outer.js"; - if (['sheet', 'doc', 'presentation'].includes(data.application)) { - path = '/common/onlyoffice/main.js'; - } - require([path], function () { - console.warn('SAO REQUIRED'); - delete window.CP_integration_outer; + + if (data.document) { return void startApp(data.document); } + getBlobServer(data.url, (err, blob) => { + if (err) { + return void cb({error: err}); + } + startApp(blob); }); }); From 062c1550d884079f4ee7e6f2d8719219de4521d3 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 30 Sep 2024 17:27:15 +0200 Subject: [PATCH 06/23] Fix issue with non-base64 keys --- www/integration/main.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/www/integration/main.js b/www/integration/main.js index 4d252f659..1cbf1f241 100644 --- a/www/integration/main.js +++ b/www/integration/main.js @@ -6,8 +6,10 @@ define([ '/api/config', '/common/sframe-common-outer.js', '/common/common-hash.js', + '/components/tweetnacl/nacl-fast.min.js' ], function (Config, SCO, Hash) { + let Nacl = window.nacl; var getTxid = function () { return Math.random().toString(16).replace('0.', ''); }; @@ -96,9 +98,17 @@ define([ }; http.send(); }; + let sanitizeKey = key => { + try { + Nacl.util.decodeBase64(key); + return key; + } catch (e) { + return Nacl.util.encodeBase64(Nacl.util.decodeUTF8(key)); + } + }; chan.on('GET_SESSION', function (data, cb) { - if (data.keepOld) { - var key = data.key + "000000000000000000000000000000000"; + if (data.keepOld) { // they provide their own key, we must turn it into a hash + var key = sanitizeKey(data.key) + "000000000000000000000000000000000"; console.warn('KEY', key); return void cb({ key: `/2/integration/edit/${key.slice(0,24)}/` @@ -181,6 +191,7 @@ define([ }; chan.on('START', function (data, cb) { console.warn('INNER START', data); + // data.key is a hash var href = Hash.hashToHref(data.key, data.application); if (data.editorConfig.lang) { var LS_LANG = "CRYPTPAD_LANG"; From 66047fed03220962f6b98ea34ced9f48a7b4fdd7 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 1 Oct 2024 12:31:23 +0200 Subject: [PATCH 07/23] Fix onlyoffice out of sync with integration API --- www/common/outer/async-store.js | 1 + 1 file changed, 1 insertion(+) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index af20c0722..704038b2c 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -2684,6 +2684,7 @@ define([ }; */ var loadOnlyOffice = function () { + if (store.onlyoffice) { return; } store.onlyoffice = OnlyOffice.init(store, function (ev, data, clients) { clients.forEach(function (cId) { postMessage(cId, 'OO_EVENT', { From 8edf6001f2dcc2a5b31dca56ed1d83d3757b22e0 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 3 Oct 2024 16:59:55 +0200 Subject: [PATCH 08/23] First version of OO save --- www/common/onlyoffice/inner.js | 12 ++++++ www/common/sframe-common-integration.js | 2 +- www/cryptpad-api.js | 9 ++++- www/integration/main.js | 49 +++++++++++++++++++++---- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index a29c10c6b..4e878005b 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -3230,11 +3230,23 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null }); } integrationChannel.on('Q_INTEGRATION_NEEDSAVE', function (data, cb) { + if (!cfg.autosave) { return; } integrationSave(function (obj) { if (obj && obj.error) { console.error(obj.error); } cb(); }); }); + + if (!cfg.autosave) { + let $save = common.createButton('save', true, {}, function () { + $save.attr('disabled', 'disabled'); + integrationSave(err => { + $save.removeAttr('disabled'); + }); + }); + $('body').prepend($save); + } + if (privateData.initialState && (!content || !content.hashes || !Object.keys(content.hashes).length)) { var blob = privateData.initialState; let title = `document.${cfg.fileType}`; diff --git a/www/common/sframe-common-integration.js b/www/common/sframe-common-integration.js index 8b1ab5374..41d81a7b9 100644 --- a/www/common/sframe-common-integration.js +++ b/www/common/sframe-common-integration.js @@ -17,7 +17,7 @@ define([ var privateData = metadataMgr.getPrivateData(); var config = privateData.integrationConfig; - if (!config.autosave) { return; } + if (!config.autosave) { return void exp; } if (typeof(saveHandler) !== "function") { throw new Error("Incorrect save handler"); } diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index 9e873cba3..efe065618 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -88,6 +88,7 @@ var start = function (config, chan) { return new Promise(function (resolve, reject) { setTimeout(function () { + var docID = config.document.key; var key = config.document.key; var blob; @@ -116,10 +117,12 @@ chan.send('START', { key: key, application: config.documentType, + name: config.document.title, url: config.document.url, + documentKey: docID, document: blob, ext: config.document.fileType, - autosave: config.autosave || 10, + autosave: config.events.onSave && (config.autosave || 10), editorConfig: config.editorConfig || {} }, function (obj) { if (obj && obj.error) { reject(obj.error); return console.error(obj.error); } @@ -134,9 +137,11 @@ blob = config.document.blob; return start(); } - return start(); // XXX use server only when not zero knowledge? i.e. no save handler? // XXX or when error with client? + if (!config.events.onSave) { + return start(); + } getBlob(function (err, _blob) { if (err) { reject(err); return console.error(err); } _blob.name = `document.${config.document.fileType}`; diff --git a/www/integration/main.js b/www/integration/main.js index 1cbf1f241..cffd22a33 100644 --- a/www/integration/main.js +++ b/www/integration/main.js @@ -129,12 +129,6 @@ define([ }); }); - var save = function (obj, cb) { - chan.send('SAVE', obj.blob, function (err) { - if (err) { return cb({error: err}); } - cb(); - }); - }; var reload = function (data) { chan.send('RELOAD', data); }; @@ -171,7 +165,6 @@ define([ let xhr = new XMLHttpRequest(); let data = encodeURIComponent(documentURL); let url = getInstanceURL() + '/ooapidl?url=' + data; - console.log(url); xhr.open('GET', url, true); xhr.responseType = 'blob'; //xhr.setRequestHeader('Content-Type', 'application/json'); @@ -189,6 +182,30 @@ define([ }; xhr.send(); }; + let saveBlobServer = function (cfg, blob, cb) { + let {callbackUrl, name, key} = cfg; + let xhr = new XMLHttpRequest(); + name = encodeURIComponent(name); + callbackUrl = encodeURIComponent(callbackUrl); + key = encodeURIComponent(key); + let query = `name=${name}&cb=${callbackUrl}&key=${key}` + let url = getInstanceURL() + `/oosave?${query}`; + xhr.open('POST', url, true); + xhr.responseType = 'blob'; + //xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.onload = function () { + console.error(this.status); + if (this.status === 200) { + cb(); + } else { + cb(this.status); + } + }; + xhr.onerror = function (e) { + cb(e.message); + }; + xhr.send(blob); + }; chan.on('START', function (data, cb) { console.warn('INNER START', data); // data.key is a hash @@ -198,6 +215,23 @@ define([ localStorage.setItem(LS_LANG, data.editorConfig.lang); } + let fileName = data.name || `document.${data.ext}`; + var save = function (obj, cb) { + let cbUrl = data.editorConfig.callbackUrl; + if (!data.autosave && cbUrl) { + saveBlobServer({ + callbackUrl: cbUrl, + name: fileName, + key: data.documentKey + }, obj.blob, cb); + return; + } + chan.send('SAVE', obj.blob, function (err) { + if (err) { return cb({error: err}); } + cb(); + }); + }; + console.error(Hash.hrefToHexChannelId(href)); let startApp = function (blob) { window.CP_integration_outer = { @@ -206,6 +240,7 @@ define([ href: href, initialState: blob, config: { + fileName: data.name, fileType: data.ext, autosave: data.autosave, user: data.editorConfig.user From 235c04f2ad9148d226d270b8753ddcaebc0533e4 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 29 Oct 2024 11:20:12 +0100 Subject: [PATCH 09/23] Fix placeholder issues --- lib/hk-util.js | 3 ++- www/cryptpad-api.js | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/hk-util.js b/lib/hk-util.js index 93c3f9c0e..069be04a3 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -143,7 +143,8 @@ const dropChannel = HK.dropChannel = function (Env, chanName) { delete Env.channel_cache[chanName]; if (meta && meta.selfdestruct && Env.selfDestructTo) { Env.selfDestructTo[chanName] = setTimeout(function () { - expireChannel(Env, chanName); + if (!Env.store) { return; } + Env.store.archiveChannel(chanName, false, () => {}); }, TEMPORARY_CHANNEL_LIFETIME); } if (Env.store) { Env.store.closeChannel(chanName, function () {}); } diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index e73ef40bd..9dfcc55b9 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -131,7 +131,10 @@ return start(); } getBlob(function (err, _blob) { - if (err) { reject(err); return console.error(err); } + if (err) { // Can't get blob from client, try from server + console.warn(err); + return void start(); + } _blob.name = `document.${config.document.fileType}`; blob = _blob; start(); From 1fb3dbc2ceb421beddc9d7f4addfde518a945e36 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 29 Oct 2024 11:23:17 +0100 Subject: [PATCH 10/23] Fix merge error --- www/cryptpad-api.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index f41aad8ec..6fd6d3be4 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -137,11 +137,6 @@ blob = config.document.blob; return start(); } - // XXX use server only when not zero knowledge? i.e. no save handler? - // XXX or when error with client? - if (!config.events.onSave) { - return start(); - } getBlob(function (err, _blob) { if (err) { // Can't get blob from client, try from server console.warn(err); From 4a881555817333798133a18073f49fc3de0a92a0 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 29 Oct 2024 11:33:45 +0100 Subject: [PATCH 11/23] Fix expire selfdestruct channel --- lib/historyKeeper.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index 7178b5f76..b8ac2ca58 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -67,7 +67,10 @@ module.exports.create = function (Env, cb) { } if (metadata && metadata.selfdestruct && metadata.selfdestruct !== Env.id) { - HK.expireChannel(Env, channelName); + if (Env.store) { + Env.store.archiveChannel(channelName, false, + () => {}); + } return void cb('ESELFDESTRUCT'); } From 3de5784be76daba5a9d0135ddf11d816e94fe279 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 7 Nov 2024 16:23:07 +0100 Subject: [PATCH 12/23] Fix issues with temporary documents --- lib/historyKeeper.js | 5 +---- lib/hk-util.js | 13 ++++++++++--- www/common/outer/async-store.js | 3 ++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index b8ac2ca58..e0a9fea2c 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -67,10 +67,7 @@ module.exports.create = function (Env, cb) { } if (metadata && metadata.selfdestruct && metadata.selfdestruct !== Env.id) { - if (Env.store) { - Env.store.archiveChannel(channelName, false, - () => {}); - } + HK.removeChannel(Env, channelName); return void cb('ESELFDESTRUCT'); } diff --git a/lib/hk-util.js b/lib/hk-util.js index 069be04a3..2ba656376 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -134,6 +134,13 @@ const expireChannel = HK.expireChannel = function (Env, channel) { }); }; +const removeChannel = HK.removeChannel = function (Env, channel) { + if (!Env.store) { return; } + Env.store.archiveChannel(channel, void 0, () => {}); + delete Env.metadata_cache[channel]; + delete Env.channel_cache[channel]; +}; + /* dropChannel * cleans up memory structures which are managed entirely by the historyKeeper */ @@ -143,8 +150,7 @@ const dropChannel = HK.dropChannel = function (Env, chanName) { delete Env.channel_cache[chanName]; if (meta && meta.selfdestruct && Env.selfDestructTo) { Env.selfDestructTo[chanName] = setTimeout(function () { - if (!Env.store) { return; } - Env.store.archiveChannel(chanName, false, () => {}); + removeChannel(Env, chanName); }, TEMPORARY_CHANNEL_LIFETIME); } if (Env.store) { Env.store.closeChannel(chanName, function () {}); } @@ -693,6 +699,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) { }, (err, reason) => { // Any error but ENOENT: abort // ENOENT is allowed in case we want to create a new pad + if (err && err.error) { err = err.error; } if (err && err.code !== 'ENOENT') { if (err.message === "EUNKNOWN") { Log.error("HK_GET_HISTORY", { @@ -708,7 +715,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) { stack: err && err.stack, }); } // FIXME err.message isn't useful for users - const parsedMsg = {error:err.message, channel: channelName, txid: txid}; + const parsedMsg = {error:err.message || 'ERROR', channel: channelName, txid: txid}; Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(parsedMsg)]); return; } diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 704038b2c..f4568dbc9 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1843,7 +1843,7 @@ define([ Store.leavePad(null, data, function () {}); }; var conf = { - Cache: Cache, // ICE pad cache + Cache: store.neverCache ? undefined : Cache, // ICE pad cache onCacheStart: function () { postMessage(clientId, "PAD_CACHE"); }, @@ -3271,6 +3271,7 @@ define([ // ==> don't create a drive // Or "neverDrive" (integration into another platform?) // ==> don't create a drive BUT create temp RPC (we may need to upload) + if (data.neverDrive) { store.neverCache = true; } if (data.neverDrive || (data.noDrive && !data.userHash && !data.anonHash)) { return void onNoDrive(clientId, function (obj) { if (obj && obj.error) { From ced24e655beb511fae94d8047fa3559cd202dc9a Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Dec 2024 16:34:10 +0100 Subject: [PATCH 13/23] Fix API issues --- lib/hk-util.js | 2 +- www/common/outer/async-store.js | 2 +- www/cryptpad-api.js | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/hk-util.js b/lib/hk-util.js index 2ba656376..61374d7a4 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -135,7 +135,7 @@ const expireChannel = HK.expireChannel = function (Env, channel) { }; const removeChannel = HK.removeChannel = function (Env, channel) { - if (!Env.store) { return; } + if (!Env.store) { return; } Env.store.archiveChannel(channel, void 0, () => {}); delete Env.metadata_cache[channel]; delete Env.channel_cache[channel]; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index f4568dbc9..6e37a97c2 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1843,7 +1843,7 @@ define([ Store.leavePad(null, data, function () {}); }; var conf = { - Cache: store.neverCache ? undefined : Cache, // ICE pad cache + Cache: store.neverCache ? undefined : Cache, onCacheStart: function () { postMessage(clientId, "PAD_CACHE"); }, diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index 6fd6d3be4..c398cdb54 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -113,7 +113,7 @@ }; var start = function () { - config.document.key = key; + //config.document.key = key; chan.send('START', { key: key, application: config.documentType, @@ -137,6 +137,7 @@ blob = config.document.blob; return start(); } + // XXX Nextcloud will log us out if we try from the client getBlob(function (err, _blob) { if (err) { // Can't get blob from client, try from server console.warn(err); From 8246195dc7d9a53a44797905a1377be132995fd8 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 6 Feb 2025 15:24:18 +0100 Subject: [PATCH 14/23] Fix config issues --- www/cryptpad-api.js | 2 +- www/integration/main.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index af56913e5..bef2bbe91 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -255,7 +255,7 @@ */ var init = function (cryptpadURL, containerId, config) { // OnlyOffice shim: don't provide a URL - if (!config && typeof(containerId) === "object") { + if (typeof(config) !== "object" && typeof(containerId) === "object") { config = containerId; containerId = cryptpadURL; cryptpadURL = getInstanceURL(); diff --git a/www/integration/main.js b/www/integration/main.js index cffd22a33..4cc88974e 100644 --- a/www/integration/main.js +++ b/www/integration/main.js @@ -103,7 +103,7 @@ define([ Nacl.util.decodeBase64(key); return key; } catch (e) { - return Nacl.util.encodeBase64(Nacl.util.decodeUTF8(key)); + return Nacl.util.encodeBase64(Nacl.util.decodeUTF8(key)).replaceAll('=', ''); } }; chan.on('GET_SESSION', function (data, cb) { From e730be0a36f23db82b72cfd9d4aa63a13a0df1fd Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 11 Feb 2025 15:47:04 +0100 Subject: [PATCH 15/23] More customization --- www/common/onlyoffice/inner.js | 45 ++++++++++++++++++++++++++++------ www/cryptpad-api.js | 22 +++++++++++++++-- www/integration/main.js | 3 ++- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 64a7af21b..313ebf468 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1685,16 +1685,20 @@ define([ || metadataMgr.getUserData().name || Messages.anonymous; + let integrationConfig = privateData?.integrationConfig?._; + let ec = integrationConfig?.editorConfig; + let dc = integrationConfig?.document; + // Config APP.ooconfig = { - "document": { - "fileType": file.type, - "key": "fresh", - "title": file.title, - "url": url, - "permissions": { - "download": false, - "print": true, + document: { + fileType: file.type, + key: "fresh", + title: dc?.title || file.title, + url: url, + permissions: { + download: dc?.permissions?.download || false, + print: dc?.permissions?.print || true, } }, "documentType": file.doc, @@ -2140,6 +2144,31 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null }, void 0, common.getCache()); }; + let copy = (a, b) => { + Object.keys(b).forEach(k => { + if (a[k]) { + if (typeof(a[k]) === "object" && typeof(b[k]) === "object") { + copy(a[k], b[k]); + } + return; + } + a[k] = b[k]; + }); + }; + let integrationConfig = privateData?.integrationConfig?._; + if (integrationConfig) { + let ec = integrationConfig.editorConfig; + copy(APP.ooconfig.editorConfig, ec); + // Open "goback" in new tabs because of csp and + // iframes + if (ec.editorConfig?.customization?.goback) { + let c = APP.ooconfig.editorConfig.customization; + c.goback.blank = true; + } + } + console.error('updated config', APP.ooconfig); + + APP.docEditor = new window.DocsAPI.DocEditor("cp-app-oo-placeholder-a", APP.ooconfig); ooLoaded = true; makeChannel(); diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index bef2bbe91..cf115c32e 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -112,6 +112,18 @@ xhr.send(); }; + let serializedConfig = () => { + let _config = {}; + _config.editorConfig = config.editorConfig; + _config.document = { + permissions: config.document?.permissions, + title: config.document?.title, + info: config.document?.info, + referenceData: config.document?.referenceData + }; + return _config; + }; + var start = function () { //config.document.key = key; chan.send('START', { @@ -123,7 +135,8 @@ document: blob, ext: config.document.fileType, autosave: config.events.onSave && (config.autosave || 10), - editorConfig: config.editorConfig || {} + editorConfig: config.editorConfig || {}, + _config: serializeConfig() }, function (obj) { if (obj && obj.error) { reject(obj.error); return console.error(obj.error); } resolve({}); @@ -137,7 +150,12 @@ blob = config.document.blob; return start(); } - // XXX Nextcloud will log us out if we try from the client + // NOTE: Nextcloud will log us out if we try from the client + // TODO: make sure the server plugin is installed if we don't + // call getBlob() + if (!config.events?.onSave) { + return void start(); + } getBlob(function (err, _blob) { if (err) { // Can't get blob from client, try from server console.warn(err); diff --git a/www/integration/main.js b/www/integration/main.js index 4cc88974e..c23a0840e 100644 --- a/www/integration/main.js +++ b/www/integration/main.js @@ -243,7 +243,8 @@ define([ fileName: data.name, fileType: data.ext, autosave: data.autosave, - user: data.editorConfig.user + user: data.editorConfig.user, + _: data._config }, utils: { onReady: onReady, From 4d180685ccd5d0d34ec82439092f62de27334330 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 11 Feb 2025 15:51:51 +0100 Subject: [PATCH 16/23] Don't override user --- www/common/onlyoffice/inner.js | 2 +- www/cryptpad-api.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 313ebf468..b5ac9e111 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -2146,6 +2146,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null let copy = (a, b) => { Object.keys(b).forEach(k => { + if (k === "user") { return; } // Don't change user values if (a[k]) { if (typeof(a[k]) === "object" && typeof(b[k]) === "object") { copy(a[k], b[k]); @@ -2155,7 +2156,6 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null a[k] = b[k]; }); }; - let integrationConfig = privateData?.integrationConfig?._; if (integrationConfig) { let ec = integrationConfig.editorConfig; copy(APP.ooconfig.editorConfig, ec); diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index cf115c32e..9efd6c569 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -136,7 +136,7 @@ ext: config.document.fileType, autosave: config.events.onSave && (config.autosave || 10), editorConfig: config.editorConfig || {}, - _config: serializeConfig() + _config: serializedConfig() }, function (obj) { if (obj && obj.error) { reject(obj.error); return console.error(obj.error); } resolve({}); From 99934958b082aae3740077710b0759f8c63c9ed5 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 11 Feb 2025 17:30:33 +0100 Subject: [PATCH 17/23] Enable save button --- www/common/onlyoffice/inner.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index b5ac9e111..f36e0fd32 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -953,7 +953,9 @@ define([ const realParticipants = Object.entries(content.ids).map(([id, user]) => { const nId = id.slice(0,32); - const username = (users[nId] || {}).name || Messages.anonymous; + const username = Util.find(privateData, ['integrationConfig', 'user', 'name']) || + (users[nId] || {}).name || Messages.anonymous; + return { id: String(user.ooid) + user.index, idOriginal: String(user.ooid), @@ -1473,6 +1475,18 @@ define([ }); } break; + case "forceSaveStart" + if (APP.integrationSave) { + APP.integrationSave(err => { + if (err) { + console.error(err); + UI.warn(Messages.error); + return; + } + UI.log(Messages.saved); + }); + } + break; case "getLock": handleLock(obj, send); break; @@ -2158,13 +2172,14 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null }; if (integrationConfig) { let ec = integrationConfig.editorConfig; + let c = APP.ooconfig.editorConfig.customization; copy(APP.ooconfig.editorConfig, ec); // Open "goback" in new tabs because of csp and // iframes if (ec.editorConfig?.customization?.goback) { - let c = APP.ooconfig.editorConfig.customization; c.goback.blank = true; } + c.forcesave = true; } console.error('updated config', APP.ooconfig); @@ -3262,7 +3277,8 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null }); }); - if (!cfg.autosave) { + if (!cfg.autosave && false) { + APP.integrationSave = integrationSave; let $save = common.createButton('save', true, {}, function () { $save.attr('disabled', 'disabled'); integrationSave(err => { From 94eb90016cebc9fdd35b8092a5517a5e8a3f6de9 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 11 Feb 2025 17:31:38 +0100 Subject: [PATCH 18/23] Fix typo --- www/common/onlyoffice/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index f36e0fd32..d517374c0 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1475,7 +1475,7 @@ define([ }); } break; - case "forceSaveStart" + case "forceSaveStart": if (APP.integrationSave) { APP.integrationSave(err => { if (err) { From 6bc999573ef7637e9f28a3aa87e2214faf77f555 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 13 Feb 2025 17:14:03 +0100 Subject: [PATCH 19/23] Use OnlyOffice forceSave button --- www/common/onlyoffice/inner.js | 33 +++++++++++++++++-------- www/common/sframe-common-integration.js | 2 +- www/common/sframe-common-outer.js | 2 +- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index d517374c0..69a5130bc 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1477,11 +1477,10 @@ define([ break; case "forceSaveStart": if (APP.integrationSave) { - APP.integrationSave(err => { - if (err) { - console.error(err); - UI.warn(Messages.error); - return; + APP.integrationSave(obj => { + if (obj?.error) { + console.error(obj.error); + return void UI.warn(Messages.error); } UI.log(Messages.saved); }); @@ -3237,13 +3236,17 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null let cfg = privateData.integrationConfig || {}; common.openIntegrationChannel(APP.onLocal); integrationChannel = common.getSframeChannel(); + let hasUnsavedChanges = false; var integrationSave = function (cb) { var ext = cfg.fileType; var upload = Util.once(function (_blob) { integrationChannel.query('Q_INTEGRATION_SAVE', { blob: _blob - }, cb, { + }, obj => { + if (!obj?.error) { hasUnsavedChanges = false; } + cb(obj); + }, { raw: true }); }); @@ -3264,11 +3267,20 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null }; var inte = common.createIntegration(integrationSave, integrationHasUnsavedChanges); - if (inte) { + if (inte && cfg.autosave) { evIntegrationSave.reg(function () { inte.changed(); }); + } else { + APP.integrationSave = integrationSave; + evIntegrationSave.reg(function () { + hasUnsavedChanges = true; + }); } + $(window).on('beforeunload', function (ev) { + if (hasUnsavedChanges) { return false; } + ev.returnValue = ''; + }); integrationChannel.on('Q_INTEGRATION_NEEDSAVE', function (data, cb) { if (!cfg.autosave) { return; } integrationSave(function (obj) { @@ -3277,16 +3289,17 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null }); }); - if (!cfg.autosave && false) { - APP.integrationSave = integrationSave; + /* test button + if (!cfg.autosave) { let $save = common.createButton('save', true, {}, function () { $save.attr('disabled', 'disabled'); - integrationSave(err => { + integrationSave(() => { $save.removeAttr('disabled'); }); }); $('body').prepend($save); } + */ if (privateData.initialState && (!content || !content.hashes || !Object.keys(content.hashes).length)) { var blob = privateData.initialState; diff --git a/www/common/sframe-common-integration.js b/www/common/sframe-common-integration.js index 41d81a7b9..8b1ab5374 100644 --- a/www/common/sframe-common-integration.js +++ b/www/common/sframe-common-integration.js @@ -17,7 +17,7 @@ define([ var privateData = metadataMgr.getPrivateData(); var config = privateData.integrationConfig; - if (!config.autosave) { return void exp; } + if (!config.autosave) { return; } if (typeof(saveHandler) !== "function") { throw new Error("Incorrect save handler"); } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 8138aec2d..4917ccefb 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -2242,7 +2242,7 @@ define([ } }; - // on server crash, try to save to Nextcloud + // on server crash, try to save to the outer platform if (ready) { return integrationSave(reload); } // if error during loading, reload without saving From cf003140b7c0ff6059bd3081b0eb873e02a4df93 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 25 Feb 2025 16:28:40 +0100 Subject: [PATCH 20/23] Tell other members in integration session when you save --- www/common/onlyoffice/inner.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 69a5130bc..219798d7c 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1482,6 +1482,9 @@ define([ console.error(obj.error); return void UI.warn(Messages.error); } + content.integrationSave = `${myUniqueOOId}-${+new Date()}`; + APP.integrationSaved = content.integrationSave; + APP.onLocal(); UI.log(Messages.saved); }); } @@ -3273,6 +3276,9 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null }); } else { APP.integrationSave = integrationSave; + APP.integrationSetSaved = () => { + hasUnsavedChanges = false; + }; evIntegrationSave.reg(function () { hasUnsavedChanges = true; }); @@ -3438,6 +3444,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null var wasMigrating = content.migration; var myLocks = getUserLock(getId(), true); + var integrationSave = content.integrationSave; content = json.content; @@ -3448,6 +3455,15 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null checkCheckpoint(); } + // Integration: mark the current content as saved + // if manually saved by someone else + if (content.integrationSave !== APP.integrationSaved) { + APP.integrationSaved = content.integrationSave; + if (APP.integrationSetSaved) { + APP.integrationSetSaved(); + } + } + var editor = getEditor(); if (content.hashes) { var latest = getLastCp(true); From 79cab48f5512f7a4706c597ac5b6bfe18006b3b3 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 25 Feb 2025 16:53:38 +0100 Subject: [PATCH 21/23] Integration API add missing username --- www/common/sframe-common-outer.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 4917ccefb..58d3023ff 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -842,6 +842,13 @@ define([ additionalPriv.initialState = cfg.initialState instanceof Blob ? cfg.initialState : undefined; + if (cfg.integrationConfig) { + if (metaObj?.user && !metaObj.user.name) { + metaObj.user.name = cfg.integrationConfig?.user?.name || + cfg.integrationConfig?.user?.firstname; + } + } + // Early access var priv = metaObj.priv; var _plan = typeof(priv.plan) === "undefined" ? Utils.LocalStore.getPremium() : priv.plan; From c1eb48c8b846f7a36d791ce9d59b1c8cfdff985f Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 25 Feb 2025 16:57:29 +0100 Subject: [PATCH 22/23] Update dependencies --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4c6655198..e15dcb7d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "chainpad": "^5.2.6", "chainpad-crypto": "^0.2.5", "chainpad-listmap": "^1.1.0", - "chainpad-netflux": "^1.2.0", + "chainpad-netflux": "^1.2.2", "chainpad-server": "^5.2.4", "ckeditor": "npm:ckeditor4@~4.22.1", "codemirror": "^5.19.0", @@ -1443,9 +1443,10 @@ } }, "node_modules/chainpad-netflux": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/chainpad-netflux/-/chainpad-netflux-1.2.0.tgz", - "integrity": "sha512-j3qzrL/tugpTNQTk1I7VMZuQJHAYRNmFaiAxLbEFre/gLIPPJqM4gHXuyTU2OFzIFSd5m5i4G3+GwZ32jLV3cA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/chainpad-netflux/-/chainpad-netflux-1.2.2.tgz", + "integrity": "sha512-fcKugW29BE4wo4l3WYLc56yeFznu15bGi6tU3uPtsLGSExXGqNwS+3kmpzI0AFAsuoI0OKPeT4uLX4YzKT/D+A==", + "license": "LGPL-2.1", "dependencies": { "netflux-websocket": "^1.2.0" } diff --git a/package.json b/package.json index 9ddd23123..b57289dd8 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "chainpad": "^5.2.6", "chainpad-crypto": "^0.2.5", "chainpad-listmap": "^1.1.0", - "chainpad-netflux": "^1.2.0", + "chainpad-netflux": "^1.2.2", "chainpad-server": "^5.2.4", "ckeditor": "npm:ckeditor4@~4.22.1", "codemirror": "^5.19.0", From 5bcc0ceb53ea8fca195975f13d2ae520abbca332 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 26 Feb 2025 18:21:20 +0100 Subject: [PATCH 23/23] lint compliance --- www/common/onlyoffice/inner.js | 4 ++-- www/integration/main.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 219798d7c..0c94aca71 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1702,7 +1702,7 @@ define([ || Messages.anonymous; let integrationConfig = privateData?.integrationConfig?._; - let ec = integrationConfig?.editorConfig; + //let ec = integrationConfig?.editorConfig; let dc = integrationConfig?.document; // Config @@ -3444,7 +3444,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null var wasMigrating = content.migration; var myLocks = getUserLock(getId(), true); - var integrationSave = content.integrationSave; + //var integrationSave = content.integrationSave; content = json.content; diff --git a/www/integration/main.js b/www/integration/main.js index c23a0840e..7db6c73c7 100644 --- a/www/integration/main.js +++ b/www/integration/main.js @@ -188,7 +188,7 @@ define([ name = encodeURIComponent(name); callbackUrl = encodeURIComponent(callbackUrl); key = encodeURIComponent(key); - let query = `name=${name}&cb=${callbackUrl}&key=${key}` + let query = `name=${name}&cb=${callbackUrl}&key=${key}`; let url = getInstanceURL() + `/oosave?${query}`; xhr.open('POST', url, true); xhr.responseType = 'blob';