diff options
Diffstat (limited to 'apps/files/src/components/TransferOwnershipDialogue.vue')
-rw-r--r-- | apps/files/src/components/TransferOwnershipDialogue.vue | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/apps/files/src/components/TransferOwnershipDialogue.vue b/apps/files/src/components/TransferOwnershipDialogue.vue new file mode 100644 index 00000000000..3d668da8144 --- /dev/null +++ b/apps/files/src/components/TransferOwnershipDialogue.vue @@ -0,0 +1,236 @@ +<!-- + - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> + +<template> + <div> + <h3>{{ t('files', 'Transfer ownership of a file or folder') }} </h3> + <form @submit.prevent="submit"> + <p class="transfer-select-row"> + <span>{{ readableDirectory }}</span> + <NcButton v-if="directory === undefined" + class="transfer-select-row__choose_button" + @click.prevent="start"> + {{ t('files', 'Choose file or folder to transfer') }} + </NcButton> + <NcButton v-else @click.prevent="start"> + {{ t('files', 'Change') }} + </NcButton> + </p> + <p class="new-owner"> + <label for="targetUser"> + <span>{{ t('files', 'New owner') }}</span> + </label> + <NcSelect v-model="selectedUser" + input-id="targetUser" + :options="formatedUserSuggestions" + :multiple="false" + :loading="loadingUsers" + :user-select="true" + @search="findUserDebounced" /> + </p> + <p> + <NcButton native-type="submit" + type="primary" + :disabled="!canSubmit"> + {{ submitButtonText }} + </NcButton> + </p> + </form> + </div> +</template> + +<script> +import axios from '@nextcloud/axios' +import debounce from 'debounce' +import { generateOcsUrl } from '@nextcloud/router' +import { getFilePickerBuilder, showSuccess, showError } from '@nextcloud/dialogs' +import NcSelect from '@nextcloud/vue/components/NcSelect' +import Vue from 'vue' +import NcButton from '@nextcloud/vue/components/NcButton' + +import logger from '../logger.ts' + +const picker = getFilePickerBuilder(t('files', 'Choose a file or folder to transfer')) + .setMultiSelect(false) + .setType(1) + .allowDirectories() + .build() + +export default { + name: 'TransferOwnershipDialogue', + components: { + NcSelect, + NcButton, + }, + data() { + return { + directory: undefined, + directoryPickerError: undefined, + submitError: undefined, + loadingUsers: false, + selectedUser: null, + userSuggestions: {}, + config: { + minSearchStringLength: parseInt(OC.config['sharing.minSearchStringLength'], 10) || 0, + }, + } + }, + computed: { + canSubmit() { + return !!this.directory && !!this.selectedUser + }, + formatedUserSuggestions() { + return Object.keys(this.userSuggestions).map((uid) => { + const user = this.userSuggestions[uid] + return { + user: user.uid, + displayName: user.displayName, + icon: 'icon-user', + subname: user.shareWithDisplayNameUnique, + } + }) + }, + submitButtonText() { + if (!this.canSubmit) { + return t('files', 'Transfer') + } + const components = this.readableDirectory.split('/') + return t('files', 'Transfer {path} to {userid}', { path: components[components.length - 1], userid: this.selectedUser.displayName }) + }, + readableDirectory() { + if (!this.directory) { + return '' + } + return this.directory.substring(1) + }, + }, + created() { + this.findUserDebounced = debounce(this.findUser, 300) + this.findUser('') + }, + methods: { + start() { + this.directoryPickerError = undefined + + picker.pick() + .then(dir => dir === '' ? '/' : dir) + .then(dir => { + logger.debug(`path ${dir} selected for transferring ownership`) + if (!dir.startsWith('/')) { + throw new Error(t('files', 'Invalid path selected')) + } + // /ocs/v2.php/apps/files/api/v1/transferownership + // /ocs/v2.php/apps/files/api/v1/transferownership + this.directory = dir + }).catch(error => { + logger.error(`Selecting object for transfer aborted: ${error.message || 'Unknown error'}`, { error }) + + this.directoryPickerError = error.message || t('files', 'Unknown error') + showError(this.directoryPickerError) + }) + }, + async findUser(query) { + this.query = query.trim() + + if (query.length < this.config.minSearchStringLength) { + return + } + + this.loadingUsers = true + try { + const response = await axios.get(generateOcsUrl('apps/files_sharing/api/v1/sharees'), { + params: { + format: 'json', + itemType: 'file', + search: query, + perPage: 20, + lookup: false, + }, + }) + + this.userSuggestions = {} + response.data.ocs.data.exact.users.concat(response.data.ocs.data.users).forEach(user => { + Vue.set(this.userSuggestions, user.value.shareWith, { + uid: user.value.shareWith, + displayName: user.label, + shareWithDisplayNameUnique: user.shareWithDisplayNameUnique, + }) + }) + } catch (error) { + logger.error('could not fetch users', { error }) + } finally { + this.loadingUsers = false + } + }, + submit() { + if (!this.canSubmit) { + logger.warn('ignoring form submit') + } + + this.submitError = undefined + const data = { + path: this.directory, + recipient: this.selectedUser.user, + } + logger.debug('submit transfer ownership form', data) + + const url = generateOcsUrl('apps/files/api/v1/transferownership') + + axios.post(url, data) + .then(resp => resp.data) + .then(data => { + logger.info('Transfer ownership request sent', { data }) + + this.directory = undefined + this.selectedUser = null + showSuccess(t('files', 'Ownership transfer request sent')) + }) + .catch(error => { + logger.error('Could not send ownership transfer request', { error }) + + if (error?.response?.status === 403) { + this.submitError = t('files', 'Cannot transfer ownership of a file or folder you do not own') + } else { + this.submitError = error.message || t('files', 'Unknown error') + } + showError(this.submitError) + }) + }, + }, +} +</script> + +<style scoped lang="scss"> +p { + margin-top: 12px; + margin-bottom: 12px; +} + +.new-owner { + display: flex; + flex-direction: column; + max-width: 400px; + + label { + display: flex; + align-items: center; + margin-bottom: calc(var(--default-grid-baseline) * 2); + + span { + margin-inline-end: 8px; + } + } +} + +.transfer-select-row { + span { + margin-inline-end: 8px; + } + + &__choose_button { + width: min(100%, 400px) !important; + } +} +</style> |