Skip to content

Commit

Permalink
[Task][New feature] Object data preview (#675)
Browse files Browse the repository at this point in the history
* First draft for preview

* Apply php-cs-fixer changes

* Refinement

* Apply php-cs-fixer changes

* Add permission

* Split line

* Add declare strict

* Apply php-cs-fixer changes

* Make internal
  • Loading branch information
mattamon authored Jan 22, 2025
1 parent 484fcc5 commit b6d4375
Show file tree
Hide file tree
Showing 10 changed files with 336 additions and 0 deletions.
4 changes: 4 additions & 0 deletions config/data_objects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ services:
Pimcore\Bundle\StudioBackendBundle\DataObject\Service\SelectOptionsServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\SelectOptionsService

Pimcore\Bundle\StudioBackendBundle\DataObject\Service\PreviewUrlServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\PreviewUrlService
arguments: [ '@request_stack' ]

Pimcore\Bundle\StudioBackendBundle\DataObject\Service\PathFormatterServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\PathFormatterService

Expand Down
34 changes: 34 additions & 0 deletions src/DataObject/Attribute/Request/PreviewRequestBody.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?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\DataObject\Attribute\Request;

use Attribute;
use OpenApi\Attributes\JsonContent;
use OpenApi\Attributes\RequestBody;
use Pimcore\Bundle\StudioBackendBundle\DataObject\MappedParameter\PreviewParameter;

#[Attribute(Attribute::TARGET_METHOD)]
final class PreviewRequestBody extends RequestBody
{
public function __construct()
{
parent::__construct(
required: true,
content: new JsonContent(ref: PreviewParameter::class)
);
}
}
62 changes: 62 additions & 0 deletions src/DataObject/Controller/PreviewController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?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\DataObject\Controller;

use OpenApi\Attributes\Post;
use Pimcore\Bundle\StudioBackendBundle\Controller\AbstractApiController;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Attribute\Request\PreviewRequestBody;
use Pimcore\Bundle\StudioBackendBundle\DataObject\MappedParameter\PreviewParameter;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\PreviewUrlServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\RedirectResponse as RedirectResponseAttribute;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Serializer\SerializerInterface;

final class PreviewController extends AbstractApiController
{
public function __construct(
SerializerInterface $serializer,
private PreviewUrlServiceInterface $previewUrlService
) {
parent::__construct($serializer);
}

#[Route('/data-objects/preview', name: 'pimcore_studio_api_data_objects_preview', methods: ['POST'])]
#[IsGranted(UserPermissions::DATA_OBJECTS->value)]
#[Post(
path: self::PREFIX . '/data-objects/preview',
operationId: 'data_object_preview_by_id',
description: 'data_object_preview_by_id_description',
summary: 'data_object_preview_by_id_summary',
tags: [Tags::DataObjects->value]
)]
#[PreviewRequestBody]
#[RedirectResponseAttribute(description: 'data_object_preview_by_id_success_response')]
#[DefaultResponses([
HttpResponseCodes::REDIRECT,
])]
public function preview(#[MapRequestPayload] PreviewParameter $previewParameter): RedirectResponse
{
return new RedirectResponse($this->previewUrlService->getPreviewUrl($previewParameter));
}
}
46 changes: 46 additions & 0 deletions src/DataObject/MappedParameter/PreviewParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?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\DataObject\MappedParameter;

use OpenApi\Attributes\Property;
use OpenApi\Attributes\Schema;

#[Schema(
title: 'Data Object Preview Parameters',
required: ['id'],
type: 'object'
)]
final readonly class PreviewParameter
{
public function __construct(
#[Property(description: 'ID', type: 'integer', example: 83)]
private int $id,
#[Property(description: 'Site', type: 'integer', default: 0, example: 1)]
private int $site = 0
) {
}

public function getId(): int
{
return $this->id;
}

public function getSite(): int
{
return $this->site;
}
}
106 changes: 106 additions & 0 deletions src/DataObject/Service/PreviewUrlService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?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\DataObject\Service;

use Exception;
use Pimcore\Bundle\StaticResolverBundle\Models\Element\ServiceResolverInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\MappedParameter\PreviewParameter;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotFoundException;
use Pimcore\Bundle\StudioBackendBundle\Util\Trait\RequestTrait;
use Pimcore\Model\DataObject\ClassDefinition\PreviewGeneratorInterface;
use Pimcore\Model\DataObject\Concrete;
use Symfony\Component\HttpFoundation\RequestStack;

/**
* @interal
*/
final readonly class PreviewUrlService implements PreviewUrlServiceInterface
{
use RequestTrait;

public function __construct(
private RequestStack $requestStack,
private PreviewGeneratorInterface $defaultPreviewGenerator,
private ServiceResolverInterface $serviceResolver
) {
}

/**
* @throws Exception|NotFoundException
*/
public function getPreviewUrl(PreviewParameter $previewParameter): string
{
$session = $this->getCurrentSession($this->requestStack);

$dataObject = $this->serviceResolver->getElementFromSession(
'object',
$previewParameter->getId(),
$session->getId()
);

if (!$dataObject instanceof Concrete) {
throw new NotFoundException('Data Object', $previewParameter->getId());
}

$url = $this->getPreviewGenerator($dataObject)?->generatePreviewUrl(
$dataObject,
['preview' => true, 'context' => $this]
);

if (!$url) {
throw new InvalidArgumentException('Could not generate preview url');
}

return $this->buildRedirectUrl($url, $previewParameter);
}

/**
* @throws Exception
*/
private function getPreviewGenerator(Concrete $dataObject): ?PreviewGeneratorInterface
{
$previewService = $dataObject->getClass()->getPreviewGenerator();

if (!$previewService && $dataObject->getClass()->getLinkGenerator()) {
$previewService = $this->defaultPreviewGenerator;
}

return $previewService;
}

private function buildRedirectUrl(string $url, PreviewParameter $previewParameter): string
{
// replace all remaining % signs
$url = str_replace('%', '%25', $url);
$urlParts = parse_url($url);

$redirectParameters = array_filter([
'pimcore_object_preview' => $previewParameter->getId(),
'site' => $previewParameter->getSite(),
'dc' => time(),
]);

$previewUrl = $urlParts['path'] . '?' . http_build_query($redirectParameters);

if (isset($urlParts['query'])) {
$previewUrl .= '&' . $urlParts['query'];
}

return $previewUrl;
}
}
27 changes: 27 additions & 0 deletions src/DataObject/Service/PreviewUrlServiceInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?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\DataObject\Service;

use Pimcore\Bundle\StudioBackendBundle\DataObject\MappedParameter\PreviewParameter;

/**
* @interal
*/
interface PreviewUrlServiceInterface
{
public function getPreviewUrl(PreviewParameter $previewParameter): string;
}
46 changes: 46 additions & 0 deletions src/OpenApi/Attribute/Response/RedirectResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?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\OpenApi\Attribute\Response;

use Attribute;
use OpenApi\Attributes\Header;
use OpenApi\Attributes\Response;
use OpenApi\Attributes\Schema;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;

#[Attribute(Attribute::TARGET_METHOD)]
final class RedirectResponse extends Response
{
public function __construct(string $description = 'Redirect')
{
parent::__construct(
response: HttpResponseCodes::REDIRECT->value,
description: $description,
headers: [
new Header(
header: 'Location',
description: 'Redirect location',
required: true,
schema: new Schema(
type: 'string',
format: 'uri'
)
),
]
);
}
}
1 change: 1 addition & 0 deletions src/Util/Constant/HttpResponseCodes.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum HttpResponseCodes: int
case CREATED = 201;
case NOT_COMPLETED = 202;
case MULTI_STATUS = 207;
case REDIRECT = 302;
case BAD_REQUEST = 400;
case UNAUTHORIZED = 401;
case FORBIDDEN = 403;
Expand Down
6 changes: 6 additions & 0 deletions src/Util/Trait/RequestTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotAuthorizedException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

/**
* @internal
Expand Down Expand Up @@ -57,6 +58,11 @@ private function getCurrentRequest(RequestStack $requestStack): Request
return $request;
}

private function getCurrentSession(RequestStack $requestStack): SessionInterface
{
return $this->getCurrentRequest($requestStack)->getSession();
}

private function removeBearerPrefix(string $token): string
{
return str_replace(self::BEARER_PREFIX, '', $token);
Expand Down
4 changes: 4 additions & 0 deletions translations/studio_api_docs.en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,10 @@ data_object_get_grid_description: |
</ul>
data_object_get_grid_success_response: Data object grid data
data_object_get_grid_summary: Get data object data for grid
data_object_preview_by_id_description: |
Preview data object by ID and site. Data Object must be stored in the session first to be able to preview it.
data_object_preview_by_id_success_response: Redirect to preview URL
data_object_preview_by_id_summary: Preview data object by ID ans site
asset_delete_grid_configuration_by_configurationId_description: |
Delete grid configuration for a specific folder and given configuration ID <strong>{configurationId}</strong>
asset_delete_grid_configuration_by_configurationId_summary: Delete grid configuration for a specific folder and given configuration ID
Expand Down

0 comments on commit b6d4375

Please sign in to comment.