diff --git a/config/assets.yaml b/config/assets.yaml index f980bafea..28aee31b2 100644 --- a/config/assets.yaml +++ b/config/assets.yaml @@ -13,4 +13,8 @@ services: # Hydrators Pimcore\Bundle\StudioBackendBundle\Asset\Hydrator\CustomSettingsHydratorInterface: - class: Pimcore\Bundle\StudioBackendBundle\Asset\Hydrator\CustomSettingsHydrator \ No newline at end of file + class: Pimcore\Bundle\StudioBackendBundle\Asset\Hydrator\CustomSettingsHydrator + + # Encoder + Pimcore\Bundle\StudioBackendBundle\Asset\Encoder\TextEncoderInterface: + class: Pimcore\Bundle\StudioBackendBundle\Asset\Encoder\TextEncoder \ No newline at end of file diff --git a/config/properties.yaml b/config/properties.yaml new file mode 100644 index 000000000..fa775d045 --- /dev/null +++ b/config/properties.yaml @@ -0,0 +1,23 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + # controllers are imported separately to make sure they're public + # and have a tag that allows actions to type-hint services + Pimcore\Bundle\StudioBackendBundle\Property\Controller\: + resource: '../src/Property/Controller' + public: true + tags: [ 'controller.service_arguments' ] + + + Pimcore\Bundle\StudioBackendBundle\Property\Repository\PropertyRepositoryInterface: + class: Pimcore\Bundle\StudioBackendBundle\Property\Repository\PropertyRepository + + Pimcore\Bundle\StudioBackendBundle\Property\Service\PropertyServiceInterface: + class: Pimcore\Bundle\StudioBackendBundle\Property\Service\PropertyService + + Pimcore\Bundle\StudioBackendBundle\Property\Hydrator\PropertyHydratorInterface: + class: Pimcore\Bundle\StudioBackendBundle\Property\Hydrator\PropertyHydrator + diff --git a/src/Asset/Controller/Data/TextController.php b/src/Asset/Controller/Data/TextController.php new file mode 100644 index 000000000..226e2e548 --- /dev/null +++ b/src/Asset/Controller/Data/TextController.php @@ -0,0 +1,79 @@ +value)] + #[Get( + path: self::API_PATH . '/assets/{id}/text', + operationId: 'getAssetDataTextById', + summary: 'Get asset data in text UTF8 representation by id', + security: self::SECURITY_SCHEME, + tags: [Tags::Assets->name] + )] + #[IdParameter(type: 'asset')] + #[SuccessResponse( + description: 'UTF8 encoded text data', + content: new DataJson('UTF 8 encoded text data') + )] + #[UnauthorizedResponse] + #[NotFoundResponse] + #[MethodNotAllowedResponse] + #[UnsupportedMediaTypeResponse] + #[UnprocessableContentResponse] + public function getTextData(int $id): JsonResponse + { + $element = $this->getElement($this->serviceResolver, 'asset', $id); + + return $this->jsonResponse(['data' => $this->textEncoder->encodeUTF8($element)]); + } +} diff --git a/src/Asset/Encoder/TextEncoder.php b/src/Asset/Encoder/TextEncoder.php new file mode 100644 index 000000000..bc35fb6b6 --- /dev/null +++ b/src/Asset/Encoder/TextEncoder.php @@ -0,0 +1,44 @@ +getFileSize() > self::MAX_FILE_SIZE) { + throw new MaxFileSizeExceededException(self::MAX_FILE_SIZE); + } + + return Encoding::toUTF8($element->getData()); + } +} diff --git a/src/Asset/Encoder/TextEncoderInterface.php b/src/Asset/Encoder/TextEncoderInterface.php new file mode 100644 index 000000000..a8aeb5574 --- /dev/null +++ b/src/Asset/Encoder/TextEncoderInterface.php @@ -0,0 +1,29 @@ +load('filters.yaml'); $loader->load('icon.yaml'); $loader->load('open_api.yaml'); + $loader->load('properties.yaml'); $loader->load('security.yaml'); $loader->load('services.yaml'); $loader->load('translation.yaml'); diff --git a/src/Event/AbstractPreResponseEvent.php b/src/Event/AbstractPreResponseEvent.php new file mode 100644 index 000000000..9c07aff25 --- /dev/null +++ b/src/Event/AbstractPreResponseEvent.php @@ -0,0 +1,48 @@ +responseObject->hasAdditionalAttribute($key); + } + + public function getAdditionalAttribute(string $key): mixed + { + return $this->responseObject->getAdditionalAttribute($key); + } + + public function addAdditionalAttribute(string $key, mixed $value): void + { + $this->responseObject->addAdditionalAttribute($key, $value); + } + + public function removeAdditionalAttribute(string $key): void { + $this->responseObject->removeAdditionalAttribute($key); + } +} diff --git a/src/Exception/ElementSavingFailedException.php b/src/Exception/ElementSavingFailedException.php new file mode 100644 index 000000000..ff327e98a --- /dev/null +++ b/src/Exception/ElementSavingFailedException.php @@ -0,0 +1,32 @@ +name, description: 'Get dependencies for a single element.' )] +#[Tag( + name: Tags::Properties->name, + description: 'Property operations to get/update/create/delete properties' +)] +#[Tag( + name: Tags::PropertiesForElement->value, + description: 'Property operations to get/update properties for an element' +)] #[Tag( name: Tags::Translation->name, description: 'Get translations either for a single key or multiple keys' @@ -45,12 +53,14 @@ name: Tags::Versions->name, description: 'Versions operations to get/list/publish/delete and cleanup versions' )] -enum Tags +enum Tags: string { - case Assets; - case Authorization; - case DataObjects; - case Dependencies; - case Translation; - case Versions; + case Assets = 'Assets'; + case Authorization = 'Authorization'; + case DataObjects = 'DataObjects'; + case Dependencies = 'Dependencies'; + case Properties = 'Properties'; + case PropertiesForElement = 'Properties for Element'; + case Translation = 'Translation'; + case Versions = 'Versions'; } diff --git a/src/Property/Attributes/Request/ElementPropertyRequestBody.php b/src/Property/Attributes/Request/ElementPropertyRequestBody.php new file mode 100644 index 000000000..00684e5f6 --- /dev/null +++ b/src/Property/Attributes/Request/ElementPropertyRequestBody.php @@ -0,0 +1,34 @@ +name] + )] + #[ElementTypeParameter(false, null)] + #[FilterParameter] + #[SuccessResponse( + description: 'Predefined properties filtered based on type and query parameters', + content: new ItemsJson(PredefinedProperty::class) + )] + #[BadRequestResponse] + #[UnauthorizedResponse] + #[MethodNotAllowedResponse] + #[UnsupportedMediaTypeResponse] + #[UnprocessableContentResponse] + public function getProperties( + #[MapQueryString] PropertiesParameters $parameters = new PropertiesParameters() + ): JsonResponse { + return $this->jsonResponse(['items' => $this->propertyService->getPredefinedProperties($parameters)]); + } +} diff --git a/src/Property/Controller/CreateController.php b/src/Property/Controller/CreateController.php new file mode 100644 index 000000000..c44cc9ed4 --- /dev/null +++ b/src/Property/Controller/CreateController.php @@ -0,0 +1,72 @@ +name] + )] + #[SuccessResponse( + description: 'Element Properties data as json', + content: new JsonContent(ref: PredefinedProperty::class, type: 'object') + )] + #[BadRequestResponse] + #[UnauthorizedResponse] + #[MethodNotAllowedResponse] + #[UnsupportedMediaTypeResponse] + #[UnprocessableContentResponse] + public function createProperty(): JsonResponse + { + return $this->jsonResponse($this->propertyService->createPredefinedProperty()); + } +} diff --git a/src/Property/Controller/DeleteController.php b/src/Property/Controller/DeleteController.php new file mode 100644 index 000000000..d56682570 --- /dev/null +++ b/src/Property/Controller/DeleteController.php @@ -0,0 +1,77 @@ +name] + )] + #[IdParameter(type: 'property', schema: new Schema(type: 'string', example: 'alpha-numerical'))] + #[SuccessResponse( + description: 'Element Properties data as json', + content: new IdJson('ID of deleted property') + )] + #[UnauthorizedResponse] + #[NotFoundResponse] + #[MethodNotAllowedResponse] + #[UnsupportedMediaTypeResponse] + #[UnprocessableContentResponse] + public function deleteProperty(string $id): JsonResponse + { + $this->propertyService->deletePredefinedProperty($id); + + return $this->jsonResponse(['id' => $id]); + } +} diff --git a/src/Property/Controller/Element/CollectionController.php b/src/Property/Controller/Element/CollectionController.php new file mode 100644 index 000000000..91644b48f --- /dev/null +++ b/src/Property/Controller/Element/CollectionController.php @@ -0,0 +1,75 @@ +value] + )] + #[ElementTypeParameter] + #[IdParameter(type: 'element')] + #[SuccessResponse( + description: 'Element Properties data as json', + content: new ItemsJson(ElementProperty::class) + )] + #[UnauthorizedResponse] + #[NotFoundResponse] + #[MethodNotAllowedResponse] + #[UnsupportedMediaTypeResponse] + #[UnprocessableContentResponse] + public function getProperties(string $elementType, int $id): JsonResponse + { + return $this->jsonResponse( + ['items' => $this->propertyService->getElementProperties($elementType, $id)] + ); + } +} diff --git a/src/Property/Controller/Element/UpdateController.php b/src/Property/Controller/Element/UpdateController.php new file mode 100644 index 000000000..e066fef11 --- /dev/null +++ b/src/Property/Controller/Element/UpdateController.php @@ -0,0 +1,84 @@ +value] + )] + #[ElementTypeParameter] + #[IdParameter(type: 'element')] + #[ElementPropertyRequestBody] + #[SuccessResponse( + description: 'Element Properties data as json', + content: new ItemsJson(ElementProperty::class) + )] + #[UnauthorizedResponse] + #[NotFoundResponse] + #[MethodNotAllowedResponse] + #[UnsupportedMediaTypeResponse] + #[UnprocessableContentResponse] + public function updateProperties( + string $elementType, + int $id, + #[MapRequestPayload] UpdateElementProperties $items + ): JsonResponse { + $this->propertyService->updateElementProperties($elementType, $id, $items); + + return $this->jsonResponse( + ['items' => $this->propertyService->getElementProperties($elementType, $id)] + ); + } +} diff --git a/src/Property/Controller/UpdateController.php b/src/Property/Controller/UpdateController.php new file mode 100644 index 000000000..d5809d184 --- /dev/null +++ b/src/Property/Controller/UpdateController.php @@ -0,0 +1,83 @@ +name] + )] + #[IdParameter(type: 'property', schema: new Schema(type: 'string', example: 'alpha-numerical'))] + #[PredefinedPropertyRequestBody] + #[SuccessResponse( + description: 'Updated property', + content: new JsonContent(ref: PredefinedProperty::class, type: 'object') + )] + #[BadRequestResponse] + #[UnauthorizedResponse] + #[MethodNotAllowedResponse] + #[UnsupportedMediaTypeResponse] + #[UnprocessableContentResponse] + public function updateProperty( + string $id, + #[MapRequestPayload] UpdatePredefinedProperty $updatePredefinedProperty + ): JsonResponse { + $this->propertyService->updatePredefinedProperty($id, $updatePredefinedProperty); + + return $this->jsonResponse($this->propertyService->getPredefinedProperty($id)); + } +} diff --git a/src/Property/Event/ElementPropertyEvent.php b/src/Property/Event/ElementPropertyEvent.php new file mode 100644 index 000000000..c6687092e --- /dev/null +++ b/src/Property/Event/ElementPropertyEvent.php @@ -0,0 +1,39 @@ +elementProperty; + } +} diff --git a/src/Property/Event/PredefinedPropertyEvent.php b/src/Property/Event/PredefinedPropertyEvent.php new file mode 100644 index 000000000..2a5d4a9d9 --- /dev/null +++ b/src/Property/Event/PredefinedPropertyEvent.php @@ -0,0 +1,39 @@ +predefinedProperty; + } +} diff --git a/src/Property/Hydrator/PropertyHydrator.php b/src/Property/Hydrator/PropertyHydrator.php new file mode 100644 index 000000000..9c85a6d34 --- /dev/null +++ b/src/Property/Hydrator/PropertyHydrator.php @@ -0,0 +1,130 @@ +getId(), + $property->getName(), + $property->getDescription(), + $property->getKey(), + $property->getType(), + $property->getData(), + $property->getConfig(), + $property->getCtype(), + $property->getInheritable(), + $property->getCreationDate(), + $property->getModificationDate() + ); + } + + public function hydrateElementProperty(Property $property): ElementProperty + { + $propertyData = $this->extractData($property); + + return new ElementProperty( + $propertyData['name'], + $propertyData['modelData'] ?? $propertyData['data'], + $propertyData['type'], + $propertyData['inheritable'], + $propertyData['inherited'], + $propertyData['config'], + $propertyData['predefinedName'] ?? 'Custom', + $propertyData['description'] + ); + } + + private function extractData(Property $property): array + { + $data['modelData'] = match (true) { + $property->getData() instanceof Document || + $property->getData() instanceof Asset || + $property->getData() instanceof AbstractObject => $this->extractDataFromModel($property->getData()), + default => null, + }; + + return [ + ... $this->excludeProperties($property->getObjectVars()), + ... $data, + ... $this->extractPredefinedPropertyData($property), + ]; + } + + private function extractDataFromModel(Document|Asset|AbstractObject $data): array + { + return array_intersect_key($data->getObjectVars(), array_flip(self::ALLOWED_MODEL_PROPERTIES)); + } + + private function excludeProperties(array $values): array + { + return array_diff_key($values, array_flip(self::EXCLUDED_PROPERTIES)); + } + + private function extractPredefinedPropertyData(Property $property): array + { + $empty = ['config' => null, 'predefinedName' => null, 'description' => null]; + if (!$property->getName() || !$property->getType()) { + return $empty; + } + + $predefinedProperty = $this->predefinedResolver->getByKey($property->getName()); + if (!$predefinedProperty || $predefinedProperty->getType() !== $property->getType()) { + return $empty; + } + + return [ + 'config' => $predefinedProperty->getConfig(), + 'predefinedName' => $predefinedProperty->getName(), + 'description' => $predefinedProperty->getDescription(), + ]; + } +} diff --git a/src/Property/Hydrator/PropertyHydratorInterface.php b/src/Property/Hydrator/PropertyHydratorInterface.php new file mode 100644 index 000000000..5bd220bde --- /dev/null +++ b/src/Property/Hydrator/PropertyHydratorInterface.php @@ -0,0 +1,32 @@ +isWriteable()) { + throw new NotWriteableException('Predefined Property'); + } + + $property = $this->predefinedResolver->create(); + $property->setCtype(ElementTypes::TYPE_DOCUMENT); + $property->setName('New Property'); + $property->setKey('new_key'); + $property->setType('text'); + $property->save(); + + return $property; + } + + public function getPredefinedProperty(string $id): Predefined + { + return $this->predefinedResolver->getById($id); + } + + public function listProperties(PropertiesParameters $parameters): PropertiesListing + { + $list = new PropertiesListing(); + $type = $parameters->getElementType(); + $filter = $parameters->getFilter(); + $translator = $this->translator; + + $list->setFilter(static function (Predefined $predefined) use ($type, $filter, $translator) { + + if ($type && !str_contains($predefined->getCtype(), $type)) { + return false; + } + if ($filter && stripos($translator->trans($predefined->getName(), [], 'admin'), $filter) === false) { + return false; + } + + return true; + }); + + return $list; + } + + /** + * @throws PropertyNotFoundException + */ + public function updatePredefinedProperty(string $id, UpdatePredefinedProperty $property): void + { + $predefined = $this->predefinedResolver->getById($id); + + if (!$predefined) { + throw new PropertyNotFoundException($id); + } + + $predefined->setName($property->getName()); + $predefined->setDescription($property->getDescription()); + $predefined->setKey($property->getKey()); + $predefined->setType($property->getType()); + $predefined->setData($property->getData()); + $predefined->setConfig($property->getConfig()); + $predefined->setCtype($property->getCtype()); + $predefined->setInheritable($property->isInheritable()); + + $predefined->save(); + } + + /** + * @throws PropertyNotFoundException + */ + public function deletePredefinedProperty(string $id): void + { + $predefined = $this->predefinedResolver->getById($id); + + if (!$predefined) { + throw new PropertyNotFoundException($id); + } + + $predefined->delete(); + } + + /** + * @throws ElementSavingFailedException + */ + public function updateElementProperties(string $elementType, int $id, UpdateElementProperties $items): void + { + $element = $this->getElement($this->serviceResolver, $elementType, $id); + $properties = []; + foreach($items->getProperties() as $updateProperty) { + $property = new Property(); + $property->setType($updateProperty->getType()); + $property->setName($updateProperty->getKey()); + $property->setData($updateProperty->getData()); + $property->setInheritable($updateProperty->getInheritable()); + $properties[$updateProperty->getKey()] = $property; + } + $element->setProperties($properties); + + try { + $element->save(); + } catch (DuplicateFullPathException $e) { + throw new ElementSavingFailedException($id, $e->getMessage()); + } + } +} diff --git a/src/Property/Repository/PropertyRepositoryInterface.php b/src/Property/Repository/PropertyRepositoryInterface.php new file mode 100644 index 000000000..9c5906b0d --- /dev/null +++ b/src/Property/Repository/PropertyRepositoryInterface.php @@ -0,0 +1,49 @@ +filter; + } + + public function getElementType(): ?string + { + if ($this->elementType === ElementTypes::TYPE_DATA_OBJECT) { + return ElementTypes::TYPE_OBJECT; + } + + return $this->elementType; + } +} diff --git a/src/Property/Request/UpdateElementProperties.php b/src/Property/Request/UpdateElementProperties.php new file mode 100644 index 000000000..bc92ffe7d --- /dev/null +++ b/src/Property/Request/UpdateElementProperties.php @@ -0,0 +1,45 @@ +properties = array_map(static function (array $propertyData) { + return new UpdateElementProperty( + $propertyData['key'], + $propertyData['data'], + $propertyData['type'], + $propertyData['inheritable'], + ); + }, $items); + } + + public function getProperties(): array + { + return $this->properties; + } +} diff --git a/src/Property/Schema/ElementProperty.php b/src/Property/Schema/ElementProperty.php new file mode 100644 index 000000000..655508341 --- /dev/null +++ b/src/Property/Schema/ElementProperty.php @@ -0,0 +1,94 @@ +key; + } + + public function getData(): mixed + { + return $this->data; + } + + public function getType(): string + { + return $this->type; + } + + public function isInheritable(): bool + { + return $this->inheritable; + } + + public function isInherited(): bool + { + return $this->inherited; + } + + public function getConfig(): ?string + { + return $this->config; + } + + public function getPredefinedName(): ?string + { + return $this->predefinedName; + } + + public function getDescription(): ?string + { + return $this->description; + } +} diff --git a/src/Property/Schema/PredefinedProperty.php b/src/Property/Schema/PredefinedProperty.php new file mode 100644 index 000000000..2064f014c --- /dev/null +++ b/src/Property/Schema/PredefinedProperty.php @@ -0,0 +1,115 @@ +id; + } + + public function getName(): string + { + return $this->name; + } + + public function getDescription(): ?string + { + return $this->description; + } + + public function getKey(): string + { + return $this->key; + } + + public function getType(): string + { + return $this->type; + } + + public function getData(): ?string + { + return $this->data; + } + + public function getConfig(): ?string + { + return $this->config; + } + + public function getCtype(): string + { + return $this->ctype; + } + + public function isInheritable(): bool + { + return $this->inheritable; + } + + public function getCreationDate(): int + { + return $this->creationDate; + } + + public function getModificationDate(): int + { + return $this->modificationDate; + } +} diff --git a/src/Property/Schema/UpdateElementProperty.php b/src/Property/Schema/UpdateElementProperty.php new file mode 100644 index 000000000..1895bf047 --- /dev/null +++ b/src/Property/Schema/UpdateElementProperty.php @@ -0,0 +1,62 @@ +key; + } + + public function getData(): mixed + { + return $this->data; + } + + public function getType(): string + { + return $this->type; + } + + public function getInheritable(): bool + { + return $this->inheritable; + } +} diff --git a/src/Property/Schema/UpdatePredefinedProperty.php b/src/Property/Schema/UpdatePredefinedProperty.php new file mode 100644 index 000000000..8b5df22bc --- /dev/null +++ b/src/Property/Schema/UpdatePredefinedProperty.php @@ -0,0 +1,90 @@ +name; + } + + public function getDescription(): ?string + { + return $this->description; + } + + public function getKey(): string + { + return $this->key; + } + + public function getType(): string + { + return $this->type; + } + + public function getData(): ?string + { + return $this->data; + } + + public function getConfig(): ?string + { + return $this->config; + } + + public function getCtype(): string + { + return $this->ctype; + } + + public function isInheritable(): bool + { + return $this->inheritable; + } +} diff --git a/src/Property/Service/PropertyService.php b/src/Property/Service/PropertyService.php new file mode 100644 index 000000000..2427a3922 --- /dev/null +++ b/src/Property/Service/PropertyService.php @@ -0,0 +1,133 @@ +propertyRepository->createPredefinedProperty(); + return $this->getPredefinedProperty($predefined->getId()); + } + + public function getPredefinedProperty(string $id): PredefinedProperty + { + $predefinedProperty = $this->propertyHydrator->hydratePredefinedProperty( + $this->propertyRepository->getPredefinedProperty($id) + ); + + $this->eventDispatcher->dispatch( + new PredefinedPropertyEvent($predefinedProperty), + PredefinedPropertyEvent::EVENT_NAME + ); + + return $predefinedProperty; + } + + /** + * @return array + */ + public function getPredefinedProperties(PropertiesParameters $parameters): array + { + $properties = $this->propertyRepository->listProperties($parameters); + $hydratedProperties = []; + foreach ($properties->load() as $property) { + + $predefinedProperty = $this->propertyHydrator->hydratePredefinedProperty($property); + + $this->eventDispatcher->dispatch( + new PredefinedPropertyEvent($predefinedProperty), + PredefinedPropertyEvent::EVENT_NAME + ); + + $hydratedProperties[] = $predefinedProperty; + } + return $hydratedProperties; + } + + /** + * @return array + */ + public function getElementProperties(string $elementType, int $id): array + { + $element = $this->getElement($this->serviceResolver, $elementType, $id); + + $hydratedProperties = []; + + foreach($element->getProperties() as $property) { + + $elementProperty = $this->propertyHydrator->hydrateElementProperty($property); + + $this->eventDispatcher->dispatch( + new ElementPropertyEvent($elementProperty), + ElementPropertyEvent::EVENT_NAME + ); + + $hydratedProperties[] = $elementProperty; + } + + return $hydratedProperties; + } + + /** + * @throws PropertyNotFoundException + */ + public function updatePredefinedProperty(string $id, UpdatePredefinedProperty $property): void + { + $this->propertyRepository->updatePredefinedProperty($id, $property); + } + + public function updateElementProperties(string $elementType, int $id, UpdateElementProperties $items): void + { + $this->propertyRepository->updateElementProperties($elementType, $id, $items); + } + + /** + * @throws PropertyNotFoundException + */ + public function deletePredefinedProperty(string $id): void + { + $this->propertyRepository->deletePredefinedProperty($id); + } +} diff --git a/src/Property/Service/PropertyServiceInterface.php b/src/Property/Service/PropertyServiceInterface.php new file mode 100644 index 000000000..f751619ce --- /dev/null +++ b/src/Property/Service/PropertyServiceInterface.php @@ -0,0 +1,56 @@ + + */ + public function getPredefinedProperties(PropertiesParameters $parameters): array; + + /** + * @return array + */ + public function getElementProperties(string $elementType, int $id): array; + + /** + * @throws PropertyNotFoundException + */ + public function updatePredefinedProperty(string $id, UpdatePredefinedProperty $property): void; + + public function updateElementProperties(string $elementType, int $id, UpdateElementProperties $items): void; + + /** + * @throws PropertyNotFoundException + */ + public function deletePredefinedProperty(string $id): void; +} diff --git a/src/Util/Schema/AdditionalAttributesInterface.php b/src/Util/Schema/AdditionalAttributesInterface.php new file mode 100644 index 000000000..db43be714 --- /dev/null +++ b/src/Util/Schema/AdditionalAttributesInterface.php @@ -0,0 +1,31 @@ +additionalAttributes); + } + + public function getAdditionalAttributes(): array + { + return $this->additionalAttributes; + } + + public function getAdditionalAttribute(string $key): mixed + { + return $this->additionalAttributes[$key] ?? null; + } + + public function addAdditionalAttribute(string $key, mixed $value): void + { + $this->additionalAttributes[$key] = $value; + } + + public function removeAdditionalAttribute(string $key): void + { + unset($this->additionalAttributes[$key]); + } +} diff --git a/src/Util/Traits/ElementProviderTrait.php b/src/Util/Traits/ElementProviderTrait.php index e16e6df30..193eefc59 100644 --- a/src/Util/Traits/ElementProviderTrait.php +++ b/src/Util/Traits/ElementProviderTrait.php @@ -38,19 +38,18 @@ trait ElementProviderTrait private function getElement( ServiceResolverInterface $serviceResolver, string $type, - int $id, - ?UserInterface $user = null, + int $id ): ElementInterface { $element = $serviceResolver->getElementById($type, $id); if ($element === null) { throw new ElementNotFoundException($id); } - return $this->getLatestVersionForUser($element, $user); + return $element; } private function getLatestVersionForUser( - Asset|Document|Concrete $element, + Asset|Document\PageSnippet|Concrete $element, ?UserInterface $user ): ElementInterface { // check for latest version diff --git a/tests/Unit/Asset/Encoder/EncoderTest.php b/tests/Unit/Asset/Encoder/EncoderTest.php new file mode 100644 index 000000000..24e8cc761 --- /dev/null +++ b/tests/Unit/Asset/Encoder/EncoderTest.php @@ -0,0 +1,71 @@ +encoder = new TextEncoder(); + } + + public function testWrongElementType(): void + { + $element = new Document(); + + $this->expectException(InvalidElementTypeException::class); + + $this->encoder->encodeUTF8($element); + } + + /** + * @throws Exception + */ + public function testFileSizeExceeded(): void + { + $element = $this->makeEmpty(Text::class, ['getFileSize' => 2000001]); + + $this->expectException(MaxFileSizeExceededException::class); + + $this->encoder->encodeUTF8($element); + } + + /** + * @throws Exception + */ + public function testUTF8Encoding(): void + { + $element = $this->makeEmpty(Text::class, ['getData' => 'Héllö, 世界!']); + $encodedData = $this->encoder->encodeUTF8($element); + + $this->assertTrue(mb_check_encoding($encodedData, 'UTF-8')); + } +} diff --git a/tests/Unit/Property/PropertyHydratorTest.php b/tests/Unit/Property/PropertyHydratorTest.php new file mode 100644 index 000000000..45b8a05b9 --- /dev/null +++ b/tests/Unit/Property/PropertyHydratorTest.php @@ -0,0 +1,134 @@ +getHydrator(); + + $data = $hydrator->hydratePredefinedProperty($this->getPredefined()); + + $this->assertSame('new_id', $data->getId()); + $this->assertSame(ElementTypes::TYPE_DOCUMENT, $data->getCtype()); + $this->assertSame('New Property', $data->getName()); + $this->assertSame('new_key', $data->getKey()); + $this->assertSame('text', $data->getType()); + $this->assertTrue($data->isInheritable()); + $this->assertSame('New Description', $data->getDescription()); + } + + /** + * @throws Exception + */ + public function testHydrateElementProperties(): void + { + $hydrator = $this->getHydrator(); + $data = $hydrator->hydrateElementProperty($this->getElementProperty()); + + $this->assertSame('New Property', $data->getPredefinedName()); + $this->assertSame('text', $data->getType()); + $this->assertTrue($data->isInheritable()); + $this->assertTrue($data->isInherited()); + $this->assertNull($data->getConfig()); + + $this->assertIsArray($data->getData()); + $this->assertArrayHasKey('path', $data->getData()); + $this->assertArrayHasKey('id', $data->getData()); + $this->assertArrayHasKey('type', $data->getData()); + $this->assertArrayHasKey('key', $data->getData()); + + $this->assertSame('/test', $data->getData()['path']); + $this->assertSame(1, $data->getData()['id']); + $this->assertSame('page', $data->getData()['type']); + $this->assertSame('test', $data->getData()['key']); + } + + /** + * @throws Exception + */ + private function getHydrator(): PropertyHydratorInterface + { + return new PropertyHydrator($this->mockPredefinedResolver()); + } + + /** + * @throws Exception + */ + private function mockPredefinedResolver(): PredefinedResolverInterface + { + return $this->makeEmpty(PredefinedResolverInterface::class, + [ + 'getById' => $this->getPredefined(), + 'getByKey' => $this->getPredefined(), + ]); + } + + private function getPredefined(): Predefined + { + $property = new Predefined(); + $property->setId('new_id'); + $property->setCtype(ElementTypes::TYPE_DOCUMENT); + $property->setName('New Property'); + $property->setKey('new_key'); + $property->setType('text'); + $property->setCreationDate(time()); + $property->setModificationDate(time()); + $property->setInheritable(true); + $property->setDescription('New Description'); + + return $property; + } + + private function getElementProperty(): Property + { + $property = new Property(); + $property->setDataFromResource($this->getDocument()); + $property->setName('New Property'); + $property->setType('text'); + $property->setCtype('document'); + $property->setInheritable(true); + $property->setInherited(true); + + return $property; + } + + private function getDocument(): Document + { + $document = new Document(); + $document->setPath('/test'); + $document->setId(1); + $document->setType('page'); + $document->setKey('test'); + + return $document; + } +}