Skip to content

Commit

Permalink
Merge pull request #6455 from owncloud/spaces-collaborators
Browse files Browse the repository at this point in the history
[full-ci]  Implement people sharing for spaces
  • Loading branch information
kulmann committed Mar 7, 2022
2 parents 9d1c1b1 + 7a4b939 commit eb10385
Show file tree
Hide file tree
Showing 29 changed files with 1,425 additions and 368 deletions.
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) {
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

0 comments on commit eb10385

Please sign in to comment.