diff --git a/src/Bundle/ChillMainBundle/Audit/Converter/ExportGenerationSubjectConverter.php b/src/Bundle/ChillMainBundle/Audit/Converter/ExportGenerationSubjectConverter.php
new file mode 100644
index 000000000..a1b2803be
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Audit/Converter/ExportGenerationSubjectConverter.php
@@ -0,0 +1,37 @@
+ $subject->getId(), 'alias' => $subject->getExportAlias()]));
+ }
+
+ public function supportsConvert(mixed $subject): bool
+ {
+ return $subject instanceof ExportGeneration;
+ }
+
+ public static function getDefaultPriority(): int
+ {
+ return 0;
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Audit/Displayer/ExportGenerationSubjectDisplayer.php b/src/Bundle/ChillMainBundle/Audit/Displayer/ExportGenerationSubjectDisplayer.php
new file mode 100644
index 000000000..b06662ea1
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Audit/Displayer/ExportGenerationSubjectDisplayer.php
@@ -0,0 +1,49 @@
+type;
+ }
+
+ public function display(Subject $subject, string $format = 'html', array $options = []): string
+ {
+ $export = $this->exportGenerationRepository->find($subject->identifiers['id']);
+
+ $msg = match (null === $export || '' === $export->getStoredObject()->getTitle()) {
+ true => $this->translator->trans('audit.export_generation.display_without_entity', ['{{ id }}' => $subject->identifiers['id']]),
+ false => $this->translator->trans('audit.export_generation.display_with_entity', [
+ '{{ id }}' => $subject->identifiers['id'],
+ '{{ title }}' => 'html' === $format ? htmlspecialchars($export->getStoredObject()->getTitle()) : $export->getStoredObject()->getTitle(),
+ ]),
+ };
+
+ if ('html' === $format) {
+ return ''.$msg.'';
+ }
+
+ return $msg;
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Tests/Audit/Converter/ExportGenerationSubjectConverterTest.php b/src/Bundle/ChillMainBundle/Tests/Audit/Converter/ExportGenerationSubjectConverterTest.php
new file mode 100644
index 000000000..92467cb6a
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Tests/Audit/Converter/ExportGenerationSubjectConverterTest.php
@@ -0,0 +1,73 @@
+converter = new ExportGenerationSubjectConverter();
+ }
+
+ public function testSupportsConvert(): void
+ {
+ $exportGeneration = $this->createMock(ExportGeneration::class);
+ $this->assertTrue($this->converter->supportsConvert($exportGeneration));
+
+ $this->assertFalse($this->converter->supportsConvert(new \stdClass()));
+ }
+
+ public function testConvert(): void
+ {
+ $exportAlias = 'my_export_alias';
+ $exportGeneration = new ExportGeneration($exportAlias);
+ $id = $exportGeneration->getId();
+
+ $subjectBag = $this->converter->convert($exportGeneration);
+
+ $subject = $subjectBag->subject;
+ $this->assertSame('export_generation', $subject->type);
+ $this->assertSame($id, $subject->identifiers['id']);
+ $this->assertSame($exportAlias, $subject->identifiers['alias']);
+ }
+
+ public function testConvertWithIncludeAssociated(): void
+ {
+ $exportAlias = 'my_export_alias';
+ $exportGeneration = new ExportGeneration($exportAlias);
+ $id = $exportGeneration->getId();
+
+ $subjectBag = $this->converter->convert($exportGeneration, true);
+
+ $subject = $subjectBag->subject;
+ $this->assertSame('export_generation', $subject->type);
+ $this->assertSame($id, $subject->identifiers['id']);
+ $this->assertSame($exportAlias, $subject->identifiers['alias']);
+ $this->assertEmpty($subjectBag->associatedSubjects);
+ }
+
+ public function testGetDefaultPriority(): void
+ {
+ $this->assertSame(0, ExportGenerationSubjectConverter::getDefaultPriority());
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Tests/Audit/Displayer/ExportGenerationSubjectDisplayerTest.php b/src/Bundle/ChillMainBundle/Tests/Audit/Displayer/ExportGenerationSubjectDisplayerTest.php
new file mode 100644
index 000000000..8acb7594a
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Tests/Audit/Displayer/ExportGenerationSubjectDisplayerTest.php
@@ -0,0 +1,142 @@
+exportGenerationRepository = $this->prophesize(ExportGenerationRepository::class);
+ $this->translator = $this->prophesize(TranslatorInterface::class);
+ $this->displayer = new ExportGenerationSubjectDisplayer(
+ $this->exportGenerationRepository->reveal(),
+ $this->translator->reveal()
+ );
+ }
+
+ public function testSupportsDisplay(): void
+ {
+ $subject = new Subject('export_generation', ['id' => 'foo']);
+ $this->assertTrue($this->displayer->supportsDisplay($subject));
+
+ $otherSubject = new Subject('other', ['id' => 'foo']);
+ $this->assertFalse($this->displayer->supportsDisplay($otherSubject));
+ }
+
+ public function testDisplayWithEntityNotFound(): void
+ {
+ $id = '550e8400-e29b-41d4-a716-446655440000';
+ $subject = new Subject('export_generation', ['id' => $id]);
+
+ $this->exportGenerationRepository->find($id)->willReturn(null);
+
+ $this->translator->trans('audit.export_generation.display_without_entity', ['{{ id }}' => $id])
+ ->willReturn('Export not found '.$id);
+
+ $result = $this->displayer->display($subject, 'text');
+
+ $this->assertSame('Export not found '.$id, $result);
+ }
+
+ public function testDisplayWithEntityFound(): void
+ {
+ $id = '550e8400-e29b-41d4-a716-446655440000';
+ $subject = new Subject('export_generation', ['id' => $id]);
+
+ $exportGeneration = new ExportGeneration('my_alias');
+ $exportGeneration->getStoredObject()->setTitle('My Export Title');
+
+ $this->exportGenerationRepository->find($id)->willReturn($exportGeneration);
+
+ $this->translator->trans('audit.export_generation.display_with_entity', [
+ '{{ id }}' => $id,
+ '{{ title }}' => 'My Export Title',
+ ])->willReturn('Export '.$id.' with title My Export Title');
+
+ $result = $this->displayer->display($subject, 'text');
+
+ $this->assertSame('Export '.$id.' with title My Export Title', $result);
+ }
+
+ public function testDisplayWithEmptyTitle(): void
+ {
+ $id = '550e8400-e29b-41d4-a716-446655440000';
+ $subject = new Subject('export_generation', ['id' => $id]);
+
+ $exportGeneration = new ExportGeneration('my_alias');
+ $exportGeneration->getStoredObject()->setTitle('');
+
+ $this->exportGenerationRepository->find($id)->willReturn($exportGeneration);
+
+ $this->translator->trans('audit.export_generation.display_without_entity', ['{{ id }}' => $id])
+ ->willReturn('Export not found '.$id);
+
+ $result = $this->displayer->display($subject, 'text');
+
+ $this->assertSame('Export not found '.$id, $result);
+ }
+
+ public function testDisplayHtmlFormatWrapsInSpan(): void
+ {
+ $id = '550e8400-e29b-41d4-a716-446655440000';
+ $subject = new Subject('export_generation', ['id' => $id]);
+
+ $this->exportGenerationRepository->find($id)->willReturn(null);
+
+ $this->translator->trans('audit.export_generation.display_without_entity', ['{{ id }}' => $id])
+ ->willReturn('Translated Msg');
+
+ $result = $this->displayer->display($subject, 'html');
+
+ $this->assertSame('Translated Msg', $result);
+ }
+
+ public function testDisplayHtmlFormatEscapesTitle(): void
+ {
+ $id = '550e8400-e29b-41d4-a716-446655440000';
+ $subject = new Subject('export_generation', ['id' => $id]);
+
+ $exportGeneration = new ExportGeneration('my_alias');
+ $exportGeneration->getStoredObject()->setTitle('Title with ');
+
+ $this->exportGenerationRepository->find($id)->willReturn($exportGeneration);
+
+ $escapedTitle = 'Title with <script>alert("xss")</script>';
+ $this->translator->trans('audit.export_generation.display_with_entity', [
+ '{{ id }}' => $id,
+ '{{ title }}' => $escapedTitle,
+ ])->willReturn('Export with title '.$escapedTitle);
+
+ $result = $this->displayer->display($subject, 'html');
+
+ $this->assertSame('Export with title '.$escapedTitle.'', $result);
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml
index fece175c6..f7ac4e306 100644
--- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml
+++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml
@@ -1046,3 +1046,8 @@ audit_trail:
waiting: En cours de préparation
failure: Erreur lors de la préparation du document
stopped: La préparation a nécessité une attente trop longue, veuillez recharger la page
+
+audit:
+ export_generation:
+ display_without_entity: "Génération d'export n°{{ id }}"
+ display_with_entity: "Génération d'export n°{{ id }}: {{ title }}"