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

Implement the spaces list in the admin settings #8178

Merged
merged 11 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Spaces list in admin settings

The admin settings now have a view to list all spaces for the current instance. This makes it possible for space admins to manage all spaces in one place.

https://github.com/owncloud/web/pull/8178
3 changes: 2 additions & 1 deletion packages/web-app-admin-settings/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"uuid": "^9.0.0",
"vue-concurrency": "4.0.0",
"vuex": "^3.6.2",
"web-pkg": "npm:@ownclouders/web-pkg"
"web-pkg": "npm:@ownclouders/web-pkg",
"web-client": "npm:@ownclouders/web-client"
}
}
325 changes: 325 additions & 0 deletions packages/web-app-admin-settings/src/components/Spaces/SpacesList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
<template>
<div>
<oc-table
ref="table"
class="spaces-table"
:sort-by="sortBy"
:sort-dir="sortDir"
:fields="fields"
:data="orderedSpaces"
:highlighted="highlighted"
:sticky="true"
:header-position="headerPosition"
:hover="true"
@sort="handleSort"
@highlight="fileClicked"
>
<template #selectHeader>
<oc-checkbox
size="large"
class="oc-ml-s"
:label="$gettext('Select all spaces')"
:value="allSpacesSelected"
hide-label
@input="$emit('toggleSelectAllSpaces')"
/>
</template>
<template #select="{ item }">
<oc-checkbox
class="oc-ml-s"
size="large"
:value="isSpaceSelected(item)"
:option="item"
:label="getSelectSpaceLabel(item)"
hide-label
@input="$emit('toggleSelectSpace', item)"
@click.native.stop
/>
</template>
<template #icon>
<oc-icon name="layout-grid" />
</template>
<template #name="{ item }">
<span
class="spaces-table-space-name"
:data-test-space-name="item.name"
v-text="item.name"
/>
</template>
<template #manager="{ item }">
{{ getManagerNames(item) }}
</template>
<template #members="{ item }">
{{ getMemberCount(item) }}
</template>
<template #availableQuota="{ item }"> {{ getAvailableQuota(item) }} </template>
<template #usedQuota="{ item }"> {{ getUsedQuota(item) }} </template>
<template #remainingQuota="{ item }"> {{ getRemainingQuota(item) }} </template>
<template #mdate="{ item }">
<span
v-oc-tooltip="formatDate(item.mdate)"
tabindex="0"
v-text="formatDateRelative(item.mdate)"
/>
</template>
<template #status="{ item }">
<span v-if="item.disabled" class="oc-flex oc-flex-middle">
<oc-icon name="stop-circle" variation="danger" class="oc-mr-s" /><span
v-text="$gettext('Disabled')"
/>
</span>
<span v-else class="oc-flex oc-flex-middle">
<oc-icon name="play-circle" variation="success" class="oc-mr-s" /><span
v-text="$gettext('Enabled')"
/>
</span>
</template>
<template #footer>
<div class="oc-text-nowrap oc-text-center oc-width-1-1 oc-my-s">
<p class="oc-text-muted">{{ footerTextTotal }}</p>
</div>
</template>
</oc-table>
</div>
</template>

<script lang="ts">
import { formatDateFromJSDate, formatRelativeDateFromJSDate } from 'web-pkg/src/helpers'
import { computed, defineComponent, getCurrentInstance, PropType, ref, unref } from 'vue'
import { SpaceResource } from 'web-client/src/helpers'
import { useTranslations } from 'web-pkg/src/composables'
import { spaceRoleEditor, spaceRoleManager, spaceRoleViewer } from 'web-client/src/helpers/share'

export default defineComponent({
name: 'SpacesList',
props: {
spaces: {
type: Array as PropType<SpaceResource[]>,
required: true
},
selectedSpaces: {
type: Array as PropType<SpaceResource[]>,
required: true
},
headerPosition: {
type: Number,
required: true
}
},
emits: ['toggleSelectSpace', 'toggleSelectAllSpaces', 'toggleUnSelectAllSpaces'],
setup: function (props) {
const instance = getCurrentInstance().proxy
const { $gettext, $gettextInterpolate } = useTranslations()
const sortBy = ref('name')
const sortDir = ref('asc')

const allSpacesSelected = computed(() => props.spaces.length === props.selectedSpaces.length)
const highlighted = computed(() => props.selectedSpaces.map((s) => s.id))
const footerTextTotal = computed(() => {
const translated = $gettext('%{spaceCount} spaces in total')
return $gettextInterpolate(translated, { spaceCount: props.spaces.length })
})

const orderBy = (list, prop, desc) => {
return [...list].sort((s1, s2) => {
let a, b

switch (prop) {
case 'members':
a = getMemberCount(s1).toString()
b = getMemberCount(s2).toString()
break
case 'availableQuota':
a = getAvailableQuota(s1).toString()
b = getAvailableQuota(s2).toString()
break
case 'usedQuota':
a = getUsedQuota(s1).toString()
b = getUsedQuota(s2).toString()
break
case 'remainingQuota':
a = getRemainingQuota(s1).toString()
b = getRemainingQuota(s2).toString()
break
case 'status':
a = s1.disabled.toString()
b = s2.disabled.toString()
break
default:
a = s1[prop] || ''
b = s2[prop] || ''
}

return desc ? b.localeCompare(a) : a.localeCompare(b)
})
}
const orderedSpaces = computed(() =>
orderBy(props.spaces, unref(sortBy), unref(sortDir) === 'desc')
)
const handleSort = (event) => {
sortBy.value = event.sortBy
sortDir.value = event.sortDir
}

const isSpaceSelected = (space: SpaceResource) => {
return props.selectedSpaces.some((s) => s.id === space.id)
}

const fields = computed(() => [
{
name: 'select',
title: '',
type: 'slot',
width: 'shrink',
headerType: 'slot'
},
{
name: 'icon',
title: '',
type: 'slot',
width: 'shrink'
},
{
name: 'name',
title: $gettext('Name'),
type: 'slot',
sortable: true
},
{
name: 'manager',
title: $gettext('Manager'),
type: 'slot'
},
{
name: 'members',
title: $gettext('Members'),
type: 'slot',
sortable: true
},
{
name: 'availableQuota',
title: $gettext('Available Quota'),
type: 'slot',
sortable: true
},
{
name: 'usedQuota',
title: $gettext('Used Quota'),
type: 'slot',
sortable: true
},
{
name: 'remainingQuota',
title: $gettext('Remaining Quota'),
type: 'slot',
sortable: true
},
{
name: 'mdate',
title: $gettext('Modified'),
type: 'slot',
sortable: true
},
{
name: 'status',
title: $gettext('Status'),
type: 'slot',
sortable: true
}
])

const getManagerNames = (space: SpaceResource) => {
const allManagers = space.spaceRoles[spaceRoleManager.name]
const managers = allManagers.length > 2 ? allManagers.slice(0, 2) : allManagers
let managerStr = managers.map((m) => m.displayName).join(', ')
if (allManagers.length > 2) {
managerStr += `... +${allManagers.length - 2}`
}
return managerStr
}
const formatDate = (date) => {
return formatDateFromJSDate(new Date(date), instance.$language.current)
}
const formatDateRelative = (date) => {
return formatRelativeDateFromJSDate(new Date(date), instance.$language.current)
}
const getAvailableQuota = (space: SpaceResource) => {
return `${space.spaceQuota.total * Math.pow(10, -9)} GB`
}
const getUsedQuota = (space: SpaceResource) => {
if (space.spaceQuota.used === undefined) {
return '-'
}
return `${(space.spaceQuota.used * Math.pow(10, -9)).toFixed(2)} GB`
}
const getRemainingQuota = (space: SpaceResource) => {
if (space.spaceQuota.remaining === undefined) {
return '-'
}
return `${(space.spaceQuota.remaining * Math.pow(10, -9)).toFixed(0)} GB`
}
const getMemberCount = (space: SpaceResource) => {
return (
space.spaceRoles[spaceRoleManager.name].length +
space.spaceRoles[spaceRoleEditor.name].length +
space.spaceRoles[spaceRoleViewer.name].length
)
}
const getSelectSpaceLabel = (space: SpaceResource) => {
const translated = $gettext('Select %{ space }')
return $gettextInterpolate(translated, { space: space.name }, true)
}

const fileClicked = (data) => {
const space = data[0]
instance.$emit('toggleUnSelectAllSpaces')
instance.$emit('toggleSelectSpace', space)
}

return {
allSpacesSelected,
sortBy,
sortDir,
footerTextTotal,
fields,
highlighted,
getManagerNames,
formatDate,
formatDateRelative,
getAvailableQuota,
getUsedQuota,
getRemainingQuota,
getMemberCount,
getSelectSpaceLabel,
handleSort,
orderedSpaces,
fileClicked,
isSpaceSelected
}
}
})
</script>

<style lang="scss">
.spaces-table {
.oc-table-header-cell-actions,
.oc-table-data-cell-actions {
white-space: nowrap;
}

.oc-table-header-cell-manager,
.oc-table-data-cell-manager,
.oc-table-header-cell-availableQuota,
.oc-table-data-cell-availableQuota,
.oc-table-header-cell-usedQuota,
.oc-table-data-cell-usedQuota,
.oc-table-header-cell-remainingQuota,
.oc-table-data-cell-remainingQuota {
display: none;

@media only screen and (min-width: 1400px) {
display: table-cell;
}
}
}
</style>
27 changes: 26 additions & 1 deletion packages/web-app-admin-settings/src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import translations from '../l10n/translations'
import Users from './views/Users.vue'
import Groups from './views/Groups.vue'
import Spaces from './views/Spaces.vue'
// just a dummy function to trick gettext tools
function $gettext(msg) {
return msg
Expand All @@ -17,7 +18,11 @@ const routes = [
{
path: '/',
redirect: () => {
return { name: 'admin-settings-users' }
const permissionManager = window.Vue.$permissionManager
if (permissionManager.hasUserManagement()) {
return { name: 'admin-settings-users' }
}
return { name: 'admin-settings-spaces' }
}
},
{
Expand All @@ -37,6 +42,15 @@ const routes = [
authContext: 'user',
title: $gettext('Groups')
}
},
{
path: '/spaces',
name: 'admin-settings-spaces',
component: Spaces,
meta: {
authContext: 'user',
title: $gettext('Spaces')
}
}
]

Expand All @@ -62,6 +76,17 @@ const navItems = [
const permissionManager = window.Vue.$permissionManager
return permissionManager.hasUserManagement()
}
},
{
name: $gettext('Spaces'),
icon: 'layout-grid',
route: {
path: `/${appInfo.id}/spaces?`
},
enabled: () => {
const permissionManager = window.Vue.$permissionManager
return permissionManager.hasSpaceManagement()
}
}
]

Expand Down
Loading