Skip to content

Commit

Permalink
Initial implementation of archiver v2
Browse files Browse the repository at this point in the history
  • Loading branch information
kulmann committed Sep 23, 2021
1 parent a19dec3 commit bde5577
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/web-app-files/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"lodash-es": "^4.17.21",
"p-queue": "^6.6.2",
"query-string": "^6.8.3",
"semver": "^7.3.5",
"vue": "^2.6.10",
"vue-gettext": "^2.1.5",
"vue2-dropzone": "^3.6.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@
</oc-button>
</template>
<oc-grid v-if="displayBulkActions" gutter="small">
<div v-if="canDownloadAsArchive">
<oc-button
id="download-selected-btn"
key="download-selected-btn"
variation="primary"
@click="downloadAsArchive"
>
<oc-icon name="archive" />
<translate>Download</translate>
</oc-button>
</div>
<div v-if="canCopy">
<oc-button
id="copy-selected-btn"
Expand Down Expand Up @@ -94,20 +105,36 @@ import { checkRoute } from '../../../helpers/route'
import { shareStatus } from '../../../helpers/shareStatus'
import { triggerShareAction } from '../../../helpers/share/triggerShareAction'
import PQueue from 'p-queue'
import { archiverService } from '../../../services'
import { triggerDownloadAsArchive } from '../../../helpers/download/downloadAsArchive'
export default {
mixins: [MixinRoutes, MixinDeleteResources],
computed: {
...mapGetters('Files', ['selectedFiles', 'currentFolder', 'activeFilesCurrentPage']),
...mapGetters(['homeFolder']),
...mapGetters(['homeFolder', 'getToken']),
emptyTrashbinButtonText() {
return this.selectedFiles.length < 1
? this.$gettext('Empty trash bin')
: this.$gettext('Delete')
},
canDownloadAsArchive() {
if (
!checkRoute(['files-personal', 'files-public-list', 'files-favorites'], this.$route.name)
) {
return false
}
if (!archiverService.available) {
return false
}
return this.currentFolder.canDownload()
},
canMove() {
if (
!checkRoute(['files-personal', 'files-public-list', 'files-favorites'], this.$route.name)
Expand Down Expand Up @@ -307,6 +334,24 @@ export default {
status: 'danger'
})
}
},
async downloadAsArchive() {
await triggerDownloadAsArchive({
folderPath: this.currentFolder.path,
fileNames: this.selectedFiles.map(f => f.name),
token: this.getToken
}).catch(e => {
console.error(e)
this.showMessage({
title: this.$ngettext(
'Error downloading the selected file.',
'Error downloading the selected files.',
this.selectedFiles.length
),
status: 'danger'
})
})
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import CreatePublicLink from '../../mixins/actions/createPublicLink'
import DeclineShare from '../../mixins/actions/declineShare'
import Delete from '../../mixins/actions/delete'
import Download from '../../mixins/actions/download'
import DownloadFolder from '../../mixins/actions/downloadFolder'
import Favorite from '../../mixins/actions/favorite'
import Move from '../../mixins/actions/move'
import Navigate from '../../mixins/actions/navigate'
Expand All @@ -55,6 +56,7 @@ export default {
DeclineShare,
Delete,
Download,
DownloadFolder,
Favorite,
Move,
Navigate,
Expand Down Expand Up @@ -124,6 +126,7 @@ export default {
menuItems.push(
...[
...this.$_download_items,
...this.$_downloadFolder_items,
...this.$_createPublicLink_items,
...this.$_showShares_items,
...this.$_favorite_items.map(action => {
Expand Down
31 changes: 31 additions & 0 deletions packages/web-app-files/src/helpers/download/downloadAsArchive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { archiverService, clientService } from '../../services'
import { AxiosResponse } from 'axios'
import { major } from 'semver'

interface TriggerDownloadAsArchiveOptions {
folderPath: string
fileNames?: string[]
token: string // TODO: solve download from a) public link b) public link with password
}

export const triggerDownloadAsArchive = async (
options: TriggerDownloadAsArchiveOptions
): Promise<AxiosResponse> => {
if (!isDownloadAsArchiveAvailable()) {
return
}
const majorVersion = major(archiverService.capability.version)
switch (majorVersion) {
case 2: {
const queryParams = ['dir=' + options.folderPath, ...options.fileNames.map(f => 'file=' + f)]
const url = archiverService.url + '?' + queryParams.join('&')
return await clientService.httpAuthenticated(options.token).get(url)
}
default:
// do nothing for unknown versions
}
}

export const isDownloadAsArchiveAvailable = (): boolean => {
return archiverService.available
}
7 changes: 7 additions & 0 deletions packages/web-app-files/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { bus } from 'web-pkg/src/instance'
import { Registry } from './services'
import fileSideBars from './fileSideBars'
import routes from './routes'
import { archiverService } from './services/archiver'

// just a dummy function to trick gettext tools
function $gettext(msg) {
Expand Down Expand Up @@ -131,5 +132,11 @@ export default {
// registry that does not rely on call order, aka first register "on" and only after emit.
bus.emit('app.search.register.provider', Registry.filterSearch)
bus.emit('app.search.register.provider', Registry.sdkSearch)

// initialize services
archiverService.initialize(
store.getters.configuration.server,
store.getters.capabilities.files.archivers
)
}
}
51 changes: 51 additions & 0 deletions packages/web-app-files/src/mixins/actions/downloadFolder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { isFavoritesRoute, isPersonalRoute, isPublicFilesRoute } from '../../helpers/route'
import {
isDownloadAsArchiveAvailable,
triggerDownloadAsArchive
} from '../../helpers/download/downloadAsArchive'
import { mapGetters } from 'vuex'

export default {
computed: {
...mapGetters(['getToken']),
$_downloadFolder_items() {
return [
{
icon: 'archive',
handler: this.$_downloadFolder_trigger,
label: () => {
return this.$gettext('Download folder')
},
isEnabled: ({ resource }) => {
if (
!isPersonalRoute(this.$route) &&
!isPublicFilesRoute(this.$route) &&
!isFavoritesRoute(this.$route)
) {
return false
}
if (!isDownloadAsArchiveAvailable()) {
return false
}
if (!resource.isFolder) {
return false
}

return resource.canDownload()
},
canBeDefault: true,
componentType: 'oc-button',
class: 'oc-files-actions-download-archive-trigger'
}
]
}
},
methods: {
async $_downloadFolder_trigger(resource) {
await triggerDownloadAsArchive({
folderPath: resource.path,
token: this.getToken
})
}
}
}
3 changes: 3 additions & 0 deletions packages/web-app-files/src/mixins/fileActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Copy from './actions/copy'
import DeclineShare from './actions/declineShare'
import Delete from './actions/delete'
import Download from './actions/download'
import DownloadFolder from './actions/downloadFolder'
import Favorite from './actions/favorite'
import Fetch from './actions/fetch'
import Move from './actions/move'
Expand All @@ -18,6 +19,7 @@ const actionsMixins = [
'fetch',
'navigate',
'download',
'downloadFolder',
'favorite',
'copy',
'move',
Expand All @@ -38,6 +40,7 @@ export default {
DeclineShare,
Delete,
Download,
DownloadFolder,
Favorite,
Fetch,
Move,
Expand Down
43 changes: 43 additions & 0 deletions packages/web-app-files/src/services/archiver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { rcompare } from 'semver'
import { RuntimeError } from 'web-runtime/src/container/error'

/**
* Archiver struct within the capabilities as defined in reva
* TODO: add github link
*/
interface ArchiverCapability {
enabled: boolean
version: string // version is just a major version, e.g. `v2`
formats: string[]
// eslint-disable-next-line camelcase
archiver_url: string
}

class ArchiverService {
serverUrl: string
capability?: ArchiverCapability

public initialize(serverUrl: string, archiverCapabilities: ArchiverCapability[] = []): void {
this.serverUrl = serverUrl
const archivers = archiverCapabilities
.filter(a => a.enabled)
.sort((a1, a2) => rcompare(a1.version, a2.version))
this.capability = archivers.length ? archivers[0] : null
}

public get available() {
return this.capability !== null
}

public get url() {
if (!this.available) {
throw new RuntimeError('no enabled archiver found')
}
return [
this.serverUrl.replace(/\/+$/, ''),
this.capability.archiver_url.replace(/^\/+/, '')
].join('/')
}
}

export const archiverService = new ArchiverService()
1 change: 1 addition & 0 deletions packages/web-app-files/src/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './archiver'
export * from './cache'
export * from './client'
export { default as Registry } from './registry'

0 comments on commit bde5577

Please sign in to comment.