services refactoring

This commit is contained in:
Yuri Kuznetsov
2020-06-26 14:12:18 +03:00
parent 578cf90f85
commit 451b8ea1e5
29 changed files with 697 additions and 242 deletions

View File

@@ -42,27 +42,16 @@ class Record extends Base
public static $defaultAction = 'list';
protected $defaultRecordServiceName = 'Record';
protected function getEntityManager()
{
return $this->getContainer()->get('entityManager');
}
protected function getRecordService($name = null)
protected function getRecordService(?string $name = null) : object
{
if (empty($name)) {
$name = $this->name;
}
$name = $name ?? $this->name;
if ($this->getServiceFactory()->checkExists($name)) {
$service = $this->getServiceFactory()->create($name);
} else {
$service = $this->getServiceFactory()->create($this->defaultRecordServiceName);
$service->setEntityType($name);
}
return $service;
return $this->getContainer()->get('recordServiceContainer')->get($name);
}
public function actionRead($params, $data, $request)
@@ -224,10 +213,9 @@ class Record extends Base
$id = $params['id'];
if ($this->getRecordService()->delete($id)) {
return true;
}
throw new Error();
$this->getRecordService()->delete($id);
return true;
}
public function actionExport($params, $data, $request)

View File

@@ -39,7 +39,7 @@ class RecordTree extends Record
{
public static $defaultAction = 'list';
protected $defaultRecordServiceName = 'RecordTree';
// protected $defaultRecordServiceName = 'RecordTree';
public function actionListTree($params, $data, $request)
{

View File

@@ -0,0 +1,37 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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\Di;
use Espo\Core\Utils\FieldManagerUtil;
interface FieldManagerUtilAware
{
public function setFieldManagerUtil(FieldManagerUtil $fieldManagerUtil);
}

View File

@@ -0,0 +1,42 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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\Di;
use Espo\Core\Utils\FieldManagerUtil;
trait FieldManagerUtilSetter
{
protected $fieldManagerUtil;
public function setFieldManagerUtil(FieldManagerUtil $fieldManagerUtil)
{
$this->fieldManagerUtil = $fieldManagerUtil;
}
}

View File

@@ -0,0 +1,37 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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\Di;
use Espo\Core\Utils\FieldValidatorManager;
interface FieldValidatorManagerAware
{
public function setFieldValidatorManager(FieldValidatorManager $fieldValidatorManager);
}

View File

@@ -0,0 +1,42 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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\Di;
use Espo\Core\Utils\FieldValidatorManager;
trait FieldValidatorManagerSetter
{
protected $fieldValidatorManager;
public function setFieldValidatorManager(FieldValidatorManager $fieldValidatorManager)
{
$this->fieldValidatorManager = $fieldValidatorManager;
}
}

View File

@@ -0,0 +1,37 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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\Di;
use Espo\Core\InjectableFactory;
interface InjectableFactoryAware
{
public function setInjectableFactory(InjectableFactory $injectableFactory);
}

View File

@@ -0,0 +1,42 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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\Di;
use Espo\Core\InjectableFactory;
trait InjectableFactorySetter
{
protected $injectableFactory;
public function setInjectableFactory(InjectableFactory $injectableFactory)
{
$this->injectableFactory = $injectableFactory;
}
}

View File

@@ -0,0 +1,37 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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\Di;
use Espo\Core\RecordServiceContainer;
interface RecordServiceContainerAware
{
public function setRecordServiceContainer(RecordServiceContainer $recordServiceContainer);
}

View File

@@ -0,0 +1,42 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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\Di;
use Espo\Core\RecordServiceContainer;
trait RecordServiceContainerSetter
{
protected $recordServiceContainer;
public function setRecordServiceContainer(RecordServiceContainer $recordServiceContainer)
{
$this->recordServiceContainer = $recordServiceContainer;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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;
use Espo\Core\Exceptions\Error;
use Espo\Core\ServiceFactory;
use Espo\Core\Utils\Metadata;
use Espo\Core\Services\Record;
/**
* Container for record services. Lazy loading is used.
* Usually there's no need to have multiple record service instances of the same entity type.
* Use this contanier instead of serviceFactory to get record services.
*/
class RecordServiceContainer
{
protected $data = [];
protected $defaultTypeMap = [
'CategoryTree' => 'RecordTree',
];
protected $serviceFactory;
protected $metadata;
public function __construct(ServiceFactory $serviceFactory, Metadata $metadata)
{
$this->serviceFactory = $serviceFactory;
$this->metadata = $metadata;
}
public function get(string $entityType) : Record
{
$name = $entityType;
if (!array_key_exists($name, $this->data)) {
if (!$this->metadata->get(['scopes', $name, 'entity'])) {
throw new Error("Can't create record service {$name}, there's no such entity type.");
}
if ($this->serviceFactory->checkExists($name)) {
$obj = $this->serviceFactory->create($name);
} else {
$default = 'Record';
$type = $this->metadata->get(['scopes', $name, 'type']);
if ($type) {
$default = $this->defaultTypeMap[$type] ?? $default;
}
$obj = $this->serviceFactory->create($default);
$service->setEntityType($name);
}
$this->data[$name] = $obj;
}
return $this->data[$name];
}
}

View File

@@ -31,6 +31,7 @@ namespace Espo\Core\Services;
use Espo\Core\Interfaces\Injectable;
/** Deprecated */
abstract class Base implements Injectable
{
protected $dependencyList = [

View File

@@ -0,0 +1,47 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://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\Services;
use Espo\ORM\Entity;
use StdClass;
interface Record
{
public function create(StdClass $data) : Entity;
public function read(string $id) : Entity;
public function update(string $id, StdClass $data) : Entity;
public function delete(string $id);
public function find(array $params) : StdClass;
}

View File

@@ -32,7 +32,7 @@ namespace Espo\Core\Traits;
/** Deprecated */
trait Injectable
{
private $injections = [];
protected $injections = [];
public function inject($name, $object)
{

View File

@@ -40,6 +40,8 @@ use Espo\Modules\Crm\Entities\Campaign;
use Espo\Core\Mail\Sender;
use Laminas\Mail\Message;
use StdClass;
class MassEmail extends \Espo\Services\Record
{
const MAX_ATTEMPT_COUNT = 3;
@@ -528,7 +530,7 @@ class MassEmail extends \Espo\Services\Record
return $this->campaignService;
}
protected function findLinkedQueueItems($id, $params)
protected function findLinkedQueueItems(string $id, array $params) : StdClass
{
$link = 'queueItems';
@@ -540,13 +542,13 @@ class MassEmail extends \Espo\Services\Record
$selectParams = array_merge($selectParams, $this->linkSelectParams[$link]);
}
$selectParams['whereClause'][] = array(
$selectParams['whereClause'][] = [
'isTest' => false
);
];
$collection = $this->getRepository()->findRelated($entity, $link, $selectParams);
$recordService = $this->getRecordService('EmailQueueItem');
$recordService = $this->recordServiceContainer->get('EmailQueueItem');
foreach ($collection as $e) {
$recordService->loadAdditionalFieldsForList($e);
@@ -555,10 +557,10 @@ class MassEmail extends \Espo\Services\Record
$total = $this->getRepository()->countRelated($entity, $link, $selectParams);
return array(
return (object) [
'total' => $total,
'collection' => $collection
);
];
}
public function getSmtpAccountDataList()

View File

@@ -29,11 +29,13 @@
namespace Espo\Modules\Crm\Services;
use \Espo\ORM\Entity;
use Espo\ORM\Entity;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\BadRequest;
use \Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Forbidden;
use StdClass;
class TargetList extends \Espo\Services\Record
{
@@ -43,12 +45,12 @@ class TargetList extends \Espo\Services\Record
protected $targetsLinkList = ['accounts', 'contacts', 'leads', 'users'];
protected $entityTypeLinkMap = array(
protected $entityTypeLinkMap = [
'Lead' => 'leads',
'Account' => 'accounts',
'Contact' => 'contacts',
'User' => 'users',
);
];
protected $linkSelectParams = [
'accounts' => [
@@ -232,7 +234,7 @@ class TargetList extends \Espo\Services\Record
}
}
protected function findLinkedOptedOut($id, $params)
protected function findLinkedOptedOut(string $id, array $params) : StdClass
{
$pdo = $this->getEntityManager()->getPDO();
$query = $this->getEntityManager()->getQuery();
@@ -297,13 +299,13 @@ class TargetList extends \Espo\Services\Record
$row = $sth->fetch(\PDO::FETCH_ASSOC);
$count = $row['count'];
return array(
return (object) [
'total' => $count,
'list' => $arr
);
];
}
public function optOut($id, $targetType, $targetId)
public function optOut(string $id, string $targetType, string $targetId)
{
$targetList = $this->getEntityManager()->getEntity('TargetList', $id);
if (!$targetList) {
@@ -342,7 +344,7 @@ class TargetList extends \Espo\Services\Record
return false;
}
public function cancelOptOut($id, $targetType, $targetId)
public function cancelOptOut(string $id, string $targetType, string $targetId)
{
$targetList = $this->getEntityManager()->getEntity('TargetList', $id);
if (!$targetList) {

View File

@@ -29,16 +29,18 @@
namespace Espo\ORM;
use Espo\Core\Utils\Util;
class Metadata
{
protected $data = array();
protected $data = [];
public function setData($data)
{
$this->data = $data;
}
public function get($entityType, $key = null, $default = null)
public function get(string $entityType, $key = null, $default = null)
{
if (!array_key_exists($entityType, $this->data)) {
return null;
@@ -46,13 +48,13 @@ class Metadata
$data = $this->data[$entityType];
if (!$key) return $data;
return \Espo\Core\Utils\Util::getValueByKey($data, $key, $default);
return Util::getValueByKey($data, $key, $default);
}
public function has($entityType)
public function has(string $entityType) : bool
{
if (!array_key_exists($entityType, $this->data)) {
return null;
return false;
}
return true;
}

View File

@@ -41,6 +41,9 @@
"serviceFactory": {
"className": "Espo\\Core\\ServiceFactory"
},
"recordServiceContainer": {
"className": "Espo\\Core\\RecordServiceContainer"
},
"templateFileManager": {
"className": "Espo\\Core\\Utils\\TemplateFileManager"
},

View File

@@ -0,0 +1,3 @@
{
"entity": true
}

View File

@@ -51,7 +51,7 @@ class Attachment extends Record
'image/webp',
];
public function upload($fileData)
public function upload($fileData) : Entity
{
if (!$this->getAcl()->checkScope('Attachment', 'create')) {
throw new Forbidden();
@@ -72,7 +72,14 @@ class Attachment extends Record
return $attachment;
}
public function create($data)
protected function afterCreateEntity(Entity $entity, $data)
{
if (!empty($data->file)) {
$entity->clear('contents');
}
}
protected function beforeCreateEntity(Entity $entity, $data)
{
if (!empty($data->file)) {
$arr = explode(',', $data->file);
@@ -134,17 +141,6 @@ class Attachment extends Record
}
}
$entity = parent::create($data);
if (!empty($data->file)) {
$entity->clear('contents');
}
return $entity;
}
protected function beforeCreateEntity(Entity $entity, $data)
{
$storage = $entity->get('storage');
if ($storage && !$this->getMetadata()->get(['app', 'fileStorage', 'implementationClassNameMap', $storage])) {
$entity->clear('storage');

View File

@@ -389,7 +389,7 @@ class Email extends Record
return $this->streamService;
}
public function create($data)
public function create(\StdClass $data) : Entity
{
$entity = parent::create($data);
@@ -448,11 +448,11 @@ class Email extends Record
$this->getEntityManager()->getRepository('Email')->loadReplyToField($entity);
}
public function getEntity($id = null)
public function getEntity(?string $id = null) : ?Entity
{
$entity = parent::getEntity($id);
if (!empty($entity) && !empty($id) && !$entity->get('isRead')) {
if ($entity && $id && !$entity->get('isRead')) {
$this->markAsRead($entity->id);
}

View File

@@ -193,7 +193,7 @@ class EmailAccount extends Record
return $storage;
}
public function create($data)
public function create(\StdClass $data) : Entity
{
if (!$this->getUser()->isAdmin()) {
$count = $this->getEntityManager()->getRepository('EmailAccount')->where(array(
@@ -211,6 +211,7 @@ class EmailAccount extends Record
}
$this->getEntityManager()->saveEntity($entity);
}
return $entity;
}

View File

@@ -29,11 +29,11 @@
namespace Espo\Services;
use \Espo\ORM\Entity;
use Espo\ORM\Entity;
use \Espo\Core\Exceptions\Forbidden;
use \Espo\Core\Exceptions\NotFound;
use \Espo\Core\Exceptions\Error;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\Error;
class EmailFolder extends Record
{

View File

@@ -113,7 +113,7 @@ class ExternalAccount extends Record
}
}
public function read($id)
public function read(string $id) : Entity
{
list($integration, $userId) = explode('__', $id);
@@ -135,6 +135,5 @@ class ExternalAccount extends Record
}
return $entity;
}
}

View File

@@ -37,6 +37,8 @@ use Espo\Core\Exceptions\BadRequest;
use Espo\ORM\Entity;
use Espo\Entities\User;
use StdClass;
class Import extends \Espo\Services\Record
{
const REVERT_PERMANENTLY_REMOVE_PERIOD_DAYS = 2;
@@ -45,9 +47,7 @@ class Import extends \Espo\Services\Record
{
parent::init();
$this->addDependency('serviceFactory');
$this->addDependency('fileManager');
$this->addDependency('selectManagerFactory');
$this->addDependency('fileStorageManager');
}
@@ -71,36 +71,29 @@ class Import extends \Espo\Services\Record
'hh:mmA' => 'h:iA',
];
protected $services = [];
protected function getSelectManagerFactory()
{
return $this->injections['selectManagerFactory'];
}
protected function getFileStorageManager()
{
return $this->injections['fileStorageManager'];
return $this->getInjection('fileStorageManager');
}
protected function getFileManager()
{
return $this->injections['fileManager'];
return $this->getInjection('fileManager');
}
protected function getAcl()
{
return $this->injections['acl'];
return $this->acl;
}
protected function getMetadata()
{
return $this->injections['metadata'];
return $this->metadata;
}
protected function getServiceFactory()
{
return $this->injections['serviceFactory'];
return $this->serviceFactory;
}
public function loadAdditionalFields(Entity $entity)
@@ -113,11 +106,11 @@ class Import extends \Espo\Services\Record
$entity->set([
'importedCount' => $importedCount,
'duplicateCount' => $duplicateCount,
'updatedCount' => $updatedCount
'updatedCount' => $updatedCount,
]);
}
public function findLinked($id, $link, $params)
public function findLinked(string $id, string $link, array $params) : StdClass
{
$entity = $this->getRepository()->get($id);
$foreignEntityType = $entity->get('entityType');
@@ -137,7 +130,7 @@ class Import extends \Espo\Services\Record
$collection = $this->getRepository()->findRelated($entity, $link, $selectParams);
$recordService = $this->getRecordService($foreignEntityType);
$recordService = $this->recordServiceContainer->get($foreignEntityType);
foreach ($collection as $e) {
$recordService->loadAdditionalFieldsForList($e);
@@ -146,9 +139,9 @@ class Import extends \Espo\Services\Record
$total = $this->getRepository()->countRelated($entity, $link, $selectParams);
return [
return (object) [
'total' => $total,
'collection' => $collection
'collection' => $collection,
];
}
@@ -318,7 +311,7 @@ class Import extends \Espo\Services\Record
return true;
}
public function jobRunIdleImport($data)
public function jobRunIdleImport(StdClass $data) : array
{
if (
empty($data->userId) ||
@@ -349,7 +342,7 @@ class Import extends \Espo\Services\Record
$this->import($entityType, $importAttributeList, $attachmentId, $params, $importId, $user);
}
public function importById(string $id, bool $startFromLastIndex = false, $forceResume = false) : array
public function importById(string $id, bool $startFromLastIndex = false, bool $forceResume = false) : array
{
$import = $this->getEntityManager()->getEntity('Import', $id);
@@ -380,7 +373,7 @@ class Import extends \Espo\Services\Record
return $this->import($entityType, $attributeList, $import->get('fileId'), $params, $id);
}
public function importFileWithParamsId(string $contents, string $importParamsId)
public function importFileWithParamsId(string $contents, string $importParamsId) : array
{
if (!$contents) {
throw new Error("File contents is empty.");
@@ -622,8 +615,9 @@ class Import extends \Espo\Services\Record
];
}
public function importRow($scope, array $importAttributeList, array $row, array $params = [], $user)
{
public function importRow(
string $scope, array $importAttributeList, array $row, array $params = [], ?User $user = null
) : ?array {
$id = null;
$action = 'create';
if (!empty($params['action'])) {
@@ -631,7 +625,7 @@ class Import extends \Espo\Services\Record
}
if (empty($importAttributeList)) {
return;
return null;
}
if (in_array($action, ['createAndUpdate', 'update'])) {
@@ -647,18 +641,18 @@ class Import extends \Espo\Services\Record
}
}
$recordService = $this->getRecordService($scope);
$recordService = $this->recordServiceContainer->get($scope);
if (in_array($action, ['createAndUpdate', 'update'])) {
if (!count($updateByAttributeList)) {
return;
return null;
}
$entity = $this->getEntityManager()->getRepository($scope)->where($whereClause)->findOne();
if ($entity) {
if (!$user->isAdmin()) {
if (!$this->getAclManager()->checkEntity($user, $entity, 'edit')) {
return;
return null;
}
}
}
@@ -669,7 +663,7 @@ class Import extends \Espo\Services\Record
$entity->set('id', $whereClause['id']);
}
} else {
return;
return null;
}
}
} else {
@@ -1091,8 +1085,8 @@ class Import extends \Espo\Services\Record
return $value;
case Entity::JSON_ARRAY:
if (!is_string($value)) return;
if (!strlen($value)) return;
if (!is_string($value)) return null;
if (!strlen($value)) return null;
if ($value[0] === '[') {
$value = \Espo\Core\Utils\Json::decode($value);
return $value;
@@ -1107,21 +1101,7 @@ class Import extends \Espo\Services\Record
return $value;
}
protected function getRecordService($scope)
{
if (empty($this->services[$scope])) {
if ($this->getServiceFactory()->checkExists($scope)) {
$service = $this->getServiceFactory()->create($scope);
} else {
$service = $this->getServiceFactory()->create('Record');
$service->setEntityType($scope);
}
$this->services[$scope] = $service;
}
return $this->services[$scope];
}
public function unmarkAsDuplicate($id, $entityType, $entityId)
public function unmarkAsDuplicate(string $id, string $entityType, string $entityId)
{
$pdo = $this->getEntityManager()->getPDO();

View File

@@ -39,27 +39,10 @@ class Note extends Record
{
protected $noteNotificationPeriod = '1 hour';
public function getEntity($id = null)
public function loadAdditionalFields(Entity $entity)
{
$entity = parent::getEntity($id);
if (!empty($id)) {
$entity->loadAttachments();
}
return $entity;
}
public function create($data)
{
if (!empty($data->parentType) && !empty($data->parentId)) {
$entity = $this->getEntityManager()->getEntity($data->parentType, $data->parentId);
if ($entity) {
if (!$this->getAcl()->check($entity, 'read')) {
throw new Forbidden();
}
}
}
return parent::create($data);
parent::loadAdditionalFields($entity);
$entity->loadAttachments();
}
protected function afterCreateEntity(Entity $entity, $data)
@@ -81,6 +64,15 @@ class Note extends Record
protected function beforeCreateEntity(Entity $entity, $data)
{
if (!empty($data->parentType) && !empty($data->parentId)) {
$entity = $this->getEntityManager()->getEntity($data->parentType, $data->parentId);
if ($entity) {
if (!$this->getAcl()->check($entity, 'read')) {
throw new Forbidden();
}
}
}
parent::beforeCreateEntity($entity, $data);
if ($entity->get('type') === 'Post') {

View File

@@ -29,44 +29,76 @@
namespace Espo\Services;
use Espo\Core\Exceptions\{
Error,
BadRequest,
Conflict,
NotFound,
NotFoundSilent,
ForbiddenSilent,
ConflictSilent,
};
use Espo\ORM\Entity;
use Espo\Core\Exceptions\Error;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Conflict;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\NotFoundSilent;
use Espo\Core\Exceptions\ForbiddenSilent;
use Espo\Core\Exceptions\ConflictSilent;
use Espo\Core\Utils\Util;
use Espo\Core\AclManager;
use Espo\Core\Acl;
use Espo\Entities\User;
class Record extends \Espo\Core\Services\Base
use Espo\Core\{
Acl,
ORM\EntityManager,
AclManager,
Utils\Util,
Services\Record as RecordServiceInterface,
};
use Espo\Core\Di;
use StdClass;
/**
* A layer between Controller and Repository. For CRUD and other operations with records.
* If a service with the name of an entity type exists then it will be used instead this one.
*/
class Record implements RecordServiceInterface,
Di\ConfigAware,
Di\ServiceFactoryAware,
Di\EntityManagerAware,
Di\UserAware,
Di\MetadataAware,
Di\AclAware,
Di\AclManagerAware,
Di\FileManagerAware,
Di\SelectManagerFactoryAware,
Di\InjectableFactoryAware,
Di\FieldManagerUtilAware,
Di\FieldValidatorManagerAware,
Di\RecordServiceContainerAware,
/** for backward compatibility, to be removed */
\Espo\Core\Interfaces\Injectable
{
protected $dependencyList = [
'entityManager',
'user',
'metadata',
'acl',
'aclManager',
'config',
'serviceFactory',
'fileManager',
'selectManagerFactory',
'fileStorageManager',
'injectableFactory',
'fieldManagerUtil',
'container',
];
use Di\ConfigSetter;
use Di\ServiceFactorySetter;
use Di\EntityManagerSetter;
use Di\UserSetter;
use Di\MetadataSetter;
use Di\AclSetter;
use Di\AclManagerSetter;
use Di\FileManagerSetter;
use Di\SelectManagerFactorySetter;
use Di\InjectableFactorySetter;
use Di\FieldManagerUtilSetter;
use Di\FieldValidatorManagerSetter;
use Di\RecordServiceContainerSetter;
/** for backward compatibility, to be removed */
use \Espo\Core\Traits\Injectable;
/** for backward compatibility, to be removed */
protected $dependencyList = [];
protected $getEntityBeforeUpdate = false;
protected $entityName;
protected $entityType;
private $streamService;
@@ -135,11 +167,11 @@ class Record extends \Espo\Core\Services\Base
protected $duplicateIgnoreAttributeList = [];
private $acl = null;
protected $acl = null;
private $user = null;
protected $user = null;
private $aclManager = null;
protected $aclManager = null;
const MAX_SELECT_TEXT_ATTRIBUTE_LENGTH = 5000;
@@ -149,7 +181,6 @@ class Record extends \Espo\Core\Services\Base
public function __construct()
{
parent::__construct();
if (empty($this->entityType)) {
$name = get_class($this);
if (preg_match('@\\\\([\w]+)$@', $name, $matches)) {
@@ -159,7 +190,14 @@ class Record extends \Espo\Core\Services\Base
$this->entityType = Util::normilizeScopeName($name);
}
}
$this->entityName = $this->entityType;
// to be removed
$this->init();
}
// for backward compatibility, to be removed
protected function init()
{
}
public function setAclManager(AclManager $aclManager)
@@ -199,77 +237,74 @@ class Record extends \Espo\Core\Services\Base
}
}
public function setEntityType($entityType)
public function setEntityType(string $entityType)
{
$this->entityType = $entityType;
$this->entityName = $entityType;
}
public function getEntityType()
public function getEntityType() : string
{
return $this->entityType;
}
protected function getConfig()
{
return $this->config;
}
protected function getServiceFactory()
{
return $this->injections['serviceFactory'];
return $this->serviceFactory;
}
protected function getSelectManagerFactory()
{
return $this->injections['selectManagerFactory'];
return $this->selectManagerFactory;
}
protected function getAcl()
{
if ($this->acl) return $this->acl;
return $this->getInjection('acl');
return $this->acl;
}
protected function getUser()
{
if ($this->user) return $this->user;
return $this->getInjection('user');
return $this->user;
}
public function setAcl(Acl $acl)
{
$this->acl = $acl;
// for backward compatibility
$this->inject('acl', $acl);
}
public function setUser(User $user)
{
$this->user = $user;
// for backward compatibility
$this->inject('user', $user);
}
protected function getAclManager()
{
if ($this->aclManager) return $this->aclManager;
return $this->getInjection('aclManager');
return $this->aclManager;
}
protected function getFileManager()
{
return $this->getInjection('fileManager');
return $this->fileManager;
}
protected function getMetadata()
{
return $this->getInjection('metadata');
return $this->metadata;
}
protected function getFieldManagerUtil()
{
return $this->getInjection('fieldManagerUtil');
return $this->fieldManagerUtil;
}
protected function getEntityManager()
{
return $this->entityManager;
}
protected function getRepository()
@@ -277,16 +312,10 @@ class Record extends \Espo\Core\Services\Base
return $this->getEntityManager()->getRepository($this->entityType);
}
/** Deprecated */
protected function getRecordService($name)
{
if ($this->getServiceFactory()->checkExists($name)) {
$service = $this->getServiceFactory()->create($name);
} else {
$service = $this->getServiceFactory()->create('Record');
$service->setEntityType($name);
}
return $service;
return $this->recordServiceContainer->get($name);
}
protected function processActionHistoryRecord($action, Entity $entity)
@@ -317,21 +346,22 @@ class Record extends \Espo\Core\Services\Base
return $this->read($id);
}
public function read($id)
public function read(string $id) : Entity
{
if (empty($id)) {
throw new Error("No ID passed.");
}
$entity = $this->getEntity($id);
if (!$entity) throw new NotFoundSilent("Record does not exist.");
if (!$entity) throw new NotFoundSilent("Record {$id} does not exist.");
$this->processActionHistoryRecord('read', $entity);
return $entity;
}
public function getEntity($id = null)
public function getEntity(?string $id = null) : ?Entity
{
if (!is_null($id)) {
$selectParams = [];
@@ -571,7 +601,7 @@ class Record extends \Espo\Core\Services\Base
$fieldType = $this->getFieldManagerUtil()->getEntityTypeFieldParam($this->entityType, $field, 'type');
$validationList = $this->getMetadata()->get(['fields', $fieldType, 'validationList'], []);
$mandatoryValidationList = $this->getMetadata()->get(['fields', $fieldType, 'mandatoryValidationList'], []);
$fieldValidatorManager = $this->getInjection('container')->get('fieldValidatorManager');
$fieldValidatorManager = $this->fieldValidatorManager;
foreach ($validationList as $type) {
$value = $this->getFieldManagerUtil()->getEntityTypeFieldParam($this->entityType, $field, $type);
@@ -843,11 +873,11 @@ class Record extends \Espo\Core\Services\Base
}
}
protected function filterCreateInput($data)
protected function filterCreateInput(StdClass $data)
{
}
protected function filterUpdateInput($data)
protected function filterUpdateInput(StdClass $data)
{
}
@@ -919,7 +949,7 @@ class Record extends \Espo\Core\Services\Base
return $this->create($data);
}
public function create($data)
public function create(StdClass $data) : Entity
{
if (!$this->getAcl()->check($this->getEntityType(), 'create'))
throw new ForbiddenSilent("No create access.");
@@ -969,11 +999,11 @@ class Record extends \Espo\Core\Services\Base
return $this->update($id, $data);
}
public function update($id, $data)
public function update(string $id, StdClass $data) : Entity
{
unset($data->deleted);
if (empty($id)) throw new BadRequest("ID is empty.");
if (empty($id)) throw new BadRequest("No ID passed.");
unset($data->deleted);
$this->filterInput($data);
$this->filterUpdateInput($data);
@@ -993,7 +1023,7 @@ class Record extends \Espo\Core\Services\Base
$entity = $this->getRepository()->get($id);
}
if (!$entity) throw new NotFound("Record not found.");
if (!$entity) throw new NotFound("Record {$id} not found.");
if (!$this->getAcl()->check($entity, 'edit')) throw new ForbiddenSilent("No edit access.");
@@ -1056,26 +1086,29 @@ class Record extends \Espo\Core\Services\Base
return $this->delete($id);
}
public function delete($id)
public function delete(string $id)
{
if (empty($id)) throw new BadRequest("No ID passed.");
if (empty($id)) throw new BadRequest("ID is empty.");
$entity = $this->getRepository()->get($id);
if (!$entity) throw new NotFound("Record not found.");
if (!$entity) throw new NotFound("Record {$id} not found.");
if (!$this->getAcl()->check($entity, 'delete')) throw new ForbiddenSilent("No delete access.");
$this->beforeDeleteEntity($entity);
$result = $this->getRepository()->remove($entity);
if ($result) {
$this->afterDeleteEntity($entity);
$this->processActionHistoryRecord('delete', $entity);
return $result;
if (!$result) {
throw new Error("Could not delete record {$id}.");
}
$this->afterDeleteEntity($entity);
$this->processActionHistoryRecord('delete', $entity);
return $result;
}
protected function getSelectParams($params)
@@ -1096,7 +1129,7 @@ class Record extends \Espo\Core\Services\Base
return $this->find($params);
}
public function find($params)
public function find(array $params) : StdClass
{
$disableCount = false;
if (
@@ -1150,13 +1183,13 @@ class Record extends \Espo\Core\Services\Base
}
}
return [
return (object) [
'total' => $total,
'collection' => $collection,
];
}
public function getListKanban($params)
public function getListKanban(array $params) : StdClass
{
$disableCount = false;
if (
@@ -1303,7 +1336,7 @@ class Record extends \Espo\Core\Services\Base
return $this->findLinked($id, $link, $params);
}
public function findLinked($id, $link, $params)
public function findLinked(string $id, string $link, array $params) : StdClass
{
$entity = $this->getRepository()->get($id);
if (!$entity) {
@@ -1349,7 +1382,7 @@ class Record extends \Espo\Core\Services\Base
}
}
$recordService = $this->getRecordService($foreignEntityType);
$recordService = $this->recordServiceContainer->get($foreignEntityType);
$disableCount = false;
$disableCountPropertyName = 'findLinked' . ucfirst($link) . 'CountQueryDisabled';
@@ -1417,9 +1450,9 @@ class Record extends \Espo\Core\Services\Base
}
}
return [
return (object) [
'total' => $total,
'collection' => $collection
'collection' => $collection,
];
}
@@ -1428,7 +1461,7 @@ class Record extends \Espo\Core\Services\Base
return $this->link($id, $link, $foreignId);
}
public function link($id, $link, $foreignId)
public function link(string $id, string $link, string $foreignId)
{
if (empty($id) || empty($link) || empty($foreignId)) {
throw new BadRequest;
@@ -1493,7 +1526,7 @@ class Record extends \Espo\Core\Services\Base
return $this->unlink($id, $link, $foreignId);
}
public function unlink($id, $link, $foreignId)
public function unlink(string $id, string $link, string $foreignId)
{
if (empty($id) || empty($link) || empty($foreignId)) {
throw new BadRequest;
@@ -1562,7 +1595,7 @@ class Record extends \Espo\Core\Services\Base
return $this->massLink($id, $link, $where, $selectData);
}
public function linkFollowers($id, $foreignId)
public function linkFollowers(string $id, string $foreignId)
{
if (!$this->getMetadata()->get(['scopes', $this->entityType, 'stream'])) throw new NotFound();
@@ -1578,7 +1611,7 @@ class Record extends \Espo\Core\Services\Base
return true;
}
public function unlinkFollowers($id, $foreignId)
public function unlinkFollowers(string $id, string $foreignId)
{
if (!$this->getMetadata()->get(['scopes', $this->entityType, 'stream'])) throw new NotFound();
@@ -1594,7 +1627,7 @@ class Record extends \Espo\Core\Services\Base
return true;
}
public function massLink($id, $link, $where, $selectData = null)
public function massLink(string $id, string $link, array $where, ?array $selectData = null)
{
if (empty($id) || empty($link)) {
throw new BadRequest;
@@ -1646,7 +1679,7 @@ class Record extends \Espo\Core\Services\Base
}
if (!is_array($where)) {
$where = array();
$where = [];
}
$params['where'] = $where;
@@ -1656,7 +1689,7 @@ class Record extends \Espo\Core\Services\Base
}
}
$selectParams = $this->getRecordService($foreignEntityType)->getSelectParams($params);
$selectParams = $this->recordServiceContainer->get($foreignEntityType)->getSelectParams($params);
if ($this->getAcl()->getLevel($foreignEntityType, $accessActionRequired) === 'all') {
return $this->getRepository()->massRelate($entity, $link, $selectParams);
@@ -1676,7 +1709,7 @@ class Record extends \Espo\Core\Services\Base
}
}
public function massUpdate(array $params, $data)
public function massUpdate(array $params, StdClass $data)
{
if ($this->getAcl()->get('massUpdatePermission') !== 'yes') throw new Forbidden();
@@ -1716,7 +1749,7 @@ class Record extends \Espo\Core\Services\Base
$result = ['count' => $count];
if (isset($params['ids'])) $result['ids'] = $resultIdList;
return $result;
return (object) $result;
}
protected function checkEntityForMassRemove(Entity $entity)
@@ -1989,7 +2022,7 @@ class Record extends \Espo\Core\Services\Base
if (empty($className)) {
throw new Error();
}
$exportObj = $this->getInjection('injectableFactory')->create($className);
$exportObj = $this->injectableFactory->create($className);
$collection = null;
@@ -2422,7 +2455,7 @@ class Record extends \Espo\Core\Services\Base
$fields = $this->getMetadata()->get(['entityDefs', $this->getEntityType(), 'fields'], []);
$fieldManager = new \Espo\Core\Utils\FieldManagerUtil($this->getMetadata());
$fieldManager = $this->fieldManagerUtil;
foreach ($fields as $field => $item) {
if (!empty($item['duplicateIgnore']) || in_array($field, $this->duplicateIgnoreFieldList)) {

View File

@@ -49,6 +49,7 @@ class RecordTree extends Record
public function __construct()
{
parent::__construct();
if (!$this->subjectEntityType) {
$this->subjectEntityType = substr($this->entityType, 0, strlen($this->entityType) -8 );
}
@@ -174,7 +175,7 @@ class RecordTree extends Record
}
}
public function update($id, $data)
public function update(string $id, \StdClass $data) : Entity
{
if (!empty($data->parentId) && $data->parentId == $id) {
throw new Forbidden();
@@ -183,7 +184,7 @@ class RecordTree extends Record
return parent::update($id, $data);
}
public function link($id, $link, $foreignId)
public function link(string $id, string $link, string $foreignId)
{
if ($id == $foreignId ) {
throw new Forbidden();

View File

@@ -36,6 +36,8 @@ use Espo\Core\Utils\Util;
use Espo\ORM\Entity;
use StdClass;
class User extends Record
{
protected function init()
@@ -95,7 +97,7 @@ class User extends Record
return $this->injections['container'];
}
public function getEntity($id = null)
public function getEntity(? string $id = null) : Entity
{
if (isset($id) && $id == 'system') {
throw new Forbidden();
@@ -111,8 +113,9 @@ class User extends Record
return $entity;
}
public function changePassword($userId, $password, $checkCurrentPassword = false, $currentPassword = null)
{
public function changePassword(
string $userId, string $password, bool $checkCurrentPassword = false, bool $currentPassword = null
) {
$user = $this->getEntityManager()->getEntity('User', $userId);
if (!$user) {
throw new NotFound();
@@ -268,7 +271,7 @@ class User extends Record
}
}
public function create($data)
public function create(StdClass $data) : Entity
{
$newPassword = null;
if (property_exists($data, 'password')) {
@@ -290,7 +293,7 @@ class User extends Record
return $user;
}
public function update($id, $data)
public function update(string $id, StdClass $data) : Entity
{
if ($id == 'system') {
throw new Forbidden();