diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c166301..c24f76df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,20 @@ and this project adheres to ## Unreleased +* vuejs: translate in French all multiselect widgets +* [address] define address lines according postal standards for France and Belgium (default) and change AddressRender, chill_entity_render_box and AddressRenderBox.vue +* [household] change translations (champs-libres/departement-de-la-vendee/accent-suivi-developpement#109) +* [household] add address i18n in household component (champs-libres/departement-de-la-vendee/accent-suivi-developpement#158) +* [household] add on the fly i18n in household component +* [household] redirect to the household page when a household is created from a person (champs-libres/departement-de-la-vendee/accent-suivi-developpement#175) +* [household] household member editor: display alert if some members have already an household (champs-libres/departement-de-la-vendee/accent-suivi-developpement#172) +* [household] household member editor: do not add in new members if the member is included in the members of household (champs-libres/departement-de-la-vendee/accent-suivi-developpement#123) +* [household] household member editor: remove markNoAddress button (champs-libres/departement-de-la-vendee/accent-suivi-developpement#109) +* [person]: ordering fields in add person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/61) +* [person]: Add email and alt names in add person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/61) +* [accompanyingCourse] Add a delete action and delete buttons to delete a accompanying course when step = DRAFT (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/64) +* [accompanyingCourse] Add a administrative location in the accompanying course, set the user current location as default, allow to select a location in a select field and do not allow to confirm the accompanying course if location is empty. +* [accompanyingCourse] Add the administrative location in the available variables for document generation * AddAddress: optimize loading: wait for the user finish typing; * UserPicker: fix bug with deprecated role * docgen: add base context + tests @@ -25,6 +39,7 @@ and this project adheres to * Household: show date validFrom and validTo when moving * address reference: add index for refid * [accompanyingCourse_work] fix styles conflicts + fix bug with remove goal (remove goals one at a time) +* [accompanyingCourse] improve masonry on resume page, add origin ## Test releases diff --git a/composer.json b/composer.json index a6f059112..4a93a9f41 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,6 @@ "doctrine/orm": "^2.7", "erusev/parsedown": "^1.7", "graylog2/gelf-php": "^1.5", - "knplabs/knp-menu": "^3.1", "knplabs/knp-menu-bundle": "^3.0", "knplabs/knp-time-bundle": "^1.12", "league/csv": "^9.7.1", @@ -32,6 +31,7 @@ "symfony/css-selector": "^4.4", "symfony/expression-language": "^4.4", "symfony/form": "^4.4", + "symfony/framework-bundle": "^4.4", "symfony/intl": "^4.4", "symfony/mime": "^4.4", "symfony/monolog-bundle": "^3.5", @@ -47,14 +47,14 @@ "symfony/yaml": "^4.4", "twig/extra-bundle": "^3.0", "twig/intl-extra": "^3.0", - "twig/markdown-extra": "^3.3", - "twig/twig": "^3.0" + "twig/markdown-extra": "^3.3" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "^3.3", "drupol/php-conventions": "^5", "fakerphp/faker": "^1.13", "nelmio/alice": "^3.8", + "phpspec/prophecy-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": ">= 7.5", "symfony/debug-bundle": "^5.1", diff --git a/phpstan-deprecations.neon b/phpstan-deprecations.neon index 24deda05f..bc372e96b 100644 --- a/phpstan-deprecations.neon +++ b/phpstan-deprecations.neon @@ -859,11 +859,6 @@ parameters: count: 1 path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverEvent.php - - - message: "#^Call to deprecated method setTimeout\\(\\) of class Redis\\.$#" - count: 1 - path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverLocker.php - - message: """ diff --git a/phpstan-types.neon b/phpstan-types.neon index 98d17b666..949ff774a 100644 --- a/phpstan-types.neon +++ b/phpstan-types.neon @@ -315,11 +315,6 @@ parameters: count: 1 path: src/Bundle/ChillMainBundle/Security/PasswordRecover/TokenManager.php - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 3 - path: src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 1 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 208c915ae..89cb80635 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -6,7 +6,6 @@ backupGlobals="false" colors="true" bootstrap="tests/app/tests/bootstrap.php" - stopOnFailure="true" > diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/ConcernedGroups.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/ConcernedGroups.vue index 49b53c8cb..f5057eb25 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/ConcernedGroups.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/ConcernedGroups.vue @@ -12,7 +12,7 @@
    -
  • +
  • {{ p.text }}
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 50eb14799..b8249ccc5 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -15,13 +15,15 @@ :searchable="true" :placeholder="$t('activity.choose_location')" :custom-label="customLabel" + :select-label="$t('multiselect.select_label')" + :deselect-label="$t('multiselect.deselect_label')" + :selected-label="$t('multiselect.selected_label')" :options="availableLocations" group-values="locations" group-label="locationGroup" v-model="location" > -
@@ -32,7 +34,6 @@ import { mapState, mapGetters } from "vuex"; import VueMultiselect from "vue-multiselect"; import NewLocation from "./Location/NewLocation.vue"; -import { getLocations, getLocationTypeByDefaultFor, getUserCurrentLocation } from "../api.js"; export default { name: "Location", diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/SocialIssuesAcc.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/SocialIssuesAcc.vue index 92aa7be0e..e585200c0 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/SocialIssuesAcc.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/SocialIssuesAcc.vue @@ -9,9 +9,9 @@ @@ -21,18 +21,18 @@ label="text" track-by="id" open-direction="bottom" - v-bind:close-on-select="true" - v-bind:preserve-search="false" - v-bind:reset-after="true" - v-bind:hide-selected="true" - v-bind:taggable="false" - v-bind:multiple="false" - v-bind:searchable="true" - v-bind:allow-empty="true" - v-bind:show-labels="false" - v-bind:loading="issueIsLoading" - v-bind:placeholder="$t('activity.choose_other_social_issue')" - v-bind:options="socialIssuesOther" + :close-on-select="true" + :preserve-search="false" + :reset-after="true" + :hide-selected="true" + :taggable="false" + :multiple="false" + :searchable="true" + :allow-empty="true" + :show-labels="false" + :loading="issueIsLoading" + :placeholder="$t('activity.choose_other_social_issue')" + :options="socialIssuesOther" @select="addIssueInList"> @@ -58,9 +58,9 @@ diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/i18n.js b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/i18n.js index fa7da3203..509e3b0d9 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/i18n.js +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/i18n.js @@ -1,4 +1,5 @@ import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n' +import { multiSelectMessages } from 'ChillMainAssets/vuejs/_js/i18n' const activityMessages = { fr: { @@ -33,12 +34,11 @@ const activityMessages = { }, create_address: 'Créer une adresse', edit_address: "Modifier l'adresse" - } } } -Object.assign(activityMessages.fr, personMessages.fr); +Object.assign(activityMessages.fr, personMessages.fr, multiSelectMessages.fr); export { activityMessages diff --git a/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityReasonCategoryControllerTest.php b/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityReasonCategoryControllerTest.php index a6277608f..0d64d0b65 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityReasonCategoryControllerTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityReasonCategoryControllerTest.php @@ -66,7 +66,7 @@ final class ActivityReasonCategoryControllerTest extends WebTestCase $crawler = $client->followRedirect(); // Check the entity has been delete on the list - $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent()); } */ diff --git a/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityReasonControllerTest.php b/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityReasonControllerTest.php index a2ae8c13f..481098eb0 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityReasonControllerTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityReasonControllerTest.php @@ -66,7 +66,7 @@ final class ActivityReasonControllerTest extends WebTestCase $crawler = $client->followRedirect(); // Check the entity has been delete on the list - $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent()); } */ diff --git a/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityTypeControllerTest.php b/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityTypeControllerTest.php index a07f03770..7ccf26cf1 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityTypeControllerTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Controller/ActivityTypeControllerTest.php @@ -66,7 +66,7 @@ final class ActivityTypeControllerTest extends WebTestCase $crawler = $client->followRedirect(); // Check the entity has been delete on the list - $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent()); } */ diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue index da9a9eaf8..494fde96c 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue @@ -13,6 +13,9 @@ :close-on-select="false" :allow-empty="true" :model-value="value" + :select-label="$t('multiselect.select_label')" + :deselect-label="$t('multiselect.deselect_label')" + :selected-label="$t('multiselect.selected_label')" @select="selectUsers" @remove="unSelectUsers" @close="coloriseSelectedValues" diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/js/i18n.js b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/js/i18n.js index f1aae5df1..2f7d15662 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/js/i18n.js +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/js/i18n.js @@ -1,13 +1,17 @@ +import { multiSelectMessages } from 'ChillMainAssets/vuejs/_js/i18n' + const calendarUserSelectorMessages = { - fr: { - choose_your_calendar_user: "Afficher les plages de disponibilités", - select_user: "Sélectionnez des calendriers", - show_my_calendar: "Afficher mon calendrier", - show_weekends: "Afficher les week-ends" - } - }; + fr: { + choose_your_calendar_user: "Afficher les plages de disponibilités", + select_user: "Sélectionnez des calendriers", + show_my_calendar: "Afficher mon calendrier", + show_weekends: "Afficher les week-ends" + } +}; - export { - calendarUserSelectorMessages - }; +Object.assign(calendarUserSelectorMessages.fr, multiSelectMessages.fr); + +export { + calendarUserSelectorMessages +}; \ No newline at end of file diff --git a/src/Bundle/ChillCustomFieldsBundle/Tests/Config/ConfigCustomizablesEntitiesTest.php b/src/Bundle/ChillCustomFieldsBundle/Tests/Config/ConfigCustomizablesEntitiesTest.php index 6a2809071..cb8136806 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Tests/Config/ConfigCustomizablesEntitiesTest.php +++ b/src/Bundle/ChillCustomFieldsBundle/Tests/Config/ConfigCustomizablesEntitiesTest.php @@ -33,11 +33,11 @@ final class ConfigCustomizablesEntitiesTest extends KernelTestCase $customizableEntities = self::$kernel->getContainer() ->getParameter('chill_custom_fields.customizables_entities'); - $this->assertInternalType('array', $customizableEntities); + $this->assertIsArray($customizableEntities); $this->assertCount(2, $customizableEntities); foreach ($customizableEntities as $key => $config) { - $this->assertInternalType('array', $config); + $this->assertIsArray($config); $this->assertArrayHasKey('name', $config); $this->assertArrayHasKey('class', $config); } @@ -56,7 +56,7 @@ final class ConfigCustomizablesEntitiesTest extends KernelTestCase $customizableEntities = self::$kernel->getContainer() ->getParameter('chill_custom_fields.customizables_entities'); - $this->assertInternalType('array', $customizableEntities); + $this->assertIsArray($customizableEntities); $this->assertCount(1, $customizableEntities); } } diff --git a/src/Bundle/ChillCustomFieldsBundle/Tests/Controller/CustomFieldControllerTest_TODO.php b/src/Bundle/ChillCustomFieldsBundle/Tests/Controller/CustomFieldControllerTest_TODO.php index 32513a837..b5e8ab438 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Tests/Controller/CustomFieldControllerTest_TODO.php +++ b/src/Bundle/ChillCustomFieldsBundle/Tests/Controller/CustomFieldControllerTest_TODO.php @@ -61,7 +61,7 @@ final class CustomFieldControllerTest_TODO extends WebTestCase $crawler = $client->followRedirect(); // Check the entity has been delete on the list - $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent()); } */ diff --git a/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php b/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php index 66d475d89..2aff33af0 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php +++ b/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php @@ -276,7 +276,6 @@ final class DocGeneratorTemplateController extends AbstractController fwrite($templateResource, $dataDecrypted); rewind($templateResource); } - $datas = $context->getData($template, $entity, $contextGenerationData); try { diff --git a/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php index 7101e562d..7a3e4ac69 100644 --- a/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php +++ b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php @@ -46,7 +46,6 @@ class RelatorioDriver implements DriverInterface 'template' => new DataPart($template, $templateName ?? uniqid('template_'), $resourceType), ]; $form = new FormDataPart($formFields); - dump(json_encode($data)); try { $response = $this->relatorioClient->request('POST', $this->url, [ diff --git a/src/Bundle/ChillDocGeneratorBundle/Resources/views/Admin/DocGeneratorTemplate/index.html.twig b/src/Bundle/ChillDocGeneratorBundle/Resources/views/Admin/DocGeneratorTemplate/index.html.twig index 29050ce02..436f816c2 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Resources/views/Admin/DocGeneratorTemplate/index.html.twig +++ b/src/Bundle/ChillDocGeneratorBundle/Resources/views/Admin/DocGeneratorTemplate/index.html.twig @@ -23,11 +23,7 @@ - + diff --git a/src/Bundle/ChillDocGeneratorBundle/Serializer/Helper/NormalizeNullValueHelper.php b/src/Bundle/ChillDocGeneratorBundle/Serializer/Helper/NormalizeNullValueHelper.php index 91e8e6170..e70171234 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Serializer/Helper/NormalizeNullValueHelper.php +++ b/src/Bundle/ChillDocGeneratorBundle/Serializer/Helper/NormalizeNullValueHelper.php @@ -11,9 +11,11 @@ declare(strict_types=1); namespace Chill\DocGeneratorBundle\Serializer\Helper; +use Symfony\Component\Serializer\Mapping\ClassMetadata; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use function array_merge; +use function is_array; class NormalizeNullValueHelper { @@ -30,7 +32,7 @@ class NormalizeNullValueHelper $this->discriminatorValue = $discriminatorValue; } - public function normalize(array $attributes, string $format = 'docgen', ?array $context = []) + public function normalize(array $attributes, string $format = 'docgen', ?array $context = [], ?ClassMetadata $classMetadata = null) { $data = []; $data['isNull'] = true; @@ -58,7 +60,7 @@ class NormalizeNullValueHelper default: $data[$key] = $this->normalizer->normalize(null, $format, array_merge( - $context, + $this->getContextForAttribute($key, $context, $classMetadata), ['docgen:expects' => $class] )); @@ -69,4 +71,25 @@ class NormalizeNullValueHelper return $data; } + + private function getContextForAttribute(string $key, array $initialContext, ?ClassMetadata $classMetadata): array + { + if (null === $classMetadata) { + return $initialContext; + } + + $attributeMetadata = $classMetadata->getAttributesMetadata()[$key] ?? null; + + if (null !== $attributeMetadata) { + /** @var \Symfony\Component\Serializer\Mapping\AttributeMetadata $attributeMetadata */ + $initialContext = array_merge( + $initialContext, + $attributeMetadata->getNormalizationContextForGroups( + is_array($initialContext['groups']) ? $initialContext['groups'] : [$initialContext['groups']] + ) + ); + } + + return $initialContext; + } } diff --git a/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php b/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php index fc4b4beb9..0feea511d 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php +++ b/src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php @@ -196,7 +196,7 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte $normalizer = new NormalizeNullValueHelper($this->normalizer, $typeKey, $typeValue); - return $normalizer->normalize($keys, $format, $context); + return $normalizer->normalize($keys, $format, $context, $metadata); } /** @@ -260,9 +260,13 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte /** @var AttributeMetadata $attribute */ $value = $this->propertyAccess->getValue($object, $attribute->getName()); $key = $attribute->getSerializedName() ?? $attribute->getName(); - $isTranslatable = $attribute->getNormalizationContextForGroups( - is_array($context['groups']) ? $context['groups'] : [$context['groups']] - )['is-translatable'] ?? false; + $objectContext = array_merge( + $context, + $attribute->getNormalizationContextForGroups( + is_array($context['groups']) ? $context['groups'] : [$context['groups']] + ) + ); + $isTranslatable = $objectContext['is-translatable'] ?? false; if ($isTranslatable) { $data[$key] = $this->translatableStringHelper @@ -273,7 +277,7 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte foreach ($value as $k => $v) { $arr[$k] = $this->normalizer->normalize($v, $format, array_merge( - $context, + $objectContext, $attribute->getNormalizationContextForGroups($expectedGroups) )); } @@ -281,11 +285,11 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte } elseif (is_object($value)) { $data[$key] = $this->normalizer->normalize($value, $format, array_merge( - $context, + $objectContext, $attribute->getNormalizationContextForGroups($expectedGroups) )); } elseif (null === $value) { - $data[$key] = $this->normalizeNullOutputValue($format, $context, $attribute, $reflection); + $data[$key] = $this->normalizeNullOutputValue($format, $objectContext, $attribute, $reflection); } else { $data[$key] = $value; } diff --git a/src/Bundle/ChillDocGeneratorBundle/tests/Serializer/Normalizer/DocGenObjectNormalizerTest.php b/src/Bundle/ChillDocGeneratorBundle/tests/Serializer/Normalizer/DocGenObjectNormalizerTest.php index ef7055048..178e9b183 100644 --- a/src/Bundle/ChillDocGeneratorBundle/tests/Serializer/Normalizer/DocGenObjectNormalizerTest.php +++ b/src/Bundle/ChillDocGeneratorBundle/tests/Serializer/Normalizer/DocGenObjectNormalizerTest.php @@ -14,6 +14,7 @@ namespace Chill\DocGeneratorBundle\tests\Serializer\Normalizer; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\Serializer\Annotation as Serializer; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -33,6 +34,49 @@ final class DocGenObjectNormalizerTest extends KernelTestCase $this->normalizer = self::$container->get(NormalizerInterface::class); } + public function testChangeContextOnAttribute() + { + $object = new TestableParentClass(); + $actual = $this->normalizer->normalize( + $object, + 'docgen', + ['groups' => 'docgen:read'] + ); + + $this->assertIsArray($actual); + $this->assertArrayHasKey('child', $actual); + $this->assertIsArray($actual['child']); + $this->assertArrayHasKey('foo', $actual['child']); + $this->assertEquals('bar', $actual['child']['foo']); + $this->assertArrayNotHasKey('baz', $actual['child']); + + // test with child = null + $object->child = null; + $actual = $this->normalizer->normalize( + $object, + 'docgen', + ['groups' => 'docgen:read'] + ); + $this->assertIsArray($actual); + $this->assertArrayHasKey('child', $actual); + $this->assertIsArray($actual['child']); + $this->assertArrayHasKey('foo', $actual['child']); + $this->assertEquals('', $actual['child']['foo']); + $this->assertArrayNotHasKey('baz', $actual['child']); + + $actual = $this->normalizer->normalize( + null, + 'docgen', + ['groups' => 'docgen:read', 'docgen:expects' => TestableParentClass::class], + ); + $this->assertIsArray($actual); + $this->assertArrayHasKey('child', $actual); + $this->assertIsArray($actual['child']); + $this->assertArrayHasKey('foo', $actual['child']); + $this->assertEquals('', $actual['child']['foo']); + $this->assertArrayNotHasKey('baz', $actual['child']); + } + public function testNormalizationBasic() { $scope = new Scope(); @@ -99,3 +143,30 @@ final class DocGenObjectNormalizerTest extends KernelTestCase $this->assertEquals($expected, $normalized, 'test normalization fo an user with null center'); } } + +class TestableParentClass +{ + /** + * @Serializer\Groups("docgen:read") + * @Serializer\Context(normalizationContext={"groups": "docgen:read:foo"}, groups={"docgen:read"}) + */ + public ?TestableChildClass $child; + + public function __construct() + { + $this->child = new TestableChildClass(); + } +} + +class TestableChildClass +{ + /** + * @Serializer\Groups("docgen:read") + */ + public string $baz = 'bloup'; + + /** + * @Serializer\Groups("docgen:read:foo") + */ + public string $foo = 'bar'; +} diff --git a/src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/BaseContextDataTest.php b/src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/BaseContextDataTest.php index e5a1c6d5f..feb9afd35 100644 --- a/src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/BaseContextDataTest.php +++ b/src/Bundle/ChillDocGeneratorBundle/tests/Service/Context/BaseContextDataTest.php @@ -13,6 +13,7 @@ namespace Chill\DocGeneratorBundle\tests\Service\Context; use Chill\DocGeneratorBundle\Service\Context\BaseContextData; use Chill\MainBundle\Entity\User; +use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Security\Core\Security; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -23,6 +24,8 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface; */ final class BaseContextDataTest extends KernelTestCase { + use ProphecyTrait; + protected function setUp(): void { parent::setUp(); diff --git a/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml b/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml index af08cf1c8..214428bd4 100644 --- a/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillDocGeneratorBundle/translations/messages.fr.yml @@ -7,6 +7,7 @@ docgen: Context: Contexte New template: Nouveau gabarit Edit template: Modifier gabarit + test generate: Tester la génération With context: 'Avec le contexte :' diff --git a/src/Bundle/ChillEventBundle/Tests/Controller/EventControllerTest.php b/src/Bundle/ChillEventBundle/Tests/Controller/EventControllerTest.php index 8966a45a1..21c5c89d9 100644 --- a/src/Bundle/ChillEventBundle/Tests/Controller/EventControllerTest.php +++ b/src/Bundle/ChillEventBundle/Tests/Controller/EventControllerTest.php @@ -66,7 +66,7 @@ final class EventControllerTest extends WebTestCase $crawler = $client->followRedirect(); // Check the entity has been delete on the list - $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent()); } */ diff --git a/src/Bundle/ChillEventBundle/Tests/Controller/EventTypeControllerTest.php b/src/Bundle/ChillEventBundle/Tests/Controller/EventTypeControllerTest.php index 4b69b9849..e2a8170d4 100644 --- a/src/Bundle/ChillEventBundle/Tests/Controller/EventTypeControllerTest.php +++ b/src/Bundle/ChillEventBundle/Tests/Controller/EventTypeControllerTest.php @@ -66,7 +66,7 @@ final class EventTypeControllerTest extends WebTestCase $crawler = $client->followRedirect(); // Check the entity has been delete on the list - $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent()); } */ diff --git a/src/Bundle/ChillEventBundle/Tests/Controller/RoleControllerTest.php b/src/Bundle/ChillEventBundle/Tests/Controller/RoleControllerTest.php index d24b10a40..d720f7e7a 100644 --- a/src/Bundle/ChillEventBundle/Tests/Controller/RoleControllerTest.php +++ b/src/Bundle/ChillEventBundle/Tests/Controller/RoleControllerTest.php @@ -66,7 +66,7 @@ final class RoleControllerTest extends WebTestCase $crawler = $client->followRedirect(); // Check the entity has been delete on the list - $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent()); } */ diff --git a/src/Bundle/ChillEventBundle/Tests/Controller/StatusControllerTest.php b/src/Bundle/ChillEventBundle/Tests/Controller/StatusControllerTest.php index d07909032..28bbab8e3 100644 --- a/src/Bundle/ChillEventBundle/Tests/Controller/StatusControllerTest.php +++ b/src/Bundle/ChillEventBundle/Tests/Controller/StatusControllerTest.php @@ -66,7 +66,7 @@ final class StatusControllerTest extends WebTestCase $crawler = $client->followRedirect(); // Check the entity has been delete on the list - $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent()); } */ diff --git a/src/Bundle/ChillMainBundle/Entity/Address.php b/src/Bundle/ChillMainBundle/Entity/Address.php index dcf0c5a96..070d40468 100644 --- a/src/Bundle/ChillMainBundle/Entity/Address.php +++ b/src/Bundle/ChillMainBundle/Entity/Address.php @@ -60,6 +60,8 @@ class Address /** * @var string|null * + * used for the CEDEX information + * * @ORM\Column(type="string", length=255, nullable=true) * @Groups({"write"}) */ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue index c333961db..6d9c32524 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue @@ -6,8 +6,9 @@ v-model="value" :placeholder="$t('select_address')" :tag-placeholder="$t('create_address')" - :select-label="$t('press_enter_to_select')" + :select-label="$t('multiselect.select_label')" :deselect-label="$t('create_address')" + :selected-label="$t('multiselect.selected_label')" @search-change="listenInputSearch" ref="addressSelector" @select="selectAddress" diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue index abd1cd8de..5191b3a31 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue @@ -12,8 +12,9 @@ label="value" :custom-label="transName" :placeholder="$t('select_city')" - :select-label="$t('press_enter_to_select')" + :select-label="$t('multiselect.select_label')" :deselect-label="$t('create_postal_code')" + :selected-label="$t('multiselect.selected_label')" :taggable="true" :multiple="false" @tag="addPostcode" diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue index c15db8d2a..c1b1d1b55 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue @@ -5,12 +5,13 @@ id="countrySelect" label="name" track-by="id" - v-bind:custom-label="transName" - v-bind:placeholder="$t('select_country')" - v-bind:options="sortedCountries" + :custom-label="transName" + :placeholder="$t('select_country')" + :options="sortedCountries" v-model="value" - :select-label="$t('press_enter_to_select')" - :deselect-label="$t('press_enter_to_remove')" + :select-label="$t('multiselect.select_label')" + :deselect-label="$t('multiselect.deselect_label')" + :selected-label="$t('multiselect.selected_label')" @select="selectCountry"> diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/SuggestPane.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/SuggestPane.vue index 15c77e414..798835a76 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/SuggestPane.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/SuggestPane.vue @@ -10,7 +10,7 @@

{{ $t('address_suggestions') }}

-
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/i18n.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/i18n.js index ab0032de6..98f31cbd3 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/i18n.js +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/i18n.js @@ -1,7 +1,7 @@ +import { multiSelectMessages } from 'ChillMainAssets/vuejs/_js/i18n' + const addressMessages = { fr: { - press_enter_to_select: 'Appuyer sur Entrée pour sélectionner', - press_enter_to_remove: 'Appuyer sur Entrée pour désélectionner', add_an_address_title: 'Créer une adresse', edit_an_address_title: 'Modifier une adresse', create_a_new_address: 'Créer une nouvelle adresse', @@ -48,6 +48,8 @@ const addressMessages = { } }; +Object.assign(addressMessages.fr, multiSelectMessages.fr); + export { addressMessages }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/AddressRenderBox.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/AddressRenderBox.vue index 475558d0c..8a069298a 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/AddressRenderBox.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/AddressRenderBox.vue @@ -2,21 +2,28 @@ -

- {{ address.text }}, -

-

- {{ address.postcode.code }} {{ address.postcode.name }} -

-

- {{ address.country.name.fr }} -

+
+

+ {{ l }} +

+
+
+

+ {{ address.text }} +

+

+ {{ address.postcode.code }} {{ address.postcode.name }} +

+

+ {{ address.country.name.fr }} +

+
-
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.js index 4b998f98a..1ab8eb29a 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.js +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.js @@ -84,3 +84,17 @@ const _createI18n = (appMessages) => { }; export { _createI18n } + +export const multiSelectMessages = { + fr: { + multiselect: { + placeholder: 'Choisir', + tag_placeholder: 'Créer un nouvel élément', + select_label: 'Appuyer sur "Entrée" pour sélectionner', + deselect_label: 'Appuyer sur "Entrée" pour désélectionner', + select_group_label: 'Appuyer sur "Entrée" pour sélectionner ce groupe', + deselect_group_label: 'Appuyer sur "Entrée" pour désélectionner ce groupe', + selected_label: 'Sélectionné' + } + } +}; \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/views/Entity/address.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Entity/address.html.twig index 4d871404f..ea8f18fc0 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Entity/address.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Entity/address.html.twig @@ -9,57 +9,23 @@ * with_delimiter bool add a delimiter between fragments * has_no_address bool * multiline bool multiline display - * extended_infos bool add extra informations (step, floor, etc.) + * extended_infos bool add extra informations (step, floor, etc.) DEPRECATED #} -{% macro raw(address, options, streetLine) %} - - {% if address.street is not empty %} -

{{ streetLine }}

- {% endif %} - {% if options['extended_infos'] %} - {{ _self.extended(address, options) }} - {% endif %} - {% if address.postCode is not empty %} -

- {{ address.postCode.code }} - {{ address.postCode.name }} -

-

{{ address.postCode.country.name|localize_translatable_string }}

- {% endif %} +{% macro raw(lines) %} + {% for l in lines %} +

{{ l }}

+ {% endfor %} {% endmacro %} -{% macro extended(address, options) %} - {% if address.floor is not empty %} - {{ 'address more.floor'|trans }} {{ address.floor }} - {% endif %} - {% if address.corridor is not empty %} - {{ 'address more.corridor'|trans }} {{ address.corridor }} - {% endif %} - {% if address.steps is not empty %} - {{ 'address more.steps'|trans }} {{ address.steps }} - {% endif %} - {% if address.buildingName is not empty %} - {{ 'address more.buildingName'|trans }} {{ address.buildingName }} - {% endif %} - {% if address.flat is not empty %} - {{ 'address more.flat'|trans }} {{ address.flat }} - {% endif %} - {% if address.extra is not empty %} - {{ 'address more.extra'|trans }} {{ address.extra }} - {% endif %} - {% if address.distribution is not empty %} - {{ 'address more.distribution'|trans }} {{ address.distribution }} - {% endif %} -{% endmacro %} - -{% macro inline(address, options, streetLine) %} +{% macro inline(address, options, streetLine, lines) %} {% if options['has_no_address'] == true and address.isNoAddress == true %} {% if address.postCode is not empty %}

{{ address.postCode.code }} {{ address.postCode.name }} + {{ address.distribution }}

{{ address.postCode.country.name|localize_translatable_string }}

{% endif %} @@ -68,7 +34,7 @@ {% else %} - {{ _self.raw(address, options, streetLine) }} + {{ _self.raw(lines) }} {% endif %} {{ _self.validity(address, options) }} @@ -97,7 +63,7 @@ {% if options['with_picto'] %} {% endif %} - {{ _self.inline(address, options, streetLine) }} + {{ _self.inline(address, options, streetLine, lines) }} {%- endif -%} @@ -106,7 +72,7 @@ {% if options['with_picto'] %} {% endif %} - {{ _self.inline(address, options, streetLine) }} + {{ _self.inline(address, options, streetLine, lines) }} {%- endif -%} @@ -118,20 +84,21 @@

{{ address.postCode.code }} {{ address.postCode.name }} + {{ address.distribution }}

{{ address.postCode.country.name|localize_translatable_string }}

-
+
{% endif %}
{{ 'address.consider homeless'|trans }}
- + {% else %}
{% if options['with_picto'] %} {% endif %} - {{ _self.raw(address, options, streetLine) }} + {{ _self.raw(lines) }}
{% endif %} {{ _self.validity(address, options) }} diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php index f66eb4de6..998cb58bf 100644 --- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php @@ -13,6 +13,7 @@ namespace Chill\MainBundle\Serializer\Normalizer; use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper; use Chill\MainBundle\Entity\Address; +use Chill\MainBundle\Templating\Entity\AddressRender; use DateTimeInterface; use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; @@ -46,6 +47,13 @@ class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAw 'validTo' => DateTimeInterface::class, ]; + private AddressRender $addressRender; + + public function __construct(AddressRender $addressRender) + { + $this->addressRender = $addressRender; + } + /** * @param Address $address * @param null|string $format @@ -53,19 +61,9 @@ class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAw public function normalize($address, $format = null, array $context = []) { if ($address instanceof Address) { - $text = $address->isNoAddress() ? '' : $address->getStreet() . ', ' . $address->getStreetNumber(); - - if (null !== $address->getPostCode()->getCountry()->getCountryCode()) { - if ($address->getPostCode()->getCountry()->getCountryCode() === 'FR') { - $text = $address->isNoAddress() ? '' : $address->getStreetNumber() . ', ' . $address->getStreet(); - } else { - $text = $address->isNoAddress() ? '' : $address->getStreetNumber() . ', ' . $address->getStreet(); - } - } - $data = [ 'address_id' => $address->getId(), - 'text' => $text, + 'text' => $address->isNoAddress() ? null : $this->addressRender->renderStreetLine($address, []), 'street' => $address->getStreet(), 'streetNumber' => $address->getStreetNumber(), 'postcode' => [ @@ -85,6 +83,7 @@ class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAw 'buildingName' => $address->getBuildingName(), 'distribution' => $address->getDistribution(), 'extra' => $address->getExtra(), + 'lines' => $this->addressRender->renderLines($address), ]; if ('json' === $format) { diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php index 082491bd0..975a7c9f8 100644 --- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\MainBundle\Serializer\Normalizer; use Chill\MainBundle\Entity\Center; +use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\UserJob; @@ -55,16 +56,21 @@ class UserNormalizer implements ContextAwareNormalizerInterface, NormalizerAware $context, ['docgen:expects' => Center::class, 'groups' => 'docgen:read'] ); + $locationContext = array_merge( + $context, + ['docgen:expects' => Location::class, 'groups' => 'dogen:read'] + ); if (null === $user && 'docgen' === $format) { return array_merge(self::NULL_USER, [ 'user_job' => $this->normalizer->normalize(null, $format, $userJobContext), 'main_center' => $this->normalizer->normalize(null, $format, $centerContext), 'main_scope' => $this->normalizer->normalize(null, $format, $scopeContext), + 'current_location' => $this->normalizer->normalize(null, $format, $locationContext), ]); } - return [ + $data = [ 'type' => 'user', 'id' => $user->getId(), 'username' => $user->getUsername(), @@ -75,6 +81,12 @@ class UserNormalizer implements ContextAwareNormalizerInterface, NormalizerAware 'main_center' => $this->normalizer->normalize($user->getMainCenter(), $format, $centerContext), 'main_scope' => $this->normalizer->normalize($user->getMainScope(), $format, $scopeContext), ]; + + if ('docgen' === $format) { + $data['current_location'] = $this->normalizer->normalize($user->getCurrentLocation(), $format, $locationContext); + } + + return $data; } public function supportsNormalization($data, $format = null, array $context = []): bool diff --git a/src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php b/src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php index 1af5607c9..66d6e9e41 100644 --- a/src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php +++ b/src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\MainBundle\Templating\Entity; use Chill\MainBundle\Entity\Address; +use Chill\MainBundle\Templating\TranslatableStringHelper; use Symfony\Component\Templating\EngineInterface; use function array_merge; @@ -26,14 +27,18 @@ class AddressRender implements ChillEntityRenderInterface 'with_delimiter' => false, 'has_no_address' => false, 'multiline' => true, + /* deprecated */ 'extended_infos' => false, ]; private EngineInterface $templating; - public function __construct(EngineInterface $templating) + private TranslatableStringHelper $translatableStringHelper; + + public function __construct(EngineInterface $templating, TranslatableStringHelper $translatableStringHelper) { $this->templating = $templating; + $this->translatableStringHelper = $translatableStringHelper; } /** @@ -50,43 +55,49 @@ class AddressRender implements ChillEntityRenderInterface 'streetLine' => $this->renderStreetLine($addr), 'render' => $options['render'] ?? 'bloc', 'options' => $options, + 'lines' => $this->renderLines($addr), ]); } /** * @param Address addr * @param mixed $addr + * + * @return string[] */ - public function renderString($addr, array $options): string + public function renderLines($addr): array { $lines = []; - $lines[0] = $this->renderStreetLine($addr); - - if (!empty($addr->getPostcode())) { - $lines[1] = strtr('{postcode} {label}', [ - '{postcode}' => $addr->getPostcode()->getCode(), - '{label}' => $addr->getPostcode()->getName(), - ]); + if (null !== $addr->getPostCode()) { + if ($addr->getPostCode()->getCountry()->getCountryCode() === 'FR') { + $lines[] = $this->renderIntraBuildingLine($addr); + $lines[] = $this->renderBuildingLine($addr); + $lines[] = $this->renderStreetLine($addr); + $lines[] = $this->renderDeliveryLine($addr); + $lines[] = $this->renderCityLine($addr); + $lines[] = $this->renderCountryLine($addr); + } else { + $lines[] = $this->renderBuildingLine($addr); + $lines[] = $this->renderDeliveryLine($addr); + $lines[] = $this->renderStreetLine($addr); + $lines[] = $this->renderCityLine($addr); + $lines[] = $this->renderCountryLine($addr); + } } - return implode(' - ', $lines); + return array_values(array_filter($lines, static fn ($l) => null !== $l)); } - public function supports($entity, array $options): bool + public function renderStreetLine(Address $addr): ?string { - return $entity instanceof Address; - } - - private function renderStreetLine($addr): string - { - if (!empty($addr->getStreet())) { + if (null !== $addr->getStreet() && $addr->getStreet() !== '') { $street = $addr->getStreet(); } else { $street = ''; } - if (!empty($addr->getStreetNumber())) { + if (null !== $addr->getStreetNumber() && $addr->getStreetNumber() !== '') { $streetNumber = $addr->getStreetNumber(); } else { $streetNumber = ''; @@ -100,8 +111,111 @@ class AddressRender implements ChillEntityRenderInterface } } - if (',' === $res) { - $res = ''; + if ((',' === $res) || ('' === $res)) { + $res = null; + } + + return $res; + } + + /** + * @param Address addr + * @param mixed $addr + */ + public function renderString($addr, array $options): string + { + return implode(' - ', $this->renderLines($addr)); + } + + public function supports($entity, array $options): bool + { + return $entity instanceof Address; + } + + private function renderBuildingLine(Address $addr): ?string + { + if (null !== $addr->getBuildingName() && $addr->getBuildingName() !== '') { + $building = $addr->getBuildingName(); + } else { + $building = ''; + } + $intraBuilding = $this->renderIntraBuildingLine($addr); + + if (null === $intraBuilding) { + $intraBuilding = ''; + } + + $res = trim($building . ' - ' . $intraBuilding, ' - '); + + if ('' === $res) { + $res = null; + } + + if (null !== $addr->getPostCode()->getCountry()->getCountryCode()) { + if ($addr->getPostCode()->getCountry()->getCountryCode() === 'FR') { + $res = $addr->getBuildingName(); + } + } + + return $res; + } + + private function renderCityLine($addr): string + { + if (null !== $addr->getPostcode()) { + $res = strtr('{postcode} {label}', [ + '{postcode}' => $addr->getPostcode()->getCode(), + '{label}' => $addr->getPostcode()->getName(), + ]); + + if (null !== $addr->getPostCode()->getCountry()->getCountryCode()) { + if ($addr->getPostCode()->getCountry()->getCountryCode() === 'FR') { + if ($addr->getDistribution()) { + $res = $res . ' ' . $addr->getDistribution(); + } + } + } + } + + return $res ?? ''; + } + + private function renderCountryLine($addr): ?string + { + return $this->translatableStringHelper->localize( + $addr->getPostCode()->getCountry()->getName() + ); + } + + private function renderDeliveryLine($addr): ?string + { + return $addr->getExtra(); + } + + private function renderIntraBuildingLine($addr): ?string + { + $arr = []; + + if ($addr->getFlat()) { + $arr[] = 'appart ' . $addr->getFlat(); + } + + if ($addr->getFloor()) { + $arr[] = 'ét ' . $addr->getFloor(); + } + + if ($addr->getCorridor()) { + $arr[] = 'coul ' . $addr->getCorridor(); + } + + if ($addr->getSteps()) { + $arr[] = 'esc ' . $addr->getSteps(); + } + + $res = implode(' - ', $arr); + + if ('' === $res) { + $res = null; } return $res; diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php index 9563ed4bf..1a91d4629 100644 --- a/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php +++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php @@ -140,8 +140,7 @@ abstract class AbstractAggregatorTest extends KernelTestCase { $filter = $this->getAggregator(); - $this->assertInternalType( - 'string', + $this->assertIsString( $filter->applyOn(), 'test that the internal type of "applyOn" is a string' ); @@ -160,8 +159,7 @@ abstract class AbstractAggregatorTest extends KernelTestCase { $queryKeys = $this->getAggregator()->getQueryKeys($data); - $this->assertInternalType( - 'array', + $this->assertIsArray( $queryKeys, 'test that the query keys returned are an array' ); @@ -252,7 +250,7 @@ abstract class AbstractAggregatorTest extends KernelTestCase { $title = $this->getAggregator()->getTitle(); - $this->assertInternalType('string', $title); + $this->assertIsString($title); $this->assertNotEmpty( $title, 'test that the title is not empty' diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php index 7a888eb01..18a3f6c66 100644 --- a/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php +++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php @@ -151,8 +151,7 @@ abstract class AbstractExportTest extends WebTestCase { $export = $this->getExport(); - $this->assertInternalType( - 'string', + $this->assertIsString( $export->getDescription(), 'Assert that the `getDescription` method return a string' ); @@ -214,8 +213,7 @@ abstract class AbstractExportTest extends WebTestCase $results = $this->getExport()->getResult($query, $data); - $this->assertInternalType( - 'array', + $this->assertIsArray( $results, 'assert that the returned result is an array' ); @@ -271,8 +269,7 @@ abstract class AbstractExportTest extends WebTestCase { $export = $this->getExport(); - $this->assertInternalType( - 'string', + $this->assertIsString( $export->getType(), 'Assert that the `getType` method return a string' ); diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php index d0f4eaa97..4355687e1 100644 --- a/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php +++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php @@ -126,7 +126,7 @@ abstract class AbstractFilterTest extends KernelTestCase { $filter = $this->getFilter(); - $this->assertInternalType('string', $filter->applyOn()); + $this->assertIsString($filter->applyOn()); } /** @@ -149,8 +149,7 @@ abstract class AbstractFilterTest extends KernelTestCase 'test that the description is not empty' ); } elseif (is_array($description)) { - $this->assertInternalType( - 'string', + $this->assertIsString( $description[0], 'test that the first element in the description array is a string' ); @@ -191,7 +190,7 @@ abstract class AbstractFilterTest extends KernelTestCase { $title = $this->getFilter()->getTitle(); - $this->assertInternalType('string', $title); + $this->assertIsString($title); $this->assertNotEmpty( $title, 'test that the title is not empty' diff --git a/src/Bundle/ChillMainBundle/Tests/Controller/CenterControllerTest.php b/src/Bundle/ChillMainBundle/Tests/Controller/CenterControllerTest.php index 981bb3094..b5bb012a2 100644 --- a/src/Bundle/ChillMainBundle/Tests/Controller/CenterControllerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Controller/CenterControllerTest.php @@ -72,6 +72,6 @@ final class CenterControllerTest extends WebTestCase $crawler = $client->request('GET', '/fr/admin/center/'); // Check the entity has been delete on the list - $this->assertRegExp('/Foo/', $client->getResponse()->getContent()); + $this->assertMatchesRegularExpression('/Foo/', $client->getResponse()->getContent()); } } diff --git a/src/Bundle/ChillMainBundle/Tests/Controller/LoginControllerTest.php b/src/Bundle/ChillMainBundle/Tests/Controller/LoginControllerTest.php index bed40b372..a7c53a75d 100644 --- a/src/Bundle/ChillMainBundle/Tests/Controller/LoginControllerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Controller/LoginControllerTest.php @@ -40,7 +40,7 @@ final class LoginControllerTest extends WebTestCase $this->assertTrue($client->getResponse()->isRedirect()); //the response is not a login page, but on a new page - $this->assertNotRegExp('/\/login$/', $client->getResponse() + $this->assertDoesNotMatchRegularExpression('/\/login$/', $client->getResponse() ->headers ->get('location')); @@ -48,7 +48,7 @@ final class LoginControllerTest extends WebTestCase $client->followRedirects(true); $crawler = $client->request('GET', '/'); - $this->assertRegExp('/center a_social/', $client->getResponse() + $this->assertMatchesRegularExpression('/center a_social/', $client->getResponse() ->getContent()); $logoutLinkFilter = $crawler->filter('a:contains("Se déconnecter")'); @@ -63,7 +63,7 @@ final class LoginControllerTest extends WebTestCase $client->followRedirect(); //redirect to login page //check we are back on login page - $this->assertRegExp('/\/login$/', $client->getResponse() + $this->assertMatchesRegularExpression('/\/login$/', $client->getResponse() ->headers ->get('location')); } diff --git a/src/Bundle/ChillMainBundle/Tests/Controller/PermissionsGroupControllerTest.php b/src/Bundle/ChillMainBundle/Tests/Controller/PermissionsGroupControllerTest.php index f27eaa7a7..b7cb73cbf 100644 --- a/src/Bundle/ChillMainBundle/Tests/Controller/PermissionsGroupControllerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Controller/PermissionsGroupControllerTest.php @@ -66,7 +66,7 @@ final class PermissionsGroupControllerTest extends WebTestCase $crawler = $client->followRedirect(); // Check the entity has been delete on the list - $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent()); } */ diff --git a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php index c09385e50..94266077a 100644 --- a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php @@ -388,7 +388,7 @@ final class ExportManagerTest extends KernelTestCase public function testGetAggregatorNonExistant() { - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $exportManager = $this->createExportManager(); @@ -446,7 +446,7 @@ final class ExportManagerTest extends KernelTestCase public function testGetExportNonExistant() { - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $exportManager = $this->createExportManager(); @@ -485,7 +485,7 @@ final class ExportManagerTest extends KernelTestCase public function testGetFilterNonExistant() { - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $exportManager = $this->createExportManager(); @@ -646,7 +646,7 @@ final class ExportManagerTest extends KernelTestCase public function testNonExistingFormatter() { - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $exportManager = $this->createExportManager(); @@ -668,10 +668,10 @@ final class ExportManagerTest extends KernelTestCase ?UserInterface $user = null ): ExportManager { $localUser = $user ?? self::$container->get( - 'doctrine.orm.entity_manager' - ) - ->getRepository('ChillMainBundle:User') - ->findOneBy(['username' => 'center a_social']); + 'doctrine.orm.entity_manager' + ) + ->getRepository('ChillMainBundle:User') + ->findOneBy(['username' => 'center a_social']); $token = new UsernamePasswordToken($localUser, 'password', 'provider'); $tokenStorage = new TokenStorage(); $tokenStorage->setToken($token); diff --git a/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php b/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php index 294ddceb6..7208c3ee9 100644 --- a/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Security/PasswordRecover/TokenManagerTest.php @@ -16,6 +16,7 @@ use Chill\MainBundle\Security\PasswordRecover\TokenManager; use DateInterval; use DateTimeImmutable; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use UnexpectedValueException; /** * @internal @@ -47,7 +48,7 @@ final class TokenManagerTest extends KernelTestCase $tokens = $tokenManager->generate($user, $expiration); - $this->assertInternalType('array', $tokens); + $this->assertIsArray($tokens); $this->assertArrayHasKey('h', $tokens); $this->assertArrayHasKey('t', $tokens); $this->assertNotEmpty($tokens['h']); @@ -57,7 +58,7 @@ final class TokenManagerTest extends KernelTestCase public function testGenerateEmptyUsernameCanonical() { - $this->expectException(\UnexpectedValueException::class); + $this->expectException(UnexpectedValueException::class); $tokenManager = $this->tokenManager; // set a username, but not a username canonical diff --git a/src/Bundle/ChillMainBundle/Tests/Services/MenuComposerTest.php b/src/Bundle/ChillMainBundle/Tests/Services/MenuComposerTest.php index b7df8018e..8ed185aa9 100644 --- a/src/Bundle/ChillMainBundle/Tests/Services/MenuComposerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Services/MenuComposerTest.php @@ -48,6 +48,6 @@ final class MenuComposerTest extends KernelTestCase $routes = $this->menuComposer->getRoutesFor('dummy0'); - $this->assertInternalType('array', $routes); + $this->assertIsArray($routes); } } diff --git a/src/Bundle/ChillMainBundle/Tests/Templating/Entity/AddressRenderTest.php b/src/Bundle/ChillMainBundle/Tests/Templating/Entity/AddressRenderTest.php index e577028ca..fcd7b374a 100644 --- a/src/Bundle/ChillMainBundle/Tests/Templating/Entity/AddressRenderTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Templating/Entity/AddressRenderTest.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\Address; use Chill\MainBundle\Entity\Country; use Chill\MainBundle\Entity\PostalCode; use Chill\MainBundle\Templating\Entity\AddressRender; +use Chill\MainBundle\Templating\TranslatableStringHelper; use Iterator; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Templating\EngineInterface; @@ -30,11 +31,11 @@ final class AddressRenderTest extends KernelTestCase self::bootKernel(); } - public function addressDataProvider(): Iterator + public function addressDataProviderBEWithBuilding(): Iterator { $addr = new Address(); $country = (new Country()) - ->setName(['fr' => 'Pays']) + ->setName(['fr' => 'Belgium']) ->setCountryCode('BE'); $postCode = new PostalCode(); $postCode->setName('Locality') @@ -45,20 +46,279 @@ final class AddressRenderTest extends KernelTestCase ->setStreetNumber('5') ->setPostcode($postCode); - yield [$addr, 'Rue ABC, 5 - 012345 Locality']; + $addr->setBuildingName('Résidence "Les Bleuets"'); + + yield [$addr, 'Résidence "Les Bleuets" - Rue ABC, 5 - 012345 Locality - Belgium']; + } + + public function addressDataProviderBEWithSteps(): Iterator + { + $addr = new Address(); + $country = (new Country()) + ->setName(['fr' => 'Belgium']) + ->setCountryCode('BE'); + $postCode = new PostalCode(); + $postCode->setName('Locality') + ->setCode('012345') + ->setCountry($country); + + $addr->setStreet('Rue ABC') + ->setStreetNumber('5') + ->setPostcode($postCode); + + $addr->setSteps('4'); + + yield [$addr, 'esc 4 - Rue ABC, 5 - 012345 Locality - Belgium']; + } + + public function addressDataProviderFRWithBuilding(): Iterator + { + $addr = new Address(); + $country = (new Country()) + ->setName(['fr' => 'France']) + ->setCountryCode('FR'); + $postCode = new PostalCode(); + $postCode->setName('Locality') + ->setCode('012345') + ->setCountry($country); + + $addr->setStreet('Rue ABC') + ->setStreetNumber('5') + ->setPostcode($postCode); + + $addr->setBuildingName('Résidence "Les Bleuets"'); + + yield [$addr, 'Résidence "Les Bleuets" - 5, Rue ABC - 012345 Locality - France']; + } + + public function addressDataProviderFRWithSteps(): Iterator + { + $addr = new Address(); + $country = (new Country()) + ->setName(['fr' => 'France']) + ->setCountryCode('FR'); + $postCode = new PostalCode(); + $postCode->setName('Locality') + ->setCode('012345') + ->setCountry($country); + + $addr->setStreet('Rue ABC') + ->setStreetNumber('5') + ->setPostcode($postCode); + + $addr->setSteps('4'); + + yield [$addr, 'esc 4 - 5, Rue ABC - 012345 Locality - France']; + } + + public function complexAddressDataProviderBE(): Iterator + { + $addr = new Address(); + $country = (new Country()) + ->setName(['fr' => 'Belgium']) + ->setCountryCode('BE'); + $postCode = new PostalCode(); + $postCode->setName('Locality') + ->setCode('012345') + ->setCountry($country); + + $addr->setStreet('Rue ABC') + ->setStreetNumber('5') + ->setPostcode($postCode); + + $addr->setBuildingName('Résidence "Les Bleuets"'); + $addr->setFlat('1'); + $addr->setFloor('2'); + $addr->setCorridor('3'); + $addr->setSteps('4'); + + yield [$addr, 'Résidence "Les Bleuets" - appart 1 - ét 2 - coul 3 - esc 4 - Rue ABC, 5 - 012345 Locality - Belgium']; + } + + public function complexAddressDataProviderFR(): Iterator + { + $addr = new Address(); + $country = (new Country()) + ->setName(['fr' => 'France']) + ->setCountryCode('FR'); + $postCode = new PostalCode(); + $postCode->setName('Locality') + ->setCode('012345') + ->setCountry($country); + + $addr->setStreet('Rue ABC') + ->setStreetNumber('5') + ->setPostcode($postCode); + + $addr->setBuildingName('Résidence "Les Bleuets"'); + $addr->setFlat('1'); + $addr->setFloor('2'); + $addr->setCorridor('3'); + $addr->setSteps('4'); + $addr->setExtra('A droite de la porte'); + $addr->setDistribution('CEDEX'); + + yield [$addr, 'appart 1 - ét 2 - coul 3 - esc 4 - Résidence "Les Bleuets" - 5, Rue ABC - A droite de la porte - 012345 Locality CEDEX - France']; + } + + public function noFullAddressDataProviderBE(): Iterator + { + $addr = new Address(); + $country = (new Country()) + ->setName(['fr' => 'Belgium']) + ->setCountryCode('BE'); + $postCode = new PostalCode(); + $postCode->setName('Locality') + ->setCode('012345') + ->setCountry($country); + + $addr->setPostcode($postCode) + ->setIsNoAddress(true); + + yield [$addr, '012345 Locality - Belgium']; + } + + public function simpleAddressDataProviderBE(): Iterator + { + $addr = new Address(); + $country = (new Country()) + ->setName(['fr' => 'Belgium']) + ->setCountryCode('BE'); + $postCode = new PostalCode(); + $postCode->setName('Locality') + ->setCode('012345') + ->setCountry($country); + + $addr->setStreet('Rue ABC') + ->setStreetNumber('5') + ->setPostcode($postCode); + + yield [$addr, 'Rue ABC, 5 - 012345 Locality - Belgium']; + } + + public function simpleAddressDataProviderFR(): Iterator + { + $addr = new Address(); + $country = (new Country()) + ->setName(['fr' => 'France']) + ->setCountryCode('FR'); + $postCode = new PostalCode(); + $postCode->setName('Locality') + ->setCode('012345') + ->setCountry($country); + + $addr->setStreet('Rue ABC') + ->setStreetNumber('5') + ->setPostcode($postCode); + + yield [$addr, '5, Rue ABC - 012345 Locality - France']; } /** - * @dataProvider addressDataProvider + * @dataProvider complexAddressDataProviderBE */ - public function testRenderString(Address $addr, string $expectedString): void + public function testRenderComplexAddressBE(Address $addr, string $expectedString): void { $engine = self::$container->get(EngineInterface::class); - $renderer = new AddressRender($engine); + $translatableStringHelper = self::$container->get(TranslatableStringHelper::class); + $renderer = new AddressRender($engine, $translatableStringHelper); $this->assertEquals($expectedString, $renderer->renderString($addr, [])); + } - return; - $this->assertIsString($renderer->renderBox($addr, [])); + /** + * @dataProvider complexAddressDataProviderFR + */ + public function testRenderComplexAddressFR(Address $addr, string $expectedString): void + { + $engine = self::$container->get(EngineInterface::class); + $translatableStringHelper = self::$container->get(TranslatableStringHelper::class); + $renderer = new AddressRender($engine, $translatableStringHelper); + + $this->assertEquals($expectedString, $renderer->renderString($addr, [])); + } + + /** + * @dataProvider noFullAddressDataProviderBE + */ + public function testRenderNoFullAddressBE(Address $addr, string $expectedString): void + { + $engine = self::$container->get(EngineInterface::class); + $translatableStringHelper = self::$container->get(TranslatableStringHelper::class); + $renderer = new AddressRender($engine, $translatableStringHelper); + + $this->assertEquals($expectedString, $renderer->renderString($addr, [])); + } + + /** + * @dataProvider simpleAddressDataProviderBE + */ + public function testRenderStringSimpleAddressBE(Address $addr, string $expectedString): void + { + $engine = self::$container->get(EngineInterface::class); + $translatableStringHelper = self::$container->get(TranslatableStringHelper::class); + $renderer = new AddressRender($engine, $translatableStringHelper); + + $this->assertEquals($expectedString, $renderer->renderString($addr, [])); + } + + /** + * @dataProvider simpleAddressDataProviderFR + */ + public function testRenderStringSimpleAddressFR(Address $addr, string $expectedString): void + { + $engine = self::$container->get(EngineInterface::class); + $translatableStringHelper = self::$container->get(TranslatableStringHelper::class); + $renderer = new AddressRender($engine, $translatableStringHelper); + + $this->assertEquals($expectedString, $renderer->renderString($addr, [])); + } + + /** + * @dataProvider addressDataProviderBEWithBuilding + */ + public function testRenderWithBuildingAddressBE(Address $addr, string $expectedString): void + { + $engine = self::$container->get(EngineInterface::class); + $translatableStringHelper = self::$container->get(TranslatableStringHelper::class); + $renderer = new AddressRender($engine, $translatableStringHelper); + + $this->assertEquals($expectedString, $renderer->renderString($addr, [])); + } + + /** + * @dataProvider addressDataProviderFRWithBuilding + */ + public function testRenderWithBuildingAddressFR(Address $addr, string $expectedString): void + { + $engine = self::$container->get(EngineInterface::class); + $translatableStringHelper = self::$container->get(TranslatableStringHelper::class); + $renderer = new AddressRender($engine, $translatableStringHelper); + + $this->assertEquals($expectedString, $renderer->renderString($addr, [])); + } + + /** + * @dataProvider addressDataProviderBEWithSteps + */ + public function testRenderWithStepsAddressBE(Address $addr, string $expectedString): void + { + $engine = self::$container->get(EngineInterface::class); + $translatableStringHelper = self::$container->get(TranslatableStringHelper::class); + $renderer = new AddressRender($engine, $translatableStringHelper); + + $this->assertEquals($expectedString, $renderer->renderString($addr, [])); + } + + /** + * @dataProvider addressDataProviderFRWithSteps + */ + public function testRenderWithStepsAddressFR(Address $addr, string $expectedString): void + { + $engine = self::$container->get(EngineInterface::class); + $translatableStringHelper = self::$container->get(TranslatableStringHelper::class); + $renderer = new AddressRender($engine, $translatableStringHelper); + + $this->assertEquals($expectedString, $renderer->renderString($addr, [])); } } diff --git a/src/Bundle/ChillPersonBundle/Config/ConfigPersonAltNamesHelper.php b/src/Bundle/ChillPersonBundle/Config/ConfigPersonAltNamesHelper.php index ae291067b..7226e96c9 100644 --- a/src/Bundle/ChillPersonBundle/Config/ConfigPersonAltNamesHelper.php +++ b/src/Bundle/ChillPersonBundle/Config/ConfigPersonAltNamesHelper.php @@ -11,8 +11,6 @@ declare(strict_types=1); namespace Chill\PersonBundle\Config; -use function count; - /** * Give help to interact with the config for alt names. */ @@ -69,6 +67,6 @@ class ConfigPersonAltNamesHelper */ public function hasAltNames(): bool { - return count($this->config) > 0; + return [] !== $this->config; } } diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php index 5088b2381..9abd85f64 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php @@ -19,6 +19,7 @@ use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepos use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\HttpFoundation\Exception\BadRequestException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -110,6 +111,60 @@ class AccompanyingCourseController extends Controller ]); } + /** + * Delete page of Accompanying Course section. + * + * @Route("/{_locale}/parcours/{accompanying_period_id}/delete", name="chill_person_accompanying_course_delete") + * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"}) + */ + public function deleteAction(Request $request, AccompanyingPeriod $accompanyingCourse) + { + $em = $this->getDoctrine()->getManager(); + + $person_id = $request->query->get('person_id'); + + $form = $this->createFormBuilder() + ->setAction($this->generateUrl('chill_person_accompanying_course_delete', [ + 'accompanying_period_id' => $accompanyingCourse->getId(), + 'person_id' => $person_id, + ])) + ->setMethod('DELETE') + ->add('submit', SubmitType::class, ['label' => 'Delete']) + ->getForm(); + + if ($request->getMethod() === Request::METHOD_DELETE) { + $form->handleRequest($request); + + if ($form->isValid()) { + $em->remove($accompanyingCourse); + $em->flush(); + + $this->addFlash('success', $this->get('translator') + ->trans('The accompanying course has been successfully removed.')); + + if (null !== $person_id) { + return $this->redirectToRoute('chill_person_accompanying_period_list', [ + 'person_id' => $person_id, + ]); + } + + return $this->redirectToRoute('chill_main_homepage'); + } + } + + if (null !== $person_id) { + $view = '@ChillPerson/AccompanyingCourse/confirm_delete_person.html.twig'; + } else { + $view = '@ChillPerson/AccompanyingCourse/confirm_delete_accompanying_course.html.twig'; + } + + return $this->render($view, [ + 'accompanyingCourse' => $accompanyingCourse, + 'person_id' => $person_id, + 'delete_form' => $form->createView(), + ]); + } + /** * Edit page of Accompanying Course section. * @@ -208,6 +263,9 @@ class AccompanyingCourseController extends Controller } } + $userLocation = $this->getUser()->getCurrentLocation(); + $period->setAdministrativeLocation($userLocation); + $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::CREATE, $period); $em->persist($period); diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonApiController.php b/src/Bundle/ChillPersonBundle/Controller/PersonApiController.php index 28966c4e5..5be598426 100644 --- a/src/Bundle/ChillPersonBundle/Controller/PersonApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/PersonApiController.php @@ -14,23 +14,52 @@ namespace Chill\PersonBundle\Controller; use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\MainBundle\Entity\Address; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Security\Authorization\PersonVoter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; - -use function array_filter; -use function array_values; +use function in_array; class PersonApiController extends ApiController { private AuthorizationHelper $authorizationHelper; - public function __construct(AuthorizationHelper $authorizationHelper) - { + private ConfigPersonAltNamesHelper $configPersonAltNameHelper; + + public function __construct( + AuthorizationHelper $authorizationHelper, + ConfigPersonAltNamesHelper $configPersonAltNameHelper + ) { $this->authorizationHelper = $authorizationHelper; + $this->configPersonAltNameHelper = $configPersonAltNameHelper; + } + + /** + * @Route("/api/1.0/person/config/alt_names.{_format}", + * name="chill_person_config_alt_names", + * requirements={ + * "_format": "json" + * } + * ) + */ + public function configAltNames(Request $request, string $_format): Response + { + $configAltNamesChoices = $this->configPersonAltNameHelper->getChoices(); + + return $this->json( + array_map( + static fn (array $data, string $key): array => ['key' => $key, 'labels' => $data], + $configAltNamesChoices, + array_keys($configAltNamesChoices) + ), + Response::HTTP_OK, + [], + ['groups' => ['read']] + ); } public function personAddressApi($id, Request $request, string $_format): Response @@ -51,22 +80,51 @@ class PersonApiController extends ApiController { $this->denyAccessUnlessGranted(PersonVoter::SEE, $person); - $addresses = []; + $seenAddressIds = []; + // collect addresses from location in courses - foreach ($person->getAccompanyingPeriodParticipations() as $participation) { - if (null !== $participation->getAccompanyingPeriod()->getAddressLocation()) { - $a = $participation->getAccompanyingPeriod()->getAddressLocation(); - $addresses[$a->getId()] = $a; - } - } + $addresses = $person + ->getAccompanyingPeriodParticipations() + ->filter( + static function (AccompanyingPeriodParticipation $accompanyingPeriodParticipation): bool { + return null !== $accompanyingPeriodParticipation->getAccompanyingPeriod()->getAddressLocation(); + } + ) + ->map( + static function (AccompanyingPeriodParticipation $accompanyingPeriodParticipation): ?Address { + return $accompanyingPeriodParticipation->getAccompanyingPeriod()->getAddressLocation(); + } + ) + ->filter( + // We remove potential null addresses. + static fn (?Address $address): bool => null !== $address + ) + ->filter( + static function (Address $address) use (&$seenAddressIds): bool { + $id = $address->getId(); + + if (in_array($id, $seenAddressIds, true)) { + return false; + } + + $seenAddressIds[] = $id; + + return true; + } + ); // remove the actual address $actual = $person->getCurrentHouseholdAddress(); if (null !== $actual) { - $addresses = array_filter($addresses, static fn ($a) => $a !== $actual); + $addresses = $addresses->filter(static fn (Address $address): bool => $address !== $actual); } - return $this->json(array_values($addresses), Response::HTTP_OK, [], ['groups' => ['read']]); + return $this->json( + $addresses->getValues(), + Response::HTTP_OK, + [], + ['groups' => ['read']] + ); } } diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 9628da187..06e4f0c8b 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Entity\Address; use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Entity\HasCentersInterface; use Chill\MainBundle\Entity\HasScopesInterface; +use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; @@ -118,6 +119,13 @@ class AccompanyingPeriod implements */ private ?Address $addressLocation = null; + /** + * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Location") + * @Groups({"read", "write"}) + * @Assert\NotBlank(groups={AccompanyingPeriod::STEP_CONFIRMED}) + */ + private ?Location $administrativeLocation = null; + /** * @var DateTime * @@ -507,6 +515,11 @@ class AccompanyingPeriod implements return $this->addressLocation; } + public function getAdministrativeLocation(): ?Location + { + return $this->administrativeLocation; + } + /** * Get a list of person which have an adresse available for a valid location. * @@ -980,6 +993,13 @@ class AccompanyingPeriod implements return $this; } + public function setAdministrativeLocation(?Location $administrativeLocation): AccompanyingPeriod + { + $this->administrativeLocation = $administrativeLocation; + + return $this; + } + /** * Set closingDate. * diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php index 0c5517b71..4dc3d958c 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php @@ -39,32 +39,32 @@ class AccompanyingPeriodWorkGoal /** * @ORM\ManyToOne(targetEntity=Goal::class) * @Serializer\Groups({"accompanying_period_work:edit"}) - * @Serializer\Groups({"read"}) + * @Serializer\Groups({"read", "docgen:read"}) */ - private $goal; + private ?Goal $goal = null; /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") - * @Serializer\Groups({"read"}) + * @Serializer\Groups({"read", "docgen:read"}) */ - private $id; + private ?int $id = null; /** * @ORM\Column(type="text") * @Serializer\Groups({"accompanying_period_work:edit"}) * @Serializer\Groups({"read"}) */ - private $note; + private ?string $note = null; /** * @ORM\ManyToMany(targetEntity=Result::class, inversedBy="accompanyingPeriodWorkGoals") * @ORM\JoinTable(name="chill_person_accompanying_period_work_goal_result") * @Serializer\Groups({"accompanying_period_work:edit"}) - * @Serializer\Groups({"read"}) + * @Serializer\Groups({"read", "docgen:read"}) */ - private $results; + private Collection $results; public function __construct() { diff --git a/src/Bundle/ChillPersonBundle/Entity/PersonAltName.php b/src/Bundle/ChillPersonBundle/Entity/PersonAltName.php index d233082c4..4fe1e5fca 100644 --- a/src/Bundle/ChillPersonBundle/Entity/PersonAltName.php +++ b/src/Bundle/ChillPersonBundle/Entity/PersonAltName.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Entity; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; /** * PersonAltName. @@ -34,6 +35,7 @@ class PersonAltName * @var string * * @ORM\Column(name="key", type="string", length=255) + * @Groups({"write"}) */ private $key; @@ -41,6 +43,7 @@ class PersonAltName * @var string * * @ORM\Column(name="label", type="text") + * @Groups({"write"}) */ private $label; diff --git a/src/Bundle/ChillPersonBundle/Entity/Relationships/Relation.php b/src/Bundle/ChillPersonBundle/Entity/Relationships/Relation.php index c78e5df33..b2be55059 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Relationships/Relation.php +++ b/src/Bundle/ChillPersonBundle/Entity/Relationships/Relation.php @@ -28,7 +28,7 @@ class Relation * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") - * @Serializer\Groups({"read"}) + * @Serializer\Groups({"read", "docgen:read"}) */ private ?int $id = null; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss b/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss index f39f64eb2..db5df905a 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss +++ b/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss @@ -254,3 +254,24 @@ abbr.referrer { // still used ? border: 1px solid black; padding: 10px; } + +/// Masonry blocs on AccompanyingCourse resume page +div#dashboards { + div.mbloc { + & > div:not(.warnings) { + border: 1px solid $chill-light-gray; + background-color: $chill-llight-gray; + border-radius: 0.35rem; + padding: 1rem; + } + & > div.warnings .alert { + margin-bottom: 0; + } + blockquote.chill-user-quote { + margin: -1.2em; + p { + margin: 0; + } + } + } +} diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/App.vue index 30a8bbf2a..c0f279760 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/App.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/App.vue @@ -8,6 +8,7 @@ + @@ -28,6 +29,7 @@ import { mapGetters, mapState } from 'vuex' import Banner from './components/Banner.vue'; import StickyNav from './components/StickyNav.vue'; import OriginDemand from './components/OriginDemand.vue'; +import AdminLocation from './components/AdminLocation.vue'; import PersonsAssociated from './components/PersonsAssociated.vue'; import Requestor from './components/Requestor.vue'; import SocialIssue from './components/SocialIssue.vue'; @@ -44,6 +46,7 @@ export default { Banner, StickyNav, OriginDemand, + AdminLocation, PersonsAssociated, Requestor, SocialIssue, diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/AdminLocation.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/AdminLocation.vue new file mode 100644 index 000000000..201360a6e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/AdminLocation.vue @@ -0,0 +1,103 @@ + + + + + + diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/PersonsAssociated.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/PersonsAssociated.vue index 00dc73bd2..435dfe0c8 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/PersonsAssociated.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/PersonsAssociated.vue @@ -1,9 +1,9 @@