Skip to content

Commit

Permalink
feat(public-links): retrieve capabilities early
Browse files Browse the repository at this point in the history
update the launch process to support late loaded data like capabilities which is needed for doing things like archiving on public links
  • Loading branch information
fschade committed Oct 21, 2021
1 parent 8537ec1 commit 51f246a
Show file tree
Hide file tree
Showing 21 changed files with 226 additions and 110 deletions.
7 changes: 7 additions & 0 deletions changelog/unreleased/enhancement-public-link-capabilities
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Enhancement: TODO

https://github.com/owncloud/web/pull/5924
https://github.com/owncloud/web/issues/5884
https://github.com/owncloud/ocis/issues/2479
https://github.com/owncloud/web/issues/2479
https://github.com/owncloud/web/issues/5901
42 changes: 14 additions & 28 deletions packages/web-app-external/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export default {
}),
computed: {
...mapGetters(['getToken', 'capabilities', 'configuration']),
...mapGetters('Files', ['publicLinkPassword']),
pageTitle() {
const translated = this.$gettext('"%{appName}" app page')
Expand All @@ -70,36 +71,21 @@ export default {
},
async created() {
this.loading = true
// TODO: Enable externalApp usage on public routes below
// initialize headers()
// if (this.isPublicRoute) {
// // send auth header here if public route
// // if password exists send it via basicauth public:password
// // headers.append('public-token', 'uUCPJghnVUspjxe')
// // const password = this.publicLinkPassword
// // if (password) {
// // headers.append( Authorization: 'Basic ' + Buffer.from('public:' + password).toString('base64') }
// // }
// } else {
// - check for token
// - abort if falsy
// - build headers as below
// }
if (!this.getToken) {
this.loading = false
this.loadingError = true
return
const publicLinkPassword = this.publicLinkPassword
const { 'public-token': publicToken } = this.$route.query
const token = this.getToken
const headers = {
'X-Requested-With': 'XMLHttpRequest',
...(publicToken && { 'public-token': publicToken }),
...(publicLinkPassword && {
Authorization:
'Basic ' + Buffer.from(['public', publicLinkPassword].join(':')).toString('base64')
}),
...(token && {
Authorization: 'Bearer ' + token
})
}
const headers = new Headers()
headers.append('Authorization', 'Bearer ' + this.getToken)
headers.append('X-Requested-With', 'XMLHttpRequest')
const configUrl = this.configuration.server
const appOpenUrl = this.capabilities.files.app_providers[0].open_url.replace('/app', 'app')
const url = configUrl + appOpenUrl + '?file_id=' + this.fileId + '&app_name=' + this.appName
Expand Down
7 changes: 4 additions & 3 deletions packages/web-app-external/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const routes = [
app: App
},
meta: {
title: $gettext('External app')
title: $gettext('External app'),
auth: false
}
}
]
Expand All @@ -30,7 +31,7 @@ export default {
routes,
store,
translations,
async ready({ store: runtimeStore }) {
await runtimeStore.dispatch('External/fetchMimeTypes')
userReady({ store }) {
store.dispatch('External/fetchMimeTypes')
}
}
11 changes: 8 additions & 3 deletions packages/web-app-external/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ const actions = {
throw new Error('Error fetching app provider MIME types')
}

const mimeTypes = await response.json()
commit('SET_MIME_TYPES', mimeTypes['mime-types'])
const { 'mime-types': mimeTypes } = await response.json()

commit('SET_MIME_TYPES', mimeTypes)
}
}

const getters = {
getMimeTypes: (state: typeof State): { [key: string]: string } => {
mimeTypes: (
state: typeof State
): {
[key: string]: string
} => {
return state.mimeTypes
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`The app provider extension should be able to load an iFrame via get 1`] = `
<main class="uk-height-viewport">
<main class="uk-height-viewport uk-flex uk-flex-center uk-flex-middle">
<h1 class="oc-invisible-sr">"exampleApp" app page</h1>
<!----> <iframe src="https://example.test/d12ab86/loe009157-MzBw" title="&quot;exampleApp&quot; app content area" class="uk-width-1-1 uk-height-viewport"></iframe>
<loadingscreen-stub></loadingscreen-stub>
<!---->
<!---->
</main>
`;

exports[`The app provider extension should be able to load an iFrame via post 1`] = `
<main class="uk-height-viewport">
<main class="uk-height-viewport uk-flex uk-flex-center uk-flex-middle">
<h1 class="oc-invisible-sr">"exampleApp" app page</h1>
<loadingscreen-stub></loadingscreen-stub>
<!---->
<!---->
<div>
<form action="https://example.test/d12ab86/loe009157-MzBw" target="app-iframe" method="post"><input type="submit" class="oc-hidden" value="[object Object]">
<div><input name="access_token" type="hidden" value="asdfsadfsadf"></div>
<div><input name="access_token_ttl" type="hidden" value="123456"></div>
</form> <iframe name="app-iframe" title="&quot;exampleApp&quot; app content area" class="uk-width-1-1 uk-height-viewport"></iframe>
</div>
</main>
`;

exports[`The app provider extension should fail for unauthenticated users 1`] = `
<main class="uk-height-viewport uk-flex uk-flex-center uk-flex-middle">
<h1 class="oc-invisible-sr">"exampleApp" app page</h1>
<errorscreen-stub></errorscreen-stub>
<loadingscreen-stub></loadingscreen-stub>
<!---->
<!---->
</main>
Expand All @@ -43,7 +39,7 @@ exports[`The app provider extension should show a loading spinner while loading
exports[`The app provider extension should show a meaningful message if an error occurs during loading 1`] = `
<main class="uk-height-viewport uk-flex uk-flex-center uk-flex-middle">
<h1 class="oc-invisible-sr">"exampleApp" app page</h1>
<errorscreen-stub></errorscreen-stub>
<loadingscreen-stub></loadingscreen-stub>
<!---->
<!---->
</main>
Expand Down
2 changes: 1 addition & 1 deletion packages/web-app-external/tests/unit/app.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const storeOptions = {
External: {
namespaced: true,
getters: {
getMimeTypes: jest.fn()
mimeTypes: jest.fn()
},
actions: {
fetchMimeTypes: jest.fn()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ import MixinRoutes from '../../../mixins/routes'
import MixinDeleteResources from '../../../mixins/deleteResources'
import { cloneStateObject } from '../../../helpers/store'
import { canBeMoved } from '../../../helpers/permissions'
import { checkRoute } from '../../../helpers/route'
import { checkRoute, isPublicFilesRoute } from '../../../helpers/route'
import { shareStatus } from '../../../helpers/shareStatus'
import { triggerShareAction } from '../../../helpers/share/triggerShareAction'
import PQueue from 'p-queue'
Expand Down Expand Up @@ -129,7 +129,7 @@ export default {
canDownloadSingleFile() {
if (
!checkRoute(['files-personal', 'files-favorites', 'files-public-list'], this.$route.name)
!checkRoute(['files-personal', 'files-public-list', 'files-favorites'], this.$route.name)
) {
return false
}
Expand All @@ -146,7 +146,9 @@ export default {
},
canDownloadAsArchive() {
if (!checkRoute(['files-personal', 'files-favorites'], this.$route.name)) {
if (
!checkRoute(['files-personal', 'files-public-list', 'files-favorites'], this.$route.name)
) {
return false
}
Expand Down Expand Up @@ -363,12 +365,16 @@ export default {
await this.downloadFile(this.selectedFiles[0])
return
}
await this.downloadAsArchive()
},
async downloadAsArchive() {
await triggerDownloadAsArchive({
fileIds: this.selectedFiles.map((r) => r.fileId)
fileIds: this.selectedFiles.map((r) => r.fileId),
...(isPublicFilesRoute(this.$route) && {
publicToken: this.$route.params.item.split('/')[0]
})
}).catch((e) => {
console.error(e)
this.showMessage({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,14 @@ export default {
required: true
}
},
data: () => ({
appList: []
}),
computed: {
...mapGetters('Files', ['currentFolder']),
appList() {
return this.$_fileActions_loadApps(this.item)
},
showExternalApps() {
return this.item.extension && this.appList?.length > 0
},
Expand Down Expand Up @@ -186,13 +187,7 @@ export default {
return [...this.$_showDetails_items].filter((item) => item.isEnabled(this.filterParams))
}
},
mounted() {
this.loadApps()
},
methods: {
loadApps() {
this.appList = this.$_fileActions_loadApps(this.item)
},
getComponentProps(action, resource) {
if (action.componentType === 'router-link' && action.route) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ export default {
return $gettext('Actions')
},
mixins: [FileActions],
data: () => ({
appList: []
}),
computed: {
...mapGetters('Files', ['highlightedFile', 'currentFolder']),
appList() {
return this.$_fileActions_loadApps(this.highlightedFile) || []
},
actions() {
const actions = this.$_fileActions_editorActions.concat(this.$_fileActions_systemActions)
Expand All @@ -60,13 +61,7 @@ export default {
)
}
},
mounted() {
this.loadApps()
},
methods: {
loadApps() {
this.appList = this.$_fileActions_loadApps(this.highlightedFile)
},
getComponentProps(action, highlightedFile) {
if (action.componentType === 'router-link' && action.route) {
return {
Expand Down
21 changes: 17 additions & 4 deletions packages/web-app-files/src/helpers/download/downloadAsArchive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,45 @@ import {
ClientService,
clientService as defaultClientService
} from '../../services'

import { major } from 'semver'
import { RuntimeError } from 'web-runtime/src/container/error'

interface TriggerDownloadAsArchiveOptions {
fileIds: string[]
archiverService?: ArchiverService
clientService?: ClientService
publicToken?: ClientService
}

export const triggerDownloadAsArchive = async (
options: TriggerDownloadAsArchiveOptions
): Promise<void> => {
const archiverService = options.archiverService || defaultArchiverService
const clientService = options.clientService || defaultClientService

if (!isDownloadAsArchiveAvailable(archiverService)) {
throw new RuntimeError('no archiver capability available')
}

if (options.fileIds.length === 0) {
throw new RuntimeError('requested archive with empty list of resources')
}

const majorVersion = major(archiverService.capability.version)
if (majorVersion === 2) {
const queryParams = [...options.fileIds.map((id) => `id=${id}`)]
const archiverUrl = archiverService.url + '?' + queryParams.join('&')
window.location = await clientService.owncloudSdk.signUrl(archiverUrl)
if (majorVersion !== 2) {
return
}

const queryParams = [
...options.fileIds.map((id) => `id=${id}`),
options.publicToken ? `public-token=${options.publicToken}` : ''
].filter(Boolean)
const archiverUrl = archiverService.url + '?' + queryParams.join('&')

window.location = options.publicToken
? (archiverUrl as any)
: await clientService.owncloudSdk.signUrl(archiverUrl)
}

export const isDownloadAsArchiveAvailable = (
Expand Down
14 changes: 7 additions & 7 deletions packages/web-app-files/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,19 +90,19 @@ export default {
navItems,
quickActions,
translations,
ready({ router: runtimeRouter, store: runtimeStore }) {
Registry.filterSearch = new FilterSearch(runtimeStore, runtimeRouter)
Registry.sdkSearch = new SDKSearch(runtimeStore, runtimeRouter)
ready({ router, store }) {
Registry.filterSearch = new FilterSearch(store, router)
Registry.sdkSearch = new SDKSearch(store, router)

// when discussing the boot process of applications we need to implement a
// registry that does not rely on call order, aka first register "on" and only after emit.
bus.publish('app.search.register.provider', Registry.filterSearch)
bus.publish('app.search.register.provider', Registry.sdkSearch)

// initialize services
},
userReady({ store }) {
archiverService.initialize(
runtimeStore.getters.configuration.server,
get(runtimeStore, 'getters.capabilities.files.archivers', [])
store.getters.configuration.server || window.location.origin,
get(store, 'getters.capabilities.files.archivers', [])
)
}
}
15 changes: 12 additions & 3 deletions packages/web-app-files/src/mixins/actions/downloadFolder.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isFavoritesRoute, isPersonalRoute } from '../../helpers/route'
import { checkRoute, isPublicFilesRoute } from '../../helpers/route'

import {
isDownloadAsArchiveAvailable,
triggerDownloadAsArchive
Expand All @@ -15,7 +16,12 @@ export default {
return this.$gettext('Download folder')
},
isEnabled: ({ resource }) => {
if (!isPersonalRoute(this.$route) && !isFavoritesRoute(this.$route)) {
if (
!checkRoute(
['files-personal', 'files-public-list', 'files-favorites'],
this.$route.name
)
) {
return false
}
if (!resource.isFolder) {
Expand All @@ -36,7 +42,10 @@ export default {
methods: {
async $_downloadFolder_trigger(resource) {
await triggerDownloadAsArchive({
fileIds: [resource.fileId]
fileIds: [resource.fileId],
...(isPublicFilesRoute(this.$route) && {
publicToken: this.$route.params.item.split('/')[0]
})
}).catch((e) => {
console.error(e)
this.showMessage({
Expand Down
Loading

0 comments on commit 51f246a

Please sign in to comment.