diff --git a/api/permissions.mjs b/api/permissions.mjs deleted file mode 100644 index 766dd53a..00000000 --- a/api/permissions.mjs +++ /dev/null @@ -1,178 +0,0 @@ -import { permissions } from '@kalisio/kdk/core.common.js' - -// FIXME: this file is duplicated in the api folder -// Indeed, permissions files are usually isomorphic files. However, when we tried to create a common folder -// to share it between frontend/backend but experienced problems with webpack - -function defineEventAbilities (subject, can, cannot, app) { - if (subject && subject._id) { - if (subject.organisations) { - subject.organisations.forEach(organisation => { - const role = permissions.Roles[organisation.permissions] - if (role >= permissions.Roles.member) { - if (organisation._id) { - // The unique identifier of a service is its path not its name. - // Indeed we have for instance a 'events' service in each organisation. - can('service', organisation._id.toString() + '/events') - can('service', organisation._id.toString() + '/event-logs') - can('service', organisation._id.toString() + '/event-templates') - can('service', organisation._id.toString() + '/archived-events') - can('service', organisation._id.toString() + '/archived-event-logs') - // A user can access the templates to create an event - can('read', 'event-templates', { context: organisation._id }) - // A user can create an event - can('create', 'events', { context: organisation._id }) - // A user can access events in which he is a participant - can('read', 'events', { context: organisation._id, 'participants._id': subject._id }) - // A coordinator can manage his events - can('all', 'events', { context: organisation._id, 'coordinators._id': subject._id }) - // BUG: adding org level participant/coordinator generates a bug because the org owner - // has the same ID than the org itself causing everybody accessing the event - // can('read', 'events', { context: organisation._id, 'participants._id': organisation._id }) - // can('all', 'events', { context: organisation._id, 'coordinators._id': organisation._id }) - can('read', 'archived-events', { context: organisation._id, 'participants._id': subject._id }) - can('read', 'archived-events', { context: organisation._id, 'coordinators._id': subject._id }) - } - if (subject.groups) { - subject.groups.forEach(group => { - if (group._id && group.context && (group.context.toString() === organisation._id.toString())) { - // A user can access events in which his group is a participant - can('read', 'events', { context: organisation._id, 'participants._id': group._id }) - // A coordinator can manage events in which his group is a coordinator - can('all', 'events', { context: organisation._id, 'coordinators._id': group._id }) - can('read', 'archived-events', { context: organisation._id, 'participants._id': group._id }) - can('read', 'archived-events', { context: organisation._id, 'coordinators._id': group._id }) - } - }) - } - if (subject.tags) { - subject.tags.forEach(tag => { - if (tag._id && tag.context && (tag.context.toString() === organisation._id.toString())) { - // A user can access events in which his tag is a participant - can('read', 'events', { context: organisation._id, 'participants._id': tag._id }) - // A coordinator can manage events in which his tag is a coordinator - can('all', 'events', { context: organisation._id, 'coordinators._id': tag._id }) - can('read', 'archived-events', { context: organisation._id, 'participants._id': tag._id }) - can('read', 'archived-events', { context: organisation._id, 'coordinators._id': tag._id }) - } - }) - } - if (organisation._id) { - // A user can create event logs for himself and coordinator for everybody within an event - // FIXME: hard to fully express this with the permission system as rights about events - // are not stored on the user but on events because it can be archived and a results in a pretty large list - can(['read', 'create'], 'event-logs', { context: organisation._id }) - can('read', 'archived-event-logs', { context: organisation._id }) - } - } - if (role >= permissions.Roles.manager) { - if (organisation._id) { - can('all', 'event-templates', { context: organisation._id }) - can('read', 'archived-events', { context: organisation._id }) - } - } - }) - } - } -} - -function definePlanAbilities (subject, can, cannot, app) { - if (subject && subject._id) { - if (subject.organisations) { - subject.organisations.forEach(organisation => { - const role = permissions.Roles[organisation.permissions] - if (role >= permissions.Roles.member) { - if (organisation._id) { - // The unique identifier of a service is its path not its name. - // Indeed we have for instance a 'events' service in each organisation. - can('service', organisation._id.toString() + '/plans') - can('service', organisation._id.toString() + '/plan-templates') - can('service', organisation._id.toString() + '/archived-plans') - // A user can access the templates to create a plan - can('read', 'plan-templates', { context: organisation._id }) - // A user can create plan and should be able to get information about running plans - can('create', 'plans', { context: organisation._id }) - // A coordinator can manage his plans - can('all', 'plans', { context: organisation._id, 'coordinators._id': subject._id }) - can('read', 'archived-plans', { context: organisation._id, 'coordinators._id': subject._id }) - // FIXME: A user can read running plans as we'd like it to see plans where he has an event - // Hard to express restrictions with current permissions system however as it will require - // request to the DB and to update abilities of all users involved in an event when creating/updating it. - // As a consequence filtering will be done at the frontend level for now. - can('read', 'plans', { context: organisation._id }) - can('read', 'archived-plans', { context: organisation._id }) - } - if (subject.groups) { - subject.groups.forEach(group => { - if (group._id && group.context && (group.context.toString() === organisation._id.toString())) { - // A coordinator can manage plans in which his group is a coordinator - can('all', 'plans', { context: organisation._id, 'coordinators._id': group._id }) - can('read', 'archived-plans', { context: organisation._id, 'coordinators._id': group._id }) - } - }) - } - if (subject.tags) { - subject.tags.forEach(tag => { - if (tag._id && tag.context && (tag.context.toString() === organisation._id.toString())) { - // A coordinator can manage plans in which his tag is a coordinator - can('all', 'plans', { context: organisation._id, 'coordinators._id': tag._id }) - can('read', 'archived-plans', { context: organisation._id, 'coordinators._id': tag._id }) - } - }) - } - } - if (role >= permissions.Roles.manager) { - if (organisation._id) { - can('all', 'plan-templates', { context: organisation._id }) - can('read', 'archived-plans', { context: organisation._id }) - } - } - }) - } - } -} - -function defineBillingAbilities (subject, can, cannot, app) { - if (subject && subject._id) { - if (subject.organisations) { - subject.organisations.forEach(organisation => { - const role = permissions.Roles[organisation.permissions] - if (role >= permissions.Roles.owner) { - if (organisation._id) { - can('service', 'billing') - can('all', 'billing', { billingObject: organisation._id }) - } - } - }) - } - } -} - -// Hook computing contextual catalog, features, events, etc. abilities for a given user -export function defineUserAbilities (subject, can, cannot, app) { - defineEventAbilities(subject, can, cannot, app) - definePlanAbilities(subject, can, cannot, app) - defineBillingAbilities(subject, can, cannot, app) - - if (subject && subject._id) { - if (subject.organisations) { - subject.organisations.forEach(organisation => { - if (organisation._id) { - const role = permissions.Roles[organisation.permissions] - if (role >= permissions.Roles.member) { - can('service', organisation._id.toString() + '/catalog') - can('read', 'catalog', { context: organisation._id }) - can('service', organisation._id.toString() + '/features') - can('all', 'features', { context: organisation._id }) - can('service', organisation._id.toString() + '/alerts') - can('read', 'alerts', { context: organisation._id }) - } - if (role >= permissions.Roles.manager) { - can(['create', 'update', 'remove'], 'catalog', { context: organisation._id }) - can(['create', 'remove'], 'alerts', { context: organisation._id }) - } - } - }) - } - } -} diff --git a/api/src/app.hooks.js b/api/src/app.hooks.js index 1e9c6664..c650083b 100644 --- a/api/src/app.hooks.js +++ b/api/src/app.hooks.js @@ -42,7 +42,7 @@ export default { }, coreHooks.processObjectIDs), coreHooks.authorise ], - find: [fuzzySearch({ fields: ['name'] }), coreHooks.marshallCollationQuery], + find: [fuzzySearch({ fields: ['name', 'profile.name', 'value'] }), coreHooks.marshallCollationQuery], get: [], create: [], update: [coreHooks.preventUpdatePerspectives], diff --git a/api/src/services/index.js b/api/src/services/index.js index 449ac1b5..2943b4e2 100644 --- a/api/src/services/index.js +++ b/api/src/services/index.js @@ -194,11 +194,11 @@ export async function checkInactiveOrganisations (app) { const isInactive = await isOrganisationInactive(organisation, db, duration) if (isInactive) { // Find owner if any - const owners = await usersService.find({ + const owners = await usersService.find({ query: { 'organisations._id': organisation._id, 'organisations.permissions': 'owner' - } + } }) const owner = _.get(owners, 'data[0]') // Remove inactive organisation anyway in case of orphan organisation @@ -318,7 +318,6 @@ export default async function () { service.name === 'groups' || service.name === 'members' || service.name === 'tags' || - service.name === 'storage' || service.name === 'devices' || service.name === 'features' || service.name === 'alerts') { diff --git a/api/src/services/storage/storage.hooks.js b/api/src/services/storage/storage.hooks.js deleted file mode 100644 index a7f36afc..00000000 --- a/api/src/services/storage/storage.hooks.js +++ /dev/null @@ -1,39 +0,0 @@ -import _ from 'lodash' -import commonHooks from 'feathers-hooks-common' - -export default { - before: { - all: [], - find: [], - get: [], - // When updating attachments on events we need to transfer the notification parameter in query - // However on create the parameters are sent via form data - create: [commonHooks.iff(hook => hook.data.notification, (hook) => { - _.set(hook, 'params.query.notification', hook.data.notification) - return hook - })], - update: [], - patch: [], - remove: [] - }, - - after: { - all: [], - find: [], - get: [], - create: [], - update: [], - patch: [], - remove: [] - }, - - error: { - all: [], - find: [], - get: [], - create: [], - update: [], - patch: [], - remove: [] - } -} diff --git a/config/default.js b/config/default.js index b4a560c1..baaa5fd4 100644 --- a/config/default.js +++ b/config/default.js @@ -564,16 +564,6 @@ module.exports = { editItemAction('EventCard.EDIT_ACTION', 'location'), editItemAction('EventCard.EDIT_ACTION', 'participants'), editItemAction('EventCard.EDIT_ACTION', 'coordinators'), - { - id: 'capture-photo', tooltip: 'EventCard.ADD_MEDIA_LABEL', icon: 'las la-camera', - visible: ['canCapturePhoto', { name: '$can', params: ['read', 'events', ':contextId', ':item'] }], - handler: 'capturePhoto', scope: 'footer' - }, - { - id: 'add-media', tooltip: 'EventCard.ADD_MEDIA_LABEL', icon: 'las la-paperclip', - visible: { name: '$can', params: ['read', 'events', ':contextId', ':item'] }, - handler: 'uploadMedia', scope: 'footer' - }, { id: 'event-map', tooltip: 'EventCard.MAP_LABEL', icon: 'las la-map-marked-alt', visible: ['hasAnyLocation', { name: '$can', params: ['read', 'events', ':contextId', ':item'] }], @@ -1054,7 +1044,7 @@ module.exports = { rightPane: { content: { 'map': [ - catalogTabbar(['user-layers', 'event-participants'], 'user-layers'), + catalogTabbar(['user-layers', 'user-views', 'catalog-layers', 'event-participants'], 'user-layers'), { id: 'user-layers', component: 'catalog/KUserLayersPanel', bind: '$data' }, { component: 'QSpace' }, { id: 'catalog-footer', component: 'frame/KPanel', content: [{ @@ -1065,8 +1055,16 @@ module.exports = { }] } ], + 'user-views': [ + catalogTabbar(['user-layers', 'user-views', 'catalog-layers', 'event-participants'], 'user-views'), + { id: 'user-views', component: 'catalog/KViewsPanel' }, + ], + 'catalog-layers': [ + catalogTabbar(['user-layers', 'user-views', 'catalog-layers', 'event-participants'], 'catalog-layers'), + { id: 'system-layers', component: 'catalog/KCatalogLayersPanel', bind: '$data', scope: 'user' } + ], 'event-participants': [ - catalogTabbar(['user-layers', 'event-participants'], 'event-participants'), + catalogTabbar(['user-layers', 'user-views', 'catalog-layers', 'event-participants'], 'event-participants'), { id: 'event-participants', component: 'EventActivityPanel', bind: '$data' } ] } diff --git a/src/boot/kdk.js b/src/boot/kdk.js index d1d39cd9..e721b969 100644 --- a/src/boot/kdk.js +++ b/src/boot/kdk.js @@ -64,7 +64,6 @@ export default async ({ app }) => { app.component('KTextArea', await kdkCoreUtils.loadComponent('frame/KTextArea')) app.component('KChipsPane', await kdkCoreUtils.loadComponent('frame/KChipsPane')) app.component('KAvatar', await kdkCoreUtils.loadComponent('frame/KAvatar')) - app.component('KUploader', await kdkCoreUtils.loadComponent('input/KUploader')) app.component('KModal', await kdkCoreUtils.loadComponent('frame/KModal')) app.component('KForm', await kdkCoreUtils.loadComponent('form/KForm')) app.component('KList', await kdkCoreUtils.loadComponent('collection/KList')) @@ -78,7 +77,7 @@ export default async ({ app }) => { app.component('KShape', await kdkCoreUtils.loadComponent('media/KShape')) app.component('KStatisticsChart', await kdkCoreUtils.loadComponent('chart/KStatisticsChart')) app.component('KLocationMap', await kdkCoreUtils.loadComponent('KLocationMap')) - app.component('KLayersPanel', await kdkCoreUtils.loadComponent('KLayersPanel')) + app.component('KLayersPanel', await kdkCoreUtils.loadComponent('catalog/KLayersPanel')) app.component('KColorLegend', await kdkCoreUtils.loadComponent('KColorLegend')) app.component('KPage', await kdkCoreUtils.loadComponent('layout/KPage')) diff --git a/src/components/ArchivedEventCard.vue b/src/components/ArchivedEventCard.vue index 1396e553..abf739f7 100644 --- a/src/components/ArchivedEventCard.vue +++ b/src/components/ArchivedEventCard.vue @@ -217,7 +217,7 @@ export default { }) }, browseMedia () { - this.$refs.mediaBrowser.show(this.item.attachments) + this.$refs.mediaBrowser.show(this.attachments) }, viewMap () { this.$router.push({ @@ -247,9 +247,10 @@ export default { } } }, - created () { + async created () { // Required alias for the event logs mixin this.event = this.item + await this.loadAttachments() // Set the required actor if (this.$store.get('user')) this.refresh() this.$events.on('user-changed', this.refresh) diff --git a/src/components/EventActivity.vue b/src/components/EventActivity.vue index 16b08b1e..65c0947e 100644 --- a/src/components/EventActivity.vue +++ b/src/components/EventActivity.vue @@ -6,15 +6,6 @@ - - - - @@ -31,7 +22,7 @@ import { mixins as kCoreMixins, utils as kCoreUtils } from '@kalisio/kdk/core.cl import { mixins as kMapMixins } from '@kalisio/kdk/map.client.map' import mixins from '../mixins' -const activityMixin = kCoreMixins.baseActivity('event-activity') +const activityMixin = kCoreMixins.baseActivity('eventActivity') export default { provide () { @@ -142,16 +133,6 @@ export default { this.refreshCollection() } }, - uploadMedia () { - this.$refs.uploaderModal.open() - // If the modal has already been created the uploader is ready otherwise wait for event - if (this.$refs.uploader) this.initializeMedias() - }, - initializeMedias () { - this.$refs.uploader.initialize(this.event.attachments) - // Open file dialog the first time - if (!this.event.attachments || (this.event.attachments.length === 0)) this.$refs.uploader.openFileInput() - }, browseMedia () { this.$refs.mediaBrowser.show(this.event.attachments) }, diff --git a/src/components/EventActivityPanel.vue b/src/components/EventActivityPanel.vue index 4a12a4d0..31d824ce 100644 --- a/src/components/EventActivityPanel.vue +++ b/src/components/EventActivityPanel.vue @@ -3,30 +3,28 @@ :layers="filteredLayers" :layerCategories="filteredCategories"> diff --git a/src/components/EventCard.vue b/src/components/EventCard.vue index 1455ce52..98e1b3fd 100644 --- a/src/components/EventCard.vue +++ b/src/components/EventCard.vue @@ -147,18 +147,6 @@ > - - - - @@ -172,16 +160,15 @@ - +