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

refactor: remove share role checks #11364

Merged
merged 3 commits into from
Aug 23, 2024
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
Expand Up @@ -9,103 +9,84 @@
<div v-if="!filteredSpaceMembers.length">
<h3 class="oc-text-bold oc-text-medium" v-text="$gettext('No members found')" />
</div>
<div
v-if="filteredSpaceManagers.length"
class="oc-mb-m"
data-testid="space-members-role-managers"
>
<h3 class="oc-text-bold oc-text-medium" v-text="$gettext('Managers')" />
<members-role-section :members="filteredSpaceManagers" />
<div v-for="(role, i) in availableRoles" :key="i">
JammingBen marked this conversation as resolved.
Show resolved Hide resolved
<div
v-if="getMembersForRole(role).length"
class="oc-mb-m"
:data-testid="`space-members-role-${role.displayName}`"
>
<h3 class="oc-text-bold oc-text-medium" v-text="role.displayName" />
<members-role-section :members="getMembersForRole(role)" />
</div>
</div>
<div
v-if="filteredSpaceEditors.length"
class="oc-mb-m"
data-testid="space-members-role-editors"
>
<h3 class="oc-text-bold oc-text-medium" v-text="$gettext('Editors')" />
<members-role-section :members="filteredSpaceEditors" />
</div>
<div
v-if="filteredSpaceViewers.length"
class="oc-mb-m"
data-testid="space-members-role-viewers"
>
<h3 class="oc-text-bold oc-text-medium" v-text="$gettext('Viewers')" />
<members-role-section :members="filteredSpaceViewers" />
</div>
<div
v-if="filteredSpaceSecureViewers.length"
class="oc-mb-m"
data-testid="space-members-role-secure-viewers"
>
<h3 class="oc-text-bold oc-text-medium" v-text="$gettext('Secure viewers')" />
<members-role-section :members="filteredSpaceSecureViewers" />
<div v-if="membersWithoutRole.length" class="space-members-custom">
<h3 class="oc-text-bold oc-text-medium" v-text="$gettext('Custom role')" />
<members-role-section :members="membersWithoutRole" />
</div>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, inject, ref, watch, unref } from 'vue'
import { SpaceResource, SpaceRole } from '@ownclouders/web-client'
import { ShareRole, SpaceMember, SpaceResource } from '@ownclouders/web-client'
import MembersRoleSection from './MembersRoleSection.vue'
import Fuse from 'fuse.js'
import Mark from 'mark.js'
import { defaultFuseOptions } from '@ownclouders/web-pkg'
import { defaultFuseOptions, useSharesStore } from '@ownclouders/web-pkg'

export default defineComponent({
name: 'MembersPanel',
components: { MembersRoleSection },
setup() {
const sharesStore = useSharesStore()

const resource = inject<SpaceResource>('resource')
const filterTerm = ref('')
const markInstance = ref(null)
const membersListRef = ref(null)
const filterMembers = (collection: Array<SpaceRole & { roleType: string }>, term: string) => {

const filterMembers = (collection: SpaceMember[], term: string) => {
if (!(term || '').trim()) {
return collection
}

const searchEngine = new Fuse(collection, { ...defaultFuseOptions, keys: ['displayName'] })
const searchEngine = new Fuse(collection, {
...defaultFuseOptions,
keys: ['grantedTo.user.displayName', 'grantedTo.group.displayName']
})
return searchEngine.search(term).map((r) => r.item)
}

const spaceMembers = computed(() => {
return [
...unref(resource).spaceRoles.manager.map((r) => ({
...r,
roleType: 'manager'
})),
...unref(resource).spaceRoles.editor.map((r) => ({
...r,
roleType: 'editor'
})),
...unref(resource).spaceRoles.viewer.map((r) => ({
...r,
roleType: 'viewer'
})),
...unref(resource).spaceRoles['secure-viewer'].map((r) => ({
...r,
roleType: 'secure-viewer'
}))
].sort((a, b) => a.displayName.localeCompare(b.displayName))
const spaceMembers = computed<SpaceMember[]>(() => {
return Object.values(unref(resource).members)
})

const filteredSpaceMembers = computed(() => {
const filteredSpaceMembers = computed<SpaceMember[]>(() => {
return filterMembers(unref(spaceMembers), unref(filterTerm))
})
const filteredSpaceManagers = computed(() => {
return unref(filteredSpaceMembers).filter((m) => m.roleType === 'manager')
})
const filteredSpaceEditors = computed(() => {
return unref(filteredSpaceMembers).filter((m) => m.roleType === 'editor')
})
const filteredSpaceViewers = computed(() => {
return unref(filteredSpaceMembers).filter((m) => m.roleType === 'viewer')

const availableRoles = computed<ShareRole[]>(() => {
const permissionsWithRole = unref(spaceMembers).filter((p) => !!p.roleId)
const roleIds = [...new Set(permissionsWithRole.map((p) => p.roleId))]
return roleIds
.map((r) => sharesStore.graphRoles[r])
.filter(Boolean)
.sort((a, b) => {
// sort roles by amount of permissions (most likely translates to manager > editor > viewer)
const permissionsA = a.rolePermissions.flatMap((r) => r.allowedResourceActions)
const permissionsB = b.rolePermissions.flatMap((r) => r.allowedResourceActions)
return permissionsB.length - permissionsA.length
})
})
const filteredSpaceSecureViewers = computed(() => {
return unref(filteredSpaceMembers).filter((m) => m.roleType === 'secure-viewer')

const membersWithoutRole = computed<SpaceMember[]>(() => {
return unref(filteredSpaceMembers).filter(({ roleId }) => !roleId)
})

const getMembersForRole = (role: ShareRole): SpaceMember[] => {
return unref(filteredSpaceMembers).filter(({ roleId }) => roleId === role.id)
}

watch(filterTerm, () => {
if (unref(membersListRef)) {
markInstance.value = new Mark(unref(membersListRef))
Expand All @@ -120,11 +101,10 @@ export default defineComponent({
return {
filterTerm,
filteredSpaceMembers,
filteredSpaceManagers,
filteredSpaceEditors,
filteredSpaceViewers,
filteredSpaceSecureViewers,
membersListRef
membersListRef,
availableRoles,
membersWithoutRole,
getMembersForRole
}
}
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<template>
<ul class="oc-list">
<li
v-for="(member, index) in members"
v-for="(m, index) in members"
:key="index"
class="oc-flex oc-flex-middle oc-mb-s"
data-testid="space-members-list"
>
<oc-avatar
v-if="member.kind === 'user'"
:user-name="member.displayName"
v-if="m.grantedTo.user"
:user-name="m.grantedTo.user.displayName"
:width="36"
class="oc-mr-s"
/><oc-avatar-item
Expand All @@ -19,20 +19,19 @@
name="group"
class="oc-mr-s"
/>
{{ member.displayName }}
{{ (m.grantedTo.user || m.grantedTo.group).displayName }}
</li>
</ul>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
import { ShareTypes } from '@ownclouders/web-client'
import { SpaceRole } from '@ownclouders/web-client'
import { ShareTypes, SpaceMember } from '@ownclouders/web-client'

export default defineComponent({
name: 'MembersRoleSection',
props: {
members: {
type: Array as PropType<SpaceRole[]>,
type: Array as PropType<SpaceMember[]>,
required: true
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ import {
unref,
watch
} from 'vue'
import { SpaceResource } from '@ownclouders/web-client'
import { getSpaceManagers, SpaceResource } from '@ownclouders/web-client'
import Mark from 'mark.js'
import Fuse from 'fuse.js'
import { useGettext } from 'vue3-gettext'
Expand Down Expand Up @@ -352,9 +352,11 @@ export default defineComponent({
])

const getManagerNames = (space: SpaceResource) => {
const allManagers = space.spaceRoles.manager
const allManagers = getSpaceManagers(space)
const managers = allManagers.length > 2 ? allManagers.slice(0, 2) : allManagers
let managerStr = managers.map((m) => m.displayName).join(', ')
let managerStr = managers
.map(({ grantedTo }) => (grantedTo.user || grantedTo.group).displayName)
.join(', ')
if (allManagers.length > 2) {
managerStr += `... +${allManagers.length - 2}`
}
Expand Down Expand Up @@ -386,11 +388,7 @@ export default defineComponent({
return formatFileSize(space.spaceQuota.remaining, language.current)
}
const getMemberCount = (space: SpaceResource) => {
return (
space.spaceRoles.manager.length +
space.spaceRoles.editor.length +
space.spaceRoles.viewer.length
)
return Object.keys(space.members).length
}

const getSelectSpaceLabel = (space: SpaceResource) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ import {
useCapabilityStore,
useEventBus,
useMessages,
useSpacesStore
useSpacesStore,
useSharesStore
} from '@ownclouders/web-pkg'
import GroupSelect from '../GroupSelect.vue'
import { cloneDeep, isEmpty, isEqual, omit } from 'lodash-es'
Expand Down Expand Up @@ -165,6 +166,7 @@ export default defineComponent({
const userStore = useUserStore()
const userSettingsStore = useUserSettingsStore()
const spacesStore = useSpacesStore()
const sharesStore = useSharesStore()
const eventBus = useEventBus()
const { showErrorMessage } = useMessages()
const { $gettext } = useGettext()
Expand Down Expand Up @@ -225,10 +227,14 @@ export default defineComponent({

const onUpdateUserDrive = async (editUser: User) => {
const client = clientService.graphAuthenticated
const updateSpace = await client.drives.updateDrive(editUser.drive.id, {
name: editUser.drive.name,
quota: { total: editUser.drive.quota.total }
})
const updateSpace = await client.drives.updateDrive(
editUser.drive.id,
{
name: editUser.drive.name,
quota: { total: editUser.drive.quota.total }
},
sharesStore.graphRoles
)

if (editUser.id === userStore.user.id) {
// Load current user quota
Expand Down
6 changes: 4 additions & 2 deletions packages/web-app-admin-settings/src/views/Spaces.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ import {
useSpaceActionsDisable,
useSpaceActionsRestore,
useSpaceActionsEditQuota,
AppLoadingSpinner
AppLoadingSpinner,
useSharesStore
} from '@ownclouders/web-pkg'
import { call, SpaceResource } from '@ownclouders/web-client'
import { computed, defineComponent, onBeforeUnmount, onMounted, ref, unref } from 'vue'
Expand Down Expand Up @@ -100,6 +101,7 @@ export default defineComponent({
const clientService = useClientService()
const { $gettext } = useGettext()
const { isSideBarOpen, sideBarActivePanel } = useSideBar()
const sharesStore = useSharesStore()

const loadResourcesEventToken = ref(null)
let updateQuotaForSpaceEventToken: string
Expand All @@ -119,7 +121,7 @@ export default defineComponent({

const loadResourcesTask = useTask(function* (signal) {
const drives = yield* call(
clientService.graphAuthenticated.drives.listAllDrives({
clientService.graphAuthenticated.drives.listAllDrives(sharesStore.graphRoles, {
orderBy: 'name asc',
filter: 'driveType eq project'
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import MembersPanel from '../../../../../src/components/Spaces/SideBar/MembersPanel.vue'
import { defaultPlugins, shallowMount } from 'web-test-helpers'
import { mock } from 'vitest-mock-extended'
import { SpaceResource } from '@ownclouders/web-client'
import { ShareRole, SpaceResource } from '@ownclouders/web-client'
import MembersRoleSection from '../../../../../src/components/Spaces/SideBar/MembersRoleSection.vue'

const spaceMock = mock<SpaceResource>({
spaceRoles: {
manager: [{ kind: 'user', displayName: 'admin' }],
editor: [
{ kind: 'user', displayName: 'einstein' },
{ kind: 'group', displayName: 'physic-haters' }
],
viewer: [{ kind: 'user', displayName: 'marie' }],
'secure-viewer': [{ kind: 'user', displayName: 'kathrine' }]
const graphRoles = {
'1': mock<ShareRole>({ id: '1', displayName: 'Managers', rolePermissions: [] }),
'2': mock<ShareRole>({ id: '2', displayName: 'Editors', rolePermissions: [] }),
'3': mock<ShareRole>({ id: '3', displayName: 'Viewers', rolePermissions: [] })
}

const spaceMock = {
members: {
'1': { roleId: '1', grantedTo: { user: { displayName: 'admin' } } },
'2': { roleId: '2', grantedTo: { user: { displayName: 'marie' } } },
'3': { roleId: '3', grantedTo: { user: { displayName: 'einstein' } } }
}
})
} as undefined as SpaceResource

const selectors = {
membersRolePanelStub: 'members-role-section-stub'
membersRolePanelStub: 'members-role-section-stub',
spaceMembersCustom: '.space-members-custom'
}

describe('MembersPanel', () => {
Expand All @@ -26,30 +29,39 @@ describe('MembersPanel', () => {
expect(wrapper.html()).toMatchSnapshot()
})
it('should filter members accordingly to the entered search term', async () => {
const userToFilterFor = spaceMock.spaceRoles.editor[0]
const userToFilterFor = spaceMock.members['3']
const { wrapper } = getWrapper()
wrapper.vm.filterTerm = 'ein'
await wrapper.vm.$nextTick
await wrapper.vm.$nextTick()
expect(wrapper.findAll(selectors.membersRolePanelStub).length).toBe(1)
expect(
wrapper.findComponent<typeof MembersRoleSection>(selectors.membersRolePanelStub).props()
.members[0].displayName
).toEqual(userToFilterFor.displayName)
.members[0].grantedTo.user.displayName
).toEqual(userToFilterFor.grantedTo.user.displayName)
})
it('should display an empty result if no matching members found', async () => {
const { wrapper } = getWrapper()
wrapper.vm.filterTerm = 'no-match'
await wrapper.vm.$nextTick
await wrapper.vm.$nextTick()
expect(wrapper.findAll(selectors.membersRolePanelStub).length).toBe(0)
expect(wrapper.html()).toMatchSnapshot()
})
it('should display members without role under the custom section', () => {
const spaceResource = {
members: {
'1': { grantedTo: { user: { displayName: 'admin' } } }
}
} as undefined as SpaceResource
const { wrapper } = getWrapper({ spaceResource })
expect(wrapper.find(selectors.spaceMembersCustom).exists()).toBeTruthy()
})
})

function getWrapper({ spaceResource = spaceMock } = {}) {
return {
wrapper: shallowMount(MembersPanel, {
global: {
plugins: [...defaultPlugins()],
plugins: [...defaultPlugins({ piniaOptions: { sharesState: { graphRoles } } })],
provide: { resource: spaceResource }
}
})
Expand Down
Loading