From 1bd6df7ce2f6c7a2e46f79afbcc15fa641492e99 Mon Sep 17 00:00:00 2001 From: nobohan Date: Tue, 23 Nov 2021 09:13:40 +0100 Subject: [PATCH 01/27] correct deprecated activity method --- .../ChillActivityBundle/Controller/ActivityController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index f4248b5e8..17eae166e 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -327,8 +327,8 @@ final class ActivityController extends AbstractController $entity->setAccompanyingPeriod($accompanyingPeriod); } - $entity->setType($activityType); - $entity->setDate(new DateTime('now')); + $entity->setActivityType($activityType); + $entity->setDate(new \DateTime('now')); if ($request->query->has('activityData')) { $activityData = $request->query->get('activityData'); From e3040f4bfb1f4dae6c23e316c789f586cb99d7f0 Mon Sep 17 00:00:00 2001 From: nobohan Date: Tue, 23 Nov 2021 09:26:38 +0100 Subject: [PATCH 02/27] correct deprecated activity method --- .../Controller/ActivityController.php | 80 +++++++++++++++++-- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 17eae166e..989ce518e 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -384,8 +384,8 @@ final class ActivityController extends AbstractController $form = $this->createForm(ActivityType::class, $entity, [ 'center' => $entity->getCenter(), - 'role' => new Role('CHILL_ACTIVITY_CREATE'), - 'activityType' => $entity->getType(), + 'role' => new Role('CHILL_ACTIVITY_CREATE'), + 'activityType' => $entity->getActivityType(), 'accompanyingPeriod' => $accompanyingPeriod, ])->handleRequest($request); @@ -482,10 +482,26 @@ final class ActivityController extends AbstractController throw $this->createNotFoundException('Unable to find Activity entity.'); } - if (null !== $accompanyingPeriod) { - // @TODO: Properties created dynamically. - $entity->personsAssociated = $entity->getPersonsAssociated(); - $entity->personsNotAssociated = $entity->getPersonsNotAssociated(); + // 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); } // TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période @@ -517,7 +533,59 @@ 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 32af0769865b144fc55107607f94110fdfa73686 Mon Sep 17 00:00:00 2001 From: nobohan Date: Tue, 23 Nov 2021 10:41:27 +0100 Subject: [PATCH 03/27] main: locationType: add defaultFor property --- .../ChillMainBundle/Entity/LocationType.php | 22 +++++++++++++ .../migrations/Version20211123093355.php | 31 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20211123093355.php diff --git a/src/Bundle/ChillMainBundle/Entity/LocationType.php b/src/Bundle/ChillMainBundle/Entity/LocationType.php index 8b171eb0f..a49bb6c1b 100644 --- a/src/Bundle/ChillMainBundle/Entity/LocationType.php +++ b/src/Bundle/ChillMainBundle/Entity/LocationType.php @@ -29,6 +29,9 @@ class LocationType public const STATUS_REQUIRED = 'required'; + const DEFAULT_FOR_PERSON = 'person'; + const DEFAULT_FOR_3PARTY = 'thirdparty'; + /** * @ORM\Column(type="boolean", nullable=true) * @Serializer\Groups({"read"}) @@ -67,6 +70,13 @@ 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; @@ -131,4 +141,16 @@ class LocationType return $this; } + + public function getDefaultFor(): ?string + { + return $this->defaultFor; + } + + public function setDefaultFor(string $defaultFor): self + { + $this->defaultFor = $defaultFor; + + return $this; + } } diff --git a/src/Bundle/ChillMainBundle/migrations/Version20211123093355.php b/src/Bundle/ChillMainBundle/migrations/Version20211123093355.php new file mode 100644 index 000000000..1736cf857 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20211123093355.php @@ -0,0 +1,31 @@ +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 7eeb2f2a7dc85a997c42749b8073af260ab454e1 Mon Sep 17 00:00:00 2001 From: nobohan Date: Tue, 23 Nov 2021 10:57:43 +0100 Subject: [PATCH 04/27] main: LocationType: add new property defaultFor in the admin --- src/Bundle/ChillMainBundle/Entity/LocationType.php | 4 ++-- .../ChillMainBundle/Form/LocationTypeType.php | 13 ++++++++++++- .../Resources/views/LocationType/index.html.twig | 2 ++ .../ChillMainBundle/translations/messages.fr.yml | 4 ++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Entity/LocationType.php b/src/Bundle/ChillMainBundle/Entity/LocationType.php index a49bb6c1b..d3dff58b7 100644 --- a/src/Bundle/ChillMainBundle/Entity/LocationType.php +++ b/src/Bundle/ChillMainBundle/Entity/LocationType.php @@ -29,8 +29,8 @@ class LocationType public const STATUS_REQUIRED = 'required'; - const DEFAULT_FOR_PERSON = 'person'; - const DEFAULT_FOR_3PARTY = 'thirdparty'; + public const DEFAULT_FOR_PERSON = 'person'; + public const DEFAULT_FOR_3PARTY = 'thirdparty'; /** * @ORM\Column(type="boolean", nullable=true) diff --git a/src/Bundle/ChillMainBundle/Form/LocationTypeType.php b/src/Bundle/ChillMainBundle/Form/LocationTypeType.php index 1e4776f8f..93bc5ba0f 100644 --- a/src/Bundle/ChillMainBundle/Form/LocationTypeType.php +++ b/src/Bundle/ChillMainBundle/Form/LocationTypeType.php @@ -71,6 +71,17 @@ final class LocationTypeType extends AbstractType ], 'expanded' => true, ] - ); + ) + ->add( + 'defaultFor', + ChoiceType::class, + [ + 'choices' => [ + 'none' => null, + 'person' => LocationType::DEFAULT_FOR_PERSON, + 'thirdparty' => LocationType::DEFAULT_FOR_3PARTY, + ], + 'expanded' => true + ]); } } diff --git a/src/Bundle/ChillMainBundle/Resources/views/LocationType/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/LocationType/index.html.twig index ec617c6c0..5402d5e89 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/LocationType/index.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/LocationType/index.html.twig @@ -11,6 +11,7 @@ {{ 'Address required'|trans }} {{ 'Contact data'|trans }} {{ 'Active'|trans }} + {{ 'Default for'|trans }} @@ -33,6 +34,7 @@ {%- endif -%} + {{ entity.defaultFor|trans }}
  • diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index 210227fbe..f8c474042 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -212,6 +212,10 @@ Location type: Type de localisation Phonenumber1: Numéro de téléphone Phonenumber2: Autre numéro de téléphone Configure location and location type: Configuration des localisations +Default for: Type de localisation par défaut pour +none: aucun +person: usager +thirdparty: tiers # circles / scopes Choose the circle: Choisir le cercle From 92e59e211d06c77e7f9b6782765e21418d13ef96 Mon Sep 17 00:00:00 2001 From: nobohan Date: Tue, 23 Nov 2021 14:19:19 +0100 Subject: [PATCH 05/27] activity: suggest location based on accompanying period (WIP) --- .../vuejs/Activity/components/Location.vue | 76 ++++++++++++------- 1 file changed, 48 insertions(+), 28 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 5fa3360b7..147184098 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -2,10 +2,9 @@
    - + v-model="location" + > @@ -27,49 +27,69 @@ From b808e970aba82915bd04894bbc44202b76459145 Mon Sep 17 00:00:00 2001 From: nobohan Date: Tue, 23 Nov 2021 15:03:26 +0100 Subject: [PATCH 06/27] 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 07/27] 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 08/27] 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 09/27] 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 10/27] 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 11/27] 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 12/27] 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 13/27] 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 14/27] 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 15/27] 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 16/27] 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 17/27] 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 18/27] 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 19/27] 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 20/27] 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 21/27] 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 22/27] 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 23/27] 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 24/27] 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 e4e1edff68446a96f1a9032bd8c9f3a5011c23c3 Mon Sep 17 00:00:00 2001 From: LenaertsJ Date: Mon, 29 Nov 2021 11:01:51 +0000 Subject: [PATCH 25/27] 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 26/27] 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 27/27] 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'}) + } + }); }, }, }