Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: resolving external URLs via file ID #9833

Merged
merged 4 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog/unreleased/bugfix-external-url-resolving
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Bugfix: Resolving external URLs

Resolving external URLs when only the file ID is given has been fixed.

https://github.com/owncloud/web/issues/9804
https://github.com/owncloud/web/pull/9833
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{{ title }}
</div>
</div>
<oc-button appearance="raw" @click="close" :aria-label="$gettext('Close')"
<oc-button appearance="raw" :aria-label="$gettext('Close')" @click="close"
><oc-icon name="close"
/></oc-button>
</div>
Expand Down
109 changes: 107 additions & 2 deletions packages/web-app-external/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,26 @@ import { mapGetters } from 'vuex'
import { computed, defineComponent, unref } from 'vue'
import { urlJoin } from 'web-client/src/utils'
import AppTopBar from 'web-pkg/src/components/AppTopBar.vue'
import { queryItemAsString, useAppDefaults, useRouteQuery } from 'web-pkg/src/composables'
import {
queryItemAsString,
useAppDefaults,
useClientService,
useRoute,
useRouteParam,
useRouteQuery,
useRouter,
useStore
} from 'web-pkg/src/composables'
import { configurationManager } from 'web-pkg/src/configuration'
import ErrorScreen from './components/ErrorScreen.vue'
import LoadingScreen from './components/LoadingScreen.vue'
import {
Resource,
SpaceResource,
buildShareSpaceResource,
isMountPointSpaceResource
} from 'web-client/src/helpers'
import { useLoadFileInfoById } from 'web-pkg/src/composables/fileInfo'

export default defineComponent({
name: 'ExternalApp',
Expand All @@ -52,14 +68,99 @@ export default defineComponent({
LoadingScreen
},
setup() {
const store = useStore()
const router = useRouter()
const currentRoute = useRoute()
const clientService = useClientService()
const { loadFileInfoByIdTask } = useLoadFileInfoById({ clientService })
const appName = useRouteQuery('app')
const applicationName = computed(() => queryItemAsString(unref(appName)))

const fileIdQueryItem = useRouteQuery('fileId')
const fileId = computed(() => {
return queryItemAsString(unref(fileIdQueryItem))
})

const driveAliasAndItem = useRouteParam('driveAliasAndItem')

const getMatchingSpace = (id): SpaceResource => {
JammingBen marked this conversation as resolved.
Show resolved Hide resolved
return store.getters['runtime/spaces/spaces'].find((space) => id.startsWith(space.id))
}
const findMatchingMountPoint = (id: string | number): SpaceResource => {
JammingBen marked this conversation as resolved.
Show resolved Hide resolved
return store.getters['runtime/spaces/spaces'].find(
(space) => isMountPointSpaceResource(space) && space.root?.remoteItem?.id === id
)
}

const addMissingDriveAliasAndItem = async () => {
const id = unref(fileId)
let path: string
let matchingSpace = getMatchingSpace(id)
if (matchingSpace) {
path = await clientService.owncloudSdk.files.getPathForFileId(id)
const driveAliasAndItem = matchingSpace.getDriveAliasAndItem({ path } as Resource)
console.log(unref(currentRoute).query)
JammingBen marked this conversation as resolved.
Show resolved Hide resolved
return router.push({
params: {
...unref(currentRoute).params,
driveAliasAndItem
},
query: {
...(unref(currentRoute).query?.app && { app: unref(currentRoute).query?.app }),
contextRouteName: 'files-spaces-generic'
}
})
}

// 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 = findMatchingMountPoint(id)
const resource = await loadFileInfoByIdTask.perform(id)
const sharePathSegments = mountPoint ? [] : [unref(resource).name]
let tmpResource = unref(resource)
while (!mountPoint) {
try {
tmpResource = await loadFileInfoByIdTask.perform(tmpResource.parentFolderId)
} catch (e) {
throw Error(e)
}
mountPoint = findMatchingMountPoint(tmpResource.id)
if (!mountPoint) {
sharePathSegments.unshift(tmpResource.name)
}
}
matchingSpace = buildShareSpaceResource({
shareId: mountPoint.nodeId,
shareName: mountPoint.name,
serverUrl: configurationManager.serverUrl
})
path = urlJoin(...sharePathSegments)

const driveAliasAndItem = matchingSpace.getDriveAliasAndItem({ path } as Resource)
return router.push({
params: {
...unref(currentRoute).params,
driveAliasAndItem
},
query: {
shareId: matchingSpace.shareId,
...(unref(currentRoute).query?.app && { app: unref(currentRoute).query?.app }),
contextRouteName: 'files-shares-with-me'
}
})
}

return {
...useAppDefaults({
applicationId: 'external',
applicationName
}),
applicationName
applicationName,
driveAliasAndItem,
addMissingDriveAliasAndItem
}
},

Expand Down Expand Up @@ -94,6 +195,10 @@ export default defineComponent({
async created() {
this.loading = true
try {
if (!this.driveAliasAndItem) {
await this.addMissingDriveAliasAndItem()
}

this.resource = await this.getFileInfo(this.currentFileContext, {
davProperties: []
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
>
<oc-icon name="calendar-event" fill-type="line" size="medium" variation="passive" />
<span
class="oc-ml-s"
v-if="isExpirationDateSet"
class="oc-ml-s"
v-text="$gettext('Edit expiration date')"
/>
<span v-else v-text="$gettext('Set expiration date')" />
Expand Down
1 change: 1 addition & 0 deletions packages/web-pkg/src/composables/fileInfo/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useLoadFileInfoById'
38 changes: 38 additions & 0 deletions packages/web-pkg/src/composables/fileInfo/useLoadFileInfoById.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ClientService } from 'web-pkg/src/services'
import { useClientService } from 'web-pkg/src/composables'
import { useTask } from 'vue-concurrency'
import { buildSpace, buildWebDavSpacesPath } from 'web-client/src/helpers'
import { DavProperty } from 'web-client/src/webdav/constants'

export interface LoadFileInfoByIdOptions {
clientService?: ClientService
davProperties?: DavProperty[]
}

export const useLoadFileInfoById = (options: LoadFileInfoByIdOptions) => {
const { webdav } = options.clientService || useClientService()
const davProperties = options.davProperties || [
DavProperty.FileId,
DavProperty.FileParent,
DavProperty.Name,
DavProperty.ResourceType
]

const loadFileInfoByIdTask = useTask(function* (signal, fileId: string | number) {
const space = buildSpace({
id: fileId,
webDavPath: buildWebDavSpacesPath(fileId)
})
return yield webdav.getFileInfo(
space,
{},
{
davProperties
}
)
})

return {
loadFileInfoByIdTask
}
}