Skip to content

Commit 21f9fe8

Browse files
authored
Merge pull request #46951 from nextcloud/backport/46918/stable29
[stable29] fix(files): Correctly create Nodes from WebDAV result in "recent"-view
2 parents e6d3642 + 4c70619 commit 21f9fe8

6 files changed

Lines changed: 95 additions & 28 deletions

File tree

apps/files/src/actions/deleteAction.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export const action = new FileAction({
5151
enabled(nodes: Node[]) {
5252
return nodes.length > 0 && nodes
5353
.map(node => node.permissions)
54-
.every(permission => (permission & Permission.DELETE) !== 0)
54+
.every(permission => Boolean(permission & Permission.DELETE))
5555
},
5656

5757
async exec(node: Node, view: View) {

apps/files/src/services/Files.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*
2121
*/
2222
import type { ContentsWithRoot } from '@nextcloud/files'
23-
import type { FileStat, ResponseDataDetailed, DAVResultResponseProps } from 'webdav'
23+
import type { FileStat, ResponseDataDetailed } from 'webdav'
2424

2525
import { CancelablePromise } from 'cancelable-promise'
2626
import { File, Folder, davGetClient, davGetDefaultPropfind, davResultToNode, davRootPath } from '@nextcloud/files'

apps/files/src/services/Recent.ts

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,27 @@
2121
*
2222
*/
2323
import type { ContentsWithRoot, Node } from '@nextcloud/files'
24-
import type { FileStat, ResponseDataDetailed } from 'webdav'
24+
import type { FileStat, ResponseDataDetailed, SearchResult } from 'webdav'
2525

2626
import { getCurrentUser } from '@nextcloud/auth'
2727
import { Folder, Permission, davGetRecentSearch, davGetClient, davResultToNode, davRootPath, davRemoteURL } from '@nextcloud/files'
28+
import { getBaseUrl } from '@nextcloud/router'
29+
import { CancelablePromise } from 'cancelable-promise'
2830
import { useUserConfigStore } from '../store/userconfig.ts'
2931
import { pinia } from '../store/index.ts'
3032

3133
const client = davGetClient()
3234

3335
const lastTwoWeeksTimestamp = Math.round((Date.now() / 1000) - (60 * 60 * 24 * 14))
3436

37+
/**
38+
* Helper to map a WebDAV result to a Nextcloud node
39+
* The search endpoint already includes the dav remote URL so we must not include it in the source
40+
*
41+
* @param stat the WebDAV result
42+
*/
43+
const resultToNode = (stat: FileStat) => davResultToNode(stat, davRootPath, getBaseUrl())
44+
3545
/**
3646
* Get recently changed nodes
3747
*
@@ -51,28 +61,41 @@ export const getContents = async (path = '/'): Promise<ContentsWithRoot> => {
5161
|| store.userConfig.show_hidden // If configured to show hidden files we can early return
5262
|| !node.dirname.split('/').some((dir) => dir.startsWith('.')) // otherwise only include the file if non of the parent directories is hidden
5363

54-
const contentsResponse = await client.getDirectoryContents(path, {
55-
details: true,
56-
data: davGetRecentSearch(lastTwoWeeksTimestamp),
57-
headers: {
58-
// Patched in WebdavClient.ts
59-
method: 'SEARCH',
60-
// Somehow it's needed to get the correct response
61-
'Content-Type': 'application/xml; charset=utf-8',
62-
},
63-
deep: true,
64-
}) as ResponseDataDetailed<FileStat[]>
64+
const abort = new AbortController()
65+
66+
return new CancelablePromise(async (resolve, reject, cancel) => {
67+
cancel(() => abort.abort())
68+
69+
let contentsResponse: ResponseDataDetailed<SearchResult>
70+
try {
71+
contentsResponse = await client.search('/', {
72+
details: true,
73+
data: davGetRecentSearch(lastTwoWeeksTimestamp),
74+
signal: abort.signal,
75+
}) as ResponseDataDetailed<SearchResult>
76+
} catch (e) {
77+
reject(e)
78+
return
79+
}
80+
81+
if (abort.signal.aborted) {
82+
reject()
83+
return
84+
}
6585

66-
const contents = contentsResponse.data
86+
const contents = contentsResponse.data.results
87+
.map(resultToNode)
88+
.filter(filterHidden)
6789

68-
return {
69-
folder: new Folder({
70-
id: 0,
71-
source: `${davRemoteURL}${davRootPath}`,
72-
root: davRootPath,
73-
owner: getCurrentUser()?.uid || null,
74-
permissions: Permission.READ,
75-
}),
76-
contents: contents.map((r) => davResultToNode(r)).filter(filterHidden),
77-
}
90+
resolve({
91+
contents,
92+
folder: new Folder({
93+
id: 0,
94+
source: `${davRemoteURL}${davRootPath}`,
95+
root: davRootPath,
96+
owner: getCurrentUser()?.uid || null,
97+
permissions: Permission.READ,
98+
}),
99+
})
100+
})
78101
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import type { User } from '@nextcloud/cypress'
7+
import { getRowForFile, triggerActionForFile } from './FilesUtils'
8+
9+
describe('files: Recent view', { testIsolation: true }, () => {
10+
let user: User
11+
12+
beforeEach(() => cy.createRandomUser().then(($user) => {
13+
user = $user
14+
15+
cy.uploadContent(user, new Blob([]), 'text/plain', '/file.txt')
16+
cy.login(user)
17+
}))
18+
19+
it('see the recently created file in the recent view', () => {
20+
cy.visit('/apps/files/recent')
21+
// All are visible by default
22+
getRowForFile('file.txt').should('be.visible')
23+
})
24+
25+
/**
26+
* Regression test: There was a bug that the files were correctly loaded but with invalid source
27+
* so the delete action failed.
28+
*/
29+
it('can delete a file in the recent view', () => {
30+
cy.intercept('DELETE', '**/remote.php/dav/files/**').as('deleteFile')
31+
32+
cy.visit('/apps/files/recent')
33+
// See the row
34+
getRowForFile('file.txt').should('be.visible')
35+
// delete the file
36+
triggerActionForFile('file.txt', 'delete')
37+
cy.wait('@deleteFile')
38+
// See it is not visible anymore
39+
getRowForFile('file.txt').should('not.exist')
40+
// also not existing in default view after reload
41+
cy.visit('/apps/files')
42+
getRowForFile('file.txt').should('not.exist')
43+
})
44+
})

dist/files-init.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/files-init.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)