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

[full-ci] Implement people sharing for spaces #6455

Merged
merged 8 commits into from
Mar 7, 2022
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
23 changes: 22 additions & 1 deletion __fixtures__/collaborators.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
peopleRoleEditorFile,
peopleRoleViewerFile
peopleRoleViewerFile,
spaceRoleEditor
} from '../packages/web-app-files/src/helpers/share'

export default [
Expand Down Expand Up @@ -142,5 +143,25 @@ export default [
role: peopleRoleViewerFile,
path: "/Neuer Ordner-'singe'",
key: "collaborator-51a8aafe-cd40-4d0a-8566-87a1149b7fea"
},
{
shareType: 7,
id: "f5c28709-b921-4ec8-b39a-4c243709b514",
collaborator: {
name: "einstein",
displayName: "Albert Einstein",
additionalInfo: "einstein@example.org"
},
owner: {
name: "admin",
displayName: "Admin",
additionalInfo: "admin@example.org"
},
fileOwner: {
name: "admin",
displayName: "Admin",
additionalInfo: "admin@example.org"
},
role: spaceRoleEditor
}
]
11 changes: 11 additions & 0 deletions changelog/unreleased/enhancement-spaces-people-sharing
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Enhancement: Implement people sharing for spaces

Spaces can now be shared with other people. This change specifically includes:

* listing all members who have access to a space (possible for all space members)
* adding members to a space and giving them dedicated roles (possible for managers only)
* editing the role of members (possible for managers only)
* removing members from a space (possible for managers only)

https://github.com/owncloud/web/pull/6455
https://github.com/owncloud/web/issues/6283
180 changes: 80 additions & 100 deletions packages/web-app-files/src/components/SideBar/Details/SpaceDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,32 @@
class="space-default-image oc-px-m oc-py-m"
/>
</div>
<div v-if="hasPeopleShares || hasLinkShares" class="oc-flex oc-flex-middle oc-mb-m">
<oc-icon v-if="hasPeopleShares" name="group" class="oc-mr-s" />
<oc-icon v-if="hasLinkShares" name="link" class="oc-mr-s" />
<span class="oc-text-small" v-text="shareLabel" />
<div
v-if="hasMemberShares || hasLinkShares"
class="oc-flex oc-flex-middle oc-mb-m oc-text-small"
>
<div v-if="hasMemberShares" class="oc-flex oc-flex-middle">
<oc-button
appearance="raw"
:aria-label="$gettext('Open the member panel')"
@click="expandMemberPanel"
>
<oc-icon name="group" class="oc-mr-s" />
</oc-button>
<span class="oc-mr-xs" v-text="memberShareLabel" />
<oc-button
appearance="raw"
:aria-label="$gettext('Open the member panel')"
size="small"
@click="expandMemberPanel"
>
<span class="oc-text-small" v-text="$gettext('Show')"></span>
</oc-button>
</div>
<div v-if="hasLinkShares" class="oc-flex oc-flex-middle">
<oc-icon name="link" class="oc-mr-s" />
<span v-text="linkShareLabel" />
</div>
</div>
<div>
<table class="details-table" :aria-label="detailsTableLabel">
Expand All @@ -30,7 +52,7 @@
<tr>
<th scope="col" class="oc-pr-s" v-text="$gettext('Manager')" />
<td>
<span v-if="!loadOwnersTask.isRunning" v-text="ownerUsernames" />
<span v-text="ownerUsernames" />
</td>
</tr>
<tr>
Expand All @@ -47,11 +69,10 @@
import { ref } from '@vue/composition-api'
import Mixins from '../../../mixins'
import MixinResources from '../../../mixins/resources'
import { mapGetters } from 'vuex'
import { mapActions, mapGetters } from 'vuex'
import { useTask } from 'vue-concurrency'
import { buildWebDavSpacesPath } from '../../../helpers/resources'
import { useStore } from 'web-pkg/src/composables'
import { clientService } from 'web-pkg/src/services'
import { spaceRoleManager } from '../../../helpers/share'
import SpaceQuota from '../../SpaceQuota.vue'

export default {
Expand All @@ -63,13 +84,7 @@ export default {
return $gettext('Details')
},
setup() {
const store = useStore()
const spaceImage = ref('')
const owners = ref([])
const graphClient = clientService.graphAuthenticated(
store.getters.configuration.server,
store.getters.getToken
)

const loadImageTask = useTask(function* (signal, ref) {
if (!ref.space?.spaceImageData) {
Expand All @@ -89,24 +104,14 @@ export default {
spaceImage.value = Buffer.from(fileContents).toString('base64')
})

const loadOwnersTask = useTask(function* (signal, ref) {
JammingBen marked this conversation as resolved.
Show resolved Hide resolved
const promises = []
for (const userId of ref.ownerUserIds) {
promises.push(graphClient.users.getUser(userId))
}

if (promises.length > 0) {
yield Promise.all(promises).then((resolvedData) => {
resolvedData.forEach((response) => {
owners.value.push(response.data)
})
})
}
})

return { loadImageTask, loadOwnersTask, spaceImage, owners }
return { loadImageTask, spaceImage }
},
computed: {
...mapGetters('Files', [
'highlightedFile',
'currentFileOutgoingCollaborators',
'currentFileOutgoingLinks'
]),
...mapGetters(['user']),

space() {
Expand All @@ -118,94 +123,69 @@ export default {
lastModifyDate() {
return this.formDateFromISO(this.space.mdate)
},
ownerUserIds() {
const permissions = this.space.spacePermissions?.filter((permission) =>
permission.roles.includes('manager')
)
if (!permissions.length) {
return []
}

const userIds = permissions.reduce((acc, item) => {
const ids = item.grantedTo.map((user) => user.user.id)
acc = acc.concat(ids)
return acc
}, [])

return [...new Set(userIds)]
},
ownerUsernames() {
const userId = this.user?.id
return this.owners
.map((owner) => {
if (owner.onPremisesSamAccountName === userId) {
return this.currentFileOutgoingCollaborators
.filter((share) => share.role.name === spaceRoleManager.name)
.map((share) => {
if (share.collaborator.name === userId) {
return this.$gettextInterpolate(this.$gettext('%{displayName} (me)'), {
displayName: owner.displayName
displayName: share.collaborator.displayName
})
}
return owner.displayName
return share.collaborator.displayName
})
.join(', ')
},
hasPeopleShares() {
return false // @TODO
hasMemberShares() {
return this.memberShareCount > 1
},
hasLinkShares() {
return false // @TODO
return this.linkShareCount > 0
},
peopleShareCount() {
return 0 // @TODO
memberShareCount() {
return this.currentFileOutgoingCollaborators.length
},
linkShareCount() {
return 0 // @TODO
return this.currentFileOutgoingLinks.length
},
shareLabel() {
let peopleString, linksString

if (this.hasPeopleShares) {
peopleString = this.$gettextInterpolate(
this.$ngettext(
'This space has been shared with %{peopleShareCount} person.',
'This space has been shared with %{peopleShareCount} people.',
this.peopleShareCount
),
{
peopleShareCount: this.peopleShareCount
}
)
}

if (this.hasLinkShares) {
linksString = this.$gettextInterpolate(
this.$ngettext(
'%{linkShareCount} link giving access.',
'%{linkShareCount} links giving access.',
this.linkShareCount
),
{
linkShareCount: this.linkShareCount
}
)
}

if (peopleString && linksString) {
return `${peopleString} ${linksString}`
}

if (peopleString) {
return peopleString
}

if (linksString) {
return linksString
}

return ''
memberShareLabel() {
return this.$gettextInterpolate(
this.$ngettext(
'This space has %{memberShareCount} member.',
'This space has %{memberShareCount} members.',
this.memberShareCount
),
{
memberShareCount: this.memberShareCount
}
)
},
linkShareLabel() {
return this.$gettextInterpolate(
this.$ngettext(
'%{linkShareCount} link giving access.',
'%{linkShareCount} links giving access.',
this.linkShareCount
),
{
linkShareCount: this.linkShareCount
}
)
}
},
mounted() {
this.loadImageTask.perform(this)
this.loadOwnersTask.perform(this)
},
methods: {
...mapActions('Files/sidebar', {
setSidebarPanel: 'setActivePanel',
closeSidebar: 'close'
}),

expandMemberPanel() {
this.setSidebarPanel('space-share-item')
}
}
}
</script>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<template>
<div
:data-testid="`collaborator-${isUser ? 'user' : 'group'}-item-${share.collaborator.name}`"
class="files-collaborators-collaborator oc-flex oc-flex-middle oc-py-xs"
:data-testid="`collaborator-${isUser || isSpace ? 'user' : 'group'}-item-${
share.collaborator.name
}`"
class="files-collaborators-collaborator oc-flex oc-flex-middle oc-py-xs oc-flex-between"
>
<div class="oc-width-2-3 oc-flex oc-flex-middle" style="gap: 10px">
<avatar-image
v-if="isUser"
v-if="isUser || isSpace"
:userid="share.collaborator.name"
:user-name="share.collaborator.displayName"
:width="48"
Expand Down Expand Up @@ -64,7 +66,7 @@
:share-id="share.id"
:existing-permissions="share.customPermissions"
:existing-role="share.role"
:allow-share-permission="!isOcis"
:allow-share-permission="!isOcis || isSpace"
class="files-collaborators-collaborator-role"
@optionChange="shareRoleChanged"
/>
Expand All @@ -77,11 +79,14 @@
@removeShare="removeShare"
/>
</div>
<div v-else>
<span class="oc-mr-xs" v-text="share.role.label" />
</div>
</div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex'
import { mapGetters, mapActions, mapState } from 'vuex'
import Mixins from '../../../../mixins'
import { DateTime } from 'luxon'

Expand Down Expand Up @@ -109,6 +114,7 @@ export default {
computed: {
...mapGetters('Files', ['highlightedFile']),
...mapGetters(['isOcis']),
...mapState(['user']),

shareType() {
return ShareTypes.getByValue(this.share.shareType)
Expand All @@ -126,6 +132,10 @@ export default {
return this.shareType === ShareTypes.user
},

isSpace() {
return this.shareType === ShareTypes.space
},

shareTypeText() {
return this.$gettext(this.shareType.label)
},
Expand All @@ -135,6 +145,11 @@ export default {
},

shareDisplayName() {
if (this.user.id === this.share.collaborator.name) {
return this.$gettextInterpolate(this.$gettext('%{collaboratorName} (me)'), {
collaboratorName: this.share.collaborator.displayName
})
}
return this.share.collaborator.displayName
},

Expand Down Expand Up @@ -243,7 +258,9 @@ export default {
saveShareChanges({ role, permissions, expirationDate }) {
const bitmask = role.hasCustomPermissions
? SharePermissions.permissionsToBitmask(permissions)
: SharePermissions.permissionsToBitmask(role.permissions(!this.isOcis))
: SharePermissions.permissionsToBitmask(
role.permissions(!this.isOcis || this.shareType === ShareTypes.space)
)
this.changeShare({
client: this.$client,
share: this.share,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
:class="collaboratorClass"
>
<avatar-image
v-if="isUser"
v-if="isUser || isSpace"
class="oc-mr-s"
:width="48"
:userid="item.value.shareWith"
Expand Down Expand Up @@ -67,6 +67,10 @@ export default {
return this.shareType === ShareTypes.user
},

isSpace() {
return this.shareType === ShareTypes.space
},

isGroup() {
return this.shareType === ShareTypes.group
},
Expand Down
Loading