diff --git a/src/Bundle/ChillMainBundle/Controller/SavedExportController.php b/src/Bundle/ChillMainBundle/Controller/SavedExportController.php index ddda96e19..20b9bf1c5 100644 --- a/src/Bundle/ChillMainBundle/Controller/SavedExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/SavedExportController.php @@ -14,6 +14,7 @@ namespace Chill\MainBundle\Controller; use Chill\MainBundle\Entity\ExportGeneration; use Chill\MainBundle\Entity\SavedExport; use Chill\MainBundle\Entity\User; +use Chill\MainBundle\Export\ExportDescriptionHelper; use Chill\MainBundle\Export\ExportManager; use Chill\MainBundle\Form\SavedExportType; use Chill\MainBundle\Security\Authorization\ExportGenerationVoter; @@ -44,6 +45,7 @@ final readonly class SavedExportController private Security $security, private TranslatorInterface $translator, private UrlGeneratorInterface $urlGenerator, + private ExportDescriptionHelper $exportDescriptionHelper, ) {} #[Route(path: '/{_locale}/exports/saved/{id}/delete', name: 'chill_main_export_saved_delete')] @@ -107,7 +109,21 @@ final readonly class SavedExportController $request->query->has('title') ? $request->query->get('title') : $title ); - return $this->handleEdit($savedExport, $request); + if ($exportGeneration->isLinkedToSavedExport()) { + $savedExport->setDescription($exportGeneration->getSavedExport()->getDescription()); + } else { + $savedExport->setDescription( + implode( + "\n", + array_map( + fn (string $item) => '- '.$item."\n", + $this->exportDescriptionHelper->describe($savedExport->getExportAlias(), $savedExport->getOptions(), includeExportTitle: false) + ) + ) + ); + } + + return $this->handleEdit($savedExport, $request, true); } #[Route(path: '/exports/saved/duplicate-from-saved-export/{id}/new', name: 'chill_main_export_saved_duplicate')] @@ -138,7 +154,7 @@ final readonly class SavedExportController } - private function handleEdit(SavedExport $savedExport, Request $request): Response + private function handleEdit(SavedExport $savedExport, Request $request, bool $showWarningAutoGeneratedDescription = false): Response { $form = $this->formFactory->create(SavedExportType::class, $savedExport); $form->handleRequest($request); @@ -161,6 +177,7 @@ final readonly class SavedExportController '@ChillMain/SavedExport/new.html.twig', [ 'form' => $form->createView(), + 'showWarningAutoGeneratedDescription' => $showWarningAutoGeneratedDescription, ], ), ); diff --git a/src/Bundle/ChillMainBundle/Export/ExportDescriptionHelper.php b/src/Bundle/ChillMainBundle/Export/ExportDescriptionHelper.php new file mode 100644 index 000000000..3016f2f79 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Export/ExportDescriptionHelper.php @@ -0,0 +1,74 @@ + + */ + public function describe(string $exportAlias, array $exportOptions, bool $includeExportTitle = true): array + { + $output = []; + $denormalized = $this->exportConfigNormalizer->denormalizeConfig($exportAlias, $exportOptions); + $user = $this->security->getUser(); + + if ($includeExportTitle) { + $output[] = $this->trans($this->exportManager->getExport($exportAlias)->getTitle()); + } + + if (!$user instanceof User) { + return $output; + } + $context = new ExportGenerationContext($user); + + foreach ($this->exportConfigProcessor->retrieveUsedFilters($denormalized['filters']) as $name => $filter) { + $output[] = $this->trans($filter->describeAction($denormalized['filters'][$name]['form'], $context)); + } + + foreach ($this->exportConfigProcessor->retrieveUsedAggregators($denormalized['aggregators']) as $name => $aggregator) { + $output[] = $this->trans($aggregator->getTitle()); + } + + return $output; + } + + private function trans(string|TranslatableInterface|array $translatable): string + { + if (is_string($translatable)) { + return $this->translator->trans($translatable); + } + + if ($translatable instanceof TranslatableInterface) { + return $translatable->trans($this->translator); + } + + // array case + return $this->translator->trans($translatable[0], $translatable[1] ?? []); + } +} diff --git a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/new.html.twig b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/new.html.twig index 3fddcd270..d6e3e3eee 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/new.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/new.html.twig @@ -18,6 +18,13 @@ {{ form_start(form) }} {{ form_row(form.title) }} + + {% if showWarningAutoGeneratedDescription|default(false) %} + + {% endif %} + {{ form_row(form.description) }} {% if form.share is defined %} diff --git a/src/Bundle/ChillMainBundle/Tests/Export/ExportDescriptionHelperTest.php b/src/Bundle/ChillMainBundle/Tests/Export/ExportDescriptionHelperTest.php new file mode 100644 index 000000000..75638dd0f --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Export/ExportDescriptionHelperTest.php @@ -0,0 +1,174 @@ +prophesize(Security::class); + $security->getUser()->willReturn($user = new User()); + + $exportConfigNormalizer = $this->prophesize(ExportConfigNormalizer::class); + $exportConfigNormalizer->denormalizeConfig('my_export', Argument::type('array'))->willReturn($options); + + $export = $this->prophesize(ExportInterface::class); + $export->getTitle()->willReturn('Title'); + + $myFilterString = $this->prophesize(FilterInterface::class); + $myFilterString->describeAction(Argument::type('array'), Argument::type(ExportGenerationContext::class))->willReturn($string0 = 'This is a filter description'); + $myFilterArray = $this->prophesize(FilterInterface::class); + $myFilterArray->describeAction(Argument::type('array'), Argument::type(ExportGenerationContext::class))->willReturn([$string1 = 'This is a filter with %argument%', $arg1 = ['%argument%' => 'zero']]); + $myFilterTranslatable = $this->prophesize(FilterInterface::class); + $myFilterTranslatable->describeAction(Argument::type('array'), Argument::type(ExportGenerationContext::class)) + ->willReturn(new class () implements TranslatableInterface { + public function trans(TranslatorInterface $translator, ?string $locale = null): string + { + return 'translatable'; + } + }); + + $myAggregator = $this->prophesize(AggregatorInterface::class); + $myAggregator->getTitle()->willReturn('Some aggregator'); + + $token = new UsernamePasswordToken($user, 'main', ['ROLE_USER']); + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken($token); + + $exportManager = new ExportManager( + new NullLogger(), + $security->reveal(), + $this->prophesize(AuthorizationHelperInterface::class)->reveal(), + $tokenStorage, + ['my_export' => $export->reveal()], + ['my_aggregator' => $myAggregator->reveal()], + [ + 'my_filter_string' => $myFilterString->reveal(), + 'my_filter_array' => $myFilterArray->reveal(), + 'my_filter_translatable' => $myFilterTranslatable->reveal(), + ], + [], + ); + + $exportConfigProcessor = new ExportConfigProcessor($exportManager); + + $translator = $this->prophesize(TranslatorInterface::class); + $translator->trans('Title')->shouldBeCalled()->willReturn('Title'); + $translator->trans($string0)->shouldBeCalled()->willReturn($string0); + $translator->trans($string1, $arg1)->shouldBeCalled()->willReturn($string1); + $translator->trans('Some aggregator')->shouldBeCalled()->willReturn('Some aggregator'); + + $exportDescriptionHelper = new ExportDescriptionHelper( + $exportManager, + $exportConfigNormalizer->reveal(), + $exportConfigProcessor, + $translator->reveal(), + $security->reveal(), + ); + + $actual = $exportDescriptionHelper->describe('my_export', $options); + + self::assertIsArray($actual); + self::assertEquals($actual[0], 'Title'); + self::assertEquals($actual[1], 'This is a filter description'); + self::assertEquals($actual[2], 'This is a filter with %argument%'); + self::assertEquals($actual[3], 'translatable'); + self::assertEquals($actual[4], 'Some aggregator'); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php index 6004a6012..d52129af7 100644 --- a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php @@ -348,7 +348,6 @@ final class ExportManagerTest extends KernelTestCase $logger ?? self::getContainer()->get(LoggerInterface::class), $authorizationChecker ?? self::getContainer()->get('security.authorization_checker'), $authorizationHelper ?? self::getContainer()->get('chill.main.security.authorization.helper'), - $tokenStorage, $exports, $aggregators, $filters, diff --git a/src/Bundle/ChillMainBundle/config/services/export.yaml b/src/Bundle/ChillMainBundle/config/services/export.yaml index acbb53f8a..3027532e1 100644 --- a/src/Bundle/ChillMainBundle/config/services/export.yaml +++ b/src/Bundle/ChillMainBundle/config/services/export.yaml @@ -18,6 +18,8 @@ services: Chill\MainBundle\Export\ExportConfigProcessor: ~ + Chill\MainBundle\Export\ExportDescriptionHelper: ~ + chill.main.export_element_validator: class: Chill\MainBundle\Validator\Constraints\Export\ExportElementConstraintValidator tags: diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index a2d718143..1141f50fc 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -807,6 +807,7 @@ saved_export: Duplicated: Dupliqué Options updated successfully: La configuration de l'export a été mise à jour Share: Partage + Alert auto generated description: La description ci-dessous a été générée automatiquement, comme si l'export était exécutée immédiatement. Veillez à l'adapter pour tenir compte des paramètres qui peuvent être modifiés (utilisateurs courant, dates glissantes, etc.). absence: # single letter for absence