diff options
author | Julius Härtl <jus@bitgrid.net> | 2019-01-29 12:22:34 +0100 |
---|---|---|
committer | Julius Härtl <jus@bitgrid.net> | 2019-03-01 20:56:18 +0100 |
commit | 53ac9bdda17e5141fceaec1a895f733c13c73c7a (patch) | |
tree | 19c81b212c82930e948dc98ae0cef5be5d427de3 | |
parent | 3777df64ae0a4a3d79e000541c0c512fb3ebf304 (diff) | |
download | nextcloud-server-53ac9bdda17e5141fceaec1a895f733c13c73c7a.tar.gz nextcloud-server-53ac9bdda17e5141fceaec1a895f733c13c73c7a.zip |
Implement frontend for search/rename
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Move to vuex
Signed-off-by: Julius Härtl <jus@bitgrid.net>
-rw-r--r-- | apps/files_sharing/package.json | 3 | ||||
-rw-r--r-- | apps/files_sharing/src/CollaborationView.vue | 36 | ||||
-rw-r--r-- | apps/files_sharing/src/collaborationresources.js | 37 | ||||
-rw-r--r-- | apps/files_sharing/src/components/CollectionList.vue | 198 | ||||
-rw-r--r-- | apps/files_sharing/src/components/CollectionListItem.vue | 70 | ||||
-rw-r--r-- | apps/files_sharing/src/files_sharing.js | 11 | ||||
-rw-r--r-- | apps/files_sharing/src/services/collections.js | 152 | ||||
-rw-r--r-- | apps/files_sharing/src/sharetabview.js | 1 | ||||
-rw-r--r-- | apps/files_sharing/src/views/CollaborationView.vue | 188 | ||||
-rw-r--r-- | core/Controller/CollaborationResourcesController.php | 2 | ||||
-rw-r--r-- | core/src/OCP/collaboration.js | 2 |
11 files changed, 438 insertions, 262 deletions
diff --git a/apps/files_sharing/package.json b/apps/files_sharing/package.json index 9aa194cbaed..f619d898249 100644 --- a/apps/files_sharing/package.json +++ b/apps/files_sharing/package.json @@ -17,7 +17,8 @@ "dependencies": { "nextcloud-axios": "^0.1.3", "nextcloud-vue": "^0.5.0", - "vue": "^2.5.17" + "vue": "^2.5.17", + "vuex": "^3.1.0" }, "devDependencies": { "@babel/core": "^7.1.0", diff --git a/apps/files_sharing/src/CollaborationView.vue b/apps/files_sharing/src/CollaborationView.vue new file mode 100644 index 00000000000..b7da5100650 --- /dev/null +++ b/apps/files_sharing/src/CollaborationView.vue @@ -0,0 +1,36 @@ +<!-- + - @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<template> + <collection-list type="files"></collection-list> +</template> + +<script> + import CollectionList from './components/CollectionList' + + export default { + name: 'CollaborationView', + components: { + CollectionList + } + } +</script> diff --git a/apps/files_sharing/src/collaborationresources.js b/apps/files_sharing/src/collaborationresources.js index 0370c367bb7..5ad35c192de 100644 --- a/apps/files_sharing/src/collaborationresources.js +++ b/apps/files_sharing/src/collaborationresources.js @@ -21,46 +21,19 @@ */ import Vue from 'vue' +import Vuex from 'vuex' import { PopoverMenu } from 'nextcloud-vue' import ClickOutside from 'vue-click-outside' import { VTooltip } from 'v-tooltip' +import { Store } from './services/collections'; Vue.prototype.t = t; Vue.component('PopoverMenu', PopoverMenu) Vue.directive('ClickOutside', ClickOutside) Vue.directive('Tooltip', VTooltip) +Vue.use(Vuex); -import View from './views/CollaborationView' +import View from './CollaborationView' -let selectAction = {}; -let icons = {}; -let types = {}; -console.log('register types'); - -/* TODO: temporary data for testing */ -window.OCP.Collaboration.registerType('calendar', { - action: () => { - return new Promise((resolve, reject) => { - var id = window.prompt("calendar id", "1"); - resolve(id); - }) - }, - icon: 'icon-calendar-dark', - typeString: 'calendar', - link: (id) => '#' + id, -}); -window.OCP.Collaboration.registerType('contact', { - action: () => { - return new Promise((resolve, reject) => { - var id = window.prompt("contacts id", "1"); - resolve(id); - }) - }, - icon: 'icon-contacts-dark', - link: (id) => '#' + id, - /** used in "Link to a {typeString}" */ - typeString: 'contact' -}); - -export { Vue, View } +export { Vue, View, Store } diff --git a/apps/files_sharing/src/components/CollectionList.vue b/apps/files_sharing/src/components/CollectionList.vue new file mode 100644 index 00000000000..afadafec87f --- /dev/null +++ b/apps/files_sharing/src/components/CollectionList.vue @@ -0,0 +1,198 @@ +<!-- + - @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<template> + <ul id="shareWithList" class="shareWithList" v-if="collections"> + <li @click="showSelect"> + <div class="avatar"><span class="icon-category-integration icon-white"></span></div> + <multiselect v-model="value" :options="options" :placeholder="placeholder" tag-placeholder="Create a new collection" ref="select" @select="select" @search-change="search" label="title" track-by="title" :reset-after="true" :limit="5"> + <template slot="singleLabel" slot-scope="props"> + <span class="option__desc"> + <span class="option__title">{{ props.option.title }}</span> + </span> + </template> + <template slot="option" slot-scope="props"> + <span class="option__wrapper"> + <span v-if="props.option.class" :class="props.option.class" class="avatar"></span> + <avatar v-else :displayName="props.option.title" :allowPlaceholder="true"></avatar> + <span class="option__title">{{ props.option.title }}</span> + </span> + </template> + </multiselect> + </li> + <collection-list-item v-for="collection in collections" :collection="collection" :key="collection.id" /> + </ul> +</template> + +<style lang="scss" scoped> + .multiselect { + width: 100%; + margin-left: 3px; + } + span.avatar { + padding: 16px; + display: block; + background-repeat: no-repeat; + background-position: center; + opacity: 0.7; + &:hover { + opacity: 1; + } + } + + /** TODO provide white icon in core */ + .icon-category-integration.icon-white { + filter: invert(100%); + padding: 16px; + display: block; + background-repeat: no-repeat; + background-position: center; + } + + .option__wrapper { + display: flex; + .avatar { + display: block; + background-color: var(--color-background-darker) !important; + } + .option__title { + padding: 4px; + } + } + +</style> +<style lang="scss"> + /** TODO check why this doesn't work when scoped */ + .shareWithList .multiselect:not(.multiselect--active ) .multiselect__tags { + border: none !important; + input::placeholder { + color: var(--color-main-text); + } + } +</style> + +<script> + import { Multiselect, Avatar } from 'nextcloud-vue'; + import CollectionListItem from '../components/CollectionListItem'; + + const METHOD_CREATE_COLLECTION = 0; + const METHOD_ADD_TO_COLLECTION = 1; + export default { + name: 'CollectionList', + components: { + CollectionListItem, + Avatar, + Multiselect: Multiselect, + }, + props: { + 'type': { + type: String, + default: '' + } + }, + data() { + return { + selectIsOpen: false, + generatingCodes: false, + codes: undefined, + value: null, + model: {}, + searchCollections: [] + }; + }, + mounted() { + this.$store.dispatch('fetchCollectionsByResource', { + resourceType: this.type, + resourceId: this.$root.model.id + }) + }, + computed: { + collections() { + return this.$store.getters.collectionsByResource(this.type, this.$root.model.id) + }, + placeholder() { + return t('files_sharing', 'Add to a collection'); + }, + options() { + let options = []; + let types = window.OCP.Collaboration.getTypes().sort(); + for(let type in types) { + options.push({ + method: METHOD_CREATE_COLLECTION, + type: types[type], + title: window.OCP.Collaboration.getLabel(types[type]), + class: window.OCP.Collaboration.getIcon(types[type]), + action: () => window.OCP.Collaboration.trigger(types[type]) + }) + } + for(let index in this.searchCollections) { + if (this.collections.findIndex((collection) => collection.id === this.searchCollections[index].id) === -1) { + options.push({ + method: METHOD_ADD_TO_COLLECTION, + title: this.searchCollections[index].name, + collectionId: this.searchCollections[index].id + }) + } + } + return options; + } + }, + methods: { + select(selectedOption, id) { + if (selectedOption.method === METHOD_CREATE_COLLECTION) { + selectedOption.action().then((id) => { + this.$store.dispatch('createCollection', { + baseResourceType: this.type, + baseResourceId: this.$root.model.id, + resourceType: selectedOption.type, + resourceId: id, + name: this.$root.model.name, + }) + }).catch((e) => { + console.error('No resource selected', e); + }); + } + + if (selectedOption.method === METHOD_ADD_TO_COLLECTION) { + this.$store.dispatch('addResourceToCollection', { + collectionId: selectedOption.collectionId, resourceType: this.type, resourceId: this.$root.model.id + }) + } + }, + search(query) { + this.$store.dispatch('search', query).then((collections) => { + this.searchCollections = collections + }) + }, + showSelect() { + this.selectIsOpen = true + this.$refs.select.$el.focus() + }, + hideSelect() { + this.selectIsOpen = false + }, + isVueComponent(object) { + return object._isVue + } + } + } +</script> diff --git a/apps/files_sharing/src/components/CollectionListItem.vue b/apps/files_sharing/src/components/CollectionListItem.vue index f22d5238d6c..8dab92e356f 100644 --- a/apps/files_sharing/src/components/CollectionListItem.vue +++ b/apps/files_sharing/src/components/CollectionListItem.vue @@ -21,12 +21,16 @@ --> <template> - <li class="collection-list"> + <li class="collection-list" v-click-outside="hideDetails"> <avatar :displayName="collection.name" :allowPlaceholder="true"></avatar> - <span class="username" title="">{{ collection.name }}</span> + <span class="username" title="" @click="showDetails" v-if="this.newName === ''">{{ collection.name }}</span> + <form v-else @submit.prevent="renameCollection"> + <input type="text" v-model="newName" autocomplete="off" autocapitalize="off"> + <input type="submit" value="" class="icon-confirm"> + </form> <transition name="fade"> <div class="linked-icons" v-if="!detailsOpen"> - <a v-for="resource in collection.resources" :href="getLink(resource)" v-tooltip="resource.name" :key="resource.id"><span :class="getIcon(resource)"></span></a> + <a v-for="resource in collection.resources" :href="resource.link" v-tooltip="resource.name" :key="resource.id"><span :class="getIcon(resource)"></span></a> </div> </transition> @@ -40,10 +44,10 @@ </div> </span> <transition name="fade"> - <ul class="resource-list-details" v-if="detailsOpen" v-click-outside="hideDetails"> + <ul class="resource-list-details" v-if="detailsOpen"> <li v-for="resource in collection.resources"> - <a :href="getLink(resource)"><span :class="getIcon(resource)"></span> {{ resource.name || '' }}</a> - <span class="icon-delete"></span> + <a :href="resource.link"><span :class="getIcon(resource)"></span><span class="resource-name">{{ resource.name || '' }}</span></a> + <span class="icon-delete" @click="removeResource(collection, resource)"></span> </li> </ul> </transition> @@ -51,6 +55,8 @@ </template> <script> + import service from '../services/collections'; + import { Avatar } from 'nextcloud-vue'; export default { @@ -66,7 +72,8 @@ data() { return { isOpen: false, - detailsOpen: false + detailsOpen: false, + newName: '', } }, computed: { @@ -81,21 +88,14 @@ text: t('files_sharing', 'Details'), }, { - action: () => { }, + action: () => this.openRename(), icon: 'icon-rename', text: t('files_sharing', 'Rename collection'), - },{ - action: () => { }, - icon: 'icon-delete', - text: t('files_sharing', 'Remove collection'), } ] }, getIcon() { - return (resource) => [window.OCP.Collaboration.getIcon(resource.type), resource.iconClass] - }, - getLink() { - return (resource) => window.OCP.Collaboration.getLink(resource.type, resource.id) + return (resource) => [resource.iconClass] } }, methods: { @@ -108,8 +108,27 @@ toggle() { this.isOpen = !this.isOpen }, + showDetails() { + this.detailsOpen = true + }, hideDetails() { this.detailsOpen = false + }, + removeResource(collection, resource) { + this.$store.dispatch('removeResource', { + collectionId: collection.id, resourceType: resource.type, resourceId: resource.id + }) + }, + openRename() { + this.newName = this.collection.name; + }, + renameCollection() { + this.$store.dispatch('renameCollection', { + collectionId: this.collection.id, + name: this.newName + }).then((collection) => { + this.newName = ''; + }); } } } @@ -138,7 +157,15 @@ } .collection-list { flex-wrap: wrap; + height: auto; + + form, .username { + flex-basis: 10%; + flex-grow: 1; + display: flex; + } .resource-list-details { + flex-basis: 100%; width: 100%; li { display: flex; @@ -146,14 +173,23 @@ a { flex-grow: 1; padding: 3px; + max-width: calc(100% - 30px); + display: flex; } } span { display: inline-block; - padding: 8px; vertical-align: top; margin-right: 10px; } + span.resource-name { + text-overflow: ellipsis; + overflow: hidden; + position: relative; + vertical-align: top; + white-space: nowrap; + flex-grow: 1; + } } } </style> diff --git a/apps/files_sharing/src/files_sharing.js b/apps/files_sharing/src/files_sharing.js index 9009bdde7ec..56bd2f67613 100644 --- a/apps/files_sharing/src/files_sharing.js +++ b/apps/files_sharing/src/files_sharing.js @@ -1,10 +1,5 @@ -// CSP config for webpack dynamic chunk loading -// eslint-disable-next-line -__webpack_nonce__ = btoa(OC.requestToken) -// Correct the root of the app for chunk loading -// OC.linkTo matches the apps folders -// eslint-disable-next-line +__webpack_nonce__ = btoa(OC.requestToken) __webpack_public_path__ = OC.linkTo('files_sharing', 'js/dist/') import '../js/app' @@ -26,10 +21,8 @@ window.OCP.Collaboration.registerType('files', { }, false); }) }, - link: (id) => OC.generateUrl('/f/') + id, - icon: 'nav-icon-files', /** used in "Link to a {typeString}" */ - typeString: 'file' + typeString: t('files_sharing', 'file') }); window.OCA.Sharing = OCA.Sharing diff --git a/apps/files_sharing/src/services/collections.js b/apps/files_sharing/src/services/collections.js index bd45b8f3c32..9406ddfffc0 100644 --- a/apps/files_sharing/src/services/collections.js +++ b/apps/files_sharing/src/services/collections.js @@ -21,39 +21,165 @@ */ import axios from 'nextcloud-axios'; +import Vuex from 'vuex'; +import Vue from 'vue'; class Service { constructor() { - this.service = axios.create(); + this.http = axios; + this.baseUrl = OC.linkToOCS(`collaboration/resources`); } listCollection(collectionId) { - return this.service.get(`/collaboration/resources/collections/${collectionId}`) + return this.http.get(`${this.baseUrl}collections/${collectionId}`) } - addResource(collectionId, resource) { - return this.service.post(`/collaboration/resources/collections/${collectionId}`) + renameCollection(collectionId, collectionName) { + const resourceBase = OC.linkToOCS(`collaboration/resources/collections`, 2); + return this.http.put(`${resourceBase}${collectionId}?format=json`, { + collectionName + }).then(result => { + return result.data.ocs.data; + }) } - removeResource() { - return this.service.post(`/collaboration/resources/collections/${collectionId}`) + getCollectionsByResource(resourceType, resourceId) { + const resourceBase = OC.linkToOCS(`collaboration/resources/${resourceType}`); + return this.http.get(`${resourceBase}${resourceId}?format=json`) + .then(result => { + return result.data.ocs.data; + }) + .catch(error => { + console.error(error); + return Promise.reject(error); + }); } - createCollectionOnResource(resourceType, resourceId) { - return this.service.post(`/collaboration/resources/${resourceType}/${resourceId}`) + createCollection(resourceType, resourceId, name) { + const resourceBase = OC.linkToOCS(`collaboration/resources/${resourceType}`, 2); + return this.http.post(`${resourceBase}${resourceId}?format=json`, { + name: name + }) + .then((response) => { + return response.data.ocs.data + }) + .catch(error => { + console.error(error); + return Promise.reject(error); + }); } - getCollectionByResource(resourceType, resourceId) { - return this.service.get(`/collaboration/resources/${resourceType}/${resourceId}`) + addResource(collectionId, resourceType, resourceId) { + resourceId = '' + resourceId; + const resourceBase = OC.linkToOCS(`collaboration/resources/collections`, 2); + return this.http.post(`${resourceBase}${collectionId}?format=json`, { + resourceType, + resourceId + }).then((response) => { + return response.data.ocs.data + }); } - getProviders() { + removeResource(collectionId, resourceType, resourceId) { + return this.http.delete(`${this.baseUrl}/collections/${collectionId}`, { params: { resourceType, resourceId } } ) + .then((response) => { + return response.data.ocs.data + }); + } + search(query) { + const searchBase = OC.linkToOCS(`collaboration/resources/collections/search`); + return this.http.get(`${searchBase}%25${query}%25?format=json`) + .then((response) => { + return response.data.ocs.data + }); } - search() { +} + +const service = new Service(); +const StoreModule = { + state: { + collections: [] + }, + mutations: { + addCollections (state, collections) { + state.collections = collections; + }, + addCollection (state, collection) { + state.collections.push(collection) + }, + removeCollection (state, collectionId) { + state.collections = state.collections.filter(item => item.id !== collectionId) + }, + updateCollection(state, collection) { + let index = state.collections.findIndex((_item) => _item.id === collection.id) + if (index !== -1) { + Vue.set(state.collections, index, collection); + } else { + state.collections.push(collection) + } + } + }, + getters: { + collectionsByResource(state) { + return (resourceType, resourceId) => { + return state.collections.filter((collection) => { + return typeof collection.resources.find((resource) => resource && resource.id === ''+resourceId && resource.type === resourceType) !== 'undefined' + }) + } + }, + getSearchResults(state) { + return (term) => { + return state.collections.filter((collection) => collection.name.contains(term)) + } + } + }, + actions: { + fetchCollectionsByResource(context, {resourceType, resourceId}) { + return service.getCollectionsByResource(resourceType, resourceId).then((collections) => { + context.commit('addCollections', collections) + return collections; + }); + }, + createCollection(context, {baseResourceType, baseResourceId, resourceType, resourceId, name}) { + return service.createCollection(baseResourceType, baseResourceId, name).then((collection) => { + context.commit('addCollection', collection) + context.dispatch('addResourceToCollection', { + collectionId: collection.id, + resourceType, resourceId + }) + }) + }, + renameCollection(context, {collectionId, name}) { + return service.renameCollection(collectionId, name).then((collection) => { + context.commit('updateCollection', collection) + return collection + }) + }, + addResourceToCollection(context, {collectionId, resourceType, resourceId}) { + return service.addResource(collectionId, resourceType, resourceId).then((collection) => { + context.commit('updateCollection', collection) + return collection + }) + }, + removeResource(context, {collectionId, resourceType, resourceId}) { + return service.removeResource(collectionId, resourceType, resourceId).then((collection) => { + if (collection.resources.length > 0) { + context.commit('updateCollection', collection) + } else { + context.commit('removeCollection', collectionId) + } + }) + }, + search(context, query) { + return service.search(query) + } } } -export default new Service; +const Store = () => new Vuex.Store(StoreModule); + +export default service; +export { StoreModule, Store }; diff --git a/apps/files_sharing/src/sharetabview.js b/apps/files_sharing/src/sharetabview.js index 8f8a655b00e..eaed674775a 100644 --- a/apps/files_sharing/src/sharetabview.js +++ b/apps/files_sharing/src/sharetabview.js @@ -87,6 +87,7 @@ var vm = new Resources.Vue({ el: '#collaborationResources', render: h => h(Resources.View), + store: Resources.Store(), data: { model: this.model.toJSON() }, diff --git a/apps/files_sharing/src/views/CollaborationView.vue b/apps/files_sharing/src/views/CollaborationView.vue deleted file mode 100644 index 99a5fb43df6..00000000000 --- a/apps/files_sharing/src/views/CollaborationView.vue +++ /dev/null @@ -1,188 +0,0 @@ -<template> - <div :class="{'icon-loading': !collections}"> - <ul id="shareWithList" class="shareWithList" v-if="collections"> - <li @click="showSelect"> - <div class="avatar"><span class="icon-category-integration icon-white"></span></div> - <multiselect v-model="value" :options="options" :placeholder="placeholder" tag-placeholder="Create a new collection" ref="select" @select="select" label="title" track-by="title" :reset-after="true"> - <template slot="singleLabel" slot-scope="props"> - <span class="option__desc"> - <span class="option__title">{{ props.option.title }}</span></span> - </template> - <template slot="option" slot-scope="props"> - <span class="option__wrapper"> - <span v-if="props.option.class" :class="props.option.class" class="avatar"></span> - <avatar v-else :displayName="props.option.title" :allowPlaceholder="true"></avatar> - <span class="option__title">{{ props.option.title }}</span> - </span> - </template> - </multiselect> - </li> - <collection-list-item v-for="collection in collections" :collection="collection" :key="collection.id" /> - </ul> - </div> -</template> - -<style lang="scss" scoped> - .multiselect { - width: 100%; - margin-left: 3px; - } - span.avatar { - padding: 16px; - display: block; - background-repeat: no-repeat; - background-position: center; - opacity: 0.7; - &:hover { - opacity: 1; - } - } - - /** TODO provide white icon in core */ - .icon-category-integration.icon-white { - filter: invert(100%); - padding: 16px; - display: block; - background-repeat: no-repeat; - background-position: center; - } - - .option__wrapper { - display: flex; - .avatar { - display: block; - background-color: var(--color-background-darker) !important; - } - .option__title { - padding: 4px; - } - } - -</style> -<style lang="scss"> - /** TODO check why this doesn't work when scoped */ - .shareWithList .multiselect:not(.multiselect--active ) .multiselect__tags { - border: none !important; - input::placeholder { - color: var(--color-main-text); - } - } -</style> - -<script> - import { Multiselect, Avatar } from 'nextcloud-vue'; - import CollectionListItem from '../components/CollectionListItem'; - import axios from 'nextcloud-axios'; - - export default { - name: 'CollaborationView', - components: { - CollectionListItem, - Avatar, - Multiselect: Multiselect, - }, - data() { - return { - selectIsOpen: false, - generatingCodes: false, - codes: undefined, - value: null, - model: {}, - collections: null - }; - }, - mounted() { - let resourceId = this.$root.model.id - /** TODO move to service */ - const resourceBase = OC.linkToOCS(`collaboration/resources/files`); - axios.get(`${resourceBase}${resourceId}?format=json`, { - headers: { - 'OCS-APIRequest': true, - 'Content-Type': 'application/json; charset=UTF-8' - } - }).then((response) => { - this.collections = response.data.ocs.data - }); - }, - computed: { - placeholder() { - return t('files_sharing', 'Add to a collection'); - }, - options() { - let options = []; - let types = window.OCP.Collaboration.getTypes(); - for(let type in types) { - options.push({ - type: types[type], - title: window.OCP.Collaboration.getLabel(types[type]), - class: window.OCP.Collaboration.getIcon(types[type]), - action: () => window.OCP.Collaboration.trigger(types[type]) - }) - } - for(let index in this.collections) { - options.push({ - title: this.collections[index].name - }) - } - return options; - } - }, - created: function() { - }, - methods: { - select(selectedOption, id) { - selectedOption.action().then((id) => { - console.log('Create a new collection with') - console.log('This file ', this.$root.model.id) - console.log('Selected resource ', selectedOption.type, id) - this.createCollection(this.$root.model.id, selectedOption.type, id) - }).catch((e) => { - console.error('No resource selected'); - }); - }, - showSelect() { - this.selectIsOpen = true - this.$refs.select.$el.focus() - }, - hideSelect() { - this.selectIsOpen = false - }, - isVueComponent(object) { - return object._isVue - }, - createCollection(resourceIdBase, resourceType, resourceId) { - /** TODO move to service */ - const resourceBase = OC.linkToOCS(`collaboration/resources/files`, 2); - axios.post(`${resourceBase}${resourceIdBase}?format=json`, { - name: 'Example collection' - }, { - headers: { - 'OCS-APIRequest': true, - 'Content-Type': 'application/json; charset=UTF-8' - } - }).then((response) => { - let newCollection = response.data.ocs.data - console.log('Add new collection', newCollection) - this.collections.push(newCollection) - this.addResourceToCollection(newCollection.id, resourceType.toString(), resourceId.toString()) - }); - }, - addResourceToCollection(collectionId, resourceType, resourceId) { - /** TODO move to service */ - const resourceBase = OC.linkToOCS(`collaboration/resources/collections`, 2); - return axios.post(`${resourceBase}${collectionId}?format=json`, { - resourceType, - resourceId - }, { - headers: { - 'OCS-APIRequest': true, - 'Content-Type': 'application/json; charset=UTF-8' - } - }).then((response) => { - console.log('Add new collection', response.data.ocs.data) - this.collections.find((_item) => _item.id === collectionId).resources.push(response.data.ocs.data) - }); - } - } - } -</script> diff --git a/core/Controller/CollaborationResourcesController.php b/core/Controller/CollaborationResourcesController.php index 565e0ba4739..a15904a4c2a 100644 --- a/core/Controller/CollaborationResourcesController.php +++ b/core/Controller/CollaborationResourcesController.php @@ -193,7 +193,7 @@ class CollaborationResourcesController extends OCSController { return [ 'id' => $collection->getId(), 'name' => $collection->getName(), - 'resources' => array_map([$this, 'prepareResources'], $collection->getResources()), + 'resources' => array_values(array_filter(array_map([$this, 'prepareResources'], $collection->getResources()))), ]; } diff --git a/core/src/OCP/collaboration.js b/core/src/OCP/collaboration.js index a74834e741e..54b580406ed 100644 --- a/core/src/OCP/collaboration.js +++ b/core/src/OCP/collaboration.js @@ -49,7 +49,7 @@ export default { return Object.keys(types); }, getIcon(type) { - return types[type].icon; + return types[type].icon || ''; }, getLabel(type) { return t('files_sharing', 'Link to a {label}', { label: types[type].typeString || type }, 1) |