aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_external/src
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_external/src')
-rw-r--r--apps/files_external/src/actions/enterCredentialsAction.ts27
-rw-r--r--apps/files_external/src/actions/inlineStorageCheckAction.ts72
-rw-r--r--apps/files_external/src/actions/openInFilesAction.ts3
-rw-r--r--apps/files_external/src/css/fileEntryStatus.scss5
-rw-r--r--apps/files_external/src/init.ts2
-rw-r--r--apps/files_external/src/settings.js49
-rw-r--r--apps/files_external/src/views/CredentialsDialog.vue10
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',
}]