Skip to content

Commit

Permalink
fix: resolving external URLs via file ID
Browse files Browse the repository at this point in the history
  • Loading branch information
JammingBen committed Oct 30, 2023
1 parent b850502 commit 86fbb74
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 104 deletions.
70 changes: 65 additions & 5 deletions packages/web-pkg/src/components/AppTemplates/AppWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,33 @@ import {
import { DateTime } from 'luxon'
import { useTask } from 'vue-concurrency'
import { useGettext } from 'vue3-gettext'
import { onBeforeRouteLeave } from 'vue-router'
import { onBeforeRouteLeave, useRouter } from 'vue-router'
import AppTopBar from '../AppTopBar.vue'
import ErrorScreen from './PartialViews/ErrorScreen.vue'
import LoadingScreen from './PartialViews/LoadingScreen.vue'
import {
UrlForResourceOptions,
queryItemAsString,
useAppDefaults,
useClientService,
useRoute,
useRouteParam,
useRouteQuery,
useStore
} from '../../composables'
import { Resource } from '@ownclouders/web-client'
import { DavPermission, DavProperty } from '@ownclouders/web-client/src/webdav/constants'
import { Action, ActionOptions } from '../../composables/actions/types'
import { isProjectSpaceResource } from '@ownclouders/web-client/src/helpers'
import {
isPersonalSpaceResource,
isProjectSpaceResource
} from '@ownclouders/web-client/src/helpers'
import { HttpError } from '@ownclouders/web-client/src/errors'
import { ModifierKey, Key, useKeyboardActions } from '../../composables/keyboardActions'
import { useAppMeta } from '../../composables/appDefaults/useAppMeta'
import { dirname } from 'path'
import { useGetResourceContext } from '../../composables'
export default defineComponent({
name: 'AppWrapper',
Expand Down Expand Up @@ -79,6 +88,10 @@ export default defineComponent({
setup(props) {
const { $gettext } = useGettext()
const store = useStore()
const router = useRouter()
const currentRoute = useRoute()
const clientService = useClientService()
const { getResourceContext } = useGetResourceContext()
const applicationName = ref('')
const resource: Ref<Resource> = ref()
Expand Down Expand Up @@ -116,16 +129,13 @@ export default defineComponent({
}
})
const clientService = useClientService()
const {
applicationConfig,
closeApp,
currentFileContext,
getFileContents,
getFileInfo,
getUrlForResource,
loadFolderForFileContext,
putFileContents,
replaceInvalidFileRoute,
revokeUrl
Expand All @@ -144,8 +154,58 @@ export default defineComponent({
})
})
const driveAliasAndItem = useRouteParam('driveAliasAndItem')
const fileIdQueryItem = useRouteQuery('fileId')
const fileId = computed(() => {
return queryItemAsString(unref(fileIdQueryItem))
})
const addMissingDriveAliasAndItem = async () => {
const id = unref(fileId)
const { space, path } = await getResourceContext(id)
const driveAliasAndItem = space.getDriveAliasAndItem({ path } as Resource)
if (isPersonalSpaceResource(space)) {
return router.push({
params: {
...unref(currentRoute).params,
driveAliasAndItem
},
query: {
fileId: id,
...(unref(currentRoute).query?.app && { app: unref(currentRoute).query?.app }),
contextRouteName: 'files-spaces-generic',
contextRouteParams: { driveAliasAndItem: dirname(driveAliasAndItem) } as any
}
})
}
return router.push({
params: {
...unref(currentRoute).params,
driveAliasAndItem
},
query: {
fileId: id,
shareId: space.shareId,
...(unref(currentRoute).query?.app && { app: unref(currentRoute).query?.app }),
contextRouteName: path === '/' ? 'files-shares-with-me' : 'files-spaces-generic',
contextRouteParams: {
driveAliasAndItem: dirname(driveAliasAndItem)
} as any,
contextRouteQuery: {
shareId: space.shareId
} as any
}
})
}
const loadFileTask = useTask(function* () {
try {
if (!unref(driveAliasAndItem)) {
yield addMissingDriveAliasAndItem()
}
resource.value = yield getFileInfo(currentFileContext, {
davProperties: [DavProperty.FileId, DavProperty.Permissions, DavProperty.Name]
})
Expand Down
1 change: 1 addition & 0 deletions packages/web-pkg/src/composables/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export * from './passwordPolicyService'
export * from './piniaStores'
export * from './portalTarget'
export * from './previewService'
export * from './resources'
export * from './router'
export * from './scrollTo'
export * from './search'
Expand Down
2 changes: 2 additions & 0 deletions packages/web-pkg/src/composables/resources/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './useGetResourceContext'
export * from './useLoadFileInfoById'
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
Resource,
SpaceResource,
buildShareSpaceResource,
isMountPointSpaceResource
} from '@ownclouders/web-client/src/helpers'
import { computed, unref } from 'vue'
import { useStore } from '../store'
import { useClientService } from '../clientService'
import { urlJoin } from '@ownclouders/web-client/src/utils'
import { useConfigurationManager } from '../configuration'
import { useLoadFileInfoById } from './useLoadFileInfoById'
import { useCapabilitySpacesEnabled } from '../capability'
import { useGetMatchingSpace } from '../spaces/useGetMatchingSpace'

export const useGetResourceContext = () => {
const store = useStore()
const clientService = useClientService()
const configurationManager = useConfigurationManager()
const { loadFileInfoByIdTask } = useLoadFileInfoById({ clientService })
const { getPersonalSpace } = useGetMatchingSpace()

const hasSpaces = useCapabilitySpacesEnabled(store)
const spaces = computed<SpaceResource[]>(() => store.getters['runtime/spaces/spaces'])

const getMatchingSpaceByFileId = (id: Resource['id']) => {
if (!unref(hasSpaces)) {
return getPersonalSpace()
}
return unref(spaces).find((space) => id.toString().startsWith(space.id.toString()))
}
const getMatchingMountPoint = (id: Resource['id']) => {
return unref(spaces).find(
(space) => isMountPointSpaceResource(space) && space.root?.remoteItem?.id === id
)
}

// get context for a resource when only having its id. be careful, this might be very expensive!
const getResourceContext = async (id: string) => {
let path: string
let resource: Resource
let space = getMatchingSpaceByFileId(id)

if (space) {
path = await clientService.webdav.getPathForFileId(id)
resource = await clientService.webdav.getFileInfo(space, { path })
return { space, resource, path }
}

// no matching space found => the file doesn't lie in own spaces => it's a share.
// do PROPFINDs on parents until root of accepted share is found in `mountpoint` spaces
await store.dispatch('runtime/spaces/loadMountPoints', {
graphClient: clientService.graphAuthenticated
})

let mountPoint = getMatchingMountPoint(id)
resource = await loadFileInfoByIdTask.perform(id)
const sharePathSegments = mountPoint ? [] : [unref(resource).name]
let tmpResource = unref(resource)

while (!mountPoint) {
tmpResource = await loadFileInfoByIdTask.perform(tmpResource.parentFolderId)
mountPoint = getMatchingMountPoint(tmpResource.id)
if (!mountPoint) {
sharePathSegments.unshift(tmpResource.name)
}
}

space = buildShareSpaceResource({
shareId: mountPoint.nodeId,
shareName: mountPoint.name,
serverUrl: configurationManager.serverUrl
})

path = urlJoin(...sharePathSegments)
return { space, resource, path }
}

return {
getResourceContext
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ClientService } from '@ownclouders/web-pkg'
import { useClientService } from '@ownclouders/web-pkg'
import { useTask } from 'vue-concurrency'
import { ClientService } from '../../services/client'
import { useClientService } from '../clientService'
import { buildSpace, buildWebDavSpacesPath } from '@ownclouders/web-client/src/helpers'
import { DavProperty, DavPropertyValue } from '@ownclouders/web-client/src/webdav/constants'

Expand Down
1 change: 0 additions & 1 deletion packages/web-runtime/src/composables/fileInfo/index.ts

This file was deleted.

Loading

0 comments on commit 86fbb74

Please sign in to comment.