diff --git a/src/Bundle/ChillMainBundle/Resources/views/Password/request_recover_password.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Password/request_recover_password.html.twig
index 43a2d4674..2ff32f2c1 100644
--- a/src/Bundle/ChillMainBundle/Resources/views/Password/request_recover_password.html.twig
+++ b/src/Bundle/ChillMainBundle/Resources/views/Password/request_recover_password.html.twig
@@ -22,7 +22,7 @@
{% block title %}{{"Recover password"|trans}}{% endblock %}
-{% block content %}
+{% block password_content %}
{{ 'Recover password'|trans }}
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Password/request_recover_password_confirm.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Password/request_recover_password_confirm.html.twig
index 4fb924077..8014179e5 100644
--- a/src/Bundle/ChillMainBundle/Resources/views/Password/request_recover_password_confirm.html.twig
+++ b/src/Bundle/ChillMainBundle/Resources/views/Password/request_recover_password_confirm.html.twig
@@ -2,7 +2,7 @@
{% block title "Check your email"|trans %}
-{% block content %}
+{% block password_content %}
diff --git a/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig b/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig
index 30f84c855..b6da857b2 100644
--- a/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig
+++ b/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig
@@ -30,7 +30,11 @@
{{ include('@ChillMain/Layout/_debug.html.twig') }}
{% endif %}
- {{ include('@ChillMain/Layout/_header.html.twig') }}
+ {% if header_logo_only is defined and header_logo_only == 1 %}
+ {{ include('@ChillMain/Layout/_header_logo_only.html.twig') }}
+ {% else %}
+ {{ include('@ChillMain/Layout/_header.html.twig') }}
+ {% endif %}
{% block top_banner %}{#
To use if you want to add a banner below the header (ie the menu)
diff --git a/src/Bundle/ChillPersonBundle/ChillPersonBundle.php b/src/Bundle/ChillPersonBundle/ChillPersonBundle.php
index e97ccecdf..2d567aaa2 100644
--- a/src/Bundle/ChillPersonBundle/ChillPersonBundle.php
+++ b/src/Bundle/ChillPersonBundle/ChillPersonBundle.php
@@ -15,6 +15,7 @@ use Chill\MainBundle\Notification\FlagProviders\NotificationFlagProviderInterfac
use Chill\PersonBundle\Actions\Remove\PersonMoveSqlHandlerInterface;
use Chill\PersonBundle\DependencyInjection\CompilerPass\AccompanyingPeriodTimelineCompilerPass;
use Chill\PersonBundle\Export\Helper\CustomizeListPersonHelperInterface;
+use Chill\PersonBundle\PersonIdentifier\PersonIdentifierEngineInterface;
use Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface;
use Chill\PersonBundle\Widget\PersonListWidgetFactory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -38,5 +39,7 @@ class ChillPersonBundle extends Bundle
->addTag('chill_person.list_person_customizer');
$container->registerForAutoconfiguration(NotificationFlagProviderInterface::class)
->addTag('chill_main.notification_flag_provider');
+ $container->registerForAutoconfiguration(PersonIdentifierEngineInterface::class)
+ ->addTag('chill_person.person_identifier_engine');
}
}
diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonController.php b/src/Bundle/ChillPersonBundle/Controller/PersonController.php
index 9f35e1b5c..4698373f1 100644
--- a/src/Bundle/ChillPersonBundle/Controller/PersonController.php
+++ b/src/Bundle/ChillPersonBundle/Controller/PersonController.php
@@ -17,7 +17,6 @@ use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Form\CreationPersonType;
-use Chill\PersonBundle\Form\PersonType;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Search\SimilarPersonMatcher;
@@ -49,56 +48,6 @@ final class PersonController extends AbstractController
private readonly EntityManagerInterface $em,
) {}
- #[Route(path: '/{_locale}/person/{person_id}/general/edit', name: 'chill_person_general_edit')]
- public function editAction(int $person_id, Request $request)
- {
- $person = $this->_getPerson($person_id);
-
- if (null === $person) {
- throw $this->createNotFoundException();
- }
-
- $this->denyAccessUnlessGranted(
- 'CHILL_PERSON_UPDATE',
- $person,
- 'You are not allowed to edit this person'
- );
-
- $form = $this->createForm(
- PersonType::class,
- $person,
- [
- 'cFGroup' => $this->getCFGroup(),
- ]
- );
-
- $form->handleRequest($request);
-
- if ($form->isSubmitted() && !$form->isValid()) {
- $this->get('session')
- ->getFlashBag()->add('error', $this->translator
- ->trans('This form contains errors'));
- } elseif ($form->isSubmitted() && $form->isValid()) {
- $this->em->flush();
-
- $this->get('session')->getFlashBag()
- ->add(
- 'success',
- $this->translator
- ->trans('The person data has been updated')
- );
-
- return $this->redirectToRoute('chill_person_view', [
- 'person_id' => $person->getId(),
- ]);
- }
-
- return $this->render(
- '@ChillPerson/Person/edit.html.twig',
- ['person' => $person, 'form' => $form->createView()]
- );
- }
-
public function getCFGroup()
{
$cFGroup = null;
diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonEditController.php b/src/Bundle/ChillPersonBundle/Controller/PersonEditController.php
new file mode 100644
index 000000000..be5b87bc3
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Controller/PersonEditController.php
@@ -0,0 +1,79 @@
+security->isGranted(PersonVoter::UPDATE, $person)) {
+ throw new AccessDeniedHttpException('You are not allowed to edit this person.');
+ }
+
+ $form = $this->formFactory->create(
+ PersonType::class,
+ $person,
+ ['cFGroup' => $this->customFieldsDefaultGroupRepository->findOneByEntity(Person::class)?->getCustomFieldsGroup()]
+ );
+
+ $form->handleRequest($request);
+
+ if ($form->isSubmitted() && !$form->isValid()) {
+ $session
+ ->getFlashBag()->add('error', new TranslatableMessage('This form contains errors'));
+ } elseif ($form->isSubmitted() && $form->isValid()) {
+ $this->entityManager->flush();
+
+ $session->getFlashBag()->add('success', new TranslatableMessage('The person data has been updated'));
+
+ return new RedirectResponse(
+ $this->urlGenerator->generate('chill_person_view', ['person_id' => $person->getId()])
+ );
+ }
+
+ return new Response($this->twig->render('@ChillPerson/Person/edit.html.twig', [
+ 'form' => $form->createView(),
+ 'person' => $person,
+ ]));
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/Configuration.php b/src/Bundle/ChillPersonBundle/DependencyInjection/Configuration.php
index 12c8b4c5b..da87ae050 100644
--- a/src/Bundle/ChillPersonBundle/DependencyInjection/Configuration.php
+++ b/src/Bundle/ChillPersonBundle/DependencyInjection/Configuration.php
@@ -110,6 +110,24 @@ class Configuration implements ConfigurationInterface
->end()
->end() // children for 'person_fields', parent = array 'person_fields'
->end() // person_fields, parent = children of root
+ ->arrayNode('person_render')
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('id_content_text')
+ ->defaultValue('n°[[ person_id ]]')
+ ->info(
+ <<<'EOF'
+ The way we display the person's id. Variables availables: "[[ person_id ]]", or, for person's
+ identifier: "[[ identifier_xx ]]" where xx is the identifier's definition's id.
+
+ There are also conditions available: "[[ if:identifier_yy ]] [[ identifier_yy ]] [[ endif:identifier_yy ]]"
+
+ Take care of keeping exactly one space between "[[" and the placeholder's content, and exactly one space before "]]"
+ EOF
+ )
+ ->end()
+ ->end() // end of person_render's children
+ ->end() // end of person_render
->arrayNode('household_fields')
->canBeDisabled()
->children()
diff --git a/src/Bundle/ChillPersonBundle/Entity/Identifier/PersonIdentifier.php b/src/Bundle/ChillPersonBundle/Entity/Identifier/PersonIdentifier.php
new file mode 100644
index 000000000..f0dea00b4
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Entity/Identifier/PersonIdentifier.php
@@ -0,0 +1,83 @@
+ '[]', 'jsonb' => true])]
+ private array $value = [];
+
+ #[ORM\Column(name: 'canonical', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '[]'])]
+ private string $canonical = '';
+
+ public function __construct(
+ #[ORM\ManyToOne(targetEntity: PersonIdentifierDefinition::class)]
+ #[ORM\JoinColumn(name: 'definition_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
+ private PersonIdentifierDefinition $definition,
+ ) {}
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function setPerson(?Person $person): self
+ {
+ $this->person = $person;
+
+ return $this;
+ }
+
+ public function getPerson(): Person
+ {
+ return $this->person;
+ }
+
+ public function getValue(): array
+ {
+ return $this->value;
+ }
+
+ public function setValue(array $value): void
+ {
+ $this->value = $value;
+ }
+
+ public function getCanonical(): string
+ {
+ return $this->canonical;
+ }
+
+ public function setCanonical(string $canonical): void
+ {
+ $this->canonical = $canonical;
+ }
+
+ public function getDefinition(): PersonIdentifierDefinition
+ {
+ return $this->definition;
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Entity/Identifier/PersonIdentifierDefinition.php b/src/Bundle/ChillPersonBundle/Entity/Identifier/PersonIdentifierDefinition.php
new file mode 100644
index 000000000..6d6112569
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Entity/Identifier/PersonIdentifierDefinition.php
@@ -0,0 +1,107 @@
+ true])]
+ private bool $active = true;
+
+ public function __construct(
+ #[ORM\Column(name: 'label', type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]'])]
+ private array $label,
+ #[ORM\Column(name: 'engine', type: \Doctrine\DBAL\Types\Types::STRING, length: 100)]
+ private string $engine,
+ #[ORM\Column(name: 'is_searchable', type: \Doctrine\DBAL\Types\Types::BOOLEAN, options: ['default' => false])]
+ private bool $isSearchable = false,
+ #[ORM\Column(name: 'is_editable_by_users', type: \Doctrine\DBAL\Types\Types::BOOLEAN, nullable: false, options: ['default' => false])]
+ private bool $isEditableByUsers = false,
+ #[ORM\Column(name: 'data', type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]', 'jsonb' => true])]
+ private array $data = [],
+ ) {}
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getLabel(): array
+ {
+ return $this->label;
+ }
+
+ public function setLabel(array $label): void
+ {
+ $this->label = $label;
+ }
+
+ public function getEngine(): string
+ {
+ return $this->engine;
+ }
+
+ public function setEngine(string $engine): void
+ {
+ $this->engine = $engine;
+ }
+
+ public function isSearchable(): bool
+ {
+ return $this->isSearchable;
+ }
+
+ public function setIsSearchable(bool $isSearchable): void
+ {
+ $this->isSearchable = $isSearchable;
+ }
+
+ public function isEditableByUsers(): bool
+ {
+ return $this->isEditableByUsers;
+ }
+
+ public function setIsEditableByUsers(bool $isEditableByUsers): void
+ {
+ $this->isEditableByUsers = $isEditableByUsers;
+ }
+
+ public function isActive(): bool
+ {
+ return $this->active;
+ }
+
+ public function setActive(bool $active): self
+ {
+ $this->active = $active;
+
+ return $this;
+ }
+
+ public function getData(): array
+ {
+ return $this->data;
+ }
+
+ public function setData(array $data): void
+ {
+ $this->data = $data;
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php
index 5d57f11af..aa78774b6 100644
--- a/src/Bundle/ChillPersonBundle/Entity/Person.php
+++ b/src/Bundle/ChillPersonBundle/Entity/Person.php
@@ -31,6 +31,7 @@ use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
+use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\Entity\Person\PersonCenterCurrent;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Entity\Person\PersonCurrentAddress;
@@ -271,6 +272,9 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
#[ORM\GeneratedValue(strategy: 'AUTO')]
private ?int $id = null;
+ #[ORM\OneToMany(mappedBy: 'person', targetEntity: PersonIdentifier::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+ private Collection $identifiers;
+
/**
* The person's last name.
*/
@@ -418,6 +422,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
$this->resources = new ArrayCollection();
$this->centerHistory = new ArrayCollection();
$this->signatures = new ArrayCollection();
+ $this->identifiers = new ArrayCollection();
}
public function __toString(): string
@@ -498,6 +503,24 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
return $this;
}
+ public function addIdentifier(PersonIdentifier $identifier): self
+ {
+ if (!$this->identifiers->contains($identifier)) {
+ $this->identifiers[] = $identifier;
+ $identifier->setPerson($this);
+ }
+
+ return $this;
+ }
+
+ public function removeIdentifier(PersonIdentifier $identifier): self
+ {
+ $this->identifiers->removeElement($identifier);
+ $identifier->setPerson(null);
+
+ return $this;
+ }
+
public function removeSignature(EntityWorkflowStepSignature $signature): self
{
$this->signatures->removeElement($signature);
@@ -1129,6 +1152,14 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
return $this->id;
}
+ /**
+ * @return ReadableCollection
+ */
+ public function getIdentifiers(): ReadableCollection
+ {
+ return $this->identifiers;
+ }
+
/**
* @return string
*/
@@ -1262,6 +1293,22 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
return $this->spokenLanguages;
}
+ public function addSpokenLanguage(Language $language): self
+ {
+ if (!$this->spokenLanguages->contains($language)) {
+ $this->spokenLanguages->add($language);
+ }
+
+ return $this;
+ }
+
+ public function removeSpokenLanguage(Language $language): self
+ {
+ $this->spokenLanguages->removeElement($language);
+
+ return $this;
+ }
+
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
diff --git a/src/Bundle/ChillPersonBundle/Form/DataMapper/PersonIdentifiersDataMapper.php b/src/Bundle/ChillPersonBundle/Form/DataMapper/PersonIdentifiersDataMapper.php
new file mode 100644
index 000000000..eea151865
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Form/DataMapper/PersonIdentifiersDataMapper.php
@@ -0,0 +1,73 @@
+ $formsByKey */
+ $formsByKey = iterator_to_array($forms);
+
+ foreach ($this->identifierManager->getWorkers() as $worker) {
+ if (!$worker->getDefinition()->isEditableByUsers()) {
+ continue;
+ }
+ $form = $formsByKey['identifier_'.$worker->getDefinition()->getId()];
+ $identifier = $viewData->findFirst(fn (int $key, PersonIdentifier $identifier) => $worker->getDefinition()->getId() === $identifier->getId());
+ if (null === $identifier) {
+ $identifier = new PersonIdentifier($worker->getDefinition());
+ }
+ $form->setData($identifier->getValue());
+ }
+ }
+
+ public function mapFormsToData(\Traversable $forms, &$viewData): void
+ {
+ if (!$viewData instanceof Collection) {
+ throw new UnexpectedTypeException($viewData, Collection::class);
+ }
+
+ foreach ($forms as $name => $form) {
+ $identifierId = (int) substr((string) $name, 11);
+ $identifier = $viewData->findFirst(fn (int $key, PersonIdentifier $identifier) => $identifier->getId() === $identifierId);
+ $definition = $this->identifierDefinitionRepository->find($identifierId);
+ if (null === $identifier) {
+ $identifier = new PersonIdentifier($definition);
+ $viewData->add($identifier);
+ }
+ if (!$identifier->getDefinition()->isEditableByUsers()) {
+ continue;
+ }
+
+ $worker = $this->identifierManager->buildWorkerByPersonIdentifierDefinition($definition);
+ $identifier->setValue($form->getData());
+ $identifier->setCanonical($worker->canonicalizeValue($identifier->getValue()));
+ }
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Form/PersonIdentifiersType.php b/src/Bundle/ChillPersonBundle/Form/PersonIdentifiersType.php
new file mode 100644
index 000000000..ea077f626
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Form/PersonIdentifiersType.php
@@ -0,0 +1,48 @@
+identifierManager->getWorkers() as $worker) {
+ if (!$worker->getDefinition()->isEditableByUsers()) {
+ continue;
+ }
+
+ $subBuilder = $builder->create(
+ 'identifier_'.$worker->getDefinition()->getId(),
+ options: [
+ 'compound' => true,
+ 'label' => $this->translatableStringHelper->localize($worker->getDefinition()->getLabel()),
+ ]
+ );
+ $worker->buildForm($subBuilder);
+ $builder->add($subBuilder);
+ }
+
+ $builder->setDataMapper($this->identifiersDataMapper);
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Form/PersonType.php b/src/Bundle/ChillPersonBundle/Form/PersonType.php
index 21d56dde7..09ab04b01 100644
--- a/src/Bundle/ChillPersonBundle/Form/PersonType.php
+++ b/src/Bundle/ChillPersonBundle/Form/PersonType.php
@@ -72,8 +72,8 @@ class PersonType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('firstName')
- ->add('lastName')
+ ->add('firstName', TextType::class, ['empty_data' => ''])
+ ->add('lastName', TextType::class, ['empty_data' => ''])
->add('birthdate', ChillDateType::class, [
'required' => false,
])
@@ -101,7 +101,7 @@ class PersonType extends AbstractType
if ('visible' === $this->config['memo']) {
$builder
- ->add('memo', ChillTextareaType::class, ['required' => false]);
+ ->add('memo', ChillTextareaType::class, ['required' => false, 'empty_data' => '']);
}
if ('visible' === $this->config['employment_status']) {
@@ -118,6 +118,7 @@ class PersonType extends AbstractType
$builder->add('placeOfBirth', TextType::class, [
'required' => false,
'attr' => ['style' => 'text-transform: uppercase;'],
+ 'empty_data' => '',
]);
$builder->get('placeOfBirth')->addModelTransformer(new CallbackTransformer(
@@ -127,7 +128,9 @@ class PersonType extends AbstractType
}
if ('visible' === $this->config['contact_info']) {
- $builder->add('contactInfo', ChillTextareaType::class, ['required' => false]);
+ $builder->add('contactInfo', ChillTextareaType::class, [
+ 'required' => false, 'empty_data' => '', 'label' => 'Notes on contact information',
+ ]);
}
if ('visible' === $this->config['phonenumber']) {
@@ -152,12 +155,12 @@ class PersonType extends AbstractType
'required' => false,
]
)
- ->add('acceptSMS', CheckboxType::class, [
+ ->add('acceptSms', CheckboxType::class, [
'required' => false,
]);
}
- $builder->add('otherPhoneNumbers', ChillCollectionType::class, [
+ $builder->add('otherPhonenumbers', ChillCollectionType::class, [
'entry_type' => PersonPhoneType::class,
'button_add_label' => 'Add new phone',
'button_remove_label' => 'Remove phone',
@@ -173,12 +176,12 @@ class PersonType extends AbstractType
if ('visible' === $this->config['email']) {
$builder
- ->add('email', EmailType::class, ['required' => false]);
+ ->add('email', EmailType::class, ['required' => false, 'empty_data' => '']);
}
if ('visible' === $this->config['acceptEmail']) {
$builder
- ->add('acceptEmail', CheckboxType::class, ['required' => false]);
+ ->add('acceptEmail', CheckboxType::class, ['required' => false, 'empty_data' => '']);
}
if ('visible' === $this->config['country_of_birth']) {
@@ -222,6 +225,10 @@ class PersonType extends AbstractType
]);
}
+ $builder->add('identifiers', PersonIdentifiersType::class, [
+ 'by_reference' => false,
+ ]);
+
if ($options['cFGroup']) {
$builder
->add(
@@ -232,10 +239,7 @@ class PersonType extends AbstractType
}
}
- /**
- * @param OptionsResolverInterface $resolver
- */
- public function configureOptions(OptionsResolver $resolver)
+ public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Person::class,
@@ -251,10 +255,7 @@ class PersonType extends AbstractType
);
}
- /**
- * @return string
- */
- public function getBlockPrefix()
+ public function getBlockPrefix(): string
{
return 'chill_personbundle_person';
}
diff --git a/src/Bundle/ChillPersonBundle/PersonIdentifier/Exception/EngineNotFoundException.php b/src/Bundle/ChillPersonBundle/PersonIdentifier/Exception/EngineNotFoundException.php
new file mode 100644
index 000000000..a3c7cf593
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/PersonIdentifier/Exception/EngineNotFoundException.php
@@ -0,0 +1,20 @@
+add('content', TextType::class, ['label' => false]);
+ }
+
+ public function renderAsString(?PersonIdentifier $identifier, PersonIdentifierDefinition $definition): string
+ {
+ return $identifier?->getValue()['content'] ?? '';
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/PersonIdentifier/PersonIdentifierEngineInterface.php b/src/Bundle/ChillPersonBundle/PersonIdentifier/PersonIdentifierEngineInterface.php
new file mode 100644
index 000000000..6c75f263e
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/PersonIdentifier/PersonIdentifierEngineInterface.php
@@ -0,0 +1,27 @@
+
+ */
+ public function getWorkers(): array
+ {
+ $workers = [];
+ foreach ($this->personIdentifierDefinitionRepository->findByActive() as $definition) {
+ try {
+ $worker = $this->getEngine($definition->getEngine());
+ } catch (EngineNotFoundException) {
+ continue;
+ }
+
+ $workers[] = new PersonIdentifierWorker($worker, $definition);
+
+ }
+
+ return $workers;
+ }
+
+ public function buildWorkerByPersonIdentifierDefinition(PersonIdentifierDefinition $personIdentifierDefinition): PersonIdentifierWorker
+ {
+ return new PersonIdentifierWorker($this->getEngine($personIdentifierDefinition->getEngine()), $personIdentifierDefinition);
+ }
+
+ /**
+ * @throw EngineNotFoundException
+ */
+ private function getEngine(string $name): PersonIdentifierEngineInterface
+ {
+ foreach ($this->engines as $engine) {
+ if ($engine->getName() === $name) {
+ return $engine;
+ }
+ }
+
+ throw new EngineNotFoundException($name);
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/PersonIdentifier/PersonIdentifierManagerInterface.php b/src/Bundle/ChillPersonBundle/PersonIdentifier/PersonIdentifierManagerInterface.php
new file mode 100644
index 000000000..9bec7d1fd
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/PersonIdentifier/PersonIdentifierManagerInterface.php
@@ -0,0 +1,26 @@
+
+ */
+ public function getWorkers(): array;
+
+ public function buildWorkerByPersonIdentifierDefinition(PersonIdentifierDefinition $personIdentifierDefinition): PersonIdentifierWorker;
+}
diff --git a/src/Bundle/ChillPersonBundle/PersonIdentifier/PersonIdentifierWorker.php b/src/Bundle/ChillPersonBundle/PersonIdentifier/PersonIdentifierWorker.php
new file mode 100644
index 000000000..94702b8eb
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/PersonIdentifier/PersonIdentifierWorker.php
@@ -0,0 +1,49 @@
+identifierEngine;
+ }
+
+ public function getDefinition(): PersonIdentifierDefinition
+ {
+ return $this->definition;
+ }
+
+ public function buildForm(FormBuilderInterface $builder): void
+ {
+ $this->identifierEngine->buildForm($builder, $this->definition);
+ }
+
+ public function canonicalizeValue(array $value): ?string
+ {
+ return $this->identifierEngine->canonicalizeValue($value, $this->definition);
+ }
+
+ public function renderAsString(?PersonIdentifier $identifier): string
+ {
+ return $this->identifierEngine->renderAsString($identifier, $this->definition);
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/PersonIdentifier/Rendering/PersonIdRendering.php b/src/Bundle/ChillPersonBundle/PersonIdentifier/Rendering/PersonIdRendering.php
new file mode 100644
index 000000000..c529b3aba
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/PersonIdentifier/Rendering/PersonIdRendering.php
@@ -0,0 +1,65 @@
+idContentText = $parameterBag->get('chill_person')['person_render']['id_content_text'];
+ }
+
+ public function renderPersonId(Person $person): string
+ {
+ $args = [
+ '[[ person_id ]]' => $person->getId(),
+ ];
+
+ foreach ($person->getIdentifiers() as $identifier) {
+ if (!$identifier->getDefinition()->isActive()) {
+ continue;
+ }
+
+ $key = 'identifier_'.$identifier->getDefinition()->getId();
+
+ $args
+ += [
+ "[[ {$key} ]]" => $this->personIdentifierManager->buildWorkerByPersonIdentifierDefinition($identifier->getDefinition())
+ ->renderAsString($identifier),
+ "[[ if:{$key} ]]" => '',
+ "[[ endif:{$key} ]]" => '',
+ ];
+ // we remove the eventual conditions
+
+
+ }
+
+ $rendered = strtr($this->idContentText, $args);
+
+ // Delete the conditions which are not met, for instance:
+ // [[ if:identifier_99 ]] ... [[ endif:identifier_99 ]]
+ // this match the same dumber for opening and closing of the condition
+ return preg_replace(
+ '/\[\[\s*if:identifier_(\d+)\s*\]\].*?\[\[\s*endif:identifier_\1\s*\]\]/s',
+ '',
+ $rendered
+ );
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/PersonIdentifier/Rendering/PersonIdRenderingInterface.php b/src/Bundle/ChillPersonBundle/PersonIdentifier/Rendering/PersonIdRenderingInterface.php
new file mode 100644
index 000000000..831dcc57d
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/PersonIdentifier/Rendering/PersonIdRenderingInterface.php
@@ -0,0 +1,19 @@
+ $this->personIdRendering->renderPersonId($person)
+ ),
+ ];
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/PersonIdentifier/Rendering/PersonIdentifierEntityRender.php b/src/Bundle/ChillPersonBundle/PersonIdentifier/Rendering/PersonIdentifierEntityRender.php
new file mode 100644
index 000000000..e84941ffd
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/PersonIdentifier/Rendering/PersonIdentifierEntityRender.php
@@ -0,0 +1,41 @@
+
+ */
+final readonly class PersonIdentifierEntityRender implements ChillEntityRenderInterface
+{
+ public function __construct(private PersonIdentifierManagerInterface $identifierManager) {}
+
+ public function renderBox(mixed $entity, array $options): string
+ {
+ return $this->renderString($entity, $options);
+ }
+
+ public function renderString(mixed $entity, array $options): string
+ {
+ $worker = $this->identifierManager->buildWorkerByPersonIdentifierDefinition($entity->getDefinition());
+
+ return $worker->renderAsString($entity);
+ }
+
+ public function supports(object $entity, array $options): bool
+ {
+ return $entity instanceof PersonIdentifier;
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Repository/Identifier/PersonIdentifierDefinitionRepository.php b/src/Bundle/ChillPersonBundle/Repository/Identifier/PersonIdentifierDefinitionRepository.php
new file mode 100644
index 000000000..33b531e5a
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Repository/Identifier/PersonIdentifierDefinitionRepository.php
@@ -0,0 +1,32 @@
+
+ */
+class PersonIdentifierDefinitionRepository extends ServiceEntityRepository
+{
+ public function __construct(ManagerRegistry $managerRegistry)
+ {
+ parent::__construct($managerRegistry, PersonIdentifierDefinition::class);
+ }
+
+ public function findByActive(): array
+ {
+ return $this->findBy(['active' => true]);
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss b/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss
index afa163cf2..846ba008e 100644
--- a/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss
+++ b/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss
@@ -281,11 +281,6 @@ abbr.referrer { // still used ?
font-style: italic;
}
-.created-updated {
- border: 1px solid black;
- padding: 10px;
-}
-
/// Masonry blocs on AccompanyingCourse resume page
div#dashboards {
div.mbloc {
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig
index bb039b5a0..fb1a81dfe 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig
@@ -8,7 +8,7 @@
{{ 'Accompanying Course'|trans }}
- {{ accompanyingCourse.id }}
+ ({{ 'accompanying_period.number'|trans({ 'id': accompanyingCourse.id}) }})
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig
index 380a17fa2..d8a930b40 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig
@@ -78,11 +78,6 @@
{%- if options['addEntity'] -%}
{%- endif -%}
- {%- if options['addId'] -%}
-