diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 2eb6d1215..234e1203d 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -47,11 +47,11 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, { /** * widget factory - * + * * @var WidgetFactoryInterface[] */ protected $widgetFactories = array(); - + /** * @param WidgetFactoryInterface $factory */ @@ -59,7 +59,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, { $this->widgetFactories[] = $factory; } - + /** * @return WidgetFactoryInterface[] */ @@ -67,7 +67,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, { return $this->widgetFactories; } - + /** * {@inheritDoc} * @param array $configs @@ -79,31 +79,31 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, // configuration for main bundle $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); - + $container->setParameter('chill_main.installation_name', $config['installation_name']); $container->setParameter('chill_main.available_languages', $config['available_languages']); - - $container->setParameter('chill_main.routing.resources', - $config['routing']['resources']); - + + $container->setParameter('chill_main.routing.resources', + $config['routing']['resources']); + $container->setParameter('chill_main.pagination.item_per_page', $config['pagination']['item_per_page']); - - $container->setParameter('chill_main.notifications', + + $container->setParameter('chill_main.notifications', $config['notifications']); - - $container->setParameter('chill_main.redis', + + $container->setParameter('chill_main.redis', $config['redis']); - - $container->setParameter('chill_main.phone_helper', + + $container->setParameter('chill_main.phone_helper', $config['phone_helper'] ?? []); - + // add the key 'widget' without the key 'enable' - $container->setParameter('chill_main.widgets', - isset($config['widgets']['homepage']) ? + $container->setParameter('chill_main.widgets', + isset($config['widgets']['homepage']) ? array('homepage' => $config['widgets']['homepage']): array() ); @@ -131,10 +131,11 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, $loader->load('services/templating.yaml'); $loader->load('services/timeline.yaml'); $loader->load('services/search.yaml'); - + $loader->load('services/serializer.yaml'); + $this->configureCruds($container, $config['cruds'], $loader); } - + /** * @param array $config * @param ContainerBuilder $container @@ -144,11 +145,11 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, { return new Configuration($this->widgetFactories, $container); } - + /** * @param ContainerBuilder $container */ - public function prepend(ContainerBuilder $container) + public function prepend(ContainerBuilder $container) { //add installation_name and date_format to globals $chillMainConfig = $container->getExtensionConfig($this->getAlias()); @@ -163,7 +164,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, 'form_themes' => array('@ChillMain/Form/fields.html.twig') ); $container->prependExtensionConfig('twig', $twigConfig); - + //add DQL function to ORM (default entity_manager) $container->prependExtensionConfig('doctrine', array( 'orm' => array( @@ -182,7 +183,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, ) ) )); - + //add dbal types (default entity_manager) $container->prependExtensionConfig('doctrine', array( 'dbal' => [ @@ -191,23 +192,23 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, ] ] )); - + //add current route to chill main $container->prependExtensionConfig('chill_main', array( 'routing' => array( 'resources' => array( '@ChillMainBundle/config/routes.yaml' ) - + ) )); - + //add a channel to log app events $container->prependExtensionConfig('monolog', array( 'channels' => array('chill') )); } - + /** * @param ContainerBuilder $container * @param array $config the config under 'cruds' key @@ -218,31 +219,31 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, if (count($config) === 0) { return; } - + $loader->load('services/crud.yaml'); - + $container->setParameter('chill_main_crud_route_loader_config', $config); - + $definition = new Definition(); $definition ->setClass(\Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader::class) ->addArgument('%chill_main_crud_route_loader_config%') ; - + $container->setDefinition('chill_main_crud_route_loader', $definition); - + $alreadyExistingNames = []; - + foreach ($config as $crudEntry) { $controller = $crudEntry['controller']; $controllerServiceName = 'cscrud_'.$crudEntry['name'].'_controller'; $name = $crudEntry['name']; - + // check for existing crud names if (\in_array($name, $alreadyExistingNames)) { throw new LogicException(sprintf("the name %s is defined twice in CRUD", $name)); } - + if (!$container->has($controllerServiceName)) { $controllerDefinition = new Definition($controller); $controllerDefinition->addTag('controller.service_arguments'); @@ -250,7 +251,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, $controllerDefinition->setClass($crudEntry['controller']); $container->setDefinition($controllerServiceName, $controllerDefinition); } - + $container->setParameter('chill_main_crud_config_'.$name, $crudEntry); $container->getDefinition($controllerServiceName) ->addMethodCall('setCrudConfig', ['%chill_main_crud_config_'.$name.'%']); diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/CenterNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/CenterNormalizer.php new file mode 100644 index 000000000..26182d936 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/CenterNormalizer.php @@ -0,0 +1,44 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Serializer\Normalizer; + +use Chill\MainBundle\Entity\Center; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * + * + */ +class CenterNormalizer implements NormalizerInterface +{ + public function normalize($center, string $format = null, array $context = array()) + { + /** @var Center $center */ + return [ + 'id' => $center->getId(), + 'name' => $center->getName() + ]; + } + + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof Center; + } +} diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/DateNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/DateNormalizer.php new file mode 100644 index 000000000..fd902b184 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/DateNormalizer.php @@ -0,0 +1,39 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Serializer\Normalizer; + +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +class DateNormalizer implements NormalizerInterface +{ + public function normalize($date, string $format = null, array $context = array()) + { + /** @var \DateTimeInterface $date */ + return [ + 'datetime' => $date->format(\DateTimeInterface::ISO8601), + 'u' => $date->getTimestamp() + ]; + } + + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof \DateTimeInterface; + } +} diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php new file mode 100644 index 000000000..2a71de52b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php @@ -0,0 +1,44 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Serializer\Normalizer; + +use Chill\MainBundle\Entity\User; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * + * + */ +class UserNormalizer implements NormalizerInterface +{ + public function normalize($user, string $format = null, array $context = array()) + { + /** @var User $user */ + return [ + 'id' => $user->getId(), + 'username' => $user->getUsername() + ]; + } + + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof User; + } +} diff --git a/src/Bundle/ChillMainBundle/config/services/serializer.yaml b/src/Bundle/ChillMainBundle/config/services/serializer.yaml new file mode 100644 index 000000000..763576a5c --- /dev/null +++ b/src/Bundle/ChillMainBundle/config/services/serializer.yaml @@ -0,0 +1,13 @@ +--- +services: + Chill\MainBundle\Serializer\Normalizer\CenterNormalizer: + tags: + - { name: 'serializer.normalizer', priority: 64 } + + Chill\MainBundle\Serializer\Normalizer\DateNormalizer: + tags: + - { name: 'serializer.normalizer', priority: 64 } + + Chill\MainBundle\Serializer\Normalizer\UserNormalizer: + tags: + - { name: 'serializer.normalizer', priority: 64 } diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php index e92c84765..9bc732d87 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php @@ -4,17 +4,21 @@ namespace Chill\PersonBundle\Controller; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent; use Chill\PersonBundle\Entity\Person; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Exception\BadRequestException; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; -use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\SerializerInterface; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Validator\Validator\ValidatorInterface; /** * Class AccompanyingCourseController @@ -23,6 +27,21 @@ use Symfony\Component\Serializer\SerializerInterface; */ class AccompanyingCourseController extends Controller { + protected SerializerInterface $serializer; + + protected EventDispatcherInterface $dispatcher; + + protected ValidatorInterface $validator; + + public function __construct( + SerializerInterface $serializer, + EventDispatcherInterface $dispatcher, + ValidatorInterface $validator + ) { + $this->serializer = $serializer; + $this->dispatcher = $dispatcher; + $this->validator = $validator; + } /** * Homepage of Accompanying Course section * @@ -68,45 +87,75 @@ class AccompanyingCourseController extends Controller } /** - * Sérialise temporairement quelques données pour donner à manger au composant vuejs + * Get API Data for showing endpoint + * * @Route( - * "/{_locale}/api/parcours/{accompanying_period_id}/show", - * name="chill_person_accompanying_course_api_show") + * "/{_locale}/person/api/1.0/accompanying-course/{accompanying_period_id}/show.{_format}", + * name="chill_person_accompanying_course_api_show" + * ) * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"}) - * @param SerializerInterface $serializer */ - public function showAPI(AccompanyingPeriod $accompanyingCourse): Response + public function showAPI(AccompanyingPeriod $accompanyingCourse, $_format): Response { - $persons = []; - foreach ($accompanyingCourse->getParticipations() as $k => $participation ) { - /** - * @var AccompanyingPeriodParticipation $participation - * @var Person $person - */ - $person = $participation->getPerson(); - $persons[$k] = [ - 'id' => $person->getId(), - 'firstname' => $person->getFirstName(), - 'lastname' => $person->getLastName(), - 'email' => $person->getEmail(), - 'phone' => $person->getPhonenumber(), - 'startdate' => ($participation->getStartDate()) ? $participation->getStartDate()->format('Y-m-d') : null, - 'enddate' => ($participation->getEndDate()) ? $participation->getEndDate()->format('Y-m-d') : null - ]; - } - $data = [ - 'id' => $accompanyingCourse->getId(), - 'remark' => $accompanyingCourse->getRemark(), - 'closing_motive' => $accompanyingCourse->getClosingMotive() ? $accompanyingCourse->getClosingMotive()->getName()['fr'] : null, - 'opening_date' => ($accompanyingCourse->getOpeningDate()) ? $accompanyingCourse->getOpeningDate()->format('Y-m-d') : null, - 'closing_date' => ($accompanyingCourse->getClosingDate()) ? $accompanyingCourse->getClosingDate()->format('Y-m-d') : null, - 'persons' => $persons - ]; + // TODO check ACL on AccompanyingPeriod + + $this->dispatcher->dispatch( + AccompanyingPeriodPrivacyEvent::ACCOMPANYING_PERIOD_PRIVACY_EVENT, + new AccompanyingPeriodPrivacyEvent($accompanyingCourse, [ + 'action' => 'showApi' + ]) + ); + + switch ($_format) { + case 'json': + return $this->json($accompanyingCourse); + default: + throw new BadRequestException('Unsupported format'); + } - $serialized = \json_encode($data); - $response = new Response($serialized); - $response->headers->set('Content-Type', 'application/json'); - return $response; } + /** + * Get API Data for showing endpoint + * + * @Route( + * "/{_locale}/person/api/1.0/accompanying-course/{accompanying_period_id}/participation.{_format}", + * name="chill_person_accompanying_course_api_add_participation", + * methods={"POST"}, + * format="json", + * requirements={ + * "_format": "json", + * } + * ) + * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"}) + */ + public function addParticipationAPI(Request $request, AccompanyingPeriod $accompanyingCourse, $_format): Response + { + switch ($_format) { + case 'json': + $person = $this->serializer->deserialize($request->getContent(), Person::class, $_format, [ + + ]); + break; + default: + throw new BadRequestException('Unsupported format'); + } + + if (NULL === $person) { + throw new BadRequestException('person id not found'); + } + + // TODO add acl + $accompanyingCourse->addPerson($person); + $errors = $this->validator->validate($accompanyingCourse); + + if ($errors->count() > 0) { + // only format accepted + return $this->json($errors); + } + + $this->getDoctrine()->getManager()->flush(); + + return new JsonResponse(); + } } diff --git a/src/Bundle/ChillPersonBundle/Controller/ApiPersonController.php b/src/Bundle/ChillPersonBundle/Controller/ApiPersonController.php new file mode 100644 index 000000000..bfaf22d7b --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Controller/ApiPersonController.php @@ -0,0 +1,30 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +namespace Chill\PersonBundle\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\HttpFoundation\JsonResponse; + + +class ApiPersonController extends Controller +{ + public function viewAction($id, $_format) + { + + } +} diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php index f8e8173de..9eaa5d17f 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php @@ -38,7 +38,7 @@ use Chill\PersonBundle\Doctrine\DQL\AddressPart; */ class ChillPersonExtension extends Extension implements PrependExtensionInterface { - + /** * {@inheritDoc} * @param array $configs @@ -49,14 +49,14 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - + // set configuration for validation $container->setParameter('chill_person.validation.birtdate_not_before', $config['validation']['birthdate_not_after']); - + $this->handlePersonFieldsParameters($container, $config['person_fields']); $this->handleAccompanyingPeriodsFieldsParameters($container, $config['accompanying_periods_fields']); - + $container->setParameter('chill_person.allow_multiple_simultaneous_accompanying_periods', $config['allow_multiple_simultaneous_accompanying_periods']); @@ -75,19 +75,20 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac $loader->load('services/repository.yaml'); $loader->load('services/templating.yaml'); $loader->load('services/alt_names.yaml'); - + $loader->load('services/serializer.yaml'); + // load service advanced search only if configure if ($config['search']['search_by_phone'] != 'never') { $loader->load('services/search_by_phone.yaml'); $container->setParameter('chill_person.search.search_by_phone', $config['search']['search_by_phone']); } - + if ($container->getParameter('chill_person.accompanying_period') !== 'hidden') { $loader->load('services/exports_accompanying_period.yaml'); } } - + /** * @param ContainerBuilder $container * @param $config @@ -97,9 +98,9 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac if (array_key_exists('enabled', $config)) { unset($config['enabled']); } - + $container->setParameter('chill_person.person_fields', $config); - + foreach ($config as $key => $value) { switch($key) { case 'accompanying_period': @@ -111,7 +112,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac } } } - + /** * @param ContainerBuilder $container * @param $config @@ -119,7 +120,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac private function handleAccompanyingPeriodsFieldsParameters(ContainerBuilder $container, $config) { $container->setParameter('chill_person.accompanying_period_fields', $config); - + foreach ($config as $key => $value) { switch($key) { case 'enabled': @@ -130,7 +131,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac } } } - + /** * @param ContainerBuilder $container * @throws MissingBundleException @@ -150,7 +151,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac ) ); } - + /** * @param ContainerBuilder $container * @throws MissingBundleException @@ -161,7 +162,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac $this->prependHomepageWidget($container); $this->prependDoctrineDQL($container); $this->prependCruds($container); - + //add person_fields parameter as global $chillPersonConfig = $container->getExtensionConfig($this->getAlias()); $config = $this->processConfiguration(new Configuration(), $chillPersonConfig); @@ -179,7 +180,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac $container->prependExtensionConfig('twig', $twigConfig); $this-> declarePersonAsCustomizable($container); - + //declare routes for person bundle $container->prependExtensionConfig('chill_main', array( 'routing' => array( @@ -189,7 +190,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac ) )); } - + /** * Add a widget "add a person" on the homepage, automatically * @@ -208,7 +209,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac ) )); } - + /** * Add role hierarchy. * @@ -225,7 +226,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac ) )); } - + /** * Add DQL function linked with person * @@ -234,7 +235,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac protected function prependDoctrineDQL(ContainerBuilder $container) { //add DQL function to ORM (default entity_manager) - + $container->prependExtensionConfig('doctrine', array( 'orm' => array( 'dql' => array( @@ -257,7 +258,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac ) )); } - + /** * @param ContainerBuilder $container */ diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index f66df6a2d..18ee535b8 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -103,7 +103,7 @@ class AccompanyingPeriod * @ORM\Column(type="text") */ private $remark = ''; - + /** * @var Collection * @@ -112,7 +112,7 @@ class AccompanyingPeriod * ) */ private $comments; - + /** * @var Collection * @@ -121,7 +121,7 @@ class AccompanyingPeriod * cascade={"persist", "remove", "merge", "detach"}) */ private $participations; - + /** * @var AccompanyingPeriod\ClosingMotive * @@ -130,19 +130,19 @@ class AccompanyingPeriod * @ORM\JoinColumn(nullable=true) */ private $closingMotive = null; - + /** * @ORM\ManyToOne(targetEntity=User::class) * @ORM\JoinColumn(nullable=true) */ private $user; - + /** * @ORM\ManyToOne(targetEntity=User::class) * @ORM\JoinColumn(nullable=true) */ private $createdBy; - + /** * @var string * @ORM\Column(type="string", length=32, nullable=true) @@ -154,7 +154,7 @@ class AccompanyingPeriod * @ORM\JoinColumn(nullable=true) */ private $origin; - + /** * @var string * @ORM\Column(type="string", nullable=true) @@ -214,7 +214,7 @@ class AccompanyingPeriod * ) */ private $resources; - + /** * AccompanyingPeriod constructor. * @@ -246,7 +246,7 @@ class AccompanyingPeriod public function setOpeningDate($openingDate) { $this->openingDate = $openingDate; - + return $this; } @@ -272,7 +272,7 @@ class AccompanyingPeriod public function setClosingDate($closingDate) { $this->closingDate = $closingDate; - + return $this; } @@ -285,7 +285,7 @@ class AccompanyingPeriod { return $this->closingDate; } - + /** * @return boolean */ @@ -298,43 +298,43 @@ class AccompanyingPeriod if ($this->getClosingDate() === null) { return true; } - + return false; } - + public function setRemark(string $remark): self { if ($remark === null) { $remark = ''; } - + $this->remark = $remark; - + return $this; } - + public function getRemark(): string { return $this->remark; } - + public function getComments(): Collection { return $this->comments; } - + public function addComment(Comment $comment): self { $this->comments[] = $comment; - + return $this; } - + public function removeComment(Comment $comment): void { $this->comments->removeElement($comment); } - + /** * Get Participations Collection */ @@ -342,7 +342,7 @@ class AccompanyingPeriod { return $this->participations; } - + /** * This private function scan Participations Collection, * searching for a given Person @@ -357,7 +357,7 @@ class AccompanyingPeriod return null; } - + /** * This public function is the same but return only true or false */ @@ -365,7 +365,7 @@ class AccompanyingPeriod { return ($this->participationsContainsPerson($person) === null) ? false : true; } - + /** * Add Person */ @@ -373,36 +373,36 @@ class AccompanyingPeriod { $participation = new AccompanyingPeriodParticipation($this, $person); $this->participations[] = $participation; - + return $this; } - + /** * Remove Person */ public function removePerson(Person $person): void { $participation = $this->participationsContainsPerson($person); - + if (! null === $participation) { $participation->setEndDate(new \DateTimeImmutable('now')); $this->participations->removeElement($participation); } } - - + + public function getClosingMotive(): ?ClosingMotive { return $this->closingMotive; } - + public function setClosingMotive(ClosingMotive $closingMotive = null): self { $this->closingMotive = $closingMotive; - + return $this; } - + /** * If the period can be reopened. * @@ -414,7 +414,7 @@ class AccompanyingPeriod if ($this->isOpen() === true) { return false; } - + $participation = $this->participationsContainsPerson($person); if (!null === $participation) { @@ -422,10 +422,10 @@ class AccompanyingPeriod $periods = $person->getAccompanyingPeriodsOrdered(); return end($periods) === $this; } - + return false; } - + /** */ public function reOpen(): void @@ -442,14 +442,14 @@ class AccompanyingPeriod if ($this->isOpen()) { return; } - + if (! $this->isClosingAfterOpening()) { $context->buildViolation('The date of closing is before the date of opening') ->atPath('dateClosing') ->addViolation(); } } - + /** * Returns true if the closing date is after the opening date. * @@ -467,16 +467,16 @@ class AccompanyingPeriod } return false; } - + function getUser(): ?User { return $this->user; } - + function setUser(User $user): self { $this->user = $user; - + return $this; } @@ -484,38 +484,38 @@ class AccompanyingPeriod { return $this->origin; } - + public function setOrigin(Origin $origin): self { $this->origin = $origin; - + return $this; } - + public function getRequestorPerson(): ?Person { return $this->requestorPerson; } - + public function setRequestorPerson(Person $requestorPerson): self { $this->requestorPerson = ($this->requestorThirdParty === null) ? $requestorPerson : null; - + return $this; } - + public function getRequestorThirdParty(): ?ThirdParty { return $this->requestorThirdParty; } - + public function setRequestorThirdParty(ThirdParty $requestorThirdParty): self { $this->requestorThirdParty = ($this->requestorPerson === null) ? $requestorThirdParty : null; - + return $this; } - + /** * @return Person|ThirdParty */ @@ -523,48 +523,48 @@ class AccompanyingPeriod { return $this->requestorPerson ?? $this->requestorThirdParty; } - + public function isRequestorAnonymous(): bool { return $this->requestorAnonymous; } - + public function setRequestorAnonymous(bool $requestorAnonymous): self { $this->requestorAnonymous = $requestorAnonymous; - + return $this; } - + public function isEmergency(): bool { return $this->emergency; } - + public function setEmergency(bool $emergency): self { $this->emergency = $emergency; - + return $this; } - + public function isConfidential(): bool { return $this->confidential; } - + public function setConfidential(bool $confidential): self { $this->confidential = $confidential; - + return $this; } - + public function getCreatedBy(): ?User { return $this->createdBy; } - + public function setCreatedBy(User $createdBy): self { $this->createdBy = $createdBy; @@ -576,11 +576,11 @@ class AccompanyingPeriod { return $this->step; } - + public function setStep(string $step): self { $this->step = $step; - + return $this; } @@ -588,45 +588,57 @@ class AccompanyingPeriod { return $this->intensity; } - + public function setIntensity(string $intensity): self { $this->intensity = $intensity; - + return $this; } - + public function getScopes(): Collection { return $this->scopes; } - + public function addScope(Scope $scope): self { $this->scopes[] = $scope; - + return $this; } - + public function removeScope(Scope $scope): void { $this->scopes->removeElement($scope); } - + public function getResources(): Collection { return $this->resources; } - + public function addResource(Resource $resource): self { $this->resources[] = $resource; - + return $this; } - + public function removeResource(Resource $resource): void { $this->resources->removeElement($resource); } + + /** + * Get a list of all persons which are participating to this course + */ + public function getPersons(): Collection + { + return $this->participations->map( + function(AccompanyingPeriodParticipation $participation) { + return $participation->getPerson(); + } + ); + } } diff --git a/src/Bundle/ChillPersonBundle/Privacy/AccompanyingPeriodPrivacyEvent.php b/src/Bundle/ChillPersonBundle/Privacy/AccompanyingPeriodPrivacyEvent.php new file mode 100644 index 000000000..e8610b4c1 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Privacy/AccompanyingPeriodPrivacyEvent.php @@ -0,0 +1,52 @@ +, + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +use Symfony\Component\EventDispatcher\Event; +use Chill\PersonBundle\Entity\Person; +use Chill\PersonBundle\Entity\AccompanyingPeriod; + +class AccompanyingPeriodPrivacyEvent extends Event +{ + public const ACCOMPANYING_PERIOD_PRIVACY_EVENT = 'chill_person.accompanying_period_privacy_event'; + + protected AccompanyingPeriod $period; + + protected array $args; + + public function __construct($period, $args = []) + { + $this->period = $period; + $this->args = $args; + } + + public function getPeriod(): AccompanyingPeriod + { + return $this->period; + } + + public function getArgs(): array + { + return $this->args; + } +} diff --git a/src/Bundle/ChillPersonBundle/Privacy/PrivacyEventSubscriber.php b/src/Bundle/ChillPersonBundle/Privacy/PrivacyEventSubscriber.php index b6cc50e54..c37fc67b2 100644 --- a/src/Bundle/ChillPersonBundle/Privacy/PrivacyEventSubscriber.php +++ b/src/Bundle/ChillPersonBundle/Privacy/PrivacyEventSubscriber.php @@ -26,20 +26,21 @@ use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Chill\PersonBundle\Entity\Person; +use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent; class PrivacyEventSubscriber implements EventSubscriberInterface { - + /** * @var LoggerInterface */ protected $logger; - + /** * @var TokenStorageInterface */ protected $token; - + /** * PrivacyEventSubscriber constructor. * @@ -50,40 +51,64 @@ class PrivacyEventSubscriber implements EventSubscriberInterface $this->logger = $logger; $this->token = $token; } - + public static function getSubscribedEvents() { - return array(PrivacyEvent::PERSON_PRIVACY_EVENT => array( - array('onPrivacyEvent') - )); + return [ + PrivacyEvent::PERSON_PRIVACY_EVENT => [ + ['onPrivacyEvent'] + ], + AccompanyingPeriodPrivacyEvent::ACCOMPANYING_PERIOD_PRIVACY_EVENT => [ + ['onAccompanyingPeriodPrivacyEvent'] + ] + ]; } - + + public function onAccompanyingPeriodPrivacyEvent(AccompanyingPeriodPrivacyEvent $event) + { + $involved = $this->getInvolved(); + $involved['period_id'] = $event->getPeriod()->getId(); + $involved['persons'] = $event->getPeriod()->getPersons() + ->map(function(Person $p) { return $p->getId(); }) + ->toArray(); + + $this->logger->notice( + "[Privacy Event] An accompanying period has been viewed", + array_merge($involved, $event->getArgs()) + ); + } + public function onPrivacyEvent(PrivacyEvent $event) { - $persons = array(); - + $persons = []; + if ($event->hasPersons() === true) { foreach ($event->getPersons() as $person) { $persons[] = $person->getId(); } } - $involved = array( - 'by_user' => $this->token->getToken()->getUser()->getUsername(), - 'by_user_id' => $this->token->getToken()->getUser()->getId(), - 'person_id' => $event->getPerson()->getId(), - ); - + $involved = $this->getInvolved(); + $involved['person_id'] = $event->getPerson()->getId(); + if ($event->hasPersons()) { $involved['persons'] = \array_map( - function(Person $p) { return $p->getId(); }, + function(Person $p) { return $p->getId(); }, $event->getPersons() ); } - + $this->logger->notice( "[Privacy Event] A Person Folder has been viewed", array_merge($involved, $event->getArgs()) ); } -} \ No newline at end of file + + protected function getInvolved(): array + { + return [ + 'by_user' => $this->token->getToken()->getUser()->getUsername(), + 'by_user_id' => $this->token->getToken()->getUser()->getId(), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodNormalizer.php new file mode 100644 index 000000000..55a59700d --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodNormalizer.php @@ -0,0 +1,59 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +namespace Chill\PersonBundle\Serializer\Normalizer; + +use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + + +class AccompanyingPeriodNormalizer implements NormalizerInterface, NormalizerAwareInterface { + + protected ?NormalizerInterface $normalizer = null; + + public function normalize($period, string $format = null, array $context = array()) + { + /** @var AccompanyingPeriod $period */ + return [ + 'id' => $period->getId(), + 'openingDate' => $this->normalizer->normalize($period->getOpeningDate(), $format), + 'closingDate' => $this->normalizer->normalize($period->getClosingDate(), $format), + 'remark' => $period->getRemark(), + 'participations' => $this->normalizer->normalize($period->getParticipations(), $format), + 'closingMotive' => $this->normalizer->normalize($period->getClosingMotive(), $format), + 'user' => $period->getUser() ? $this->normalize($period->getUser(), $format) : null, + 'step' => $period->getStep(), + 'origin' => $this->normalizer->normalize($period->getOrigin(), $format), + 'intensity' => $period->getIntensity(), + 'emergency' => $period->isEmergency(), + 'confidential' => $period->isConfidential() + ]; + } + + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof AccompanyingPeriod; + } + + public function setNormalizer(NormalizerInterface $normalizer) + { + $this->normalizer = $normalizer; + } +} diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodParticipationNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodParticipationNormalizer.php new file mode 100644 index 000000000..59e430827 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodParticipationNormalizer.php @@ -0,0 +1,51 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +namespace Chill\PersonBundle\Serializer\Normalizer; + +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + + +class AccompanyingPeriodParticipationNormalizer implements NormalizerInterface, NormalizerAwareInterface { + + protected ?NormalizerInterface $normalizer = null; + + public function normalize($participation, string $format = null, array $context = array()) + { + /** @var AccompanyingPeriodParticipation $participation */ + return [ + 'id' => $participation->getId(), + 'startDate' => $this->normalizer->normalize($participation->getStartDate(), $format), + 'endDate' => $this->normalizer->normalize($participation->getEndDate(), $format), + 'person' => $this->normalizer->normalize($participation->getPerson(), $format) + ]; + } + + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof AccompanyingPeriodParticipation; + } + + public function setNormalizer(NormalizerInterface $normalizer) + { + $this->normalizer = $normalizer; + } +} diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php new file mode 100644 index 000000000..90a816ebc --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php @@ -0,0 +1,95 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +namespace Chill\PersonBundle\Serializer\Normalizer; + +use Chill\PersonBundle\Entity\Person; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; +use Chill\PersonBundle\Repository\PersonRepository; +use Symfony\Component\Serializer\Exception\RuntimeException; +use Symfony\Component\Serializer\Exception\UnexpectedValueException; + + +/** + * Serialize a Person entity + * + */ +class PersonNormalizer implements + NormalizerInterface, + NormalizerAwareInterface, + DenormalizerInterface +{ + + protected NormalizerInterface $normalizer; + + protected PersonRepository $repository; + + public const GET_PERSON = 'get_person'; + + public function __construct(PersonRepository $repository) + { + $this->repository = $repository; + } + + public function normalize($person, string $format = null, array $context = array()) + { + /** @var Person $person */ + return [ + 'id' => $person->getId(), + 'firstName' => $person->getFirstName(), + 'lastName' => $person->getLastName(), + 'birthdate' => $person->getBirthdate(), + 'center' => $this->normalizer->normalize($person->getCenter()) + ]; + } + + public function denormalize($data, string $type, string $format = null, array $context = []): Person + { + if ($context[self::GET_PERSON] ?? true) { + $id = $data['id'] ?? null; + if (NULL === $id) { + throw new RuntimeException("missing id into person object"); + } + } + /** var Person $person */ + $person = $this->repository->findOneById($id); + + if (NULL === $person) { + return UnexpectedValueException("person id not found"); + } + + return $person; + } + + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof Person; + } + + public function supportsDenormalization($data, string $type, ?string $format = NULL): bool + { + return Person::class === $type; + } + + public function setNormalizer(NormalizerInterface $normalizer) + { + $this->normalizer = $normalizer; + } +} diff --git a/src/Bundle/ChillPersonBundle/config/services/controller.yaml b/src/Bundle/ChillPersonBundle/config/services/controller.yaml index 24b35a8e3..893b1cfd3 100644 --- a/src/Bundle/ChillPersonBundle/config/services/controller.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/controller.yaml @@ -40,4 +40,8 @@ services: tags: ['controller.service_arguments'] Chill\PersonBundle\Controller\AccompanyingCourseController: + arguments: + $serializer: '@Symfony\Component\Serializer\SerializerInterface' + $dispatcher: '@Symfony\Contracts\EventDispatcher\EventDispatcherInterface' + $validator: '@Symfony\Component\Validator\Validator\ValidatorInterface' tags: ['controller.service_arguments'] diff --git a/src/Bundle/ChillPersonBundle/config/services/serializer.yaml b/src/Bundle/ChillPersonBundle/config/services/serializer.yaml new file mode 100644 index 000000000..092dc2320 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/config/services/serializer.yaml @@ -0,0 +1,15 @@ +--- +services: + Chill\PersonBundle\Serializer\Normalizer\PersonNormalizer: + arguments: + $repository: '@Chill\PersonBundle\Repository\PersonRepository' + tags: + - { name: 'serializer.normalizer', priority: 64 } + + Chill\PersonBundle\Serializer\Normalizer\AccompanyingPeriodNormalizer: + tags: + - { name: 'serializer.normalizer', priority: 64 } + + Chill\PersonBundle\Serializer\Normalizer\AccompanyingPeriodParticipationNormalizer: + tags: + - { name: 'serializer.normalizer', priority: 64 }