From cedbdb296c69d8a91f1369d2abde605670d3b429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Thu, 14 May 2020 08:00:33 +0200 Subject: Basic viewer integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- appinfo/app.php | 5 +- lib/AppInfo/Application.php | 9 + src/files.js | 517 +++++++++++++++++++++++++++++++++++++++ src/view/FilesAppIntegration.js | 4 +- src/view/Office.vue | 166 +++++++++++++ src/viewer.js | 518 +--------------------------------------- templates/documents.php | 2 +- webpack.common.js | 1 + 8 files changed, 710 insertions(+), 512 deletions(-) create mode 100644 src/files.js create mode 100644 src/view/Office.vue diff --git a/appinfo/app.php b/appinfo/app.php index 9785bc7d..087b31de 100644 --- a/appinfo/app.php +++ b/appinfo/app.php @@ -37,14 +37,13 @@ $eventDispatcher = \OC::$server->getEventDispatcher(); $eventDispatcher->addListener( 'OCA\Files::loadAdditionalScripts', function() { - \OCP\Util::addScript('richdocuments', 'viewer'); - \OCP\Util::addStyle('richdocuments', 'viewer'); + \OCP\Util::addScript('richdocuments', 'files'); } ); $eventDispatcher->addListener( 'OCA\Files_Sharing::loadAdditionalScripts', function() { - \OCP\Util::addScript('richdocuments', 'viewer'); + \OCP\Util::addScript('richdocuments', 'files'); } ); diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index b9ca89f5..bf8237f0 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -34,7 +34,9 @@ use OCA\Richdocuments\Preview\OOXML; use OCA\Richdocuments\Preview\OpenDocument; use OCA\Richdocuments\Preview\Pdf; use OCA\Richdocuments\Service\FederationService; +use OCA\Viewer\Event\LoadViewer; use OCP\AppFramework\App; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IPreview; class Application extends App { @@ -44,6 +46,12 @@ class Application extends App { public function __construct(array $urlParams = array()) { parent::__construct(self::APPNAME, $urlParams); + /** @var IEventDispatcher $eventDispatcher */ + $eventDispatcher = $this->getContainer()->getServer()->query(IEventDispatcher::class); + $eventDispatcher->addListener(LoadViewer::class, function () { + \OCP\Util::addScript('richdocuments', 'viewer'); + }); + $this->getContainer()->registerCapability(Capabilities::class); } @@ -93,6 +101,7 @@ class Application extends App { $cspManager = $container->getServer()->getContentSecurityPolicyManager(); $policy = new ContentSecurityPolicy(); if ($publicWopiUrl !== '') { + $policy->addAllowedFrameDomain('\'self\''); $policy->addAllowedFrameDomain($publicWopiUrl); if (method_exists($policy, 'addAllowedFormActionDomain')) { $policy->addAllowedFormActionDomain($publicWopiUrl); diff --git a/src/files.js b/src/files.js new file mode 100644 index 00000000..1d8f862a --- /dev/null +++ b/src/files.js @@ -0,0 +1,517 @@ +import { getDocumentUrlFromTemplate, getDocumentUrlForPublicFile, getDocumentUrlForFile, getSearchParam } from './helpers/url' +import PostMessageService from './services/postMessage' +import Config from './services/config' +import Types from './helpers/types' +import FilesAppIntegration from './view/FilesAppIntegration' +import '../css/viewer.scss' + +const FRAME_DOCUMENT = 'FRAME_DOCUMENT' +const PostMessages = new PostMessageService({ + FRAME_DOCUMENT: () => document.getElementById('richdocumentsframe').contentWindow +}) + +const preloadCreate = getSearchParam('richdocuments_create') +const preloadOpen = getSearchParam('richdocuments_open') +const Preload = {} + +if (preloadCreate) { + Preload.create = { + type: getSearchParam('richdocuments_create'), + filename: getSearchParam('richdocuments_filename') + } +} + +if (preloadOpen) { + Preload.open = { + filename: preloadOpen, + id: getSearchParam('richdocuments_fileId'), + dir: getSearchParam('dir') + } +} + +const isDownloadHidden = document.getElementById('hideDownload') && document.getElementById('hideDownload').value === 'true' + +const isPublic = document.getElementById('isPublic') && document.getElementById('isPublic').value === '1' + +const odfViewer = { + + open: false, + receivedLoading: false, + isCollaboraConfigured: typeof OC.getCapabilities().richdocuments.collabora === 'object' && OC.getCapabilities().richdocuments.collabora.length !== 0, + supportedMimes: OC.getCapabilities().richdocuments.mimetypes.concat(OC.getCapabilities().richdocuments.mimetypesNoDefaultOpen), + excludeMimeFromDefaultOpen: OC.getCapabilities().richdocuments.mimetypesNoDefaultOpen, + hideDownloadMimes: ['image/jpeg', 'image/svg+xml', 'image/cgm', 'image/vnd.dxf', 'image/x-emf', 'image/x-wmf', 'image/x-wpg', 'image/x-freehand', 'image/bmp', 'image/png', 'image/gif', 'image/tiff', 'image/jpg', 'image/jpeg', 'text/plain', 'application/pdf'], + + register() { + const EDIT_ACTION_NAME = 'Edit with ' + OC.getCapabilities().richdocuments.productName + for (let mime of odfViewer.supportedMimes) { + OCA.Files.fileActions.register( + mime, + EDIT_ACTION_NAME, + 0, + OC.imagePath('core', 'actions/rename'), + this.onEdit, + t('richdocuments', 'Edit with {productName}', { productName: OC.getCapabilities().richdocuments.productName }) + ) + if (odfViewer.excludeMimeFromDefaultOpen.indexOf(mime) === -1 || isDownloadHidden) { + OCA.Files.fileActions.setDefault(mime, EDIT_ACTION_NAME) + } + } + }, + + onEdit: function(fileName, context) { + if (!odfViewer.isCollaboraConfigured) { + const setupUrl = OC.generateUrl('/settings/admin/richdocuments') + const installHint = OC.isUserAdmin() + ? `Collabora Online is not setup yet.
Click here to configure your own server or connect to a demo server.
` + : t('richdocuments', 'Collabora Online is not setup yet. Please contact your administrator.') + + if (OCP.Toast) { + OCP.Toast.error(installHint, { + isHTML: true, + timeout: 0 + }) + } else { + OC.Notification.showHtml(installHint) + } + return + } + if (odfViewer.open === true) { + return + } + odfViewer.open = true + let fileList = null + if (context) { + fileList = context.fileList + var fileDir = context.dir + var fileId = context.fileId || context.$file.attr('data-id') + var templateId = context.templateId + if (context.fileList) { + context.fileList.setViewerMode(true) + context.fileList.setPageTitle(fileName) + context.fileList.showMask() + } + } + odfViewer.receivedLoading = false + + let documentUrl = getDocumentUrlForFile(fileDir, fileId) + if (isPublic) { + documentUrl = getDocumentUrlForPublicFile(fileName, fileId) + } + if (typeof (templateId) !== 'undefined') { + documentUrl = getDocumentUrlFromTemplate(templateId, fileName, fileDir) + } + + /** + * We need to reload the page to set a proper CSP if the file is federated + * and the reload didn't happen for the exact same file + */ + const canAccessCSP = (url, callback) => { + let canEmbed = false + let frame = document.createElement('iframe') + frame.setAttribute('src', url) + frame.setAttribute('onload', () => { + canEmbed = true + }) + document.body.appendChild(frame) + setTimeout(() => { + if (!canEmbed) { + callback() + } + document.body.removeChild(frame) + }, 50) + + } + + const reloadForFederationCSP = (fileName) => { + const preloadId = Preload.open ? parseInt(Preload.open.id) : -1 + const fileModel = fileList.findFile(fileName) + const shareOwnerId = fileModel.shareOwnerId + if (typeof shareOwnerId !== 'undefined') { + const lastIndex = shareOwnerId.lastIndexOf('@') + // only redirect if remote file, not opened though reload and csp blocks the request + if (shareOwnerId.substr(lastIndex).indexOf('/') !== -1 && fileModel.id !== preloadId) { + canAccessCSP('https://' + shareOwnerId.substr(lastIndex) + '/status.php', () => { + window.location = OC.generateUrl('/apps/richdocuments/open?fileId=' + fileId) + }) + } + } + return false + } + + if (context) { + reloadForFederationCSP(fileName) + } + + OC.addStyle('richdocuments', 'mobile') + + var $iframe = $('