Files
nextcloud-server-mirror/dist/hashUtils-BlT4ancg.chunk.mjs.map
2026-10-07 08:58:40 +00:00

1 line
43 KiB
Plaintext

{"version":3,"file":"hashUtils-BlT4ancg.chunk.mjs","sources":["../build/frontend/apps/files/src/utils/permissions.ts","../node_modules/@mdi/svg/svg/folder-move-outline.svg?raw","../build/frontend/apps/files/src/utils/conflicts.ts","../build/frontend/apps/files/src/actions/moveOrCopyActionUtils.ts","../build/frontend/apps/files/src/actions/moveOrCopyAction.ts","../build/frontend/apps/files/src/actions/sidebarAction.ts","../build/frontend/apps/files/src/utils/filenameValidity.ts","../build/frontend/apps/files/src/components/NewNodeDialog.vue","../build/frontend/apps/files/src/utils/newNodeDialog.ts","../build/frontend/apps/files/src/services/LivePhotos.ts","../build/frontend/apps/files/src/utils/hashUtils.ts"],"sourcesContent":["/*!\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { INode } from '@nextcloud/files'\nimport type { ShareAttribute } from '../../../files_sharing/src/sharing.ts'\n\nimport { Permission } from '@nextcloud/files'\n\n/**\n * Check permissions on the node if it can be downloaded\n *\n * @param node The node to check\n * @return True if downloadable, false otherwise\n */\nexport function isDownloadable(node: INode): boolean {\n\tif ((node.permissions & Permission.READ) === 0) {\n\t\treturn false\n\t}\n\n\t// check hide-download property of shares\n\tif (node.attributes['hide-download'] === true\n\t\t|| node.attributes['hide-download'] === 'true') {\n\t\treturn false\n\t}\n\n\t// If the mount type is a share, ensure it got download permissions.\n\tif (node.attributes['share-attributes']) {\n\t\tconst shareAttributes = JSON.parse(node.attributes['share-attributes'] || '[]') as Array<ShareAttribute>\n\t\tconst downloadAttribute = shareAttributes.find(({ scope, key }: ShareAttribute) => scope === 'permissions' && key === 'download')\n\t\tif (downloadAttribute !== undefined) {\n\t\t\treturn downloadAttribute.value === true\n\t\t}\n\t}\n\n\treturn true\n}\n\n/**\n * Check permissions on the node if it can be synced/open locally\n *\n * @param node The node to check\n * @return True if syncable, false otherwise\n */\nexport function isSyncable(node: INode): boolean {\n\tif (!node.isDavResource) {\n\t\treturn false\n\t}\n\n\tif ((node.permissions & Permission.WRITE) === 0) {\n\t\treturn false\n\t}\n\n\t// Syncable has the same permissions as downloadable for now\n\treturn isDownloadable(node)\n}\n","export default \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" id=\\\"mdi-folder-move-outline\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M20 18H4V8H20V18M12 6L10 4H4C2.9 4 2 4.89 2 6V18C2 19.11 2.9 20 4 20H20C21.11 20 22 19.11 22 18V8C22 6.9 21.11 6 20 6H12M11 14V12H15V9L19 13L15 17V14H11Z\\\" /></svg>\"","/*\n * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { INode } from '@nextcloud/files'\n\ntype ConflictInput = INode | File | FileSystemEntry\n\n/**\n * Check if there are conflicts between two sets of nodes\n *\n * @param sourceNodes - The nodes that might conflict\n * @param targetNodes - The nodes in the target directory\n */\nexport function getConflicts<T extends ConflictInput>(sourceNodes: T[], targetNodes: INode[]): T[] {\n\tconst targetNames = targetNodes.map((node) => node.basename)\n\treturn sourceNodes.filter((node) => targetNames.includes('basename' in node ? node.basename : node.name))\n}\n","/*!\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { IFolder, INode } from '@nextcloud/files'\nimport type { ShareAttribute } from '../../../files_sharing/src/sharing.ts'\n\nimport { Permission } from '@nextcloud/files'\nimport { loadState } from '@nextcloud/initial-state'\nimport { isPublicShare } from '@nextcloud/sharing/public'\nimport PQueue from 'p-queue'\n\nconst sharePermissions = loadState<number>('files_sharing', 'sharePermissions', Permission.NONE)\n\n// This is the processing queue. We only want to allow 3 concurrent requests\nlet queue: PQueue\n\n// Maximum number of concurrent operations\nconst MAX_CONCURRENCY = 5\n\n/**\n * Get the processing queue\n */\nexport function getQueue() {\n\tif (!queue) {\n\t\tqueue = new PQueue({ concurrency: MAX_CONCURRENCY })\n\t}\n\treturn queue\n}\n\nexport enum MoveCopyAction {\n\tMOVE = 'Move',\n\tCOPY = 'Copy',\n\tMOVE_OR_COPY = 'move-or-copy',\n}\n\nexport type MoveCopyResult = {\n\tdestination: IFolder\n\taction: MoveCopyAction.COPY | MoveCopyAction.MOVE\n}\n\n/**\n * Check if the given nodes can be moved\n *\n * @param nodes - The nodes to check\n */\nexport function canMove(nodes: INode[]) {\n\tconst minPermission = nodes.reduce((min, node) => Math.min(min, node.permissions), Permission.ALL)\n\treturn Boolean(minPermission & Permission.DELETE)\n}\n\n/**\n * Check if the given nodes can be downloaded\n *\n * @param nodes - The nodes to check\n */\nexport function canDownload(nodes: INode[]) {\n\treturn nodes.every((node) => {\n\t\tconst shareAttributes = JSON.parse(node.attributes?.['share-attributes'] ?? '[]') as Array<ShareAttribute>\n\t\treturn !shareAttributes.some((attribute) => attribute.scope === 'permissions' && attribute.value === false && attribute.key === 'download')\n\t})\n}\n\n/**\n * Check if the given nodes can be copied\n *\n * @param nodes - The nodes to check\n */\nexport function canCopy(nodes: INode[]) {\n\t// a shared file cannot be copied if the download is disabled\n\tif (!canDownload(nodes)) {\n\t\treturn false\n\t}\n\t// it cannot be copied if the user has only view permissions\n\tif (nodes.some((node) => node.permissions === Permission.NONE)) {\n\t\treturn false\n\t}\n\t// on public shares all files have the same permission so copy is only possible if write permission is granted\n\tif (isPublicShare()) {\n\t\treturn Boolean(sharePermissions & Permission.CREATE)\n\t}\n\t// otherwise permission is granted\n\treturn true\n}\n","/*!\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { IFilePickerButton } from '@nextcloud/dialogs'\nimport type { IFileAction, IFolder, INode } from '@nextcloud/files'\nimport type { FileStat, ResponseDataDetailed, WebDAVClientError } from 'webdav'\nimport type { MoveCopyResult } from './moveOrCopyActionUtils.ts'\n\nimport FolderMoveSvg from '@mdi/svg/svg/folder-move-outline.svg?raw'\nimport CopyIconSvg from '@mdi/svg/svg/folder-multiple-outline.svg?raw'\nimport { FilePickerClosed, getFilePickerBuilder, openConflictPicker, showError, showLoading } from '@nextcloud/dialogs'\nimport { emit } from '@nextcloud/event-bus'\nimport { FileType, getUniqueName, NodeStatus, Permission } from '@nextcloud/files'\nimport { defaultRootPath, getClient, getDefaultPropfind, resultToNode } from '@nextcloud/files/dav'\nimport { n, t } from '@nextcloud/l10n'\nimport { basename, join } from '@nextcloud/paths'\nimport { getContents } from '../services/Files.ts'\nimport { getConflicts } from '../utils/conflicts.ts'\nimport { logger } from '../utils/logger.ts'\nimport { canCopy, canMove, getQueue, MoveCopyAction } from './moveOrCopyActionUtils.ts'\n\n/**\n * Exception to hint the user about something.\n * The message is intended to be shown to the user.\n */\nexport class HintException extends Error {}\n\nexport const ACTION_COPY_MOVE = 'move-copy'\n\nexport const action: IFileAction = {\n\tid: ACTION_COPY_MOVE,\n\torder: 15,\n\tdisplayName({ nodes }) {\n\t\tswitch (getActionForNodes(nodes)) {\n\t\t\tcase MoveCopyAction.MOVE:\n\t\t\t\treturn t('files', 'Move')\n\t\t\tcase MoveCopyAction.COPY:\n\t\t\t\treturn t('files', 'Copy')\n\t\t\tcase MoveCopyAction.MOVE_OR_COPY:\n\t\t\t\treturn t('files', 'Move or copy')\n\t\t}\n\t},\n\ticonSvgInline: () => FolderMoveSvg,\n\tenabled({ nodes, view }): boolean {\n\t\t// We can not copy or move in single file shares\n\t\tif (view.id === 'public-file-share') {\n\t\t\treturn false\n\t\t}\n\t\t// We only support moving/copying files within the user folder\n\t\tif (!nodes.every((node) => node.root?.startsWith('/files/'))) {\n\t\t\treturn false\n\t\t}\n\t\treturn nodes.length > 0 && (canMove(nodes) || canCopy(nodes))\n\t},\n\n\tasync exec(context) {\n\t\treturn this.execBatch!(context)[0]\n\t},\n\n\tasync execBatch({ nodes, folder }) {\n\t\tconst action = getActionForNodes(nodes)\n\t\tconst target = await openFilePickerForAction(action, folder.path, nodes)\n\t\t// Handle cancellation silently\n\t\tif (target === false) {\n\t\t\treturn nodes.map(() => null)\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await Array.fromAsync(handleCopyMoveNodesTo(nodes, target.destination, target.action))\n\t\t\treturn result.map(() => true)\n\t\t} catch (error) {\n\t\t\tlogger.error(`Failed to ${target.action} node`, { nodes, error })\n\t\t\tif (error instanceof HintException && !!error.message) {\n\t\t\t\tshowError(error.message)\n\t\t\t\t// Silent action as we handle the toast\n\t\t\t\treturn nodes.map(() => null)\n\t\t\t}\n\t\t\t// We need to keep the selection on error!\n\t\t\t// So we do not return null, and for batch action\n\t\t\treturn nodes.map(() => false)\n\t\t}\n\t},\n}\n\n/**\n * Handle the copy/move of a node to a destination\n * This can be imported and used by other scripts/components on server\n *\n * @param nodes The nodes to copy/move\n * @param destination The destination to copy/move the nodes to\n * @param method The method to use for the copy/move\n * @param overwrite Whether to overwrite the destination if it exists\n * @yields {AsyncGenerator<void, void, never>} A promise that resolves when the copy/move is done\n */\nexport async function* handleCopyMoveNodesTo(nodes: INode[], destination: IFolder, method: MoveCopyAction.COPY | MoveCopyAction.MOVE, overwrite = false): AsyncGenerator<void, void, never> {\n\tif (!destination) {\n\t\treturn\n\t}\n\n\tif (destination.type !== FileType.Folder) {\n\t\tthrow new Error(t('files', 'Destination is not a folder'))\n\t}\n\n\t// Do not allow to MOVE a node to the same folder it is already located\n\tif (method === MoveCopyAction.MOVE && nodes.some((node) => node.dirname === destination.path)) {\n\t\tthrow new Error(t('files', 'This file/folder is already in that directory'))\n\t}\n\n\t/**\n\t * Example:\n\t * - node: /foo/bar/file.txt -> path = /foo/bar/file.txt, destination: /foo\n\t * Allow move of /foo does not start with /foo/bar/file.txt so allow\n\t * - node: /foo , destination: /foo/bar\n\t * Do not allow as it would copy foo within itself\n\t * - node: /foo/bar.txt, destination: /foo\n\t * Allow copy a file to the same directory\n\t * - node: \"/foo/bar\", destination: \"/foo/bar 1\"\n\t * Allow to move or copy but we need to check with trailing / otherwise it would report false positive\n\t */\n\tif (nodes.some((node) => `${destination.path}/`.startsWith(`${node.path}/`))) {\n\t\tthrow new Error(t('files', 'You cannot move a file/folder onto itself or into a subfolder of itself'))\n\t}\n\n\tconst nameMapping = new Map<string, string>()\n\t// Check for conflicts if we do not want to overwrite\n\tif (!overwrite) {\n\t\tconst otherNodes = (await getContents(destination.path)).contents\n\t\tconst conflicts = getConflicts(nodes, otherNodes) as unknown as INode[]\n\t\tconst nodesToRename: INode[] = []\n\t\tif (conflicts.length > 0) {\n\t\t\tif (method === MoveCopyAction.MOVE) {\n\t\t\t\t// Let the user choose what to do with the conflicting files\n\t\t\t\tconst content = otherNodes.filter((n) => conflicts.some((c) => c.basename === n.basename))\n\t\t\t\tconst result = await openConflictPicker(destination.path, conflicts, content, { overwriting: true })\n\t\t\t\tif (!result) {\n\t\t\t\t\t// User cancelled\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tnodes = nodes.filter((n) => !result.skipped.includes(n as never))\n\t\t\t\tnodesToRename.push(...(result.renamed as unknown as INode[]))\n\t\t\t} else {\n\t\t\t\t// for COPY we always rename conflicting files\n\t\t\t\tnodesToRename.push(...conflicts)\n\t\t\t}\n\n\t\t\tconst usedNames = [...otherNodes, ...nodes.filter((n) => !conflicts.includes(n))].map((n) => n.basename)\n\t\t\tfor (const node of nodesToRename) {\n\t\t\t\tconst newName = getUniqueName(node.basename, usedNames, { ignoreFileExtension: node.type === FileType.Folder })\n\t\t\t\tnameMapping.set(node.source, newName)\n\t\t\t\tusedNames.push(newName) // add the new name to avoid duplicates for following re-namimgs\n\t\t\t}\n\t\t}\n\t}\n\n\tconst actionFinished = createLoadingNotification(\n\t\tmethod,\n\t\tnodes.map((node) => node.displayname),\n\t\tjoin(destination.dirname, destination.displayname).replace(/^\\//, ''),\n\t)\n\tconst queue = getQueue()\n\ttry {\n\t\tfor (const node of nodes) {\n\t\t\t// Set loading state\n\t\t\tnode.status = NodeStatus.LOADING\n\t\t\tyield queue.add(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst client = getClient()\n\n\t\t\t\t\tconst currentPath = join(defaultRootPath, node.path)\n\t\t\t\t\tconst destinationPath = join(defaultRootPath, destination.path, nameMapping.get(node.source) ?? node.basename)\n\n\t\t\t\t\tif (method === MoveCopyAction.COPY) {\n\t\t\t\t\t\tawait client.copyFile(currentPath, destinationPath)\n\t\t\t\t\t\t// If the node is copied into current directory the view needs to be updated\n\t\t\t\t\t\tif (node.dirname === destination.path) {\n\t\t\t\t\t\t\tconst { data } = await client.stat(\n\t\t\t\t\t\t\t\tdestinationPath,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tdetails: true,\n\t\t\t\t\t\t\t\t\tdata: getDefaultPropfind(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t) as ResponseDataDetailed<FileStat>\n\t\t\t\t\t\t\temit('files:node:created', resultToNode(data))\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait client.moveFile(currentPath, destinationPath)\n\t\t\t\t\t\t// Delete the node as it will be fetched again\n\t\t\t\t\t\t// when navigating to the destination folder\n\t\t\t\t\t\temit('files:node:deleted', node)\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.debug(`Error while trying to ${method === MoveCopyAction.COPY ? 'copy' : 'move'} node`, { node, error })\n\t\t\t\t\tif (error.response?.status === 412) {\n\t\t\t\t\t\tthrow new HintException(t('files', 'A file or folder with that name already exists in this folder'))\n\t\t\t\t\t} else if (error.response?.status === 423) {\n\t\t\t\t\t\tthrow new HintException(t('files', 'The files are locked'))\n\t\t\t\t\t} else if (error.response?.status === 404) {\n\t\t\t\t\t\tthrow new HintException(t('files', 'The file does not exist anymore'))\n\t\t\t\t\t} else if ('response' in error && error.response) {\n\t\t\t\t\t\tconst parser = new DOMParser()\n\t\t\t\t\t\tconst text = await (error as WebDAVClientError).response!.text()\n\t\t\t\t\t\tconst message = parser.parseFromString(text ?? '', 'text/xml')\n\t\t\t\t\t\t\t.querySelector('message')?.textContent\n\t\t\t\t\t\tif (message) {\n\t\t\t\t\t\t\tthrow new HintException(message)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthrow error\n\t\t\t\t} finally {\n\t\t\t\t\tnode.status = undefined\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t} finally {\n\t\tactionFinished()\n\t}\n}\n\n/**\n * Return the action that is possible for the given nodes\n *\n * @param nodes The nodes to check against\n * @return The action that is possible for the given nodes\n */\nfunction getActionForNodes(nodes: INode[]): MoveCopyAction {\n\tif (canMove(nodes)) {\n\t\tif (canCopy(nodes)) {\n\t\t\treturn MoveCopyAction.MOVE_OR_COPY\n\t\t}\n\t\treturn MoveCopyAction.MOVE\n\t}\n\n\t// Assuming we can copy as the enabled checks for copy permissions\n\treturn MoveCopyAction.COPY\n}\n\n/**\n * Create a loading notification toast\n *\n * @param mode The move or copy mode\n * @param sources Names of the nodes that are copied / moved\n * @param destination Destination path\n * @return Function to hide the notification\n */\nfunction createLoadingNotification(mode: MoveCopyAction, sources: string[], destination: string): () => void {\n\tconst text = mode === MoveCopyAction.MOVE\n\t\t? (sources.length === 1\n\t\t\t\t? t('files', 'Moving \"{source}\" to \"{destination}\" …', { source: sources[0]!, destination })\n\t\t\t\t: n('files', 'Moving %n file to \"{destination}\" …', 'Moving %n files to \"{destination}\" …', sources.length, { destination })\n\t\t\t)\n\t\t: (sources.length === 1\n\t\t\t\t? t('files', 'Copying \"{source}\" to \"{destination}\" …', { source: sources[0]!, destination })\n\t\t\t\t: n('files', 'Copying %n file to \"{destination}\" …', 'Copying %n files to \"{destination}\" …', sources.length, { destination })\n\t\t\t)\n\n\tconst toast = showLoading(text)\n\treturn () => toast && toast.hideToast()\n}\n\n/**\n * Open a file picker for the given action\n *\n * @param action The action to open the file picker for\n * @param dir The directory to start the file picker in\n * @param nodes The nodes to move/copy\n * @return The picked destination or false if cancelled by user\n */\nasync function openFilePickerForAction(\n\taction: MoveCopyAction,\n\tdir = '/',\n\tnodes: INode[],\n): Promise<MoveCopyResult | false> {\n\tconst { resolve, reject, promise } = Promise.withResolvers<MoveCopyResult | false>()\n\tconst fileIDs = nodes.map((node) => node.fileid).filter(Boolean)\n\tconst filePicker = getFilePickerBuilder(t('files', 'Choose destination'))\n\t\t.allowDirectories(true)\n\t\t.setFilter((n) => {\n\t\t\t// We don't want to show the current nodes in the file picker\n\t\t\treturn !fileIDs.includes(n.fileid)\n\t\t})\n\t\t.setCanPick((n) => {\n\t\t\tconst hasCreatePermissions = (n.permissions & Permission.CREATE) === Permission.CREATE\n\t\t\treturn hasCreatePermissions\n\t\t})\n\t\t.setMimeTypeFilter([])\n\t\t.setMultiSelect(false)\n\t\t.startAt(dir)\n\t\t.setButtonFactory((selection, path) => {\n\t\t\tconst buttons: IFilePickerButton[] = []\n\t\t\tconst target = basename(path)\n\n\t\t\tconst dirnames = nodes.map((node) => node.dirname)\n\t\t\tconst paths = nodes.map((node) => node.path)\n\n\t\t\tif (action === MoveCopyAction.COPY || action === MoveCopyAction.MOVE_OR_COPY) {\n\t\t\t\tbuttons.push({\n\t\t\t\t\tlabel: target ? t('files', 'Copy to {target}', { target }, { escape: false, sanitize: false }) : t('files', 'Copy'),\n\t\t\t\t\tdisabled: selection.length === 0,\n\t\t\t\t\tvariant: 'primary',\n\t\t\t\t\ticon: CopyIconSvg,\n\t\t\t\t\tasync callback(destination) {\n\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\tdestination: destination[0] as unknown as IFolder,\n\t\t\t\t\t\t\taction: MoveCopyAction.COPY,\n\t\t\t\t\t\t} as MoveCopyResult)\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// Invalid MOVE targets (but valid copy targets)\n\t\t\tif (dirnames.includes(path)) {\n\t\t\t\t// This file/folder is already in that directory\n\t\t\t\treturn buttons\n\t\t\t}\n\n\t\t\tif (paths.includes(path)) {\n\t\t\t\t// You cannot move a file/folder onto itself\n\t\t\t\treturn buttons\n\t\t\t}\n\n\t\t\tif (selection.some((node) => (node.permissions & Permission.CREATE) === 0)) {\n\t\t\t\t// Missing 'CREATE' permissions for selected destination\n\t\t\t\treturn buttons\n\t\t\t}\n\n\t\t\tif (action === MoveCopyAction.MOVE || action === MoveCopyAction.MOVE_OR_COPY) {\n\t\t\t\tbuttons.push({\n\t\t\t\t\tlabel: target ? t('files', 'Move to {target}', { target }, { escape: false, sanitize: false }) : t('files', 'Move'),\n\t\t\t\t\tdisabled: selection.length === 0,\n\t\t\t\t\tvariant: action === MoveCopyAction.MOVE ? 'primary' : 'secondary',\n\t\t\t\t\ticon: FolderMoveSvg,\n\t\t\t\t\tasync callback(destination) {\n\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\tdestination: destination[0] as unknown as IFolder,\n\t\t\t\t\t\t\taction: MoveCopyAction.MOVE,\n\t\t\t\t\t\t} as MoveCopyResult)\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\n\t\t\treturn buttons\n\t\t})\n\t\t.build()\n\n\tfilePicker.pick()\n\t\t.catch((error: Error) => {\n\t\t\tlogger.debug(error as Error)\n\t\t\tif (error instanceof FilePickerClosed) {\n\t\t\t\tresolve(false)\n\t\t\t} else {\n\t\t\t\treject(new Error(t('files', 'Move or copy operation failed')))\n\t\t\t}\n\t\t})\n\n\treturn promise\n}\n","/*!\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { IFileAction } from '@nextcloud/files'\n\nimport InformationSvg from '@mdi/svg/svg/information-outline.svg?raw'\nimport { getSidebar, Permission } from '@nextcloud/files'\nimport { t } from '@nextcloud/l10n'\nimport { isPublicShare } from '@nextcloud/sharing/public'\nimport { logger } from '../utils/logger.ts'\n\nexport const ACTION_DETAILS = 'details'\n\nexport const action: IFileAction = {\n\tid: ACTION_DETAILS,\n\tdisplayName: () => t('files', 'Details'),\n\ticonSvgInline: () => InformationSvg,\n\n\t// Sidebar currently supports user folder only, /files/USER\n\tenabled: ({ nodes }) => {\n\t\tconst node = nodes[0]\n\t\tif (nodes.length !== 1 || !node) {\n\t\t\treturn false\n\t\t}\n\n\t\tconst sidebar = getSidebar()\n\t\tif (!sidebar.available) {\n\t\t\treturn false\n\t\t}\n\n\t\tif (isPublicShare()) {\n\t\t\treturn false\n\t\t}\n\n\t\treturn node.root.startsWith('/files/') && node.permissions !== Permission.NONE\n\t},\n\n\tasync exec({ nodes }) {\n\t\tconst sidebar = getSidebar()\n\t\tconst [node] = nodes\n\t\ttry {\n\t\t\t// If the sidebar is already open for the current file, do nothing\n\t\t\tif (sidebar.node?.source === node.source) {\n\t\t\t\tlogger.debug('Sidebar already open for this file', { node })\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tsidebar.open(node, 'sharing')\n\t\t\treturn null\n\t\t} catch (error) {\n\t\t\tlogger.error('Error while opening sidebar', { error })\n\t\t\treturn false\n\t\t}\n\t},\n\n\torder: -50,\n\n\thotkey: {\n\t\tkey: 'D',\n\t\tdescription: t('files', 'Open the details sidebar'),\n\t},\n}\n","/*!\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\nimport { InvalidFilenameError, InvalidFilenameErrorReason, validateFilename } from '@nextcloud/files'\nimport { t } from '@nextcloud/l10n'\n\n/**\n * Get the validity of a filename (empty if valid).\n * This can be used for `setCustomValidity` on input elements\n *\n * @param name The filename\n * @param escape Escape the matched string in the error (only set when used in HTML)\n * @param isFolder Whether the filename is for a folder\n */\nexport function getFilenameValidity(name: string, escape = false, isFolder = false): string {\n\tif (name.trim() === '') {\n\t\tif (isFolder) {\n\t\t\treturn t('files', 'Folder name must not be empty.')\n\t\t}\n\t\treturn t('files', 'Filename must not be empty.')\n\t}\n\n\ttry {\n\t\tvalidateFilename(name)\n\t\treturn ''\n\t} catch (error) {\n\t\tif (!(error instanceof InvalidFilenameError)) {\n\t\t\tthrow error\n\t\t}\n\n\t\tswitch (error.reason) {\n\t\t\tcase InvalidFilenameErrorReason.Character:\n\t\t\t\tif (isFolder) {\n\t\t\t\t\treturn t('files', '\"{char}\" is not allowed inside a folder name.', { char: error.segment }, { escape })\n\t\t\t\t}\n\t\t\t\treturn t('files', '\"{char}\" is not allowed inside a filename.', { char: error.segment }, { escape })\n\t\t\tcase InvalidFilenameErrorReason.ReservedName:\n\t\t\t\tif (isFolder) {\n\t\t\t\t\treturn t('files', '\"{segment}\" is a reserved name and not allowed for folder names.', { segment: error.segment }, { escape: false })\n\t\t\t\t}\n\t\t\t\treturn t('files', '\"{segment}\" is a reserved name and not allowed for filenames.', { segment: error.segment }, { escape: false })\n\t\t\tcase InvalidFilenameErrorReason.Extension:\n\t\t\t\tif (!isFolder && error.segment.match(/\\.[a-z]/i)) {\n\t\t\t\t\treturn t('files', '\"{extension}\" is not an allowed filetype.', { extension: error.segment }, { escape: false })\n\t\t\t\t}\n\t\t\t\tif (isFolder) {\n\t\t\t\t\treturn t('files', 'Folder names must not end with \"{extension}\".', { extension: error.segment }, { escape: false })\n\t\t\t\t}\n\t\t\t\treturn t('files', 'Filenames must not end with \"{extension}\".', { extension: error.segment }, { escape: false })\n\t\t\tdefault:\n\t\t\t\tif (isFolder) {\n\t\t\t\t\treturn t('files', 'Invalid folder name.')\n\t\t\t\t}\n\t\t\t\treturn t('files', 'Invalid filename.')\n\t\t}\n\t}\n}\n","<!--\n - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<NcDialog\n\t\tdata-cy-files-new-node-dialog\n\t\t:name=\"name\"\n\t\t:open=\"open\"\n\t\tcloseOnClickOutside\n\t\toutTransition\n\t\t@update:open=\"emit('close', null)\">\n\t\t<template #actions>\n\t\t\t<NcButton\n\t\t\t\tdata-cy-files-new-node-dialog-submit\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"validity !== ''\"\n\t\t\t\t@click=\"submit\">\n\t\t\t\t{{ submitLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t\t<form\n\t\t\tref=\"formElement\"\n\t\t\tclass=\"new-node-dialog__form\"\n\t\t\t@submit.prevent=\"emit('close', localDefaultName)\">\n\t\t\t<NcTextField\n\t\t\t\tref=\"nameInput\"\n\t\t\t\tv-model=\"localDefaultName\"\n\t\t\t\tdata-cy-files-new-node-dialog-input\n\t\t\t\t:error=\"validity !== ''\"\n\t\t\t\t:helperText=\"validity\"\n\t\t\t\t:label=\"label\" />\n\n\t\t\t<!-- Hidden file warning -->\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"isHiddenFileName\"\n\t\t\t\ttype=\"warning\"\n\t\t\t\t:text=\"t('files', 'Files starting with a dot are hidden by default')\" />\n\t\t</form>\n\t</NcDialog>\n</template>\n\n<script setup lang=\"ts\">\nimport type { ComponentPublicInstance, PropType } from 'vue'\n\nimport { getUniqueName } from '@nextcloud/files'\nimport { t } from '@nextcloud/l10n'\nimport { extname } from 'path'\nimport { computed, nextTick, onMounted, ref, watch, watchEffect } from 'vue'\nimport NcButton from '@nextcloud/vue/components/NcButton'\nimport NcDialog from '@nextcloud/vue/components/NcDialog'\nimport NcNoteCard from '@nextcloud/vue/components/NcNoteCard'\nimport NcTextField from '@nextcloud/vue/components/NcTextField'\nimport { getFilenameValidity } from '../utils/filenameValidity.ts'\n\nconst props = defineProps({\n\t/**\n\t * The name to be used by default\n\t */\n\tdefaultName: {\n\t\ttype: String,\n\t\tdefault: t('files', 'New folder'),\n\t},\n\t/**\n\t * Other files that are in the current directory\n\t */\n\totherNames: {\n\t\ttype: Array as PropType<string[]>,\n\t\tdefault: () => [],\n\t},\n\t/**\n\t * Open state of the dialog\n\t */\n\topen: {\n\t\ttype: Boolean,\n\t\tdefault: true,\n\t},\n\t/**\n\t * Dialog name\n\t */\n\tname: {\n\t\ttype: String,\n\t\tdefault: t('files', 'Create new folder'),\n\t},\n\t/**\n\t * Input label\n\t */\n\tlabel: {\n\t\ttype: String,\n\t\tdefault: t('files', 'Folder name'),\n\t},\n\n\t/**\n\t * Label of the submit button\n\t */\n\tsubmitLabel: {\n\t\ttype: String,\n\t\tdefault: t('files', 'Create'),\n\t},\n\n\t/**\n\t * Whether the name is for a folder, which affects the validation of the name. Defaults to false.\n\t */\n\tisFolder: {\n\t\ttype: Boolean,\n\t\tdefault: false,\n\t},\n})\n\nconst emit = defineEmits<{\n\t(event: 'close', name: string | null): void\n}>()\n\nconst localDefaultName = ref<string>(props.defaultName)\nconst nameInput = ref<ComponentPublicInstance>()\nconst formElement = ref<HTMLFormElement>()\nconst validity = ref('')\n\nconst isHiddenFileName = computed(() => {\n\t// Check if the name starts with a dot, which indicates a hidden file\n\treturn localDefaultName.value.trim().startsWith('.')\n})\n\n/**\n * Focus the filename input field\n */\nfunction focusInput() {\n\tnextTick(() => {\n\t\t// get the input element\n\t\tconst input = nameInput.value?.$el.querySelector('input')\n\t\tif (!props.open || !input) {\n\t\t\treturn\n\t\t}\n\n\t\t// length of the basename\n\t\tconst length = localDefaultName.value.length - extname(localDefaultName.value).length\n\t\t// focus the input\n\t\tinput.focus()\n\t\t// and set the selection to the basename (name without extension)\n\t\tinput.setSelectionRange(0, length)\n\t})\n}\n\n/**\n * Trigger submit on the form\n */\nfunction submit() {\n\tformElement.value?.requestSubmit()\n}\n\n// Reset local name on props change\nwatch(() => [props.defaultName, props.otherNames], () => {\n\tlocalDefaultName.value = getUniqueName(props.defaultName, props.otherNames).trim()\n})\n\n// Validate the local name\nwatchEffect(() => {\n\tif (props.otherNames.includes(localDefaultName.value.trim())) {\n\t\tvalidity.value = t('files', 'This name is already in use.')\n\t} else {\n\t\tvalidity.value = getFilenameValidity(localDefaultName.value.trim(), false, props.isFolder)\n\t}\n\tconst input = nameInput.value?.$el.querySelector('input')\n\tif (input) {\n\t\tinput.setCustomValidity(validity.value)\n\t\tinput.reportValidity()\n\t}\n})\n\n// Ensure the input is focussed even if the dialog is already mounted but not open\nwatch(() => props.open, () => {\n\tnextTick(() => {\n\t\tfocusInput()\n\t})\n})\n\nonMounted(() => {\n\t// on mounted lets use the unique name\n\tlocalDefaultName.value = getUniqueName(localDefaultName.value, props.otherNames).trim()\n\tnextTick(() => focusInput())\n})\n</script>\n\n<style scoped>\n.new-node-dialog__form {\n\t/* Ensure the dialog does not jump when there is a validity error */\n\tmin-height: calc(2 * var(--default-clickable-area));\n}\n</style>\n","/**\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { INode } from '@nextcloud/files'\n\nimport { spawnDialog } from '@nextcloud/vue/functions/dialog'\nimport NewNodeDialog from '../components/NewNodeDialog.vue'\n\ninterface NewNodeDialogOptions {\n\t/**\n\t * Dialog heading, defaults to \"New folder name\"\n\t */\n\tname?: string\n\t/**\n\t * Label for input box, defaults to \"New folder\"\n\t */\n\tlabel?: string\n\n\t/**\n\t * Label for the submit button, defaults to \"Create\"\n\t */\n\tsubmitLabel?: string\n\n\t/**\n\t * Whether the name is for a folder, defaults to false.\n\t */\n\tisFolder?: boolean\n}\n\n/**\n * Ask user for file or folder name\n *\n * @param defaultName Default name to use\n * @param folderContent Nodes or names within the current folder to check for unique name\n * @param options Options for the dialog\n * @return string if successful otherwise null if aborted\n */\nexport async function newNodeName(defaultName: string, folderContent: (INode | string)[], options: NewNodeDialogOptions = {}): Promise<string | null> {\n\tconst contentNames = folderContent.map((node) => typeof node === 'string' ? node : node.basename)\n\n\treturn await spawnDialog(NewNodeDialog, {\n\t\t...options,\n\t\tdefaultName,\n\t\totherNames: contentNames,\n\t})\n}\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\nimport type { Node } from '@nextcloud/files'\n\nimport { registerDavProperty } from '@nextcloud/files/dav'\n\n/**\n *\n */\nexport function initLivePhotos(): void {\n\tregisterDavProperty('nc:metadata-files-live-photo', { nc: 'http://nextcloud.org/ns' })\n}\n\n/**\n * @param node - The node\n */\nexport function isLivePhoto(node: Node): boolean {\n\treturn node.attributes['metadata-files-live-photo'] !== undefined\n}\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\n/**\n * Simple non-secure hashing function similar to Java's `hashCode`\n *\n * @param str The string to hash\n * @return a non secure hash of the string\n */\nexport function hashCode(str: string): number {\n\tlet hash = 0\n\tfor (let i = 0; i < str.length; i++) {\n\t\thash = ((hash << 5) - hash + str.charCodeAt(i)) | 0\n\t}\n\treturn (hash >>> 0)\n}\n"],"names":["isDownloadable","node","Permission","downloadAttribute","scope","key","isSyncable","FolderMoveSvg","getConflicts","sourceNodes","targetNodes","targetNames","sharePermissions","loadState","queue","MAX_CONCURRENCY","getQueue","PQueue","MoveCopyAction","canMove","nodes","min","canDownload","attribute","canCopy","isPublicShare","HintException","ACTION_COPY_MOVE","action","getActionForNodes","t","view","context","folder","target","openFilePickerForAction","handleCopyMoveNodesTo","error","logger","showError","destination","method","overwrite","FileType","nameMapping","otherNodes","getContents","conflicts","nodesToRename","content","n","c","result","openConflictPicker","usedNames","newName","getUniqueName","actionFinished","createLoadingNotification","join","NodeStatus","client","getClient","currentPath","defaultRootPath","destinationPath","data","getDefaultPropfind","emit","resultToNode","parser","text","message","mode","sources","toast","showLoading","dir","resolve","reject","promise","fileIDs","getFilePickerBuilder","selection","path","buttons","basename","dirnames","paths","CopyIconSvg","FilePickerClosed","ACTION_DETAILS","InformationSvg","getSidebar","sidebar","getFilenameValidity","name","escape","isFolder","validateFilename","InvalidFilenameError","InvalidFilenameErrorReason","props","__props","__emit","localDefaultName","ref","nameInput","formElement","validity","isHiddenFileName","computed","focusInput","nextTick","input","length","extname","submit","watch","watchEffect","onMounted","_createBlock","_unref","NcDialog","_createVNode","NcButton","_createElementVNode","_cache","_withModifiers","$event","NcTextField","NcNoteCard","newNodeName","defaultName","folderContent","options","contentNames","spawnDialog","NewNodeDialog","initLivePhotos","registerDavProperty","isLivePhoto","hashCode","str","hash","i"],"mappings":"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","x_google_ignoreList":[1]}