diff --git a/application/Espo/Core/Acl/Permission.php b/application/Espo/Core/Acl/Permission.php index 91921af774..cce507ca1d 100644 --- a/application/Espo/Core/Acl/Permission.php +++ b/application/Espo/Core/Acl/Permission.php @@ -40,4 +40,7 @@ class Permission public const DATA_PRIVACY = 'dataPrivacy'; public const MESSAGE = 'message'; public const MENTION = 'mention'; + public const USER_CALENDAR = 'userCalendar'; + public const FOLLOWER_MANAGEMENT = 'followerManagement'; + public const GROUP_EMAIL_ACCOUNT = 'groupEmailAccount'; } diff --git a/application/Espo/Core/Upgrades/Migrations/V9_0/AfterUpgrade.php b/application/Espo/Core/Upgrades/Migrations/V9_0/AfterUpgrade.php index 798f723e67..9180306918 100644 --- a/application/Espo/Core/Upgrades/Migrations/V9_0/AfterUpgrade.php +++ b/application/Espo/Core/Upgrades/Migrations/V9_0/AfterUpgrade.php @@ -34,11 +34,14 @@ use Espo\Core\Upgrades\Migration\Script; use Espo\Core\Utils\Config; use Espo\Core\Utils\Metadata; use Espo\Entities\Preferences; +use Espo\Entities\Role; use Espo\Entities\ScheduledJob; use Espo\Entities\User; use Espo\Modules\Crm\Entities\Account; use Espo\Modules\Crm\Entities\Contact; use Espo\ORM\EntityManager; +use Espo\ORM\Query\Part\Expression; +use Espo\ORM\Query\UpdateBuilder; class AfterUpgrade implements Script { @@ -51,12 +54,23 @@ class AfterUpgrade implements Script public function run(): void { + $this->updateRoles(); $this->setReactionNotifications(); $this->createScheduledJob(); $this->setAclLinks(); $this->fixTimezone(); } + private function updateRoles(): void + { + $query = UpdateBuilder::create() + ->in(Role::ENTITY_TYPE) + ->set(['userCalendarPermission' => Expression::column('userPermission')]) + ->build(); + + $this->entityManager->getQueryExecutor()->execute($query); + } + private function createScheduledJob(): void { $found = $this->entityManager diff --git a/application/Espo/Modules/Crm/Tools/Activities/Service.php b/application/Espo/Modules/Crm/Tools/Activities/Service.php index caf9f2722a..523007f3fc 100644 --- a/application/Espo/Modules/Crm/Tools/Activities/Service.php +++ b/application/Espo/Modules/Crm/Tools/Activities/Service.php @@ -737,7 +737,7 @@ class Service protected function accessCheck(Entity $entity): void { if ($entity instanceof User) { - if (!$this->acl->checkUserPermission($entity)) { + if (!$this->acl->checkUserPermission($entity, Acl\Permission::USER_CALENDAR)) { throw new Forbidden(); } diff --git a/application/Espo/Modules/Crm/Tools/Activities/UpcomingService.php b/application/Espo/Modules/Crm/Tools/Activities/UpcomingService.php index 819baca75f..2b676ce274 100644 --- a/application/Espo/Modules/Crm/Tools/Activities/UpcomingService.php +++ b/application/Espo/Modules/Crm/Tools/Activities/UpcomingService.php @@ -260,7 +260,7 @@ class UpcomingService private function accessCheck(Entity $entity): void { if ($entity instanceof User) { - if (!$this->acl->checkUserPermission($entity)) { + if (!$this->acl->checkUserPermission($entity, Acl\Permission::USER_CALENDAR)) { throw new Forbidden(); } diff --git a/application/Espo/Modules/Crm/Tools/Calendar/Service.php b/application/Espo/Modules/Crm/Tools/Calendar/Service.php index 44522c034d..b2ef0bb660 100644 --- a/application/Espo/Modules/Crm/Tools/Calendar/Service.php +++ b/application/Espo/Modules/Crm/Tools/Calendar/Service.php @@ -197,8 +197,7 @@ class Service private function accessCheck(Entity $entity): void { if ($entity instanceof User) { - /** @noinspection PhpRedundantOptionalArgumentInspection */ - if (!$this->acl->checkUserPermission($entity, Acl\Permission::USER)) { + if (!$this->acl->checkUserPermission($entity, Acl\Permission::USER_CALENDAR)) { throw new Forbidden(); } @@ -621,16 +620,16 @@ class Service */ public function fetchForTeams(array $teamIdList, FetchParams $fetchParams): array { - if ($this->acl->getPermissionLevel(Acl\Permission::USER) === Table::LEVEL_NO) { - throw new Forbidden("User Permission not allowing to view calendars of other users."); + if ($this->acl->getPermissionLevel(Acl\Permission::USER_CALENDAR) === Table::LEVEL_NO) { + throw new Forbidden("User Calendar Permission not allowing to view calendars of other users."); } - if ($this->acl->getPermissionLevel(Acl\Permission::USER) === Table::LEVEL_TEAM) { + if ($this->acl->getPermissionLevel(Acl\Permission::USER_CALENDAR) === Table::LEVEL_TEAM) { $userTeamIdList = $this->user->getLinkMultipleIdList(Field::TEAMS); foreach ($teamIdList as $teamId) { if (!in_array($teamId, $userTeamIdList)) { - throw new Forbidden("User Permission not allowing to view calendars of other teams."); + throw new Forbidden("User Calendar Permission not allowing to view calendars of other teams."); } } } diff --git a/application/Espo/Resources/i18n/en_US/Role.json b/application/Espo/Resources/i18n/en_US/Role.json index ee3c234831..4e01245393 100644 --- a/application/Espo/Resources/i18n/en_US/Role.json +++ b/application/Espo/Resources/i18n/en_US/Role.json @@ -13,6 +13,7 @@ "dataPrivacyPermission": "Data Privacy Permission", "auditPermission": "Audit Permission", "mentionPermission": "Mention Permission", + "userCalendarPermission": "User Calendar Permission", "data": "Data", "fieldData": "Field Data" }, @@ -23,7 +24,8 @@ "tooltips": { "messagePermission": "Allows to send messages to other users.\n\n* all – can send to all\n* team – can send only to teammates\n* no – cannot send", "assignmentPermission": "Allows to assign records to other users.\n\n* all – no restriction\n* team – can assign only to teammates\n* no – can assign only to self", - "userPermission": "Allows view activities, calendar and stream of other users.\n\n* all – can view all\n* team – can view activities of teammates only\n* no – can't view", + "userPermission": "Allows to view stream of other users. Allows users to view the access levels other users have for specific records.", + "userCalendarPermission": "Allows to view calendars of other users.", "portalPermission": "Access to portal information, the ability to post messages to portal users.", "groupEmailAccountPermission": "Access to group email accounts, the ability to send emails from group SMTP.", "exportPermission": "Allows to export records.", diff --git a/application/Espo/Resources/layouts/Role/detail.json b/application/Espo/Resources/layouts/Role/detail.json index f4182322d9..8ef1638ca7 100644 --- a/application/Espo/Resources/layouts/Role/detail.json +++ b/application/Espo/Resources/layouts/Role/detail.json @@ -12,13 +12,13 @@ "rows": [ [ {"name": "exportPermission"}, - {"name": "userPermission"}, + {"name": "mentionPermission"}, {"name": "assignmentPermission"} ], [ {"name": "portalPermission"}, {"name": "groupEmailAccountPermission"}, - {"name": "dataPrivacyPermission"} + {"name": "userCalendarPermission"} ], [ {"name": "massUpdatePermission"}, @@ -27,8 +27,8 @@ ], [ {"name": "auditPermission"}, - {"name": "mentionPermission"}, - false + {"name": "userPermission"}, + {"name": "dataPrivacyPermission"} ] ] } diff --git a/application/Espo/Resources/metadata/app/acl.json b/application/Espo/Resources/metadata/app/acl.json index cf9411d69e..ead93f75ff 100644 --- a/application/Espo/Resources/metadata/app/acl.json +++ b/application/Espo/Resources/metadata/app/acl.json @@ -160,16 +160,17 @@ }, "valuePermissionList": [ "assignmentPermission", - "userPermission", "messagePermission", - "portalPermission", - "groupEmailAccountPermission", + "mentionPermission", + "userCalendarPermission", + "auditPermission", "exportPermission", "massUpdatePermission", + "userPermission", + "portalPermission", + "groupEmailAccountPermission", "followerManagementPermission", - "dataPrivacyPermission", - "auditPermission", - "mentionPermission" + "dataPrivacyPermission" ], "valuePermissionHighestLevels": { "assignmentPermission": "all", @@ -182,7 +183,8 @@ "followerManagementPermission": "all", "dataPrivacyPermission": "yes", "auditPermission": "yes", - "mentionPermission": "yes" + "mentionPermission": "yes", + "userCalendarPermission": "all" }, "permissionsStrictDefaults": { "assignmentPermission": "no", @@ -195,6 +197,7 @@ "followerManagementPermission": "no", "dataPrivacyPermission": "no", "auditPermission": "no", - "mentionPermission": "no" + "mentionPermission": "no", + "userCalendarPermission": "no" } } diff --git a/application/Espo/Resources/metadata/entityDefs/Role.json b/application/Espo/Resources/metadata/entityDefs/Role.json index ff80c2a038..23bfeedbd9 100644 --- a/application/Espo/Resources/metadata/entityDefs/Role.json +++ b/application/Espo/Resources/metadata/entityDefs/Role.json @@ -105,6 +105,15 @@ "view": "views/role/fields/permission", "audited": true }, + "userCalendarPermission": { + "type": "enum", + "options": ["not-set", "all", "team", "no"], + "default": "not-set", + "tooltip": true, + "translation": "Role.options.levelList", + "view": "views/role/fields/permission", + "audited": true + }, "data": { "type": "jsonObject", "audited": true diff --git a/application/Espo/Tools/App/AppService.php b/application/Espo/Tools/App/AppService.php index dbf9a4ee63..4e00464fdb 100644 --- a/application/Espo/Tools/App/AppService.php +++ b/application/Espo/Tools/App/AppService.php @@ -339,7 +339,7 @@ class AppService */ private function getUserGroupEmailAddressList(User $user): array { - $groupEmailAccountPermission = $this->acl->getPermissionLevel('groupEmailAccount'); + $groupEmailAccountPermission = $this->acl->getPermissionLevel(Acl\Permission::GROUP_EMAIL_ACCOUNT); if (!$groupEmailAccountPermission || $groupEmailAccountPermission === Acl\Table::LEVEL_NO) { return []; diff --git a/application/Espo/Tools/Stream/FollowerRecordService.php b/application/Espo/Tools/Stream/FollowerRecordService.php index 6fefd57b10..f86b9ee797 100644 --- a/application/Espo/Tools/Stream/FollowerRecordService.php +++ b/application/Espo/Tools/Stream/FollowerRecordService.php @@ -221,7 +221,7 @@ class FollowerRecordService if ( !$user->isPortal() && $this->user->getId() !== $user->getId() && - !$this->acl->checkUserPermission($user, 'followerManagement') + !$this->acl->checkUserPermission($user, Acl\Permission::FOLLOWER_MANAGEMENT) ) { throw new Forbidden("No 'followerManagement' permission."); } diff --git a/application/Espo/Tools/Stream/UserRecordService.php b/application/Espo/Tools/Stream/UserRecordService.php index 693a67720f..6e01db8ba6 100644 --- a/application/Espo/Tools/Stream/UserRecordService.php +++ b/application/Espo/Tools/Stream/UserRecordService.php @@ -93,7 +93,8 @@ class UserRecordService throw new NotFound("User not found."); } - if (!$this->acl->checkUserPermission($user, 'user')) { + /** @noinspection PhpRedundantOptionalArgumentInspection */ + if (!$this->acl->checkUserPermission($user, Acl\Permission::USER)) { throw new Forbidden("No user permission access."); } @@ -135,7 +136,8 @@ class UserRecordService throw new NotFound("User not found."); } - if (!$this->acl->checkUserPermission($user, 'user')) { + /** @noinspection PhpRedundantOptionalArgumentInspection */ + if (!$this->acl->checkUserPermission($user, Acl\Permission::USER)) { throw new Forbidden("No user permission access."); } diff --git a/application/Espo/Tools/User/UsersAccessService.php b/application/Espo/Tools/User/UsersAccessService.php index 82f096a9e8..e1e31ff4d1 100644 --- a/application/Espo/Tools/User/UsersAccessService.php +++ b/application/Espo/Tools/User/UsersAccessService.php @@ -102,8 +102,8 @@ class UsersAccessService } if ( - $this->acl->getPermissionLevel('user') !== Acl\Table::LEVEL_TEAM && - $this->acl->getPermissionLevel('user') !== Acl\Table::LEVEL_ALL + $this->acl->getPermissionLevel(Acl\Permission::USER) !== Acl\Table::LEVEL_TEAM && + $this->acl->getPermissionLevel(Acl\Permission::USER) !== Acl\Table::LEVEL_ALL ) { throw new Forbidden("No user permission."); } @@ -129,7 +129,7 @@ class UsersAccessService ] ]); - if ($this->acl->getPermissionLevel('user') === Acl\Table::LEVEL_TEAM) { + if ($this->acl->getPermissionLevel(Acl\Permission::USER) === Acl\Table::LEVEL_TEAM) { $queryBuilder ->where( Condition::in( diff --git a/client/modules/crm/src/views/calendar/calendar.js b/client/modules/crm/src/views/calendar/calendar.js index 4eddebc25d..b63257d8ee 100644 --- a/client/modules/crm/src/views/calendar/calendar.js +++ b/client/modules/crm/src/views/calendar/calendar.js @@ -192,7 +192,7 @@ class CalendarView extends View { this.colors = {...this.colors, ...this.getHelper().themeManager.getParam('calendarColors')}; - this.isCustomViewAvailable = this.getAcl().getPermissionLevel('userPermission') !== 'no'; + this.isCustomViewAvailable = this.getAcl().getPermissionLevel('userCalendar') !== 'no'; if (this.options.userId) { this.isCustomViewAvailable = false; diff --git a/client/modules/crm/src/views/calendar/fields/teams.js b/client/modules/crm/src/views/calendar/fields/teams.js index ddc28e3a4f..8b2095aad9 100644 --- a/client/modules/crm/src/views/calendar/fields/teams.js +++ b/client/modules/crm/src/views/calendar/fields/teams.js @@ -33,7 +33,7 @@ define('crm:views/calendar/fields/teams', ['views/fields/link-multiple'], functi foreignScope: 'Team', getSelectBoolFilterList: function () { - if (this.getAcl().get('userPermission') === 'team') { + if (this.getAcl().getPermissionLevel('userCalendar') === 'team') { return ['onlyMy']; } } diff --git a/client/modules/crm/src/views/calendar/fields/users.js b/client/modules/crm/src/views/calendar/fields/users.js index 63915181b1..37aad58dfb 100644 --- a/client/modules/crm/src/views/calendar/fields/users.js +++ b/client/modules/crm/src/views/calendar/fields/users.js @@ -34,7 +34,7 @@ class CalendarUsersFieldView extends LinkMultipleFieldView { sortable = true getSelectBoolFilterList() { - if (this.getAcl().getPermissionLevel('userPermission') === 'team') { + if (this.getAcl().getPermissionLevel('userCalendar') === 'team') { return ['onlyMyTeam']; } } diff --git a/client/modules/crm/src/views/calendar/timeline.js b/client/modules/crm/src/views/calendar/timeline.js index 7831edbf4c..bf4573c5e4 100644 --- a/client/modules/crm/src/views/calendar/timeline.js +++ b/client/modules/crm/src/views/calendar/timeline.js @@ -157,7 +157,7 @@ class TimelineView extends View { this.colors = {...this.colors, ...this.getHelper().themeManager.getParam('calendarColors')}; - this.isCustomViewAvailable = this.getAcl().getPermissionLevel('userPermission') !== 'no'; + this.isCustomViewAvailable = this.getAcl().getPermissionLevel('userCalendar') !== 'no'; if (this.options.userId) { this.isCustomViewAvailable = false; @@ -201,7 +201,7 @@ class TimelineView extends View { } } - if (this.getAcl().getPermissionLevel('userPermission') === 'no') { + if (this.getAcl().getPermissionLevel('userCalendar') === 'no') { if (this.calendarType === 'shared') { this.calendarType = 'single'; } @@ -249,7 +249,7 @@ class TimelineView extends View { return list; } - if (this.getAcl().getPermissionLevel('userPermission') !== 'no') { + if (this.getAcl().getPermissionLevel('userCalendar') !== 'no') { list.push({ type: 'shared', label: this.getCalendarTypeLabel('shared'), @@ -997,7 +997,7 @@ class TimelineView extends View { actionAddUser() { const boolFilterList = []; - if (this.getAcl().getPermissionLevel('userPermission') === 'team') { + if (this.getAcl().getPermissionLevel('userCalendar') === 'team') { boolFilterList.push('onlyMyTeam'); } diff --git a/client/modules/crm/src/views/dashlets/options/calendar.js b/client/modules/crm/src/views/dashlets/options/calendar.js index 7612507e8e..667fdcf61c 100644 --- a/client/modules/crm/src/views/dashlets/options/calendar.js +++ b/client/modules/crm/src/views/dashlets/options/calendar.js @@ -52,7 +52,7 @@ define('crm:views/dashlets/options/calendar', ['views/dashlets/options/base'], f } if ( - this.getAcl().get('userPermission') !== 'no' + this.getAcl().getPermissionLevel('userCalendar') !== 'no' && ~['basicWeek', 'month', 'basicDay'].indexOf(this.model.get('mode')) ) { diff --git a/client/src/views/user/detail.js b/client/src/views/user/detail.js index a85fc6b59c..85b4573818 100644 --- a/client/src/views/user/detail.js +++ b/client/src/views/user/detail.js @@ -83,15 +83,15 @@ export default class extends DetailView { this.getAcl().checkScope('Calendar') && (this.getUserModel().isRegular() || this.getUserModel().isAdmin()) ) { - const showActivities = this.getAcl().checkUserPermission(this.getUserModel()); + const showActivities = this.getAcl().checkPermission('userCalendar', this.getUserModel()); if ( !showActivities && - this.getAcl().getPermissionLevel('userPermission') === 'team' && + this.getAcl().getPermissionLevel('userCalendar') === 'team' && !this.model.has('teamsIds') ) { this.listenToOnce(this.model, 'sync', () => { - if (this.getAcl().checkUserPermission(this.getUserModel())) { + if (this.getAcl().checkPermission('userCalendar', this.getUserModel())) { this.showHeaderActionItem('calendar'); } }); diff --git a/client/src/views/user/record/detail-bottom.js b/client/src/views/user/record/detail-bottom.js index 46c53a1db4..a0e566bb51 100644 --- a/client/src/views/user/record/detail-bottom.js +++ b/client/src/views/user/record/detail-bottom.js @@ -33,15 +33,17 @@ class UserDetailBottomRecordView extends DetailBottomRecordView { setupPanels() { super.setupPanels(); - let streamAllowed = this.getAcl().checkUserPermission(this.model); + const userModel = /** @type {import('models/user').default} */ this.model; + + const streamAllowed = this.getAcl().checkPermission('userCalendar', userModel); if ( !streamAllowed && - this.getAcl().getPermissionLevel('userPermission') === 'team' && + this.getAcl().getPermissionLevel('userCalendar') === 'team' && !this.model.has('teamsIds') ) { this.listenToOnce(this.model, 'sync', () => { - if (this.getAcl().checkUserPermission(this.model)) { + if (this.getAcl().checkPermission('userCalendar', userModel)) { this.onPanelsReady(() => { this.showPanel('stream', 'acl'); }); diff --git a/client/src/views/user/record/detail-side.js b/client/src/views/user/record/detail-side.js index 4ffa2cd345..475042c1a6 100644 --- a/client/src/views/user/record/detail-side.js +++ b/client/src/views/user/record/detail-side.js @@ -42,24 +42,27 @@ define('views/user/record/detail-side', ['views/record/detail-side'], function ( return; } - var showActivities = this.getAcl().checkUserPermission(this.model); + const showActivities = this.getAcl().checkPermission('userCalendar', this.model); - if (!showActivities) { - if (this.getAcl().get('userPermission') === 'team') { - if (!this.model.has('teamsIds')) { - this.listenToOnce(this.model, 'sync', function () { - if (this.getAcl().checkUserPermission(this.model)) { - this.onPanelsReady(function () { - this.showPanel('activities', 'acl'); - this.showPanel('history', 'acl'); - if (!this.model.isPortal()) { - this.showPanel('tasks', 'acl'); - } - }); - } - }, this); + if ( + !showActivities && + this.getAcl().getPermissionLevel('userCalendar') === 'team' && + !this.model.has('teamsIds') + ) { + this.listenToOnce(this.model, 'sync', () => { + if (!this.getAcl().checkPermission('userCalendar', this.model)) { + return; } - } + + this.onPanelsReady(() => { + this.showPanel('activities', 'acl'); + this.showPanel('history', 'acl'); + + if (!this.model.isPortal()) { + this.showPanel('tasks', 'acl'); + } + }); + }); } if (!showActivities) {