dev
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -215,7 +215,8 @@ class Container
|
||||
{
|
||||
return new \Espo\Core\Utils\Layout(
|
||||
$this->get('fileManager'),
|
||||
$this->get('metadata')
|
||||
$this->get('metadata'),
|
||||
$this->get('user')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* 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.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils;
|
||||
|
||||
class LayoutPortal extends Layout
|
||||
{
|
||||
|
||||
public function get($scope, $name)
|
||||
{
|
||||
$scope = $this->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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* 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.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
|
||||
class Portal extends \Espo\Core\ORM\Repositories\RDB
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
$this->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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"onlyMyTeam": "Only My Team"
|
||||
},
|
||||
"presetFilters": {
|
||||
"active": "Active"
|
||||
"active": "Active",
|
||||
"activePortal": "Portal"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"label": "General",
|
||||
"rows": [
|
||||
[{"name": "name"}, {"name": "isActive"}],
|
||||
[{"name": "url"}, {"name": "isDefault"}]
|
||||
[{"name": "url"}, {"name": "isDefault"}],
|
||||
[{"name": "portalRoles"}, false]
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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"}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -20,6 +20,7 @@
|
||||
"delete": "own"
|
||||
},
|
||||
"EmailAccount": false,
|
||||
"ExternalAccount": false,
|
||||
"Role": false,
|
||||
"PortalRole": false,
|
||||
"EmailFilter": false,
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
"editQuick":"User.Record.Edit",
|
||||
"list":"User.Record.List"
|
||||
},
|
||||
"filterList": ["active"],
|
||||
"filterList": ["active","activePortal"],
|
||||
"boolFilterList": ["onlyMyTeam"]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"entity":true,
|
||||
"layouts":false,
|
||||
"tab":false,
|
||||
"acl":false,
|
||||
"acl": "boolean",
|
||||
"aclPortal": false,
|
||||
"customizable":false
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class User extends \Espo\Core\SelectManagers\Base
|
||||
);
|
||||
}
|
||||
|
||||
protected function filterActivePortalUsers(&$result)
|
||||
protected function filterActivePortal(&$result)
|
||||
{
|
||||
$result['whereClause'][] = array(
|
||||
'isActive' => true,
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* 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.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Services;
|
||||
|
||||
use \Espo\ORM\Entity;
|
||||
|
||||
class Portal extends Record
|
||||
{
|
||||
public function loadAdditionalFields(Entity $entity)
|
||||
{
|
||||
parent::loadAdditionalFields($entity);
|
||||
$siteUrl = $this->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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="pull-right btn-group">
|
||||
{{#if buttonList}}
|
||||
{{#each buttonList}}
|
||||
<button type="button" class="btn btn-default btn-sm action{{#if hidden}} hidden{{/if}}" data-action="{{action}}" data-panel="{{../../name}}" {{#each data}} data-{{@key}}="{{./this}}"{{/each}} title="{{translate title scope=../../scope}}">{{#if html}}{{{html}}}{{else}}{{translate label scope=../scope}}{{/if}}</button>
|
||||
<button type="button" class="btn btn-default btn-sm action{{#if hidden}} hidden{{/if}}" data-action="{{action}}" data-panel="{{../../name}}" {{#each data}} data-{{hyphen @key}}="{{./this}}"{{/each}} title="{{translate title scope=../../scope}}">{{#if html}}{{{html}}}{{else}}{{translate label scope=../scope}}{{/if}}</button>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{#if actionList}}
|
||||
@@ -13,7 +13,7 @@
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{#each actionList}}
|
||||
<li><a {{#if link}}href="{{link}}"{{else}}href="javascript:"{{/if}} class="action{{#if hidden}} hidden{{/if}}" data-panel="{{../../name}}" {{#if action}} data-action={{action}}{{/if}}{{#each data}} data-{{@key}}="{{./this}}"{{/each}}>{{#if html}}{{{html}}}{{else}}{{translate label scope=../scope}}{{/if}}</a></li>
|
||||
<li><a {{#if link}}href="{{link}}"{{else}}href="javascript:"{{/if}} class="action{{#if hidden}} hidden{{/if}}" data-panel="{{../../name}}" {{#if action}} data-action={{action}}{{/if}}{{#each data}} data-{{hyphen @key}}="{{./this}}"{{/each}}>{{#if html}}{{{html}}}{{else}}{{translate label scope=../scope}}{{/if}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="pull-right btn-group">
|
||||
{{#if buttonList}}
|
||||
{{#each buttonList}}
|
||||
<button type="button" class="btn btn-default btn-sm action{{#if hidden}} hidden{{/if}}" data-action="{{action}}" data-panel="{{../../name}}" {{#each data}} data-{{@key}}="{{./this}}"{{/each}} title="{{translate title scope=../scope}}">{{#if html}}{{{html}}}{{else}}{{translate label scope=../scope}}{{/if}}</button>
|
||||
<button type="button" class="btn btn-default btn-sm action{{#if hidden}} hidden{{/if}}" data-action="{{action}}" data-panel="{{../../name}}" {{#each data}} data-{{hyphen @key}}="{{./this}}"{{/each}} title="{{translate title scope=../scope}}">{{#if html}}{{{html}}}{{else}}{{translate label scope=../scope}}{{/if}}</button>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{#if actionList}}
|
||||
@@ -14,7 +14,7 @@
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{#each actionList}}
|
||||
<li><a {{#if link}}href="{{link}}"{{else}}href="javascript:"{{/if}} class="action{{#if hidden}} hidden{{/if}}" {{#if action}} data-panel="{{../../name}}" data-action="{{action}}"{{/if}}{{#each data}} data-{{@key}}="{{./this}}"{{/each}}>{{#if html}}{{{html}}}{{else}}{{translate label scope=../scope}}{{/if}}</a></li>
|
||||
<li><a {{#if link}}href="{{link}}"{{else}}href="javascript:"{{/if}} class="action{{#if hidden}} hidden{{/if}}" {{#if action}} data-panel="{{../../name}}" data-action="{{action}}"{{/if}}{{#each data}} data-{{hyphen @key}}="{{./this}}"{{/each}}>{{#if html}}{{{html}}}{{else}}{{translate label scope=../scope}}{{/if}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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}}',
|
||||
|
||||
Reference in New Issue
Block a user