Skip to content

Commit

Permalink
Merge pull request #1361 from nextcloud-libraries/refactor/public-shares
Browse files Browse the repository at this point in the history
refactor: `@nextcloud/files` is now aware of public shares also now have `@nextcloud/sharing`
  • Loading branch information
susnux authored Jun 21, 2024
2 parents 29ac805 + 6a3de02 commit 010044c
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 84 deletions.
10 changes: 2 additions & 8 deletions lib/components/FilePicker/FilePicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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<{
Expand Down Expand Up @@ -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)

/**
Expand Down Expand Up @@ -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 : '/',
)
Expand All @@ -236,7 +230,7 @@ const {
isLoading,
loadFiles,
createDirectory,
} = useDAVFiles(currentView, currentPath, isPublic)
} = useDAVFiles(currentView, currentPath)

onMounted(() => loadFiles())

Expand Down
12 changes: 6 additions & 6 deletions lib/composables/dav.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const waitRefLoaded = (isLoading: Ref<boolean>) => 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,
}
Expand Down Expand Up @@ -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 () => {
Expand All @@ -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` })
Expand All @@ -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()
Expand All @@ -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')

Expand Down
51 changes: 12 additions & 39 deletions lib/composables/dav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,28 @@ 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'

/**
* Handle file loading using WebDAV
*
* @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<string> | ComputedRef<string>,
isPublicEndpoint: Ref<boolean> | ComputedRef<boolean>,
) {

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<Node[]> => {
const controller = new AbortController()
Expand All @@ -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),
Expand All @@ -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<FileStat[]>
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)
Expand Down Expand Up @@ -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<Folder>} The created directory
*/
async function createDirectory(name: string): Promise<Folder> {
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
Expand All @@ -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<FileStat>
Expand All @@ -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 {
Expand Down
10 changes: 3 additions & 7 deletions lib/composables/filesSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ 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'

import axios from '@nextcloud/axios'

import { t } from '../utils/l10n'
import { showError } from '../toast'
import { useIsPublic } from './isPublic'

interface OCAFilesUserConfig {
show_hidden: boolean
Expand Down Expand Up @@ -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'))

Expand Down Expand Up @@ -97,10 +95,8 @@ export const useFilesViews = (currentView?: FileListViews|Ref<FileListViews>|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 = {
Expand Down
19 changes: 0 additions & 19 deletions lib/composables/isPublic.ts

This file was deleted.

28 changes: 24 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 010044c

Please sign in to comment.