diff options
Diffstat (limited to 'apps/files/src/views/TemplatePicker.vue')
-rw-r--r-- | apps/files/src/views/TemplatePicker.vue | 139 |
1 files changed, 86 insertions, 53 deletions
diff --git a/apps/files/src/views/TemplatePicker.vue b/apps/files/src/views/TemplatePicker.vue index 5f248602d4d..cddacc863e1 100644 --- a/apps/files/src/views/TemplatePicker.vue +++ b/apps/files/src/views/TemplatePicker.vue @@ -1,24 +1,7 @@ <!-- - - @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com> - - - - @author John Molakvoæ <skjnldsv@protonmail.com> - - - - @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/>. - - - --> + - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> <template> <NcModal v-if="opened" @@ -34,7 +17,9 @@ <!-- Templates list --> <ul class="templates-picker__list"> <TemplatePreview v-bind="emptyTemplate" + ref="emptyTemplatePreview" :checked="checked === emptyTemplate.fileid" + @confirm-click="onConfirmClick" @check="onCheck" /> <TemplatePreview v-for="template in provider.templates" @@ -42,6 +27,7 @@ v-bind="template" :checked="checked === template.fileid" :ratio="provider.ratio" + @confirm-click="onConfirmClick" @check="onCheck" /> </ul> @@ -61,23 +47,28 @@ </template> <script lang="ts"> -import { emit, subscribe } from '@nextcloud/event-bus' +import type { TemplateFile } from '../types.ts' + +import { getCurrentUser } from '@nextcloud/auth' +import { showError, spawnDialog } from '@nextcloud/dialogs' +import { emit } from '@nextcloud/event-bus' import { File } from '@nextcloud/files' +import { translate as t } from '@nextcloud/l10n' import { generateRemoteUrl } from '@nextcloud/router' -import { getCurrentUser } from '@nextcloud/auth' import { normalize, extname, join } from 'path' -import { showError } from '@nextcloud/dialogs' -import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js' -import NcModal from '@nextcloud/vue/dist/Components/NcModal.js' -import Vue from 'vue' +import { defineComponent } from 'vue' +import { createFromTemplate, getTemplates, getTemplateFields } from '../services/Templates.js' -import { createFromTemplate, getTemplates } from '../services/Templates.js' +import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent' +import NcModal from '@nextcloud/vue/components/NcModal' import TemplatePreview from '../components/TemplatePreview.vue' +import TemplateFiller from '../components/TemplateFiller.vue' +import logger from '../logger.ts' const border = 2 const margin = 8 -export default Vue.extend({ +export default defineComponent({ name: 'TemplatePicker', components: { @@ -87,9 +78,12 @@ export default Vue.extend({ }, props: { - logger: { + /** + * The parent folder where to create the node + */ + parent: { type: Object, - required: true, + default: () => null, }, }, @@ -98,28 +92,29 @@ export default Vue.extend({ // Check empty template by default checked: -1, loading: false, - name: null, + name: null as string|null, opened: false, - provider: null, + provider: null as TemplateFile|null, } }, computed: { extension() { - return extname(this.name) + return extname(this.name ?? '') }, + nameWithoutExt() { // Strip extension from name if defined return !this.extension - ? this.name - : this.name.slice(0, 0 - this.extension.length) + ? this.name! + : this.name!.slice(0, 0 - this.extension.length) }, emptyTemplate() { return { basename: t('files', 'Blank'), fileid: -1, - filename: this.t('files', 'Blank'), + filename: t('files', 'Blank'), hasPreview: false, mime: this.provider?.mimetypes[0] || this.provider?.mimetypes, } @@ -130,7 +125,7 @@ export default Vue.extend({ return null } - return this.provider.templates.find(template => template.fileid === this.checked) + return this.provider.templates!.find((template) => template.fileid === this.checked) }, /** @@ -159,6 +154,8 @@ export default Vue.extend({ }, methods: { + t, + /** * Open the picker * @@ -185,6 +182,11 @@ export default Vue.extend({ // Else, open the picker this.opened = true + + // Set initial focus to the empty template preview + this.$nextTick(() => { + this.$refs.emptyTemplatePreview?.focus() + }) }, /** @@ -201,34 +203,40 @@ export default Vue.extend({ /** * Manages the radio template picker change * - * @param {number} fileid the selected template file id + * @param fileid the selected template file id */ - onCheck(fileid) { + onCheck(fileid: number) { this.checked = fileid }, - async onSubmit() { - this.loading = true + onConfirmClick(fileid: number) { + if (fileid === this.checked) { + this.onSubmit() + } + }, + + async createFile(templateFields = []) { const currentDirectory = new URL(window.location.href).searchParams.get('dir') || '/' // If the file doesn't have an extension, add the default one if (this.nameWithoutExt === this.name) { - this.logger.warn('Fixed invalid filename', { name: this.name, extension: this.provider?.extension }) - this.name = this.name + this.provider?.extension + logger.warn('Fixed invalid filename', { name: this.name, extension: this.provider?.extension }) + this.name = `${this.name}${this.provider?.extension ?? ''}` } try { const fileInfo = await createFromTemplate( normalize(`${currentDirectory}/${this.name}`), - this.selectedTemplate?.filename, - this.selectedTemplate?.templateType, + this.selectedTemplate?.filename as string ?? '', + this.selectedTemplate?.templateType as string ?? '', + templateFields, ) - this.logger.debug('Created new file', fileInfo) + logger.debug('Created new file', fileInfo) const owner = getCurrentUser()?.uid || null const node = new File({ id: fileInfo.fileid, - source: generateRemoteUrl(join('dav/files', owner, fileInfo.filename)), + source: generateRemoteUrl(join(`dav/files/${owner}`, fileInfo.filename)), root: `/files/${owner}`, mime: fileInfo.mime, mtime: new Date(fileInfo.lastmod * 1000), @@ -236,6 +244,10 @@ export default Vue.extend({ size: fileInfo.size, permissions: fileInfo.permissions, attributes: { + // Inherit some attributes from parent folder like the mount type and real owner + 'mount-type': this.parent?.attributes?.['mount-type'], + 'owner-id': this.parent?.attributes?.['owner-id'], + 'owner-display-name': this.parent?.attributes?.['owner-display-name'], ...fileInfo, 'has-preview': fileInfo.hasPreview, }, @@ -248,18 +260,39 @@ export default Vue.extend({ window.OCP.Files.Router.goToRoute( null, // use default route { view: 'files', fileid: node.fileid }, - { dir: node.dirname, openfile: true }, + { dir: node.dirname, openfile: 'true' }, ) // Close the picker this.close() } catch (error) { - this.logger.error('Error while creating the new file from template', { error }) - showError(this.t('files', 'Unable to create new file from template')) + logger.error('Error while creating the new file from template', { error }) + showError(t('files', 'Unable to create new file from template')) } finally { this.loading = false } }, + + async onSubmit() { + const fileId = this.selectedTemplate?.fileid + + // Only request field extraction if there is a valid template + // selected and it's not the blank template + let fields = [] + if (fileId && fileId !== this.emptyTemplate.fileid) { + fields = await getTemplateFields(fileId) + } + + if (fields.length > 0) { + spawnDialog(TemplateFiller, { + fields, + onSubmit: this.createFile, + }) + } else { + this.loading = true + await this.createFile() + } + }, }, }) </script> @@ -297,7 +330,7 @@ export default Vue.extend({ padding: calc(var(--margin) * 2) var(--margin); position: sticky; bottom: 0; - background-image: linear-gradient(0, var(--gradient-main-background)); + background-image: linear-gradient(0deg, var(--gradient-main-background)); button, input[type='submit'] { height: 44px; @@ -305,14 +338,14 @@ export default Vue.extend({ } // Make sure we're relative for the loading emptycontent on top - ::v-deep .modal-container { + :deep(.modal-container) { position: relative; } &__loading { position: absolute; top: 0; - left: 0; + inset-inline-start: 0; justify-content: center; width: 100%; height: 100%; |