. * * 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 Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ namespace Espo\Core\Controllers; use Espo\Core\Exceptions\Conflict; use Espo\Core\Exceptions\Error; use Espo\Core\Exceptions\Forbidden; use Espo\Core\Exceptions\BadRequest; use Espo\Core\Exceptions\ForbiddenSilent; use Espo\Core\Exceptions\NotFound; use Espo\Core\Exceptions\NotFoundSilent; use Espo\Core\InjectableFactory; use Espo\Core\Record\ServiceContainer as RecordServiceContainer; use Espo\Core\Record\SearchParamsFetcher; use Espo\Core\Record\CreateParamsFetcher; use Espo\Core\Record\ReadParamsFetcher; use Espo\Core\Record\UpdateParamsFetcher; use Espo\Core\Record\DeleteParamsFetcher; use Espo\Core\Record\FindParamsFetcher; use Espo\Core\Record\Service as RecordService; use Espo\Core\Acl; use Espo\Core\Utils\Config; use Espo\Core\Api\Request; use Espo\Core\Api\Response; use Espo\Core\Select\SearchParams; use Espo\Entities\User; use Espo\ORM\Entity; use stdClass; class RecordBase { /** * @internal */ protected string $name; public static string $defaultAction = 'list'; /** * @throws Forbidden */ public function __construct( protected SearchParamsFetcher $searchParamsFetcher, protected CreateParamsFetcher $createParamsFetcher, protected ReadParamsFetcher $readParamsFetcher, protected UpdateParamsFetcher $updateParamsFetcher, protected DeleteParamsFetcher $deleteParamsFetcher, protected RecordServiceContainer $recordServiceContainer, protected FindParamsFetcher $findParamsFetcher, protected Config $config, protected User $user, protected Acl $acl, protected InjectableFactory $injectableFactory, ) { if (empty($this->name)) { $name = get_class($this); $matches = null; if (preg_match('@\\\\([\w]+)$@', $name, $matches)) { $name = $matches[1]; } $this->name = $name; } if (!$this->checkAccess()) { throw new Forbidden("No access to '$this->name'."); } } /** * Check access to controller. */ protected function checkAccess(): bool { return true; } protected function getEntityType(): string { return $this->name; } /** * @return RecordService */ protected function getRecordService(?string $entityType = null): RecordService { return $this->recordServiceContainer->get($entityType ?? $this->getEntityType()); } /** * Read a record. * * @throws NotFoundSilent * @throws ForbiddenSilent * @throws BadRequest * @throws Forbidden * @noinspection PhpUnusedParameterInspection */ public function getActionRead(Request $request, Response $response): stdClass { if (method_exists($this, 'actionRead')) { // For backward compatibility. return (object) $this->actionRead($request->getRouteParams(), $request->getParsedBody(), $request); } $id = $request->getRouteParam('id'); $params = $this->readParamsFetcher->fetch($request); if (!$id) { throw new BadRequest("No ID."); } $result = $this->getRecordService()->read($id, $params); return $result->getValueMap(); } /** * Create a record. * * @throws Forbidden * @throws Conflict * @throws BadRequest */ public function postActionCreate(Request $request, Response $response): stdClass { if (method_exists($this, 'actionCreate')) { // For backward compatibility. return (object) $this->actionCreate($request->getRouteParams(), $request->getParsedBody(), $request); } $data = $request->getParsedBody(); $params = $this->createParamsFetcher->fetch($request); $result = $this->getRecordService()->create($data, $params); return $result->getValueMap(); } /** * @throws BadRequest * @throws NotFound * @throws Forbidden * @throws Conflict * @noinspection PhpUnused */ public function patchActionUpdate(Request $request, Response $response): stdClass { return $this->putActionUpdate($request, $response); } /** * Update a record. * * @throws BadRequest * @throws NotFound * @throws Forbidden * @throws Conflict * @noinspection PhpUnusedParameterInspection */ public function putActionUpdate(Request $request, Response $response): stdClass { if (method_exists($this, 'actionUpdate')) { // For backward compatibility. return (object) $this->actionUpdate($request->getRouteParams(), $request->getParsedBody(), $request); } $id = $request->getRouteParam('id'); $data = $request->getParsedBody(); if (!$id) { throw new BadRequest("No ID."); } $params = $this->updateParamsFetcher->fetch($request); $result = $this->getRecordService()->update($id, $data, $params); if ($result->isLinkUpdated()) { $response->setHeader('X-Record-Link-Updated', 'true'); } return $result->getValueMap(); } /** * List records. * * @throws Forbidden * @throws BadRequest * @throws Error */ public function getActionList(Request $request, Response $response): stdClass { if (method_exists($this, 'actionList')) { // For backward compatibility. return (object) $this->actionList($request->getRouteParams(), $request->getParsedBody(), $request); } $searchParams = $this->fetchSearchParamsFromRequest($request); $findParams = $this->findParamsFetcher->fetch($request); $recordCollection = $this->getRecordService()->find($searchParams, $findParams); return $recordCollection->toApiOutput(); } /** * Delete a record. * * @throws Forbidden * @throws BadRequest * @throws NotFound * @throws Conflict * @noinspection PhpUnusedParameterInspection */ public function deleteActionDelete(Request $request, Response $response): bool { if (method_exists($this, 'actionDelete')) { // For backward compatibility. return $this->actionDelete($request->getRouteParams(), $request->getParsedBody(), $request); } $id = $request->getRouteParam('id'); $params = $this->deleteParamsFetcher->fetch($request); if (!$id) { throw new BadRequest("No ID."); } $this->getRecordService()->delete($id, $params); return true; } /** * @throws BadRequest * @throws Forbidden */ protected function fetchSearchParamsFromRequest(Request $request): SearchParams { return $this->searchParamsFetcher->fetch($request); } /** * @throws BadRequest * @throws Forbidden * @throws NotFound * @throws ForbiddenSilent * @noinspection PhpUnused */ public function postActionGetDuplicateAttributes(Request $request): stdClass { $id = $request->getParsedBody()->id ?? null; if (!$id) { throw new BadRequest(); } return $this->getRecordService()->getDuplicateAttributes($id); } /** * @throws BadRequest * @throws Forbidden * @throws NotFound * @throws Conflict * @noinspection PhpUnused */ public function postActionRestoreDeleted(Request $request): bool { if (!$this->user->isAdmin()) { throw new Forbidden(); } $id = $request->getParsedBody()->id ?? null; if (!$id) { throw new BadRequest(); } $this->getRecordService()->restoreDeleted($id); return true; } }