diff options
Diffstat (limited to 'apps/files_external/src')
-rw-r--r-- | apps/files_external/src/actions/enterCredentialsAction.ts | 27 | ||||
-rw-r--r-- | apps/files_external/src/actions/inlineStorageCheckAction.ts | 72 | ||||
-rw-r--r-- | apps/files_external/src/actions/openInFilesAction.ts | 3 | ||||
-rw-r--r-- | apps/files_external/src/css/fileEntryStatus.scss | 5 | ||||
-rw-r--r-- | apps/files_external/src/init.ts | 2 | ||||
-rw-r--r-- | apps/files_external/src/settings.js | 49 | ||||
-rw-r--r-- | apps/files_external/src/views/CredentialsDialog.vue | 10 |
7 files changed, 104 insertions, 64 deletions
diff --git a/apps/files_external/src/actions/enterCredentialsAction.ts b/apps/files_external/src/actions/enterCredentialsAction.ts index 20821298db5..580f15ad876 100644 --- a/apps/files_external/src/actions/enterCredentialsAction.ts +++ b/apps/files_external/src/actions/enterCredentialsAction.ts @@ -7,6 +7,7 @@ import type { AxiosResponse } from '@nextcloud/axios' import type { Node } from '@nextcloud/files' import type { StorageConfig } from '../services/externalStorage' +import { addPasswordConfirmationInterceptors, PwdConfirmationMode } from '@nextcloud/password-confirmation' import { generateUrl } from '@nextcloud/router' import { showError, showSuccess, spawnDialog } from '@nextcloud/dialogs' import { translate as t } from '@nextcloud/l10n' @@ -18,6 +19,10 @@ import { FileAction, DefaultType } from '@nextcloud/files' import { STORAGE_STATUS, isMissingAuthConfig } from '../utils/credentialsUtils' import { isNodeExternalStorage } from '../utils/externalStorageUtils' +// Add password confirmation interceptors as +// the backend requires the user to confirm their password +addPasswordConfirmationInterceptors(axios) + type CredentialResponse = { login?: string, password?: string, @@ -31,8 +36,13 @@ type CredentialResponse = { * @param password The password */ async function setCredentials(node: Node, login: string, password: string): Promise<null|true> { - const configResponse = await axios.put(generateUrl('apps/files_external/userglobalstorages/{id}', { id: node.attributes.id }), { - backendOptions: { user: login, password }, + const configResponse = await axios.request({ + method: 'PUT', + url: generateUrl('apps/files_external/userglobalstorages/{id}', { id: node.attributes.id }), + confirmPassword: PwdConfirmationMode.Strict, + data: { + backendOptions: { user: login, password }, + }, }) as AxiosResponse<StorageConfig> const config = configResponse.data @@ -49,8 +59,10 @@ async function setCredentials(node: Node, login: string, password: string): Prom return true } +export const ACTION_CREDENTIALS_EXTERNAL_STORAGE = 'credentials-external-storage' + export const action = new FileAction({ - id: 'credentials-external-storage', + id: ACTION_CREDENTIALS_EXTERNAL_STORAGE, displayName: () => t('files', 'Enter missing credentials'), iconSvgInline: () => LoginSvg, @@ -83,7 +95,14 @@ export const action = new FileAction({ )) if (login && password) { - return await setCredentials(node, login, password) + try { + await setCredentials(node, login, password) + showSuccess(t('files_external', 'Credentials successfully set')) + } catch (error) { + showError(t('files_external', 'Error while setting credentials: {error}', { + error: (error as Error).message, + })) + } } return null diff --git a/apps/files_external/src/actions/inlineStorageCheckAction.ts b/apps/files_external/src/actions/inlineStorageCheckAction.ts index 42b1d5b3718..27c9b0bcabb 100644 --- a/apps/files_external/src/actions/inlineStorageCheckAction.ts +++ b/apps/files_external/src/actions/inlineStorageCheckAction.ts @@ -6,6 +6,7 @@ import type { AxiosError } from '@nextcloud/axios' import type { Node } from '@nextcloud/files' +import { FileAction } from '@nextcloud/files' import { showWarning } from '@nextcloud/dialogs' import { translate as t } from '@nextcloud/l10n' import AlertSvg from '@mdi/svg/svg/alert-circle.svg?raw' @@ -15,7 +16,6 @@ import '../css/fileEntryStatus.scss' import { getStatus, type StorageConfig } from '../services/externalStorage' import { isMissingAuthConfig, STORAGE_STATUS } from '../utils/credentialsUtils' import { isNodeExternalStorage } from '../utils/externalStorageUtils' -import { FileAction } from '@nextcloud/files' export const action = new FileAction({ id: 'check-external-storage', @@ -34,45 +34,51 @@ export const action = new FileAction({ * @param node The node to render inline */ async renderInline(node: Node) { + const span = document.createElement('span') + span.className = 'files-list__row-status' + span.innerHTML = t('files_external', 'Checking storage …') + let config = null as unknown as StorageConfig - try { - const response = await getStatus(node.attributes.id, node.attributes.scope === 'system') - config = response.data - Vue.set(node.attributes, 'config', config) + getStatus(node.attributes.id, node.attributes.scope === 'system') + .then(response => { + + config = response.data + Vue.set(node.attributes, 'config', config) + + 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, + })) + } - return null - } 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, - })) - return null - } + // Reset inline status + span.innerHTML = '' - // Checking if we really have an error - const isWarning = 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'}`) - const span = document.createElement('span') - span.className = 'files-list__row-status' + // 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 - } + return span }, order: 10, diff --git a/apps/files_external/src/actions/openInFilesAction.ts b/apps/files_external/src/actions/openInFilesAction.ts index 62fdd394bb2..e5f065e4871 100644 --- a/apps/files_external/src/actions/openInFilesAction.ts +++ b/apps/files_external/src/actions/openInFilesAction.ts @@ -10,6 +10,7 @@ import { translate as t } from '@nextcloud/l10n' import { FileAction, DefaultType } from '@nextcloud/files' import { STORAGE_STATUS } from '../utils/credentialsUtils' +import { getCurrentUser } from '@nextcloud/auth' export const action = new FileAction({ id: 'open-in-files-external-storage', @@ -32,7 +33,7 @@ export const action = new FileAction({ t('files_external', 'External mount error'), (redirect) => { if (redirect === true) { - const scope = node.attributes.scope === 'personal' ? 'user' : 'admin' + const scope = getCurrentUser()?.isAdmin ? 'admin' : 'user' window.location.href = generateUrl(`/settings/${scope}/externalstorages`) } }, diff --git a/apps/files_external/src/css/fileEntryStatus.scss b/apps/files_external/src/css/fileEntryStatus.scss index 34d57e91970..295234032bb 100644 --- a/apps/files_external/src/css/fileEntryStatus.scss +++ b/apps/files_external/src/css/fileEntryStatus.scss @@ -4,10 +4,13 @@ */ .files-list__row-status { display: flex; - width: 44px; + min-width: 44px; justify-content: center; align-items: center; height: 100%; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; svg { width: 24px; diff --git a/apps/files_external/src/init.ts b/apps/files_external/src/init.ts index 3f33e9cf5b6..a8a265500dd 100644 --- a/apps/files_external/src/init.ts +++ b/apps/files_external/src/init.ts @@ -5,7 +5,7 @@ import { loadState } from '@nextcloud/initial-state' import { translate as t } from '@nextcloud/l10n' import { View, getNavigation, Column, registerFileAction } from '@nextcloud/files' -import FolderNetworkSvg from '@mdi/svg/svg/folder-network.svg?raw' +import FolderNetworkSvg from '@mdi/svg/svg/folder-network-outline.svg?raw' import { action as enterCredentialsAction } from './actions/enterCredentialsAction' import { action as inlineStorageCheckAction } from './actions/inlineStorageCheckAction' diff --git a/apps/files_external/src/settings.js b/apps/files_external/src/settings.js index 2601fbe6ddb..033696c9d24 100644 --- a/apps/files_external/src/settings.js +++ b/apps/files_external/src/settings.js @@ -4,9 +4,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import axios from '@nextcloud/axios' -import { t } from '@nextcloud/l10n' import { addPasswordConfirmationInterceptors, PwdConfirmationMode } from '@nextcloud/password-confirmation' +import { generateUrl } from '@nextcloud/router' +import { showError } from '@nextcloud/dialogs' +import { t } from '@nextcloud/l10n' +import axios, { isAxiosError } from '@nextcloud/axios' import jQuery from 'jquery' @@ -645,13 +647,13 @@ const MountConfigListView = function($el, options) { MountConfigListView.ParameterFlags = { OPTIONAL: 1, USER_PROVIDED: 2, + HIDDEN: 4, } MountConfigListView.ParameterTypes = { TEXT: 0, BOOLEAN: 1, PASSWORD: 2, - HIDDEN: 3, } /** @@ -1140,13 +1142,13 @@ MountConfigListView.prototype = _.extend({ let newElement const trimmedPlaceholder = placeholder.value - if (placeholder.type === MountConfigListView.ParameterTypes.PASSWORD) { + if (hasFlag(MountConfigListView.ParameterFlags.HIDDEN)) { + newElement = $('<input type="hidden" class="' + classes.join(' ') + '" data-parameter="' + parameter + '" />') + } else if (placeholder.type === MountConfigListView.ParameterTypes.PASSWORD) { newElement = $('<input type="password" class="' + classes.join(' ') + '" data-parameter="' + parameter + '" placeholder="' + trimmedPlaceholder + '" />') } else if (placeholder.type === MountConfigListView.ParameterTypes.BOOLEAN) { const checkboxId = _.uniqueId('checkbox_') newElement = $('<div><label><input type="checkbox" id="' + checkboxId + '" class="' + classes.join(' ') + '" data-parameter="' + parameter + '" />' + trimmedPlaceholder + '</label></div>') - } else if (placeholder.type === MountConfigListView.ParameterTypes.HIDDEN) { - newElement = $('<input type="hidden" class="' + classes.join(' ') + '" data-parameter="' + parameter + '" />') } else { newElement = $('<input type="text" class="' + classes.join(' ') + '" data-parameter="' + parameter + '" placeholder="' + trimmedPlaceholder + '" />') } @@ -1522,21 +1524,30 @@ window.addEventListener('DOMContentLoaded', function() { const uid = $form.find('[name=uid]').val() const user = $form.find('[name=username]').val() const password = $form.find('[name=password]').val() - await axios.request({ - method: 'POST', - data: JSON.stringify({ - uid, - user, - password, - }), - url: OC.generateUrl('apps/files_external/globalcredentials'), - confirmPassword: PwdConfirmationMode.Strict, - }) - $submit.val(t('files_external', 'Saved')) - setTimeout(function() { + try { + await axios.request({ + method: 'POST', + data: { + uid, + user, + password, + }, + url: generateUrl('apps/files_external/globalcredentials'), + confirmPassword: PwdConfirmationMode.Strict, + }) + + $submit.val(t('files_external', 'Saved')) + setTimeout(function() { + $submit.val(t('files_external', 'Save')) + }, 2500) + } catch (error) { $submit.val(t('files_external', 'Save')) - }, 2500) + if (isAxiosError(error)) { + const message = error.response?.data?.message || t('files_external', 'Failed to save global credentials') + showError(t('files_external', 'Failed to save global credentials: {message}', { message })) + } + } return false }) diff --git a/apps/files_external/src/views/CredentialsDialog.vue b/apps/files_external/src/views/CredentialsDialog.vue index 56c8093db56..1d506628f6d 100644 --- a/apps/files_external/src/views/CredentialsDialog.vue +++ b/apps/files_external/src/views/CredentialsDialog.vue @@ -45,10 +45,10 @@ import { defineComponent } from 'vue' import { t } from '@nextcloud/l10n' -import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js' -import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js' -import NcPasswordField from '@nextcloud/vue/dist/Components/NcPasswordField.js' -import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' +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', @@ -76,7 +76,7 @@ export default defineComponent({ computed: { dialogButtons() { return [{ - label: t('files_external', 'Submit'), + label: t('files_external', 'Confirm'), type: 'primary', nativeType: 'submit', }] |