diff --git a/config/assets.yaml b/config/assets.yaml index a967cb079..a6d69cf0d 100644 --- a/config/assets.yaml +++ b/config/assets.yaml @@ -77,7 +77,6 @@ services: Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\AssetCloneHandler: ~ Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\AssetUploadHandler: ~ Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\ExportDataCollectionHandler: ~ - Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\ExportFolderDataCollectionHandler: ~ Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\ZipDownloadHandler: ~ Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\ZipUploadHandler: ~ diff --git a/config/data_objects.yaml b/config/data_objects.yaml index 22fec0efd..bebb04e96 100644 --- a/config/data_objects.yaml +++ b/config/data_objects.yaml @@ -206,7 +206,6 @@ services: Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Handler\CloneHandler: ~ Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Handler\ExportDataCollectionHandler: ~ - Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Handler\ExportFolderDataCollectionHandler: ~ # # Event Subscriber diff --git a/config/elements.yaml b/config/elements.yaml index c69d9aee5..410333bf5 100644 --- a/config/elements.yaml +++ b/config/elements.yaml @@ -62,6 +62,7 @@ services: Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Handler\PatchHandler: ~ Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Handler\PatchFolderHandler: ~ Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Handler\ElementUsageReplaceHandler: ~ + Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Handler\RefreshJobHandler: ~ # # Event Subscriber diff --git a/config/export.yaml b/config/export.yaml index 68f1343c0..8265dc63e 100644 --- a/config/export.yaml +++ b/config/export.yaml @@ -23,6 +23,8 @@ services: arguments: $xlsxExportService: '@Pimcore\Bundle\StudioBackendBundle\Export\Service\XlsxExportService' + Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Handler\FolderCollectionHandler: ~ + # # Services # @@ -46,4 +48,6 @@ services: Pimcore\Bundle\StudioBackendBundle\Export\EventSubscriber\XlsxCreationSubscriber: arguments: - $xlsxExportService: '@Pimcore\Bundle\StudioBackendBundle\Export\Service\XlsxExportService' \ No newline at end of file + $xlsxExportService: '@Pimcore\Bundle\StudioBackendBundle\Export\Service\XlsxExportService' + + Pimcore\Bundle\StudioBackendBundle\Export\EventSubscriber\FolderCollectionSubscriber: ~ \ No newline at end of file diff --git a/config/pimcore/execution_engine.yaml b/config/pimcore/execution_engine.yaml index 2a5a67b21..8edeb11a4 100644 --- a/config/pimcore/execution_engine.yaml +++ b/config/pimcore/execution_engine.yaml @@ -14,21 +14,21 @@ framework: Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\AssetCloneMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\AssetUploadMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage: pimcore_generic_execution_engine - Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ExportFolderDataCollectionMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ZipDownloadMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ZipUploadMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\CloneMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage: pimcore_generic_execution_engine - Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\ExportFolderDataCollectionMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Document\ExecutionEngine\AutomationAction\Messenger\Messages\CloneMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\BatchDeleteMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\ElementDeleteMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\PatchFolderMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\PatchMessage: pimcore_generic_execution_engine + Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\RefreshJobMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\RecycleBinMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\RewriteRefMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\CsvCreationMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\XlsxCreationMessage: pimcore_generic_execution_engine + Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\FolderCollectionMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Tag\ExecutionEngine\AutomationAction\Messenger\Messages\BatchTagOperationMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\RecycleBin\ExecutionEngine\Messages\DeleteItemsMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\RecycleBin\ExecutionEngine\Messages\RestoreItemsMessage: pimcore_generic_execution_engine diff --git a/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php b/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php index 9713a47c0..a78afd138 100644 --- a/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php +++ b/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php @@ -14,7 +14,6 @@ namespace Pimcore\Bundle\StudioBackendBundle\Asset\Attribute\Request; use Attribute; -use OpenApi\Attributes\Items; use OpenApi\Attributes\JsonContent; use OpenApi\Attributes\Property; use OpenApi\Attributes\RequestBody; @@ -39,23 +38,13 @@ public function __construct() properties: [ new Property( property: 'data', - type: 'array', - items: new Items( - required: ['folderId'], - properties: [ - new Property( - property: 'folderId', - description: 'Folder ID', - type: 'integer', - example: 83 - ), - new UpdateIntegerProperty('parentId'), - new UpdateStringProperty('key'), - new UpdateStringProperty('locked'), - new CustomMetadata(PatchCustomMetadata::class), - ], - type: 'object', - ), + properties: [ + new UpdateIntegerProperty('parentId'), + new UpdateStringProperty('key'), + new UpdateStringProperty('locked'), + new CustomMetadata(PatchCustomMetadata::class), + ], + type: 'object', ), new Property( property: 'filters', diff --git a/src/Asset/Controller/PatchFolderController.php b/src/Asset/Controller/PatchFolderController.php index e9ad1c9cb..5bb332097 100644 --- a/src/Asset/Controller/PatchFolderController.php +++ b/src/Asset/Controller/PatchFolderController.php @@ -18,6 +18,7 @@ use Pimcore\Bundle\StudioBackendBundle\Controller\AbstractApiController; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\UserNotFoundException; use Pimcore\Bundle\StudioBackendBundle\MappedParameter\PatchFolderParameter; +use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Path\IdParameter; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\Content\IdJson; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\CreatedResponse; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses; @@ -38,6 +39,8 @@ */ final class PatchFolderController extends AbstractApiController { + private const string ROUTE = '/assets/folder/{id}'; + public function __construct( SerializerInterface $serializer, private readonly PatchServiceInterface $patchService, @@ -49,15 +52,16 @@ public function __construct( /** * @throws UserNotFoundException */ - #[Route('/assets/folder', name: 'pimcore_studio_api_patch_asset_folder', methods: ['PATCH'])] + #[Route(self::ROUTE, name: 'pimcore_studio_api_patch_asset_folder', methods: ['PATCH'])] #[IsGranted(UserPermissions::ASSETS->value)] #[Patch( - path: self::PREFIX . '/assets/folder', + path: self::PREFIX . self::ROUTE, operationId: 'asset_patch_folder_by_id', description: 'asset_patch_folder_by_id_description', summary: 'asset_patch_folder_by_id_summary', tags: [Tags::Assets->name] )] + #[IdParameter(type: ElementTypes::TYPE_FOLDER, name: 'id')] #[PatchAssetFolderRequestBody] #[CreatedResponse( description: 'asset_patch_by_id_created_response', @@ -67,10 +71,12 @@ public function __construct( HttpResponseCodes::NOT_FOUND, HttpResponseCodes::UNAUTHORIZED, ])] - public function assetPatchFolderById(#[MapRequestPayload] PatchFolderParameter $patchFolderParameter): Response - { - + public function assetPatchFolderById( + int $id, + #[MapRequestPayload] PatchFolderParameter $patchFolderParameter + ): Response { $jobRunId = $this->patchService->patchFolder( + $id, ElementTypes::TYPE_ASSET, $patchFolderParameter, $this->securityService->getCurrentUser() diff --git a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php index d61db752b..8209a139f 100644 --- a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php +++ b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php @@ -16,7 +16,6 @@ use Exception; use Pimcore\Bundle\StaticResolverBundle\Models\User\UserResolverInterface; use Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage; -use Pimcore\Bundle\StudioBackendBundle\Asset\Service\AssetServiceInterface; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\AutomationAction\AbstractHandler; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig; @@ -28,6 +27,7 @@ use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\UserTopicServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Symfony\Component\Messenger\Attribute\AsMessageHandler; +use function count; /** * @internal @@ -44,7 +44,6 @@ public function __construct( private readonly UserResolverInterface $userResolver, private readonly UserTopicServiceInterface $userTopicService, private readonly GridServiceInterface $gridService, - private readonly AssetServiceInterface $assetService ) { parent::__construct(); } @@ -70,70 +69,62 @@ public function __invoke(ExportDataCollectionMessage $message): void )); } - $jobAsset = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TO_EXPORT->value); - - $asset = $this->assetService->getAssetForUser($jobAsset['id'], $user); - - if ($asset->getType() === ElementTypes::TYPE_FOLDER) { - $this->abort($this->getAbortData( - Config::ELEMENT_FOLDER_COLLECTION_NOT_SUPPORTED->value, - [ - 'folderId' => $asset->getId(), - ] - )); - - return; - } - + $assets = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENTS_TO_EXPORT->value); + $totalAssets = count($assets); $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); $columnsDefinitions = $this->columnConfigurationService->getAvailableAssetColumnConfiguration(); - $columnCollection = $this->gridService->getConfigurationForExport( - $columns, - $columnsDefinitions - ); + $columnCollection = $this->gridService->getConfigurationForExport($columns, $columnsDefinitions); - try { - $assetData = [ - $asset->getId() => $this->gridService->getGridValuesForElement( + $assetsData = []; + foreach ($assets as $asset) { + try { + $assetsData[$asset['id']] = $this->gridService->getGridValuesForElement( $columnCollection, ElementTypes::TYPE_ASSET, - $asset->getId(), - true, + $asset['id'], $user - ), - ]; - - $this->updateContextArrayValues($jobRun, StepConfig::GRID_EXPORT_DATA->value, $assetData); - - $csvExportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? null; + ); - if ($csvExportDataInfo === null) { - $this->updateContextArrayValues( - $jobRun, - StepConfig::GRID_EXPORT_DATA_INFO->value, + } catch (Exception $e) { + $this->abort($this->getAbortData( + Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, [ - 'type' => ElementTypes::TYPE_ASSET, + 'id' => $asset['id'], + 'message' => $e->getMessage(), ] - ); + )); } + $this->updateProgress( + $this->publishService, + $this->userTopicService, + $jobRun, + $this->getJobStep($message)->getName(), + $totalAssets, + $totalAssets + ); + } + + try { + if (!empty($assetsData)) { + $context = $jobRun->getContext(); + if (isset($context[StepConfig::GRID_EXPORT_DATA->value])) { + $assetsData = array_merge( + $context[StepConfig::GRID_EXPORT_DATA->value], + $assetsData + ); + } + $this->updateJobRunContext($jobRun, StepConfig::GRID_EXPORT_DATA->value, $assetsData); + } } catch (Exception $e) { $this->abort($this->getAbortData( Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, [ - 'id' => $asset->getId(), 'message' => $e->getMessage(), ] )); } - - $this->updateProgress( - $this->publishService, - $this->userTopicService, - $jobRun, - $this->getJobStep($message)->getName() - ); } } diff --git a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php deleted file mode 100644 index 80612c1d7..000000000 --- a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php +++ /dev/null @@ -1,171 +0,0 @@ -getJobRun($message); - if (!$this->shouldBeExecuted($jobRun)) { - return; - } - - $user = $this->userResolver->getById($jobRun->getOwnerId()); - - if ($user === null) { - $this->abort($this->getAbortData( - Config::USER_NOT_FOUND_MESSAGE->value, - [ - 'userId' => $jobRun->getOwnerId(), - ] - )); - } - - $jobFolder = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TO_EXPORT->value); - - $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); - - $filters = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_FILTERS->value); - - $assets = $this->gridSearch->searchElementIdsForUser( - ElementTypes::TYPE_ASSET, - new GridParameter( - $jobFolder['id'], - $columns, - $this->filterParameterMapper->fromArray($filters) - ), - $user - ); - - if (count($assets) === 0) { - $this->updateProgress( - $this->publishService, - $this->userTopicService, - $jobRun, - $this->getJobStep($message)->getName() - ); - - return; - } - - $columnsDefinitions = $this->columnConfigurationService->getAvailableAssetColumnConfiguration(); - - $columnCollection = $this->gridService->getConfigurationForExport( - $columns, - $columnsDefinitions - ); - - foreach ($assets as $assetId) { - try { - $assetData = [ - $assetId => $this->gridService->getGridValuesForElement( - $columnCollection, - ElementTypes::TYPE_ASSET, - $assetId, - true, - $user - ), - ]; - - $this->updateContextArrayValues($jobRun, StepConfig::GRID_EXPORT_DATA->value, $assetData); - } catch (Exception $e) { - $this->abort($this->getAbortData( - Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, - [ - 'id' => $assetId, - 'message' => $e->getMessage(), - ] - )); - } - } - - $csvExportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? null; - - if ($csvExportDataInfo === null) { - $this->updateContextArrayValues( - $jobRun, - StepConfig::GRID_EXPORT_DATA_INFO->value, - [ - 'type' => ElementTypes::TYPE_ASSET, - ] - ); - } - - $this->updateProgress( - $this->publishService, - $this->userTopicService, - $jobRun, - $this->getJobStep($message)->getName() - ); - } - - protected function configureStep(): void - { - $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TO_EXPORT->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::ELEMENT_TO_EXPORT->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_COLUMNS->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - $this->stepConfiguration->setRequired(StepConfig::CONFIG_FILTERS->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_FILTERS->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - } -} diff --git a/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php b/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php index 1c2c41ce4..8c85a913b 100644 --- a/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php +++ b/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php @@ -14,7 +14,6 @@ namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Attribute\Request; use Attribute; -use OpenApi\Attributes\Items; use OpenApi\Attributes\JsonContent; use OpenApi\Attributes\Property; use OpenApi\Attributes\RequestBody; @@ -39,27 +38,17 @@ public function __construct() properties: [ new Property( property: 'data', - type: 'array', - items: new Items( - required: ['folderId'], - properties: [ - new Property( - property: 'folderId', - description: 'Folder ID', - type: 'integer', - example: 2 - ), - new UpdateIntegerProperty('parentId'), - new UpdateIntegerProperty('index', 0), - new UpdateStringProperty('key'), - new UpdateStringProperty('locked'), - new UpdateStringProperty('childrenSortBy'), - new UpdateStringProperty('childrenSortOrder'), - new UpdateBooleanProperty('published'), - new UpdateObjectProperty('editableData'), - ], - type: 'object', - ), + properties: [ + new UpdateIntegerProperty('parentId'), + new UpdateIntegerProperty('index', 0), + new UpdateStringProperty('key'), + new UpdateStringProperty('locked'), + new UpdateStringProperty('childrenSortBy'), + new UpdateStringProperty('childrenSortOrder'), + new UpdateBooleanProperty('published'), + new UpdateObjectProperty('editableData'), + ], + type: 'object', ), new Property( property: 'filters', diff --git a/src/DataObject/Controller/PatchFolderController.php b/src/DataObject/Controller/PatchFolderController.php index 2533d4770..ca63aabd4 100644 --- a/src/DataObject/Controller/PatchFolderController.php +++ b/src/DataObject/Controller/PatchFolderController.php @@ -19,6 +19,7 @@ use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\UserNotFoundException; use Pimcore\Bundle\StudioBackendBundle\MappedParameter\PatchFolderParameter; +use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Path\IdParameter; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\Content\IdJson; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\CreatedResponse; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses; @@ -39,6 +40,8 @@ */ final class PatchFolderController extends AbstractApiController { + private const string ROUTE = '/data-objects/folder/{id}'; + public function __construct( SerializerInterface $serializer, private readonly PatchServiceInterface $patchService, @@ -50,15 +53,16 @@ public function __construct( /** * @throws InvalidArgumentException|UserNotFoundException */ - #[Route('/data-objects/folder', name: 'pimcore_studio_api_patch_data_object_folder', methods: ['PATCH'])] + #[Route(self::ROUTE, name: 'pimcore_studio_api_patch_data_object_folder', methods: ['PATCH'])] #[IsGranted(UserPermissions::DATA_OBJECTS->value)] #[Patch( - path: self::PREFIX . '/data-objects/folder', + path: self::PREFIX . self::ROUTE, operationId: 'data_object_patch_folder_by_id', description: 'data_object_patch_folder_by_id_description', summary: 'data_object_patch_folder_by_id_summary', tags: [Tags::DataObjects->value] )] + #[IdParameter(type: ElementTypes::TYPE_FOLDER, name: 'id')] #[PatchDataObjectFolderRequestBody] #[CreatedResponse( description: 'data_object_patch_by_id_created_response', @@ -69,10 +73,11 @@ public function __construct( HttpResponseCodes::UNAUTHORIZED, ])] public function dataObjectsPatchFolderById( + int $id, #[MapRequestPayload] PatchFolderParameter $patchFolderParameter ): Response { - $jobRunId = $this->patchService->patchFolder( + $id, ElementTypes::TYPE_OBJECT, $patchFolderParameter, $this->securityService->getCurrentUser() diff --git a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php index 6089834a1..272533952 100644 --- a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php +++ b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php @@ -15,9 +15,7 @@ use Exception; use Pimcore\Bundle\StaticResolverBundle\Models\User\UserResolverInterface; -use Pimcore\Bundle\StudioBackendBundle\Class\Repository\ClassDefinitionRepositoryInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage; -use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataObjectServiceInterface; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\AutomationAction\AbstractHandler; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig; @@ -29,6 +27,7 @@ use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\UserTopicServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Symfony\Component\Messenger\Attribute\AsMessageHandler; +use function count; /** * @internal @@ -41,8 +40,6 @@ final class ExportDataCollectionHandler extends AbstractHandler public function __construct( private readonly ColumnConfigurationServiceInterface $columnConfigurationService, - private readonly ClassDefinitionRepositoryInterface $classDefinitionRepository, - private readonly DataObjectServiceInterface $dataObjectService, private readonly PublishServiceInterface $publishService, private readonly UserTopicServiceInterface $userTopicService, private readonly UserResolverInterface $userResolver, @@ -72,22 +69,9 @@ public function __invoke(ExportDataCollectionMessage $message): void )); } - $jobDataObject = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TO_EXPORT->value); - - $dataObject = $this->dataObjectService->getDataObjectForUser($jobDataObject['id'], $user); - $classId = $this->classDefinitionRepository->getClassDefinition($dataObject->getClassName())->getId(); - - if ($dataObject->getType() === ElementTypes::TYPE_FOLDER) { - $this->abort($this->getAbortData( - Config::ELEMENT_FOLDER_COLLECTION_NOT_SUPPORTED->value, - [ - 'folderId' => $dataObject->getId(), - ] - )); - - return; - } - + $dataObjects = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENTS_TO_EXPORT->value); + $totalObjects = count($dataObjects); + $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); $columnsDefinitions = $this->columnConfigurationService->getAvailableDataObjectColumnConfiguration( $classId, @@ -97,47 +81,53 @@ public function __invoke(ExportDataCollectionMessage $message): void $columnCollection = $this->gridService->getConfigurationForExport($columns, $columnsDefinitions); - try { - $dataObjectData = [ - $dataObject->getId() => $this->gridService->getGridValuesForElement( + $data = []; + foreach ($dataObjects as $object) { + try { + $data[$object['id']] = $this->gridService->getGridValuesForElement( $columnCollection, ElementTypes::TYPE_OBJECT, - $dataObject->getId(), - true, + $object['id'], $user - ), - ]; - - $this->updateContextArrayValues($jobRun, StepConfig::GRID_EXPORT_DATA->value, $dataObjectData); - - $csvExportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? null; + ); - if ($csvExportDataInfo === null) { - $this->updateContextArrayValues( - $jobRun, - StepConfig::GRID_EXPORT_DATA_INFO->value, + } catch (Exception $e) { + $this->abort($this->getAbortData( + Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, [ - 'type' => ElementTypes::TYPE_OBJECT, - 'classId' => $classId, + 'id' => $object['id'], + 'message' => $e->getMessage(), ] - ); + )); } + $this->updateProgress( + $this->publishService, + $this->userTopicService, + $jobRun, + $this->getJobStep($message)->getName(), + $totalObjects + ); + } + + try { + if (!empty($data)) { + $context = $jobRun->getContext(); + if (isset($context[StepConfig::GRID_EXPORT_DATA->value])) { + $data = array_merge( + $context[StepConfig::GRID_EXPORT_DATA->value], + $data + ); + } + $this->updateJobRunContext($jobRun, StepConfig::GRID_EXPORT_DATA->value, $data); + } } catch (Exception $e) { $this->abort($this->getAbortData( Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, [ - 'id' => $dataObject->getId(), 'message' => $e->getMessage(), ] )); } - - $this->updateProgress( - $this->publishService, - $this->userTopicService, - $jobRun, - $this->getJobStep($message)->getName() - ); } } diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php index bc2d8cce3..8d1fdffc2 100644 --- a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php @@ -60,13 +60,14 @@ public function __invoke(ElementUsageReplaceMessage $message): void $jobRun = $this->getJobRun($message); $user = $this->getUser($jobRun); $this->initializeElements($message); + $elementCount = 0; $elements = $jobRun->getJob()?->getSelectedElements(); if (empty($elements)) { $elements = $this->sourceElement->getDependencies()->getRequiredByWithPath(); + $elementCount = count($elements); } - $elementCount = count($elements); foreach ($elements as $elementData) { $isArray = is_array($elementData); $element = $this->elementUsageService->getElementById( @@ -96,13 +97,22 @@ public function __invoke(ElementUsageReplaceMessage $message): void )); } - $this->updateProgress( - $this->publishService, - $this->userTopicService, - $jobRun, - $this->getJobStep($message)->getName(), - $elementCount - ); + if ($elementCount > 0) { + $this->updateProgress( + $this->publishService, + $this->userTopicService, + $jobRun, + $this->getJobStep($message)->getName(), + $elementCount + ); + } else { + $this->updateProgress( + $this->publishService, + $this->userTopicService, + $jobRun, + $this->getJobStep($message)->getName() + ); + } } } diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchFolderHandler.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchFolderHandler.php index e3c9e0fb1..531c3eb7b 100644 --- a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchFolderHandler.php +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchFolderHandler.php @@ -17,9 +17,7 @@ use Pimcore\Bundle\StaticResolverBundle\Models\User\UserResolverInterface; use Pimcore\Bundle\StudioBackendBundle\DataIndex\Grid\GridSearchInterface; use Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\PatchFolderMessage; -use Pimcore\Bundle\StudioBackendBundle\Element\Service\ElementServiceInterface; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\AutomationAction\AbstractHandler; -use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Model\AbortActionData; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Trait\HandlerProgressTrait; @@ -27,8 +25,8 @@ use Pimcore\Bundle\StudioBackendBundle\Grid\Mapper\FilterParameterMapperInterface; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\UserTopicServiceInterface; -use Pimcore\Bundle\StudioBackendBundle\Patcher\Service\PatchServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Util\Trait\ElementProviderTrait; +use Pimcore\Model\Element\ElementDescriptor; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use function count; @@ -44,8 +42,6 @@ final class PatchFolderHandler extends AbstractHandler public function __construct( private readonly FilterParameterMapperInterface $filterParameterMapper, private readonly PublishServiceInterface $publishService, - private readonly ElementServiceInterface $elementService, - private readonly PatchServiceInterface $patchService, private readonly UserResolverInterface $userResolver, private readonly UserTopicServiceInterface $userTopicService, private readonly GridSearchInterface $gridSearch, @@ -63,18 +59,28 @@ public function __invoke(PatchFolderMessage $message): void return; } - $validatedParameters = $this->validateFullParameters( - $message, - $jobRun, - $this->userResolver, - ); + $job = $jobRun->getJob(); + if ($job === null) { + return; + } - if ($validatedParameters instanceof AbortActionData) { - $this->abort($validatedParameters); + $selectedElements = $job->getSelectedElements(); + if (empty($selectedElements)) { + $this->abort($this->getAbortData(Config::NO_ELEMENT_PROVIDED->value)); + } + + $folderDescriptor = $selectedElements[0]; + $folderId = $folderDescriptor->getId(); + $elementType = $folderDescriptor->getType(); + + $user = $this->userResolver->getById($jobRun->getOwnerId()); + if ($user === null) { + $this->abort($this->getAbortData( + Config::USER_NOT_FOUND_MESSAGE->value, + ['userId' => $jobRun->getOwnerId()] + )); } - $folderId = $validatedParameters->getSubject()->getId(); - $elementType = $validatedParameters->getSubject()->getType(); $filters = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_FILTERS->value); $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); $filters = $this->filterParameterMapper->fromArray($filters); @@ -85,7 +91,7 @@ public function __invoke(PatchFolderMessage $message): void $elementIds = $this->gridSearch->searchElementIdsForUser( $elementType, new GridParameter($folderId, [], $filters), - $validatedParameters->getUser() + $user ); if (empty($elementIds)) { @@ -99,42 +105,20 @@ public function __invoke(PatchFolderMessage $message): void return; } - $jobEnvironmentData = $jobRun->getJob()?->getEnvironmentData(); - - $elementCount = count($elementIds); - foreach ($elementIds as $elementId) { - $element = $this->elementService->getAllowedElementById( - $elementType, - $elementId, - $validatedParameters->getUser() - ); + $newSelectedElements = array_map( + static fn ($id) => new ElementDescriptor($elementType, $id), + $elementIds + ); - try { - $this->patchService->patchElement( - $element, - $elementType, - $jobEnvironmentData[$folderId], - $validatedParameters->getUser() - ); - } catch (Exception $exception) { - $this->abort($this->getAbortData( - Config::ELEMENT_PATCH_FAILED_MESSAGE->value, - [ - 'type' => $elementType, - 'id' => $element->getId(), - 'message' => $exception->getMessage(), - ], - )); - } + $this->updateProgress( + $this->publishService, + $this->userTopicService, + $jobRun, + $this->getJobStep($message)->getName() + ); - $this->updateProgress( - $this->publishService, - $this->userTopicService, - $jobRun, - $this->getJobStep($message)->getName(), - $elementCount - ); - } + $jobRun->setTotalElements(count($elementIds)); + $this->setSelectedElementsForNextJobStep($jobRun, $newSelectedElements); } protected function configureStep(): void diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchHandler.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchHandler.php index b0528a3f9..d478f9464 100644 --- a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchHandler.php +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchHandler.php @@ -20,6 +20,7 @@ use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\AutomationAction\AbstractHandler; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Model\AbortActionData; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; +use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Trait\HandlerProgressTrait; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\UserTopicServiceInterface; @@ -72,9 +73,12 @@ public function __invoke(PatchMessage $message): void $this->elementService ); $elementId = $element->getId(); - $elementType = $this->getElementType($element); + $elementType = $this->getElementType($element, true); + $folderId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::FOLDER_TO_EXPORT->value); $jobEnvironmentData = $jobRun->getJob()?->getEnvironmentData(); - if (!isset($jobEnvironmentData[(string)$elementId])) { + + $patchDataKey = $folderId ?? $elementId; + if (!isset($jobEnvironmentData[$patchDataKey])) { $this->abort($this->getAbortData( Config::ELEMENT_PATCH_FAILED_MESSAGE->value, [ @@ -89,7 +93,7 @@ public function __invoke(PatchMessage $message): void $this->patchService->patchElement( $element, $elementType, - $jobEnvironmentData[$elementId], + $jobEnvironmentData[$patchDataKey], $validatedParameters->getUser() ); } catch (Exception $exception) { @@ -107,7 +111,18 @@ public function __invoke(PatchMessage $message): void $this->publishService, $this->userTopicService, $jobRun, - $this->getJobStep($message)->getName() + $this->getJobStep($message)->getName(), + 1, + $jobRun->getTotalElements() + ); + } + + protected function configureStep(): void + { + $this->stepConfiguration->setDefault(StepConfig::FOLDER_TO_EXPORT->value, null); + $this->stepConfiguration->setAllowedTypes( + StepConfig::FOLDER_TO_EXPORT->value, + [StepConfig::CONFIG_TYPE_INT->value, 'null'] ); } } diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php new file mode 100644 index 000000000..06c78689d --- /dev/null +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php @@ -0,0 +1,66 @@ +getJobRun($message); + if (!$this->shouldBeExecuted($jobRun)) { + return; + } + + $selectedElementCount = count($jobRun->getJob()?->getSelectedElements() ?? []); + if ($selectedElementCount === $jobRun->getTotalElements()) { + return; + } + + $jobRun->setTotalElements($selectedElementCount); + + $this->updateProgress( + $this->publishService, + $this->userTopicService, + $jobRun, + $this->getJobStep($message)->getName() + ); + } +} diff --git a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Messages/RefreshJobMessage.php similarity index 72% rename from src/Asset/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php rename to src/Element/ExecutionEngine/AutomationAction/Messenger/Messages/RefreshJobMessage.php index ea4c4e6c6..63772fbf0 100644 --- a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Messages/RefreshJobMessage.php @@ -11,13 +11,13 @@ * @license Pimcore Open Core License (POCL) */ -namespace Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages; +namespace Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages; use Pimcore\Bundle\GenericExecutionEngineBundle\Messenger\Messages\AbstractExecutionEngineMessage; /** * @internal */ -final class ExportFolderDataCollectionMessage extends AbstractExecutionEngineMessage +final class RefreshJobMessage extends AbstractExecutionEngineMessage { } diff --git a/src/Element/ExecutionEngine/Util/JobSteps.php b/src/Element/ExecutionEngine/Util/JobSteps.php index 645240bc3..d0f1e2eb8 100644 --- a/src/Element/ExecutionEngine/Util/JobSteps.php +++ b/src/Element/ExecutionEngine/Util/JobSteps.php @@ -22,4 +22,5 @@ enum JobSteps: string case ELEMENT_BATCH_TAG_ASSIGN = 'studio_ee_job_step_batch_tag_assign'; case ELEMENT_BATCH_TAG_REPLACE = 'studio_ee_job_step_batch_tag_replace'; case ELEMENT_USAGE_REPLACE = 'studio_ee_job_step_element_usage_replace'; + case ELEMENT_REFRESH_COUNT = 'studio_ee_job_step_element_refresh_count'; } diff --git a/src/ExecutionEngine/AutomationAction/AbstractHandler.php b/src/ExecutionEngine/AutomationAction/AbstractHandler.php index 09b2aa184..39cb1450a 100644 --- a/src/ExecutionEngine/AutomationAction/AbstractHandler.php +++ b/src/ExecutionEngine/AutomationAction/AbstractHandler.php @@ -169,6 +169,22 @@ protected function updateContextArrayValues(JobRun $jobRun, string $key, array $ $this->updateJobRunContext($jobRun, $key, $contextValue); } + protected function updateJobRunContextValues( + JobRun $jobRun, + array $values, + bool $persist = true + ): void { + $jobRun->setContext( + array_merge( + $jobRun->getContext() ?? [], + $values + ) + ); + if ($persist) { + $this->jobRunRepository->update($jobRun); + } + } + private function getExecutionActionData( UserInterface $user, array $jobEnvironmentData, diff --git a/src/ExecutionEngine/Util/Jobs.php b/src/ExecutionEngine/Util/Jobs.php index ea4597996..b35faf68f 100644 --- a/src/ExecutionEngine/Util/Jobs.php +++ b/src/ExecutionEngine/Util/Jobs.php @@ -21,6 +21,7 @@ enum Jobs: string case UPLOAD_ASSETS = 'studio_ee_job_upload_assets'; case ZIP_FILE_UPLOAD = 'studio_ee_job_upload_zip_file'; case CREATE_CSV = 'studio_ee_job_create_csv'; + case COLLECT_EXPORT_FOLDER_ELEMENTS = 'studio_ee_job_collect_folder_export_elements'; case CREATE_XLSX = 'studio_ee_job_create_xlsx'; case PATCH_ELEMENTS = 'studio_ee_job_patch_elements'; case CLONE_DATA_OBJECTS = 'studio_ee_job_clone_data_objects'; diff --git a/src/ExecutionEngine/Util/StepConfig.php b/src/ExecutionEngine/Util/StepConfig.php index 2b8216167..7cd37668b 100644 --- a/src/ExecutionEngine/Util/StepConfig.php +++ b/src/ExecutionEngine/Util/StepConfig.php @@ -23,8 +23,9 @@ enum StepConfig: string case CUSTOM_REPORT_CONFIG = 'custom_report_config'; case CUSTOM_REPORT_TO_EXPORT = 'custom_report_to_export'; case ELEMENT_CLASS_ID = 'element_class_id'; - case ELEMENT_TO_EXPORT = 'element_to_export'; + case ELEMENTS_TO_EXPORT = 'elements_to_export'; case ELEMENT_TYPE = 'element_type'; + case EXPORT_FORMAT = 'export_format'; case FOLDER_TO_EXPORT = 'folder_to_export'; case GRID_EXPORT_DATA = 'grid_export_data'; case GRID_EXPORT_DATA_INFO = 'grid_export_data_info'; diff --git a/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php b/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php index 196efa986..57ce9000f 100644 --- a/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php +++ b/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php @@ -41,7 +41,8 @@ private function updateProgress( UserTopicServiceInterface $userTopicService, JobRun $jobRun, string $jobStepName, - int $stepElements = 1 + int $stepElements = 1, + int $frequency = self::FREQUENCY ): void { $currentStep = $this->getCurrentStep($jobRun); $totalSteps = $this->getTotalSteps($jobRun); @@ -49,8 +50,13 @@ private function updateProgress( $processedElements = $jobRun->getContext()[self::PROCESSED_ELEMENTS] ?? 0; $processedElements++; - $this->updateJobRunContext($jobRun, self::PROCESSED_ELEMENTS, $processedElements); - $updateFrequency = max(1, (int)($totalEvents / self::FREQUENCY)); + $this->updateJobRunContextValues( + $jobRun, + [ + self::PROCESSED_ELEMENTS => $processedElements, + ] + ); + $updateFrequency = max(1, (int)($totalEvents / $frequency)); $progress = (int)($processedElements / $totalEvents * 100); @@ -81,7 +87,13 @@ private function getTotalSteps(JobRun $jobRun): int } $totalSteps = count($jobRun->getJob()?->getSteps() ?? []); - $this->updateJobRunContext($jobRun, self::TOTAL_STEPS, $totalSteps); + $this->updateJobRunContextValues( + $jobRun, + [ + self::TOTAL_STEPS => $totalSteps, + ], + false + ); return $totalSteps; } @@ -95,9 +107,15 @@ private function getCurrentStep(JobRun $jobRun): int } $currentStep = $jobRun->getCurrentStep(); - $this->updateJobRunContext($jobRun, self::PROCESSED_ELEMENTS, 0); - $this->updateJobRunContext($jobRun, self::ELEMENTS_PER_STEP, null); - $this->updateJobRunContext($jobRun, self::CURRENT_STEP, $currentStep); + $this->updatejobRunContextValues( + $jobRun, + [ + self::PROCESSED_ELEMENTS => 0, + self::CURRENT_STEP => $currentStep, + self::ELEMENTS_PER_STEP => null, + ], + false + ); return $currentStep; } @@ -114,7 +132,13 @@ private function getElementsPerStep(JobRun $jobRun, int $stepElements): int $elementsPerStep = $jobRun->getTotalElements() * $elementsPerStep; } - $this->updateJobRunContext($jobRun, self::ELEMENTS_PER_STEP, $elementsPerStep); + $this->updateJobRunContextValues( + $jobRun, + [ + self::ELEMENTS_PER_STEP => $elementsPerStep, + ], + false + ); return $elementsPerStep; } diff --git a/src/Export/Attribute/Request/ExportDataRequestBody.php b/src/Export/Attribute/Request/ExportDataRequestBody.php index 4391474bd..24c2a05b4 100644 --- a/src/Export/Attribute/Request/ExportDataRequestBody.php +++ b/src/Export/Attribute/Request/ExportDataRequestBody.php @@ -61,6 +61,12 @@ enum: StepConfig::values(), enum: ElementTypes::ALLOWED_TYPES, example: ElementTypes::TYPE_ASSET ), + new Property( + property: 'classId', + type: 'string', + example: 'CAR', + nullable: true + ), ], type: 'object' ) diff --git a/src/Export/Attribute/Request/ExportFolderDataRequestBody.php b/src/Export/Attribute/Request/ExportFolderDataRequestBody.php index aeefa6d34..329341966 100644 --- a/src/Export/Attribute/Request/ExportFolderDataRequestBody.php +++ b/src/Export/Attribute/Request/ExportFolderDataRequestBody.php @@ -50,7 +50,6 @@ enum: StepConfig::values(), parent::__construct( content: new JsonContent( properties: [ - new Property(property: 'folders', type: 'array', items: new Items(type: 'integer'), example: [1]), new Property( property: 'columns', type: 'array', diff --git a/src/Export/Controller/Csv/FolderController.php b/src/Export/Controller/Csv/FolderController.php index 5e84387e1..769dadc59 100644 --- a/src/Export/Controller/Csv/FolderController.php +++ b/src/Export/Controller/Csv/FolderController.php @@ -19,10 +19,12 @@ use Pimcore\Bundle\StudioBackendBundle\Export\MappedParameter\ExportFolderParameter; use Pimcore\Bundle\StudioBackendBundle\Export\Service\ExecutionEngine\ExportServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Export\Util\Constant\ExportFormat; +use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Path\IdParameter; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\Content\IdJson; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\CreatedResponse; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses; 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\Response; @@ -36,6 +38,8 @@ */ final class FolderController extends AbstractApiController { + private const string ROUTE = '/export/csv/folder/{id}'; + public function __construct( SerializerInterface $serializer, private readonly ExportServiceInterface $exportService @@ -43,15 +47,16 @@ public function __construct( parent::__construct($serializer); } - #[Route('/export/csv/folder', name: 'pimcore_studio_api_export_csv_folder', methods: ['POST'])] + #[Route(path: self::ROUTE, name: 'pimcore_studio_api_export_csv_folder', methods: ['POST'])] #[IsGranted(UserPermissions::ASSETS->value)] #[Post( - path: self::PREFIX . '/export/csv/folder', + path: self::PREFIX . self::ROUTE, operationId: 'export_csv_folder', description: 'export_csv_folder_description', summary: 'export_csv_folder_summary', tags: [Tags::Export->name] )] + #[IdParameter(type: ElementTypes::TYPE_FOLDER, name: 'id')] #[ExportFolderDataRequestBody] #[CreatedResponse( description: 'export_csv_created_response', @@ -62,11 +67,13 @@ public function __construct( HttpResponseCodes::NOT_FOUND, ])] public function exportCsvFolder( + int $id, #[MapRequestPayload] ExportFolderParameter $exportParameter ): Response { return $this->jsonResponse( [ 'jobRunId' => $this->exportService->generateExportFileForFolders( + $id, $exportParameter, ExportFormat::CSV->value ), diff --git a/src/Export/Controller/Xlsx/FolderController.php b/src/Export/Controller/Xlsx/FolderController.php index d4b46be1d..fe9fff4f3 100644 --- a/src/Export/Controller/Xlsx/FolderController.php +++ b/src/Export/Controller/Xlsx/FolderController.php @@ -19,10 +19,12 @@ use Pimcore\Bundle\StudioBackendBundle\Export\MappedParameter\ExportFolderParameter; use Pimcore\Bundle\StudioBackendBundle\Export\Service\ExecutionEngine\ExportServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Export\Util\Constant\ExportFormat; +use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Path\IdParameter; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\Content\IdJson; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\CreatedResponse; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses; 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\Response; @@ -36,6 +38,8 @@ */ final class FolderController extends AbstractApiController { + private const string ROUTE = '/export/xlsx/folder/{id}'; + public function __construct( SerializerInterface $serializer, private readonly ExportServiceInterface $exportService @@ -43,15 +47,16 @@ public function __construct( parent::__construct($serializer); } - #[Route('/export/xlsx/folder', name: 'pimcore_studio_api_export_xlsx_folder', methods: ['POST'])] + #[Route(path: self::ROUTE, name: 'pimcore_studio_api_export_xlsx_folder', methods: ['POST'])] #[IsGranted(UserPermissions::ASSETS->value)] #[Post( - path: self::PREFIX . '/export/xlsx/folder', + path: self::PREFIX . self::ROUTE, operationId: 'export_xlsx_folder', description: 'export_xlsx_folder_description', summary: 'export_xlsx_folder_summary', tags: [Tags::Export->name] )] + #[IdParameter(type: ElementTypes::TYPE_FOLDER, name: 'id')] #[ExportFolderDataRequestBody(addDelimiter: false)] #[CreatedResponse( description: 'export_xlsx_created_response', @@ -62,11 +67,13 @@ public function __construct( HttpResponseCodes::NOT_FOUND, ])] public function exportXlsxFolder( + int $id, #[MapRequestPayload] ExportFolderParameter $exportParameter ): Response { return $this->jsonResponse( [ 'jobRunId' => $this->exportService->generateExportFileForFolders( + $id, $exportParameter, ExportFormat::XLSX->value ), diff --git a/src/Export/EventSubscriber/FolderCollectionSubscriber.php b/src/Export/EventSubscriber/FolderCollectionSubscriber.php new file mode 100644 index 000000000..11d7bcd34 --- /dev/null +++ b/src/Export/EventSubscriber/FolderCollectionSubscriber.php @@ -0,0 +1,70 @@ + 'onStateChanged', + ]; + } + + public function onStateChanged(JobRunStateChangedEvent $event): void + { + if ($event->getJobName() !== Jobs::COLLECT_EXPORT_FOLDER_ELEMENTS->value) { + + return; + } + + $jobRun = $this->jobRunRepository->getJobRunById($event->getJobRunId()); + match ($event->getNewState()) { + JobRunStates::FINISHED->value => $this->publishService->publish( + $this->userTopicService->getUserTopic($event->getJobRunOwnerId()), + new Finished( + $event->getJobRunId(), + $event->getJobName(), + $event->getJobRunOwnerId(), + $event->getNewState(), + [JobRunContext::CHILD_JOB_RUN->value => + $jobRun->getContext()[JobRunContext::CHILD_JOB_RUN->value] ?? null, + ] + ) + ), + default => null, + }; + } +} diff --git a/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/CsvCreationHandler.php b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/CsvCreationHandler.php index 6c9450775..3b5d22a5a 100644 --- a/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/CsvCreationHandler.php +++ b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/CsvCreationHandler.php @@ -22,6 +22,7 @@ use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\CsvCreationMessage; use Pimcore\Bundle\StudioBackendBundle\Export\Model\GridExportData; use Pimcore\Bundle\StudioBackendBundle\Export\Service\ExportServiceInterface; +use Pimcore\Bundle\StudioBackendBundle\Export\Util\Trait\ExportCreationHandlerSetupTrait; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\UserTopicServiceInterface; use Symfony\Component\Messenger\Attribute\AsMessageHandler; @@ -32,6 +33,7 @@ #[AsMessageHandler] final class CsvCreationHandler extends AbstractHandler { + use ExportCreationHandlerSetupTrait; use HandlerProgressTrait; public function __construct( @@ -64,6 +66,8 @@ public function __invoke(CsvCreationMessage $message): void $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); $settings = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_CONFIGURATION->value); + $elementType = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TYPE->value); + $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); $headers = $settings[StepConfig::SETTINGS_HEADER->value] ?? StepConfig::SETTINGS_HEADER_NO_HEADER->value; $delimiter = $settings[StepConfig::SETTINGS_DELIMITER->value] ?? null; @@ -75,15 +79,13 @@ public function __invoke(CsvCreationMessage $message): void } $csvData = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA->value]; - $csvExportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? []; - try { $this->csvExportService->createExportFile( $jobRun->getId(), new GridExportData( $columns, $csvData, - $csvExportDataInfo, + ['type' => $elementType, 'classId' => $classId], $headers !== StepConfig::SETTINGS_HEADER_NO_HEADER->value, $headers === StepConfig::SETTINGS_HEADER_NAME, ), @@ -104,23 +106,4 @@ public function __invoke(CsvCreationMessage $message): void $this->getJobStep($message)->getName() ); } - - protected function configureStep(): void - { - $this->stepConfiguration->setRequired(StepConfig::CONFIG_CONFIGURATION->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_CONFIGURATION->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_COLUMNS->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - - $this->stepConfiguration->setDefaults([ - StepConfig::CONFIG_COLUMNS->value => [], - StepConfig::CONFIG_CONFIGURATION->value => [], - ]); - } } diff --git a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/FolderCollectionHandler.php similarity index 50% rename from src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php rename to src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/FolderCollectionHandler.php index 7cdc74497..6f993585c 100644 --- a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php +++ b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/FolderCollectionHandler.php @@ -11,23 +11,23 @@ * @license Pimcore Open Core License (POCL) */ -namespace Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Handler; +namespace Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Handler; use Exception; use Pimcore\Bundle\StaticResolverBundle\Models\User\UserResolverInterface; use Pimcore\Bundle\StudioBackendBundle\DataIndex\Grid\GridSearchInterface; -use Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\ExportFolderDataCollectionMessage; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\AutomationAction\AbstractHandler; -use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; +use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Model\AbortActionData; +use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\JobRunContext; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Trait\HandlerProgressTrait; +use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\FolderCollectionMessage; +use Pimcore\Bundle\StudioBackendBundle\Export\MappedParameter\ExportParameter; +use Pimcore\Bundle\StudioBackendBundle\Export\Service\ExecutionEngine\ExportServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Grid\MappedParameter\GridParameter; use Pimcore\Bundle\StudioBackendBundle\Grid\Mapper\FilterParameterMapperInterface; -use Pimcore\Bundle\StudioBackendBundle\Grid\Service\ColumnConfigurationServiceInterface; -use Pimcore\Bundle\StudioBackendBundle\Grid\Service\GridServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\UserTopicServiceInterface; -use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use function count; @@ -35,17 +35,16 @@ * @internal */ #[AsMessageHandler] -final class ExportFolderDataCollectionHandler extends AbstractHandler +final class FolderCollectionHandler extends AbstractHandler { use HandlerProgressTrait; public function __construct( - private readonly ColumnConfigurationServiceInterface $columnConfigurationService, + private readonly ExportServiceInterface $exportService, private readonly FilterParameterMapperInterface $filterParameterMapper, private readonly PublishServiceInterface $publishService, private readonly UserResolverInterface $userResolver, private readonly UserTopicServiceInterface $userTopicService, - private readonly GridServiceInterface $gridService, private readonly GridSearchInterface $gridSearch ) { parent::__construct(); @@ -54,41 +53,40 @@ public function __construct( /** * @throws Exception */ - public function __invoke(ExportFolderDataCollectionMessage $message): void + public function __invoke(FolderCollectionMessage $message): void { $jobRun = $this->getJobRun($message); if (!$this->shouldBeExecuted($jobRun)) { return; } - $user = $this->userResolver->getById($jobRun->getOwnerId()); + $validatedParameters = $this->validateFullParameters( + $message, + $jobRun, + $this->userResolver, + ); - if ($user === null) { - $this->abort($this->getAbortData( - Config::USER_NOT_FOUND_MESSAGE->value, - [ - 'userId' => $jobRun->getOwnerId(), - ] - )); + if ($validatedParameters instanceof AbortActionData) { + $this->abort($validatedParameters); } - $jobFolder = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TO_EXPORT->value); - + $elementType = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TYPE->value); $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); - - $filters = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_FILTERS->value); - + $filters = $this->filterParameterMapper->fromArray( + $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_FILTERS->value) + ); $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); - $filters = $this->filterParameterMapper->fromArray($filters); - $filters->setClassId($classId); + if ($classId !== '') { + $filters->setClassId($classId); + } - $dataObjects = $this->gridSearch->searchElementIdsForUser( - ElementTypes::TYPE_OBJECT, - new GridParameter($jobFolder['id'], $columns, $filters), - $user + $elements = $this->gridSearch->searchElementIdsForUser( + $elementType, + new GridParameter($validatedParameters->getSubject()->getId(), $columns, $filters), + $validatedParameters->getUser() ); - if (count($dataObjects) === 0) { + if (count($elements) === 0) { $this->updateProgress( $this->publishService, $this->userTopicService, @@ -99,53 +97,14 @@ public function __invoke(ExportFolderDataCollectionMessage $message): void return; } - $columnsDefinitions = $this->columnConfigurationService->getAvailableDataObjectColumnConfiguration( - $classId, - $jobFolder['id'], - $user + $config = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_CONFIGURATION->value); + $childJobRunId = $this->exportService->generateExportFileForElements( + new ExportParameter($columns, $filters, $config, $elements, $elementType, $classId), + $this->extractConfigFieldFromJobStepConfig($message, StepConfig::EXPORT_FORMAT->value), + $validatedParameters->getUser() ); - $columnCollection = $this->gridService->getConfigurationForExport( - $columns, - $columnsDefinitions - ); - - foreach ($dataObjects as $dataObjectId) { - try { - $dataObjectData = [ - $dataObjectId => $this->gridService->getGridValuesForElement( - $columnCollection, - ElementTypes::TYPE_OBJECT, - $dataObjectId, - true, - $user - ), - ]; - - $this->updateContextArrayValues($jobRun, StepConfig::GRID_EXPORT_DATA->value, $dataObjectData); - } catch (Exception $e) { - $this->abort($this->getAbortData( - Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, - [ - 'id' => $dataObjectId, - 'message' => $e->getMessage(), - ] - )); - } - } - - $csvExportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? null; - - if ($csvExportDataInfo === null) { - $this->updateContextArrayValues( - $jobRun, - StepConfig::GRID_EXPORT_DATA_INFO->value, - [ - 'type' => ElementTypes::TYPE_OBJECT, - 'classId' => $classId, - ] - ); - } + $this->updateJobRunContext($jobRun, JobRunContext::CHILD_JOB_RUN->value, $childJobRunId); $this->updateProgress( $this->publishService, @@ -157,10 +116,20 @@ public function __invoke(ExportFolderDataCollectionMessage $message): void protected function configureStep(): void { - $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TO_EXPORT->value); + $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TYPE->value); $this->stepConfiguration->setAllowedTypes( - StepConfig::ELEMENT_TO_EXPORT->value, - StepConfig::CONFIG_TYPE_ARRAY->value + StepConfig::ELEMENT_TYPE->value, + StepConfig::CONFIG_TYPE_STRING->value + ); + $this->stepConfiguration->setRequired(StepConfig::ELEMENT_CLASS_ID->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::ELEMENT_CLASS_ID->value, + StepConfig::CONFIG_TYPE_STRING->value + ); + $this->stepConfiguration->setRequired(StepConfig::EXPORT_FORMAT->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::EXPORT_FORMAT->value, + StepConfig::CONFIG_TYPE_STRING->value ); $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); $this->stepConfiguration->setAllowedTypes( @@ -172,10 +141,13 @@ protected function configureStep(): void StepConfig::CONFIG_FILTERS->value, StepConfig::CONFIG_TYPE_ARRAY->value ); - $this->stepConfiguration->setRequired(StepConfig::ELEMENT_CLASS_ID->value); + $this->stepConfiguration->setRequired(StepConfig::CONFIG_CONFIGURATION->value); $this->stepConfiguration->setAllowedTypes( - StepConfig::ELEMENT_CLASS_ID->value, - StepConfig::CONFIG_TYPE_STRING->value + StepConfig::CONFIG_CONFIGURATION->value, + StepConfig::CONFIG_TYPE_ARRAY->value ); + $this->stepConfiguration->setDefaults([ + StepConfig::CONFIG_CONFIGURATION->value => [], + ]); } } diff --git a/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/XlsxCreationHandler.php b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/XlsxCreationHandler.php index 528723e10..f99f59a11 100644 --- a/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/XlsxCreationHandler.php +++ b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/XlsxCreationHandler.php @@ -22,6 +22,7 @@ use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\XlsxCreationMessage; use Pimcore\Bundle\StudioBackendBundle\Export\Model\GridExportData; use Pimcore\Bundle\StudioBackendBundle\Export\Service\ExportServiceInterface; +use Pimcore\Bundle\StudioBackendBundle\Export\Util\Trait\ExportCreationHandlerSetupTrait; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\UserTopicServiceInterface; use Symfony\Component\Messenger\Attribute\AsMessageHandler; @@ -32,6 +33,7 @@ #[AsMessageHandler] final class XlsxCreationHandler extends AbstractHandler { + use ExportCreationHandlerSetupTrait; use HandlerProgressTrait; public function __construct( @@ -64,6 +66,8 @@ public function __invoke(XlsxCreationMessage $message): void $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); $settings = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_CONFIGURATION->value); + $elementType = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TYPE->value); + $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); $headers = $settings[StepConfig::SETTINGS_HEADER->value] ?? StepConfig::SETTINGS_HEADER_NO_HEADER->value; if (!isset($jobRun->getContext()[StepConfig::GRID_EXPORT_DATA->value])) { @@ -74,15 +78,13 @@ public function __invoke(XlsxCreationMessage $message): void } $exportData = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA->value]; - $exportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? []; - try { $this->xlsxExportService->createExportFile( $jobRun->getId(), new GridExportData( $columns, $exportData, - $exportDataInfo, + ['type' => $elementType, 'classId' => $classId], $headers !== StepConfig::SETTINGS_HEADER_NO_HEADER->value, $headers === StepConfig::SETTINGS_HEADER_NAME ), @@ -102,23 +104,4 @@ public function __invoke(XlsxCreationMessage $message): void $this->getJobStep($message)->getName() ); } - - protected function configureStep(): void - { - $this->stepConfiguration->setRequired(StepConfig::CONFIG_CONFIGURATION->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_CONFIGURATION->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_COLUMNS->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - - $this->stepConfiguration->setDefaults([ - StepConfig::CONFIG_COLUMNS->value => [], - StepConfig::CONFIG_CONFIGURATION->value => [], - ]); - } } diff --git a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php b/src/Export/ExecutionEngine/AutomationAction/Messenger/Messages/FolderCollectionMessage.php similarity index 71% rename from src/DataObject/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php rename to src/Export/ExecutionEngine/AutomationAction/Messenger/Messages/FolderCollectionMessage.php index 8d2918026..aee009d7a 100644 --- a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php +++ b/src/Export/ExecutionEngine/AutomationAction/Messenger/Messages/FolderCollectionMessage.php @@ -11,13 +11,13 @@ * @license Pimcore Open Core License (POCL) */ -namespace Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages; +namespace Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages; use Pimcore\Bundle\GenericExecutionEngineBundle\Messenger\Messages\AbstractExecutionEngineMessage; /** * @internal */ -final class ExportFolderDataCollectionMessage extends AbstractExecutionEngineMessage +final class FolderCollectionMessage extends AbstractExecutionEngineMessage { } diff --git a/src/Export/ExecutionEngine/Util/JobSteps.php b/src/Export/ExecutionEngine/Util/JobSteps.php index dd1a69bd0..1bafc7c47 100644 --- a/src/Export/ExecutionEngine/Util/JobSteps.php +++ b/src/Export/ExecutionEngine/Util/JobSteps.php @@ -15,6 +15,7 @@ enum JobSteps: string { case DATA_COLLECTION = 'studio_ee_job_step_export_data_collection'; + case FOLDER_DATA_COLLECTION = 'studio_ee_job_step_export_folder_data_collection'; case CSV_CREATION = 'studio_ee_job_step_csv_creation'; case XLSX_CREATION = 'studio_ee_job_step_xlsx_creation'; } diff --git a/src/Export/MappedParameter/ExportFolderParameter.php b/src/Export/MappedParameter/ExportFolderParameter.php index fbfda936e..ecf15ae45 100644 --- a/src/Export/MappedParameter/ExportFolderParameter.php +++ b/src/Export/MappedParameter/ExportFolderParameter.php @@ -17,7 +17,6 @@ use Pimcore\Bundle\StudioBackendBundle\Export\Util\Trait\ExportConfigValidationTrait; use Pimcore\Bundle\StudioBackendBundle\Filter\MappedParameter\FilterParameter; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; -use Pimcore\Model\Element\ElementDescriptor; /** * @internal @@ -26,21 +25,13 @@ { use ExportConfigValidationTrait; - /** - * @param array $folders - */ public function __construct( private array $columns, private ?FilterParameter $filters, private array $config, - private array $folders, private string $elementType, private ?string $classId = null ) { - if (empty($this->getFolders())) { - throw new InvalidArgumentException('No folders provided'); - } - if ($this->classId === null && $this->getValidElementType($this->elementType) === ElementTypes::TYPE_OBJECT) { throw new InvalidArgumentException('Class ID must be provided for data object exports'); } @@ -63,17 +54,6 @@ public function getConfig(): array return $this->config; } - /** - * @return array - */ - public function getFolders(): array - { - return array_map( - fn (int $id) => new ElementDescriptor($this->getElementType(), $id), - $this->folders - ); - } - public function getElementType(): string { return $this->getValidElementType($this->elementType); diff --git a/src/Export/MappedParameter/ExportParameter.php b/src/Export/MappedParameter/ExportParameter.php index 0ea09c2da..0cf552bc7 100644 --- a/src/Export/MappedParameter/ExportParameter.php +++ b/src/Export/MappedParameter/ExportParameter.php @@ -16,6 +16,7 @@ use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\Export\Util\Trait\ExportConfigValidationTrait; use Pimcore\Bundle\StudioBackendBundle\Filter\MappedParameter\FilterParameter; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Model\Element\ElementDescriptor; /** @@ -33,8 +34,12 @@ public function __construct( private ?FilterParameter $filters, private array $config, private array $elements, - private string $elementType + private string $elementType, + private ?string $classId = null ) { + if (empty($this->classId) && $this->getValidElementType($this->elementType) === ElementTypes::TYPE_OBJECT) { + throw new InvalidArgumentException('Class ID must be provided for data object exports'); + } if (empty($this->getElements())) { throw new InvalidArgumentException('No elements provided'); @@ -73,4 +78,9 @@ public function getElementType(): string { return $this->getValidElementType($this->elementType); } + + public function getClassId(): ?string + { + return $this->classId; + } } diff --git a/src/Export/Service/ExecutionEngine/ExportService.php b/src/Export/Service/ExecutionEngine/ExportService.php index d7898e15b..88812505c 100644 --- a/src/Export/Service/ExecutionEngine/ExportService.php +++ b/src/Export/Service/ExecutionEngine/ExportService.php @@ -13,18 +13,19 @@ namespace Pimcore\Bundle\StudioBackendBundle\Export\Service\ExecutionEngine; +use Generator; use Pimcore\Bundle\GenericExecutionEngineBundle\Agent\JobExecutionAgentInterface; use Pimcore\Bundle\GenericExecutionEngineBundle\Model\Job; use Pimcore\Bundle\GenericExecutionEngineBundle\Model\JobStep; use Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage as AssetCollectionMessage; -use Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ExportFolderDataCollectionMessage as AssetFolderCollectionMessage; use Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage as DataObjectCollectionMessage; -use Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\ExportFolderDataCollectionMessage as DataObjectFolderCollectionMessage; +use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidElementTypeException; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Jobs; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig; use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\CsvCreationMessage; +use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\FolderCollectionMessage; use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\XlsxCreationMessage; use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\Util\JobSteps; use Pimcore\Bundle\StudioBackendBundle\Export\MappedParameter\ExportFolderParameter; @@ -33,80 +34,118 @@ use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Model\Element\ElementDescriptor; +use Pimcore\Model\UserInterface; +use function array_slice; +use function count; /** * @internal */ final readonly class ExportService implements ExportServiceInterface { + private const int EXPORT_BATCH_SIZE = 500; + public function __construct( private JobExecutionAgentInterface $jobExecutionAgent, private SecurityServiceInterface $securityService ) { } - public function generateExportFileForElements(ExportParameter $exportParameter, string $exportFormat): int - { - $collectionSettings = [ - StepConfig::CONFIG_COLUMNS->value => $exportParameter->getColumns(), - StepConfig::ELEMENT_TYPE->value => $exportParameter->getElementType(), - ]; + /** + * {@inheritdoc} + */ + public function generateExportFileForElements( + ExportParameter $exportParameter, + string $exportFormat, + ?UserInterface $user = null, + ): int { + $elementType = $exportParameter->getElementType(); + $classId = $exportParameter->getClassId(); + if ($elementType === ElementTypes::TYPE_OBJECT && empty($classId)) { + throw new InvalidArgumentException('Class ID must be provided for object folder patching'); + } - $creationSettings = [ + $collectionSettings = [ + StepConfig::ELEMENT_CLASS_ID->value => $classId ?? '', StepConfig::CONFIG_COLUMNS->value => $exportParameter->getColumns(), - StepConfig::CONFIG_CONFIGURATION->value => $exportParameter->getConfig(), ]; - return $this->generateExportFileJob( - $exportParameter->getElements(), + $creationSettings = array_merge( $collectionSettings, - $creationSettings, - $this->getMessageClass($exportParameter->getElementType()), - $exportFormat + [ + StepConfig::CONFIG_CONFIGURATION->value => $exportParameter->getConfig(), + StepConfig::ELEMENT_TYPE->value => $exportParameter->getElementType(), + ] ); - } - public function generateExportFileForFolders(ExportFolderParameter $exportParameter, string $exportFormat): int - { - $collectionSettings = [ - StepConfig::CONFIG_COLUMNS->value => $exportParameter->getColumns(), - StepConfig::CONFIG_FILTERS->value => $exportParameter->getFilters(), + $jobSteps = [ + ...$this->mapJobSteps( + $exportParameter->getElements(), + $collectionSettings, + $this->getMessageClass($exportParameter->getElementType()) + ), + ...[$this->getExportFileStep($creationSettings, $exportFormat)], ]; - if ($exportParameter->getElementType() === ElementTypes::TYPE_OBJECT) { - $collectionSettings[StepConfig::ELEMENT_CLASS_ID->value] = $exportParameter->getClassId(); + if ($user === null) { + $user = $this->securityService->getCurrentUser(); } - $creationSettings = [ - StepConfig::CONFIG_COLUMNS->value => $exportParameter->getColumns(), - StepConfig::CONFIG_CONFIGURATION->value => $exportParameter->getConfig(), - ]; + return $this->generateExportFileJob($jobSteps, $exportFormat, $user->getId()); + } + + /** + * {@inheritdoc} + */ + public function generateExportFileForFolders( + int $folderId, + ExportFolderParameter $exportParameter, + string $exportFormat + ): int { + $elementType = $exportParameter->getElementType(); + $classId = $exportParameter->getClassId(); + if ($elementType === ElementTypes::TYPE_OBJECT && empty($classId)) { + throw new InvalidArgumentException('Class ID must be provided for object folder patching'); + } return $this->generateExportFileJob( - $exportParameter->getFolders(), - $collectionSettings, - $creationSettings, - $this->getMessageClassForFolder($exportParameter->getElementType()), - $exportFormat + [ + new JobStep( + JobSteps::FOLDER_DATA_COLLECTION->value, + FolderCollectionMessage::class, + '', + [ + StepConfig::ELEMENT_CLASS_ID->value => $classId ?? '', + StepConfig::CONFIG_COLUMNS->value => $exportParameter->getColumns(), + StepConfig::CONFIG_CONFIGURATION->value => $exportParameter->getConfig(), + StepConfig::CONFIG_FILTERS->value => $exportParameter->getFilters(), + StepConfig::EXPORT_FORMAT->value => $exportFormat, + StepConfig::ELEMENT_TYPE->value => $elementType, + ] + ), + ], + $exportFormat, + $this->securityService->getCurrentUser()->getId(), + [new ElementDescriptor($elementType, $folderId)], + true ); } private function generateExportFileJob( - array $elements, - array $collectionSettings, - array $creationSettings, - string $messageFQCN, - string $exportFormat + array $jobSteps, + string $exportFormat, + int $ownerId, + array $selectedElements = [], + bool $isFolder = false ): int { - - $jobSteps = [ - ...$this->mapJobSteps($elements, $collectionSettings, $messageFQCN, StepConfig::ELEMENT_TO_EXPORT), - ...[$this->getExportFileStep($creationSettings, $exportFormat)], - ]; + $name = $this->createJobNameByFormat($exportFormat); + if ($isFolder) { + $name = Jobs::COLLECT_EXPORT_FOLDER_ELEMENTS->value; + } $jobRun = $this->jobExecutionAgent->startJobExecution( - $this->createJobByFormat($jobSteps, $exportFormat), - $this->securityService->getCurrentUser()->getId(), + new Job($name, $jobSteps, $selectedElements), + $ownerId, Config::CONTEXT_STOP_ON_ERROR->value ); @@ -117,17 +156,33 @@ private function mapJobSteps( array $elements, array $collectionSettings, string $messageFQCN, - StepConfig $export ): array { - return array_map( - static fn (ElementDescriptor $element) => new JobStep( + $steps = []; + + foreach ($this->chunkGenerator($elements, self::EXPORT_BATCH_SIZE) as $batch) { + + $config = [ + StepConfig::ELEMENTS_TO_EXPORT->value => $batch, + ] + $collectionSettings; + + $steps[] = new JobStep( JobSteps::DATA_COLLECTION->value, $messageFQCN, '', - array_merge([$export->value => $element], $collectionSettings) - ), - $elements, - ); + $config + ); + } + + return $steps; + } + + private function chunkGenerator(array $elements, int $size): Generator + { + $total = count($elements); + + for ($i = 0; $i < $total; $i += $size) { + yield array_slice($elements, $i, $size); + } } private function getExportFileStep(array $settings, string $exportFormat): JobStep @@ -159,17 +214,14 @@ private function getXlsxCreationStep(array $settings): JobStep ); } - private function createJobByFormat(array $jobSteps, string $exportFormat): Job + private function createJobNameByFormat(string $exportFormat): string { $name = Jobs::CREATE_CSV->value; if ($exportFormat === ExportFormat::XLSX->value) { $name = Jobs::CREATE_XLSX->value; } - return new Job( - name: $name, - steps: $jobSteps - ); + return $name; } private function getMessageClass(string $elementType): string @@ -180,13 +232,4 @@ private function getMessageClass(string $elementType): string default => throw new InvalidElementTypeException($elementType) }; } - - private function getMessageClassForFolder(string $elementType): string - { - return match($elementType) { - ElementTypes::TYPE_ASSET => AssetFolderCollectionMessage::class, - ElementTypes::TYPE_OBJECT => DataObjectFolderCollectionMessage::class, - default => throw new InvalidElementTypeException($elementType) - }; - } } diff --git a/src/Export/Service/ExecutionEngine/ExportServiceInterface.php b/src/Export/Service/ExecutionEngine/ExportServiceInterface.php index 14a5e12ab..847e84b4b 100644 --- a/src/Export/Service/ExecutionEngine/ExportServiceInterface.php +++ b/src/Export/Service/ExecutionEngine/ExportServiceInterface.php @@ -13,15 +13,31 @@ namespace Pimcore\Bundle\StudioBackendBundle\Export\Service\ExecutionEngine; +use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\Export\MappedParameter\ExportFolderParameter; use Pimcore\Bundle\StudioBackendBundle\Export\MappedParameter\ExportParameter; +use Pimcore\Model\UserInterface; /** * @internal */ interface ExportServiceInterface { - public function generateExportFileForElements(ExportParameter $exportParameter, string $exportFormat): int; + /** + * @throws InvalidArgumentException + */ + public function generateExportFileForElements( + ExportParameter $exportParameter, + string $exportFormat, + ?UserInterface $user = null, + ): int; - public function generateExportFileForFolders(ExportFolderParameter $exportParameter, string $exportFormat): int; + /** + * @throws InvalidArgumentException + */ + public function generateExportFileForFolders( + int $folderId, + ExportFolderParameter $exportParameter, + string $exportFormat + ): int; } diff --git a/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php b/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php index ddf457339..8b593fdfc 100644 --- a/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php +++ b/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php @@ -22,9 +22,14 @@ trait CsvExportHandlerSetupTrait { protected function configureStep(): void { - $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TO_EXPORT->value); + $this->stepConfiguration->setRequired(StepConfig::ELEMENT_CLASS_ID->value); $this->stepConfiguration->setAllowedTypes( - StepConfig::ELEMENT_TO_EXPORT->value, + StepConfig::ELEMENT_CLASS_ID->value, + StepConfig::CONFIG_TYPE_STRING->value + ); + $this->stepConfiguration->setRequired(StepConfig::ELEMENTS_TO_EXPORT->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::ELEMENTS_TO_EXPORT->value, StepConfig::CONFIG_TYPE_ARRAY->value ); $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); @@ -32,10 +37,5 @@ protected function configureStep(): void StepConfig::CONFIG_COLUMNS->value, StepConfig::CONFIG_TYPE_ARRAY->value ); - $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TYPE->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::ELEMENT_TYPE->value, - StepConfig::CONFIG_TYPE_STRING->value - ); } } diff --git a/src/Export/Util/Trait/ExportCreationHandlerSetupTrait.php b/src/Export/Util/Trait/ExportCreationHandlerSetupTrait.php new file mode 100644 index 000000000..f2017ad30 --- /dev/null +++ b/src/Export/Util/Trait/ExportCreationHandlerSetupTrait.php @@ -0,0 +1,50 @@ +stepConfiguration->setRequired(StepConfig::ELEMENT_CLASS_ID->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::ELEMENT_CLASS_ID->value, + StepConfig::CONFIG_TYPE_STRING->value + ); + $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TYPE->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::ELEMENT_TYPE->value, + StepConfig::CONFIG_TYPE_STRING->value + ); + $this->stepConfiguration->setRequired(StepConfig::CONFIG_CONFIGURATION->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::CONFIG_CONFIGURATION->value, + StepConfig::CONFIG_TYPE_ARRAY->value + ); + $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::CONFIG_COLUMNS->value, + StepConfig::CONFIG_TYPE_ARRAY->value + ); + $this->stepConfiguration->setDefaults([ + StepConfig::CONFIG_COLUMNS->value => [], + StepConfig::CONFIG_CONFIGURATION->value => [], + ]); + } +} diff --git a/src/Grid/Service/GridService.php b/src/Grid/Service/GridService.php index b560bd1b3..70c82a7d5 100644 --- a/src/Grid/Service/GridService.php +++ b/src/Grid/Service/GridService.php @@ -20,6 +20,7 @@ use Pimcore\Bundle\StudioBackendBundle\DataIndex\SearchResult\SearchResultItemInterface; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotFoundException; +use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; use Pimcore\Bundle\StudioBackendBundle\Filter\MappedParameter\FilterParameter; use Pimcore\Bundle\StudioBackendBundle\Grid\Column\ColumnCollectorInterface; use Pimcore\Bundle\StudioBackendBundle\Grid\Column\ColumnDefinitionInterface; @@ -36,6 +37,8 @@ use Pimcore\Bundle\StudioBackendBundle\Grid\Util\Collection\ColumnCollection; use Pimcore\Bundle\StudioBackendBundle\Response\Collection; use Pimcore\Bundle\StudioBackendBundle\Response\StudioElementInterface; +use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementPermissions; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Trait\ElementProviderTrait; use Pimcore\Model\DataObject\ClassDefinition; @@ -72,6 +75,7 @@ public function __construct( private readonly ColumnCollectorLoaderInterface $columnCollectorLoader, private readonly GridSearchInterface $gridSearch, private readonly EventDispatcherInterface $eventDispatcher, + private readonly SecurityServiceInterface $securityService, private readonly ServiceResolverInterface $serviceResolver, private readonly ClassDefinitionResolverInterface $classDefinitionResolver ) { @@ -164,7 +168,7 @@ public function getPreviewOfAdvancedColumn(AdvancedColumnPreviewParameter $param } /** - * @throws InvalidArgumentException + * {@inheritdoc} */ public function getGridDataForElement( ColumnCollection $columnCollection, @@ -179,6 +183,20 @@ public function getGridDataForElement( $databaseElement = null; if ($isExport || $elementType === ElementTypes::TYPE_OBJECT) { $databaseElement = $this->getElement($this->serviceResolver, $elementType, $elementId); + if ($user !== null) { + $this->securityService->hasElementPermission( + $databaseElement, + $user, + ElementPermissions::VIEW_PERMISSION + ); + } + } + + if ($isExport && $databaseElement->getType() === ElementTypes::TYPE_FOLDER) { + throw new InvalidArgumentException( + message: 'Exporting folder elements is not supported', + errorKey: Config::ELEMENT_FOLDER_COLLECTION_NOT_SUPPORTED->value + ); } foreach ($columnCollection->getColumns() as $column) { @@ -224,18 +242,10 @@ public function getGridValuesForElement( ColumnCollection $columnCollection, string $elementType, int $elementId, - bool $isExport = false, - ?UserInterface $user = null, + UserInterface $user ): array { - $data = $this->getGridDataForElement( - $columnCollection, - null, - $elementType, - $elementId, - $isExport, - $user - ); + $data = $this->getGridDataForElement($columnCollection, null, $elementType, $elementId, true, $user); return array_map( static fn (ColumnData $columnData) => $columnData->getValue(), @@ -343,9 +353,7 @@ private function supports(Column $column, string $elementType): bool return false; } - /** @var ColumnResolverInterface $resolver */ $resolver = $this->getColumnResolvers()[$column->getType()]; - if (!in_array($elementType, $resolver->supportedElementTypes(), true)) { return false; } diff --git a/src/Grid/Service/GridServiceInterface.php b/src/Grid/Service/GridServiceInterface.php index 104f2ddf0..1337e9b02 100644 --- a/src/Grid/Service/GridServiceInterface.php +++ b/src/Grid/Service/GridServiceInterface.php @@ -56,8 +56,7 @@ public function getGridValuesForElement( ColumnCollection $columnCollection, string $elementType, int $elementId, - bool $isExport = false, - ?UserInterface $user = null, + UserInterface $user ): array; /** diff --git a/src/Patcher/Service/PatchService.php b/src/Patcher/Service/PatchService.php index d7c37174c..69e043dff 100644 --- a/src/Patcher/Service/PatchService.php +++ b/src/Patcher/Service/PatchService.php @@ -17,6 +17,7 @@ use Pimcore\Bundle\GenericExecutionEngineBundle\Agent\JobExecutionAgentInterface; use Pimcore\Bundle\GenericExecutionEngineBundle\Model\Job; use Pimcore\Bundle\GenericExecutionEngineBundle\Model\JobStep; +use Pimcore\Bundle\GenericExecutionEngineBundle\Utils\Enums\SelectionProcessingMode; use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterServiceInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\Util\Trait\ValidateObjectDataTrait; use Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\PatchFolderMessage; @@ -87,6 +88,7 @@ public function patch( * {@inheritdoc} */ public function patchFolder( + int $folderId, string $elementType, PatchFolderParameter $patchFolderParameter, UserInterface $user, @@ -97,8 +99,8 @@ public function patchFolder( } $job = new Job( - name: Jobs::PATCH_ELEMENTS->value, - steps: [ + Jobs::PATCH_ELEMENTS->value, + [ new JobStep( JobSteps::ELEMENT_FOLDER_PATCHING->value, PatchFolderMessage::class, @@ -106,17 +108,20 @@ public function patchFolder( [ StepConfig::CONFIG_FILTERS->value => $patchFolderParameter->getFilters(), StepConfig::ELEMENT_CLASS_ID->value => $classId ?? '', + ], + SelectionProcessingMode::ONCE + ), + new JobStep( + JobSteps::ELEMENT_PATCHING->value, + PatchMessage::class, + '', + [ + StepConfig::FOLDER_TO_EXPORT->value => $folderId, ] ), ], - selectedElements: array_map( - static fn (array $data) => new ElementDescriptor( - $elementType, - $data['folderId'] - ), - $patchFolderParameter->getData() - ), - environmentData: array_column($patchFolderParameter->getData(), null, 'folderId'), + [new ElementDescriptor($elementType, $folderId)], + [$folderId => $patchFolderParameter->getData()], ); $jobRun = $this->jobExecutionAgent->startJobExecution( diff --git a/src/Patcher/Service/PatchServiceInterface.php b/src/Patcher/Service/PatchServiceInterface.php index d626ee889..4014c9b13 100644 --- a/src/Patcher/Service/PatchServiceInterface.php +++ b/src/Patcher/Service/PatchServiceInterface.php @@ -39,6 +39,7 @@ public function patch( * @throws InvalidArgumentException */ public function patchFolder( + int $folderId, string $elementType, PatchFolderParameter $patchFolderParameter, UserInterface $user, diff --git a/translations/studio_api_docs.en.yaml b/translations/studio_api_docs.en.yaml index 27481af72..43b5f4d0e 100644 --- a/translations/studio_api_docs.en.yaml +++ b/translations/studio_api_docs.en.yaml @@ -175,7 +175,7 @@ asset_patch_by_id_description: | asset_patch_by_id_success_response: Successfully patched asset asset_patch_by_id_summary: Patch assets by ID asset_patch_folder_by_id_description: | - Patching assets based on the given folder ID and data.
Patching on folders will be done asynchronously.
+ Patching assets based on the given folder ID and data.
Patching on folder will be done asynchronously.
You can also use filters and sorting. Filter assets from folder based on the grid filter schema asset_patch_folder_by_id_summary: Patch all assets based on folder ID and filters asset_replace_description: | @@ -387,7 +387,7 @@ data_object_patch_by_id_description: | data_object_patch_by_id_success_response: Successfully patched data object data_object_patch_by_id_summary: Patch data objects by ID data_object_patch_folder_by_id_description: | - Patching data objects based on the given folder ID, filters and data.
Patching on folders will be done asynchronously.
+ Patching data objects based on the given folder ID, filters and data.
Patching on folder will be done asynchronously.
You can also use filters and sorting. Filter data objects from folder based on the grid filter schema data_object_patch_folder_by_id_summary: Patch all data objects based on folder ID and filters data_object_replace_content_description: | @@ -664,8 +664,8 @@ export_csv_description: | Download has to be triggered separately via the csv download route with the {jobRunId} returned in the response export_csv_summary: Creating CSV file for elements export_csv_folder_description: | - Creating the CSV file for elements based on the folder.
Parameters are: