From e7afe688686e1c805a0e6fbef2bc8476551b47b3 Mon Sep 17 00:00:00 2001 From: yuri Date: Mon, 11 Jan 2016 16:02:03 +0200 Subject: [PATCH] dev --- api/v1/portal/index.php | 5 +- application/Espo/Controllers/Email.php | 7 +- .../Espo/Controllers/ExternalAccount.php | 7 + application/Espo/Core/Container.php | 3 +- application/Espo/Core/ContainerPortal.php | 10 ++ .../Espo/Core/ORM/Repositories/RDB.php | 5 + application/Espo/Core/Utils/Layout.php | 36 ++--- application/Espo/Core/Utils/LayoutPortal.php | 127 ++++++++++++++++++ .../defaults/{layout => layouts}/detail.json | 0 .../{layout => layouts}/detailSmall.json | 0 .../defaults/{layout => layouts}/filters.json | 0 .../defaults/{layout => layouts}/list.json | 0 .../{layout => layouts}/listDashlet.json | 0 .../{layout => layouts}/listSmall.json | 0 .../{layout => layouts}/massUpdate.json | 0 .../{layout => layouts}/relationships.json | 0 application/Espo/EntryPoints/Portal.php | 11 +- application/Espo/Repositories/Attachment.php | 2 +- application/Espo/Repositories/Portal.php | 62 +++++++++ .../Espo/Resources/i18n/en_US/User.json | 3 +- .../Espo/Resources/layouts/Portal/detail.json | 3 +- .../layouts/Preferences/portal/detail.json | 31 +++++ .../Resources/metadata/app/aclPortal.json | 1 + .../Resources/metadata/clientDefs/Portal.json | 5 +- .../Resources/metadata/clientDefs/Team.json | 17 +-- .../Resources/metadata/clientDefs/User.json | 2 +- .../Resources/metadata/entityDefs/Portal.json | 4 +- .../metadata/scopes/ExternalAccount.json | 3 +- application/Espo/SelectManagers/User.php | 2 +- application/Espo/Services/Email.php | 60 +++++---- application/Espo/Services/Portal.php | 49 +++++++ .../client/res/templates/record/bottom.tpl | 4 +- frontend/client/res/templates/record/side.tpl | 4 +- frontend/client/src/app.js | 5 +- frontend/client/src/layout-manager.js | 31 +++-- frontend/client/src/views/detail.js | 6 +- frontend/client/src/views/email/detail.js | 8 +- .../src/views/fields/attachment-multiple.js | 2 +- frontend/client/src/views/fields/file.js | 2 +- frontend/client/src/views/fields/image.js | 2 +- .../views/portal/fields/quick-create-list.js | 45 +++++++ .../src/views/portal/fields/tab-list.js | 45 +++++++ .../src/views/record/panels/relationship.js | 11 +- frontend/client/src/views/user/detail.js | 37 ++--- frontend/html/portal.html | 1 + html/portal.html | 1 + 46 files changed, 538 insertions(+), 121 deletions(-) create mode 100644 application/Espo/Core/Utils/LayoutPortal.php rename application/Espo/Core/defaults/{layout => layouts}/detail.json (100%) rename application/Espo/Core/defaults/{layout => layouts}/detailSmall.json (100%) rename application/Espo/Core/defaults/{layout => layouts}/filters.json (100%) rename application/Espo/Core/defaults/{layout => layouts}/list.json (100%) rename application/Espo/Core/defaults/{layout => layouts}/listDashlet.json (100%) rename application/Espo/Core/defaults/{layout => layouts}/listSmall.json (100%) rename application/Espo/Core/defaults/{layout => layouts}/massUpdate.json (100%) rename application/Espo/Core/defaults/{layout => layouts}/relationships.json (100%) create mode 100644 application/Espo/Repositories/Portal.php create mode 100644 application/Espo/Resources/layouts/Preferences/portal/detail.json create mode 100644 application/Espo/Services/Portal.php create mode 100644 frontend/client/src/views/portal/fields/quick-create-list.js create mode 100644 frontend/client/src/views/portal/fields/tab-list.js diff --git a/api/v1/portal/index.php b/api/v1/portal/index.php index 57687bb851..6ab5c3479f 100644 --- a/api/v1/portal/index.php +++ b/api/v1/portal/index.php @@ -29,10 +29,7 @@ require_once('../../../bootstrap.php'); -//print_r($_SERVER); -//die; - -$portalId = '567d4a0503c88c061'; +$portalId = explode('/', $_SERVER['REQUEST_URI'])[count(explode('/', $_SERVER['SCRIPT_NAME'])) - 1]; $app = new \Espo\Core\ApplicationPortal($portalId); $app->run(); diff --git a/application/Espo/Controllers/Email.php b/application/Espo/Controllers/Email.php index e109565c22..61cf329dd7 100644 --- a/application/Espo/Controllers/Email.php +++ b/application/Espo/Controllers/Email.php @@ -35,9 +35,12 @@ use \Espo\Core\Exceptions\Error; class Email extends \Espo\Core\Controllers\Record { - public function actionGetCopiedAttachments($params, $data, $request) + public function postActionGetCopiedAttachments($params, $data, $request) { - $id = $request->get('id'); + if (empty($data['id'])) { + throw new BadRequest(); + } + $id = $data['id']; return $this->getRecordService()->getCopiedAttachments($id); } diff --git a/application/Espo/Controllers/ExternalAccount.php b/application/Espo/Controllers/ExternalAccount.php index 28663947cc..10119f61ef 100644 --- a/application/Espo/Controllers/ExternalAccount.php +++ b/application/Espo/Controllers/ExternalAccount.php @@ -37,6 +37,13 @@ class ExternalAccount extends \Espo\Core\Controllers\Record { public static $defaultAction = 'list'; + protected function checkControllerAccess() + { + if (!$this->getAcl()->checkScope('ExternalAccount')) { + throw new Forbidden(); + } + } + public function actionList($params, $data, $request) { $integrations = $this->getEntityManager()->getRepository('Integration')->find(); diff --git a/application/Espo/Core/Container.php b/application/Espo/Core/Container.php index 31895283dd..c2ca214690 100644 --- a/application/Espo/Core/Container.php +++ b/application/Espo/Core/Container.php @@ -215,7 +215,8 @@ class Container { return new \Espo\Core\Utils\Layout( $this->get('fileManager'), - $this->get('metadata') + $this->get('metadata'), + $this->get('user') ); } diff --git a/application/Espo/Core/ContainerPortal.php b/application/Espo/Core/ContainerPortal.php index bc142805a2..f81102aaee 100644 --- a/application/Espo/Core/ContainerPortal.php +++ b/application/Espo/Core/ContainerPortal.php @@ -63,6 +63,16 @@ class ContainerPortal extends Container $this->get('portal') ); } + + protected function loadLayout() + { + return new \Espo\Core\Utils\LayoutPortal( + $this->get('fileManager'), + $this->get('metadata'), + $this->get('user') + ); + } + public function setPortal(\Espo\Entities\Portal $portal) { $this->set('portal', $portal); diff --git a/application/Espo/Core/ORM/Repositories/RDB.php b/application/Espo/Core/ORM/Repositories/RDB.php index c08c68f573..d3c32c0f08 100644 --- a/application/Espo/Core/ORM/Repositories/RDB.php +++ b/application/Espo/Core/ORM/Repositories/RDB.php @@ -47,6 +47,11 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable private $restoreData = null; + protected function addDependency($name) + { + $this->dependencies[] = $name; + } + public function inject($name, $object) { $this->injections[$name] = $object; diff --git a/application/Espo/Core/Utils/Layout.php b/application/Espo/Core/Utils/Layout.php index 7b8e838d42..8d06504a2b 100644 --- a/application/Espo/Core/Utils/Layout.php +++ b/application/Espo/Core/Utils/Layout.php @@ -28,18 +28,16 @@ ************************************************************************/ namespace Espo\Core\Utils; + class Layout { private $fileManager; private $metadata; - private $changedData = array(); + private $user; - /** - * @var string - uses for loading default values - */ - private $name = 'layout'; + protected $changedData = array(); protected $params = array( 'defaultsPath' => 'application/Espo/Core/defaults', @@ -49,17 +47,18 @@ class Layout /** * @var array - path to layout files */ - private $paths = array( + protected $paths = array( 'corePath' => 'application/Espo/Resources/layouts', 'modulePath' => 'application/Espo/Modules/{*}/Resources/layouts', 'customPath' => 'custom/Espo/Custom/Resources/layouts', ); - public function __construct(\Espo\Core\Utils\File\Manager $fileManager, \Espo\Core\Utils\Metadata $metadata) + public function __construct(\Espo\Core\Utils\File\Manager $fileManager, \Espo\Core\Utils\Metadata $metadata, \Espo\Entities\User $user) { $this->fileManager = $fileManager; $this->metadata = $metadata; + $this->user = $user; } protected function getFileManager() @@ -72,6 +71,11 @@ class Layout return $this->metadata; } + protected function getUser() + { + return $this->user; + } + protected function sanitizeInput($name) { return preg_replace("([\.]{2,})", '', $name); @@ -94,16 +98,14 @@ class Layout return Json::encode($this->changedData[$scope][$name]); } - $fileFullPath = Util::concatPath($this->getLayoutPath($scope, true), $name.'.json'); + $fileFullPath = Util::concatPath($this->getLayoutPath($scope, true), $name . '.json'); if (!file_exists($fileFullPath)) { - $fileFullPath = Util::concatPath($this->getLayoutPath($scope), $name.'.json'); + $fileFullPath = Util::concatPath($this->getLayoutPath($scope), $name . '.json'); } if (!file_exists($fileFullPath)) { - //load defaults $defaultPath = $this->params['defaultsPath']; - $fileFullPath = Util::concatPath( Util::concatPath($defaultPath, $this->name), $name.'.json' ); - //END: load defaults + $fileFullPath = Util::concatPath(Util::concatPath($defaultPath, 'layouts'), $name . '.json' ); if (!file_exists($fileFullPath)) { return false; @@ -162,11 +164,9 @@ class Layout if (!empty($this->changedData)) { foreach ($this->changedData as $scope => $rowData) { foreach ($rowData as $layoutName => $layoutData) { - if (empty($scope) || empty($layoutName)) { continue; } - $layoutPath = $this->getLayoutPath($scope, true); $data = Json::encode($layoutData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); @@ -221,17 +221,17 @@ class Layout /** * Get Layout path, ex. application/Modules/Crm/Layouts/Account * - * @param string $entityName + * @param string $entityType * @param bool $isCustom - if need to check custom folder * * @return string */ - protected function getLayoutPath($entityName, $isCustom = false) + protected function getLayoutPath($entityType, $isCustom = false) { $path = $this->paths['customPath']; if (!$isCustom) { - $moduleName = $this->getMetadata()->getScopeModuleName($entityName); + $moduleName = $this->getMetadata()->getScopeModuleName($entityType); $path = $this->paths['corePath']; if ($moduleName !== false) { @@ -239,7 +239,7 @@ class Layout } } - $path = Util::concatPath($path, $entityName); + $path = Util::concatPath($path, $entityType); return $path; } diff --git a/application/Espo/Core/Utils/LayoutPortal.php b/application/Espo/Core/Utils/LayoutPortal.php new file mode 100644 index 0000000000..73d22e9fe0 --- /dev/null +++ b/application/Espo/Core/Utils/LayoutPortal.php @@ -0,0 +1,127 @@ +sanitizeInput($scope); + $name = $this->sanitizeInput($name); + + if (isset($this->changedData[$scope][$name])) { + return Json::encode($this->changedData[$scope][$name]); + } + + $fileFullPath = Util::concatPath($this->getLayoutPath($scope, true), 'portal/' . $name . '.json'); + + if (!file_exists($fileFullPath)) { + $fileFullPath = Util::concatPath($this->getLayoutPath($scope), 'portal/' . $name . '.json'); + } + if (!file_exists($fileFullPath)) { + $fileFullPath = Util::concatPath($this->getLayoutPath($scope, true), $name . '.json'); + } + if (!file_exists($fileFullPath)) { + $fileFullPath = Util::concatPath($this->getLayoutPath($scope), $name . '.json'); + } + + + if (!file_exists($fileFullPath)) { + $defaultPath = $this->params['defaultsPath']; + $fileFullPath = Util::concatPath(Util::concatPath($defaultPath, 'layouts'), $name . '.json' ); + + if (!file_exists($fileFullPath)) { + return false; + } + } + + return $this->getFileManager()->getContents($fileFullPath); + } + + + public function set($data, $scope, $name) + { + $scope = $this->sanitizeInput($scope); + $name = $this->sanitizeInput($name); + + if (empty($scope) || empty($name)) { + return false; + } + + $this->changedData[$scope][$name] = $data; + } + + public function resetToDefault($scope, $name) + { + $scope = $this->sanitizeInput($scope); + $name = $this->sanitizeInput($name); + + $filePath = 'custom/Espo/Custom/Resources/layouts/' . $scope . '/' . $name . '.json'; + if ($this->getFileManager()->isFile($filePath)) { + $this->getFileManager()->removeFile($filePath); + } + if (!empty($this->changedData[$scope]) && !empty($this->changedData[$scope][$name])) { + unset($this->changedData[$scope][$name]); + } + return $this->get($scope, $name); + } + + /** + * Save changes + * + * @return bool + */ + public function save() + { + $result = true; + + if (!empty($this->changedData)) { + foreach ($this->changedData as $scope => $rowData) { + foreach ($rowData as $layoutName => $layoutData) { + if (empty($scope) || empty($layoutName)) { + continue; + } + $layoutPath = $this->getLayoutPath($scope, true); + $data = Json::encode($layoutData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + + $result &= $this->getFileManager()->putContents(array($layoutPath, $layoutName.'.json'), $data); + } + } + } + + if ($result == true) { + $this->clearChanges(); + } + + return (bool) $result; + } + +} diff --git a/application/Espo/Core/defaults/layout/detail.json b/application/Espo/Core/defaults/layouts/detail.json similarity index 100% rename from application/Espo/Core/defaults/layout/detail.json rename to application/Espo/Core/defaults/layouts/detail.json diff --git a/application/Espo/Core/defaults/layout/detailSmall.json b/application/Espo/Core/defaults/layouts/detailSmall.json similarity index 100% rename from application/Espo/Core/defaults/layout/detailSmall.json rename to application/Espo/Core/defaults/layouts/detailSmall.json diff --git a/application/Espo/Core/defaults/layout/filters.json b/application/Espo/Core/defaults/layouts/filters.json similarity index 100% rename from application/Espo/Core/defaults/layout/filters.json rename to application/Espo/Core/defaults/layouts/filters.json diff --git a/application/Espo/Core/defaults/layout/list.json b/application/Espo/Core/defaults/layouts/list.json similarity index 100% rename from application/Espo/Core/defaults/layout/list.json rename to application/Espo/Core/defaults/layouts/list.json diff --git a/application/Espo/Core/defaults/layout/listDashlet.json b/application/Espo/Core/defaults/layouts/listDashlet.json similarity index 100% rename from application/Espo/Core/defaults/layout/listDashlet.json rename to application/Espo/Core/defaults/layouts/listDashlet.json diff --git a/application/Espo/Core/defaults/layout/listSmall.json b/application/Espo/Core/defaults/layouts/listSmall.json similarity index 100% rename from application/Espo/Core/defaults/layout/listSmall.json rename to application/Espo/Core/defaults/layouts/listSmall.json diff --git a/application/Espo/Core/defaults/layout/massUpdate.json b/application/Espo/Core/defaults/layouts/massUpdate.json similarity index 100% rename from application/Espo/Core/defaults/layout/massUpdate.json rename to application/Espo/Core/defaults/layouts/massUpdate.json diff --git a/application/Espo/Core/defaults/layout/relationships.json b/application/Espo/Core/defaults/layouts/relationships.json similarity index 100% rename from application/Espo/Core/defaults/layout/relationships.json rename to application/Espo/Core/defaults/layouts/relationships.json diff --git a/application/Espo/EntryPoints/Portal.php b/application/Espo/EntryPoints/Portal.php index 9f7b240e6c..2b72b92d47 100644 --- a/application/Espo/EntryPoints/Portal.php +++ b/application/Espo/EntryPoints/Portal.php @@ -39,13 +39,16 @@ class Portal extends \Espo\Core\EntryPoints\Base public function run() { - if (empty($_GET['id'])) { - throw new BadRequest(); + if (!empty($_GET['id'])) { + $id = $_GET['id']; + } else { + $id = $this->getConfig()->get('defaultPortalId'); + if (!$id) { + throw new NotFound(); + } } - $id = $_GET['id']; $application = new \Espo\Core\ApplicationPortal($id); $application->runClient(); } } - diff --git a/application/Espo/Repositories/Attachment.php b/application/Espo/Repositories/Attachment.php index f047a96f84..9867468034 100644 --- a/application/Espo/Repositories/Attachment.php +++ b/application/Espo/Repositories/Attachment.php @@ -25,7 +25,7 @@ * * 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. - ************************************************************************/ + ************************************************************************/ namespace Espo\Repositories; diff --git a/application/Espo/Repositories/Portal.php b/application/Espo/Repositories/Portal.php new file mode 100644 index 0000000000..cccd61b3ea --- /dev/null +++ b/application/Espo/Repositories/Portal.php @@ -0,0 +1,62 @@ +addDependency('config'); + } + + protected function getConfig() + { + return $this->getInjection('config'); + } + + protected function afterSave(Entity $entity, array $options) + { + parent::afterSave($entity, $options); + + if ($entity->has('isDefault')) { + if ($entity->get('isDefault')) { + $this->getConfig()->set('defaultPortalId', $entity->id); + } else { + $this->getConfig()->set('defaultPortalId', null); + } + $this->getConfig()->save(); + } + } +} + diff --git a/application/Espo/Resources/i18n/en_US/User.json b/application/Espo/Resources/i18n/en_US/User.json index 47fda00b03..9d88f2d8d0 100644 --- a/application/Espo/Resources/i18n/en_US/User.json +++ b/application/Espo/Resources/i18n/en_US/User.json @@ -74,6 +74,7 @@ "onlyMyTeam": "Only My Team" }, "presetFilters": { - "active": "Active" + "active": "Active", + "activePortal": "Portal" } } diff --git a/application/Espo/Resources/layouts/Portal/detail.json b/application/Espo/Resources/layouts/Portal/detail.json index 3ec4df0f4d..563f31afdf 100644 --- a/application/Espo/Resources/layouts/Portal/detail.json +++ b/application/Espo/Resources/layouts/Portal/detail.json @@ -3,7 +3,8 @@ "label": "General", "rows": [ [{"name": "name"}, {"name": "isActive"}], - [{"name": "url"}, {"name": "isDefault"}] + [{"name": "url"}, {"name": "isDefault"}], + [{"name": "portalRoles"}, false] ] }, { diff --git a/application/Espo/Resources/layouts/Preferences/portal/detail.json b/application/Espo/Resources/layouts/Preferences/portal/detail.json new file mode 100644 index 0000000000..7aa8371cdb --- /dev/null +++ b/application/Espo/Resources/layouts/Preferences/portal/detail.json @@ -0,0 +1,31 @@ +[ + { + "label": "Locale", + "rows": [ + [{"name": "dateFormat"}, {"name": "timeFormat"}], + [{"name": "timeZone"}, {"name": "weekStart"}], + [{"name": "defaultCurrency"}, false], + [{"name": "thousandSeparator"}, {"name": "decimalMark"}], + [{"name": "language"}, false] + ] + }, + { + "label": "Misc", + "rows": [ + [{"name": "exportDelimiter"}, {"name":"autoFollowEntityTypeList"}] + ] + }, + { + "label": "User Interface", + "rows": [ + [ + {"name":"theme"}, + false + ], + [ + {"name":"useCustomTabList"}, + {"name":"tabList"} + ] + ] + } +] diff --git a/application/Espo/Resources/metadata/app/aclPortal.json b/application/Espo/Resources/metadata/app/aclPortal.json index 2e1384a6af..a3c267fdeb 100644 --- a/application/Espo/Resources/metadata/app/aclPortal.json +++ b/application/Espo/Resources/metadata/app/aclPortal.json @@ -20,6 +20,7 @@ "delete": "own" }, "EmailAccount": false, + "ExternalAccount": false, "Role": false, "PortalRole": false, "EmailFilter": false, diff --git a/application/Espo/Resources/metadata/clientDefs/Portal.json b/application/Espo/Resources/metadata/clientDefs/Portal.json index eb8ec9625e..e4d518c93a 100644 --- a/application/Espo/Resources/metadata/clientDefs/Portal.json +++ b/application/Espo/Resources/metadata/clientDefs/Portal.json @@ -2,9 +2,10 @@ "controller": "controllers/record", "relationshipPanels": { "users": { - "create": true, + "create": false, "rowActionsView": "views/record/row-actions/relationship-unlink-only", - "layout": "listForTeam" + "layout": "listSmall", + "selectPrimaryFilterName": "activePortal" } }, "recordViews": { diff --git a/application/Espo/Resources/metadata/clientDefs/Team.json b/application/Espo/Resources/metadata/clientDefs/Team.json index de25fa5d15..90bcb069db 100644 --- a/application/Espo/Resources/metadata/clientDefs/Team.json +++ b/application/Espo/Resources/metadata/clientDefs/Team.json @@ -1,15 +1,16 @@ { - "relationshipPanels":{ + "relationshipPanels": { "users":{ - "create":false, - "rowActionsView": "Record.RowActions.RelationshipUnlinkOnly", - "layout":"listForTeam" + "create": false, + "rowActionsView": "views/record/row-actions/relationship-unlink-only", + "layout": "listForTeam", + "selectPrimaryFilterName": "active" } }, - "recordViews":{ - "detail":"Team.Record.Detail", - "edit":"Team.Record.Edit", - "list":"Team.Record.List" + "recordViews": { + "detail": "views/team/record/detail", + "edit": "views/team/record/edit", + "list": "views/team/record/list" }, "boolFilterList": ["onlyMy"] } diff --git a/application/Espo/Resources/metadata/clientDefs/User.json b/application/Espo/Resources/metadata/clientDefs/User.json index 928eef3057..db8056d6c1 100644 --- a/application/Espo/Resources/metadata/clientDefs/User.json +++ b/application/Espo/Resources/metadata/clientDefs/User.json @@ -9,6 +9,6 @@ "editQuick":"User.Record.Edit", "list":"User.Record.List" }, - "filterList": ["active"], + "filterList": ["active","activePortal"], "boolFilterList": ["onlyMyTeam"] } diff --git a/application/Espo/Resources/metadata/entityDefs/Portal.json b/application/Espo/Resources/metadata/entityDefs/Portal.json index b21301f53f..678a5ff597 100644 --- a/application/Espo/Resources/metadata/entityDefs/Portal.json +++ b/application/Espo/Resources/metadata/entityDefs/Portal.json @@ -28,12 +28,12 @@ "tabList": { "type": "array", "translation": "Global.scopeNamesPlural", - "view": "views/settings/fields/tab-list" + "view": "views/portal/fields/tab-list" }, "quickCreateList": { "type": "array", "translation": "Global.scopeNames", - "view": "views/settings/fields/quick-create-list" + "view": "views/portal/fields/quick-create-list" }, "companyLogo": { "type": "image" diff --git a/application/Espo/Resources/metadata/scopes/ExternalAccount.json b/application/Espo/Resources/metadata/scopes/ExternalAccount.json index 432d5091d0..1bb0cdec39 100644 --- a/application/Espo/Resources/metadata/scopes/ExternalAccount.json +++ b/application/Espo/Resources/metadata/scopes/ExternalAccount.json @@ -2,6 +2,7 @@ "entity":true, "layouts":false, "tab":false, - "acl":false, + "acl": "boolean", + "aclPortal": false, "customizable":false } diff --git a/application/Espo/SelectManagers/User.php b/application/Espo/SelectManagers/User.php index e72ee66093..647d559b25 100644 --- a/application/Espo/SelectManagers/User.php +++ b/application/Espo/SelectManagers/User.php @@ -53,7 +53,7 @@ class User extends \Espo\Core\SelectManagers\Base ); } - protected function filterActivePortalUsers(&$result) + protected function filterActivePortal(&$result) { $result['whereClause'][] = array( 'isActive' => true, diff --git a/application/Espo/Services/Email.php b/application/Espo/Services/Email.php index 50b349819b..b7dac4d9ff 100644 --- a/application/Espo/Services/Email.php +++ b/application/Espo/Services/Email.php @@ -34,6 +34,9 @@ use \Espo\Entities; use \Espo\Core\Exceptions\Error; use \Espo\Core\Exceptions\Forbidden; +use \Espo\Core\Exceptions\NotFound; +use \Espo\Core\Exceptions\BadRequest; + class Email extends Record { @@ -548,36 +551,41 @@ class Email extends Record $ids = array(); $names = new \stdClass(); - if (!empty($id)) { - $email = $this->getEntityManager()->getEntity('Email', $id); - if ($email && $this->getAcl()->check($email, 'read')) { - $email->loadLinkMultipleField('attachments'); - $attachmentsIds = $email->get('attachmentsIds'); + if (empty($id)) { + throw new BadRequest(); + } + $email = $this->getEntityManager()->getEntity('Email', $id); + if (!$email) { + throw new NotFound(); + } + if (!$this->getAcl()->checkEntity($email, 'read')) { + throw new Forbidden(); + } + $email->loadLinkMultipleField('attachments'); + $attachmentsIds = $email->get('attachmentsIds'); - foreach ($attachmentsIds as $attachmentId) { - $source = $this->getEntityManager()->getEntity('Attachment', $attachmentId); - if ($source) { - $attachment = $this->getEntityManager()->getEntity('Attachment'); - $attachment->set('role', 'Attachment'); - $attachment->set('type', $source->get('type')); - $attachment->set('size', $source->get('size')); - $attachment->set('global', $source->get('global')); - $attachment->set('name', $source->get('name')); + foreach ($attachmentsIds as $attachmentId) { + $source = $this->getEntityManager()->getEntity('Attachment', $attachmentId); + if ($source) { + $attachment = $this->getEntityManager()->getEntity('Attachment'); + $attachment->set('role', 'Attachment'); + $attachment->set('type', $source->get('type')); + $attachment->set('size', $source->get('size')); + $attachment->set('global', $source->get('global')); + $attachment->set('name', $source->get('name')); - if (!empty($parentType) && !empty($parentId)) { - $attachment->set('parentType', $parentType); - $attachment->set('parentId', $parentId); - } + if (!empty($parentType) && !empty($parentId)) { + $attachment->set('parentType', $parentType); + $attachment->set('parentId', $parentId); + } - $this->getEntityManager()->saveEntity($attachment); + $this->getEntityManager()->saveEntity($attachment); - $contents = $this->getFileManager()->getContents('data/upload/' . $source->id); - if (!empty($contents)) { - $this->getFileManager()->putContents('data/upload/' . $attachment->id, $contents); - $ids[] = $attachment->id; - $names->{$attachment->id} = $attachment->get('name'); - } - } + $contents = $this->getFileManager()->getContents('data/upload/' . $source->id); + if (!empty($contents)) { + $this->getFileManager()->putContents('data/upload/' . $attachment->id, $contents); + $ids[] = $attachment->id; + $names->{$attachment->id} = $attachment->get('name'); } } } diff --git a/application/Espo/Services/Portal.php b/application/Espo/Services/Portal.php new file mode 100644 index 0000000000..4acccfbe9e --- /dev/null +++ b/application/Espo/Services/Portal.php @@ -0,0 +1,49 @@ +getConfig()->get('siteUrl'); + $url = $siteUrl . '?entryPoint=portal'; + if ($entity->id === $this->getConfig()->get('defaultPortalId')) { + $entity->set('isDefault', true); + } else { + $url .= '&id=' . $entity->id; + } + $entity->set('url', $url); + } +} + diff --git a/frontend/client/res/templates/record/bottom.tpl b/frontend/client/res/templates/record/bottom.tpl index 3e3111b0ed..c3e4108e6d 100644 --- a/frontend/client/res/templates/record/bottom.tpl +++ b/frontend/client/res/templates/record/bottom.tpl @@ -4,7 +4,7 @@
{{#if buttonList}} {{#each buttonList}} - + {{/each}} {{/if}} {{#if actionList}} @@ -13,7 +13,7 @@ {{/if}} diff --git a/frontend/client/res/templates/record/side.tpl b/frontend/client/res/templates/record/side.tpl index 01f64c103e..b61b5fc48a 100644 --- a/frontend/client/res/templates/record/side.tpl +++ b/frontend/client/res/templates/record/side.tpl @@ -5,7 +5,7 @@
{{#if buttonList}} {{#each buttonList}} - + {{/each}} {{/if}} {{#if actionList}} @@ -14,7 +14,7 @@ {{/if}} diff --git a/frontend/client/src/app.js b/frontend/client/src/app.js index 8051b01f0d..2838c213ea 100644 --- a/frontend/client/src/app.js +++ b/frontend/client/src/app.js @@ -35,6 +35,8 @@ Espo.define( var App = function (options, callback) { var options = options || {}; + this.id = options.id || 'espocrm-application-id'; + this.useCache = options.useCache || this.useCache; this.url = options.url || this.url; @@ -159,6 +161,7 @@ Espo.define( this.settings.defs = this.metadata.get('entityDefs.Settings'); this.user.defs = this.metadata.get('entityDefs.User'); this.preferences.defs = this.metadata.get('entityDefs.Preferences'); + this.viewHelper.layoutManager.userId = this.user.id; if (this.themeManager.isUserTheme()) { $('#main-stylesheet').attr('href', this.themeManager.getStylesheet()); @@ -295,7 +298,7 @@ Espo.define( initView: function () { var helper = this.viewHelper = new ViewHelper(); - helper.layoutManager = new LayoutManager({cache: this.cache}); + helper.layoutManager = new LayoutManager({cache: this.cache, applicationId: this.id}); helper.settings = this.settings; helper.user = this.user; helper.preferences = this.preferences; diff --git a/frontend/client/src/layout-manager.js b/frontend/client/src/layout-manager.js index b5dd186bc5..14fbe6db72 100644 --- a/frontend/client/src/layout-manager.js +++ b/frontend/client/src/layout-manager.js @@ -28,11 +28,13 @@ Espo.define('layout-manager', [], function () { - var LayoutManager = function (options) { + var LayoutManager = function (options, userId) { var options = options || {}; this.cache = options.cache || null; + this.applicationId = options.applicationId || 'default-id'; this.data = {}; this.ajax = $.ajax; + this.userId = userId; } _.extend(LayoutManager.prototype, { @@ -41,11 +43,14 @@ Espo.define('layout-manager', [], function () { data: null, - _getKey: function (scope, type) { - return scope + '-' + type; + getKey: function (scope, type) { + if (this.userId) { + return this.applicationId + '-' + this.userId + '-' + scope + '-' + type; + } + return this.applicationId + '-' + scope + '-' + type; }, - _getUrl: function (scope, type) { + getUrl: function (scope, type) { return scope + '/layout/' + type; }, @@ -54,7 +59,7 @@ Espo.define('layout-manager', [], function () { cache = true; } - var key = this._getKey(scope, type); + var key = this.getKey(scope, type); if (cache) { if (key in this.data) { @@ -65,7 +70,7 @@ Espo.define('layout-manager', [], function () { } } - if (this.cache !== null && cache) { + if (this.cache && cache) { var cached = this.cache.get('app-layout', key); if (cached) { if (typeof callback === 'function') { @@ -77,7 +82,7 @@ Espo.define('layout-manager', [], function () { } this.ajax({ - url: this._getUrl(scope, type), + url: this.getUrl(scope, type), type: 'GET', dataType: 'json', success: function (layout) { @@ -85,7 +90,7 @@ Espo.define('layout-manager', [], function () { callback(layout); } this.data[key] = layout; - if (this.cache !== null) { + if (this.cache) { this.cache.set('app-layout', key, layout); } }.bind(this) @@ -93,14 +98,14 @@ Espo.define('layout-manager', [], function () { }, set: function (scope, type, layout, callback) { - var key = this._getKey(scope, type); + var key = this.getKey(scope, type); this.ajax({ - url: this._getUrl(scope, type), + url: this.getUrl(scope, type), type: 'PUT', data: JSON.stringify(layout), success: function () { - if (this.cache !== null) { + if (this.cache && key) { this.cache.set('app-layout', key, layout); } this.data[key] = layout; @@ -113,7 +118,7 @@ Espo.define('layout-manager', [], function () { }, resetToDefault: function (scope, type, callback) { - var key = this._getKey(scope, type); + var key = this.getKey(scope, type); this.ajax({ url: 'Layout/action/resetToDefault', @@ -123,7 +128,7 @@ Espo.define('layout-manager', [], function () { name: type }), success: function (layout) { - if (this.cache !== null) { + if (this.cache) { this.cache.clear('app-layout', key); } this.data[key] = layout; diff --git a/frontend/client/src/views/detail.js b/frontend/client/src/views/detail.js index f850fc80eb..cc4659501c 100644 --- a/frontend/client/src/views/detail.js +++ b/frontend/client/src/views/detail.js @@ -187,6 +187,8 @@ Espo.define('views/detail', 'views/main', function (Dep) { selectBoolFilterLists: [], actionCreateRelated: function (data) { + data = data || {}; + var link = data.link; var scope = this.model.defs['links'][link].entity; var foreignLink = this.model.defs['links'][link].foreign; @@ -252,12 +254,12 @@ Espo.define('views/detail', 'views/main', function (Dep) { } } - var primaryFilterName = this.selectPrimaryFilterNames[link] || null; + var primaryFilterName = data.primaryFilterName || this.selectPrimaryFilterNames[link] || null; if (typeof primaryFilterName == 'function') { primaryFilterName = primaryFilterName.call(this); } - var boolFilterList = Espo.Utils.cloneDeep(this.selectBoolFilterLists[link] || []); + var boolFilterList = data.boolFilterList || Espo.Utils.cloneDeep(this.selectBoolFilterLists[link] || []); if (typeof boolFilterList == 'function') { boolFilterList = boolFilterList.call(this); } diff --git a/frontend/client/src/views/email/detail.js b/frontend/client/src/views/email/detail.js index 850e0ff1ab..3a1364e196 100644 --- a/frontend/client/src/views/email/detail.js +++ b/frontend/client/src/views/email/detail.js @@ -351,16 +351,16 @@ Espo.define('views/email/detail', ['views/detail', 'email-helper'], function (De $.ajax({ url: 'Email/action/getCopiedAttachments', - type: 'GET', - data: { + type: 'POST', + data: JSON.stringify({ id: this.model.id - } + }) }).done(function (data) { attributes['attachmentsIds'] = data.ids; attributes['attachmentsNames'] = data.names; this.notify('Loading...'); - this.createView('quickCreate', 'Modals.ComposeEmail', { + this.createView('quickCreate', 'views/modals/compose-email', { attributes: attributes, }, function (view) { view.render(function () { diff --git a/frontend/client/src/views/fields/attachment-multiple.js b/frontend/client/src/views/fields/attachment-multiple.js index 80170eb6ba..3be67722e6 100644 --- a/frontend/client/src/views/fields/attachment-multiple.js +++ b/frontend/client/src/views/fields/attachment-multiple.js @@ -172,7 +172,7 @@ Espo.define('views/fields/attachment-multiple', 'views/fields/base', function (D getImageUrl: function (id, size) { var url = '?entryPoint=image&id=' + id; if (size) { - size += '&size=' + size; + url += '&size=' + size; } if (this.getUser().get('portalId')) { url += '&portalId=' + this.getUser().get('portalId'); diff --git a/frontend/client/src/views/fields/file.js b/frontend/client/src/views/fields/file.js index 78c1d1a57e..1087c867f6 100644 --- a/frontend/client/src/views/fields/file.js +++ b/frontend/client/src/views/fields/file.js @@ -189,7 +189,7 @@ Espo.define('views/fields/file', 'views/fields/link', function (Dep) { getImageUrl: function (id, size) { var url = '?entryPoint=image&id=' + id; if (size) { - size += '&size=' + size; + url += '&size=' + size; } if (this.getUser().get('portalId')) { url += '&portalId=' + this.getUser().get('portalId'); diff --git a/frontend/client/src/views/fields/image.js b/frontend/client/src/views/fields/image.js index 0b5bcf20d6..fb6289de1a 100644 --- a/frontend/client/src/views/fields/image.js +++ b/frontend/client/src/views/fields/image.js @@ -46,7 +46,7 @@ Espo.define('views/fields/image', 'views/fields/file', function (Dep) { if ('previewSize' in this.params && this.params.previewSize) { this.previewSize = this.params.previewSize; } - }, + } }); }); diff --git a/frontend/client/src/views/portal/fields/quick-create-list.js b/frontend/client/src/views/portal/fields/quick-create-list.js new file mode 100644 index 0000000000..56d2e73ab4 --- /dev/null +++ b/frontend/client/src/views/portal/fields/quick-create-list.js @@ -0,0 +1,45 @@ +/************************************************************************ + * 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/fields/quick-create-list', 'views/settings/fields/quick-create-list', function (Dep) { + + return Dep.extend({ + + setup: function () { + Dep.prototype.setup.call(this); + + this.params.options = this.params.options.filter(function (tab) { + if (!!this.getMetadata().get('scopes.' + tab + '.aclPortal')) { + return true; + } + }, this); + }, + + }); + +}); diff --git a/frontend/client/src/views/portal/fields/tab-list.js b/frontend/client/src/views/portal/fields/tab-list.js new file mode 100644 index 0000000000..ef75258510 --- /dev/null +++ b/frontend/client/src/views/portal/fields/tab-list.js @@ -0,0 +1,45 @@ +/************************************************************************ + * 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/fields/tab-list', 'views/settings/fields/tab-list', function (Dep) { + + return Dep.extend({ + + setup: function () { + Dep.prototype.setup.call(this); + + this.params.options = this.params.options.filter(function (tab) { + if (!!this.getMetadata().get('scopes.' + tab + '.aclPortal')) { + return true; + } + }, this); + }, + + }); + +}); diff --git a/frontend/client/src/views/record/panels/relationship.js b/frontend/client/src/views/record/panels/relationship.js index c5375d6759..cf3c370197 100644 --- a/frontend/client/src/views/record/panels/relationship.js +++ b/frontend/client/src/views/record/panels/relationship.js @@ -89,12 +89,17 @@ Espo.define('views/record/panels/relationship', ['views/record/panels/bottom', ' } if (this.defs.select) { + var data = {link: this.link}; + if (this.defs.selectPrimaryFilterName) { + data.primaryFilterName = this.defs.selectPrimaryFilterName; + } + if (this.defs.selectBoolFilterList) { + data.boolFilterList = this.defs.selectBoolFilterList; + } this.actionList.unshift({ label: 'Select', action: this.defs.selectAction || 'selectRelated', - data: { - link: this.link, - } + data: data }); } diff --git a/frontend/client/src/views/user/detail.js b/frontend/client/src/views/user/detail.js index c647183f27..6153d8f274 100644 --- a/frontend/client/src/views/user/detail.js +++ b/frontend/client/src/views/user/detail.js @@ -50,7 +50,7 @@ Espo.define('views/user/detail', 'views/detail', function (Dep) { }); } - if (this.model.id == this.getUser().id) { + if (this.model.id == this.getUser().id && this.getAcl().checkScope('ExternalAccount')) { this.menu.buttons.push({ name: 'externalAccounts', label: 'External Accounts', @@ -60,26 +60,27 @@ Espo.define('views/user/detail', 'views/detail', function (Dep) { } } - var showActivities = this.getAcl().checkUserPermission(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.showHeaderActionItem('calendar'); - } - }, this); + if (this.getAcl().checkScope('Calendar')) { + var showActivities = this.getAcl().checkUserPermission(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.showHeaderActionItem('calendar'); + } + }, this); + } } } + this.menu.buttons.push({ + name: 'calendar', + html: this.translate('Calendar', 'scopeNames'), + style: 'default', + link: '#Calendar/show/userId=' + this.model.id + '&userName=' + this.model.get('name'), + hidden: !showActivities + }); } - - this.menu.buttons.push({ - name: 'calendar', - html: this.translate('Calendar', 'scopeNames'), - style: 'default', - link: '#Calendar/show/userId=' + this.model.id + '&userName=' + this.model.get('name'), - hidden: !showActivities - }); }, actionPreferences: function () { diff --git a/frontend/html/portal.html b/frontend/html/portal.html index a7268f64f1..0d1e085937 100644 --- a/frontend/html/portal.html +++ b/frontend/html/portal.html @@ -36,6 +36,7 @@ $(function () { Espo.require('app-portal', function (App) { var app = new App({ + id: '{{portalId}}', useCache: false, url: '../api/v1/portal/{{portalId}}', }, function (app) { diff --git a/html/portal.html b/html/portal.html index e0db903d7f..0331b973f1 100644 --- a/html/portal.html +++ b/html/portal.html @@ -15,6 +15,7 @@ $(function () { Espo.require('app-portal', function (App) { var app = new App({ + id: '{{portalId}}', useCache: {{useCache}}, cacheTimestamp: {{cacheTimestamp}}, url: '../api/v1/portal/{{portalId}}',