Skip to content

Commit

Permalink
Merge pull request #1419 from nextcloud-libraries/revert-dav-changes
Browse files Browse the repository at this point in the history
fix!: Revert breaking changes in DAV endpoint handling
  • Loading branch information
susnux authored Aug 16, 2024
2 parents 1c61723 + a519cd5 commit 95d2f2e
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 23 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@

All notable changes to this project will be documented in this file.

## 5.3.6
[Full Changelog](https://github.com/nextcloud-libraries/nextcloud-dialogs/compare/v5.3.5...v5.3.6)

### Breaking

In v5.3.3 breaking changes were introduced as it dropped support for Nextcloud 28 and older on public shares.
This reverts the changes to the DAV handling, for Nextcloud 29+ please use the now released version v6.0.0+.

### Changed
* chore: Enhance docs about generic dialogs and export all related types [\#1380](https://github.com/nextcloud-libraries/nextcloud-dialogs/pull/1380) \([susnux](https://github.com/susnux)\)
* Migrate REUSE to TOML format [\#1386](https://github.com/nextcloud-libraries/nextcloud-dialogs/pull/1386) \([AndyScherzinger](https://github.com/AndyScherzinger)\)
* chore(deps): Bump @nextcloud/typings to 1.9.1
* chore(deps): Bump @nextcloud/sharing to 0.2.3
* chore(deps): Bump fast-xml-parser to 4.4.1
* chore(deps): Bump @nextcloud/files to 3.8.0
* chore(deps): Bump @vueuse/core to 10.11.1
* chore(deps): Bump webdav to 5.7.1
* chore(deps): Bump axios to 1.7.4

## 5.3.5
[Full Changelog](https://github.com/nextcloud-libraries/nextcloud-dialogs/compare/v5.3.4...v5.3.5)

Expand Down
6 changes: 1 addition & 5 deletions lib/composables/dav.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const waitRefLoaded = (isLoading: Ref<boolean>) => new Promise((resolve) => {
})

const TestComponent = defineComponent({
props: ['currentView', 'currentPath', 'isPublic'],
props: ['currentView', 'currentPath'],
setup(props) {
const dav = useDAVFiles(toRef(props, 'currentView'), toRef(props, 'currentPath'))
return {
Expand All @@ -60,7 +60,6 @@ describe('dav composable', () => {
propsData: {
currentView: 'files',
currentPath: '/',
isPublic: false,
},
})
// Loading is set to true
Expand All @@ -85,7 +84,6 @@ describe('dav composable', () => {
propsData: {
currentView: 'files',
currentPath: '/',
isPublic: false,
},
})

Expand All @@ -105,7 +103,6 @@ describe('dav composable', () => {
propsData: {
currentView: 'files',
currentPath: '/',
isPublic: false,
},
})

Expand Down Expand Up @@ -133,7 +130,6 @@ describe('dav composable', () => {
propsData: {
currentView: 'files',
currentPath: '/',
isPublic: false,
},
})

Expand Down
106 changes: 95 additions & 11 deletions lib/composables/dav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
*/
import type { ContentsWithRoot, Folder, Node } from '@nextcloud/files'
import type { ComputedRef, Ref } from 'vue'
import type { FileStat, ResponseDataDetailed, SearchResult } from 'webdav'

import { davGetClient, davRootPath, getFavoriteNodes } from '@nextcloud/files'
import { CancelablePromise } from 'cancelable-promise'
import { davGetClient, davGetDefaultPropfind, davGetRecentSearch, davRemoteURL, davResultToNode, davRootPath, getFavoriteNodes } from '@nextcloud/files'
import { generateRemoteUrl } from '@nextcloud/router'
import { isPublicShare } from '@nextcloud/sharing/public'
import { join } from 'node:path'
import { onMounted, ref, shallowRef, watch } from 'vue'
import { getFile, getNodes, getRecentNodes } from '../utils/dav'
import { computed, onMounted, ref, shallowRef, watch } from 'vue'
import { CancelablePromise } from 'cancelable-promise'

/**
* Handle file loading using WebDAV
Expand All @@ -22,10 +24,76 @@ export const useDAVFiles = function(
currentPath: Ref<string> | ComputedRef<string>,
) {

const isPublicEndpoint = isPublicShare()

const defaultRootPath = isPublicEndpoint ? '/' : davRootPath

const defaultRemoteUrl = computed(() => {
if (isPublicEndpoint) {
return generateRemoteUrl('webdav').replace('/remote.php', '/public.php')
}
return davRemoteURL
})

/**
* The WebDAV client
*/
const client = davGetClient()
const client = computed(() => {
if (isPublicEndpoint) {
const token = (document.getElementById('sharingToken')! as HTMLInputElement).value
const authorization = btoa(`${token}:null`)

return davGetClient(defaultRemoteUrl.value, {
Authorization: `Basic ${authorization}`,
})
}

return davGetClient()
})

const resultToNode = (result: FileStat) => davResultToNode(result, defaultRootPath, defaultRemoteUrl.value)

const getRecentNodes = (): CancelablePromise<Node[]> => {
const controller = new AbortController()
// unix timestamp in seconds, two weeks ago
const lastTwoWeek = Math.round(Date.now() / 1000) - (60 * 60 * 24 * 14)
return new CancelablePromise(async (resolve, reject, onCancel) => {
onCancel(() => controller.abort())
try {
const { data } = await client.value.search('/', {
signal: controller.signal,
details: true,
data: davGetRecentSearch(lastTwoWeek),
}) as ResponseDataDetailed<SearchResult>
const nodes = data.results.map(resultToNode)
resolve(nodes)
} catch (error) {
reject(error)
}
})
}

const getNodes = (): CancelablePromise<Node[]> => {
const controller = new AbortController()
return new CancelablePromise(async (resolve, reject, onCancel) => {
onCancel(() => controller.abort())
try {
const results = await client.value.getDirectoryContents(`${defaultRootPath}${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) {
nodes = nodes.filter((file) => file.path !== currentPath.value)
}
resolve(nodes)
} catch (error) {
reject(error)
}
})
}

/**
* All files in current view and path
Expand All @@ -51,17 +119,33 @@ export const useDAVFiles = function(
* Create a new directory in the current path
* The directory will be added to the current file list
* @param name Name of the new directory
* @return {Promise<Folder>} The created directory
* @return The created directory
*/
async function createDirectory(name: string): Promise<Folder> {
const path = join(currentPath.value, name)

await client.createDirectory(join(davRootPath, path))
const directory = await getFile(client, path) as Folder
await client.value.createDirectory(join(defaultRootPath, path))
const directory = await getFile(path) as Folder
files.value = [...files.value, directory]
return directory
}

/**
* Get information for one file
*
* @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

const { data } = await client.value.stat(`${rootPath}${path}`, {
details: true,
data: davGetDefaultPropfind(),
}) as ResponseDataDetailed<FileStat>
return resultToNode(data)
}

/**
* Force reload files using the DAV client
*/
Expand All @@ -72,11 +156,11 @@ export const useDAVFiles = function(
isLoading.value = true

if (currentView.value === 'favorites') {
promise.value = getFavoriteNodes(client, currentPath.value)
promise.value = getFavoriteNodes(client.value, currentPath.value, defaultRootPath)
} else if (currentView.value === 'recent') {
promise.value = getRecentNodes(client)
promise.value = getRecentNodes()
} else {
promise.value = getNodes(client, currentPath.value)
promise.value = getNodes()
}
const content = await promise.value
if ('folder' in content) {
Expand Down
4 changes: 2 additions & 2 deletions lib/composables/filesSettings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import { useFilesSettings } from './filesSettings'
const axios = vi.hoisted(() => ({
get: vi.fn(),
}))
const isPublic = vi.hoisted(() => ({ value: false }))
const nextcloudSharing = vi.hoisted(() => ({ isPublicShare: vi.fn(() => false) }))

vi.mock('@nextcloud/axios', () => ({ default: axios }))
vi.mock('./isPublic', () => ({ useIsPublic: () => ({ isPublic }) }))
vi.mock('@nextcloud/sharing/public', () => nextcloudSharing)

const TestComponent = defineComponent({
setup() {
Expand Down
2 changes: 1 addition & 1 deletion lib/toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export function showUndo(text: string, onUndo: (e: MouseEvent) => void, options?
// force 10 seconds of timeout
timeout: TOAST_UNDO_TIMEOUT,
// remove close button
close: false
close: false,
})

// Generate undo layout
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/dav.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ describe('DAV utils', () => {
expect(client.stat).toBeCalledWith(`${nextcloudFiles.davRootPath}/some/path/file.ext`, { details: true, data: 'propfind content' })
expect(nextcloudFiles.davResultToNode).toBeCalledWith({ path: `${nextcloudFiles.davRootPath}/some/path/file.ext` })
})
})
})
1 change: 1 addition & 0 deletions lib/utils/dialogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Vue, { toRaw } from 'vue'
* @param props Properties to pass to the dialog
* @param onClose Callback when the dialog is closed
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const spawnDialog = (dialog: Component | AsyncComponent, props: any, onClose: (...rest: unknown[]) => void = () => {}): Vue => {
const el = document.createElement('div')

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nextcloud/dialogs",
"version": "5.3.5",
"version": "5.3.6",
"description": "Nextcloud dialog helpers",
"types": "dist/index.d.ts",
"main": "dist/index.cjs",
Expand Down
3 changes: 3 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"lib": ["DOM", "ESNext"],
"outDir": "./dist",
"rootDir": "lib/",
"module": "ESNext",
"moduleResolution": "Bundler",
"target": "ESNext",
"sourceMap": true,
"plugins": [
{ "name": "typescript-plugin-css-modules" }
Expand Down

0 comments on commit 95d2f2e

Please sign in to comment.