From 63fdcb6e39aab3df06e821dd69d71f8dd0371cb6 Mon Sep 17 00:00:00 2001 From: roadiz-ci Date: Wed, 24 Jul 2024 10:25:01 +0000 Subject: [PATCH] feat!: Changed all node exports to CSV format. --- src/Controllers/Nodes/ExportController.php | 59 +++---- src/Controllers/SearchController.php | 170 ++++++++++----------- src/Resources/translations/messages.en.xlf | 4 + src/Resources/translations/messages.fr.xlf | 4 + src/Resources/translations/messages.xlf | 1 + src/Resources/views/nodes/list.html.twig | 2 +- 6 files changed, 123 insertions(+), 117 deletions(-) diff --git a/src/Controllers/Nodes/ExportController.php b/src/Controllers/Nodes/ExportController.php index abc4d1f1..3b1f02ef 100644 --- a/src/Controllers/Nodes/ExportController.php +++ b/src/Controllers/Nodes/ExportController.php @@ -4,77 +4,80 @@ namespace Themes\Rozier\Controllers\Nodes; -use PhpOffice\PhpSpreadsheet\Writer\Exception; +use Doctrine\Persistence\ManagerRegistry; use RZ\Roadiz\CoreBundle\Entity\Node; use RZ\Roadiz\CoreBundle\Entity\NodesSources; use RZ\Roadiz\CoreBundle\Entity\Translation; use RZ\Roadiz\CoreBundle\Security\Authorization\Voter\NodeVoter; -use RZ\Roadiz\CoreBundle\Xlsx\NodeSourceXlsxSerializer; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\Serializer\SerializerInterface; use Themes\Rozier\RozierApp; class ExportController extends RozierApp { - public function __construct(private readonly NodeSourceXlsxSerializer $xlsxSerializer) - { + public function __construct( + private readonly ManagerRegistry $managerRegistry, + private readonly SerializerInterface $serializer + ) { } /** - * Export all Node in a XLSX file (Excel). + * Export all Node in a CSV file. * - * @param Request $request * @param int $translationId * @param int|null $parentNodeId * * @return Response - * @throws \PhpOffice\PhpSpreadsheet\Exception - * @throws Exception */ - public function exportAllXlsxAction(Request $request, int $translationId, ?int $parentNodeId = null): Response + public function exportAllAction(int $translationId, ?int $parentNodeId = null): Response { - $translation = $this->em() - ->find(Translation::class, $translationId); + $translation = $this->managerRegistry + ->getRepository(Translation::class) + ->find($translationId); if (null === $translation) { - $translation = $this->em() + $translation = $this->managerRegistry ->getRepository(Translation::class) ->findDefault(); } $criteria = ["translation" => $translation]; $order = ['node.nodeType' => 'ASC']; - $filename = 'nodes-' . date("YmdHis") . '.' . $translation->getLocale() . '.xlsx'; + $filename = 'nodes-' . date("YmdHis") . '.' . $translation->getLocale() . '.csv'; if (null !== $parentNodeId) { /** @var Node|null $parentNode */ - $parentNode = $this->em()->find(Node::class, $parentNodeId); + $parentNode = $this->managerRegistry + ->getRepository(Node::class) + ->find($parentNodeId); if (null === $parentNode) { throw $this->createNotFoundException(); } $this->denyAccessUnlessGranted(NodeVoter::READ, $parentNode); $criteria['node.parent'] = $parentNode; - $filename = $parentNode->getNodeName() . '-' . date("YmdHis") . '.' . $translation->getLocale() . '.xlsx'; + $filename = $parentNode->getNodeName() . '-' . date("YmdHis") . '.' . $translation->getLocale() . '.csv'; } else { $this->denyAccessUnlessGranted(NodeVoter::READ_AT_ROOT); } - $sources = $this->em() + $sources = $this->managerRegistry ->getRepository(NodesSources::class) ->setDisplayingAllNodesStatuses(true) ->setDisplayingNotPublishedNodes(true) ->findBy($criteria, $order); - $this->xlsxSerializer->setOnlyTexts(true); - $this->xlsxSerializer->addUrls(); - $xlsx = $this->xlsxSerializer->serialize($sources); - - $response = new Response( - $xlsx, - Response::HTTP_OK, - [] - ); - + $response = new StreamedResponse(function () use ($sources) { + echo $this->serializer->serialize($sources, 'csv', [ + 'groups' => [ + 'nodes_sources', + 'urls', + 'tag_base', + 'document_display', + ], + ]); + }); + $response->headers->set('Content-Type', 'text/csv'); $response->headers->set( 'Content-Disposition', $response->headers->makeDisposition( @@ -83,8 +86,6 @@ public function exportAllXlsxAction(Request $request, int $translationId, ?int $ ) ); - $response->prepare($request); - return $response; } } diff --git a/src/Controllers/SearchController.php b/src/Controllers/SearchController.php index e615d884..34529eb9 100644 --- a/src/Controllers/SearchController.php +++ b/src/Controllers/SearchController.php @@ -5,7 +5,7 @@ namespace Themes\Rozier\Controllers; use DateTime; -use PhpOffice\PhpSpreadsheet\Exception; +use Doctrine\Persistence\ManagerRegistry; use RZ\Roadiz\Core\AbstractEntities\AbstractField; use RZ\Roadiz\CoreBundle\Entity\Node; use RZ\Roadiz\CoreBundle\Entity\NodeType; @@ -17,7 +17,6 @@ use RZ\Roadiz\CoreBundle\Form\NodeStatesType; use RZ\Roadiz\CoreBundle\Form\NodeTypesType; use RZ\Roadiz\CoreBundle\Form\SeparatorType; -use RZ\Roadiz\CoreBundle\Xlsx\NodeSourceXlsxSerializer; use Symfony\Component\Form\ClickableInterface; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -28,11 +27,14 @@ use Symfony\Component\Form\Form; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Validator\Constraints\GreaterThan; use Themes\Rozier\Forms\NodeSource\NodeSourceType; use Themes\Rozier\RozierApp; @@ -43,8 +45,11 @@ class SearchController extends RozierApp protected bool $pagination = true; protected ?int $itemPerPage = null; - public function __construct(protected readonly NodeSourceXlsxSerializer $xlsxSerializer) - { + public function __construct( + protected readonly ManagerRegistry $managerRegistry, + protected readonly FormFactoryInterface $formFactory, + protected readonly SerializerInterface $serializer + ) { } /** @@ -87,9 +92,9 @@ protected function appendDateTimeCriteria(array &$data, string $fieldName): arra /** * @param array $data * @param string $prefix - * @return mixed + * @return array */ - protected function processCriteria($data, string $prefix = ""): mixed + protected function processCriteria(array $data, string $prefix = ""): array { if (!empty($data[$prefix . "nodeName"])) { if (!isset($data[$prefix . "nodeName_exact"]) || $data[$prefix . "nodeName_exact"] !== true) { @@ -131,7 +136,7 @@ protected function processCriteria($data, string $prefix = ""): mixed if (isset($data["tags"])) { $data["tags"] = array_map('trim', explode(',', $data["tags"])); foreach ($data["tags"] as $key => $value) { - $data["tags"][$key] = $this->em()->getRepository(Tag::class)->findByPath($value); + $data["tags"][$key] = $this->managerRegistry->getRepository(Tag::class)->findByPath($value); } array_filter($data["tags"]); } @@ -255,14 +260,12 @@ public function searchNodeAction(Request $request): Response * @param int $nodetypeId * * @return Response - * @throws Exception - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception * @throws RuntimeError */ public function searchNodeSourceAction(Request $request, int $nodetypeId): Response { /** @var NodeType|null $nodetype */ - $nodetype = $this->em()->find(NodeType::class, $nodetypeId); + $nodetype = $this->managerRegistry->getRepository(NodeType::class)->find($nodetypeId); $builder = $this->buildSimpleForm("__node__"); $this->extendForm($builder, $nodetype); @@ -279,7 +282,7 @@ public function searchNodeSourceAction(Request $request, int $nodetypeId): Respo return $response; } - if (null !== $response = $this->handleNodeForm($request, $form, $nodetype)) { + if (null !== $response = $this->handleNodeForm($form, $nodetype)) { return $response; } @@ -298,7 +301,7 @@ public function searchNodeSourceAction(Request $request, int $nodetypeId): Respo */ protected function buildNodeTypeForm(?int $nodetypeId = null): FormBuilderInterface { - $builderNodeType = $this->createNamedFormBuilder('nodeTypeForm', [], ["method" => "get"]); + $builderNodeType = $this->formFactory->createNamedBuilder('nodeTypeForm', FormType::class, [], ["method" => "get"]); $builderNodeType->add( "nodetype", NodeTypesType::class, @@ -365,87 +368,84 @@ protected function handleNodeTypeForm(FormInterface $nodeTypeForm): ?RedirectRes } /** - * @param Request $request * @param FormInterface $form * @param NodeType $nodetype * * @return null|Response - * @throws Exception - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception */ - protected function handleNodeForm(Request $request, FormInterface $form, NodeType $nodetype): ?Response + protected function handleNodeForm(FormInterface $form, NodeType $nodetype): ?Response { - if ($form->isSubmitted() && $form->isValid()) { - $data = []; - foreach ($form->getData() as $key => $value) { - if ( - (!is_array($value) && $this->notBlank($value)) - || (is_array($value) && isset($value["compareDatetime"])) - || (is_array($value) && isset($value["compareDate"])) - || (is_array($value) && $value != [] && !isset($value["compareOp"])) - ) { - if (\is_string($key) & \str_contains($key, "__node__")) { - /** @var string $newKey */ - $newKey = \str_replace("__node__", "node.", $key); - $data[$newKey] = $value; - } else { - $data[$key] = $value; - } - } - } - $data = $this->processCriteria($data, "node."); - $data = $this->processCriteriaNodetype($data, $nodetype); - - $listManager = $this->createEntityListManager( - $nodetype->getSourceEntityFullQualifiedClassName(), - $data - ); - $listManager->setDisplayingNotPublishedNodes(true); - $listManager->setDisplayingAllNodesStatuses(true); - if ($this->pagination === false) { - $listManager->setItemPerPage($this->itemPerPage ?? 999); - $listManager->disablePagination(); - } - $listManager->handle(); - $entities = $listManager->getEntities(); - $nodes = []; - foreach ($entities as $nodesSource) { - if (!in_array($nodesSource->getNode(), $nodes)) { - $nodes[] = $nodesSource->getNode(); + if (!$form->isSubmitted() || !$form->isValid()) { + return null; + } + $data = []; + foreach ($form->getData() as $key => $value) { + if ( + (!is_array($value) && $this->notBlank($value)) + || (is_array($value) && isset($value["compareDatetime"])) + || (is_array($value) && isset($value["compareDate"])) + || (is_array($value) && $value != [] && !isset($value["compareOp"])) + ) { + if (\is_string($key) & \str_contains($key, "__node__")) { + /** @var string $newKey */ + $newKey = \str_replace("__node__", "node.", $key); + $data[$newKey] = $value; + } else { + $data[$key] = $value; } } - /* - * Export all entries into XLSX format - */ - $button = $form->get('export'); - if ($button instanceof ClickableInterface && $button->isClicked()) { - $filename = 'search-' . $nodetype->getName() . '-' . date("YmdHis") . '.xlsx'; - $this->xlsxSerializer->setOnlyTexts(true); - $this->xlsxSerializer->addUrls(); - $xlsx = $this->xlsxSerializer->serialize($entities); - - $response = new Response( - $xlsx, - Response::HTTP_OK, - [] - ); - - $response->headers->set( - 'Content-Disposition', - $response->headers->makeDisposition( - ResponseHeaderBag::DISPOSITION_ATTACHMENT, - $filename - ) - ); + } + $data = $this->processCriteria($data, "node."); + $data = $this->processCriteriaNodetype($data, $nodetype); - $response->prepare($request); - return $response; + $listManager = $this->createEntityListManager( + $nodetype->getSourceEntityFullQualifiedClassName(), + $data + ); + $listManager->setDisplayingNotPublishedNodes(true); + $listManager->setDisplayingAllNodesStatuses(true); + if ($this->pagination === false) { + $listManager->setItemPerPage($this->itemPerPage ?? 999); + $listManager->disablePagination(); + } + $listManager->handle(); + $entities = $listManager->getEntities(); + $nodes = []; + foreach ($entities as $nodesSource) { + if (!in_array($nodesSource->getNode(), $nodes)) { + $nodes[] = $nodesSource->getNode(); } - - $this->assignation['filters'] = $listManager->getAssignation(); - $this->assignation['nodesSources'] = $entities; - $this->assignation['nodes'] = $nodes; } + /* + * Export all entries into XLSX format + */ + $button = $form->get('export'); + if ($button instanceof ClickableInterface && $button->isClicked()) { + $filename = 'search-' . $nodetype->getName() . '-' . date("YmdHis") . '.csv'; + $response = new StreamedResponse(function () use ($entities) { + echo $this->serializer->serialize($entities, 'csv', [ + 'groups' => [ + 'nodes_sources', + 'urls', + 'tag_base', + 'document_display', + ], + ]); + }); + $response->headers->set('Content-Type', 'text/csv'); + $response->headers->set( + 'Content-Disposition', + $response->headers->makeDisposition( + ResponseHeaderBag::DISPOSITION_ATTACHMENT, + $filename + ) + ); + return $response; + } + + $this->assignation['filters'] = $listManager->getAssignation(); + $this->assignation['nodesSources'] = $entities; + $this->assignation['nodes'] = $nodes; return null; } @@ -559,9 +559,8 @@ protected function createTextSearchForm( /** * @param FormBuilderInterface $builder * @param NodeType $nodetype - * @return FormBuilderInterface */ - private function extendForm(FormBuilderInterface $builder, NodeType $nodetype): FormBuilderInterface + private function extendForm(FormBuilderInterface $builder, NodeType $nodetype): void { $fields = $nodetype->getFields(); @@ -587,8 +586,6 @@ private function extendForm(FormBuilderInterface $builder, NodeType $nodetype): ); } - - /** @var NodeTypeField $field */ foreach ($fields as $field) { $option = ["label" => $field->getLabel()]; $option['required'] = false; @@ -657,6 +654,5 @@ private function extendForm(FormBuilderInterface $builder, NodeType $nodetype): $builder->add($field->getVarName(), $type, $option); } } - return $builder; } } diff --git a/src/Resources/translations/messages.en.xlf b/src/Resources/translations/messages.en.xlf index c4378271..30bc0cc2 100644 --- a/src/Resources/translations/messages.en.xlf +++ b/src/Resources/translations/messages.en.xlf @@ -4069,6 +4069,10 @@ export_format.excel.%language% Excel file format (%language%) + + export_format.csv.%language% + CSV file format (%language%) + delete.tags diff --git a/src/Resources/translations/messages.fr.xlf b/src/Resources/translations/messages.fr.xlf index 1142ebc5..bc91e2e1 100644 --- a/src/Resources/translations/messages.fr.xlf +++ b/src/Resources/translations/messages.fr.xlf @@ -4069,6 +4069,10 @@ export_format.excel.%language% Format de fichier Excel (%language%) + + export_format.csv.%language% + Format de fichier CSV (%language%) + delete.tags diff --git a/src/Resources/translations/messages.xlf b/src/Resources/translations/messages.xlf index e2b993d9..0914a9cd 100644 --- a/src/Resources/translations/messages.xlf +++ b/src/Resources/translations/messages.xlf @@ -1151,6 +1151,7 @@ export_format.rzn export_format.excel.%language% + export_format.csv.%language% delete.tags diff --git a/src/Resources/views/nodes/list.html.twig b/src/Resources/views/nodes/list.html.twig index 6797b9ab..4ee76478 100644 --- a/src/Resources/views/nodes/list.html.twig +++ b/src/Resources/views/nodes/list.html.twig @@ -25,7 +25,7 @@