diff --git a/.changes/unreleased/Feature-20250901-094055.yaml b/.changes/unreleased/Feature-20250901-094055.yaml new file mode 100644 index 000000000..b240a1bfc --- /dev/null +++ b/.changes/unreleased/Feature-20250901-094055.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: Add external identifier for a Person +time: 2025-09-01T09:40:55.990365093+02:00 +custom: + Issue: "64" + SchemaChange: Add columns or tables diff --git a/.gitignore b/.gitignore index 88458b64e..be72f296f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,9 @@ migrations/* templates/* translations/* +# we allow developers to add customization on their installation, without commiting it +config/packages/dev/* + ###> symfony/framework-bundle ### /.env.local /.env.local.php diff --git a/src/Bundle/ChillCustomFieldsBundle/EntityRepository/CustomFieldsDefaultGroupRepository.php b/src/Bundle/ChillCustomFieldsBundle/EntityRepository/CustomFieldsDefaultGroupRepository.php new file mode 100644 index 000000000..a336970dc --- /dev/null +++ b/src/Bundle/ChillCustomFieldsBundle/EntityRepository/CustomFieldsDefaultGroupRepository.php @@ -0,0 +1,29 @@ +findOneBy(['entity' => $className]); + } +} diff --git a/src/Bundle/ChillCustomFieldsBundle/config/services.yaml b/src/Bundle/ChillCustomFieldsBundle/config/services.yaml index d27323a0a..bdc447996 100644 --- a/src/Bundle/ChillCustomFieldsBundle/config/services.yaml +++ b/src/Bundle/ChillCustomFieldsBundle/config/services.yaml @@ -127,3 +127,7 @@ services: factory: ["@doctrine", getRepository] arguments: - "Chill\\CustomFieldsBundle\\Entity\\CustomFieldLongChoice\\Option" + + Chill\CustomFieldsBundle\EntityRepository\CustomFieldsDefaultGroupRepository: + autowire: true + autoconfigure: true diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss index 1dc56dded..bb570f378 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss @@ -170,13 +170,14 @@ div.banner { font-weight: lighter; font-size: 50%; margin-left: 0.5em; - &:before { content: '(n°'; } - &:after { content: ')'; } + + &.same-size { + font-size: unset; + font-weight: unset; + } } span.age { margin-left: 0.5em; - &:before { content: '('; } - &:after { content: ')'; } } } diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss index 57fa17648..7e854d1ca 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/render_box.scss @@ -44,8 +44,6 @@ section.chill-entity { margin-left: 0.5em; } span.id-number { - &:before { content: '(n°'; } - &:after { content: ')'; } } } p.moreinfo {} 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'] -%} {{ 'Person'|trans }} {%- endif -%} - {%- if options['addId'] -%} - - {{ person.id|upper -}} - - {%- endif -%} {%- if options['addInfo'] -%}

@@ -99,6 +94,12 @@ {%- if options['addAge'] -%}  {{ 'years_old'|trans({ 'age': person.age }) }} {%- endif -%} + {%- if options['addId'] -%} + {%- set personId = person|chill_person_id_render_text %} + + ({{ personId }}) + + {%- endif -%} {%- elseif person.birthdate is not null -%}

{%- endif -%} {#- tricks to remove easily whitespace after template -#} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/edit.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/edit.html.twig index cb2d867c4..fe5dde242 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Person/edit.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/edit.html.twig @@ -31,7 +31,7 @@ {% if form.memo is defined %}

{{ 'Memo'|trans }}

- {{ form_row(form.memo, {'label' : 'Memo'} ) }} + {{ form_widget(form.memo, {'label' : 'Memo'} ) }}
{% endif %} @@ -85,15 +85,17 @@ {{ form_row(form.mobilenumber, {'label': 'Mobilenumber'}) }}
- {{ form_row(form.acceptSMS, {'label' : 'Accept short text message ?'}) }} + {{ form_row(form.acceptSms, {'label' : 'Accept short text message ?'}) }}
{%- endif -%} - {%- if form.otherPhoneNumbers is defined -%} - {{ form_widget(form.otherPhoneNumbers) }} - {{ form_errors(form.otherPhoneNumbers) }} + {%- if form.otherPhonenumbers is defined -%} + {{ form_widget(form.otherPhonenumbers) }} + {{ form_errors(form.otherPhonenumbers) }} {%- endif -%} {%- if form.contactInfo is defined -%} - {{ form_row(form.contactInfo, {'label': 'Notes on contact information'}) }} + {{ form_label(form.contactInfo) }} + {{ form_widget(form.contactInfo) }} + {{ form_errors(form.contactInfo) }} {%- endif -%} {%- endif -%} @@ -134,6 +136,20 @@ {%- endif -%} + {% if form.identifiers|length > 0 %} +
+

{{ 'person.Identifiers'|trans }}

+
+ {% for f in form.identifiers %} + {{ form_row(f) }} + {% endfor %} +
+
+ {% else %} + {{ form_widget(form.identifiers) }} + {% endif %} + + {{ form_rest(form) }}