Implement circle sharing by @jakobroehrl

Signed-off-by: Georg Ehrke <developer@georgehrke.com>
This commit is contained in:
Georg Ehrke 2019-10-16 18:59:15 +02:00
parent 5066abb6eb
commit 3e2fcb4c07
No known key found for this signature in database
GPG Key ID: 9D98FD9380A1CB43
7 changed files with 113 additions and 32 deletions

View File

@ -156,7 +156,8 @@
.avatar { .avatar {
width: 32px; width: 32px;
height: 32px; height: 32px;
background-color: #DBDBDB; background-color: var(--color-border-dark);
background-size: 16px;
} }
.avatar.published { .avatar.published {

View File

@ -20,6 +20,7 @@
* *
*/ */
@include icon-black-white('briefcase', 'calendar', 5); @include icon-black-white('briefcase', 'calendar', 5);
@include icon-black-white('circle', 'calendar', 1);
@include icon-black-white('color-picker', 'calendar', 1); @include icon-black-white('color-picker', 'calendar', 1);
@include icon-black-white('embed', 'calendar', 1); @include icon-black-white('embed', 'calendar', 1);
@include icon-black-white('eye', 'calendar', 4); @include icon-black-white('eye', 'calendar', 4);

1
img/circle.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57 57" width="64" height="64"><path d="M7 29A21 21 0 0 1 29 7m10 40a21 21 0 0 1-29-8m29-29a21 21 0 0 1 8 29" fill="none" stroke="#000" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><circle cx="29" cy="7" r="7"/><circle cx="39" cy="-10" r="7" transform="rotate(90)"/><circle cx="39" cy="-47" r="7" transform="rotate(90)"/></svg>

After

Width:  |  Height:  |  Size: 400 B

View File

@ -1,6 +1,9 @@
<!-- <!--
- @copyright Copyright (c) 2019 Georg Ehrke <oc.list@georgehrke.com> - @copyright Copyright (c) 2019 Georg Ehrke <oc.list@georgehrke.com>
- @copyright Copyright (c) 2019 Jakob Röhrl <jakob.roehrl@web.de>
-
- @author Georg Ehrke <oc.list@georgehrke.com> - @author Georg Ehrke <oc.list@georgehrke.com>
- @author Jakob Röhrl <jakob.roehrl@web.de>
- -
- @license GNU AGPL version 3 or any later version - @license GNU AGPL version 3 or any later version
- -
@ -35,7 +38,7 @@
track-by="user" track-by="user"
label="user" label="user"
@search-change="findSharee" @search-change="findSharee"
@input="shareCalendar"> @change="shareCalendar">
<span slot="noResult">{{ $t('calendar', 'No users or groups') }}</span> <span slot="noResult">{{ $t('calendar', 'No users or groups') }}</span>
</multiselect> </multiselect>
</li> </li>
@ -43,7 +46,9 @@
<script> <script>
import client from '../../../services/caldavService.js' import client from '../../../services/caldavService.js'
import HttpClient from '@nextcloud/axios'
import debounce from 'debounce' import debounce from 'debounce'
import { generateOcsUrl } from '@nextcloud/router'
export default { export default {
name: 'CalendarListItemSharingSearch', name: 'CalendarListItemSharingSearch',
@ -69,14 +74,16 @@ export default {
* @param {string} data.displayName the displayName * @param {string} data.displayName the displayName
* @param {string} data.uri the sharing principalScheme uri * @param {string} data.uri the sharing principalScheme uri
* @param {Boolean} data.isGroup is this a group ? * @param {Boolean} data.isGroup is this a group ?
* @param {Boolean} data.isCircle is this a circle-group ?
*/ */
shareCalendar({ user, displayName, uri, isGroup }) { shareCalendar({ user, displayName, uri, isGroup, isCircle }) {
this.$store.dispatch('shareCalendar', { this.$store.dispatch('shareCalendar', {
calendar: this.calendar, calendar: this.calendar,
user, user,
displayName, displayName,
uri, uri,
isGroup isGroup,
isCircle
}) })
}, },
@ -87,11 +94,10 @@ export default {
*/ */
findSharee: debounce(async function(query) { findSharee: debounce(async function(query) {
const hiddenPrincipalSchemes = [] const hiddenPrincipalSchemes = []
const hiddenUrls = []
this.calendar.shares.forEach((share) => { this.calendar.shares.forEach((share) => {
hiddenPrincipalSchemes.push(share.uri) hiddenPrincipalSchemes.push(share.uri)
}) })
const hiddenUrls = []
if (this.$store.getters.getCurrentUserPrincipal) { if (this.$store.getters.getCurrentUserPrincipal) {
hiddenUrls.push(this.$store.getters.getCurrentUserPrincipal.url) hiddenUrls.push(this.$store.getters.getCurrentUserPrincipal.url)
} }
@ -101,35 +107,100 @@ export default {
this.isLoading = true this.isLoading = true
this.usersOrGroups = [] this.usersOrGroups = []
if (query.length > 0) {
const results = await client.principalPropertySearchByDisplayname(query)
this.usersOrGroups = results.reduce((list, result) => {
if (hiddenPrincipalSchemes.includes(result.principalScheme)) {
return list
}
if (hiddenUrls.includes(result.url)) {
return list
}
if (['GROUP', 'INDIVIDUAL'].indexOf(result.calendarUserType) > -1) { if (query.length > 0) {
const davPromise = this.findShareesFromDav(query, hiddenPrincipalSchemes, hiddenUrls)
const ocsPromise = this.findShareesFromCircles(query, hiddenPrincipalSchemes, hiddenUrls)
return Promise.all([davPromise, ocsPromise]).then(([davResults, ocsResults]) => {
this.usersOrGroups = [
...davResults,
...ocsResults
]
this.isLoading = false
this.inputGiven = true
})
} else {
this.inputGiven = false
this.isLoading = false
}
}, 500),
/**
*
* @param {String} query The search query
* @param {String[]} hiddenPrincipals A list of principals to exclude from search results
* @param {String[]} hiddenUrls A list of urls to exclude from search results
* @returns {Promise<Object[]>}
*/
async findShareesFromDav(query, hiddenPrincipals, hiddenUrls) {
return client.principalPropertySearchByDisplayname(query)
.then(results => {
return results.reduce((list, result) => {
if (hiddenPrincipals.includes(result.principalScheme)) {
return list
}
if (hiddenUrls.includes(result.url)) {
return list
}
// Don't show resources and rooms
if (!['GROUP', 'INDIVIDUAL'].includes(result.calendarUserType)) {
return list
}
const isGroup = result.calendarUserType === 'GROUP' const isGroup = result.calendarUserType === 'GROUP'
list.push({ list.push({
user: result[isGroup ? 'groupId' : 'userId'], user: result[isGroup ? 'groupId' : 'userId'],
displayName: result.displayname, displayName: result.displayname,
icon: isGroup ? 'icon-group' : 'icon-user', icon: isGroup ? 'icon-group' : 'icon-user',
uri: result.principalScheme, uri: result.principalScheme,
isGroup isGroup,
isCircle: false,
isNoUser: isGroup,
search: query
}) })
} return list
return list }, [])
}, []) })
this.isLoading = false },
this.inputGiven = true /**
} else { *
this.inputGiven = false * @param {String} query The search query
this.isLoading = false * @param {String[]} hiddenPrincipals A list of principals to exclude from search results
} * @param {String[]} hiddenUrls A list of urls to exclude from search results
}, 500) * @returns {Promise<Object[]>}
*/
async findShareesFromCircles(query, hiddenPrincipals, hiddenUrls) {
return HttpClient.get(generateOcsUrl('apps/files_sharing/api/v1') + 'sharees', {
params: {
format: 'json',
search: query,
perPage: 200,
itemType: 'principals'
}
})
.catch(() => [])
.then(results => results.data.ocs.data.circles)
.then(circles => {
return circles.filter((circle) => {
return !hiddenPrincipals.includes('principal:principals/circles/' + circle.value.shareWith)
})
})
.then(circles => {
return circles.map(circle => ({
user: circle.label,
displayName: circle.label,
icon: 'icon-circle',
uri: 'principal:principals/circles/' + circle.value.shareWith,
isGroup: false,
isCircle: true,
isNoUser: true,
search: query
}))
})
}
} }
} }
</script> </script>

View File

@ -25,6 +25,7 @@
<template slot="icon"> <template slot="icon">
<div v-if="sharee.isGroup" class="avatar icon-group" /> <div v-if="sharee.isGroup" class="avatar icon-group" />
<div v-else-if="sharee.isCircle" class="avatar icon-circle" />
<Avatar v-else :user="sharee.id" :display-name="sharee.displayName" /> <Avatar v-else :user="sharee.id" :display-name="sharee.displayName" />
</template> </template>

View File

@ -117,15 +117,20 @@ export function mapDavCollectionToCalendar(calendar) {
*/ */
export function mapDavShareeToSharee(sharee) { export function mapDavShareeToSharee(sharee) {
const id = btoa(sharee.href) const id = btoa(sharee.href)
const name = sharee['common-name'] let name = sharee['common-name']
? sharee['common-name'] ? sharee['common-name']
: id : sharee.href
if (sharee.href.startsWith('principal:principals/groups/') && name === sharee.href) {
name = sharee.href.substr(28)
}
return { return {
displayName: name, displayName: name,
id: id, id: id,
writeable: sharee.access[0].endsWith('read-write'), writeable: sharee.access[0].endsWith('read-write'),
isGroup: sharee.href.indexOf('principal:principals/groups/') === 0, isGroup: sharee.href.indexOf('principal:principals/groups/') === 0,
isCircle: sharee.href.indexOf('principal:principals/circles/') === 0,
uri: sharee.href uri: sharee.href
} }
} }

View File

@ -199,12 +199,13 @@ const mutations = {
* @param {string} data.uri the sharing principalScheme uri * @param {string} data.uri the sharing principalScheme uri
* @param {Boolean} data.isGroup is this a group ? * @param {Boolean} data.isGroup is this a group ?
*/ */
shareCalendar(state, { calendar, user, displayName, uri, isGroup }) { shareCalendar(state, { calendar, user, displayName, uri, isGroup, isCircle }) {
const newSharee = { const newSharee = {
displayName, displayName,
id: user, id: user,
writeable: false, writeable: false,
isGroup, isGroup,
isCircle,
uri uri
} }
state.calendarsById[calendar.id].shares.push(newSharee) state.calendarsById[calendar.id].shares.push(newSharee)
@ -567,11 +568,11 @@ const actions = {
* @param {string} data.uri the sharing principalScheme uri * @param {string} data.uri the sharing principalScheme uri
* @param {Boolean} data.isGroup is this a group ? * @param {Boolean} data.isGroup is this a group ?
*/ */
async shareCalendar(context, { calendar, user, displayName, uri, isGroup }) { async shareCalendar(context, { calendar, user, displayName, uri, isGroup, isCircle }) {
// Share calendar with entered group or user // Share calendar with entered group or user
try { try {
await calendar.dav.share(uri) await calendar.dav.share(uri)
context.commit('shareCalendar', { calendar, user, displayName, uri, isGroup }) context.commit('shareCalendar', { calendar, user, displayName, uri, isGroup, isCircle })
} catch (error) { } catch (error) {
throw error throw error
} }