diff options
-rw-r--r-- | docs/frontend-integration.md | 87 | ||||
-rw-r--r-- | package-lock.json | 64 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/document.js | 24 | ||||
-rw-r--r-- | src/files.js | 26 | ||||
-rw-r--r-- | src/view/FilesAppIntegration.js | 16 |
6 files changed, 198 insertions, 20 deletions
diff --git a/docs/frontend-integration.md b/docs/frontend-integration.md index ce9e6c02..84948c64 100644 --- a/docs/frontend-integration.md +++ b/docs/frontend-integration.md @@ -149,3 +149,90 @@ The following code shows an example on how to register the different handlers: })() ``` + +## Events + +Events are emitted through the `@nextcloud/event-bus` for the different document loading states. + +### Files app scope + +When Collabora files are opened through the files app integration (either in the files app or on public share pages), the following events will be emitted to be able to track document opening progress and its success or failure state. + +``` +subscribe('richdocuments:file-open:started', data => console.error('richdocuments:file-open:started', data)) +subscribe('richdocuments:file-open:succeeded', data => console.error('richdocuments:file-open:succeeded', data)) +subscribe('richdocuments:file-open:failed', data => console.error('richdocuments:file-open:failed', data)) +``` + +Event names: +- richdocuments:file-open:started +- richdocuments:file-open:succeeded +- richdocuments:file-open:failed + +Data properties: + + +- currentUser (string|null): user id of the currently logged-in user +- file (object) + - fileId (integer|undefined): undefined for single public shares, otherwise the file id is available + - filePath (string): "/testfile.odt" + - sharingToken (string|undefined): Optional sharing token when opened on a public share link +- reason (string): Only for a failed event, timeout|collabora +- collaboraResponse (object); Optional when reason is collabora + +Example succeeded event data: +``` +{ + "currentUser": "admin", + "file": { + "fileId": "204", + "filePath": "/test.odt", + "sharingToken": undefined + } +} +``` + +Example failed event data: +``` +{ + "reason": "collabora", + "collaboraResponse": "WOPI::GetFile failed: []", + "currentUser": "admin", + "file": { + "fileId": "204", + "filePath": "/test.odt", + "sharingToken": undefined + } +} +``` + +### Document scope + +The event in the document scope is also emitted on direct editing through mobile apps, as there the files app integration is not available. + +``` +subscribe('richdocuments:wopi-load:started', data => console.error('richdocuments:wopi-load:started', data)) +subscribe('richdocuments:wopi-load:succeeded', data => console.error('richdocuments:wopi-load:succeeded', data)) +subscribe('richdocuments:wopi-load:failed', data => console.error('richdocuments:wopi-load:failed', data)) +``` + +Event names: +- richdocuments:wopi-load:started +- richdocuments:wopi-load:succeeded +- richdocuments:wopi-load:failed + +Data properties: + +- wopiFileId (string): file id constructed to be used by Collabora for the WOPI access +- reason (string): Only for a failed event, timeout|collabora +- collaboraResponse (string): Optional when failure reason is collabora + + +Example failed event data: +``` +{ + "reason": "collabora", + "collaboraResponse": "WOPI::GetFile failed: []", + "wopiFileId": "288_oct9lk1km6wy" +} +``` diff --git a/package-lock.json b/package-lock.json index e76212ce..538227cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4416,24 +4416,40 @@ } }, "@nextcloud/event-bus": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-1.1.4.tgz", - "integrity": "sha512-It27KzmUaSQ7w22nHFwOn8XgeVG0HYYOSNG9gs4UkP5VqcZ16m4ydt3GkMpWcyFec4OUjJc+yf7omRc3pNxsSw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-1.2.0.tgz", + "integrity": "sha512-pNS0R6Mvgj4WnbJQ8LYjxRjCbRndpwjHNyZYm0zl8U71gbHsUvQIIzTdW7WYg6Nz/FjAlrdmDXJDFLh1DDcIFA==", "requires": { - "@types/semver": "^6.2.1", + "@types/semver": "^7.1.0", "core-js": "^3.6.2", - "semver": "^6.3.0" + "semver": "^7.3.2" }, "dependencies": { "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.9.0.tgz", + "integrity": "sha512-PyFBJaLq93FlyYdsndE5VaueA9K5cNB7CGzeCj191YYLhkQM0gdZR2SKihM70oF0wdqKSKClv/tEBOpoRmdOVQ==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -4766,9 +4782,9 @@ "dev": true }, "@types/semver": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.1.tgz", - "integrity": "sha512-+beqKQOh9PYxuHvijhVl+tIHvT6tuwOrE9m14zd+MT2A38KoKZhh7pYJ0SNleLtwDsiIxHDsIk9bv01oOxvSvA==" + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==" }, "@types/stack-utils": { "version": "1.0.1", @@ -10558,7 +10574,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "hammerjs": { "version": "2.0.8", @@ -11226,7 +11243,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", - "dev": true + "dev": true, + "optional": true }, "is-extendable": { "version": "0.1.1", @@ -16652,6 +16670,7 @@ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", "is-wsl": "^2.2.0", @@ -16666,6 +16685,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "requires": { "is-docker": "^2.0.0" } @@ -16675,6 +16695,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "optional": true, "requires": { "yallist": "^4.0.0" } @@ -16684,6 +16705,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, + "optional": true, "requires": { "lru-cache": "^6.0.0" } @@ -16692,13 +16714,15 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "dev": true, + "optional": true }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "optional": true, "requires": { "isexe": "^2.0.0" } @@ -16707,7 +16731,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "optional": true } } }, @@ -19104,7 +19129,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "sigmund": { "version": "1.0.1", diff --git a/package.json b/package.json index 14c67122..2ad14a50 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@nextcloud/auth": "^1.3.0", "@nextcloud/axios": "^1.6.0", "@nextcloud/capabilities": "^1.0.2", + "@nextcloud/event-bus": "^1.2.0", "@nextcloud/l10n": "^1.4.1", "@nextcloud/router": "^1.2.0", "@nextcloud/vue": "^3.5.4", diff --git a/src/document.js b/src/document.js index 145a7759..4d000da0 100644 --- a/src/document.js +++ b/src/document.js @@ -1,3 +1,4 @@ +import { emit } from '@nextcloud/event-bus' import { getRootUrl } from '@nextcloud/router' import { getRequestToken } from '@nextcloud/auth' import Config from './services/config' @@ -275,12 +276,27 @@ const documentsMain = { $('#mainContainer').append(form) $('#mainContainer').append(frame) + emit('richdocuments:wopi-load:started', { + wopiFileId: fileId + }) // Listen for App_LoadingStatus as soon as possible $('#loleafletframe').ready(function() { const editorInitListener = ({ parsed, data }) => { console.debug('[document] editorInitListener: Received post message ', parsed) const { msgId, args } = parsed + if (msgId === 'Action_Load_Resp') { + if (!parsed.args.success) { + emit('richdocuments:wopi-load:failed', { + reason: 'collabora', + collaboraResponse: parsed?.args?.errorMsg, + wopiFileId: fileId + }) + } + if (!isDirectEditing()) { + PostMessages.sendPostMessage('parent', data) + } + } if (msgId !== 'App_LoadingStatus') { return } @@ -312,10 +328,18 @@ const documentsMain = { PostMessages.sendWOPIPostMessage('loolframe', 'Hide_Menu_Item', { id: 'insertgraphicremote' }) } + emit('richdocuments:wopi-load:succeeded', { + wopiFileId: fileId + }) break case 'Failed': // Loading failed but editor shows the error documentsMain.isFrameReady = true + emit('richdocuments:wopi-load:failed', { + reason: 'collabora', + collaboraResponse: 'App_LoadingStatus Failed', + wopiFileId: fileId + }) break } } diff --git a/src/files.js b/src/files.js index fd5e8dd4..7868159d 100644 --- a/src/files.js +++ b/src/files.js @@ -1,3 +1,4 @@ +import { emit } from '@nextcloud/event-bus' import { getDocumentUrlFromTemplate, getDocumentUrlForPublicFile, getDocumentUrlForFile } from './helpers/url' import PostMessageService from './services/postMessage' import Config from './services/config' @@ -174,12 +175,16 @@ const odfViewer = { FilesAppIntegration.init({ fileName, fileId, + filePath: fileDir, fileList: context ? context.fileList : undefined, fileModel: context ? context.fileModel : undefined, sendPostMessage: (msgId, values) => { PostMessages.sendWOPIPostMessage(FRAME_DOCUMENT, msgId, values) } }) + emit('richdocuments:file-open:started', { + ...FilesAppIntegration.loggingContext() + }) }) }, @@ -217,6 +222,10 @@ const odfViewer = { onTimeout: function() { if (!odfViewer.receivedLoading && !odfViewer.isProxyStarting) { + emit('richdocuments:file-open:failed', { + reason: 'timeout', + ...FilesAppIntegration.loggingContext() + }) odfViewer.onClose() OC.Notification.showTemporary(t('richdocuments', 'Failed to load {productName} - please try again later', { productName: OC.getCapabilities().richdocuments.productName || 'Collabora Online' })) } else if (!odfViewer.receivedLoading) { @@ -319,8 +328,25 @@ $(document).ready(function() { case 'loading': odfViewer.onReceiveLoading() break + case 'Action_Load_Resp': + if (!args?.success) { + emit('richdocuments:file-open:failed', { + reason: 'collabora', + collaboraResponse: parsed?.args?.errorMsg, + ...FilesAppIntegration.loggingContext() + }) + } else { + emit('richdocuments:file-open:succeeded', { + ...FilesAppIntegration.loggingContext() + }) + } + break case 'App_LoadingStatus': if (args.Status === 'Timeout') { + emit('richdocuments:file-open:failed', { + reason: 'timeout', + ...FilesAppIntegration.loggingContext() + }) odfViewer.onClose() OC.Notification.showTemporary(t('richdocuments', 'Failed to connect to {productName}. Please try again later or contact your server administrator.', { productName: OC.getCapabilities().richdocuments.productName } diff --git a/src/view/FilesAppIntegration.js b/src/view/FilesAppIntegration.js index 01e41ba9..6a657c8f 100644 --- a/src/view/FilesAppIntegration.js +++ b/src/view/FilesAppIntegration.js @@ -34,6 +34,8 @@ export default { fileList: undefined, + filePath: undefined, + /* Views: people currently editing the file */ views: {}, @@ -50,10 +52,11 @@ export default { } }, - init({ fileName, fileId, sendPostMessage, fileList, fileModel }) { + init({ fileName, fileId, filePath, sendPostMessage, fileList, fileModel }) { this.fileName = fileName this.fileId = fileId this.fileList = fileList + this.filePath = filePath this.fileModel = fileModel this.sendPostMessage = sendPostMessage @@ -595,6 +598,17 @@ export default { NewFileMenu._openTemplatePicker(Preload.create.type, fileType.mime, Preload.create.filename + '.' + fileType.extension) }) }, 250) + }, + + loggingContext: function() { + return { + currentUser: OC.getCurrentUser()?.uid, + file: { + sharingToken: document.getElementById('sharingToken')?.value, + fileId: this.fileId, + filePath: (this.filePath ?? '') + '/' + this.fileName + } + } } } |