mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
refactor(files_external): migrate files integration to script-setup and Vue 3
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
a5225bdf99
commit
5c652484e3
11 changed files with 84 additions and 107 deletions
|
|
@ -10,12 +10,13 @@ import type { StorageConfig } from '../services/externalStorage.ts'
|
|||
import LoginSvg from '@mdi/svg/svg/login.svg?raw'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { showError, showSuccess } from '@nextcloud/dialogs'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { DefaultType, FileAction } from '@nextcloud/files'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { addPasswordConfirmationInterceptors, PwdConfirmationMode } from '@nextcloud/password-confirmation'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { spawnDialog } from '@nextcloud/vue/functions/dialog'
|
||||
import Vue, { defineAsyncComponent } from 'vue'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { isMissingAuthConfig, STORAGE_STATUS } from '../utils/credentialsUtils.ts'
|
||||
import { isNodeExternalStorage } from '../utils/externalStorageUtils.ts'
|
||||
|
||||
|
|
@ -23,11 +24,6 @@ import { isNodeExternalStorage } from '../utils/externalStorageUtils.ts'
|
|||
// the backend requires the user to confirm their password
|
||||
addPasswordConfirmationInterceptors(axios)
|
||||
|
||||
type CredentialResponse = {
|
||||
login?: string
|
||||
password?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Set credentials for external storage
|
||||
*
|
||||
|
|
@ -55,7 +51,9 @@ async function setCredentials(node: Node, login: string, password: string): Prom
|
|||
|
||||
// Success update config attribute
|
||||
showSuccess(t('files_external', 'New configuration successfully saved'))
|
||||
Vue.set(node.attributes, 'config', config)
|
||||
node.attributes.config = config
|
||||
emit('files:node:updated', node)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -86,14 +84,7 @@ export const action = new FileAction({
|
|||
},
|
||||
|
||||
async exec({ nodes }) {
|
||||
const { login, password } = await new Promise<CredentialResponse>((resolve) => spawnDialog(
|
||||
defineAsyncComponent(() => import('../views/CredentialsDialog.vue')),
|
||||
{},
|
||||
(args) => {
|
||||
resolve(args as CredentialResponse)
|
||||
},
|
||||
))
|
||||
|
||||
const { login, password } = await spawnDialog(defineAsyncComponent(() => import('../views/CredentialsDialog.vue'))) ?? {}
|
||||
if (login && password) {
|
||||
try {
|
||||
await setCredentials(nodes[0], login, password)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
|
@ -8,9 +8,9 @@ import type { StorageConfig } from '../services/externalStorage.ts'
|
|||
|
||||
import AlertSvg from '@mdi/svg/svg/alert-circle.svg?raw'
|
||||
import { showWarning } from '@nextcloud/dialogs'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { FileAction } from '@nextcloud/files'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import Vue from 'vue'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { getStatus } from '../services/externalStorage.ts'
|
||||
import { isMissingAuthConfig, STORAGE_STATUS } from '../utils/credentialsUtils.ts'
|
||||
import { isNodeExternalStorage } from '../utils/externalStorageUtils.ts'
|
||||
|
|
@ -31,7 +31,8 @@ export const action = new FileAction({
|
|||
* Use this function to check the storage availability
|
||||
* We then update the node attributes directly.
|
||||
*
|
||||
* @param node The node to render inline
|
||||
* @param context - The action context
|
||||
* @param context.nodes - The node to render inline
|
||||
*/
|
||||
async renderInline({ nodes }) {
|
||||
if (nodes.length !== 1 || !nodes[0]) {
|
||||
|
|
@ -44,43 +45,43 @@ export const action = new FileAction({
|
|||
span.innerHTML = t('files_external', 'Checking storage …')
|
||||
|
||||
let config = null as unknown as StorageConfig
|
||||
getStatus(node.attributes.id, node.attributes.scope === 'system')
|
||||
.then((response) => {
|
||||
config = response.data
|
||||
Vue.set(node.attributes, 'config', config)
|
||||
try {
|
||||
const { data } = await getStatus(node.attributes.id, node.attributes.scope === 'system')
|
||||
config = data
|
||||
node.attributes.config = config
|
||||
emit('files:node:updated', node)
|
||||
|
||||
if (config.status !== STORAGE_STATUS.SUCCESS) {
|
||||
throw new Error(config?.statusMessage || t('files_external', 'There was an error with this external storage.'))
|
||||
}
|
||||
if (config.status !== STORAGE_STATUS.SUCCESS) {
|
||||
throw new Error(config?.statusMessage || t('files_external', 'There was an error with this external storage.'))
|
||||
}
|
||||
|
||||
span.remove()
|
||||
})
|
||||
.catch((error) => {
|
||||
// If axios failed or if something else prevented
|
||||
// us from getting the config
|
||||
if ((error as AxiosError).response && !config) {
|
||||
showWarning(t('files_external', 'We were unable to check the external storage {basename}', {
|
||||
basename: node.basename,
|
||||
}))
|
||||
}
|
||||
span.remove()
|
||||
} catch (error) {
|
||||
// If axios failed or if something else prevented
|
||||
// us from getting the config
|
||||
if ((error as AxiosError).response && !config) {
|
||||
showWarning(t('files_external', 'We were unable to check the external storage {basename}', {
|
||||
basename: node.basename,
|
||||
}))
|
||||
}
|
||||
|
||||
// Reset inline status
|
||||
span.innerHTML = ''
|
||||
// Reset inline status
|
||||
span.innerHTML = ''
|
||||
|
||||
// Checking if we really have an error
|
||||
const isWarning = !config ? false : isMissingAuthConfig(config)
|
||||
const overlay = document.createElement('span')
|
||||
overlay.classList.add(`files-list__row-status--${isWarning ? 'warning' : 'error'}`)
|
||||
// Checking if we really have an error
|
||||
const isWarning = !config ? false : isMissingAuthConfig(config)
|
||||
const overlay = document.createElement('span')
|
||||
overlay.classList.add(`files-list__row-status--${isWarning ? 'warning' : 'error'}`)
|
||||
|
||||
// Only show an icon for errors, warning like missing credentials
|
||||
// have a dedicated inline action button
|
||||
if (!isWarning) {
|
||||
span.innerHTML = AlertSvg
|
||||
span.title = (error as Error).message
|
||||
}
|
||||
// Only show an icon for errors, warning like missing credentials
|
||||
// have a dedicated inline action button
|
||||
if (!isWarning) {
|
||||
span.innerHTML = AlertSvg
|
||||
span.title = (error as Error).message
|
||||
}
|
||||
|
||||
span.prepend(overlay)
|
||||
})
|
||||
span.prepend(overlay)
|
||||
}
|
||||
|
||||
return span
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import type { StorageConfig } from '../services/externalStorage.ts'
|
|||
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { DefaultType, FileAction } from '@nextcloud/files'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { STORAGE_STATUS } from '../utils/credentialsUtils.ts'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import FolderNetworkSvg from '@mdi/svg/svg/folder-network-outline.svg?raw'
|
||||
import { Column, getNavigation, registerFileAction, View } from '@nextcloud/files'
|
||||
/**
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import FolderNetworkSvg from '@mdi/svg/svg/folder-network-outline.svg?raw'
|
||||
import { Column, getNavigation, registerFileAction, View } from '@nextcloud/files'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { action as enterCredentialsAction } from './actions/enterCredentialsAction.ts'
|
||||
|
|
|
|||
|
|
@ -48,8 +48,9 @@ export type MountEntry = {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convert an OCS api result (mount entry) to a Folder instance
|
||||
*
|
||||
* @param ocsEntry
|
||||
* @param ocsEntry - The OCS mount entry
|
||||
*/
|
||||
function entryToFolder(ocsEntry: MountEntry): Folder {
|
||||
const path = (ocsEntry.path + '/' + ocsEntry.name).replace(/^\//gm, '')
|
||||
|
|
@ -69,7 +70,7 @@ function entryToFolder(ocsEntry: MountEntry): Folder {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Fetch the contents of external storage mounts
|
||||
*/
|
||||
export async function getContents(): Promise<ContentsWithRoot> {
|
||||
const response = await axios.get(generateOcsUrl('apps/files_external/api/v1/mounts')) as AxiosResponse<OCSResponse<MountEntry[]>>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ export enum STORAGE_STATUS {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param config
|
||||
* Check if the given storage configuration is missing authentication configuration
|
||||
*
|
||||
* @param config - The storage configuration to check
|
||||
*/
|
||||
export function isMissingAuthConfig(config: StorageConfig) {
|
||||
// If we don't know the status, assume it is ok
|
||||
|
|
|
|||
|
|
@ -3,15 +3,17 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { Node } from '@nextcloud/files'
|
||||
import type { INode } from '@nextcloud/files'
|
||||
import type { MountEntry } from '../services/externalStorage.ts'
|
||||
|
||||
import { FileType } from '@nextcloud/files'
|
||||
|
||||
/**
|
||||
* @param node
|
||||
* Check if the given node represents an external storage mount
|
||||
*
|
||||
* @param node - The node to check
|
||||
*/
|
||||
export function isNodeExternalStorage(node: Node) {
|
||||
export function isNodeExternalStorage(node: INode) {
|
||||
// Not a folder, not a storage
|
||||
if (node.type === FileType.File) {
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -3,6 +3,28 @@
|
|||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<script setup lang="ts">
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { ref } from 'vue'
|
||||
import NcDialog from '@nextcloud/vue/components/NcDialog'
|
||||
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
|
||||
import NcPasswordField from '@nextcloud/vue/components/NcPasswordField'
|
||||
import NcTextField from '@nextcloud/vue/components/NcTextField'
|
||||
|
||||
defineEmits<{
|
||||
close: [payload?: { login: string, password: string }]
|
||||
}>()
|
||||
|
||||
const login = ref('')
|
||||
const password = ref('')
|
||||
|
||||
const dialogButtons: InstanceType<typeof NcDialog>['buttons'] = [{
|
||||
label: t('files_external', 'Confirm'),
|
||||
type: 'submit',
|
||||
variant: 'primary',
|
||||
}]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NcDialog
|
||||
:buttons="dialogButtons"
|
||||
|
|
@ -44,46 +66,3 @@
|
|||
required />
|
||||
</NcDialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { defineComponent } from 'vue'
|
||||
import NcDialog from '@nextcloud/vue/components/NcDialog'
|
||||
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
|
||||
import NcPasswordField from '@nextcloud/vue/components/NcPasswordField'
|
||||
import NcTextField from '@nextcloud/vue/components/NcTextField'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CredentialsDialog',
|
||||
|
||||
components: {
|
||||
NcDialog,
|
||||
NcNoteCard,
|
||||
NcTextField,
|
||||
NcPasswordField,
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
t,
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
login: '',
|
||||
password: '',
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
dialogButtons() {
|
||||
return [{
|
||||
label: t('files_external', 'Confirm'),
|
||||
type: 'submit',
|
||||
variant: 'primary',
|
||||
}]
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -40,10 +40,6 @@ module.exports = {
|
|||
'settings-personal': path.join(__dirname, 'apps/files/src', 'main-settings-personal.ts'),
|
||||
'reference-files': path.join(__dirname, 'apps/files/src', 'reference-files.ts'),
|
||||
},
|
||||
files_external: {
|
||||
init: path.join(__dirname, 'apps/files_external/src', 'init.ts'),
|
||||
settings: path.join(__dirname, 'apps/files_external/src', 'settings.js'),
|
||||
},
|
||||
files_sharing: {
|
||||
additionalScripts: path.join(__dirname, 'apps/files_sharing/src', 'additionalScripts.js'),
|
||||
collaboration: path.join(__dirname, 'apps/files_sharing/src', 'collaborationresourceshandler.js'),
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ const modules = {
|
|||
'settings-admin': resolve(import.meta.dirname, 'apps/federatedfilesharing/src', 'settings-admin.ts'),
|
||||
'settings-personal': resolve(import.meta.dirname, 'apps/federatedfilesharing/src', 'settings-personal.ts'),
|
||||
},
|
||||
files_external: {
|
||||
init: resolve(import.meta.dirname, 'apps/files_external/src', 'init.ts'),
|
||||
settings: resolve(import.meta.dirname, 'apps/files_external/src', 'settings.js'),
|
||||
},
|
||||
files_reminders: {
|
||||
init: resolve(import.meta.dirname, 'apps/files_reminders/src', 'files-init.ts'),
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue