Skip to content

Commit 079d026

Browse files
committed
feat(files): Allow uploading directories
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent 879eaa7 commit 079d026

4 files changed

Lines changed: 162 additions & 89 deletions

File tree

__mocks__/@nextcloud/axios.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55
export default {
6+
interceptors: {
7+
response: {
8+
use: () => {},
9+
},
10+
request: {
11+
use: () => {},
12+
},
13+
},
614
get: async () => ({ status: 200, data: {} }),
715
delete: async () => ({ status: 200, data: {} }),
816
post: async () => ({ status: 200, data: {} }),

apps/files/src/views/FilesList.vue

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@
3737

3838
<!-- Uploader -->
3939
<UploadPicker v-else-if="currentFolder"
40-
:content="dirContents"
41-
:destination="currentFolder"
42-
:multiple="true"
40+
allow-folders
4341
class="files-list__header-upload-button"
42+
:content="getContent"
43+
:destination="currentFolder"
44+
:forbidden-characters="forbiddenCharacters"
45+
multiple
4446
@failed="onUploadFail"
4547
@uploaded="onUpload" />
4648
</template>
@@ -79,9 +81,11 @@
7981
<template v-if="dir !== '/'" #action>
8082
<!-- Uploader -->
8183
<UploadPicker v-if="currentFolder && canUpload && !isQuotaExceeded"
82-
:content="dirContents"
83-
:destination="currentFolder"
84+
allow-folders
8485
class="files-list__header-upload-button"
86+
:content="getContent"
87+
:destination="currentFolder"
88+
:forbidden-characters="forbiddenCharacters"
8589
multiple
8690
@failed="onUploadFail"
8791
@uploaded="onUpload" />
@@ -118,10 +122,10 @@ import { getCapabilities } from '@nextcloud/capabilities'
118122
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
119123
import { Folder, Node, Permission } from '@nextcloud/files'
120124
import { translate as t } from '@nextcloud/l10n'
121-
import { join, dirname } from 'path'
122-
import { showError } from '@nextcloud/dialogs'
125+
import { join, dirname, normalize } from 'path'
126+
import { showError, showWarning } from '@nextcloud/dialogs'
123127
import { Type } from '@nextcloud/sharing'
124-
import { UploadPicker } from '@nextcloud/upload'
128+
import { UploadPicker, UploadStatus } from '@nextcloud/upload'
125129
import { loadState } from '@nextcloud/initial-state'
126130
import { defineComponent } from 'vue'
127131
@@ -190,6 +194,7 @@ export default defineComponent({
190194
const { currentView } = useNavigation()
191195
192196
const enableGridView = (loadState('core', 'config', [])['enable_non-accessible_features'] ?? true)
197+
const forbiddenCharacters = loadState<string[]>('files', 'forbiddenCharacters', [])
193198
194199
return {
195200
currentView,
@@ -200,9 +205,10 @@ export default defineComponent({
200205
uploaderStore,
201206
userConfigStore,
202207
viewConfigStore,
203-
enableGridView,
204208
205209
// non reactive data
210+
enableGridView,
211+
forbiddenCharacters,
206212
Type,
207213
}
208214
},
@@ -228,6 +234,19 @@ export default defineComponent({
228234
}, 500)
229235
},
230236
237+
/**
238+
* Get a callback function for the uploader to fetch directory contents for conflict resolution
239+
*/
240+
getContent() {
241+
const view = this.currentView
242+
return async (path?: string) => {
243+
// as the path is allowed to be undefined we need to normalize the path ('//' to '/')
244+
const normalizedPath = normalize(`${this.currentFolder?.path ?? ''}/${path ?? ''}`)
245+
// use the current view to fetch the content for the requested path
246+
return (await view.getContents(normalizedPath)).contents
247+
}
248+
},
249+
231250
userConfig(): UserConfig {
232251
return this.userConfigStore.userConfig
233252
},
@@ -590,8 +609,7 @@ export default defineComponent({
590609
onUpload(upload: Upload) {
591610
// Let's only refresh the current Folder
592611
// Navigating to a different folder will refresh it anyway
593-
const destinationSource = dirname(upload.source)
594-
const needsRefresh = destinationSource === this.currentFolder?.source
612+
const needsRefresh = dirname(upload.source) === this.currentFolder!.source
595613
596614
// TODO: fetch uploaded files data only
597615
// Use parseInt(upload.response?.headers?.['oc-fileid']) to get the fileid
@@ -604,6 +622,11 @@ export default defineComponent({
604622
async onUploadFail(upload: Upload) {
605623
const status = upload.response?.status || 0
606624
625+
if (upload.status === UploadStatus.CANCELLED) {
626+
showWarning(t('files', 'Upload was cancelled by user'))
627+
return
628+
}
629+
607630
// Check known status codes
608631
if (status === 507) {
609632
showError(t('files', 'Not enough free space'))

0 commit comments

Comments
 (0)