Merge remote-tracking branch 'origin/master' into doc/authorizaton-documentation-update

This commit is contained in:
Julien Fastré 2021-11-22 10:30:05 +01:00
commit 98760bc5e8
300 changed files with 9998 additions and 5391 deletions

View File

@ -12,12 +12,45 @@ and this project adheres to
<!-- write down unreleased development here --> <!-- write down unreleased development here -->
* [task] Select2 field in task form to allow search for a user (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/167)
* remove "search by phone configuration option": search by phone is now executed by default
* remplacer le classement par ordre alphabétique par un classement par ordre de pertinence, qui tient compte:
* de la présence d'une string avec le nom de la ville;
* de la similarité;
* du fait que la recherche commence par une partie du mot recherché
* ajouter la recherche par numéro de téléphone directement dans la barre de recherche et dans le formulaire recherche avancée;
* ajouter la recherche par date de naissance directement dans la barre de recherche;
* ajouter la recherche par ville dans la recherche avancée
* ajouter un lien vers le ménage dans les résultats de recherche
* ajouter l'id du parcours dans les résultats de recherche
* ajouter le demandeur dans les résultats de recherche
* ajout d'un bouton "recherche avancée" sur la page d'accueil
* [person] create an accompanying course: add client-side validation if no origin (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/210)
* [person] fix bounds for computing current person address: the new address appears immediatly
* [docgen] create a normalizer and serializer for normalization on doc format
## Test releases ## Test releases
### Test release 2021-11-15
* [main] fix adding multiple AddresseDeRelais (combine PickAddressType with ChillCollection)
* [person]: do not suggest the current household of the person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/51)
* [person]: display other phone numbers in view + add message in case no others phone numbers (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/184)
* unnecessary whitespace removed from person banner after person-id + double parentheses removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/290)
* [person]: delete accompanying period work, including related objects (cascade) (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/36)
* [address]: Display of incomplete address adjusted.
* [household]: improve relationship graph
* add form to create/edit/delete relationship link,
* improve graph refresh mechanism
* add feature to export canvas as image (png)
* [person suggest] In widget "add person", improve the pertinence of persons when one of the names starts with the pattern;
* [person] do not ask for center any more on person creation
* [3party] do not ask for center any more on 3party creation
### Test release 2021-11-08 ### Test release 2021-11-08
* [person]: Display the name of a user when searching after a User (TMS)
* [person]: Add civility to the person * [person]: Add civility to the person
* [person]: Various improvements on the edit person form * [person]: Various improvements on the edit person form
* [person]: Set available_languages and available_countries as parameters for use in the edit person form * [person]: Set available_languages and available_countries as parameters for use in the edit person form
@ -42,10 +75,9 @@ and this project adheres to
* [tasks]: different layout for task list / my tasks, and fix link to tasks in alert or in warning * [tasks]: different layout for task list / my tasks, and fix link to tasks in alert or in warning
* [admin]: links to activity admin section added again. * [admin]: links to activity admin section added again.
* [household]: household addresses ordered by ValidFrom date and by id to show the last created address on top. * [household]: household addresses ordered by ValidFrom date and by id to show the last created address on top.
* [socialWorkAction]: display of social issue and parent issues + banner context added. * [socialWorkAction]: display of social issue and parent issues + banner context added.
* [DBAL dependencies] Upgrade to DBAL 3.1 * [DBAL dependencies] Upgrade to DBAL 3.1
### Test release 2021-10-27 ### Test release 2021-10-27
* [person]: delete double actions buttons on search person page * [person]: delete double actions buttons on search person page
@ -63,7 +95,10 @@ and this project adheres to
* [3party]: fix address creation * [3party]: fix address creation
* [household members editor] finalisation of editor * [household members editor] finalisation of editor
* [AccompanyingCourse banner]: replace translation referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/70) * [AccompanyingCourse banner]: replace translation referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/70)
* [Location]: add location system in activity and RV (calendar). User can choose in location list or create a new location. * [Location]: add location system in activity and RV (calendar). User can choose in location list or create a new location.
* [household]: add relationship page with dynamic data visualisation graph
## Test releases
### Test release 2021-10-11 ### Test release 2021-10-11
@ -130,7 +165,7 @@ and this project adheres to
## Test released ## Test released
<!-- <!--
Coming soon... Coming soon...
@ -143,4 +178,4 @@ DO NOT ADD unreleased items here. Add them under "Unreleased" title
## Stable releases ## Stable releases
No stable releases for v2+ No stable releases for v2+

View File

@ -54,6 +54,9 @@
"doctrine/doctrine-fixtures-bundle": "^3.3", "doctrine/doctrine-fixtures-bundle": "^3.3",
"fakerphp/faker": "^1.13", "fakerphp/faker": "^1.13",
"nelmio/alice": "^3.8", "nelmio/alice": "^3.8",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^7.0", "phpunit/phpunit": "^7.0",
"symfony/debug-bundle": "^5.1", "symfony/debug-bundle": "^5.1",
"symfony/dotenv": "^5.1", "symfony/dotenv": "^5.1",
@ -88,7 +91,8 @@
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"App\\": "tests/app/src/" "App\\": "tests/app/src/",
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
} }
}, },
"minimum-stability": "dev", "minimum-stability": "dev",

111
phpstan-baseline.neon Normal file
View File

@ -0,0 +1,111 @@
parameters:
ignoreErrors:
-
message: "#^Variable property access on \\$this\\(Chill\\\\ActivityBundle\\\\Entity\\\\ActivityType\\)\\.$#"
count: 3
path: src/Bundle/ChillActivityBundle/Entity/ActivityType.php
-
message: "#^Foreach overwrites \\$key with its key variable\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
-
message: "#^Instantiated class PhpOffice\\\\PhpWord\\\\TemplateProcessor not found\\.$#"
count: 1
path: src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorController.php
-
message: "#^Instantiated class PhpOffice\\\\PhpWord\\\\TemplateProcessor not found\\.$#"
count: 1
path: src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php
-
message: "#^Variable \\$participation might not be defined\\.$#"
count: 3
path: src/Bundle/ChillEventBundle/Controller/ParticipationController.php
-
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
-
message: "#^Variable method call on object\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php
-
message: "#^Cannot unset offset '_token' on array\\{formatter\\: mixed, export\\: mixed, centers\\: mixed, alias\\: string\\}\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/ExportController.php
-
message: "#^Foreach overwrites \\$line with its value variable\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Export/Formatter/CSVFormatter.php
-
message: "#^Foreach overwrites \\$key with its key variable\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php
-
message: "#^Foreach overwrites \\$key with its value variable\\.$#"
count: 3
path: src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php
-
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/ChoiceLoader/PostalCodeChoiceLoader.php
-
message: "#^Variable \\$message on left side of \\?\\? always exists and is not nullable\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Templating/ChillTwigHelper.php
-
message: "#^Variable \\$sqls on left side of \\?\\? always exists and is not nullable\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php
-
message: "#^Class Chill\\\\PersonBundle\\\\Entity\\\\Person constructor invoked with 1 parameter, 0 required\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Variable \\$street1Value might not be defined\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Variable method call on mixed\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPeople.php
-
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
-
message: "#^Foreach overwrites \\$action with its value variable\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Foreach overwrites \\$action with its value variable\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php
-
message: "#^Foreach overwrites \\$action with its value variable\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php
-
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Form/ChoiceLoader/ThirdPartyChoiceLoader.php

131
phpstan-critical.neon Normal file
View File

@ -0,0 +1,131 @@
parameters:
ignoreErrors:
-
message: "#^Implicit array creation is not allowed \\- variable \\$centers might not exist\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
-
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:\\$currentHouseholdParticipationAt\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Entity/Person.php
-
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Entity\\\\Household\\\\PersonHouseholdAddress\\:\\:\\$relation\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/Household/PersonHouseholdAddress.php
-
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Entity\\\\AccompanyingPeriod\\:\\:\\$work\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
-
message: "#^Undefined variable\\: \\$person$#"
count: 1
path: src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php
-
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Household\\\\MembersEditorFactory\\:\\:\\$validator\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Household/MembersEditorFactory.php
-
message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:countBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$action$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$limit$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$offset$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$orderBy$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Variable variables are not allowed\\.$#"
count: 4
path: src/Bundle/ChillPersonBundle/Search/PersonSearch.php
-
message: "#^Function Chill\\\\PersonBundle\\\\Serializer\\\\Normalizer\\\\·\\\\is_array not found\\.$#"
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
path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php
-
message: "#^Undefined variable\\: \\$choiceSlug$#"
count: 1
path: src/Bundle/ChillReportBundle/Export/Export/ReportList.php
-
message: "#^Undefined variable\\: \\$type$#"
count: 1
path: src/Bundle/ChillTaskBundle/Controller/TaskController.php
-
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\AbstractCRUDController\\:\\:getRoleFor\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php
-
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Controller\\\\UserController\\:\\:createEditForm\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/UserController.php
-
message: "#^Undefined variable\\: \\$current$#"
count: 1
path: src/Bundle/ChillMainBundle/Pagination/PageGenerator.php
-
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AbstractChillVoter\\:\\:getSupportedAttributes\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php
-
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AbstractChillVoter\\:\\:getSupportedClasses\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php
-
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AbstractChillVoter\\:\\:isGranted\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php
-
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Controller\\\\PersonController\\:\\:\\$security\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Controller/PersonController.php
-
message: "#^Call to an undefined method Chill\\\\ThirdPartyBundle\\\\Form\\\\Type\\\\PickThirdPartyTypeCategoryType\\:\\:transform\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Form/Type/PickThirdPartyTypeCategoryType.php

852
phpstan-types.neon Normal file
View File

@ -0,0 +1,852 @@
parameters:
ignoreErrors:
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivitytACL.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Entity/Activity.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Entity/ActivityReason.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Entity/ActivityReasonCategory.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Export/Export/ListActivity.php
-
message: "#^Method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\StatActivityDuration\\:\\:getDescription\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php
-
message: "#^Method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\StatActivityDuration\\:\\:getTitle\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Only booleans are allowed in &&, mixed given on the right side\\.$#"
count: 3
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Menu/AdminMenuBuilder.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Security/Authorization/ActivityStatsVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillActivityBundle/Timeline/TimelineActivityProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillAsideActivityBundle/src/Menu/AdminMenuBuilder.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 3
path: src/Bundle/ChillBudgetBundle/Form/ChargeType.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillBudgetBundle/Form/ResourceType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillBudgetBundle/Security/Authorization/BudgetElementVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/Entity/Calendar.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Command/CreateFieldsOnGroupCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/AbstractCustomField.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 4
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldChoice\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldDate.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldDate\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldDate.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldLongChoice\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldLongChoice.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldNumber.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldNumber\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldNumber.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldText\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldText.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldTitle\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldTitle.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldsGroup.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Form/CustomFieldType.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillDocStoreBundle/Entity/DocumentCategory.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Entity/Participation.php
-
message: "#^Method Chill\\\\EventBundle\\\\Entity\\\\Participation\\:\\:offsetGet\\(\\) should return mixed but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Entity/Participation.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
-
message: "#^Parameter \\$resolver of method Chill\\\\EventBundle\\\\Form\\\\EventTypeType\\:\\:setDefaultOptions\\(\\) has invalid type Symfony\\\\Component\\\\OptionsResolver\\\\OptionsResolverInterface\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/EventTypeType.php
-
message: "#^Parameter \\$resolver of method Chill\\\\EventBundle\\\\Form\\\\RoleType\\:\\:setDefaultOptions\\(\\) has invalid type Symfony\\\\Component\\\\OptionsResolver\\\\OptionsResolverInterface\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/RoleType.php
-
message: "#^Parameter \\$resolver of method Chill\\\\EventBundle\\\\Form\\\\StatusType\\:\\:setDefaultOptions\\(\\) has invalid type Symfony\\\\Component\\\\OptionsResolver\\\\OptionsResolverInterface\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/StatusType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/Type/PickEventType.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillEventBundle/Search/EventSearch.php
-
message: "#^Method Chill\\\\EventBundle\\\\Search\\\\EventSearch\\:\\:renderResult\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Search/EventSearch.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillEventBundle/Security/Authorization/EventVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillEventBundle/Security/Authorization/ParticipationVoter.php
-
message: "#^Casting to string something that's already string\\.$#"
count: 5
path: src/Bundle/ChillFamilyMembersBundle/Entity/AbstractFamilyMember.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillFamilyMembersBundle/Form/FamilyMemberType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillFamilyMembersBundle/Security/Voter/FamilyMemberVoter.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php
-
message: "#^Parameter \\$scope of method Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\CRUDController\\:\\:getReachableCenters\\(\\) has invalid type Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\Scope\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Resolver/Resolver.php
-
message: "#^Method Chill\\\\MainBundle\\\\CRUD\\\\Resolver\\\\Resolver\\:\\:getConfigValue\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Resolver/Resolver.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\ChillImportUsersCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Command/ChillUserSendRenewPasswordCodeCommand.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/ChillUserSendRenewPasswordCodeCommand.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\ChillUserSendRenewPasswordCodeCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/ChillUserSendRenewPasswordCodeCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Command/LoadAndUpdateLanguagesCommand.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\LoadAndUpdateLanguagesCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/LoadAndUpdateLanguagesCommand.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Command/LoadAndUpdateLanguagesCommand.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\LoadCountriesCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/LoadCountriesCommand.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\LoadPostalCodesCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/LoadPostalCodesCommand.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\SetPasswordCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/SetPasswordCommand.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/PasswordController.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/PostalCodeController.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadLanguages.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 5
path: src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/ExportsCompilerPass.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/SearchableServicesCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/AbstractWidgetsCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/AbstractWidgetsCompilerPass.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/AbstractWidgetsCompilerPass.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Entity/Address.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Entity/Embeddable/CommentEmbeddable.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Entity/User.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Entity/User.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 6
path: src/Bundle/ChillMainBundle/Export/ExportManager.php
-
message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/ChoiceLoader/PostalCodeChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/DataMapper/AddressDataMapper.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Form/Type/AddressType.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Form/Type/AddressType.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/ChillTextareaType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/ComposedRoleScopeType.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 3
path: src/Bundle/ChillMainBundle/Form/Type/DataTransformer/DateIntervalTransformer.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/DataTransformer/ObjectToIdTransformer.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/TranslatableStringFormType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Search/Entity/SearchUserApiProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Security/Authorization/DefaultVoterHelper.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Security/ParentRoleHelper.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverEvent.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverVoter.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
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
path: src/Bundle/ChillMainBundle/Timeline/TimelineBuilder.php
-
message: "#^Method Chill\\\\MainBundle\\\\Timeline\\\\TimelineBuilder\\:\\:getTemplateData\\(\\) should return array but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Timeline/TimelineBuilder.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Validation/Validator/RoleScopeScopePresence.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Validation/Validator/ValidPhonenumber.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/CRUD/Controller/OneToOneEntityPersonCRUDController.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php
-
message: "#^Method Chill\\\\PersonBundle\\\\Command\\\\ChillPersonMoveCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 6
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/PersonController.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/DependencyInjection/CompilerPass/AccompanyingPeriodTimelineCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/Person.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Entity/PersonPhone.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Export/Aggregator/CountryOfBirthAggregator.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Export/Aggregator/NationalityAggregator.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Export/Filter/GenderFilter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/DataMapper/PersonAltNameDataMapper.php
-
message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PersonAltNameType.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PickPersonType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Household/MembersEditor.php
-
message: "#^Method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid return type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\QueryBuilder\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Repository/PersonRepository.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Search/PersonSearch.php
-
message: "#^Method Chill\\\\PersonBundle\\\\Search\\\\PersonSearch\\:\\:renderResult\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Search/PersonSearch.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Search/SearchPersonApiProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodWorkDenormalizer.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Templating/Entity/ClosingMotiveRender.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Timeline/AbstractTimelineAccompanyingPeriod.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReportACL.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php
-
message: "#^Method Chill\\\\ReportBundle\\\\DataFixtures\\\\ORM\\\\LoadReports\\:\\:getRandomChoice\\(\\) should return array\\<string\\>\\|string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 4
path: src/Bundle/ChillReportBundle/Export/Export/ReportList.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillReportBundle/Export/Export/ReportList.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Security/Authorization/ReportVoter.php
-
message: "#^Method Chill\\\\ReportBundle\\\\Security\\\\Authorization\\\\ReportVoter\\:\\:supports\\(\\) should return bool but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Security/Authorization/ReportVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 4
path: src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/DataFixtures/ORM/LoadTaskACL.php
-
message: "#^Casting to string something that's already string\\.$#"
count: 3
path: src/Bundle/ChillTaskBundle/Entity/AbstractTask.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 5
path: src/Bundle/ChillTaskBundle/Repository/SingleTaskRepository.php
-
message: "#^Method Chill\\\\TaskBundle\\\\Timeline\\\\SingleTaskTaskLifeCycleEventTimelineProvider\\:\\:getTransitionByName\\(\\) should return Symfony\\\\Component\\\\Workflow\\\\Transition but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Timeline/SingleTaskTaskLifeCycleEventTimelineProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Timeline/TaskLifeCycleEventTimelineProvider.php
-
message: "#^Method Chill\\\\TaskBundle\\\\Timeline\\\\TaskLifeCycleEventTimelineProvider\\:\\:getTransitionByName\\(\\) should return Symfony\\\\Component\\\\Workflow\\\\Transition but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Timeline/TaskLifeCycleEventTimelineProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/DependencyInjection/CompilerPass/ThirdPartyTypeCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 4
path: src/Bundle/ChillThirdPartyBundle/Entity/ThirdParty.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Form/ChoiceLoader/ThirdPartyChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Form/ChoiceLoader/ThirdPartyChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Repository/ThirdPartyRepository.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php
-
message: "#^Method Chill\\\\ThirdPartyBundle\\\\Search\\\\ThirdPartySearch\\:\\:renderResult\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Search/ThirdPartySearch.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Security/Voter/ThirdPartyVoter.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Templating/Entity/ThirdPartyRender.php

24
phpstan.neon.dist Normal file
View File

@ -0,0 +1,24 @@
parameters:
level: 1
paths:
- src/
excludePaths:
- src/Bundle/*/Tests/*
- src/Bundle/*/tests/*
- src/Bundle/*/Test/*
- src/Bundle/*/config/*
- src/Bundle/*/migrations/*
- src/Bundle/*/translations/*
- src/Bundle/*/Resources/*
- src/Bundle/*/src/Tests/*
- src/Bundle/*/src/Test/*
- src/Bundle/*/src/config/*
- src/Bundle/*/src/migrations/*
- src/Bundle/*/src/translations/*
- src/Bundle/*/src/Resources/*
includes:
- phpstan-types.neon
- phpstan-critical.neon
- phpstan-baseline.neon

View File

@ -37,6 +37,9 @@
<testsuite name="CalendarBundle"> <testsuite name="CalendarBundle">
<directory suffix="Test.php">src/Bundle/ChillCalendarBundle/Tests/</directory> <directory suffix="Test.php">src/Bundle/ChillCalendarBundle/Tests/</directory>
</testsuite> </testsuite>
<testsuite name="DocGeneratorBundle">
<directory suffix="Test.php">src/Bundle/ChillDocGeneratorBundle/tests/</directory>
</testsuite>
</testsuites> </testsuites>
<listeners> <listeners>

View File

@ -1,37 +1,26 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Controller; namespace Chill\ActivityBundle\Controller;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepository; use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface; use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter; use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\Repository\LocationRepository;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Privacy\PrivacyEvent; use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Form; use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
@ -42,33 +31,56 @@ use Chill\ActivityBundle\Form\ActivityType;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
/** final class ActivityController extends AbstractController
* Class ActivityController
*
* @package Chill\ActivityBundle\Controller
*/
class ActivityController extends AbstractController
{ {
protected EventDispatcherInterface $eventDispatcher; private EventDispatcherInterface $eventDispatcher;
protected AuthorizationHelper $authorizationHelper; private LoggerInterface $logger;
protected LoggerInterface $logger; private SerializerInterface $serializer;
protected SerializerInterface $serializer; private ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
protected ActivityACLAwareRepositoryInterface $activityACLAwareRepository; private ActivityTypeRepository $activityTypeRepository;
private ThirdPartyRepository $thirdPartyRepository;
private PersonRepository $personRepository;
private LocationRepository $locationRepository;
private EntityManagerInterface $entityManager;
private ActivityRepository $activityRepository;
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
private ActivityTypeCategoryRepository $activityTypeCategoryRepository;
public function __construct( public function __construct(
ActivityACLAwareRepositoryInterface $activityACLAwareRepository, ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
ActivityTypeRepository $activityTypeRepository,
ActivityTypeCategoryRepository $activityTypeCategoryRepository,
PersonRepository $personRepository,
ThirdPartyRepository $thirdPartyRepository,
LocationRepository $locationRepository,
ActivityRepository $activityRepository,
AccompanyingPeriodRepository $accompanyingPeriodRepository,
EntityManagerInterface $entityManager,
EventDispatcherInterface $eventDispatcher, EventDispatcherInterface $eventDispatcher,
AuthorizationHelper $authorizationHelper,
LoggerInterface $logger, LoggerInterface $logger,
SerializerInterface $serializer SerializerInterface $serializer
) { ) {
$this->activityACLAwareRepository = $activityACLAwareRepository; $this->activityACLAwareRepository = $activityACLAwareRepository;
$this->activityTypeRepository = $activityTypeRepository;
$this->activityTypeCategoryRepository = $activityTypeCategoryRepository;
$this->personRepository = $personRepository;
$this->thirdPartyRepository = $thirdPartyRepository;
$this->locationRepository = $locationRepository;
$this->activityRepository = $activityRepository;
$this->accompanyingPeriodRepository = $accompanyingPeriodRepository;
$this->entityManager = $entityManager;
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
$this->authorizationHelper = $authorizationHelper;
$this->logger = $logger; $this->logger = $logger;
$this->serializer = $serializer; $this->serializer = $serializer;
} }
@ -78,8 +90,8 @@ class ActivityController extends AbstractController
*/ */
public function listAction(Request $request): Response public function listAction(Request $request): Response
{ {
$em = $this->getDoctrine()->getManager();
$view = null; $view = null;
$activities = [];
// TODO: add pagination // TODO: add pagination
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@ -89,10 +101,10 @@ class ActivityController extends AbstractController
$activities = $this->activityACLAwareRepository $activities = $this->activityACLAwareRepository
->findByPerson($person, ActivityVoter::SEE, 0, null); ->findByPerson($person, ActivityVoter::SEE, 0, null);
$event = new PrivacyEvent($person, array( $event = new PrivacyEvent($person, [
'element_class' => Activity::class, 'element_class' => Activity::class,
'action' => 'list' 'action' => 'list'
)); ]);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
$view = 'ChillActivityBundle:Activity:listPerson.html.twig'; $view = 'ChillActivityBundle:Activity:listPerson.html.twig';
@ -105,16 +117,18 @@ class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig'; $view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig';
} }
return $this->render($view, array( return $this->render(
'activities' => $activities, $view,
'person' => $person, [
'accompanyingCourse' => $accompanyingPeriod, 'activities' => $activities,
)); 'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
]
);
} }
public function selectTypeAction(Request $request): Response public function selectTypeAction(Request $request): Response
{ {
$em = $this->getDoctrine()->getManager();
$view = null; $view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@ -127,12 +141,17 @@ class ActivityController extends AbstractController
$data = []; $data = [];
$activityTypeCategories = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityTypeCategory::class) $activityTypeCategories = $this
->activityTypeCategoryRepository
->findBy(['active' => true], ['ordering' => 'ASC']); ->findBy(['active' => true], ['ordering' => 'ASC']);
foreach ($activityTypeCategories as $activityTypeCategory) { foreach ($activityTypeCategories as $activityTypeCategory) {
$activityTypes = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class) $activityTypes = $this
->findBy(['active' => true, 'category' => $activityTypeCategory], ['ordering' => 'ASC']); ->activityTypeRepository
->findBy(
['active' => true, 'category' => $activityTypeCategory],
['ordering' => 'ASC']
);
$data[] = [ $data[] = [
'activityTypeCategory' => $activityTypeCategory, 'activityTypeCategory' => $activityTypeCategory,
@ -140,12 +159,6 @@ class ActivityController extends AbstractController
]; ];
} }
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
} else {
$activityData = [];
}
if ($view === null) { if ($view === null) {
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
@ -154,13 +167,13 @@ class ActivityController extends AbstractController
'person' => $person, 'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'data' => $data, 'data' => $data,
'activityData' => $activityData 'activityData' => $request->query->get('activityData', []),
]); ]);
} }
public function newAction(Request $request): Response public function newAction(Request $request): Response
{ {
$em = $this->getDoctrine()->getManager(); $view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@ -171,8 +184,7 @@ class ActivityController extends AbstractController
} }
$activityType_id = $request->get('activityType_id', 0); $activityType_id = $request->get('activityType_id', 0);
$activityType = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class) $activityType = $this->activityTypeRepository->find($activityType_id);
->find($activityType_id);
if (isset($activityType) && !$activityType->isActive()) { if (isset($activityType) && !$activityType->isActive()) {
throw new \InvalidArgumentException('Activity type must be active'); throw new \InvalidArgumentException('Activity type must be active');
@ -230,23 +242,23 @@ class ActivityController extends AbstractController
if (array_key_exists('personsId', $activityData)) { if (array_key_exists('personsId', $activityData)) {
foreach($activityData['personsId'] as $personId){ foreach($activityData['personsId'] as $personId){
$concernedPerson = $em->getRepository(\Chill\PersonBundle\Entity\Person::class)->find($personId); $concernedPerson = $this->personRepository->find($personId);
$entity->addPerson($concernedPerson); $entity->addPerson($concernedPerson);
} }
} }
if (array_key_exists('professionalsId', $activityData)) { if (array_key_exists('professionalsId', $activityData)) {
foreach($activityData['professionalsId'] as $professionalsId){ foreach($activityData['professionalsId'] as $professionalsId){
$professional = $em->getRepository(\Chill\ThirdPartyBundle\Entity\ThirdParty::class)->find($professionalsId); $professional = $this->thirdPartyRepository->find($professionalsId);
$entity->addThirdParty($professional); $entity->addThirdParty($professional);
} }
} }
if (array_key_exists('location', $activityData)) { if (array_key_exists('location', $activityData)) {
$location = $em->getRepository(\Chill\MainBundle\Entity\Location::class)->find($activityData['location']); $location = $this->locationRepository->find($activityData['location']);
$entity->setLocation($location); $entity->setLocation($location);
} }
if (array_key_exists('comment', $activityData)) { if (array_key_exists('comment', $activityData)) {
$comment = new CommentEmbeddable(); $comment = new CommentEmbeddable();
$comment->setComment($activityData['comment']); $comment->setComment($activityData['comment']);
@ -268,8 +280,8 @@ class ActivityController extends AbstractController
])->handleRequest($request); ])->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$em->persist($entity); $this->entityManager->persist($entity);
$em->flush(); $this->entityManager->flush();
$this->addFlash('success', $this->get('translator')->trans('Success : activity created!')); $this->addFlash('success', $this->get('translator')->trans('Success : activity created!'));
@ -297,7 +309,7 @@ class ActivityController extends AbstractController
public function showAction(Request $request, $id): Response public function showAction(Request $request, $id): Response
{ {
$em = $this->getDoctrine()->getManager(); $view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@ -307,13 +319,14 @@ class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:showPerson.html.twig'; $view = 'ChillActivityBundle:Activity:showPerson.html.twig';
} }
$entity = $em->getRepository('ChillActivityBundle:Activity')->find($id); $entity = $this->activityRepository->find($id);
if (!$entity) { if (null === $entity) {
throw $this->createNotFoundException('Unable to find Activity entity.'); throw $this->createNotFoundException('Unable to find Activity entity.');
} }
if (null !== $accompanyingPeriod) { if (null !== $accompanyingPeriod) {
// @TODO: Properties created dynamically.
$entity->personsAssociated = $entity->getPersonsAssociated(); $entity->personsAssociated = $entity->getPersonsAssociated();
$entity->personsNotAssociated = $entity->getPersonsNotAssociated(); $entity->personsNotAssociated = $entity->getPersonsNotAssociated();
} }
@ -321,7 +334,7 @@ class ActivityController extends AbstractController
// TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période // TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_SEE', $entity); // $this->denyAccessUnlessGranted('CHILL_ACTIVITY_SEE', $entity);
$deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod); $deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
// TODO // TODO
/* /*
@ -337,21 +350,20 @@ class ActivityController extends AbstractController
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
return $this->render($view, array( return $this->render($view, [
'person' => $person, 'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'entity' => $entity, 'entity' => $entity,
'delete_form' => $deleteForm->createView(), 'delete_form' => $deleteForm->createView(),
)); ]);
} }
/** /**
* Displays a form to edit an existing Activity entity. * Displays a form to edit an existing Activity entity.
*
*/ */
public function editAction($id, Request $request): Response public function editAction($id, Request $request): Response
{ {
$em = $this->getDoctrine()->getManager(); $view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@ -361,9 +373,9 @@ class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:editPerson.html.twig'; $view = 'ChillActivityBundle:Activity:editPerson.html.twig';
} }
$entity = $em->getRepository('ChillActivityBundle:Activity')->find($id); $entity = $this->activityRepository->find($id);
if (!$entity) { if (null === $entity) {
throw $this->createNotFoundException('Unable to find Activity entity.'); throw $this->createNotFoundException('Unable to find Activity entity.');
} }
@ -378,17 +390,18 @@ class ActivityController extends AbstractController
])->handleRequest($request); ])->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$em->persist($entity); $this->entityManager->persist($entity);
$em->flush(); $this->entityManager->flush();
$this->addFlash('success', $this->get('translator')->trans('Success : activity updated!')); $this->addFlash('success', $this->get('translator')->trans('Success : activity updated!'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod); $params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $id; $params['id'] = $entity->getId();
return $this->redirectToRoute('chill_activity_activity_show', $params); return $this->redirectToRoute('chill_activity_activity_show', $params);
} }
$deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod); $deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
/* /*
* TODO * TODO
@ -406,23 +419,22 @@ class ActivityController extends AbstractController
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']); $activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, array( return $this->render($view, [
'entity' => $entity, 'entity' => $entity,
'edit_form' => $form->createView(), 'edit_form' => $form->createView(),
'delete_form' => $deleteForm->createView(), 'delete_form' => $deleteForm->createView(),
'person' => $person, 'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'activity_json' => $activity_array 'activity_json' => $activity_array
)); ]);
} }
/** /**
* Deletes a Activity entity. * Deletes a Activity entity.
*
*/ */
public function deleteAction(Request $request, $id) public function deleteAction(Request $request, $id)
{ {
$em = $this->getDoctrine()->getManager(); $view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@ -432,8 +444,7 @@ class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig'; $view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig';
} }
/* @var $activity Activity */ $activity = $this->activityRepository->find($id);
$activity = $em->getRepository('ChillActivityBundle:Activity')->find($id);
if (!$activity) { if (!$activity) {
throw $this->createNotFoundException('Unable to find Activity entity.'); throw $this->createNotFoundException('Unable to find Activity entity.');
@ -442,35 +453,37 @@ class ActivityController extends AbstractController
// TODO // TODO
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_DELETE', $activity); // $this->denyAccessUnlessGranted('CHILL_ACTIVITY_DELETE', $activity);
$form = $this->createDeleteForm($id, $person, $accompanyingPeriod); $form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
if ($request->getMethod() === Request::METHOD_DELETE) { if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isValid()) { if ($form->isValid()) {
$this->logger->notice("An activity has been removed", [
$this->logger->notice("An activity has been removed", array(
'by_user' => $this->getUser()->getUsername(), 'by_user' => $this->getUser()->getUsername(),
'activity_id' => $activity->getId(), 'activity_id' => $activity->getId(),
'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null, 'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null,
'comment' => $activity->getComment()->getComment(), 'comment' => $activity->getComment()->getComment(),
'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null, 'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null,
'reasons_ids' => $activity->getReasons() 'reasons_ids' => $activity->getReasons()
->map(function ($ar) { return $ar->getId(); }) ->map(
static fn (ActivityReason $ar): int => $ar->getId()
)
->toArray(), ->toArray(),
'type_id' => $activity->getType()->getId(), 'type_id' => $activity->getType()->getId(),
'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null, 'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null,
'date' => $activity->getDate()->format('Y-m-d'), 'date' => $activity->getDate()->format('Y-m-d'),
'attendee' => $activity->getAttendee() 'attendee' => $activity->getAttendee()
)); ]);
$em->remove($activity); $this->entityManager->remove($activity);
$em->flush(); $this->entityManager->flush();
$this->addFlash('success', $this->get('translator') $this->addFlash('success', $this->get('translator')
->trans("The activity has been successfully removed.")); ->trans("The activity has been successfully removed."));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod); $params = $this->buildParamsToUrl($person, $accompanyingPeriod);
return $this->redirectToRoute('chill_activity_activity_list', $params); return $this->redirectToRoute('chill_activity_activity_list', $params);
} }
} }
@ -479,18 +492,18 @@ class ActivityController extends AbstractController
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
return $this->render($view, array( return $this->render($view, [
'activity' => $activity, 'activity' => $activity,
'delete_form' => $form->createView(), 'delete_form' => $form->createView(),
'person' => $person, 'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
)); ]);
} }
/** /**
* Creates a form to delete a Activity entity by id. * Creates a form to delete a Activity entity by id.
*/ */
private function createDeleteForm(int $id, ?Person $person, ?AccompanyingPeriod $accompanyingPeriod): Form private function createDeleteForm(int $id, ?Person $person, ?AccompanyingPeriod $accompanyingPeriod): FormInterface
{ {
$params = $this->buildParamsToUrl($person, $accompanyingPeriod); $params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $id; $params['id'] = $id;
@ -498,19 +511,17 @@ class ActivityController extends AbstractController
return $this->createFormBuilder() return $this->createFormBuilder()
->setAction($this->generateUrl('chill_activity_activity_delete', $params)) ->setAction($this->generateUrl('chill_activity_activity_delete', $params))
->setMethod('DELETE') ->setMethod('DELETE')
->add('submit', SubmitType::class, array('label' => 'Delete')) ->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm() ->getForm();
;
} }
private function getEntity(Request $request): array private function getEntity(Request $request): array
{ {
$em = $this->getDoctrine()->getManager();
$person = $accompanyingPeriod = null; $person = $accompanyingPeriod = null;
if ($request->query->has('person_id')) { if ($request->query->has('person_id')) {
$person_id = $request->get('person_id'); $person_id = $request->get('person_id');
$person = $em->getRepository(Person::class)->find($person_id); $person = $this->personRepository->find($person_id);
if ($person === null) { if ($person === null) {
throw $this->createNotFoundException('Person not found'); throw $this->createNotFoundException('Person not found');
@ -519,7 +530,7 @@ class ActivityController extends AbstractController
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person); $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
} elseif ($request->query->has('accompanying_period_id')) { } elseif ($request->query->has('accompanying_period_id')) {
$accompanying_period_id = $request->get('accompanying_period_id'); $accompanying_period_id = $request->get('accompanying_period_id');
$accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($accompanying_period_id); $accompanyingPeriod = $this->accompanyingPeriodRepository->find($accompanying_period_id);
if ($accompanyingPeriod === null) { if ($accompanyingPeriod === null) {
throw $this->createNotFoundException('Accompanying Period not found'); throw $this->createNotFoundException('Accompanying Period not found');
@ -532,21 +543,20 @@ class ActivityController extends AbstractController
} }
return [ return [
$person, $accompanyingPeriod $person,
$accompanyingPeriod
]; ];
} }
private function buildParamsToUrl( private function buildParamsToUrl(?Person $person, ?AccompanyingPeriod $accompanyingPeriod): array
?Person $person, {
?AccompanyingPeriod $accompanyingPeriod
): array {
$params = []; $params = [];
if ($person) { if (null !== $person) {
$params['person_id'] = $person->getId(); $params['person_id'] = $person->getId();
} }
if ($accompanyingPeriod) { if (null !== $accompanyingPeriod) {
$params['accompanying_period_id'] = $accompanyingPeriod->getId(); $params['accompanying_period_id'] = $accompanyingPeriod->getId();
} }

View File

@ -22,9 +22,10 @@
namespace Chill\ActivityBundle\DataFixtures\ORM; namespace Chill\ActivityBundle\DataFixtures\ORM;
use Chill\PersonBundle\DataFixtures\Helper\RandomPersonHelperTrait; use Chill\PersonBundle\Entity\Person;
use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Faker\Factory as FakerFactory; use Faker\Factory as FakerFactory;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
@ -32,26 +33,21 @@ use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
use Chill\ActivityBundle\DataFixtures\ORM\LoadActivityReason; use Chill\ActivityBundle\DataFixtures\ORM\LoadActivityReason;
use Chill\ActivityBundle\DataFixtures\ORM\LoadActivityType; use Chill\ActivityBundle\DataFixtures\ORM\LoadActivityType;
use Chill\MainBundle\DataFixtures\ORM\LoadScopes; use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
/** class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
* Load reports into DB
*
* @author Champs-Libres Coop
*/
class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
{ {
use \Symfony\Component\DependencyInjection\ContainerAwareTrait; use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
use RandomPersonHelperTrait;
/** /**
* @var \Faker\Generator * @var \Faker\Generator
*/ */
private $faker; private $faker;
private EntityManagerInterface $em;
public function __construct() public function __construct(EntityManagerInterface $em)
{ {
$this->faker = FakerFactory::create('fr_FR'); $this->faker = FakerFactory::create('fr_FR');
$this->em = $em;
} }
public function getOrder() public function getOrder()
@ -86,15 +82,10 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, C
* *
* @return \Chill\ActivityBundle\Entity\ActivityReason * @return \Chill\ActivityBundle\Entity\ActivityReason
*/ */
private function getRandomActivityReason(array $excludingIds) private function getRandomActivityReason()
{ {
$reasonRef = LoadActivityReason::$references[array_rand(LoadActivityReason::$references)]; $reasonRef = LoadActivityReason::$references[array_rand(LoadActivityReason::$references)];
if (in_array($this->getReference($reasonRef)->getId(), $excludingIds)) {
// we have a reason which should be excluded. Find another...
return $this->getRandomActivityReason($excludingIds);
}
return $this->getReference($reasonRef); return $this->getReference($reasonRef);
} }
@ -109,7 +100,7 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, C
return $this->getReference($userRef); return $this->getReference($userRef);
} }
public function newRandomActivity($person) public function newRandomActivity($person): ?Activity
{ {
$activity = (new Activity()) $activity = (new Activity())
->setUser($this->getRandomUser()) ->setUser($this->getRandomUser())
@ -122,11 +113,13 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, C
// ->setAttendee($this->faker->boolean()) // ->setAttendee($this->faker->boolean())
$usedId = array();
for ($i = 0; $i < rand(0, 4); $i++) { for ($i = 0; $i < rand(0, 4); $i++) {
$reason = $this->getRandomActivityReason($usedId); $reason = $this->getRandomActivityReason();
$usedId[] = $reason->getId(); if (null !== $reason) {
$activity->addReason($reason); $activity->addReason($reason);
} else {
return null;
}
} }
return $activity; return $activity;
@ -134,20 +127,19 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, C
public function load(ObjectManager $manager) public function load(ObjectManager $manager)
{ {
$persons = $this->container->get('doctrine.orm.entity_manager') $persons = $this->em
->getRepository('ChillPersonBundle:Person') ->getRepository(Person::class)
->findAll(); ->findAll();
foreach($persons as $person) { foreach ($persons as $person) {
$activityNbr = rand(0,3); $activityNbr = rand(0,3);
$ref = 'activity_'.$person->getFullnameCanonical();
for($i = 0; $i < $activityNbr; $i ++) { for ($i = 0; $i < $activityNbr; $i ++) {
$activity = $this->newRandomActivity($person); $activity = $this->newRandomActivity($person);
$manager->persist($activity); if (null !== $activity) {
$manager->persist($activity);
}
} }
$this->setReference($ref, $activity);
} }
$manager->flush(); $manager->flush();
} }

View File

@ -1,27 +1,8 @@
<?php <?php
/*
*
* Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Entity; namespace Chill\ActivityBundle\Entity;
use Chill\DocStoreBundle\Entity\Document; use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Location;
use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface; use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface;
@ -38,7 +19,7 @@ use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasScopeInterface; use Chill\MainBundle\Entity\HasScopeInterface;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistency; use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
@ -202,7 +183,7 @@ class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPer
return $this->id; return $this->id;
} }
public function setUser(User $user): self public function setUser(UserInterface $user): self
{ {
$this->user = $user; $this->user = $user;

View File

@ -1,31 +1,12 @@
<?php <?php
/* declare(strict_types=1);
*
* Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Entity; namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* Class ActivityTypeCateogry
*
* @package Chill\ActivityBundle\Entity
* @ORM\Entity() * @ORM\Entity()
* @ORM\Table(name="activitytypecategory") * @ORM\Table(name="activitytypecategory")
* @ORM\HasLifecycleCallbacks() * @ORM\HasLifecycleCallbacks()
@ -37,7 +18,7 @@ class ActivityTypeCategory
* @ORM\Column(name="id", type="integer") * @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO") * @ORM\GeneratedValue(strategy="AUTO")
*/ */
private ?int $id; private ?int $id = null;
/** /**
* @ORM\Column(type="json") * @ORM\Column(type="json")
@ -54,10 +35,7 @@ class ActivityTypeCategory
*/ */
private float $ordering = 0.0; private float $ordering = 0.0;
/** public function getId(): ?int
* Get id
*/
public function getId(): int
{ {
return $this->id; return $this->id;
} }

View File

@ -1,70 +1,39 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Aggregator; namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
/** class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityReasonAggregator implements AggregatorInterface,
ExportElementValidatedInterface
{ {
/** protected ActivityReasonCategoryRepository $activityReasonCategoryRepository;
*
* @var EntityRepository
*/
protected $categoryRepository;
/** protected ActivityReasonRepository $activityReasonRepository;
*
* @var EntityRepository
*/
protected $reasonRepository;
/** protected TranslatableStringHelperInterface $translatableStringHelper;
*
* @var TranslatableStringHelper
*/
protected $stringHelper;
public function __construct( public function __construct(
EntityRepository $categoryRepository, ActivityReasonCategoryRepository $activityReasonCategoryRepository,
EntityRepository $reasonRepository, ActivityReasonRepository $activityReasonRepository,
TranslatableStringHelper $stringHelper TranslatableStringHelper $translatableStringHelper
) { ) {
$this->categoryRepository = $categoryRepository; $this->activityReasonCategoryRepository = $activityReasonCategoryRepository;
$this->reasonRepository = $reasonRepository; $this->activityReasonRepository = $activityReasonRepository;
$this->stringHelper = $stringHelper; $this->translatableStringHelper = $translatableStringHelper;
} }
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
@ -77,7 +46,7 @@ class ActivityReasonAggregator implements AggregatorInterface,
$elem = 'category.id'; $elem = 'category.id';
$alias = 'activity_categories_id'; $alias = 'activity_categories_id';
} else { } else {
throw new \RuntimeException('the data provided are not recognized'); throw new \RuntimeException('The data provided are not recognized.');
} }
$qb->addSelect($elem.' as '.$alias); $qb->addSelect($elem.' as '.$alias);
@ -93,11 +62,12 @@ class ActivityReasonAggregator implements AggregatorInterface,
(! array_key_exists('activity', $join)) (! array_key_exists('activity', $join))
) { ) {
$qb->add( $qb->add(
'join', 'join',
array('activity' => [
new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons') 'activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')
), ],
true); true
);
} }
// join category if necessary // join category if necessary
@ -143,28 +113,33 @@ class ActivityReasonAggregator implements AggregatorInterface,
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('level', ChoiceType::class, array( $builder->add(
'choices' => array( 'level',
'By reason' => 'reasons', ChoiceType::class,
'By category of reason' => 'categories' [
), 'choices' => [
'multiple' => false, 'By reason' => 'reasons',
'expanded' => true, 'By category of reason' => 'categories'
'label' => 'Reason\'s level' ],
)); 'multiple' => false,
'expanded' => true,
'label' => "Reason's level"
]
);
} }
public function validateForm($data, ExecutionContextInterface $context) public function validateForm($data, ExecutionContextInterface $context)
{ {
if ($data['level'] === null) { if ($data['level'] === null) {
$context->buildViolation("The reasons's level should not be empty") $context
->buildViolation("The reasons's level should not be empty.")
->addViolation(); ->addViolation();
} }
} }
public function getTitle() public function getTitle()
{ {
return "Aggregate by activity reason"; return 'Aggregate by activity reason';
} }
public function addRole() public function addRole()
@ -177,41 +152,33 @@ class ActivityReasonAggregator implements AggregatorInterface,
// for performance reason, we load data from db only once // for performance reason, we load data from db only once
switch ($data['level']) { switch ($data['level']) {
case 'reasons': case 'reasons':
$this->reasonRepository->findBy(array('id' => $values)); $this->activityReasonRepository->findBy(['id' => $values]);
break; break;
case 'categories': case 'categories':
$this->categoryRepository->findBy(array('id' => $values)); $this->activityReasonCategoryRepository->findBy(['id' => $values]);
break; break;
default: default:
throw new \RuntimeException(sprintf("the level data '%s' is invalid", throw new \RuntimeException(sprintf("The level data '%s' is invalid.", $data['level']));
$data['level']));
} }
return function($value) use ($data) { return function($value) use ($data) {
if ($value === '_header') { if ($value === '_header') {
return $data['level'] === 'reasons' ? return $data['level'] === 'reasons' ? 'Group by reasons' : 'Group by categories of reason';
'Group by reasons'
:
'Group by categories of reason'
;
} }
switch ($data['level']) { switch ($data['level']) {
case 'reasons': case 'reasons':
/* @var $r \Chill\ActivityBundle\Entity\ActivityReason */ $r = $this->activityReasonRepository->find($value);
$r = $this->reasonRepository->find($value);
return $this->stringHelper->localize($r->getCategory()->getName()) return sprintf(
." > " "%s > %s",
. $this->stringHelper->localize($r->getName()); $this->translatableStringHelper->localize($r->getCategory()->getName()),
; $this->translatableStringHelper->localize($r->getName())
break; );
case 'categories': case 'categories':
$c = $this->categoryRepository->find($value); $c = $this->activityReasonCategoryRepository->find($value);
return $this->stringHelper->localize($c->getName()); return $this->translatableStringHelper->localize($c->getName());
break;
// no need for a default : the default was already set above
} }
}; };
@ -222,12 +189,14 @@ class ActivityReasonAggregator implements AggregatorInterface,
// add select element // add select element
if ($data['level'] === 'reasons') { if ($data['level'] === 'reasons') {
return array('activity_reasons_id'); return array('activity_reasons_id');
} elseif ($data['level'] === 'categories') {
return array ('activity_categories_id');
} else {
throw new \RuntimeException('the data provided are not recognised');
} }
if ($data['level'] === 'categories') {
return array ('activity_categories_id');
}
throw new \RuntimeException('The data provided are not recognised.');
} }
} }

View File

@ -1,75 +1,46 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Aggregator; namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityTypeAggregator implements AggregatorInterface class ActivityTypeAggregator implements AggregatorInterface
{ {
protected ActivityTypeRepository $activityTypeRepository;
/**
* protected TranslatableStringHelperInterface $translatableStringHelper;
* @var EntityRepository
*/ public const KEY = 'activity_type_aggregator';
protected $typeRepository;
/**
*
* @var TranslatableStringHelper
*/
protected $stringHelper;
const KEY = 'activity_type_aggregator';
public function __construct( public function __construct(
EntityRepository $typeRepository, ActivityTypeRepository $activityTypeRepository,
TranslatableStringHelper $stringHelper TranslatableStringHelperInterface $translatableStringHelper
) { ) {
$this->typeRepository = $typeRepository; $this->activityTypeRepository = $activityTypeRepository;
$this->stringHelper = $stringHelper; $this->translatableStringHelper = $translatableStringHelper;
} }
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
// add select element // add select element
$qb->addSelect(sprintf('IDENTITY(activity.type) AS %s', self::KEY)); $qb->addSelect(sprintf('IDENTITY(activity.type) AS %s', self::KEY));
// add the "group by" part // add the "group by" part
$groupBy = $qb->addGroupBy(self::KEY); $qb->addGroupBy(self::KEY);
} }
/** /**
* Check if a join between Activity and another alias * Check if a join between Activity and another alias
* *
* @param Join[] $joins * @param Join[] $joins
* @param string $alias the alias to search for * @param string $alias the alias to search for
* @return boolean * @return boolean
@ -81,7 +52,7 @@ class ActivityTypeAggregator implements AggregatorInterface
return true; return true;
} }
} }
return false; return false;
} }
@ -97,35 +68,33 @@ class ActivityTypeAggregator implements AggregatorInterface
public function getTitle() public function getTitle()
{ {
return "Aggregate by activity type"; return 'Aggregate by activity type';
} }
public function addRole() public function addRole()
{ {
return new Role(ActivityStatsVoter::STATS); return new Role(ActivityStatsVoter::STATS);
} }
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data): \Closure
{ {
// for performance reason, we load data from db only once // for performance reason, we load data from db only once
$this->typeRepository->findBy(array('id' => $values)); $this->activityTypeRepository->findBy(['id' => $values]);
return function($value) use ($data) { return function($value): string {
if ($value === '_header') { if ($value === '_header') {
return 'Activity type'; return 'Activity type';
} }
/* @var $r \Chill\ActivityBundle\Entity\ActivityType */ $t = $this->activityTypeRepository->find($value);
$t = $this->typeRepository->find($value);
return $this->stringHelper->localize($t->getName()); return $this->translatableStringHelper->localize($t->getName());
}; };
} }
public function getQueryKeys($data) public function getQueryKeys($data): array
{ {
return array(self::KEY); return [self::KEY];
} }
} }

View File

@ -1,51 +1,26 @@
<?php <?php
/*
* Copyright (C) 2019 Champs Libres Cooperative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Aggregator; namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\MainBundle\Repository\UserRepository;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query\Expr\Join;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityManagerInterface;
use Chill\MainBundle\Entity\User;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityUserAggregator implements AggregatorInterface class ActivityUserAggregator implements AggregatorInterface
{ {
/** public const KEY = 'activity_user_id';
*
* @var EntityManagerInterface private UserRepository $userRepository;
*/
protected $em; public function __construct(
UserRepository $userRepository
const KEY = 'activity_user_id'; ) {
$this->userRepository = $userRepository;
function __construct(EntityManagerInterface $em)
{
$this->em = $em;
} }
public function addRole() public function addRole()
{ {
return new Role(ActivityStatsVoter::STATS); return new Role(ActivityStatsVoter::STATS);
@ -53,9 +28,9 @@ class ActivityUserAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
// add select element // add select element
$qb->addSelect(sprintf('IDENTITY(activity.user) AS %s', self::KEY)); $qb->addSelect(sprintf('IDENTITY(activity.user) AS %s', self::KEY));
// add the "group by" part // add the "group by" part
$qb->addGroupBy(self::KEY); $qb->addGroupBy(self::KEY);
} }
@ -73,17 +48,14 @@ class ActivityUserAggregator implements AggregatorInterface
public function getLabels($key, $values, $data): \Closure public function getLabels($key, $values, $data): \Closure
{ {
// preload users at once // preload users at once
$this->em->getRepository(User::class) $this->userRepository->findBy(['id' => $values]);
->findBy(['id' => $values]);
return function($value) { return function($value) {
switch ($value) { if ($value === '_header') {
case '_header': return 'activity user';
return 'activity user';
default:
return $this->em->getRepository(User::class)->find($value)
->getUsername();
} }
return $this->userRepository->find($value)->getUsername();
}; };
} }
@ -94,6 +66,6 @@ class ActivityUserAggregator implements AggregatorInterface
public function getTitle(): string public function getTitle(): string
{ {
return "Aggregate by activity user"; return 'Aggregate by activity user';
} }
} }

View File

@ -1,64 +1,40 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2015 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Export; namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\ExportInterface;
use Doctrine\ORM\QueryBuilder; use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityManagerInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class CountActivity implements ExportInterface class CountActivity implements ExportInterface
{ {
/** protected ActivityRepository $activityRepository;
*
* @var EntityManagerInterface
*/
protected $entityManager;
public function __construct( public function __construct(
EntityManagerInterface $em ActivityRepository $activityRepository
) ) {
{ $this->activityRepository = $activityRepository;
$this->entityManager = $em;
} }
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
} }
public function getDescription() public function getDescription()
{ {
return "Count activities by various parameters."; return 'Count activities by various parameters.';
} }
public function getTitle() public function getTitle()
{ {
return "Count activities"; return 'Count activities';
} }
public function getType() public function getType()
@ -68,26 +44,26 @@ class CountActivity implements ExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array()) public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
{ {
$qb = $this->entityManager->createQueryBuilder(); $centers = array_map(static fn($el) => $el['center'], $acl);
$centers = array_map(function($el) { return $el['center']; }, $acl);
$qb = $this
$qb->select('COUNT(activity.id) as export_count_activity') ->activityRepository
->from('ChillActivityBundle:Activity', 'activity') ->createQueryBuilder('activity')
->join('activity.person', 'person') ->select('COUNT(activity.id) as export_count_activity')
; ->join('activity.person', 'person');
$qb->where($qb->expr()->in('person.center', ':centers')) $qb
->setParameter('centers', $centers) ->where($qb->expr()->in('person.center', ':centers'))
; ->setParameter('centers', $centers);
return $qb; return $qb;
} }
public function supportsModifiers() public function supportsModifiers()
{ {
return array('person', 'activity'); return ['person', 'activity'];
} }
public function requiredRole() public function requiredRole()
{ {
return new Role(ActivityStatsVoter::STATS); return new Role(ActivityStatsVoter::STATS);
@ -95,7 +71,7 @@ class CountActivity implements ExportInterface
public function getAllowedFormattersTypes() public function getAllowedFormattersTypes()
{ {
return array(\Chill\MainBundle\Export\FormatterInterface::TYPE_TABULAR); return [FormatterInterface::TYPE_TABULAR];
} }
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data)
@ -103,19 +79,13 @@ class CountActivity implements ExportInterface
if ($key !== 'export_count_activity') { if ($key !== 'export_count_activity') {
throw new \LogicException("the key $key is not used by this export"); throw new \LogicException("the key $key is not used by this export");
} }
return function($value) { return static fn($value) => $value === '_header' ? 'Number of activities' : $value;
return $value === '_header' ?
'Number of activities'
:
$value
;
};
} }
public function getQueryKeys($data) public function getQueryKeys($data)
{ {
return array('export_count_activity'); return ['export_count_activity'];
} }
public function getResult($qb, $data) public function getResult($qb, $data)

View File

@ -1,31 +1,14 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Export; namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Export\ListInterface;
use Chill\ActivityBundle\Entity\ActivityReason; use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Entity\Scope; use Doctrine\DBAL\Exception\InvalidArgumentException;
use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
@ -37,33 +20,17 @@ use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Create a list for all activities
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ListActivity implements ListInterface class ListActivity implements ListInterface
{ {
private ActivityRepository $activityRepository;
/** protected EntityManagerInterface $entityManager;
*
* @var EntityManagerInterface
*/
protected $entityManager;
/** protected TranslatorInterface $translator;
*
* @var TranslatorInterface
*/
protected $translator;
/** protected TranslatableStringHelperInterface $translatableStringHelper;
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
protected $fields = array( protected array $fields = [
'id', 'id',
'date', 'date',
'durationTime', 'durationTime',
@ -75,32 +42,28 @@ class ListActivity implements ListInterface
'person_firstname', 'person_firstname',
'person_lastname', 'person_lastname',
'person_id' 'person_id'
); ];
public function __construct( public function __construct(
EntityManagerInterface $em, EntityManagerInterface $em,
TranslatorInterface $translator, TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper TranslatableStringHelperInterface $translatableStringHelper,
) ActivityRepository $activityRepository
{ ) {
$this->entityManager = $em; $this->entityManager = $em;
$this->translator = $translator; $this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
$this->activityRepository = $activityRepository;
} }
/**
* {@inheritDoc}
*
* @param FormBuilderInterface $builder
*/
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('fields', ChoiceType::class, array( $builder->add('fields', ChoiceType::class, [
'multiple' => true, 'multiple' => true,
'expanded' => true, 'expanded' => true,
'choices' => array_combine($this->fields, $this->fields), 'choices' => array_combine($this->fields, $this->fields),
'label' => 'Fields to include in export', 'label' => 'Fields to include in export',
'constraints' => [new Callback(array( 'constraints' => [new Callback([
'callback' => function($selected, ExecutionContextInterface $context) { 'callback' => function($selected, ExecutionContextInterface $context) {
if (count($selected) === 0) { if (count($selected) === 0) {
$context->buildViolation('You must select at least one element') $context->buildViolation('You must select at least one element')
@ -108,19 +71,14 @@ class ListActivity implements ListInterface
->addViolation(); ->addViolation();
} }
} }
))] ])]
)); ]);
} }
/**
* {@inheritDoc}
*
* @return type
*/
public function getAllowedFormattersTypes() public function getAllowedFormattersTypes()
{ {
return array(FormatterInterface::TYPE_LIST); return [FormatterInterface::TYPE_LIST];
} }
public function getDescription() public function getDescription()
@ -133,29 +91,32 @@ class ListActivity implements ListInterface
switch ($key) switch ($key)
{ {
case 'date' : case 'date' :
return function($value) { return static function($value) {
if ($value === '_header') return 'date'; if ($value === '_header') {
return 'date';
}
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $value); $date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
return $date->format('d-m-Y'); return $date->format('d-m-Y');
}; };
case 'attendee': case 'attendee':
return function($value) { return static function($value) {
if ($value === '_header') return 'attendee'; if ($value === '_header') {
return 'attendee';
}
return $value ? 1 : 0; return $value ? 1 : 0;
}; };
case 'list_reasons' : case 'list_reasons' :
/* @var $activityReasonsRepository EntityRepository */ $activityRepository = $this->activityRepository;
$activityRepository = $this->entityManager
->getRepository('ChillActivityBundle:Activity');
return function($value) use ($activityRepository) { return function($value) use ($activityRepository): string {
if ($value === '_header') return 'activity reasons'; if ($value === '_header') {
return 'activity reasons';
}
$activity = $activityRepository $activity = $activityRepository->find($value);
->find($value);
return implode(", ", array_map(function(ActivityReason $r) { return implode(", ", array_map(function(ActivityReason $r) {
@ -168,21 +129,25 @@ class ListActivity implements ListInterface
}; };
case 'circle_name' : case 'circle_name' :
return function($value) { return function($value) {
if ($value === '_header') return 'circle'; if ($value === '_header') {
return 'circle';
}
return $this->translatableStringHelper return $this->translatableStringHelper->localize(json_decode($value, true));
->localize(json_decode($value, true));
}; };
case 'type_name' : case 'type_name' :
return function($value) { return function($value) {
if ($value === '_header') return 'activity type'; if ($value === '_header') {
return 'activity type';
}
return $this->translatableStringHelper return $this->translatableStringHelper->localize(json_decode($value, true));
->localize(json_decode($value, true));
}; };
default: default:
return function($value) use ($key) { return static function($value) use ($key) {
if ($value === '_header') return $key; if ($value === '_header') {
return $key;
}
return $value; return $value;
}; };
@ -209,14 +174,13 @@ class ListActivity implements ListInterface
return 'activity'; return 'activity';
} }
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array()) public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{ {
$centers = array_map(function($el) { return $el['center']; }, $acl); $centers = array_map(function($el) { return $el['center']; }, $acl);
// throw an error if any fields are present // throw an error if any fields are present
if (!\array_key_exists('fields', $data)) { if (!\array_key_exists('fields', $data)) {
throw new \Doctrine\DBAL\Exception\InvalidArgumentException("any fields " throw new InvalidArgumentException('Any fields have been checked.');
. "have been checked");
} }
$qb = $this->entityManager->createQueryBuilder(); $qb = $this->entityManager->createQueryBuilder();
@ -227,7 +191,6 @@ class ListActivity implements ListInterface
->join('person.center', 'center') ->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)') ->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers); ->setParameter('authorized_centers', $centers);
;
foreach ($this->fields as $f) { foreach ($this->fields as $f) {
if (in_array($f, $data['fields'])) { if (in_array($f, $data['fields'])) {
@ -269,8 +232,6 @@ class ListActivity implements ListInterface
} }
} }
return $qb; return $qb;
} }
@ -281,7 +242,7 @@ class ListActivity implements ListInterface
public function supportsModifiers() public function supportsModifiers()
{ {
return array('activity', 'person'); return ['activity', 'person'];
} }
} }

View File

@ -1,89 +1,62 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2015 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Export; namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\ExportInterface;
use Doctrine\ORM\QueryBuilder; use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityManagerInterface;
/** /**
* This export allow to compute stats on activity duration. * This export allow to compute stats on activity duration.
* *
* The desired stat must be given in constructor. * The desired stat must be given in constructor.
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/ */
class StatActivityDuration implements ExportInterface class StatActivityDuration implements ExportInterface
{ {
/** private ActivityRepository $activityRepository;
*
* @var EntityManagerInterface public const SUM = 'sum';
*/
protected $entityManager;
const SUM = 'sum';
/** /**
* The action for this report. * The action for this report.
*
* @var string
*/ */
protected $action; protected string $action;
/** /**
* constructor
*
* @param EntityManagerInterface $em
* @param string $action the stat to perform * @param string $action the stat to perform
*/ */
public function __construct( public function __construct(
EntityManagerInterface $em, ActivityRepository $activityRepository,
$action = 'sum' string $action = 'sum'
) ) {
{
$this->entityManager = $em;
$this->action = $action; $this->action = $action;
$this->activityRepository = $activityRepository;
} }
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
} }
public function getDescription() public function getDescription()
{ {
if ($this->action === self::SUM) { if ($this->action === self::SUM) {
return "Sum activities duration by various parameters."; return 'Sum activities duration by various parameters.';
} }
} }
public function getTitle() public function getTitle()
{ {
if ($this->action === self::SUM) { if ($this->action === self::SUM) {
return "Sum activity duration"; return 'Sum activity duration';
} }
} }
public function getType() public function getType()
@ -91,31 +64,33 @@ class StatActivityDuration implements ExportInterface
return 'activity'; return 'activity';
} }
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array()) public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{ {
$centers = array_map(function($el) { return $el['center']; }, $acl); $centers = array_map(
$qb = $this->entityManager->createQueryBuilder(); static fn(array $el): string => $el['center'],
$acl
);
$qb = $this->activityRepository->createQueryBuilder('activity');
$select = null;
if ($this->action === self::SUM) { if ($this->action === self::SUM) {
$select = "SUM(activity.durationTime) AS export_stat_activity"; $select = 'SUM(activity.durationTime) AS export_stat_activity';
} }
$qb->select($select) return $qb->select($select)
->from('ChillActivityBundle:Activity', 'activity') ->join('activity.person', 'person')
->join('activity.person', 'person') ->join('person.center', 'center')
->join('person.center', 'center') ->where($qb->expr()->in('center', ':centers'))
->where($qb->expr()->in('center', ':centers')) ->setParameter(':centers', $centers);
->setParameter(':centers', $centers)
;
return $qb;
} }
public function supportsModifiers() public function supportsModifiers()
{ {
return array('person', 'activity'); return ['person', 'activity'];
} }
public function requiredRole() public function requiredRole()
{ {
return new Role(ActivityStatsVoter::STATS); return new Role(ActivityStatsVoter::STATS);
@ -123,32 +98,23 @@ class StatActivityDuration implements ExportInterface
public function getAllowedFormattersTypes() public function getAllowedFormattersTypes()
{ {
return array(\Chill\MainBundle\Export\FormatterInterface::TYPE_TABULAR); return [FormatterInterface::TYPE_TABULAR];
} }
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data)
{ {
if ($key !== 'export_stat_activity') { if ($key !== 'export_stat_activity') {
throw new \LogicException("the key $key is not used by this export"); throw new \LogicException(sprintf('The key %s is not used by this export', $key));
} }
switch ($this->action) { $header = $this->action === self::SUM ? 'Sum of activities duration' : false;
case self::SUM:
$header = "Sum of activities duration"; return static fn(string $value) => $value === '_header' ? $header : $value;
}
return function($value) use ($header) {
return $value === '_header' ?
$header
:
$value
;
};
} }
public function getQueryKeys($data) public function getQueryKeys($data)
{ {
return array('export_stat_activity'); return ['export_stat_activity'];
} }
public function getResult($qb, $data) public function getResult($qb, $data)

View File

@ -1,25 +1,12 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\DateType;
@ -28,34 +15,24 @@ use Chill\MainBundle\Form\Type\Export\FilterType;
use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Component\Translation\TranslatorInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityDateFilter implements FilterInterface class ActivityDateFilter implements FilterInterface
{ {
/** protected TranslatorInterface $translator;
*
* @var TranslatorInterface
*/
protected $translator;
function __construct(TranslatorInterface $translator) function __construct(TranslatorInterface $translator)
{ {
$this->translator = $translator; $this->translator = $translator;
} }
public function addRole() public function addRole()
{ {
return null; return null;
} }
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
$clause = $qb->expr()->between('activity.date', ':date_from', $clause = $qb->expr()->between('activity.date', ':date_from',
':date_to'); ':date_to');
if ($where instanceof Expr\Andx) { if ($where instanceof Expr\Andx) {
@ -63,7 +40,7 @@ class ActivityDateFilter implements FilterInterface
} else { } else {
$where = $qb->expr()->andX($clause); $where = $qb->expr()->andX($clause);
} }
$qb->add('where', $where); $qb->add('where', $where);
$qb->setParameter('date_from', $data['date_from']); $qb->setParameter('date_from', $data['date_from']);
$qb->setParameter('date_to', $data['date_to']); $qb->setParameter('date_to', $data['date_to']);
@ -74,35 +51,43 @@ class ActivityDateFilter implements FilterInterface
return 'activity'; return 'activity';
} }
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('date_from', DateType::class, array( $builder->add(
'label' => "Activities after this date", 'date_from',
'data' => new \DateTime(), DateType::class,
'attr' => array('class' => 'datepicker'), [
'widget'=> 'single_text', 'label' => 'Activities after this date',
'format' => 'dd-MM-yyyy', 'data' => new \DateTime(),
)); 'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text',
$builder->add('date_to', DateType::class, array( 'format' => 'dd-MM-yyyy',
'label' => "Activities before this date", ]
'data' => new \DateTime(), );
'attr' => array('class' => 'datepicker'),
'widget'=> 'single_text', $builder->add(
'format' => 'dd-MM-yyyy', 'date_to',
)); DateType::class,
[
'label' => 'Activities before this date',
'data' => new \DateTime(),
'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
]
);
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) { $builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
/* @var $filterForm \Symfony\Component\Form\FormInterface */ /* @var $filterForm \Symfony\Component\Form\FormInterface */
$filterForm = $event->getForm()->getParent(); $filterForm = $event->getForm()->getParent();
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData(); $enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
if ($enabled === true) { if ($enabled === true) {
// if the filter is enabled, add some validation // if the filter is enabled, add some validation
$form = $event->getForm(); $form = $event->getForm();
$date_from = $form->get('date_from')->getData(); $date_from = $form->get('date_from')->getData();
$date_to = $form->get('date_to')->getData(); $date_to = $form->get('date_to')->getData();
// check that fields are not empty // check that fields are not empty
if ($date_from === null) { if ($date_from === null) {
$form->get('date_from')->addError(new FormError( $form->get('date_from')->addError(new FormError(
@ -113,8 +98,8 @@ class ActivityDateFilter implements FilterInterface
$form->get('date_to')->addError(new FormError( $form->get('date_to')->addError(new FormError(
$this->translator->trans('This field ' $this->translator->trans('This field '
. 'should not be empty'))); . 'should not be empty')));
} }
// check that date_from is before date_to // check that date_from is before date_to
if ( if (
($date_from !== null && $date_to !== null) ($date_from !== null && $date_to !== null)
@ -132,17 +117,18 @@ class ActivityDateFilter implements FilterInterface
public function describeAction($data, $format = 'string') public function describeAction($data, $format = 'string')
{ {
return array( return [
"Filtered by date of activity: only between %date_from% and %date_to%", 'Filtered by date of activity: only between %date_from% and %date_to%',
array( [
"%date_from%" => $data['date_from']->format('d-m-Y'), '%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y') '%date_to%' => $data['date_to']->format('d-m-Y')
)); ]
];
} }
public function getTitle() public function getTitle()
{ {
return "Filtered by date activity"; return 'Filtered by date activity';
} }
} }

View File

@ -1,25 +1,12 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@ -28,41 +15,24 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface;
/** class ActivityReasonFilter implements FilterInterface, ExportElementValidatedInterface
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityReasonFilter implements FilterInterface,
ExportElementValidatedInterface
{ {
/** protected TranslatableStringHelperInterface $translatableStringHelper;
*
* @var TranslatableStringHelper protected ActivityReasonRepository $activityReasonRepository;
*/
protected $translatableStringHelper;
/**
* The repository for activity reasons
*
* @var EntityRepository
*/
protected $reasonRepository;
public function __construct( public function __construct(
TranslatableStringHelper $helper, TranslatableStringHelper $helper,
EntityRepository $reasonRepository ActivityReasonRepository $activityReasonRepository
) { ) {
$this->translatableStringHelper = $helper; $this->translatableStringHelper = $helper;
$this->reasonRepository = $reasonRepository; $this->activityReasonRepository = $activityReasonRepository;
} }
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
@ -75,12 +45,12 @@ class ActivityReasonFilter implements FilterInterface,
&& &&
!$this->checkJoinAlreadyDefined($join['activity'], 'reasons') !$this->checkJoinAlreadyDefined($join['activity'], 'reasons')
) )
OR ||
(! array_key_exists('activity', $join)) (! array_key_exists('activity', $join))
) { ) {
$qb->add( $qb->add(
'join', 'join',
array('activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')), array('activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')),
true true
); );
} }
@ -90,25 +60,25 @@ class ActivityReasonFilter implements FilterInterface,
} else { } else {
$where = $qb->expr()->andX($clause); $where = $qb->expr()->andX($clause);
} }
$qb->add('where', $where); $qb->add('where', $where);
$qb->setParameter('selected_activity_reasons', $data['reasons']); $qb->setParameter('selected_activity_reasons', $data['reasons']);
} }
/** /**
* Check if a join between Activity and Reason is already defined * Check if a join between Activity and Reason is already defined
* *
* @param Join[] $joins * @param Join[] $joins
* @return boolean * @return boolean
*/ */
private function checkJoinAlreadyDefined(array $joins, $alias) private function checkJoinAlreadyDefined(array $joins, $alias): bool
{ {
foreach ($joins as $join) { foreach ($joins as $join) {
if ($join->getAlias() === $alias) { if ($join->getAlias() === $alias) {
return true; return true;
} }
} }
return false; return false;
} }
@ -119,51 +89,47 @@ class ActivityReasonFilter implements FilterInterface,
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
//create a local copy of translatableStringHelper $builder->add('reasons', EntityType::class, [
$helper = $this->translatableStringHelper; 'class' => ActivityReason::class,
'choice_label' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getName()),
$builder->add('reasons', EntityType::class, array( 'group_by' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
'class' => 'ChillActivityBundle:ActivityReason',
'choice_label' => function (ActivityReason $reason) use ($helper) {
return $helper->localize($reason->getName());
},
'group_by' => function(ActivityReason $reason) use ($helper) {
return $helper->localize($reason->getCategory()->getName());
},
'multiple' => true, 'multiple' => true,
'expanded' => false 'expanded' => false
)); ]);
} }
public function validateForm($data, ExecutionContextInterface $context) public function validateForm($data, ExecutionContextInterface $context)
{ {
if ($data['reasons'] === null || count($data['reasons']) === 0) { if ($data['reasons'] === null || count($data['reasons']) === 0) {
$context->buildViolation("At least one reason must be choosen") $context
->buildViolation('At least one reason must be chosen')
->addViolation(); ->addViolation();
} }
} }
public function getTitle() public function getTitle()
{ {
return 'Filter by reason'; return 'Filter by reason';
} }
public function addRole() public function addRole()
{ {
return new Role(ActivityStatsVoter::STATS); return new Role(ActivityStatsVoter::STATS);
} }
public function describeAction($data, $format = 'string') public function describeAction($data, $format = 'string')
{ {
// collect all the reasons'name used in this filter in one array // collect all the reasons'name used in this filter in one array
$reasonsNames = array_map( $reasonsNames = array_map(
function(ActivityReason $r) { fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
return "\"".$this->translatableStringHelper->localize($r->getName())."\""; $this->activityReasonRepository->findBy(array('id' => $data['reasons']->toArray()))
}, );
$this->reasonRepository->findBy(array('id' => $data['reasons']->toArray()))
); return [
'Filtered by reasons: only %list%',
return array("Filtered by reasons: only %list%", [
["%list%" => implode(", ", $reasonsNames)]); '%list%' => implode(", ", $reasonsNames),
]
];
} }
} }

View File

@ -1,67 +1,37 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2018 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Entity\ActivityType;
/** class ActivityTypeFilter implements FilterInterface, ExportElementValidatedInterface
*
*
*/
class ActivityTypeFilter implements FilterInterface,
ExportElementValidatedInterface
{ {
/** protected TranslatableStringHelperInterface $translatableStringHelper;
*
* @var TranslatableStringHelper protected ActivityTypeRepository $activityTypeRepository;
*/
protected $translatableStringHelper;
/**
* The repository for activity reasons
*
* @var EntityRepository
*/
protected $typeRepository;
public function __construct( public function __construct(
TranslatableStringHelper $helper, TranslatableStringHelperInterface $translatableStringHelper,
EntityRepository $typeRepository ActivityTypeRepository $activityTypeRepository
) { ) {
$this->translatableStringHelper = $helper; $this->translatableStringHelper = $translatableStringHelper;
$this->typeRepository = $typeRepository; $this->activityTypeRepository = $activityTypeRepository;
} }
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
@ -72,14 +42,14 @@ class ActivityTypeFilter implements FilterInterface,
} else { } else {
$where = $qb->expr()->andX($clause); $where = $qb->expr()->andX($clause);
} }
$qb->add('where', $where); $qb->add('where', $where);
$qb->setParameter('selected_activity_types', $data['types']); $qb->setParameter('selected_activity_types', $data['types']);
} }
/** /**
* Check if a join between Activity and Reason is already defined * Check if a join between Activity and Reason is already defined
* *
* @param Join[] $joins * @param Join[] $joins
* @return boolean * @return boolean
*/ */
@ -90,7 +60,7 @@ class ActivityTypeFilter implements FilterInterface,
return true; return true;
} }
} }
return false; return false;
} }
@ -101,48 +71,50 @@ class ActivityTypeFilter implements FilterInterface,
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
//create a local copy of translatableStringHelper $builder->add(
$helper = $this->translatableStringHelper; 'types',
EntityType::class,
$builder->add('types', EntityType::class, array( [
'class' => ActivityType::class, 'class' => ActivityType::class,
'choice_label' => function (ActivityType $type) use ($helper) { 'choice_label' => fn(ActivityType $type) => $this->translatableStringHelper->localize($type->getName()),
return $helper->localize($type->getName()); 'multiple' => true,
}, 'expanded' => false
'multiple' => true, ]
'expanded' => false );
));
} }
public function validateForm($data, ExecutionContextInterface $context) public function validateForm($data, ExecutionContextInterface $context)
{ {
if ($data['types'] === null || count($data['types']) === 0) { if ($data['types'] === null || count($data['types']) === 0) {
$context->buildViolation("At least one type must be choosen") $context
->buildViolation('At least one type must be chosen')
->addViolation(); ->addViolation();
} }
} }
public function getTitle() public function getTitle()
{ {
return 'Filter by activity type'; return 'Filter by activity type';
} }
public function addRole() public function addRole()
{ {
return new Role(ActivityStatsVoter::STATS); return new Role(ActivityStatsVoter::STATS);
} }
public function describeAction($data, $format = 'string') public function describeAction($data, $format = 'string')
{ {
// collect all the reasons'name used in this filter in one array // collect all the reasons'name used in this filter in one array
$reasonsNames = array_map( $reasonsNames = array_map(
function(ActivityType $t) { fn(ActivityType $t): string => '"' . $this->translatableStringHelper->localize($t->getName()) . '"',
return "\"".$this->translatableStringHelper->localize($t->getName())."\""; $this->activityTypeRepository->findBy(['id' => $data['types']->toArray()])
}, );
$this->typeRepository->findBy(array('id' => $data['types']->toArray()))
); return [
'Filtered by activity type: only %list%',
return array("Filtered by activity type: only %list%", [
["%list%" => implode(", ", $reasonsNames)]); '%list%' => implode(", ", $reasonsNames),
]
];
} }
} }

View File

@ -1,25 +1,14 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\DateType;
@ -29,78 +18,58 @@ use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\ActivityBundle\Entity\ActivityReason; use Chill\ActivityBundle\Entity\ActivityReason;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\EntityManager;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface;
/** class PersonHavingActivityBetweenDateFilter implements FilterInterface, ExportElementValidatedInterface
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class PersonHavingActivityBetweenDateFilter implements FilterInterface,
ExportElementValidatedInterface
{ {
protected TranslatableStringHelperInterface $translatableStringHelper;
/**
* protected ActivityReasonRepository $activityReasonRepository;
* @var TranslatableStringHelper
*/ protected TranslatorInterface $translator;
protected $translatableStringHelper;
/**
*
* @var EntityRepository
*/
protected $activityReasonRepository;
/**
*
* @var TranslatorInterface
*/
protected $translator;
public function __construct( public function __construct(
TranslatableStringHelper $translatableStringHelper, TranslatableStringHelper $translatableStringHelper,
EntityRepository $activityReasonRepository, ActivityReasonRepository $activityReasonRepository,
TranslatorInterface $translator TranslatorInterface $translator
) { ) {
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
$this->activityReasonRepository = $activityReasonRepository; $this->activityReasonRepository = $activityReasonRepository;
$this->translator = $translator; $this->translator = $translator;
} }
public function addRole() public function addRole()
{ {
return null; return null;
} }
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
// create a query for activity // create a query for activity
$sqb = $qb->getEntityManager()->createQueryBuilder(); $sqb = $qb->getEntityManager()->createQueryBuilder();
$sqb->select("person_person_having_activity.id") $sqb->select('person_person_having_activity.id')
->from("ChillActivityBundle:Activity", "activity_person_having_activity") ->from('ChillActivityBundle:Activity', 'activity_person_having_activity')
->join("activity_person_having_activity.person", "person_person_having_activity") ->join('activity_person_having_activity.person', 'person_person_having_activity');
;
// add clause between date // add clause between date
$sqb->where("activity_person_having_activity.date BETWEEN " $sqb->where('activity_person_having_activity.date BETWEEN '
. ":person_having_activity_between_date_from" . ':person_having_activity_between_date_from'
. " AND " . ' AND '
. ":person_having_activity_between_date_to"); . ':person_having_activity_between_date_to');
// add clause activity reason // add clause activity reason
$sqb->join('activity_person_having_activity.reasons', $sqb->join('activity_person_having_activity.reasons', 'reasons_person_having_activity');
'reasons_person_having_activity');
$sqb->andWhere( $sqb->andWhere(
$sqb->expr()->in( $sqb->expr()->in(
'reasons_person_having_activity', 'reasons_person_having_activity', ':person_having_activity_reasons'
":person_having_activity_reasons") )
); );
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('person.id', $sqb->getDQL()); $clause = $qb->expr()->in('person.id', $sqb->getDQL());
@ -109,11 +78,11 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
} else { } else {
$where = $qb->expr()->andX($clause); $where = $qb->expr()->andX($clause);
} }
$qb->add('where', $where); $qb->add('where', $where);
$qb->setParameter('person_having_activity_between_date_from', $qb->setParameter('person_having_activity_between_date_from',
$data['date_from']); $data['date_from']);
$qb->setParameter('person_having_activity_between_date_to', $qb->setParameter('person_having_activity_between_date_to',
$data['date_to']); $data['date_to']);
$qb->setParameter('person_having_activity_reasons', $data['reasons']); $qb->setParameter('person_having_activity_reasons', $data['reasons']);
} }
@ -123,51 +92,45 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
return Declarations::PERSON_IMPLIED_IN; return Declarations::PERSON_IMPLIED_IN;
} }
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('date_from', DateType::class, array( $builder->add('date_from', DateType::class, [
'label' => "Implied in an activity after this date", 'label' => 'Implied in an activity after this date',
'data' => new \DateTime(), 'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'), 'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text', 'widget'=> 'single_text',
'format' => 'dd-MM-yyyy', 'format' => 'dd-MM-yyyy',
)); ]);
$builder->add('date_to', DateType::class, array( $builder->add('date_to', DateType::class, [
'label' => "Implied in an activity before this date", 'label' => 'Implied in an activity before this date',
'data' => new \DateTime(), 'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'), 'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text', 'widget'=> 'single_text',
'format' => 'dd-MM-yyyy', 'format' => 'dd-MM-yyyy',
)); ]);
$builder->add('reasons', EntityType::class, array( $builder->add('reasons', EntityType::class, [
'class' => 'ChillActivityBundle:ActivityReason', 'class' => ActivityReason::class,
'choice_label' => function (ActivityReason $reason) { 'choice_label' => fn (ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getName()),
return $this->translatableStringHelper 'group_by' => fn(ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
->localize($reason->getName());
},
'group_by' => function(ActivityReason $reason) {
return $this->translatableStringHelper
->localize($reason->getCategory()->getName());
},
'data' => $this->activityReasonRepository->findAll(), 'data' => $this->activityReasonRepository->findAll(),
'multiple' => true, 'multiple' => true,
'expanded' => false, 'expanded' => false,
'label' => "Activity reasons for those activities" 'label' => 'Activity reasons for those activities'
)); ]);
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) { $builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
/* @var $filterForm \Symfony\Component\Form\FormInterface */ /* @var FormInterface $filterForm */
$filterForm = $event->getForm()->getParent(); $filterForm = $event->getForm()->getParent();
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData(); $enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
if ($enabled === true) { if ($enabled === true) {
// if the filter is enabled, add some validation // if the filter is enabled, add some validation
$form = $event->getForm(); $form = $event->getForm();
$date_from = $form->get('date_from')->getData(); $date_from = $form->get('date_from')->getData();
$date_to = $form->get('date_to')->getData(); $date_to = $form->get('date_to')->getData();
// check that fields are not empty // check that fields are not empty
if ($date_from === null) { if ($date_from === null) {
$form->get('date_from')->addError(new FormError( $form->get('date_from')->addError(new FormError(
@ -178,8 +141,8 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
$form->get('date_to')->addError(new FormError( $form->get('date_to')->addError(new FormError(
$this->translator->trans('This field ' $this->translator->trans('This field '
. 'should not be empty'))); . 'should not be empty')));
} }
// check that date_from is before date_to // check that date_from is before date_to
if ( if (
($date_from !== null && $date_to !== null) ($date_from !== null && $date_to !== null)
@ -194,35 +157,37 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
} }
}); });
} }
public function validateForm($data, ExecutionContextInterface $context) public function validateForm($data, ExecutionContextInterface $context)
{ {
if ($data['reasons'] === null || count($data['reasons']) === 0) { if ($data['reasons'] === null || count($data['reasons']) === 0) {
$context->buildViolation("At least one reason must be choosen") $context->buildViolation('At least one reason must be chosen')
->addViolation(); ->addViolation();
} }
} }
public function describeAction($data, $format = 'string') public function describeAction($data, $format = 'string')
{ {
return array( return [
"Filtered by person having an activity between %date_from% and " 'Filtered by person having an activity between %date_from% and '
. "%date_to% with reasons %reasons_name%", . '%date_to% with reasons %reasons_name%',
array( [
"%date_from%" => $data['date_from']->format('d-m-Y'), '%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'), '%date_to%' => $data['date_to']->format('d-m-Y'),
"%reasons_name%" => implode(", ", array_map( '%reasons_name%' => implode(
function (ActivityReason $r) { ", ",
return '"'.$this->translatableStringHelper-> array_map(
localize($r->getName()).'"'; fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
}, $data['reasons']
$data['reasons'])) )
)); )
]
];
} }
public function getTitle() public function getTitle()
{ {
return "Filtered by person having an activity in a period"; return 'Filtered by person having an activity in a period';
} }
} }

View File

@ -1,56 +1,29 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Form\Type; namespace Chill\ActivityBundle\Form\Type;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Entity\ActivityType;
/**
* Description of TranslatableActivityType
*
* @author Champs-Libres Coop
*/
class TranslatableActivityType extends AbstractType class TranslatableActivityType extends AbstractType
{ {
protected TranslatableStringHelperInterface $translatableStringHelper;
/** protected ActivityTypeRepository $activityTypeRepository;
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
protected $activityTypeRepository;
public function __construct( public function __construct(
TranslatableStringHelper $helper, TranslatableStringHelperInterface $helper,
EntityRepository $activityTypeRepository ActivityTypeRepository $activityTypeRepository
) ) {
{
$this->translatableStringHelper = $helper; $this->translatableStringHelper = $helper;
$this->activityTypeRepository = $activityTypeRepository; $this->activityTypeRepository = $activityTypeRepository;
} }
@ -65,22 +38,21 @@ class TranslatableActivityType extends AbstractType
return EntityType::class; return EntityType::class;
} }
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, array $options) { public function buildForm(FormBuilderInterface $builder, array $options) {
/* @var $qb \Doctrine\ORM\QueryBuilder */ /* @var QueryBuilder $qb */
$qb = $options['query_builder']; $qb = $options['query_builder'];
if ($options['active_only'] === true) { if ($options['active_only'] === true) {
$qb->where($qb->expr()->eq('at.active', ':active')); $qb->where($qb->expr()->eq('at.active', ':active'));
$qb->setParameter('active', true, \Doctrine\DBAL\Types\Types::BOOLEAN); $qb->setParameter('active', true, Types::BOOLEAN);
} }
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults( $resolver->setDefaults(
array( array(
'class' => 'ChillActivityBundle:ActivityType', 'class' => ActivityType::class,
'active_only' => true, 'active_only' => true,
'query_builder' => $this->activityTypeRepository 'query_builder' => $this->activityTypeRepository
->createQueryBuilder('at'), ->createQueryBuilder('at'),

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityReasonCategory;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityReasonCategory|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityReasonCategory|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityReasonCategory[] findAll()
* @method ActivityReasonCategory[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityReasonCategoryRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityReasonCategory::class);
}
}

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityReason;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityReason|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityReason|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityReason[] findAll()
* @method ActivityReason[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityReasonRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityReason::class);
}
}

View File

@ -1,24 +1,6 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;
@ -29,10 +11,10 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
/** /**
* @method AccompanyingPeriodParticipation|null find($id, $lockMode = null, $lockVersion = null) * @method Activity|null find($id, $lockMode = null, $lockVersion = null)
* @method AccompanyingPeriodParticipation|null findOneBy(array $criteria, array $orderBy = null) * @method Activity|null findOneBy(array $criteria, array $orderBy = null)
* @method AccompanyingPeriodParticipation[] findAll() * @method Activity[] findAll()
* @method AccompanyingPeriodParticipation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @method Activity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/ */
class ActivityRepository extends ServiceEntityRepository class ActivityRepository extends ServiceEntityRepository
{ {
@ -42,12 +24,7 @@ class ActivityRepository extends ServiceEntityRepository
} }
/** /**
* @param $person * @return Activity[]
* @param array $scopes
* @param string[] $orderBy
* @param int $limit
* @param int $offset
* @return array|Activity[]
*/ */
public function findByPersonImplied(Person $person, array $scopes, ?array $orderBy = [ 'date' => 'DESC'], ?int $limit = 100, ?int $offset = 0): array public function findByPersonImplied(Person $person, array $scopes, ?array $orderBy = [ 'date' => 'DESC'], ?int $limit = 100, ?int $offset = 0): array
{ {
@ -63,8 +40,7 @@ class ActivityRepository extends ServiceEntityRepository
':person MEMBER OF a.persons' ':person MEMBER OF a.persons'
) )
) )
->setParameter('person', $person) ->setParameter('person', $person);
;
foreach ($orderBy as $k => $dir) { foreach ($orderBy as $k => $dir) {
$qb->addOrderBy('a.'.$k, $dir); $qb->addOrderBy('a.'.$k, $dir);
@ -72,17 +48,11 @@ class ActivityRepository extends ServiceEntityRepository
$qb->setMaxResults($limit)->setFirstResult($offset); $qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery() return $qb->getQuery()->getResult();
->getResult();
} }
/** /**
* @param AccompanyingPeriod $period * @return Activity[]
* @param array $scopes
* @param int|null $limit
* @param int|null $offset
* @param array|string[] $orderBy
* @return array|Activity[]
*/ */
public function findByAccompanyingPeriod(AccompanyingPeriod $period, array $scopes, ?bool $allowNullScope = false, ?int $limit = 100, ?int $offset = 0, array $orderBy = ['date' => 'desc']): array public function findByAccompanyingPeriod(AccompanyingPeriod $period, array $scopes, ?bool $allowNullScope = false, ?int $limit = 100, ?int $offset = 0, array $orderBy = ['date' => 'desc']): array
{ {
@ -92,8 +62,7 @@ class ActivityRepository extends ServiceEntityRepository
if (!$allowNullScope) { if (!$allowNullScope) {
$qb $qb
->where($qb->expr()->in('a.scope', ':scopes')) ->where($qb->expr()->in('a.scope', ':scopes'))
->setParameter('scopes', $scopes) ->setParameter('scopes', $scopes);
;
} else { } else {
$qb $qb
->where( ->where(
@ -102,16 +71,14 @@ class ActivityRepository extends ServiceEntityRepository
$qb->expr()->isNull('a.scope') $qb->expr()->isNull('a.scope')
) )
) )
->setParameter('scopes', $scopes) ->setParameter('scopes', $scopes);
;
} }
$qb $qb
->andWhere( ->andWhere(
$qb->expr()->eq('a.accompanyingPeriod', ':period') $qb->expr()->eq('a.accompanyingPeriod', ':period')
) )
->setParameter('period', $period) ->setParameter('period', $period);
;
foreach ($orderBy as $k => $dir) { foreach ($orderBy as $k => $dir) {
$qb->addOrderBy('a.'.$k, $dir); $qb->addOrderBy('a.'.$k, $dir);
@ -119,7 +86,6 @@ class ActivityRepository extends ServiceEntityRepository
$qb->setMaxResults($limit)->setFirstResult($offset); $qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery() return $qb->getQuery()->getResult();
->getResult();
} }
} }

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityTypeCategory;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityTypeCategory|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityTypeCategory|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityTypeCategory[] findAll()
* @method ActivityTypeCategory[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityTypeCategoryRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityTypeCategory::class);
}
}

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityType|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityType|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityType[] findAll()
* @method ActivityType[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityTypeRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityType::class);
}
}

View File

@ -11,9 +11,12 @@
</div> </div>
<div v-if="getContext === 'accompanyingCourse' && filterSuggestedPersons.length > 0"> <div v-if="getContext === 'accompanyingCourse' && filterSuggestedPersons.length > 0">
<ul> <ul class="list-unstyled">
<li v-for="p in filterSuggestedPersons" @click="addNewPerson(p)"> <li v-for="p in filterSuggestedPersons" @click="addNewPerson(p)">
{{ p.text }} <span class="badge bg-primary" style="cursor: pointer;">
<i class="fa fa-plus fa-fw text-success"></i>
{{ p.text }}
</span>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -4,7 +4,7 @@
<span class="chill_denomination"> <span class="chill_denomination">
{{ textCutted }} {{ textCutted }}
</span> </span>
<a class="fa fa-fw fa-times" <a class="fa fa-fw fa-times text-danger text-decoration-none"
@click.prevent="$emit('remove', person)"> @click.prevent="$emit('remove', person)">
</a> </a>
</span> </span>

View File

@ -27,14 +27,16 @@
{{ activity.type.name | localize_translatable_string }} {{ activity.type.name | localize_translatable_string }}
<ul class="small_in_title"> <ul class="small_in_title">
{% if activity.location and t.locationVisible %}
<li> <li>
<abbr title="{{ 'location'|trans }}">{{ 'location'|trans ~ ': ' }}</abbr> <span class="item-key">{{ 'location'|trans ~ ': ' }}</span>
{# TODO {% if activity.location %}{{ activity.location }}{% endif %} #} <span>{{ activity.location.locationType.title|localize_translatable_string }}</span>
Domicile de l'usager {{ activity.location.name }}
</li> </li>
{% endif %}
{% if activity.user and t.userVisible %} {% if activity.user and t.userVisible %}
<li> <li>
<abbr title="{{ 'Referrer'|trans }}">{{ 'Referrer'|trans ~ ': ' }}</abbr> <span class="item-key">{{ 'Referrer'|trans ~ ': ' }}</span>
{{ activity.user.usernameCanonical }} {{ activity.user.usernameCanonical }}
</li> </li>
{% endif %} {% endif %}

View File

@ -40,7 +40,6 @@
}, },
{ 'title': 'Users concerned'|trans, { 'title': 'Users concerned'|trans,
'items': entity.users, 'items': entity.users,
'path' : 'admin_user_show',
'key' : 'id' 'key' : 'id'
}, },
] %} ] %}
@ -58,6 +57,7 @@
<ul class="list-content"> <ul class="list-content">
{% for item in bloc.items %} {% for item in bloc.items %}
<li> <li>
{% if bloc.path is defined %}
<a href="{{ _self.href(bloc.path, bloc.key, item.id) }}"> <a href="{{ _self.href(bloc.path, bloc.key, item.id) }}">
<span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}"> <span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}">
{{ item|chill_entity_render_box({ {{ item|chill_entity_render_box({
@ -66,6 +66,14 @@
}) }} }) }}
</span> </span>
</a> </a>
{% else %}
<span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}">
{{ item|chill_entity_render_box({
'render': 'raw',
'addAltNames': false
}) }}
</span>
{% endif %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
@ -85,6 +93,7 @@
<ul class="list-content"> <ul class="list-content">
{% for item in bloc.items %} {% for item in bloc.items %}
<li> <li>
{% if bloc.path is defined %}
<a href="{{ _self.href(bloc.path, bloc.key, item.id) }}"> <a href="{{ _self.href(bloc.path, bloc.key, item.id) }}">
<span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}"> <span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}">
{{ item|chill_entity_render_box({ {{ item|chill_entity_render_box({
@ -93,6 +102,12 @@
}) }} }) }}
</span> </span>
</a> </a>
{% else %}
{{ item|chill_entity_render_box({
'render': 'raw',
'addAltNames': false
}) }}
{% endif %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
@ -114,12 +129,19 @@
{% for item in bloc.items %} {% for item in bloc.items %}
<span class="wl-item {% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}"> <span class="wl-item {% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}">
{% if bloc.path is defined %}
<a href="{{ _self.href(bloc.path, bloc.key, item.id) }}"> <a href="{{ _self.href(bloc.path, bloc.key, item.id) }}">
{{ item|chill_entity_render_box({ {{ item|chill_entity_render_box({
'render': 'raw', 'render': 'raw',
'addAltNames': false 'addAltNames': false
}) }} }) }}
</a> </a>
{% else %}
{{ item|chill_entity_render_box({
'render': 'raw',
'addAltNames': false
}) }}
{% endif %}
</span> </span>
{% endfor %} {% endfor %}

View File

@ -10,7 +10,7 @@
'title' : 'Remove activity'|trans, 'title' : 'Remove activity'|trans,
'confirm_question' : 'Are you sure you want to remove the activity about "%name%" ?'|trans({ '%name%' : accompanyingCourse.id } ), 'confirm_question' : 'Are you sure you want to remove the activity about "%name%" ?'|trans({ '%name%' : accompanyingCourse.id } ),
'cancel_route' : 'chill_activity_activity_list', 'cancel_route' : 'chill_activity_activity_list',
'cancel_parameters' : { 'accompanying_course_id' : accompanyingCourse.id, 'id' : activity.id }, 'cancel_parameters' : { 'accompanying_period_id' : accompanyingCourse.id, 'id' : activity.id },
'form' : delete_form 'form' : delete_form
} ) }} } ) }}
{% endblock %} {% endblock %}

View File

@ -55,7 +55,7 @@
{% endif %} {% endif %}
<h2 class="chill-red">{{ 'Concerned groups'|trans }}</h2> <h2 class="chill-red">{{ 'Concerned groups'|trans }}</h2>
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': context, 'with_display': 'bloc' } %} {% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': context, 'with_display': 'bloc', 'badge_person': 'true' } %}
<h2 class="chill-red">{{ 'Activity data'|trans }}</h2> <h2 class="chill-red">{{ 'Activity data'|trans }}</h2>

View File

@ -1,4 +1,2 @@
{{ dump(notification) }}
<a href="{{ path('chill_activity_activity_show', {'id': notification.relatedEntityId }) }}">Go to Activity</a> <a href="{{ path('chill_activity_activity_show', {'id': notification.relatedEntityId }) }}">Go to Activity</a>

View File

@ -1,94 +1,53 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
* Copyright (C) 2015 Champs Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Timeline; namespace Chill\ActivityBundle\Timeline;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
use Chill\MainBundle\Timeline\TimelineProviderInterface; use Chill\MainBundle\Timeline\TimelineProviderInterface;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepository; use Chill\ActivityBundle\Repository\ActivityACLAwareRepository;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Mapping\ClassMetadata;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Entity\Scope;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
use Chill\MainBundle\Timeline\TimelineSingleQuery; use Chill\MainBundle\Timeline\TimelineSingleQuery;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Provide activity for inclusion in timeline
*
*/
class TimelineActivityProvider implements TimelineProviderInterface class TimelineActivityProvider implements TimelineProviderInterface
{ {
protected EntityManagerInterface $em;
/**
* protected AuthorizationHelperInterface $helper;
* @var EntityManager
*/ protected UserInterface $user;
protected $em;
/**
*
* @var AuthorizationHelper
*/
protected $helper;
/**
*
* @var \Chill\MainBundle\Entity\User
*/
protected $user;
protected ActivityACLAwareRepository $aclAwareRepository; protected ActivityACLAwareRepository $aclAwareRepository;
private const SUPPORTED_CONTEXTS = [ 'center', 'person']; private const SUPPORTED_CONTEXTS = [ 'center', 'person'];
/**
* TimelineActivityProvider constructor.
*
* @param EntityManager $em
* @param AuthorizationHelper $helper
* @param TokenStorageInterface $storage
*/
public function __construct( public function __construct(
EntityManager $em, EntityManagerInterface $em,
AuthorizationHelper $helper, AuthorizationHelperInterface $helper,
TokenStorageInterface $storage, TokenStorageInterface $storage,
ActivityACLAwareRepository $aclAwareRepository ActivityACLAwareRepository $aclAwareRepository
) ) {
{
$this->em = $em; $this->em = $em;
$this->helper = $helper; $this->helper = $helper;
$this->aclAwareRepository = $aclAwareRepository; $this->aclAwareRepository = $aclAwareRepository;
if (!$storage->getToken()->getUser() instanceof \Chill\MainBundle\Entity\User) if (!$storage->getToken()->getUser() instanceof User)
{ {
throw new \RuntimeException('A user should be authenticated !'); throw new \RuntimeException('A user should be authenticated !');
} }
$this->user = $storage->getToken()->getUser(); $this->user = $storage->getToken()->getUser();
} }
/** /**
* *
* {@inheritDoc} * {@inheritDoc}
*/ */
public function fetchQuery($context, array $args) public function fetchQuery($context, array $args)
@ -97,43 +56,42 @@ class TimelineActivityProvider implements TimelineProviderInterface
return TimelineSingleQuery::fromArray($this->aclAwareRepository return TimelineSingleQuery::fromArray($this->aclAwareRepository
->queryTimelineIndexer($context, $args)); ->queryTimelineIndexer($context, $args));
} }
$metadataActivity = $this->em->getClassMetadata(Activity::class); $metadataActivity = $this->em->getClassMetadata(Activity::class);
[$where, $parameters] = $this->getWhereClauseForPerson($args['person']); [$where, $parameters] = $this->getWhereClauseForPerson($args['person']);
return TimelineSingleQuery::fromArray([ return TimelineSingleQuery::fromArray([
'id' => $metadataActivity->getTableName() 'id' => $metadataActivity->getTableName()
.'.'.$metadataActivity->getColumnName('id'), .'.'.$metadataActivity->getColumnName('id'),
'type' => 'activity', 'type' => 'activity',
'date' => $metadataActivity->getTableName() 'date' => $metadataActivity->getTableName()
.'.'.$metadataActivity->getColumnName('date'), .'.'.$metadataActivity->getColumnName('date'),
'FROM' => $this->getFromClausePerson($args['person']), 'FROM' => $this->getFromClausePerson(),
'WHERE' => $where, 'WHERE' => $where,
'parameters' => $parameters 'parameters' => $parameters
]); ]);
} }
private function getWhereClauseForPerson(Person $person) private function getWhereClauseForPerson(Person $person)
{ {
$parameters = []; $parameters = [];
$metadataActivity = $this->em->getClassMetadata(Activity::class); $metadataActivity = $this->em->getClassMetadata(Activity::class);
$associationMapping = $metadataActivity->getAssociationMapping('person'); $associationMapping = $metadataActivity->getAssociationMapping('person');
$role = new Role('CHILL_ACTIVITY_SEE'); $role = new Role('CHILL_ACTIVITY_SEE');
$reachableScopes = $this->helper->getReachableScopes($this->user, $reachableScopes = $this->helper->getReachableScopes($this->user, $role->getRole(), $person->getCenter());
$role, $person->getCenter());
$whereClause = sprintf(' {activity.person_id} = ? AND {activity.scope_id} IN ({scopes_ids}) '); $whereClause = sprintf(' {activity.person_id} = ? AND {activity.scope_id} IN ({scopes_ids}) ');
$scopes_ids = []; $scopes_ids = [];
// first parameter: activity.person_id // first parameter: activity.person_id
$parameters[] = $person->getId(); $parameters[] = $person->getId();
// loop on reachable scopes // loop on reachable scopes
foreach ($reachableScopes as $scope) { foreach ($reachableScopes as $scope) {
if (\in_array($scope->getId(), $scopes_ids)) { if (\in_array($scope->getId(), $scopes_ids)) {
continue; continue;
} }
$scopes_ids[] = '?'; $scopes_ids[] = '?';
$parameters[] = $scope->getId(); $parameters[] = $scope->getId();
} }
@ -151,47 +109,40 @@ class TimelineActivityProvider implements TimelineProviderInterface
$parameters $parameters
]; ];
} }
private function getFromClausePerson() private function getFromClausePerson(): string
{ {
$metadataActivity = $this->em->getClassMetadata(Activity::class); $metadataActivity = $this->em->getClassMetadata(Activity::class);
$metadataPerson = $this->em->getClassMetadata(Person::class); $metadataPerson = $this->em->getClassMetadata(Person::class);
$associationMapping = $metadataActivity->getAssociationMapping('person'); $associationMapping = $metadataActivity->getAssociationMapping('person');
return $metadataActivity->getTableName().' JOIN ' return sprintf(
.$metadataPerson->getTableName().' ON ' "%s JOIN %s ON %s.%s = %s",
.$metadataPerson->getTableName().'.'. $metadataActivity->getTableName(),
$associationMapping['joinColumns'][0]['referencedColumnName'] $metadataPerson->getTableName(),
.' = ' $metadataPerson->getTableName(),
.$associationMapping['joinColumns'][0]['name'] $associationMapping['joinColumns'][0]['referencedColumnName'],
; $associationMapping['joinColumns'][0]['name']
);
} }
/** public function getEntities(array $ids): array
*
* {@inheritDoc}
*/
public function getEntities(array $ids)
{ {
$activities = $this->em->getRepository(Activity::class) $activities = $this->em->getRepository(Activity::class)
->findBy(array('id' => $ids)); ->findBy(array('id' => $ids));
$result = array(); $result = array();
foreach($activities as $activity) { foreach($activities as $activity) {
$result[$activity->getId()] = $activity; $result[$activity->getId()] = $activity;
} }
return $result; return $result;
} }
/** public function getEntityTemplate($entity, $context, array $args): array
*
* {@inheritDoc}
*/
public function getEntityTemplate($entity, $context, array $args)
{ {
$this->checkContext($context); $this->checkContext($context);
return [ return [
'template' => 'ChillActivityBundle:Timeline:activity_person_context.html.twig', 'template' => 'ChillActivityBundle:Timeline:activity_person_context.html.twig',
'template_data' => [ 'template_data' => [
@ -201,26 +152,25 @@ class TimelineActivityProvider implements TimelineProviderInterface
]; ];
} }
/** public function supportsType($type): bool
*
* {@inheritDoc}
*/
public function supportsType($type)
{ {
return $type === 'activity'; return $type === 'activity';
} }
/** /**
* check if the context is supported * Check if the context is supported.
* *
* @param string $context
* @throws \LogicException if the context is not supported * @throws \LogicException if the context is not supported
*/ */
private function checkContext($context) private function checkContext(string $context)
{ {
if (FALSE === \in_array($context, self::SUPPORTED_CONTEXTS)) { if (FALSE === \in_array($context, self::SUPPORTED_CONTEXTS)) {
throw new \LogicException("The context '$context' is not " throw new \LogicException(
. "supported. Currently only 'person' is supported"); sprintf(
"The context '%s' is not supported. Currently only 'person' is supported",
$context
)
);
} }
} }

View File

@ -1,4 +1,7 @@
services: services:
_defaults:
autowire: true
autoconfigure: true
chill.activity.timeline: chill.activity.timeline:
class: Chill\ActivityBundle\Timeline\TimelineActivityProvider class: Chill\ActivityBundle\Timeline\TimelineActivityProvider
@ -13,17 +16,14 @@ services:
- { name: chill.timeline, context: 'center' } - { name: chill.timeline, context: 'center' }
Chill\ActivityBundle\Menu\: Chill\ActivityBundle\Menu\:
autowire: true
autoconfigure: true
resource: '../Menu/' resource: '../Menu/'
tags: ['chill.menu_builder'] tags: ['chill.menu_builder']
Chill\ActivityBundle\Notification\: Chill\ActivityBundle\Notification\:
autowire: true
autoconfigure: true
resource: '../Notification' resource: '../Notification'
Chill\ActivityBundle\Security\Authorization\: Chill\ActivityBundle\Security\Authorization\:
resource: '../Security/Authorization/' resource: '../Security/Authorization/'
autowire: true
autoconfigure: true Chill\ActivityBundle\Repository\:
resource: '../Repository/'

View File

@ -1,82 +1,56 @@
services: services:
_defaults:
autowire: true
autoconfigure: true
chill.activity.export.count_activity: chill.activity.export.count_activity:
class: Chill\ActivityBundle\Export\Export\CountActivity class: Chill\ActivityBundle\Export\Export\CountActivity
arguments:
- "@doctrine.orm.entity_manager"
tags: tags:
- { name: chill.export, alias: 'count_activity' } - { name: chill.export, alias: 'count_activity' }
chill.activity.export.sum_activity_duration: chill.activity.export.sum_activity_duration:
class: Chill\ActivityBundle\Export\Export\StatActivityDuration class: Chill\ActivityBundle\Export\Export\StatActivityDuration
arguments:
- "@doctrine.orm.entity_manager"
- "sum"
tags: tags:
- { name: chill.export, alias: 'sum_activity_duration' } - { name: chill.export, alias: 'sum_activity_duration' }
chill.activity.export.list_activity: chill.activity.export.list_activity:
class: Chill\ActivityBundle\Export\Export\ListActivity class: Chill\ActivityBundle\Export\Export\ListActivity
arguments:
- "@doctrine.orm.entity_manager"
- "@translator"
- "@chill.main.helper.translatable_string"
tags: tags:
- { name: chill.export, alias: 'list_activity' } - { name: chill.export, alias: 'list_activity' }
chill.activity.export.reason_filter: chill.activity.export.reason_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityReasonFilter class: Chill\ActivityBundle\Export\Filter\ActivityReasonFilter
arguments:
- "@chill.main.helper.translatable_string"
- "@chill_activity.repository.reason"
tags: tags:
- { name: chill.export_filter, alias: 'activity_reason_filter' } - { name: chill.export_filter, alias: 'activity_reason_filter' }
chill.activity.export.type_filter: chill.activity.export.type_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityTypeFilter class: Chill\ActivityBundle\Export\Filter\ActivityTypeFilter
arguments:
- "@chill.main.helper.translatable_string"
- "@chill_activity.repository.activity_type"
tags: tags:
- { name: chill.export_filter, alias: 'activity_type_filter' } - { name: chill.export_filter, alias: 'activity_type_filter' }
chill.activity.export.date_filter: chill.activity.export.date_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityDateFilter class: Chill\ActivityBundle\Export\Filter\ActivityDateFilter
arguments:
- "@translator"
tags: tags:
- { name: chill.export_filter, alias: 'activity_date_filter' } - { name: chill.export_filter, alias: 'activity_date_filter' }
chill.activity.export.person_having_an_activity_between_date_filter: chill.activity.export.person_having_an_activity_between_date_filter:
class: Chill\ActivityBundle\Export\Filter\PersonHavingActivityBetweenDateFilter class: Chill\ActivityBundle\Export\Filter\PersonHavingActivityBetweenDateFilter
arguments:
- "@chill.main.helper.translatable_string"
- "@chill_activity.repository.reason"
- "@translator"
tags: tags:
- #0 register as a filter - #0 register as a filter
name: chill.export_filter name: chill.export_filter
alias: 'activity_person_having_ac_bw_date_filter' alias: 'activity_person_having_ac_bw_date_filter'
chill.activity.export.reason_aggregator: chill.activity.export.reason_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator class: Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator
arguments:
- "@chill_activity.repository.reason_category"
- "@chill_activity.repository.reason"
- "@chill.main.helper.translatable_string"
tags: tags:
- { name: chill.export_aggregator, alias: activity_reason_aggregator } - { name: chill.export_aggregator, alias: activity_reason_aggregator }
chill.activity.export.type_aggregator: chill.activity.export.type_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ActivityTypeAggregator class: Chill\ActivityBundle\Export\Aggregator\ActivityTypeAggregator
arguments:
- "@chill_activity.repository.activity_type"
- "@chill.main.helper.translatable_string"
tags: tags:
- { name: chill.export_aggregator, alias: activity_type_aggregator } - { name: chill.export_aggregator, alias: activity_type_aggregator }
chill.activity.export.user_aggregator: chill.activity.export.user_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator
arguments:
$em: "@doctrine.orm.entity_manager"
tags: tags:
- { name: chill.export_aggregator, alias: activity_user_aggregator } - { name: chill.export_aggregator, alias: activity_user_aggregator }

View File

@ -1,4 +1,6 @@
services: services:
Chill\ActivityBundle\DataFixtures\ORM\: Chill\ActivityBundle\DataFixtures\ORM\:
autowire: true
autoconfigure: true
resource: ../../DataFixtures/ORM resource: ../../DataFixtures/ORM
tags: [ 'doctrine.fixture.orm' ] tags: [ 'doctrine.fixture.orm' ]

View File

@ -1,28 +1,12 @@
--- ---
services: services:
chill_activity.repository.activity_type: chill_activity.repository.activity_type: '@Chill\ActivityBundle\Repository\ActivityTypeRepository'
class: Doctrine\ORM\EntityRepository chill_activity.repository.reason: '@Chill\ActivityBundle\Repository\ActivityReasonRepository'
factory: ['@doctrine.orm.entity_manager', getRepository] chill_activity.repository.reason_category: '@Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository'
arguments:
- 'Chill\ActivityBundle\Entity\ActivityType'
chill_activity.repository.reason:
class: Doctrine\ORM\EntityRepository
factory: ['@doctrine.orm.entity_manager', getRepository]
arguments:
- 'Chill\ActivityBundle\Entity\ActivityReason'
chill_activity.repository.reason_category:
class: Doctrine\ORM\EntityRepository
factory: ['@doctrine.orm.entity_manager', getRepository]
arguments:
- 'Chill\ActivityBundle\Entity\ActivityReasonCategory'
Chill\ActivityBundle\Repository\ActivityRepository:
tags: [doctrine.repository_service]
arguments:
- '@Doctrine\Persistence\ManagerRegistry'
# This is not a repository but merely a service. It needs to be moved out for simplicity.
# The autowire and autoconfigure should be enabled globally and removed from the definition of the service.
# Once autoloaded, there is no need to alias it to the interface here, it will be done automatically by Symfony.
Chill\ActivityBundle\Repository\ActivityACLAwareRepository: Chill\ActivityBundle\Repository\ActivityACLAwareRepository:
autowire: true autowire: true
autoconfigure: true autoconfigure: true

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Activity;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173555 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'activitytype.name',
'activitytypecategory.name'
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@ -15,8 +15,7 @@ use Doctrine\Common\Collections\Criteria;
final class AsideActivityController extends CRUDController final class AsideActivityController extends CRUDController
{ {
private AsideActivityCategoryRepository $categoryRepository;
private $categoryRepository;
public function __construct(AsideActivityCategoryRepository $categoryRepository) public function __construct(AsideActivityCategoryRepository $categoryRepository)
{ {
@ -25,7 +24,7 @@ final class AsideActivityController extends CRUDController
protected function buildQueryEntities(string $action, Request $request, ?FilterOrderHelper $filterOrder = null) protected function buildQueryEntities(string $action, Request $request, ?FilterOrderHelper $filterOrder = null)
{ {
$qb = parent::buildQueryEntities($action, $request, $filterOrder); $qb = parent::buildQueryEntities($action, $request);
if ('index' === $action) { if ('index' === $action) {
$qb->where($qb->expr()->eq('e.agent', ':user')); $qb->where($qb->expr()->eq('e.agent', ':user'));

View File

@ -19,8 +19,6 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte
{ {
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @phpstan-ignore-next-line
*/ */
public function load(array $configs, ContainerBuilder $container): void public function load(array $configs, ContainerBuilder $container): void
{ {
@ -111,4 +109,4 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte
] ]
]); ]);
} }
} }

View File

@ -44,13 +44,15 @@ class AsideActivityCategory
* @ORM\ManyToOne(targetEntity=AsideActivityCategory::class, inversedBy="children") * @ORM\ManyToOne(targetEntity=AsideActivityCategory::class, inversedBy="children")
* @ORM\JoinColumn(nullable=true) * @ORM\JoinColumn(nullable=true)
*/ */
private $parent; private ?AsideActivityCategory $parent = null;
/** /**
* @ORM\OneToMany(targetEntity=AsideActivityCategory::class, mappedBy="parent") * @ORM\OneToMany(targetEntity=AsideActivityCategory::class, mappedBy="parent")
*/ */
private $children; private $children;
private AsideActivityCategory $oldParent;
public function __construct() public function __construct()
{ {
$this->children = new ArrayCollection(); $this->children = new ArrayCollection();

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\AsideActivityBundle\Form; namespace Chill\AsideActivityBundle\Form;
use Chill\AsideActivityBundle\Entity\AsideActivityCategory; use Chill\AsideActivityBundle\Entity\AsideActivityCategory;
@ -14,12 +16,11 @@ use Symfony\Component\Form\FormBuilderInterface;
final class AsideActivityCategoryType extends AbstractType final class AsideActivityCategoryType extends AbstractType
{ {
private CategoryRender $categoryRender;
protected $translatableStringHelper; public function __construct(
CategoryRender $categoryRender
public function __construct(TranslatableStringHelper $translatableStringHelper, CategoryRender $categoryRender) ) {
{
$this->translatableStringHelper = $translatableStringHelper;
$this->categoryRender = $categoryRender; $this->categoryRender = $categoryRender;
} }

View File

@ -25,18 +25,16 @@ use Symfony\Component\Templating\EngineInterface;
final class AsideActivityFormType extends AbstractType final class AsideActivityFormType extends AbstractType
{ {
protected array $timeChoices; private array $timeChoices;
private TokenStorageInterface $storage; private TokenStorageInterface $storage;
private CategoryRender $categoryRender; private CategoryRender $categoryRender;
public function __construct ( public function __construct (
TranslatableStringHelper $translatableStringHelper,
ParameterBagInterface $parameterBag, ParameterBagInterface $parameterBag,
TokenStorageInterface $storage, TokenStorageInterface $storage,
CategoryRender $categoryRender CategoryRender $categoryRender
){ ){
$this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration'); $this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration');
$this->translatableStringHelper = $translatableStringHelper;
$this->storage = $storage; $this->storage = $storage;
$this->categoryRender = $categoryRender; $this->categoryRender = $categoryRender;
} }

View File

@ -30,7 +30,9 @@ final class CategoryRender implements ChillEntityRenderInterface
{ {
$options = array_merge(self::DEFAULT_ARGS, $options); $options = array_merge(self::DEFAULT_ARGS, $options);
$titles[] = $this->translatableStringHelper->localize($asideActivityCategory->getTitle()); $titles = [
$this->translatableStringHelper->localize($asideActivityCategory->getTitle()),
];
while ($asideActivityCategory->hasParent()) { while ($asideActivityCategory->hasParent()) {
$asideActivityCategory = $asideActivityCategory->getParent(); $asideActivityCategory = $asideActivityCategory->getParent();

View File

@ -1,42 +1,34 @@
<?php <?php
declare(strict_types=1);
namespace Chill\AMLI\BudgetBundle\Form; namespace Chill\AMLI\BudgetBundle\Form;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType; use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\AMLI\BudgetBundle\Config\ConfigRepository; use Chill\AMLI\BudgetBundle\Config\ConfigRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\AMLI\BudgetBundle\Entity\Charge; use Chill\AMLI\BudgetBundle\Entity\Charge;
class ChargeType extends AbstractType class ChargeType extends AbstractType
{ {
/** protected ConfigRepository $configRepository;
*
* @var ConfigRepository protected TranslatableStringHelperInterface $translatableStringHelper;
*/
protected $configRepository;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
public function __construct( public function __construct(
ConfigRepository $configRepository, ConfigRepository $configRepository,
TranslatableStringHelper $translatableStringHelper TranslatableStringHelperInterface $translatableStringHelper
) { ) {
$this->configRepository = $configRepository; $this->configRepository = $configRepository;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder $builder
@ -45,24 +37,23 @@ class ChargeType extends AbstractType
'placeholder' => 'Choose a charge type' 'placeholder' => 'Choose a charge type'
]) ])
->add('amount', MoneyType::class) ->add('amount', MoneyType::class)
->add('comment', TextAreaType::class, [ ->add('comment', TextareaType::class, [
'required' => false 'required' => false
]) ]);
;
if ($options['show_start_date']) { if ($options['show_start_date']) {
$builder->add('startDate', ChillDateType::class, [ $builder->add('startDate', ChillDateType::class, [
'label' => 'Start of validity period' 'label' => 'Start of validity period'
]); ]);
} }
if ($options['show_end_date']) { if ($options['show_end_date']) {
$builder->add('endDate', ChillDateType::class, [ $builder->add('endDate', ChillDateType::class, [
'required' => false, 'required' => false,
'label' => 'End of validity period' 'label' => 'End of validity period'
]); ]);
} }
if ($options['show_help']) { if ($options['show_help']) {
$builder->add('help', ChoiceType::class, [ $builder->add('help', ChoiceType::class, [
'choices' => [ 'choices' => [
@ -77,48 +68,39 @@ class ChargeType extends AbstractType
]); ]);
} }
} }
private function getTypes() private function getTypes()
{ {
$charges = $this->configRepository $charges = $this->configRepository
->getChargesLabels(); ->getChargesLabels();
// rewrite labels to filter in language // rewrite labels to filter in language
foreach ($charges as $key => $labels) { foreach ($charges as $key => $labels) {
$charges[$key] = $this->translatableStringHelper->localize($labels); $charges[$key] = $this->translatableStringHelper->localize($labels);
} }
\asort($charges); \asort($charges);
return \array_flip($charges); return \array_flip($charges);
} }
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults(array( $resolver->setDefaults(array(
'data_class' => 'Chill\AMLI\BudgetBundle\Entity\Charge', 'data_class' => Charge::class,
'show_start_date' => true, 'show_start_date' => true,
'show_end_date' => true, 'show_end_date' => true,
'show_help' => true 'show_help' => true
)); ));
$resolver $resolver
->setAllowedTypes('show_start_date', 'boolean') ->setAllowedTypes('show_start_date', 'boolean')
->setAllowedTypes('show_end_date', 'boolean') ->setAllowedTypes('show_end_date', 'boolean')
->setAllowedTypes('show_help', 'boolean') ->setAllowedTypes('show_help', 'boolean');
;
} }
/**
* {@inheritdoc}
*/
public function getBlockPrefix() public function getBlockPrefix()
{ {
return 'chill_amli_budgetbundle_charge'; return 'chill_amli_budgetbundle_charge';
} }
} }

View File

@ -1,7 +1,10 @@
<?php <?php
declare(strict_types=1);
namespace Chill\AMLI\BudgetBundle\Form; namespace Chill\AMLI\BudgetBundle\Form;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
@ -10,32 +13,22 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType; use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\AMLI\BudgetBundle\Config\ConfigRepository; use Chill\AMLI\BudgetBundle\Config\ConfigRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextareaType;
class ResourceType extends AbstractType class ResourceType extends AbstractType
{ {
/** protected ConfigRepository $configRepository;
*
* @var ConfigRepository protected TranslatableStringHelperInterface $translatableStringHelper;
*/
protected $configRepository;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
public function __construct( public function __construct(
ConfigRepository $configRepository, ConfigRepository $configRepository,
TranslatableStringHelper $translatableStringHelper TranslatableStringHelperInterface $translatableStringHelper
) { ) {
$this->configRepository = $configRepository; $this->configRepository = $configRepository;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder $builder
@ -45,17 +38,16 @@ class ResourceType extends AbstractType
'label' => 'Resource element type' 'label' => 'Resource element type'
]) ])
->add('amount', MoneyType::class) ->add('amount', MoneyType::class)
->add('comment', TextAreaType::class, [ ->add('comment', TextareaType::class, [
'required' => false 'required' => false
]) ]);
;
if ($options['show_start_date']) { if ($options['show_start_date']) {
$builder->add('startDate', ChillDateType::class, [ $builder->add('startDate', ChillDateType::class, [
'label' => 'Start of validity period' 'label' => 'Start of validity period'
]); ]);
} }
if ($options['show_end_date']) { if ($options['show_end_date']) {
$builder->add('endDate', ChillDateType::class, [ $builder->add('endDate', ChillDateType::class, [
'required' => false, 'required' => false,
@ -63,25 +55,7 @@ class ResourceType extends AbstractType
]); ]);
} }
} }
private function getTypes()
{
$resources = $this->configRepository
->getResourcesLabels();
// rewrite labels to filter in language
foreach ($resources as $key => $labels) {
$resources[$key] = $this->translatableStringHelper->localize($labels);
}
asort($resources);
return \array_flip($resources);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults(array( $resolver->setDefaults(array(
@ -89,20 +63,29 @@ class ResourceType extends AbstractType
'show_start_date' => true, 'show_start_date' => true,
'show_end_date' => true 'show_end_date' => true
)); ));
$resolver $resolver
->setAllowedTypes('show_start_date', 'boolean') ->setAllowedTypes('show_start_date', 'boolean')
->setAllowedTypes('show_end_date', 'boolean') ->setAllowedTypes('show_end_date', 'boolean');
;
} }
/**
* {@inheritdoc}
*/
public function getBlockPrefix() public function getBlockPrefix()
{ {
return 'chill_amli_budgetbundle_resource'; return 'chill_amli_budgetbundle_resource';
} }
private function getTypes()
{
$resources = $this->configRepository
->getResourcesLabels();
// rewrite labels to filter in language
foreach ($resources as $key => $labels) {
$resources[$key] = $this->translatableStringHelper->localize($labels);
}
asort($resources);
return \array_flip($resources);
}
} }

View File

@ -1,24 +1,6 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\CalendarBundle\Controller; namespace Chill\CalendarBundle\Controller;
@ -26,9 +8,11 @@ use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Privacy\PrivacyEvent; use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Form; use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
@ -42,11 +26,6 @@ use Chill\MainBundle\Pagination\PaginatorFactory;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
/**
* Class CalendarController
*
* @package Chill\CalendarBundle\Controller
*/
class CalendarController extends AbstractController class CalendarController extends AbstractController
{ {
protected EventDispatcherInterface $eventDispatcher; protected EventDispatcherInterface $eventDispatcher;
@ -97,9 +76,9 @@ class CalendarController extends AbstractController
'calendarItems' => $calendarItems, 'calendarItems' => $calendarItems,
'user' => $user 'user' => $user
]); ]);
}
} elseif ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$total = $this->calendarRepository->countByAccompanyingPeriod($accompanyingPeriod); $total = $this->calendarRepository->countByAccompanyingPeriod($accompanyingPeriod);
$paginator = $this->paginator->create($total); $paginator = $this->paginator->create($total);
$calendarItems = $this->calendarRepository->findBy( $calendarItems = $this->calendarRepository->findBy(
@ -117,6 +96,8 @@ class CalendarController extends AbstractController
'paginator' => $paginator 'paginator' => $paginator
]); ]);
} }
throw new \Exception('Unable to list actions.');
} }
/** /**
@ -125,13 +106,14 @@ class CalendarController extends AbstractController
*/ */
public function newAction(Request $request): Response public function newAction(Request $request): Response
{ {
$view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/newByAccompanyingCourse.html.twig'; $view = '@ChillCalendar/Calendar/newByAccompanyingCourse.html.twig';
} }
// elseif ($user instanceof User) { // elseif ($user instanceof User) {
// $view = '@ChillCalendar/Calendar/newUser.html.twig'; // $view = '@ChillCalendar/Calendar/newUser.html.twig';
// } // }
@ -161,17 +143,18 @@ class CalendarController extends AbstractController
$params = $this->buildParamsToUrl($user, $accompanyingPeriod); $params = $this->buildParamsToUrl($user, $accompanyingPeriod);
return $this->redirectToRoute('chill_calendar_calendar_list', $params); return $this->redirectToRoute('chill_calendar_calendar_list', $params);
} elseif ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors'));
} }
if ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors'));
}
if ($view === null) { if ($view === null) {
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
$entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']); $entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [ return $this->render($view, [
'user' => $user, 'user' => $user,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
@ -187,44 +170,49 @@ class CalendarController extends AbstractController
*/ */
public function showAction(Request $request, $id): Response public function showAction(Request $request, $id): Response
{ {
$view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/showByAccompanyingCourse.html.twig'; $view = '@ChillCalendar/Calendar/showByAccompanyingCourse.html.twig';
} }
elseif ($user instanceof User) { elseif ($user instanceof User) {
$view = '@ChillCalendar/Calendar/showByUser.html.twig'; $view = '@ChillCalendar/Calendar/showByUser.html.twig';
} }
$entity = $em->getRepository('ChillCalendarBundle:Calendar')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Calendar entity.');
}
if (null !== $accompanyingPeriod) {
$entity->personsAssociated = $entity->getPersonsAssociated();
$entity->personsNotAssociated = $entity->getPersonsNotAssociated();
}
// $deleteForm = $this->createDeleteForm($id, $accompanyingPeriod);
if ($view === null) { if ($view === null) {
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
$personsId = []; /** @var Calendar $entity */
foreach ($entity->getPersons() as $p) { $entity = $em->getRepository('ChillCalendarBundle:Calendar')->find($id);
array_push($personsId, $p->getId());
if (null === $entity) {
throw $this->createNotFoundException('Unable to find Calendar entity.');
} }
$professionalsId = []; if (null !== $accompanyingPeriod) {
foreach ($entity->getProfessionals() as $p) { // @TODO: These properties are declared dynamically.
array_push($professionalsId, $p->getId()); // It must be removed.
// @See https://wiki.php.net/rfc/deprecate_dynamic_properties
$entity->personsAssociated = $entity->getPersonsAssociated();
$entity->personsNotAssociated = $entity->getPersonsNotAssociated();
} }
// $deleteForm = $this->createDeleteForm($id, $accompanyingPeriod);
$personsId = array_map(
static fn (Person $p): int => $p->getId(),
$entity->getPersons()
);
$professionalsId = array_map(
static fn (ThirdParty $thirdParty): ?int => $thirdParty->getId(),
$entity->getProfessionals()
);
$durationTime = $entity->getEndDate()->diff($entity->getStartDate()); $durationTime = $entity->getEndDate()->diff($entity->getStartDate());
$durationTimeInMinutes = $durationTime->days*1440 + $durationTime->h*60 + $durationTime->i; $durationTimeInMinutes = $durationTime->days*1440 + $durationTime->h*60 + $durationTime->i;
@ -240,7 +228,7 @@ class CalendarController extends AbstractController
return $this->render($view, [ return $this->render($view, [
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'entity' => $entity, 'entity' => $entity,
'user' => $user, 'user' => $user,
'activityData' => $activityData 'activityData' => $activityData
//'delete_form' => $deleteForm->createView(), //'delete_form' => $deleteForm->createView(),
@ -255,6 +243,7 @@ class CalendarController extends AbstractController
*/ */
public function editAction($id, Request $request): Response public function editAction($id, Request $request): Response
{ {
$view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
@ -283,8 +272,11 @@ class CalendarController extends AbstractController
$this->addFlash('success', $this->get('translator')->trans('Success : calendar item updated!')); $this->addFlash('success', $this->get('translator')->trans('Success : calendar item updated!'));
$params = $this->buildParamsToUrl($user, $accompanyingPeriod); $params = $this->buildParamsToUrl($user, $accompanyingPeriod);
return $this->redirectToRoute('chill_calendar_calendar_list', $params); return $this->redirectToRoute('chill_calendar_calendar_list', $params);
} elseif ($form->isSubmitted() and !$form->isValid()) { }
if ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors')); $this->addFlash('error', $this->get('translator')->trans('This form contains errors'));
} }
@ -295,7 +287,7 @@ class CalendarController extends AbstractController
} }
$entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']); $entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [ return $this->render($view, [
'entity' => $entity, 'entity' => $entity,
'form' => $form->createView(), 'form' => $form->createView(),
@ -312,13 +304,14 @@ class CalendarController extends AbstractController
*/ */
public function deleteAction(Request $request, $id) public function deleteAction(Request $request, $id)
{ {
$view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) { if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/confirm_deleteByAccompanyingCourse.html.twig'; $view = '@ChillCalendar/Calendar/confirm_deleteByAccompanyingCourse.html.twig';
} }
elseif ($user instanceof User) { elseif ($user instanceof User) {
$view = '@ChillCalendar/Calendar/confirm_deleteByUser.html.twig'; $view = '@ChillCalendar/Calendar/confirm_deleteByUser.html.twig';
} }
@ -367,7 +360,7 @@ class CalendarController extends AbstractController
/** /**
* Creates a form to delete a Calendar entity by id. * Creates a form to delete a Calendar entity by id.
*/ */
private function createDeleteForm(int $id, ?User $user, ?AccompanyingPeriod $accompanyingPeriod): Form private function createDeleteForm(int $id, ?User $user, ?AccompanyingPeriod $accompanyingPeriod): FormInterface
{ {
$params = $this->buildParamsToUrl($user, $accompanyingPeriod); $params = $this->buildParamsToUrl($user, $accompanyingPeriod);
$params['id'] = $id; $params['id'] = $id;
@ -414,17 +407,14 @@ class CalendarController extends AbstractController
]; ];
} }
private function buildParamsToUrl( private function buildParamsToUrl(?User $user, ?AccompanyingPeriod $accompanyingPeriod): array {
?User $user,
?AccompanyingPeriod $accompanyingPeriod
): array {
$params = []; $params = [];
if ($user) { if (null !== $user) {
$params['user_id'] = $user->getId(); $params['user_id'] = $user->getId();
} }
if ($accompanyingPeriod) { if (null !== $accompanyingPeriod) {
$params['accompanying_period_id'] = $accompanyingPeriod->getId(); $params['accompanying_period_id'] = $accompanyingPeriod->getId();
} }

View File

@ -1,25 +1,30 @@
<?php <?php
declare(strict_types=1);
namespace Chill\CalendarBundle\DataFixtures\ORM; namespace Chill\CalendarBundle\DataFixtures\ORM;
use Chill\CalendarBundle\Entity\CalendarRange; use Chill\CalendarBundle\Entity\CalendarRange;
use Chill\MainBundle\DataFixtures\ORM\LoadUsers; use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\UserRepository;
use DateTimeImmutable; use DateTimeImmutable;
use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface; use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
class LoadCalendarRange extends Fixture implements FixtureGroupInterface, OrderedFixtureInterface class LoadCalendarRange extends Fixture implements FixtureGroupInterface, OrderedFixtureInterface
{ {
private UserRepository $userRepository;
public static array $references = [];
public function __construct( public function __construct(
EntityManagerInterface $em UserRepository $userRepository
) { ) {
$this->userRepository = $em->getRepository(User::class); $this->userRepository = $userRepository;
} }
public function getOrder(): int public function getOrder(): int
@ -32,12 +37,10 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
return ['calendar']; return ['calendar'];
} }
public static $references = [];
public function load(ObjectManager $manager): void public function load(ObjectManager $manager): void
{ {
$arr = range(-50, 50); $arr = range(-50, 50);
print "Creating calendar range ('plage de disponibilités')\n"; print "Creating calendar range ('plage de disponibilités')\n";
$users = $this->userRepository->findAll(); $users = $this->userRepository->findAll();
@ -70,7 +73,7 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
->setUser($u) ->setUser($u)
->setStartDate($startEvent) ->setStartDate($startEvent)
->setEndDate($endEvent); ->setEndDate($endEvent);
$manager->persist($calendarRange); $manager->persist($calendarRange);
} }
@ -79,4 +82,4 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
} }
$manager->flush(); $manager->flush();
} }
} }

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Calendar;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173557 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'chill_calendar.cancel_reason.name',
'chill_calendar.invite.status',
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@ -45,25 +45,25 @@ class CreateFieldsOnGroupCommand extends Command
{ {
const ARG_PATH = 'path'; const ARG_PATH = 'path';
const ARG_DELETE = 'delete'; const ARG_DELETE = 'delete';
/** /**
* @var CustomFieldProvider * @var CustomFieldProvider
*/ */
private $customFieldProvider; private $customFieldProvider;
/** /**
* @var EntityManager * @var EntityManager
*/ */
private $entityManager; private $entityManager;
/** /**
* @var ValidatorInterface * @var ValidatorInterface
*/ */
private $validator; private $validator;
private $availableLanguages; private $availableLanguages;
private $customizablesEntities; private $customizablesEntities;
/** /**
* CreateFieldsOnGroupCommand constructor. * CreateFieldsOnGroupCommand constructor.
* *
@ -87,7 +87,7 @@ class CreateFieldsOnGroupCommand extends Command
$this->customizablesEntities = $customizablesEntities; $this->customizablesEntities = $customizablesEntities;
parent::__construct(); parent::__construct();
} }
protected function configure() protected function configure()
{ {
$this->setName('chill:custom_fields:populate_group') $this->setName('chill:custom_fields:populate_group')
@ -111,7 +111,7 @@ class CreateFieldsOnGroupCommand extends Command
$em->remove($field); $em->remove($field);
} }
} }
/** /**
* @param InputInterface $input * @param InputInterface $input
* @param OutputInterface $output * @param OutputInterface $output
@ -120,18 +120,18 @@ class CreateFieldsOnGroupCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$helper = $this->getHelperSet()->get('question'); $helper = $this->getHelperSet()->get('question');
$em = $this->entityManager; $em = $this->entityManager;
$customFieldsGroups = $em $customFieldsGroups = $em
->getRepository('ChillCustomFieldsBundle:CustomFieldsGroup') ->getRepository('ChillCustomFieldsBundle:CustomFieldsGroup')
->findAll(); ->findAll();
if (count($customFieldsGroups) === 0) { if (count($customFieldsGroups) === 0) {
$output->writeln('<error>There aren\'t any CustomFieldsGroup recorded' $output->writeln('<error>There aren\'t any CustomFieldsGroup recorded'
. ' Please create at least one.</error>'); . ' Please create at least one.</error>');
} }
$table = new Table($output); $table = new Table($output);
$table $table
->setHeaders(array_merge( ->setHeaders(array_merge(
@ -141,7 +141,7 @@ class CreateFieldsOnGroupCommand extends Command
->setRows($this->_prepareRows($customFieldsGroups)) ->setRows($this->_prepareRows($customFieldsGroups))
->render() ->render()
; ;
$question = new Question( $question = new Question(
"Enter the customfieldGroup's id on which the custom fields should be added: "); "Enter the customfieldGroup's id on which the custom fields should be added: ");
$question->setNormalizer( $question->setNormalizer(
@ -151,24 +151,23 @@ class CreateFieldsOnGroupCommand extends Command
return $customFieldsGroup; return $customFieldsGroup;
} }
} }
throw new \RunTimeException('The id does not match an existing ' throw new \RuntimeException('The id does not match an existing CustomFieldsGroup');
. 'CustomFieldsGroup');
} }
); );
$customFieldsGroup = $helper->ask($input, $output, $question); $customFieldsGroup = $helper->ask($input, $output, $question);
if ($input->getOption(self::ARG_DELETE)) { if ($input->getOption(self::ARG_DELETE)) {
$this->deleteFieldsForCFGroup($customFieldsGroup); $this->deleteFieldsForCFGroup($customFieldsGroup);
} }
$fieldsInput = $this->_parse($input->getArgument(self::ARG_PATH), $fieldsInput = $this->_parse($input->getArgument(self::ARG_PATH),
$output); $output);
$fields = $this->_addFields($customFieldsGroup, $fieldsInput, $output); $fields = $this->_addFields($customFieldsGroup, $fieldsInput, $output);
} }
private function _prepareRows ($customFieldsGroups) private function _prepareRows ($customFieldsGroups)
{ {
$rows = array(); $rows = array();
$languages = $this->availableLanguages; $languages = $this->availableLanguages;
@ -177,8 +176,8 @@ class CreateFieldsOnGroupCommand extends Command
foreach ($this->customizablesEntities as $entry) { foreach ($this->customizablesEntities as $entry) {
$customizableEntities[$entry['class']] = $entry['name']; $customizableEntities[$entry['class']] = $entry['name'];
} }
array_walk($customFieldsGroups, array_walk($customFieldsGroups,
function(CustomFieldsGroup $customFieldGroup, $key) function(CustomFieldsGroup $customFieldGroup, $key)
use ($languages, &$rows, $customizableEntities) { use ($languages, &$rows, $customizableEntities) {
//set id and entity //set id and entity
@ -186,7 +185,7 @@ class CreateFieldsOnGroupCommand extends Command
$customFieldGroup->getId(), $customFieldGroup->getId(),
$customizableEntities[$customFieldGroup->getEntity()] $customizableEntities[$customFieldGroup->getEntity()]
); );
foreach ($languages as $lang) { foreach ($languages as $lang) {
//todo replace with service to find lang when available //todo replace with service to find lang when available
$row[] = (isset($customFieldGroup->getName()[$lang])) ? $row[] = (isset($customFieldGroup->getName()[$lang])) ?
@ -196,42 +195,42 @@ class CreateFieldsOnGroupCommand extends Command
$rows[] = $row; $rows[] = $row;
} }
); );
return $rows; return $rows;
} }
private function _parse($path, OutputInterface $output) private function _parse($path, OutputInterface $output)
{ {
$parser = new Parser(); $parser = new Parser();
if (!file_exists($path)) { if (!file_exists($path)) {
throw new \RunTimeException("file does not exist"); throw new \RuntimeException("file does not exist");
} }
try { try {
$values = $parser->parse(file_get_contents($path)); $values = $parser->parse(file_get_contents($path));
} catch (ParseException $ex) { } catch (ParseException $ex) {
throw new \RunTimeException("The yaml file is not valid", 0, $ex); throw new \RuntimeException("The yaml file is not valid", 0, $ex);
} }
return $values; return $values;
} }
private function _addFields(CustomFieldsGroup $group, $values, OutputInterface $output) private function _addFields(CustomFieldsGroup $group, $values, OutputInterface $output)
{ {
$em = $this->entityManager; $em = $this->entityManager;
$languages = $this->availableLanguages; $languages = $this->availableLanguages;
foreach($values['fields'] as $slug => $field) { foreach($values['fields'] as $slug => $field) {
//check the cf type exists //check the cf type exists
$cfType = $this->customFieldProvider->getCustomFieldByType($field['type']); $cfType = $this->customFieldProvider->getCustomFieldByType($field['type']);
if ($cfType === NULL) { if ($cfType === NULL) {
throw new \RunTimeException('the type '.$field['type'].' ' throw new \RuntimeException('the type '.$field['type'].' '
. 'does not exists'); . 'does not exists');
} }
$cf = new CustomField(); $cf = new CustomField();
$cf->setSlug($slug) $cf->setSlug($slug)
->setName($field['name']) ->setName($field['name'])
@ -239,7 +238,7 @@ class CreateFieldsOnGroupCommand extends Command
->setOrdering($field['ordering']) ->setOrdering($field['ordering'])
->setType($field['type']) ->setType($field['type'])
->setCustomFieldsGroup($group); ->setCustomFieldsGroup($group);
//add to table //add to table
$names = array(); $names = array();
foreach ($languages as $lang) { foreach ($languages as $lang) {
@ -248,17 +247,17 @@ class CreateFieldsOnGroupCommand extends Command
$cf->getName()[$lang] : $cf->getName()[$lang] :
'Not available in this language'; 'Not available in this language';
} }
if ($this->validator->validate($cf)) { if ($this->validator->validate($cf)) {
$em->persist($cf); $em->persist($cf);
$output->writeln("<info>Adding Custom Field of type " $output->writeln("<info>Adding Custom Field of type "
.$cf->getType()."\t with slug ".$cf->getSlug(). .$cf->getType()."\t with slug ".$cf->getSlug().
"\t and names : ".implode($names, ', ')."</info>"); "\t and names : ".implode($names, ', ')."</info>");
} else { } else {
throw new \RunTimeException("Error in field ".$slug); throw new \RuntimeException("Error in field ".$slug);
} }
} }
$em->flush(); $em->flush();
} }
} }

View File

@ -1,21 +1,6 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\CustomFieldsBundle\CustomFields; namespace Chill\CustomFieldsBundle\CustomFields;
@ -29,36 +14,21 @@ use Symfony\Bridge\Twig\TwigEngine;
use Chill\MainBundle\Form\Type\Select2ChoiceType; use Chill\MainBundle\Form\Type\Select2ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class CustomFieldLongChoice extends AbstractCustomField class CustomFieldLongChoice extends AbstractCustomField
{ {
/** private OptionRepository $optionRepository;
*
* @var OptionRepository
*/
private $optionRepository;
/** private TranslatableStringHelper $translatableStringHelper;
*
* @var TranslatableStringHelper
*/
private $translatableStringHelper;
/** private TwigEngine $templating;
* @var TwigEngine
*/
private $templating;
const KEY = 'key'; public const KEY = 'key';
public function __construct(OptionRepository $optionRepository, public function __construct(
OptionRepository $optionRepository,
TranslatableStringHelper $translatableStringHelper, TranslatableStringHelper $translatableStringHelper,
TwigEngine $twigEngine) TwigEngine $twigEngine
{ ) {
$this->optionRepository = $optionRepository; $this->optionRepository = $optionRepository;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
$this->templating = $twigEngine; $this->templating = $twigEngine;
@ -76,12 +46,7 @@ class CustomFieldLongChoice extends AbstractCustomField
'choice_label' => function(Option $option) use ($translatableStringHelper) { 'choice_label' => function(Option $option) use ($translatableStringHelper) {
return $translatableStringHelper->localize($option->getText()); return $translatableStringHelper->localize($option->getText());
}, },
'choice_value' => function ($key) use ($entries) { 'choice_value' => static fn (Option $key): ?int => $key === null ? null : $key->getId(),
if ($key === NULL) {
return null;
}
return $key->getId();
},
'multiple' => false, 'multiple' => false,
'expanded' => false, 'expanded' => false,
'required' => $customField->isRequired(), 'required' => $customField->isRequired(),
@ -89,15 +54,16 @@ class CustomFieldLongChoice extends AbstractCustomField
'group_by' => function(Option $option) use ($translatableStringHelper) { 'group_by' => function(Option $option) use ($translatableStringHelper) {
if ($option->hasParent()) { if ($option->hasParent()) {
return $translatableStringHelper->localize($option->getParent()->getText()); return $translatableStringHelper->localize($option->getParent()->getText());
} else {
return $translatableStringHelper->localize($option->getText());
} }
return $translatableStringHelper->localize($option->getText());
}, },
'label' => $translatableStringHelper->localize($customField->getName()) 'label' => $translatableStringHelper->localize($customField->getName())
)); ));
$builder->get($customField->getSlug())
->addModelTransformer(new CustomFieldDataTransformer($this, $customField));
$builder
->get($customField->getSlug())
->addModelTransformer(new CustomFieldDataTransformer($this, $customField));
} }
public function buildOptionsForm(FormBuilderInterface $builder) public function buildOptionsForm(FormBuilderInterface $builder)

View File

@ -28,27 +28,31 @@ class ChillCustomFieldsExtension extends Extension implements PrependExtensionIn
$loader->load('services/fixtures.yaml'); $loader->load('services/fixtures.yaml');
$loader->load('services/controller.yaml'); $loader->load('services/controller.yaml');
$loader->load('services/command.yaml'); $loader->load('services/command.yaml');
//add at least a blank array at 'customizable_entities' options //add at least a blank array at 'customizable_entities' options
//$customizable_entities = (isset($config['customizables_entities']) //$customizable_entities = (isset($config['customizables_entities'])
// && $config['customizables_entities'] !== FALSE) // && $config['customizables_entities'] !== FALSE)
// ? $config['customizables_entities'] : array(); // ? $config['customizables_entities'] : array();
$container->setParameter('chill_custom_fields.customizables_entities', $container->setParameter('chill_custom_fields.customizables_entities',
$config['customizables_entities']); $config['customizables_entities']);
$container->setParameter('chill_custom_fields.show_empty_values', $container->setParameter('chill_custom_fields.show_empty_values',
$config['show_empty_values_in_views']); $config['show_empty_values_in_views']);
} }
/* (non-PHPdoc) /* (non-PHPdoc)
* @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend() * @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend()
*/ */
public function prepend(ContainerBuilder $container) public function prepend(ContainerBuilder $container)
{ {
// add form layout to twig resources // add form layout to twig resources
$twigConfig['form_themes'][] = 'ChillCustomFieldsBundle:Form:fields.html.twig'; $twigConfig = [
'form_themes' => [
'ChillCustomFieldsBundle:Form:fields.html.twig',
],
];
$container->prependExtensionConfig('twig', $twigConfig); $container->prependExtensionConfig('twig', $twigConfig);
//add routes for custom bundle //add routes for custom bundle
$container->prependExtensionConfig('chill_main', array( $container->prependExtensionConfig('chill_main', array(
'routing' => array( 'routing' => array(

View File

@ -2,6 +2,7 @@
namespace Chill\CustomFieldsBundle\Form; namespace Chill\CustomFieldsBundle\Form;
use Chill\CustomFieldsBundle\Entity\CustomFieldsGroup;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
@ -15,7 +16,7 @@ use Chill\MainBundle\Form\Type\TranslatableStringFormType;
class CustomFieldsGroupType extends AbstractType class CustomFieldsGroupType extends AbstractType
{ {
private $customizableEntities; //TODO : add comment about this variable private $customizableEntities; //TODO : add comment about this variable
/** /**
@ -52,50 +53,48 @@ class CustomFieldsGroupType extends AbstractType
)) ))
; ;
$builder->addEventListener(FormEvents::POST_SET_DATA, $builder->addEventListener(
function(FormEvent $event) use ($customizableEntities, $builder){ FormEvents::POST_SET_DATA,
$form = $event->getForm(); function(FormEvent $event) use ($customizableEntities, $builder) {
$group = $event->getData(); $form = $event->getForm();
$group = $event->getData();
//stop the function if entity is not set //stop the function if entity is not set
if ($group->getEntity() === NULL) { if ($group->getEntity() === NULL) {
return; return;
} }
if (count($customizableEntities[$group->getEntity()]['options']) > 0) { $optionBuilder = null;
$optionBuilder = $builder if (count($customizableEntities[$group->getEntity()]['options']) > 0) {
->getFormFactory() $optionBuilder = $builder
->createBuilderForProperty( ->getFormFactory()
'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup', ->createBuilderForProperty(CustomFieldsGroup::class, 'options')
'options' ->create(
) 'options',
->create('options', null, array( null,
'compound' => true, [
'auto_initialize' => false, 'compound' => true,
'required' => false) 'auto_initialize' => false,
); 'required' => false
} ]
);
foreach($customizableEntities[$group->getEntity()]['options'] as $key => $option) { foreach($customizableEntities[$group->getEntity()]['options'] as $key => $option) {
$optionBuilder $optionBuilder->add($key, $option['form_type'], $option['form_options']);
->add($key, $option['form_type'], $option['form_options'])
;
}
if (isset($optionBuilder) && $optionBuilder->count() > 0) {
$form->add($optionBuilder->getForm());
} }
}
}); if ((null !== $optionBuilder) && $optionBuilder->count() > 0) {
$form->add($optionBuilder->getForm());
}
});
} }
/**
* @param OptionsResolverInterface $resolver
*/
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults(array( $resolver->setDefaults([
'data_class' => 'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup' 'data_class' => CustomFieldsGroup::class,
)); ]);
} }
/** /**

View File

@ -33,12 +33,12 @@ class CustomFieldsGroupToIdTransformer implements DataTransformerInterface
if (null === $customFieldsGroup) { if (null === $customFieldsGroup) {
return ""; return "";
} }
if (!$customFieldsGroup instanceof CustomFieldsGroup) { if (!$customFieldsGroup instanceof CustomFieldsGroup) {
throw new TransformationFailedException(sprintf('Transformation failed: ' throw new TransformationFailedException(sprintf('Transformation failed: '
. 'the expected type of the transforme function is an ' . 'the expected type of the transforme function is an '
. 'object of type Chill\CustomFieldsBundle\Entity\CustomFieldsGroup, ' . 'object of type Chill\CustomFieldsBundle\Entity\CustomFieldsGroup, '
. '%s given (value : %s)', gettype($customFieldsGroup), . '%s given (value : %s)', gettype($customFieldsGroup),
$customFieldsGroup)); $customFieldsGroup));
} }
@ -57,26 +57,31 @@ class CustomFieldsGroupToIdTransformer implements DataTransformerInterface
if (!$id) { if (!$id) {
return null; return null;
} }
if ($id instanceof CustomFieldsGroup) { if ($id instanceof CustomFieldsGroup) {
throw new TransformationFailedException(sprintf( throw new TransformationFailedException(
sprintf(
'The transformation failed: the expected argument on ' 'The transformation failed: the expected argument on '
. 'reverseTransform is an object of type int,' . 'reverseTransform is an object of type int,'
. 'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup, ' . 'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup, '
. 'given', gettype($id))); . 'given'
)
);
} }
$customFieldsGroup = $this->om $customFieldsGroup = $this->om
->getRepository('ChillCustomFieldsBundle:customFieldsGroup')->find($id) ->getRepository(CustomFieldsGroup::class)->find($id)
; ;
if (null === $customFieldsGroup) { if (null === $customFieldsGroup) {
throw new TransformationFailedException(sprintf( throw new TransformationFailedException(
'Le group avec le numéro "%s" ne peut pas être trouvé!', sprintf(
$id 'Le group avec le numéro "%s" ne peut pas être trouvé!',
)); $id
)
);
} }
return $customFieldsGroup; return $customFieldsGroup;
} }
} }

View File

@ -1,28 +1,27 @@
<?php <?php
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Form\DataTransformer; namespace Chill\CustomFieldsBundle\Form\DataTransformer;
use Chill\CustomFieldsBundle\Entity\CustomField;
use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\DataTransformerInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Doctrine\Common\Collections\ArrayCollection;
class JsonCustomFieldToArrayTransformer implements DataTransformerInterface { class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
/** private ObjectManager $om;
* @var ObjectManager
*/ private array $customField;
private $om;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om) public function __construct(ObjectManager $om)
{ {
$this->om = $om; $this->om = $om;
$customFields = $this->om $customFields = $this->om
->getRepository('ChillCustomFieldsBundle:CustomField') ->getRepository(CustomField::class)
->findAll(); ->findAll();
// @TODO: in the array_map callback, CustomField::getLabel() does not exist. What do we do here?
$customFieldsLablels = array_map( $customFieldsLablels = array_map(
function($e) { return $e->getLabel(); }, function($e) { return $e->getLabel(); },
$customFields); $customFields);
@ -36,20 +35,12 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
{ {
echo $customFieldsJSON; echo $customFieldsJSON;
if($customFieldsJSON === null) { // lors de la creation if($customFieldsJSON === null) {
$customFieldsArray = array(); $customFieldsArray = [];
} else { } else {
$customFieldsArray = json_decode($customFieldsJSON,true); $customFieldsArray = json_decode($customFieldsJSON, true, 512, JSON_THROW_ON_ERROR);
} }
/*
echo "<br> - 4 - <br>";
var_dump($customFieldsArray);
echo "<br> - 5 - <br>";
*/
$customFieldsArrayRet = array(); $customFieldsArrayRet = array();
foreach ($customFieldsArray as $key => $value) { foreach ($customFieldsArray as $key => $value) {
@ -62,7 +53,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
} else { } else {
$entityClass = substr($type, 10, -1); $entityClass = substr($type, 10, -1);
} }
$customFieldsArrayRet[$key] = $this->om $customFieldsArrayRet[$key] = $this->om
->getRepository('ChillCustomFieldsBundle:' . $entityClass) ->getRepository('ChillCustomFieldsBundle:' . $entityClass)
->findOneById($value); ->findOneById($value);
@ -86,10 +77,10 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
{ {
/* /*
echo "<br> - - 7 - <br>"; echo "<br> - - 7 - <br>";
var_dump(array_keys($customFieldsArray)); var_dump(array_keys($customFieldsArray));
echo "<br> - - 8 - <br>"; echo "<br> - - 8 - <br>";
var_dump(array_keys($this->customField)); var_dump(array_keys($this->customField));
@ -112,7 +103,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
//$entityClass = substr($type, 10, -1); //$entityClass = substr($type, 10, -1);
//echo $entityClasss; //echo $entityClasss;
if(strpos($type, 'ManyToOnePersist') === 0) { if(strpos($type, 'ManyToOnePersist') === 0) {
// PEUT ETRE A FAIRE SI SEULEMENT $value->getId() ne renvoie rien... // PEUT ETRE A FAIRE SI SEULEMENT $value->getId() ne renvoie rien...
// //
// //
$this->om->persist($value); // pas bon ici $this->om->persist($value); // pas bon ici
@ -121,7 +112,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
// et faire le persist qd fait sur l'obj parent // et faire le persist qd fait sur l'obj parent
// regarder : http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html // regarder : http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html
// ou : http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html // ou : http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html
// dans yml : // dans yml :
// lifecycleCallbacks: // lifecycleCallbacks:
// prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ] // prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
$this->om->flush(); // sinon l'id pose pbm $this->om->flush(); // sinon l'id pose pbm
@ -142,4 +133,4 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
return json_encode($customFieldsArrayRet); return json_encode($customFieldsArrayRet);
} }
} }

View File

@ -145,5 +145,7 @@ class DocGeneratorTemplateController extends AbstractController
} catch (TransferException $e) { } catch (TransferException $e) {
throw $e; throw $e;
} }
throw new \Exception('Unable to generate document.');
} }
} }

View File

@ -0,0 +1,69 @@
<?php
namespace Chill\DocGeneratorBundle\Serializer\Encoder;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
class DocGenEncoder implements \Symfony\Component\Serializer\Encoder\EncoderInterface
{
/**
* @inheritDoc
*/
public function encode($data, string $format, array $context = [])
{
if (!$this->isAssociative($data)) {
throw new UnexpectedValueException("Only associative arrays are allowed; lists are not allowed");
}
$result = [];
$this->recusiveEncoding($data, $result, '');
return $result;
}
private function recusiveEncoding(array $data, array &$result, $path)
{
if ($this->isAssociative($data)) {
foreach ($data as $key => $value) {
if (\is_array($value)) {
$this->recusiveEncoding($value, $result, $this->canonicalizeKey($path, $key));
} else {
$result[$this->canonicalizeKey($path, $key)] = $value;
}
}
} else {
foreach ($data as $elem) {
if (!$this->isAssociative($elem)) {
throw new UnexpectedValueException(sprintf("Embedded loops are not allowed. See data under %s path", $path));
}
$sub = [];
$this->recusiveEncoding($elem, $sub, '');
$result[$path][] = $sub;
}
}
}
private function canonicalizeKey(string $path, string $key): string
{
return $path === '' ? $key : $path.'_'.$key;
}
private function isAssociative(array $data)
{
$keys = \array_keys($data);
return $keys !== \array_keys($keys);
}
/**
* @inheritDoc
*/
public function supportsEncoding(string $format)
{
return $format === 'docgen';
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace Chill\DocGeneratorBundle\Serializer\Helper;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class NormalizeNullValueHelper
{
private NormalizerInterface $normalizer;
public function __construct(NormalizerInterface $normalizer)
{
$this->normalizer = $normalizer;
}
public function normalize(array $attributes, string $format = 'docgen', ?array $context = [])
{
$data = [];
foreach ($attributes as $key => $class) {
if (is_numeric($key)) {
$data[$class] = '';
} else {
switch ($class) {
case 'array':
case 'bool':
case 'double':
case 'float':
case 'int':
case 'resource':
case 'string':
case 'null':
$data[$key] = '';
break;
default:
$data[$key] = $this->normalizer->normalize(null, $format, \array_merge(
$context,
['docgen:expects' => $class]
));
break;
}
}
}
return $data;
}
}

View File

@ -0,0 +1,177 @@
<?php
namespace Chill\DocGeneratorBundle\Serializer\Normalizer;
use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class DocGenObjectNormalizer implements NormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
private ClassMetadataFactoryInterface $classMetadataFactory;
private PropertyAccessor $propertyAccess;
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory)
{
$this->classMetadataFactory = $classMetadataFactory;
$this->propertyAccess = PropertyAccess::createPropertyAccessor();
}
/**
* @inheritDoc
*/
public function normalize($object, string $format = null, array $context = [])
{
$classMetadataKey = $object ?? $context['docgen:expects'];
if (!$this->classMetadataFactory->hasMetadataFor($classMetadataKey)) {
throw new LogicException(sprintf("This object does not have metadata: %s. Add groups on this entity to allow to serialize with the format %s and groups %s", is_object($object) ? get_class($object) : $context['docgen:expects'], $format, \implode(', ', $context['groups'])));
}
$metadata = $this->classMetadataFactory->getMetadataFor($classMetadataKey);
$expectedGroups = \array_key_exists(AbstractNormalizer::GROUPS, $context) ?
\is_array($context[AbstractNormalizer::GROUPS]) ? $context[AbstractNormalizer::GROUPS] : [$context[AbstractNormalizer::GROUPS]]
: [];
$attributes = \array_filter(
$metadata->getAttributesMetadata(),
function (AttributeMetadata $a) use ($expectedGroups) {
foreach ($a->getGroups() as $g) {
if (\in_array($g, $expectedGroups, true)) {
return true;
}
}
return false;
});
if (null === $object) {
return $this->normalizeNullData($format, $context, $metadata, $attributes);
}
return $this->normalizeObject($object, $format, $context, $expectedGroups, $metadata, $attributes);
}
/**
* @param string $format
* @param array $context
* @param array $expectedGroups
* @param ClassMetadata $metadata
* @param array|AttributeMetadata[] $attributes
*/
private function normalizeNullData(string $format, array $context, ClassMetadata $metadata, array $attributes): array
{
$keys = [];
foreach ($attributes as $attribute) {
$key = $attribute->getSerializedName() ?? $attribute->getName();
$keys[$key] = $this->getExpectedType($attribute, $metadata->getReflectionClass());
}
$normalizer = new NormalizeNullValueHelper($this->normalizer);
return $normalizer->normalize($keys, $format, $context);
}
/**
* @param $object
* @param $format
* @param array $context
* @param array $expectedGroups
* @param ClassMetadata $metadata
* @param array|AttributeMetadata[] $attributes
* @return array
* @throws ExceptionInterface
*/
private function normalizeObject($object, $format, array $context, array $expectedGroups, ClassMetadata $metadata, array $attributes)
{
$data = [];
$reflection = $metadata->getReflectionClass();
foreach ($attributes as $attribute) {
/** @var AttributeMetadata $attribute */
$value = $this->propertyAccess->getValue($object, $attribute->getName());
$key = $attribute->getSerializedName() ?? $attribute->getName();
if (is_object($value)) {
$data[$key] =
$this->normalizer->normalize($value, $format, \array_merge(
$context, $attribute->getNormalizationContextForGroups($expectedGroups)
));
} elseif (null === $value) {
$data[$key] = $this->normalizeNullOutputValue($format, $context, $attribute, $reflection);
} else {
$data[$key] = (string) $value;
}
}
return $data;
}
private function getExpectedType(AttributeMetadata $attribute, \ReflectionClass $reflection): string
{
// we have to get the expected content
if ($reflection->hasProperty($attribute->getName())) {
$type = $reflection->getProperty($attribute->getName())->getType();
} elseif ($reflection->hasMethod($attribute->getName())) {
$type = $reflection->getMethod($attribute->getName())->getReturnType();
} else {
throw new \LogicException(sprintf(
"Could not determine how the content is determined for the attribute %s. Add attribute property only on property or method", $attribute->getName()
));
}
if (null === $type) {
throw new \LogicException(sprintf(
"Could not determine the type for this attribute: %s. Add a return type to the method or property declaration", $attribute->getName()
));
}
return $type->getName();
}
/**
*/
private function normalizeNullOutputValue($format, array $context, AttributeMetadata $attribute, \ReflectionClass $reflection)
{
$type = $this->getExpectedType($attribute, $reflection);
switch ($type) {
case 'array':
case 'bool':
case 'double':
case 'float':
case 'int':
case 'resource':
case 'string':
return '';
default:
return $this->normalizer->normalize(
null,
$format,
\array_merge(
$context,
['docgen:expects' => $type]
)
);
}
}
/**
* @inheritDoc
*/
public function supportsNormalization($data, string $format = null): bool
{
return $format === 'docgen' && (is_object($data) || null === $data);
}
}

View File

@ -8,3 +8,10 @@ services:
autowire: true autowire: true
autoconfigure: true autoconfigure: true
resource: '../Repository/' resource: '../Repository/'
Chill\DocGeneratorBundle\Serializer\Normalizer\:
autowire: true
autoconfigure: true
resource: '../Serializer/Normalizer/'
tags:
- { name: 'serializer.normalizer', priority: -152 }

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\DocGenerator;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173556 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'chill_docgen_template.name'
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@ -0,0 +1,113 @@
<?php
namespace Chill\DocGeneratorBundle\Tests\Serializer\Encoder;
use Chill\DocGeneratorBundle\Serializer\Encoder\DocGenEncoder;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
class DocGenEncoderTest extends TestCase
{
private DocGenEncoder $encoder;
protected function setUp()
{
parent::setUp();
$this->encoder = new DocGenEncoder();
}
/**
* @dataProvider generateEncodeData
*/
public function testEncode($expected, $data, string $msg)
{
$generated = $this->encoder->encode($data, 'docgen');
$this->assertEquals($expected, $generated, $msg);
}
public function testEmbeddedLoopsThrowsException()
{
$this->expectException(UnexpectedValueException::class);
$data = [
'data' => [
['item' => 'one'],
[
'embedded' => [
[
['subitem' => 'two'],
['subitem' => 'three']
]
]
],
]
];
$this->encoder->encode($data, 'docgen');
}
public function generateEncodeData()
{
yield [ ['tests' => 'ok'], ['tests' => 'ok'], "A simple test with a simple array"];
yield [
// expected:
['item_subitem' => 'value'],
// data:
['item' => ['subitem' => 'value']],
"A test with multidimensional array"
];
yield [
// expected:
[ 'data' => [['item' => 'one'], ['item' => 'two']] ],
// data:
[ 'data' => [['item' => 'one'], ['item' => 'two']] ],
"a list of items"
];
yield [
// expected:
[ 'data' => [['item_subitem' => 'alpha'], ['item' => 'two']] ],
// data:
[ 'data' => [['item' => ['subitem' => 'alpha']], ['item' => 'two'] ] ],
"a list of items with multidimensional array inside item"
];
yield [
// expected:
[
'persons' => [
[
'firstname' => 'Jonathan',
'lastname' => 'Dupont',
'dateOfBirth_long' => '16 juin 1981',
'dateOfBirth_short' => '16/06/1981',
'father_firstname' => 'Marcel',
'father_lastname' => 'Dupont',
'father_dateOfBirth_long' => '10 novembre 1953',
'father_dateOfBirth_short' => '10/11/1953'
],
]
],
// data:
[
'persons' => [
[
'firstname' => 'Jonathan',
'lastname' => 'Dupont',
'dateOfBirth' => [ 'long' => '16 juin 1981', 'short' => '16/06/1981'],
'father' => [
'firstname' => 'Marcel',
'lastname' => 'Dupont',
'dateOfBirth' => ['long' => '10 novembre 1953', 'short' => '10/11/1953']
]
],
]
],
"a longer list, with near real data inside and embedded associative arrays"
];
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace Chill\DocGeneratorBundle\tests\Serializer\Normalizer;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class DocGenObjectNormalizerTest extends KernelTestCase
{
private NormalizerInterface $normalizer;
protected function setUp()
{
parent::setUp();
self::bootKernel();
$this->normalizer = self::$container->get(NormalizerInterface::class);
}
public function testNormalizationBasic()
{
$user = new User();
$user->setUsername('User Test');
$user->setMainCenter($center = new Center());
$center->setName('test');
$normalized = $this->normalizer->normalize($user, 'docgen', [ AbstractNormalizer::GROUPS => ['docgen:read']]);
$expected = [
'label' => 'User Test',
'email' => '',
'mainCenter' => [
'name' => 'test'
]
];
$this->assertEquals($expected, $normalized, "test normalization fo an user");
}
public function testNormalizeWithNullValueEmbedded()
{
$user = new User();
$user->setUsername('User Test');
$normalized = $this->normalizer->normalize($user, 'docgen', [ AbstractNormalizer::GROUPS => ['docgen:read']]);
$expected = [
'label' => 'User Test',
'email' => '',
'mainCenter' => [
'name' => ''
]
];
$this->assertEquals($expected, $normalized, "test normalization fo an user with null center");
}
public function testNormalizeNullObjectWithObjectEmbedded()
{
$normalized = $this->normalizer->normalize(null, 'docgen', [
AbstractNormalizer::GROUPS => ['docgen:read'],
'docgen:expects' => User::class,
]);
$expected = [
'label' => '',
'email' => '',
'mainCenter' => [
'name' => ''
]
];
$this->assertEquals($expected, $normalized, "test normalization for a null user");
}
}

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\DocStoreBundle\Controller; namespace Chill\DocStoreBundle\Controller;
use Chill\DocStoreBundle\Entity\DocumentCategory; use Chill\DocStoreBundle\Entity\DocumentCategory;
@ -9,11 +11,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Chill\DocStoreBundle\ChillDocStoreBundle;
/** /**
* Class DocumentCategoryController
*
* @package Chill\DocStoreBundle\Controller
* @Route("/{_locale}/admin/document/category") * @Route("/{_locale}/admin/document/category")
*/ */
class DocumentCategoryController extends AbstractController class DocumentCategoryController extends AbstractController
@ -24,11 +24,14 @@ class DocumentCategoryController extends AbstractController
public function index(): Response public function index(): Response
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$categories = $em->getRepository("ChillDocStoreBundle:DocumentCategory")->findAll(); $categories = $em->getRepository(DocumentCategory::class)->findAll();
return $this->render( return $this->render(
'ChillDocStoreBundle:DocumentCategory:index.html.twig', 'ChillDocStoreBundle:DocumentCategory:index.html.twig',
['document_categories' => $categories]); [
'document_categories' => $categories,
]
);
} }
/** /**
@ -37,13 +40,10 @@ class DocumentCategoryController extends AbstractController
public function new(Request $request): Response public function new(Request $request): Response
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$documentCategory = new DocumentCategory(); $documentCategory = new DocumentCategory(
$documentCategory ChillDocStoreBundle::class,
->setBundleId('Chill\DocStoreBundle\ChillDocStoreBundle'); $em->getRepository(DocumentCategory::class)->nextIdInsideBundle()
$documentCategory );
->setIdInsideBundle(
$em->getRepository("ChillDocStoreBundle:DocumentCategory")
->nextIdInsideBundle());
$documentCategory $documentCategory
->setDocumentClass(PersonDocument::class); ->setDocumentClass(PersonDocument::class);
@ -56,11 +56,10 @@ class DocumentCategoryController extends AbstractController
$em->flush(); $em->flush();
return $this->redirectToRoute('document_category_index'); return $this->redirectToRoute('document_category_index');
} else {
$documentCategory->setBundleId(
'Chill\DocStoreBundle\ChillDocStoreBundle');
} }
$documentCategory->setBundleId(ChillDocStoreBundle::class);
return $this->render('ChillDocStoreBundle:DocumentCategory:new.html.twig', [ return $this->render('ChillDocStoreBundle:DocumentCategory:new.html.twig', [
'document_category' => $documentCategory, 'document_category' => $documentCategory,
'form' => $form->createView(), 'form' => $form->createView(),

View File

@ -38,7 +38,7 @@ class LoadDocumentACL extends AbstractFixture implements OrderedFixtureInterface
return 35000; return 35000;
} }
public function load(ObjectManager $manager) public function load(ObjectManager $manager)
{ {
foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) { foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) {
@ -57,15 +57,15 @@ class LoadDocumentACL extends AbstractFixture implements OrderedFixtureInterface
break; break;
case 'administrative': case 'administrative':
case 'direction': case 'direction':
if (in_array($scope->getName()['en'], array('administrative', 'social'))) { if (in_array($scope->getName()['en'], array('administrative', 'social'), true)) {
printf("denying power on %s\n", $scope->getName()['en']); printf("denying power on %s\n", $scope->getName()['en']);
break 2; // we do not want any power on social or administrative break 2; // we do not want any power on social or administrative
} }
break; break;
} }
printf("Adding Person report acl to %s " printf("Adding Person report acl to %s "
. "permission group, scope '%s' \n", . "permission group, scope '%s' \n",
$permissionsGroup->getName(), $scope->getName()['en']); $permissionsGroup->getName(), $scope->getName()['en']);
$roleScopeUpdate = (new RoleScope()) $roleScopeUpdate = (new RoleScope())
->setRole(PersonDocumentVoter::CREATE) ->setRole(PersonDocumentVoter::CREATE)
@ -83,9 +83,9 @@ class LoadDocumentACL extends AbstractFixture implements OrderedFixtureInterface
$manager->persist($roleScopeCreate); $manager->persist($roleScopeCreate);
$manager->persist($roleScopeDelete); $manager->persist($roleScopeDelete);
} }
} }
$manager->flush(); $manager->flush();
} }

View File

@ -1,8 +1,10 @@
<?php <?php
namespace Chill\DocStoreBundle\Repository; declare(strict_types=1);
use App\Entity\AccompanyingCourseDocument; namespace Chill\DocStoreBundle\EntityRepository;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;

View File

@ -34,6 +34,6 @@ class DocumentCategoryRepository extends EntityRepository
'SELECT MAX(c.idInsideBundle) + 1 FROM ChillDocStoreBundle:DocumentCategory c') 'SELECT MAX(c.idInsideBundle) + 1 FROM ChillDocStoreBundle:DocumentCategory c')
->getSingleResult(); ->getSingleResult();
return $array_res[1] ?: 0; return reset($array_res);
} }
} }

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\DocStore;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173558 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'chill_doc.document_category.name',
'chill_doc.stored_object.key',
'chill_doc.stored_object.iv',
'chill_doc.stored_object.datas',
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@ -23,6 +23,7 @@ use ArrayIterator;
use Chill\EventBundle\Entity\Event; use Chill\EventBundle\Entity\Event;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Chill\EventBundle\Entity\Participation; use Chill\EventBundle\Entity\Participation;
@ -39,12 +40,12 @@ use Symfony\Component\Form\Extension\Core\Type\CollectionType;
*/ */
class ParticipationController extends AbstractController class ParticipationController extends AbstractController
{ {
/** /**
* @var \Psr\Log\LoggerInterface * @var \Psr\Log\LoggerInterface
*/ */
private $logger; private $logger;
/** /**
* ParticipationController constructor. * ParticipationController constructor.
* *
@ -54,10 +55,10 @@ class ParticipationController extends AbstractController
{ {
$this->logger = $logger; $this->logger = $logger;
} }
/** /**
* Show a form to add a participation * Show a form to add a participation
* *
* This function parse the person_id / persons_ids query argument * This function parse the person_id / persons_ids query argument
* and decide if it should process a single or multiple participation. Depending * and decide if it should process a single or multiple participation. Depending
* on this, the appropriate layout and form. * on this, the appropriate layout and form.
@ -67,46 +68,46 @@ class ParticipationController extends AbstractController
*/ */
public function newAction(Request $request) public function newAction(Request $request)
{ {
// test the request is correct // test the request is correct
try { try {
$this->testRequest($request); $this->testRequest($request);
} catch (\RuntimeException $ex) { } catch (\RuntimeException $ex) {
$this->logger->warning($ex->getMessage()); $this->logger->warning($ex->getMessage());
return (new Response()) return (new Response())
->setStatusCode(Response::HTTP_BAD_REQUEST) ->setStatusCode(Response::HTTP_BAD_REQUEST)
->setContent($ex->getMessage()); ->setContent($ex->getMessage());
} }
// forward to other action // forward to other action
$single = $request->query->has('person_id'); $single = $request->query->has('person_id');
$multiple = $request->query->has('persons_ids'); $multiple = $request->query->has('persons_ids');
if ($single === true) { if ($single === true) {
return $this->newSingle($request); return $this->newSingle($request);
} }
if ($multiple === true) { if ($multiple === true) {
return $this->newMultiple($request); return $this->newMultiple($request);
} }
// at this point, we miss the required fields. Throw an error // at this point, we miss the required fields. Throw an error
return (new Response()) return (new Response())
->setStatusCode(Response::HTTP_BAD_REQUEST) ->setStatusCode(Response::HTTP_BAD_REQUEST)
->setContent("You must provide either 'person_id' or " ->setContent("You must provide either 'person_id' or "
. "'persons_ids' argument in query"); . "'persons_ids' argument in query");
} }
/** /**
* *
* Test that the query parameters are valid : * Test that the query parameters are valid :
* *
* - an `event_id` is existing ; * - an `event_id` is existing ;
* - `person_id` and `persons_ids` are **not** both present ; * - `person_id` and `persons_ids` are **not** both present ;
* - `persons_id` is correct (contains only numbers and a ','. * - `persons_id` is correct (contains only numbers and a ','.
* *
* @param Request $request * @param Request $request
* @throws \RuntimeException if an error is detected * @throws \RuntimeException if an error is detected
*/ */
@ -114,64 +115,64 @@ class ParticipationController extends AbstractController
{ {
$single = $request->query->has('person_id'); $single = $request->query->has('person_id');
$multiple = $request->query->has('persons_ids'); $multiple = $request->query->has('persons_ids');
if ($single === true AND $multiple === true) { if ($single === true AND $multiple === true) {
// we are not allowed to have both person_id and persons_ids // we are not allowed to have both person_id and persons_ids
throw new \RuntimeException("You are not allow to provide both 'person_id' and " throw new \RuntimeException("You are not allow to provide both 'person_id' and "
. "'persons_ids' simulaneously"); . "'persons_ids' simulaneously");
} }
if ($multiple === true) { if ($multiple === true) {
$persons_ids = $request->query->get('persons_ids'); $persons_ids = $request->query->get('persons_ids');
if (!preg_match('/^([0-9]{1,},{0,1}){1,}[0-9]{0,}$/', $persons_ids)) { if (!preg_match('/^([0-9]{1,},{0,1}){1,}[0-9]{0,}$/', $persons_ids)) {
throw new \RuntimeException("The persons_ids value should " throw new \RuntimeException("The persons_ids value should "
. "contains int separated by ','"); . "contains int separated by ','");
} }
} }
// check for event_id - this could be removed later // check for event_id - this could be removed later
if ($request->query->has('event_id') === FALSE) { if ($request->query->has('event_id') === FALSE) {
throw new \RuntimeException("You must provide an event_id"); throw new \RuntimeException("You must provide an event_id");
} }
} }
/** /**
* Show a form with single participation. * Show a form with single participation.
* *
* @param Request $request * @param Request $request
* @return Response * @return Response
*/ */
protected function newSingle(Request $request) protected function newSingle(Request $request)
{ {
$returnPath = $request->query->get('return_path') ? $returnPath = $request->query->get('return_path') ?
$request->query->get('return_path') : null; $request->query->get('return_path') : null;
$participation = $this->handleRequest($request, new Participation(), false); $participation = $this->handleRequest($request, new Participation(), false);
$this->denyAccessUnlessGranted(ParticipationVoter::CREATE, $this->denyAccessUnlessGranted(ParticipationVoter::CREATE,
$participation, 'The user is not allowed to create this participation'); $participation, 'The user is not allowed to create this participation');
$form = $this->createCreateForm($participation, $returnPath); $form = $this->createCreateForm($participation, $returnPath);
return $this->render('ChillEventBundle:Participation:new.html.twig', array( return $this->render('ChillEventBundle:Participation:new.html.twig', array(
'form' => $form->createView(), 'form' => $form->createView(),
'participation' => $participation, 'participation' => $participation,
'ignored_participations' => array() // this is required, see self::newMultiple 'ignored_participations' => array() // this is required, see self::newMultiple
)); ));
} }
/** /**
* Show a form with multiple participation. * Show a form with multiple participation.
* *
* If a person is already participating on the event (if a participation with * If a person is already participating on the event (if a participation with
* the same person is associated with the event), the participation is ignored. * the same person is associated with the event), the participation is ignored.
* *
* If all but one participation is ignored, the page show the same response * If all but one participation is ignored, the page show the same response
* than the newSingle function. * than the newSingle function.
* *
* If all participations must be ignored, an error is shown and the method redirects * If all participations must be ignored, an error is shown and the method redirects
* to the event 'show' view with an appropriate flash message. * to the event 'show' view with an appropriate flash message.
* *
@ -181,24 +182,24 @@ class ParticipationController extends AbstractController
protected function newMultiple(Request $request) protected function newMultiple(Request $request)
{ {
$participations = $this->handleRequest($request, new Participation(), true); $participations = $this->handleRequest($request, new Participation(), true);
$ignoredParticipations = $newParticipations = [];
foreach ($participations as $i => $participation) { foreach ($participations as $participation) {
// check for authorization // check for authorization
$this->denyAccessUnlessGranted(ParticipationVoter::CREATE, $this->denyAccessUnlessGranted(ParticipationVoter::CREATE,
$participation, 'The user is not allowed to create this participation'); $participation, 'The user is not allowed to create this participation');
// create a collection of person's id participating to the event // create a collection of person's id participating to the event
/* @var $peopleParticipating \Doctrine\Common\Collections\ArrayCollection */ /* @var $peopleParticipating \Doctrine\Common\Collections\ArrayCollection */
$peopleParticipating = isset($peopleParticipating) ? $peopleParticipating : $peopleParticipating = isset($peopleParticipating) ? $peopleParticipating :
$participation->getEvent()->getParticipations()->map( $participation->getEvent()->getParticipations()->map(
function(Participation $p) { return $p->getPerson()->getId(); } function(Participation $p) { return $p->getPerson()->getId(); }
); );
// check that the user is not already in the event // check that the user is not already in the event
if ($peopleParticipating->contains($participation->getPerson()->getId())) { if ($peopleParticipating->contains($participation->getPerson()->getId())) {
$ignoredParticipations[] = $participation $ignoredParticipations[] = $participation
->getEvent()->getParticipations()->filter( ->getEvent()->getParticipations()->filter(
function (Participation $p) use ($participation) { function (Participation $p) use ($participation) {
return $p->getPerson()->getId() === $participation->getPerson()->getId(); return $p->getPerson()->getId() === $participation->getPerson()->getId();
} }
)->first(); )->first();
@ -206,40 +207,47 @@ class ParticipationController extends AbstractController
$newParticipations[] = $participation; $newParticipations[] = $participation;
} }
} }
// this is where the function redirect depending on valid participation // this is where the function redirect depending on valid participation
if (!isset($newParticipations)) { if ([] === $newParticipations) {
// if we do not have nay participants, redirect to event view // if we do not have nay participants, redirect to event view
$this->addFlash('error', $this->get('translator')->trans( $this->addFlash('error', $this->get('translator')->trans(
'None of the requested people may participate ' 'None of the requested people may participate '
. 'the event: they are maybe already participating.')); . 'the event: they are maybe already participating.'));
return $this->redirectToRoute('chill_event__event_show', array( return $this->redirectToRoute('chill_event__event_show', array(
'event_id' => $request->query->getInt('event_id', 0) 'event_id' => $request->query->getInt('event_id', 0)
)); ));
} elseif (count($newParticipations) > 1) { }
if (count($newParticipations) > 1) {
// if we have multiple participations, show a form with multiple participations // if we have multiple participations, show a form with multiple participations
$form = $this->createCreateFormMultiple($newParticipations); $form = $this->createCreateFormMultiple($newParticipations);
return $this->render('ChillEventBundle:Participation:new-multiple.html.twig', array( return $this->render(
'ChillEventBundle:Participation:new-multiple.html.twig',
[
'form' => $form->createView(), 'form' => $form->createView(),
'participations' => $newParticipations, 'participations' => $newParticipations,
'ignored_participations' => isset($ignoredParticipations) ? $ignoredParticipations : array() 'ignored_participations' => $ignoredParticipations
)); ]
} else { );
// if we have only one participation, show the same form than for single participation }
$form = $this->createCreateForm($participation);
// if we have only one participation, show the same form than for single participation
return $this->render('ChillEventBundle:Participation:new.html.twig', array( $form = $this->createCreateForm($participation);
return $this->render(
'ChillEventBundle:Participation:new.html.twig',
[
'form' => $form->createView(), 'form' => $form->createView(),
'participation' => $participation, 'participation' => $participation,
'ignored_participations' => isset($ignoredParticipations) ? $ignoredParticipations : array() 'ignored_participations' => $ignoredParticipations,
)); ]
);
}
} }
/** /**
* @param Request $request * @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|Response * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
@ -251,32 +259,32 @@ class ParticipationController extends AbstractController
$this->testRequest($request); $this->testRequest($request);
} catch (\RuntimeException $ex) { } catch (\RuntimeException $ex) {
$this->logger->warning($ex->getMessage()); $this->logger->warning($ex->getMessage());
return (new Response()) return (new Response())
->setStatusCode(Response::HTTP_BAD_REQUEST) ->setStatusCode(Response::HTTP_BAD_REQUEST)
->setContent($ex->getMessage()); ->setContent($ex->getMessage());
} }
// forward to other action // forward to other action
$single = $request->query->has('person_id'); $single = $request->query->has('person_id');
$multiple = $request->query->has('persons_ids'); $multiple = $request->query->has('persons_ids');
if ($single === true) { if ($single === true) {
return $this->createSingle($request); return $this->createSingle($request);
} }
if ($multiple === true) { if ($multiple === true) {
return $this->createMultiple($request); return $this->createMultiple($request);
} }
// at this point, we miss the required fields. Throw an error // at this point, we miss the required fields. Throw an error
return (new Response()) return (new Response())
->setStatusCode(Response::HTTP_BAD_REQUEST) ->setStatusCode(Response::HTTP_BAD_REQUEST)
->setContent("You must provide either 'person_id' or " ->setContent("You must provide either 'person_id' or "
. "'persons_ids' argument in query"); . "'persons_ids' argument in query");
} }
/** /**
* @param Request $request * @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|Response * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
@ -284,41 +292,41 @@ class ParticipationController extends AbstractController
public function createSingle(Request $request) public function createSingle(Request $request)
{ {
$participation = $this->handleRequest($request, new Participation(), false); $participation = $this->handleRequest($request, new Participation(), false);
$this->denyAccessUnlessGranted(ParticipationVoter::CREATE, $this->denyAccessUnlessGranted(ParticipationVoter::CREATE,
$participation, 'The user is not allowed to create this participation'); $participation, 'The user is not allowed to create this participation');
$form = $this->createCreateForm($participation); $form = $this->createCreateForm($participation);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$em->persist($participation); $em->persist($participation);
$em->flush(); $em->flush();
$this->addFlash('success', $this->get('translator')->trans( $this->addFlash('success', $this->get('translator')->trans(
'The participation was created' 'The participation was created'
)); ));
if ($request->query->get('return_path')) if ($request->query->get('return_path'))
{ {
return $this->redirect($request->query->get('return_path')); return $this->redirect($request->query->get('return_path'));
} else { } else {
return $this->redirectToRoute('chill_event__event_show', array( return $this->redirectToRoute('chill_event__event_show', array(
'event_id' => $participation->getEvent()->getId() 'event_id' => $participation->getEvent()->getId()
)); ));
} }
} }
return $this->render('ChillEventBundle:Participation:new.html.twig', array( return $this->render('ChillEventBundle:Participation:new.html.twig', array(
'form' => $form->createView(), 'form' => $form->createView(),
'participation' => $participation 'participation' => $participation
)); ));
} }
/** /**
* @param Request $request * @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|Response * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
@ -326,95 +334,92 @@ class ParticipationController extends AbstractController
public function createMultiple(Request $request) public function createMultiple(Request $request)
{ {
$participations = $this->handleRequest($request, new Participation(), true); $participations = $this->handleRequest($request, new Participation(), true);
foreach($participations as $participation) { foreach($participations as $participation) {
$this->denyAccessUnlessGranted(ParticipationVoter::CREATE, $this->denyAccessUnlessGranted(ParticipationVoter::CREATE,
$participation, 'The user is not allowed to create this participation'); $participation, 'The user is not allowed to create this participation');
} }
$form = $this->createCreateFormMultiple($participations); $form = $this->createCreateFormMultiple($participations);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$data = $form->getData(); $data = $form->getData();
foreach($data['participations'] as $participation) { foreach($data['participations'] as $participation) {
$em->persist($participation); $em->persist($participation);
} }
$em->flush(); $em->flush();
$this->addFlash('success', $this->get('translator')->trans( $this->addFlash('success', $this->get('translator')->trans(
'The participations were created' 'The participations were created'
)); ));
return $this->redirectToRoute('chill_event__event_show', array( return $this->redirectToRoute('chill_event__event_show', array(
'event_id' => $participations[0]->getEvent()->getId() 'event_id' => $participations[0]->getEvent()->getId()
)); ));
} }
return $this->render('ChillEventBundle:Participation:new.html.twig', array( return $this->render('ChillEventBundle:Participation:new.html.twig', array(
'form' => $form->createView(), 'form' => $form->createView(),
'participation' => $participation 'participation' => $participation
)); ));
} }
/** /**
* *
* Handle the request to adapt $participation. * Handle the request to adapt $participation.
* *
* If the request is multiple, the $participation object is cloned. * If the request is multiple, the $participation object is cloned.
* Limitations: the $participation should not be persisted. * Limitations: the $participation should not be persisted.
* *
* @param Request $request
* @param Participation $participation
* @param boolean $multiple (default false)
* @return Participation|Participations[] return one single participation if $multiple == false * @return Participation|Participations[] return one single participation if $multiple == false
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the event/person is not found * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the event/person is not found
* @throws \Symfony\Component\Security\Core\Exception\AccessDeniedException if the user does not have access to event/person * @throws \Symfony\Component\Security\Core\Exception\AccessDeniedException if the user does not have access to event/person
*/ */
protected function handleRequest( protected function handleRequest(
Request $request, Request $request,
Participation $participation, Participation $participation,
$multiple = false) bool $multiple = false)
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
if ($em->contains($participation)) { if ($em->contains($participation)) {
throw new \LogicException("The participation object should not be managed by " throw new \LogicException("The participation object should not be managed by "
. "the object manager using the method ".__METHOD__); . "the object manager using the method ".__METHOD__);
} }
$event_id = $request->query->getInt('event_id', 0); // sf4 check: $event_id = $request->query->getInt('event_id', 0); // sf4 check:
// prevent error: `Argument 2 passed to ::getInt() must be of the type int, null given` // prevent error: `Argument 2 passed to ::getInt() must be of the type int, null given`
if ($event_id !== NULL) { if ($event_id !== NULL) {
$event = $em->getRepository('ChillEventBundle:Event') $event = $em->getRepository('ChillEventBundle:Event')
->find($event_id); ->find($event_id);
if ($event === NULL) { if ($event === NULL) {
throw $this->createNotFoundException('The event with id '.$event_id.' is not found'); throw $this->createNotFoundException('The event with id '.$event_id.' is not found');
} }
$this->denyAccessUnlessGranted('CHILL_EVENT_SEE', $event, $this->denyAccessUnlessGranted('CHILL_EVENT_SEE', $event,
'The user is not allowed to see the event'); 'The user is not allowed to see the event');
$participation->setEvent($event); $participation->setEvent($event);
} }
// this script should be able to handle multiple, so we translate // this script should be able to handle multiple, so we translate
// single person_id in an array // single person_id in an array
$persons_ids = $request->query->has('person_id') ? $persons_ids = $request->query->has('person_id') ?
[$request->query->getInt('person_id', 0)] // sf4 check: [$request->query->getInt('person_id', 0)] // sf4 check:
// prevent error: `Argument 2 passed to ::getInt() must be of the type int, null given` // prevent error: `Argument 2 passed to ::getInt() must be of the type int, null given`
: explode(',', $request->query->get('persons_ids')); : explode(',', $request->query->get('persons_ids'));
$participations = array(); $participations = array();
foreach($persons_ids as $person_id) { foreach($persons_ids as $person_id) {
// clone if we have to reuse the $participation // clone if we have to reuse the $participation
$participation = count($persons_ids) > 1 ? clone $participation : $participation; $participation = count($persons_ids) > 1 ? clone $participation : $participation;
if ($person_id !== NULL) { if ($person_id !== NULL) {
$person = $em->getRepository('ChillPersonBundle:Person') $person = $em->getRepository('ChillPersonBundle:Person')
->find($person_id); ->find($person_id);
@ -428,21 +433,19 @@ class ParticipationController extends AbstractController
$participation->setPerson($person); $participation->setPerson($person);
} }
$participations[] = $participation; $participations[] = $participation;
} }
return $multiple ? $participations : $participations[0]; return $multiple ? $participations : $participations[0];
} }
/** /**
* @param Participation $participation
* @param null $return_path * @param null $return_path
* @return \Symfony\Component\Form\FormInterface
*/ */
public function createCreateForm(Participation $participation, $return_path = null) public function createCreateForm(Participation $participation, $return_path = null): FormInterface
{ {
$form = $this->createForm(ParticipationType::class, $participation, array( $form = $this->createForm(ParticipationType::class, $participation, array(
'event_type' => $participation->getEvent()->getType(), 'event_type' => $participation->getEvent()->getType(),
'action' => $this->generateUrl('chill_event_participation_create', array( 'action' => $this->generateUrl('chill_event_participation_create', array(
@ -451,26 +454,22 @@ class ParticipationController extends AbstractController
'person_id' => $participation->getPerson()->getId() 'person_id' => $participation->getPerson()->getId()
)) ))
)); ));
$form->add('submit', SubmitType::class, array( $form->add('submit', SubmitType::class, array(
'label' => 'Create' 'label' => 'Create'
)); ));
return $form; return $form;
} }
/** public function createCreateFormMultiple(array $participations): FormInterface
* @param array $participations
* @return \Symfony\Component\Form\FormInterface
*/
public function createCreateFormMultiple(array $participations)
{ {
$form = $this->createForm(\Symfony\Component\Form\Extension\Core\Type\FormType::class, $form = $this->createForm(\Symfony\Component\Form\Extension\Core\Type\FormType::class,
array('participations' => $participations), array( array('participations' => $participations), array(
'action' => $this->generateUrl('chill_event_participation_create', array( 'action' => $this->generateUrl('chill_event_participation_create', array(
'event_id' => current($participations)->getEvent()->getId(), 'event_id' => current($participations)->getEvent()->getId(),
'persons_ids' => implode(',', array_map( 'persons_ids' => implode(',', array_map(
function(Participation $p) { return $p->getPerson()->getId(); }, function(Participation $p) { return $p->getPerson()->getId(); },
$participations)) $participations))
) )
))); )));
@ -481,109 +480,107 @@ class ParticipationController extends AbstractController
), ),
) )
); );
$form->add('submit', SubmitType::class, array( $form->add('submit', SubmitType::class, array(
'label' => 'Create' 'label' => 'Create'
)); ));
return $form; return $form;
} }
/** /**
* show an edit form for the participation with the given id. * Show an edit form for the participation with the given id.
* *
* @param int $participation_id
* @return \Symfony\Component\HttpFoundation\Response
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the participation is not found * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the participation is not found
* @throws \Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException if the user is not allowed to edit the participation * @throws \Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException if the user is not allowed to edit the participation
*/ */
public function editAction($participation_id) public function editAction(int $participation_id): Response
{ {
/* @var $participation Participation */ /* @var $participation Participation */
$participation = $this->getDoctrine()->getManager() $participation = $this->getDoctrine()->getManager()
->getRepository('ChillEventBundle:Participation') ->getRepository(Participation::class)
->find($participation_id); ->find($participation_id);
if ($participation === NULL) { if ($participation === NULL) {
throw $this->createNotFoundException('The participation is not found'); throw $this->createNotFoundException('The participation is not found');
} }
$this->denyAccessUnlessGranted(ParticipationVoter::UPDATE, $participation, $this->denyAccessUnlessGranted(ParticipationVoter::UPDATE, $participation,
'You are not allowed to edit this participation'); 'You are not allowed to edit this participation');
$form = $this->createEditForm($participation); $form = $this->createEditForm($participation);
return $this->render('ChillEventBundle:Participation:edit.html.twig', array( return $this->render('ChillEventBundle:Participation:edit.html.twig', [
'form' => $form->createView(), 'form' => $form->createView(),
'participation' => $participation 'participation' => $participation,
)); ]);
} }
/** public function updateAction(int $participation_id, Request $request): Response
* @param $participation_id
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
*/
public function updateAction($participation_id, Request $request)
{ {
/* @var $participation Participation */ /* @var $participation Participation */
$participation = $this->getDoctrine()->getManager() $participation = $this->getDoctrine()->getManager()
->getRepository('ChillEventBundle:Participation') ->getRepository(Participation::class)
->find($participation_id); ->find($participation_id);
if ($participation === NULL) { if ($participation === NULL) {
throw $this->createNotFoundException('The participation is not found'); throw $this->createNotFoundException('The participation is not found');
} }
$this->denyAccessUnlessGranted(ParticipationVoter::UPDATE, $participation, $this->denyAccessUnlessGranted(ParticipationVoter::UPDATE, $participation,
'You are not allowed to edit this participation'); 'You are not allowed to edit this participation');
$form = $this->createEditForm($participation); $form = $this->createEditForm($participation);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$em->flush(); $em->flush();
$this->addFlash('success', $this->get('translator')->trans( $this->addFlash('success', $this->get('translator')->trans(
'The participation was updated' 'The participation was updated'
)); ));
return $this->redirectToRoute('chill_event__event_show', array( return $this->redirectToRoute('chill_event__event_show', [
'event_id' => $participation->getEvent()->getId() 'event_id' => $participation->getEvent()->getId(),
)); ]);
} }
return $this->render('ChillEventBundle:Participation:edit.html.twig', array( return $this->render('ChillEventBundle:Participation:edit.html.twig', [
'form' => $form->createView(), 'form' => $form->createView(),
'participation' => $participation 'participation' => $participation,
)); ]);
} }
/** public function createEditForm(Participation $participation): FormInterface
*
* @param Participation $participation
* @return \Symfony\Component\Form\FormInterface
*/
public function createEditForm(Participation $participation)
{ {
$form = $this->createForm(ParticipationType::class, $participation, array( $form = $this->createForm(
'event_type' => $participation->getEvent()->getType(), ParticipationType::class,
'action' => $this->generateUrl('chill_event_participation_update', array( $participation,
'participation_id' => $participation->getId() [
)) 'event_type' => $participation->getEvent()->getType(),
)); 'action' => $this->generateUrl(
'chill_event_participation_update',
$form->add('submit', SubmitType::class, array( [
'label' => 'Edit' 'participation_id' => $participation->getId(),
)); ]
),
]
);
$form->add(
'submit',
SubmitType::class, [
'label' => 'Edit',
]
);
return $form; return $form;
} }
/** /**
* show a form to edit multiple participation for the same event. * show a form to edit multiple participation for the same event.
* *
@ -594,84 +591,84 @@ class ParticipationController extends AbstractController
{ {
$event = $this->getDoctrine()->getRepository('ChillEventBundle:Event') $event = $this->getDoctrine()->getRepository('ChillEventBundle:Event')
->find($event_id); ->find($event_id);
if ($event === null) { if ($event === null) {
throw $this->createNotFoundException("The event with id $event_id is not found"); throw $this->createNotFoundException("The event with id $event_id is not found");
} }
// check for ACL, on Event level and on Participation Level // check for ACL, on Event level and on Participation Level
$this->denyAccessUnlessGranted('CHILL_EVENT_SEE', $event, "You are not allowed " $this->denyAccessUnlessGranted('CHILL_EVENT_SEE', $event, "You are not allowed "
. "to see this event"); . "to see this event");
foreach ($event->getParticipations() as $participation) { foreach ($event->getParticipations() as $participation) {
$this->denyAccessUnlessGranted(ParticipationVoter::UPDATE, $participation, $this->denyAccessUnlessGranted(ParticipationVoter::UPDATE, $participation,
"You are not allowed to update participation with id ".$participation->getId()); "You are not allowed to update participation with id ".$participation->getId());
} }
switch ($event->getParticipations()->count()) { switch ($event->getParticipations()->count()) {
case 0: case 0:
// if there aren't any participation, redirect to the 'show' view with an add flash // if there aren't any participation, redirect to the 'show' view with an add flash
$this->addFlash('warning', $this->get('translator') $this->addFlash('warning', $this->get('translator')
->trans( "There are no participation to edit for this event")); ->trans( "There are no participation to edit for this event"));
return $this->redirectToRoute('chill_event__event_show', return $this->redirectToRoute('chill_event__event_show',
array('event_id' => $event->getId())); array('event_id' => $event->getId()));
case 1: case 1:
// redirect to the form for a single participation // redirect to the form for a single participation
return $this->redirectToRoute('chill_event_participation_edit', array( return $this->redirectToRoute('chill_event_participation_edit', array(
'participation_id' => $event->getParticipations()->current()->getId() 'participation_id' => $event->getParticipations()->current()->getId()
)); ));
} }
$form = $this->createEditFormMultiple($event->getParticipations(), $event); $form = $this->createEditFormMultiple($event->getParticipations(), $event);
return $this->render('ChillEventBundle:Participation:edit-multiple.html.twig', array( return $this->render('ChillEventBundle:Participation:edit-multiple.html.twig', array(
'event' => $event, 'event' => $event,
'participations' => $event->getParticipations(), 'participations' => $event->getParticipations(),
'form' => $form->createView() 'form' => $form->createView()
)); ));
} }
public function updateMultipleAction($event_id, Request $request) public function updateMultipleAction($event_id, Request $request)
{ {
/* @var $event \Chill\EventBundle\Entity\Event */ /* @var $event \Chill\EventBundle\Entity\Event */
$event = $this->getDoctrine()->getRepository('ChillEventBundle:Event') $event = $this->getDoctrine()->getRepository('ChillEventBundle:Event')
->find($event_id); ->find($event_id);
if ($event === null) { if ($event === null) {
throw $this->createNotFoundException("The event with id $event_id is not found"); throw $this->createNotFoundException("The event with id $event_id is not found");
} }
$this->denyAccessUnlessGranted('CHILL_EVENT_SEE', $event, "You are not allowed " $this->denyAccessUnlessGranted('CHILL_EVENT_SEE', $event, "You are not allowed "
. "to see this event"); . "to see this event");
foreach ($event->getParticipations() as $participation) { foreach ($event->getParticipations() as $participation) {
$this->denyAccessUnlessGranted(ParticipationVoter::UPDATE, $participation, $this->denyAccessUnlessGranted(ParticipationVoter::UPDATE, $participation,
"You are not allowed to update participation with id ".$participation->getId()); "You are not allowed to update participation with id ".$participation->getId());
} }
$form = $this->createEditFormMultiple($event->getParticipations(), $event); $form = $this->createEditFormMultiple($event->getParticipations(), $event);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush(); $this->getDoctrine()->getManager()->flush();
$this->addFlash('success', $this->get('translator')->trans("The participations " $this->addFlash('success', $this->get('translator')->trans("The participations "
. "have been successfully updated.")); . "have been successfully updated."));
return $this->redirectToRoute('chill_event__event_show', return $this->redirectToRoute('chill_event__event_show',
array('event_id' => $event->getId())); array('event_id' => $event->getId()));
} }
return $this->render('ChillEventBundle:Participation:edit-multiple.html.twig', array( return $this->render('ChillEventBundle:Participation:edit-multiple.html.twig', array(
'event' => $event, 'event' => $event,
'participations' => $event->getParticipations(), 'participations' => $event->getParticipations(),
'form' => $form->createView() 'form' => $form->createView()
)); ));
} }
/** /**
* @param ArrayIterator $participations * @param ArrayIterator $participations
* @param Event $event * @param Event $event
@ -679,14 +676,14 @@ class ParticipationController extends AbstractController
*/ */
protected function createEditFormMultiple(ArrayIterator $participations, Event $event) protected function createEditFormMultiple(ArrayIterator $participations, Event $event)
{ {
$form = $this->createForm(\Symfony\Component\Form\Extension\Core\Type\FormType::class, $form = $this->createForm(\Symfony\Component\Form\Extension\Core\Type\FormType::class,
array('participations' => $participations), array( array('participations' => $participations), array(
'method' => 'POST', 'method' => 'POST',
'action' => $this->generateUrl('chill_event_participation_update_multiple', array( 'action' => $this->generateUrl('chill_event_participation_update_multiple', array(
'event_id' => $event->getId() 'event_id' => $event->getId()
)) ))
)); ));
$form->add('participations', CollectionType::class, array( $form->add('participations', CollectionType::class, array(
'entry_type' => ParticipationType::class, 'entry_type' => ParticipationType::class,
'entry_options' => array( 'entry_options' => array(
@ -694,14 +691,14 @@ class ParticipationController extends AbstractController
), ),
) )
); );
$form->add('submit', SubmitType::class, array( $form->add('submit', SubmitType::class, array(
'label' => 'Update' 'label' => 'Update'
)); ));
return $form; return $form;
} }
/** /**
* @param integer $participation_id * @param integer $participation_id
* @param Request $request * @param Request $request
@ -713,28 +710,28 @@ class ParticipationController extends AbstractController
$participation = $em->getRepository('ChillEventBundle:Participation')->findOneBy([ $participation = $em->getRepository('ChillEventBundle:Participation')->findOneBy([
'id' => $participation_id 'id' => $participation_id
]); ]);
if (! $participation) { if (! $participation) {
throw $this->createNotFoundException('Unable to find participation.'); throw $this->createNotFoundException('Unable to find participation.');
} }
/** @var Event $event */ /** @var Event $event */
$event = $participation->getEvent(); $event = $participation->getEvent();
$form = $this->createDeleteForm($participation_id); $form = $this->createDeleteForm($participation_id);
if ($request->getMethod() === Request::METHOD_DELETE) { if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isValid()) { if ($form->isValid()) {
$em->remove($participation); $em->remove($participation);
$em->flush(); $em->flush();
$this->addFlash('success', $this->get('translator') $this->addFlash('success', $this->get('translator')
->trans("The participation has been sucessfully removed") ->trans("The participation has been sucessfully removed")
); );
return $this->redirectToRoute('chill_event__event_show', [ return $this->redirectToRoute('chill_event__event_show', [
'event_id' => $event->getId() 'event_id' => $event->getId()
]); ]);
@ -744,9 +741,9 @@ class ParticipationController extends AbstractController
'event_id' => $event->getId(), 'event_id' => $event->getId(),
'delete_form' => $form->createView() 'delete_form' => $form->createView()
]); ]);
} }
/** /**
* @param $participation_id * @param $participation_id
* @return \Symfony\Component\Form\FormInterface * @return \Symfony\Component\Form\FormInterface
@ -762,5 +759,5 @@ class ParticipationController extends AbstractController
->getForm() ->getForm()
; ;
} }
} }

View File

@ -50,19 +50,19 @@ class LoadRolesACL extends AbstractFixture implements OrderedFixtureInterface
break; break;
case 'administrative': case 'administrative':
case 'direction': case 'direction':
if (in_array($scope->getName()['en'], array('administrative', 'social'))) { if (in_array($scope->getName()['en'], array('administrative', 'social'), true)) {
break 2; // we do not want any power on social or administrative break 2; // we do not want any power on social or administrative
} }
break; break;
} }
printf("Adding CHILL_EVENT_UPDATE & CHILL_EVENT_CREATE " printf("Adding CHILL_EVENT_UPDATE & CHILL_EVENT_CREATE "
. "& CHILL_EVENT_PARTICIPATION_UPDATE & CHILL_EVENT_PARTICIPATION_CREATE " . "& CHILL_EVENT_PARTICIPATION_UPDATE & CHILL_EVENT_PARTICIPATION_CREATE "
. "& CHILL_EVENT_SEE & CHILL_EVENT_SEE_DETAILS " . "& CHILL_EVENT_SEE & CHILL_EVENT_SEE_DETAILS "
. "to %s " . "to %s "
. "permission group, scope '%s' \n", . "permission group, scope '%s' \n",
$permissionsGroup->getName(), $scope->getName()['en']); $permissionsGroup->getName(), $scope->getName()['en']);
$roleScopeUpdate = (new RoleScope()) $roleScopeUpdate = (new RoleScope())
->setRole('CHILL_EVENT_UPDATE') ->setRole('CHILL_EVENT_UPDATE')
->setScope($scope); ->setScope($scope);
@ -71,7 +71,7 @@ class LoadRolesACL extends AbstractFixture implements OrderedFixtureInterface
->setScope($scope); ->setScope($scope);
$permissionsGroup->addRoleScope($roleScopeUpdate); $permissionsGroup->addRoleScope($roleScopeUpdate);
$permissionsGroup->addRoleScope($roleScopeUpdate2); $permissionsGroup->addRoleScope($roleScopeUpdate2);
$roleScopeCreate = (new RoleScope()) $roleScopeCreate = (new RoleScope())
->setRole('CHILL_EVENT_CREATE') ->setRole('CHILL_EVENT_CREATE')
->setScope($scope); ->setScope($scope);
@ -80,7 +80,7 @@ class LoadRolesACL extends AbstractFixture implements OrderedFixtureInterface
->setScope($scope); ->setScope($scope);
$permissionsGroup->addRoleScope($roleScopeCreate); $permissionsGroup->addRoleScope($roleScopeCreate);
$permissionsGroup->addRoleScope($roleScopeCreate2); $permissionsGroup->addRoleScope($roleScopeCreate2);
$roleScopeSee = (new RoleScope()) $roleScopeSee = (new RoleScope())
->setRole('CHILL_EVENT_SEE') ->setRole('CHILL_EVENT_SEE')
->setScope($scope); ->setScope($scope);
@ -89,7 +89,7 @@ class LoadRolesACL extends AbstractFixture implements OrderedFixtureInterface
->setScope($scope); ->setScope($scope);
$permissionsGroup->addRoleScope($roleScopeSee); $permissionsGroup->addRoleScope($roleScopeSee);
$permissionsGroup->addRoleScope($roleScopeSee2); $permissionsGroup->addRoleScope($roleScopeSee2);
$manager->persist($roleScopeUpdate); $manager->persist($roleScopeUpdate);
$manager->persist($roleScopeUpdate2); $manager->persist($roleScopeUpdate2);
$manager->persist($roleScopeCreate); $manager->persist($roleScopeCreate);
@ -97,9 +97,9 @@ class LoadRolesACL extends AbstractFixture implements OrderedFixtureInterface
$manager->persist($roleScopeSee); $manager->persist($roleScopeSee);
$manager->persist($roleScopeSee2); $manager->persist($roleScopeSee2);
} }
} }
$manager->flush(); $manager->flush();
} }

View File

@ -1,50 +1,46 @@
<?php <?php
declare(strict_types=1);
namespace Chill\AMLI\FamilyMembersBundle\Controller; namespace Chill\AMLI\FamilyMembersBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Chill\AMLI\FamilyMembersBundle\Repository\FamilyMemberRepository;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember; use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember;
use Chill\AMLI\FamilyMembersBundle\Security\Voter\FamilyMemberVoter; use Chill\AMLI\FamilyMembersBundle\Security\Voter\FamilyMemberVoter;
use Chill\AMLI\FamilyMembersBundle\Form\FamilyMemberType; use Chill\AMLI\FamilyMembersBundle\Form\FamilyMemberType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Component\Translation\TranslatorInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Annotation\Route;
class FamilyMemberController extends Controller class FamilyMemberController extends AbstractController
{ {
/** private EntityManagerInterface $em;
*
* @var EntityManagerInterface protected TranslatorInterface $translator;
*/
protected $em; protected LoggerInterface $chillMainLogger;
/** private FamilyMemberRepository $familyMemberRepository;
*
* @var TranslatorInterface
*/
protected $translator;
/**
*
* @var LoggerInterface
*/
protected $chillMainLogger;
public function __construct( public function __construct(
EntityManagerInterface $em, EntityManagerInterface $entityManager,
TranslatorInterface $translator, TranslatorInterface $translator,
LoggerInterface $chillMainLogger LoggerInterface $chillMainLogger,
FamilyMemberRepository $familyMemberRepository
) { ) {
$this->em = $em; $this->em = $entityManager;
$this->translator = $translator; $this->translator = $translator;
$this->chillMainLogger = $chillMainLogger; $this->chillMainLogger = $chillMainLogger;
$this->familyMemberRepository = $familyMemberRepository;
} }
/** /**
* @Route( * @Route(
* "{_locale}/family-members/family-members/by-person/{id}", * "{_locale}/family-members/family-members/by-person/{id}",
@ -54,15 +50,13 @@ class FamilyMemberController extends Controller
public function indexAction(Person $person) public function indexAction(Person $person)
{ {
$this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $person); $this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $person);
$familyMembers = $this->em $familyMembers = $this->familyMemberRepository->findByPerson($person);
->getRepository(FamilyMember::class)
->findByPerson($person); return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:index.html.twig', [
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:index.html.twig', array(
'person' => $person, 'person' => $person,
'familyMembers' => $familyMembers 'familyMembers' => $familyMembers
)); ]);
} }
/** /**
@ -73,33 +67,30 @@ class FamilyMemberController extends Controller
*/ */
public function newAction(Person $person, Request $request) public function newAction(Person $person, Request $request)
{ {
$familyMember = (new FamilyMember()) $familyMember = (new FamilyMember())->setPerson($person);
->setPerson($person)
;
$this->denyAccessUnlessGranted(FamilyMemberVoter::CREATE, $familyMember); $this->denyAccessUnlessGranted(FamilyMemberVoter::CREATE, $familyMember);
$form = $this->createForm(FamilyMemberType::class, $familyMember); $form = $this->createForm(FamilyMemberType::class, $familyMember);
$form->add('submit', SubmitType::class); $form->add('submit', SubmitType::class);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager(); $this->em->persist($familyMember);
$em->persist($familyMember); $this->em->flush();
$em->flush();
$this->addFlash('success', $this->translator->trans('Family member created')); $this->addFlash('success', $this->translator->trans('Family member created'));
return $this->redirectToRoute('chill_family_members_family_members_index', [ return $this->redirectToRoute('chill_family_members_family_members_index', [
'id' => $person->getId() 'id' => $person->getId()
]); ]);
} }
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:new.html.twig', array( return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:new.html.twig', [
'form' => $form->createView(), 'form' => $form->createView(),
'person' => $person 'person' => $person
)); ]);
} }
/** /**
@ -111,79 +102,73 @@ class FamilyMemberController extends Controller
public function editAction(FamilyMember $familyMember, Request $request) public function editAction(FamilyMember $familyMember, Request $request)
{ {
$this->denyAccessUnlessGranted(FamilyMemberVoter::UPDATE, $familyMember); $this->denyAccessUnlessGranted(FamilyMemberVoter::UPDATE, $familyMember);
$form = $this->createForm(FamilyMemberType::class, $familyMember); $form = $this->createForm(FamilyMemberType::class, $familyMember);
$form->add('submit', SubmitType::class); $form->add('submit', SubmitType::class);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager(); $this->em->flush();
$em->flush();
$this->addFlash('success', $this->translator->trans('Family member updated')); $this->addFlash('success', $this->translator->trans('Family member updated'));
return $this->redirectToRoute('chill_family_members_family_members_index', [ return $this->redirectToRoute('chill_family_members_family_members_index', [
'id' => $familyMember->getPerson()->getId() 'id' => $familyMember->getPerson()->getId()
]); ]);
} }
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:edit.html.twig', array( return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:edit.html.twig', [
'familyMember' => $familyMember, 'familyMember' => $familyMember,
'form' => $form->createView(), 'form' => $form->createView(),
'person' => $familyMember->getPerson() 'person' => $familyMember->getPerson()
)); ]);
} }
/** /**
* *
* @Route( * @Route(
* "{_locale}/family-members/family-members/{id}/delete", * "{_locale}/family-members/family-members/{id}/delete",
* name="chill_family_members_family_members_delete" * name="chill_family_members_family_members_delete"
* ) * )
*
* @param FamilyMember $familyMember
* @param Request $request
* @return \Symfony\Component\BrowserKit\Response
*/ */
public function deleteAction(FamilyMember $familyMember, Request $request) public function deleteAction(FamilyMember $familyMember, Request $request): Response
{ {
$this->denyAccessUnlessGranted(FamilyMemberVoter::DELETE, $familyMember, 'You are not ' $this->denyAccessUnlessGranted(FamilyMemberVoter::DELETE, $familyMember, 'You are not '
. 'allowed to delete this family membership'); . 'allowed to delete this family membership');
$form = $this->createDeleteForm($id); $form = $this->createDeleteForm();
if ($request->getMethod() === Request::METHOD_DELETE) { if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isValid()) { if ($form->isValid()) {
$this->chillMainLogger->notice("A family member has been removed", array( $this->chillMainLogger->notice("A family member has been removed", [
'by_user' => $this->getUser()->getUsername(), 'by_user' => $this->getUser()->getUsername(),
'family_member_id' => $familyMember->getId(), 'family_member_id' => $familyMember->getId(),
'name' => $familyMember->getFirstname()." ".$familyMember->getLastname(), 'name' => $familyMember->getFirstname()." ".$familyMember->getLastname(),
'link' => $familyMember->getLink() 'link' => $familyMember->getLink()
)); ]);
$em = $this->getDoctrine()->getManager(); $this->em->remove($familyMember);
$em->remove($familyMember); $this->em->flush();
$em->flush();
$this->addFlash('success', $this->translator $this->addFlash('success', $this->translator
->trans("The family member has been successfully removed.")); ->trans("The family member has been successfully removed."));
return $this->redirectToRoute('chill_family_members_family_members_index', array( return $this->redirectToRoute('chill_family_members_family_members_index', [
'id' => $familyMember->getPerson()->getId() 'id' => $familyMember->getPerson()->getId()
)); ]);
} }
} }
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:confirm_delete.html.twig', array( return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:confirm_delete.html.twig', [
'familyMember' => $familyMember, 'familyMember' => $familyMember,
'delete_form' => $form->createView() 'delete_form' => $form->createView()
)); ]);
} }
/** /**
* @Route( * @Route(
* "{_locale}/family-members/family-members/{id}/view", * "{_locale}/family-members/family-members/{id}/view",
@ -193,24 +178,21 @@ class FamilyMemberController extends Controller
public function viewAction(FamilyMember $familyMember) public function viewAction(FamilyMember $familyMember)
{ {
$this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $familyMember); $this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $familyMember);
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:view.html.twig', array( return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:view.html.twig', [
'familyMember' => $familyMember 'familyMember' => $familyMember
)); ]);
} }
/** /**
* Creates a form to delete a help request entity by id. * Creates a form to delete a help request entity by id.
*
* @param mixed $id The entity id
*
* @return \Symfony\Component\Form\Form The form
*/ */
private function createDeleteForm($id) private function createDeleteForm(): FormInterface
{ {
return $this->createFormBuilder() return $this
->createFormBuilder()
->setMethod(Request::METHOD_DELETE) ->setMethod(Request::METHOD_DELETE)
->add('submit', SubmitType::class, array('label' => 'Delete')) ->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm() ->getForm()
; ;
} }

View File

@ -1,17 +1,32 @@
<?php <?php
declare(strict_types=1);
namespace Chill\AMLI\FamilyMembersBundle\Repository; namespace Chill\AMLI\FamilyMembersBundle\Repository;
use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/** /**
* FamilyMemberRepository * @method FamilyMember|null find($id, $lockMode = null, $lockVersion = null)
* * @method FamilyMember|null findOneBy(array $criteria, array $orderBy = null)
* @method FamilyMember[] findAll()
* @method FamilyMember[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/ */
class FamilyMemberRepository extends \Doctrine\ORM\EntityRepository class FamilyMemberRepository extends ServiceEntityRepository
{ {
public function findActiveByPerson(Person $person) public function __construct(ManagerRegistry $registry)
{ {
return $this->findBy([ 'person' => $person ]); parent::__construct($registry, FamilyMember::class);
}
/**
* @return FamilyMember[]
*/
public function findByPerson(Person $person): array
{
return $this->findBy(['person' => $person]);
} }
} }

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\CRUD\Controller; namespace Chill\MainBundle\CRUD\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@ -14,94 +16,77 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Component\Translation\TranslatorInterface;
class AbstractCRUDController extends AbstractController abstract class AbstractCRUDController extends AbstractController
{ {
/** /**
* The crud configuration * The crud configuration
* *
* This configuration si defined by `chill_main['crud']` or `chill_main['apis']` * This configuration si defined by `chill_main['crud']` or `chill_main['apis']`
*
* @var array
*/ */
protected array $crudConfig = []; protected array $crudConfig = [];
/** /**
* get the instance of the entity with the given id * get the instance of the entity with the given id
* *
* @param string $id
* @return object
* @throw Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the object is not found * @throw Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the object is not found
*/ */
protected function getEntity($action, $id, Request $request): object protected function getEntity($action, string $id, Request $request): object
{ {
$e = $this->getDoctrine() $e = $this
->getDoctrine()
->getRepository($this->getEntityClass()) ->getRepository($this->getEntityClass())
->find($id); ->find($id);
if (NULL === $e) { if (null === $e) {
throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id)); throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id));
} }
return $e; return $e;
} }
/**
* Create an entity.
*
* @param string $action
* @param Request $request
* @return object
*/
protected function createEntity(string $action, Request $request): object protected function createEntity(string $action, Request $request): object
{ {
$type = $this->getEntityClass(); $class = $this->getEntityClass();
return new $type; return new $class;
} }
/** /**
* Count the number of entities * Count the number of entities
* *
* By default, count all entities. You can customize the query by * By default, count all entities. You can customize the query by
* using the method `customizeQuery`. * using the method `customizeQuery`.
*
* @param string $action
* @param Request $request
* @return int
*/ */
protected function countEntities(string $action, Request $request, $_format): int protected function countEntities(string $action, Request $request, $_format): int
{ {
return $this->buildQueryEntities($action, $request) return $this->buildQueryEntities($action, $request)
->select('COUNT(e)') ->select('COUNT(e)')
->getQuery() ->getQuery()
->getSingleScalarResult() ->getSingleScalarResult();
;
} }
/** /**
* Query the entity. * Query the entity.
* *
* By default, get all entities. You can customize the query by using the * By default, get all entities. You can customize the query by using the
* method `customizeQuery`. * method `customizeQuery`.
* *
* The method `orderEntity` is called internally to order entities. * The method `orderEntity` is called internally to order entities.
* *
* It returns, by default, a query builder. * It returns, by default, a query builder.
*
*/ */
protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator) protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator)
{ {
$query = $this->buildQueryEntities($action, $request) $query = $this->buildQueryEntities($action, $request)
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber()) ->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
->setMaxResults($paginator->getItemsPerPage()); ->setMaxResults($paginator->getItemsPerPage());
// allow to order queries and return the new query // allow to order queries and return the new query
return $this->orderQuery($action, $query, $request, $paginator, $_format); return $this->orderQuery($action, $query, $request, $paginator, $_format);
} }
/** /**
* Add ordering fields in the query build by self::queryEntities * Add ordering fields in the query build by self::queryEntities
*
*/ */
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format) protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
{ {
@ -112,14 +97,12 @@ class AbstractCRUDController extends AbstractController
* Build the base query for listing all entities. * Build the base query for listing all entities.
* *
* This method is used internally by `countEntities` `queryEntities` * This method is used internally by `countEntities` `queryEntities`
* *
* This base query does not contains any `WHERE` or `SELECT` clauses. You * This base query does not contains any `WHERE` or `SELECT` clauses. You
* can add some by using the method `customizeQuery`. * can add some by using the method `customizeQuery`.
* *
* The alias for the entity is "e". * The alias for the entity is "e".
* *
* @param string $action
* @param Request $request
* @return QueryBuilder * @return QueryBuilder
*/ */
protected function buildQueryEntities(string $action, Request $request) protected function buildQueryEntities(string $action, Request $request)
@ -127,8 +110,7 @@ class AbstractCRUDController extends AbstractController
$qb = $this->getDoctrine()->getManager() $qb = $this->getDoctrine()->getManager()
->createQueryBuilder() ->createQueryBuilder()
->select('e') ->select('e')
->from($this->getEntityClass(), 'e') ->from($this->getEntityClass(), 'e');
;
$this->customizeQuery($action, $request, $qb); $this->customizeQuery($action, $request, $qb);
@ -138,55 +120,54 @@ class AbstractCRUDController extends AbstractController
protected function customizeQuery(string $action, Request $request, $query): void {} protected function customizeQuery(string $action, Request $request, $query): void {}
/** /**
* Get the result of the query * Get the result of the query.
*/ */
protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query) protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query)
{ {
return $query->getQuery()->getResult(); return $query->getQuery()->getResult();
} }
protected function onPreIndex(string $action, Request $request, string $_format): ?Response protected function onPreIndex(string $action, Request $request, string $_format): ?Response
{ {
return null;
}
/**
* method used by indexAction
*/
protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response
{
return null; return null;
} }
/** /**
* method used by indexAction * Method used by indexAction.
*/
protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response
{
return null;
}
/**
* Method used by indexAction.
*/ */
protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response
{ {
return null; return null;
} }
/** /**
* method used by indexAction * Method used by indexAction.
*/ */
protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response
{ {
return null; return null;
} }
/** /**
* Get the complete FQDN of the class * Get the FQDN of the class.
* *
* @return string the complete fqdn of the class * @return class-string The FQDN of the class
*/ */
protected function getEntityClass(): string protected function getEntityClass(): string
{ {
return $this->crudConfig['class']; return $this->crudConfig['class'];
} }
/** /**
* called on post fetch entity * Called on post fetch entity.
*/ */
protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response
{ {
@ -194,7 +175,7 @@ class AbstractCRUDController extends AbstractController
} }
/** /**
* Called on post check ACL * Called on post check ACL.
*/ */
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
{ {
@ -203,23 +184,23 @@ class AbstractCRUDController extends AbstractController
/** /**
* check the acl. Called by every action. * check the acl. Called by every action.
* *
* By default, check the role given by `getRoleFor` for the value given in * By default, check the role given by `getRoleFor` for the value given in
* $entity. * $entity.
* *
* Throw an \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException * Throw an \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
* if not accessible. * if not accessible.
* *
* @throws \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException * @throws \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
*/ */
protected function checkACL(string $action, Request $request, string $_format, $entity = null) protected function checkACL(string $action, Request $request, string $_format, $entity = null)
{ {
// @TODO: Implements abstract getRoleFor method or do it in the interface.
$this->denyAccessUnlessGranted($this->getRoleFor($action, $request, $entity, $_format), $entity); $this->denyAccessUnlessGranted($this->getRoleFor($action, $request, $entity, $_format), $entity);
} }
/** /**
* * @return string The crud name.
* @return string the crud name
*/ */
protected function getCrudName(): string protected function getCrudName(): string
{ {
@ -230,7 +211,7 @@ class AbstractCRUDController extends AbstractController
{ {
return $this->crudConfig['actions'][$action]; return $this->crudConfig['actions'][$action];
} }
/** /**
* Set the crud configuration * Set the crud configuration
* *
@ -241,9 +222,6 @@ class AbstractCRUDController extends AbstractController
$this->crudConfig = $config; $this->crudConfig = $config;
} }
/**
* @return PaginatorFactory
*/
protected function getPaginatorFactory(): PaginatorFactory protected function getPaginatorFactory(): PaginatorFactory
{ {
return $this->container->get('chill_main.paginator_factory'); return $this->container->get('chill_main.paginator_factory');
@ -254,9 +232,6 @@ class AbstractCRUDController extends AbstractController
return $this->get('validator'); return $this->get('validator');
} }
/**
* @return array
*/
public static function getSubscribedServices(): array public static function getSubscribedServices(): array
{ {
return \array_merge( return \array_merge(

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\CRUD\Controller; namespace Chill\MainBundle\CRUD\Controller;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -17,36 +19,36 @@ class ApiController extends AbstractCRUDController
{ {
/** /**
* The view action. * The view action.
* *
* Some steps may be overriden during this process of rendering: * Some steps may be overriden during this process of rendering:
* *
* This method: * This method:
* *
* 1. fetch the entity, using `getEntity` * 1. fetch the entity, using `getEntity`
* 2. launch `onPostFetchEntity`. If postfetch is an instance of Response, * 2. launch `onPostFetchEntity`. If postfetch is an instance of Response,
* this response is returned. * this response is returned.
* 2. throw an HttpNotFoundException if entity is null * 2. throw an HttpNotFoundException if entity is null
* 3. check ACL using `checkACL` ; * 3. check ACL using `checkACL` ;
* 4. launch `onPostCheckACL`. If the result is an instance of Response, * 4. launch `onPostCheckACL`. If the result is an instance of Response,
* this response is returned ; * this response is returned ;
* 5. Serialize the entity and return the result. The serialization context is given by `getSerializationContext` * 5. Serialize the entity and return the result. The serialization context is given by `getSerializationContext`
* *
*/ */
protected function entityGet(string $action, Request $request, $id, $_format = 'html'): Response protected function entityGet(string $action, Request $request, $id, $_format = 'html'): Response
{ {
$entity = $this->getEntity($action, $id, $request, $_format); $entity = $this->getEntity($action, $id, $request);
$postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format); $postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format);
if ($postFetch instanceof Response) { if ($postFetch instanceof Response) {
return $postFetch; return $postFetch;
} }
$response = $this->checkACL($action, $request, $_format, $entity); $response = $this->checkACL($action, $request, $_format, $entity);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
} }
$response = $this->onPostCheckACL($action, $request, $_format, $entity); $response = $this->onPostCheckACL($action, $request, $_format, $entity);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
@ -86,7 +88,7 @@ class ApiController extends AbstractCRUDController
case Request::METHOD_PATCH: case Request::METHOD_PATCH:
return $this->entityPut('_entity', $request, $id, $_format); return $this->entityPut('_entity', $request, $id, $_format);
case Request::METHOD_POST: case Request::METHOD_POST:
return $this->entityPostAction('_entity', $request, $id, $_format); return $this->entityPostAction('_entity', $request, $id);
case Request::METHOD_DELETE: case Request::METHOD_DELETE:
return $this->entityDelete('_entity', $request, $id, $_format); return $this->entityDelete('_entity', $request, $id, $_format);
default: default:
@ -112,9 +114,9 @@ class ApiController extends AbstractCRUDController
} catch (NotEncodableValueException $e) { } catch (NotEncodableValueException $e) {
throw new BadRequestException("invalid json", 400, $e); throw new BadRequestException("invalid json", 400, $e);
} }
$errors = $this->validate($action, $request, $_format, $entity); $errors = $this->validate($action, $request, $_format, $entity);
$response = $this->onAfterValidation($action, $request, $_format, $entity, $errors); $response = $this->onAfterValidation($action, $request, $_format, $entity, $errors);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
@ -126,12 +128,12 @@ class ApiController extends AbstractCRUDController
return $response; return $response;
} }
$response = $this->checkACL($action, $request, $_format, $entity); $response = $this->checkACL($action, $request, $_format, $entity);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
} }
$response = $this->onPostCheckACL($action, $request, $_format, $entity); $response = $this->onPostCheckACL($action, $request, $_format, $entity);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
@ -148,33 +150,33 @@ class ApiController extends AbstractCRUDController
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
} }
return $this->json( return $this->json(
$entity, $entity,
Response::HTTP_OK, Response::HTTP_OK,
[], [],
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity) $this->getContextForSerializationPostAlter($action, $request, $_format, $entity)
); );
} }
public function entityPut($action, Request $request, $id, string $_format): Response public function entityPut($action, Request $request, $id, string $_format): Response
{ {
$entity = $this->getEntity($action, $id, $request, $_format); $entity = $this->getEntity($action, $id, $request);
$postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format); $postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format);
if ($postFetch instanceof Response) { if ($postFetch instanceof Response) {
return $postFetch; return $postFetch;
} }
if (NULL === $entity) { if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s " throw $this->createNotFoundException(sprintf("The %s with id %s "
. "is not found", $this->getCrudName(), $id)); . "is not found", $this->getCrudName(), $id));
} }
$response = $this->checkACL($action, $request, $_format, $entity); $response = $this->checkACL($action, $request, $_format, $entity);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
} }
$response = $this->onPostCheckACL($action, $request, $_format, $entity); $response = $this->onPostCheckACL($action, $request, $_format, $entity);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
@ -184,7 +186,7 @@ class ApiController extends AbstractCRUDController
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
} }
try { try {
$entity = $this->deserialize($action, $request, $_format, $entity); $entity = $this->deserialize($action, $request, $_format, $entity);
} catch (NotEncodableValueException $e) { } catch (NotEncodableValueException $e) {
@ -215,13 +217,13 @@ class ApiController extends AbstractCRUDController
return $this->json( return $this->json(
$entity, $entity,
Response::HTTP_OK, Response::HTTP_OK,
[], [],
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity) $this->getContextForSerializationPostAlter($action, $request, $_format, $entity)
); );
} }
public function entityDelete($action, Request $request, $id, string $_format): Response public function entityDelete($action, Request $request, $id, string $_format): Response
{ {
$entity = $this->getEntity($action, $id, $request, $_format); $entity = $this->getEntity($action, $id, $request);
if (NULL === $entity) { if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s " throw $this->createNotFoundException(sprintf("The %s with id %s "
@ -287,7 +289,7 @@ class ApiController extends AbstractCRUDController
protected function validate(string $action, Request $request, string $_format, $entity, array $more = []): ConstraintViolationListInterface protected function validate(string $action, Request $request, string $_format, $entity, array $more = []): ConstraintViolationListInterface
{ {
$validationGroups = $this->getValidationGroups($action, $request, $_format, $entity); $validationGroups = $this->getValidationGroups($action, $request, $_format, $entity);
return $this->getValidator()->validate($entity, null, $validationGroups); return $this->getValidator()->validate($entity, null, $validationGroups);
} }
@ -309,7 +311,7 @@ class ApiController extends AbstractCRUDController
return $this->getSerializer()->deserialize($request->getContent(), $this->getEntityClass(), $_format, $context); return $this->getSerializer()->deserialize($request->getContent(), $this->getEntityClass(), $_format, $context);
} }
/** /**
* Base action for indexing entities * Base action for indexing entities
@ -327,11 +329,11 @@ class ApiController extends AbstractCRUDController
/** /**
* Build an index page. * Build an index page.
* *
* Some steps may be overriden during this process of rendering. * Some steps may be overriden during this process of rendering.
* *
* This method: * This method:
* *
* 1. Launch `onPreIndex` * 1. Launch `onPreIndex`
* x. check acl. If it does return a response instance, return it * x. check acl. If it does return a response instance, return it
* x. launch `onPostCheckACL`. If it does return a response instance, return it * x. launch `onPostCheckACL`. If it does return a response instance, return it
@ -342,58 +344,60 @@ class ApiController extends AbstractCRUDController
* x. fetch the results, using `getQueryResult` * x. fetch the results, using `getQueryResult`
* x. Launch `onPostIndexFetchQuery`. If it does return a response instance, return it * x. Launch `onPostIndexFetchQuery`. If it does return a response instance, return it
* 4. Serialize the entities in a Collection, using `SerializeCollection` * 4. Serialize the entities in a Collection, using `SerializeCollection`
* *
* @param string $action * @param string $action
* @param Request $request * @param Request $request
* @return type
*/ */
protected function indexApiAction($action, Request $request, $_format) protected function indexApiAction($action, Request $request, $_format)
{ {
$this->onPreIndex($action, $request, $_format); $this->onPreIndex($action, $request, $_format);
$response = $this->checkACL($action, $request, $_format); $response = $this->checkACL($action, $request, $_format);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
} }
if (!isset($entity)) { $entity = '';
$entity = '';
}
$response = $this->onPostCheckACL($action, $request, $_format, $entity); $response = $this->onPostCheckACL($action, $request, $_format, $entity);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
} }
$totalItems = $this->countEntities($action, $request, $_format); $totalItems = $this->countEntities($action, $request, $_format);
$paginator = $this->getPaginatorFactory()->create($totalItems); $paginator = $this->getPaginatorFactory()->create($totalItems);
$response = $this->onPreIndexBuildQuery($action, $request, $_format, $totalItems, $response = $this->onPreIndexBuildQuery(
$paginator); $action,
$request,
$_format,
$totalItems,
$paginator
);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
} }
$query = $this->queryEntities($action, $request, $_format, $paginator); $query = $this->queryEntities($action, $request, $_format, $paginator);
$response = $this->onPostIndexBuildQuery($action, $request, $_format, $totalItems, $response = $this->onPostIndexBuildQuery($action, $request, $_format, $totalItems,
$paginator, $query); $paginator, $query);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
} }
$entities = $this->getQueryResult($action, $request, $_format, $totalItems, $paginator, $query); $entities = $this->getQueryResult($action, $request, $_format, $totalItems, $paginator, $query);
$response = $this->onPostIndexFetchQuery($action, $request, $_format, $totalItems, $response = $this->onPostIndexFetchQuery($action, $request, $_format, $totalItems,
$paginator, $entities); $paginator, $entities);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;
} }
return $this->serializeCollection($action, $request, $_format, $paginator, $entities); return $this->serializeCollection($action, $request, $_format, $paginator, $entities);
} }
/** /**
@ -402,7 +406,7 @@ class ApiController extends AbstractCRUDController
* This method: * This method:
* *
* 1. Fetch the base entity (throw 404 if not found) * 1. Fetch the base entity (throw 404 if not found)
* 2. checkACL, * 2. checkACL,
* 3. run onPostCheckACL, return response if any, * 3. run onPostCheckACL, return response if any,
* 4. deserialize posted data into the entity given by $postedDataType, with the context in $postedDataContext * 4. deserialize posted data into the entity given by $postedDataType, with the context in $postedDataContext
* 5. run 'add+$property' for POST method, or 'remove+$property' for DELETE method * 5. run 'add+$property' for POST method, or 'remove+$property' for DELETE method
@ -410,7 +414,7 @@ class ApiController extends AbstractCRUDController
* 7. run onAfterValidation * 7. run onAfterValidation
* 8. if errors, return a 422 response with errors * 8. if errors, return a 422 response with errors
* 9. if $forcePersist === true, persist the entity * 9. if $forcePersist === true, persist the entity
* 10. flush the data * 10. flush the data
* 11. run onAfterFlush * 11. run onAfterFlush
* 12. return a 202 response for DELETE with empty body, or HTTP 200 for post with serialized posted entity * 12. return a 202 response for DELETE with empty body, or HTTP 200 for post with serialized posted entity
* *
@ -425,7 +429,7 @@ class ApiController extends AbstractCRUDController
* @throw BadRequestException if unable to deserialize the posted data * @throw BadRequestException if unable to deserialize the posted data
* @throw BadRequestException if the method is not POST or DELETE * @throw BadRequestException if the method is not POST or DELETE
* *
*/ */
protected function addRemoveSomething(string $action, $id, Request $request, string $_format, string $property, string $postedDataType, array $postedDataContext = [], bool $forcePersist = false): Response protected function addRemoveSomething(string $action, $id, Request $request, string $_format, string $property, string $postedDataType, array $postedDataContext = [], bool $forcePersist = false): Response
{ {
$entity = $this->getEntity($action, $id, $request); $entity = $this->getEntity($action, $id, $request);
@ -500,12 +504,14 @@ class ApiController extends AbstractCRUDController
return $this->json( return $this->json(
$postedData, $postedData,
Response::HTTP_OK, Response::HTTP_OK,
[], [],
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity, [$postedData]) $this->getContextForSerializationPostAlter($action, $request, $_format, $entity, [$postedData])
); );
} }
throw new \Exception('Unable to handle such request method.');
} }
/** /**
* Serialize collections * Serialize collections
* *
@ -518,7 +524,7 @@ class ApiController extends AbstractCRUDController
return $this->json($model, Response::HTTP_OK, [], $context); return $this->json($model, Response::HTTP_OK, [], $context);
} }
protected function getContextForSerialization(string $action, Request $request, string $_format, $entity): array protected function getContextForSerialization(string $action, Request $request, string $_format, $entity): array
{ {
@ -535,7 +541,7 @@ class ApiController extends AbstractCRUDController
} }
/** /**
* Get the context for serialization post alter query (in case of * Get the context for serialization post alter query (in case of
* PATCH, PUT, or POST method) * PATCH, PUT, or POST method)
* *
* This is called **after** the entity was altered. * This is called **after** the entity was altered.
@ -563,7 +569,7 @@ class ApiController extends AbstractCRUDController
throw new \RuntimeException(sprintf("the config does not have any role for the ". throw new \RuntimeException(sprintf("the config does not have any role for the ".
"method %s nor a global role for the whole action. Add those to your ". "method %s nor a global role for the whole action. Add those to your ".
"configuration or override the required method", $request->getMethod())); "configuration or override the required method", $request->getMethod()));
} }
protected function getSerializer(): SerializerInterface protected function getSerializer(): SerializerInterface

View File

@ -1,22 +1,4 @@
<?php <?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2019, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\CRUD\Controller; namespace Chill\MainBundle\CRUD\Controller;
@ -37,60 +19,40 @@ use Chill\MainBundle\Pagination\PaginatorInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
/**
* Class CRUDController
*
* @package Chill\MainBundle\CRUD\Controller
*/
class CRUDController extends AbstractController class CRUDController extends AbstractController
{ {
/** /**
* The crud configuration * The crud configuration
* *
* This configuration si defined by `chill_main['crud']`. * This configuration si defined by `chill_main['crud']`.
*
* @var array
*/ */
protected $crudConfig; protected array $crudConfig;
/** public function setCrudConfig(array $config): void
* @param array $config
*/
public function setCrudConfig(array $config)
{ {
$this->crudConfig = $config; $this->crudConfig = $config;
} }
/** public function CRUD(?string $parameter): Response
* @param $parameter
* @return Response
*/
public function CRUD($parameter)
{ {
return new Response($parameter); return new Response($parameter);
} }
/** /**
* @param Request $request
* @param $id * @param $id
* @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
*/ */
public function delete(Request $request, $id) public function delete(Request $request, $id): Response
{ {
return $this->deleteAction('delete', $request, $id); return $this->deleteAction('delete', $request, $id);
} }
/** /**
* @param string $action
* @param Request $request
* @param $id * @param $id
* @param null $formClass * @param null $formClass
* @return null|\Symfony\Component\HttpFoundation\RedirectResponse|Response|void
*/ */
protected function deleteAction(string $action, Request $request, $id, $formClass = null) protected function deleteAction(string $action, Request $request, $id, $formClass = null): Response
{ {
$this->onPreDelete($action, $request, $id); $this->onPreDelete($action, $request);
$entity = $this->getEntity($action, $id, $request); $entity = $this->getEntity($action, $id, $request);
@ -101,8 +63,13 @@ class CRUDController extends AbstractController
} }
if (NULL === $entity) { if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s " throw $this->createNotFoundException(
. "is not found"), $this->getCrudName(), $id); sprintf(
'The %s with id %s is not found',
$this->getCrudName(),
$id
)
);
} }
$response = $this->checkACL($action, $entity); $response = $this->checkACL($action, $entity);
@ -141,7 +108,9 @@ class CRUDController extends AbstractController
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', ['id' => $entity->getId()]); return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', ['id' => $entity->getId()]);
} elseif ($form->isSubmitted()) { }
if ($form->isSubmitted()) {
$this->addFlash('error', $this->generateFormErrorMessage($action, $form)); $this->addFlash('error', $this->generateFormErrorMessage($action, $form));
} }
@ -233,7 +202,6 @@ class CRUDController extends AbstractController
* *
* @param string $action * @param string $action
* @param Request $request * @param Request $request
* @return type
*/ */
protected function indexEntityAction($action, Request $request) protected function indexEntityAction($action, Request $request)
{ {
@ -244,9 +212,7 @@ class CRUDController extends AbstractController
return $response; return $response;
} }
if (!isset($entity)) { $entity = '';
$entity = '';
}
$response = $this->onPostCheckACL($action, $request, $entity); $response = $this->onPostCheckACL($action, $request, $entity);
if ($response instanceof Response) { if ($response instanceof Response) {
@ -342,11 +308,12 @@ class CRUDController extends AbstractController
*/ */
protected function buildQueryEntities(string $action, Request $request) protected function buildQueryEntities(string $action, Request $request)
{ {
$query = $this->getDoctrine()->getManager() $query = $this
->getDoctrine()
->getManager()
->createQueryBuilder() ->createQueryBuilder()
->select('e') ->select('e')
->from($this->getEntityClass(), 'e') ->from($this->getEntityClass(), 'e');
;
$this->customizeQuery($action, $request, $query); $this->customizeQuery($action, $request, $query);
@ -371,7 +338,7 @@ class CRUDController extends AbstractController
*/ */
protected function queryEntities(string $action, Request $request, PaginatorInterface $paginator, ?FilterOrderHelper $filterOrder = null) protected function queryEntities(string $action, Request $request, PaginatorInterface $paginator, ?FilterOrderHelper $filterOrder = null)
{ {
$query = $this->buildQueryEntities($action, $request, $filterOrder) $query = $this->buildQueryEntities($action, $request)
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber()) ->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
->setMaxResults($paginator->getItemsPerPage()); ->setMaxResults($paginator->getItemsPerPage());
@ -420,7 +387,7 @@ class CRUDController extends AbstractController
*/ */
protected function countEntities(string $action, Request $request, ?FilterOrderHelper $filterOrder = null): int protected function countEntities(string $action, Request $request, ?FilterOrderHelper $filterOrder = null): int
{ {
return $this->buildQueryEntities($action, $request, $filterOrder) return $this->buildQueryEntities($action, $request)
->select('COUNT(e)') ->select('COUNT(e)')
->getQuery() ->getQuery()
->getSingleScalarResult() ->getSingleScalarResult()
@ -505,8 +472,13 @@ class CRUDController extends AbstractController
} }
if (NULL === $entity) { if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s " throw $this->createNotFoundException(
. "is not found", $this->getCrudName(), $id)); sprintf(
'The %s with id %s is not found',
$this->getCrudName(),
$id
)
);
} }
$response = $this->checkACL($action, $entity); $response = $this->checkACL($action, $entity);
@ -598,8 +570,13 @@ class CRUDController extends AbstractController
$entity = $this->getEntity($action, $id, $request); $entity = $this->getEntity($action, $id, $request);
if (NULL === $entity) { if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s " throw $this->createNotFoundException(
. "is not found", $this->getCrudName(), $id)); sprintf(
'The %s with id %s is not found',
$this->getCrudName(),
$id
)
);
} }
$response = $this->checkACL($action, $entity); $response = $this->checkACL($action, $entity);

View File

@ -1,22 +1,6 @@
<?php <?php
/*
* Chill is a software for social workers declare(strict_types=1);
*
* Copyright (C) 2019, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\CRUD\Routing; namespace Chill\MainBundle\CRUD\Routing;
@ -27,22 +11,13 @@ use Symfony\Component\HttpFoundation\Request;
use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\CRUD\Controller\CRUDController; use Chill\MainBundle\CRUD\Controller\CRUDController;
/**
* Class CRUDRoutesLoader
* Load the route for CRUD
*
* @package Chill\MainBundle\CRUD\Routing
*/
class CRUDRoutesLoader extends Loader class CRUDRoutesLoader extends Loader
{ {
protected array $crudConfig = []; protected array $crudConfig = [];
protected array $apiCrudConfig = []; protected array $apiCrudConfig = [];
/** private bool $isLoaded = false;
* @var bool
*/
private $isLoaded = false;
private const ALL_SINGLE_METHODS = [ private const ALL_SINGLE_METHODS = [
Request::METHOD_GET, Request::METHOD_GET,
@ -52,19 +27,15 @@ class CRUDRoutesLoader extends Loader
]; ];
private const ALL_INDEX_METHODS = [ Request::METHOD_GET, Request::METHOD_HEAD ]; private const ALL_INDEX_METHODS = [ Request::METHOD_GET, Request::METHOD_HEAD ];
/** public function __construct(array $crudConfig, array $apiCrudConfig)
* CRUDRoutesLoader constructor.
*
* @param $crudConfig the config from cruds
* @param $apicrudConfig the config from api_crud
*/
public function __construct(array $crudConfig, array $apiConfig)
{ {
$this->crudConfig = $crudConfig; $this->crudConfig = $crudConfig;
$this->apiConfig = $apiConfig; $this->apiCrudConfig = $apiCrudConfig;
parent::__construct();
} }
/** /**
* @param mixed $resource * @param mixed $resource
* @param null $type * @param null $type
@ -74,7 +45,7 @@ class CRUDRoutesLoader extends Loader
{ {
return 'CRUD' === $type; return 'CRUD' === $type;
} }
/** /**
* Load routes for CRUD and CRUD Api * Load routes for CRUD and CRUD Api
*/ */
@ -85,17 +56,17 @@ class CRUDRoutesLoader extends Loader
} }
$collection = new RouteCollection(); $collection = new RouteCollection();
foreach ($this->crudConfig as $crudConfig) { foreach ($this->crudConfig as $crudConfig) {
$collection->addCollection($this->loadCrudConfig($crudConfig)); $collection->addCollection($this->loadCrudConfig($crudConfig));
} }
foreach ($this->apiConfig as $crudConfig) { foreach ($this->apiCrudConfig as $crudConfig) {
$collection->addCollection($this->loadApi($crudConfig)); $collection->addCollection($this->loadApi($crudConfig));
} }
return $collection; return $collection;
} }
/** /**
* Load routes for CRUD (without api) * Load routes for CRUD (without api)
* *
@ -112,7 +83,7 @@ class CRUDRoutesLoader extends Loader
$defaults = [ $defaults = [
'_controller' => $controller.':'.($action['controller_action'] ?? $name) '_controller' => $controller.':'.($action['controller_action'] ?? $name)
]; ];
if ($name === 'index') { if ($name === 'index') {
$path = "{_locale}".$crudConfig['base_path']; $path = "{_locale}".$crudConfig['base_path'];
$route = new Route($path, $defaults); $route = new Route($path, $defaults);
@ -126,10 +97,10 @@ class CRUDRoutesLoader extends Loader
]; ];
$route = new Route($path, $defaults, $requirements); $route = new Route($path, $defaults, $requirements);
} }
$collection->add('chill_crud_'.$crudConfig['name'].'_'.$name, $route); $collection->add('chill_crud_'.$crudConfig['name'].'_'.$name, $route);
} }
return $collection; return $collection;
} }
@ -186,14 +157,14 @@ class CRUDRoutesLoader extends Loader
if (count($methods) === 0) { if (count($methods) === 0) {
throw new \RuntimeException("The api configuration named \"{$crudConfig['name']}\", action \"{$name}\", ". throw new \RuntimeException("The api configuration named \"{$crudConfig['name']}\", action \"{$name}\", ".
"does not have any allowed methods. You should remove this action from the config ". "does not have any allowed methods. You should remove this action from the config ".
"or allow, at least, one method"); "or allow, at least, one method");
} }
if ('_entity' === $name && \in_array(Request::METHOD_POST, $methods)) { if ('_entity' === $name && \in_array(Request::METHOD_POST, $methods)) {
unset($methods[\array_search(Request::METHOD_POST, $methods)]); unset($methods[\array_search(Request::METHOD_POST, $methods)]);
$entityPostRoute = $this->createEntityPostRoute($name, $crudConfig, $action, $entityPostRoute = $this->createEntityPostRoute($name, $crudConfig, $action,
$controller); $controller);
$collection->add("chill_api_single_{$crudConfig['name']}_{$name}_create", $collection->add("chill_api_single_{$crudConfig['name']}_{$name}_create",
$entityPostRoute); $entityPostRoute);
} }
@ -205,7 +176,7 @@ class CRUDRoutesLoader extends Loader
$route = new Route($path, $defaults, $requirements); $route = new Route($path, $defaults, $requirements);
$route->setMethods($methods); $route->setMethods($methods);
$collection->add('chill_api_single_'.$crudConfig['name'].'_'.$name, $route); $collection->add('chill_api_single_'.$crudConfig['name'].'_'.$name, $route);
} }

View File

@ -3,6 +3,7 @@
namespace Chill\MainBundle; namespace Chill\MainBundle;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Chill\MainBundle\Search\SearchApiInterface;
use Chill\MainBundle\Search\SearchInterface; use Chill\MainBundle\Search\SearchInterface;
use Chill\MainBundle\Security\Authorization\ChillVoterInterface; use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
use Chill\MainBundle\Security\ProvideRoleInterface; use Chill\MainBundle\Security\ProvideRoleInterface;
@ -41,6 +42,8 @@ class ChillMainBundle extends Bundle
->addTag('chill_main.scope_resolver'); ->addTag('chill_main.scope_resolver');
$container->registerForAutoconfiguration(ChillEntityRenderInterface::class) $container->registerForAutoconfiguration(ChillEntityRenderInterface::class)
->addTag('chill.render_entity'); ->addTag('chill.render_entity');
$container->registerForAutoconfiguration(SearchApiInterface::class)
->addTag('chill.search_api_provider');
$container->addCompilerPass(new SearchableServicesCompilerPass()); $container->addCompilerPass(new SearchableServicesCompilerPass());
$container->addCompilerPass(new ConfigConsistencyCompilerPass()); $container->addCompilerPass(new ConfigConsistencyCompilerPass());

View File

@ -1,7 +1,10 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\Command; namespace Chill\MainBundle\Command;
use Chill\MainBundle\Repository\UserRepository;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@ -23,101 +26,51 @@ use League\Csv\Writer;
class ChillImportUsersCommand extends Command class ChillImportUsersCommand extends Command
{ {
protected EntityManagerInterface $em;
/**
* protected ValidatorInterface $validator;
* @var EntityManagerInterface
*/ protected LoggerInterface $logger;
protected $em;
protected UserPasswordEncoderInterface $passwordEncoder;
/**
* protected UserRepository $userRepository;
* @var ValidatorInterface
*/ protected bool $doChanges = true;
protected $validator;
protected OutputInterface $tempOutput;
/**
* protected InputInterface $tempInput;
* @var LoggerInterface
*/
protected $logger;
/**
*
* @var UserPasswordEncoderInterface
*/
protected $passwordEncoder;
/**
*
* @var \Chill\MainBundle\Repository\UserRepository
*/
protected $userRepository;
/**
*
* @var bool
*/
protected $doChanges = true;
/**
*
* @var OutputInterface
*/
protected $tempOutput;
/**
*
* @var InputInterface
*/
protected $tempInput;
/** /**
* Centers and aliases. * Centers and aliases.
* *
* key are aliases, values are an array of centers * key are aliases, values are an array of centers
*
* @var array
*/ */
protected $centers = []; protected array $centers;
/** protected array $permissionGroups;
*
* @var array protected array $groupCenters;
*/
protected $permissionGroups = []; protected Writer $output;
/**
*
* @var array
*/
protected $groupCenters = [];
/**
*
* @var Writer
*/
protected $output = null;
public function __construct( public function __construct(
EntityManagerInterface $em, EntityManagerInterface $em,
LoggerInterface $logger, LoggerInterface $logger,
UserPasswordEncoderInterface $passwordEncoder, UserPasswordEncoderInterface $passwordEncoder,
ValidatorInterface $validator ValidatorInterface $validator,
UserRepository $userRepository
) { ) {
$this->em = $em; $this->em = $em;
$this->passwordEncoder = $passwordEncoder; $this->passwordEncoder = $passwordEncoder;
$this->validator = $validator; $this->validator = $validator;
$this->logger = $logger; $this->logger = $logger;
$this->userRepository = $userRepository;
$this->userRepository = $em->getRepository(User::class);
parent::__construct('chill:main:import-users'); parent::__construct('chill:main:import-users');
} }
protected function configure() protected function configure()
{ {
$this $this
@ -126,25 +79,24 @@ class ChillImportUsersCommand extends Command
->addArgument('csvfile', InputArgument::REQUIRED, 'Path to the csv file. Columns are: `username`, `email`, `center` (can contain alias), `permission group`') ->addArgument('csvfile', InputArgument::REQUIRED, 'Path to the csv file. Columns are: `username`, `email`, `center` (can contain alias), `permission group`')
->addOption('grouping-centers', null, InputOption::VALUE_OPTIONAL, 'Path to a csv file to aggregate multiple centers into a single alias') ->addOption('grouping-centers', null, InputOption::VALUE_OPTIONAL, 'Path to a csv file to aggregate multiple centers into a single alias')
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not commit the changes') ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not commit the changes')
->addOption('csv-dump', null, InputOption::VALUE_REQUIRED, 'A path to dump a summary of the created file') ->addOption('csv-dump', null, InputOption::VALUE_REQUIRED, 'A path to dump a summary of the created file');
;
} }
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$this->tempOutput = $output; $this->tempOutput = $output;
$this->tempInput = $input; $this->tempInput = $input;
if ($input->getOption('dry-run')) { if ($input->getOption('dry-run')) {
$this->doChanges = false; $this->doChanges = false;
} }
$this->prepareWriter(); $this->prepareWriter();
if ($input->hasOption('grouping-centers')) { if ($input->hasOption('grouping-centers')) {
$this->prepareGroupingCenters(); $this->prepareGroupingCenters();
} }
try { try {
$this->loadUsers(); $this->loadUsers();
} }
@ -152,19 +104,19 @@ class ChillImportUsersCommand extends Command
throw $e; throw $e;
} }
} }
protected function prepareWriter() protected function prepareWriter()
{ {
$this->output = $output = Writer::createFromPath($this->tempInput $this->output = $output = Writer::createFromPath($this->tempInput
->getOption('csv-dump'), 'a+'); ->getOption('csv-dump'), 'a+');
$output->insertOne([ $output->insertOne([
'email', 'email',
'username', 'username',
'id' 'id'
]); ]);
} }
protected function appendUserToFile(User $user) protected function appendUserToFile(User $user)
{ {
$this->output->insertOne( [ $this->output->insertOne( [
@ -173,35 +125,35 @@ class ChillImportUsersCommand extends Command
$user->getId() $user->getId()
]); ]);
} }
protected function loadUsers() protected function loadUsers()
{ {
$reader = Reader::createFromPath($this->tempInput->getArgument('csvfile')); $reader = Reader::createFromPath($this->tempInput->getArgument('csvfile'));
$reader->setHeaderOffset(0); $reader->setHeaderOffset(0);
foreach ($reader->getRecords() as $line => $r) { foreach ($reader->getRecords() as $line => $r) {
$this->logger->debug("starting handling new line", [ $this->logger->debug("starting handling new line", [
'line' => $line 'line' => $line
]); ]);
if ($this->doesUserExists($r)) { if ($this->doesUserExists($r)) {
$this->tempOutput->writeln(sprintf("User with username '%s' already " $this->tempOutput->writeln(sprintf("User with username '%s' already "
. "exists, skipping", $r["username"])); . "exists, skipping", $r["username"]));
$this->logger->info("One user already exists, skipping creation", [ $this->logger->info("One user already exists, skipping creation", [
'username_in_file' => $r['username'], 'username_in_file' => $r['username'],
'email_in_file' => $r['email'], 'email_in_file' => $r['email'],
'line' => $line 'line' => $line
]); ]);
continue; continue;
} }
$user = $this->createUser($line, $r); $user = $this->createUser($line, $r);
$this->appendUserToFile($user); $this->appendUserToFile($user);
} }
} }
protected function doesUserExists($data) protected function doesUserExists($data)
{ {
if ($this->userRepository->countByUsernameOrEmail($data['username']) > 0) { if ($this->userRepository->countByUsernameOrEmail($data['username']) > 0) {
@ -211,10 +163,10 @@ class ChillImportUsersCommand extends Command
if ($this->userRepository->countByUsernameOrEmail($data['email']) > 0) { if ($this->userRepository->countByUsernameOrEmail($data['email']) > 0) {
return true; return true;
} }
return false; return false;
} }
protected function createUser($offset, $data) protected function createUser($offset, $data)
{ {
$user = new User(); $user = new User();
@ -222,41 +174,41 @@ class ChillImportUsersCommand extends Command
->setEmail(\trim($data['email'])) ->setEmail(\trim($data['email']))
->setUsername(\trim($data['username'])) ->setUsername(\trim($data['username']))
->setEnabled(true) ->setEnabled(true)
->setPassword($this->passwordEncoder->encodePassword($user, ->setPassword($this->passwordEncoder->encodePassword($user,
\bin2hex(\random_bytes(32)))) \bin2hex(\random_bytes(32))))
; ;
$errors = $this->validator->validate($user); $errors = $this->validator->validate($user);
if ($errors->count() > 0) { if ($errors->count() > 0) {
$errorMessages = $this->concatenateViolations($errors); $errorMessages = $this->concatenateViolations($errors);
$this->tempOutput->writeln(sprintf("%d errors found with user with username \"%s\" at line %d", $errors->count(), $data['username'], $offset)); $this->tempOutput->writeln(sprintf("%d errors found with user with username \"%s\" at line %d", $errors->count(), $data['username'], $offset));
$this->tempOutput->writeln($errorMessages); $this->tempOutput->writeln($errorMessages);
throw new \RuntimeException("Found errors while creating an user. " throw new \RuntimeException("Found errors while creating an user. "
. "Watch messages in command output"); . "Watch messages in command output");
} }
$pgs = $this->getPermissionGroup($data['permission group']); $pgs = $this->getPermissionGroup($data['permission group']);
$centers = $this->getCenters($data['center']); $centers = $this->getCenters($data['center']);
foreach($pgs as $pg) { foreach($pgs as $pg) {
foreach ($centers as $center) { foreach ($centers as $center) {
$groupcenter = $this->createOrGetGroupCenter($center, $pg); $groupcenter = $this->createOrGetGroupCenter($center, $pg);
if (FALSE === $user->getGroupCenters()->contains($groupcenter)) { if (FALSE === $user->getGroupCenters()->contains($groupcenter)) {
$user->addGroupCenter($groupcenter); $user->addGroupCenter($groupcenter);
} }
} }
} }
if ($this->doChanges) { if ($this->doChanges) {
$this->em->persist($user); $this->em->persist($user);
$this->em->flush(); $this->em->flush();
} }
$this->logger->notice("Create user", [ $this->logger->notice("Create user", [
'username' => $user->getUsername(), 'username' => $user->getUsername(),
'id' => $user->getId(), 'id' => $user->getId(),
@ -265,65 +217,58 @@ class ChillImportUsersCommand extends Command
return $user; return $user;
} }
protected function getPermissionGroup($alias) protected function getPermissionGroup($alias)
{ {
if (\array_key_exists($alias, $this->permissionGroups)) { if (\array_key_exists($alias, $this->permissionGroups)) {
return $this->permissionGroups[$alias]; return $this->permissionGroups[$alias];
} }
$permissionGroupsByName = []; $permissionGroupsByName = [];
foreach($this->em->getRepository(PermissionsGroup::class) foreach($this->em->getRepository(PermissionsGroup::class)
->findAll() as $permissionGroup) { ->findAll() as $permissionGroup) {
$permissionGroupsByName[$permissionGroup->getName()] = $permissionGroup; $permissionGroupsByName[$permissionGroup->getName()] = $permissionGroup;
} }
if (count($permissionGroupsByName) === 0) { if (count($permissionGroupsByName) === 0) {
throw new \RuntimeException("no permission groups found. Create them " throw new \RuntimeException("no permission groups found. Create them "
. "before importing users"); . "before importing users");
} }
$question = new ChoiceQuestion("To which permission groups associate with \"$alias\" ?", $question = new ChoiceQuestion("To which permission groups associate with \"$alias\" ?",
\array_keys($permissionGroupsByName)); \array_keys($permissionGroupsByName));
$question $question
->setMultiselect(true) ->setMultiselect(true)
->setAutocompleterValues(\array_keys($permissionGroupsByName)) ->setAutocompleterValues(\array_keys($permissionGroupsByName))
->setNormalizer(function($value) { ->setNormalizer(function($value) {
if (NULL === $value) { return ''; } if (NULL === $value) { return ''; }
return \trim($value); return \trim($value);
}) })
; ;
$helper = $this->getHelper('question'); $helper = $this->getHelper('question');
$keys = $helper->ask($this->tempInput, $this->tempOutput, $question); $keys = $helper->ask($this->tempInput, $this->tempOutput, $question);
$this->tempOutput->writeln("You have chosen ".\implode(", ", $keys)); $this->tempOutput->writeln("You have chosen ".\implode(", ", $keys));
if ($helper->ask($this->tempInput, $this->tempOutput, if ($helper->ask($this->tempInput, $this->tempOutput,
new ConfirmationQuestion("Are you sure ?", true))) { new ConfirmationQuestion("Are you sure ?", true))) {
foreach ($keys as $key) { foreach ($keys as $key) {
$this->permissionGroups[$alias][] = $permissionGroupsByName[$key]; $this->permissionGroups[$alias][] = $permissionGroupsByName[$key];
} }
return $this->permissionGroups[$alias]; return $this->permissionGroups[$alias];
} else {
$this->logger->error("Error while responding to a a question");
$this->tempOutput("Ok, I accept, but I do not know what to do. Please try again.");
throw new \RuntimeException("Error while responding to a question");
} }
$this->logger->error('Error while responding to a a question');
$this->tempOutput->writeln('Ok, I accept, but I do not know what to do. Please try again.');
throw new \RuntimeException('Error while responding to a question');
} }
/**
*
* @param Center $center
* @param \Chill\MainBundle\Command\PermissionGroup $pg
* @return GroupCenter
*/
protected function createOrGetGroupCenter(Center $center, PermissionsGroup $pg): GroupCenter protected function createOrGetGroupCenter(Center $center, PermissionsGroup $pg): GroupCenter
{ {
if (\array_key_exists($center->getId(), $this->groupCenters)) { if (\array_key_exists($center->getId(), $this->groupCenters)) {
@ -331,36 +276,36 @@ class ChillImportUsersCommand extends Command
return $this->groupCenters[$center->getId()][$pg->getId()]; return $this->groupCenters[$center->getId()][$pg->getId()];
} }
} }
$repository = $this->em->getRepository(GroupCenter::class); $repository = $this->em->getRepository(GroupCenter::class);
$groupCenter = $repository->findOneBy(array( $groupCenter = $repository->findOneBy(array(
'center' => $center, 'center' => $center,
'permissionsGroup' => $pg 'permissionsGroup' => $pg
)); ));
if ($groupCenter === NULL) { if ($groupCenter === NULL) {
$groupCenter = new GroupCenter(); $groupCenter = new GroupCenter();
$groupCenter $groupCenter
->setCenter($center) ->setCenter($center)
->setPermissionsGroup($pg) ->setPermissionsGroup($pg)
; ;
$this->em->persist($groupCenter); $this->em->persist($groupCenter);
} }
$this->groupCenters[$center->getId()][$pg->getId()] = $groupCenter; $this->groupCenters[$center->getId()][$pg->getId()] = $groupCenter;
return $groupCenter; return $groupCenter;
} }
protected function prepareGroupingCenters() protected function prepareGroupingCenters()
{ {
$reader = Reader::createFromPath($this->tempInput->getOption('grouping-centers')); $reader = Reader::createFromPath($this->tempInput->getOption('grouping-centers'));
$reader->setHeaderOffset(0); $reader->setHeaderOffset(0);
foreach ($reader->getRecords() as $r) { foreach ($reader->getRecords() as $r) {
$this->centers[$r['alias']] = $this->centers[$r['alias']] =
\array_merge( \array_merge(
$this->centers[$r['alias']] ?? [], $this->centers[$r['alias']] ?? [],
$this->getCenters($r['center'] $this->getCenters($r['center']
@ -368,18 +313,18 @@ class ChillImportUsersCommand extends Command
); );
} }
} }
/** /**
* return a list of centers matching the name of alias. * return a list of centers matching the name of alias.
* *
* If the name match one center, this center is returned in an array. * If the name match one center, this center is returned in an array.
* *
* If the name match an alias, the centers corresponding to the alias are * If the name match an alias, the centers corresponding to the alias are
* returned in an array. * returned in an array.
* *
* If the center is not found or alias is not created, a new center is created * If the center is not found or alias is not created, a new center is created
* and suggested to user * and suggested to user
* *
* @param string $name the name of the center or the alias regrouping center * @param string $name the name of the center or the alias regrouping center
* @return Center[] * @return Center[]
*/ */
@ -387,62 +332,62 @@ class ChillImportUsersCommand extends Command
{ {
// sanitize // sanitize
$name = \trim($name); $name = \trim($name);
if (\array_key_exists($name, $this->centers)) { if (\array_key_exists($name, $this->centers)) {
return $this->centers[$name]; return $this->centers[$name];
} }
// search for a center with given name // search for a center with given name
$center = $this->em->getRepository(Center::class) $center = $this->em->getRepository(Center::class)
->findOneByName($name); ->findOneByName($name);
if ($center instanceof Center) { if ($center instanceof Center) {
$this->centers[$name] = [$center]; $this->centers[$name] = [$center];
return $this->centers[$name]; return $this->centers[$name];
} }
// suggest and create // suggest and create
$center = (new Center()) $center = (new Center())
->setName($name); ->setName($name);
$this->tempOutput->writeln("Center with name \"$name\" not found."); $this->tempOutput->writeln("Center with name \"$name\" not found.");
$qFormatter = $this->getHelper('question'); $qFormatter = $this->getHelper('question');
$question = new ConfirmationQuestion("Create a center with name \"$name\" ?", true); $question = new ConfirmationQuestion("Create a center with name \"$name\" ?", true);
if ($qFormatter->ask($this->tempInput, $this->tempOutput, $question)) { if ($qFormatter->ask($this->tempInput, $this->tempOutput, $question)) {
$this->centers[$name] = [ $center ]; $this->centers[$name] = [ $center ];
$errors = $this->validator->validate($center); $errors = $this->validator->validate($center);
if ($errors->count() > 0) { if ($errors->count() > 0) {
$errorMessages = $this->concatenateViolations($errors); $errorMessages = $this->concatenateViolations($errors);
$this->tempOutput->writeln(sprintf("%d errors found with center with name \"%s\"", $errors->count(), $name)); $this->tempOutput->writeln(sprintf("%d errors found with center with name \"%s\"", $errors->count(), $name));
$this->tempOutput->writeln($errorMessages); $this->tempOutput->writeln($errorMessages);
throw new \RuntimeException("Found errors while creating one center. " throw new \RuntimeException("Found errors while creating one center. "
. "Watch messages in command output"); . "Watch messages in command output");
} }
$this->em->persist($center); $this->em->persist($center);
return $this->centers[$name]; return $this->centers[$name];
} }
return null; return null;
} }
protected function concatenateViolations(ConstraintViolationListInterface $list) protected function concatenateViolations(ConstraintViolationListInterface $list)
{ {
$str = []; $str = [];
foreach ($list as $e) { foreach ($list as $e) {
/* @var $e \Symfony\Component\Validator\ConstraintViolationInterface */ /* @var $e \Symfony\Component\Validator\ConstraintViolationInterface */
$str[] = $e->getMessage(); $str[] = $e->getMessage();
} }
return \implode(";", $str); return \implode(";", $str);
} }
} }

View File

@ -10,18 +10,18 @@ use Symfony\Component\Console\Output\OutputInterface;
/** /**
* *
* @author Julien Fastré <julien.fastre@champs-libres.coop * @author Julien Fastré <julien.fastre@champs-libres.coop
* *
*/ */
class LoadCountriesCommand extends Command class LoadCountriesCommand extends Command
{ {
/** /**
* @var EntityManager * @var EntityManager
*/ */
private $entityManager; private $entityManager;
private $availableLanguages; private $availableLanguages;
/** /**
* LoadCountriesCommand constructor. * LoadCountriesCommand constructor.
* *
@ -34,7 +34,7 @@ class LoadCountriesCommand extends Command
$this->availableLanguages=$availableLanguages; $this->availableLanguages=$availableLanguages;
parent::__construct(); parent::__construct();
} }
/* /*
* (non-PHPdoc) * (non-PHPdoc)
* @see \Symfony\Component\Console\Command\Command::configure() * @see \Symfony\Component\Console\Command\Command::configure()
@ -45,7 +45,7 @@ class LoadCountriesCommand extends Command
->setDescription('Load or update countries in db. This command does not delete existing countries, '. ->setDescription('Load or update countries in db. This command does not delete existing countries, '.
'but will update names according to available languages'); 'but will update names according to available languages');
} }
/* /*
* (non-PHPdoc) * (non-PHPdoc)
* @see \Symfony\Component\Console\Command\Command::execute() * @see \Symfony\Component\Console\Command\Command::execute()
@ -54,43 +54,44 @@ class LoadCountriesCommand extends Command
{ {
$countries = static::prepareCountryList($this->availableLanguages); $countries = static::prepareCountryList($this->availableLanguages);
$em = $this->entityManager; $em = $this->entityManager;
foreach($countries as $country) { foreach($countries as $country) {
$countryStored = $em->getRepository('ChillMainBundle:Country') $countryStored = $em->getRepository('ChillMainBundle:Country')
->findOneBy(array('countryCode' => $country->getCountryCode())); ->findOneBy(array('countryCode' => $country->getCountryCode()));
if (NULL === $countryStored) { if (NULL === $countryStored) {
$em->persist($country); $em->persist($country);
} else { } else {
$countryStored->setName($country->getName()); $countryStored->setName($country->getName());
} }
} }
$em->flush(); $em->flush();
} }
public static function prepareCountryList($languages) public static function prepareCountryList($languages)
{ {
$regionBundle = Intl::getRegionBundle(); $regionBundle = Intl::getRegionBundle();
$countries = [];
foreach ($languages as $language) { foreach ($languages as $language) {
$countries[$language] = $regionBundle->getCountryNames($language); $countries[$language] = $regionBundle->getCountryNames($language);
} }
$countryEntities = array(); $countryEntities = array();
foreach ($countries[$languages[0]] as $countryCode => $name) { foreach ($countries[$languages[0]] as $countryCode => $name) {
$names = array(); $names = array();
foreach ($languages as $language) { foreach ($languages as $language) {
$names[$language] = $countries[$language][$countryCode]; $names[$language] = $countries[$language][$countryCode];
} }
$country = new \Chill\MainBundle\Entity\Country(); $country = new \Chill\MainBundle\Entity\Country();
$country->setName($names)->setCountryCode($countryCode); $country->setName($names)->setCountryCode($countryCode);
$countryEntities[] = $country; $countryEntities[] = $country;
} }
return $countryEntities; return $countryEntities;
} }
} }

View File

@ -1,27 +1,13 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Command; namespace Chill\MainBundle\Command;
use Chill\MainBundle\Doctrine\Model\Point; use Chill\MainBundle\Doctrine\Model\Point;
use Chill\MainBundle\Entity\Country; use Chill\MainBundle\Entity\Country;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
@ -31,38 +17,19 @@ use Symfony\Component\Filesystem\Filesystem;
use Chill\MainBundle\Entity\PostalCode; use Chill\MainBundle\Entity\PostalCode;
use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Class LoadPostalCodesCommand
*
* @package Chill\MainBundle\Command
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class LoadPostalCodesCommand extends Command class LoadPostalCodesCommand extends Command
{ {
private EntityManagerInterface $entityManager;
/**
* @var EntityManager private ValidatorInterface $validator;
*/
private $entityManager; public function __construct(EntityManagerInterface $entityManager, ValidatorInterface $validator)
/**
* @var ValidatorInterface
*/
private $validator;
/**
* LoadPostalCodesCommand constructor.
*
* @param EntityManager $entityManager
* @param ValidatorInterface $validator
*/
public function __construct(EntityManager $entityManager, ValidatorInterface $validator)
{ {
$this->entityManager = $entityManager; $this->entityManager = $entityManager;
$this->validator = $validator; $this->validator = $validator;
parent::__construct(); parent::__construct();
} }
protected function configure() protected function configure()
{ {
$this->setName('chill:main:postal-code:populate') $this->setName('chill:main:postal-code:populate')
@ -78,10 +45,10 @@ class LoadPostalCodesCommand extends Command
->addArgument('csv_file', InputArgument::REQUIRED, "the path to " ->addArgument('csv_file', InputArgument::REQUIRED, "the path to "
. "the csv file. See the help for specifications.") . "the csv file. See the help for specifications.")
->addOption( ->addOption(
'delimiter', 'delimiter',
'd', 'd',
InputOption::VALUE_OPTIONAL, InputOption::VALUE_OPTIONAL,
"The delimiter character of the csv file", "The delimiter character of the csv file",
",") ",")
->addOption( ->addOption(
'enclosure', 'enclosure',
@ -99,31 +66,26 @@ class LoadPostalCodesCommand extends Command
) )
; ;
} }
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
try { $csv = $this->getCSVResource($input);
$csv = $this->getCSVResource($input);
} catch (\RuntimeException $e) {
$output->writeln('<error>Error during opening the csv file : '.
$e->getMessage().'</error>');
}
if ($output->getVerbosity() === OutputInterface::VERBOSITY_VERY_VERBOSE) { if ($output->getVerbosity() === OutputInterface::VERBOSITY_VERY_VERBOSE) {
$output->writeln('The content of the file is ...'); $output->writeln('The content of the file is ...');
$output->write(file_get_contents($input->getArgument('csv_file'))); $output->write(file_get_contents($input->getArgument('csv_file')));
} }
$num = 0; $num = 0;
$line = 0; $line = 0;
while (($row = fgetcsv( while (($row = fgetcsv(
$csv, $csv,
0, 0,
$input->getOption('delimiter'), $input->getOption('delimiter'),
$input->getOption('enclosure'), $input->getOption('enclosure'),
$input->getOption('escape'))) !== false) { $input->getOption('escape'))) !== false) {
try{ try{
$this->addPostalCode($row, $output); $this->addPostalCode($row, $output);
$num++; $num++;
@ -136,31 +98,31 @@ class LoadPostalCodesCommand extends Command
} }
$line ++; $line ++;
} }
$this->entityManager->flush(); $this->entityManager->flush();
$output->writeln('<info>'.$num.' were added !</info>'); $output->writeln('<info>'.$num.' were added !</info>');
} }
private function getCSVResource(InputInterface $input) private function getCSVResource(InputInterface $input)
{ {
$fs = new Filesystem(); $fs = new Filesystem();
$filename = $input->getArgument('csv_file'); $filename = $input->getArgument('csv_file');
if (!$fs->exists($filename)) { if (!$fs->exists($filename)) {
throw new \RuntimeException("The file does not exists or you do not " throw new \RuntimeException("The file does not exists or you do not "
. "have the right to read it."); . "have the right to read it.");
} }
$resource = fopen($filename, 'r'); $resource = fopen($filename, 'r');
if ($resource == FALSE) { if ($resource == FALSE) {
throw new \RuntimeException("The file '$filename' could not be opened."); throw new \RuntimeException("The file '$filename' could not be opened.");
} }
return $resource; return $resource;
} }
private function addPostalCode($row, OutputInterface $output) private function addPostalCode($row, OutputInterface $output)
{ {
if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
@ -170,28 +132,28 @@ class LoadPostalCodesCommand extends Command
$country = $em $country = $em
->getRepository(Country::class) ->getRepository(Country::class)
->findOneBy(array('countryCode' => $row[2])); ->findOneBy(array('countryCode' => $row[2]));
if ($country === NULL) { if ($country === NULL) {
throw new CountryCodeNotFoundException(sprintf("The country with code %s is not found. Aborting to insert postal code with %s - %s", throw new CountryCodeNotFoundException(sprintf("The country with code %s is not found. Aborting to insert postal code with %s - %s",
$row[2], $row[0], $row[1])); $row[2], $row[0], $row[1]));
} }
// try to find an existing postal code // try to find an existing postal code
$existingPC = $em $existingPC = $em
->getRepository(PostalCode::class) ->getRepository(PostalCode::class)
->findBy(array('code' => $row[0], 'name' => $row[1])); ->findBy(array('code' => $row[0], 'name' => $row[1]));
if (count($existingPC) > 0) { if (count($existingPC) > 0) {
throw new ExistingPostalCodeException(sprintf("A postal code with code : %s and name : %s already exists, skipping", throw new ExistingPostalCodeException(sprintf("A postal code with code : %s and name : %s already exists, skipping",
$row[0], $row[1])); $row[0], $row[1]));
} }
$postalCode = (new PostalCode()) $postalCode = (new PostalCode())
->setCode($row[0]) ->setCode($row[0])
->setName($row[1]) ->setName($row[1])
->setCountry($country) ->setCountry($country)
; ;
if (NULL != $row[3]){ if (NULL != $row[3]){
$postalCode->setRefPostalCodeId($row[3]); $postalCode->setRefPostalCodeId($row[3]);
} }
@ -205,7 +167,7 @@ class LoadPostalCodesCommand extends Command
} }
$errors = $this->validator->validate($postalCode); $errors = $this->validator->validate($postalCode);
if ($errors->count() == 0) { if ($errors->count() == 0) {
$em->persist($postalCode); $em->persist($postalCode);
} else { } else {
@ -213,12 +175,12 @@ class LoadPostalCodesCommand extends Command
foreach ($errors as $error) { foreach ($errors as $error) {
$msg .= " ".$error->getMessage(); $msg .= " ".$error->getMessage();
} }
throw new PostalCodeNotValidException($msg); throw new PostalCodeNotValidException($msg);
} }
if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
$output->writeln(sprintf('Creating postal code with code: %s, name: %s, countryCode: %s', $output->writeln(sprintf('Creating postal code with code: %s, name: %s, countryCode: %s',
$postalCode->getCode(), $postalCode->getName(), $postalCode->getCountry()->getCountryCode())); $postalCode->getCode(), $postalCode->getName(), $postalCode->getCountry()->getCountryCode()));
@ -229,15 +191,15 @@ class LoadPostalCodesCommand extends Command
class ExistingPostalCodeException extends \Exception class ExistingPostalCodeException extends \Exception
{ {
} }
class CountryCodeNotFoundException extends \Exception class CountryCodeNotFoundException extends \Exception
{ {
} }
class PostalCodeNotValidException extends \Exception class PostalCodeNotValidException extends \Exception
{ {
} }

View File

@ -1,20 +1,12 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\Controller; namespace Chill\MainBundle\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController; use Chill\MainBundle\CRUD\Controller\CRUDController;
use Chill\MainBundle\Entity\Country;
use Chill\MainBundle\Pagination\PaginatorFactory;
/**
*
*
*/
class AdminCountryCRUDController extends CRUDController class AdminCountryCRUDController extends CRUDController
{ {
function __construct(PaginatorFactory $paginator)
{
$this->paginatorFactory = $paginator;
}
} }

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\Controller; namespace Chill\MainBundle\Controller;
use Chill\MainBundle\CRUD\Controller\AbstractCRUDController; use Chill\MainBundle\CRUD\Controller\AbstractCRUDController;
@ -7,6 +9,7 @@ use Chill\MainBundle\CRUD\Controller\CRUDController;
use Chill\MainBundle\Pagination\PaginatorInterface; use Chill\MainBundle\Pagination\PaginatorInterface;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -16,40 +19,23 @@ use Chill\MainBundle\Form\UserType;
use Chill\MainBundle\Entity\GroupCenter; use Chill\MainBundle\Entity\GroupCenter;
use Chill\MainBundle\Form\Type\ComposedGroupCenterType; use Chill\MainBundle\Form\Type\ComposedGroupCenterType;
use Chill\MainBundle\Form\UserPasswordType; use Chill\MainBundle\Form\UserPasswordType;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
/**
* Class UserController
*
* @package Chill\MainBundle\Controller
*/
class UserController extends CRUDController class UserController extends CRUDController
{ {
const FORM_GROUP_CENTER_COMPOSED = 'composed_groupcenter'; const FORM_GROUP_CENTER_COMPOSED = 'composed_groupcenter';
/** private LoggerInterface $logger;
* @var \Psr\Log\LoggerInterface
*/
private $logger;
/** private ValidatorInterface $validator;
* @var ValidatorInterface
*/
private $validator;
private UserPasswordEncoderInterface $passwordEncoder; private UserPasswordEncoderInterface $passwordEncoder;
/**
* UserController constructor.
*
* @param LoggerInterface $logger
* @param ValidatorInterface $validator
*/
public function __construct( public function __construct(
LoggerInterface $chillLogger, LoggerInterface $chillLogger,
ValidatorInterface $validator, ValidatorInterface $validator,
@ -121,7 +107,7 @@ class UserController extends CRUDController
*/ */
public function editPasswordAction(User $user, Request $request) public function editPasswordAction(User $user, Request $request)
{ {
$editForm = $this->createEditPasswordForm($user, $request); $editForm = $this->createEditPasswordForm($user);
$editForm->handleRequest($request); $editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) { if ($editForm->isSubmitted() && $editForm->isValid()) {
@ -150,20 +136,17 @@ class UserController extends CRUDController
]); ]);
} }
/** private function createEditPasswordForm(User $user): FormInterface
*
*
* @param User $user
* @return \Symfony\Component\Form\Form
*/
private function createEditPasswordForm(User $user)
{ {
return $this->createForm(UserPasswordType::class, null, array( return $this->createForm(
'user' => $user UserPasswordType::class,
)) null,
[
'user' => $user
]
)
->add('submit', SubmitType::class, array('label' => 'Change password')) ->add('submit', SubmitType::class, array('label' => 'Change password'))
->remove('actual_password') ->remove('actual_password');
;
} }
/** /**
@ -208,7 +191,7 @@ class UserController extends CRUDController
* @Route("/{_locale}/admin/main/user/{uid}/add_link_groupcenter", * @Route("/{_locale}/admin/main/user/{uid}/add_link_groupcenter",
* name="admin_user_add_groupcenter") * name="admin_user_add_groupcenter")
*/ */
public function addLinkGroupCenterAction(Request $request, $uid): RedirectResponse public function addLinkGroupCenterAction(Request $request, $uid): Response
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
@ -238,23 +221,22 @@ class UserController extends CRUDController
return $this->redirect($this->generateUrl('chill_crud_admin_user_edit', return $this->redirect($this->generateUrl('chill_crud_admin_user_edit',
\array_merge(['id' => $uid], $returnPathParams))); \array_merge(['id' => $uid], $returnPathParams)));
} else { }
foreach($this->validator->validate($user) as $error)
foreach($this->validator->validate($user) as $error) {
$this->addFlash('error', $error->getMessage()); $this->addFlash('error', $error->getMessage());
} }
} }
return $this->render('@ChillMain/User/edit.html.twig', array( return $this->render('@ChillMain/User/edit.html.twig', [
'entity' => $user, 'entity' => $user,
'edit_form' => $this->createEditForm($user)->createView(), 'edit_form' => $this->createEditForm($user)->createView(),
'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($user)->createView(), 'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($user, $request)->createView(),
'delete_groupcenter_form' => array_map( 'delete_groupcenter_form' => array_map(
function(\Symfony\Component\Form\Form $form) { static fn(Form $form) => $form->createView(),
return $form->createView(); iterator_to_array($this->getDeleteLinkGroupCenterByUser($user, $request), true)
)
}, ]);
iterator_to_array($this->getDeleteLinkGroupCenterByUser($user), true))
));
} }
private function getPersistedGroupCenter(GroupCenter $groupCenter) private function getPersistedGroupCenter(GroupCenter $groupCenter)
@ -279,10 +261,8 @@ class UserController extends CRUDController
* Creates a form to delete a link to a GroupCenter * Creates a form to delete a link to a GroupCenter
* *
* @param mixed $permissionsGroup The entity id * @param mixed $permissionsGroup The entity id
*
* @return \Symfony\Component\Form\Form The form
*/ */
private function createDeleteLinkGroupCenterForm(User $user, GroupCenter $groupCenter, $request) private function createDeleteLinkGroupCenterForm(User $user, GroupCenter $groupCenter, $request): FormInterface
{ {
$returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : []; $returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : [];
@ -291,39 +271,29 @@ class UserController extends CRUDController
array_merge($returnPathParams, ['uid' => $user->getId(), 'gcid' => $groupCenter->getId()]))) array_merge($returnPathParams, ['uid' => $user->getId(), 'gcid' => $groupCenter->getId()])))
->setMethod('DELETE') ->setMethod('DELETE')
->add('submit', SubmitType::class, array('label' => 'Delete')) ->add('submit', SubmitType::class, array('label' => 'Delete'))
->getForm() ->getForm();
;
} }
/** /**
* create a form to add a link to a groupcenter * Create a form to add a link to a groupcenter.
*
* @param User $user
* @return \Symfony\Component\Form\Form
*/ */
private function createAddLinkGroupCenterForm(User $user, Request $request) private function createAddLinkGroupCenterForm(User $user, Request $request): FormInterface
{ {
$returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : []; $returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : [];
return $this->createFormBuilder() return $this->createFormBuilder()
->setAction($this->generateUrl('admin_user_add_groupcenter', ->setAction($this->generateUrl('admin_user_add_groupcenter',
array_merge($returnPathParams, ['uid' => $user->getId()]))) array_merge($returnPathParams, ['uid' => $user->getId()])))
->setMethod('POST') ->setMethod('POST')
->add(self::FORM_GROUP_CENTER_COMPOSED, ComposedGroupCenterType::class) ->add(self::FORM_GROUP_CENTER_COMPOSED, ComposedGroupCenterType::class)
->add('submit', SubmitType::class, array('label' => 'Add a new groupCenter')) ->add('submit', SubmitType::class, array('label' => 'Add a new groupCenter'))
->getForm() ->getForm();
;
} }
/**
*
* @param User $user
*/
private function getDeleteLinkGroupCenterByUser(User $user, Request $request) private function getDeleteLinkGroupCenterByUser(User $user, Request $request)
{ {
foreach ($user->getGroupCenters() as $groupCenter) { foreach ($user->getGroupCenters() as $groupCenter) {
yield $groupCenter->getId() => $this yield $groupCenter->getId() => $this->createDeleteLinkGroupCenterForm($user, $groupCenter, $request);
->createDeleteLinkGroupCenterForm($user, $groupCenter, $request);
} }
} }
} }

View File

@ -24,26 +24,26 @@ class LoadLanguages extends AbstractFixture implements ContainerAwareInterface,
// Array of ancien languages (to exclude) // Array of ancien languages (to exclude)
private $ancientToExclude = ["ang", "egy", "fro", "goh", "grc", "la", "non", "peo", "pro", "sga", private $ancientToExclude = ["ang", "egy", "fro", "goh", "grc", "la", "non", "peo", "pro", "sga",
"dum", "enm", "frm", "gmh", "mga", "akk", "phn", "zxx", "got", "und"]; "dum", "enm", "frm", "gmh", "mga", "akk", "phn", "zxx", "got", "und"];
/** /**
* *
* @var ContainerInterface * @var ContainerInterface
*/ */
private $container; private $container;
public function setContainer(ContainerInterface $container = null) public function setContainer(ContainerInterface $container = null)
{ {
$this->container = $container; $this->container = $container;
} }
public function getOrder() { public function getOrder() {
return 10; return 10;
} }
public function load(ObjectManager $manager) { public function load(ObjectManager $manager) {
echo "loading languages... \n"; echo "loading languages... \n";
foreach (Intl::getLanguageBundle()->getLanguageNames() as $code => $language) { foreach (Intl::getLanguageBundle()->getLanguageNames() as $code => $language) {
if ( if (
!in_array($code, $this->regionalVersionToInclude) !in_array($code, $this->regionalVersionToInclude)
@ -58,23 +58,24 @@ class LoadLanguages extends AbstractFixture implements ContainerAwareInterface,
$manager->persist($lang); $manager->persist($lang);
} }
} }
$manager->flush(); $manager->flush();
} }
/** /**
* prepare names for languages * Prepare names for languages.
* *
* @param string $languageCode
* @return string[] languages name indexed by available language code * @return string[] languages name indexed by available language code
*/ */
private function prepareName($languageCode) { private function prepareName(string $languageCode): array {
$names = [];
foreach ($this->container->getParameter('chill_main.available_languages') as $lang) { foreach ($this->container->getParameter('chill_main.available_languages') as $lang) {
$names[$lang] = Intl::getLanguageBundle()->getLanguageName($languageCode); $names[$lang] = Intl::getLanguageBundle()->getLanguageName($languageCode);
} }
return $names; return $names;
} }
} }

View File

@ -1,35 +1,32 @@
<?php <?php
/*
*/ declare(strict_types=1);
namespace Chill\MainBundle\DependencyInjection\CompilerPass; namespace Chill\MainBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Chill\MainBundle\Form\PermissionsGroupType; use Chill\MainBundle\Form\PermissionsGroupType;
use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Reference;
use LogicException;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ACLFlagsCompilerPass implements CompilerPassInterface class ACLFlagsCompilerPass implements CompilerPassInterface
{ {
public function process(ContainerBuilder $container) public function process(ContainerBuilder $container)
{ {
$permissionGroupType = $container->getDefinition(PermissionsGroupType::class); $permissionGroupType = $container->getDefinition(PermissionsGroupType::class);
foreach($container->findTaggedServiceIds('chill_main.flags') as $id => $tags) { foreach($container->findTaggedServiceIds('chill_main.flags') as $id => $tags) {
$reference = new Reference($id); $reference = new Reference($id);
foreach ($tags as $tag) { foreach ($tags as $tag) {
switch($tag['scope']) { switch($tag['scope']) {
case PermissionsGroupType::FLAG_SCOPE: case PermissionsGroupType::FLAG_SCOPE:
$permissionGroupType->addMethodCall('addFlagProvider', [ $reference ]); $permissionGroupType->addMethodCall('addFlagProvider', [ $reference ]);
break; break;
default: default:
throw new \LogicalException(sprintf( throw new LogicException(sprintf(
"This tag 'scope' is not implemented: %s, on service with id %s", $tag['scope'], $id) "This tag 'scope' is not implemented: %s, on service with id %s", $tag['scope'], $id)
); );
} }

View File

@ -19,14 +19,11 @@ class Configuration implements ConfigurationInterface
use AddWidgetConfigurationTrait; use AddWidgetConfigurationTrait;
/** private ContainerBuilder $containerBuilder;
*
* @var ContainerBuilder
*/
private $containerBuilder;
public function __construct(array $widgetFactories = array(), public function __construct(
array $widgetFactories,
ContainerBuilder $containerBuilder) ContainerBuilder $containerBuilder)
{ {
$this->setWidgetFactories($widgetFactories); $this->setWidgetFactories($widgetFactories);
@ -107,6 +104,9 @@ class Configuration implements ConfigurationInterface
->booleanNode('form_show_scopes') ->booleanNode('form_show_scopes')
->defaultTrue() ->defaultTrue()
->end() ->end()
->booleanNode('form_show_centers')
->defaultTrue()
->end()
->end() ->end()
->end() ->end()
->arrayNode('redis') ->arrayNode('redis')

View File

@ -30,27 +30,27 @@ use Chill\MainBundle\DependencyInjection\Widget\HasWidgetFactoriesExtensionInter
/** /**
* Compile the configurations and inject required service into container. * Compile the configurations and inject required service into container.
* *
* The widgets are services tagged with : * The widgets are services tagged with :
* *
* ``` * ```
* { name: chill_widget, alias: my_alias, place: my_place } * { name: chill_widget, alias: my_alias, place: my_place }
* ``` * ```
* *
* Or, if the tag does not exist or if you need to add some config to your * Or, if the tag does not exist or if you need to add some config to your
* service depending on the config, you should use a `WidgetFactory` (see * service depending on the config, you should use a `WidgetFactory` (see
* `WidgetFactoryInterface`. * `WidgetFactoryInterface`.
* *
* To reuse this compiler pass, simple execute the doProcess metho in your * To reuse this compiler pass, simple execute the doProcess metho in your
* compiler. Example : * compiler. Example :
* *
* ``` * ```
* namespace Chill\MainBundle\DependencyInjection\CompilerPass; * namespace Chill\MainBundle\DependencyInjection\CompilerPass;
* *
* use Symfony\Component\DependencyInjection\ContainerBuilder; * use Symfony\Component\DependencyInjection\ContainerBuilder;
* use Chill\MainBundle\DependencyInjection\Widget\AbstractWidgetsCompilerPass; * use Chill\MainBundle\DependencyInjection\Widget\AbstractWidgetsCompilerPass;
* class WidgetsCompilerPass extends AbstractWidgetsCompilerPass { * class WidgetsCompilerPass extends AbstractWidgetsCompilerPass {
* *
* public function process(ContainerBuilder $container) * public function process(ContainerBuilder $container)
* { * {
* $this->doProcess($container, 'chill_main', 'chill_main.widgets'); * $this->doProcess($container, 'chill_main', 'chill_main.widgets');
@ -58,58 +58,58 @@ use Chill\MainBundle\DependencyInjection\Widget\HasWidgetFactoriesExtensionInter
* } * }
* ``` * ```
* *
* *
*/ */
abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
{ {
private $widgetServices = array(); private $widgetServices = array();
/** /**
* *
* @var WidgetFactoryInterface[] * @var WidgetFactoryInterface[]
*/ */
private $widgetFactories; private $widgetFactories;
/** /**
* The service which will manage the widgets * The service which will manage the widgets
* *
* @var string * @var string
*/ */
const WIDGET_MANAGER = 'chill.main.twig.widget'; const WIDGET_MANAGER = 'chill.main.twig.widget';
/** /**
* the method wich register the widget into give service. * the method wich register the widget into give service.
*/ */
const WIDGET_MANAGER_METHOD_REGISTER = 'addWidget'; const WIDGET_MANAGER_METHOD_REGISTER = 'addWidget';
/** /**
* the value of the `name` key in service definitions's tag * the value of the `name` key in service definitions's tag
* *
* @var string * @var string
*/ */
const WIDGET_SERVICE_TAG_NAME = 'chill_widget'; const WIDGET_SERVICE_TAG_NAME = 'chill_widget';
/** /**
* the key used to collect the alias in the service definition's tag. * the key used to collect the alias in the service definition's tag.
* the alias must be * the alias must be
* injected into the configuration under 'alias' key. * injected into the configuration under 'alias' key.
* *
* @var string * @var string
*/ */
const WIDGET_SERVICE_TAG_ALIAS = 'alias'; const WIDGET_SERVICE_TAG_ALIAS = 'alias';
/** /**
* the key used to collect the authorized place in the service definition's tag * the key used to collect the authorized place in the service definition's tag
* *
* @var string * @var string
*/ */
const WIDGET_SERVICE_TAG_PLACES = 'place'; const WIDGET_SERVICE_TAG_PLACES = 'place';
/** /**
* the key to use to order widget for a given place * the key to use to order widget for a given place
*/ */
const WIDGET_CONFIG_ORDER = 'order'; const WIDGET_CONFIG_ORDER = 'order';
/** /**
* the key to use to identify widget for a given place * the key to use to identify widget for a given place
*/ */
@ -118,24 +118,25 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
/** /**
* process the configuration and the container to add the widget available * process the configuration and the container to add the widget available
* *
* @param ContainerBuilder $container * @param ContainerBuilder $container
* @param string $extension the extension of your bundle * @param string $extension the extension of your bundle
* @param string $containerWidgetConfigParameterName the key under which we can use the widget configuration * @param string $containerWidgetConfigParameterName the key under which we can use the widget configuration
* @throws \LogicException * @throws \LogicException
* @throws \UnexpectedValueException if the given extension does not implement HasWidgetExtensionInterface * @throws \UnexpectedValueException if the given extension does not implement HasWidgetExtensionInterface
* @throws \InvalidConfigurationException if there are errors in the config
*/ */
public function doProcess(ContainerBuilder $container, $extension, public function doProcess(
$containerWidgetConfigParameterName) ContainerBuilder $container,
{ $extension,
$containerWidgetConfigParameterName
) {
if (!$container->hasDefinition(self::WIDGET_MANAGER)) { if (!$container->hasDefinition(self::WIDGET_MANAGER)) {
throw new \LogicException("the service ".self::WIDGET_MANAGER." should". throw new \LogicException("the service ".self::WIDGET_MANAGER." should".
" be present. It is required by ".self::class); " be present. It is required by ".self::class);
} }
$managerDefinition = $container->getDefinition(self::WIDGET_MANAGER); $managerDefinition = $container->getDefinition(self::WIDGET_MANAGER);
// collect the widget factories // collect the widget factories
/* @var $extensionClass HasWidgetFactoriesExtensionInterface */ /* @var $extensionClass HasWidgetFactoriesExtensionInterface */
$extensionClass = $container->getExtension($extension); $extensionClass = $container->getExtension($extension);
@ -148,19 +149,19 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
HasWidgetFactoriesExtensionInterface::class, HasWidgetFactoriesExtensionInterface::class,
get_class($extensionClass))); get_class($extensionClass)));
} }
$this->widgetFactories = $extensionClass->getWidgetFactories(); $this->widgetFactories = $extensionClass->getWidgetFactories();
// collect the availabled tagged services // collect the availabled tagged services
$this->collectTaggedServices($container); $this->collectTaggedServices($container);
// collect the widgets and their config : // collect the widgets and their config :
$widgetParameters = $container->getParameter($containerWidgetConfigParameterName); $widgetParameters = $container->getParameter($containerWidgetConfigParameterName);
// and add them to the delegated_block // and add them to the delegated_block
foreach($widgetParameters as $place => $widgets) { foreach($widgetParameters as $place => $widgets) {
foreach ($widgets as $param) { foreach ($widgets as $param) {
$alias = $param[self::WIDGET_CONFIG_ALIAS]; $alias = $param[self::WIDGET_CONFIG_ALIAS];
// check that the service exists // check that the service exists
@ -168,43 +169,43 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
throw new InvalidConfigurationException(sprintf("The alias %s". throw new InvalidConfigurationException(sprintf("The alias %s".
" is not defined.", $alias)); " is not defined.", $alias));
} }
// check that the widget is allowed at this place // check that the widget is allowed at this place
if (!$this->isPlaceAllowedForWidget($place, $alias, $container)) { if (!$this->isPlaceAllowedForWidget($place, $alias, $container)) {
throw new \InvalidConfigurationException(sprintf( throw new InvalidConfigurationException(sprintf(
"The widget with alias %s is not allowed at place %s", "The widget with alias %s is not allowed at place %s",
$alias, $alias,
$place $place
)); ));
} }
// get the order, eventually corrected // get the order, eventually corrected
$order = $this->cacheAndGetOrdering($place, $param[self::WIDGET_CONFIG_ORDER]); $order = $this->cacheAndGetOrdering($place, $param[self::WIDGET_CONFIG_ORDER]);
// register the widget with config to the service, using the method // register the widget with config to the service, using the method
// `addWidget` // `addWidget`
if ($this->widgetServices[$alias] instanceof WidgetFactoryInterface) { if ($this->widgetServices[$alias] instanceof WidgetFactoryInterface) {
/* @var $factory WidgetFactoryInterface */ /* @var $factory WidgetFactoryInterface */
$factory = $this->widgetServices[$alias]; $factory = $this->widgetServices[$alias];
// get the config (under the key which equals to widget_alias // get the config (under the key which equals to widget_alias
$config = isset($param[$factory->getWidgetAlias()]) ? $config = isset($param[$factory->getWidgetAlias()]) ?
$param[$factory->getWidgetAlias()] : array(); $param[$factory->getWidgetAlias()] : array();
// register the service into the container // register the service into the container
$serviceId =$this->registerServiceIntoContainer($container, $serviceId =$this->registerServiceIntoContainer($container,
$factory, $place, $order, $config); $factory, $place, $order, $config);
$managerDefinition->addMethodCall(self::WIDGET_MANAGER_METHOD_REGISTER, $managerDefinition->addMethodCall(self::WIDGET_MANAGER_METHOD_REGISTER,
array( array(
$place, $place,
$order, $order,
new Reference($serviceId), new Reference($serviceId),
$config $config
)); ));
} else { } else {
$managerDefinition->addMethodCall(self::WIDGET_MANAGER_METHOD_REGISTER, $managerDefinition->addMethodCall(self::WIDGET_MANAGER_METHOD_REGISTER,
array( array(
$place, $place,
$order, $order,
new Reference($this->widgetServices[$alias]), new Reference($this->widgetServices[$alias]),
array() // the config is alway an empty array array() // the config is alway an empty array
)); ));
@ -212,10 +213,10 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
} }
} }
} }
/** /**
* register the service into container. * register the service into container.
* *
* @param ContainerBuilder $container * @param ContainerBuilder $container
* @param WidgetFactoryInterface $factory * @param WidgetFactoryInterface $factory
* @param string $place * @param string $place
@ -231,28 +232,28 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
array $config array $config
) { ) {
$serviceId = $factory->getServiceId($container, $place, $order, $config); $serviceId = $factory->getServiceId($container, $place, $order, $config);
$definition = $factory->createDefinition($container, $place, $definition = $factory->createDefinition($container, $place,
$order, $config); $order, $config);
$container->setDefinition($serviceId, $definition); $container->setDefinition($serviceId, $definition);
return $serviceId; return $serviceId;
} }
/** /**
* cache of ordering by place. * cache of ordering by place.
* *
* @internal used by function cacheAndGetOrdering * @internal used by function cacheAndGetOrdering
* @var array * @var array
*/ */
private $cacheOrdering = array(); private $cacheOrdering = array();
/** /**
* check if the ordering has already be used for the given $place and, * check if the ordering has already be used for the given $place and,
* if yes, correct the ordering by incrementation of 1 until the ordering * if yes, correct the ordering by incrementation of 1 until the ordering
* has not be used. * has not be used.
* *
* recursive method. * recursive method.
* *
* @param string $place * @param string $place
* @param float $ordering * @param float $ordering
* @return float * @return float
@ -262,7 +263,7 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
if (!array_key_exists($place, $this->cacheOrdering)) { if (!array_key_exists($place, $this->cacheOrdering)) {
$this->cacheOrdering[$place] = array(); $this->cacheOrdering[$place] = array();
} }
// check if the order exists // check if the order exists
if (array_search($ordering, $this->cacheOrdering[$place])) { if (array_search($ordering, $this->cacheOrdering[$place])) {
// if the order exists, increment of 1 and try again // if the order exists, increment of 1 and try again
@ -270,14 +271,14 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
} else { } else {
// cache the ordering // cache the ordering
$this->cacheOrdering[$place][] = $ordering; $this->cacheOrdering[$place][] = $ordering;
return $ordering; return $ordering;
} }
} }
/** /**
* get the places where the service is allowed * get the places where the service is allowed
* *
* @param Definition $definition * @param Definition $definition
* @return unknown * @return unknown
*/ */
@ -288,7 +289,7 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
->getAllowedPlaces())) { ->getAllowedPlaces())) {
return true; return true;
} }
} else { } else {
$definition = $container->findDefinition($this->widgetServices[$widgetAlias]); $definition = $container->findDefinition($this->widgetServices[$widgetAlias]);
@ -300,17 +301,17 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
} }
} }
} }
return false; return false;
} }
/** /**
* This method collect all service tagged with `self::WIDGET_SERVICE_TAG`, and * This method collect all service tagged with `self::WIDGET_SERVICE_TAG`, and
* add also the widget defined by factories * add also the widget defined by factories
* *
* This method also check that the service is correctly tagged with `alias` and * This method also check that the service is correctly tagged with `alias` and
* `places`, or the factory give a correct alias and more than one place. * `places`, or the factory give a correct alias and more than one place.
* *
* @param ContainerBuilder $container * @param ContainerBuilder $container
* @throws InvalidConfigurationException * @throws InvalidConfigurationException
* @throws InvalidArgumentException * @throws InvalidArgumentException
@ -320,13 +321,13 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
// first, check the service tagged in service definition // first, check the service tagged in service definition
foreach ($container->findTaggedServiceIds(self::WIDGET_SERVICE_TAG_NAME) as $id => $attrs) { foreach ($container->findTaggedServiceIds(self::WIDGET_SERVICE_TAG_NAME) as $id => $attrs) {
foreach ($attrs as $attr) { foreach ($attrs as $attr) {
// check the alias is set // check the alias is set
if (!isset($attr[self::WIDGET_SERVICE_TAG_ALIAS])) { if (!isset($attr[self::WIDGET_SERVICE_TAG_ALIAS])) {
throw new InvalidConfigurationException("you should add an ".self::WIDGET_SERVICE_TAG_ALIAS. throw new InvalidConfigurationException("you should add an ".self::WIDGET_SERVICE_TAG_ALIAS.
" key on the service ".$id); " key on the service ".$id);
} }
// check the place is set // check the place is set
if (!isset($attr[self::WIDGET_SERVICE_TAG_PLACES])) { if (!isset($attr[self::WIDGET_SERVICE_TAG_PLACES])) {
throw new InvalidConfigurationException(sprintf( throw new InvalidConfigurationException(sprintf(
@ -335,54 +336,54 @@ abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
$id $id
)); ));
} }
// check the alias does not exists yet // check the alias does not exists yet
if (array_key_exists($attr[self::WIDGET_SERVICE_TAG_ALIAS], $this->widgetServices)) { if (array_key_exists($attr[self::WIDGET_SERVICE_TAG_ALIAS], $this->widgetServices)) {
throw new InvalidArgumentException("a service has already be defined with the ". throw new InvalidArgumentException("a service has already be defined with the ".
self::WIDGET_SERVICE_TAG_ALIAS." ".$attr[self::WIDGET_SERVICE_TAG_ALIAS]); self::WIDGET_SERVICE_TAG_ALIAS." ".$attr[self::WIDGET_SERVICE_TAG_ALIAS]);
} }
// register the service as available // register the service as available
$this->widgetServices[$attr[self::WIDGET_SERVICE_TAG_ALIAS]] = $id; $this->widgetServices[$attr[self::WIDGET_SERVICE_TAG_ALIAS]] = $id;
} }
} }
// add the services defined by factories // add the services defined by factories
foreach($this->widgetFactories as $factory) { foreach($this->widgetFactories as $factory) {
/* @var $factory WidgetFactoryInterface */ /* @var $factory WidgetFactoryInterface */
$alias = $factory->getWidgetAlias(); $alias = $factory->getWidgetAlias();
// check the alias is not empty // check the alias is not empty
if (empty($alias)) { if (empty($alias)) {
throw new \LogicException(sprintf( throw new \LogicException(sprintf(
"the widget factory %s returns an empty alias", "the widget factory %s returns an empty alias",
get_class($factory))); get_class($factory)));
} }
// check the places are not empty // check the places are not empty
if (!is_array($factory->getAllowedPlaces())) { if (!is_array($factory->getAllowedPlaces())) {
throw new \UnexpectedValueException("the method 'getAllowedPlaces' " throw new \UnexpectedValueException("the method 'getAllowedPlaces' "
. "should return a non-empty array. Unexpected value on ". . "should return a non-empty array. Unexpected value on ".
get_class($factory)); get_class($factory));
} }
if (count($factory->getAllowedPlaces()) == 0) { if (count($factory->getAllowedPlaces()) == 0) {
throw new \LengthException("The method 'getAllowedPlaces' should " throw new \LengthException("The method 'getAllowedPlaces' should "
. "return a non-empty array, but returned 0 elements on ". . "return a non-empty array, but returned 0 elements on ".
get_class($factory).'::getAllowedPlaces()'); get_class($factory).'::getAllowedPlaces()');
} }
// check the alias does not exists yet // check the alias does not exists yet
if (array_key_exists($alias, $this->widgetServices)) { if (array_key_exists($alias, $this->widgetServices)) {
throw new InvalidArgumentException("a service has already be defined with the ". throw new InvalidArgumentException("a service has already be defined with the ".
self::WIDGET_SERVICE_TAG_ALIAS." ".$alias); self::WIDGET_SERVICE_TAG_ALIAS." ".$alias);
} }
// register the factory as available // register the factory as available
$this->widgetServices[$factory->getWidgetAlias()] = $factory; $this->widgetServices[$factory->getWidgetAlias()] = $factory;
} }
} }
} }

View File

@ -1,58 +1,43 @@
<?php <?php
/*
* Chill is a software for social workers declare(strict_types=1);
* Copyright (C) 2018 Champs-Libres Coopérative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Doctrine\DQL; namespace Chill\MainBundle\Doctrine\DQL;
use Doctrine\ORM\Query\AST\Functions\FunctionNode; use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\Lexer; use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
/** /**
* DQL function for OVERLAPS function in postgresql * DQL function for OVERLAPS function in postgresql
* *
* If a value is null in period start, it will be replaced by -infinity. * If a value is null in period start, it will be replaced by -infinity.
* If a value is null in period end, it will be replaced by infinity * If a value is null in period end, it will be replaced by infinity
*
*/ */
class OverlapsI extends FunctionNode class OverlapsI extends FunctionNode
{ {
private $firstPeriodStart; private $firstPeriodStart;
private $firstPeriodEnd; private $firstPeriodEnd;
private $secondPeriodStart; private $secondPeriodStart;
private $secondPeriodEnd; private $secondPeriodEnd;
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{ {
return '(' return sprintf(
.$this->makeCase($sqlWalker, $this->firstPeriodStart, 'start').', ' '(%s, %s) OVERLAPS (%s, %s)',
.$this->makeCase($sqlWalker, $this->firstPeriodEnd, 'end'). $this->makeCase($sqlWalker, $this->firstPeriodStart, 'start'),
') OVERLAPS (' $this->makeCase($sqlWalker, $this->firstPeriodEnd, 'end'),
.$this->makeCase($sqlWalker, $this->secondPeriodStart, 'start').', ' $this->makeCase($sqlWalker, $this->secondPeriodStart, 'start'),
.$this->makeCase($sqlWalker, $this->secondPeriodEnd, 'end').')' $this->makeCase($sqlWalker, $this->secondPeriodEnd, 'end')
; );
} }
protected function makeCase($sqlWalker, $part, $position) protected function makeCase($sqlWalker, $part, string $position): string
{ {
//return $part->dispatch($sqlWalker);
switch ($position) { switch ($position) {
case 'start' : case 'start' :
$p = '-infinity'; $p = '-infinity';
@ -60,51 +45,51 @@ class OverlapsI extends FunctionNode
case 'end': case 'end':
$p = 'infinity'; $p = 'infinity';
break; break;
default:
throw new \Exception('Unexpected position value.');
} }
if ($part instanceof \Doctrine\ORM\Query\AST\PathExpression) { if ($part instanceof PathExpression) {
return 'CASE WHEN ' return sprintf(
.' '.$part->dispatch($sqlWalker).' IS NOT NULL ' "CASE WHEN %s IS NOT NULL THEN %s ELSE '%s'::date END",
. 'THEN '. $part->dispatch($sqlWalker),
$part->dispatch($sqlWalker) $part->dispatch($sqlWalker),
. ' ELSE '. $p
"'".$p."'::date " );
. 'END';
} else {
return 'CASE WHEN '
.' '.$part->dispatch($sqlWalker).'::date IS NOT NULL '
. 'THEN '.
$part->dispatch($sqlWalker)
. '::date ELSE '.
"'".$p."'::date "
. 'END';
} }
return sprintf(
"CASE WHEN %s::date IS NOT NULL THEN %s::date ELSE '%s'::date END",
$part->dispatch($sqlWalker),
$part->dispatch($sqlWalker),
$p
);
} }
public function parse(\Doctrine\ORM\Query\Parser $parser) public function parse(Parser $parser): void
{ {
$parser->match(Lexer::T_IDENTIFIER); $parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS); $parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstPeriodStart = $parser->StringPrimary(); $this->firstPeriodStart = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA); $parser->match(Lexer::T_COMMA);
$this->firstPeriodEnd = $parser->StringPrimary(); $this->firstPeriodEnd = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS); $parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(Lexer::T_COMMA); $parser->match(Lexer::T_COMMA);
$parser->match(Lexer::T_OPEN_PARENTHESIS); $parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->secondPeriodStart = $parser->StringPrimary(); $this->secondPeriodStart = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA); $parser->match(Lexer::T_COMMA);
$this->secondPeriodEnd = $parser->StringPrimary(); $this->secondPeriodEnd = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS); $parser->match(Lexer::T_CLOSE_PARENTHESIS);
} }
} }

View File

@ -4,13 +4,9 @@ namespace Chill\MainBundle\Doctrine\Model;
use \JsonSerializable; use \JsonSerializable;
/**
* Description of Point
*
*/
class Point implements JsonSerializable { class Point implements JsonSerializable {
private ?float $lat = null; private ?float $lat;
private ?float $lon = null; private ?float $lon;
public static string $SRID = '4326'; public static string $SRID = '4326';
private function __construct(?float $lon, ?float $lat) private function __construct(?float $lon, ?float $lat)
@ -22,6 +18,7 @@ class Point implements JsonSerializable {
public function toGeoJson(): string public function toGeoJson(): string
{ {
$array = $this->toArrayGeoJson(); $array = $this->toArrayGeoJson();
return \json_encode($array); return \json_encode($array);
} }
@ -33,60 +30,53 @@ class Point implements JsonSerializable {
public function toArrayGeoJson(): array public function toArrayGeoJson(): array
{ {
return [ return [
"type" => "Point", 'type' => 'Point',
"coordinates" => [ $this->lon, $this->lat ] 'coordinates' => [$this->lon, $this->lat],
]; ];
} }
/**
*
* @return string
*/
public function toWKT(): string public function toWKT(): string
{ {
return 'SRID='.self::$SRID.';POINT('.$this->lon.' '.$this->lat.')'; return sprintf("SRID=%s;POINT(%s %s)", self::$SRID, $this->lon, $this->lat);
} }
/** public static function fromGeoJson(string $geojson): self
*
* @param type $geojson
* @return Point
*/
public static function fromGeoJson(string $geojson): Point
{ {
$a = json_decode($geojson); $a = json_decode($geojson);
//check if the geojson string is correct
if (NULL === $a or !isset($a->type) or !isset($a->coordinates)){ if (null === $a) {
throw PointException::badJsonString($geojson); throw PointException::badJsonString($geojson);
} }
if ($a->type != 'Point'){ if (null === $a->type || null === $a->coordinates) {
throw PointException::badJsonString($geojson);
}
if ($a->type !== 'Point'){
throw PointException::badGeoType(); throw PointException::badGeoType();
} }
$lat = $a->coordinates[1]; [$lon, $lat] = $a->coordinates;
$lon = $a->coordinates[0];
return Point::fromLonLat($lon, $lat); return Point::fromLonLat($lon, $lat);
} }
public static function fromLonLat(float $lon, float $lat): Point public static function fromLonLat(float $lon, float $lat): self
{ {
if (($lon > -180 && $lon < 180) && ($lat > -90 && $lat < 90)) if (($lon > -180 && $lon < 180) && ($lat > -90 && $lat < 90)) {
{
return new Point($lon, $lat); return new Point($lon, $lat);
} else {
throw PointException::badCoordinates($lon, $lat);
} }
throw PointException::badCoordinates($lon, $lat);
} }
public static function fromArrayGeoJson(array $array): Point public static function fromArrayGeoJson(array $array): self
{ {
if ($array['type'] == 'Point' && if ($array['type'] === 'Point' && isset($array['coordinates'])) {
isset($array['coordinates']))
{
return self::fromLonLat($array['coordinates'][0], $array['coordinates'][1]); return self::fromLonLat($array['coordinates'][0], $array['coordinates'][1]);
} }
throw new \Exception('Unable to build a point from input data.');
} }
public function getLat(): float public function getLat(): float

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\Doctrine\Type; namespace Chill\MainBundle\Doctrine\Type;
use Chill\MainBundle\Doctrine\Model\Point; use Chill\MainBundle\Doctrine\Model\Point;
@ -7,40 +9,32 @@ use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use Chill\MainBundle\Doctrine\Model\PointException; use Chill\MainBundle\Doctrine\Model\PointException;
/** /**
* A Type for Doctrine to implement the Geography Point type * A Type for Doctrine to implement the Geography Point type
* implemented by Postgis on postgis+postgresql databases * implemented by Postgis on postgis+postgresql databases
*
*/ */
class PointType extends Type { class PointType extends Type
{
const POINT = 'point'; public const POINT = 'point';
/** /**
*
* @param array $fieldDeclaration
* @param AbstractPlatform $platform
* @return string * @return string
*/ */
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform) public function getSQLDeclaration(array $column, AbstractPlatform $platform)
{ {
return 'geometry(POINT,'.Point::$SRID.')'; return 'geometry(POINT,'.Point::$SRID.')';
} }
/** /**
*
* @param type $value
* @param AbstractPlatform $platform
* @return ?Point * @return ?Point
*/ */
public function convertToPHPValue($value, AbstractPlatform $platform) public function convertToPHPValue($value, AbstractPlatform $platform)
{ {
if ($value === NULL){ if ($value === NULL){
return NULL; return NULL;
} else {
return Point::fromGeoJson($value);
} }
return Point::fromGeoJson($value);
} }
public function getName() public function getName()
@ -52,9 +46,9 @@ class PointType extends Type {
{ {
if ($value === NULL){ if ($value === NULL){
return NULL; return NULL;
} else {
return $value->toWKT();
} }
return $value->toWKT();
} }
public function canRequireSQLConversion() public function canRequireSQLConversion()

View File

@ -1,19 +1,19 @@
<?php <?php
/* /*
* *
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop> * Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as * it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the * published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version. * License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
@ -23,12 +23,11 @@ namespace Chill\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Annotation as Serializer;
/** /**
* @ORM\Entity * @ORM\Entity
* @ORM\Table(name="centers") * @ORM\Table(name="centers")
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/ */
class Center implements HasCenterInterface class Center implements HasCenterInterface
{ {
@ -46,9 +45,10 @@ class Center implements HasCenterInterface
* @var string * @var string
* *
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
* @Serializer\Groups({"docgen:read"})
*/ */
private $name; private string $name = '';
/** /**
* @var Collection * @var Collection
* *
@ -58,8 +58,8 @@ class Center implements HasCenterInterface
* ) * )
*/ */
private $groupCenters; private $groupCenters;
/** /**
* Center constructor. * Center constructor.
*/ */
@ -67,7 +67,7 @@ class Center implements HasCenterInterface
{ {
$this->groupCenters = new \Doctrine\Common\Collections\ArrayCollection(); $this->groupCenters = new \Doctrine\Common\Collections\ArrayCollection();
} }
/** /**
* @return string * @return string
*/ */
@ -75,7 +75,7 @@ class Center implements HasCenterInterface
{ {
return $this->name; return $this->name;
} }
/** /**
* @param $name * @param $name
* @return $this * @return $this
@ -85,7 +85,7 @@ class Center implements HasCenterInterface
$this->name = $name; $this->name = $name;
return $this; return $this;
} }
/** /**
* @return int * @return int
*/ */
@ -93,7 +93,7 @@ class Center implements HasCenterInterface
{ {
return $this->id; return $this->id;
} }
/** /**
* @return ArrayCollection|Collection * @return ArrayCollection|Collection
*/ */
@ -101,7 +101,7 @@ class Center implements HasCenterInterface
{ {
return $this->groupCenters; return $this->groupCenters;
} }
/** /**
* @param GroupCenter $groupCenter * @param GroupCenter $groupCenter
* @return $this * @return $this
@ -111,7 +111,7 @@ class Center implements HasCenterInterface
$this->groupCenters->add($groupCenter); $this->groupCenters->add($groupCenter);
return $this; return $this;
} }
/** /**
* @return string * @return string
*/ */
@ -119,7 +119,7 @@ class Center implements HasCenterInterface
{ {
return $this->getName(); return $this->getName();
} }
/** /**
* @return $this|Center * @return $this|Center
*/ */

View File

@ -1,22 +1,6 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a suite of a modules, Chill is a software for social workers
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Entity; namespace Chill\MainBundle\Entity;
@ -28,38 +12,30 @@ use Doctrine\Common\Collections\ArrayCollection;
* @ORM\Entity * @ORM\Entity
* @ORM\Table(name="role_scopes") * @ORM\Table(name="role_scopes")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region") * @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/ */
class RoleScope class RoleScope
{ {
/** /**
* @var integer
*
* @ORM\Id * @ORM\Id
* @ORM\Column(name="id", type="integer") * @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO") * @ORM\GeneratedValue(strategy="AUTO")
*/ */
private $id; private ?int $id = null;
/** /**
* @var string
*
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
*/ */
private $role; private ?string $role = null;
/** /**
* @var Scope
*
* @ORM\ManyToOne( * @ORM\ManyToOne(
* targetEntity="Chill\MainBundle\Entity\Scope", * targetEntity="Chill\MainBundle\Entity\Scope",
* inversedBy="roleScopes") * inversedBy="roleScopes")
* @ORM\JoinColumn(nullable=true, name="scope_id") * @ORM\JoinColumn(nullable=true, name="scope_id")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE") * @ORM\Cache(usage="NONSTRICT_READ_WRITE")
*/ */
private $scope; private ?Scope $scope = null;
/** /**
* @var Collection * @var Collection
* *
@ -68,59 +44,37 @@ class RoleScope
* mappedBy="roleScopes") * mappedBy="roleScopes")
*/ */
private $permissionsGroups; private $permissionsGroups;
/**
* RoleScope constructor.
*/
public function __construct() { public function __construct() {
$this->new = true;
$this->permissionsGroups = new ArrayCollection(); $this->permissionsGroups = new ArrayCollection();
} }
/** public function getId(): ?int
* @return int
*/
public function getId()
{ {
return $this->id; return $this->id;
} }
/** public function getRole(): ?string
* @return string
*/
public function getRole()
{ {
return $this->role; return $this->role;
} }
/** public function getScope(): ?Scope
* @return Scope
*/
public function getScope()
{ {
return $this->scope; return $this->scope;
} }
/** public function setRole(?string $role = null): self
* @param type $role
* @return RoleScope
*/
public function setRole($role)
{ {
$this->role = $role; $this->role = $role;
return $this; return $this;
} }
/** public function setScope(?Scope $scope = null): self
* @param Scope $scope
* @return RoleScope
*/
public function setScope(Scope $scope = null)
{ {
$this->scope = $scope; $this->scope = $scope;
return $this; return $this;
} }
} }

View File

@ -8,7 +8,7 @@ use Doctrine\Common\Collections\ArrayCollection;
use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Entity\UserJob;
use Symfony\Component\Security\Core\User\AdvancedUserInterface; use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Serializer\Annotation as Serializer;
/** /**
* User * User
@ -16,7 +16,7 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
* @ORM\Entity * @ORM\Entity
* @ORM\Table(name="users") * @ORM\Table(name="users")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region") * @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
* @DiscriminatorMap(typeProperty="type", mapping={ * @Serializer\DiscriminatorMap(typeProperty="type", mapping={
* "user"=User::class * "user"=User::class
* }) * })
*/ */
@ -51,6 +51,7 @@ class User implements AdvancedUserInterface {
/** /**
* @ORM\Column(type="string", length=200) * @ORM\Column(type="string", length=200)
* @Serializer\Groups({"docgen:read"})
*/ */
private string $label = ''; private string $label = '';
@ -58,8 +59,9 @@ class User implements AdvancedUserInterface {
* @var string * @var string
* *
* @ORM\Column(type="string", length=150, nullable=true) * @ORM\Column(type="string", length=150, nullable=true)
* @Serializer\Groups({"docgen:read"})
*/ */
private $email; private ?string $email = null;
/** /**
* @var string * @var string
@ -123,6 +125,7 @@ class User implements AdvancedUserInterface {
/** /**
* @var Center|null * @var Center|null
* @ORM\ManyToOne(targetEntity=Center::class) * @ORM\ManyToOne(targetEntity=Center::class)
* @Serializer\Groups({"docgen:read"})
*/ */
private ?Center $mainCenter = null; private ?Center $mainCenter = null;

View File

@ -1,48 +1,25 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Export\Formatter; namespace Chill\MainBundle\Export\Formatter;
use Chill\MainBundle\Export\ExportInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Chill\MainBundle\Export\ExportManager; use Chill\MainBundle\Export\ExportManager;
use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
// command to get the report with curl : curl --user "center a_social:password" "http://localhost:8000/fr/exports/generate/count_person?export[filters][person_gender_filter][enabled]=&export[filters][person_nationality_filter][enabled]=&export[filters][person_nationality_filter][form][nationalities]=&export[aggregators][person_nationality_aggregator][order]=1&export[aggregators][person_nationality_aggregator][form][group_by_level]=country&export[submit]=&export[_token]=RHpjHl389GrK-bd6iY5NsEqrD5UKOTHH40QKE9J1edU" --globoff
/** /**
* * Command to get the report with curl:
* * curl --user "center a_social:password" "http://localhost:8000/fr/exports/generate/count_person?export[filters][person_gender_filter][enabled]=&export[filters][person_nationality_filter][enabled]=&export[filters][person_nationality_filter][form][nationalities]=&export[aggregators][person_nationality_aggregator][order]=1&export[aggregators][person_nationality_aggregator][form][group_by_level]=country&export[submit]=&export[_token]=RHpjHl389GrK-bd6iY5NsEqrD5UKOTHH40QKE9J1edU" --globoff
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* @deprecated this formatter is not used any more. * @deprecated this formatter is not used any more.
*/ */
class CSVFormatter implements FormatterInterface class CSVFormatter implements FormatterInterface
{ {
/** protected TranslatorInterface $translator;
*
* @var TranslatorInterface
*/
protected $translator;
protected $result; protected $result;
@ -85,11 +62,7 @@ class CSVFormatter implements FormatterInterface
} }
/** /**
*
* @uses appendAggregatorForm * @uses appendAggregatorForm
* @param FormBuilderInterface $builder
* @param type $exportAlias
* @param array $aggregatorAliases
*/ */
public function buildForm(FormBuilderInterface $builder, $exportAlias, array $aggregatorAliases) public function buildForm(FormBuilderInterface $builder, $exportAlias, array $aggregatorAliases)
{ {
@ -172,20 +145,35 @@ class CSVFormatter implements FormatterInterface
* If two aggregators have the same order, the second given will be placed * If two aggregators have the same order, the second given will be placed
* after. This is not significant for the first ordering. * after. This is not significant for the first ordering.
* *
* @param type $formatterData
* @return type
*/ */
protected function orderingHeaders($formatterData) protected function orderingHeaders(array $formatterData)
{ {
$this->formatterData = $formatterData; $this->formatterData = $formatterData;
uasort($this->formatterData, function($a, $b) { uasort(
$this->formatterData,
static fn(array $a, array $b): int => ($a['order'] <= $b['order'] ? -1 : 1)
);
}
return ($a['order'] <= $b['order'] ? -1 : 1); private function findColumnPosition(&$columnHeaders, $columnToFind): int
}); {
$i = 0;
foreach($columnHeaders as $set) {
if ($set === $columnToFind) {
return $i;
}
$i++;
}
//we didn't find it, adding the column
$columnHeaders[] = $columnToFind;
return $i++;
} }
protected function generateContent() protected function generateContent()
{ {
$line = null;
$rowKeysNb = count($this->getRowHeaders()); $rowKeysNb = count($this->getRowHeaders());
$columnKeysNb = count($this->getColumnHeaders()); $columnKeysNb = count($this->getColumnHeaders());
$resultsKeysNb = count($this->export->getQueryKeys($this->exportData)); $resultsKeysNb = count($this->export->getQueryKeys($this->exportData));
@ -196,21 +184,6 @@ class CSVFormatter implements FormatterInterface
$contentData = array(); $contentData = array();
$content = array(); $content = array();
function findColumnPosition(&$columnHeaders, $columnToFind) {
$i = 0;
foreach($columnHeaders as $set) {
if ($set === $columnToFind) {
return $i;
}
$i++;
}
//we didn't find it, adding the column
$columnHeaders[] = $columnToFind;
return $i++;
}
// create a file pointer connected to the output stream // create a file pointer connected to the output stream
$output = fopen('php://output', 'w'); $output = fopen('php://output', 'w');
@ -244,7 +217,7 @@ class CSVFormatter implements FormatterInterface
// add the column headers // add the column headers
/* @var $columns string[] the column for this row */ /* @var $columns string[] the column for this row */
$columns = array_slice($row, $rowKeysNb, $columnKeysNb); $columns = array_slice($row, $rowKeysNb, $columnKeysNb);
$columnPosition = findColumnPosition($columnHeaders, $columns); $columnPosition = $this->findColumnPosition($columnHeaders, $columns);
//fill with blank at the position given by the columnPosition + nbRowHeaders //fill with blank at the position given by the columnPosition + nbRowHeaders
for ($i=0; $i < $columnPosition; $i++) { for ($i=0; $i < $columnPosition; $i++) {

View File

@ -229,7 +229,7 @@ class SpreadSheetFormatter implements FormatterInterface
$this->getContentType($this->formatterData['format'])); $this->getContentType($this->formatterData['format']));
$this->tempfile = \tempnam(\sys_get_temp_dir(), ''); $this->tempfile = \tempnam(\sys_get_temp_dir(), '');
$this->generatecontent(); $this->generateContent();
$f = \fopen($this->tempfile, 'r'); $f = \fopen($this->tempfile, 'r');
$response->setContent(\stream_get_contents($f)); $response->setContent(\stream_get_contents($f));

View File

@ -51,8 +51,10 @@ class CenterTransformer implements DataTransformerInterface
} }
} }
$ids = [];
if ($this->multiple) { if ($this->multiple) {
$ids = \explode(',', $id); $ids = explode(',', $id);
} else { } else {
$ids[] = (int) $id; $ids[] = (int) $id;
} }
@ -68,9 +70,9 @@ class CenterTransformer implements DataTransformerInterface
if ($this->multiple) { if ($this->multiple) {
return new ArrayCollection($centers); return new ArrayCollection($centers);
} else {
return $centers[0];
} }
return $centers[0];
} }
public function transform($center) public function transform($center)

View File

@ -1,48 +1,25 @@
<?php <?php
/*
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop> declare(strict_types=1);
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Form\Type\DataTransformer; namespace Chill\MainBundle\Form\Type\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\Exception\UnexpectedTypeException;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class DateIntervalTransformer implements DataTransformerInterface class DateIntervalTransformer implements DataTransformerInterface
{ {
/**
*
* @param \DateInterval $value
* @throws UnexpectedTypeException
*/
public function transform($value) public function transform($value)
{ {
if (empty($value)) { if (empty($value)) {
return null; return null;
} }
if (!$value instanceof \DateInterval) { if (!$value instanceof \DateInterval) {
throw new UnexpectedTypeException($value, \DateInterval::class); throw new UnexpectedTypeException($value, \DateInterval::class);
} }
if ($value->d > 0) { if ($value->d > 0) {
// we check for weeks (weeks are converted to 7 days) // we check for weeks (weeks are converted to 7 days)
if ($value->d % 7 === 0) { if ($value->d % 7 === 0) {
@ -50,43 +27,49 @@ class DateIntervalTransformer implements DataTransformerInterface
'n' => $value->d / 7, 'n' => $value->d / 7,
'unit' => 'W' 'unit' => 'W'
]; ];
} else {
return [
'n' => $value->d,
'unit' => 'D'
];
} }
} elseif ($value->m > 0) {
return [
'n' => $value->d,
'unit' => 'D'
];
}
if ($value->m > 0) {
return [ return [
'n' => $value->m, 'n' => $value->m,
'unit' => 'M' 'unit' => 'M'
]; ];
} elseif ($value->y > 0) { }
if ($value->y > 0) {
return [ return [
'n' => $value->y, 'n' => $value->y,
'unit' => 'Y' 'unit' => 'Y'
]; ];
} }
throw new TransformationFailedException('the date interval does not ' throw new TransformationFailedException(
. 'contains any days, months or years'); 'The date interval does not contains any days, months or years.'
);
} }
public function reverseTransform($value) public function reverseTransform($value)
{ {
if (empty($value) or empty($value['n'])) { if (empty($value) || empty($value['n'])) {
return null; return null;
} }
$string = 'P'.$value['n'].$value['unit']; $string = 'P'.$value['n'].$value['unit'];
try { try {
return new \DateInterval($string); return new \DateInterval($string);
} catch (\Exception $e) { } catch (\Exception $e) {
throw new TransformationFailedException("Could not transform value " throw new TransformationFailedException(
. "into DateInterval", 1542, $e); 'Could not transform value into DateInterval',
1542,
$e
);
} }
} }
} }

View File

@ -1,50 +1,23 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
* Copyright (C) 2016 Champs-Libres Coopérative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Pagination; namespace Chill\MainBundle\Pagination;
/** /**
* PageGenerator associated with a Paginator * PageGenerator associated with a Paginator.
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* @author Champs Libres <info@champs-libres.coop>
*/ */
class PageGenerator implements \Iterator class PageGenerator implements \Iterator
{ {
/** protected Paginator $paginator;
*
* @var Paginator protected int $current = 1;
*/
protected $paginator; public function __construct(Paginator $paginator)
/**
*
* @var int
*/
protected $current = 1;
public function __construct(Paginator $paginator)
{ {
$this->paginator = $paginator;; $this->paginator = $paginator;;
} }
public function current() public function current()
{ {
return $this->paginator->getPage($current); return $this->paginator->getPage($current);
@ -67,7 +40,7 @@ class PageGenerator implements \Iterator
public function valid() public function valid()
{ {
return $this->current > 0 return $this->current > 0
&& $this->current <= $this->paginator->countPages(); && $this->current <= $this->paginator->countPages();
} }
} }

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Chill\MainBundle\Repository; namespace Chill\MainBundle\Repository;
use Chill\MainBundle\Entity\GroupCenter;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;

Some files were not shown because too many files have changed in this diff Show More