From a472e6d348f46ceb3ee6ac40ddf8db67b1978236 Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 28 Dec 2015 14:38:27 +0200 Subject: [PATCH] Portal Roles --- application/Espo/Core/Acl/Table.php | 106 +++++++++++------- application/Espo/Core/Acl/TablePortal.php | 84 ++++++++++++++ .../Core/Templates/Metadata/Base/scopes.json | 1 + .../Metadata/CategoryTree/scopes.json | 1 + .../Templates/Metadata/Person/scopes.json | 1 + .../Espo/Core/defaults/layout/list.json | 4 +- .../Resources/metadata/scopes/Account.json | 1 + .../Crm/Resources/metadata/scopes/Call.json | 1 + .../Crm/Resources/metadata/scopes/Case.json | 1 + .../Resources/metadata/scopes/Contact.json | 1 + .../Resources/metadata/scopes/Document.json | 1 + .../Crm/Resources/metadata/scopes/Lead.json | 1 + .../Resources/metadata/scopes/Meeting.json | 1 + .../metadata/scopes/Opportunity.json | 1 + .../Crm/Resources/metadata/scopes/Task.json | 1 + .../Espo/Resources/i18n/en_US/Admin.json | 5 + .../Espo/Resources/i18n/en_US/Global.json | 6 +- .../Espo/Resources/i18n/en_US/PortalRole.json | 15 +++ .../Espo/Resources/i18n/en_US/Role.json | 1 + .../Espo/Resources/metadata/app/acl.json | 3 +- .../Resources/metadata/app/aclPortal.json | 81 +++++++++++++ .../Resources/metadata/app/adminPanel.json | 15 +++ .../metadata/clientDefs/PortalRole.json | 17 +++ .../Resources/metadata/clientDefs/Role.json | 38 +++---- .../Espo/Resources/metadata/scopes/Email.json | 1 + application/Espo/Services/PortalRole.php | 58 ++++++++++ .../client/src/controllers/portal-role.js | 42 +++++++ frontend/client/src/views/portal-role/list.js | 36 ++++++ .../src/views/portal-role/record/detail.js | 36 ++++++ .../src/views/portal-role/record/edit.js | 38 +++++++ .../src/views/portal-role/record/list.js | 37 ++++++ .../src/views/portal-role/record/table.js | 67 +++++++++++ frontend/client/src/views/role/list.js | 19 ++-- .../src/views/role/record/detail-side.js | 4 +- .../client/src/views/role/record/detail.js | 22 +--- frontend/client/src/views/role/record/edit.js | 4 +- frontend/client/src/views/role/record/list.js | 2 +- .../src/views/role/record/panels/side.js | 6 +- .../client/src/views/role/record/table.js | 46 ++++---- 39 files changed, 682 insertions(+), 123 deletions(-) create mode 100644 application/Espo/Core/Acl/TablePortal.php create mode 100644 application/Espo/Resources/i18n/en_US/PortalRole.json create mode 100644 application/Espo/Resources/metadata/app/aclPortal.json create mode 100644 application/Espo/Resources/metadata/clientDefs/PortalRole.json create mode 100644 application/Espo/Services/PortalRole.php create mode 100644 frontend/client/src/controllers/portal-role.js create mode 100644 frontend/client/src/views/portal-role/list.js create mode 100644 frontend/client/src/views/portal-role/record/detail.js create mode 100644 frontend/client/src/views/portal-role/record/edit.js create mode 100644 frontend/client/src/views/portal-role/record/list.js create mode 100644 frontend/client/src/views/portal-role/record/table.js diff --git a/application/Espo/Core/Acl/Table.php b/application/Espo/Core/Acl/Table.php index 7a2fcdaabb..26620049a0 100644 --- a/application/Espo/Core/Acl/Table.php +++ b/application/Espo/Core/Acl/Table.php @@ -41,9 +41,13 @@ use \Espo\Core\Utils\File\Manager as FileManager; class Table { + protected $type = 'acl'; + + protected $defaultAclType = 'recordAllTeamOwnNo'; + private $data = null; - private $cacheFile; + protected $cacheFilePath; protected $actionList = ['read', 'stream', 'edit', 'delete']; @@ -53,6 +57,8 @@ class Table protected $fieldLevelList = ['yes', 'no']; + protected $valuePermissionList = ['assignmentPermission', 'userPermission']; + private $fileManager; private $metadata; @@ -88,11 +94,12 @@ class Table if ($fileManager) { $this->fileManager = $fileManager; } + $this->valuePermissionList = $this->metadata->get('app.' . $this->type . '.defs.valuePermissionList', $this->valuePermissionList); - $this->cacheFile = 'data/cache/application/acl/' . $user->id . '.php'; + $this->initCacheFilePath(); - if ($config && $config->get('useCache') && file_exists($this->cacheFile)) { - $cached = include $this->cacheFile; + if ($config && $config->get('useCache') && file_exists($this->cacheFilePath)) { + $cached = include $this->cacheFilePath; $this->data = $cached; } else { $this->load(); @@ -102,6 +109,11 @@ class Table } } + protected function initCacheFilePath() + { + $this->cacheFilePath = 'data/cache/application/acl/' . $this->user->id . '.php'; + } + protected function getMetadata() { return $this->metadata; @@ -161,8 +173,11 @@ class Table { $aclTableList = []; $fieldTableList = []; - $assignmentPermissionList = []; - $userPermissionList = []; + + $valuePermissionLists = (object)[]; + foreach ($this->valuePermissionList as $permission) { + $valuePermissionLists->$permission = []; + } if (!$this->user->isAdmin()) { $userRoles = $this->user->get('roles'); @@ -170,8 +185,9 @@ class Table foreach ($userRoles as $role) { $aclTableList[] = $role->get('data'); $fieldTableList[] = $role->get('fieldData'); - $assignmentPermissionList[] = $role->get('assignmentPermission'); - $userPermissionList[] = $role->get('userPermission'); + foreach ($this->valuePermissionList as $permission) { + $valuePermissionLists->{$permission}[] = $role->get($permission); + } } $teams = $this->user->get('teams'); @@ -180,8 +196,9 @@ class Table foreach ($teamRoles as $role) { $aclTableList[] = $role->get('data'); $fieldTableList[] = $role->get('fieldData'); - $assignmentPermissionList[] = $role->get('assignmentPermission'); - $userPermissionList[] = $role->get('userPermission'); + foreach ($this->valuePermissionList as $permission) { + $valuePermissionLists->{$permission}[] = $role->get($permission); + } } } @@ -189,14 +206,7 @@ class Table $fieldTable = $this->mergeFieldTableList($fieldTableList); $this->applyDefault($aclTable, $fieldTable); - - foreach ($this->getScopeList() as $scope) { - if ($this->metadata->get('scopes.' . $scope . '.disabled')) { - $aclTable->$scope = false; - unset($fieldTable->$scope); - } - } - + $this->applyDisabled($aclTable, $fieldTable); $this->applyMandatory($aclTable, $fieldTable); } else { $aclTable = (object) []; @@ -229,18 +239,17 @@ class Table $this->fillFieldTableQuickAccess(); if (!$this->user->isAdmin()) { - $this->data->assignmentPermission = $this->mergeValueList($assignmentPermissionList, $this->metadata->get('app.acl.default.assignmentPermission', 'all')); - $this->data->userPermission = $this->mergeValueList($userPermissionList, $this->metadata->get('app.acl.default.userPermission', 'no')); + foreach ($this->valuePermissionList as $permission) { + $this->data->$permission = $this->mergeValueList($valuePermissionLists->$permission, $this->metadata->get('app.acl.default.' . $permission, 'all')); + if ($this->metadata->get('app.acl.mandatory.' . $permission)) { + $this->data->$permission = $this->metadata->get('app.acl.mandatory.' . $permission); + } + } - if ($this->metadata->get('app.acl.mandatory.assignmentPermission')) { - $this->data->assignmentPermission = $this->metadata->get('app.acl.mandatory.assignmentPermission'); - } - if ($this->metadata->get('app.acl.mandatory.userPermission')) { - $this->data->userPermission = $this->metadata->get('app.acl.mandatory.userPermission'); - } } else { - $this->data->assignmentPermission = 'all'; - $this->data->userPermission = 'all'; + foreach ($this->valuePermissionList as $permission) { + $this->data->$permission = 'all'; + } } } @@ -359,10 +368,6 @@ class Table protected function applyDefault(&$table, &$fieldTable) { - if (!$this->metadata) { - return; - } - if ($this->user->isAdmin()) { return; } @@ -381,7 +386,9 @@ class Table $fieldData = $this->metadata->get('app.acl.default.fieldLevel', array()); foreach ($fieldData as $scope => $s) { + $fieldList = array_keys($this->getMetadata()->get("entityDefs.{$scope}.fields", [])); foreach ($s as $field => $f) { + if (!in_array($field, $fieldList)) continue; if (!isset($fieldTable->$scope)) { $fieldTable->$scope = (object) []; } @@ -399,9 +406,9 @@ class Table foreach ($this->getScopeWithAclList() as $scope) { if (!isset($table->$scope)) { - $aclType = $this->metadata->get('scopes.' . $scope . '.acl'); + $aclType = $this->metadata->get('scopes.' . $scope . '.' . $this->type); if ($aclType === true) { - $aclType = 'recordAllTeamOwnNo'; + $aclType = $this->defaultAclType; } if (!empty($aclType)) { $defaultValue = $this->metadata->get('app.acl.scopeLevelTypesDefaults.' . $aclType, true); @@ -416,10 +423,6 @@ class Table protected function applyMandatory(&$table, &$fieldTable) { - if (!$this->metadata) { - return; - } - if ($this->user->isAdmin()) { return; } @@ -437,10 +440,12 @@ class Table $fieldData = $this->metadata->get('app.acl.mandatory.fieldLevel', array()); foreach ($fieldData as $scope => $s) { + $fieldList = array_keys($this->getMetadata()->get("entityDefs.{$scope}.fields", [])); if (!isset($fieldTable->$scope)) { $fieldTable->$scope = (object) []; } foreach ($s as $field => $f) { + if (!in_array($field, $fieldList)) continue; $fieldTable->$scope->$field = (object) []; foreach ($this->fieldActionList as $action) { $level = 'no'; @@ -453,6 +458,20 @@ class Table } } + protected function applyDisabled(&$table, &$fieldTable) + { + if ($this->user->isAdmin()) { + return; + } + + foreach ($this->getScopeList() as $scope) { + if ($this->getMetadata()->get('scopes.' . $scope . '.disabled')) { + $aclTable->$scope = false; + unset($fieldTable->$scope); + } + } + } + private function mergeValueList(array $list, $defaultValue) { $result = null; @@ -478,9 +497,8 @@ class Table $scopeList = []; $scopes = $this->metadata->get('scopes'); foreach ($scopes as $scope => $d) { - if (!empty($d['acl'])) { - $scopeList[] = $scope; - } + if (empty($d['acl'])) continue; + $scopeList[] = $scope; } return $scopeList; } @@ -564,9 +582,13 @@ class Table if (!is_object($table->$scope)) continue; + $fieldList = array_keys($this->getMetadata()->get("entityDefs.{$scope}.fields", [])); + foreach (get_object_vars($table->$scope) as $field => $row) { if (!is_object($row)) continue; + if (!in_array($field, $fieldList)) continue; + if (!isset($data->$scope->$field)) { $data->$scope->$field = (object) []; } @@ -593,7 +615,7 @@ class Table private function buildCache() { $contents = '<' . '?'. 'php return ' . $this->varExport($this->data) . ';'; - $this->fileManager->putContents($this->cacheFile, $contents); + $this->fileManager->putContents($this->cacheFilePath, $contents); } private function varExport($variable) diff --git a/application/Espo/Core/Acl/TablePortal.php b/application/Espo/Core/Acl/TablePortal.php new file mode 100644 index 0000000000..8779290133 --- /dev/null +++ b/application/Espo/Core/Acl/TablePortal.php @@ -0,0 +1,84 @@ +metadata->get('scopes'); + foreach ($scopes as $scope => $d) { + if (empty($d['acl'])) continue; + if (empty($d['aclPortal'])) continue; + $scopeList[] = $scope; + } + return $scopeList; + } + + protected function applyDefault(&$table, &$fieldTable) + { + parent::applyDefault($table, $fieldTable); + + foreach ($this->getScopeList() as $scope) { + if (!isset($table->$scope)) { + $table->$scope = false; + } + } + } + + protected function applyDisabled(&$table, &$fieldTable) + { + foreach ($this->getScopeList() as $scope) { + $d = $this->getMetadata()->get('scopes.' . $scope); + if ($d['disabled'] || $d['portalDisabled']) { + $aclTable->$scope = false; + unset($fieldTable->$scope); + } + } + } +} + diff --git a/application/Espo/Core/Templates/Metadata/Base/scopes.json b/application/Espo/Core/Templates/Metadata/Base/scopes.json index dcdd10884a..630d4722ab 100644 --- a/application/Espo/Core/Templates/Metadata/Base/scopes.json +++ b/application/Espo/Core/Templates/Metadata/Base/scopes.json @@ -3,6 +3,7 @@ "layouts": true, "tab": true, "acl": true, + "aclPortal": true, "customizable": true, "importable": true, "notifications": true diff --git a/application/Espo/Core/Templates/Metadata/CategoryTree/scopes.json b/application/Espo/Core/Templates/Metadata/CategoryTree/scopes.json index 369be4d548..78db1a0b32 100644 --- a/application/Espo/Core/Templates/Metadata/CategoryTree/scopes.json +++ b/application/Espo/Core/Templates/Metadata/CategoryTree/scopes.json @@ -3,6 +3,7 @@ "layouts": true, "tab": true, "acl": true, + "aclPortal": true, "customizable": true, "importable": false, "notifications": false diff --git a/application/Espo/Core/Templates/Metadata/Person/scopes.json b/application/Espo/Core/Templates/Metadata/Person/scopes.json index dcdd10884a..630d4722ab 100644 --- a/application/Espo/Core/Templates/Metadata/Person/scopes.json +++ b/application/Espo/Core/Templates/Metadata/Person/scopes.json @@ -3,6 +3,7 @@ "layouts": true, "tab": true, "acl": true, + "aclPortal": true, "customizable": true, "importable": true, "notifications": true diff --git a/application/Espo/Core/defaults/layout/list.json b/application/Espo/Core/defaults/layout/list.json index b575eecc67..5c1765d118 100644 --- a/application/Espo/Core/defaults/layout/list.json +++ b/application/Espo/Core/defaults/layout/list.json @@ -1 +1,3 @@ -[{"name":"name","link":true,"width":"30"}] +[ + {"name":"name", "link":true} +] diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Account.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Account.json index 825b49bc68..458149d177 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Account.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Account.json @@ -3,6 +3,7 @@ "layouts": true, "tab": true, "acl": true, + "aclPortal": "recordAllOwnNo", "module": "Crm", "customizable": true, "stream": true, diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Call.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Call.json index 693bcd537f..eead663783 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Call.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Call.json @@ -3,6 +3,7 @@ "layouts": true, "tab": true, "acl": true, + "aclPortal": "recordAllAccountOwnNo", "module": "Crm", "customizable": true, "importable": true, diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Case.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Case.json index 825b49bc68..5e92ca86b5 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Case.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Case.json @@ -3,6 +3,7 @@ "layouts": true, "tab": true, "acl": true, + "aclPortal": "recordAllAccountOwnNo", "module": "Crm", "customizable": true, "stream": true, diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Contact.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Contact.json index e1c1310d76..208adcfaf1 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Contact.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Contact.json @@ -3,6 +3,7 @@ "layouts":true, "tab":true, "acl":true, + "aclPortal": "recordAllAccountOwnNo", "module":"Crm", "customizable": true, "stream": true, diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Document.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Document.json index 215e7e1dcc..4037e0055b 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Document.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Document.json @@ -3,6 +3,7 @@ "layouts": true, "tab": true, "acl": true, + "aclPortal": "recordAllAccountOwnNo", "module": "Crm", "customizable": true, "importable": false, diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Lead.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Lead.json index 825b49bc68..458149d177 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Lead.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Lead.json @@ -3,6 +3,7 @@ "layouts": true, "tab": true, "acl": true, + "aclPortal": "recordAllOwnNo", "module": "Crm", "customizable": true, "stream": true, diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Meeting.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Meeting.json index 693bcd537f..eead663783 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Meeting.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Meeting.json @@ -3,6 +3,7 @@ "layouts": true, "tab": true, "acl": true, + "aclPortal": "recordAllAccountOwnNo", "module": "Crm", "customizable": true, "importable": true, diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Opportunity.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Opportunity.json index 825b49bc68..5e92ca86b5 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Opportunity.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Opportunity.json @@ -3,6 +3,7 @@ "layouts": true, "tab": true, "acl": true, + "aclPortal": "recordAllAccountOwnNo", "module": "Crm", "customizable": true, "stream": true, diff --git a/application/Espo/Modules/Crm/Resources/metadata/scopes/Task.json b/application/Espo/Modules/Crm/Resources/metadata/scopes/Task.json index 693bcd537f..eead663783 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/scopes/Task.json +++ b/application/Espo/Modules/Crm/Resources/metadata/scopes/Task.json @@ -3,6 +3,7 @@ "layouts": true, "tab": true, "acl": true, + "aclPortal": "recordAllAccountOwnNo", "module": "Crm", "customizable": true, "importable": true, diff --git a/application/Espo/Resources/i18n/en_US/Admin.json b/application/Espo/Resources/i18n/en_US/Admin.json index c556a8b68f..8bffc16d17 100644 --- a/application/Espo/Resources/i18n/en_US/Admin.json +++ b/application/Espo/Resources/i18n/en_US/Admin.json @@ -19,6 +19,9 @@ "Rebuild": "Rebuild", "Teams": "Teams", "Roles": "Roles", + "Potal": "Portal", + "Portals": "Portals", + "Portal Roles": "Portal Roles", "Outbound Emails": "Outbound Emails", "Group Email Accounts": "Group Email Accounts", "Personal Email Accounts": "Personal Email Accounts", @@ -149,6 +152,8 @@ "users": "Users management.", "teams": "Teams management.", "roles": "Roles management.", + "portals": "Portals management.", + "portalRoles": "Roles for portal.", "outboundEmails": "SMTP settings for outgoing emails.", "groupEmailAccounts": "Group IMAP email accounts. Email import and Email-to-Case.", "personalEmailAccounts": "Users email accounts.", diff --git a/application/Espo/Resources/i18n/en_US/Global.json b/application/Espo/Resources/i18n/en_US/Global.json index 34b525f3b0..0281daf320 100644 --- a/application/Espo/Resources/i18n/en_US/Global.json +++ b/application/Espo/Resources/i18n/en_US/Global.json @@ -18,7 +18,8 @@ "Template": "Template", "Job": "Job", "EmailFilter": "Email Filter", - "Portal": "Portal" + "Portal": "Portal", + "PortalRole": "Portal Role" }, "scopeNamesPlural": { "Email": "Emails", @@ -39,7 +40,8 @@ "Template": "Templates", "Job": "Jobs", "EmailFilter": "Email Filters", - "Portal": "Portals" + "Portal": "Portals", + "PortalRole": "Portal Roles" }, "labels": { "Misc": "Misc", diff --git a/application/Espo/Resources/i18n/en_US/PortalRole.json b/application/Espo/Resources/i18n/en_US/PortalRole.json new file mode 100644 index 0000000000..1dd3e2f694 --- /dev/null +++ b/application/Espo/Resources/i18n/en_US/PortalRole.json @@ -0,0 +1,15 @@ +{ + "fields": { + }, + "links": { + "users": "Users" + }, + "tooltips": { + }, + "labels": { + "Access": "Access", + "Create PortalRole": "Create Portal Role", + "Scope Level": "Scope Level", + "Field Level": "Field Level" + } +} diff --git a/application/Espo/Resources/i18n/en_US/Role.json b/application/Espo/Resources/i18n/en_US/Role.json index 6e1a5fc0cb..217908817d 100644 --- a/application/Espo/Resources/i18n/en_US/Role.json +++ b/application/Espo/Resources/i18n/en_US/Role.json @@ -28,6 +28,7 @@ "levelList": { "all": "all", "team": "team", + "account": "account", "own": "own", "no": "no", "yes": "yes", diff --git a/application/Espo/Resources/metadata/app/acl.json b/application/Espo/Resources/metadata/app/acl.json index b81174d991..964e2e0756 100644 --- a/application/Espo/Resources/metadata/app/acl.json +++ b/application/Espo/Resources/metadata/app/acl.json @@ -4,7 +4,8 @@ "User": { "read": "all", "edit": "no", - "delete": "no" + "delete": "no", + "stream": "no" }, "Team": { "read": "all", diff --git a/application/Espo/Resources/metadata/app/aclPortal.json b/application/Espo/Resources/metadata/app/aclPortal.json new file mode 100644 index 0000000000..80100715f4 --- /dev/null +++ b/application/Espo/Resources/metadata/app/aclPortal.json @@ -0,0 +1,81 @@ +{ + "mandatory": { + "scopeLevel": { + "User": { + "read": "no", + "edit": "no", + "delete": "no", + "stream": "no" + }, + "Team": { + "read": "no", + "edit": "no", + "delete": "no" + }, + "Note": { + "read": "own", + "edit": "own", + "delete": "own" + }, + "Portal": false, + "EmailAddress": false, + "Attachment": { + "read": "own", + "edit": "own", + "delete": "own" + }, + "PhoneNumber": false, + "EmailAccount": { + "read": "own", + "edit": "own", + "delete": "own" + }, + "Role": false, + "PortalRole": false, + "EmailFilter": false, + "EmailAccount": false, + "EmailTemplate": false + } + }, + "default": { + "scopeLevel": { + }, + "fieldLevel": { + "assignedUser": { + "read": "yes", + "edit": "no" + }, + "assignedUsers": { + "read": "yes", + "edit": "no" + }, + "teams": { + "read": "yes", + "edit": "no" + } + } + }, + "scopeLevelTypesDefaults": { + "boolean": false, + "recordAllAccountOwnNo": { + "read": "no", + "edit": "no", + "delete": "no" + }, + "recordAllOwnNo": { + "read": "no", + "edit": "no", + "delete": "no" + }, + "recordAllAccountNo": { + "read": "no", + "edit": "no", + "delete": "no" + }, + "recordAllNo": { + "read": "no", + "edit": "no", + "delete": "no" + } + } +} diff --git a/application/Espo/Resources/metadata/app/adminPanel.json b/application/Espo/Resources/metadata/app/adminPanel.json index 2af58e55fd..b1707c20f5 100644 --- a/application/Espo/Resources/metadata/app/adminPanel.json +++ b/application/Espo/Resources/metadata/app/adminPanel.json @@ -74,6 +74,21 @@ } ] }, + "portal": { + "label":"Portal", + "items":[ + { + "url":"#Portal", + "label":"Portals", + "description":"portals" + }, + { + "url":"#PortalRole", + "label":"Portal Roles", + "description":"portalRoles" + } + ] + }, "email":{ "label":"Email", "items":[ diff --git a/application/Espo/Resources/metadata/clientDefs/PortalRole.json b/application/Espo/Resources/metadata/clientDefs/PortalRole.json new file mode 100644 index 0000000000..62da7bc203 --- /dev/null +++ b/application/Espo/Resources/metadata/clientDefs/PortalRole.json @@ -0,0 +1,17 @@ +{ + "recordViews": { + "detail":"views/portal-role/record/detail", + "edit":"views/portal-role/record/edit", + "editQuick":"views/portal-role/record/edit", + "list":"views/portal-role/record/list" + }, + "relationshipPanels": { + "users": { + "create":false, + "rowActionsView": "views/record/row-actions/relationship-unlink-only" + } + }, + "views": { + "list": "views/portal-role/list" + } +} diff --git a/application/Espo/Resources/metadata/clientDefs/Role.json b/application/Espo/Resources/metadata/clientDefs/Role.json index 46a0bb7d01..75e9c7547e 100644 --- a/application/Espo/Resources/metadata/clientDefs/Role.json +++ b/application/Espo/Resources/metadata/clientDefs/Role.json @@ -1,21 +1,21 @@ { - "recordViews":{ - "detail":"Role.Record.Detail", - "edit":"Role.Record.Edit", - "editQuick":"Role.Record.Edit", - "list":"Role.Record.List" - }, - "relationshipPanels":{ - "users":{ - "create":false, - "rowActionsView": "Record.RowActions.RelationshipUnlinkOnly" - }, - "teams":{ - "create":false, - "rowActionsView": "Record.RowActions.RelationshipUnlinkOnly" - } - }, - "views": { - "list": "Role.List" - } + "recordViews":{ + "detail":"views/role/record/detail", + "edit":"views/role/record/edit", + "editQuick":"views/role/record/edit", + "list":"views/role/record/list" + }, + "relationshipPanels": { + "users":{ + "create":false, + "rowActionsView": "views/record/row-actions/relationship-unlink-only" + }, + "teams":{ + "create":false, + "rowActionsView": "views/record/row-actions/relationship-unlink-only" + } + }, + "views": { + "list": "views/role/list" + } } diff --git a/application/Espo/Resources/metadata/scopes/Email.json b/application/Espo/Resources/metadata/scopes/Email.json index faf6104901..91f26f7d14 100644 --- a/application/Espo/Resources/metadata/scopes/Email.json +++ b/application/Espo/Resources/metadata/scopes/Email.json @@ -3,6 +3,7 @@ "layouts": false, "tab": true, "acl": true, + "aclPortal": "recordAllAccountOwnNo", "notifications": true, "object": true, "customizable": true diff --git a/application/Espo/Services/PortalRole.php b/application/Espo/Services/PortalRole.php new file mode 100644 index 0000000000..cd8ef5aa6d --- /dev/null +++ b/application/Espo/Services/PortalRole.php @@ -0,0 +1,58 @@ +dependencies[] = 'fileManager'; + } + + public function afterCreate(Entity $entity, array $data) + { + parent::afterCreate($entity, $data); + $this->clearRolesCache(); + } + + public function afterUpdate(Entity $entity, array $data) + { + parent::afterUpdate($entity, $data); + $this->clearRolesCache(); + } + + protected function clearRolesCache() + { + $this->getInjection('fileManager')->removeInDir('data/cache/application/acl/portal'); + } +} + diff --git a/frontend/client/src/controllers/portal-role.js b/frontend/client/src/controllers/portal-role.js new file mode 100644 index 0000000000..2e6d49f30f --- /dev/null +++ b/frontend/client/src/controllers/portal-role.js @@ -0,0 +1,42 @@ +/************************************************************************ + * 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/. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +Espo.define('controllers/portal-role', 'controllers/record', function (Dep) { + + return Dep.extend({ + + checkAccess: function () { + if (this.getUser().isAdmin()) { + return true; + } + return false; + } + + }); + +}); diff --git a/frontend/client/src/views/portal-role/list.js b/frontend/client/src/views/portal-role/list.js new file mode 100644 index 0000000000..940bf181b9 --- /dev/null +++ b/frontend/client/src/views/portal-role/list.js @@ -0,0 +1,36 @@ +/************************************************************************ + * 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/. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +Espo.define('views/portal-role/list', 'views/list', function (Dep) { + + return Dep.extend({ + + searchPanel: false + + }); +}); diff --git a/frontend/client/src/views/portal-role/record/detail.js b/frontend/client/src/views/portal-role/record/detail.js new file mode 100644 index 0000000000..59e1b030ca --- /dev/null +++ b/frontend/client/src/views/portal-role/record/detail.js @@ -0,0 +1,36 @@ +/************************************************************************ + * 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/. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +Espo.define('views/portal-role/record/detail', 'views/role/record/detail', function (Dep) { + + return Dep.extend({ + + tableView: 'views/portal-role/record/table', + + }); +}); diff --git a/frontend/client/src/views/portal-role/record/edit.js b/frontend/client/src/views/portal-role/record/edit.js new file mode 100644 index 0000000000..d3d4936791 --- /dev/null +++ b/frontend/client/src/views/portal-role/record/edit.js @@ -0,0 +1,38 @@ +/************************************************************************ + * 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/. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +Espo.define('views/portal-role/record/edit', 'views/role/record/edit', function (Dep) { + + return Dep.extend({ + + tableView: 'views/portal-role/record/table', + + }); +}); + + diff --git a/frontend/client/src/views/portal-role/record/list.js b/frontend/client/src/views/portal-role/record/list.js new file mode 100644 index 0000000000..e736800044 --- /dev/null +++ b/frontend/client/src/views/portal-role/record/list.js @@ -0,0 +1,37 @@ +/************************************************************************ + * 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/. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +Espo.define('views/portal-role/record/list', 'views/role/record/list', function (Dep) { + + return Dep.extend({ + + + }); + +}); + diff --git a/frontend/client/src/views/portal-role/record/table.js b/frontend/client/src/views/portal-role/record/table.js new file mode 100644 index 0000000000..98f77f77fa --- /dev/null +++ b/frontend/client/src/views/portal-role/record/table.js @@ -0,0 +1,67 @@ +/************************************************************************ + * 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/. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +Espo.define('views/portal-role/record/table', 'views/role/record/table', function (Dep) { + + return Dep.extend({ + + levelListMap: { + 'recordAllAccountOwnNo': ['all', 'account', 'own', 'no'], + 'recordAllAccountNo': ['all', 'account', 'no'], + 'recordAllOwnNo': ['all', 'own', 'no'], + 'recordAllNo': ['all', 'no'], + 'record': ['all', 'own', 'no'] + }, + + levelList: ['all', 'account', 'own', 'no'], + + setupScopeList: function () { + this.aclTypeMap = {}; + this.scopeList = []; + + var scopeListAll = Object.keys(this.getMetadata().get('scopes')).sort(function (v1, v2) { + return this.translate(v1, 'scopeNamesPlural').localeCompare(this.translate(v2, 'scopeNamesPlural')); + }.bind(this)); + + scopeListAll.forEach(function (scope) { + if (this.getMetadata().get('scopes.' + scope + '.disabled') || this.getMetadata().get('scopes.' + scope + '.disabledPortal')) return; + var acl = this.getMetadata().get('scopes.' + scope + '.aclPortal'); + if (acl) { + this.scopeList.push(scope); + this.aclTypeMap[scope] = acl; + if (acl === true) { + this.aclTypeMap[scope] = 'recordAllOwnNo'; + } + } + }, this); + } + + }); +}); + + diff --git a/frontend/client/src/views/role/list.js b/frontend/client/src/views/role/list.js index 7d07c698ed..f46838b8fa 100644 --- a/frontend/client/src/views/role/list.js +++ b/frontend/client/src/views/role/list.js @@ -24,18 +24,13 @@ * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. - ************************************************************************/ -Espo.define('Views.Role.List', 'Views.List', function (Dep) { + ************************************************************************/ +Espo.define('views/role/list', 'views/list', function (Dep) { + + return Dep.extend({ + + searchPanel: false - return Dep.extend({ - - searchPanel: false, - - setup: function () { - Dep.prototype.setup.call(this); - - }, - }); - + }); diff --git a/frontend/client/src/views/role/record/detail-side.js b/frontend/client/src/views/role/record/detail-side.js index f5def853c1..3b27f177ff 100644 --- a/frontend/client/src/views/role/record/detail-side.js +++ b/frontend/client/src/views/role/record/detail-side.js @@ -26,7 +26,7 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -Espo.define('Views.Role.Record.DetailSide', 'Views.Record.DetailSide', function (Dep) { +Espo.define('views/role/record/detail-side', 'views/record/detail-side', function (Dep) { return Dep.extend({ @@ -34,7 +34,7 @@ Espo.define('Views.Role.Record.DetailSide', 'Views.Record.DetailSide', function { name: 'default', label: false, - view: 'Role.Record.Panels.Side' + view: 'views/role/record/panels/side' } ], diff --git a/frontend/client/src/views/role/record/detail.js b/frontend/client/src/views/role/record/detail.js index 83c77873d8..07f1685b72 100644 --- a/frontend/client/src/views/role/record/detail.js +++ b/frontend/client/src/views/role/record/detail.js @@ -30,31 +30,15 @@ Espo.define('views/role/record/detail', 'views/record/detail', function (Dep) { return Dep.extend({ + tableView: 'views/role/record/table', + sideView: 'views/role/record/detail-side', editModeDisabled: true, - getDetailLayout: function (callback) { - var simpleLayout = [ - { - label: '', - cells: [ - { - name: 'name', - type: 'base', - }, - ] - } - ]; - callback({ - type: 'record', - layout: this._convertSimplifiedLayout(simpleLayout) - }); - }, - setup: function () { Dep.prototype.setup.call(this); - this.createView('extra', 'views/role/record/table', { + this.createView('extra', this.tableView, { acl: { data: this.model.get('data') || {}, fieldData: this.model.get('fieldData') || {} diff --git a/frontend/client/src/views/role/record/edit.js b/frontend/client/src/views/role/record/edit.js index 4b5ee6c1a3..4896195fcf 100644 --- a/frontend/client/src/views/role/record/edit.js +++ b/frontend/client/src/views/role/record/edit.js @@ -30,6 +30,8 @@ Espo.define('views/role/record/edit', 'views/record/edit', function (Dep) { return Dep.extend({ + tableView: 'views/role/record/table', + sideView: 'views/role/record/detail-side', events: _.extend({ @@ -101,7 +103,7 @@ Espo.define('views/role/record/edit', 'views/record/edit', function (Dep) { setup: function () { Dep.prototype.setup.call(this); - this.createView('extra', 'views/role/record/table', { + this.createView('extra', this.tableView, { mode: 'edit', acl: { data: this.model.get('data') || {}, diff --git a/frontend/client/src/views/role/record/list.js b/frontend/client/src/views/role/record/list.js index aa6634e3f4..3fbe7434ad 100644 --- a/frontend/client/src/views/role/record/list.js +++ b/frontend/client/src/views/role/record/list.js @@ -26,7 +26,7 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -Espo.define('Views.Role.Record.List', 'Views.Record.List', function (Dep) { +Espo.define('views/role/record/list', 'views/record/list', function (Dep) { return Dep.extend({ diff --git a/frontend/client/src/views/role/record/panels/side.js b/frontend/client/src/views/role/record/panels/side.js index 71ce0a578a..25ae65bb20 100644 --- a/frontend/client/src/views/role/record/panels/side.js +++ b/frontend/client/src/views/role/record/panels/side.js @@ -24,13 +24,13 @@ * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. - ************************************************************************/ + ************************************************************************/ -Espo.define('Views.Role.Record.Panels.Side', 'Views.Record.Panels.Side', function (Dep) { +Espo.define('views/role/record/panels/side', 'views/record/panels/side', function (Dep) { return Dep.extend({ - template: 'role.record.panels.side', + template: 'role/record/panels/side', }); }); diff --git a/frontend/client/src/views/role/record/table.js b/frontend/client/src/views/role/record/table.js index 898d9fd8df..ff209b0240 100644 --- a/frontend/client/src/views/role/record/table.js +++ b/frontend/client/src/views/role/record/table.js @@ -50,9 +50,12 @@ Espo.define('views/role/record/table', 'view', function (Dep) { 'record': ['all', 'team', 'own', 'no'] }, + levelList: ['all', 'team', 'own', 'no'], + colors: { yes: '#6BC924', all: '#6BC924', + account: '#999900', team: '#999900', own: '#CC9900', no: '#F23333', @@ -172,8 +175,24 @@ Espo.define('views/role/record/table', 'view', function (Dep) { this.final = this.options.final || false; - this.aclTypeMap = {}; + this.setupScopeList(); + this.listenTo(this.model, 'sync', function () { + if (this.isRendered()) { + this.reRender(); + } + }, this); + + this.setupFieldTableDataList(); + + this.template = 'role/table'; + if (this.mode == 'edit') { + this.template = 'role/table-edit'; + } + }, + + setupScopeList: function () { + this.aclTypeMap = {}; this.scopeList = []; var scopeListAll = Object.keys(this.getMetadata().get('scopes')).sort(function (v1, v2) { @@ -191,19 +210,6 @@ Espo.define('views/role/record/table', 'view', function (Dep) { } } }, this); - - this.listenTo(this.model, 'sync', function () { - if (this.isRendered()) { - this.reRender(); - } - }, this); - - this.setupFieldTableDataList(); - - this.template = 'role/table'; - if (this.mode == 'edit') { - this.template = 'role/table-edit'; - } }, setupFieldTableDataList: function () { @@ -339,14 +345,14 @@ Espo.define('views/role/record/table', 'view', function (Dep) { var $edit = this.$el.find('select[name="'+scope+'-edit"]'); if (!dontChange) { - if (this.levelListMap.record.indexOf($edit.val()) < this.levelListMap.record.indexOf(value)) { + if (this.levelList.indexOf($edit.val()) < this.levelList.indexOf(value)) { $edit.val(value); } } $edit.find('option').each(function (i, o) { var $o = $(o); - if (this.levelListMap.record.indexOf($o.val()) < this.levelListMap.record.indexOf(value)) { + if (this.levelList.indexOf($o.val()) < this.levelList.indexOf(value)) { $o.attr('disabled', 'disabled'); } else { $o.removeAttr('disabled'); @@ -358,14 +364,14 @@ Espo.define('views/role/record/table', 'view', function (Dep) { var $stream = this.$el.find('select[name="'+scope+'-stream"]'); if (!dontChange) { - if (this.levelListMap.record.indexOf($stream.val()) < this.levelListMap.record.indexOf(value)) { + if (this.levelList.indexOf($stream.val()) < this.levelList.indexOf(value)) { $stream.val(value); } } $stream.find('option').each(function (i, o) { var $o = $(o); - if (this.levelListMap.record.indexOf($o.val()) < this.levelListMap.record.indexOf(value)) { + if (this.levelList.indexOf($o.val()) < this.levelList.indexOf(value)) { $o.attr('disabled', 'disabled'); } else { $o.removeAttr('disabled'); @@ -377,14 +383,14 @@ Espo.define('views/role/record/table', 'view', function (Dep) { var $delete = this.$el.find('select[name="'+scope+'-delete"]'); if (!dontChange) { - if (this.levelListMap.record.indexOf($delete.val()) < this.levelListMap.record.indexOf(value)) { + if (this.levelList.indexOf($delete.val()) < this.levelList.indexOf(value)) { $delete.val(value); } } $delete.find('option').each(function (i, o) { var $o = $(o); - if (this.levelListMap.record.indexOf($o.val()) < this.levelListMap.record.indexOf(value)) { + if (this.levelList.indexOf($o.val()) < this.levelList.indexOf(value)) { $o.attr('disabled', 'disabled'); } else { $o.removeAttr('disabled');