From 82d8556f2451c955eb9816da2020c8f9beb86553 Mon Sep 17 00:00:00 2001 From: Jean-Francois Monfort Date: Thu, 29 Apr 2021 16:35:39 +0200 Subject: [PATCH] Activity Form : display field according to the parameters --- .../Controller/ActivityController.php | 78 ++--- .../ChillActivityBundle/Entity/Activity.php | 8 +- .../Entity/ActivityType.php | 33 ++ .../Form/ActivitySelectTypeType.php | 19 - .../ChillActivityBundle/Form/ActivityType.php | 330 +++++++++++------- .../Resources/views/Activity/edit.html.twig | 40 ++- .../Resources/views/Activity/new.html.twig | 38 +- .../views/Activity/selectType.html.twig | 17 +- 8 files changed, 354 insertions(+), 209 deletions(-) delete mode 100644 src/Bundle/ChillActivityBundle/Form/ActivitySelectTypeType.php diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 58d7e68ea..036646104 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -22,11 +22,12 @@ namespace Chill\ActivityBundle\Controller; -use Chill\ActivityBundle\Form\ActivitySelectTypeType; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Privacy\PrivacyEvent; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\SubmitType; @@ -69,7 +70,7 @@ class ActivityController extends AbstractController * Lists all Activity entities. * */ - public function listAction($person_id, Request $request) + public function listAction($person_id) { $em = $this->getDoctrine()->getManager(); $person = $em->getRepository('ChillPersonBundle:Person')->find($person_id); @@ -102,31 +103,21 @@ class ActivityController extends AbstractController )); } - public function selectTypeAction(int $person_id, Request $request): Response + public function selectTypeAction(int $person_id): Response { $em = $this->getDoctrine()->getManager(); - $person = $em->getRepository('ChillPersonBundle:Person')->find($person_id); + $person = $em->getRepository(Person::class)->find($person_id); if ($person === NULL) { throw $this->createNotFoundException('Person not found'); } - $form = $this->createForm(ActivitySelectTypeType::class); - - $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { - $activityType = $form->get('type')->getData(); - if ($activityType instanceof \Chill\ActivityBundle\Entity\ActivityType) { - return $this->redirectToRoute('chill_activity_activity_new', [ - 'person_id' => $person->getId(), - 'activityType_id' => $activityType->getId(), - ]); - } - } + $activityTypes = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class) + ->findBy(['active' => true]); return $this->render('ChillActivityBundle:Activity:selectType.html.twig', [ - 'form' => $form->createView(), - 'person' => $person + 'person' => $person, + 'activityTypes' => $activityTypes, ]); } @@ -191,20 +182,17 @@ class ActivityController extends AbstractController * * @return \Symfony\Component\Form\Form The form */ - private function createCreateForm(Activity $entity) + private function createCreateForm(Activity $entity): FormInterface { - $form = $this->createForm(ActivityType::class, $entity, - array( - 'action' => $this->generateUrl('chill_activity_activity_create', [ - 'person_id' => $entity->getPerson()->getId(), - ]), - 'method' => 'POST', - 'center' => $entity->getCenter(), - 'role' => new Role('CHILL_ACTIVITY_CREATE') - ) - ); - - return $form; + return $this->createForm(ActivityType::class, $entity, [ + 'action' => $this->generateUrl('chill_activity_activity_create', [ + 'person_id' => $entity->getPerson()->getId(), + ]), + 'method' => 'POST', + 'center' => $entity->getCenter(), + 'role' => new Role('CHILL_ACTIVITY_CREATE'), + 'activityType' => $entity->getType(), + ]); } /** @@ -224,7 +212,8 @@ class ActivityController extends AbstractController $activityType = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class) ->find($activityType_id); - if (!$activityType instanceof \Chill\ActivityBundle\Entity\ActivityType) { + if (!$activityType instanceof \Chill\ActivityBundle\Entity\ActivityType || + !$activityType->isActive()) { return $this->redirectToRoute('chill_activity_activity_select_type', [ 'person_id' => $person->getId(), ]); @@ -240,7 +229,7 @@ class ActivityController extends AbstractController $this->denyAccessUnlessGranted('CHILL_ACTIVITY_CREATE', $entity); - $form = $this->createCreateForm($entity); + $form = $this->createCreateForm($entity); return $this->render('ChillActivityBundle:Activity:new.html.twig', array( 'person' => $person, @@ -333,24 +322,21 @@ class ActivityController extends AbstractController * Creates a form to edit a Activity entity. * * @param Activity $entity The entity - * - * @return \Symfony\Component\Form\Form The form */ - private function createEditForm(Activity $entity) + private function createEditForm(Activity $entity): FormInterface { - $form = $this->createForm(ActivityType::class, $entity, array( - 'action' => $this->generateUrl('chill_activity_activity_update', - array( - 'id' => $entity->getId(), - 'person_id' => $entity->getPerson()->getId() - )), + return $this->createForm(ActivityType::class, $entity, [ + 'action' => $this->generateUrl('chill_activity_activity_update', [ + 'id' => $entity->getId(), + 'person_id' => $entity->getPerson()->getId() + ]), 'method' => 'PUT', 'center' => $entity->getCenter(), - 'role' => new Role('CHILL_ACTIVITY_UPDATE') - )); - - return $form; + 'role' => new Role('CHILL_ACTIVITY_UPDATE'), + 'activityType' => $entity->getType(), + ]); } + /** * Edits an existing Activity entity. * diff --git a/src/Bundle/ChillActivityBundle/Entity/Activity.php b/src/Bundle/ChillActivityBundle/Entity/Activity.php index 57a72e5a8..005644670 100644 --- a/src/Bundle/ChillActivityBundle/Entity/Activity.php +++ b/src/Bundle/ChillActivityBundle/Entity/Activity.php @@ -76,7 +76,7 @@ class Activity implements HasCenterInterface, HasScopeInterface /** * @ORM\Column(type="time", nullable=true) */ - private ?\DateTime $travelTime; + private ?\DateTime $travelTime = null; /** * @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\ActivityPresence") @@ -96,7 +96,7 @@ class Activity implements HasCenterInterface, HasScopeInterface /** * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope") */ - private Scope $scope; + private ?Scope $scope = null; /** * @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\Person") @@ -217,7 +217,7 @@ class Activity implements HasCenterInterface, HasScopeInterface return $this; } - public function getTravelTime(): \DateTime + public function getTravelTime(): ?\DateTime { return $this->travelTime; } @@ -288,7 +288,7 @@ class Activity implements HasCenterInterface, HasScopeInterface /** * Get scope */ - public function getScope(): Scope + public function getScope(): ?Scope { return $this->scope; } diff --git a/src/Bundle/ChillActivityBundle/Entity/ActivityType.php b/src/Bundle/ChillActivityBundle/Entity/ActivityType.php index fe5e87dec..65fc243d5 100644 --- a/src/Bundle/ChillActivityBundle/Entity/ActivityType.php +++ b/src/Bundle/ChillActivityBundle/Entity/ActivityType.php @@ -632,4 +632,37 @@ class ActivityType { $this->socialDataLabel = $socialDataLabel; } + + public function isVisible(string $field): bool + { + $property = $field.'Visible'; + + if (!property_exists($this, $property)) { + throw new \InvalidArgumentException('Field "'.$field.'" not found'); + } + + return self::FIELD_INVISIBLE !== $this->$property; + } + + public function isRequired(string $field): bool + { + $property = $field.'Visible'; + + if (!property_exists($this, $property)) { + throw new \InvalidArgumentException('Field "'.$field.'" not found'); + } + + return self::FIELD_REQUIRED === $this->$property; + } + + public function getLabel(string $field): ?string + { + $property = $field.'Label'; + + if (!property_exists($this, $property)) { + throw new \InvalidArgumentException('Field "'.$field.'" not found'); + } + + return $this->$property; + } } diff --git a/src/Bundle/ChillActivityBundle/Form/ActivitySelectTypeType.php b/src/Bundle/ChillActivityBundle/Form/ActivitySelectTypeType.php deleted file mode 100644 index c637e5bcc..000000000 --- a/src/Bundle/ChillActivityBundle/Form/ActivitySelectTypeType.php +++ /dev/null @@ -1,19 +0,0 @@ -add('type', TranslatableActivityType::class, array( - 'placeholder' => 'Choose a type', - 'active_only' => true, - 'mapped' => false, - )); - } -} diff --git a/src/Bundle/ChillActivityBundle/Form/ActivityType.php b/src/Bundle/ChillActivityBundle/Form/ActivityType.php index eb63b6beb..2378c6db5 100644 --- a/src/Bundle/ChillActivityBundle/Form/ActivityType.php +++ b/src/Bundle/ChillActivityBundle/Form/ActivityType.php @@ -2,8 +2,16 @@ namespace Chill\ActivityBundle\Form; +use Chill\ActivityBundle\Entity\Activity; +use Chill\ActivityBundle\Entity\ActivityPresence; +use Chill\ActivityBundle\Entity\ActivityReason; use Chill\MainBundle\Form\Type\CommentType; +use Chill\PersonBundle\Entity\Person; +use Chill\ThirdPartyBundle\Entity\ThirdParty; +use Doctrine\ORM\EntityRepository; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; @@ -15,51 +23,33 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTra use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; -use Chill\ActivityBundle\Form\Type\TranslatableActivityType; -use Chill\ActivityBundle\Form\Type\TranslatableActivityReason; use Chill\MainBundle\Form\Type\UserPickerType; use Chill\MainBundle\Form\Type\ScopePickerType; use Chill\MainBundle\Form\Type\ChillDateType; class ActivityType extends AbstractType { - /** - * the user running this form - * - * @var User - */ - protected $user; + protected User $user; - /** - * - * @var AuthorizationHelper - */ protected AuthorizationHelper $authorizationHelper; - /** - * - * @var ObjectManager - */ - protected $om; + protected ObjectManager $om; - /** - * - * @var TranslatableStringHelper - */ - protected $translatableStringHelper; + protected TranslatableStringHelper $translatableStringHelper; - protected $timeChoices; + protected array $timeChoices; - public function __construct( - TokenStorageInterface $tokenStorage, - AuthorizationHelper $authorizationHelper, ObjectManager $om, - TranslatableStringHelper $translatableStringHelper, - array $timeChoices - ) - { + public function __construct ( + TokenStorageInterface $tokenStorage, + AuthorizationHelper $authorizationHelper, + ObjectManager $om, + TranslatableStringHelper $translatableStringHelper, + array $timeChoices + ) { if (!$tokenStorage->getToken()->getUser() instanceof User) { throw new \RuntimeException("you should have a valid user"); } + $this->user = $tokenStorage->getToken()->getUser(); $this->authorizationHelper = $authorizationHelper; $this->om = $om; @@ -67,121 +57,219 @@ class ActivityType extends AbstractType $this->timeChoices = $timeChoices; } - /** - * @param FormBuilderInterface $builder - * @param array $options - */ - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { // handle times choices - $timeChoices = array(); + $timeChoices = []; foreach ($this->timeChoices as $e) { $timeChoices[$e['label']] = $e['seconds']; - }; + } $durationTimeTransformer = new DateTimeToTimestampTransformer('GMT', 'GMT'); - $durationTimeOptions = array( - 'choices' => $timeChoices, - 'placeholder' => 'Choose the duration', - ); + $durationTimeOptions = [ + 'choices' => $timeChoices, + 'placeholder' => 'Choose the duration', + ]; - $builder - ->add('date', ChillDateType::class, array( - 'required' => true - )) - ->add('durationTime', ChoiceType::class, $durationTimeOptions) - ->add('attendee', ChoiceType::class, array( - 'expanded' => true, - 'required' => false, - 'choices' => array( - 'present' => true, - 'not present' => false - ) - )) - ->add('user', UserPickerType::class, [ + /** @var \Chill\ActivityBundle\Entity\ActivityType $activityType */ + $activityType = $options['activityType']; + + if (!$activityType->isActive()) { + throw new \InvalidArgumentException('Activity type must be active'); + } + + $builder->add('scope', ScopePickerType::class, [ + 'center' => $options['center'], + 'role' => $options['role'] + ]); + + if ($activityType->isVisible('date')) { + $builder->add('date', ChillDateType::class, [ + 'label' => $activityType->getLabel('date'), + 'required' => $activityType->isRequired('date'), + ]); + } + + if ($activityType->isVisible('durationTime')) { + $durationTimeOptions['label'] = $activityType->getLabel('durationTime'); + $durationTimeOptions['required'] = $activityType->isRequired('durationTime'); + + $builder->add('durationTime', ChoiceType::class, $durationTimeOptions); + } + + if ($activityType->isVisible('travelTime')) { + $durationTimeOptions['label'] = $activityType->getLabel('travelTime'); + $durationTimeOptions['required'] = $activityType->isRequired('travelTime'); + + $builder->add('travelTime', ChoiceType::class, $durationTimeOptions); + } + + if ($activityType->isVisible('travelTime')) { + $builder->add('attendee', EntityType::class, [ + 'label' => $activityType->getLabel('attendee'), + 'required' => $activityType->isRequired('attendee'), + 'class' => ActivityPresence::class, + 'choice_label' => function (ActivityPresence $activityPresence) { + return $this->translatableStringHelper->localize($activityPresence->getName()); + }, + 'query_builder' => function (EntityRepository $er) { + return $er->createQueryBuilder('a') + ->where('a.active = true'); + }, + ]); + } + + if ($activityType->isVisible('user')) { + $builder->add('user', UserPickerType::class, [ + 'label' => $activityType->getLabel('user'), + 'required' => $activityType->isRequired('user'), 'center' => $options['center'], 'role' => $options['role'] - ]) - ->add('scope', ScopePickerType::class, [ - 'center' => $options['center'], - 'role' => $options['role'] - ]) - ->add('reasons', TranslatableActivityReason::class, array( + ]); + } + + if ($activityType->isVisible('reasons')) { + $builder->add('reasons', EntityType::class, [ + 'label' => $activityType->getLabel('reasons'), + 'required' => $activityType->isRequired('reasons'), + 'class' => ActivityReason::class, + 'choice_label' => function (ActivityReason $activityReason) { + return $this->translatableStringHelper->localize($activityReason->getName()); + }, + 'query_builder' => function (EntityRepository $er) { + return $er->createQueryBuilder('a') + ->where('a.active = true'); + }, + ]); + } + + if ($activityType->isVisible('comment')) { + $builder->add('comment', CommentType::class, [ + 'label' => $activityType->getLabel('comment'), + 'required' => $activityType->isRequired('comment'), + ]); + } + + if ($activityType->isVisible('persons')) { + // TODO Faire évoluer le selecteur et la query + $builder->add('persons', EntityType::class, [ + 'label' => $activityType->getLabel('persons'), + 'required' => $activityType->isRequired('persons'), + 'class' => Person::class, 'multiple' => true, - 'required' => false, - )) - ->add('comment', CommentType::class, [ - 'required' => false, - ]) - ; + 'choice_label' => function (Person $person) { + return $person->getFullnameCanonical(); + }, + 'query_builder' => function (EntityRepository $er) { + return $er->createQueryBuilder('a'); + }, + ]); + } + + if ($activityType->isVisible('thirdParties')) { + $builder->add('thirdParties', EntityType::class, [ + 'label' => $activityType->getLabel('thirdParties'), + 'required' => $activityType->isRequired('thirdParties'), + 'class' => ThirdParty::class, + 'choice_label' => function (ThirdParty $thirdParty) { + return $thirdParty->getName(); + }, + 'query_builder' => function (EntityRepository $er) { + return $er->createQueryBuilder('a'); + }, + ]); + } + + // TODO : documents + //$documents + + if ($activityType->isVisible('users')) { + $builder->add('users', EntityType::class, [ + 'label' => $activityType->getLabel('users'), + 'required' => $activityType->isRequired('users'), + 'class' => User::class, + 'choice_label' => function (User $user) { + return $user->getUsername(); + }, + 'query_builder' => function (EntityRepository $er) { + return $er->createQueryBuilder('u'); + }, + ]); + } + + if ($activityType->isVisible('emergency')) { + $builder->add('emergency', CheckboxType::class, [ + 'label' => $activityType->getLabel('emergency'), + 'required' => $activityType->isRequired('emergency'), + ]); + } + + if ($activityType->isVisible('sentReceived')) { + $builder->add('sentReceived', ChoiceType::class, [ + 'label' => $activityType->getLabel('sentReceived'), + 'required' => $activityType->isRequired('sentReceived'), + 'choices' => [ + 'Sent' => Activity::SENTRECEIVED_SENT, + 'Received' => Activity::SENTRECEIVED_RECEIVED, + ], + ]); + } $builder->get('durationTime') - ->addModelTransformer($durationTimeTransformer); - + ->addModelTransformer($durationTimeTransformer); $builder->get('durationTime') - ->addEventListener( - FormEvents::PRE_SET_DATA, - function(FormEvent $formEvent) use ( - $timeChoices, - $builder, - $durationTimeTransformer, - $durationTimeOptions - ) - { - // set the timezone to GMT, and fix the difference between current and GMT - // the datetimetransformer will then handle timezone as GMT - $timezoneUTC = new \DateTimeZone('GMT'); - /* @var $data \DateTime */ - $data = $formEvent->getData() === NULL ? - \DateTime::createFromFormat('U', 300) : - $formEvent->getData(); - $seconds = $data->getTimezone()->getOffset($data); - $data->setTimeZone($timezoneUTC); - $data->add(new \DateInterval('PT'.$seconds.'S')); + ->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $formEvent) use ( + $timeChoices, + $builder, + $durationTimeTransformer, + $durationTimeOptions + ) { + // set the timezone to GMT, and fix the difference between current and GMT + // the datetimetransformer will then handle timezone as GMT + $timezoneUTC = new \DateTimeZone('GMT'); + /* @var $data \DateTime */ + $data = $formEvent->getData() === NULL ? + \DateTime::createFromFormat('U', 300) : + $formEvent->getData(); + $seconds = $data->getTimezone()->getOffset($data); + $data->setTimeZone($timezoneUTC); + $data->add(new \DateInterval('PT'.$seconds.'S')); - // test if the timestamp is in the choices. - // If not, recreate the field with the new timestamp - if (!in_array($data->getTimestamp(), $timeChoices)) { - // the data are not in the possible values. add them - $timeChoices[$data->format('H:i')] = $data->getTimestamp(); - $form = $builder->create( - 'durationTime', - ChoiceType::class, - array_merge( - $durationTimeOptions, - array( - 'choices' => $timeChoices, - 'auto_initialize' => false - ) - )); - $form->addModelTransformer($durationTimeTransformer); - $formEvent->getForm()->getParent()->add($form->getForm()); - } - }); + // test if the timestamp is in the choices. + // If not, recreate the field with the new timestamp + if (!in_array($data->getTimestamp(), $timeChoices)) { + // the data are not in the possible values. add them + $timeChoices[$data->format('H:i')] = $data->getTimestamp(); + $form = $builder->create('durationTime', ChoiceType::class, array_merge( + $durationTimeOptions, + [ + 'choices' => $timeChoices, + 'auto_initialize' => false + ] + )); + $form->addModelTransformer($durationTimeTransformer); + $formEvent->getForm()->getParent()->add($form->getForm()); + } + }); } - /** - * @param OptionsResolverInterface $resolver - */ - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { - $resolver->setDefaults(array( + $resolver->setDefaults([ 'data_class' => 'Chill\ActivityBundle\Entity\Activity' - )); + ]); $resolver - ->setRequired(array('center', 'role')) - ->setAllowedTypes('center', 'Chill\MainBundle\Entity\Center') - ->setAllowedTypes('role', 'Symfony\Component\Security\Core\Role\Role') - ; + ->setRequired(['center', 'role', 'activityType']) + ->setAllowedTypes('center', 'Chill\MainBundle\Entity\Center') + ->setAllowedTypes('role', 'Symfony\Component\Security\Core\Role\Role') + ->setAllowedTypes('activityType', \Chill\ActivityBundle\Entity\ActivityType::class) + ; } - /** - * @return string - */ - public function getBlockPrefix() + public function getBlockPrefix(): string { return 'chill_activitybundle_activity'; } diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/edit.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/edit.html.twig index 7a69a8eee..e4db4d943 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/edit.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/edit.html.twig @@ -29,12 +29,40 @@ {{ form_row(edit_form.scope) }}

{{ 'Activity data'|trans }}

- {{ form_row(edit_form.date) }} - {{ form_row(edit_form.durationTime) }} - {{ form_row(edit_form.type) }} - {{ form_row(edit_form.attendee) }} - {{ form_row(edit_form.reasons) }} - {{ form_row(edit_form.comment) }} + + {%- if form.date is defined -%} + {{ form_row(form.date) }} + {% endif %} + {%- if form.durationTime is defined -%} + {{ form_row(form.durationTime) }} + {% endif %} + {%- if form.travelTime is defined -%} + {{ form_row(form.travelTime) }} + {% endif %} + {%- if form.attendee is defined -%} + {{ form_row(form.attendee) }} + {% endif %} + {%- if form.comment is defined -%} + {{ form_row(form.comment) }} + {% endif %} + {%- if form.reasons is defined -%} + {{ form_row(form.reasons) }} + {% endif %} + {%- if form.persons is defined -%} + {{ form_row(form.persons) }} + {% endif %} + {%- if form.thirdParties is defined -%} + {{ form_row(form.thirdParties) }} + {% endif %} + {%- if form.users is defined -%} + {{ form_row(form.users) }} + {% endif %} + {%- if form.emergency is defined -%} + {{ form_row(form.emergency) }} + {% endif %} + {%- if form.sentReceived is defined -%} + {{ form_row(form.sentReceived) }} + {% endif %} {{ form_widget(edit_form) }}