Skip to content
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
29 changes: 13 additions & 16 deletions packages/web-app-admin-settings/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,38 @@ import General from './views/General.vue'
import Users from './views/Users.vue'
import Groups from './views/Groups.vue'
import Spaces from './views/Spaces.vue'
import { Ability, urlJoin } from '@opencloud-eu/web-client'
import { urlJoin } from '@opencloud-eu/web-client'
import {
ApplicationInformation,
AppMenuItemExtension,
AppNavigationItem,
ClassicApplicationScript,
defineWebApplication,
useAbility,
useUserStore
} from '@opencloud-eu/web-pkg'
import { RouteRecordRaw } from 'vue-router'
import { computed } from 'vue'

// just a dummy function to trick gettext tools
function $gettext(msg: string) {
return msg
}
import { useGettext } from 'vue3-gettext'

const appId = 'admin-settings'

export const routes = ({ $ability }: { $ability: Ability }): RouteRecordRaw[] => [
export const routes: ClassicApplicationScript['routes'] = ({ $ability, $gettext }) => [
{
path: '/',
redirect: () => {
component: General,
beforeEnter: (to, from, next) => {
if ($ability.can('read-all', 'Setting')) {
return { name: 'admin-settings-general' }
next({ name: 'admin-settings-general' })
}
if ($ability.can('read-all', 'Account')) {
return { name: 'admin-settings-users' }
next({ name: 'admin-settings-users' })
}
if ($ability.can('read-all', 'Group')) {
return { name: 'admin-settings-groups' }
next({ name: 'admin-settings-groups' })
}
if ($ability.can('read-all', 'Drive')) {
return { name: 'admin-settings-spaces' }
next({ name: 'admin-settings-spaces' })
}
throw Error('Insufficient permissions')
next({ path: '/' })
}
},
{
Expand Down Expand Up @@ -103,7 +99,7 @@ export const routes = ({ $ability }: { $ability: Ability }): RouteRecordRaw[] =>
}
]

export const navItems = ({ $ability }: { $ability: Ability }): AppNavigationItem[] => [
export const navItems: ClassicApplicationScript['navItems'] = ({ $ability, $gettext }) => [
{
name: $gettext('General'),
icon: 'settings-4',
Expand Down Expand Up @@ -154,6 +150,7 @@ export default defineWebApplication({
setup() {
const { can } = useAbility()
const userStore = useUserStore()
const { $gettext } = useGettext()

const appInfo: ApplicationInformation = {
name: $gettext('Admin Settings'),
Expand Down
53 changes: 35 additions & 18 deletions packages/web-app-admin-settings/tests/unit/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { navItems, routes } from '../../src/index'
import { Ability } from '@opencloud-eu/web-client'
import { AppNavigationItem, GlobalProperties } from '@opencloud-eu/web-pkg'
import { mock } from 'vitest-mock-extended'
import { RouteRecordRaw } from 'vue-router'

const getAbilityMock = (hasPermission: boolean) => mock<Ability>({ can: () => hasPermission })
const callableNavItems = navItems as (args: GlobalProperties) => AppNavigationItem[]
const callableRoutes = routes as (args: GlobalProperties) => RouteRecordRaw[]
const getPropMock = (ability: Ability) =>
mock<GlobalProperties>({ $ability: ability, $gettext: (s: string) => s })

describe('admin settings index', () => {
describe('navItems', () => {
describe('general', () => {
it.each([true, false])('should be enabled according to the permissions', (enabled) => {
expect(
navItems({ $ability: getAbilityMock(enabled) })
callableNavItems(getPropMock(getAbilityMock(enabled)))
.find((n) => n.name === 'General')
.isVisible()
).toBe(enabled)
Expand All @@ -18,7 +24,7 @@ describe('admin settings index', () => {
describe('user management', () => {
it.each([true, false])('should be enabled according to the permissions', (enabled) => {
expect(
navItems({ $ability: getAbilityMock(enabled) })
callableNavItems(getPropMock(getAbilityMock(enabled)))
.find((n) => n.name === 'Users')
.isVisible()
).toBe(enabled)
Expand All @@ -27,7 +33,7 @@ describe('admin settings index', () => {
describe('group management', () => {
it.each([true, false])('should be enabled according to the permissions', (enabled) => {
expect(
navItems({ $ability: getAbilityMock(enabled) })
callableNavItems(getPropMock(getAbilityMock(enabled)))
.find((n) => n.name === 'Groups')
.isVisible()
).toBe(enabled)
Expand All @@ -36,7 +42,7 @@ describe('admin settings index', () => {
describe('space management', () => {
it.each([true, false])('should be enabled according to the permissions', (enabled) => {
expect(
navItems({ $ability: getAbilityMock(enabled) })
callableNavItems(getPropMock(getAbilityMock(enabled)))
.find((n) => n.name === 'Spaces')
.isVisible()
).toBe(enabled)
Expand All @@ -48,45 +54,56 @@ describe('admin settings index', () => {
it('should redirect to general if permission given', () => {
const ability = mock<Ability>()
ability.can.mockReturnValueOnce(true)
const route = routes({ $ability: ability }).find((n) => n.path === '/')
expect((route.redirect as any)().name).toEqual('admin-settings-general')
const route = callableRoutes(getPropMock(ability)).find((n) => n.path === '/')
const nextMock = vi.fn()
;(route.beforeEnter as any)({}, {}, nextMock)
expect(nextMock).toHaveBeenCalledWith({ name: 'admin-settings-general' })
})
it('should redirect to user management if permission given', () => {
const ability = mock<Ability>()
ability.can.mockReturnValueOnce(false)
ability.can.mockReturnValueOnce(true)
const route = routes({ $ability: ability }).find((n) => n.path === '/')
expect((route.redirect as any)().name).toEqual('admin-settings-users')
const route = callableRoutes(getPropMock(ability)).find((n) => n.path === '/')
const nextMock = vi.fn()
;(route.beforeEnter as any)({}, {}, nextMock)
expect(nextMock).toHaveBeenCalledWith({ name: 'admin-settings-users' })
})
it('should redirect to group management if permission given', () => {
const ability = mock<Ability>()
ability.can.mockReturnValueOnce(false)
ability.can.mockReturnValueOnce(false)
ability.can.mockReturnValueOnce(true)
const route = routes({ $ability: ability }).find((n) => n.path === '/')
expect((route.redirect as any)().name).toEqual('admin-settings-groups')
const route = callableRoutes(getPropMock(ability)).find((n) => n.path === '/')
const nextMock = vi.fn()
;(route.beforeEnter as any)({}, {}, nextMock)
expect(nextMock).toHaveBeenCalledWith({ name: 'admin-settings-groups' })
})
it('should redirect to space management if permission given', () => {
const ability = mock<Ability>()
ability.can.mockReturnValueOnce(false)
ability.can.mockReturnValueOnce(false)
ability.can.mockReturnValueOnce(false)
ability.can.mockReturnValueOnce(true)
const route = routes({ $ability: ability }).find((n) => n.path === '/')
expect((route.redirect as any)().name).toEqual('admin-settings-spaces')
const route = callableRoutes(getPropMock(ability)).find((n) => n.path === '/')
const nextMock = vi.fn()
;(route.beforeEnter as any)({}, {}, nextMock)
expect(nextMock).toHaveBeenCalledWith({ name: 'admin-settings-spaces' })
})
it('should throw an error if permissions are insufficient', () => {
it('redirects to / if permissions are insufficient', () => {
const ability = mock<Ability>()
ability.can.mockReturnValue(false)
expect(routes({ $ability: ability }).find((n) => n.path === '/').redirect).toThrow()
const route = callableRoutes(getPropMock(ability)).find((n) => n.path === '/')
const nextMock = vi.fn()
;(route.beforeEnter as any)({}, {}, nextMock)
expect(nextMock).toHaveBeenCalledWith({ path: '/' })
})
})
it.each([
{ can: true, redirect: null },
{ can: false, redirect: { path: '/' } }
])('redirects "/general" with sufficient permissions', ({ can, redirect }) => {
const ability = mock<Ability>({ can: vi.fn(() => can) })
const route = routes({ $ability: ability }).find((n) => n.path === '/general')
const route = callableRoutes(getPropMock(ability)).find((n) => n.path === '/general')
const nextMock = vi.fn()
;(route.beforeEnter as any)({}, {}, nextMock)
const args = [...(redirect ? [redirect] : [])]
Expand All @@ -97,7 +114,7 @@ describe('admin settings index', () => {
{ can: false, redirect: { path: '/' } }
])('redirects "/users" with sufficient permissions', ({ can, redirect }) => {
const ability = mock<Ability>({ can: vi.fn(() => can) })
const route = routes({ $ability: ability }).find((n) => n.path === '/users')
const route = callableRoutes(getPropMock(ability)).find((n) => n.path === '/users')
const nextMock = vi.fn()
;(route.beforeEnter as any)({}, {}, nextMock)
const args = [...(redirect ? [redirect] : [])]
Expand All @@ -108,7 +125,7 @@ describe('admin settings index', () => {
{ can: false, redirect: { path: '/' } }
])('redirects "/groups" with sufficient permissions', ({ can, redirect }) => {
const ability = mock<Ability>({ can: vi.fn(() => can) })
const route = routes({ $ability: ability }).find((n) => n.path === '/groups')
const route = callableRoutes(getPropMock(ability)).find((n) => n.path === '/groups')
const nextMock = vi.fn()
;(route.beforeEnter as any)({}, {}, nextMock)
const args = [...(redirect ? [redirect] : [])]
Expand All @@ -119,7 +136,7 @@ describe('admin settings index', () => {
{ can: false, redirect: { path: '/' } }
])('redirects "/spaces" with sufficient permissions', ({ can, redirect }) => {
const ability = mock<Ability>({ can: vi.fn(() => can) })
const route = routes({ $ability: ability }).find((n) => n.path === '/spaces')
const route = callableRoutes(getPropMock(ability)).find((n) => n.path === '/spaces')
const nextMock = vi.fn()
;(route.beforeEnter as any)({}, {}, nextMock)
const args = [...(redirect ? [redirect] : [])]
Expand Down
18 changes: 8 additions & 10 deletions packages/web-app-files/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import TrashOverview from './views/trash/Overview.vue'
import translations from '../l10n/translations.json'
import {
ApplicationInformation,
ClassicApplicationScript,
defineWebApplication,
useCapabilityStore,
useEmbedMode,
Expand All @@ -18,28 +19,22 @@ import {
} from '@opencloud-eu/web-pkg'
import { extensions } from './extensions'
import { buildRoutes } from '@opencloud-eu/web-pkg'
import { AppNavigationItem } from '@opencloud-eu/web-pkg'

// dirty: importing view from other extension within project
import SearchResults from '../../web-app-search/src/views/List.vue'
import { isPersonalSpaceResource, isShareSpaceResource } from '@opencloud-eu/web-client'
import { ComponentCustomProperties, unref } from 'vue'
import { unref } from 'vue'
import { extensionPoints } from './extensionPoints'

// just a dummy function to trick gettext tools
function $gettext(msg: string) {
return msg
}
import { useGettext } from 'vue3-gettext'

const appInfo: ApplicationInformation = {
name: $gettext('Files'),
id: 'files',
icon: 'resource-type-folder',
color: 'var(--oc-role-secondary)',
extensions: []
}

export const navItems = (context: ComponentCustomProperties): AppNavigationItem[] => {
export const navItems: ClassicApplicationScript['navItems'] = ({ $ability, $gettext }) => {
const spacesStores = useSpacesStore()
const userStore = useUserStore()
const capabilityStore = useCapabilityStore()
Expand Down Expand Up @@ -75,7 +70,7 @@ export const navItems = (context: ComponentCustomProperties): AppNavigationItem[
path: `/${appInfo.id}/favorites`
},
isVisible() {
return capabilityStore.filesFavorites && context.$ability.can('read', 'Favorite')
return capabilityStore.filesFavorites && $ability.can('read', 'Favorite')
},
priority: 20
},
Expand Down Expand Up @@ -133,6 +128,9 @@ export const navItems = (context: ComponentCustomProperties): AppNavigationItem[

export default defineWebApplication({
setup() {
const { $gettext } = useGettext()
appInfo.name = $gettext('Files')

return {
appInfo,
routes: buildRoutes({
Expand Down
8 changes: 5 additions & 3 deletions packages/web-app-files/tests/unit/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { createPinia, setActivePinia } from 'pinia'
import { navItems } from '../../src/index'
import { useSpacesStore } from '@opencloud-eu/web-pkg'
import { AppNavigationItem, GlobalProperties, useSpacesStore } from '@opencloud-eu/web-pkg'
import { SpaceResource } from '@opencloud-eu/web-client'
import { mock } from 'vitest-mock-extended'

const callableNavItems = navItems as (args: GlobalProperties) => AppNavigationItem[]

describe('Web app files', () => {
beforeEach(() => {
setActivePinia(createPinia())
Expand All @@ -18,15 +20,15 @@ describe('Web app files', () => {
spacesStore.spaces = [
mock<SpaceResource>({ id: '1', driveType: 'personal', isOwner: () => true })
]
const items = navItems(undefined)
const items = callableNavItems(mock<GlobalProperties>())
expect(items[0].isVisible()).toBeTruthy()
})
it('should be disabled if user has no a personal space', () => {
const spacesStore = useSpacesStore()
spacesStore.spaces = [
mock<SpaceResource>({ id: '1', driveType: 'project', isOwner: () => false })
]
const items = navItems(undefined)
const items = callableNavItems(mock<GlobalProperties>())
expect(items[0].isVisible()).toBeFalsy()
})
})
Expand Down
24 changes: 9 additions & 15 deletions packages/web-app-search/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import App from './App.vue'
import List from './views/List.vue'

// @ts-ignore
import translations from '../l10n/translations.json'
import { ApplicationInformation, defineWebApplication } from '@opencloud-eu/web-pkg'
import { defineWebApplication } from '@opencloud-eu/web-pkg'
import { extensions } from './extensions'
import { extensionPoints } from './extensionPoints'

// just a dummy function to trick gettext tools
const $gettext = (msg: string) => {
return msg
}

const appInfo: ApplicationInformation = {
name: $gettext('Search'),
id: 'search',
icon: 'folder'
}
import { useGettext } from 'vue3-gettext'

export default defineWebApplication({
setup() {
const { $gettext } = useGettext()

return {
appInfo,
appInfo: {
name: $gettext('Search'),
id: 'search',
icon: 'folder'
},
routes: [
{
name: 'search',
Expand Down
Loading