mirror of
https://github.com/nextcloud/server.git
synced 2026-06-13 18:50:47 -04:00
fix(files): ensure creating folders in public shares work
The root of the webdav client needs to be the public share root, as accessing the `/files` folder is not possible for public shares. Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
c331c81c3c
commit
276fdca6be
2 changed files with 34 additions and 27 deletions
|
|
@ -3,17 +3,18 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { Folder, Node, IFolder, INode } from '@nextcloud/files'
|
||||
import type { Upload } from '@nextcloud/upload'
|
||||
import type { RootDirectory } from './DropServiceUtils'
|
||||
import type { RootDirectory } from './DropServiceUtils.ts'
|
||||
|
||||
import { Folder, Node, NodeStatus, davRootPath } from '@nextcloud/files'
|
||||
import { NodeStatus } from '@nextcloud/files'
|
||||
import { getUploader, hasConflict } from '@nextcloud/upload'
|
||||
import { join } from '@nextcloud/paths'
|
||||
import { showError, showInfo, showSuccess, showWarning } from '@nextcloud/dialogs'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import Vue from 'vue'
|
||||
|
||||
import { Directory, traverseTree, resolveConflict, createDirectoryIfNotExists } from './DropServiceUtils'
|
||||
import { Directory, traverseTree, resolveConflict, createDirectoryIfNotExists } from './DropServiceUtils.ts'
|
||||
import { handleCopyMoveNodeTo } from '../actions/moveOrCopyAction'
|
||||
import { MoveCopyAction } from '../actions/moveOrCopyActionUtils'
|
||||
import logger from '../logger.ts'
|
||||
|
|
@ -95,11 +96,11 @@ export const dataTransferToFileTree = async (items: DataTransferItem[]): Promise
|
|||
* @param destination - The destination folder
|
||||
* @param contents - The contents of the destination folder
|
||||
*/
|
||||
export async function onDropExternalFiles(root: RootDirectory, destination: Folder, contents: Node[]): Promise<Upload[]> {
|
||||
export async function onDropExternalFiles(root: RootDirectory, destination: IFolder, contents: INode[]): Promise<Upload[]> {
|
||||
const uploader = getUploader()
|
||||
|
||||
// Check for conflicts on root elements
|
||||
if (await hasConflict(root.contents, contents)) {
|
||||
if (hasConflict(root.contents, contents as Node[])) {
|
||||
root.contents = await resolveConflict(root.contents, destination, contents)
|
||||
if (root.contents.length === 0) {
|
||||
// user cancelled the upload
|
||||
|
|
@ -124,14 +125,13 @@ export async function onDropExternalFiles(root: RootDirectory, destination: Fold
|
|||
// If the file is a directory, we need to create it first
|
||||
// then browse its tree and upload its contents.
|
||||
if (file instanceof Directory) {
|
||||
const absolutePath = join(davRootPath, destination.path, relativePath)
|
||||
try {
|
||||
console.debug('Processing directory', { relativePath })
|
||||
await createDirectoryIfNotExists(absolutePath)
|
||||
logger.debug('Processing directory', { relativePath })
|
||||
await createDirectoryIfNotExists(relativePath)
|
||||
await uploadDirectoryContents(file, relativePath)
|
||||
} catch (error) {
|
||||
showError(t('files', 'Unable to create the directory {directory}', { directory: file.name }))
|
||||
logger.error('', { error, absolutePath, directory: file })
|
||||
logger.error('Unable to create the directory', { error, relativePath, directory: file })
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
|
@ -170,11 +170,11 @@ export async function onDropExternalFiles(root: RootDirectory, destination: Fold
|
|||
return Promise.all(queue)
|
||||
}
|
||||
|
||||
export const onDropInternalFiles = async (nodes: Node[], destination: Folder, contents: Node[], isCopy = false) => {
|
||||
export const onDropInternalFiles = async (nodes: Node[], destination: IFolder, contents: INode[], isCopy = false) => {
|
||||
const queue = [] as Promise<void>[]
|
||||
|
||||
// Check for conflicts on root elements
|
||||
if (await hasConflict(nodes, contents)) {
|
||||
if (hasConflict(nodes, contents as Node[])) {
|
||||
nodes = await resolveConflict(nodes, destination, contents)
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +186,7 @@ export const onDropInternalFiles = async (nodes: Node[], destination: Folder, co
|
|||
|
||||
for (const node of nodes) {
|
||||
Vue.set(node, 'status', NodeStatus.LOADING)
|
||||
queue.push(handleCopyMoveNodeTo(node, destination, isCopy ? MoveCopyAction.COPY : MoveCopyAction.MOVE, true))
|
||||
queue.push(handleCopyMoveNodeTo(node, destination as Folder, isCopy ? MoveCopyAction.COPY : MoveCopyAction.MOVE, true))
|
||||
}
|
||||
|
||||
// Wait for all promises to settle
|
||||
|
|
|
|||
|
|
@ -2,13 +2,16 @@
|
|||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { IFolder, INode, Node } from '@nextcloud/files'
|
||||
import type { FileStat, ResponseDataDetailed } from 'webdav'
|
||||
|
||||
import { showWarning, showInfo } from '@nextcloud/dialogs'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { Folder, Node, davGetClient, davGetDefaultPropfind, davResultToNode } from '@nextcloud/files'
|
||||
import { defaultRemoteURL, defaultRootPath, getClient, getDefaultPropfind, resultToNode } from '@nextcloud/files/dav'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { join } from '@nextcloud/paths'
|
||||
import { openConflictPicker } from '@nextcloud/upload'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
|
||||
import logger from '../logger.ts'
|
||||
|
||||
|
|
@ -129,31 +132,35 @@ const readDirectory = (directory: FileSystemDirectoryEntry): Promise<FileSystemE
|
|||
})
|
||||
}
|
||||
|
||||
export const createDirectoryIfNotExists = async (absolutePath: string) => {
|
||||
const davClient = davGetClient()
|
||||
const dirExists = await davClient.exists(absolutePath)
|
||||
/**
|
||||
* @param path - The path relative to the dav root
|
||||
*/
|
||||
export async function createDirectoryIfNotExists(path: string) {
|
||||
const davUrl = join(defaultRemoteURL, defaultRootPath)
|
||||
const davClient = getClient(davUrl)
|
||||
const dirExists = await davClient.exists(path)
|
||||
if (!dirExists) {
|
||||
logger.debug('Directory does not exist, creating it', { absolutePath })
|
||||
await davClient.createDirectory(absolutePath, { recursive: true })
|
||||
const stat = await davClient.stat(absolutePath, { details: true, data: davGetDefaultPropfind() }) as ResponseDataDetailed<FileStat>
|
||||
emit('files:node:created', davResultToNode(stat.data))
|
||||
logger.debug('Directory does not exist, creating it', { path })
|
||||
await davClient.createDirectory(path, { recursive: true })
|
||||
const stat = await davClient.stat(path, { details: true, data: getDefaultPropfind() }) as ResponseDataDetailed<FileStat>
|
||||
emit('files:node:created', resultToNode(stat.data, defaultRootPath, davUrl))
|
||||
}
|
||||
}
|
||||
|
||||
export const resolveConflict = async <T extends ((Directory|File)|Node)>(files: Array<T>, destination: Folder, contents: Node[]): Promise<T[]> => {
|
||||
export const resolveConflict = async <T extends ((Directory|File)|INode)>(files: Array<T>, destination: IFolder, contents: INode[]): Promise<T[]> => {
|
||||
try {
|
||||
// List all conflicting files
|
||||
const conflicts = files.filter((file: File|Node) => {
|
||||
return contents.find((node: Node) => node.basename === (file instanceof File ? file.name : file.basename))
|
||||
}).filter(Boolean) as (File|Node)[]
|
||||
const conflicts = files.filter((file: File|INode) => {
|
||||
return contents.find((node: INode) => node.basename === (file instanceof File ? file.name : file.basename))
|
||||
}).filter(Boolean) as (File|INode)[]
|
||||
|
||||
// List of incoming files that are NOT in conflict
|
||||
const uploads = files.filter((file: File|Node) => {
|
||||
const uploads = files.filter((file: T) => {
|
||||
return !conflicts.includes(file)
|
||||
})
|
||||
|
||||
// Let the user choose what to do with the conflicting files
|
||||
const { selected, renamed } = await openConflictPicker(destination.path, conflicts, contents)
|
||||
const { selected, renamed } = await openConflictPicker(destination.path, conflicts as (File | Node)[], contents as Node[])
|
||||
|
||||
logger.debug('Conflict resolution', { uploads, selected, renamed })
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue