Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/richdocuments.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Controller/WopiController.php2
-rw-r--r--src/services/collabora.js80
-rw-r--r--src/view/Office.vue97
3 files changed, 165 insertions, 14 deletions
diff --git a/lib/Controller/WopiController.php b/lib/Controller/WopiController.php
index d01db98f..07bd8a38 100644
--- a/lib/Controller/WopiController.php
+++ b/lib/Controller/WopiController.php
@@ -195,7 +195,7 @@ class WopiController extends Controller {
'UserCanRename' => !$isPublic && !$isVersion,
'EnableInsertRemoteImage' => true,
'EnableShare' => $file->isShareable() && !$isVersion,
- 'HideUserList' => 'desktop',
+ 'HideUserList' => '',
'DisablePrint' => $wopi->getHideDownload(),
'DisableExport' => $wopi->getHideDownload(),
'DisableCopy' => $wopi->getHideDownload(),
diff --git a/src/services/collabora.js b/src/services/collabora.js
new file mode 100644
index 00000000..8be1612c
--- /dev/null
+++ b/src/services/collabora.js
@@ -0,0 +1,80 @@
+/*
+ * @copyright Copyright (c) 2021 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+import { getCapabilities } from '@nextcloud/capabilities'
+import axios from '@nextcloud/axios'
+
+export const isCollaboraConfigured = () => {
+ const collaboraCapabilities = getCapabilities()?.richdocuments?.collabora
+ return isBuiltinCodeServerUsed() || collaboraCapabilities.length !== 0
+}
+
+export const isBuiltinCodeServerUsed = () => {
+ const richdocumentsCapabilities = getCapabilities()?.richdocuments
+ return richdocumentsCapabilities?.config?.wopi_url?.indexOf('proxy.php') !== -1
+}
+
+let proxyStatusCheckRetry = 0
+export const checkProxyStatus = async(_resolve, _reject) => {
+
+ const wopiUrl = getCapabilities()?.richdocuments?.config?.wopi_url
+ if (!wopiUrl) {
+ throw Error(t('richdocuments', 'Collabora is not configured'))
+ }
+
+ if (wopiUrl.indexOf('proxy.php') === -1) {
+ return true
+ }
+
+ const url = wopiUrl.substr(0, wopiUrl.indexOf('proxy.php') + 'proxy.php'.length)
+ const proxyStatusUrl = url + '?status'
+
+ const checkProxyStatusCallback = async(resolve, reject) => {
+ const result = await axios.get(proxyStatusUrl)
+ if (!result || !result.status) {
+ reject('Failed to contact status endpoint')
+ }
+
+ if (result.status === 'OK') {
+ return resolve(true)
+ }
+ if (result.status === 'error') {
+ return reject(t('richdocuments', 'Built-in CODE server failed to start'))
+ }
+
+ if (proxyStatusCheckRetry < 3 && (result.status === 'starting' || result.status === 'stopped' || result.status === 'restarting')) {
+ setTimeout(() => {
+ proxyStatusCheckRetry++
+ checkProxyStatus(resolve, reject)
+ })
+ } else {
+ reject('Maximum retries reached')
+ }
+
+ }
+
+ if (_reject && _resolve) {
+ return checkProxyStatusCallback(_reject, _resolve)
+ } else {
+ return new Promise(checkProxyStatusCallback)
+ }
+}
diff --git a/src/view/Office.vue b/src/view/Office.vue
index 9a847935..78b567af 100644
--- a/src/view/Office.vue
+++ b/src/view/Office.vue
@@ -22,9 +22,19 @@
<template>
<transition name="fade" appear>
- <div v-show="!loading" id="richdocuments-wrapper">
- <div class="header">
- <!-- This is obviously not the way to go since it would require absolute positioning and therefore not be compatible with viewer actions/sidebar -->
+ <div id="richdocuments-wrapper">
+ <div v-if="showLoadingIndicator" id="cool-loading-overlay" :class="{ debug: debug }">
+ <EmptyContent v-if="!error" icon="icon-loading">
+ Loading document
+ </EmptyContent>
+ <EmptyContent v-else icon="icon-error">
+ {{ t('richdocuments', 'Document loading failed') }}
+ <template #desc>
+ {{ errorMessage }}
+ </template>
+ </EmptyContent>
+ </div>
+ <div v-show="!useNativeHeader && showIframe" class="header">
<div class="avatars">
<Avatar v-for="view in avatarViews"
:key="view.ViewId"
@@ -38,7 +48,10 @@
<ActionButton icon="icon-menu-sidebar" @click="share" />
</Actions>
</div>
- <iframe id="collaboraframe" ref="documentFrame" :src="src" />
+ <iframe v-show="showIframe"
+ id="collaboraframe"
+ ref="documentFrame"
+ :src="src" />
</div>
</transition>
</template>
@@ -47,23 +60,38 @@
import Avatar from '@nextcloud/vue/dist/Components/Avatar'
import Actions from '@nextcloud/vue/dist/Components/Actions'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
+import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
import { basename, dirname } from 'path'
import { getDocumentUrlForFile, getDocumentUrlForPublicFile } from '../helpers/url'
import PostMessageService from '../services/postMessage.tsx'
import FilesAppIntegration from './FilesAppIntegration'
+import { checkProxyStatus } from '../services/collabora'
const FRAME_DOCUMENT = 'FRAME_DOCUMENT'
const PostMessages = new PostMessageService({
FRAME_DOCUMENT: () => document.getElementById('collaboraframe').contentWindow,
})
+const LOADING_STATE = {
+ LOADING: 0,
+ FRAME_READY: 1,
+ DOCUMENT_READY: 2,
+
+ FAILED: -1,
+}
+
+const LOADING_ERROR = {
+ PROXY_FAILED: 1,
+}
+
export default {
name: 'Office',
components: {
Avatar,
Actions,
ActionButton,
+ EmptyContent,
},
props: {
filename: {
@@ -83,11 +111,15 @@ export default {
data() {
return {
src: null,
- loading: true,
+ loading: LOADING_STATE.LOADING,
+ error: null,
views: [],
}
},
computed: {
+ useNativeHeader() {
+ return true
+ },
avatarViews() {
return this.views
},
@@ -98,9 +130,34 @@ export default {
'border-style': 'solid',
})
},
+ showIframe() {
+ return this.loading >= LOADING_STATE.FRAME_READY
+ },
+ showLoadingIndicator() {
+ return this.loading !== LOADING_STATE.DOCUMENT_READY
+ },
+ errorMessage() {
+ switch (this.error) {
+ case LOADING_ERROR.PROXY_FAILED:
+ return t('richdocuments', 'Starting the built-in CODE server failed')
+ default:
+ return t('richdocuments', 'Unknown error')
+ }
+ },
+ debug() {
+ return !!window.TESTING
+ },
},
- mounted() {
- const fileList = OCA?.Files?.App?.getCurrentFileList()
+ async mounted() {
+ try {
+ await checkProxyStatus()
+ } catch (e) {
+ this.error = LOADING_ERROR.PROXY_FAILED
+ this.loading = LOADING_STATE.FAILED
+ return
+ }
+
+ const fileList = OCA?.Files?.App?.getCurrentFileList?.()
FilesAppIntegration.init({
fileName: basename(this.filename),
fileId: this.fileid,
@@ -120,15 +177,14 @@ export default {
case 'App_LoadingStatus':
if (args.Status === 'Frame_Ready') {
// defer showing the frame until collabora has finished also loading the document
- this.loading = false
+ this.loading = LOADING_STATE.FRAME_READY
this.$emit('update:loaded', true)
FilesAppIntegration.initAfterReady()
}
if (args.Status === 'Document_Loaded') {
- this.loading = false
- this.$emit('update:loaded', true)
+ this.loading = LOADING_STATE.DOCUMENT_READY
} else if (args.Status === 'Failed') {
- this.loading = false
+ this.loading = LOADING_STATE.FAILED
this.$emit('update:loaded', true)
}
break
@@ -175,7 +231,7 @@ export default {
if (isPublic) {
this.src = getDocumentUrlForPublicFile(this.filename, this.fileid)
}
- this.loading = true
+ this.loading = LOADING_STATE.LOADING
},
async share() {
if (OCA.Files.Sidebar) {
@@ -186,16 +242,31 @@ export default {
}
</script>
<style lang="scss">
+ #cool-loading-overlay {
+ border-top: 3px solid var(--color-primary-element);
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ z-index: 1;
+ top: 0;
+ left: 0;
+ background-color: #fff;
+ &.debug {
+ opacity: .5;
+ }
+ }
+
.header {
position: absolute;
right: 44px;
top: 3px;
z-index: 99999;
display: flex;
+ background-color: #fff;
.avatars {
display: flex;
- padding: 6px;
+ padding: 4px;
.avatardiv {
margin-left: -15px;