Skip to content

Commit

Permalink
Merge pull request #2877 from owncloud/dummy-share-indicators
Browse files Browse the repository at this point in the history
Share indicator for direct and indirect shares in file list
  • Loading branch information
Vincent Petry authored Jan 23, 2020
2 parents 7bdbf0b + 71ee5d2 commit 5c055ff
Show file tree
Hide file tree
Showing 15 changed files with 855 additions and 6 deletions.
88 changes: 86 additions & 2 deletions apps/files/src/components/AllFilesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<span class="oc-visually-hidden" v-text="favoritesHeaderText" />
<oc-star id="files-table-header-star" aria-hidden="true" class="uk-display-block uk-disabled" />
</div>
<div></div>
<div class="uk-text-truncate uk-text-meta uk-width-expand" v-translate>Name</div>
<div :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-hidden' : _sidebarOpen }" class="uk-text-meta uk-width-small" v-translate>Size</div>
<div type="head" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-hidden' : _sidebarOpen }" class="uk-text-nowrap uk-text-meta uk-width-small" v-translate>Updated</div>
Expand All @@ -28,6 +29,14 @@
class="uk-margin-small-left"
/>
</div>
<div>
<oc-button v-if="$_isUserShare(item)" class="file-row-share-indicator uk-text-middle" :aria-label="$_shareUserIconLabel(item)" @click="$_openSideBar(item, 'files-sharing')" variation="raw">
<oc-icon name="group" class="uk-text-middle" size="small" :variation="$_shareUserIconVariation(item)"/>
</oc-button>
<oc-button v-if="$_isLinkShare(item)" class="file-row-share-indicator uk-text-middle" :aria-label="$_shareLinkIconLabel(item)" @click="$_openSideBar(item, 'file-link')" variation="raw">
<oc-icon name="link" class="uk-text-middle" size="small" :variation="$_shareLinkIconVariation(item)"/>
</oc-button>
</div>
<div class="uk-text-meta uk-text-nowrap uk-width-small" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-hidden' : _sidebarOpen }">
{{ item.size | fileSize }}
</div>
Expand Down Expand Up @@ -66,9 +75,14 @@
<script>
import FileList from './FileList.vue'
import { mapGetters, mapActions, mapState } from 'vuex'
import { shareTypes } from '../helpers/shareTypes'
import { getParentPaths } from '../helpers/path'
import Mixins from '../mixins'
import FileActions from '../fileactions'
import intersection from 'lodash/intersection'
const userShareTypes = [shareTypes.user, shareTypes.group, shareTypes.guest, shareTypes.remote]
export default {
components: {
Expand All @@ -92,6 +106,50 @@ export default {
...mapActions('Files', ['loadFolder', 'setFilterTerm', 'markFavorite',
'setHighlightedFile', 'setPublicLinkPassword']),
$_openSideBar (item, sideBarName) {
this.$emit('sideBarOpen', item, sideBarName)
},
$_isDirectUserShare (item) {
return (intersection(userShareTypes, item.shareTypes).length > 0)
},
$_isIndirectUserShare (item) {
return (item.isReceivedShare() || intersection(userShareTypes, this.$_shareTypesIndirect).length > 0)
},
$_isDirectLinkShare (item) {
return (item.shareTypes.indexOf(shareTypes.link) >= 0)
},
$_isIndirectLinkShare (item) {
return (this.$_shareTypesIndirect.indexOf(shareTypes.link) >= 0)
},
$_isUserShare (item) {
return this.$_isDirectUserShare(item) || this.$_isIndirectUserShare(item)
},
$_isLinkShare (item) {
return this.$_isDirectLinkShare(item) || this.$_isIndirectLinkShare(item)
},
$_shareUserIconVariation (item) {
return this.$_isDirectUserShare(item) ? 'active' : 'passive'
},
$_shareLinkIconVariation (item) {
return this.$_isDirectLinkShare(item) ? 'active' : 'passive'
},
$_shareUserIconLabel (item) {
return this.$_isDirectUserShare(item) ? this.$gettext('Directly shared with collaborators') : this.$gettext('Shared with collaborators through one of the parent folders')
},
$_shareLinkIconLabel (item) {
return this.$_isDirectLinkShare(item) ? this.$gettext('Directly shared with links') : this.$gettext('Shared with links through one of the parent folders')
},
$_ocFilesFolder_getFolder () {
this.setFilterTerm('')
let absolutePath
Expand All @@ -106,7 +164,8 @@ export default {
client: this.$client,
absolutePath: absolutePath,
$gettext: this.$gettext,
routeName: this.$route.name
routeName: this.$route.name,
loadSharesTree: !this.publicPage()
}).then(() => {
const scrollTo = this.$route.query.scrollTo
if (scrollTo && this.activeFiles.length > 0) {
Expand Down Expand Up @@ -157,13 +216,38 @@ export default {
},
computed: {
...mapState(['route']),
...mapGetters('Files', ['loadingFolder', 'activeFiles', 'quota', 'filesTotalSize', 'activeFilesCount', 'currentFolder']),
...mapGetters('Files', ['loadingFolder', 'activeFiles', 'quota', 'filesTotalSize', 'activeFilesCount', 'currentFolder', 'sharesTree']),
...mapGetters(['configuration']),
item () {
return this.$route.params.item
},
$_shareTypesIndirect () {
const parentPaths = getParentPaths(this.currentFolder.path, true)
if (parentPaths.length === 0) {
return []
}
// remove root entry
parentPaths.pop()
const shareTypes = {}
parentPaths.forEach((parentPath) => {
// TODO: optimize for performance by skipping once we got all known types
const shares = this.sharesTree[parentPath]
if (shares) {
shares.forEach((share) => {
// note: no distinction between incoming and outgoing shares as we display the same
// indirect indicator for them
shareTypes[share.info.share_type] = true
})
}
})
return Object.keys(shareTypes).map(shareType => parseInt(shareType, 10))
},
quotaVisible () {
return (
!this.publicPage() &&
Expand Down
7 changes: 5 additions & 2 deletions apps/files/src/components/Collaborators/NewCollaborator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
:items="autocompleteResults"
:itemsLoading="autocompleteInProgress"
:placeholder="$_ocCollaborationStatus_autocompletePlacholder"
@update:input="onAutocompleteInput"
@update:input="$_onAutocompleteInput"
:filter="filterRecipients"
:fillOnSelection="false"
id="oc-sharing-autocomplete"
Expand Down Expand Up @@ -76,6 +76,7 @@
</template>

<script>
import _ from 'lodash'
import { mapActions, mapGetters } from 'vuex'
import Mixins from '../../mixins/collaborators'
import { roleToBitmask } from '../../helpers/collaborators'
Expand Down Expand Up @@ -119,6 +120,8 @@ export default {
this.$nextTick(() => {
this.$refs.ocSharingAutocomplete.focus()
})
this.$_onAutocompleteInput = _.debounce(this.$_onAutocompleteInput, 1000)
},
methods: {
Expand All @@ -136,7 +139,7 @@ export default {
this.$emit('close')
},
onAutocompleteInput (value) {
$_onAutocompleteInput (value) {
if (
value.length <
parseInt(this.user.capabilities.files_sharing.search_min_length, 10)
Expand Down
36 changes: 36 additions & 0 deletions apps/files/src/helpers/path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Return all absolute parent paths.
*
* For example if passing in "a/b/c" it will return
* ["a/b", "a", ""]
* If an empty string or "/" is passed in, an empty array is returned.
*
* @param {String} path path to process
* @param {Boolean} includeCurrent whether to include the current path (with leading slash)
* @return {Array.<String>} parent paths
*/
export function getParentPaths (path, includeCurrent = false) {
if (path === '' || path === '/') {
return []
}

if (path.charAt(0) !== '/') {
path = '/' + path
}

const paths = []
const sections = path.split('/')

if (includeCurrent) {
paths.push(path)
}

sections.pop()
while (sections.length > 0) {
paths.push(sections.join('/'))
sections.pop()
}

return paths
}
11 changes: 11 additions & 0 deletions apps/files/src/helpers/shareTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Share types
*/
export const shareTypes = {
user: 0,
group: 1,
userGroup: 2,
link: 3,
guest: 4,
remote: 6
}
98 changes: 97 additions & 1 deletion apps/files/src/store/actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import moment from 'moment'
import _ from 'lodash'
import { getParentPaths } from '../helpers/path'
import { bitmaskToRole, permissionsBitmask } from '../helpers/collaborators'
const { default: PQueue } = require('p-queue')

function _buildFile (file) {
let ext = ''
Expand Down Expand Up @@ -40,6 +43,17 @@ function _buildFile (file) {
permissions: file.fileInfo['{http://owncloud.org/ns}permissions'] || '',
etag: file.fileInfo['{DAV:}getetag'],
sharePermissions: file.fileInfo['{http://open-collaboration-services.org/ns}share-permissions'],
shareTypes: (function () {
let shareTypes = file.fileInfo['{http://owncloud.org/ns}share-types']
if (shareTypes) {
shareTypes = _.chain(shareTypes).filter((xmlvalue) =>
(xmlvalue.namespaceURI === 'http://owncloud.org/ns' && xmlvalue.nodeName.split(':')[1] === 'share-type')
).map((xmlvalue) =>
parseInt(xmlvalue.textContent || xmlvalue.text, 10)
).value()
}
return shareTypes || []
}()),
privateLink: file.fileInfo['{http://owncloud.org/ns}privatelink'],
owner: {
username: file.fileInfo['{http://owncloud.org/ns}owner-id'],
Expand All @@ -62,6 +76,9 @@ function _buildFile (file) {
},
isMounted: function () {
return this.permissions.indexOf('M') >= 0
},
isReceivedShare: function () {
return this.permissions.indexOf('S') >= 0
}
})
}
Expand Down Expand Up @@ -241,7 +258,7 @@ function _buildShare (s, file) {
}

export default {
loadFolder (context, { client, absolutePath, $gettext, routeName }) {
loadFolder (context, { client, absolutePath, $gettext, routeName, loadSharesTree = false }) {
context.commit('UPDATE_FOLDER_LOADING', true)
context.commit('CLEAR_CURRENT_FILES_LIST')

Expand Down Expand Up @@ -278,6 +295,12 @@ export default {
currentFolder: res[0],
files: res.splice(1)
})
if (loadSharesTree) {
context.dispatch('loadSharesTree', {
client: client,
path: absolutePath
})
}
}
}
context.dispatch('resetFileSelection')
Expand Down Expand Up @@ -631,6 +654,79 @@ export default {
resetSearch (context) {
context.commit('SET_SEARCH_TERM', '')
},
/**
* Prune all branches of the shares tree that are
* unrelated to the given path
*/
pruneSharesTreeOutsidePath (context, path) {
context.commit('SHARESTREE_PRUNE_OUTSIDE_PATH', path)
},
/**
* Load shares for each parent of the given path.
* This will add new entries into the shares tree and will
* not remove unrelated existing ones.
*/
loadSharesTree (context, { client, path }) {
context.commit('SHARESTREE_ERROR', null)
// prune shares tree cache for all unrelated paths, keeping only
// existing relevant parent entries
context.dispatch('pruneSharesTreeOutsidePath', path)

const parentPaths = getParentPaths(path, true)
const sharesTree = {}

if (!parentPaths.length) {
return Promise.resolve()
}

// remove last entry which is the root folder
parentPaths.pop()

context.commit('SHARESTREE_LOADING', true)

const shareQueriesQueue = new PQueue({ concurrency: 2 })
const shareQueriesPromises = []
parentPaths.forEach((queryPath) => {
// skip already cached paths
if (context.getters.sharesTree[queryPath]) {
return
}
sharesTree[queryPath] = []
// query the outgoing share information for each of the parent paths
shareQueriesPromises.push(shareQueriesQueue.add(() =>
client.shares.getShares(queryPath, { reshares: true })
.then(data => {
data.forEach(element => {
sharesTree[queryPath].push({ ..._buildShare(element.shareInfo, { type: 'folder' }), outgoing: true })
})
})
.catch(error => {
console.error('SHARESTREE_ERROR', error)
context.commit('SHARESTREE_ERROR', error.message)
context.commit('SHARESTREE_LOADING', false)
})
))
// query the incoming share information for each of the parent paths
shareQueriesPromises.push(shareQueriesQueue.add(() =>
client.shares.getShares(queryPath, { reshares: true, shared_with_me: true })
.then(data => {
data.forEach(element => {
sharesTree[queryPath].push({ ..._buildShare(element.shareInfo, { type: 'folder' }), incoming: true })
})
})
.catch(error => {
console.error('SHARESTREE_ERROR', error)
context.commit('SHARESTREE_ERROR', error.message)
context.commit('SHARESTREE_LOADING', false)
})
))
})

return Promise.all(shareQueriesPromises).then(() => {
context.commit('SHARESTREE_ADD', sharesTree)
context.commit('SHARESTREE_LOADING', false)
})
},
dragOver (context, value) {
context.commit('DRAG_OVER', value)
},
Expand Down
4 changes: 3 additions & 1 deletion apps/files/src/store/getters.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ export default {
sharesLoading: state => {
return state.sharesLoading
},
sharesTree: state => state.sharesTree,
sharesTreeLoading: state => state.sharesTreeLoading,
loadingFolder: state => {
return state.loadingFolder
return state.loadingFolder || state.sharesTreeLoading
},
quota: state => {
return state.quota
Expand Down
Loading

0 comments on commit 5c055ff

Please sign in to comment.