diff --git a/lib/components/FilePicker/FilePicker.vue b/lib/components/FilePicker/FilePicker.vue index 3ca48dc8..1ed104fa 100644 --- a/lib/components/FilePicker/FilePicker.vue +++ b/lib/components/FilePicker/FilePicker.vue @@ -77,7 +77,6 @@ import { showError } from '../../toast' import { useDAVFiles } from '../../composables/dav' import { useMimeFilter } from '../../composables/mime' import { useFilesSettings } from '../../composables/filesSettings' -import { useIsPublic } from '../../composables/isPublic' import { t } from '../../utils/l10n' const props = withDefaults(defineProps<{ @@ -142,11 +141,6 @@ const emit = defineEmits<{ (e: 'close', v?: Node[]): void }>() -/** - * Whether we are on a public endpoint (e.g. public share) - */ -const { isPublic } = useIsPublic() - const isOpen = ref(true) /** @@ -218,7 +212,7 @@ watch([navigatedPath], () => { /** * The current path that should be picked from */ -const currentPath = computed(() => +const currentPath = computed(() => // Only use the path for the files view as favorites and recent only works on the root currentView.value === 'files' ? navigatedPath.value || props.path || savedPath.value : '/', ) @@ -236,7 +230,7 @@ const { isLoading, loadFiles, createDirectory, -} = useDAVFiles(currentView, currentPath, isPublic) +} = useDAVFiles(currentView, currentPath) onMounted(() => loadFiles()) diff --git a/lib/composables/dav.spec.ts b/lib/composables/dav.spec.ts index 2a592aaf..6e24e482 100644 --- a/lib/composables/dav.spec.ts +++ b/lib/composables/dav.spec.ts @@ -39,7 +39,7 @@ const waitRefLoaded = (isLoading: Ref) => new Promise((resolve) => { const TestComponent = defineComponent({ props: ['currentView', 'currentPath', 'isPublic'], setup(props) { - const dav = useDAVFiles(toRef(props, 'currentView'), toRef(props, 'currentPath'), toRef(props, 'isPublic')) + const dav = useDAVFiles(toRef(props, 'currentView'), toRef(props, 'currentPath')) return { ...dav, } @@ -161,13 +161,13 @@ describe('dav composable', () => { nextcloudFiles.davGetClient.mockImplementation(() => client) nextcloudFiles.davResultToNode.mockImplementation((v) => v) - const { getFile } = useDAVFiles(ref('files'), ref('/'), ref(false)) + const { getFile } = useDAVFiles(ref('files'), ref('/')) const node = await getFile('/some/path/file.ext') expect(node).toEqual({ path: `${nextcloudFiles.davRootPath}/some/path/file.ext` }) // Check mock usage expect(client.stat).toBeCalledWith(`${nextcloudFiles.davRootPath}/some/path/file.ext`, { details: true }) - expect(nextcloudFiles.davResultToNode).toBeCalledWith({ path: `${nextcloudFiles.davRootPath}/some/path/file.ext` }, nextcloudFiles.davRootPath, nextcloudFiles.davRemoteURL) + expect(nextcloudFiles.davResultToNode).toBeCalledWith({ path: `${nextcloudFiles.davRootPath}/some/path/file.ext` }) }) it('createDirectory works', async () => { @@ -178,7 +178,7 @@ describe('dav composable', () => { nextcloudFiles.davGetClient.mockImplementation(() => client) nextcloudFiles.davResultToNode.mockImplementation((v) => v) - const { createDirectory } = useDAVFiles(ref('files'), ref('/foo/'), ref(false)) + const { createDirectory } = useDAVFiles(ref('files'), ref('/foo/')) const node = await createDirectory('my-name') expect(node).toEqual({ path: `${nextcloudFiles.davRootPath}/foo/my-name` }) @@ -197,7 +197,7 @@ describe('dav composable', () => { const view = ref<'files' | 'recent' | 'favorites'>('files') const path = ref('/') - const { loadFiles, isLoading } = useDAVFiles(view, path, ref(false)) + const { loadFiles, isLoading } = useDAVFiles(view, path) expect(isLoading.value).toBe(true) await loadFiles() @@ -224,7 +224,7 @@ describe('dav composable', () => { const view = ref<'files' | 'recent' | 'favorites'>('files') const path = ref('/') - const { loadFiles, isLoading } = useDAVFiles(view, path, ref(false)) + const { loadFiles, isLoading } = useDAVFiles(view, path) const abort = vi.spyOn(AbortController.prototype, 'abort') diff --git a/lib/composables/dav.ts b/lib/composables/dav.ts index 94ea6626..159c20f5 100644 --- a/lib/composables/dav.ts +++ b/lib/composables/dav.ts @@ -6,10 +6,9 @@ import type { Folder, Node } from '@nextcloud/files' import type { ComputedRef, Ref } from 'vue' import type { FileStat, ResponseDataDetailed, SearchResult } from 'webdav' -import { davGetClient, davGetDefaultPropfind, davGetRecentSearch, davRemoteURL, davResultToNode, davRootPath, getFavoriteNodes } from '@nextcloud/files' -import { generateRemoteUrl } from '@nextcloud/router' +import { davGetClient, davGetDefaultPropfind, davGetRecentSearch, davResultToNode, davRootPath, getFavoriteNodes } from '@nextcloud/files' import { join } from 'path' -import { computed, onMounted, ref, shallowRef, watch } from 'vue' +import { onMounted, ref, shallowRef, watch } from 'vue' import { CancelablePromise } from 'cancelable-promise' /** @@ -17,40 +16,18 @@ import { CancelablePromise } from 'cancelable-promise' * * @param currentView Reference to the current files view * @param currentPath Reference to the current files path - * @param isPublicEndpoint True if the public `public.php` WebDAV endpoint should be used instead of `remote.php` */ export const useDAVFiles = function( currentView: Ref<'files'|'recent'|'favorites'> | ComputedRef<'files'|'recent'|'favorites'>, currentPath: Ref | ComputedRef, - isPublicEndpoint: Ref | ComputedRef, ) { - const defaultRootPath = computed(() => isPublicEndpoint.value ? '/' : davRootPath) - - const defaultRemoteUrl = computed(() => { - if (isPublicEndpoint.value) { - return generateRemoteUrl('webdav').replace('/remote.php', '/public.php') - } - return davRemoteURL - }) - /** * The WebDAV client */ - const client = computed(() => { - if (isPublicEndpoint.value) { - const token = (document.getElementById('sharingToken')! as HTMLInputElement).value - const authorization = btoa(`${token}:null`) - - return davGetClient(defaultRemoteUrl.value, { - Authorization: `Basic ${authorization}`, - }) - } - - return davGetClient() - }) + const client = davGetClient() - const resultToNode = (result: FileStat) => davResultToNode(result, defaultRootPath.value, defaultRemoteUrl.value) + const resultToNode = (result: FileStat) => davResultToNode(result) const getRecentNodes = (): CancelablePromise => { const controller = new AbortController() @@ -59,7 +36,7 @@ export const useDAVFiles = function( return new CancelablePromise(async (resolve, reject, onCancel) => { onCancel(() => controller.abort()) try { - const { data } = await client.value.search('/', { + const { data } = await client.search('/', { signal: controller.signal, details: true, data: davGetRecentSearch(lastTwoWeek), @@ -77,16 +54,14 @@ export const useDAVFiles = function( return new CancelablePromise(async (resolve, reject, onCancel) => { onCancel(() => controller.abort()) try { - const results = await client.value.getDirectoryContents(`${defaultRootPath.value}${currentPath.value}`, { + const results = await client.getDirectoryContents(join(davRootPath, currentPath.value), { signal: controller.signal, details: true, data: davGetDefaultPropfind(), }) as ResponseDataDetailed let nodes = results.data.map(resultToNode) // Hack for the public endpoint which always returns folder itself - if (isPublicEndpoint.value) { - nodes = nodes.filter((file) => file.path !== currentPath.value) - } + nodes = nodes.filter((file) => file.path !== currentPath.value) resolve(nodes) } catch (error) { reject(error) @@ -120,12 +95,12 @@ export const useDAVFiles = function( /** * Create a new directory in the current path * @param name Name of the new directory - * @return The created directory + * @return {Promise} The created directory */ async function createDirectory(name: string): Promise { const path = join(currentPath.value, name) - await client.value.createDirectory(join(defaultRootPath.value, path)) + await client.createDirectory(join(davRootPath, path)) const directory = await getFile(path) as Folder files.value = [...files.value, directory] return directory @@ -137,10 +112,8 @@ export const useDAVFiles = function( * @param path The path of the file or folder * @param rootPath DAV root path, defaults to '/files/USERID' */ - async function getFile(path: string, rootPath: string|undefined = undefined) { - rootPath = rootPath ?? defaultRootPath.value - - const { data } = await client.value.stat(`${rootPath}${path}`, { + async function getFile(path: string, rootPath: string = davRootPath) { + const { data } = await client.stat(join(rootPath, path), { details: true, data: davGetDefaultPropfind(), }) as ResponseDataDetailed @@ -157,7 +130,7 @@ export const useDAVFiles = function( isLoading.value = true if (currentView.value === 'favorites') { - promise.value = getFavoriteNodes(client.value, currentPath.value, defaultRootPath.value) + promise.value = getFavoriteNodes(client, currentPath.value) } else if (currentView.value === 'recent') { promise.value = getRecentNodes() } else { diff --git a/lib/composables/filesSettings.ts b/lib/composables/filesSettings.ts index c8c46f87..d1af64f9 100644 --- a/lib/composables/filesSettings.ts +++ b/lib/composables/filesSettings.ts @@ -7,6 +7,7 @@ import type { ComputedRef, Ref } from 'vue' import { loadState } from '@nextcloud/initial-state' import { generateUrl } from '@nextcloud/router' +import { isPublicShare } from '@nextcloud/sharing/public' import { toValue } from '@vueuse/core' import { computed, onMounted, ref } from 'vue' @@ -14,7 +15,6 @@ import axios from '@nextcloud/axios' import { t } from '../utils/l10n' import { showError } from '../toast' -import { useIsPublic } from './isPublic' interface OCAFilesUserConfig { show_hidden: boolean @@ -49,10 +49,8 @@ export const useFilesSettings = () => { const sortFavoritesFirst = ref(filesUserState?.sort_favorites_first ?? true) const cropImagePreviews = ref(filesUserState?.crop_image_previews ?? true) - const { isPublic } = useIsPublic() - onMounted(async () => { - if (!isPublic.value) { + if (!isPublicShare()) { try { const { data } = await axios.get(generateUrl('/apps/files/api/v1/configs')) @@ -97,10 +95,8 @@ export const useFilesViews = (currentView?: FileListViews|Ref|Com order: convertOrder(filesViewsState?.favorites?.sorting_direction ?? 'asc'), }) - const { isPublic } = useIsPublic() - onMounted(async () => { - if (!isPublic.value) { + if (!isPublicShare()) { try { const { data } = await axios.get(generateUrl('/apps/files/api/v1/views')) filesViewConfig.value = { diff --git a/lib/composables/isPublic.ts b/lib/composables/isPublic.ts deleted file mode 100644 index ec89e8b8..00000000 --- a/lib/composables/isPublic.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -import { onBeforeMount, ref } from "vue" - -/** - * Check whether the component is mounted in a public share - */ -export const useIsPublic = () => { - const checkIsPublic = () => (document.getElementById('isPublic') as HTMLInputElement|null)?.value === '1' - - const isPublic = ref(true) - onBeforeMount(() => { isPublic.value = checkIsPublic() }) - - return { - isPublic, - } -} diff --git a/package-lock.json b/package-lock.json index 33717b21..b648d8c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,11 @@ "@nextcloud/auth": "^2.3.0", "@nextcloud/axios": "^2.5.0", "@nextcloud/event-bus": "^3.3.1", - "@nextcloud/files": "^3.4.1", + "@nextcloud/files": "^3.5.1", "@nextcloud/initial-state": "^2.2.0", "@nextcloud/l10n": "^3.1.0", "@nextcloud/router": "^3.0.1", + "@nextcloud/sharing": "^0.2.2", "@nextcloud/typings": "^1.8.0", "@types/toastify-js": "^1.12.3", "@vueuse/core": "^10.11.0", @@ -1491,17 +1492,19 @@ } }, "node_modules/@nextcloud/files": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.4.1.tgz", - "integrity": "sha512-0riNodzy/epHRpjIR0uuibzu+WO5GEjYqc9HjifR7fGb4Umi7cNykX7vR5o6BoEg64eYEHau0I4wBUtX/vRssQ==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.5.1.tgz", + "integrity": "sha512-GkVWUgkBSVt27Carmp/DbnDiqHq03w3VQWt8xszacp/IQSB9G+8/KCvi8zxldac2q7lQ8NpHlB/Bqy8o+OOc0A==", "dependencies": { "@nextcloud/auth": "^2.3.0", "@nextcloud/l10n": "^3.1.0", "@nextcloud/logger": "^3.0.2", "@nextcloud/paths": "^2.1.0", "@nextcloud/router": "^3.0.1", + "@nextcloud/sharing": "^0.2.1", "cancelable-promise": "^4.3.1", "is-svg": "^5.0.1", + "typescript-event-target": "^1.1.1", "webdav": "^5.6.0" }, "engines": { @@ -1582,6 +1585,18 @@ "npm": "^10.0.0" } }, + "node_modules/@nextcloud/sharing": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@nextcloud/sharing/-/sharing-0.2.2.tgz", + "integrity": "sha512-ui0ZoVazroA+cF4+homhFSFAddd/P4uRYMfG3rw3QR8o6igrVFe0f0l21kYtUwXU0oC0K4v3k8j93zCTfz6v3g==", + "dependencies": { + "@nextcloud/initial-state": "^2.2.0" + }, + "engines": { + "node": "^20.0.0", + "npm": "^10.0.0" + } + }, "node_modules/@nextcloud/stylelint-config": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@nextcloud/stylelint-config/-/stylelint-config-2.4.0.tgz", @@ -12576,6 +12591,11 @@ "node": ">=14.17" } }, + "node_modules/typescript-event-target": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/typescript-event-target/-/typescript-event-target-1.1.1.tgz", + "integrity": "sha512-dFSOFBKV6uwaloBCCUhxlD3Pr/P1a/tJdcmPrTXCHlEFD3faj0mztjcGn6VBAhQ0/Bdy8K3VWrrqwbt/ffsYsg==" + }, "node_modules/typescript-plugin-css-modules": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-5.1.0.tgz", diff --git a/package.json b/package.json index eb77591d..c73c768c 100644 --- a/package.json +++ b/package.json @@ -58,10 +58,11 @@ "@nextcloud/auth": "^2.3.0", "@nextcloud/axios": "^2.5.0", "@nextcloud/event-bus": "^3.3.1", - "@nextcloud/files": "^3.4.1", + "@nextcloud/files": "^3.5.1", "@nextcloud/initial-state": "^2.2.0", "@nextcloud/l10n": "^3.1.0", "@nextcloud/router": "^3.0.1", + "@nextcloud/sharing": "^0.2.2", "@nextcloud/typings": "^1.8.0", "@types/toastify-js": "^1.12.3", "@vueuse/core": "^10.11.0",