Skip to content

Commit

Permalink
Merge pull request #2929 from owncloud/share-indirect-outgoing-shares
Browse files Browse the repository at this point in the history
Display indirect outgoing shares in collaborators panel
  • Loading branch information
Vincent Petry authored Jan 29, 2020
2 parents 5e46263 + b87485c commit 61e1498
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 78 deletions.
21 changes: 15 additions & 6 deletions apps/files/src/components/Collaborators/Collaborator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</oc-table-row>
<oc-table-row class="files-collaborators-collaborator-table-row-info">
<oc-table-cell shrink>
<oc-button v-if="collaborator.modifiable" :ariaLabel="$gettext('Delete share')" @click="$emit('onDelete', collaborator)" variation="raw" class="files-collaborators-collaborator-delete">
<oc-button v-if="modifiable" :ariaLabel="$gettext('Delete share')" @click="$emit('onDelete', collaborator)" variation="raw" class="files-collaborators-collaborator-delete">
<oc-icon name="close" />
</oc-button>
<oc-icon v-else name="lock"></oc-icon>
Expand All @@ -43,7 +43,7 @@
</div>
</oc-table-cell>
<oc-table-cell shrink>
<oc-button v-if="collaborator.modifiable" :aria-label="$gettext('Edit share')" @click="$emit('onEdit', collaborator)" variation="raw" class="files-collaborators-collaborator-edit">
<oc-button v-if="modifiable" :aria-label="$gettext('Edit share')" @click="$emit('onEdit', collaborator)" variation="raw" class="files-collaborators-collaborator-edit">
<oc-icon name="edit" />
</oc-button>
</oc-table-cell>
Expand All @@ -59,7 +59,16 @@ import Mixins from '../../mixins/collaborators'
export default {
name: 'Collaborator',
props: ['collaborator'],
props: {
collaborator: {
type: Object,
required: true
},
modifiable: {
type: Boolean,
default: false
}
},
mixins: [
Mixins
],
Expand All @@ -71,11 +80,11 @@ export default {
computed: {
...mapGetters(['user']),
$_isIndirectIncomingShare () {
$_isIndirectShare () {
// it is assumed that the "incoming" attribute only exists
// on shares coming from this.sharesTree which are all indirect
// and not related to the current folder
return this.collaborator.incoming
return this.collaborator.incoming || this.collaborator.outgoing
},
$_reshareInformation () {
Expand All @@ -91,7 +100,7 @@ export default {
},
$_getViaLabel () {
if (!this.$_isIndirectIncomingShare) {
if (!this.$_isIndirectShare) {
return null
}
const translated = this.$gettext('Via %{folderName}')
Expand Down
209 changes: 144 additions & 65 deletions apps/files/src/components/FileSharingSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,42 @@
key="no-reshare-permissions-message"
v-text="noResharePermsMessage"
/>
<transition-group v-if="$_ocCollaborators.length > 0"
id="files-collaborators-list"
class="uk-list uk-list-divider uk-overflow-hidden"
enter-active-class="uk-animation-slide-left-medium"
leave-active-class="uk-animation-slide-right-medium uk-animation-reverse"
name="custom-classes-transition"
tag="ul">
<li v-for="collaborator in $_ocCollaborators" :key="collaborator.key">
<collaborator :collaborator="collaborator" @onDelete="$_ocCollaborators_deleteShare" @onEdit="$_ocCollaborators_editShare"/>
</li>
</transition-group>
<div v-else-if="!$_sharesLoading" key="oc-collaborators-no-results"><translate>No collaborators</translate></div>
<section v-if="$_ownerAndResharers.length > 0" class="uk-margin-medium-bottom">
<div class="uk-text-bold">
<translate>Owner</translate>
</div>
<ul class="uk-list uk-list-divider uk-overflow-hidden">
<li v-for="collaborator in $_ownerAndResharers" :key="collaborator.key">
<collaborator :collaborator="collaborator"/>
</li>
</ul>
</section>
<section v-if="$_directOutgoingShares.length > 0" class="uk-margin-medium-bottom">
<div class="uk-text-bold">
<translate>Shares</translate>
</div>
<transition-group id="files-collaborators-list"
class="uk-list uk-list-divider uk-overflow-hidden"
enter-active-class="uk-animation-slide-left-medium"
leave-active-class="uk-animation-slide-right-medium uk-animation-reverse"
name="custom-classes-transition"
tag="ul">
<li v-for="collaborator in $_directOutgoingShares" :key="collaborator.key">
<collaborator :collaborator="collaborator" :modifiable="true" @onDelete="$_ocCollaborators_deleteShare" @onEdit="$_ocCollaborators_editShare"/>
</li>
</transition-group>
</section>
<section v-if="$_indirectOutgoingShares.length > 0" class="uk-margin-medium-bottom">
<div class="uk-text-bold">
<translate>Via Parent</translate>
</div>
<ul class="uk-list uk-list-divider uk-overflow-hidden">
<li v-for="collaborator in $_indirectOutgoingShares" :key="collaborator.key">
<collaborator :collaborator="collaborator"/>
</li>
</ul>
</section>
<div v-if="$_noCollaborators && !$_sharesLoading" key="oc-collaborators-no-results"><translate>No collaborators</translate></div>
</template>
</div>
<div :aria-hidden="visiblePanel != 'newCollaborator'" :inert="visiblePanel != 'newCollaborator'">
Expand Down Expand Up @@ -106,59 +130,27 @@ export default {
$_sharesLoading () {
return this.sharesLoading && this.incomingSharesLoading
},
$_allIncomingShares () {
const allShares = [...this.incomingShares]
const parentPaths = getParentPaths(this.highlightedFile.path, true)
if (parentPaths.length === 0) {
return []
}
// remove root entry
parentPaths.pop()
parentPaths.forEach((parentPath) => {
const shares = this.sharesTree[parentPath]
if (shares) {
shares.forEach((share) => {
if (share.incoming) {
allShares.push(share)
}
})
}
})
return allShares
$_noCollaborators () {
return this.$_ownerAndResharers.length === 0 &&
this.$_directOutgoingShares.length === 0 &&
this.$_indirectOutgoingShares.length === 0
},
$_ocCollaborators () {
const shares = this.shares
.filter(collaborator => this.$_ocCollaborators_isUser(collaborator) || this.$_ocCollaborators_isGroup(collaborator))
.sort((c1, c2) => {
const name1 = c1.displayName.toLowerCase().trim()
const name2 = c2.displayName.toLowerCase().trim()
// sorting priority 1: display name (lower case, ascending), 2: share type (groups first), 3: id (ascending)
if (name1 === name2) {
if (this.$_ocCollaborators_isGroup(c1) === this.$_ocCollaborators_isGroup(c2)) {
return parseInt(c1.info.id, 10) < parseInt(c2.info.id, 10) ? -1 : 1
} else {
return this.$_ocCollaborators_isGroup(c1) ? -1 : 1
}
} else {
return textUtils.naturalSortCompare(name1, name2)
}
})
.map(collaborator => {
collaborator.key = 'collaborator-' + collaborator.info.id
collaborator.modifiable = true
return collaborator
})
$_ownerAndResharers () {
const ownerArray = this.$_shareOwnerAsCollaborator ? [this.$_shareOwnerAsCollaborator] : []
return [
...ownerArray,
...this.$_resharersAsCollaborators
]
},
$_shareOwnerAsCollaborator () {
if (!this.$_allIncomingShares.length) {
return shares
return null
}
const resharers = new Map()
const firstShare = this.$_allIncomingShares[0]
const owner = {
return {
...firstShare,
name: firstShare.info.uid_file_owner,
displayName: firstShare.info.displayname_file_owner,
Expand All @@ -169,9 +161,13 @@ export default {
share_type: '' + shareTypes.user, // most code requires string..
share_with_additional_info: firstShare.info.additional_info_file_owner || []
},
role: this.ownerRole,
modifiable: false
role: this.ownerRole
}
},
$_resharersAsCollaborators () {
const resharers = new Map()
const owner = this.$_shareOwnerAsCollaborator
this.$_allIncomingShares.forEach(share => {
if (share.info.uid_owner !== owner.name) {
Expand All @@ -186,17 +182,83 @@ export default {
// set to user share for displaying user
share_type: '' + shareTypes.user, // most code requires string..
share_with_additional_info: share.info.additional_info_owner || []
},
modifiable: false
}
})
}
})
Array.prototype.unshift.apply(shares, Array.from(resharers.values()))
shares.unshift(owner)
// make them unique then sort
return Array.from(resharers.values()).sort(this.$_collaboratorsComparator.bind(this))
},
/**
* Returns all incoming shares, direct and indirect
*
* @return {Array.<Object>} list of incoming shares
*/
$_allIncomingShares () {
// direct incoming shares
const allShares = [...this.incomingShares]
// indirect incoming shares
const parentPaths = getParentPaths(this.highlightedFile.path, true)
if (parentPaths.length === 0) {
return []
}
// remove root entry
parentPaths.pop()
return shares
parentPaths.forEach((parentPath) => {
const shares = this.sharesTree[parentPath]
if (shares) {
shares.forEach((share) => {
if (share.incoming) {
allShares.push(share)
}
})
}
})
return allShares
},
$_directOutgoingShares () {
// direct outgoing shares
return this.shares
.filter(this.$_isCollaboratorShare.bind(this))
.sort(this.$_collaboratorsComparator.bind(this))
.map(collaborator => {
collaborator.key = 'collaborator-' + collaborator.info.id
return collaborator
})
},
$_indirectOutgoingShares () {
const allShares = []
const parentPaths = getParentPaths(this.highlightedFile.path, true)
if (parentPaths.length === 0) {
return []
}
// remove root entry
parentPaths.pop()
parentPaths.forEach((parentPath) => {
const shares = this.sharesTree[parentPath]
if (shares) {
shares.forEach((share) => {
if (share.outgoing && this.$_isCollaboratorShare(share)) {
share.key = 'indirect-collaborator-' + share.info.id
allShares.push(share)
}
})
}
})
return allShares
},
$_ocCollaborators_canShare () {
return this.highlightedFile.canShare()
},
Expand All @@ -213,6 +275,23 @@ export default {
'loadIncomingShares',
'incomingSharesClearState'
]),
$_isCollaboratorShare (collaborator) {
return this.$_ocCollaborators_isUser(collaborator) || this.$_ocCollaborators_isGroup(collaborator)
},
$_collaboratorsComparator (c1, c2) {
const name1 = c1.displayName.toLowerCase().trim()
const name2 = c2.displayName.toLowerCase().trim()
// sorting priority 1: display name (lower case, ascending), 2: share type (groups first), 3: id (ascending)
if (name1 === name2) {
if (this.$_ocCollaborators_isGroup(c1) === this.$_ocCollaborators_isGroup(c2)) {
return parseInt(c1.info.id, 10) < parseInt(c2.info.id, 10) ? -1 : 1
} else {
return this.$_ocCollaborators_isGroup(c1) ? -1 : 1
}
} else {
return textUtils.naturalSortCompare(name1, name2)
}
},
$_ocCollaborators_editShare (share) {
this.currentShare = share
this.visiblePanel = 'editCollaborator'
Expand Down
8 changes: 8 additions & 0 deletions changelog/unreleased/2897
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Enhancement: Show indirect outgoing shares in collaborators list

Whenever outgoing shares exist on any parent resource from the currently viewed resource,
the collaborators panel will now show these outgoing shares with a link to jump to the matching parent
resource.

https://github.com/owncloud/phoenix/issues/2897
https://github.com/owncloud/phoenix/pull/2929
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ Feature: Sharing files and folders with internal users
| fileName | expectedIndicators |
| sub-folder | user-indirect |

@skip @yetToImplement @issue-2897
@issue-2897
Scenario: sharing details of items inside a shared folder
Given user "user3" has been created with default attributes
And user "user1" has uploaded file with content "test" to "/simple-folder/lorem.txt"
Expand All @@ -434,26 +434,26 @@ Feature: Sharing files and folders with internal users
When the user opens the share dialog for file "lorem.txt" using the webUI
Then user "User Two" should be listed as "Editor" via "simple-folder" in the collaborators list on the webUI

@skip @yetToImplement @issue-2897
@issue-2897
Scenario: sharing details of items inside a re-shared folder
Given user "user3" has been created with default attributes
And user "user1" has uploaded file with content "test" to "/simple-folder/lorem.txt"
And user "user1" has shared folder "simple-folder" with user "user2"
And user "user2" has shared folder "simple-folder" with user "user3"
And user "user2" has shared folder "simple-folder (2)" with user "user3"
And user "user2" 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 Three" should be listed as "Editor" via "simple-folder" in the collaborators list on the webUI
Then user "User Three" should be listed as "Editor" via "simple-folder (2)" in the collaborators list on the webUI
When the user opens the share dialog for file "lorem.txt" using the webUI
Then user "User Three" should be listed as "Editor" via "simple-folder" in the collaborators list on the webUI
Then user "User Three" should be listed as "Editor" via "simple-folder (2)" in the collaborators list on the webUI

@skip @yetToImplement @issue-2897
@issue-2897
Scenario: sharing details of items inside a shared folder shared with multiple users
Given user "user3" has been created with default attributes
And user "user1" has created folder "/simple-folder/sub-folder"
And user "user1" has uploaded file with content "test" to "/simple-folder/sub-folder/lorem.txt"
And user "user1" has shared folder "simple-folder" with user "user2"
And user "user1" has shared folder "/simple-folder/sub-folder" with user "user3"
And user "user1" has shared folder "simple-folder/sub-folder" with user "user3"
And user "user1" has logged in using the webUI
And the user opens folder "simple-folder/sub-folder" directly on the webUI
When the user opens the share dialog for file "lorem.txt" using the webUI
Expand Down

0 comments on commit 61e1498

Please sign in to comment.