diff --git a/application/Espo/Resources/metadata/app/acl.json b/application/Espo/Resources/metadata/app/acl.json index ae04433292..a679eb5f2f 100644 --- a/application/Espo/Resources/metadata/app/acl.json +++ b/application/Espo/Resources/metadata/app/acl.json @@ -11,7 +11,7 @@ "delete": "no" }, "Note": { - "read": "all", + "read": "own", "edit": "own", "delete": "own" }, diff --git a/application/Espo/Resources/metadata/entityDefs/Note.json b/application/Espo/Resources/metadata/entityDefs/Note.json index a205760cf0..f453920390 100644 --- a/application/Espo/Resources/metadata/entityDefs/Note.json +++ b/application/Espo/Resources/metadata/entityDefs/Note.json @@ -11,6 +11,10 @@ "type": "varchar", "readOnly": true }, + "targetType": { + "type": "varchar", + "notStorable": true + }, "parent": { "type": "linkParent", "readOnly": true @@ -21,7 +25,7 @@ }, "attachments": { "type": "linkMultiple", - "view": "Stream.Fields.AttachmentMultiple" + "view": "views/stream/fields/attachment-multiple" }, "number": { "type": "autoincrement", diff --git a/application/Espo/Services/Note.php b/application/Espo/Services/Note.php index d10361839a..73f035ce1b 100644 --- a/application/Espo/Services/Note.php +++ b/application/Espo/Services/Note.php @@ -24,6 +24,7 @@ namespace Espo\Services; use \Espo\Core\Exceptions\Forbidden; use \Espo\Core\Exceptions\NotFound; +use \Espo\Core\Exceptions\BadRequest; use Espo\ORM\Entity; @@ -49,9 +50,129 @@ class Note extends Record } } - return parent::createEntity($data); } + protected function afterCreate(Entity $entity, array $data = array()) + { + parent::afterCreate($entity, $data); + } + + protected function beforeCreate(Entity $entity, array $data = array()) + { + parent::beforeUpdate($entity, $data); + $targetType = $entity->get('targetType'); + + $entity->clear('isGlobal'); + + switch ($targetType) { + case 'all': + $entity->clear('usersIds'); + $entity->clear('teamsIds'); + $entity->set('isGlobal', true); + break; + case 'self': + $entity->clear('usersIds'); + $entity->clear('teamsIds'); + $entity->set('usersIds', [$this->getUser()->id]); + break; + case 'users': + $entity->clear('teamsIds'); + break; + case 'teams': + $entity->clear('usersIds'); + break; + } + } + + protected function beforeUpdate(Entity $entity, array $data = array()) + { + parent::beforeUpdate($entity, $data); + $entity->clear('targetType'); + $entity->clear('usersIds'); + $entity->clear('teamsIds'); + $entity->clear('isGlobal'); + } + + + public function checkAssignment(Entity $entity) + { + if ($entity->isNew()) { + $targetType = $entity->get('targetType'); + + if ($targetType) { + $assignmentPermission = $this->getAcl()->get('assignmentPermission'); + if ($assignmentPermission === false || $assignmentPermission === 'no') { + if ($targetType !== 'self') { + throw new Forbidden('Not permitted to post to anybody except self.'); + } + } + + if ($targetType === 'teams') { + $teamIdList = $entity->get('teamsIds'); + if (empty($teamIdList) || !is_array($teamIdList)) { + throw new BadRequest(); + } + } + if ($targetType === 'users') { + $userIdList = $entity->get('usersIds'); + if (empty($userIdList) || !is_array($userIdList)) { + throw new BadRequest(); + } + } + + if ($assignmentPermission === 'team') { + if ($targetType === 'all') { + throw new Forbidden('Not permitted to post to all.'); + } + + $userTeamIdList = $this->getUser()->getTeamIdList(); + + if ($targetType === 'teams') { + if (empty($userTeamIdList)) { + throw new Forbidden('Not permitted to post to foreign teams.'); + } + foreach ($teamIdList as $teamId) { + if (!in_array($teamId, $userTeamIdList)) { + throw new Forbidden('Not permitted to post to foreign teams.'); + } + } + } else if ($targetType === 'users') { + if (empty($userTeamIdList)) { + throw new Forbidden('Not permitted to post to users from foreign teams.'); + } + + foreach ($userIdList as $userId) { + if ($userId === $this->getUser()->id) { + continue; + } + if (!$this->getEntityManager()->getRepository('User')->checkBelongsToAnyOfTeams($userId, $userTeamIdList)) { + throw new Forbidden('Not permitted to post to users from foreign teams.'); + } + } + } + } + } + } + return true; + } + + public function linkEntity($id, $link, $foreignId) + { + if ($link === 'teams' || $link === 'users') { + throw new Forbidden(); + } + return parant::linkEntity($id, $link, $foreignId); + } + + + public function unlinkEntity($id, $link, $foreignId) + { + if ($link === 'teams' || $link === 'users') { + throw new Forbidden(); + } + return parant::unlinkEntity($id, $link, $foreignId); + } + } diff --git a/frontend/client/src/app.js b/frontend/client/src/app.js index 9f7c1208b9..ca30a282da 100644 --- a/frontend/client/src/app.js +++ b/frontend/client/src/app.js @@ -484,7 +484,7 @@ Espo.define( self.baseController.error403(); } else { var msg = self.language.translate('Error') + ' ' + xhr.status; - msg += ': ' + this.translate('Access denied'); + msg += ': ' + self.language.translate('Access denied'); Espo.Ui.error(msg); } break; @@ -493,7 +493,7 @@ Espo.define( self.baseController.error404(); } else { var msg = self.language.translate('Error') + ' ' + xhr.status; - msg += ': ' + this.translate('Not found'); + msg += ': ' + self.language.translate('Not found'); Espo.Ui.error(msg); } break; diff --git a/frontend/client/src/views/fields/base.js b/frontend/client/src/views/fields/base.js index 1d1292d627..28f0173e16 100644 --- a/frontend/client/src/views/fields/base.js +++ b/frontend/client/src/views/fields/base.js @@ -19,21 +19,21 @@ * along with EspoCRM. If not, see http://www.gnu.org/licenses/. ************************************************************************/ -Espo.define('Views.Fields.Base', 'View', function (Dep) { +Espo.define('views/fields/base', 'view', function (Dep) { return Dep.extend({ type: 'base', - listTemplate: 'fields.base.list', + listTemplate: 'fields/base/list', - listLinkTemplate: 'fields.base.list-link', + listLinkTemplate: 'fields/base/list-link', - detailTemplate: 'fields.base.detail', + detailTemplate: 'fields/base/detail', - editTemplate: 'fields.base.edit', + editTemplate: 'fields/base/edit', - searchTemplate: 'fields.base.search', + searchTemplate: 'fields/base/search', validations: ['required'], diff --git a/frontend/client/src/views/record/row-actions/edit-and-remove.js b/frontend/client/src/views/record/row-actions/edit-and-remove.js new file mode 100644 index 0000000000..12b32393a5 --- /dev/null +++ b/frontend/client/src/views/record/row-actions/edit-and-remove.js @@ -0,0 +1,53 @@ +/************************************************************************ + * This file is part of EspoCRM. + * + * EspoCRM - Open Source CRM application. + * Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko + * Website: http://www.espocrm.com + * + * EspoCRM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EspoCRM 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EspoCRM. If not, see http://www.gnu.org/licenses/. + ************************************************************************/ + +Espo.define('views/record/row-actions/edit-and-remove', 'views/record/row-actions/default', function (Dep) { + + return Dep.extend({ + + getActionList: function () { + var list = []; + if (this.options.acl.edit) { + list = list.concat([ + { + action: 'quickEdit', + label: 'Edit', + data: { + id: this.model.id + } + }, + { + action: 'quickRemove', + label: 'Remove', + data: { + id: this.model.id + } + } + ]); + } + return list; + } + + }); + +}); + + diff --git a/frontend/client/src/views/stream/modals/create-post.js b/frontend/client/src/views/stream/modals/create-post.js index ee90c7f961..b2d8af4e7b 100644 --- a/frontend/client/src/views/stream/modals/create-post.js +++ b/frontend/client/src/views/stream/modals/create-post.js @@ -46,6 +46,8 @@ Espo.define('views/stream/modals/create-post', 'views/modal', function (Dep) { this.wait(true); this.getModelFactory().create('Note', function (model) { + model.set('type', 'Post'); + this.createView('record', 'views/stream/record/edit', { model: model, el: this.options.el + ' .record' diff --git a/frontend/client/src/views/stream/record/edit.js b/frontend/client/src/views/stream/record/edit.js index 1b39c88973..4f67088a62 100644 --- a/frontend/client/src/views/stream/record/edit.js +++ b/frontend/client/src/views/stream/record/edit.js @@ -87,7 +87,7 @@ Espo.define('views/stream/record/edit', 'views/record/base', function (Dep) { var assignmentPermission = this.getAcl().get('assignmentPermission'); - if (assignmentPermission === true || assignmentPermission === 'team') { + if (assignmentPermission === true || assignmentPermission === 'team' || assignmentPermission === 'all') { optionList.push('users'); optionList.push('teams'); } diff --git a/frontend/client/src/views/stream/row-actions/default.js b/frontend/client/src/views/stream/row-actions/default.js index 951b91d575..3bb574adb1 100644 --- a/frontend/client/src/views/stream/row-actions/default.js +++ b/frontend/client/src/views/stream/row-actions/default.js @@ -19,11 +19,11 @@ * along with EspoCRM. If not, see http://www.gnu.org/licenses/. ************************************************************************/ -Espo.define('Views.Stream.RowActions.Default', 'View', function (Dep) { +Espo.define('views/stream/row-actions/default', 'views/record/row-actions/edit-and-remove', function (Dep) { return Dep.extend({ - template: 'stream.row-actions.default', + /*template: 'stream/row-actions/default', afterRender: function () { var $dd = this.$el.find('button[data-toggle="dropdown"]').parent(); @@ -43,7 +43,7 @@ Espo.define('Views.Stream.RowActions.Default', 'View', function (Dep) { isRemovable: this.options.isRemovable, isEnabled: this.options.isEditable || this.options.isRemovable } - } + }*/ }); });