Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Clean urls and spaces mock
Browse files Browse the repository at this point in the history
This commit is a workaround of the lack of spaces in our Reva backend. The idea was to implement the URL schema that we agreed in the past and to re-use the spaces logic for showing our EOS Projects.
diocas committed Jun 1, 2023
1 parent a5b1717 commit 6b774f2
Showing 22 changed files with 445 additions and 71 deletions.
14 changes: 10 additions & 4 deletions packages/web-app-files/src/components/AppBar/CreateSpace.vue
Original file line number Diff line number Diff line change
@@ -2,14 +2,14 @@
<oc-button
id="new-space-menu-btn"
key="new-space-menu-btn-enabled"
v-oc-tooltip="$gettext('Create a new space')"
:aria-label="$gettext('Create a new space')"
v-oc-tooltip="$gettext('Request new Project')"
:aria-label="$gettext('Request new Project')"
appearance="filled"
variation="primary"
@click="showCreateSpaceModal"
@click="onNewProjectButtonClick"
>
<oc-icon name="add" />
<span v-translate>New Space</span>
<span v-translate>New Project</span>
</oc-button>
</template>

@@ -33,6 +33,12 @@ export default defineComponent({
...mapMutations('runtime/spaces', ['UPSERT_SPACE']),
...mapMutations('Files', ['UPSERT_RESOURCE', 'UPDATE_RESOURCE_FIELD']),
onNewProjectButtonClick() {
window.open(
'https://cern.service-now.com/service-portal?id=sc_cat_item&name=request-storage-space&se=CERNBox-Service',
'_blank'
)
},
showCreateSpaceModal() {
const modal = {
variation: 'passive',
Original file line number Diff line number Diff line change
@@ -18,12 +18,14 @@ import SpaceNavigate from 'web-pkg/src/mixins/spaces/navigate'
import { PropType } from 'vue'
import { Resource } from 'web-client'
import { SpaceResource } from 'web-client/src/helpers'
import DeletedFiles from 'web-pkg/src/mixins/spaces/deletedFiles'
export default {
name: 'ContextActions',
components: { ContextActionMenu },
mixins: [
FileActions,
DeletedFiles,
CreateQuicklink,
EmptyTrashBin,
Paste,
@@ -155,6 +157,7 @@ export default {
return action
}),
...fileHandlers,
...this.$_deletedFiles_items_generic,
...this.$_showDetails_items
].filter((item) => item.isEnabled(this.filterParams))
}
18 changes: 17 additions & 1 deletion packages/web-app-files/src/components/Spaces/SpaceHeader.vue
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@
<div class="space-header-infos">
<div class="oc-flex oc-mb-s oc-flex-middle oc-flex-between">
<div class="oc-flex oc-flex-middle space-header-infos-heading">
<h1 class="space-header-name">{{ space.name }}</h1>
<h1 class="space-header-name">{{ spaceName }}</h1>
<oc-button
:id="`space-context-btn`"
v-oc-tooltip="$gettext('Show context menu')"
@@ -89,6 +89,7 @@ import SpaceContextActions from './SpaceContextActions.vue'
import { eventBus } from 'web-pkg/src/services/eventBus'
import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar'
import { useGettext } from 'vue3-gettext'
import { basename } from 'path'
const visibilityObserver = new VisibilityObserver()
const markdownContainerCollapsedClass = 'collapsed'
@@ -265,6 +266,21 @@ export default defineComponent({
memberCountString,
openSideBarSharePanel
}
},
data(){
return {
name: null
}
},
computed: {
spaceName() {
return this.name || this.space.name
}
},
mounted(){
if (this.$route?.params?.driveAliasAndItem.includes("eos/project"))
this.name = basename(this.$route.params.driveAliasAndItem)
}
})
</script>
Original file line number Diff line number Diff line change
@@ -41,21 +41,21 @@ export const useResourceRouteResolver = (options: ResourceRouteResolverOptions,
return unref(targetRouteCallback)(createTargetRouteOptions)
}

const { path, fileId, resource } = createTargetRouteOptions
const { path, fileId = null, resource } = createTargetRouteOptions
let space
if (resource.shareId) {
space = buildShareSpaceResource({
shareId: resource.shareId,
shareName: basename(resource.shareRoot),
serverUrl: configurationManager.serverUrl
})
} else if (!resource.shareId && !getInternalSpace(resource.storageId)) {
} /*else if (!resource.shareId && !getInternalSpace(resource.storageId)) {
if (path === '/') {
return createLocationShares('files-shares-with-me')
}
// FIXME: This is a hacky way to resolve re-shares, but we don't have other options currently
return { name: 'resolvePrivateLink', params: { fileId } }
} else {
} */ else {
space = getMatchingSpace(resource)
}
if (!space) {
2 changes: 1 addition & 1 deletion packages/web-app-files/src/fileSideBars.ts
Original file line number Diff line number Diff line change
@@ -230,7 +230,7 @@ const panelGenerators: (({
}
},
get enabled() {
return resource?.type === 'space' && !multipleSelection
return false//resource?.type === 'space' && !multipleSelection
}
}),
({ capabilities, resource, router, multipleSelection, rootFolder }) => ({
9 changes: 5 additions & 4 deletions packages/web-app-files/src/index.ts
Original file line number Diff line number Diff line change
@@ -35,12 +35,13 @@ const appInfo = {
const navItems = [
{
name(capabilities) {
return capabilities.spaces?.enabled ? $gettext('Personal') : $gettext('All files')
return capabilities.spaces?.enabled ? $gettext('All files') : $gettext('All files')
},
icon: appInfo.icon,
route: {
path: `/${appInfo.id}/spaces/personal`
}
},
activeFor: [{ path: `/${appInfo.id}/spaces/eos` }]
},
{
name: $gettext('Favorites'),
@@ -64,7 +65,7 @@ const navItems = [
}
},
{
name: $gettext('Spaces'),
name: $gettext('Projects'),
icon: 'layout-grid',
route: {
path: `/${appInfo.id}/spaces/projects`
@@ -78,7 +79,7 @@ const navItems = [
name: $gettext('Deleted files'),
icon: 'delete-bin-5',
route: {
path: `/${appInfo.id}/trash`
path: `/${appInfo.id}/trash/eos`
},
enabled(capabilities) {
return capabilities.dav && capabilities.dav.trashbin === '1.0'
15 changes: 10 additions & 5 deletions packages/web-app-files/src/services/folder/loaderTrashbin.ts
Original file line number Diff line number Diff line change
@@ -27,13 +27,18 @@ export class FolderLoaderTrashbin implements FolderLoader {
} = context
const hasShareJail = useCapabilityShareJailEnabled(store)

return useTask(function* (signal1, signal2, space: Resource) {
return useTask(function* (signal1, signal2, space: Resource, projectName) {
store.commit('Files/CLEAR_CURRENT_FILES_LIST')

const path = unref(hasShareJail)
? buildWebDavSpacesTrashPath(space.id)
: buildWebDavFilesTrashPath(space.id)
const resources = yield client.fileTrash.list(path, '1', DavProperties.Trashbin)
// const path = unref(hasShareJail)
// ? buildWebDavSpacesTrashPath(space.id)
// : buildWebDavFilesTrashPath(space.id)
const path = buildWebDavFilesTrashPath(space.id) //user.id
const query = projectName
? { base_path: `/eos/project/${projectName[0]}/${projectName}` }
: undefined

const resources = yield client.fileTrash.list(path, '1', DavProperties.Trashbin, query)

store.commit('Files/LOAD_FILES', {
currentFolder: buildResource(resources[0]),
2 changes: 1 addition & 1 deletion packages/web-app-files/src/views/Favorites.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="oc-flex">
<files-view-wrapper>
<app-bar :display-view-mode-switch="true" :side-bar-open="sideBarOpen" />
<app-bar :side-bar-open="sideBarOpen" />
<app-loading-spinner v-if="areResourcesLoading" />
<template v-else>
<no-content-message
2 changes: 1 addition & 1 deletion packages/web-app-files/src/views/spaces/DriveRedirect.vue
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ import { createFileRouteOptions } from 'web-pkg/src/helpers/router'
// 'personal/home' is used as personal drive alias from static contexts
// (i.e. places where we can't load the actual personal space)
const fakePersonalDriveAlias = 'personal/home'
const fakePersonalDriveAlias = '/'
export default defineComponent({
name: 'DriveRedirect',
6 changes: 5 additions & 1 deletion packages/web-app-files/src/views/spaces/GenericSpace.vue
Original file line number Diff line number Diff line change
@@ -247,7 +247,12 @@ export default defineComponent({
return createLocationSpaces('files-spaces-generic', { params, query })
}
const route = useRoute()
const hasSpaceHeader = computed(() => {
const elems = decodeURIComponent(route.value?.path)?.split('/').filter(Boolean) || [] //"/files/spaces/eos/project/c/cernbox"
if (elems.length === 6 && elems[3] === 'project') {
return true
}
// for now the space header is only available in the root of a project space.
return props.space.driveType === 'project' && props.item === '/'
})
@@ -263,7 +268,6 @@ export default defineComponent({
useDocumentTitle({ titleSegments })
const { $gettext } = useGettext()
const route = useRoute()
const breadcrumbs = computed(() => {
const space = props.space
const rootBreadcrumbItems: BreadcrumbItem[] = []
69 changes: 58 additions & 11 deletions packages/web-app-files/src/views/spaces/GenericTrash.vue
Original file line number Diff line number Diff line change
@@ -78,10 +78,14 @@ import { useResourcesViewDefaults } from '../../composables'
import { computed, defineComponent, PropType, onMounted, onBeforeUnmount, unref } from 'vue'
import { Resource } from 'web-client'
import { useCapabilityShareJailEnabled, useCapabilitySpacesEnabled } from 'web-pkg/src/composables'
import { createLocationTrash } from '../../router'
import { createLocationTrash, createLocationSpaces } from '../../router'
import { isProjectSpaceResource, SpaceResource } from 'web-client/src/helpers'
import { useDocumentTitle } from 'web-pkg/src/composables/appDefaults/useDocumentTitle'
import { useGettext } from 'vue3-gettext'
import { createFileRouteOptions } from 'web-pkg/src/helpers/router'
import { useRoute, useStore } from 'web-pkg/src/composables'
export default defineComponent({
name: 'GenericTrash',
@@ -115,9 +119,7 @@ export default defineComponent({
const { $gettext } = useGettext()
let loadResourcesEventToken
const noContentMessage = computed(() => {
return props.space.driveType === 'personal'
? $gettext('You have no deleted files')
: $gettext('Space has no deleted files')
return $gettext('There are no deleted files')
})
const hasSpaces = useCapabilitySpacesEnabled()
@@ -130,9 +132,18 @@ export default defineComponent({
})
useDocumentTitle({ titleSegments })
const route = useRoute()
const projectName = () => {
const path = route.value?.params?.driveAliasAndItem || ''
const re = /eos\/project\/[a-z]\/([a-z0-9\-]+)/i
const found = path.match(re)
return found ? found[1] : undefined
}
const resourcesViewDefaults = useResourcesViewDefaults<Resource, any, any[]>()
const performLoaderTask = async () => {
await resourcesViewDefaults.loadResourcesTask.perform(props.space)
await resourcesViewDefaults.loadResourcesTask.perform(props.space, projectName())
resourcesViewDefaults.refreshFileListHeaderPosition()
resourcesViewDefaults.scrollToResourceFromRoute(
unref(resourcesViewDefaults.paginatedResources)
@@ -153,7 +164,9 @@ export default defineComponent({
return {
...resourcesViewDefaults,
hasShareJail: useCapabilityShareJailEnabled(),
noContentMessage
noContentMessage,
performLoaderTask,
projectName
}
},
@@ -167,6 +180,7 @@ export default defineComponent({
},
breadcrumbs() {
/*
let allowContextActions = true
let currentNodeName = this.space?.name
if (this.space.driveType === 'personal') {
@@ -184,14 +198,47 @@ export default defineComponent({
onClick: () => eventBus.publish('app.files.list.load')
}
]
*/
const projectName = this.projectName()
return [
{
text: this.$gettext('Deleted files'),
to: createLocationTrash('files-trash-generic', createFileRouteOptions(this.space, {
path: '/eos'
}))
},
...(projectName
? [
{
text: this.$gettext('Projects'),
to: createLocationSpaces('files-spaces-projects')
},
{
text: projectName,
onClick: () => eventBus.publish('app.files.list.load')
}] : [
{
text: this.$gettext('Personal'),
onClick: () => eventBus.publish('app.files.list.load')
}
])
]
},
showActions() {
return (
!isProjectSpaceResource(this.space) ||
this.space.isEditor(this.user) ||
this.space.isManager(this.user)
)
// TODO only admins can restore?
return true
}
},
watch: {
$route: {
handler: function (to, from) {
if (
to.name === 'files-trash-generic' &&
from.params?.driveAliasAndItem !== to.params?.driveAliasAndItem)
this.performLoaderTask()
}
}
}
})
73 changes: 61 additions & 12 deletions packages/web-app-files/src/views/spaces/Projects.vue
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
icon="layout-grid"
>
<template #message>
<span v-translate>You don't have access to any spaces</span>
<span v-translate>You don't have access to any Project</span>
</template>
</no-content-message>
<div v-else class="spaces-list oc-px-m oc-mt-l">
@@ -43,14 +43,14 @@
/>
</template>
<template #actions="{ resource }">
<oc-button
<!-- <oc-button
v-oc-tooltip="showSpaceMemberLabel"
:aria-label="showSpaceMemberLabel"
appearance="raw"
@click="openSidebarSharePanel(resource)"
>
<oc-icon name="group" fill-type="line" />
</oc-button>
</oc-button> -->
</template>
<template #contextMenuActions="{ resource }">
<space-context-actions :space="resource" :items="[resource]" />
@@ -88,6 +88,7 @@ import { WebDAV } from 'web-client/src/webdav'
import { useScrollTo } from 'web-app-files/src/composables/scrollTo'
import { useSelectedResources, useSort } from 'web-app-files/src/composables'
import { sortFields as availableSortFields } from '../../helpers/ui/resourceTiles'
import { buildSpace } from 'web-client/src/helpers'
export default defineComponent({
components: {
@@ -104,9 +105,8 @@ export default defineComponent({
const store = useStore()
const { selectedResourcesIds } = useSelectedResources({ store })
const runtimeSpaces = computed(
() => store.getters['runtime/spaces/spaces'].filter((s) => isProjectSpaceResource(s)) || []
)
const runtimeSpaces = computed(() => store.getters['Files/activeFiles'] || [])
const sortFields = [availableSortFields[0], availableSortFields[1]]
const {
@@ -122,13 +122,21 @@ export default defineComponent({
const accessToken = useAccessToken({ store })
const { graphClient } = useGraphClient()
const loadResourcesTask = useTask(function* () {
const loadResourcesTask = useTask(function* (signal, ref) {
store.commit('Files/CLEAR_FILES_SEARCHED')
store.commit('Files/CLEAR_CURRENT_FILES_LIST')
/*
yield store.dispatch('runtime/spaces/reloadProjectSpaces', {
graphClient: unref(graphClient)
})
store.commit('Files/LOAD_FILES', { currentFolder: null, files: unref(spaces) })
*/
let loadedSpaces = yield ref.getProjects(accessToken.value)
loadedSpaces = loadedSpaces.map((s) =>
buildSpace({ ...s, serverUrl: configurationManager.serverUrl })
)
store.commit('CLEAR_PROJECT_SPACES')
store.commit('Files/LOAD_FILES', { currentFolder: null, files: loadedSpaces })
})
const areResourcesLoading = computed(() => {
@@ -161,16 +169,16 @@ export default defineComponent({
breadcrumbs() {
return [
{
text: this.$gettext('Spaces'),
onClick: () => this.loadResourcesTask.perform()
text: this.$gettext('Projects'),
onClick: () => this.loadResourcesTask.perform(this)
}
]
},
showSpaceMemberLabel() {
return this.$gettext('Show members')
},
hasCreatePermission() {
return this.$permissionManager.hasSpaceManagement()
return true
}
},
watch: {
@@ -220,11 +228,52 @@ export default defineComponent({
}
},
async created() {
await this.loadResourcesTask.perform()
await this.loadResourcesTask.perform(this)
this.scrollToResourceFromRoute(this.spaces)
},
methods: {
...mapMutations('Files', ['SET_FILE_SELECTION']),
async getProjects(accessToken) {
const headers = new Headers()
headers.append('Authorization', 'Bearer ' + accessToken)
headers.append('X-Requested-With', 'XMLHttpRequest')
const response = await fetch('api/v0/projects', {
method: 'GET',
headers
})
if (!response.ok) {
const message = `An error has occured: ${response.status}`
throw new Error(message)
}
const data = await response.json()
// const data = {
// projects: [
// {
// name: 'cernbox',
// path: '/eos/project/c/cernbox',
// permissions: 'admin'
// },
// {
// name: 'awesomeproject',
// path: '/eos/project/a/awesomeproject',
// permissions: 'admin'
// }
// ]
// }
let projects = []
data.projects.forEach((project) => {
projects.push({
name: project.name,
id: project.name,
//driveType: 'project',
driveAlias: project.path,
quota: { remaining: 1, state: 'normal', total: 2, used: 1 }
})
})
return projects
},
openSidebarSharePanel(space: SpaceResource) {
this.SET_FILE_SELECTION([space])
eventBus.publish(SideBarEventTopics.openWithPanel, 'space-share')
4 changes: 3 additions & 1 deletion packages/web-client/src/ocs/capabilities.ts
Original file line number Diff line number Diff line change
@@ -120,7 +120,9 @@ export const GetCapabilitiesFactory = (baseURI: string, axios: AxiosInstance) =>
return {
async getCapabilities(): Promise<Capabilities> {
const response = await axios.get(endpoint)
return get(response, 'data.ocs.data', { capabilities: null, version: null })
const cap = get(response, 'data.ocs.data', { capabilities: null, version: null })
cap.capabilities.spaces.enabled = false
return cap
}
}
}
5 changes: 3 additions & 2 deletions packages/web-client/src/webdav/restoreFile.ts
Original file line number Diff line number Diff line change
@@ -8,13 +8,14 @@ export const RestoreFileFactory = ({ sdk }: WebDavOptions) => {
space: SpaceResource,
{ id }: { id: string | number },
{ path: restorePath }: { path: string },
{ overwrite }: { overwrite?: boolean }
{ overwrite }: { overwrite?: boolean },
query: any
): Promise<FileResource> {
if (isPublicSpaceResource(space)) {
return
}
const restoreWebDavPath = urlJoin(space.webDavPath, restorePath)
return sdk.fileTrash.restore(space.webDavTrashPath, id, restoreWebDavPath, overwrite)
return sdk.fileTrash.restore(space.webDavTrashPath, id, restoreWebDavPath, overwrite, query)
}
}
}
4 changes: 4 additions & 0 deletions packages/web-pkg/package.json
Original file line number Diff line number Diff line change
@@ -12,7 +12,11 @@
"url": "https://github.com/owncloud/web",
"directory": "packages/web-pkg"
},
"devDependencies": {
"@vueuse/core": "^9.8.2"
},
"peerDependencies": {
"@vueuse/core": "^9.8.2",
"axios": "^0.27.2",
"filesize": "^9.0.11",
"fuse.js": "^6.5.3",
Original file line number Diff line number Diff line change
@@ -43,16 +43,98 @@
<span class="oc-text-small" v-text="$gettext('Show')" />
</oc-button>
</div>

<table class="details-table" :aria-label="detailsTableLabel">
<tr>
<!--<tr>
<th scope="col" class="oc-pr-s" v-text="$gettext('Last activity')" />
<td v-text="lastModifiedDate" />
</tr>
</tr>-->
<tr v-if="resource.description">
<th scope="col" class="oc-pr-s" v-text="$gettext('Subtitle')" />
<td v-text="resource.description" />
</tr>
<tr>
<tr v-if="runningOnEos && !isPublicLinkContext">
<th scope="col" class="oc-pr-s" v-text="eosPathLabel" />
<td>
<div class="oc-flex oc-flex-middle oc-flex-between oc-width-1-1">
<p
ref="filePath"
v-oc-tooltip="resource.driveAlias"
class="oc-my-rm oc-text-truncate"
v-text="resource.driveAlias"
/>
<oc-button
v-if="isClipboardCopySupported"
v-oc-tooltip="copyEosPathLabel"
:aria-label="copyEosPathLabel"
appearance="raw"
:variation="copiedEos ? 'success' : 'passive'"
@click="copyEosPathToClipboard"
>
<oc-icon
v-if="copiedEos"
key="oc-copy-to-clipboard-copied"
name="checkbox-circle"
class="_clipboard-success-animation"
/>
<oc-icon v-else key="oc-copy-to-clipboard-copy" name="clipboard" />
</oc-button>
</div>
</td>
</tr>
<tr v-if="cernFeatures && getSambaPath(resource.driveAlias) && !isPublicLinkContext">
<th scope="col" class="oc-pr-s" v-text="sambaPathLabel" />
<td>
<div class="oc-flex oc-flex-middle oc-flex-between oc-width-1-1">
<p
ref="sambaFilePath"
v-oc-tooltip="getSambaPath(resource.driveAlias)"
class="oc-my-rm oc-text-truncate"
v-text="getSambaPath(resource.driveAlias)"
/>
<oc-button
v-oc-tooltip="copySambaPathLabel"
:aria-label="copySambaPathLabel"
appearance="raw"
:variation="copiedSamba ? 'success' : 'passive'"
@click="copySambaPathToClipboard"
>
<oc-icon
v-if="copiedSamba"
key="oc-copy-to-clipboard-copied"
name="checkbox-circle"
class="_clipboard-success-animation"
/>
<oc-icon v-else key="oc-copy-to-clipboard-copy" name="clipboard" />
</oc-button>
</div>
</td>
</tr>
<tr v-if="runningOnEos">
<th scope="col" class="oc-pr-s" v-text="directLinkLabel" />
<td>
<div class="oc-flex oc-flex-middle oc-flex-between oc-width-1-1">
<p v-oc-tooltip="directLink" class="oc-my-rm oc-text-truncate" v-text="directLink" />
<oc-button
v-if="isClipboardCopySupported"
v-oc-tooltip="copyDirectLinkLabel"
:aria-label="copyDirectLinkLabel"
appearance="raw"
:variation="copiedDirect ? 'success' : 'passive'"
@click="copyDirectLinkToClipboard"
>
<oc-icon
v-if="copiedDirect"
key="oc-copy-to-clipboard-copied"
name="checkbox-circle"
class="_clipboard-success-animation"
/>
<oc-icon v-else key="oc-copy-to-clipboard-copy" name="clipboard" />
</oc-button>
</div>
</td>
</tr>
<!--<tr>
<th scope="col" class="oc-pr-s" v-text="$gettext('Manager')" />
<td>
<span v-text="ownerUsernames" />
@@ -63,25 +145,28 @@
<td>
<space-quota :space-quota="resource.spaceQuota" />
</td>
</tr>
</tr>-->
</table>
</div>
</template>
<script lang="ts">
import { defineComponent, inject, ref, unref } from 'vue'
import { computed, defineComponent, inject, ref, unref } from 'vue'
import { mapGetters } from 'vuex'
import { useTask } from 'vue-concurrency'
import { buildResource, Resource } from 'web-client/src/helpers'
import { loadPreview } from 'web-pkg/src/helpers/preview'
import { spaceRoleManager } from 'web-client/src/helpers/share'
import { buildWebDavSpacesPath } from 'web-client/src/helpers'
import { ImageDimension } from 'web-pkg/src/constants'
import { useAccessToken, useStore } from 'web-pkg/src/composables'
import { useAccessToken, useStore, usePublicLinkContext } from 'web-pkg/src/composables'
import SpaceQuota from '../../../SpaceQuota.vue'
import { formatDateFromISO } from 'web-pkg/src/helpers'
import { configurationManager } from 'web-pkg/src/configuration'
import { eventBus } from 'web-pkg/src/services/eventBus'
import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar'
import { useClipboard } from '@vueuse/core'
import { useGettext } from 'vue3-gettext'
import { encodePath } from '../../../../utils'
export default defineComponent({
name: 'SpaceDetails',
@@ -104,6 +189,64 @@ export default defineComponent({
const resource = inject<Resource>('resource')
const spaceImage = ref('')
const { $gettext } = useGettext()
const copiedDirect = ref(false)
const copiedEos = ref(false)
const copiedSamba = ref(false)
const {
copy,
copied,
isSupported: isClipboardCopySupported
} = useClipboard({ legacy: true, copiedDuring: 550 })
const isPublicLinkContext = usePublicLinkContext({ store })
const directLink = computed(() => {
return !unref(isPublicLinkContext)
? `${store.getters.configuration.server}files/spaces${encodePath(
unref(resource).driveAlias
)}`
: `${store.getters.configuration.server.replace(/\/+$/, '')}${unref(resource).downloadURL}`
})
const copyEosPathToClipboard = () => {
copy(unref(resource).driveAlias)
copiedEos.value = unref(copied)
store.dispatch('showMessage', {
title: $gettext('FUSE path copied'),
desc: $gettext('The FUSE path has been copied to your clipboard.')
})
}
const copySambaPathToClipboard = () => {
copy(getSambaPath(unref(resource).driveAlias))
copiedSamba.value = unref(copied)
store.dispatch('showMessage', {
title: $gettext('Windows path copied'),
desc: $gettext('The Windows path has been copied to your clipboard.')
})
}
const pathMapping = {
project: '\\\\eosproject-smb\\eos\\project\\'
}
const getSambaPath = (path) => {
const pathComponents = path?.split('/').filter(Boolean)
if (pathComponents.length > 1 && pathComponents[0] === 'eos') {
const translated = pathMapping[pathComponents[1]]
return translated && `${translated}${pathComponents.slice(2).join('\\')}`
}
}
const copyDirectLinkToClipboard = () => {
copy(unref(directLink))
copiedDirect.value = unref(copied)
store.dispatch('showMessage', {
title: $gettext('Direct link copied'),
desc: $gettext('The direct link has been copied to your clipboard.')
})
}
const loadImageTask = useTask(function* (signal, ref) {
if (!ref.resource?.spaceImageData || !props.showSpaceImage) {
spaceImage.value = undefined
@@ -134,12 +277,51 @@ export default defineComponent({
})
})
return { loadImageTask, spaceImage, resource }
return {
loadImageTask,
spaceImage,
resource,
copyEosPathToClipboard,
copySambaPathToClipboard,
getSambaPath,
copiedEos,
copiedSamba,
copiedDirect,
directLink,
copyDirectLinkToClipboard,
isClipboardCopySupported,
isPublicLinkContext
}
},
computed: {
...mapGetters('Files', ['currentFileOutgoingLinks']),
...mapGetters('runtime/spaces', ['spaceMembers']),
...mapGetters(['user']),
...mapGetters(['user', 'configuration']),
runningOnEos() {
return !!this.configuration?.options?.runningOnEos
},
cernFeatures() {
return !!this.configuration?.options?.cernFeatures
},
directLinkLabel() {
return this.$gettext('Direct link')
},
copyDirectLinkLabel() {
return this.$gettext('Copy direct link')
},
eosPathLabel() {
return this.$gettext('FUSE Path')
},
copyEosPathLabel() {
return this.$gettext('Copy FUSE path')
},
sambaPathLabel() {
return this.$gettext('Windows Path')
},
copySambaPathLabel() {
return this.$gettext('Copy Windows path')
},
hasShares() {
return this.hasMemberShares || this.hasLinkShares
},
@@ -294,10 +476,21 @@ export default defineComponent({
tr {
height: 1.5rem;
td {
max-width: 0;
width: 100%;
overflow-wrap: break-word;
div {
min-width: 0;
}
}
}
th {
font-weight: 600;
white-space: nowrap;
}
}
</style>
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
</div>
</div>
<div>
<table class="details-table" :aria-label="detailsTableLabel">
<!-- <table class="details-table" :aria-label="detailsTableLabel">
<tr>
<th scope="col" class="oc-pr-s" v-text="$gettext('Total quota:')" />
<td v-text="totalSelectedSpaceQuotaTotal" />
@@ -28,7 +28,7 @@
<th scope="col" class="oc-pr-s" v-text="$gettext('Disabled:')" />
<td v-text="totalDisabledSpaces" />
</tr>
</table>
</table> -->
</div>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -95,6 +95,9 @@ export const useDriveResolver = (options: DriveResolverOptions = {}): DriveResol
}
if (matchingSpace) {
path = driveAliasAndItem.slice(matchingSpace.driveAlias.length)
} else if (driveAliasAndItem.startsWith('eos')) {
matchingSpace = unref(spaces).find((s) => s.driveType === 'personal')
path = driveAliasAndItem
}
}
space.value = matchingSpace
61 changes: 49 additions & 12 deletions packages/web-pkg/src/mixins/spaces/deletedFiles.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
import { createFileRouteOptions } from 'web-pkg/src/helpers/router'
import { unref } from 'vue'

export default {
computed: {
_deletedFiles_base() {
return {
name: 'deletedFiles',
icon: 'delete-bin-5',
label: () => {
return this.$gettext('Deleted files')
},
handler: this.$_deletedFiles_trigger,
componentType: 'button',
class: 'oc-files-actions-delete-trigger'
}
},
$_deletedFiles_items() {
return [
{
name: 'deletedFiles',
icon: 'delete-bin-5',
label: () => {
return this.$gettext('Deleted files')
},
handler: this.$_deletedFiles_trigger,
isEnabled: ({ resources }) => {
return resources.length === 1
},
componentType: 'button',
class: 'oc-files-actions-delete-trigger'
...this._deletedFiles_base,
isEnabled: this._deletedFiles_enabled_spaces,
}
]
},
$_deletedFiles_items_generic() {
return [
{
...this._deletedFiles_base,
isEnabled: this._deletedFiles_enabled_generic,
}
]
}
@@ -24,8 +36,33 @@ export default {
$_deletedFiles_trigger() {
return this.$router.push({
name: 'files-trash-generic',
...createFileRouteOptions(this.space, { fileId: this.space.fileId })
...createFileRouteOptions(this.space, {
path: unref(this.$router.currentRoute)
?.path?.split('/')
.filter(Boolean)
.slice(2)
.join('/')
})
})
},
_deletedFiles_enabled_generic({ resources }) {

if (resources.length !== 1) {
return false
}

if (unref(this.$router.currentRoute).name !== 'files-spaces-generic') {
return false
}
const elems = (resources[0].driveAlias || resources[0].path || '').split('/').filter(Boolean) //"/eos/project/c/cernbox"
if (elems.length !== 4 || elems[1] !== 'project') {
return false
}

return true
},
_deletedFiles_enabled_spaces({ resources }) {
return resources.length === 1
}
}
}
2 changes: 1 addition & 1 deletion packages/web-pkg/src/mixins/spaces/showMembers.ts
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ export default {
icon: 'group',
label: () => this.$gettext('Members'),
handler: this.$_showMembers_trigger,
isEnabled: ({ resources }) => resources.length === 1,
isEnabled: ({ resources }) => false,
componentType: 'button',
class: 'oc-files-actions-show-details-trigger'
}
4 changes: 2 additions & 2 deletions packages/web-runtime/src/index.ts
Original file line number Diff line number Diff line change
@@ -134,9 +134,9 @@ export const bootstrapApp = async (configurationPath: string): Promise<void> =>
const user = store.getters.user
const space = buildSpace({
id: user.id,
driveAlias: `personal/${user.id}`,
driveAlias: `/`,
driveType: 'personal',
name: app.config.globalProperties.$gettext('All files'),
name: app.config.globalProperties.$gettext('CERNBox'),
webDavPath: `/files/${user.id}`,
webDavTrashPath: `/trash-bin/${user.id}`,
serverUrl: configurationManager.serverUrl
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

0 comments on commit 6b774f2

Please sign in to comment.