aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_trashbin/src/services
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_trashbin/src/services')
-rw-r--r--apps/files_trashbin/src/services/api.spec.ts43
-rw-r--r--apps/files_trashbin/src/services/api.ts28
-rw-r--r--apps/files_trashbin/src/services/client.ts44
-rw-r--r--apps/files_trashbin/src/services/trashbin.ts82
4 files changed, 94 insertions, 103 deletions
diff --git a/apps/files_trashbin/src/services/api.spec.ts b/apps/files_trashbin/src/services/api.spec.ts
new file mode 100644
index 00000000000..b50a53b8e07
--- /dev/null
+++ b/apps/files_trashbin/src/services/api.spec.ts
@@ -0,0 +1,43 @@
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+import { emptyTrash } from './api.ts'
+import * as ncAuth from '@nextcloud/auth'
+import * as ncDialogs from '@nextcloud/dialogs'
+import * as logger from '../logger.ts'
+
+const axiosMock = vi.hoisted(() => ({
+ delete: vi.fn(),
+}))
+vi.mock('@nextcloud/axios', () => ({ default: axiosMock }))
+
+describe('files_trashbin: API - emptyTrash', () => {
+ beforeEach(() => {
+ vi.spyOn(ncAuth, 'getCurrentUser').mockImplementationOnce(() => ({
+ uid: 'test',
+ displayName: 'Test',
+ isAdmin: false,
+ }))
+ })
+
+ it('shows success', async () => {
+ const dialogSpy = vi.spyOn(ncDialogs, 'showSuccess')
+ expect(await emptyTrash()).toBe(true)
+ expect(axiosMock.delete).toBeCalled()
+ expect(dialogSpy).toBeCalledWith('All files have been permanently deleted')
+ })
+
+ it('shows failure', async () => {
+ axiosMock.delete.mockImplementationOnce(() => { throw new Error() })
+ const dialogSpy = vi.spyOn(ncDialogs, 'showError')
+ const loggerSpy = vi.spyOn(logger.logger, 'error').mockImplementationOnce(() => {})
+
+ expect(await emptyTrash()).toBe(false)
+ expect(axiosMock.delete).toBeCalled()
+ expect(dialogSpy).toBeCalledWith('Failed to empty deleted files')
+ expect(loggerSpy).toBeCalled()
+ })
+})
diff --git a/apps/files_trashbin/src/services/api.ts b/apps/files_trashbin/src/services/api.ts
new file mode 100644
index 00000000000..b1f2e98b2d9
--- /dev/null
+++ b/apps/files_trashbin/src/services/api.ts
@@ -0,0 +1,28 @@
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { getCurrentUser } from '@nextcloud/auth'
+import { showError, showSuccess } from '@nextcloud/dialogs'
+import { defaultRemoteURL } from '@nextcloud/files/dav'
+import { t } from '@nextcloud/l10n'
+import axios from '@nextcloud/axios'
+
+import { logger } from '../logger.ts'
+
+/**
+ * Send API request to empty the trashbin.
+ * Returns true if request succeeded - otherwise false is returned.
+ */
+export async function emptyTrash(): Promise<boolean> {
+ try {
+ await axios.delete(`${defaultRemoteURL}/trashbin/${getCurrentUser()!.uid}/trash`)
+ showSuccess(t('files_trashbin', 'All files have been permanently deleted'))
+ return true
+ } catch (error) {
+ showError(t('files_trashbin', 'Failed to empty deleted files'))
+ logger.error('Failed to empty deleted files', { error })
+ return false
+ }
+}
diff --git a/apps/files_trashbin/src/services/client.ts b/apps/files_trashbin/src/services/client.ts
index e9ea06a9a5e..5ee25a6a94f 100644
--- a/apps/files_trashbin/src/services/client.ts
+++ b/apps/files_trashbin/src/services/client.ts
@@ -1,46 +1,12 @@
/**
- * @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @license AGPL-3.0-or-later
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-import { createClient } from 'webdav'
-import { generateRemoteUrl } from '@nextcloud/router'
-import { getCurrentUser, getRequestToken, onRequestTokenUpdate } from '@nextcloud/auth'
+import { getCurrentUser } from '@nextcloud/auth'
+import { davGetClient } from '@nextcloud/files'
// init webdav client
export const rootPath = `/trashbin/${getCurrentUser()?.uid}/trash`
-export const rootUrl = generateRemoteUrl('dav' + rootPath)
-const client = createClient(rootUrl)
-// set CSRF token header
-const setHeaders = (token: string | null) => {
- client.setHeaders({
- // Add this so the server knows it is an request from the browser
- 'X-Requested-With': 'XMLHttpRequest',
- // Inject user auth
- requesttoken: token ?? '',
- })
-}
-
-// refresh headers when request token changes
-onRequestTokenUpdate(setHeaders)
-setHeaders(getRequestToken())
-
-export default client
+export const client = davGetClient()
diff --git a/apps/files_trashbin/src/services/trashbin.ts b/apps/files_trashbin/src/services/trashbin.ts
index 9aef75ef6d5..9fef16d032f 100644
--- a/apps/files_trashbin/src/services/trashbin.ts
+++ b/apps/files_trashbin/src/services/trashbin.ts
@@ -1,90 +1,44 @@
/**
- * @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @license AGPL-3.0-or-later
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { FileStat, ResponseDataDetailed } from 'webdav'
import type { ContentsWithRoot } from '@nextcloud/files'
-import { File, Folder, davParsePermissions, getDavNameSpaces, getDavProperties } from '@nextcloud/files'
-import { generateRemoteUrl, generateUrl } from '@nextcloud/router'
-import { getCurrentUser } from '@nextcloud/auth'
-
-import client, { rootPath } from './client'
+import { File, Folder, davResultToNode, getDavNameSpaces, getDavProperties } from '@nextcloud/files'
+import { client, rootPath } from './client'
+import { generateUrl } from '@nextcloud/router'
const data = `<?xml version="1.0"?>
<d:propfind ${getDavNameSpaces()}>
<d:prop>
- <nc:trashbin-filename />
<nc:trashbin-deletion-time />
<nc:trashbin-original-location />
<nc:trashbin-title />
+ <nc:trashbin-deleted-by-id />
+ <nc:trashbin-deleted-by-display-name />
${getDavProperties()}
</d:prop>
</d:propfind>`
-const resultToNode = function(node: FileStat): File | Folder {
- const permissions = davParsePermissions(node.props?.permissions)
- const owner = getCurrentUser()?.uid as string
- const previewUrl = generateUrl('/apps/files_trashbin/preview?fileId={fileid}&x=32&y=32', node.props)
-
- const nodeData = {
- id: node.props?.fileid as number || 0,
- source: generateRemoteUrl('dav' + rootPath + node.filename),
- // do not show the mtime column
- // mtime: new Date(node.lastmod),
- mime: node.mime as string,
- size: node.props?.size as number || 0,
- permissions,
- owner,
- root: rootPath,
- attributes: {
- ...node,
- ...node.props,
- // Override displayed name on the list
- displayName: node.props?.['trashbin-filename'],
- previewUrl,
- },
- }
-
- delete nodeData.attributes.props
-
- return node.type === 'file'
- ? new File(nodeData)
- : new Folder(nodeData)
+const resultToNode = (stat: FileStat): File | Folder => {
+ const node = davResultToNode(stat, rootPath)
+ node.attributes.previewUrl = generateUrl('/apps/files_trashbin/preview?fileId={fileid}&x=32&y=32', { fileid: node.fileid })
+ return node
}
export const getContents = async (path = '/'): Promise<ContentsWithRoot> => {
- // TODO: use only one request when webdav-client supports it
- // @see https://github.com/perry-mitchell/webdav-client/pull/334
- const rootResponse = await client.stat(path, {
- details: true,
- data,
- }) as ResponseDataDetailed<FileStat>
-
- const contentsResponse = await client.getDirectoryContents(path, {
+ const contentsResponse = await client.getDirectoryContents(`${rootPath}${path}`, {
details: true,
data,
+ includeSelf: true,
}) as ResponseDataDetailed<FileStat[]>
+ const contents = contentsResponse.data.map(resultToNode)
+ const [folder] = contents.splice(contents.findIndex((node) => node.path === path), 1)
+
return {
- folder: resultToNode(rootResponse.data) as Folder,
- contents: contentsResponse.data.map(resultToNode),
+ folder: folder as Folder,
+ contents,
}
}