From aca76ae3a65728ff40825f10cda192d16d7e228f Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Thu, 29 Feb 2024 20:04:34 +0200 Subject: [PATCH] ref --- .../Record/Attachment/CreateInputFilter.php} | 170 +++++++++--------- .../Record/Attachment/UpdateInputFilter.php | 49 +++++ .../metadata/recordDefs/Attachment.json | 6 + 3 files changed, 144 insertions(+), 81 deletions(-) rename application/Espo/{Services/Attachment.php => Classes/Record/Attachment/CreateInputFilter.php} (50%) create mode 100644 application/Espo/Classes/Record/Attachment/UpdateInputFilter.php diff --git a/application/Espo/Services/Attachment.php b/application/Espo/Classes/Record/Attachment/CreateInputFilter.php similarity index 50% rename from application/Espo/Services/Attachment.php rename to application/Espo/Classes/Record/Attachment/CreateInputFilter.php index a0c114828c..5075df612f 100644 --- a/application/Espo/Services/Attachment.php +++ b/application/Espo/Classes/Record/Attachment/CreateInputFilter.php @@ -27,122 +27,130 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -namespace Espo\Services; +namespace Espo\Classes\Record\Attachment; use Espo\Core\Exceptions\BadRequest; -use Espo\Core\Exceptions\Forbidden; use Espo\Core\Exceptions\Error; -use Espo\Entities\Attachment as AttachmentEntity; +use Espo\Core\Exceptions\Forbidden; +use Espo\Core\Record\Input\Data; +use Espo\Core\Record\Input\Filter; +use Espo\Entities\Attachment; +use Espo\ORM\EntityManager; use Espo\Tools\Attachment\AccessChecker; use Espo\Tools\Attachment\DetailsObtainer; use Espo\Tools\Attachment\FieldData; -use stdClass; - /** - * @extends Record + * @noinspection PhpUnused */ -class Attachment extends Record +class CreateInputFilter implements Filter { - public function filterUpdateInput(stdClass $data): void - { - parent::filterUpdateInput($data); - - unset($data->parentId); - unset($data->parentType); - unset($data->relatedId); - unset($data->relatedType); - unset($data->isBeingUploaded); - unset($data->storage); - } + public function __construct( + private EntityManager $entityManager, + private AccessChecker $accessChecker, + private DetailsObtainer $detailsObtainer + ) {} /** * @throws BadRequest - * @throws Forbidden * @throws Error + * @throws Forbidden */ - public function filterCreateInput(stdClass $data): void + public function filter(Data $data): void { - parent::filterCreateInput($data); + $data->clear('parentId'); + $data->clear('relatedId'); - unset($data->parentId); - unset($data->relatedId); + $contents = $this->handleContents($data); - $isBeingUploaded = (bool) ($data->isBeingUploaded ?? false); + $relatedEntityType = $this->getRelatedEntityType($data); - $contents = ''; - - if (!$isBeingUploaded) { - if (!property_exists($data, 'file')) { - throw new BadRequest("No file contents."); - } - - if (!is_string($data->file)) { - throw new BadRequest("Non-string file contents."); - } - - $arr = explode(',', $data->file); - - if (count($arr) > 1) { - $contents = $arr[1]; - } - - $contents = base64_decode($contents); - } - - $data->contents = $contents; - - $relatedEntityType = null; - - if (isset($data->parentType)) { - $relatedEntityType = $data->parentType; - - unset($data->relatedType); - } - else if (isset($data->relatedType)) { - $relatedEntityType = $data->relatedType; - } - - $field = $data->field ?? null; - $role = $data->role ?? AttachmentEntity::ROLE_ATTACHMENT; + $field = $data->get('field'); + $role = $data->get('role') ?? Attachment::ROLE_ATTACHMENT; if (!$relatedEntityType || !$field) { throw new BadRequest("No `field` and `parentType`."); } - $fieldData = new FieldData( - $field, - $data->parentType ?? null, - $data->relatedType ?? null - ); + $fieldData = new FieldData($field, $data->get('parentType'), $data->get('relatedType')); - $this->getAccessChecker()->check($fieldData, $role); + $this->accessChecker->check($fieldData, $role); + $this->checkMaxSize($contents, $data, $field, $role); + } + private function getRelatedEntityType(Data $data): ?string + { + if ($data->get('parentType') !== null) { + $data->clear('relatedType'); + + return $data->get('parentType'); + } + + if ($data->get('relatedType') !== null) { + return $data->get('relatedType'); + } + + return null; + } + + /** + * @throws BadRequest + */ + private function handleContents(Data $data): string + { + $isBeingUploaded = $data->get('isBeingUploaded') ?? false; + + $contents = ''; + + if (!$isBeingUploaded) { + if (!$data->has('file')) { + throw new BadRequest("No file contents."); + } + + $file = $data->get('file'); + + if (!is_string($file)) { + throw new BadRequest("Non-string file contents."); + } + + $arr = explode(',', $file); + + if (count($arr) < 2) { + throw new BadRequest("Bad file contents."); + } + + $contents = base64_decode($arr[1]); + + if ($contents === false) { + throw new BadRequest("Could not decode file contents."); + } + } + + $data->set('contents', $contents); + + return $contents; + } + + /** + * @throws BadRequest + */ + private function checkMaxSize(string $contents, Data $data, mixed $field, mixed $role): void + { $size = mb_strlen($contents, '8bit'); - $dummy = $this->entityManager->getRepositoryByClass(AttachmentEntity::class)->getNew(); + $dummy = $this->entityManager->getRepositoryByClass(Attachment::class)->getNew(); $dummy->set([ - 'parentType' => $data->parentType ?? null, - 'relatedType' => $data->relatedType ?? null, - 'field' => $data->field ?? null, + 'parentType' => $data->get('parentType'), + 'relatedType' => $data->get('relatedType'), + 'field' => $field, 'role' => $role, ]); - $maxSize = $this->getDetailsObtainer()->getUploadMaxSize($dummy); + $maxSize = $this->detailsObtainer->getUploadMaxSize($dummy); if ($maxSize && $size > $maxSize * 1024 * 1024) { - throw new Error("File size should not exceed $maxSize Mb."); + throw new BadRequest("File size should not exceed $maxSize Mb."); } } - - private function getDetailsObtainer(): DetailsObtainer - { - return $this->injectableFactory->create(DetailsObtainer::class); - } - - private function getAccessChecker(): AccessChecker - { - return $this->injectableFactory->create(AccessChecker::class); - } } diff --git a/application/Espo/Classes/Record/Attachment/UpdateInputFilter.php b/application/Espo/Classes/Record/Attachment/UpdateInputFilter.php new file mode 100644 index 0000000000..ab5f1fd5a8 --- /dev/null +++ b/application/Espo/Classes/Record/Attachment/UpdateInputFilter.php @@ -0,0 +1,49 @@ +. + * + * 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\Classes\Record\Attachment; + +use Espo\Core\Record\Input\Data; +use Espo\Core\Record\Input\Filter; + +/** + * @noinspection PhpUnused + */ +class UpdateInputFilter implements Filter +{ + public function filter(Data $data): void + { + $data->clear('parentId'); + $data->clear('parentType'); + $data->clear('relatedId'); + $data->clear('relatedType'); + $data->clear('isBeingUploaded'); + $data->clear('storage'); + } +} diff --git a/application/Espo/Resources/metadata/recordDefs/Attachment.json b/application/Espo/Resources/metadata/recordDefs/Attachment.json index dcfd0b2a7b..4cd3604f94 100644 --- a/application/Espo/Resources/metadata/recordDefs/Attachment.json +++ b/application/Espo/Resources/metadata/recordDefs/Attachment.json @@ -4,6 +4,12 @@ "allowed": true } }, + "createInputFilterClassNameList": [ + "Espo\\Classes\\Record\\Attachment\\CreateInputFilter" + ], + "updateInputFilterClassNameList": [ + "Espo\\Classes\\Record\\Attachment\\UpdateInputFilter" + ], "beforeCreateHookClassNameList": [ "Espo\\Classes\\RecordHooks\\Attachment\\BeforeCreate" ],