diff --git a/config/api_platform/resources/asset.yaml b/config/api_platform/resources/asset.yaml index d4998a710..924e3e4c7 100644 --- a/config/api_platform/resources/asset.yaml +++ b/config/api_platform/resources/asset.yaml @@ -12,15 +12,13 @@ resources: ApiPlatform\Metadata\Get: normalizationContext: groups: [ 'asset:read', 'asset:item:get', 'dependency:read', 'property:read', 'element:read', 'element:item:get'] - ApiPlatform\Metadata\Put: - ApiPlatform\Metadata\Post: ApiPlatform\Metadata\Delete: - ApiPlatform\Metadata\Patch: - provider: Pimcore\Bundle\StudioApiBundle\State\AssetProvider + provider: Pimcore\Bundle\StudioApiBundle\State\Asset\Provider + processor: Pimcore\Bundle\StudioApiBundle\State\Asset\Processor normalizationContext: groups: ['asset:read', 'dependency:read', 'property:read', 'element:read'] denormalizationContext: - groups: ['asset:write'] + groups: ['asset:write', 'element:write'] properties: id: identifier: true diff --git a/config/api_platform/resources/asset/image.yaml b/config/api_platform/resources/asset/image.yaml index 11f47d019..b9c4e5484 100644 --- a/config/api_platform/resources/asset/image.yaml +++ b/config/api_platform/resources/asset/image.yaml @@ -1,14 +1,16 @@ resources: Pimcore\Bundle\StudioApiBundle\Dto\Asset\Image: - provider: Pimcore\Bundle\StudioApiBundle\State\AssetProvider + provider: Pimcore\Bundle\StudioApiBundle\State\Asset\Provider + processor: Pimcore\Bundle\StudioApiBundle\State\Asset\Processor operations: ApiPlatform\Metadata\Get: normalizationContext: groups: ['image:read', 'asset:read', 'asset:item:get', 'dependency:read', 'property:read', 'element:read', 'element:item:get' ] + ApiPlatform\Metadata\Patch: properties: id: identifier: true normalizationContext: groups: ['image:read', 'asset:read', 'dependency:read', 'property:read', 'element:read'] denormalizationContext: - groups: ['image:write', 'asset:write'] + groups: ['image:write', 'asset:write', 'element:write'] diff --git a/config/serialization/asset.yaml b/config/serialization/asset.yaml index 245aad6a3..bcd33afb9 100644 --- a/config/serialization/asset.yaml +++ b/config/serialization/asset.yaml @@ -1,20 +1,20 @@ Pimcore\Bundle\StudioApiBundle\Dto\Asset: attributes: type: - groups: ['asset:read'] + groups: ['asset:read', 'asset:write'] iconName: - groups: [ 'asset:read' ] + groups: [ 'asset:read' , 'asset:write'] children: - groups: ['asset:read'] + groups: ['asset:read', 'asset:write'] filename: - groups: ['asset:read'] + groups: ['asset:read', 'asset:write'] mimetype: - groups: ['asset:read'] + groups: ['asset:read', 'asset:write'] metadata: - groups: ['asset:read'] + groups: ['asset:read', 'asset:write'] customSettings: - groups: ['asset:item:get'] + groups: ['asset:item:get', 'asset:write'] hasMetaData: - groups: ['asset:read'] + groups: ['asset:read', 'asset:write'] userPermissions: - groups: ['asset:read'] + groups: ['asset:read', 'asset:write'] diff --git a/config/serialization/asset/image.yaml b/config/serialization/asset/image.yaml index 1398e0ded..70059e174 100644 --- a/config/serialization/asset/image.yaml +++ b/config/serialization/asset/image.yaml @@ -1,19 +1,13 @@ Pimcore\Bundle\StudioApiBundle\Dto\Asset\Image: attributes: thumbnail: - groups: ['image:read', 'asset:read'] - format: - groups: ['image:read', 'asset:read'] + groups: ['image:read', 'asset:read', 'image:write'] dimensions: - groups: ['image:read', 'asset:read'] + groups: ['image:read', 'asset:read', 'image:write'] width: - groups: ['image:read', 'asset:read'] + groups: ['image:read', 'asset:read', 'image:write'] height: - groups: ['image:read', 'asset:read'] - animated: - groups: ['image:read', 'asset:read'] - vectorGraphic: - groups: ['image:read', 'asset:read'] + groups: ['image:read', 'asset:read', 'image:write'] # lowQualityPreviewDataUri: # groups: ['image:read'] # lowQualityPreviewPath: diff --git a/config/serialization/element.yaml b/config/serialization/element.yaml index fe0eb019a..eb7de15a5 100644 --- a/config/serialization/element.yaml +++ b/config/serialization/element.yaml @@ -1,36 +1,36 @@ Pimcore\Bundle\StudioApiBundle\Dto\Element: attributes: id: - groups: ['element:read'] + groups: ['element:read', 'element:write'] parentId: - groups: ['element:read'] + groups: ['element:read', 'element:write'] permissions: - groups: ['element:read'] + groups: ['element:read', 'element:write'] path: - groups: ['element:read'] + groups: ['element:read', 'element:write'] creationDate: - groups: ['element:read'] + groups: ['element:read', 'element:write'] modificationDate: - groups: ['element:read'] + groups: ['element:read', 'element:write'] userOwner: - groups: ['element:read'] + groups: ['element:read', 'element:write'] userModification: - groups: ['element:read'] + groups: ['element:read', 'element:write'] properties: - groups: ['element:item:get'] + groups: ['element:item:get', 'element:write'] versions: - groups: ['element:read'] + groups: ['element:read', 'element:write'] locked: - groups: ['element:read'] + groups: ['element:read', 'element:write'] lock: - groups: ['element:read'] + groups: ['element:read', 'element:write'] dependencies: - groups: ['element:item:get'] + groups: ['element:item:get', 'element:write'] scheduledTasks: - groups: ['element:item:get'] + groups: ['element:item:get', 'element:write'] versionCount: - groups: ['element:item:get'] + groups: ['element:item:get', 'element:write'] fullPath: - groups: ['element:read'] + groups: ['element:read', 'element:write'] userPermissions: - groups: ['element:read'] + groups: ['element:read', 'element:write'] diff --git a/config/services.yaml b/config/services.yaml index 83bf2f682..bb571b5f6 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -20,12 +20,13 @@ services: tags: [ 'controller.service_arguments' ] # Providers - Pimcore\Bundle\StudioApiBundle\State\AssetProvider: ~ + Pimcore\Bundle\StudioApiBundle\State\Asset\Provider: ~ Pimcore\Bundle\StudioApiBundle\State\UserProvider: ~ Pimcore\Bundle\StudioApiBundle\State\ScheduledTaskProvider: ~ Pimcore\Bundle\StudioApiBundle\State\VersionProvider: ~ # Processors + Pimcore\Bundle\StudioApiBundle\State\Asset\Processor: ~ Pimcore\Bundle\StudioApiBundle\State\ResetPasswordProcessor: ~ Pimcore\Bundle\StudioApiBundle\State\Token\Create\Processor: ~ Pimcore\Bundle\StudioApiBundle\State\Token\Refresh\Processor: ~ @@ -70,12 +71,27 @@ services: Pimcore\Bundle\StudioApiBundle\Service\GenericData\V1\Hydrator\PermissionsHydratorInterface: class: Pimcore\Bundle\StudioApiBundle\Service\GenericData\V1\Hydrator\PermissionsHydrator + # Core Data + Pimcore\Bundle\StudioApiBundle\Service\CoreData\V1\Hydrator\Asset\ImageHydratorInterface: + class: Pimcore\Bundle\StudioApiBundle\Service\CoreData\V1\Hydrator\Asset\ImageHydrator + + Pimcore\Bundle\StudioApiBundle\Service\CoreData\V1\Hydrator\PermissionsHydratorInterface: + class: Pimcore\Bundle\StudioApiBundle\Service\CoreData\V1\Hydrator\PermissionsHydrator + # Services Pimcore\Bundle\StudioApiBundle\Service\GenericData\V1\Hydrator\AssetHydratorServiceInterface: class: Pimcore\Bundle\StudioApiBundle\Service\GenericData\V1\Hydrator\AssetHydratorService arguments: $assetHydratorLocator: '@generic_data_index.asset_hydrator.service_locator' + Pimcore\Bundle\StudioApiBundle\Service\CoreData\V1\Hydrator\AssetHydratorServiceInterface: + class: Pimcore\Bundle\StudioApiBundle\Service\CoreData\V1\Hydrator\AssetHydratorService + arguments: + $assetHydratorLocator: '@pimcore_model_data.asset_hydrator.service_locator' + + Pimcore\Bundle\StudioApiBundle\Service\AssetServiceInterface: + class: Pimcore\Bundle\StudioApiBundle\Service\AssetService + Pimcore\Bundle\StudioApiBundle\Service\AssetSearchServiceInterface: class: Pimcore\Bundle\StudioApiBundle\Service\AssetSearchService @@ -126,3 +142,8 @@ services: Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Asset\SearchResult\SearchResultItem\Text: '@Pimcore\Bundle\StudioApiBundle\Service\GenericData\V1\Hydrator\Asset\TextHydratorInterface' Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Asset\SearchResult\SearchResultItem\Unknown: '@Pimcore\Bundle\StudioApiBundle\Service\GenericData\V1\Hydrator\Asset\UnknownHydratorInterface' Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Asset\SearchResult\SearchResultItem\Video: '@Pimcore\Bundle\StudioApiBundle\Service\GenericData\V1\Hydrator\Asset\VideoHydratorInterface' + + pimcore_model_data.asset_hydrator.service_locator: + class: Symfony\Component\DependencyInjection\ServiceLocator + arguments: + - Pimcore\Model\Asset\Image: '@Pimcore\Bundle\StudioApiBundle\Service\CoreData\V1\Hydrator\Asset\ImageHydratorInterface' \ No newline at end of file diff --git a/src/Dto/Asset.php b/src/Dto/Asset.php index 3f69a8fac..aa85ed450 100644 --- a/src/Dto/Asset.php +++ b/src/Dto/Asset.php @@ -25,7 +25,7 @@ public function __construct( private readonly string $iconName, private readonly bool $hasChildren, private readonly string $type, - private readonly string $filename, + private string $filename, private readonly ?string $mimeType, private readonly array $metaData, private readonly bool $workflowWithPermissions, @@ -70,11 +70,16 @@ public function hasWorkflowWithPermissions(): bool return $this->workflowWithPermissions; } - public function getFilename(): ?string + public function getFilename(): string { return $this->filename; } + public function setFilename(string $filename): void + { + $this->filename = $filename; + } + public function getType(): string { return $this->type; diff --git a/src/Dto/Asset/Image.php b/src/Dto/Asset/Image.php index a3d0f6ff9..2259d09e7 100644 --- a/src/Dto/Asset/Image.php +++ b/src/Dto/Asset/Image.php @@ -18,16 +18,13 @@ use Pimcore\Bundle\StudioApiBundle\Dto\Asset; -class Image extends Asset +final class Image extends Asset { //use MetaData\EmbeddedMetaDataTrait; public function __construct( - private readonly string $format, private readonly int $width, private readonly int $height, - private readonly bool $vectorGraphic, - private readonly bool $animated, private readonly string $thumbnailPath, string $iconName, bool $hasChildren, diff --git a/src/Dto/Element.php b/src/Dto/Element.php index 3a5d30092..cd0ab53bc 100644 --- a/src/Dto/Element.php +++ b/src/Dto/Element.php @@ -23,7 +23,7 @@ class Element { public function __construct( private readonly int $id, - private readonly int $parentId, + private int $parentId, private readonly string $path, private readonly int $userOwner, private readonly int $userModification, @@ -45,6 +45,11 @@ public function getParentId(): int return $this->parentId; } + public function setParentId(int $parentId): void + { + $this->parentId = $parentId; + } + public function getPath(): string { return $this->path; diff --git a/src/Service/AssetService.php b/src/Service/AssetService.php new file mode 100644 index 000000000..e4cf40054 --- /dev/null +++ b/src/Service/AssetService.php @@ -0,0 +1,79 @@ +assetResolver->getById($data->getId()); + + if(!$asset) { + throw new NotFoundException('Asset not found'); + } + + $differences = $this->compare($asset, $data); + foreach($differences as $field => $difference) { + $setter = 'set' . $field; + $asset->$setter($difference['new']); + } + $this->synchronousProcessingService->enable(); + $asset->save(); + + return $asset; + } + + private function compare(CoreAsset $current, Asset $data): array + { + $fieldsToCheck = $this->fieldsToCheck(get_class_methods($data)); + $differences = []; + foreach($fieldsToCheck as $field) { + if($current->$field() !== $data->$field()) { + $fieldName = str_replace('get', '', $field); + $differences[$fieldName] = [ + 'current' => $current->$field(), + 'new' => $data->$field(), + ]; + } + } + + return $differences; + } + + private function fieldsToCheck(array $methods): array + { + $setters = array_filter($methods, static fn ($method) => str_starts_with($method, 'set')); + + return array_map(static fn ($setter) => str_replace('set', 'get', $setter), $setters); + } +} diff --git a/src/Service/AssetServiceInterface.php b/src/Service/AssetServiceInterface.php new file mode 100644 index 000000000..10254c4c6 --- /dev/null +++ b/src/Service/AssetServiceInterface.php @@ -0,0 +1,25 @@ +getWidth(), + $item->getHeight(), + '', + $this->iconService->getIconForAsset($item->getType(), $item->getMimeType()), + false, + $item->getType(), + $item->getFilename(), + $item->getMimeType(), + [], + false, + $item->getFullPath(), + $item->getId(), + $item->getParentId(), + $item->getPath(), + $item->getUserOwner(), + $item->getUserModification(), + $item->getLocked(), + $item->isLocked(), + $item->getCreationDate(), + $item->getModificationDate(), + $this->permissionsHydrator->hydrate($item) + ); + } +} diff --git a/src/Service/CoreData/V1/Hydrator/Asset/ImageHydratorInterface.php b/src/Service/CoreData/V1/Hydrator/Asset/ImageHydratorInterface.php new file mode 100644 index 000000000..16f1bd48f --- /dev/null +++ b/src/Service/CoreData/V1/Hydrator/Asset/ImageHydratorInterface.php @@ -0,0 +1,25 @@ +assetHydratorLocator->has($class)) { + return $this->assetHydratorLocator->get($class)->hydrate($item); + } + + return null; + //return new Asset($item->getId()); + } +} diff --git a/src/Service/CoreData/V1/Hydrator/AssetHydratorServiceInterface.php b/src/Service/CoreData/V1/Hydrator/AssetHydratorServiceInterface.php new file mode 100644 index 000000000..925228bb0 --- /dev/null +++ b/src/Service/CoreData/V1/Hydrator/AssetHydratorServiceInterface.php @@ -0,0 +1,30 @@ +getUserPermissions()); + + return new Permissions( + $permission->isList(), + $permission->isView(), + $permission->isPublish(), + $permission->isDelete(), + $permission->isRename(), + $permission->isCreate(), + $permission->isSettings(), + $permission->isVersions(), + $permission->isProperties() + ); + } +} diff --git a/src/Service/CoreData/V1/Hydrator/PermissionsHydratorInterface.php b/src/Service/CoreData/V1/Hydrator/PermissionsHydratorInterface.php new file mode 100644 index 000000000..b12695106 --- /dev/null +++ b/src/Service/CoreData/V1/Hydrator/PermissionsHydratorInterface.php @@ -0,0 +1,29 @@ +getWidth(), $item->getHeight(), - false, // TODO: Implement isVectorGraphic - false,// TODO: Implement isAnimated $item->getThumbnail(), $this->iconService->getIconForAsset($item->getType(), $item->getMimeType()), $item->isHasChildren(), diff --git a/src/Service/TokenService.php b/src/Service/TokenService.php index 3089802a9..3bddfa9ce 100644 --- a/src/Service/TokenService.php +++ b/src/Service/TokenService.php @@ -17,7 +17,9 @@ namespace Pimcore\Bundle\StudioApiBundle\Service; use Pimcore\Bundle\StaticResolverBundle\Models\Tool\TmpStoreResolverInterface; +use Pimcore\Bundle\StaticResolverBundle\Models\User\UserResolver; use Pimcore\Bundle\StudioApiBundle\Dto\Token\Info; +use Pimcore\Model\User; use Symfony\Component\Security\Core\Exception\TokenNotFoundException; use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface; @@ -28,6 +30,7 @@ final class TokenService implements TokenServiceInterface private const TMP_STORE_TAG_PLACEHOLDER = '{userId}'; public function __construct( + private readonly UserResolver $userResolver, private readonly TokenGeneratorInterface $tokenGenerator, private readonly TmpStoreResolverInterface $tmpStoreResolver, private readonly int $tokenLifetime, @@ -46,20 +49,16 @@ public function generateAndSaveToken(string $userIdentifier): string return $token; } - public function refreshToken(string $token): Info + public function getUserForToken(string $token): User { - $entry = $this->tmpStoreResolver->get($token); - if($entry === null) { - throw new TokenNotFoundException('Token not found'); - } - - $data = $entry->getData(); + $tokenInfo = $this->getInfoForToken($token); - if(!isset($data['username'])) { - throw new TokenNotFoundException('Token not found'); - } + return $this->userResolver->getByName($tokenInfo->getUsername()); + } - $tokenInfo = new Info($token, $data['username']); + public function refreshToken(string $token): Info + { + $tokenInfo = $this->getInfoForToken($token); $this->saveToken($tokenInfo); return $tokenInfo; @@ -90,4 +89,18 @@ private function getTmpStoreTag(string $userId): string self::TMP_STORE_TAG ); } + + private function getInfoForToken(string $token): Info + { + $entry = $this->tmpStoreResolver->get($token); + if($entry === null) { + throw new TokenNotFoundException('Token not found'); + } + + if(!isset($data['username'])) { + throw new TokenNotFoundException('Token not found'); + } + + return new Info($token, $entry->getData()['username']); + } } diff --git a/src/State/Asset/Processor.php b/src/State/Asset/Processor.php new file mode 100644 index 000000000..ae99278fa --- /dev/null +++ b/src/State/Asset/Processor.php @@ -0,0 +1,48 @@ +assetService->processAsset($data); + + return $this->assetHydratorService->hydrate($asset); + } +} diff --git a/src/State/AssetProvider.php b/src/State/Asset/Provider.php similarity index 94% rename from src/State/AssetProvider.php rename to src/State/Asset/Provider.php index 41d0f751d..1514e8a7d 100644 --- a/src/State/AssetProvider.php +++ b/src/State/Asset/Provider.php @@ -14,7 +14,7 @@ * @license http://www.pimcore.org/license GPLv3 and PCL */ -namespace Pimcore\Bundle\StudioApiBundle\State; +namespace Pimcore\Bundle\StudioApiBundle\State\Asset; use ApiPlatform\Metadata\CollectionOperationInterface; use ApiPlatform\Metadata\Operation; @@ -26,7 +26,7 @@ use Pimcore\Bundle\StudioApiBundle\Service\GenericData\V1\AssetQueryContextTrait; use Pimcore\Bundle\StudioApiBundle\Service\GenericData\V1\AssetQueryProviderInterface; -final readonly class AssetProvider implements ProviderInterface +final readonly class Provider implements ProviderInterface { use AssetQueryContextTrait; diff --git a/src/Util/ValueObjects/Permission.php b/src/Util/ValueObjects/Permission.php new file mode 100644 index 000000000..761c80e74 --- /dev/null +++ b/src/Util/ValueObjects/Permission.php @@ -0,0 +1,93 @@ + $value) { + if (property_exists($this, $key)) { + $this->$key = $value === 0; + } + } + } + + public function isList(): bool + { + return $this->list; + } + + public function isView(): bool + { + return $this->view; + } + + public function isPublish(): bool + { + return $this->publish; + } + + public function isDelete(): bool + { + return $this->delete; + } + + public function isRename(): bool + { + return $this->rename; + } + + public function isCreate(): bool + { + return $this->create; + } + + public function isSettings(): bool + { + return $this->settings; + } + + public function isVersions(): bool + { + return $this->versions; + } + + public function isProperties(): bool + { + return $this->properties; + } +}