From 21504eb5a895c1c80f937e1b5eeb5ecdf89c8711 Mon Sep 17 00:00:00 2001 From: Pranav Kant Date: Wed, 27 Sep 2017 06:53:06 +0000 Subject: [PATCH] support for external apps (#124) --- appinfo/info.xml | 2 +- appinfo/routes.php | 3 + js/admin.js | 128 ++++++++++++++++++++++++++ lib/Controller/DocumentController.php | 60 ++++++++++++ lib/Controller/SettingsController.php | 7 +- lib/Settings/Admin.php | 1 + templates/admin.php | 12 +++ 7 files changed, 211 insertions(+), 2 deletions(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index d1017b1a..42052817 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -5,7 +5,7 @@ Collabora Online allows you to to work with all kinds of office documents directly in your browser. This application requires Collabora Cloudsuite to be installed on one of your servers, please read the documentation to learn more about that. Edit office documents directly in your browser. AGPL - 1.12.34 + 1.12.35 Collabora Productivity based on work of Frank Karlitschek, Victor Dubiniuk https://github.com/nextcloud/richdocuments/issues https://github.com/nextcloud/richdocuments.git diff --git a/appinfo/routes.php b/appinfo/routes.php index 67dfc3fa..a92dc45d 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -18,6 +18,9 @@ return [ ['name' => 'document#publicPage', 'url' => '/public', 'verb' => 'GET'], ['name' => 'document#create', 'url' => 'ajax/documents/create', 'verb' => 'POST'], + // external api access + ['name' => 'document#extAppGetData', 'url' => '/ajax/extapp/data/{fileId}', 'verb' => 'POST'], + // WOPI access ['name' => 'wopi#checkFileInfo', 'url' => 'wopi/files/{fileId}', 'verb' => 'GET'], ['name' => 'wopi#getFile', 'url' => 'wopi/files/{fileId}/contents', 'verb' => 'GET'], diff --git a/js/admin.js b/js/admin.js index 81e1c0cd..58162f69 100644 --- a/js/admin.js +++ b/js/admin.js @@ -1,6 +1,31 @@ /*global OC, $ */ var documentsSettings = { + _createExtApp: function() { + var app1 = document.createElement('div'); + app1.setAttribute('class', 'external-app'); + + var appname1 = document.createElement('input'); + appname1.setAttribute('class', 'external-apps-name'); + $(app1).append(appname1); + + var apptoken1 = document.createElement('input'); + apptoken1.setAttribute('class', 'external-apps-token'); + $(app1).append(apptoken1); + + var apptokenbutton = document.createElement('button'); + apptokenbutton.setAttribute('class', 'external-apps-gen-token-button'); + apptokenbutton.innerHTML = 'Generate Token'; + $(app1).append(apptokenbutton); + + var appremovebutton = document.createElement('button'); + appremovebutton.setAttribute('class', 'external-apps-remove-button'); + appremovebutton.innerHTML = 'Remove'; + $(app1).append(appremovebutton); + + return app1; + }, + save : function() { $('#wopi_apply').attr('disabled', true); var data = { @@ -38,6 +63,23 @@ var documentsSettings = { OC.msg.finishedAction('#documents-admin-msg', response); }, + saveExternalApps: function(externalAppsData) { + var data = { + 'external_apps': externalAppsData + }; + + OC.msg.startAction('#enable-external-apps-section-msg', t('richdocuments', 'Saving...')); + $.post( + OC.filePath('richdocuments', 'ajax', 'admin.php'), + data, + documentsSettings.afterSaveExternalApps + ); + }, + + afterSaveExternalApps: function(response) { + OC.msg.finishedAction('#enable-external-apps-section-msg', response); + }, + initEditGroups: function() { var groups = $('#edit_group_select').val(); if (groups !== '') { @@ -48,11 +90,97 @@ var documentsSettings = { } }, + initExternalApps: function() { + var externalAppsRaw = $(document).find('#external-apps-raw').val(); + var apps = externalAppsRaw.split(','); + for (var i = 0; i < apps.length; ++i) { + if (apps[i] !== '') { + var app = apps[i].split(':'); + var app1 = this._createExtApp(); + // create a placeholder for adding new app + $('#external-apps-section').append(app1); + $(app1).find('.external-apps-name').val(app[0]); + $(app1).find('.external-apps-token').val(app[1]); + } + } + }, + initialize: function() { documentsSettings.initEditGroups(); + documentsSettings.initExternalApps(); $('#wopi_apply').on('click', documentsSettings.save); + // destroy or create app name and token fields depending on whether the checkbox is on or off + $(document).on('change', '#enable_external_apps_cb-richdocuments', function() { + var page = $(this).parent(); + + page.find('#enable-external-apps-section').toggleClass('hidden', !this.checked); + if (this.checked) { + var app1 = documentsSettings._createExtApp(); + $('#external-apps-section').append(app1); + } else { + page.find('.external-app').remove(); + page.find('#external-apps-raw').val(''); + documentsSettings.saveExternalApps(''); + } + }); + + $(document).on('click', '.external-apps-gen-token-button', function() { + var appSection = $(this).parent(); + var appToken = appSection.find('.external-apps-token'); + + // generate a random string + var len = 3; + var array = new Uint32Array(len); + window.crypto.getRandomValues(array); + var random = ''; + for (var i = 0; i < len; ++i) { + random += array[i].toString(36); + } + + // set the token in the field + appToken.val(random); + }); + + $(document).on('click', '.external-apps-remove-button', function() { + $(this).parent().remove(); + }); + + $(document).on('click', '#external-apps-save-button', function() { + // read all the data in input fields, save the data in input-raw and send to backedn + var extAppsSection = $(this).parent(); + var apps = extAppsSection.find('.external-app'); + // convert all values into one single string and store it in raw input field + // as well as send the data to server + var raw = ''; + for (var i = 0; i < apps.length; ++i) { + var appname = $(apps[i]).find('.external-apps-name'); + var apptoken = $(apps[i]).find('.external-apps-token'); + raw += appname.val() + ':' + apptoken.val() + ','; + } + + extAppsSection.find('#external-apps-raw').val(raw); + documentsSettings.saveExternalApps(raw); + }); + + $(document).on('click', '#external-apps-add-button', function() { + // create a placeholder for adding new app + var app1 = documentsSettings._createExtApp(); + $('#external-apps-section').append(app1); + }); + + $(document).on('click', '#test_wopi_apply', function() { + var groups = $(this).parent().find('#test_server_group_select').val(); + var testserver = $(this).parent().find('#test_wopi_url').val(); + + if (groups !== '' && testserver !== '') { + documentsSettings.saveTestWopi(groups, testserver); + } else { + OC.msg.finishedError('#test-documents-admin-msg', 'Both fields required'); + } + }); + $(document).on('change', '.doc-format-ooxml', function() { var ooxml = this.checked; documentsSettings.saveDocFormat(ooxml ? 'ooxml' : 'odf'); diff --git a/lib/Controller/DocumentController.php b/lib/Controller/DocumentController.php index ccbc46ee..ba316498 100644 --- a/lib/Controller/DocumentController.php +++ b/lib/Controller/DocumentController.php @@ -92,6 +92,66 @@ class DocumentController extends Controller { $this->logger = $logger; } + /** + * @PublicPage + * @NoCSRFRequired + * + * Returns the access_token and urlsrc for WOPI access for given $fileId + * Requests is accepted only when a secret_token is provided set by admin in + * settings page + * + * @param string $fileId + * @return access_token, urlsrc + */ + public function extAppGetData($fileId) { + $secretToken = $this->request->getParam('secret_token'); + $apps = array_filter(explode(',', $this->appConfig->getAppValue('external_apps'))); + foreach($apps as $app) { + if ($app !== '') { + if ($secretToken === $app) { + $appName = explode(':', $app); + \OC::$server->getLogger()->debug('External app "{extApp}" authenticated; issuing access token for fileId {fileId}', [ + 'app' => $this->appName, + 'extApp' => $appName[0], + 'fileId' => $fileId + ]); + try { + $folder = $this->rootFolder->getUserFolder($this->uid); + $item = $folder->getById($fileId)[0]; + if(!($item instanceof Node)) { + throw new \Exception(); + } + list($urlSrc, $token) = $this->tokenManager->getToken($item->getId()); + return array( + 'status' => 'success', + 'urlsrc' => $urlSrc, + 'token' => $token + ); + } catch (\Exception $e) { + $this->logger->logException($e, ['app'=>'richdocuments']); + $params = [ + 'remoteAddr' => $this->request->getRemoteAddress(), + 'requestID' => $this->request->getId(), + 'debugMode' => $this->settings->getSystemValue('debug'), + 'errorClass' => get_class($e), + 'errorCode' => $e->getCode(), + 'errorMsg' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => $e->getTraceAsString() + ]; + return new TemplateResponse('core', 'exception', $params, 'guest'); + } + } + } + + return array( + 'status' => 'error', + 'message' => 'Permission denied' + ); + } + } + /** * @NoAdminRequired * diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 8d609c02..850b28d1 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -66,7 +66,8 @@ class SettingsController extends Controller{ */ public function setSettings($wopi_url, $edit_groups, - $doc_format){ + $doc_format, + $external_apps){ $message = $this->l10n->t('Saved'); if ($wopi_url !== null){ @@ -86,6 +87,10 @@ class SettingsController extends Controller{ $this->appConfig->setAppValue('doc_format', $doc_format); } + if ($external_apps !== null) { + $this->appConfig->setAppValue('external_apps', $external_apps); + } + $this->discoveryManager->refretch(); $response = [ diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index 2a837065..e4faa02f 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -49,6 +49,7 @@ class Admin implements ISettings { 'wopi_url' => $this->config->getAppValue('richdocuments', 'wopi_url'), 'edit_groups' => $this->config->getAppValue('richdocuments', 'edit_groups'), 'doc_format' => $this->config->getAppValue('richdocuments', 'doc_format'), + 'external_apps' => $this->config->getAppValue('richdocuments', 'external_apps'), ], 'blank' ); diff --git a/templates/admin.php b/templates/admin.php index bbcdcb9f..6dc8aa81 100644 --- a/templates/admin.php +++ b/templates/admin.php @@ -15,4 +15,16 @@ script('richdocuments', 'admin');
/> +
+ /> + +
+
+ +
+ + + + +