Skip to content

Commit

Permalink
[Image Editor] Add endpoint to return original image stream (#642)
Browse files Browse the repository at this point in the history
* add endpoint to return original image stream

* Apply php-cs-fixer changes

---------

Co-authored-by: lukmzig <[email protected]>
  • Loading branch information
lukmzig and lukmzig authored Dec 16, 2024
1 parent 4fa65e6 commit 88a332c
Show file tree
Hide file tree
Showing 21 changed files with 165 additions and 24 deletions.
3 changes: 2 additions & 1 deletion src/Asset/Controller/Document/PreviewStreamController.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags;
use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions;
use Pimcore\Model\Asset\Document;
Expand Down Expand Up @@ -97,7 +98,7 @@ public function streamDocumentPreview(int $id): StreamedResponse
);

if (!$asset instanceof Document) {
throw new InvalidElementTypeException($asset->getType());
throw new InvalidElementTypeException($asset->getType(), ElementTypes::TYPE_ASSET);
}

return $this->documentService->getPreviewStream($asset);
Expand Down
98 changes: 98 additions & 0 deletions src/Asset/Controller/Image/StreamController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);

/**
* Pimcore
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - Pimcore Commercial License (PCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license http://www.pimcore.org/license GPLv3 and PCL
*/

namespace Pimcore\Bundle\StudioBackendBundle\Asset\Controller\Image;

use OpenApi\Attributes\Get;
use Pimcore\Bundle\StudioBackendBundle\Asset\Attribute\Response\Header\ContentDisposition;
use Pimcore\Bundle\StudioBackendBundle\Asset\Service\AssetServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\Asset\Service\BinaryServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\Controller\AbstractApiController;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\AccessDeniedException;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\ElementStreamResourceNotFoundException;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidElementTypeException;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotFoundException;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\UserNotFoundException;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Path\IdParameter;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\Content\MediaType;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags;
use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseHeaders;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Serializer\SerializerInterface;

/**
* @internal
*/
final class StreamController extends AbstractApiController
{
public function __construct(
private readonly AssetServiceInterface $assetService,
private readonly BinaryServiceInterface $binaryService,
private readonly SecurityServiceInterface $securityService,
SerializerInterface $serializer
) {
parent::__construct($serializer);
}

/**
* @throws AccessDeniedException
* @throws ElementStreamResourceNotFoundException
* @throws NotFoundException
* @throws InvalidElementTypeException
* @throws UserNotFoundException
*/
#[Route(
'/assets/{id}/image/stream',
name: 'pimcore_studio_api_stream_image',
methods: ['GET']
)]
#[IsGranted(UserPermissions::ASSETS->value)]
#[Get(
path: self::PREFIX . '/assets/{id}/image/stream',
operationId: 'asset_image_stream',
description: 'asset_image_stream_description',
summary: 'asset_image_stream_summary',
tags: [Tags::Assets->name]
)]
#[IdParameter(type: 'image')]
#[SuccessResponse(
description: 'asset_image_stream_success_response',
content: [new MediaType('image/*')],
headers: [new ContentDisposition(HttpResponseHeaders::INLINE_TYPE->value)]
)]
#[DefaultResponses([
HttpResponseCodes::BAD_REQUEST,
HttpResponseCodes::UNAUTHORIZED,
HttpResponseCodes::NOT_FOUND,
])]
public function streamOriginalImage(
int $id,
): StreamedResponse {
$asset = $this->assetService->getAssetElement(
$this->securityService->getCurrentUser(),
$id
);

return $this->binaryService->streamImage($asset);
}
}
10 changes: 9 additions & 1 deletion src/Asset/Encoder/TextEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\MaxFileSizeExceededException;
use Pimcore\Model\Asset\Text;
use Pimcore\Model\Element\ElementInterface;
use function sprintf;

final class TextEncoder implements TextEncoderInterface
{
Expand All @@ -32,7 +33,14 @@ final class TextEncoder implements TextEncoderInterface
public function encodeUTF8(ElementInterface $element): string
{
if (!$element instanceof Text) {
throw new InvalidElementTypeException('Element must be an instance of Text');
throw new InvalidElementTypeException(
sprintf(
'should have been (%s) but was (%s)',
Text::class,
$element->getType(),
),
'Asset'
);
}

if ($element->getFileSize() > self::MAX_FILE_SIZE) {
Expand Down
24 changes: 19 additions & 5 deletions src/Asset/Service/BinaryService.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidElementTypeException;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidThumbnailConfigurationException;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidThumbnailException;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseHeaders;
use Pimcore\Bundle\StudioBackendBundle\Util\Trait\StreamedResponseTrait;
use Pimcore\Messenger\AssetPreviewImageMessage;
Expand Down Expand Up @@ -59,19 +60,32 @@ public function downloadVideoByThumbnail(
string $thumbnailName
): StreamedResponse {
if (!$video instanceof Video) {
throw new InvalidElementTypeException($video->getType());
throw new InvalidElementTypeException($video->getType(), ElementTypes::TYPE_ASSET);
}

return $this->getVideoByThumbnail($video, $thumbnailName, HttpResponseHeaders::ATTACHMENT_TYPE->value);
}

/**
* @throws ElementStreamResourceNotFoundException|InvalidElementTypeException
*/
public function streamImage(
Asset $image
): StreamedResponse {
if (!$image instanceof Image) {
throw new InvalidElementTypeException($image->getType(), ElementTypes::TYPE_ASSET);
}

return $this->getStreamedResponse($image, HttpResponseHeaders::INLINE_TYPE->value);
}

/**
* @throws InvalidElementTypeException|InvalidThumbnailException
*/
public function streamPreviewImageThumbnail(Asset $image): StreamedResponse
{
if (!$image instanceof Image) {
throw new InvalidElementTypeException($image->getType());
throw new InvalidElementTypeException($image->getType(), ElementTypes::TYPE_ASSET);
}

return $this->getStreamedResponse(
Expand All @@ -85,7 +99,7 @@ public function streamImageThumbnailFromConfig(
ImageDownloadConfigParameter $configParameter
): StreamedResponse {
if (!$image instanceof Image) {
throw new InvalidElementTypeException($image->getType());
throw new InvalidElementTypeException($image->getType(), ElementTypes::TYPE_ASSET);
}

return $this->getStreamedResponse(
Expand All @@ -105,7 +119,7 @@ public function streamVideoByThumbnail(
string $thumbnailName
): StreamedResponse {
if (!$video instanceof Video) {
throw new InvalidElementTypeException($video->getType());
throw new InvalidElementTypeException($video->getType(), ElementTypes::TYPE_ASSET);
}

return $this->getVideoByThumbnail($video, $thumbnailName, HttpResponseHeaders::INLINE_TYPE->value);
Expand All @@ -122,7 +136,7 @@ public function streamVideoImageThumbnail(
VideoImageStreamConfigParameter $imageConfig
): StreamedResponse {
if (!$video instanceof Video) {
throw new InvalidElementTypeException($video->getType());
throw new InvalidElementTypeException($video->getType(), ElementTypes::TYPE_ASSET);
}
$this->thumbnailService->validateCustomVideoThumbnailConfig($imageConfig);

Expand Down
7 changes: 7 additions & 0 deletions src/Asset/Service/BinaryServiceInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ public function downloadVideoByThumbnail(
string $thumbnailName
): StreamedResponse;

/**
* @throws ElementStreamResourceNotFoundException|InvalidElementTypeException
*/
public function streamImage(
Asset $image
): StreamedResponse;

/**
* @throws InvalidElementTypeException|InvalidThumbnailException
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Dependency/Controller/Element/CollectionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public function __construct(
tags: [Tags::Dependencies->name]
)]
#[ElementTypeParameter]
#[IdParameter('element')]
#[IdParameter]
#[PageParameter]
#[PageSizeParameter]
#[DependencyModeParameter]
Expand Down
3 changes: 2 additions & 1 deletion src/Exception/Api/ElementProcessingNotCompletedException.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

namespace Pimcore\Bundle\StudioBackendBundle\Exception\Api;

use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
use function sprintf;

Expand All @@ -24,7 +25,7 @@
*/
final class ElementProcessingNotCompletedException extends AbstractApiException
{
public function __construct(int $id, string $type = 'Element')
public function __construct(int $id, string $type = ElementTypes::TYPE_ELEMENT)
{
parent::__construct(
HttpResponseCodes::NOT_COMPLETED->value,
Expand Down
3 changes: 2 additions & 1 deletion src/Exception/Api/ElementStreamResourceNotFoundException.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

namespace Pimcore\Bundle\StudioBackendBundle\Exception\Api;

use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
use function sprintf;

Expand All @@ -24,7 +25,7 @@
*/
final class ElementStreamResourceNotFoundException extends AbstractApiException
{
public function __construct(int $id, string $type = 'Element')
public function __construct(int $id, string $type = ElementTypes::TYPE_ELEMENT)
{
parent::__construct(
HttpResponseCodes::NOT_FOUND->value,
Expand Down
8 changes: 5 additions & 3 deletions src/Exception/Api/InvalidElementTypeException.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

namespace Pimcore\Bundle\StudioBackendBundle\Exception\Api;

use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
use function sprintf;

Expand All @@ -24,13 +25,14 @@
*/
final class InvalidElementTypeException extends AbstractApiException
{
public function __construct(string $type)
public function __construct(string $subType, string $elementType = ElementTypes::TYPE_ELEMENT)
{
parent::__construct(
HttpResponseCodes::BAD_REQUEST->value,
sprintf(
'Invalid element type: %s',
$type
'Invalid %s type: %s',
$elementType,
$subType
)
);
}
Expand Down
3 changes: 2 additions & 1 deletion src/Note/Controller/Element/CollectionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions;
use Pimcore\Bundle\StudioBackendBundle\Util\Trait\PaginatedResponseTrait;
Expand Down Expand Up @@ -78,7 +79,7 @@ public function __construct(
tags: [Tags::Notes->name]
)]
#[ElementTypeParameter]
#[IdParameter(type: 'element')]
#[IdParameter(type: ElementTypes::TYPE_ELEMENT)]
#[PageParameter]
#[PageSizeParameter(50)]
#[NoteSortByParameter]
Expand Down
3 changes: 2 additions & 1 deletion src/Note/Controller/Element/CreateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions;
use Symfony\Component\HttpFoundation\JsonResponse;
Expand Down Expand Up @@ -66,7 +67,7 @@ public function __construct(
tags: [Tags::Notes->name]
)]
#[ElementTypeParameter]
#[IdParameter(type: 'element')]
#[IdParameter(type: ElementTypes::TYPE_ELEMENT)]
#[CreateNoteRequestBody]
#[SuccessResponse(
description: 'note_element_create_success_response',
Expand Down
2 changes: 1 addition & 1 deletion src/Property/Controller/Element/CollectionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function __construct(
tags: [Tags::Properties->value]
)]
#[ElementTypeParameter]
#[IdParameter(type: 'element')]
#[IdParameter]
#[SuccessResponse(
description: 'property_get_collection_for_element_by_type_and_id_success_response',
content: new ItemsJson(ElementProperty::class)
Expand Down
2 changes: 1 addition & 1 deletion src/Schedule/Controller/Element/CollectionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function __construct(
tags: [Tags::Schedule->name]
)]
#[ElementTypeParameter]
#[IdParameter(type: 'element')]
#[IdParameter]
#[SuccessResponse(
description: 'schedule_get_collection_for_element_by_type_and_id_success_response',
content: new ItemsJson(Schedule::class)
Expand Down
2 changes: 1 addition & 1 deletion src/Schedule/Controller/Element/CreateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function __construct(
tags: [Tags::Schedule->name]
)]
#[ElementTypeParameter]
#[IdParameter(type: 'element')]
#[IdParameter]
#[SuccessResponse(
description: 'schedule_create_for_element_by_type_and_id_success_response',
content: new JsonContent(ref: Schedule::class)
Expand Down
2 changes: 1 addition & 1 deletion src/Schedule/Controller/Element/UpdateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function __construct(
tags: [Tags::Schedule->name]
)]
#[ElementTypeParameter]
#[IdParameter(type: 'element')]
#[IdParameter]
#[ElementScheduleRequestBody]
#[SuccessResponse(
description: 'schedule_update_for_element_by_type_and_id_success_response',
Expand Down
2 changes: 1 addition & 1 deletion src/Tag/Controller/Element/AssignController.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public function __construct(
tags: [Tags::TagsForElement->value]
)]
#[ElementTypeParameter]
#[IdParameter(type: 'element')]
#[IdParameter]
#[IdParameter(type: 'tag', name: 'tagId')]
#[DefaultResponses([
HttpResponseCodes::UNAUTHORIZED,
Expand Down
2 changes: 1 addition & 1 deletion src/Tag/Controller/Element/CollectionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public function __construct(
tags: [Tags::TagsForElement->value]
)]
#[ElementTypeParameter]
#[IdParameter(type: 'element')]
#[IdParameter]
#[SuccessResponse(
description: 'tag_get_collection_for_element_by_type_and_id_success_response',
content: new CollectionJson(new TagCollection())
Expand Down
2 changes: 1 addition & 1 deletion src/Tag/Controller/Element/UnassignController.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function __construct(
tags: [Tags::TagsForElement->value]
)]
#[ElementTypeParameter]
#[IdParameter(type: 'element')]
#[IdParameter]
#[IdParameter(type: 'tag', name: 'tagId')]
#[DefaultResponses([
HttpResponseCodes::UNAUTHORIZED,
Expand Down
2 changes: 2 additions & 0 deletions src/Util/Constant/ElementTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

public const TYPE_DATA_OBJECT = 'data-object';

public const TYPE_ELEMENT = 'element';

public const TYPE_OBJECT = 'object';

public const TYPE_VARIANT = 'variant';
Expand Down
Loading

0 comments on commit 88a332c

Please sign in to comment.