Skip to content

Commit

Permalink
Add popup with reshare information + refactor share object
Browse files Browse the repository at this point in the history
Added popup with reshare information which lists the resharers, their
avatar and additional info.

Refactored the share/collaborator object to have a clearer distinction
between recipient/collaborator, owner and file owner, which eases the
manipulation that is needed in some places where all fields are copied
over for the resharers and owner.
  • Loading branch information
Vincent Petry committed Feb 26, 2020
1 parent 053bc4d commit a3d108d
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 81 deletions.
64 changes: 43 additions & 21 deletions apps/files/src/components/Collaborators/Collaborator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,35 @@
<oc-table-row v-if="$_reshareInformation" class="files-collaborators-collaborator-table-row-top">
<oc-table-cell shrink :colspan="firstColumn ? 2 : 1"></oc-table-cell>
<oc-table-cell colspan="2">
<div class="uk-text-meta uk-flex uk-flex-middle">
<oc-icon name="repeat" class="uk-preserve-width oc-icon-xsmall" />
<span class="uk-padding-remove uk-margin-xsmall-left uk-text-truncate files-collaborators-collaborator-reshare-information">{{ $_reshareInformation }}</span>
<div class="uk-text-meta">
<oc-button type="a" variation="raw" :id="$_resharerToggleId" :aria-label="$gettext('Show resharer details')">
<span class="uk-flex uk-flex-middle">
<oc-icon name="repeat" class="uk-preserve-width oc-icon-xsmall" />
<span class="uk-padding-remove uk-margin-xsmall-left uk-text-truncate files-collaborators-collaborator-reshare-information">{{ $_reshareInformation }}</span>
</span>
</oc-button>
<oc-drop
:dropId="$_resharerToggleId + '-drop'"
:toggle="'#' + $_resharerToggleId"
mode="click"
:options="{pos:'bottom-left', delayHide: 0}"
class="uk-width-large uk-margin-small-top"
ref="menu"
closeOnClick
>
<translate tag="h4">Shared by:</translate>
<ul class="uk-list uk-list-divider uk-overflow-hidden uk-margin-remove">
<li v-for="resharer in collaborator.resharers" :key="resharer.name">
<div class="uk-flex uk-flex-middle uk-flex-left">
<avatar-image class="uk-margin-small-right" :width="48" :userid="resharer.name" :userName="resharer.displayName" />
<div>
<span class="files-collaborators-resharer-name uk-text-bold">{{ resharer.displayName }}</span>
<span v-if="resharer.additionalInfo" class="uk-text-meta files-collaborators-resharer-additional-info">({{ resharer.additionalInfo }})</span>
</div>
</div>
</li>
</ul>
</oc-drop>
</div>
</oc-table-cell>
</oc-table-row>
Expand All @@ -19,7 +45,7 @@
</oc-table-cell>
<oc-table-cell shrink>
<div key="collaborator-avatar-loaded">
<avatar-image v-if="collaborator.shareType === shareTypes.user" class="uk-margin-small-right" :width="48" :userid="collaborator.name" :userName="collaborator.displayName" />
<avatar-image v-if="collaborator.shareType === shareTypes.user" class="uk-margin-small-right" :width="48" :userid="collaborator.collaborator.name" :userName="collaborator.collaborator.displayName" />
<div v-else key="collaborator-avatar-placeholder">
<oc-icon v-if="collaborator.shareType === shareTypes.group" class="uk-margin-small-right" name="group" size="large" key="avatar-group" />
<oc-icon v-else class="uk-margin-small-right" name="person" size="large" key="avatar-generic-person" />
Expand All @@ -29,18 +55,18 @@
<oc-table-cell>
<div class="uk-flex uk-flex-column uk-flex-center">
<div class="oc-text">
<span class="files-collaborators-collaborator-name uk-text-bold">{{ collaborator.displayName }}</span>
<span v-if="collaborator.shareType === shareTypes.user && collaborator.info.share_with_additional_info.length > 0" class="uk-text-meta files-collaborators-collaborator-additional-info">({{ collaborator.info.share_with_additional_info }})</span>
<span class="files-collaborators-collaborator-name uk-text-bold">{{ collaborator.collaborator.displayName }}</span>
<span v-if="collaborator.shareType === shareTypes.user && collaborator.collaborator.additionalInfo" class="uk-text-meta files-collaborators-collaborator-additional-info">({{ collaborator.collaborator.additionalInfo }})</span>
<translate
v-if="collaborator.name === user.id"
v-if="collaborator.collaborator.name === user.id"
translate-comment="Indicator for current user in collaborators list"
class="uk-text-meta files-collaborators-collaborator-additional-info"
>
(me)
</translate>
</div>
<span class="oc-text"><span class="files-collaborators-collaborator-role">{{ originalRole.label }}</span><template v-if="collaborator.expires"> | <translate :translate-params="{expires: formDateFromNow(collaborator.expires)}">Expires: %{expires}</translate></template></span>
<span class="uk-text-meta files-collaborators-collaborator-share-type" v-text="$_ocCollaborators_collaboratorType(collaborator.info.share_type)" />
<span class="uk-text-meta files-collaborators-collaborator-share-type" v-text="$_ocCollaborators_collaboratorType(collaborator.shareType)" />
</div>
</oc-table-cell>
<oc-table-cell shrink>
Expand Down Expand Up @@ -98,6 +124,10 @@ export default {
computed: {
...mapGetters(['user']),
$_resharerToggleId () {
return 'collaborator-' + this.collaborator.collaborator.name + '-resharer-details-toggle'
},
$_loadingSpinnerVisible () {
return this.modifiable && this.removalInProgress
},
Expand All @@ -110,36 +140,28 @@ export default {
$_isIndirectShare () {
// it is assumed that the "incoming" attribute only exists
// on shares coming from this.sharesTree which are all indirect
// on shares coming from this.collaborator.sTree which are all indirect
// and not related to the current folder
return this.collaborator.incoming || this.collaborator.outgoing
},
$_reshareInformation () {
if (this.user.id === this.collaborator.info.uid_owner) {
return null
}
if (this.collaborator.role.name === 'owner') {
if (this.collaborator.resharers) {
return this.collaborator.resharers.map(share => share.displayName).join(', ')
}
if (!this.collaborator.resharers) {
return null
}
return this.collaborator.info.displayname_owner
return this.collaborator.resharers.map(share => share.displayName).join(', ')
},
$_viaLabel () {
if (!this.$_isIndirectShare) {
return null
}
const translated = this.$gettext('Via %{folderName}')
return this.$gettextInterpolate(translated, { folderName: basename(this.collaborator.info.path) }, true)
return this.$gettextInterpolate(translated, { folderName: basename(this.collaborator.path) }, true)
},
$_viaRouterParams () {
const viaPath = this.collaborator.info.path
const viaPath = this.collaborator.path
return {
name: 'files-list',
params: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export default {
const exists = this.currentFileOutgoingCollaborators.find(existingCollaborator => {
return (
collaborator.value.shareWith === existingCollaborator.name &&
collaborator.value.shareWith === existingCollaborator.collaborator.name &&
parseInt(collaborator.value.shareType, 10) === existingCollaborator.shareType
)
})
Expand Down
67 changes: 33 additions & 34 deletions apps/files/src/components/FileSharingSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,14 @@ export default {
}
return {
name: this.user.id,
displayName: this.user.displayname,
info: {
share_type: 0,
share_with_additional_info: {}
collaborator: {
name: this.user.id,
displayName: this.user.displayname,
additionalInfo: null
},
owner: {},
fileOwner: {},
shareType: shareTypes.user,
role
}
},
Expand All @@ -155,36 +157,26 @@ export default {
if (!this.$_allIncomingShares.length) {
return null
}
const firstShare = this.$_allIncomingShares[0]
const ownerAsCollaborator = {
...firstShare,
collaborator: firstShare.fileOwner,
owner: {},
fileOwner: {},
key: 'owner-' + firstShare.id,
role: this.ownerRole
}
const ownerAsCollaborator = this.$_allIncomingShares[0]
let resharers = new Map()
const resharers = new Map()
this.$_allIncomingShares.forEach(share => {
if (share.info.uid_owner !== ownerAsCollaborator.info.uid_owner) {
resharers.set(share.info.uid_owner, {
name: share.info.uid_owner,
displayName: share.info.displayname_owner
})
if (share.owner.name !== ownerAsCollaborator.collaborator.name) {
resharers.set(share.owner.name, share.owner)
}
})
// make them unique then sort
resharers = Array.from(resharers.values()).sort(this.$_collaboratorsComparator.bind(this))
return {
...ownerAsCollaborator,
name: ownerAsCollaborator.info.uid_file_owner,
displayName: ownerAsCollaborator.info.displayname_file_owner,
key: 'owner-' + ownerAsCollaborator.info.id,
info: {
path: ownerAsCollaborator.info.path,
// set to user share for displaying user
share_type: shareTypes.user,
share_with_additional_info: ownerAsCollaborator.info.additional_info_file_owner || []
},
role: this.ownerRole,
resharers: resharers
}
ownerAsCollaborator.resharers = Array.from(resharers.values()).sort(this.$_collaboratorsComparator.bind(this))
return ownerAsCollaborator
},
/**
Expand Down Expand Up @@ -224,7 +216,10 @@ export default {
return [...this.currentFileOutgoingCollaborators]
.sort(this.$_collaboratorsComparator)
.map(collaborator => {
collaborator.key = 'collaborator-' + collaborator.info.id
collaborator.key = 'collaborator-' + collaborator.id
if (collaborator.owner.name !== collaborator.fileOwner.name && collaborator.owner.name !== this.user.id) {
collaborator.resharers = [collaborator.owner]
}
return collaborator
})
},
Expand All @@ -244,7 +239,7 @@ export default {
if (shares) {
shares.forEach((share) => {
if (share.outgoing && this.$_isCollaboratorShare(share)) {
share.key = 'indirect-collaborator-' + share.info.id
share.key = 'indirect-collaborator-' + share.id
allShares.push(share)
}
})
Expand Down Expand Up @@ -300,14 +295,18 @@ export default {
return userShareTypes.includes(collaborator.shareType)
},
$_collaboratorsComparator (c1, c2) {
const name1 = c1.displayName.toLowerCase().trim()
const name2 = c2.displayName.toLowerCase().trim()
// resharer entries have displayName on first level,
// but shares/collaborators have it under the collaborator attribute
const c1DisplayName = c1.collaborator ? c1.collaborator.displayName : c1.displayName
const c2DisplayName = c2.collaborator ? c2.collaborator.displayName : c2.displayName
const name1 = c1DisplayName.toLowerCase().trim()
const name2 = c2DisplayName.toLowerCase().trim()
// sorting priority 1: display name (lower case, ascending), 2: share type (groups first), 3: id (ascending)
if (name1 === name2) {
const c1GroupShare = c1.shareType === shareTypes.group
const c2GroupShare = c2.shareType === shareTypes.group
if (c1GroupShare === c2GroupShare) {
return textUtils.naturalSortCompare(c1.info.id + '', c2.info.id + '')
return textUtils.naturalSortCompare(c1.id + '', c2.id + '')
} else {
return c1GroupShare ? -1 : 1
}
Expand Down
26 changes: 24 additions & 2 deletions apps/files/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ function _buildLink (link, $gettext) {
}
}

function _fixAdditionalInfo (data) {
if (typeof data !== 'string') {
return null
}
return data
}

function _buildCollaboratorShare (s, file) {
const share = {
shareType: parseInt(s.share_type, 10),
Expand All @@ -249,8 +256,22 @@ function _buildCollaboratorShare (s, file) {
case (shareTypes.group): // group share
share.role = bitmaskToRole(s.permissions, file.type === 'folder')
share.permissions = s.permissions
share.name = s.share_with // this is the recipient userid, rename to uid or subject? add separate field userName?
share.displayName = s.share_with_displayname
// FIXME: SDK is returning empty object for additional info when empty
share.collaborator = {
name: s.share_with,
displayName: s.share_with_displayname,
additionalInfo: _fixAdditionalInfo(s.share_with_additional_info)
}
share.owner = {
name: s.uid_owner,
displayName: s.displayname_owner,
additionalInfo: _fixAdditionalInfo(s.additional_info_owner)
}
share.fileOwner = {
name: s.uid_file_owner,
displayName: s.displayname_file_owner,
additionalInfo: _fixAdditionalInfo(s.additional_info_file_owner)
}
// TODO: Refactor to work with roles / prepare for roles API
share.customPermissions = {
update: s.permissions & permissionsBitmask.update,
Expand All @@ -266,6 +287,7 @@ function _buildCollaboratorShare (s, file) {
if (typeof s.expiration === 'string' || s.expiration instanceof String) {
share.expires = Date.parse(s.expiration)
}
share.path = s.path

return share
}
Expand Down
5 changes: 2 additions & 3 deletions tests/acceptance/features/webUIResharing/reshareUsers.feature
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,8 @@ Feature: Resharing shared files with different permissions
When the user shares folder "simple-folder (2)" with user "User Three" as "Advanced permissions" with permissions "share, delete" using the webUI
And the user re-logs in as "user3" using the webUI
And the user opens the share dialog for folder "simple-folder (2)" using the webUI
Then the current collaborators list should have order "User Two,User One,User Three"
And user "User Two" should be listed as "Owner" in the collaborators list on the webUI
And user "User One" should be listed as "Resharer" in the collaborators list on the webUI
Then the current collaborators list should have order "User Two,User Three"
And user "User Two" should be listed as "Owner" reshared through "User One" in the collaborators list on the webUI
And user "user3" should have received a share with these details:
| field | value |
| uid_owner | user1 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,5 @@ Feature: Sharing files and folders with internal groups
And user "user3" has logged in using the webUI
And the user opens folder "simple-folder (2)" using the webUI
When the user opens the share dialog for folder "simple-empty-folder" using the webUI
Then user "User One" should be listed as "Owner" via "simple-folder (2)" in the collaborators list on the webUI
And user "User Two" should be listed as "Resharer" via "simple-folder (2)" in the collaborators list on the webUI
And the current collaborators list should have order "User One,User Two,User Three"
Then user "User One" should be listed as "Owner" reshared through "User Two" via "simple-folder (2)" in the collaborators list on the webUI
And the current collaborators list should have order "User One,User Three"
Original file line number Diff line number Diff line change
Expand Up @@ -512,9 +512,8 @@ Feature: Sharing files and folders with internal users
And user "user2" has shared folder "simple-folder (2)" with user "user3"
And user "user3" has logged in using the webUI
When the user opens the share dialog for folder "simple-folder (2)" using the webUI
Then user "User One" should be listed as "Owner" in the collaborators list on the webUI
And user "User Two" should be listed as "Resharer" in the collaborators list on the webUI
And the current collaborators list should have order "User One,User Two,User Three"
Then user "User One" should be listed as "Owner" reshared through "User Two" in the collaborators list on the webUI
And the current collaborators list should have order "User One,User Three"

@issue-2898
Scenario: see resource owner of parent shares in collaborators list
Expand All @@ -524,9 +523,8 @@ Feature: Sharing files and folders with internal users
And user "user3" has logged in using the webUI
And the user opens folder "simple-folder (2)" using the webUI
When the user opens the share dialog for folder "simple-empty-folder" using the webUI
Then user "User One" should be listed as "Owner" via "simple-folder (2)" in the collaborators list on the webUI
And user "User Two" should be listed as "Resharer" via "simple-folder (2)" in the collaborators list on the webUI
And the current collaborators list should have order "User One,User Two,User Three"
Then user "User One" should be listed as "Owner" reshared through "User Two" via "simple-folder (2)" in the collaborators list on the webUI
And the current collaborators list should have order "User One,User Three"

@issue-2898
Scenario: see resource owner for direct shares in "shared with me"
Expand Down
Loading

0 comments on commit a3d108d

Please sign in to comment.