Skip to content

Commit

Permalink
Merge branch '1.x' into 662-task-reports-export-csv
Browse files Browse the repository at this point in the history
# Conflicts:
#	translations/studio_api_docs.en.yaml
  • Loading branch information
mcop1 committed Jan 21, 2025
2 parents 14cafb1 + 1d4bd63 commit d76509b
Show file tree
Hide file tree
Showing 57 changed files with 1,141 additions and 251 deletions.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
"lcobucci/jwt": "^5.3"
},
"require-dev": {
"pimcore/admin-ui-classic-bundle": "^v1.3",
"pimcore/admin-ui-classic-bundle": "2.x-dev",
"codeception/codeception": "^5.0.10",
"roave/security-advisories": "dev-latest",
"codeception/phpunit-wrapper": "^9",
"codeception/module-asserts": "^2",
"codeception/module-symfony": "^3.1.1",
"phpstan/phpstan": "1.10.5",
"phpstan/phpstan": "1.12.15",
"phpstan/phpstan-symfony": "^1.2.20",
"phpunit/phpunit": "10.2.7",
"nyholm/psr7": "^1",
Expand Down
9 changes: 9 additions & 0 deletions config/data_objects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,28 @@ services:
Pimcore\Bundle\StudioBackendBundle\DataObject\Service\SelectOptionsServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\SelectOptionsService

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

#
# Legacy Services
#
Pimcore\Bundle\StudioBackendBundle\DataObject\Legacy\ApplyChangesHelperInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Legacy\ApplyChangesHelper

Pimcore\Bundle\StudioBackendBundle\DataObject\Legacy\PathFormatterHelperInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Legacy\PathFormatterHelper


#
# Hydrator
#
Pimcore\Bundle\StudioBackendBundle\DataObject\Hydrator\SelectOptionHydratorInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Hydrator\SelectOptionHydrator

Pimcore\Bundle\StudioBackendBundle\DataObject\Hydrator\FormatedPathHydratorInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Hydrator\FormatedPathHydrator


#
# Data Adapters
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: '3.0'
services:
php-studio-backend-bundle:
image: pimcore/pimcore:php8.2-debug-latest
image: pimcore/pimcore:php8.3-debug-latest
environment:
PHP_IDE_CONFIG: serverName=localhost
volumes:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* @license http://www.pimcore.org/license GPLv3 and PCL
*/

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

use OpenApi\Attributes\Delete;
use Pimcore\Bundle\StudioBackendBundle\Asset\Service\AssetServiceInterface;
Expand All @@ -36,7 +36,7 @@
/**
* @internal
*/
final class ThumbnailImageClearController extends AbstractApiController
final class ThumbnailClearController extends AbstractApiController
{
use PaginatedResponseTrait;

Expand All @@ -51,16 +51,16 @@ public function __construct(
* @throws UserNotFoundException
*/
#[Route(
path: '/assets/{id}/image/thumbnail/clear',
name: 'pimcore_studio_api_clear_image_thumbnail',
path: '/assets/{id}/thumbnail/clear',
name: 'pimcore_studio_api_clear_thumbnail',
methods: ['DELETE']
)]
#[IsGranted(UserPermissions::ASSETS->value)]
#[Delete(
path: self::PREFIX . '/assets/{id}/image/thumbnail/clear',
operationId: 'asset_image_clear_thumbnail',
description: 'asset_image_clear_thumbnail_description',
summary: 'asset_image_clear_thumbnail_summary',
path: self::PREFIX . '/assets/{id}/thumbnail/clear',
operationId: 'asset_clear_thumbnail',
description: 'asset_clear_thumbnail_description',
summary: 'asset_clear_thumbnail_summary',
tags: [Tags::Assets->value]
)]
#[IdParameter(type: ElementTypes::TYPE_ASSET)]
Expand All @@ -70,7 +70,7 @@ public function __construct(
HttpResponseCodes::NOT_FOUND,
HttpResponseCodes::INTERNAL_SERVER_ERROR,
])]
public function clearImageThumbnails(int $id): Response
public function clearThumbnails(int $id): Response
{
$this->assetService->clearThumbnails($id);

Expand Down
2 changes: 2 additions & 0 deletions src/DataIndex/Hydrator/DataObject/FolderHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public function hydrate(Folder $item): DataObjectFolder
$item->getFullPath(),
$this->permissionsHydrator->hydrate($item->getPermissions()),
$item->getIndex(),
$item->getChildrenSortBy(),
$item->getChildrenSortOrder(),
$item->getId(),
$item->getParentId(),
$item->getPath(),
Expand Down
2 changes: 2 additions & 0 deletions src/DataIndex/Hydrator/DataObjectHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public function hydrate(DataObjectSearchResultItem $dataObject): DataObject
$dataObject->getFullPath(),
$this->permissionsHydrator->hydrate($dataObject->getPermissions()),
$dataObject->getIndex(),
$dataObject->getChildrenSortBy(),
$dataObject->getChildrenSortOrder(),
$dataObject->getId(),
$dataObject->getParentId(),
$dataObject->getPath(),
Expand Down
52 changes: 52 additions & 0 deletions src/DataObject/Attribute/Request/PathFormatterRequestBody.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?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\Property;
use OpenApi\Attributes\RequestBody;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Property\SingleInteger;

#[Attribute(Attribute::TARGET_METHOD)]
final class PathFormatterRequestBody extends RequestBody
{
public function __construct()
{
parent::__construct(
required: true,
content: new JsonContent(
required: ['objectId', 'targets', 'context'],
properties: [
new SingleInteger('objectId'),
new Property(
property: 'targets',
type: 'object',
example: '{"object_10":{"id":10,"type":"object","label":"/Product
Data/Cars/jaguar/E-Type/coupé","path":"/Product Data/Cars/jaguar/E-Type/coupé",
"nicePathKey":"object_10"}}'
),
new Property(
property: 'context',
type: 'object',
example: '{"containerType":"object","fieldname":"select","objectId":40,"layoutId":"0"}'
),
]
)
);
}
}
87 changes: 87 additions & 0 deletions src/DataObject/Controller/PathFormatterController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?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\PathFormatterRequestBody;
use Pimcore\Bundle\StudioBackendBundle\DataObject\MappedParameter\PathFormatterParameter;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Schema\FormatedPath;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\PathFormatterServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotFoundException;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Property\GenericCollection;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\Content\CollectionJson;
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\UserPermissions;
use Pimcore\Bundle\StudioBackendBundle\Util\Trait\PaginatedResponseTrait;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Serializer\SerializerInterface;
use function count;

/**
* @internal
*/
final class PathFormatterController extends AbstractApiController
{
use PaginatedResponseTrait;

public function __construct(
SerializerInterface $serializer,
private readonly PathFormatterServiceInterface $formatterService,
) {
parent::__construct($serializer);
}

/**
* @throws NotFoundException
*/
#[Route(
path: '/data-objects/format-path',
name: 'pimcore_studio_api_get_data_object_format_path',
methods: ['POST']
)]
#[IsGranted(UserPermissions::DATA_OBJECTS->value)]
#[Post(
path: self::PREFIX . '/data-objects/format-path',
operationId: 'data_object_format_path',
description: 'data_object_format_path_description',
summary: 'data_object_format_path_summary',
tags: [Tags::DataObjects->value]
)]
#[PathFormatterRequestBody]
#[SuccessResponse(
description: 'data_object_format_path_success_response',
content: new CollectionJson(new GenericCollection(FormatedPath::class))
)]
#[DefaultResponses([
])]
public function formatPath(#[MapRequestPayload] PathFormatterParameter $pathFormatterParameter): JsonResponse
{
$formattedPaths = $this->formatterService->formatPath($pathFormatterParameter);

return $this->getPaginatedCollection(
$this->serializer,
$formattedPaths,
count($formattedPaths),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@
namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Adapter;

use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\ConcreteObjectResolverInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataNormalizerInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\FieldContextData;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterLoaderInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Util\Trait\RelationDataTrait;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Util\Trait\RelationMetadataTrait;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;
use Pimcore\Model\DataObject\ClassDefinition\Data;
use Pimcore\Model\DataObject\ClassDefinition\Data\AdvancedManyToManyObjectRelation;
use Pimcore\Model\DataObject\Concrete;
Expand All @@ -31,8 +35,11 @@
* @internal
*/
#[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)]
final readonly class AdvancedManyToManyObjectRelationAdapter implements SetterDataInterface
final readonly class AdvancedManyToManyObjectRelationAdapter implements SetterDataInterface, DataNormalizerInterface
{
use RelationDataTrait;
use RelationMetadataTrait;

public function __construct(
private ConcreteObjectResolverInterface $concreteObjectResolver
) {
Expand All @@ -54,6 +61,26 @@ public function getDataForSetter(
return $this->buildRelationsMetadata($relationData, $fieldDefinition);
}

public function normalize(
mixed $value,
Data $fieldDefinition
): ?array {
if (!is_array($value)) {
return null;
}

$normalizedData = [];
foreach ($value as $relation) {
if (!$relation instanceof ObjectMetadata) {
continue;
}

$normalizedData[] = $this->getAdvancedRelationElementData($relation);
}

return $normalizedData;
}

private function buildRelationsMetadata(array $relationData, Data $fieldDefinition): array
{
if (!$fieldDefinition instanceof AdvancedManyToManyObjectRelation) {
Expand All @@ -62,39 +89,21 @@ private function buildRelationsMetadata(array $relationData, Data $fieldDefiniti

$relationsMetadata = [];
foreach ($relationData as $relation) {
$object = $this->concreteObjectResolver->getById($relation['id']);
if ($object && $object->getClassName() === $fieldDefinition->getAllowedClassId()) {
$relationsMetadata[] = $this->createObjectMetadata($object, $fieldDefinition, $relation);
if (empty($relation['element']['id']) || $relation['element']['type'] !== ElementTypes::TYPE_OBJECT) {
continue;
}
}

return $relationsMetadata;
}

private function createObjectMetadata(
Concrete $object,
AdvancedManyToManyObjectRelation $fieldDefinition,
array $relation,
): ObjectMetadata {
$metaData = new ObjectMetadata(
$fieldDefinition->getName(),
$fieldDefinition->getColumnKeys(),
$object
);
$metaData->_setOwner($object);
$metaData->_setOwnerFieldname($fieldDefinition->getName());

foreach ($fieldDefinition->getColumns() as $column) {
$setter = 'set' . ucfirst($column['key']);
$value = $relation[$column['key']] ?? null;

if ($column['type'] === 'multiselect' && is_array($value)) {
$value = implode(',', $value);
$object = $this->concreteObjectResolver->getById($relation['element']['id']);
if ($object && $object->getClassName() === $fieldDefinition->getAllowedClassId()) {
$fieldName = $fieldDefinition->getName();
$relationsMetadata[] = $this->addRelationMetadata(
$object,
$relation['data'],
new ObjectMetadata($fieldName, $fieldDefinition->getColumnKeys(), $object)
);
}

$metaData->$setter($value);
}

return $metaData;
return $relationsMetadata;
}
}
Loading

0 comments on commit d76509b

Please sign in to comment.