From 6973a0c18a7b97a92b32b359ef17f41dfb268c25 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Mon, 22 Aug 2022 16:35:09 +0200 Subject: [PATCH 01/29] It aint much but it kinda works --- .../src/components/AppBar/CreateAndUpload.vue | 46 +++++++++++++------ .../src/helpers/resource/copyMove.ts | 4 +- .../src/composables/upload/useUpload.ts | 6 ++- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index f2f6c7e523d..57cc8c3064e 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -156,6 +156,7 @@ import { SHARE_JAIL_ID } from '../../services/folder' import { bus } from 'web-pkg/src/instance' import { buildWebDavSpacesPath } from 'web-client/src/helpers' import { resolveFileNameDuplicate, extractNameWithoutExtension } from '../../helpers/resource' +import { resolveFileExists, ResolveStrategy } from '../../helpers/resource/copyMove' export default defineComponent({ components: { @@ -678,7 +679,9 @@ export default defineComponent({ return null }, - onFilesSelected(files: File[]) { + async onFilesSelected(files: File[]) { + /*alert("no") + return;*/ const conflicts = [] const uppyResources: UppyResource[] = this.inputFilesToUppyFiles(files) const quotaExceeded = this.checkQuotaExceeded(uppyResources) @@ -686,33 +689,48 @@ export default defineComponent({ if (quotaExceeded) { return this.$uppyService.clearInputs() } - + let resolveStrategy = null + let resolveStrategyFolder = null for (const file of uppyResources) { const relativeFilePath = file.meta.relativePath if (relativeFilePath) { const rootFolder = relativeFilePath.replace(/^\/+/, '').split('/')[0] + console.log(rootFolder) const exists = this.files.find((f) => f.name === rootFolder) if (exists) { - this.showMessage({ - title: this.$gettextInterpolate( - this.$gettext('Folder "%{folder}" already exists.'), - { folder: rootFolder }, + if(resolveStrategy == null || resolveStrategyFolder != rootFolder) { + resolveStrategyFolder = rootFolder + resolveStrategy = await resolveFileExists( + this.createModal, + this.hideModal, + rootFolder, + 1, + this.$gettext, + this.$gettextInterpolate, true - ), - status: 'danger' - }) - return + ) + } + if (resolveStrategy.strategy === ResolveStrategy.SKIP) { + continue + } + if (resolveStrategy.strategy === ResolveStrategy.REPLACE) { + + } + if (resolveStrategy.strategy === ResolveStrategy.KEEP_BOTH) { + /*targetName = resolveFileNameDuplicate(resource.name, resource.extension, [ + ...movedResources, + ...targetFolderResources + ]) + resource.name = targetName*/ + } } - - // Folder does not exist, no need to care about files inside -> continue - continue } const exists = this.files.find((f) => f.name === file.name) if (exists) { conflicts.push(file) } } - + console.log(uppyResources) if (conflicts.length) { this.displayOverwriteDialog(uppyResources, conflicts) } else { diff --git a/packages/web-app-files/src/helpers/resource/copyMove.ts b/packages/web-app-files/src/helpers/resource/copyMove.ts index 73243bf6522..f0095cb410c 100644 --- a/packages/web-app-files/src/helpers/resource/copyMove.ts +++ b/packages/web-app-files/src/helpers/resource/copyMove.ts @@ -4,7 +4,7 @@ import { join } from 'path' import { buildResource } from '../resources' import { DavProperties } from 'web-pkg/src/constants' -enum ResolveStrategy { +export enum ResolveStrategy { SKIP, REPLACE, KEEP_BOTH @@ -18,7 +18,7 @@ interface FileConflict { strategy?: ResolveStrategy } -const resolveFileExists = ( +export const resolveFileExists = ( createModal, hideModal, resource, diff --git a/packages/web-runtime/src/composables/upload/useUpload.ts b/packages/web-runtime/src/composables/upload/useUpload.ts index 3805dc1273e..68db1e3c8b1 100644 --- a/packages/web-runtime/src/composables/upload/useUpload.ts +++ b/packages/web-runtime/src/composables/upload/useUpload.ts @@ -181,7 +181,11 @@ const createDirectoryTree = ({ unref(publicLinkPassword) ) } else { - await client.files.createFolder(`${file.meta.webDavBasePath}/${folderToCreate}`) + try { + await client.files.createFolder(`${file.meta.webDavBasePath}/${folderToCreate}`) + }catch(ex) { + + } } uppyService.publish('uploadSuccess', uppyResource) From af74fba73ac505f4fd99393c5fecd7b50694b4b2 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Tue, 23 Aug 2022 10:56:00 +0200 Subject: [PATCH 02/29] Implement "keep both" --- .../src/components/AppBar/CreateAndUpload.vue | 29 ++++++++++--------- .../src/composables/upload/useUpload.ts | 4 +-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 57cc8c3064e..b4087e2649a 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -155,8 +155,11 @@ import { useUploadHelpers } from '../../composables/upload' import { SHARE_JAIL_ID } from '../../services/folder' import { bus } from 'web-pkg/src/instance' import { buildWebDavSpacesPath } from 'web-client/src/helpers' -import { resolveFileNameDuplicate, extractNameWithoutExtension } from '../../helpers/resource' -import { resolveFileExists, ResolveStrategy } from '../../helpers/resource/copyMove' +import { + resolveFileExists, + ResolveStrategy, + resolveFileNameDuplicate +} from '../../helpers/resource/copyMove' export default defineComponent({ components: { @@ -680,8 +683,6 @@ export default defineComponent({ }, async onFilesSelected(files: File[]) { - /*alert("no") - return;*/ const conflicts = [] const uppyResources: UppyResource[] = this.inputFilesToUppyFiles(files) const quotaExceeded = this.checkQuotaExceeded(uppyResources) @@ -695,10 +696,9 @@ export default defineComponent({ const relativeFilePath = file.meta.relativePath if (relativeFilePath) { const rootFolder = relativeFilePath.replace(/^\/+/, '').split('/')[0] - console.log(rootFolder) const exists = this.files.find((f) => f.name === rootFolder) if (exists) { - if(resolveStrategy == null || resolveStrategyFolder != rootFolder) { + if (resolveStrategy == null || resolveStrategyFolder !== rootFolder) { resolveStrategyFolder = rootFolder resolveStrategy = await resolveFileExists( this.createModal, @@ -712,16 +712,17 @@ export default defineComponent({ } if (resolveStrategy.strategy === ResolveStrategy.SKIP) { continue - } - if (resolveStrategy.strategy === ResolveStrategy.REPLACE) { - } if (resolveStrategy.strategy === ResolveStrategy.KEEP_BOTH) { - /*targetName = resolveFileNameDuplicate(resource.name, resource.extension, [ - ...movedResources, - ...targetFolderResources - ]) - resource.name = targetName*/ + const newFolderName = resolveFileNameDuplicate(rootFolder, '', this.files) + file.meta.relativeFolder = file.meta.relativeFolder.replace( + `/${rootFolder}`, + `/${newFolderName}` + ) + file.meta.relativePath = file.meta.relativePath.replace( + `/${rootFolder}/`, + `/${newFolderName}/` + ) } } } diff --git a/packages/web-runtime/src/composables/upload/useUpload.ts b/packages/web-runtime/src/composables/upload/useUpload.ts index 68db1e3c8b1..b5d85e864f6 100644 --- a/packages/web-runtime/src/composables/upload/useUpload.ts +++ b/packages/web-runtime/src/composables/upload/useUpload.ts @@ -183,9 +183,7 @@ const createDirectoryTree = ({ } else { try { await client.files.createFolder(`${file.meta.webDavBasePath}/${folderToCreate}`) - }catch(ex) { - - } + } catch (ex) {} } uppyService.publish('uploadSuccess', uppyResource) From 21143ab97f7c3c290b8a56e84982c22ee43b525e Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Tue, 23 Aug 2022 10:59:02 +0200 Subject: [PATCH 03/29] Add changelog --- changelog/unreleased/bugfix-resolve-upload-existing-folder | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog/unreleased/bugfix-resolve-upload-existing-folder diff --git a/changelog/unreleased/bugfix-resolve-upload-existing-folder b/changelog/unreleased/bugfix-resolve-upload-existing-folder new file mode 100644 index 00000000000..998189c1ede --- /dev/null +++ b/changelog/unreleased/bugfix-resolve-upload-existing-folder @@ -0,0 +1,6 @@ +Bugfix: Resolve upload existing folder + +We've added an conflict dialog when uploading an already existing folder + +https://github.com/owncloud/web/pull/7504 +https://github.com/owncloud/web/issues/6996 \ No newline at end of file From f08b9f8e7cef803b8355258c11038b21a9c8dd67 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Tue, 23 Aug 2022 11:00:14 +0200 Subject: [PATCH 04/29] remove dev leftover --- packages/web-app-files/src/components/AppBar/CreateAndUpload.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index b4087e2649a..c0d16e54cb2 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -731,7 +731,6 @@ export default defineComponent({ conflicts.push(file) } } - console.log(uppyResources) if (conflicts.length) { this.displayOverwriteDialog(uppyResources, conflicts) } else { From 7b71a91f8ebfa6799918391089bc912da23d83b2 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Tue, 23 Aug 2022 11:06:42 +0200 Subject: [PATCH 05/29] Fix folder name --- .../web-app-files/src/components/AppBar/CreateAndUpload.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index c0d16e54cb2..3a111273717 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -703,7 +703,7 @@ export default defineComponent({ resolveStrategy = await resolveFileExists( this.createModal, this.hideModal, - rootFolder, + { name: rootFolder }, 1, this.$gettext, this.$gettextInterpolate, From 133cc2b9031e204654b8b1ddea847e1bb02a80b0 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Fri, 26 Aug 2022 17:21:10 +0200 Subject: [PATCH 06/29] Add isFolder --- .../web-app-files/src/components/AppBar/CreateAndUpload.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 3a111273717..0ce176f3c8e 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -703,7 +703,7 @@ export default defineComponent({ resolveStrategy = await resolveFileExists( this.createModal, this.hideModal, - { name: rootFolder }, + { name: rootFolder, isFolder: true }, 1, this.$gettext, this.$gettextInterpolate, From 81c53f16a7282c711fb1f6145d6480bb92f63982 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 31 Aug 2022 15:56:58 +0200 Subject: [PATCH 07/29] Make file conflict dialog work --- .../src/components/AppBar/CreateAndUpload.vue | 94 +++++++++---------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 0ce176f3c8e..399c38dc91d 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -154,10 +154,12 @@ import { UppyResource, useUpload } from 'web-runtime/src/composables/upload' import { useUploadHelpers } from '../../composables/upload' import { SHARE_JAIL_ID } from '../../services/folder' import { bus } from 'web-pkg/src/instance' -import { buildWebDavSpacesPath } from 'web-client/src/helpers' +import { buildWebDavSpacesPath, Resource } from 'web-client/src/helpers' +import { extractExtensionFromFile } from '../../helpers/resource' import { resolveFileExists, ResolveStrategy, + ResolveConflict, resolveFileNameDuplicate } from '../../helpers/resource/copyMove' @@ -690,17 +692,17 @@ export default defineComponent({ if (quotaExceeded) { return this.$uppyService.clearInputs() } - let resolveStrategy = null - let resolveStrategyFolder = null + console.log(this.files) + let resolveStrategies = {} for (const file of uppyResources) { const relativeFilePath = file.meta.relativePath if (relativeFilePath) { + // Logic for folders, applies to all files inside folder and subfolders const rootFolder = relativeFilePath.replace(/^\/+/, '').split('/')[0] const exists = this.files.find((f) => f.name === rootFolder) if (exists) { - if (resolveStrategy == null || resolveStrategyFolder !== rootFolder) { - resolveStrategyFolder = rootFolder - resolveStrategy = await resolveFileExists( + if(!(rootFolder in resolveStrategies)) { + const resolveStrategy = await resolveFileExists( this.createModal, this.hideModal, { name: rootFolder, isFolder: true }, @@ -709,7 +711,9 @@ export default defineComponent({ this.$gettextInterpolate, true ) + resolveStrategies[rootFolder] = resolveStrategy } + const resolveStrategy = resolveStrategies[rootFolder] if (resolveStrategy.strategy === ResolveStrategy.SKIP) { continue } @@ -723,16 +727,18 @@ export default defineComponent({ `/${rootFolder}/`, `/${newFolderName}/` ) + console.log(newFolderName) } } } + // Logic for files const exists = this.files.find((f) => f.name === file.name) if (exists) { conflicts.push(file) } } if (conflicts.length) { - this.displayOverwriteDialog(uppyResources, conflicts) + await this.displayOverwriteDialog(uppyResources, conflicts) } else { this.handleUppyFileUpload(uppyResources) } @@ -815,51 +821,43 @@ export default defineComponent({ this.$uppyService.uploadFiles(files) }, - displayOverwriteDialog(files: UppyResource[], conflicts) { - const title = this.$ngettext( - 'File already exists', - 'Some files already exist', - conflicts.length - ) - - const isVersioningEnabled = this.isUserContext && this.capabilities?.files?.versioning - - let translatedMsg - if (isVersioningEnabled) { - translatedMsg = this.$ngettext( - 'The following resource already exists: %{resources}. Do you want to create a new version for it?', - 'The following resources already exist: %{resources}. Do you want to create a new version for them?', - conflicts.length - ) - } else { - translatedMsg = this.$ngettext( - 'The following resource already exists: %{resources}. Do you want to overwrite it?', - 'The following resources already exist: %{resources}. Do you want to overwrite them?', - conflicts.length + async displayOverwriteDialog(files: UppyResource[], conflicts) { + let count = 0 + for(const file of files) { + const resolveConflict: ResolveConflict = await resolveFileExists( + this.createModal, + this.hideModal, + { name: file.name, isFolder: false }, + (files.length - count), + this.$gettext, + this.$gettextInterpolate, + false ) - } - const message = this.$gettextInterpolate(translatedMsg, { - resources: conflicts.map((f) => `${f.name}`).join(', ') - }) - - const modal = { - variation: isVersioningEnabled ? 'passive' : 'danger', - icon: 'upload-cloud', - title, - message, - cancelText: this.$gettext('Cancel'), - confirmText: isVersioningEnabled ? this.$gettext('Create') : this.$gettext('Overwrite'), - onCancel: () => { - this.$uppyService.clearInputs() - this.hideModal() - }, - onConfirm: () => { - this.hideModal() + count += 1 + if(resolveConflict.doForAllConflicts) { + if(resolveConflict.strategy === ResolveStrategy.SKIP) { + return + } + if(resolveConflict.strategy === ResolveStrategy.KEEP_BOTH) { + for(const f of files) { + const ext = extractExtensionFromFile({ name: f.name } as Resource) + f.name = resolveFileNameDuplicate(f.name, ext, this.files) + } + } + // strategy replace doesn't need a case here this.handleUppyFileUpload(files) + return + } + if(resolveConflict.strategy === ResolveStrategy.SKIP) { + continue + } + if(resolveConflict.strategy === ResolveStrategy.KEEP_BOTH) { + const ext = extractExtensionFromFile({ name: file.name } as Resource) + file.name = resolveFileNameDuplicate(file.name, ext, this.files) } + // strategy replace doesn't need a case here + this.handleUppyFileUpload([file]) } - - this.createModal(modal) } } }) From 9994412eb16edf4b547665f6fc60206c08a4871c Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 31 Aug 2022 15:57:08 +0200 Subject: [PATCH 08/29] Linting --- .../src/components/AppBar/CreateAndUpload.vue | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 399c38dc91d..d0314b298e0 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -693,7 +693,7 @@ export default defineComponent({ return this.$uppyService.clearInputs() } console.log(this.files) - let resolveStrategies = {} + const resolveStrategies = {} for (const file of uppyResources) { const relativeFilePath = file.meta.relativePath if (relativeFilePath) { @@ -701,7 +701,7 @@ export default defineComponent({ const rootFolder = relativeFilePath.replace(/^\/+/, '').split('/')[0] const exists = this.files.find((f) => f.name === rootFolder) if (exists) { - if(!(rootFolder in resolveStrategies)) { + if (!(rootFolder in resolveStrategies)) { const resolveStrategy = await resolveFileExists( this.createModal, this.hideModal, @@ -823,23 +823,23 @@ export default defineComponent({ async displayOverwriteDialog(files: UppyResource[], conflicts) { let count = 0 - for(const file of files) { + for (const file of files) { const resolveConflict: ResolveConflict = await resolveFileExists( this.createModal, this.hideModal, { name: file.name, isFolder: false }, - (files.length - count), + files.length - count, this.$gettext, this.$gettextInterpolate, false ) count += 1 - if(resolveConflict.doForAllConflicts) { - if(resolveConflict.strategy === ResolveStrategy.SKIP) { + if (resolveConflict.doForAllConflicts) { + if (resolveConflict.strategy === ResolveStrategy.SKIP) { return } - if(resolveConflict.strategy === ResolveStrategy.KEEP_BOTH) { - for(const f of files) { + if (resolveConflict.strategy === ResolveStrategy.KEEP_BOTH) { + for (const f of files) { const ext = extractExtensionFromFile({ name: f.name } as Resource) f.name = resolveFileNameDuplicate(f.name, ext, this.files) } @@ -848,10 +848,10 @@ export default defineComponent({ this.handleUppyFileUpload(files) return } - if(resolveConflict.strategy === ResolveStrategy.SKIP) { + if (resolveConflict.strategy === ResolveStrategy.SKIP) { continue } - if(resolveConflict.strategy === ResolveStrategy.KEEP_BOTH) { + if (resolveConflict.strategy === ResolveStrategy.KEEP_BOTH) { const ext = extractExtensionFromFile({ name: file.name } as Resource) file.name = resolveFileNameDuplicate(file.name, ext, this.files) } From 1380d73ead0c2d2f70760b1b70de505a5ddeac95 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 31 Aug 2022 16:19:13 +0200 Subject: [PATCH 09/29] Fix folder keep both --- package.json | 3 +++ .../src/components/AppBar/CreateAndUpload.vue | 13 ++++++++++++- yarn.lock | 9 ++++----- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 00e55e3d4ae..4c36c15e8ef 100644 --- a/package.json +++ b/package.json @@ -148,5 +148,8 @@ "vue-concurrency": { "built": true } + }, + "resolutions": { + "owncloud-design-system": "portal:/Users/paul/repos/owncloud-design-system" } } diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index d0314b298e0..30732580bc7 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -696,6 +696,7 @@ export default defineComponent({ const resolveStrategies = {} for (const file of uppyResources) { const relativeFilePath = file.meta.relativePath + console.log(file) if (relativeFilePath) { // Logic for folders, applies to all files inside folder and subfolders const rootFolder = relativeFilePath.replace(/^\/+/, '').split('/')[0] @@ -719,6 +720,7 @@ export default defineComponent({ } if (resolveStrategy.strategy === ResolveStrategy.KEEP_BOTH) { const newFolderName = resolveFileNameDuplicate(rootFolder, '', this.files) + file.meta.relativeFolder = file.meta.relativeFolder.replace( `/${rootFolder}`, `/${newFolderName}` @@ -727,7 +729,16 @@ export default defineComponent({ `/${rootFolder}/`, `/${newFolderName}/` ) - console.log(newFolderName) + file.meta.tusEndpoint = file.meta.tusEndpoint.replace( + `/${rootFolder}`, + `/${newFolderName}` + ) + const data = file.data as any + data.relativePath = data.relativePath.replace( + `/${rootFolder}/`, + `/${newFolderName}/` + ) + file.meta.routeItem = `/${newFolderName}` } } } diff --git a/yarn.lock b/yarn.lock index 00b544aca65..01fe95cb11c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9745,9 +9745,9 @@ __metadata: languageName: node linkType: hard -"owncloud-design-system@npm:14.0.0-alpha.16": - version: 14.0.0-alpha.16 - resolution: "owncloud-design-system@npm:14.0.0-alpha.16" +"owncloud-design-system@portal:/Users/paul/repos/owncloud-design-system::locator=root-workspace-0b6124%40workspace%3A.": + version: 0.0.0-use.local + resolution: "owncloud-design-system@portal:/Users/paul/repos/owncloud-design-system::locator=root-workspace-0b6124%40workspace%3A." peerDependencies: "@popperjs/core": ^2.4.0 "@vue/composition-api": ^1.4.3 @@ -9764,9 +9764,8 @@ __metadata: vue-inline-svg: ^2.0.0 vue-select: ^3.12.0 webfontloader: ^1.6.28 - checksum: 182d05099f648cf93abe997fcbea15064630be311fce788e467ca33f96a3918dc39c1f31a9d8ca1ce4c30765a799799b18fad88f00d28b236dac271582f6d396 languageName: node - linkType: hard + linkType: soft "owncloud-sdk@npm:~3.0.0-alpha.15": version: 3.0.0-alpha.15 From 21ace2ce20b58663f5005366dd20acdb4ded77b0 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 31 Aug 2022 16:45:23 +0200 Subject: [PATCH 10/29] Check for folder to already exist --- .../src/components/AppBar/CreateAndUpload.vue | 7 ++----- .../src/composables/upload/useUpload.ts | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 30732580bc7..2d67951334a 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -720,7 +720,7 @@ export default defineComponent({ } if (resolveStrategy.strategy === ResolveStrategy.KEEP_BOTH) { const newFolderName = resolveFileNameDuplicate(rootFolder, '', this.files) - + file.meta.relativeFolder = file.meta.relativeFolder.replace( `/${rootFolder}`, `/${newFolderName}` @@ -734,10 +734,7 @@ export default defineComponent({ `/${newFolderName}` ) const data = file.data as any - data.relativePath = data.relativePath.replace( - `/${rootFolder}/`, - `/${newFolderName}/` - ) + data.relativePath = data.relativePath.replace(`/${rootFolder}/`, `/${newFolderName}/`) file.meta.routeItem = `/${newFolderName}` } } diff --git a/packages/web-runtime/src/composables/upload/useUpload.ts b/packages/web-runtime/src/composables/upload/useUpload.ts index b5d85e864f6..c82ebde6392 100644 --- a/packages/web-runtime/src/composables/upload/useUpload.ts +++ b/packages/web-runtime/src/composables/upload/useUpload.ts @@ -12,6 +12,8 @@ import { import { computed, Ref, unref, watch } from '@vue/composition-api' import { UppyService } from '../../services/uppyService' import * as uuid from 'uuid' +import { DavProperties } from 'web-pkg/src/constants' +import { buildResource } from 'files/src/helpers/resources' export interface UppyResource { id?: string @@ -124,10 +126,19 @@ const createDirectoryTree = ({ return async (files: UppyResource[]) => { const { owncloudSdk: client } = clientService const createdFolders = [] + for (const file of files) { const currentFolder = file.meta.currentFolder const directory = file.meta.relativeFolder + let existingFiles + if (unref(isPublicLinkContext)) { + existingFiles = await client.publicFiles.list(`${file.meta.webDavBasePath}/`, unref(publicLinkPassword), DavProperties.Default, 'infinity') + } + existingFiles = await client.files.list(`${file.meta.webDavBasePath}/`, 'infinity', DavProperties.Default) + existingFiles = existingFiles.map(buildResource) + console.log(existingFiles) + if (!directory || createdFolders.includes(directory)) { continue } @@ -181,9 +192,9 @@ const createDirectoryTree = ({ unref(publicLinkPassword) ) } else { - try { + if(!existingFiles.some(f => f.path === folderToCreate)) { await client.files.createFolder(`${file.meta.webDavBasePath}/${folderToCreate}`) - } catch (ex) {} + } } uppyService.publish('uploadSuccess', uppyResource) From 9c438913e91ff83379ef39c58459a21e08348406 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 31 Aug 2022 16:48:11 +0200 Subject: [PATCH 11/29] remove dev leftover --- .../web-app-files/src/components/AppBar/CreateAndUpload.vue | 2 -- packages/web-runtime/src/composables/upload/useUpload.ts | 1 - 2 files changed, 3 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 2d67951334a..c925922d35d 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -692,11 +692,9 @@ export default defineComponent({ if (quotaExceeded) { return this.$uppyService.clearInputs() } - console.log(this.files) const resolveStrategies = {} for (const file of uppyResources) { const relativeFilePath = file.meta.relativePath - console.log(file) if (relativeFilePath) { // Logic for folders, applies to all files inside folder and subfolders const rootFolder = relativeFilePath.replace(/^\/+/, '').split('/')[0] diff --git a/packages/web-runtime/src/composables/upload/useUpload.ts b/packages/web-runtime/src/composables/upload/useUpload.ts index c82ebde6392..ae8612b05b5 100644 --- a/packages/web-runtime/src/composables/upload/useUpload.ts +++ b/packages/web-runtime/src/composables/upload/useUpload.ts @@ -137,7 +137,6 @@ const createDirectoryTree = ({ } existingFiles = await client.files.list(`${file.meta.webDavBasePath}/`, 'infinity', DavProperties.Default) existingFiles = existingFiles.map(buildResource) - console.log(existingFiles) if (!directory || createdFolders.includes(directory)) { continue From 50b67f3406eb4f2f5fc9f3af1ad87c1ba56f5ff4 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Fri, 2 Sep 2022 14:49:23 +0200 Subject: [PATCH 12/29] Address PR issues --- .../unreleased/bugfix-resolve-upload-existing-folder | 2 +- package.json | 3 --- packages/web-runtime/src/composables/upload/useUpload.ts | 4 ++-- yarn.lock | 9 +++++---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/changelog/unreleased/bugfix-resolve-upload-existing-folder b/changelog/unreleased/bugfix-resolve-upload-existing-folder index 998189c1ede..1c0b1301386 100644 --- a/changelog/unreleased/bugfix-resolve-upload-existing-folder +++ b/changelog/unreleased/bugfix-resolve-upload-existing-folder @@ -1,6 +1,6 @@ Bugfix: Resolve upload existing folder -We've added an conflict dialog when uploading an already existing folder +We've added an conflict dialog when uploading files and folders https://github.com/owncloud/web/pull/7504 https://github.com/owncloud/web/issues/6996 \ No newline at end of file diff --git a/package.json b/package.json index 4c36c15e8ef..00e55e3d4ae 100644 --- a/package.json +++ b/package.json @@ -148,8 +148,5 @@ "vue-concurrency": { "built": true } - }, - "resolutions": { - "owncloud-design-system": "portal:/Users/paul/repos/owncloud-design-system" } } diff --git a/packages/web-runtime/src/composables/upload/useUpload.ts b/packages/web-runtime/src/composables/upload/useUpload.ts index ae8612b05b5..6232d81fb16 100644 --- a/packages/web-runtime/src/composables/upload/useUpload.ts +++ b/packages/web-runtime/src/composables/upload/useUpload.ts @@ -133,9 +133,9 @@ const createDirectoryTree = ({ let existingFiles if (unref(isPublicLinkContext)) { - existingFiles = await client.publicFiles.list(`${file.meta.webDavBasePath}/`, unref(publicLinkPassword), DavProperties.Default, 'infinity') + existingFiles = await client.publicFiles.list(`${file.meta.webDavBasePath}/`, unref(publicLinkPassword), DavProperties.Default, 1) } - existingFiles = await client.files.list(`${file.meta.webDavBasePath}/`, 'infinity', DavProperties.Default) + existingFiles = await client.files.list(`${file.meta.webDavBasePath}/`, 1, DavProperties.Default) existingFiles = existingFiles.map(buildResource) if (!directory || createdFolders.includes(directory)) { diff --git a/yarn.lock b/yarn.lock index 01fe95cb11c..00b544aca65 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9745,9 +9745,9 @@ __metadata: languageName: node linkType: hard -"owncloud-design-system@portal:/Users/paul/repos/owncloud-design-system::locator=root-workspace-0b6124%40workspace%3A.": - version: 0.0.0-use.local - resolution: "owncloud-design-system@portal:/Users/paul/repos/owncloud-design-system::locator=root-workspace-0b6124%40workspace%3A." +"owncloud-design-system@npm:14.0.0-alpha.16": + version: 14.0.0-alpha.16 + resolution: "owncloud-design-system@npm:14.0.0-alpha.16" peerDependencies: "@popperjs/core": ^2.4.0 "@vue/composition-api": ^1.4.3 @@ -9764,8 +9764,9 @@ __metadata: vue-inline-svg: ^2.0.0 vue-select: ^3.12.0 webfontloader: ^1.6.28 + checksum: 182d05099f648cf93abe997fcbea15064630be311fce788e467ca33f96a3918dc39c1f31a9d8ca1ce4c30765a799799b18fad88f00d28b236dac271582f6d396 languageName: node - linkType: soft + linkType: hard "owncloud-sdk@npm:~3.0.0-alpha.15": version: 3.0.0-alpha.15 From 20f72fbd7dc17d335c61994404bafa036e8b6f0d Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Mon, 5 Sep 2022 09:40:15 +0200 Subject: [PATCH 13/29] Use store --- .../src/composables/upload/useUpload.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/web-runtime/src/composables/upload/useUpload.ts b/packages/web-runtime/src/composables/upload/useUpload.ts index 6232d81fb16..9e5fea46feb 100644 --- a/packages/web-runtime/src/composables/upload/useUpload.ts +++ b/packages/web-runtime/src/composables/upload/useUpload.ts @@ -14,6 +14,7 @@ import { UppyService } from '../../services/uppyService' import * as uuid from 'uuid' import { DavProperties } from 'web-pkg/src/constants' import { buildResource } from 'files/src/helpers/resources' +import { Store } from 'vuex' export interface UppyResource { id?: string @@ -107,7 +108,8 @@ export function useUpload(options: UploadOptions): UploadResult { clientService, isPublicLinkContext, publicLinkPassword, - uppyService: options.uppyService + uppyService: options.uppyService, + store }) } } @@ -116,28 +118,23 @@ const createDirectoryTree = ({ clientService, isPublicLinkContext, publicLinkPassword, - uppyService + uppyService, + store }: { clientService: ClientService isPublicLinkContext: Ref publicLinkPassword?: Ref - uppyService: UppyService + uppyService: UppyService, + store: Store }) => { return async (files: UppyResource[]) => { const { owncloudSdk: client } = clientService const createdFolders = [] - + const existingFiles = store.getters['Files/files'] for (const file of files) { const currentFolder = file.meta.currentFolder const directory = file.meta.relativeFolder - let existingFiles - if (unref(isPublicLinkContext)) { - existingFiles = await client.publicFiles.list(`${file.meta.webDavBasePath}/`, unref(publicLinkPassword), DavProperties.Default, 1) - } - existingFiles = await client.files.list(`${file.meta.webDavBasePath}/`, 1, DavProperties.Default) - existingFiles = existingFiles.map(buildResource) - if (!directory || createdFolders.includes(directory)) { continue } From 9d98ed1b2f719588729ae1ac7e5e96f6be60cf61 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Mon, 5 Sep 2022 10:10:32 +0200 Subject: [PATCH 14/29] Provide existing files with function parameter --- .../src/components/AppBar/CreateAndUpload.vue | 4 ++-- .../src/composables/upload/useUpload.ts | 20 +++++++------------ .../unit/composables/upload/useUpload.spec.ts | 2 +- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index c925922d35d..0f950c9de6a 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -155,7 +155,7 @@ import { useUploadHelpers } from '../../composables/upload' import { SHARE_JAIL_ID } from '../../services/folder' import { bus } from 'web-pkg/src/instance' import { buildWebDavSpacesPath, Resource } from 'web-client/src/helpers' -import { extractExtensionFromFile } from '../../helpers/resource' +import { extractExtensionFromFile, extractNameWithoutExtension } from '../../helpers/resource' import { resolveFileExists, ResolveStrategy, @@ -822,7 +822,7 @@ export default defineComponent({ async handleUppyFileUpload(files: UppyResource[]) { this.$uppyService.publish('uploadStarted') - await this.createDirectoryTree(files) + await this.createDirectoryTree(files, this.files) this.$uppyService.publish('addedForUpload', files) this.$uppyService.uploadFiles(files) }, diff --git a/packages/web-runtime/src/composables/upload/useUpload.ts b/packages/web-runtime/src/composables/upload/useUpload.ts index 9e5fea46feb..27c01a75134 100644 --- a/packages/web-runtime/src/composables/upload/useUpload.ts +++ b/packages/web-runtime/src/composables/upload/useUpload.ts @@ -12,9 +12,7 @@ import { import { computed, Ref, unref, watch } from '@vue/composition-api' import { UppyService } from '../../services/uppyService' import * as uuid from 'uuid' -import { DavProperties } from 'web-pkg/src/constants' -import { buildResource } from 'files/src/helpers/resources' -import { Store } from 'vuex' +import { Resource } from 'web-client/src/helpers/resource' export interface UppyResource { id?: string @@ -46,7 +44,7 @@ interface UploadOptions { } interface UploadResult { - createDirectoryTree(files: UppyResource[]): void + createDirectoryTree(files: UppyResource[], existingFiles: []): void } export function useUpload(options: UploadOptions): UploadResult { @@ -108,8 +106,7 @@ export function useUpload(options: UploadOptions): UploadResult { clientService, isPublicLinkContext, publicLinkPassword, - uppyService: options.uppyService, - store + uppyService: options.uppyService }) } } @@ -118,19 +115,16 @@ const createDirectoryTree = ({ clientService, isPublicLinkContext, publicLinkPassword, - uppyService, - store + uppyService }: { clientService: ClientService isPublicLinkContext: Ref publicLinkPassword?: Ref - uppyService: UppyService, - store: Store + uppyService: UppyService }) => { - return async (files: UppyResource[]) => { + return async (files: UppyResource[], existingFiles: Resource[]) => { const { owncloudSdk: client } = clientService const createdFolders = [] - const existingFiles = store.getters['Files/files'] for (const file of files) { const currentFolder = file.meta.currentFolder const directory = file.meta.relativeFolder @@ -188,7 +182,7 @@ const createDirectoryTree = ({ unref(publicLinkPassword) ) } else { - if(!existingFiles.some(f => f.path === folderToCreate)) { + if (!existingFiles.some((f) => f.path === folderToCreate)) { await client.files.createFolder(`${file.meta.webDavBasePath}/${folderToCreate}`) } } diff --git a/packages/web-runtime/tests/unit/composables/upload/useUpload.spec.ts b/packages/web-runtime/tests/unit/composables/upload/useUpload.spec.ts index 73452e0137e..4945be69bd3 100644 --- a/packages/web-runtime/tests/unit/composables/upload/useUpload.spec.ts +++ b/packages/web-runtime/tests/unit/composables/upload/useUpload.spec.ts @@ -61,7 +61,7 @@ describe('useUpload', () => { } ] - await wrapper.vm.createDirectoryTree(uppyResources) + await wrapper.vm.createDirectoryTree(uppyResources, []) expect(createFolderMock).toHaveBeenCalledTimes(4) }) }) From 5f772f5468c33c70c5737233fda556d96ab64307 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Mon, 5 Sep 2022 10:11:52 +0200 Subject: [PATCH 15/29] Add type to interface --- packages/web-runtime/src/composables/upload/useUpload.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-runtime/src/composables/upload/useUpload.ts b/packages/web-runtime/src/composables/upload/useUpload.ts index 27c01a75134..725a5d5fa5c 100644 --- a/packages/web-runtime/src/composables/upload/useUpload.ts +++ b/packages/web-runtime/src/composables/upload/useUpload.ts @@ -44,7 +44,7 @@ interface UploadOptions { } interface UploadResult { - createDirectoryTree(files: UppyResource[], existingFiles: []): void + createDirectoryTree(files: UppyResource[], existingFiles: Resource[]): void } export function useUpload(options: UploadOptions): UploadResult { From f3649d5acd1a1dfe0c338cada1fc99d5d08ad9f5 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Tue, 6 Sep 2022 14:40:48 +0200 Subject: [PATCH 16/29] Refactor resolve file & folder conflicts --- .../src/components/AppBar/CreateAndUpload.vue | 155 +++++++++++++----- 1 file changed, 114 insertions(+), 41 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 0f950c9de6a..4d153da499c 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -162,6 +162,7 @@ import { ResolveConflict, resolveFileNameDuplicate } from '../../helpers/resource/copyMove' +import { resolve } from 'dns' export default defineComponent({ components: { @@ -685,7 +686,8 @@ export default defineComponent({ }, async onFilesSelected(files: File[]) { - const conflicts = [] + const fileConflicts = [] + const folderConflicts = [] const uppyResources: UppyResource[] = this.inputFilesToUppyFiles(files) const quotaExceeded = this.checkQuotaExceeded(uppyResources) @@ -700,51 +702,19 @@ export default defineComponent({ const rootFolder = relativeFilePath.replace(/^\/+/, '').split('/')[0] const exists = this.files.find((f) => f.name === rootFolder) if (exists) { - if (!(rootFolder in resolveStrategies)) { - const resolveStrategy = await resolveFileExists( - this.createModal, - this.hideModal, - { name: rootFolder, isFolder: true }, - 1, - this.$gettext, - this.$gettextInterpolate, - true - ) - resolveStrategies[rootFolder] = resolveStrategy - } - const resolveStrategy = resolveStrategies[rootFolder] - if (resolveStrategy.strategy === ResolveStrategy.SKIP) { - continue - } - if (resolveStrategy.strategy === ResolveStrategy.KEEP_BOTH) { - const newFolderName = resolveFileNameDuplicate(rootFolder, '', this.files) - - file.meta.relativeFolder = file.meta.relativeFolder.replace( - `/${rootFolder}`, - `/${newFolderName}` - ) - file.meta.relativePath = file.meta.relativePath.replace( - `/${rootFolder}/`, - `/${newFolderName}/` - ) - file.meta.tusEndpoint = file.meta.tusEndpoint.replace( - `/${rootFolder}`, - `/${newFolderName}` - ) - const data = file.data as any - data.relativePath = data.relativePath.replace(`/${rootFolder}/`, `/${newFolderName}/`) - file.meta.routeItem = `/${newFolderName}` - } + if(folderConflicts.includes(rootFolder)) continue + folderConflicts.push(rootFolder) + continue } } // Logic for files const exists = this.files.find((f) => f.name === file.name) if (exists) { - conflicts.push(file) + fileConflicts.push(file) } } - if (conflicts.length) { - await this.displayOverwriteDialog(uppyResources, conflicts) + if (fileConflicts.length || folderConflicts.length) { + await this.displayOverwriteDialog(uppyResources, fileConflicts, folderConflicts) } else { this.handleUppyFileUpload(uppyResources) } @@ -827,8 +797,111 @@ export default defineComponent({ this.$uppyService.uploadFiles(files) }, - async displayOverwriteDialog(files: UppyResource[], conflicts) { + async displayOverwriteDialog(files: UppyResource[], fileConflicts, folderConflicts) { let count = 0 + const allConflictsCount = fileConflicts.length + folderConflicts.length + const resolvedConflicts = [] + let doForAllConflicts = false + let allConflictsStrategy + for (const uppyResource of fileConflicts) { + if(doForAllConflicts) { + resolvedConflicts.push({ + uppyResource, + strategy: allConflictsStrategy + }) + continue + } + const resolvedConflict: ResolveConflict = await resolveFileExists( + this.createModal, + this.hideModal, + { name: uppyResource.name, isFolder: false } as Resource, + allConflictsCount - count, + this.$gettext, + this.$gettextInterpolate, + false + ) + count += 1 + if(resolvedConflict.doForAllConflicts) { + doForAllConflicts = true + allConflictsStrategy = resolvedConflict.strategy + } + resolvedConflicts.push({ + uppyResource, + strategy: resolvedConflict.strategy + }) + } + const filesToSkip = resolvedConflicts.filter(e => e.strategy === ResolveStrategy.SKIP).map(e => e.uppyResource) + const filesToOverwrite = resolvedConflicts.filter(e => e.strategy === ResolveStrategy.REPLACE).map(e => e.uppyResource) + const filesToKeepBoth = resolvedConflicts.filter(e => e.strategy === ResolveStrategy.KEEP_BOTH).map(e => e.uppyResource) + + files = files.filter(e => !filesToSkip.includes(e) && ! filesToKeepBoth.includes(e)) + for (const uppyResource of filesToKeepBoth) { + const extension = extractExtensionFromFile({ name: uppyResource.name } as Resource) + uppyResource.name = resolveFileNameDuplicate(uppyResource.name, extension, this.files) + files.push(uppyResource) + } + + const resolvedConflictsFolders = [] + for (const folderName of folderConflicts) { + if(doForAllConflicts) { + resolvedConflictsFolders.push({ + name: folderName, + strategy: allConflictsStrategy + }) + continue + } + const resolvedConflict: ResolveConflict = await resolveFileExists( + this.createModal, + this.hideModal, + { name: folderName, isFolder: true } as Resource, + allConflictsCount - count, + this.$gettext, + this.$gettextInterpolate, + false + ) + count += 1 + if(resolvedConflict.doForAllConflicts) { + doForAllConflicts = true + allConflictsStrategy = resolvedConflict.strategy + } + resolvedConflictsFolders.push({ + name: folderName, + strategy: resolvedConflict.strategy + }) + } + const foldersToSkip = resolvedConflictsFolders.filter(e => e.strategy === ResolveStrategy.SKIP).map(e => e.name) + const foldersToOverwrite = resolvedConflictsFolders.filter(e => e.strategy === ResolveStrategy.REPLACE).map(e => e.name) + const foldersToKeepBoth = resolvedConflictsFolders.filter(e => e.strategy === ResolveStrategy.KEEP_BOTH).map(e => e.name) + + for(const folder of foldersToKeepBoth) { + const filesInFolder = files.filter(e => e.meta.relativeFolder.startsWith(`/${folder}`)) + for(const file of filesInFolder) { + const newFolderName = resolveFileNameDuplicate(folder, '', this.files) + file.meta.relativeFolder = file.meta.relativeFolder.replace( + `/${folder}`, + `/${newFolderName}` + ) + file.meta.relativePath = file.meta.relativePath.replace( + `/${folder}/`, + `/${newFolderName}/` + ) + file.meta.tusEndpoint = file.meta.tusEndpoint.replace( + `/${folder}`, + `/${newFolderName}` + ) + const data = file.data as any + data.relativePath = data.relativePath.replace(`/${folder}/`, `/${newFolderName}/`) + file.meta.routeItem = `/${newFolderName}` + } + } + files = files.filter(file => !foldersToSkip.some(folderName => file.meta.relativeFolder.startsWith(`/${folderName}`))) + if(files.length === 0) return + this.handleUppyFileUpload(files) + + // problem: folder /abc and /abcd treated the same + + + /*let count = 0 for (const file of files) { const resolveConflict: ResolveConflict = await resolveFileExists( this.createModal, @@ -863,7 +936,7 @@ export default defineComponent({ } // strategy replace doesn't need a case here this.handleUppyFileUpload([file]) - } + }*/ } } }) From fa29985e3caf2ca25c14e86792fe13375c515ece Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Tue, 6 Sep 2022 15:04:52 +0200 Subject: [PATCH 17/29] Refactor conflict dialog --- .../src/components/AppBar/CreateAndUpload.vue | 101 ++++++++---------- 1 file changed, 47 insertions(+), 54 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 4d153da499c..aeb7dd01d17 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -686,8 +686,7 @@ export default defineComponent({ }, async onFilesSelected(files: File[]) { - const fileConflicts = [] - const folderConflicts = [] + const conflicts = [] const uppyResources: UppyResource[] = this.inputFilesToUppyFiles(files) const quotaExceeded = this.checkQuotaExceeded(uppyResources) @@ -702,19 +701,25 @@ export default defineComponent({ const rootFolder = relativeFilePath.replace(/^\/+/, '').split('/')[0] const exists = this.files.find((f) => f.name === rootFolder) if (exists) { - if(folderConflicts.includes(rootFolder)) continue - folderConflicts.push(rootFolder) + if(conflicts.some(conflict => conflict.data === rootFolder)) continue + conflicts.push({ + data: rootFolder, + type: 'folder' + }) continue } } // Logic for files const exists = this.files.find((f) => f.name === file.name) if (exists) { - fileConflicts.push(file) + conflicts.push({ + data: file, + type: 'file' + }) } } - if (fileConflicts.length || folderConflicts.length) { - await this.displayOverwriteDialog(uppyResources, fileConflicts, folderConflicts) + if (conflicts.length) { + await this.displayOverwriteDialog(uppyResources, conflicts) } else { this.handleUppyFileUpload(uppyResources) } @@ -797,24 +802,33 @@ export default defineComponent({ this.$uppyService.uploadFiles(files) }, - async displayOverwriteDialog(files: UppyResource[], fileConflicts, folderConflicts) { + async displayOverwriteDialog(files: UppyResource[], conflicts) { let count = 0 - const allConflictsCount = fileConflicts.length + folderConflicts.length - const resolvedConflicts = [] + const allConflictsCount = conflicts.length + const resolvedFileConflicts = [] + const resolvedFolderConflicts = [] let doForAllConflicts = false let allConflictsStrategy - for (const uppyResource of fileConflicts) { + for (const conflict of conflicts) { if(doForAllConflicts) { - resolvedConflicts.push({ - uppyResource, - strategy: allConflictsStrategy - }) + if(conflict.type === 'file') { + resolvedFileConflicts.push({ + uppyResource: conflict.data, + strategy: allConflictsStrategy, + }) + }else if(conflict.type === 'folder') { + resolvedFolderConflicts.push({ + name: conflict.data, + strategy: allConflictsStrategy, + }) + } continue } + const name = conflict.type === 'folder' ? conflict.data : conflict.data.name const resolvedConflict: ResolveConflict = await resolveFileExists( this.createModal, this.hideModal, - { name: uppyResource.name, isFolder: false } as Resource, + { name, isFolder: conflict.type === 'folder' } as Resource, allConflictsCount - count, this.$gettext, this.$gettextInterpolate, @@ -825,14 +839,20 @@ export default defineComponent({ doForAllConflicts = true allConflictsStrategy = resolvedConflict.strategy } - resolvedConflicts.push({ - uppyResource, - strategy: resolvedConflict.strategy - }) + if(conflict.type === 'file') { + resolvedFileConflicts.push({ + uppyResource: conflict.data, + strategy: resolvedConflict.strategy, + }) + }else if(conflict.type === 'folder') { + resolvedFolderConflicts.push({ + name: conflict.data, + strategy: resolvedConflict.strategy, + }) + } } - const filesToSkip = resolvedConflicts.filter(e => e.strategy === ResolveStrategy.SKIP).map(e => e.uppyResource) - const filesToOverwrite = resolvedConflicts.filter(e => e.strategy === ResolveStrategy.REPLACE).map(e => e.uppyResource) - const filesToKeepBoth = resolvedConflicts.filter(e => e.strategy === ResolveStrategy.KEEP_BOTH).map(e => e.uppyResource) + const filesToSkip = resolvedFileConflicts.filter(e => e.strategy === ResolveStrategy.SKIP).map(e => e.uppyResource) + const filesToKeepBoth = resolvedFileConflicts.filter(e => e.strategy === ResolveStrategy.KEEP_BOTH).map(e => e.uppyResource) files = files.filter(e => !filesToSkip.includes(e) && ! filesToKeepBoth.includes(e)) for (const uppyResource of filesToKeepBoth) { @@ -841,37 +861,10 @@ export default defineComponent({ files.push(uppyResource) } - const resolvedConflictsFolders = [] - for (const folderName of folderConflicts) { - if(doForAllConflicts) { - resolvedConflictsFolders.push({ - name: folderName, - strategy: allConflictsStrategy - }) - continue - } - const resolvedConflict: ResolveConflict = await resolveFileExists( - this.createModal, - this.hideModal, - { name: folderName, isFolder: true } as Resource, - allConflictsCount - count, - this.$gettext, - this.$gettextInterpolate, - false - ) - count += 1 - if(resolvedConflict.doForAllConflicts) { - doForAllConflicts = true - allConflictsStrategy = resolvedConflict.strategy - } - resolvedConflictsFolders.push({ - name: folderName, - strategy: resolvedConflict.strategy - }) - } - const foldersToSkip = resolvedConflictsFolders.filter(e => e.strategy === ResolveStrategy.SKIP).map(e => e.name) - const foldersToOverwrite = resolvedConflictsFolders.filter(e => e.strategy === ResolveStrategy.REPLACE).map(e => e.name) - const foldersToKeepBoth = resolvedConflictsFolders.filter(e => e.strategy === ResolveStrategy.KEEP_BOTH).map(e => e.name) + const foldersToSkip = resolvedFolderConflicts.filter(e => e.strategy === ResolveStrategy.SKIP).map(e => e.name) + // needs a solution how to handle overwrite + const foldersToOverwrite = resolvedFolderConflicts.filter(e => e.strategy === ResolveStrategy.REPLACE).map(e => e.name) + const foldersToKeepBoth = resolvedFolderConflicts.filter(e => e.strategy === ResolveStrategy.KEEP_BOTH).map(e => e.name) for(const folder of foldersToKeepBoth) { const filesInFolder = files.filter(e => e.meta.relativeFolder.startsWith(`/${folder}`)) From d2408cdbec7aae20f6b170f51b08c11e02be8a25 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Tue, 6 Sep 2022 15:13:14 +0200 Subject: [PATCH 18/29] Bugfix, remove dev leftover --- .../src/components/AppBar/CreateAndUpload.vue | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index aeb7dd01d17..d04e5d8f848 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -710,7 +710,7 @@ export default defineComponent({ } } // Logic for files - const exists = this.files.find((f) => f.name === file.name) + const exists = this.files.find((f) => f.name === file.name && !file.meta.relativeFolder) if (exists) { conflicts.push({ data: file, @@ -890,46 +890,6 @@ export default defineComponent({ files = files.filter(file => !foldersToSkip.some(folderName => file.meta.relativeFolder.startsWith(`/${folderName}`))) if(files.length === 0) return this.handleUppyFileUpload(files) - - // problem: folder /abc and /abcd treated the same - - - /*let count = 0 - for (const file of files) { - const resolveConflict: ResolveConflict = await resolveFileExists( - this.createModal, - this.hideModal, - { name: file.name, isFolder: false }, - files.length - count, - this.$gettext, - this.$gettextInterpolate, - false - ) - count += 1 - if (resolveConflict.doForAllConflicts) { - if (resolveConflict.strategy === ResolveStrategy.SKIP) { - return - } - if (resolveConflict.strategy === ResolveStrategy.KEEP_BOTH) { - for (const f of files) { - const ext = extractExtensionFromFile({ name: f.name } as Resource) - f.name = resolveFileNameDuplicate(f.name, ext, this.files) - } - } - // strategy replace doesn't need a case here - this.handleUppyFileUpload(files) - return - } - if (resolveConflict.strategy === ResolveStrategy.SKIP) { - continue - } - if (resolveConflict.strategy === ResolveStrategy.KEEP_BOTH) { - const ext = extractExtensionFromFile({ name: file.name } as Resource) - file.name = resolveFileNameDuplicate(file.name, ext, this.files) - } - // strategy replace doesn't need a case here - this.handleUppyFileUpload([file]) - }*/ } } }) From 1f217cad60c124058f68b166433223c238494fc7 Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Tue, 6 Sep 2022 15:30:38 +0200 Subject: [PATCH 19/29] Simplify conflict-array structure --- .../src/components/AppBar/CreateAndUpload.vue | 103 +++++++++--------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index d04e5d8f848..438f66beacc 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -162,7 +162,6 @@ import { ResolveConflict, resolveFileNameDuplicate } from '../../helpers/resource/copyMove' -import { resolve } from 'dns' export default defineComponent({ components: { @@ -701,9 +700,9 @@ export default defineComponent({ const rootFolder = relativeFilePath.replace(/^\/+/, '').split('/')[0] const exists = this.files.find((f) => f.name === rootFolder) if (exists) { - if(conflicts.some(conflict => conflict.data === rootFolder)) continue + if (conflicts.some((conflict) => conflict.data === rootFolder)) continue conflicts.push({ - data: rootFolder, + name: rootFolder, type: 'folder' }) continue @@ -713,7 +712,7 @@ export default defineComponent({ const exists = this.files.find((f) => f.name === file.name && !file.meta.relativeFolder) if (exists) { conflicts.push({ - data: file, + name: file.name, type: 'file' }) } @@ -809,66 +808,68 @@ export default defineComponent({ const resolvedFolderConflicts = [] let doForAllConflicts = false let allConflictsStrategy + for (const conflict of conflicts) { - if(doForAllConflicts) { - if(conflict.type === 'file') { - resolvedFileConflicts.push({ - uppyResource: conflict.data, - strategy: allConflictsStrategy, - }) - }else if(conflict.type === 'folder') { - resolvedFolderConflicts.push({ - name: conflict.data, - strategy: allConflictsStrategy, - }) - } + const isFolder = conflict.type === 'folder' + const conflictArray = isFolder ? resolvedFolderConflicts : resolvedFileConflicts + + if (doForAllConflicts) { + conflictArray.push({ + name: conflict.name, + strategy: allConflictsStrategy + }) continue } - const name = conflict.type === 'folder' ? conflict.data : conflict.data.name + const resolvedConflict: ResolveConflict = await resolveFileExists( this.createModal, this.hideModal, - { name, isFolder: conflict.type === 'folder' } as Resource, + { name: conflict.name, isFolder } as Resource, allConflictsCount - count, this.$gettext, this.$gettextInterpolate, false ) count += 1 - if(resolvedConflict.doForAllConflicts) { + if (resolvedConflict.doForAllConflicts) { doForAllConflicts = true allConflictsStrategy = resolvedConflict.strategy } - if(conflict.type === 'file') { - resolvedFileConflicts.push({ - uppyResource: conflict.data, - strategy: resolvedConflict.strategy, - }) - }else if(conflict.type === 'folder') { - resolvedFolderConflicts.push({ - name: conflict.data, - strategy: resolvedConflict.strategy, - }) - } + + conflictArray.push({ + name: conflict.name, + strategy: resolvedConflict.strategy + }) } - const filesToSkip = resolvedFileConflicts.filter(e => e.strategy === ResolveStrategy.SKIP).map(e => e.uppyResource) - const filesToKeepBoth = resolvedFileConflicts.filter(e => e.strategy === ResolveStrategy.KEEP_BOTH).map(e => e.uppyResource) + const filesToSkip = resolvedFileConflicts + .filter((e) => e.strategy === ResolveStrategy.SKIP) + .map((e) => e.name) + const filesToKeepBoth = resolvedFileConflicts + .filter((e) => e.strategy === ResolveStrategy.KEEP_BOTH) + .map((e) => e.name) - files = files.filter(e => !filesToSkip.includes(e) && ! filesToKeepBoth.includes(e)) - for (const uppyResource of filesToKeepBoth) { - const extension = extractExtensionFromFile({ name: uppyResource.name } as Resource) - uppyResource.name = resolveFileNameDuplicate(uppyResource.name, extension, this.files) - files.push(uppyResource) + for (const fileName of filesToKeepBoth) { + const file = files.find((e) => e.name === fileName && !e.meta.relativeFolder) + const extension = extractExtensionFromFile({ name: fileName } as Resource) + file.name = resolveFileNameDuplicate(fileName, extension, this.files) } - - const foldersToSkip = resolvedFolderConflicts.filter(e => e.strategy === ResolveStrategy.SKIP).map(e => e.name) - // needs a solution how to handle overwrite - const foldersToOverwrite = resolvedFolderConflicts.filter(e => e.strategy === ResolveStrategy.REPLACE).map(e => e.name) - const foldersToKeepBoth = resolvedFolderConflicts.filter(e => e.strategy === ResolveStrategy.KEEP_BOTH).map(e => e.name) - for(const folder of foldersToKeepBoth) { - const filesInFolder = files.filter(e => e.meta.relativeFolder.startsWith(`/${folder}`)) - for(const file of filesInFolder) { + files = files.filter((e) => !filesToSkip.includes(e.name)) + + const foldersToSkip = resolvedFolderConflicts + .filter((e) => e.strategy === ResolveStrategy.SKIP) + .map((e) => e.name) + // needs a solution how to handle overwrite + const foldersToOverwrite = resolvedFolderConflicts + .filter((e) => e.strategy === ResolveStrategy.REPLACE) + .map((e) => e.name) + const foldersToKeepBoth = resolvedFolderConflicts + .filter((e) => e.strategy === ResolveStrategy.KEEP_BOTH) + .map((e) => e.name) + + for (const folder of foldersToKeepBoth) { + const filesInFolder = files.filter((e) => e.meta.relativeFolder.startsWith(`/${folder}`)) + for (const file of filesInFolder) { const newFolderName = resolveFileNameDuplicate(folder, '', this.files) file.meta.relativeFolder = file.meta.relativeFolder.replace( `/${folder}`, @@ -878,17 +879,17 @@ export default defineComponent({ `/${folder}/`, `/${newFolderName}/` ) - file.meta.tusEndpoint = file.meta.tusEndpoint.replace( - `/${folder}`, - `/${newFolderName}` - ) + file.meta.tusEndpoint = file.meta.tusEndpoint.replace(`/${folder}`, `/${newFolderName}`) const data = file.data as any data.relativePath = data.relativePath.replace(`/${folder}/`, `/${newFolderName}/`) file.meta.routeItem = `/${newFolderName}` } } - files = files.filter(file => !foldersToSkip.some(folderName => file.meta.relativeFolder.startsWith(`/${folderName}`))) - if(files.length === 0) return + files = files.filter( + (file) => + !foldersToSkip.some((folderName) => file.meta.relativeFolder.startsWith(`/${folderName}`)) + ) + if (files.length === 0) return this.handleUppyFileUpload(files) } } From fade050439bdb8a153d2d3e7bc2eda957717479c Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Tue, 6 Sep 2022 15:35:42 +0200 Subject: [PATCH 20/29] Fix folder upload --- .../web-app-files/src/components/AppBar/CreateAndUpload.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 438f66beacc..5b1f9304921 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -700,7 +700,7 @@ export default defineComponent({ const rootFolder = relativeFilePath.replace(/^\/+/, '').split('/')[0] const exists = this.files.find((f) => f.name === rootFolder) if (exists) { - if (conflicts.some((conflict) => conflict.data === rootFolder)) continue + if (conflicts.some((conflict) => conflict.name === rootFolder)) continue conflicts.push({ name: rootFolder, type: 'folder' From dc7615db12daf075a18a5727ecc067e77f0235c9 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 7 Sep 2022 00:56:17 +0200 Subject: [PATCH 21/29] Add merge to folders --- .../src/components/AppBar/CreateAndUpload.vue | 7 ++----- .../web-app-files/src/helpers/resource/copyMove.ts | 12 +++++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 5b1f9304921..80ade0b5739 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -828,7 +828,8 @@ export default defineComponent({ allConflictsCount - count, this.$gettext, this.$gettextInterpolate, - false + false, + isFolder ) count += 1 if (resolvedConflict.doForAllConflicts) { @@ -859,10 +860,6 @@ export default defineComponent({ const foldersToSkip = resolvedFolderConflicts .filter((e) => e.strategy === ResolveStrategy.SKIP) .map((e) => e.name) - // needs a solution how to handle overwrite - const foldersToOverwrite = resolvedFolderConflicts - .filter((e) => e.strategy === ResolveStrategy.REPLACE) - .map((e) => e.name) const foldersToKeepBoth = resolvedFolderConflicts .filter((e) => e.strategy === ResolveStrategy.KEEP_BOTH) .map((e) => e.name) diff --git a/packages/web-app-files/src/helpers/resource/copyMove.ts b/packages/web-app-files/src/helpers/resource/copyMove.ts index f0095cb410c..7de3d963732 100644 --- a/packages/web-app-files/src/helpers/resource/copyMove.ts +++ b/packages/web-app-files/src/helpers/resource/copyMove.ts @@ -7,7 +7,8 @@ import { DavProperties } from 'web-pkg/src/constants' export enum ResolveStrategy { SKIP, REPLACE, - KEEP_BOTH + KEEP_BOTH, + MERGE } export interface ResolveConflict { strategy: ResolveStrategy @@ -25,7 +26,8 @@ export const resolveFileExists = ( conflictCount, $gettext, $gettextInterpolate, - isSingleConflict + isSingleConflict, + suggestMerge=false ): Promise => { return new Promise((resolve) => { let doForAllConflicts = false @@ -41,8 +43,8 @@ export const resolveFileExists = ( true ), cancelText: $gettext('Skip'), - confirmText: $gettext('Keep both'), - buttonSecondaryText: $gettext('Replace'), + confirmText: $gettext('Keep both') , + buttonSecondaryText: suggestMerge ? $gettext('Merge') : $gettext('Replace'), checkboxLabel: isSingleConflict ? '' : $gettextInterpolate( @@ -59,7 +61,7 @@ export const resolveFileExists = ( }, onConfirmSecondary: () => { hideModal() - const strategy = ResolveStrategy.REPLACE + const strategy = suggestMerge ? ResolveStrategy.MERGE : ResolveStrategy.REPLACE resolve({ strategy, doForAllConflicts } as ResolveConflict) }, onConfirm: () => { From c040bdd21ac81ac30cb4adf8133ec7b97475e94c Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 7 Sep 2022 01:02:26 +0200 Subject: [PATCH 22/29] Ignore existing folder errors for now --- .../src/components/AppBar/CreateAndUpload.vue | 2 +- packages/web-app-files/src/helpers/resource/copyMove.ts | 4 ++-- packages/web-runtime/src/composables/upload/useUpload.ts | 9 +++++---- .../tests/unit/composables/upload/useUpload.spec.ts | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 80ade0b5739..59a68d8a107 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -796,7 +796,7 @@ export default defineComponent({ async handleUppyFileUpload(files: UppyResource[]) { this.$uppyService.publish('uploadStarted') - await this.createDirectoryTree(files, this.files) + await this.createDirectoryTree(files) this.$uppyService.publish('addedForUpload', files) this.$uppyService.uploadFiles(files) }, diff --git a/packages/web-app-files/src/helpers/resource/copyMove.ts b/packages/web-app-files/src/helpers/resource/copyMove.ts index 7de3d963732..727ad086ea8 100644 --- a/packages/web-app-files/src/helpers/resource/copyMove.ts +++ b/packages/web-app-files/src/helpers/resource/copyMove.ts @@ -27,7 +27,7 @@ export const resolveFileExists = ( $gettext, $gettextInterpolate, isSingleConflict, - suggestMerge=false + suggestMerge = false ): Promise => { return new Promise((resolve) => { let doForAllConflicts = false @@ -43,7 +43,7 @@ export const resolveFileExists = ( true ), cancelText: $gettext('Skip'), - confirmText: $gettext('Keep both') , + confirmText: $gettext('Keep both'), buttonSecondaryText: suggestMerge ? $gettext('Merge') : $gettext('Replace'), checkboxLabel: isSingleConflict ? '' diff --git a/packages/web-runtime/src/composables/upload/useUpload.ts b/packages/web-runtime/src/composables/upload/useUpload.ts index 725a5d5fa5c..7301ac389e9 100644 --- a/packages/web-runtime/src/composables/upload/useUpload.ts +++ b/packages/web-runtime/src/composables/upload/useUpload.ts @@ -12,7 +12,6 @@ import { import { computed, Ref, unref, watch } from '@vue/composition-api' import { UppyService } from '../../services/uppyService' import * as uuid from 'uuid' -import { Resource } from 'web-client/src/helpers/resource' export interface UppyResource { id?: string @@ -44,7 +43,7 @@ interface UploadOptions { } interface UploadResult { - createDirectoryTree(files: UppyResource[], existingFiles: Resource[]): void + createDirectoryTree(files: UppyResource[]): void } export function useUpload(options: UploadOptions): UploadResult { @@ -122,7 +121,7 @@ const createDirectoryTree = ({ publicLinkPassword?: Ref uppyService: UppyService }) => { - return async (files: UppyResource[], existingFiles: Resource[]) => { + return async (files: UppyResource[]) => { const { owncloudSdk: client } = clientService const createdFolders = [] for (const file of files) { @@ -182,8 +181,10 @@ const createDirectoryTree = ({ unref(publicLinkPassword) ) } else { - if (!existingFiles.some((f) => f.path === folderToCreate)) { + try { await client.files.createFolder(`${file.meta.webDavBasePath}/${folderToCreate}`) + } catch (error) { + console.log(error) } } diff --git a/packages/web-runtime/tests/unit/composables/upload/useUpload.spec.ts b/packages/web-runtime/tests/unit/composables/upload/useUpload.spec.ts index 4945be69bd3..73452e0137e 100644 --- a/packages/web-runtime/tests/unit/composables/upload/useUpload.spec.ts +++ b/packages/web-runtime/tests/unit/composables/upload/useUpload.spec.ts @@ -61,7 +61,7 @@ describe('useUpload', () => { } ] - await wrapper.vm.createDirectoryTree(uppyResources, []) + await wrapper.vm.createDirectoryTree(uppyResources) expect(createFolderMock).toHaveBeenCalledTimes(4) }) }) From 9179c7e842d38a1849cdff561f2a0b0888682c51 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 7 Sep 2022 10:46:35 +0200 Subject: [PATCH 23/29] Make Merge reappear if "do for all" ticked --- .../src/components/AppBar/CreateAndUpload.vue | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 59a68d8a107..9169443bb3f 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -808,18 +808,27 @@ export default defineComponent({ const resolvedFolderConflicts = [] let doForAllConflicts = false let allConflictsStrategy + let doForAllConflictsFolders = false + let allConflictsStrategyFolders for (const conflict of conflicts) { const isFolder = conflict.type === 'folder' const conflictArray = isFolder ? resolvedFolderConflicts : resolvedFileConflicts - if (doForAllConflicts) { + if (doForAllConflicts && !isFolder) { conflictArray.push({ name: conflict.name, strategy: allConflictsStrategy }) continue } + if (doForAllConflictsFolders && isFolder) { + conflictArray.push({ + name: conflict.name, + strategy: allConflictsStrategyFolders + }) + continue + } const resolvedConflict: ResolveConflict = await resolveFileExists( this.createModal, @@ -833,8 +842,13 @@ export default defineComponent({ ) count += 1 if (resolvedConflict.doForAllConflicts) { - doForAllConflicts = true - allConflictsStrategy = resolvedConflict.strategy + if(isFolder){ + doForAllConflictsFolders = true + allConflictsStrategyFolders = resolvedConflict.strategy + }else { + doForAllConflicts = true + allConflictsStrategy = resolvedConflict.strategy + } } conflictArray.push({ From 4511d5b89938a76834edacaf31e1cce7a5398c01 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 7 Sep 2022 11:06:04 +0200 Subject: [PATCH 24/29] Address PR issues --- .../src/components/AppBar/CreateAndUpload.vue | 10 +++++----- .../web-runtime/src/composables/upload/useUpload.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 9169443bb3f..1a6c56f6773 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -692,7 +692,6 @@ export default defineComponent({ if (quotaExceeded) { return this.$uppyService.clearInputs() } - const resolveStrategies = {} for (const file of uppyResources) { const relativeFilePath = file.meta.relativePath if (relativeFilePath) { @@ -842,10 +841,10 @@ export default defineComponent({ ) count += 1 if (resolvedConflict.doForAllConflicts) { - if(isFolder){ + if (isFolder) { doForAllConflictsFolders = true allConflictsStrategyFolders = resolvedConflict.strategy - }else { + } else { doForAllConflicts = true allConflictsStrategy = resolvedConflict.strategy } @@ -879,9 +878,10 @@ export default defineComponent({ .map((e) => e.name) for (const folder of foldersToKeepBoth) { - const filesInFolder = files.filter((e) => e.meta.relativeFolder.startsWith(`/${folder}`)) + const filesInFolder = files.filter((e) => e.meta.relativeFolder.split('/')[1] === folder) for (const file of filesInFolder) { const newFolderName = resolveFileNameDuplicate(folder, '', this.files) + console.log(newFolderName) file.meta.relativeFolder = file.meta.relativeFolder.replace( `/${folder}`, `/${newFolderName}` @@ -898,7 +898,7 @@ export default defineComponent({ } files = files.filter( (file) => - !foldersToSkip.some((folderName) => file.meta.relativeFolder.startsWith(`/${folderName}`)) + !foldersToSkip.some((folderName) => file.meta.relativeFolder.split('/')[1] === folderName) ) if (files.length === 0) return this.handleUppyFileUpload(files) diff --git a/packages/web-runtime/src/composables/upload/useUpload.ts b/packages/web-runtime/src/composables/upload/useUpload.ts index 7301ac389e9..bcd410ce88d 100644 --- a/packages/web-runtime/src/composables/upload/useUpload.ts +++ b/packages/web-runtime/src/composables/upload/useUpload.ts @@ -184,7 +184,7 @@ const createDirectoryTree = ({ try { await client.files.createFolder(`${file.meta.webDavBasePath}/${folderToCreate}`) } catch (error) { - console.log(error) + console.error(error) } } From afedbd73d06f9c38cb77ff5f6c8329c08b62e447 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 7 Sep 2022 16:20:32 +0200 Subject: [PATCH 25/29] Add unittests --- .../src/components/AppBar/CreateAndUpload.vue | 7 ++- .../components/AppBar/CreateAndUpload.spec.js | 51 ++++++++++++++++++- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 1a6c56f6773..3282e6aec0d 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -717,7 +717,7 @@ export default defineComponent({ } } if (conflicts.length) { - await this.displayOverwriteDialog(uppyResources, conflicts) + await this.displayOverwriteDialog(uppyResources, conflicts, resolveFileExists) } else { this.handleUppyFileUpload(uppyResources) } @@ -800,7 +800,7 @@ export default defineComponent({ this.$uppyService.uploadFiles(files) }, - async displayOverwriteDialog(files: UppyResource[], conflicts) { + async displayOverwriteDialog(files: UppyResource[], conflicts, resolveFileExistsMethod) { let count = 0 const allConflictsCount = conflicts.length const resolvedFileConflicts = [] @@ -829,7 +829,7 @@ export default defineComponent({ continue } - const resolvedConflict: ResolveConflict = await resolveFileExists( + const resolvedConflict: ResolveConflict = await resolveFileExistsMethod( this.createModal, this.hideModal, { name: conflict.name, isFolder } as Resource, @@ -881,7 +881,6 @@ export default defineComponent({ const filesInFolder = files.filter((e) => e.meta.relativeFolder.split('/')[1] === folder) for (const file of filesInFolder) { const newFolderName = resolveFileNameDuplicate(folder, '', this.files) - console.log(newFolderName) file.meta.relativeFolder = file.meta.relativeFolder.replace( `/${folder}`, `/${newFolderName}` diff --git a/packages/web-app-files/tests/unit/components/AppBar/CreateAndUpload.spec.js b/packages/web-app-files/tests/unit/components/AppBar/CreateAndUpload.spec.js index c686803f4b6..e8a3fcd688c 100644 --- a/packages/web-app-files/tests/unit/components/AppBar/CreateAndUpload.spec.js +++ b/packages/web-app-files/tests/unit/components/AppBar/CreateAndUpload.spec.js @@ -6,6 +6,7 @@ import GetTextPlugin from 'vue-gettext' import stubs from '@/tests/unit/stubs' import CreateAndUpload from 'files/src/components/AppBar/CreateAndUpload' import { createLocationSpaces } from '../../../../src/router' +import { ResolveStrategy } from '../../../../src/helpers/resource/copyMove' const localVue = createLocalVue() localVue.use(Vuex) @@ -207,6 +208,53 @@ describe('CreateAndUpload component', () => { expect(showMessageStub).toHaveBeenCalledTimes(0) }) }) + describe('upload conflict dialog', () => { + it.each([ResolveStrategy.REPLACE, ResolveStrategy.KEEP_BOTH])( + 'should upload file if user chooses replace or keep both', + async (strategy) => { + const uppyResourceOne = { + name: 'test123', + meta: { + relativeFolder: '' + } + } + const conflict = { + name: uppyResourceOne.name, + type: 'file' + } + + const store = createStore({ currentFolder }, newFileHandlers) + const wrapper = getShallowWrapper(route, store) + const handleUppyFileUpload = jest.fn() + wrapper.vm.handleUppyFileUpload = handleUppyFileUpload + + await wrapper.vm.displayOverwriteDialog([uppyResourceOne], [conflict], () => + Promise.resolve({ strategy: strategy, doForAllConflicts: true }) + ) + + expect(handleUppyFileUpload).toBeCalledTimes(1) + expect(handleUppyFileUpload).toBeCalledWith([uppyResourceOne]) + } + ) + it('should not upload file if user chooses skip', async () => { + const uppyResourceOne = { name: 'test123' } + const conflict = { + name: uppyResourceOne.name, + type: 'file' + } + + const store = createStore({ currentFolder }, newFileHandlers) + const wrapper = getShallowWrapper(route, store) + const handleUppyFileUpload = jest.fn() + wrapper.vm.handleUppyFileUpload = handleUppyFileUpload + + await wrapper.vm.displayOverwriteDialog([uppyResourceOne], [conflict], () => + Promise.resolve({ strategy: ResolveStrategy.SKIP, doForAllConflicts: true }) + ) + + expect(handleUppyFileUpload).not.toHaveBeenCalled() + }) + }) }) function getFileHandlerSelector(extension) { @@ -311,7 +359,8 @@ function createStore(state = { currentFolder: {} }, fileHandlers = []) { getters: { currentFolder: () => state.currentFolder, clipboardResources: () => [], - selectedFiles: () => [] + selectedFiles: () => [], + files: () => [] } } } From 9a0d3e268c3689f3ee149902bece6389bf4bae95 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 7 Sep 2022 16:32:40 +0200 Subject: [PATCH 26/29] Add more unittests --- .../components/AppBar/CreateAndUpload.spec.js | 84 +++++++++++++++++-- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/packages/web-app-files/tests/unit/components/AppBar/CreateAndUpload.spec.js b/packages/web-app-files/tests/unit/components/AppBar/CreateAndUpload.spec.js index e8a3fcd688c..17d212c747b 100644 --- a/packages/web-app-files/tests/unit/components/AppBar/CreateAndUpload.spec.js +++ b/packages/web-app-files/tests/unit/components/AppBar/CreateAndUpload.spec.js @@ -213,7 +213,7 @@ describe('CreateAndUpload component', () => { 'should upload file if user chooses replace or keep both', async (strategy) => { const uppyResourceOne = { - name: 'test123', + name: 'test', meta: { relativeFolder: '' } @@ -227,17 +227,23 @@ describe('CreateAndUpload component', () => { const wrapper = getShallowWrapper(route, store) const handleUppyFileUpload = jest.fn() wrapper.vm.handleUppyFileUpload = handleUppyFileUpload + const resolveFileConflictMethod = jest.fn(() => + Promise.resolve({ strategy, doForAllConflicts: true }) + ) - await wrapper.vm.displayOverwriteDialog([uppyResourceOne], [conflict], () => - Promise.resolve({ strategy: strategy, doForAllConflicts: true }) + await wrapper.vm.displayOverwriteDialog( + [uppyResourceOne], + [conflict], + resolveFileConflictMethod ) + expect(resolveFileConflictMethod).toHaveBeenCalledTimes(1) expect(handleUppyFileUpload).toBeCalledTimes(1) expect(handleUppyFileUpload).toBeCalledWith([uppyResourceOne]) } ) it('should not upload file if user chooses skip', async () => { - const uppyResourceOne = { name: 'test123' } + const uppyResourceOne = { name: 'test' } const conflict = { name: uppyResourceOne.name, type: 'file' @@ -247,13 +253,79 @@ describe('CreateAndUpload component', () => { const wrapper = getShallowWrapper(route, store) const handleUppyFileUpload = jest.fn() wrapper.vm.handleUppyFileUpload = handleUppyFileUpload - - await wrapper.vm.displayOverwriteDialog([uppyResourceOne], [conflict], () => + const resolveFileConflictMethod = jest.fn(() => Promise.resolve({ strategy: ResolveStrategy.SKIP, doForAllConflicts: true }) ) + await wrapper.vm.displayOverwriteDialog( + [uppyResourceOne], + [conflict], + resolveFileConflictMethod + ) + + expect(resolveFileConflictMethod).toHaveBeenCalledTimes(1) expect(handleUppyFileUpload).not.toHaveBeenCalled() }) + it('should show dialog once if do for all conflicts is ticked', async () => { + const uppyResourceOne = { name: 'test' } + const uppyResourceTwo = { name: 'test2' } + const conflictOne = { + name: uppyResourceOne.name, + type: 'file' + } + const conflictTwo = { + name: uppyResourceTwo.name, + type: 'file' + } + + const store = createStore({ currentFolder }, newFileHandlers) + const wrapper = getShallowWrapper(route, store) + const handleUppyFileUpload = jest.fn() + wrapper.vm.handleUppyFileUpload = handleUppyFileUpload + const resolveFileConflictMethod = jest.fn(() => + Promise.resolve({ strategy: ResolveStrategy.REPLACE, doForAllConflicts: true }) + ) + + await wrapper.vm.displayOverwriteDialog( + [uppyResourceOne, uppyResourceTwo], + [conflictOne, conflictTwo], + resolveFileConflictMethod + ) + + expect(resolveFileConflictMethod).toHaveBeenCalledTimes(1) + expect(handleUppyFileUpload).toBeCalledTimes(1) + expect(handleUppyFileUpload).toBeCalledWith([uppyResourceOne, uppyResourceTwo]) + }) + it('should show dialog twice if do for all conflicts is ticked and folders and files are uploaded', async () => { + const uppyResourceOne = { name: 'test' } + const uppyResourceTwo = { name: 'folder' } + const conflictOne = { + name: uppyResourceOne.name, + type: 'file' + } + const conflictTwo = { + name: uppyResourceTwo.name, + type: 'folder' + } + + const store = createStore({ currentFolder }, newFileHandlers) + const wrapper = getShallowWrapper(route, store) + const handleUppyFileUpload = jest.fn() + wrapper.vm.handleUppyFileUpload = handleUppyFileUpload + const resolveFileConflictMethod = jest.fn(() => + Promise.resolve({ strategy: ResolveStrategy.REPLACE, doForAllConflicts: true }) + ) + + await wrapper.vm.displayOverwriteDialog( + [uppyResourceOne, uppyResourceTwo], + [conflictOne, conflictTwo], + resolveFileConflictMethod + ) + + expect(resolveFileConflictMethod).toHaveBeenCalledTimes(2) + expect(handleUppyFileUpload).toBeCalledTimes(1) + expect(handleUppyFileUpload).toBeCalledWith([uppyResourceOne, uppyResourceTwo]) + }) }) }) From d79fe543fd5f2c28af85ba58e9fe3d0ece9a17ba Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Wed, 7 Sep 2022 18:06:07 +0200 Subject: [PATCH 27/29] Fix e2e upload version --- tests/e2e/support/objects/app-files/resource/actions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/e2e/support/objects/app-files/resource/actions.ts b/tests/e2e/support/objects/app-files/resource/actions.ts index b9d565ec386..4016ffdaffc 100644 --- a/tests/e2e/support/objects/app-files/resource/actions.ts +++ b/tests/e2e/support/objects/app-files/resource/actions.ts @@ -24,6 +24,7 @@ const breadcrumbRoot = '//nav[contains(@class, "oc-breadcrumb")]/ol/li[1]/a' const fileRenameInput = '.oc-text-input' const deleteButton = 'button.oc-files-actions-delete-trigger' const actionConfirmationButton = '.oc-modal-body-actions-confirm' +const actionSecondaryConfirmationButton = '.oc-modal-body-actions-secondary' const versionRevertButton = '//*[@data-testid="file-versions-revert-button"]' const emptyTrashBinButton = '.oc-files-actions-empty-trash-bin-trigger' const notificationMessageDialog = '.oc-notification-message-title' @@ -108,7 +109,7 @@ export const uploadResource = async (args: uploadResourceArgs): Promise => await page.locator(fileUploadInput).setInputFiles(resources.map((file) => file.path)) if (createVersion) { - await page.locator(actionConfirmationButton).click() + await page.locator(actionSecondaryConfirmationButton).click() // @TODO check if upload was successful } From b4fe000133077117129eb1cd00be73abfa7d5f35 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Thu, 8 Sep 2022 10:18:47 +0200 Subject: [PATCH 28/29] Fix file overwrite acceptance tests --- tests/acceptance/pageObjects/personalPage.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/acceptance/pageObjects/personalPage.js b/tests/acceptance/pageObjects/personalPage.js index df491e1c633..396f2c334bc 100644 --- a/tests/acceptance/pageObjects/personalPage.js +++ b/tests/acceptance/pageObjects/personalPage.js @@ -239,8 +239,8 @@ module.exports = { }, confirmFileOverwrite: async function () { await this.waitForAnimationToFinish() // wait for transition on the modal to finish - .waitForElementVisible('@dialogConfirmBtnEnabled') - .click('@dialogConfirmBtnEnabled') + .waitForElementVisible('@dialogConfirmBtnSecondaryEnabled') + .click('@dialogConfirmBtnSecondaryEnabled') .waitForElementNotPresent('@dialog') .waitForAjaxCallsToStartAndFinish() .waitForElementVisible('@fileUploadStatus') @@ -397,6 +397,9 @@ module.exports = { dialogConfirmBtnEnabled: { selector: '.oc-modal-body-actions-confirm:enabled' }, + dialogConfirmBtnSecondaryEnabled: { + selector: '.oc-modal-body-actions-secondary:enabled' + }, dialogConfirmBtnDisabled: { selector: '.oc-modal-body-actions-confirm:disabled' }, From 4ac0d5a0e86b6ea7a292697fe545a9cc0e759b32 Mon Sep 17 00:00:00 2001 From: Paul Neubauer Date: Fri, 9 Sep 2022 13:35:15 +0200 Subject: [PATCH 29/29] Address PR issues --- .../bugfix-resolve-upload-existing-folder | 2 +- .../src/components/AppBar/CreateAndUpload.vue | 39 +++++++++++-------- .../src/helpers/resource/copyMove.ts | 13 +++++++ 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/changelog/unreleased/bugfix-resolve-upload-existing-folder b/changelog/unreleased/bugfix-resolve-upload-existing-folder index 1c0b1301386..08312e8b16d 100644 --- a/changelog/unreleased/bugfix-resolve-upload-existing-folder +++ b/changelog/unreleased/bugfix-resolve-upload-existing-folder @@ -1,6 +1,6 @@ Bugfix: Resolve upload existing folder -We've added an conflict dialog when uploading files and folders +We've added a conflict dialog which handles name clashes when uploading files and folders. https://github.com/owncloud/web/pull/7504 https://github.com/owncloud/web/issues/6996 \ No newline at end of file diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 3282e6aec0d..0657ab5f653 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -160,7 +160,8 @@ import { resolveFileExists, ResolveStrategy, ResolveConflict, - resolveFileNameDuplicate + resolveFileNameDuplicate, + FileExistsResolver } from '../../helpers/resource/copyMove' export default defineComponent({ @@ -800,7 +801,11 @@ export default defineComponent({ this.$uppyService.uploadFiles(files) }, - async displayOverwriteDialog(files: UppyResource[], conflicts, resolveFileExistsMethod) { + async displayOverwriteDialog( + files: UppyResource[], + conflicts, + resolveFileExistsMethod: FileExistsResolver + ) { let count = 0 const allConflictsCount = conflicts.length const resolvedFileConflicts = [] @@ -839,7 +844,7 @@ export default defineComponent({ false, isFolder ) - count += 1 + count++ if (resolvedConflict.doForAllConflicts) { if (isFolder) { doForAllConflictsFolders = true @@ -858,25 +863,28 @@ export default defineComponent({ const filesToSkip = resolvedFileConflicts .filter((e) => e.strategy === ResolveStrategy.SKIP) .map((e) => e.name) - const filesToKeepBoth = resolvedFileConflicts - .filter((e) => e.strategy === ResolveStrategy.KEEP_BOTH) + const foldersToSkip = resolvedFolderConflicts + .filter((e) => e.strategy === ResolveStrategy.SKIP) .map((e) => e.name) - for (const fileName of filesToKeepBoth) { - const file = files.find((e) => e.name === fileName && !e.meta.relativeFolder) - const extension = extractExtensionFromFile({ name: fileName } as Resource) - file.name = resolveFileNameDuplicate(fileName, extension, this.files) - } - files = files.filter((e) => !filesToSkip.includes(e.name)) + files = files.filter( + (file) => + !foldersToSkip.some((folderName) => file.meta.relativeFolder.split('/')[1] === folderName) + ) - const foldersToSkip = resolvedFolderConflicts - .filter((e) => e.strategy === ResolveStrategy.SKIP) + const filesToKeepBoth = resolvedFileConflicts + .filter((e) => e.strategy === ResolveStrategy.KEEP_BOTH) .map((e) => e.name) const foldersToKeepBoth = resolvedFolderConflicts .filter((e) => e.strategy === ResolveStrategy.KEEP_BOTH) .map((e) => e.name) + for (const fileName of filesToKeepBoth) { + const file = files.find((e) => e.name === fileName && !e.meta.relativeFolder) + const extension = extractExtensionFromFile({ name: fileName } as Resource) + file.name = resolveFileNameDuplicate(fileName, extension, this.files) + } for (const folder of foldersToKeepBoth) { const filesInFolder = files.filter((e) => e.meta.relativeFolder.split('/')[1] === folder) for (const file of filesInFolder) { @@ -895,10 +903,7 @@ export default defineComponent({ file.meta.routeItem = `/${newFolderName}` } } - files = files.filter( - (file) => - !foldersToSkip.some((folderName) => file.meta.relativeFolder.split('/')[1] === folderName) - ) + if (files.length === 0) return this.handleUppyFileUpload(files) } diff --git a/packages/web-app-files/src/helpers/resource/copyMove.ts b/packages/web-app-files/src/helpers/resource/copyMove.ts index 727ad086ea8..378201df4a5 100644 --- a/packages/web-app-files/src/helpers/resource/copyMove.ts +++ b/packages/web-app-files/src/helpers/resource/copyMove.ts @@ -19,6 +19,19 @@ interface FileConflict { strategy?: ResolveStrategy } +export interface FileExistsResolver { + ( + createModal: void, + hideModal: void, + resource: Resource, + conflictCount: number, + $gettext: void, + $gettextInterpolate: void, + isSingleConflict: boolean, + suggestMerge: boolean + ) +} + export const resolveFileExists = ( createModal, hideModal,