ActionHistory interface

This commit is contained in:
Yuri Kuznetsov
2023-07-25 14:25:19 +03:00
parent 7fdd7cd280
commit 025cdf246a
11 changed files with 195 additions and 51 deletions
+5
View File
@@ -292,6 +292,11 @@ class Binding implements BindingProcessor
'Espo\\Tools\\Api\\Cors\\Helper',
'Espo\\Tools\\Api\\Cors\\DefaultHelper'
);
$binder->bindImplementation(
'Espo\\Core\\Record\\ActionHistory\\ActionLogger',
'Espo\\Core\\Record\\ActionHistory\\DefaultActionLogger'
);
}
private function bindAcl(Binder $binder): void
@@ -35,10 +35,10 @@ use Espo\Core\Action\Params;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\ORM\EntityManager;
use Espo\Core\Record\ActionHistory\Action;
use Espo\Core\Record\ServiceContainer;
use Espo\Core\Utils\Metadata;
use Espo\Core\Utils\ObjectUtil;
use Espo\Entities\ActionHistoryRecord;
use Espo\ORM\Entity;
use Espo\Entities\EmailAddress;
use Espo\Entities\PhoneNumber;
@@ -137,7 +137,7 @@ class Merger
foreach ($sourceEntityList as $sourceEntity) {
$this->entityManager->removeEntity($sourceEntity);
$service->processActionHistoryRecord(ActionHistoryRecord::ACTION_DELETE, $sourceEntity);
$service->processActionHistoryRecord(Action::DELETE, $sourceEntity);
}
if ($hasPhoneNumber) {
@@ -152,7 +152,7 @@ class Merger
$this->entityManager->saveEntity($entity);
$service->processActionHistoryRecord(ActionHistoryRecord::ACTION_UPDATE, $entity);
$service->processActionHistoryRecord(Action::UPDATE, $entity);
}
/**
@@ -29,6 +29,7 @@
namespace Espo\Core\MassAction\Actions;
use Espo\Core\Record\ActionHistory\Action;
use Espo\Entities\ActionHistoryRecord;
use Espo\Entities\User;
use Espo\Core\Acl;
@@ -97,7 +98,7 @@ class MassDelete implements MassAction
$count++;
$service->processActionHistoryRecord(ActionHistoryRecord::ACTION_DELETE, $entity);
$service->processActionHistoryRecord(Action::DELETE, $entity);
}
$result = [
@@ -0,0 +1,38 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii 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\Record\ActionHistory;
class Action
{
public const CREATE = 'create';
public const READ = 'read';
public const UPDATE = 'update';
public const DELETE = 'delete';
}
@@ -0,0 +1,45 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii 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\Record\ActionHistory;
use Espo\ORM\Entity;
/**
* Logs actions users do with records.
*/
interface ActionLogger
{
/**
* Log an action.
*
* @param Action::* $action
*/
public function log(string $action, Entity $entity): void;
}
@@ -0,0 +1,64 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii 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\Record\ActionHistory;
use Espo\Core\Field\LinkParent;
use Espo\Entities\ActionHistoryRecord;
use Espo\Entities\User;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
class DefaultActionLogger implements ActionLogger
{
public function __construct(
private EntityManager $entityManager,
private User $user
) {}
/**
* @inheritDoc
*/
public function log(string $action, Entity $entity): void
{
$historyRecord = $this->entityManager
->getRepositoryByClass(ActionHistoryRecord::class)
->getNew();
$historyRecord
->setAction($action)
->setUserId($this->user->getId())
->setAuthTokenId($this->user->get('authTokenId'))
->setAuthLogRecordId($this->user->get('authLogRecordId'))
->setIpAddress($this->user->get('ipAddress'))
->setTarget(LinkParent::createFromEntity($entity));
$this->entityManager->saveEntity($historyRecord);
}
}
+17 -18
View File
@@ -40,9 +40,10 @@ use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\ForbiddenSilent;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\NotFoundSilent;
use Espo\Core\Field\LinkParent;
use Espo\Core\ORM\Entity as CoreEntity;
use Espo\Core\Record\Access\LinkCheck;
use Espo\Core\Record\ActionHistory\Action;
use Espo\Core\Record\ActionHistory\ActionLogger;
use Espo\Core\Record\Formula\Processor as FormulaProcessor;
use Espo\Core\Utils\Json;
use Espo\Core\Acl;
@@ -66,8 +67,7 @@ use Espo\ORM\EntityManager;
use Espo\ORM\Query\Part\WhereClause;
use Espo\Tools\Stream\Service as StreamService;
use Espo\Entities\User;
use Espo\Entities\ActionHistoryRecord;
use Hoa\Ustring\Search;
use stdClass;
use InvalidArgumentException;
use LogicException;
@@ -189,6 +189,7 @@ class Service implements Crud,
private ?ListLoadProcessor $listLoadProcessor = null;
private ?DuplicateFinder $duplicateFinder = null;
private ?LinkCheck $linkCheck = null;
private ?ActionLogger $actionLogger = null;
protected const MAX_SELECT_TEXT_ATTRIBUTE_LENGTH = 10000;
@@ -208,7 +209,7 @@ class Service implements Crud,
/**
* Add an action-history record.
*
* @param ActionHistoryRecord::ACTION_* $action
* @param Action::* $action
*/
public function processActionHistoryRecord(string $action, Entity $entity): void
{
@@ -220,18 +221,16 @@ class Service implements Crud,
return;
}
/** @var ActionHistoryRecord $historyRecord */
$historyRecord = $this->entityManager->getNewEntity(ActionHistoryRecord::ENTITY_TYPE);
$this->getActionLogger()->log($action, $entity);
}
$historyRecord
->setAction($action)
->setUserId($this->user->getId())
->setAuthTokenId($this->user->get('authTokenId'))
->setAuthLogRecordId($this->user->get('authLogRecordId'))
->setIpAddress($this->user->get('ipAddress'))
->setTarget(LinkParent::createFromEntity($entity));
private function getActionLogger(): ActionLogger
{
if (!$this->actionLogger) {
$this->actionLogger = $this->injectableFactory->createResolved(ActionLogger::class);
}
$this->entityManager->saveEntity($historyRecord);
return $this->actionLogger;
}
/**
@@ -259,7 +258,7 @@ class Service implements Crud,
}
$this->recordHookManager->processBeforeRead($entity, $params);
$this->processActionHistoryRecord(ActionHistoryRecord::ACTION_READ, $entity);
$this->processActionHistoryRecord(Action::READ, $entity);
return $entity;
}
@@ -709,7 +708,7 @@ class Service implements Crud,
$this->afterCreateProcessDuplicating($entity, $params);
$this->loadAdditionalFields($entity);
$this->prepareEntityForOutput($entity);
$this->processActionHistoryRecord(ActionHistoryRecord::ACTION_CREATE, $entity);
$this->processActionHistoryRecord(Action::CREATE, $entity);
return $entity;
}
@@ -777,7 +776,7 @@ class Service implements Crud,
$this->afterUpdateEntity($entity, $data);
$this->prepareEntityForOutput($entity);
$this->processActionHistoryRecord(ActionHistoryRecord::ACTION_UPDATE, $entity);
$this->processActionHistoryRecord(Action::UPDATE, $entity);
return $entity;
}
@@ -813,7 +812,7 @@ class Service implements Crud,
$this->beforeDeleteEntity($entity);
$this->getRepository()->remove($entity);
$this->afterDeleteEntity($entity);
$this->processActionHistoryRecord(ActionHistoryRecord::ACTION_DELETE, $entity);
$this->processActionHistoryRecord(Action::DELETE, $entity);
}
/**
@@ -31,18 +31,19 @@ namespace Espo\Entities;
use Espo\Core\Field\LinkParent;
use Espo\Core\ORM\Entity;
use Espo\Core\Record\ActionHistory\Action;
class ActionHistoryRecord extends Entity
{
public const ENTITY_TYPE = 'ActionHistoryRecord';
public const ACTION_READ = 'read';
public const ACTION_UPDATE = 'update';
public const ACTION_CREATE = 'create';
public const ACTION_DELETE = 'delete';
public const ACTION_CREATE = Action::CREATE;
public const ACTION_READ = Action::READ;
public const ACTION_UPDATE = Action::UPDATE;
public const ACTION_DELETE = Action::DELETE;
/**
* @param self::ACTION_* $action
* @param Action::* $action
*/
public function setAction(string $action): self
{
@@ -29,33 +29,23 @@
namespace Espo\Tools\ActionHistory;
use Espo\Core\Record\ActionHistory\Action;
use Espo\Core\Record\Collection as RecordCollection;
use Espo\Entities\ActionHistoryRecord;
use Espo\Core\FieldProcessing\ListLoadProcessor;
use Espo\Core\ORM\EntityManager;
use Espo\Core\Utils\Metadata;
use Espo\Core\Utils\Util;
use Espo\Entities\User;
class Service
{
private Metadata $metadata;
private EntityManager $entityManager;
private User $user;
private ListLoadProcessor $listLoadProcessor;
public function __construct(
Metadata $metadata,
EntityManager $entityManager,
User $user,
ListLoadProcessor $listLoadProcessor
) {
$this->metadata = $metadata;
$this->entityManager = $entityManager;
$this->user = $user;
$this->listLoadProcessor = $listLoadProcessor;
}
private Metadata $metadata,
private EntityManager $entityManager,
private User $user,
private ListLoadProcessor $listLoadProcessor
) {}
/**
* @return RecordCollection<ActionHistoryRecord>
@@ -78,7 +68,7 @@ class Service
->getRDBRepositoryByClass(ActionHistoryRecord::class)
->where([
'userId' => $this->user->getId(),
'action' => ActionHistoryRecord::ACTION_READ,
'action' => Action::READ,
'targetType' => $targetTypeList,
])
->order('MAX:createdAt', 'DESC')
+6 -6
View File
@@ -29,12 +29,12 @@
namespace Espo\Tools\Import;
use Espo\Entities\ActionHistoryRecord;
use GuzzleHttp\Psr7\Utils as Psr7Utils;
use Espo\Core\Record\ActionHistory\Action;
use Espo\ORM\Entity;
use Espo\ORM\Query\DeleteBuilder;
use Espo\ORM\Type\RelationType;
use GuzzleHttp\Psr7\Utils as Psr7Utils;
use Espo\Core\ORM\Repository\Option\SaveOption;
use Espo\Entities\ImportEntity as ImportEntityEntity;
use Espo\Core\Exceptions\Error;
@@ -98,12 +98,12 @@ class Service
$id = $result->getId();
if ($id) {
$import = $this->entityManager->getEntity(ImportEntity::ENTITY_TYPE, $id);
$import = $this->entityManager->getEntityById(ImportEntity::ENTITY_TYPE, $id);
if ($import) {
$this->recordServiceContainer
->get(ImportEntity::ENTITY_TYPE)
->processActionHistoryRecord(ActionHistoryRecord::ACTION_CREATE, $import);
->processActionHistoryRecord(Action::CREATE, $import);
}
}
@@ -283,7 +283,7 @@ class Service
$this->recordServiceContainer
->get(ImportEntity::ENTITY_TYPE)
->processActionHistoryRecord(ActionHistoryRecord::ACTION_DELETE, $import);
->processActionHistoryRecord(Action::DELETE, $import);
}
private function deleteRelations(Entity $entity): void
@@ -40,6 +40,7 @@ use Espo\Core\MassAction\Result;
use Espo\Core\Acl;
use Espo\Core\Acl\Table;
use Espo\Core\Record\Access\LinkCheck;
use Espo\Core\Record\ActionHistory\Action as RecordAction;
use Espo\Core\Record\ServiceFactory;
use Espo\Core\Record\Service;
@@ -198,7 +199,7 @@ class Processor
'modifiedById' => $this->user->getId(),
]);
$service->processActionHistoryRecord(ActionHistoryRecord::ACTION_UPDATE, $entity);
$service->processActionHistoryRecord(RecordAction::UPDATE, $entity);
return true;
}