diff --git a/customize.dist/template.js b/customize.dist/template.js index b71136e71..de72a7c9c 100644 --- a/customize.dist/template.js +++ b/customize.dist/template.js @@ -6,12 +6,18 @@ define([ 'jquery', '/common/hyperscript.js', '/customize/pages.js', + '/customize/application_config.js', '/components/nthen/index.js', -], function ($, h, Pages, nThen) { +], function ($, h, Pages, AppConfig, nThen) { // we consider that there is no valid reason to load any of the info pages // in an iframe. abort everything if you detect that you are embedded. if (window.top !== window) { return; } + if (AppConfig.integrationOnly) { + window.location.href = '/customize/404.html'; + return; + } + $(function () { var $body = $('body'); var pathname = location.pathname; diff --git a/www/common/application_config_internal.js b/www/common/application_config_internal.js index 9012ac5e1..d74378505 100644 --- a/www/common/application_config_internal.js +++ b/www/common/application_config_internal.js @@ -233,6 +233,11 @@ const factory = () => { // NOTE: this is only enforced client-side and will not prevent malicious clients from storing data AppConfig.disableAnonymousPadCreation = false; + // If your application is meant to be used exclusively with the + // integration API, you can disable the direct access to the app by + // setting integrationOnly to true + AppConfig.integrationOnly = false; + // Hide the usage bar in settings and drive //AppConfig.hideUsageBar = true; diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 6adfde21a..3df2cfe92 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1913,6 +1913,9 @@ define([ mediasData: mediasData }, function (err, obj) { if (err || !obj || !obj.data) { + if (integrationChannel) { + integrationChannel.event('EV_INTEGRATION_ERROR', 'X2T_ERROR'); + } UI.alert(Messages.oo_couldNotConvertDocument, cb); return; } @@ -1969,6 +1972,9 @@ define([ const onError = function() { console.error(arguments); + if (integrationChannel) { + integrationChannel.event('EV_INTEGRATION_ERROR', 'DOCUMENT_ERROR'); + } if (APP.isDownload) { var sframeChan = common.getSframeChannel(); sframeChan.event('EV_OOIFRAME_DONE', ''); @@ -2074,9 +2080,7 @@ define([ delete APP.oldCursor; } if (integrationChannel) { - APP.onDocumentUnlock = () => { - integrationChannel.event('EV_INTEGRATION_READY'); - }; + integrationChannel.event('EV_INTEGRATION_READY'); } } delete APP.startNew; @@ -2382,7 +2386,7 @@ define([ if (ec.editorConfig?.customization?.goback) { c.goback.blank = true; } - if (!privateData?.integrationConfig?.autosave) { + if (typeof(privateData?.integrationConfig?.autosave) !== "number") { c.forcesave = true; } } @@ -3706,6 +3710,11 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null cb(); }); }); + integrationChannel.on('EV_INTEGRATION_MANUAL_SAVE', function () { + integrationSave(function () { + console.log('Integration manual save'); + }); + }); /* test button if (!cfg.autosave) { diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 5e900c951..edea3e002 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -662,6 +662,11 @@ define([ cb(); }); }); + integrationChannel.on('EV_INTEGRATION_MANUAL_SAVE', function () { + integrationSave(function () { + console.log('Integration manual save'); + }); + }); } } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 5b5362dbb..8c2754192 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -837,6 +837,10 @@ define([ !Utils.PadTypes.isAvailable(parsed.type)) { additionalPriv.disabledApp = true; } + if (AppConfig.integrationOnly && !cfg.integration) { + additionalPriv.disabledApp = true; + } + if (!Utils.LocalStore.isLoggedIn() && AppConfig.registeredOnlyTypes.indexOf(parsed.type) !== -1 && parsed.type !== "file") { @@ -2122,6 +2126,11 @@ define([ cfg.integrationUtils.onHasUnsavedChanges(obj, cb); } }); + sframeChan.on('Q_INTEGRATION_ERROR', function (obj) { + if (cfg.integrationUtils && cfg.integrationUtils.onError) { + cfg.integrationUtils.onError(obj); + } + }); sframeChan.on('Q_INTEGRATION_ON_INSERT_IMAGE', function (data, cb) { if (cfg.integrationUtils && cfg.integrationUtils.onInsertImage) { cfg.integrationUtils.onInsertImage(data, cb); @@ -2136,6 +2145,11 @@ define([ cfg.integrationUtils.setDownloadAs(format => { sframeChan.event('EV_INTEGRATION_DOWNLOADAS', format); }); + if (cfg.integrationUtils.setSave) { + cfg.integrationUtils.setSave(() => { + sframeChan.event('EV_INTEGRATION_MANUAL_SAVE'); + }); + } } } diff --git a/www/cryptpad-api.js b/www/cryptpad-api.js index 03470c044..d5e5ee288 100644 --- a/www/cryptpad-api.js +++ b/www/cryptpad-api.js @@ -126,6 +126,7 @@ var start = function () { //config.document.key = key; + const autosave = typeof(config.autosave) === "number" ? config.autosave : 10; chan.send('START', { key: key, application: config.documentType, @@ -134,7 +135,7 @@ documentKey: docID, document: blob, ext: config.document.fileType, - autosave: config.events.onSave && (config.autosave || 10), + autosave: config.events.onSave && autosave, readOnly: config.mode === 'view', editorConfig: config.editorConfig || {}, _config: serializedConfig() @@ -216,6 +217,12 @@ }); }); + chan.on('DOCUMENT_ERROR', function (err) { + if (config.events.onError) { + config.events.onError(err); + } + }); + chan.on('ON_DOWNLOADAS', blob => { let url = URL.createObjectURL(blob); if (!config.events.onDownloadAs) { return; } @@ -383,6 +390,15 @@ chan.send('DOWNLOAD_AS', arg); }; + ret.save = () => { + if (!chan) { + return void onDocumentReady.push(() => { + ret.save(); + }); + } + + chan.send('MANUAL_SAVE'); + }; return ret; }; diff --git a/www/integration/main.js b/www/integration/main.js index b82bb52c3..c9a70d2d6 100644 --- a/www/integration/main.js +++ b/www/integration/main.js @@ -138,6 +138,9 @@ define([ var onInsertImage = function (data, cb) { chan.send('ON_INSERT_IMAGE', data, cb); }; + var onError = function (err) { + chan.send('DOCUMENT_ERROR', err); + }; var onReady = function () { chan.send('DOCUMENT_READY', {}); }; @@ -157,6 +160,18 @@ define([ chan.send('ON_DOWNLOADAS', blob); }; + let manualSave; + chan.on('MANUAL_SAVE', function (format) { + if (typeof(manualSave) !== "function") { + console.error('UNSUPPORTED COMMAND', 'save'); + return; + } + manualSave(); + }); + let setSave = f => { + manualSave = f; + }; + let getInstanceURL = function () { return Config.httpUnsafeOrigin; @@ -254,9 +269,11 @@ define([ _: data._config }, utils: { + onError, onReady: onReady, onDownloadAs, setDownloadAs, + setSave, save: save, reload: reload, onHasUnsavedChanges: onHasUnsavedChanges,