Add support for audit display and conversion of AccompanyingPeriodWorkEvaluationDocument entities

- Introduced `AccompanyingPeriodWorkEvaluationDocumentSubjectConverter` for converting entities into audit subjects.
- Added `AccompanyingPeriodWorkEvaluationDocumentSubjectDisplayer` for translating and displaying audit subjects.
- Added necessary translation keys for display messages.
- Implemented unit tests for both the converter and displayer to ensure correctness and reliability.
- Updated `AccompanyingPeriodWorkSubjectConverter` to handle related accompanying periods.
This commit is contained in:
2026-03-17 12:49:58 +01:00
parent eba4527093
commit 09aba2e5ed
6 changed files with 306 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Audit\Displayer;
use Chill\MainBundle\Audit\Subject;
use Chill\MainBundle\Audit\SubjectDisplayerInterface;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocumentRepository;
use Symfony\Contracts\Translation\TranslatorInterface;
class AccompanyingPeriodWorkEvaluationDocumentSubjectDisplayer implements SubjectDisplayerInterface
{
public function __construct(
private readonly AccompanyingPeriodWorkEvaluationDocumentRepository $repository,
private readonly TranslatorInterface $translator,
) {}
public function supportsDisplay(Subject $subject, array $options = []): bool
{
return 'accompanying_period_work_evaluation_document' === $subject->type;
}
public function display(Subject $subject, string $format = 'html', array $options = []): string
{
$id = $subject->identifiers['id'];
$msg = $this->translator->trans('audit.accompanying_period_work_evaluation_document.display', ['{id}' => $id]);
if ('html' === $format) {
$document = $this->repository->find($id);
if (null !== $document && null !== $title = $document->getTitle()) {
$msg .= ': '.htmlspecialchars($title);
}
return '<span>'.$msg.'</span>';
}
return $msg;
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Audit\SubjectConverter;
use Chill\MainBundle\Audit\Subject;
use Chill\MainBundle\Audit\SubjectBag;
use Chill\MainBundle\Audit\SubjectConverterInterface;
use Chill\MainBundle\Audit\SubjectConverterManagerAwareInterface;
use Chill\MainBundle\Audit\SubjectConverterManagerAwareTrait;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
/**
* @implements SubjectConverterInterface<AccompanyingPeriodWorkEvaluationDocument>
*/
class AccompanyingPeriodWorkEvaluationDocumentSubjectConverter implements SubjectConverterInterface, SubjectConverterManagerAwareInterface
{
use SubjectConverterManagerAwareTrait;
public function convert(mixed $subject, bool $includeAssociated = false): SubjectBag
{
$data = new SubjectBag(new Subject('accompanying_period_work_evaluation_document', ['id' => $subject->getId()]));
if (null !== $accompanyingPeriodWork = $subject->getAccompanyingPeriodWorkEvaluation()?->getAccompanyingPeriodWork()) {
$data->append($this->subjectConverterManager->getSubjectsForEntity($accompanyingPeriodWork, $includeAssociated));
}
return $data;
}
public function supportsConvert(mixed $subject): bool
{
return $subject instanceof AccompanyingPeriodWorkEvaluationDocument;
}
public static function getDefaultPriority(): int
{
return 10;
}
}

View File

@@ -29,6 +29,8 @@ class AccompanyingPeriodWorkSubjectConverter implements SubjectConverterInterfac
{
$data = new SubjectBag(new Subject('accompanying_period_work', ['id' => $subject->getId()]));
$data->append($this->subjectConverterManager->getSubjectsForEntity($subject->getAccompanyingPeriod()));
foreach ($subject->getPersons() as $person) {
$data->append($this->subjectConverterManager->getSubjectsForEntity($person));
}

View File

@@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Tests\Audit\Displayer;
use Chill\MainBundle\Audit\Subject;
use Chill\PersonBundle\Audit\Displayer\AccompanyingPeriodWorkEvaluationDocumentSubjectDisplayer;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocumentRepository;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @internal
*
* @covers \Chill\PersonBundle\Audit\Displayer\AccompanyingPeriodWorkEvaluationDocumentSubjectDisplayer
*/
class AccompanyingPeriodWorkEvaluationDocumentSubjectDisplayerTest extends TestCase
{
use ProphecyTrait;
private $repository;
private $translator;
private AccompanyingPeriodWorkEvaluationDocumentSubjectDisplayer $displayer;
protected function setUp(): void
{
$this->repository = $this->prophesize(AccompanyingPeriodWorkEvaluationDocumentRepository::class);
$this->translator = $this->prophesize(TranslatorInterface::class);
$this->displayer = new AccompanyingPeriodWorkEvaluationDocumentSubjectDisplayer(
$this->repository->reveal(),
$this->translator->reveal()
);
}
public function testSupportsDisplay(): void
{
$subject = new Subject('accompanying_period_work_evaluation_document', ['id' => 123]);
$this->assertTrue($this->displayer->supportsDisplay($subject));
$otherSubject = new Subject('other', ['id' => 123]);
$this->assertFalse($this->displayer->supportsDisplay($otherSubject));
}
public function testDisplayStringFormat(): void
{
$id = 123;
$subject = new Subject('accompanying_period_work_evaluation_document', ['id' => $id]);
$this->translator->trans('audit.accompanying_period_work_evaluation_document.display', ['{id}' => $id])
->willReturn('Translated document 123');
$result = $this->displayer->display($subject, 'string');
$this->assertSame('Translated document 123', $result);
}
public function testDisplayHtmlFormatWithoutDocument(): void
{
$id = 123;
$subject = new Subject('accompanying_period_work_evaluation_document', ['id' => $id]);
$this->repository->find($id)->willReturn(null);
$this->translator->trans('audit.accompanying_period_work_evaluation_document.display', ['{id}' => $id])
->willReturn('Translated document 123');
$result = $this->displayer->display($subject, 'html');
$this->assertSame('<span>Translated document 123</span>', $result);
}
public function testDisplayHtmlFormatWithDocumentAndTitle(): void
{
$id = 123;
$subject = new Subject('accompanying_period_work_evaluation_document', ['id' => $id]);
$document = $this->prophesize(AccompanyingPeriodWorkEvaluationDocument::class);
$document->getTitle()->willReturn('My Document Title');
$this->repository->find($id)->willReturn($document->reveal());
$this->translator->trans('audit.accompanying_period_work_evaluation_document.display', ['{id}' => $id])
->willReturn('Translated document 123');
$result = $this->displayer->display($subject, 'html');
$this->assertSame('<span>Translated document 123: My Document Title</span>', $result);
}
public function testDisplayHtmlFormatWithXssInTitle(): void
{
$id = 123;
$subject = new Subject('accompanying_period_work_evaluation_document', ['id' => $id]);
$document = $this->prophesize(AccompanyingPeriodWorkEvaluationDocument::class);
$document->getTitle()->willReturn('Title <script>alert("XSS")</script>');
$this->repository->find($id)->willReturn($document->reveal());
$this->translator->trans('audit.accompanying_period_work_evaluation_document.display', ['{id}' => $id])
->willReturn('Translated document 123');
$result = $this->displayer->display($subject, 'html');
$expectedTitle = htmlspecialchars('Title <script>alert("XSS")</script>');
$this->assertSame('<span>Translated document 123: '.$expectedTitle.'</span>', $result);
}
}

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Tests\Audit\SubjectConverter;
use Chill\MainBundle\Audit\Subject;
use Chill\MainBundle\Audit\SubjectBag;
use Chill\MainBundle\Audit\SubjectConverterManagerInterface;
use Chill\PersonBundle\Audit\SubjectConverter\AccompanyingPeriodWorkEvaluationDocumentSubjectConverter;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
/**
* @internal
*
* @coversNothing
*/
class AccompanyingPeriodWorkEvaluationDocumentSubjectConverterTest extends TestCase
{
use ProphecyTrait;
private AccompanyingPeriodWorkEvaluationDocumentSubjectConverter $converter;
protected function setUp(): void
{
$this->converter = new AccompanyingPeriodWorkEvaluationDocumentSubjectConverter();
}
public function testSupportsConvert(): void
{
$this->assertTrue($this->converter->supportsConvert($this->prophesize(AccompanyingPeriodWorkEvaluationDocument::class)->reveal()));
$this->assertFalse($this->converter->supportsConvert(new \stdClass()));
}
public function testConvert(): void
{
$document = $this->prophesize(AccompanyingPeriodWorkEvaluationDocument::class);
$document->getId()->willReturn(123);
$evaluation = $this->prophesize(AccompanyingPeriodWorkEvaluation::class);
$work = $this->prophesize(AccompanyingPeriodWork::class);
$document->getAccompanyingPeriodWorkEvaluation()->willReturn($evaluation->reveal());
$evaluation->getAccompanyingPeriodWork()->willReturn($work->reveal());
$subjectConverterManager = $this->prophesize(SubjectConverterManagerInterface::class);
$workSubject = new Subject('accompanying_period_work', ['id' => 456]);
$workBag = new SubjectBag($workSubject);
$subjectConverterManager->getSubjectsForEntity($work->reveal(), false)->willReturn($workBag);
$this->converter->setSubjectConverterManager($subjectConverterManager->reveal());
$result = $this->converter->convert($document->reveal());
$this->assertSame('accompanying_period_work_evaluation_document', $result->subject->type);
$this->assertSame(['id' => 123], $result->subject->identifiers);
$this->assertCount(1, $result->associatedSubjects);
$this->assertSame($workSubject, $result->associatedSubjects[0]);
}
public function testConvertWithoutEvaluation(): void
{
$document = $this->prophesize(AccompanyingPeriodWorkEvaluationDocument::class);
$document->getId()->willReturn(123);
$document->getAccompanyingPeriodWorkEvaluation()->willReturn(null);
$result = $this->converter->convert($document->reveal());
$this->assertSame('accompanying_period_work_evaluation_document', $result->subject->type);
$this->assertEmpty($result->associatedSubjects);
}
public function testGetDefaultPriority(): void
{
$this->assertSame(10, AccompanyingPeriodWorkEvaluationDocumentSubjectConverter::getDefaultPriority());
}
}

View File

@@ -1598,6 +1598,8 @@ audit:
accompanying_period_work:
accompanying_period_work_number: "Action d'accompagnement n°{id}"
list: Liste des actions d'accompagnement d'un parcours
accompanying_period_work_evaluation_document:
display: "Document d'évaluation n°{id}"
person_resource:
list: Liste des personnes ressources
person_resource_number: "Personne ressource n°{id}: {name}"