From fdbaa8cbef9b64ab2ef96acb6d0ec25389daa2a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 23 Nov 2021 23:04:29 +0100 Subject: [PATCH 01/51] add endpoint for getting permissions info --- .../Controller/PermissionApiController.php | 67 +++++++++++++++++++ .../PermissionApiControllerTest.php | 36 ++++++++++ .../ChillMainBundle/chill.api.specs.yaml | 37 ++++++++++ 3 files changed, 140 insertions(+) create mode 100644 src/Bundle/ChillMainBundle/Controller/PermissionApiController.php create mode 100644 src/Bundle/ChillMainBundle/Tests/Controller/PermissionApiControllerTest.php diff --git a/src/Bundle/ChillMainBundle/Controller/PermissionApiController.php b/src/Bundle/ChillMainBundle/Controller/PermissionApiController.php new file mode 100644 index 000000000..beeb76089 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/PermissionApiController.php @@ -0,0 +1,67 @@ +denormalizer = $denormalizer; + $this->security = $security; + } + + /** + * @Route("/api/1.0/main/permissions/info.json", methods={"POST"}) + * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface + */ + public function getPermissions(Request $request): JsonResponse + { + $this->denyAccessUnlessGranted('ROLE_USER'); + + $data = \json_decode($request->getContent(), true); + + if (null === $data) { + throw new BadRequestHttpException(sprintf( + "Could not decode json received, or data invalid: %s, %s", \json_last_error(), \json_last_error_msg() + )); + } + + if (!\array_key_exists('object', $data)) { + throw new BadRequestHttpException("the object key is not present"); + } + if (!\array_key_exists('class', $data)) { + throw new BadRequestHttpException("the class key is not present"); + } + + if (null !== $data['object']) { + $object = $this->denormalizer->denormalize($data['object'], $data['class'], 'json'); + } else { + $object = null; + } + $roles = []; + + foreach (($data['roles'] ?? []) as $role) { + $roles[$role] = $this->security->isGranted($role, $object); + } + + return $this->json( + ['roles' => $roles, ], + 200, + [], + ); + } + +} diff --git a/src/Bundle/ChillMainBundle/Tests/Controller/PermissionApiControllerTest.php b/src/Bundle/ChillMainBundle/Tests/Controller/PermissionApiControllerTest.php new file mode 100644 index 000000000..0eac990ec --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Controller/PermissionApiControllerTest.php @@ -0,0 +1,36 @@ +getClientAuthenticated(); + + $client->request( + 'POST', + '/api/1.0/main/permissions/info.json', + [], // parameters + [], // files + [], // server + \json_encode([ + 'object' => null, + 'class' => null, + 'roles' => ['ROLE_USER', 'ROLE_ADMIN'] + ]) + ); + + $this->assertResponseIsSuccessful(); + + $data = \json_decode($client->getResponse()->getContent(), true); + $this->assertTrue($data['roles']['ROLE_USER']); + $this->assertFalse($data['roles']['ROLE_ADMIN']); + } + +} diff --git a/src/Bundle/ChillMainBundle/chill.api.specs.yaml b/src/Bundle/ChillMainBundle/chill.api.specs.yaml index 110547201..ff0c844df 100644 --- a/src/Bundle/ChillMainBundle/chill.api.specs.yaml +++ b/src/Bundle/ChillMainBundle/chill.api.specs.yaml @@ -624,3 +624,40 @@ paths: 401: description: "Unauthorized" + /1.0/main/permissions/info.json: + post: + tags: + - permissions + summary: Return info about permissions on entity + responses: + 200: + description: "ok" + 401: + description: "Unauthorized" + 400: + description: "Bad request" + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + object: + type: object + class: + type: string + roles: + type: array + items: + type: string + examples: + an-accompanying-period: + value: + object: + type: accompanying_period + id: 1 + class: 'Chill\PersonBundle\Entity\AccompanyingPeriod' + roles: + - 'CHILL_PERSON_ACCOMPANYING_PERIOD_SEE' + From 9993bfc96f1b8b1d444f8b1a01e0d518fe6a9c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 23 Nov 2021 23:28:56 +0100 Subject: [PATCH 02/51] fix cs and add test --- .../Controller/PermissionApiController.php | 31 +++++++++---- .../PermissionApiControllerTest.php | 44 +++++++++++++++++-- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/PermissionApiController.php b/src/Bundle/ChillMainBundle/Controller/PermissionApiController.php index beeb76089..3476c4bbe 100644 --- a/src/Bundle/ChillMainBundle/Controller/PermissionApiController.php +++ b/src/Bundle/ChillMainBundle/Controller/PermissionApiController.php @@ -1,5 +1,12 @@ denyAccessUnlessGranted('ROLE_USER'); - $data = \json_decode($request->getContent(), true); + $data = json_decode($request->getContent(), true); if (null === $data) { throw new BadRequestHttpException(sprintf( - "Could not decode json received, or data invalid: %s, %s", \json_last_error(), \json_last_error_msg() + 'Could not decode json received, or data invalid: %s, %s', + json_last_error(), + json_last_error_msg() )); } - if (!\array_key_exists('object', $data)) { - throw new BadRequestHttpException("the object key is not present"); + if (!array_key_exists('object', $data)) { + throw new BadRequestHttpException('the object key is not present'); } - if (!\array_key_exists('class', $data)) { - throw new BadRequestHttpException("the class key is not present"); + + if (!array_key_exists('class', $data)) { + throw new BadRequestHttpException('the class key is not present'); } if (null !== $data['object']) { @@ -54,14 +68,13 @@ class PermissionApiController extends AbstractController $roles = []; foreach (($data['roles'] ?? []) as $role) { - $roles[$role] = $this->security->isGranted($role, $object); + $roles[$role] = $this->security->isGranted($role, $object); } return $this->json( - ['roles' => $roles, ], + ['roles' => $roles], 200, [], ); } - } diff --git a/src/Bundle/ChillMainBundle/Tests/Controller/PermissionApiControllerTest.php b/src/Bundle/ChillMainBundle/Tests/Controller/PermissionApiControllerTest.php index 0eac990ec..f267421bf 100644 --- a/src/Bundle/ChillMainBundle/Tests/Controller/PermissionApiControllerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Controller/PermissionApiControllerTest.php @@ -1,14 +1,51 @@ getClientAuthenticated(); + + $client->request( + 'POST', + '/api/1.0/main/permissions/info.json', + [], // parameters + [], // files + [], // server + json_encode([ + 'object' => [ + 'datetime' => '1969-07-09T00:00:00+0100', + ], + 'class' => DateTime::class, + 'roles' => ['FOO_ROLE'], + ]) + ); + + $this->assertResponseIsSuccessful(); + + $data = json_decode($client->getResponse()->getContent(), true); + $this->assertFalse($data['roles']['FOO_ROLE']); + } + public function testNullObject() { $client = $this->getClientAuthenticated(); @@ -19,18 +56,17 @@ class PermissionApiControllerTest extends WebTestCase [], // parameters [], // files [], // server - \json_encode([ + json_encode([ 'object' => null, 'class' => null, - 'roles' => ['ROLE_USER', 'ROLE_ADMIN'] + 'roles' => ['ROLE_USER', 'ROLE_ADMIN'], ]) ); $this->assertResponseIsSuccessful(); - $data = \json_decode($client->getResponse()->getContent(), true); + $data = json_decode($client->getResponse()->getContent(), true); $this->assertTrue($data['roles']['ROLE_USER']); $this->assertFalse($data['roles']['ROLE_ADMIN']); } - } From a2681a02f9e6e925c123a80930add8800e23f223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 24 Nov 2021 14:20:17 +0000 Subject: [PATCH 03/51] remove deprecation on PersonHasCenterValidator and fix tests --- phpstan-deprecations.neon | 8 -------- .../Person/PersonHasCenterValidatorTest.php | 16 +++++++++++++--- .../Person/PersonHasCenterValidator.php | 10 +++++----- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/phpstan-deprecations.neon b/phpstan-deprecations.neon index bf1ec095e..6f9f19956 100644 --- a/phpstan-deprecations.neon +++ b/phpstan-deprecations.neon @@ -1330,14 +1330,6 @@ parameters: count: 1 path: src/Bundle/ChillPersonBundle/Search/SimilarPersonMatcher.php - - - message: - """ - #^Parameter \\$centerResolverDispatcher of method Chill\\\\PersonBundle\\\\Validator\\\\Constraints\\\\Person\\\\PersonHasCenterValidator\\:\\:__construct\\(\\) has typehint with deprecated interface Chill\\\\MainBundle\\\\Security\\\\Resolver\\\\CenterResolverDispatcherInterface\\: - Use CenterResolverManager and its interface CenterResolverManagerInterface$# - """ - count: 1 - path: src/Bundle/ChillPersonBundle/Validator/Constraints/Person/PersonHasCenterValidator.php - message: diff --git a/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonHasCenterValidatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonHasCenterValidatorTest.php index 5d1bbeb9e..5c101740e 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonHasCenterValidatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonHasCenterValidatorTest.php @@ -12,10 +12,11 @@ declare(strict_types=1); namespace Validator\Person; use Chill\MainBundle\Entity\Center; -use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface; +use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Validator\Constraints\Person\PersonHasCenter; use Chill\PersonBundle\Validator\Constraints\Person\PersonHasCenterValidator; +use Prophecy\Argument; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -52,9 +53,18 @@ class PersonHasCenterValidatorTest extends ConstraintValidatorTestCase ], ]); - $centerResolverDispatcher = $this->createMock(CenterResolverDispatcherInterface::class); + $prophecy = $this->prophesize(CenterResolverManagerInterface::class); + $prophecy->resolveCenters(Argument::type(Person::class), Argument::any())->will(function ($args) { + $center = $args[0]->getCenter(); - return new PersonHasCenterValidator($parameterBag, $centerResolverDispatcher); + if ($center instanceof Center) { + return [$center]; + } + + return []; + }); + + return new PersonHasCenterValidator($parameterBag, $prophecy->reveal()); } protected function getConstraint() diff --git a/src/Bundle/ChillPersonBundle/Validator/Constraints/Person/PersonHasCenterValidator.php b/src/Bundle/ChillPersonBundle/Validator/Constraints/Person/PersonHasCenterValidator.php index 7612cec8c..967b5672f 100644 --- a/src/Bundle/ChillPersonBundle/Validator/Constraints/Person/PersonHasCenterValidator.php +++ b/src/Bundle/ChillPersonBundle/Validator/Constraints/Person/PersonHasCenterValidator.php @@ -11,7 +11,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Validator\Constraints\Person; -use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface; +use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface; use Chill\PersonBundle\Entity\Person; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Validator\Constraint; @@ -22,12 +22,12 @@ class PersonHasCenterValidator extends ConstraintValidator { private bool $centerRequired; - private CenterResolverDispatcherInterface $centerResolverDispatcher; + private CenterResolverManagerInterface $centerResolverManager; - public function __construct(ParameterBagInterface $parameterBag, CenterResolverDispatcherInterface $centerResolverDispatcher) + public function __construct(ParameterBagInterface $parameterBag, CenterResolverManagerInterface $centerResolverManager) { $this->centerRequired = $parameterBag->get('chill_person')['validation']['center_required']; - $this->centerResolverDispatcher = $centerResolverDispatcher; + $this->centerResolverManager = $centerResolverManager; } public function validate($person, Constraint $constraint) @@ -40,7 +40,7 @@ class PersonHasCenterValidator extends ConstraintValidator return; } - if (null === $this->centerResolverDispatcher->resolveCenter($person)) { + if ([] === $this->centerResolverManager->resolveCenters($person)) { $this ->context ->buildViolation($constraint->message) From b81c834b5e4664843fa75039d88da1b65dbf97fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 24 Nov 2021 15:14:06 +0000 Subject: [PATCH 04/51] check acl before showing button for creating activity in person/activity --- CHANGELOG.md | 2 ++ .../Resources/views/Activity/list.html.twig | 1 - .../Resources/views/Activity/listPerson.html.twig | 4 +++- .../Tests/Validator/Person/PersonHasCenterValidatorTest.php | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80eb4fd1e..8805ed944 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to * Use the user.label in accompanying course banner, instead of username; * fix: show validation message when closing accompanying course; * [thirdparty] link from modal to thirdparty detail page fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/228) +* [activity] remove the "plus" button in activity list +* [activity] check ACL on activity list in person context ## Test releases diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig index fef381527..7c75f5bf0 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig @@ -3,7 +3,6 @@ {% if activities|length == 0 %}

{{ "There isn't any activities."|trans }} -

{% else %} diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/listPerson.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/listPerson.html.twig index 830fecfb9..158bd0e1b 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/listPerson.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/listPerson.html.twig @@ -36,12 +36,14 @@ {% include 'ChillActivityBundle:Activity:list.html.twig' with {'context': 'person'} %} + {% if is_granted('CHILL_ACTIVITY_CREATE', person) %} + {% endif %} {% endblock %} diff --git a/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonHasCenterValidatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonHasCenterValidatorTest.php index 5c101740e..6603ca795 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonHasCenterValidatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Validator/Person/PersonHasCenterValidatorTest.php @@ -54,6 +54,7 @@ class PersonHasCenterValidatorTest extends ConstraintValidatorTestCase ]); $prophecy = $this->prophesize(CenterResolverManagerInterface::class); + $prophecy->resolveCenters(Argument::type(Person::class), Argument::any())->will(function ($args) { $center = $args[0]->getCenter(); From 09a96df6a48983d6b8ef80735261645936cc9a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 24 Nov 2021 16:16:40 +0100 Subject: [PATCH 05/51] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8805ed944..a54cee2e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to * [thirdparty] link from modal to thirdparty detail page fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/228) * [activity] remove the "plus" button in activity list * [activity] check ACL on activity list in person context +* add an endpoint for checking permissions. See https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/232 ## Test releases From 551486b5eaae9b57aa1313c2203f4d2acf0493c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 24 Nov 2021 15:55:45 +0000 Subject: [PATCH 06/51] List for accompanying course for a person: filter using acl --- CHANGELOG.md | 1 + .../Controller/AccompanyingPeriodController.php | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8805ed944..c98d7db27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to * [thirdparty] link from modal to thirdparty detail page fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/228) * [activity] remove the "plus" button in activity list * [activity] check ACL on activity list in person context +* [list for accompanying course in person] filter list using ACL ## Test releases diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodController.php index 893dda03c..55b18381e 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodController.php @@ -213,9 +213,14 @@ class AccompanyingPeriodController extends AbstractController ]); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); - $accompanyingPeriods = $this->accompanyingPeriodACLAwareRepository + $accompanyingPeriodsRaw = $this->accompanyingPeriodACLAwareRepository ->findByPerson($person, AccompanyingPeriodVoter::SEE); + // filter visible or not visible + $accompanyingPeriods = array_filter($accompanyingPeriodsRaw, function (AccompanyingPeriod $ap) { + return $this->isGranted(AccompanyingPeriodVoter::SEE, $ap); + }); + return $this->render('@ChillPerson/AccompanyingPeriod/list.html.twig', [ 'accompanying_periods' => $accompanyingPeriods, 'person' => $person, From 5781f2af890bd1800f4dbda5651ab230ed968507 Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 24 Nov 2021 17:21:43 +0100 Subject: [PATCH 07/51] main: add the coordinates of the postal code if no full address is selected --- .../public/vuejs/Address/components/AddAddress.vue | 6 ++++++ .../Address/components/AddAddress/AddressSelection.vue | 1 - .../vuejs/Address/components/AddAddress/CitySelection.vue | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue index 7bf088fb6..87ecff7fb 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue @@ -588,6 +588,12 @@ export default { newAddress = Object.assign(newAddress, { 'point': this.entity.selected.address.point.coordinates }); + } else { + if (this.entity.selected.postcode.coordinates) { + newAddress = Object.assign(newAddress, { + 'point': this.entity.selected.postcode.coordinates + }); + } } // add the address reference, if any diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue index ca2f5d634..f3d5a9adc 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue @@ -141,7 +141,6 @@ export default { } }, addAddress() { - console.log('addAddress: pass here ?? never, it seems'); this.entity.selected.writeNew.address = true; } } diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue index 3e3dd98d4..aeb5be613 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue @@ -101,6 +101,7 @@ export default { this.entity.selected.city = value; this.entity.selected.postcode.name = value.name; this.entity.selected.postcode.code = value.code; + this.entity.selected.postcode.coordinates = value.center.coordinates; this.entity.selected.writeNew.postcode = false; console.log('writeNew.postcode false, in selectCity'); this.$emit('getReferenceAddresses', value); From 4c4ba73c89493ba2c8653a3b5b3a0c0c8904a0e7 Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 24 Nov 2021 17:41:58 +0100 Subject: [PATCH 08/51] address: translate multi-select messages --- .../Address/components/AddAddress/AddressSelection.vue | 6 ++++-- .../vuejs/Address/components/AddAddress/CitySelection.vue | 2 ++ .../Address/components/AddAddress/CountrySelection.vue | 2 ++ .../ChillMainBundle/Resources/public/vuejs/Address/i18n.js | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue index f3d5a9adc..2409dca53 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue @@ -4,6 +4,10 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue index aeb5be613..6e6c064b5 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue @@ -12,6 +12,8 @@ label="value" :custom-label="transName" :placeholder="$t('select_city')" + :select-label="$t('press_enter_to_select')" + :deselect-label="$t('create_postal_code')" :taggable="true" :multiple="false" @tag="addPostcode" diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue index 2e6ec86a7..c15db8d2a 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue @@ -9,6 +9,8 @@ v-bind:placeholder="$t('select_country')" v-bind:options="sortedCountries" v-model="value" + :select-label="$t('press_enter_to_select')" + :deselect-label="$t('press_enter_to_remove')" @select="selectCountry"> diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/i18n.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/i18n.js index 9d7185696..ab0032de6 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/i18n.js +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/i18n.js @@ -1,5 +1,7 @@ const addressMessages = { fr: { + press_enter_to_select: 'Appuyer sur Entrée pour sélectionner', + press_enter_to_remove: 'Appuyer sur Entrée pour désélectionner', add_an_address_title: 'Créer une adresse', edit_an_address_title: 'Modifier une adresse', create_a_new_address: 'Créer une nouvelle adresse', From b594826f1ec642672051bfe271936aa67586d563 Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 24 Nov 2021 17:44:51 +0100 Subject: [PATCH 09/51] upd CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80eb4fd1e..7f5d7cb87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to ## Unreleased +* [main] translate multiselect messages when selecting/creating address +* [main] set the coordinates of the city when creating a new address OR choosing "pas d'adresse complète" * Use the user.label in accompanying course banner, instead of username; * fix: show validation message when closing accompanying course; * [thirdparty] link from modal to thirdparty detail page fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/228) From 983cfb646cd456472d59a01926ae989f3f28bc91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 25 Nov 2021 12:11:22 +0100 Subject: [PATCH 10/51] Merge add_toasters: handle 422 method and add toaster into course editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/221 Squashed commit of the following: commit 4f68f83aba74a88898779037aeb24d45c622759e Author: Julien Fastré Date: Thu Nov 25 12:07:37 2021 +0100 scope in course editor: show toast when error, instead of restoring the previous state commit fdca8c1c87a4972bb6107bb16ab76844a28cae72 Merge: 53d6e68f8 b97f49782 Author: Julien Fastré Date: Wed Nov 24 17:39:27 2021 +0100 Merge remote-tracking branch 'origin/add_toasters' into add_toasters commit 53d6e68f8c5e158ac1848403d18146690ffeacf2 Author: Julien Fastré Date: Wed Nov 24 17:38:52 2021 +0100 better validation messages commit b97f497822fa6f6057e3b1cf3697044192ea9052 Author: Julie Lenaerts Date: Wed Nov 24 17:36:59 2021 +0100 methods added to api.js again so they can be imported in other bundles still. To be refactored later with general makeFetch method? commit e6089f75b4f66669cbd84f496c3b867901de9c8c Author: Julien Fastré Date: Wed Nov 24 17:20:05 2021 +0100 fix validator for participation overlaps commit e47e6741b9f179dae63f39a596b7ba7410b3ac88 Merge: 25a80fcb2 05385509e Author: Julien Fastré Date: Wed Nov 24 16:31:50 2021 +0100 Merge remote-tracking branch 'origin/master' into add_toasters commit 25a80fcb26cfb3ab7afab0abc1fcb6c0d0af7a6d Author: Julie Lenaerts Date: Tue Nov 23 18:22:35 2021 +0100 error object/message changed if not 422 or 403 commit 0c602cee8c127ab7f0723819a691a98c4e0615e2 Author: Julie Lenaerts Date: Tue Nov 23 17:13:47 2021 +0100 Toasts displayed with differentiation between different errors error objects created accordingly commit 252e57a06da1101e4ebc025974b247c472b8c2ed Author: Julie Lenaerts Date: Tue Nov 23 17:12:55 2021 +0100 generic makeFetch implemented almost everywhere api.js file no longer needs to contain a separate function to make an api call. commit 8532eeee7656f6f9761168fb5e2102778ee932b7 Author: Julie Lenaerts Date: Tue Nov 23 17:10:48 2021 +0100 Moved generic api methods into one file apiMethods.js imports adapted throughout bundles commit c44bd5e75b32d5fbc8951247324ebf5fb85f3b37 Author: Julie Lenaerts Date: Tue Nov 23 11:27:24 2021 +0100 migration deleted commit dbeee090f46b3653c306a1825dcb05637e1f9b31 Author: Julie Lenaerts Date: Tue Nov 23 11:27:13 2021 +0100 toast plugin initialized with options in the root commit ec3919e357ee26f6c5016360d2083d19688217f4 Merge: 1ab401b5d b8889ed58 Author: Julie Lenaerts Date: Tue Nov 23 10:52:24 2021 +0100 merge conflicts fixed commit 1ab401b5d55a28f9c027ee539da58ef1da3d797c Author: Julie Lenaerts Date: Tue Nov 23 10:40:02 2021 +0100 add options when vue toast plugin is loaded commit ad4e630bdd87c8ec16455bab0c2b053c1d838050 Author: Julie Lenaerts Date: Mon Nov 22 18:58:44 2021 +0100 fixes to throw and catch error commit 1c19748866d738bcb9e336f1035d9d78c6941d34 Author: Julie Lenaerts Date: Wed Nov 17 20:22:06 2021 +0100 toast added to banner commit 35949b90532b60161e14e815a78b3b69b053b62d Author: Julie Lenaerts Date: Wed Nov 17 20:16:35 2021 +0100 few more actions using makeFetch commit e94c13e396f496565955294aec360db37f92374b Author: Julie Lenaerts Date: Wed Nov 17 19:48:58 2021 +0100 For accompanyingcourse app general makeFetch + displayToast implemented commit 35eac75edf2c8d39ee2a04f119bb99a946fe3ea9 Author: Julie Lenaerts Date: Wed Nov 17 17:12:31 2021 +0100 general makeFetch method implemented + toasts displayed in progress commit 541bb10547bd7b96815a22a755ecf7f809d8cc87 Author: Julie Lenaerts Date: Wed Nov 17 12:36:20 2021 +0100 general fetch method adjusted and tested on addParticipation: works commit b7f27e8079bc815d88ffc925ec7a4fae3fba5f02 Author: Julie Lenaerts Date: Wed Nov 17 11:50:09 2021 +0100 changelog updated commit 028519ee3e93991aeb3131656e5f17e728ebdb1c Author: Julie Lenaerts Date: Wed Nov 17 11:42:55 2021 +0100 adjustment of error message using templating render commit 508139b4476a46028a40b0b5300a8c65f79414a9 Author: Julie Lenaerts Date: Wed Nov 17 11:41:52 2021 +0100 toast display for 422 errors on participations, requestor, resources + bugfix in requestor component (display of 'remove' btn if non-anonymous commit 3e5d4862fcbbf5708f52c0038bf030dc0cbae5bb Author: Julie Lenaerts Date: Tue Nov 16 15:38:03 2021 +0100 debugging fetch method commit 8b971a2357aac952ea6cd9487da20f4064c26a8d Author: Julie Lenaerts Date: Tue Nov 16 14:31:45 2021 +0100 general fetch method in _abstratAPI and testcase implementation; still needs to take care of commit into store commit 05488740ab138ed279a4626310c5427d9f732793 Author: Julie Lenaerts Date: Tue Nov 16 11:13:28 2021 +0100 testcase: toaster working when adding a resource to an accompanying course. Needs to be generalized commit 5050e8a516107710216fa11aafcf852c52e1eb03 Author: Julie Lenaerts Date: Tue Nov 16 09:22:36 2021 +0100 start of toaster popup commit b8889ed58d3abb68d1c2859b31bf7428addda36e Author: Julie Lenaerts Date: Mon Nov 22 18:58:44 2021 +0100 fixes to throw and catch error commit 0c017eb908e65919876d3bed69d3daa27699f2cc Author: Julie Lenaerts Date: Wed Nov 17 20:22:06 2021 +0100 toast added to banner commit 80d8c5f12be4b0cfcded2948f385b4e3627e9afb Author: Julie Lenaerts Date: Wed Nov 17 20:16:35 2021 +0100 few more actions using makeFetch commit 9cfcc61d995f75651902af97fe640f00f994ce9b Author: Julie Lenaerts Date: Wed Nov 17 19:48:58 2021 +0100 For accompanyingcourse app general makeFetch + displayToast implemented commit 2855b0293a4a3fef2baf22fa035316bac13abf3f Author: Julie Lenaerts Date: Wed Nov 17 17:12:31 2021 +0100 general makeFetch method implemented + toasts displayed in progress commit febbc8b9cdd9ec811546ce2948e2e1bded7c2116 Author: Julie Lenaerts Date: Wed Nov 17 12:36:20 2021 +0100 general fetch method adjusted and tested on addParticipation: works commit 16b59681b97cf995689aa464ae2a310c3efbb603 Author: Julie Lenaerts Date: Wed Nov 17 11:50:09 2021 +0100 changelog updated commit 41df29bdef84af010892708bfd0e37ff826f7aee Author: Julie Lenaerts Date: Wed Nov 17 11:42:55 2021 +0100 adjustment of error message using templating render commit 8c98fe602c26e61285f12f5f7d58d646655f4caa Author: Julie Lenaerts Date: Wed Nov 17 11:41:52 2021 +0100 toast display for 422 errors on participations, requestor, resources + bugfix in requestor component (display of 'remove' btn if non-anonymous commit 689ca8aa0ac249d577fe2ead5b3fcc0298dbad90 Author: Julie Lenaerts Date: Tue Nov 16 15:38:03 2021 +0100 debugging fetch method commit 07234c5fa996fb9f0352719ee366b34b093807eb Author: Julie Lenaerts Date: Tue Nov 16 14:31:45 2021 +0100 general fetch method in _abstratAPI and testcase implementation; still needs to take care of commit into store commit 2a482516e952aa720b5aa8b4cb68ab33f93ce47b Author: Julie Lenaerts Date: Tue Nov 16 11:13:28 2021 +0100 testcase: toaster working when adding a resource to an accompanying course. Needs to be generalized commit b7dcc5e7d4b1489a8edc175a0c2c1b0d649c6c0f Author: Julie Lenaerts Date: Tue Nov 16 09:22:36 2021 +0100 start of toaster popup --- CHANGELOG.md | 8 + .../Resources/public/lib/api/apiMethods.js | 106 +++++ .../Resources/public/lib/api/download.js | 64 +-- .../Resources/public/lib/api/scope.js | 14 +- .../public/vuejs/_components/Confidential.vue | 1 - .../Entity/AccompanyingPeriod.php | 4 +- .../Resources/public/lib/household.js | 2 +- .../public/vuejs/AccompanyingCourse/App.vue | 2 +- .../public/vuejs/AccompanyingCourse/api.js | 226 +--------- .../components/Banner/ToggleFlags.vue | 27 +- .../components/ButtonLocation.vue | 10 +- .../AccompanyingCourse/components/Comment.vue | 20 +- .../AccompanyingCourse/components/Confirm.vue | 11 +- .../components/CourseLocation.vue | 19 +- .../components/OriginDemand.vue | 26 +- .../components/PersonsAssociated.vue | 36 +- .../PersonsAssociated/ParticipationItem.vue | 9 +- .../components/Referrer.vue | 26 +- .../components/Requestor.vue | 38 +- .../components/Resources.vue | 18 +- .../components/Resources/ResourceItem.vue | 9 +- .../AccompanyingCourse/components/Scopes.vue | 20 +- .../components/SocialIssue.vue | 28 +- .../public/vuejs/AccompanyingCourse/index.js | 11 +- .../vuejs/AccompanyingCourse/store/index.js | 390 +++++++++++------- .../ParticipationOverlap.php | 2 +- .../ParticipationOverlapValidator.php | 48 ++- .../ResourceDuplicateCheckValidator.php | 4 + .../translations/validators.fr.yml | 4 +- 29 files changed, 713 insertions(+), 470 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.js diff --git a/CHANGELOG.md b/CHANGELOG.md index c98d7db27..8256536da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,14 @@ and this project adheres to * [person suggest] In widget "add person", improve the pertinence of persons when one of the names starts with the pattern; * [person] do not ask for center any more on person creation * [3party] do not ask for center any more on 3party creation +<<<<<<< HEAD +<<<<<<< HEAD +======= +* [validation] toasts are displayed for errors when modifying accompanying course (generalization required). +>>>>>>> 16b59681 (changelog updated) +======= +* [validation] toasts are displayed for errors when modifying accompanying course (generalization required). +>>>>>>> b8889ed58d3abb68d1c2859b31bf7428addda36e ## Test releases diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.js b/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.js new file mode 100644 index 000000000..5b49ba546 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.js @@ -0,0 +1,106 @@ +/** + * Generic api method that can be adapted to any fetch request + */ + const makeFetch = (method, url, body) => { + return fetch(url, { + method: method, + headers: { + 'Content-Type': 'application/json;charset=utf-8' + }, + body: (body !== null) ? JSON.stringify(body) : null + }) + .then(response => { + + if (response.ok) { + return response.json(); + } + + if (response.status === 422) { + return response.json().then(response => { + throw ValidationException(response) + }); + } + + if (response.status === 403) { + return response.json().then(() => { + throw AccessException(); + }); + } + + throw { + name: 'Exception', + sta: response.status, + txt: response.statusText, + err: new Error(), + violations: response.body + }; + }); +} + +/** + * Fetch results with certain parameters + */ +const _fetchAction = (page, uri, params) => { + const item_per_page = 50; + if (params === undefined) { + params = {}; + } + let url = uri + '?' + new URLSearchParams({ item_per_page, page, ...params }); + + return fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json;charset=utf-8' + }, + }).then(response => { + if (response.ok) { return response.json(); } + throw Error({ m: response.statusText }); + }); +}; + +const fetchResults = async (uri, params) => { + let promises = [], + page = 1; + let firstData = await _fetchAction(page, uri, params); + + promises.push(Promise.resolve(firstData.results)); + + if (firstData.pagination.more) { + do { + page = ++page; + promises.push(_fetchAction(page, uri, params).then(r => Promise.resolve(r.results))); + } while (page * firstData.pagination.items_per_page < firstData.count) + } + + return Promise.all(promises).then(values => values.flat()); +}; + +const fetchScopes = () => { + return fetchResults('/api/1.0/main/scope.json'); +}; + + +/** + * Error objects to be thrown + */ +const ValidationException = (response) => { + const error = {}; + error.name = 'ValidationException'; + error.violations = response.violations.map((violation) => `${violation.title}`); + + return error; +} + +const AccessException = () => { + const error = {}; + error.name = 'AccessException'; + error.violations = ['You are no longer permitted to perform this action']; + + return error; +} + +export { + makeFetch, + fetchResults, + fetchScopes +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/download.js b/src/Bundle/ChillMainBundle/Resources/public/lib/api/download.js index 625103ebe..dec8ba727 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/download.js +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/download.js @@ -1,39 +1,39 @@ -const _fetchAction = (page, uri, params) => { - const item_per_page = 50; - if (params === undefined) { - params = {}; - } - let url = uri + '?' + new URLSearchParams({ item_per_page, page, ...params }); +// const _fetchAction = (page, uri, params) => { +// const item_per_page = 50; +// if (params === undefined) { +// params = {}; +// } +// let url = uri + '?' + new URLSearchParams({ item_per_page, page, ...params }); - return fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - }).then(response => { - if (response.ok) { return response.json(); } - throw Error({ m: response.statusText }); - }); -}; +// return fetch(url, { +// method: 'GET', +// headers: { +// 'Content-Type': 'application/json;charset=utf-8' +// }, +// }).then(response => { +// if (response.ok) { return response.json(); } +// throw Error({ m: response.statusText }); +// }); +// }; -const fetchResults = async (uri, params) => { - let promises = [], - page = 1; - let firstData = await _fetchAction(page, uri, params); +// const fetchResults = async (uri, params) => { +// let promises = [], +// page = 1; +// let firstData = await _fetchAction(page, uri, params); - promises.push(Promise.resolve(firstData.results)); +// promises.push(Promise.resolve(firstData.results)); - if (firstData.pagination.more) { - do { - page = ++page; - promises.push(_fetchAction(page, uri, params).then(r => Promise.resolve(r.results))); - } while (page * firstData.pagination.items_per_page < firstData.count) - } +// if (firstData.pagination.more) { +// do { +// page = ++page; +// promises.push(_fetchAction(page, uri, params).then(r => Promise.resolve(r.results))); +// } while (page * firstData.pagination.items_per_page < firstData.count) +// } - return Promise.all(promises).then(values => values.flat()); -}; +// return Promise.all(promises).then(values => values.flat()); +// }; -export { - fetchResults -}; +// export { +// fetchResults +// }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/scope.js b/src/Bundle/ChillMainBundle/Resources/public/lib/api/scope.js index 4c4223d77..a155c84a2 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/scope.js +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/scope.js @@ -1,9 +1,9 @@ -import { fetchResults } from 'ChillMainAssets/lib/api/download.js'; +// import { fetchResults } from 'ChillMainAssets/lib/api/download.js'; -const fetchScopes = () => { - return fetchResults('/api/1.0/main/scope.json'); -}; +// const fetchScopes = () => { +// return fetchResults('/api/1.0/main/scope.json'); +// }; -export { - fetchScopes -}; +// export { +// fetchScopes +// }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Confidential.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Confidential.vue index 5fe040e14..efdf62926 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Confidential.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Confidential.vue @@ -13,7 +13,6 @@ export default { methods : { toggleBlur: function(e){ if(e.target.matches('.toggle')){ - console.log(e); e.target.previousElementSibling.classList.toggle("blur"); e.target.classList.toggle("fa-eye"); e.target.classList.toggle("fa-eye-slash"); diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 958d7d58b..6b0c5d768 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -277,7 +277,7 @@ class AccompanyingPeriod implements * inverseJoinColumns={@ORM\JoinColumn(name="scope_id", referencedColumnName="id")} * ) * @Groups({"read"}) - * @Assert\Count(min=1, groups={AccompanyingPeriod::STEP_CONFIRMED}) + * @Assert\Count(min=1, groups={AccompanyingPeriod::STEP_CONFIRMED}, minMessage="A course must be associated to at least one scope") */ private $scopes; @@ -289,7 +289,7 @@ class AccompanyingPeriod implements * name="chill_person_accompanying_period_social_issues" * ) * @Groups({"read"}) - * @Assert\Count(min=1, groups={AccompanyingPeriod::STEP_CONFIRMED}) + * @Assert\Count(min=1, groups={AccompanyingPeriod::STEP_CONFIRMED}, minMessage="A course must contains at least one social issue") */ private Collection $socialIssues; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/lib/household.js b/src/Bundle/ChillPersonBundle/Resources/public/lib/household.js index 52754fa97..c96694dcb 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/lib/household.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/lib/household.js @@ -1,4 +1,4 @@ -import { fetchResults } from 'ChillMainAssets/lib/api/download.js'; +import { fetchResults } from 'ChillMainAssets/lib/api/apiMethods.js'; const fetchHouseholdByAddressReference = async (reference) => { const url = `/api/1.0/person/household/by-address-reference/${reference.id}.json` diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/App.vue index c559ae56e..30a8bbf2a 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/App.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/App.vue @@ -59,7 +59,7 @@ export default { 'accompanyingCourse', 'addressContext' ]), - }, + } }; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js index c372ac7a7..4eb6f2a33 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js @@ -1,4 +1,4 @@ -import { fetchResults } from 'ChillMainAssets/lib/api/download.js'; +import { fetchResults } from 'ChillMainAssets/lib/api/apiMethods.js'; /* * Endpoint v.2 chill_api_single_accompanying_course__entity @@ -15,44 +15,17 @@ const getAccompanyingCourse = (id) => { }); }; -/* -* Endpoint v.2 chill_api_single_accompanying_course__entity -* method PATCH, patch AccompanyingCourse Instance -* -* @id integer - id of accompanyingCourse -* @body Object - dictionary with changes to post -*/ -const patchAccompanyingCourse = (id, body) => { - //console.log('body', body); - const url = `/api/1.0/person/accompanying-course/${id}.json`; - return fetch(url, { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - body: JSON.stringify(body) - }) - .then(response => { - if (response.ok) { return response.json(); } - console.log(response); - throw { msg: 'Error while updating AccompanyingPeriod Course.', sta: response.status, txt: response.statusText, err: new Error(), body: response.body }; - }); +const getUsers = () => { + const url = `/api/1.0/main/user.json`; + + return fetchResults(url); }; -/* -* Endpoint to change 'DRAFT' step to 'CONFIRMED' -*/ -const confirmAccompanyingCourse = (id) => { - const url = `/api/1.0/person/accompanying-course/${id}/confirm.json` - return fetch(url, { - method: 'POST', - headers: {'Content-Type': 'application/json;charset=utf-8'} - }) - .then(response => { - if (response.ok) { return response.json(); } - throw { msg: 'Error while confirming AccompanyingPeriod Course.', sta: response.status, txt: response.statusText, err: new Error(), body: response.body }; - }); -}; +const getReferrersSuggested = (course) => { + const url = `/api/1.0/person/accompanying-course/${course.id}/referrers-suggested.json`; + + return fetchResults(url); +} /* * Endpoint @@ -66,115 +39,6 @@ const getSocialIssues = () => { }); }; -/* -* Endpoint v.2 chill_api_single_accompanying_course_participation, -* method POST/DELETE, add/close a participation to the accompanyingCourse -* -* @id integer - id of accompanyingCourse -* @payload integer - id of person -* @method string - POST or DELETE -*/ -const postParticipation = (id, payload, method) => { - const body = { type: payload.type, id: payload.id }; - const url = `/api/1.0/person/accompanying-course/${id}/participation.json`; - return fetch(url, { - method: method, - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - body: JSON.stringify(body) - }) - .then(response => { - if (response.ok) { return response.json(); } - // TODO: adjust message according to status code? Or how to access the message from the violation array? - throw { msg: 'Error while sending AccompanyingPeriod Course participation', sta: response.status, txt: response.statusText, err: new Error(), body: response.body }; - }); -}; - -/* -* Endpoint v.2 chill_api_single_accompanying_course_requestor, -* method POST/DELETE, add/close a requestor to the accompanyingCourse -* -* @id integer - id of accompanyingCourse -* @payload object of type person|thirdparty -* @method string - POST or DELETE -*/ -const postRequestor = (id, payload, method) => { - //console.log('payload', payload); - const body = (payload)? { type: payload.type, id: payload.id } : {}; - //console.log('body', body); - const url = `/api/1.0/person/accompanying-course/${id}/requestor.json`; - return fetch(url, { - method: method, - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - body: JSON.stringify(body) - }) - .then(response => { - if (response.ok) { return response.json(); } - throw { msg: 'Error while sending AccompanyingPeriod Course requestor', sta: response.status, txt: response.statusText, err: new Error(), body: response.body }; - }); -}; - -/* -* Endpoint v.2 chill_api_single_accompanying_course_resource, -* method POST/DELETE, add/remove a resource to the accompanyingCourse -* -* @id integer - id of accompanyingCourse -* @payload object of type person|thirdparty -* @method string - POST or DELETE -*/ -const postResource = (id, payload, method) => { - //console.log('payload', payload); - const body = { type: "accompanying_period_resource" }; - switch (method) { - case 'DELETE': - body['id'] = payload.id; - break; - default: - body['resource'] = { type: payload.type, id: payload.id }; - } - //console.log('body', body); - const url = `/api/1.0/person/accompanying-course/${id}/resource.json`; - return fetch(url, { - method: method, - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - body: JSON.stringify(body) - }) - .then(response => { - if (response.ok) { return response.json(); } - throw { msg: 'Error while sending AccompanyingPeriod Course resource.', sta: response.status, txt: response.statusText, err: new Error(), body: response.body }; - }); -}; - -/* -* Endpoint to Add/remove SocialIssue -*/ -const postSocialIssue = (id, body, method) => { - //console.log('api body and method', body, method); - const url = `/api/1.0/person/accompanying-course/${id}/socialissue.json`; - return fetch(url, { - method: method, - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - body: JSON.stringify(body) - }) - .then(response => { - if (response.ok) { return response.json(); } - throw { msg: 'Error while updating SocialIssue.', sta: response.status, txt: response.statusText, err: new Error(), body: response.body }; - }); -}; - -const getUsers = () => { - const url = `/api/1.0/main/user.json`; - - return fetchResults(url); -}; - const whoami = () => { const url = `/api/1.0/main/whoami.json`; return fetch(url) @@ -184,74 +48,10 @@ const whoami = () => { }); }; -const getListOrigins = () => { - const url = `/api/1.0/person/accompanying-period/origin.json`; - return fetch(url) - .then(response => { - if (response.ok) { return response.json(); } - throw { msg: 'Error while retriving origin\'s list.', sta: response.status, txt: response.statusText, err: new Error(), body: response.body }; - }); -}; - -const addScope = (id, scope) => { - const url = `/api/1.0/person/accompanying-course/${id}/scope.json`; - console.log(url); - console.log(scope); - - return fetch(url, { - method: 'POST', - body: JSON.stringify({ - id: scope.id, - type: scope.type, - }), - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - }) - .then(response => { - if (response.ok) { return response.json(); } - throw { msg: 'Error while adding scope', sta: response.status, txt: response.statusText, err: new Error(), body: response.body }; - }); -}; - -const removeScope = (id, scope) => { - const url = `/api/1.0/person/accompanying-course/${id}/scope.json`; - - return fetch(url, { - method: 'DELETE', - body: JSON.stringify({ - id: scope.id, - type: scope.type, - }), - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - }) - .then(response => { - if (response.ok) { return response.json(); } - throw { msg: 'Error while adding scope', sta: response.status, txt: response.statusText, err: new Error(), body: response.body }; - }); -}; - -const getReferrersSuggested = (course) => { - const url = `/api/1.0/person/accompanying-course/${course.id}/referrers-suggested.json`; - - return fetchResults(url); -} - export { - getAccompanyingCourse, - patchAccompanyingCourse, - confirmAccompanyingCourse, - getSocialIssues, - postParticipation, - postRequestor, - postResource, - getUsers, whoami, - getListOrigins, - postSocialIssue, - addScope, - removeScope, + getSocialIssues, + getAccompanyingCourse, + getUsers, getReferrersSuggested, }; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue index fca3cee20..459e5e758 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue @@ -53,13 +53,34 @@ export default { //temporaire (modif backend) value = "occasional"; } - this.$store.dispatch('toggleIntensity', value); + this.$store.dispatch('toggleIntensity', value) + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }); }, toggleEmergency() { - this.$store.dispatch('toggleEmergency', (!this.isEmergency)); + this.$store.dispatch('toggleEmergency', (!this.isEmergency)) + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }); }, toggleConfidential() { - this.$store.dispatch('toggleConfidential', (!this.isConfidential)); + this.$store.dispatch('toggleConfidential', (!this.isConfidential)) + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }); } } } diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/ButtonLocation.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/ButtonLocation.vue index b9e52f300..344119c44 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/ButtonLocation.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/ButtonLocation.vue @@ -59,7 +59,15 @@ export default { locationStatusTo: 'person', personId: this.person.id }; - this.$store.dispatch('updateLocation', payload); + this.$store.dispatch('updateLocation', payload) + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }); + window.location.assign('#section-20'); this.modal.showModal = false; } diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Comment.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Comment.vue index 1bff3f44d..7f5b04f99 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Comment.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Comment.vue @@ -83,12 +83,24 @@ export default { }, methods: { submitform() { - //console.log('submit'); - this.$store.dispatch('postFirstComment', this.formdata); + this.$store.dispatch('postFirstComment', this.formdata) + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }); }, removeComment() { - //console.log('remove'); - this.$store.dispatch('postFirstComment', {}); + this.$store.dispatch('postFirstComment', {}) + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }); } } } diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue index 4142b4f50..d3e0ae4d7 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue @@ -115,9 +115,14 @@ export default { }, methods: { confirmCourse() { - //console.log('@@ CLICK confirmCourse'); - this.$store.dispatch('confirmAccompanyingCourse'); - //console.log('confirm last'); + this.$store.dispatch('confirmAccompanyingCourse') + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }); } } } diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/CourseLocation.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/CourseLocation.vue index 79e983301..ae85af6a9 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/CourseLocation.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/CourseLocation.vue @@ -187,7 +187,14 @@ export default { locationStatusTo: 'none' }; //console.log('remove address'); - this.$store.dispatch('updateLocation', payload); + this.$store.dispatch('updateLocation', payload) + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }); }, displayErrors() { return this.$refs.addAddress.errorMsg; @@ -195,7 +202,15 @@ export default { submitTemporaryAddress(payload) { //console.log('@@@ click on Submit Temporary Address Button', payload); payload['locationStatusTo'] = 'address'; // <== temporary, not none, not person - this.$store.dispatch('updateLocation', payload); + this.$store.dispatch('updateLocation', payload) + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }); + this.$store.commit('setEditContextTrue', payload); } }, diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/OriginDemand.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/OriginDemand.vue index b88bf0747..ee5ad469a 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/OriginDemand.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/OriginDemand.vue @@ -29,7 +29,7 @@ diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/SocialIssue.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/SocialIssue.vue index 41fd85f0e..5046e641d 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/SocialIssue.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/SocialIssue.vue @@ -30,7 +30,7 @@ From b808e970aba82915bd04894bbc44202b76459145 Mon Sep 17 00:00:00 2001 From: nobohan Date: Tue, 23 Nov 2021 15:03:26 +0100 Subject: [PATCH 17/51] activity: correct activityController after rebase --- .../Controller/ActivityController.php | 84 ++----------------- 1 file changed, 8 insertions(+), 76 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 989ce518e..0c43c4d91 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -137,7 +137,7 @@ final class ActivityController extends AbstractController static fn (ActivityReason $ar): int => $ar->getId() ) ->toArray(), - 'type_id' => $activity->getType()->getId(), + 'type_id' => $activity->getActivityType()->getId(), 'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null, 'date' => $activity->getDate()->format('Y-m-d'), 'attendee' => $activity->getAttendee(), @@ -194,7 +194,7 @@ final class ActivityController extends AbstractController $form = $this->createForm(ActivityType::class, $entity, [ 'center' => $entity->getCenter(), 'role' => new Role('CHILL_ACTIVITY_UPDATE'), - 'activityType' => $entity->getType(), + 'activityType' => $entity->getActivityType(), 'accompanyingPeriod' => $accompanyingPeriod, ])->handleRequest($request); @@ -328,7 +328,7 @@ final class ActivityController extends AbstractController } $entity->setActivityType($activityType); - $entity->setDate(new \DateTime('now')); + $entity->setDate(new DateTime('now')); if ($request->query->has('activityData')) { $activityData = $request->query->get('activityData'); @@ -384,7 +384,7 @@ final class ActivityController extends AbstractController $form = $this->createForm(ActivityType::class, $entity, [ 'center' => $entity->getCenter(), - 'role' => new Role('CHILL_ACTIVITY_CREATE'), + 'role' => new Role('CHILL_ACTIVITY_CREATE'), 'activityType' => $entity->getActivityType(), 'accompanyingPeriod' => $accompanyingPeriod, ])->handleRequest($request); @@ -482,26 +482,10 @@ final class ActivityController extends AbstractController throw $this->createNotFoundException('Unable to find Activity entity.'); } - // TODO - // $this->denyAccessUnlessGranted('CHILL_ACTIVITY_UPDATE', $entity); - - $form = $this->createForm(ActivityType::class, $entity, [ - 'center' => $entity->getCenter(), - 'role' => new Role('CHILL_ACTIVITY_UPDATE'), - 'activityType' => $entity->getActivityType(), - 'accompanyingPeriod' => $accompanyingPeriod, - ])->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - $this->entityManager->persist($entity); - $this->entityManager->flush(); - - $this->addFlash('success', $this->get('translator')->trans('Success : activity updated!')); - - $params = $this->buildParamsToUrl($person, $accompanyingPeriod); - $params['id'] = $entity->getId(); - - return $this->redirectToRoute('chill_activity_activity_show', $params); + if (null !== $accompanyingPeriod) { + // @TODO: Properties created dynamically. + $entity->personsAssociated = $entity->getPersonsAssociated(); + $entity->personsNotAssociated = $entity->getPersonsNotAssociated(); } // TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période @@ -533,59 +517,7 @@ final class ActivityController extends AbstractController private function buildParamsToUrl(?Person $person, ?AccompanyingPeriod $accompanyingPeriod): array { -<<<<<<< HEAD $params = []; -======= - $view = null; - - [$person, $accompanyingPeriod] = $this->getEntity($request); - - if ($accompanyingPeriod instanceof AccompanyingPeriod) { - $view = 'ChillActivityBundle:Activity:confirm_deleteAccompanyingCourse.html.twig'; - } elseif ($person instanceof Person) { - $view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig'; - } - - $activity = $this->activityRepository->find($id); - - if (!$activity) { - throw $this->createNotFoundException('Unable to find Activity entity.'); - } - - // TODO - // $this->denyAccessUnlessGranted('CHILL_ACTIVITY_DELETE', $activity); - - $form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod); - - if ($request->getMethod() === Request::METHOD_DELETE) { - $form->handleRequest($request); - - if ($form->isValid()) { - $this->logger->notice("An activity has been removed", [ - 'by_user' => $this->getUser()->getUsername(), - 'activity_id' => $activity->getId(), - 'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null, - 'comment' => $activity->getComment()->getComment(), - 'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null, - 'reasons_ids' => $activity->getReasons() - ->map( - static fn (ActivityReason $ar): int => $ar->getId() - ) - ->toArray(), - 'type_id' => $activity->getActivityType()->getId(), - 'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null, - 'date' => $activity->getDate()->format('Y-m-d'), - 'attendee' => $activity->getAttendee() - ]); - - $this->entityManager->remove($activity); - $this->entityManager->flush(); - - $this->addFlash('success', $this->get('translator') - ->trans("The activity has been successfully removed.")); - - $params = $this->buildParamsToUrl($person, $accompanyingPeriod); ->>>>>>> correct deprecated activity method if (null !== $person) { $params['person_id'] = $person->getId(); From 1519fcd4dd0c73d692bb68e9f305d1916e1615e4 Mon Sep 17 00:00:00 2001 From: nobohan Date: Tue, 23 Nov 2021 16:10:34 +0100 Subject: [PATCH 18/51] activity: add location of the accompanying course --- .../Resources/public/vuejs/Activity/api.js | 12 +++++ .../vuejs/Activity/components/Location.vue | 45 +++++++++++-------- .../components/Location/NewLocation.vue | 1 - 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js index 9dab14ef1..96a8d0107 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js @@ -36,6 +36,17 @@ const getLocationTypes = () => { }); }; + +/* +* Load Location Types by defaultFor +* @param {string} entity - can be "person" or "thirdparty" + */ +const getLocationTypesByDefaultFor = (entity) => { + return getLocationTypes().then(response => + response.results.filter(t => t.defaultFor === entity)[0] + ); +}; + /* * Post a Location */ @@ -59,5 +70,6 @@ export { getSocialActionByIssue, getLocations, getLocationTypes, + getLocationTypesByDefaultFor, postLocation }; diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index 147184098..b64d7ccb2 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -30,7 +30,7 @@ import { mapState, mapGetters } from "vuex"; import VueMultiselect from "vue-multiselect"; import NewLocation from "./Location/NewLocation.vue"; -import { getLocations } from "../api.js"; +import { getLocations, getLocationTypesByDefaultFor } from "../api.js"; export default { name: "Location", @@ -56,22 +56,34 @@ export default { }, }, mounted() { + this.getAccompanyingPeriodLocation(); getLocations().then( (response) => new Promise((resolve) => { - console.log("getLocations", response); - this.locations = [ - ...this.getAccompanyingPeriodLocation(), - ...response.results, - ...this.createConcernedPersonsLocation(), - ]; - if (window.default_location_id) { - let location = this.locations.filter( - (l) => l.id === window.default_location_id - ); - this.$store.dispatch("updateLocation", location); - } - resolve(); + getLocationTypesByDefaultFor('person').then( + t => { + const accPeriodLocation = this.activity.accompanyingPeriod.location; + const personLocation = { + type: 'location', + name: 'XXXXXX Domicile de l\'usager du parcours', + address: { id: accPeriodLocation.address_id }, //TODO is the id sufficient? + locationType: t + } + this.locations = [ + personLocation, + ...response.results, + ]; + console.log(this.locations); + if (window.default_location_id) { + let location = this.locations.filter( + (l) => l.id === window.default_location_id + ); + this.$store.dispatch("updateLocation", location); + } + resolve(); + } + ) + }) ); }, @@ -81,13 +93,8 @@ export default { value.name ? value.name : "" }`; }, - getAccompanyingPeriodLocation() { - let location = this.activity.accompanyingPeriod.location; - return location; - }, createConcernedPersonsLocation() { let entities = this.suggestedEntities; - console.log(entities); return []; // TODO WIP }, }, diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue index 2920e15b2..18190564d 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue @@ -215,7 +215,6 @@ export default { }, getLocationTypesList() { getLocationTypes().then(response => new Promise(resolve => { - console.log('getLocationTypes', response); this.locationTypes = response.results.filter(t => t.availableForUsers === true); resolve(); })) From c1a2112d4828e7b93d215e721201d654ce900acd Mon Sep 17 00:00:00 2001 From: nobohan Date: Tue, 23 Nov 2021 17:24:54 +0100 Subject: [PATCH 19/51] activity: add location from concerned person and 3rd parties in the location selector --- .../Resources/public/vuejs/Activity/api.js | 6 +- .../vuejs/Activity/components/Location.vue | 94 +++++++++++++------ 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js index 96a8d0107..70ba4c04e 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js @@ -38,10 +38,10 @@ const getLocationTypes = () => { /* -* Load Location Types by defaultFor +* Load Location Type by defaultFor * @param {string} entity - can be "person" or "thirdparty" */ -const getLocationTypesByDefaultFor = (entity) => { +const getLocationTypeByDefaultFor = (entity) => { return getLocationTypes().then(response => response.results.filter(t => t.defaultFor === entity)[0] ); @@ -70,6 +70,6 @@ export { getSocialActionByIssue, getLocations, getLocationTypes, - getLocationTypesByDefaultFor, + getLocationTypeByDefaultFor, postLocation }; diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index b64d7ccb2..6cad3dd8d 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -30,7 +30,7 @@ import { mapState, mapGetters } from "vuex"; import VueMultiselect from "vue-multiselect"; import NewLocation from "./Location/NewLocation.vue"; -import { getLocations, getLocationTypesByDefaultFor } from "../api.js"; +import { getLocations, getLocationTypeByDefaultFor } from "../api.js"; export default { name: "Location", @@ -56,34 +56,36 @@ export default { }, }, mounted() { - this.getAccompanyingPeriodLocation(); getLocations().then( (response) => new Promise((resolve) => { - getLocationTypesByDefaultFor('person').then( - t => { - const accPeriodLocation = this.activity.accompanyingPeriod.location; - const personLocation = { - type: 'location', - name: 'XXXXXX Domicile de l\'usager du parcours', - address: { id: accPeriodLocation.address_id }, //TODO is the id sufficient? - locationType: t - } - this.locations = [ - personLocation, - ...response.results, - ]; - console.log(this.locations); - if (window.default_location_id) { - let location = this.locations.filter( - (l) => l.id === window.default_location_id - ); - this.$store.dispatch("updateLocation", location); - } - resolve(); + getLocationTypeByDefaultFor('person').then( + personLocationType => { + const personLocation = this.makePersonLocation(personLocationType); + const concernedPersonsLocation = + this.makeConcernedPersonsLocation(personLocationType); + getLocationTypeByDefaultFor('thirdparty').then( + thirdpartyLocationType => { + const concernedThirdPartiesLocation = + this.makeConcernedThirdPartiesLocation(thirdpartyLocationType); + this.locations = [ + personLocation, + ...concernedPersonsLocation, + ...concernedThirdPartiesLocation, + ...response.results, + ]; + console.log(this.locations); + if (window.default_location_id) { + let location = this.locations.filter( + (l) => l.id === window.default_location_id + ); + this.$store.dispatch("updateLocation", location); + } + resolve(); + } + ) } ) - }) ); }, @@ -93,10 +95,48 @@ export default { value.name ? value.name : "" }`; }, - createConcernedPersonsLocation() { - let entities = this.suggestedEntities; - return []; // TODO WIP + makeConcernedPersonsLocation(locationType) { + let locations = []; + this.suggestedEntities.forEach( + (e) => { + if (e.type === 'person' && e.current_household_address !== null){ + locations.push({ + type: 'location', + name: e.text, + address: { id: e.current_household_address.id }, + locationType: locationType + }); + } + } + ) + return locations; }, + makeConcernedThirdPartiesLocation(locationType) { + let locations = []; + this.suggestedEntities.forEach( + (e) => { + if (e.type === 'thirdparty' && e.address !== null){ + locations.push({ + type: 'location', + name: e.text, + address: { id: e.address.id }, + locationType: locationType + }); + } + } + ) + return locations; + }, + makePersonLocation(locationType) { + console.log(this.activity) + const accPeriodLocation = this.activity.accompanyingPeriod.location; + return { + type: 'location', + name: 'Adresse du parcours', //TODO trans + address: { id: accPeriodLocation.address_id }, //TODO is the id sufficient? + locationType: locationType + } + } }, }; From 1b579f7930e839dd95007385f57783ca92d5d429 Mon Sep 17 00:00:00 2001 From: nobohan Date: Tue, 23 Nov 2021 17:32:11 +0100 Subject: [PATCH 20/51] activity: label location --- .../public/vuejs/Activity/components/Location.vue | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index 6cad3dd8d..ccce99075 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -61,7 +61,7 @@ export default { new Promise((resolve) => { getLocationTypeByDefaultFor('person').then( personLocationType => { - const personLocation = this.makePersonLocation(personLocationType); + const personLocation = this.makeAccompanyingPeriodLocation(personLocationType); const concernedPersonsLocation = this.makeConcernedPersonsLocation(personLocationType); getLocationTypeByDefaultFor('thirdparty').then( @@ -91,9 +91,11 @@ export default { }, methods: { customLabel(value) { - return `${value.locationType.title.fr} ${ - value.name ? value.name : "" - }`; + return value.name ? + value.name === '__AccompanyingCourseLocation__' ? + 'Localisation du parcours' : + `${value.name} (${value.locationType.title.fr})` : + value.locationType.title.fr; }, makeConcernedPersonsLocation(locationType) { let locations = []; @@ -127,12 +129,12 @@ export default { ) return locations; }, - makePersonLocation(locationType) { + makeAccompanyingPeriodLocation(locationType) { console.log(this.activity) const accPeriodLocation = this.activity.accompanyingPeriod.location; return { type: 'location', - name: 'Adresse du parcours', //TODO trans + name: '__AccompanyingCourseLocation__', //TODO not perfect... address: { id: accPeriodLocation.address_id }, //TODO is the id sufficient? locationType: locationType } From a2f6f2b5cb4b4d1fcf067c6a7a94384e12bf2dfd Mon Sep 17 00:00:00 2001 From: nobohan Date: Tue, 23 Nov 2021 17:50:32 +0100 Subject: [PATCH 21/51] activity: post new location on-the-fly --- .../public/vuejs/Activity/components/Location.vue | 5 ++++- .../Activity/components/Location/NewLocation.vue | 1 - .../Resources/public/vuejs/Activity/store.js | 15 ++++++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index ccce99075..e9f4e39e6 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -104,6 +104,7 @@ export default { if (e.type === 'person' && e.current_household_address !== null){ locations.push({ type: 'location', + onthefly: true, name: e.text, address: { id: e.current_household_address.id }, locationType: locationType @@ -120,6 +121,7 @@ export default { if (e.type === 'thirdparty' && e.address !== null){ locations.push({ type: 'location', + onthefly: true, name: e.text, address: { id: e.address.id }, locationType: locationType @@ -134,7 +136,8 @@ export default { const accPeriodLocation = this.activity.accompanyingPeriod.location; return { type: 'location', - name: '__AccompanyingCourseLocation__', //TODO not perfect... + onthefly: true, + name: '__AccompanyingCourseLocation__', //TODO not perfect... shoould show the address address: { id: accPeriodLocation.address_id }, //TODO is the id sufficient? locationType: locationType } diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue index 18190564d..34aa607e9 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue @@ -246,7 +246,6 @@ export default { postLocation(body) .then( location => new Promise(resolve => { - console.log('postLocation', location); this.locations.push(location); this.$store.dispatch('updateLocation', location); resolve(); diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js index ef9cf3b1f..182a3b01a 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js @@ -1,5 +1,6 @@ import 'es6-promise/auto'; import { createStore } from 'vuex'; +import { postLocation } from './api'; const debug = process.env.NODE_ENV !== 'production'; //console.log('window.activity', window.activity); @@ -303,7 +304,19 @@ const store = createStore({ let hiddenLocation = document.getElementById( "chill_activitybundle_activity_location" ); - hiddenLocation.value = value.id; + //TODO post the location if new location on-the-fly + if (value.onthefly) { + postLocation(value) + .then( + location => hiddenLocation.value = location.id + ).catch( + err => { + this.errors.push(err.message); + } + ); + } else { + hiddenLocation.value = value.id; + } commit("updateLocation", value); }, }, From c7f27176763274e5a566d284cd6f84a21e211c3a Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 24 Nov 2021 11:45:29 +0100 Subject: [PATCH 22/51] activity: new activity: show address in parcours location --- .../vuejs/Activity/components/Location.vue | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index e9f4e39e6..3a7841cd9 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -90,15 +90,19 @@ export default { ); }, methods: { + labelAccompanyingCourseLocation(value) { + return `${value.address.text} (Localisation du parcours)` + }, customLabel(value) { return value.name ? value.name === '__AccompanyingCourseLocation__' ? - 'Localisation du parcours' : + this.labelAccompanyingCourseLocation(value) : `${value.name} (${value.locationType.title.fr})` : value.locationType.title.fr; }, makeConcernedPersonsLocation(locationType) { let locations = []; + console.log(this.suggestedEntities) this.suggestedEntities.forEach( (e) => { if (e.type === 'person' && e.current_household_address !== null){ @@ -106,7 +110,9 @@ export default { type: 'location', onthefly: true, name: e.text, - address: { id: e.current_household_address.id }, + address: { + id: e.current_household_address.id, + }, locationType: locationType }); } @@ -137,8 +143,11 @@ export default { return { type: 'location', onthefly: true, - name: '__AccompanyingCourseLocation__', //TODO not perfect... shoould show the address - address: { id: accPeriodLocation.address_id }, //TODO is the id sufficient? + name: '__AccompanyingCourseLocation__', + address: { + id: accPeriodLocation.address_id, + text: `${accPeriodLocation.text} - ${accPeriodLocation.postcode.code} ${accPeriodLocation.postcode.name}` + }, //TODO is the id sufficient? locationType: locationType } } From d27f085ac9d9f341753f41b632adcdad94bc941e Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 24 Nov 2021 14:07:02 +0100 Subject: [PATCH 23/51] location: change display in twig --- .../ChillActivityBundle/Resources/views/Activity/show.html.twig | 2 +- .../ChillCalendarBundle/Resources/views/Calendar/show.html.twig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig index a30034257..e9782b8e1 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig @@ -67,8 +67,8 @@
{% if entity.location is not null %}

- {{ entity.location.locationType.title|localize_translatable_string }} {{ entity.location.name }} + ({{ entity.location.locationType.title|localize_translatable_string }})

{{ entity.location.address|chill_entity_render_box }} {% else %} diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/show.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/show.html.twig index 5dfde6fc3..7c59996d2 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/show.html.twig +++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/show.html.twig @@ -41,8 +41,8 @@
{% if entity.location is not null %}

- {{ entity.location.locationType.title|localize_translatable_string }} {{ entity.location.name }} + ({{ entity.location.locationType.title|localize_translatable_string }})

{{ entity.location.address|chill_entity_render_box }} {% else %} From 0bd0487801c11ed446c15de90c3caaabc7fbf15e Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 24 Nov 2021 14:11:25 +0100 Subject: [PATCH 24/51] activity: fix the on-the-fly POSTing of the location entity --- .../vuejs/Activity/components/Location.vue | 9 +++----- .../Resources/public/vuejs/Activity/store.js | 21 +++++++++++++++---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index 3a7841cd9..69913323c 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -74,7 +74,6 @@ export default { ...concernedThirdPartiesLocation, ...response.results, ]; - console.log(this.locations); if (window.default_location_id) { let location = this.locations.filter( (l) => l.id === window.default_location_id @@ -102,7 +101,6 @@ export default { }, makeConcernedPersonsLocation(locationType) { let locations = []; - console.log(this.suggestedEntities) this.suggestedEntities.forEach( (e) => { if (e.type === 'person' && e.current_household_address !== null){ @@ -111,7 +109,7 @@ export default { onthefly: true, name: e.text, address: { - id: e.current_household_address.id, + id: e.current_household_address.address_id, }, locationType: locationType }); @@ -129,7 +127,7 @@ export default { type: 'location', onthefly: true, name: e.text, - address: { id: e.address.id }, + address: { id: e.address.address_id }, locationType: locationType }); } @@ -138,7 +136,6 @@ export default { return locations; }, makeAccompanyingPeriodLocation(locationType) { - console.log(this.activity) const accPeriodLocation = this.activity.accompanyingPeriod.location; return { type: 'location', @@ -147,7 +144,7 @@ export default { address: { id: accPeriodLocation.address_id, text: `${accPeriodLocation.text} - ${accPeriodLocation.postcode.code} ${accPeriodLocation.postcode.name}` - }, //TODO is the id sufficient? + }, locationType: locationType } } diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js index 182a3b01a..5b6297553 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js @@ -28,7 +28,6 @@ const store = createStore({ }, getters: { suggestedEntities(state) { - console.log(state.activity); if (typeof state.activity.accompanyingPeriod === "undefined") { return []; } @@ -304,14 +303,28 @@ const store = createStore({ let hiddenLocation = document.getElementById( "chill_activitybundle_activity_location" ); - //TODO post the location if new location on-the-fly if (value.onthefly) { - postLocation(value) + const body = { + "type": "location", + "name": value.name === '__AccompanyingCourseLocation__' ? null : value.name, + "locationType": { + "id": value.locationType.id, + "type": "location-type" + } + }; + if (value.address.id) { + Object.assign(body, { + "address": { + "id": value.address.id + }, + }) + } + postLocation(body) .then( location => hiddenLocation.value = location.id ).catch( err => { - this.errors.push(err.message); + console.log(err.message); } ); } else { From d13d42b9bf3db06b8d071b3cd5697efce76afcad Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 24 Nov 2021 14:43:07 +0100 Subject: [PATCH 25/51] activity: group locations by type in the selector --- .../vuejs/Activity/components/Location.vue | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index 69913323c..dafd3872a 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -16,6 +16,8 @@ :placeholder="$t('activity.choose_location')" :custom-label="customLabel" :options="locations" + group-values="locations" + group-label="locationGroup" v-model="location" > @@ -69,10 +71,17 @@ export default { const concernedThirdPartiesLocation = this.makeConcernedThirdPartiesLocation(thirdpartyLocationType); this.locations = [ - personLocation, - ...concernedPersonsLocation, - ...concernedThirdPartiesLocation, - ...response.results, + { + locationGroup: 'Localisation du parcours', + locations: [personLocation] + }, + { + locationGroup: 'Parties concernées', + locations: [...concernedPersonsLocation, ...concernedThirdPartiesLocation]}, + { + locationGroup: 'Autres localisations', + locations: response.results + } ]; if (window.default_location_id) { let location = this.locations.filter( @@ -90,7 +99,7 @@ export default { }, methods: { labelAccompanyingCourseLocation(value) { - return `${value.address.text} (Localisation du parcours)` + return `${value.address.text}` }, customLabel(value) { return value.name ? @@ -106,6 +115,7 @@ export default { if (e.type === 'person' && e.current_household_address !== null){ locations.push({ type: 'location', + id: -this.suggestedEntities.indexOf(e)*10, onthefly: true, name: e.text, address: { @@ -122,9 +132,11 @@ export default { let locations = []; this.suggestedEntities.forEach( (e) => { + console.log(this.suggestedEntities.indexOf(e)) if (e.type === 'thirdparty' && e.address !== null){ locations.push({ type: 'location', + id: -this.suggestedEntities.indexOf(e)*10, onthefly: true, name: e.text, address: { id: e.address.address_id }, @@ -139,6 +151,7 @@ export default { const accPeriodLocation = this.activity.accompanyingPeriod.location; return { type: 'location', + id: -1, onthefly: true, name: '__AccompanyingCourseLocation__', address: { From 220f2973911fa085ce3dd4cf8c5cd771641856db Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 24 Nov 2021 15:11:46 +0100 Subject: [PATCH 26/51] calendar: cast collection to array --- .../ChillCalendarBundle/Controller/CalendarController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php b/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php index 0165142f7..6e6bce842 100644 --- a/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php +++ b/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php @@ -332,12 +332,12 @@ class CalendarController extends AbstractController $personsId = array_map( static fn (Person $p): int => $p->getId(), - $entity->getPersons() + $entity->getPersons()->toArray() ); $professionalsId = array_map( static fn (ThirdParty $thirdParty): ?int => $thirdParty->getId(), - $entity->getProfessionals() + $entity->getProfessionals()->toArray() ); $durationTime = $entity->getEndDate()->diff($entity->getStartDate()); From 2ee4a6b2ac54d24ca2774c05724cb7f4511f76f0 Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 24 Nov 2021 15:12:21 +0100 Subject: [PATCH 27/51] calendar: dynamic way of creating location when creating a calendar --- .../Resources/public/vuejs/Calendar/store.js | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store.js b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store.js index 457d31799..edfb7f236 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store.js +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store.js @@ -1,5 +1,6 @@ import 'es6-promise/auto'; import { createStore } from 'vuex'; +import { postLocation } from 'ChillActivityAssets/vuejs/Activity/api'; const debug = process.env.NODE_ENV !== 'production'; @@ -33,7 +34,6 @@ const store = createStore({ }, getters: { suggestedEntities(state) { - console.log(state.activity) if (typeof(state.activity.accompanyingPeriod) === 'undefined') { return []; } @@ -189,8 +189,35 @@ const store = createStore({ updateLocation({ commit }, value) { console.log('### action: updateLocation', value); let hiddenLocation = document.getElementById("chill_calendarbundle_calendar_location"); - hiddenLocation.value = value.id; - commit('updateLocation', value); + if (value.onthefly) { + const body = { + "type": "location", + "name": value.name === '__AccompanyingCourseLocation__' ? null : value.name, + "locationType": { + "id": value.locationType.id, + "type": "location-type" + } + }; + if (value.address.id) { + Object.assign(body, { + "address": { + "id": value.address.id + }, + }) + } + postLocation(body) + .then( + location => hiddenLocation.value = location.id + ).catch( + err => { + console.log(err.message); + } + ); + } else { + hiddenLocation.value = value.id; + } + commit("updateLocation", value); + } } From e73179688ac208b34bf27768848a97e4b2e9939d Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 24 Nov 2021 15:25:36 +0100 Subject: [PATCH 28/51] activity: format location label for parcours location --- .../Resources/public/vuejs/Activity/components/Location.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index dafd3872a..25c75ed8b 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -99,7 +99,7 @@ export default { }, methods: { labelAccompanyingCourseLocation(value) { - return `${value.address.text}` + return `${value.address.text} (${value.locationType.title.fr})` }, customLabel(value) { return value.name ? @@ -132,7 +132,6 @@ export default { let locations = []; this.suggestedEntities.forEach( (e) => { - console.log(this.suggestedEntities.indexOf(e)) if (e.type === 'thirdparty' && e.address !== null){ locations.push({ type: 'location', From 3d5db29a4c5918f4cc2e5809c65a184a49ba7aae Mon Sep 17 00:00:00 2001 From: nobohan Date: Thu, 25 Nov 2021 10:18:40 +0100 Subject: [PATCH 29/51] location: filter location api point only by active and availableForUsers --- .../Controller/LocationApiController.php | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/LocationApiController.php b/src/Bundle/ChillMainBundle/Controller/LocationApiController.php index 52b6f1e81..3e4911788 100644 --- a/src/Bundle/ChillMainBundle/Controller/LocationApiController.php +++ b/src/Bundle/ChillMainBundle/Controller/LocationApiController.php @@ -21,22 +21,11 @@ class LocationApiController extends ApiController { public function customizeQuery(string $action, Request $request, $query): void { - $query->andWhere($query->expr()->orX( - $query->expr()->andX( - $query->expr()->eq('e.createdBy', ':user'), - $query->expr()->gte('e.createdAt', ':dateBefore') - ), + $query->andWhere( $query->expr()->andX( $query->expr()->eq('e.availableForUsers', "'TRUE'"), $query->expr()->eq('e.active', "'TRUE'"), - $query->expr()->isNotNull('e.name'), - $query->expr()->neq('e.name', ':emptyString'), ) - )) - ->setParameters([ - 'user' => $this->getUser(), - 'dateBefore' => (new DateTime())->sub(new DateInterval('P6M')), - 'emptyString' => '', - ]); + ); } } From 09a679ffb9574ddb05660b2ba94664f2c9fcc21a Mon Sep 17 00:00:00 2001 From: nobohan Date: Thu, 25 Nov 2021 10:34:47 +0100 Subject: [PATCH 30/51] upd CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index caf9a9249..eb3036107 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ and this project adheres to * [list for accompanying course in person] filter list using ACL * [validation] toasts are displayed for errors when modifying accompanying course (generalization required). * add an endpoint for checking permissions. See https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/232 +* [activity] for a new activity: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties +* [calendar] for a new rdv: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties ## Test releases From 3f35f0e2517d1a773ab24631ea3cae33296e25fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 25 Nov 2021 14:08:12 +0100 Subject: [PATCH 31/51] add unique constraint on property `validFor` for LocationType --- src/Bundle/ChillMainBundle/Entity/LocationType.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Bundle/ChillMainBundle/Entity/LocationType.php b/src/Bundle/ChillMainBundle/Entity/LocationType.php index d3dff58b7..9385620cd 100644 --- a/src/Bundle/ChillMainBundle/Entity/LocationType.php +++ b/src/Bundle/ChillMainBundle/Entity/LocationType.php @@ -11,6 +11,7 @@ namespace Chill\MainBundle\Entity; use Chill\MainBundle\Repository\LocationTypeRepository; use Doctrine\ORM\Mapping as ORM; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation as Serializer; use Symfony\Component\Serializer\Annotation\DiscriminatorMap; @@ -20,6 +21,7 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap; * @DiscriminatorMap(typeProperty="type", mapping={ * "location-type": LocationType::class * }) + * @UniqueEntity({"defaultFor"}) */ class LocationType { From 36c57511e874f9f49cb7a6491f87ff5c0404aa18 Mon Sep 17 00:00:00 2001 From: nobohan Date: Thu, 25 Nov 2021 16:10:08 +0100 Subject: [PATCH 32/51] location: fix code style with phpstan --- .../Controller/LocationApiController.php | 2 - .../ChillMainBundle/Entity/LocationType.php | 44 +++++++++---------- .../ChillMainBundle/Form/LocationTypeType.php | 19 ++++---- .../migrations/Version20211123093355.php | 23 ++++++---- 4 files changed, 47 insertions(+), 41 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/LocationApiController.php b/src/Bundle/ChillMainBundle/Controller/LocationApiController.php index 3e4911788..16fc414d9 100644 --- a/src/Bundle/ChillMainBundle/Controller/LocationApiController.php +++ b/src/Bundle/ChillMainBundle/Controller/LocationApiController.php @@ -10,8 +10,6 @@ namespace Chill\MainBundle\Controller; use Chill\MainBundle\CRUD\Controller\ApiController; -use DateInterval; -use DateTime; use Symfony\Component\HttpFoundation\Request; /** diff --git a/src/Bundle/ChillMainBundle/Entity/LocationType.php b/src/Bundle/ChillMainBundle/Entity/LocationType.php index 9385620cd..a9ff33414 100644 --- a/src/Bundle/ChillMainBundle/Entity/LocationType.php +++ b/src/Bundle/ChillMainBundle/Entity/LocationType.php @@ -25,15 +25,16 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap; */ class LocationType { + public const DEFAULT_FOR_3PARTY = 'thirdparty'; + + public const DEFAULT_FOR_PERSON = 'person'; + public const STATUS_NEVER = 'never'; public const STATUS_OPTIONAL = 'optional'; public const STATUS_REQUIRED = 'required'; - public const DEFAULT_FOR_PERSON = 'person'; - public const DEFAULT_FOR_3PARTY = 'thirdparty'; - /** * @ORM\Column(type="boolean", nullable=true) * @Serializer\Groups({"read"}) @@ -58,6 +59,12 @@ class LocationType */ private string $contactData = self::STATUS_OPTIONAL; + /** + * @ORM\Column(type="string", nullable=true, length=32, unique=true) + * @Serializer\Groups({"read"}) + */ + private ?string $defaultFor = null; + /** * @ORM\Id * @ORM\GeneratedValue @@ -72,13 +79,6 @@ class LocationType */ private array $title = []; - /** - * @ORM\Column(type="string", nullable=true, length=32, unique=true) - * @Serializer\Groups({"read"}) - */ - private ?string $defaultFor = null; - - public function getActive(): ?bool { return $this->active; @@ -99,6 +99,11 @@ class LocationType return $this->contactData; } + public function getDefaultFor(): ?string + { + return $this->defaultFor; + } + public function getId(): ?int { return $this->id; @@ -137,22 +142,17 @@ class LocationType return $this; } - public function setTitle(array $title): self - { - $this->title = $title; - - return $this; - } - - public function getDefaultFor(): ?string - { - return $this->defaultFor; - } - public function setDefaultFor(string $defaultFor): self { $this->defaultFor = $defaultFor; return $this; } + + public function setTitle(array $title): self + { + $this->title = $title; + + return $this; + } } diff --git a/src/Bundle/ChillMainBundle/Form/LocationTypeType.php b/src/Bundle/ChillMainBundle/Form/LocationTypeType.php index 93bc5ba0f..41372b48e 100644 --- a/src/Bundle/ChillMainBundle/Form/LocationTypeType.php +++ b/src/Bundle/ChillMainBundle/Form/LocationTypeType.php @@ -73,15 +73,16 @@ final class LocationTypeType extends AbstractType ] ) ->add( - 'defaultFor', + 'defaultFor', ChoiceType::class, - [ - 'choices' => [ - 'none' => null, - 'person' => LocationType::DEFAULT_FOR_PERSON, - 'thirdparty' => LocationType::DEFAULT_FOR_3PARTY, - ], - 'expanded' => true - ]); + [ + 'choices' => [ + 'none' => null, + 'person' => LocationType::DEFAULT_FOR_PERSON, + 'thirdparty' => LocationType::DEFAULT_FOR_3PARTY, + ], + 'expanded' => true, + ] + ); } } diff --git a/src/Bundle/ChillMainBundle/migrations/Version20211123093355.php b/src/Bundle/ChillMainBundle/migrations/Version20211123093355.php index 1736cf857..b6618d25e 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20211123093355.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20211123093355.php @@ -1,5 +1,12 @@ addSql('DROP INDEX UNIQ_A459B5CADD3E4105'); + $this->addSql('ALTER TABLE chill_main_location_type DROP defaultFor'); + } + public function getDescription(): string { return 'Add defaultFor to LocationType'; @@ -22,10 +35,4 @@ final class Version20211123093355 extends AbstractMigration $this->addSql('ALTER TABLE chill_main_location_type ADD defaultFor VARCHAR(32) DEFAULT NULL'); $this->addSql('CREATE UNIQUE INDEX UNIQ_A459B5CADD3E4105 ON chill_main_location_type (defaultFor)'); } - - public function down(Schema $schema): void - { - $this->addSql('DROP INDEX UNIQ_A459B5CADD3E4105'); - $this->addSql('ALTER TABLE chill_main_location_type DROP defaultFor'); - } -} \ No newline at end of file +} From aad4b6e5d03022f18bee837f765bd93423d8e33a Mon Sep 17 00:00:00 2001 From: nobohan Date: Thu, 25 Nov 2021 16:18:45 +0100 Subject: [PATCH 33/51] location: allow null value for defaultFor --- src/Bundle/ChillMainBundle/Entity/LocationType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Entity/LocationType.php b/src/Bundle/ChillMainBundle/Entity/LocationType.php index a9ff33414..b0d91a4f1 100644 --- a/src/Bundle/ChillMainBundle/Entity/LocationType.php +++ b/src/Bundle/ChillMainBundle/Entity/LocationType.php @@ -142,7 +142,7 @@ class LocationType return $this; } - public function setDefaultFor(string $defaultFor): self + public function setDefaultFor(?string $defaultFor): self { $this->defaultFor = $defaultFor; From f002d48efdd0074274c56a6a6704df734737adaa Mon Sep 17 00:00:00 2001 From: nobohan Date: Thu, 25 Nov 2021 16:53:02 +0100 Subject: [PATCH 34/51] location: fix error when there is no defaultFor defined for locationType entities --- .../vuejs/Activity/components/Location.vue | 78 +++++++++++-------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index 25c75ed8b..012c550aa 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -63,35 +63,45 @@ export default { new Promise((resolve) => { getLocationTypeByDefaultFor('person').then( personLocationType => { - const personLocation = this.makeAccompanyingPeriodLocation(personLocationType); - const concernedPersonsLocation = - this.makeConcernedPersonsLocation(personLocationType); - getLocationTypeByDefaultFor('thirdparty').then( - thirdpartyLocationType => { - const concernedThirdPartiesLocation = - this.makeConcernedThirdPartiesLocation(thirdpartyLocationType); - this.locations = [ - { - locationGroup: 'Localisation du parcours', - locations: [personLocation] - }, - { - locationGroup: 'Parties concernées', - locations: [...concernedPersonsLocation, ...concernedThirdPartiesLocation]}, - { - locationGroup: 'Autres localisations', - locations: response.results - } - ]; - if (window.default_location_id) { - let location = this.locations.filter( - (l) => l.id === window.default_location_id - ); - this.$store.dispatch("updateLocation", location); + if (personLocationType) { + const personLocation = this.makeAccompanyingPeriodLocation(personLocationType); + const concernedPersonsLocation = + this.makeConcernedPersonsLocation(personLocationType); + getLocationTypeByDefaultFor('thirdparty').then( + thirdpartyLocationType => { + const concernedThirdPartiesLocation = + this.makeConcernedThirdPartiesLocation(thirdpartyLocationType); + this.locations = [ + { + locationGroup: 'Localisation du parcours', + locations: [personLocation] + }, + { + locationGroup: 'Parties concernées', + locations: [...concernedPersonsLocation, ...concernedThirdPartiesLocation] + }, + { + locationGroup: 'Autres localisations', + locations: response.results + } + ]; } - resolve(); - } - ) + ) + } else { + this.locations = [ + { + locationGroup: 'Localisations', + locations: response.results + } + ]; + } + if (window.default_location_id) { + let location = this.locations.filter( + (l) => l.id === window.default_location_id + ); + this.$store.dispatch("updateLocation", location); + } + resolve(); } ) }) @@ -102,11 +112,13 @@ export default { return `${value.address.text} (${value.locationType.title.fr})` }, customLabel(value) { - return value.name ? - value.name === '__AccompanyingCourseLocation__' ? - this.labelAccompanyingCourseLocation(value) : - `${value.name} (${value.locationType.title.fr})` : - value.locationType.title.fr; + return value.locationType + ? value.name + ? value.name === '__AccompanyingCourseLocation__' + ? this.labelAccompanyingCourseLocation(value) + : `${value.name} (${value.locationType.title.fr})` + : value.locationType.title.fr + : ''; }, makeConcernedPersonsLocation(locationType) { let locations = []; From 5aa5cb3e08c9bfd7e54d9bb9b75f8c3f764db94d Mon Sep 17 00:00:00 2001 From: nobohan Date: Thu, 25 Nov 2021 17:22:25 +0100 Subject: [PATCH 35/51] location: use fetchResults for getLocations and getLocationTypes --- .../Resources/public/vuejs/Activity/api.js | 33 ++------ .../vuejs/Activity/components/Location.vue | 79 +++++++++---------- .../components/Location/NewLocation.vue | 7 +- 3 files changed, 46 insertions(+), 73 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js index 70ba4c04e..8d4bcac3b 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/api.js @@ -1,4 +1,5 @@ import { getSocialIssues } from 'ChillPersonAssets/vuejs/AccompanyingCourse/api.js'; +import { fetchResults } from 'ChillMainAssets/lib/api/apiMethods'; /* * Load socialActions by socialIssue (id) @@ -12,44 +13,20 @@ const getSocialActionByIssue = (id) => { }); }; -/* -* Load Locations - */ -const getLocations = () => { - const url = `/api/1.0/main/location.json`; - return fetch(url) - .then(response => { - if (response.ok) { return response.json(); } - throw Error('Error with request resource response'); - }); -}; - -/* -* Load Location Types - */ -const getLocationTypes = () => { - const url = `/api/1.0/main/location-type.json`; - return fetch(url) - .then(response => { - if (response.ok) { return response.json(); } - throw Error('Error with request resource response'); - }); -}; +const getLocations = () => fetchResults('/api/1.0/main/location.json'); +const getLocationTypes = () => fetchResults('/api/1.0/main/location-type.json'); /* * Load Location Type by defaultFor * @param {string} entity - can be "person" or "thirdparty" */ const getLocationTypeByDefaultFor = (entity) => { - return getLocationTypes().then(response => - response.results.filter(t => t.defaultFor === entity)[0] + return getLocationTypes().then(results => + results.filter(t => t.defaultFor === entity)[0] ); }; -/* -* Post a Location - */ const postLocation = (body) => { const url = `/api/1.0/main/location.json`; return fetch(url, { diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index 012c550aa..9a4b78334 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -59,53 +59,50 @@ export default { }, mounted() { getLocations().then( - (response) => - new Promise((resolve) => { - getLocationTypeByDefaultFor('person').then( - personLocationType => { - if (personLocationType) { - const personLocation = this.makeAccompanyingPeriodLocation(personLocationType); - const concernedPersonsLocation = - this.makeConcernedPersonsLocation(personLocationType); - getLocationTypeByDefaultFor('thirdparty').then( - thirdpartyLocationType => { - const concernedThirdPartiesLocation = - this.makeConcernedThirdPartiesLocation(thirdpartyLocationType); - this.locations = [ - { - locationGroup: 'Localisation du parcours', - locations: [personLocation] - }, - { - locationGroup: 'Parties concernées', - locations: [...concernedPersonsLocation, ...concernedThirdPartiesLocation] - }, - { - locationGroup: 'Autres localisations', - locations: response.results - } - ]; - } - ) - } else { - this.locations = [ - { - locationGroup: 'Localisations', - locations: response.results - } - ]; - } + (results) => { + getLocationTypeByDefaultFor('person').then( + (personLocationType) => { + if (personLocationType) { + const personLocation = this.makeAccompanyingPeriodLocation(personLocationType); + const concernedPersonsLocation = + this.makeConcernedPersonsLocation(personLocationType); + getLocationTypeByDefaultFor('thirdparty').then( + thirdpartyLocationType => { + const concernedThirdPartiesLocation = + this.makeConcernedThirdPartiesLocation(thirdpartyLocationType); + this.locations = [ + { + locationGroup: 'Localisation du parcours', + locations: [personLocation] + }, + { + locationGroup: 'Parties concernées', + locations: [...concernedPersonsLocation, ...concernedThirdPartiesLocation] + }, + { + locationGroup: 'Autres localisations', + locations: results + } + ]; + } + ) + } else { + this.locations = [ + { + locationGroup: 'Localisations', + locations: response.results + } + ]; + } if (window.default_location_id) { let location = this.locations.filter( (l) => l.id === window.default_location_id ); this.$store.dispatch("updateLocation", location); } - resolve(); - } - ) - }) - ); + } + ) + }) }, methods: { labelAccompanyingCourseLocation(value) { diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue index 34aa607e9..35bf9a065 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue @@ -214,10 +214,9 @@ export default { return cond; }, getLocationTypesList() { - getLocationTypes().then(response => new Promise(resolve => { - this.locationTypes = response.results.filter(t => t.availableForUsers === true); - resolve(); - })) + getLocationTypes().then(results => { + this.locationTypes = results.filter(t => t.availableForUsers === true); + }) }, openModal() { this.modal.showModal = true; From 44a09903a031b14b1ce0010540de51b616541dba Mon Sep 17 00:00:00 2001 From: nobohan Date: Fri, 26 Nov 2021 21:08:07 +0100 Subject: [PATCH 36/51] person: init suggested entities in accompanying course --- .../components/PersonsAssociated.vue | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue index 93363fed1..b1bb1e25d 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue @@ -47,6 +47,17 @@ +
+
    +
  • + + + {{ p.text }} + +
  • +
+
+
state.accompanyingCourse.id, - participations: state => state.accompanyingCourse.participations + participations: state => state.accompanyingCourse.participations, + suggestedEntities: state => [state.accompanyingCourse.requestor, ...state.accompanyingCourse.resources] }), ...mapGetters([ 'isParticipationValid' From 5fab0ffe27582fcd48493ea0701491ca695d11d8 Mon Sep 17 00:00:00 2001 From: nobohan Date: Sun, 28 Nov 2021 21:16:48 +0100 Subject: [PATCH 37/51] accompanying course: add resource --- .../components/PersonsAssociated.vue | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue index b1bb1e25d..69f16358f 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue @@ -47,7 +47,7 @@
-
+
  • @@ -102,7 +102,10 @@ export default { ...mapState({ courseId: state => state.accompanyingCourse.id, participations: state => state.accompanyingCourse.participations, - suggestedEntities: state => [state.accompanyingCourse.requestor, ...state.accompanyingCourse.resources] + suggestedEntities: state => [ + state.accompanyingCourse.requestor, + ...state.accompanyingCourse.resources.map(r => r.resource) + ].filter(e => e !== null) }), ...mapGetters([ 'isParticipationValid' @@ -118,7 +121,14 @@ export default { }, getReturnPath() { return window.location.pathname + window.location.search + window.location.hash; - } + }, + addSuggestedPerson(person) { //TODO + this.$store.dispatch('addPersonsInvolved', { result: person, type: 'person' }); + this.setPersonsInBloc(); + }, + }, + mounted() { + console.log(this.suggestedEntities) }, methods: { removeParticipation(item) { From 0ad7ca62355ccbd16683b597ee845f8d4a4072ca Mon Sep 17 00:00:00 2001 From: nobohan Date: Sun, 28 Nov 2021 22:17:00 +0100 Subject: [PATCH 38/51] person: init suggested entities in accompanying course (working) --- .../components/PersonsAssociated.vue | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue index 69f16358f..095cb0815 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue @@ -47,9 +47,9 @@
-
+
    -
  • +
  • {{ p.text }} @@ -102,10 +102,15 @@ export default { ...mapState({ courseId: state => state.accompanyingCourse.id, participations: state => state.accompanyingCourse.participations, - suggestedEntities: state => [ + suggestedPersons: state => [ state.accompanyingCourse.requestor, ...state.accompanyingCourse.resources.map(r => r.resource) - ].filter(e => e !== null) + ] + .filter((e) => e !== null) + .filter((e) => e.type === 'person') + .filter( + (p) => !state.accompanyingCourse.participations.map((p) => p.person.id).includes(p.id) + ) }), ...mapGetters([ 'isParticipationValid' @@ -122,13 +127,6 @@ export default { getReturnPath() { return window.location.pathname + window.location.search + window.location.hash; }, - addSuggestedPerson(person) { //TODO - this.$store.dispatch('addPersonsInvolved', { result: person, type: 'person' }); - this.setPersonsInBloc(); - }, - }, - mounted() { - console.log(this.suggestedEntities) }, methods: { removeParticipation(item) { @@ -165,7 +163,17 @@ export default { ); this.$refs.addPersons.resetSearch(); // to cast child method modal.showModal = false; - } + }, + addSuggestedPerson(person) { + this.$store.dispatch('addParticipation', { result: person, type: 'person' }) + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }) + }, } } From 4abb1a7a5733cec7a98207715cb8ae43ee76926e Mon Sep 17 00:00:00 2001 From: nobohan Date: Sun, 28 Nov 2021 22:32:19 +0100 Subject: [PATCH 39/51] person: suggest entities for requestor --- .../components/Requestor.vue | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue index fd705d374..915edfc8e 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue @@ -133,6 +133,17 @@
+
+
    +
  • + + + {{ p.text }} + +
  • +
+
+
[ + ...state.accompanyingCourse.participations.map(p => p.person), + ...state.accompanyingCourse.resources.map(r => r.resource) + ] + .filter((e) => e !== null) + }), accompanyingCourse() { return this.$store.state.accompanyingCourse }, @@ -227,6 +246,19 @@ export default { this.$toast.open({message: 'An error occurred'}) } }); + }, + addSuggestedEntity(e) { + this.$store.dispatch('addRequestor', { result: e, type: e.type }) //TODO check person | thirdparty + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }) + }, + uniqueId(e) { + return `${e.type}-${e.id}`; } } } From 60422f5eeeceb33afc950ac21c2c63fb70fbe501 Mon Sep 17 00:00:00 2001 From: nobohan Date: Sun, 28 Nov 2021 22:53:36 +0100 Subject: [PATCH 40/51] person: suggest entities for resources --- .../components/PersonsAssociated.vue | 2 +- .../components/Requestor.vue | 2 +- .../components/Resources.vue | 46 ++++++++++++++++++- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue index 095cb0815..96a66c761 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue @@ -109,7 +109,7 @@ export default { .filter((e) => e !== null) .filter((e) => e.type === 'person') .filter( - (p) => !state.accompanyingCourse.participations.map((p) => p.person.id).includes(p.id) + (p) => !state.accompanyingCourse.participations.map((pa) => pa.person.id).includes(p.id) ) }), ...mapGetters([ diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue index 915edfc8e..2d15c11b5 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue @@ -248,7 +248,7 @@ export default { }); }, addSuggestedEntity(e) { - this.$store.dispatch('addRequestor', { result: e, type: e.type }) //TODO check person | thirdparty + this.$store.dispatch('addRequestor', { result: e, type: e.type }) .catch(({name, violations}) => { if (name === 'ValidationException' || name === 'AccessException') { violations.forEach((violation) => this.$toast.open({message: violation})); diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue index 88b0af90b..d42399c4b 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue @@ -18,6 +18,18 @@ @remove="removeResource">
+ +
+
    +
  • + + + {{ p.text }} + +
  • +
+
+
state.accompanyingCourse.resources, - counter: state => state.accompanyingCourse.resources.length + counter: state => state.accompanyingCourse.resources.length, + suggestedEntities: state => [ + state.accompanyingCourse.requestor, + ...state.accompanyingCourse.participations.map(p => p.person), + ] + .filter((e) => e !== null) + .filter( + (e) => { + if (e.type === 'person') { + return !state.accompanyingCourse.resources + .filter((r) => r.resource.type === 'person') + .map((r) => r.resource.id).includes(e.id) + } + if (e.type === 'thirdparty') { + return !state.accompanyingCourse.resources + .filter((r) => r.resource.type === 'thirdparty') + .map((r) => r.resource.id).includes(e.id) + } + } + ) }), methods: { removeResource(item) { @@ -86,6 +117,19 @@ export default { ); this.$refs.addPersons.resetSearch(); // to cast child method modal.showModal = false; + }, + addSuggestedEntity(e) { + this.$store.dispatch('addResource', { result: e, type: e.type}) + .catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }) + }, + uniqueId(e) { + return `${e.type}-${e.id}`; } } } From 037532d13c5eb7e02c811a9f53590fa1fe8734b7 Mon Sep 17 00:00:00 2001 From: nobohan Date: Mon, 29 Nov 2021 09:31:33 +0100 Subject: [PATCH 41/51] person: entities suggestion in parcours: style list --- .../vuejs/AccompanyingCourse/components/PersonsAssociated.vue | 2 +- .../public/vuejs/AccompanyingCourse/components/Requestor.vue | 2 +- .../public/vuejs/AccompanyingCourse/components/Resources.vue | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue index 96a66c761..0468d9103 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue @@ -48,7 +48,7 @@
-
    +
    • diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue index 2d15c11b5..d03aee5a8 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue @@ -134,7 +134,7 @@
-
    +
    • diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue index d42399c4b..c4177b2f4 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue @@ -20,7 +20,7 @@
-
    +
    • From efaaa72a7e3434f22d5145ed305a638ffec57825 Mon Sep 17 00:00:00 2001 From: nobohan Date: Mon, 29 Nov 2021 09:34:46 +0100 Subject: [PATCH 42/51] upd CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index caf9a9249..bfb349b2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to ## Unreleased +* [person] suggest entities (person | thirdparty) when creating/editing the accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/119) * [main] translate multiselect messages when selecting/creating address * [main] set the coordinates of the city when creating a new address OR choosing "pas d'adresse complète" * Use the user.label in accompanying course banner, instead of username; From cb89fc5ef593a9345664fd163df56347bd7e5ed1 Mon Sep 17 00:00:00 2001 From: nobohan Date: Mon, 29 Nov 2021 10:16:23 +0100 Subject: [PATCH 43/51] person: style list-suggest --- .../vuejs/AccompanyingCourse/components/PersonsAssociated.vue | 2 +- .../public/vuejs/AccompanyingCourse/components/Requestor.vue | 2 +- .../public/vuejs/AccompanyingCourse/components/Resources.vue | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue index 0468d9103..877b97244 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue @@ -48,7 +48,7 @@
-
    +
    • diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue index d03aee5a8..b8bab08c6 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue @@ -134,7 +134,7 @@
-
    +
    • diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue index c4177b2f4..8769c47fc 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue @@ -20,7 +20,7 @@
-
    +
    • From e4e1edff68446a96f1a9032bd8c9f3a5011c23c3 Mon Sep 17 00:00:00 2001 From: LenaertsJ Date: Mon, 29 Nov 2021 11:01:51 +0000 Subject: [PATCH 44/51] confidential toggle rights --- CHANGELOG.md | 1 + .../Resources/public/lib/api/apiMethods.js | 1 - .../AccompanyingCourseApiController.php | 16 +++ .../ChillPersonExtension.php | 11 +- .../Entity/AccompanyingPeriod.php | 4 + .../public/vuejs/AccompanyingCourse/api.js | 1 + .../components/Banner/ToggleFlags.vue | 23 +-- .../vuejs/AccompanyingCourse/store/index.js | 53 ++++++- .../Authorization/AccompanyingPeriodVoter.php | 18 ++- .../AccompanyingPeriodConfidentialTest.php | 133 ++++++++++++++++++ .../ChillPersonBundle/chill.api.specs.yaml | 38 +++++ 11 files changed, 278 insertions(+), 21 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/AccompanyingPeriodConfidentialTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index eb3036107..5f22e6dd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to * [activity] check ACL on activity list in person context * [list for accompanying course in person] filter list using ACL * [validation] toasts are displayed for errors when modifying accompanying course (generalization required). +* [period] only the user can enable confidentiality * add an endpoint for checking permissions. See https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/232 * [activity] for a new activity: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties * [calendar] for a new rdv: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.js b/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.js index 5b49ba546..96a95ad93 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.js +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.js @@ -10,7 +10,6 @@ body: (body !== null) ? JSON.stringify(body) : null }) .then(response => { - if (response.ok) { return response.json(); } diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php index 7c19864bd..35fb8d64b 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php @@ -249,6 +249,22 @@ final class AccompanyingCourseApiController extends ApiController ); } + /** + * @Route("/api/1.0/person/accompanying-course/{id}/confidential.json", name="chill_api_person_accompanying_period_confidential") + * @ParamConverter("accompanyingCourse", options={"id": "id"}) + */ + public function toggleConfidentialApi(AccompanyingPeriod $accompanyingCourse, Request $request) + { + if ($request->getMethod() === 'POST') { + $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::CONFIDENTIAL, $accompanyingCourse); + + $accompanyingCourse->setConfidential(!$accompanyingCourse->isConfidential()); + $this->getDoctrine()->getManager()->flush(); + } + + return $this->json($accompanyingCourse->isConfidential(), Response::HTTP_OK, [], ['groups' => ['read']]); + } + public function workApi($id, Request $request, string $_format): Response { return $this->addRemoveSomething( diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php index 84d1c1a71..0ad6c1fd6 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php @@ -378,7 +378,6 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac Request::METHOD_DELETE => 'ALWAYS_FAILS', ], ], - 'confirm' => [ 'methods' => [ Request::METHOD_POST => true, @@ -389,6 +388,16 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, ], ], + 'confidential' => [ + 'methods' => [ + Request::METHOD_POST => true, + Request::METHOD_GET => true, + ], + 'controller_action' => 'toggleConfidentialApi', + 'roles' => [ + Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::TOGGLE_CONFIDENTIAL, + ], + ], 'findAccompanyingPeriodsByPerson' => [ 'path' => '/by-person/{person_id}.{_format}', 'controller_action' => 'getAccompanyingPeriodsByPerson', diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 6b0c5d768..f3eb6538a 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -49,6 +49,10 @@ use UnexpectedValueException; * "accompanying_period": AccompanyingPeriod::class * }) * @Assert\GroupSequenceProvider + * @Assert\Expression( + * "this.isConfidential and this.getUser === NULL", + * message="If the accompanying course is confirmed and confidential, a referrer must remain assigned." + * ) */ class AccompanyingPeriod implements TrackCreationInterface, diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js index 4eb6f2a33..35349164f 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js @@ -48,6 +48,7 @@ const whoami = () => { }); }; + export { whoami, getSocialIssues, diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue index 459e5e758..edb363cc2 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue @@ -21,6 +21,7 @@ diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/store/index.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/store/index.js index e91de0be0..a1858c6b3 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/store/index.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/store/index.js @@ -37,6 +37,7 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) referrersSuggested: [], // all the users available users: [], + permissions: {} }, getters: { isParticipationValid(state) { @@ -70,7 +71,14 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) return true; } return false; - } + }, + canTogglePermission(state) { + if (state.permissions.roles) { + return state.permissions.roles['CHILL_PERSON_ACCOMPANYING_PERIOD_TOGGLE_CONFIDENTIAL']; + } + + return false; + }, }, mutations: { catchError(state, error) { @@ -201,6 +209,10 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) return u; }); }, + setPermissions(state, permissions) { + state.permissions = permissions; + // console.log('permissions', state.permissions); + }, updateLocation(state, r) { //console.log('### mutation: set location attributes', r); state.accompanyingCourse.locationStatus = r.locationStatus; @@ -625,6 +637,33 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) let users = await getUsers(); commit('setUsers', users); }, + /** + * By adding more roles to body['roles'], more permissions can be checked. + */ + fetchPermissions({commit}) { + const url = '/api/1.0/main/permissions/info.json'; + const body = { + "object": { + "type": "accompanying_period", + "id": id + }, + "class": "Chill\\PersonBundle\\Entity\\AccompanyingPeriod", + "roles": [ + "CHILL_PERSON_ACCOMPANYING_PERIOD_TOGGLE_CONFIDENTIAL" + ] + } + + return makeFetch('POST', url, body) + .then((response) => { + commit('setPermissions', response); + + return Promise.resolve(); + }) + .catch((error) => { + commit('catchError', error); + throw error; + }) + }, updateLocation({ commit, dispatch }, payload) { //console.log('## action: updateLocation', payload.locationStatusTo); const url = `/api/1.0/person/accompanying-course/${payload.targetId}.json`; @@ -642,12 +681,12 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) Object.assign(body, location); makeFetch('PATCH', url, body) .then((response) => { - commit('updateLocation', { - location: response.location, - locationStatus: response.locationStatus, - personLocation: response.personLocation - }); - dispatch('fetchReferrersSuggested'); + commit('updateLocation', { + location: response.location, + locationStatus: response.locationStatus, + personLocation: response.personLocation + }); + dispatch('fetchReferrersSuggested'); }) .catch((error) => { commit('catchError', error); diff --git a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php index ae226e848..ca212108a 100644 --- a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php +++ b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php @@ -31,6 +31,7 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH self::EDIT, self::DELETE, self::FULL, + self::TOGGLE_CONFIDENTIAL_ALL, ]; public const CREATE = 'CHILL_PERSON_ACCOMPANYING_PERIOD_CREATE'; @@ -53,6 +54,13 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH */ public const SEE_DETAILS = 'CHILL_PERSON_ACCOMPANYING_PERIOD_SEE_DETAILS'; + public const TOGGLE_CONFIDENTIAL = 'CHILL_PERSON_ACCOMPANYING_PERIOD_TOGGLE_CONFIDENTIAL'; + + /** + * Right to toggle confidentiality. + */ + public const TOGGLE_CONFIDENTIAL_ALL = 'CHILL_PERSON_ACCOMPANYING_PERIOD_TOGGLE_CONFIDENTIAL_ALL'; + private Security $security; private VoterHelperInterface $voterHelper; @@ -65,7 +73,7 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH $this->voterHelper = $voterHelperFactory ->generate(self::class) ->addCheckFor(null, [self::CREATE]) - ->addCheckFor(AccompanyingPeriod::class, self::ALL) + ->addCheckFor(AccompanyingPeriod::class, [self::TOGGLE_CONFIDENTIAL, ...self::ALL]) ->addCheckFor(Person::class, [self::SEE]) ->build(); } @@ -113,6 +121,14 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH return false; } + if (self::TOGGLE_CONFIDENTIAL === $attribute) { + if ($subject->getUser() === $token->getUser()) { + return true; + } + + return $this->voterHelper->voteOnAttribute(self::TOGGLE_CONFIDENTIAL_ALL, $subject, $token); + } + // if confidential, only the referent can see it if ($subject->isConfidential()) { return $token->getUser() === $subject->getUser(); diff --git a/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/AccompanyingPeriodConfidentialTest.php b/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/AccompanyingPeriodConfidentialTest.php new file mode 100644 index 000000000..f6cd76b79 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/AccompanyingPeriodConfidentialTest.php @@ -0,0 +1,133 @@ +client = static::createClient([], [ + 'PHP_AUTH_USER' => 'fred', + 'PHP_AUTH_PW' => 'password', + ]); + } + + public function dataGenerateRandomAccompanyingCourse() + { + $maxGenerated = 3; + $maxResults = $maxGenerated * 8; + + static::bootKernel(); + $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager'); + $center = $em->getRepository(Center::class) + ->findOneBy(['name' => 'Center A']); + + $qb = $em->createQueryBuilder(); + $personIds = $qb + ->select('p.id') + ->distinct(true) + ->from(Person::class, 'p') + ->join('p.accompanyingPeriodParticipations', 'participation') + ->join('participation.accompanyingPeriod', 'ap') + ->andWhere( + $qb->expr()->eq('ap.step', ':step') + ) + ->andWhere( + $qb->expr()->eq('ap.confidential', ':confidential') + ) + ->setParameter('step', AccompanyingPeriod::STEP_CONFIRMED) + ->setParameter('confidential', true) + ->setMaxResults($maxResults) + ->getQuery() + ->getScalarResult(); + + // create a random order + shuffle($personIds); + + $nbGenerated = 0; + + while ($nbGenerated < $maxGenerated) { + $id = array_pop($personIds)['id']; + + $person = $em->getRepository(Person::class) + ->find($id); + $periods = $person->getAccompanyingPeriods(); + + yield [array_pop($personIds)['id'], $periods[array_rand($periods)]->getId()]; + + ++$nbGenerated; + } + } + + /** + * @dataProvider dataGenerateRandomAccompanyingCourse + */ + public function testRemoveUserWhenConfidential(int $periodId) + { + $period = self::$container->get(AccompanyingPeriodRepository::class) + ->find($periodId); + $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager'); + + $isConfidential = $period->isConfidential(); + $step = $period->getStep(); + + $initialUser = $period->getUser(); + + $user = new stdClass(); + $user->id = 0; + $user->type = 'user'; + dump($user); + + $this->client->request( + Request::METHOD_PATCH, + sprintf('/api/1.0/person/accompanying-course/%d.json', $periodId), + [], // parameters + [], // files + [], // server parameters + json_encode(['type' => 'accompanying_period', 'user' => $user]) + ); + $response = $this->client->getResponse(); + + // if ($isConfidential === true && $step === 'CONFIRMED') { + $this->assertEquals(422, $response->getStatusCode()); + // } + + $this->assertEquals(200, $response->getStatusCode()); + $period = $em->getRepository(AccompanyingPeriod::class) + ->find($periodId); + $this->assertEquals($user, $period->getUser()); + + // assign initial user again + $period->setUser($initialUser); + $em->flush(); + } +} diff --git a/src/Bundle/ChillPersonBundle/chill.api.specs.yaml b/src/Bundle/ChillPersonBundle/chill.api.specs.yaml index 5e1f0d937..c67884a19 100644 --- a/src/Bundle/ChillPersonBundle/chill.api.specs.yaml +++ b/src/Bundle/ChillPersonBundle/chill.api.specs.yaml @@ -1113,6 +1113,44 @@ paths: description: "OK" 400: description: "transition cannot be applyed" + + /1.0/person/accompanying-course/{id}/confidential.json: + post: + tags: + - person + summary: "Toggle confidentiality of accompanying course" + parameters: + - name: id + in: path + required: true + description: The accompanying period's id + schema: + type: integer + format: integer + minimum: 1 + requestBody: + description: "Confidentiality toggle" + required: true + content: + application/json: + schema: + type: object + properties: + type: + type: string + enum: + - "accompanying_period" + confidential: + type: boolean + responses: + 401: + description: "Unauthorized" + 404: + description: "Not found" + 200: + description: "OK" + 422: + description: "object with validation errors" /1.0/person/accompanying-course/by-person/{person_id}.json: get: From b300858bdd483f9c9a016d05ceca624846e819c9 Mon Sep 17 00:00:00 2001 From: juminet Date: Mon, 29 Nov 2021 12:27:54 +0000 Subject: [PATCH 45/51] activity: avoid existing entities being added in Users, ThirdParties, Persons --- CHANGELOG.md | 1 + phpstan-critical.neon | 5 - .../Controller/ActivityController.php | 4 +- .../AdminActivityTypeController.php | 3 +- .../ChillActivityBundle/Entity/Activity.php | 14 +- .../Entity/ActivityType.php | 56 ++------ .../Form/ActivityTypeType.php | 2 +- .../Resources/public/vuejs/Activity/App.vue | 7 +- .../Resources/public/vuejs/Activity/index.js | 13 +- .../Resources/views/Activity/edit.html.twig | 5 +- .../Resources/views/Activity/new.html.twig | 28 ++-- .../Activity/newAccompanyingCourse.html.twig | 2 +- .../Constraints/ActivityValidity.php | 42 ++++++ .../Constraints/ActivityValidityValidator.php | 126 ++++++++++++++++++ .../ChillActivityBundle/config/services.yaml | 5 + .../translations/messages.fr.yml | 36 ++--- .../translations/validators.fr.yml | 20 +++ .../LocationValidityValidator.php | 2 +- 18 files changed, 277 insertions(+), 94 deletions(-) create mode 100644 src/Bundle/ChillActivityBundle/Validator/Constraints/ActivityValidity.php create mode 100644 src/Bundle/ChillActivityBundle/Validator/Constraints/ActivityValidityValidator.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f22e6dd9..467ed8037 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to ## Unreleased +* [activity] add custom validation on the Activity class, based on what is required from the ActivityType (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/188) * [main] translate multiselect messages when selecting/creating address * [main] set the coordinates of the city when creating a new address OR choosing "pas d'adresse complète" * Use the user.label in accompanying course banner, instead of username; diff --git a/phpstan-critical.neon b/phpstan-critical.neon index 6147f2022..b214654bf 100644 --- a/phpstan-critical.neon +++ b/phpstan-critical.neon @@ -70,11 +70,6 @@ parameters: count: 1 path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/MembersEditorNormalizer.php - - - message: "#^Undefined variable\\: \\$value$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Validator/Constraints/AccompanyingPeriod/LocationValidityValidator.php - - message: "#^Undefined variable\\: \\$choiceSlug$#" count: 1 diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 0c43c4d91..0f725c2b3 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -408,7 +408,7 @@ final class ActivityController extends AbstractController $activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']); - $defaultLocationId = $this->getUser()->getCurrentLocation()->getId(); + $defaultLocation = $this->getUser()->getCurrentLocation(); return $this->render($view, [ 'person' => $person, @@ -416,7 +416,7 @@ final class ActivityController extends AbstractController 'entity' => $entity, 'form' => $form->createView(), 'activity_json' => $activity_array, - 'default_location_id' => $defaultLocationId, + 'default_location' => $defaultLocation, ]); } diff --git a/src/Bundle/ChillActivityBundle/Controller/AdminActivityTypeController.php b/src/Bundle/ChillActivityBundle/Controller/AdminActivityTypeController.php index 140a0b855..81c978bf2 100644 --- a/src/Bundle/ChillActivityBundle/Controller/AdminActivityTypeController.php +++ b/src/Bundle/ChillActivityBundle/Controller/AdminActivityTypeController.php @@ -23,6 +23,7 @@ class AdminActivityTypeController extends CRUDController protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator) { /** @var \Doctrine\ORM\QueryBuilder $query */ - return $query->orderBy('e.ordering', 'ASC'); + return $query->orderBy('e.ordering', 'ASC') + ->addOrderBy('e.id', 'ASC'); } } diff --git a/src/Bundle/ChillActivityBundle/Entity/Activity.php b/src/Bundle/ChillActivityBundle/Entity/Activity.php index 85abed5e5..c63ce90f7 100644 --- a/src/Bundle/ChillActivityBundle/Entity/Activity.php +++ b/src/Bundle/ChillActivityBundle/Entity/Activity.php @@ -9,6 +9,7 @@ namespace Chill\ActivityBundle\Entity; +use Chill\ActivityBundle\Validator\Constraints as ActivityValidator; use Chill\DocStoreBundle\Entity\Document; use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; @@ -41,6 +42,7 @@ use Symfony\Component\Serializer\Annotation\SerializedName; * @DiscriminatorMap(typeProperty="type", mapping={ * "activity": Activity::class * }) + * @ActivityValidator\ActivityValidity */ /* @@ -202,7 +204,9 @@ class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPer public function addPerson(?Person $person): self { if (null !== $person) { - $this->persons[] = $person; + if (!$this->persons->contains($person)) { + $this->persons[] = $person; + } } return $this; @@ -236,7 +240,9 @@ class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPer public function addThirdParty(?ThirdParty $thirdParty): self { if (null !== $thirdParty) { - $this->thirdParties[] = $thirdParty; + if (!$this->thirdParties->contains($thirdParty)) { + $this->thirdParties[] = $thirdParty; + } } return $this; @@ -245,7 +251,9 @@ class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPer public function addUser(?User $user): self { if (null !== $user) { - $this->users[] = $user; + if (!$this->users->contains($user)) { + $this->users[] = $user; + } } return $this; diff --git a/src/Bundle/ChillActivityBundle/Entity/ActivityType.php b/src/Bundle/ChillActivityBundle/Entity/ActivityType.php index 0e9b1150a..4cb4250b5 100644 --- a/src/Bundle/ChillActivityBundle/Entity/ActivityType.php +++ b/src/Bundle/ChillActivityBundle/Entity/ActivityType.php @@ -12,6 +12,7 @@ namespace Chill\ActivityBundle\Entity; use Doctrine\ORM\Mapping as ORM; use InvalidArgumentException; use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints as Assert; /** * Class ActivityType. @@ -29,11 +30,13 @@ class ActivityType public const FIELD_REQUIRED = 2; /** + * @deprecated not in use * @ORM\Column(type="string", nullable=false, options={"default": ""}) */ private string $accompanyingPeriodLabel = ''; /** + * @deprecated not in use * @ORM\Column(type="smallint", nullable=false, options={"default": 1}) */ private int $accompanyingPeriodVisible = self::FIELD_INVISIBLE; @@ -195,16 +198,21 @@ class ActivityType /** * @ORM\Column(type="smallint", nullable=false, options={"default": 1}) + * @Assert\EqualTo(propertyPath="socialIssuesVisible", message="This parameter must be equal to social issue parameter") */ private int $socialActionsVisible = self::FIELD_INVISIBLE; /** * @ORM\Column(type="string", nullable=false, options={"default": ""}) + * + * @deprecated not in use */ private string $socialDataLabel = ''; /** * @ORM\Column(type="smallint", nullable=false, options={"default": 1}) + * + * @deprecated not in use */ private int $socialDataVisible = self::FIELD_INVISIBLE; @@ -260,16 +268,6 @@ class ActivityType */ private int $userVisible = self::FIELD_REQUIRED; - public function getAccompanyingPeriodLabel(): string - { - return $this->accompanyingPeriodLabel; - } - - public function getAccompanyingPeriodVisible(): int - { - return $this->accompanyingPeriodVisible; - } - /** * Get active * return true if the type is active. @@ -446,16 +444,6 @@ class ActivityType return $this->socialActionsVisible; } - public function getSocialDataLabel(): string - { - return $this->socialDataLabel; - } - - public function getSocialDataVisible(): int - { - return $this->socialDataVisible; - } - public function getSocialIssuesLabel(): ?string { return $this->socialIssuesLabel; @@ -537,20 +525,6 @@ class ActivityType return self::FIELD_INVISIBLE !== $this->{$property}; } - public function setAccompanyingPeriodLabel(string $accompanyingPeriodLabel): self - { - $this->accompanyingPeriodLabel = $accompanyingPeriodLabel; - - return $this; - } - - public function setAccompanyingPeriodVisible(int $accompanyingPeriodVisible): self - { - $this->accompanyingPeriodVisible = $accompanyingPeriodVisible; - - return $this; - } - /** * Set active * set to true if the type is active. @@ -768,20 +742,6 @@ class ActivityType return $this; } - public function setSocialDataLabel(string $socialDataLabel): self - { - $this->socialDataLabel = $socialDataLabel; - - return $this; - } - - public function setSocialDataVisible(int $socialDataVisible): self - { - $this->socialDataVisible = $socialDataVisible; - - return $this; - } - public function setSocialIssuesLabel(string $socialIssuesLabel): self { $this->socialIssuesLabel = $socialIssuesLabel; diff --git a/src/Bundle/ChillActivityBundle/Form/ActivityTypeType.php b/src/Bundle/ChillActivityBundle/Form/ActivityTypeType.php index 300be5c4c..682d73aaf 100644 --- a/src/Bundle/ChillActivityBundle/Form/ActivityTypeType.php +++ b/src/Bundle/ChillActivityBundle/Form/ActivityTypeType.php @@ -56,7 +56,7 @@ class ActivityTypeType extends AbstractType 'persons', 'user', 'date', 'place', 'persons', 'thirdParties', 'durationTime', 'travelTime', 'attendee', 'reasons', 'comment', 'sentReceived', 'documents', - 'emergency', 'accompanyingPeriod', 'socialData', 'users', + 'emergency', 'socialIssues', 'socialActions', 'users', ]; foreach ($fields as $field) { diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/App.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/App.vue index 52454c2f7..2fb9d022d 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/App.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/App.vue @@ -1,7 +1,7 @@ {{ encore_entry_script_tags('vue_activity') }} {% endblock %} diff --git a/src/Bundle/ChillActivityBundle/Validator/Constraints/ActivityValidity.php b/src/Bundle/ChillActivityBundle/Validator/Constraints/ActivityValidity.php new file mode 100644 index 000000000..6164c3c5d --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Validator/Constraints/ActivityValidity.php @@ -0,0 +1,42 @@ +getActivityType()->getPersonsVisible() === 2 && count($activity->getPersons()) === 0) { + $this->context + ->buildViolation($constraint->noPersonsMessage) + ->addViolation(); + } + + if ($activity->getActivityType()->getUsersVisible() === 2 && count($activity->getUsers()) === 0) { + $this->context + ->buildViolation($constraint->noUsersMessage) + ->addViolation(); + } + + if ($activity->getActivityType()->getThirdPartiesVisible() === 2 && count($activity->getThirdParties()) === 0) { + $this->context + ->buildViolation($constraint->noThirdPartiesMessage) + ->addViolation(); + } + + if ($activity->getActivityType()->getUserVisible() === 2 && null === $activity->getUser()) { + $this->context + ->buildViolation($constraint->makeIsRequiredMessage('user')) + ->addViolation(); + } + + if ($activity->getActivityType()->getDateVisible() === 2 && null === $activity->getDate()) { + $this->context + ->buildViolation($constraint->makeIsRequiredMessage('date')) + ->addViolation(); + } + + if ($activity->getActivityType()->getLocationVisible() === 2 && null === $activity->getLocation()) { + $this->context + ->buildViolation($constraint->makeIsRequiredMessage('location')) + ->addViolation(); + } + + if ($activity->getActivityType()->getDurationTimeVisible() === 2 && null === $activity->getDurationTime()) { + $this->context + ->buildViolation($constraint->makeIsRequiredMessage('duration time')) + ->addViolation(); + } + + if ($activity->getActivityType()->getTravelTimeVisible() === 2 && null === $activity->getTravelTime()) { + $this->context + ->buildViolation($constraint->makeIsRequiredMessage('travel time')) + ->addViolation(); + } + + if ($activity->getActivityType()->getAttendeeVisible() === 2 && null === $activity->getAttendee()) { + $this->context + ->buildViolation($constraint->makeIsRequiredMessage('attendee')) + ->addViolation(); + } + + if ($activity->getActivityType()->getReasonsVisible() === 2 && null === $activity->getReasons()) { + $this->context + ->buildViolation($constraint->makeIsRequiredMessage('reasons')) + ->addViolation(); + } + + if ($activity->getActivityType()->getCommentVisible() === 2 && null === $activity->getComment()) { + $this->context + ->buildViolation($constraint->makeIsRequiredMessage('comment')) + ->addViolation(); + } + + if ($activity->getActivityType()->getSentReceivedVisible() === 2 && null === $activity->getSentReceived()) { + $this->context + ->buildViolation($constraint->makeIsRequiredMessage('sent/received')) + ->addViolation(); + } + + if ($activity->getActivityType()->getDocumentsVisible() === 2 && null === $activity->getDocuments()) { + $this->context + ->buildViolation($constraint->makeIsRequiredMessage('document')) + ->addViolation(); + } + + if ($activity->getActivityType()->getEmergencyVisible() === 2 && null === $activity->getEmergency()) { + $this->context + ->buildViolation($constraint->makeIsRequiredMessage('emergency')) + ->addViolation(); + } + + if ($activity->getActivityType()->getSocialIssuesVisible() === 2 && $activity->getSocialIssues()->count() === 0) { + $this->context + ->buildViolation($constraint->socialIssuesMessage) + ->addViolation(); + } + + if ($activity->getActivityType()->getSocialActionsVisible() === 2 && $activity->getSocialActions()->count() === 0) { + $this->context + ->buildViolation($constraint->socialActionsMessage) + ->addViolation(); + } + } +} diff --git a/src/Bundle/ChillActivityBundle/config/services.yaml b/src/Bundle/ChillActivityBundle/config/services.yaml index 4e93e38be..1ca413f0e 100644 --- a/src/Bundle/ChillActivityBundle/config/services.yaml +++ b/src/Bundle/ChillActivityBundle/config/services.yaml @@ -27,3 +27,8 @@ services: Chill\ActivityBundle\Repository\: resource: '../Repository/' + + Chill\ActivityBundle\Validator\Constraints\: + autowire: true + autoconfigure: true + resource: '../Validator/Constraints/' diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index d0c3ddc6d..959eee233 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -139,34 +139,40 @@ ActivityReasonCategory is inactive and won't be proposed: La catégorie est inac # activity type type admin ActivityType list: Types d'activités Create a new activity type: Créer un nouveau type d'activité -Persons visible: Visibilté du champ Personnes +Persons visible: Visibilité du champ Personnes Persons label: Libellé du champ Personnes -User visible: Visibilté du champ Utilisateur +User visible: Visibilité du champ Utilisateur User label: Libellé du champ Utilisateur -Date visible: Visibilté du champ Date +Date visible: Visibilité du champ Date Date label: Libellé du champ Date -Place visible: Visibilté du champ Lieu +Place visible: Visibilité du champ Lieu Place label: Libellé du champ Lieu -Third parties visible: Visibilté du champ Tiers +Third parties visible: Visibilité du champ Tiers Third parties label: Libellé du champ Tiers -Duration time visible: Visibilté du champ Durée +Duration time visible: Visibilité du champ Durée Duration time label: Libellé du champ Durée -Travel time visible: Visibilté du champ Durée de déplacement +Travel time visible: Visibilité du champ Durée de déplacement Travel time label: Libellé du champ Durée de déplacement -Attendee visible: Visibilté du champ Présence de l'usager +Attendee visible: Visibilité du champ Présence de l'usager Attendee label: Libellé du champ Présence de l'usager -Reasons visible: Visibilté du champ Sujet +Reasons visible: Visibilité du champ Sujet Reasons label: Libellé du champ Sujet -Comment visible: Visibilté du champ Commentaire +Comment visible: Visibilité du champ Commentaire Comment label: Libellé du champ Commentaire -Emergency visible: Visibilté du champ Urgent +Emergency visible: Visibilité du champ Urgent Emergency label: Libellé du champ Urgent -Accompanying period visible: Visibilté du champ Période d'accompagnement +Accompanying period visible: Visibilité du champ Période d'accompagnement Accompanying period label: Libellé du champ Période d'accompagnement -Social data visible: Visibilté du champ Données sociales -Social data label: Libellé du champ Données sociales -Users visible: Visibilté du champ Utilisateurs +Social issues visible: Visibilité du champ Problématiques sociales +Social issues label: Libellé du champ Problématiques sociales +Social actions visible: Visibilité du champ Action sociale +Social actions label: Libellé du champ Action sociale +Users visible: Visibilité du champ Utilisateurs Users label: Libellé du champ Utilisateurs +Sent received visible: Visibilité du champ Entrant / Sortant +Sent received label: Libellé du champ Entrant / Sortant +Documents visible: Visibilité du champ Documents +Documents label: Libellé du champ Documents # activity type category admin ActivityTypeCategory list: Liste des catégories des types d'activité diff --git a/src/Bundle/ChillActivityBundle/translations/validators.fr.yml b/src/Bundle/ChillActivityBundle/translations/validators.fr.yml index edda0b67b..072ac55d2 100644 --- a/src/Bundle/ChillActivityBundle/translations/validators.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/validators.fr.yml @@ -1,2 +1,22 @@ The reasons's level should not be empty: Le niveau du sujet ne peut pas être vide At least one reason must be choosen: Au moins un sujet doit être choisi +For this type of activity, you must add at least one person: Pour ce type d'activité, vous devez ajouter au moins un usager +For this type of activity, you must add at least one user: Pour ce type d'activité, vous devez ajouter au moins un utilisateur +For this type of activity, you must add at least one third party: Pour ce type d'activité, vous devez ajouter au moins un tiers +For this type of activity, user is required: Pour ce type d'activité, l'utilisateur est requis +For this type of activity, date is required: Pour ce type d'activité, la date est requise +For this type of activity, location is required: Pour ce type d'activité, la localisation est requise +For this type of activity, attendee is required: Pour ce type d'activité, le champ "Présence de la personne" est requis +For this type of activity, duration time is required: Pour ce type d'activité, la durée est requise +For this type of activity, travel time is required: Pour ce type d'activité, la durée du trajet est requise +For this type of activity, reasons is required: Pour ce type d'activité, le champ "sujet" est requis +For this type of activity, comment is required: Pour ce type d'activité, un commentaire est requis +For this type of activity, sent/received is required: Pour ce type d'activité, le champ Entrant/Sortant est requis +For this type of activity, document is required: Pour ce type d'activité, un document est requis +For this type of activity, emergency is required: Pour ce type d'activité, le champ "Urgent" est requis +For this type of activity, accompanying period is required: Pour ce type d'activité, le parcours d'accompagnement est requis +For this type of activity, you must add at least one social issue: Pour ce type d'activité, vous devez ajouter au moins une problématique sociale +For this type of activity, you must add at least one social action: Pour ce type d'activité, vous devez indiquez au moins une action sociale + +# admin +This parameter must be equal to social issue parameter: Ce paramètre doit être égal au paramètre "Visibilité du champs Problématiques sociales" diff --git a/src/Bundle/ChillPersonBundle/Validator/Constraints/AccompanyingPeriod/LocationValidityValidator.php b/src/Bundle/ChillPersonBundle/Validator/Constraints/AccompanyingPeriod/LocationValidityValidator.php index 167553909..275ab281f 100644 --- a/src/Bundle/ChillPersonBundle/Validator/Constraints/AccompanyingPeriod/LocationValidityValidator.php +++ b/src/Bundle/ChillPersonBundle/Validator/Constraints/AccompanyingPeriod/LocationValidityValidator.php @@ -32,7 +32,7 @@ class LocationValidityValidator extends ConstraintValidator } if (!$period instanceof AccompanyingPeriod) { - throw new UnexpectedValueException($value, AccompanyingPeriod::class); + throw new UnexpectedValueException($period, AccompanyingPeriod::class); } if ($period->getLocationStatus() === 'person') { From d5a269356b33d02bc363c53c494db9140543afa1 Mon Sep 17 00:00:00 2001 From: LenaertsJ Date: Mon, 29 Nov 2021 12:39:11 +0000 Subject: [PATCH 46/51] Display of toasts for accompanying course + general makeFetch --- .../Resources/public/vuejs/AccompanyingCourse/api.js | 1 - .../components/Banner/ToggleFlags.vue | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js index 35349164f..4eb6f2a33 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js @@ -48,7 +48,6 @@ const whoami = () => { }); }; - export { whoami, getSocialIssues, diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue index edb363cc2..39fe50acc 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue @@ -78,10 +78,17 @@ export default { this.$store.dispatch('fetchPermissions').then(() => { if (!this.$store.getters.canTogglePermission) { this.$toast.open({message: "Seul le référent peut modifier la confidentialité"}); + return Promise.resolve(); } else { - this.$store.dispatch('toggleConfidential', (!this.isConfidential)); + return this.$store.dispatch('toggleConfidential', (!this.isConfidential)); } - }); + }).catch(({name, violations}) => { + if (name === 'ValidationException' || name === 'AccessException') { + violations.forEach((violation) => this.$toast.open({message: violation})); + } else { + this.$toast.open({message: 'An error occurred'}) + } + }); }, }, } From 4acedc84b0671851188577f2ba1c1f801d57eb74 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 29 Nov 2021 13:55:38 +0100 Subject: [PATCH 47/51] vue_accourse: fix list-suggest asset for items suggestions --- .../AccompanyingCourse/components/PersonsAssociated.vue | 7 ++----- .../vuejs/AccompanyingCourse/components/Requestor.vue | 9 +++------ .../vuejs/AccompanyingCourse/components/Resources.vue | 7 ++----- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue index 877b97244..0b46c558a 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue @@ -48,12 +48,9 @@
-
    +
    • - - - {{ p.text }} - + {{ p.text }}
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue index b8bab08c6..e2ead7a01 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue @@ -32,7 +32,7 @@ - + - +