diff options
author | Julius Härtl <jus@bitgrid.net> | 2020-01-29 20:45:31 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-29 20:45:31 +0300 |
commit | 12cc416415b70dba996e37c770877a0b618dbcb3 (patch) | |
tree | 5c403c4012b3cc4844906698396bb5158dfbd4b0 /src | |
parent | f3531a133e4cfa4957ca0063a0d1d7f27a7d9dfc (diff) | |
parent | a4cdbdce1d20023e7bafe01197b9549037cef5ee (diff) |
Merge pull request #796 from nextcloud/enh/demo
Add demo servers
Diffstat (limited to 'src')
-rw-r--r-- | src/admin.js | 258 | ||||
-rw-r--r-- | src/components/AdminSettings.vue | 371 | ||||
-rw-r--r-- | src/components/SettingsCheckbox.vue | 11 | ||||
-rw-r--r-- | src/components/SettingsExternalApps.vue | 125 | ||||
-rw-r--r-- | src/viewer.js | 17 |
5 files changed, 474 insertions, 308 deletions
diff --git a/src/admin.js b/src/admin.js index 150c2916..c4155aa6 100644 --- a/src/admin.js +++ b/src/admin.js @@ -23,263 +23,6 @@ new Vue({ render: h => h(AdminSettings, { props: { initial: JSON.parse(element.dataset.initial) } }) }).$mount('#admin-vue') -const 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, #disable_certificate_verification').attr('disabled', true) - var data = { - wopi_url: $('#wopi_url').val().replace(/\/$/, ''), - disable_certificate_verification: document.getElementById('disable_certificate_verification').checked - } - - OC.msg.startAction('#documents-admin-msg', t('richdocuments', 'Saving…')) - $.post( - OC.filePath('richdocuments', 'ajax', 'admin.php'), - data, - documentsSettings.afterSave - ) - }, - - saveGroups: function(data) { - $.post( - OC.filePath('richdocuments', 'ajax', 'admin.php'), - data - ) - }, - - saveDocFormat: function(format) { - $.post( - OC.filePath('richdocuments', 'ajax', 'admin.php'), - { 'doc_format': format } - ) - }, - - afterSave: function(response) { - $('#wopi_apply, #disable_certificate_verification').attr('disabled', false) - 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 - ) - }, - - saveWebroot: function(value) { - var data = { - 'canonical_webroot': value - } - $.post( - OC.filePath('richdocuments', 'ajax', 'admin.php'), - data - ) - }, - - afterSaveExternalApps: function(response) { - OC.msg.finishedAction('#enable-external-apps-section-msg', response) - }, - - initGroups: function() { - var selectorPrefixes = [ - 'edit', - 'use' - ] - - for (var i = 0; i < selectorPrefixes.length; i++) { - var selectorPrefix = selectorPrefixes[i] - - var groups = $('#' + selectorPrefix + '_group_select').val() - if (groups !== '') { - OC.Settings.setupGroupsSelect($('#' + selectorPrefix + '_group_select')) - $('.' + selectorPrefix + '-groups-enable').attr('checked', 'checked') - } else { - $('.' + selectorPrefix + '-groups-enable').attr('checked', null) - } - } - }, - - 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.initGroups() - documentsSettings.initExternalApps() - - var page = $('#richdocuments') - - $('#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() { - 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 appToken = page.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 = page.find('#test_server_group_select').val() - var testserver = page.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') - }) - - $(document).on('change', '#edit_group_select', function() { - var groups = $(this).val() - documentsSettings.saveGroups({ edit_groups: groups }) - }) - - $(document).on('change', '.edit-groups-enable', function() { - var $select = page.find('#edit_group_select') - $select.val('') - - if (this.checked) { - OC.Settings.setupGroupsSelect($select, { - placeholder: t('core', 'All') - }) - } else { - $select.select2('destroy') - } - - $select.change() - }) - - $(document).on('change', '#use_group_select', function() { - var groups = $(this).val() - documentsSettings.saveGroups({ use_groups: groups }) - }) - - $(document).on('change', '.use-groups-enable', function() { - var $select = page.find('#use_group_select') - $select.val('') - - if (this.checked) { - OC.Settings.setupGroupsSelect($select, { - placeholder: t('core', 'All') - }) - } else { - $select.select2('destroy') - } - - $select.change() - }) - - $(document).on('change', '#enable_canonical_webroot_cb-richdocuments', function() { - page.find('#enable-canonical-webroot-section').toggleClass('hidden', !this.checked) - if (!this.checked) { - documentsSettings.saveWebroot('') - } else { - var val = $('#canonical-webroot').val() - if (val) { documentsSettings.saveWebroot() } - } - }) - - $(document).on('change', '#canonical-webroot', function() { - documentsSettings.saveWebroot(this.value) - }) - } -} - /** * Append a new template to the dom * @@ -394,6 +137,5 @@ function initTemplateManager() { } $(document).ready(function() { - documentsSettings.initialize() initTemplateManager() }) diff --git a/src/components/AdminSettings.vue b/src/components/AdminSettings.vue index c7d1ba67..a8c50f8e 100644 --- a/src/components/AdminSettings.vue +++ b/src/components/AdminSettings.vue @@ -21,66 +21,188 @@ --> <template> - <div class="section"> - <h2>Secure view settings</h2> - - <p>{{ t('richdocuments', 'Secure view enables you to secure documents by embedding a watermark') }}</p> - <settings-checkbox v-model="settings.watermark.enabled" label="Enable watermarking" hint="" - :disabled="updating" @input="update" /> - <settings-input-text v-if="settings.watermark.enabled" v-model="settings.watermark.text" label="Watermark text" - :hint="t('richdocuments', 'Supported placeholders: {userId}, {date}')" - :disabled="updating" @update="update" /> - <div v-if="settings.watermark.enabled"> - <settings-checkbox v-model="settings.watermark.allTags" label="Show watermark on tagged files" :disabled="updating" - @input="update" /> - <p v-if="settings.watermark.allTags" class="checkbox-details"> - <settings-select-tag v-model="settings.watermark.allTagsList" label="Select tags to enforce watermarking" @input="update" /> - </p> - <settings-checkbox v-model="settings.watermark.allGroups" label="Show watermark for users of groups" :disabled="updating" - @input="update" /> - <p v-if="settings.watermark.allGroups" class="checkbox-details"> - <settings-select-group v-model="settings.watermark.allGroupsList" label="Select tags to enforce watermarking" @input="update" /> - </p> - <settings-checkbox v-model="settings.watermark.shareAll" label="Show watermark for all shares" hint="" - :disabled="updating" @input="update" /> - <settings-checkbox v-if="!settings.watermark.shareAll" v-model="settings.watermark.shareRead" label="Show watermark for read only shares" - hint="" - :disabled="updating" @input="update" /> + <div> + <div class="section"> + <h2>Collabora Online</h2> + <p>{{ t('richdocuments', 'Collabora Online is a powerful LibreOffice-based online office suite with collaborative editing, which supports all major documents, spreadsheet and presentation file formats and works together with all modern browsers.') }}</p> - <h3>Link shares</h3> - <settings-checkbox v-model="settings.watermark.linkAll" label="Show watermark for all link shares" hint="" - :disabled="updating" @input="update" /> - <settings-checkbox v-if="!settings.watermark.linkAll" v-model="settings.watermark.linkSecure" label="Show watermark for download hidden shares" - hint="" - :disabled="updating" @input="update" /> - <settings-checkbox v-if="!settings.watermark.linkAll" v-model="settings.watermark.linkRead" label="Show watermark for read only link shares" - hint="" - :disabled="updating" @input="update" /> - <settings-checkbox v-if="!settings.watermark.linkAll" v-model="settings.watermark.linkTags" label="Show watermark on link shares with specific system tags" + <div v-if="settings.wopi_url && settings.wopi_url !== ''"> + <div v-if="serverError == 2" id="security-warning-state-failure"> + <span class="icon icon-close-white" /><span class="message">{{ t('richdocuments', 'Could not establish connection to the Collabora Online server.') }}</span> + </div> + <div v-else-if="serverError == 1" id="security-warning-state-failure"> + <span class="icon icon-loading" /><span class="message">{{ t('richdocuments', 'Setting new server') }}</span> + </div> + <div v-else id="security-warning-state-ok"> + <span class="icon icon-checkmark-white" /><span class="message">{{ t('richdocuments', 'Collabora Online server is reachable.') }}</span> + </div> + </div> + <div v-else id="security-warning-state-warning"> + <span class="icon icon-error-white" /><span class="message">{{ t('richdocuments', 'Please configure a Collabora Online server to start editing documents') }}</span> + </div> + + <fieldset> + <div> + <input id="customserver" v-model="serverMode" type="radio" + name="serverMode" value="custom" class="radio" + :disabled="updating"> + <label for="customserver">{{ t('richdocuments', 'Use your own server') }}</label><br> + <p class="option-inline"> + <em>{{ t('richdocuments', 'Collabora Online requires a seperate server acting as a WOPI-like Client to provide editing capabilities.') }}</em> + </p> + <div v-if="serverMode === 'custom'" class="option-inline"> + <form @submit="updateServer"> + <p> + <label for="wopi_url">{{ t('richdocuments', 'URL (and Port) of Collabora Online-server') }}</label><br> + <input id="wopi_url" v-model="settings.wopi_url" type="text" + :disabled="updating"> + <input type="submit" value="Save" :disabled="updating" + @click="updateServer"><br> + </p> + <p> + <input id="disable_certificate_verification" v-model="settings.disable_certificate_verification" type="checkbox" + class="checkbox" :disabled="updating" @change="updateServer"> + <label for="disable_certificate_verification">{{ t('richdocuments', 'Disable certificate verification (insecure)') }}</label><br> + <em>{{ t('Enable if your Collabora Online server uses a self signed certificate') }}</em> + </p> + </form> + </div> + </div> + <div> + <input id="demoserver" v-model="serverMode" type="radio" + name="serverMode" value="demo" class="radio" + :disabled="updating" @input="fetchDemoServers"> + <label for="demoserver">{{ t('richdocuments', 'Use a demo server') }}</label><br> + <p class="option-inline"> + <em>{{ t('richdocuments', 'You can use a demo server provided by Collabora and other service providers for giving Collabora Online a try.') }}</em> + </p> + <div v-if="serverMode === 'demo'" class="option-inline"> + <p v-if="demoServers === null"> + {{ t('richdocuments', 'Loading available demo servers …') }} + </p> + <p v-else-if="demoServers.length > 0"> + <multiselect v-if="serverMode === 'demo'" v-model="settings.demoUrl" :custom-label="demoServerLabel" + track-by="demo_url" label="demo_url" placeholder="Select a demo server" + :options="demoServers" :searchable="false" :allow-empty="false" + :disabled="updating" @input="setDemoServer" /> + </p> + <p v-else> + {{ t('richdocuments', 'No available demo servers found.') }} + </p> + + <p v-if="settings.demoUrl"> + <em> + {{ t('richdocuments', 'Documents opened with the demo server configured will be sent to a 3rd party server. Only use this for evaluating Collabora Online.') }}<br> + <a :href="settings.demoUrl.provider_url" target="_blank" rel="noreferrer noopener" + class="external">{{ providerDescription }}</a> + </em> + </p> + </div> + </div> + </fieldset> + </div> + + <div v-if="isSetup" id="advanced-settings" class="section"> + <h2>{{ t('richdocuments', 'Advanced settings') }}</h2> + <settings-checkbox :value="isOoxml" :label="t('richdocuments', 'Use Office Open XML (OOXML) instead of OpenDocument Format (ODF) by default for new files')" hint="" + :disabled="updating" @input="updateOoxml" /> + + <settings-checkbox :value="settings.use_groups !== null" :label="t('richdocuments', 'Restrict usage to specific groups')" :hint="t('richdocuments', 'Collabora Online is enabled for all users by default. When this setting is active, only members of the specified groups can use it.')" + :disabled="updating" @input="updateUseGroups"> + <settings-select-group v-if="settings.use_groups !== null" v-model="settings.use_groups" :label="t('richdocuments', 'Select groups')" + class="option-inline" :disabled="updating" @input="updateUseGroups" /> + </settings-checkbox> + + <settings-checkbox :value="settings.edit_groups !== null" :label="t('richdocuments', 'Restrict edit to specific groups')" hint="All users can edit documents with Collabora Online by default. When this setting is active, only the members of the specified groups can edit and the others can only view documents.')" + :disabled="updating" @input="updateEditGroups"> + <settings-select-group v-if="settings.edit_groups !== null" v-model="settings.edit_groups" :label="t('richdocuments', 'Select groups')" + class="option-inline" :disabled="updating" @input="updateEditGroups" /> + </settings-checkbox> + + <settings-checkbox v-model="uiVisible.canonical_webroot" :label="t('richdocuments', 'Use Canonical webroot')" hint="" + :disabled="updating" @input="updateCanonicalWebroot"> + <settings-input-text v-if="uiVisible.canonical_webroot" v-model="settings.canonical_webroot" label="" + :hint="t('richdocuments', 'Canonical webroot, in case there are multiple, for Collabora to use. Provide the one with least restrictions. Eg: Use non-shibbolized webroot if this instance is accessed by both shibbolized and non-shibbolized webroots. You can ignore this setting if only one webroot is used to access this instance.')" + :disabled="updating" class="option-inline" @update="updateCanonicalWebroot" /> + </settings-checkbox> + + <settings-checkbox v-model="uiVisible.external_apps" :label="t('richdocuments', 'Enable access for external apps')" hint="" + :disabled="updating" @input="updateExternalApps"> + <div v-if="uiVisible.external_apps"> + <settings-external-apps class="option-inline" :external-apps="settings.external_apps" :disabled="updating" + @input="updateExternalApps" /> + </div> + </settings-checkbox> + </div> + + <div v-if="isSetup" id="secure-view-settings" class="section"> + <h2>{{ t('richdocuments', 'Secure view settings') }}</h2> + <p>{{ t('richdocuments', 'Secure view enables you to secure documents by embedding a watermark') }}</p> + <settings-checkbox v-model="settings.watermark.enabled" :label="t('richdocuments', 'Enable watermarking')" hint="" :disabled="updating" @input="update" /> - <p v-if="!settings.watermark.linkAll && settings.watermark.linkTags" class="checkbox-details"> - <settings-select-tag v-model="settings.watermark.linkTagsList" label="Select tags to enforce watermarking" @input="update" /> - </p> + <settings-input-text v-if="settings.watermark.enabled" v-model="settings.watermark.text" label="Watermark text" + :hint="t('richdocuments', 'Supported placeholders: {userId}, {date}')" + :disabled="updating" @update="update" /> + <div v-if="settings.watermark.enabled"> + <settings-checkbox v-model="settings.watermark.allTags" :label="t('richdocuments', 'Show watermark on tagged files')" :disabled="updating" + @input="update" /> + <p v-if="settings.watermark.allTags" class="checkbox-details"> + <settings-select-tag v-model="settings.watermark.allTagsList" :label="t('richdocuments', 'Select tags to enforce watermarking')" @input="update" /> + </p> + <settings-checkbox v-model="settings.watermark.allGroups" :label="t('richdocuments', 'Show watermark for users of groups')" :disabled="updating" + @input="update" /> + <p v-if="settings.watermark.allGroups" class="checkbox-details"> + <settings-select-group v-model="settings.watermark.allGroupsList" :label="t('richdocuments', 'Select tags to enforce watermarking')" @input="update" /> + </p> + <settings-checkbox v-model="settings.watermark.shareAll" :label="t('richdocuments', 'Show watermark for all shares')" hint="" + :disabled="updating" @input="update" /> + <settings-checkbox v-if="!settings.watermark.shareAll" v-model="settings.watermark.shareRead" :label="t('richdocuments', 'Show watermark for read only shares')" + hint="" + :disabled="updating" @input="update" /> + + <h3>Link shares</h3> + <settings-checkbox v-model="settings.watermark.linkAll" :label="t('richdocuments', 'Show watermark for all link shares')" hint="" + :disabled="updating" @input="update" /> + <settings-checkbox v-if="!settings.watermark.linkAll" v-model="settings.watermark.linkSecure" :label="t('richdocuments', 'Show watermark for download hidden shares')" + hint="" + :disabled="updating" @input="update" /> + <settings-checkbox v-if="!settings.watermark.linkAll" v-model="settings.watermark.linkRead" :label="t('richdocuments', 'Show watermark for read only link shares')" + hint="" + :disabled="updating" @input="update" /> + <settings-checkbox v-if="!settings.watermark.linkAll" v-model="settings.watermark.linkTags" :label="t('richdocuments', 'Show watermark on link shares with specific system tags')" + :disabled="updating" @input="update" /> + <p v-if="!settings.watermark.linkAll && settings.watermark.linkTags" class="checkbox-details"> + <settings-select-tag v-model="settings.watermark.linkTagsList" :label="t('richdocuments', 'Select tags to enforce watermarking')" @input="update" /> + </p> + </div> </div> </div> </template> <script> import Vue from 'vue' +import { Multiselect } from 'nextcloud-vue' import axios from 'nextcloud-axios' import SettingsCheckbox from './SettingsCheckbox' import SettingsInputText from './SettingsInputText' import SettingsSelectTag from './SettingsSelectTag' import SettingsSelectGroup from './SettingsSelectGroup' +import SettingsExternalApps from './SettingsExternalApps' import { generateUrl } from 'nextcloud-router' +const SERVER_STATE_OK = 0 +const SERVER_STATE_LOADING = 1 +const SERVER_STATE_CONNECTION_ERROR = 2 + export default { name: 'AdminSettings', components: { SettingsCheckbox, SettingsInputText, SettingsSelectTag, - SettingsSelectGroup + SettingsSelectGroup, + Multiselect, + SettingsExternalApps }, props: { initial: { @@ -90,10 +212,19 @@ export default { }, data() { return { + serverMode: '', + serverError: SERVER_STATE_OK, + demoServers: null, updating: false, groups: [], tags: [], + uiVisible: { + canonical_webroot: false, + external_apps: false + }, settings: { + demoUrl: null, + wopi_url: null, watermark: { enabled: false, shareAll: false, @@ -112,23 +243,53 @@ export default { } } }, + computed: { + providerDescription() { + return t('richdocuments', 'Contact {0} to get an own installation.', [this.settings.demoUrl.provider_name]) + }, + isSetup() { + return this.serverError === SERVER_STATE_OK + }, + isOoxml() { + return this.settings.doc_format === 'ooxml' + } + }, beforeMount() { - for (let key in this.initial) { - if (!this.initial.hasOwnProperty(key)) { + for (let key in this.initial.settings) { + if (!this.initial.settings.hasOwnProperty(key)) { continue } let [ parent, setting ] = key.split('_') if (parent === 'watermark') { - Vue.set(this.settings[parent], setting, this.initial[key]) + Vue.set(this.settings[parent], setting, this.initial.settings[key]) } else { - Vue.set(this.settings, key, this.initial[key]) + Vue.set(this.settings, key, this.initial.settings[key]) } } - Vue.set(this.settings, 'data', this.initial) + Vue.set(this.settings, 'data', this.initial.settings) + if (this.settings.wopi_url === '') { + this.serverError = SERVER_STATE_CONNECTION_ERROR + } + Vue.set(this.settings, 'edit_groups', this.settings.edit_groups ? this.settings.edit_groups.split('|') : null) + Vue.set(this.settings, 'use_groups', this.settings.use_groups ? this.settings.use_groups.split('|') : null) + + this.uiVisible.canonical_webroot = !!(this.settings.canonical_webroot && this.settings.canonical_webroot !== '') + this.uiVisible.external_apps = !!(this.settings.external_apps && this.settings.external_apps !== '') + + this.demoServers = this.initial.demo_servers + this.checkIfDemoServerIsActive() }, methods: { + async fetchDemoServers() { + try { + const result = await axios.get(generateUrl('/apps/richdocuments/settings/demo')) + this.demoServers = result.data + } catch (e) { + this.demoServers = [] + } + }, update() { this.updating = true let settings = this.settings @@ -136,15 +297,104 @@ export default { this.updating = false }).catch((error) => { this.updating = false - OC.Notification.showTemporary('Failed to save settings') + OC.Notification.showTemporary(t('richdocuments', 'Failed to save settings')) console.error(error) }) + }, + async updateUseGroups(enabled) { + if (enabled) { + this.settings.use_groups = enabled === true ? [] : enabled + } else { + this.settings.use_groups = null + } + await this.updateSettings({ + use_groups: this.settings.use_groups !== null ? this.settings.use_groups.join('|') : '' + }) + }, + async updateEditGroups(enabled) { + if (enabled) { + this.settings.edit_groups = enabled === true ? [] : enabled + } else { + this.settings.edit_groups = null + } + await this.updateSettings({ + edit_groups: this.settings.edit_groups !== null ? this.settings.edit_groups.join('|') : '' + }) + }, + async updateCanonicalWebroot(canonicalWebroot) { + this.settings.canonical_webroot = (typeof canonicalWebroot === 'boolean') ? '' : canonicalWebroot + if (canonicalWebroot === true) { + return + } + await this.updateSettings({ + canonical_webroot: this.settings.canonical_webroot + }) + }, + async updateExternalApps(externalApps) { + this.settings.external_apps = (typeof externalApps === 'boolean') ? '' : externalApps + if (externalApps === true) { + return + } + await this.updateSettings({ + external_apps: this.settings.external_apps + }) + }, + async updateOoxml(enabled) { + this.settings.doc_format = enabled ? 'ooxml' : '' + await this.updateSettings({ + doc_format: this.settings.doc_format + }) + }, + async updateServer() { + this.serverError = SERVER_STATE_LOADING + try { + await this.updateSettings({ + wopi_url: this.settings.wopi_url, + disable_certificate_verification: this.settings.disable_certificate_verification + }) + this.serverError = SERVER_STATE_OK + } catch (e) { + console.error(e) + this.serverError = SERVER_STATE_CONNECTION_ERROR + } + this.checkIfDemoServerIsActive() + }, + async updateSettings(data) { + this.updating = true + try { + const result = await axios.post( + OC.filePath('richdocuments', 'ajax', 'admin.php'), + data + ) + this.updating = false + return result + } catch (e) { + this.updating = false + throw e + } + }, + checkIfDemoServerIsActive() { + this.settings.demoUrl = this.demoServers ? this.demoServers.find((server) => server.demo_url === this.settings.wopi_url) : null + if (this.settings.wopi_url && this.settings.wopi_url !== '') { + this.serverMode = 'custom' + } + if (this.settings.demoUrl) { + this.serverMode = 'demo' + } + }, + demoServerLabel(server) { + return `${server.provider_name} — ${server.provider_location}` + }, + async setDemoServer(server) { + this.settings.wopi_url = server.demo_url + this.settings.disable_certificate_verification = false + await this.updateServer() } } } </script> -<style scoped> +<style lang="scss" scoped> p { margin-bottom: 15px; } @@ -153,9 +403,36 @@ export default { margin-top: -10px; margin-bottom: 20px; } - input, + + input[type='text'], .multiselect { width: 100%; max-width: 400px; } + + input#wopi_url { + width: 300px; + } + + #secure-view-settings { + margin-top: 20px; + } + + .section { + border-bottom: 1px solid var(--color-border); + } + + #security-warning-state-failure, + #security-warning-state-warning, + #security-warning-state-ok { + margin-top: 10px; + margin-bottom: 20px; + } + + .option-inline { + margin-left: 25px; + &:not(.multiselect) { + margin-top: 10px; + } + } </style> diff --git a/src/components/SettingsCheckbox.vue b/src/components/SettingsCheckbox.vue index 94a3c32a..e6495b04 100644 --- a/src/components/SettingsCheckbox.vue +++ b/src/components/SettingsCheckbox.vue @@ -21,12 +21,15 @@ --> <template> - <p> + <div class="settings-entry"> <input :id="id" type="checkbox" class="checkbox" :checked="inputVal" :disabled="disabled" @change="$emit('input', $event.target.checked)"> <label :for="id">{{ label }}</label><br> <em v-if="hint !== ''">{{ hint }}</em> - </p> + <div> + <slot /> + </div> + </div> </template> <script> @@ -74,5 +77,7 @@ export default { </script> <style scoped> - + .settings-entry { + padding-bottom: 15px; + } </style> diff --git a/src/components/SettingsExternalApps.vue b/src/components/SettingsExternalApps.vue new file mode 100644 index 00000000..9588a88e --- /dev/null +++ b/src/components/SettingsExternalApps.vue @@ -0,0 +1,125 @@ +<!-- + - @copyright Copyright (c) 2020 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/>. + - + --> + +<template> + <div> + <ul> + <li v-for="token in tokens" :key="token.token"> + <input v-model="token.description" type="text" :placeholder="t('richdocuments', 'Description')" + :disabled="disabled"> + <input class="token" type="text" disabled + :value="token.token"> + <button class="icon icon-history" :disabled="disabled" @click="regenerateToken(token)" /> + <button class="icon icon-close" :disabled="disabled" @click="removeToken(token)" /> + </li> + </ul> + <button :disabled="disabled" @click="addNewToken"> + {{ t('richdocuments', 'Add new token') }} + </button> + <input type="button" value="Save" :disabled="disabled" + @click="updateTokens"> + </div> +</template> + +<script> +const generateRandomToken = () => { + const len = 3 + const array = new Uint32Array(len) + window.crypto.getRandomValues(array) + let random = '' + for (var i = 0; i < len; ++i) { + random += array[i].toString(36) + } + return random +} + +const appsStringToArray = (apps) => { + if (apps === '') { + return [] + } + return apps.replace(/,$/g, '').split(',').map((item) => { + const data = item.split(':') + return { + description: data[0], + token: data[1] + } + }) +} +const appsArrayToString = (apps) => { + let result = '' + apps.forEach((item) => { + result += item.description + ':' + item.token + ',' + }) + return result +} +export default { + name: 'SettingsExternalApps', + props: { + externalApps: { + type: String, + default: '' + }, + disabled: { + type: Boolean, + default: false + } + }, + data() { + return { + tokens: [] + } + }, + beforeMount() { + this.tokens = appsStringToArray(this.externalApps) + }, + methods: { + addNewToken() { + this.tokens.push({ + description: '', + token: generateRandomToken() + }) + }, + removeToken(token) { + this.tokens.splice(this.tokens.indexOf(token), 1) + }, + updateTokens() { + this.$emit('input', appsArrayToString(this.tokens)) + }, + regenerateToken(token) { + token.token = generateRandomToken() + } + } +} +</script> + +<style scoped> + li { + display: flex; + } + .token { + width: 200px; + background-color: #fff; + background-color: var(--color-main-background) !important; + color: var(--color-text-light); + text-align: center; + } +</style> diff --git a/src/viewer.js b/src/viewer.js index 644a4524..906affe8 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -36,6 +36,7 @@ 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'], @@ -58,6 +59,22 @@ const odfViewer = { }, onEdit: function(fileName, context) { + if (!odfViewer.isCollaboraConfigured) { + const setupUrl = OC.generateUrl('/settings/admin/richdocuments') + const installHint = OC.isUserAdmin() + ? `<a href="${setupUrl}">Collabora Online is not setup yet. <br />Click here to configure your own server or connect to a demo server.</a>` + : 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 } |