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 -->
* [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 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
* [person]: Display the name of a user when searching after a User (TMS)
* [person]: Add civility to the person
* [person]: Various improvements on 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
* [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.
* [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
### Test release 2021-10-27
* [person]: delete double actions buttons on search person page
@ -63,7 +95,10 @@ and this project adheres to
* [3party]: fix address creation
* [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)
* [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
@ -130,7 +165,7 @@ and this project adheres to
## Test released
<!--
<!--
Coming soon...
@ -143,4 +178,4 @@ DO NOT ADD unreleased items here. Add them under "Unreleased" title
## Stable releases
No stable releases for v2+

View File

@ -54,6 +54,9 @@
"doctrine/doctrine-fixtures-bundle": "^3.3",
"fakerphp/faker": "^1.13",
"nelmio/alice": "^3.8",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^7.0",
"symfony/debug-bundle": "^5.1",
"symfony/dotenv": "^5.1",
@ -88,7 +91,8 @@
},
"autoload-dev": {
"psr-4": {
"App\\": "tests/app/src/"
"App\\": "tests/app/src/",
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
}
},
"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">
<directory suffix="Test.php">src/Bundle/ChillCalendarBundle/Tests/</directory>
</testsuite>
<testsuite name="DocGeneratorBundle">
<directory suffix="Test.php">src/Bundle/ChillDocGeneratorBundle/tests/</directory>
</testsuite>
</testsuites>
<listeners>

View File

@ -1,37 +1,26 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Controller;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepository;
use Chill\ActivityBundle\Entity\ActivityReason;
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\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Repository\LocationRepository;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
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 Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
@ -42,33 +31,56 @@ use Chill\ActivityBundle\Form\ActivityType;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Class ActivityController
*
* @package Chill\ActivityBundle\Controller
*/
class ActivityController extends AbstractController
final 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(
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
ActivityTypeRepository $activityTypeRepository,
ActivityTypeCategoryRepository $activityTypeCategoryRepository,
PersonRepository $personRepository,
ThirdPartyRepository $thirdPartyRepository,
LocationRepository $locationRepository,
ActivityRepository $activityRepository,
AccompanyingPeriodRepository $accompanyingPeriodRepository,
EntityManagerInterface $entityManager,
EventDispatcherInterface $eventDispatcher,
AuthorizationHelper $authorizationHelper,
LoggerInterface $logger,
SerializerInterface $serializer
) {
$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->authorizationHelper = $authorizationHelper;
$this->logger = $logger;
$this->serializer = $serializer;
}
@ -78,8 +90,8 @@ class ActivityController extends AbstractController
*/
public function listAction(Request $request): Response
{
$em = $this->getDoctrine()->getManager();
$view = null;
$activities = [];
// TODO: add pagination
[$person, $accompanyingPeriod] = $this->getEntity($request);
@ -89,10 +101,10 @@ class ActivityController extends AbstractController
$activities = $this->activityACLAwareRepository
->findByPerson($person, ActivityVoter::SEE, 0, null);
$event = new PrivacyEvent($person, array(
$event = new PrivacyEvent($person, [
'element_class' => Activity::class,
'action' => 'list'
));
]);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
$view = 'ChillActivityBundle:Activity:listPerson.html.twig';
@ -105,16 +117,18 @@ class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig';
}
return $this->render($view, array(
'activities' => $activities,
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
));
return $this->render(
$view,
[
'activities' => $activities,
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
]
);
}
public function selectTypeAction(Request $request): Response
{
$em = $this->getDoctrine()->getManager();
$view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request);
@ -127,12 +141,17 @@ class ActivityController extends AbstractController
$data = [];
$activityTypeCategories = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityTypeCategory::class)
$activityTypeCategories = $this
->activityTypeCategoryRepository
->findBy(['active' => true], ['ordering' => 'ASC']);
foreach ($activityTypeCategories as $activityTypeCategory) {
$activityTypes = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class)
->findBy(['active' => true, 'category' => $activityTypeCategory], ['ordering' => 'ASC']);
$activityTypes = $this
->activityTypeRepository
->findBy(
['active' => true, 'category' => $activityTypeCategory],
['ordering' => 'ASC']
);
$data[] = [
'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) {
throw $this->createNotFoundException('Template not found');
}
@ -154,13 +167,13 @@ class ActivityController extends AbstractController
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'data' => $data,
'activityData' => $activityData
'activityData' => $request->query->get('activityData', []),
]);
}
public function newAction(Request $request): Response
{
$em = $this->getDoctrine()->getManager();
$view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request);
@ -171,8 +184,7 @@ class ActivityController extends AbstractController
}
$activityType_id = $request->get('activityType_id', 0);
$activityType = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class)
->find($activityType_id);
$activityType = $this->activityTypeRepository->find($activityType_id);
if (isset($activityType) && !$activityType->isActive()) {
throw new \InvalidArgumentException('Activity type must be active');
@ -230,23 +242,23 @@ class ActivityController extends AbstractController
if (array_key_exists('personsId', $activityData)) {
foreach($activityData['personsId'] as $personId){
$concernedPerson = $em->getRepository(\Chill\PersonBundle\Entity\Person::class)->find($personId);
$concernedPerson = $this->personRepository->find($personId);
$entity->addPerson($concernedPerson);
}
}
if (array_key_exists('professionalsId', $activityData)) {
foreach($activityData['professionalsId'] as $professionalsId){
$professional = $em->getRepository(\Chill\ThirdPartyBundle\Entity\ThirdParty::class)->find($professionalsId);
$professional = $this->thirdPartyRepository->find($professionalsId);
$entity->addThirdParty($professional);
}
}
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);
}
if (array_key_exists('comment', $activityData)) {
$comment = new CommentEmbeddable();
$comment->setComment($activityData['comment']);
@ -268,8 +280,8 @@ class ActivityController extends AbstractController
])->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($entity);
$em->flush();
$this->entityManager->persist($entity);
$this->entityManager->flush();
$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
{
$em = $this->getDoctrine()->getManager();
$view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request);
@ -307,13 +319,14 @@ class ActivityController extends AbstractController
$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.');
}
if (null !== $accompanyingPeriod) {
// @TODO: Properties created dynamically.
$entity->personsAssociated = $entity->getPersonsAssociated();
$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
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_SEE', $entity);
$deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod);
$deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
// TODO
/*
@ -337,21 +350,20 @@ class ActivityController extends AbstractController
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, array(
return $this->render($view, [
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'entity' => $entity,
'delete_form' => $deleteForm->createView(),
));
]);
}
/**
* Displays a form to edit an existing Activity entity.
*
*/
public function editAction($id, Request $request): Response
{
$em = $this->getDoctrine()->getManager();
$view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request);
@ -361,9 +373,9 @@ class ActivityController extends AbstractController
$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.');
}
@ -378,17 +390,18 @@ class ActivityController extends AbstractController
])->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($entity);
$em->flush();
$this->entityManager->persist($entity);
$this->entityManager->flush();
$this->addFlash('success', $this->get('translator')->trans('Success : activity updated!'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $id;
$params['id'] = $entity->getId();
return $this->redirectToRoute('chill_activity_activity_show', $params);
}
$deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod);
$deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
/*
* TODO
@ -406,23 +419,22 @@ class ActivityController extends AbstractController
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, array(
return $this->render($view, [
'entity' => $entity,
'edit_form' => $form->createView(),
'delete_form' => $deleteForm->createView(),
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'activity_json' => $activity_array
));
]);
}
/**
* Deletes a Activity entity.
*
*/
public function deleteAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request);
@ -432,8 +444,7 @@ class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig';
}
/* @var $activity Activity */
$activity = $em->getRepository('ChillActivityBundle:Activity')->find($id);
$activity = $this->activityRepository->find($id);
if (!$activity) {
throw $this->createNotFoundException('Unable to find Activity entity.');
@ -442,35 +453,37 @@ class ActivityController extends AbstractController
// TODO
// $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) {
$form->handleRequest($request);
if ($form->isValid()) {
$this->logger->notice("An activity has been removed", array(
$this->logger->notice("An activity has been removed", [
'by_user' => $this->getUser()->getUsername(),
'activity_id' => $activity->getId(),
'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null,
'comment' => $activity->getComment()->getComment(),
'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null,
'reasons_ids' => $activity->getReasons()
->map(function ($ar) { return $ar->getId(); })
->map(
static fn (ActivityReason $ar): int => $ar->getId()
)
->toArray(),
'type_id' => $activity->getType()->getId(),
'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null,
'date' => $activity->getDate()->format('Y-m-d'),
'attendee' => $activity->getAttendee()
));
]);
$em->remove($activity);
$em->flush();
$this->entityManager->remove($activity);
$this->entityManager->flush();
$this->addFlash('success', $this->get('translator')
->trans("The activity has been successfully removed."));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
return $this->redirectToRoute('chill_activity_activity_list', $params);
}
}
@ -479,18 +492,18 @@ class ActivityController extends AbstractController
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, array(
return $this->render($view, [
'activity' => $activity,
'delete_form' => $form->createView(),
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
));
]);
}
/**
* 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['id'] = $id;
@ -498,19 +511,17 @@ class ActivityController extends AbstractController
return $this->createFormBuilder()
->setAction($this->generateUrl('chill_activity_activity_delete', $params))
->setMethod('DELETE')
->add('submit', SubmitType::class, array('label' => 'Delete'))
->getForm()
;
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm();
}
private function getEntity(Request $request): array
{
$em = $this->getDoctrine()->getManager();
$person = $accompanyingPeriod = null;
if ($request->query->has('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) {
throw $this->createNotFoundException('Person not found');
@ -519,7 +530,7 @@ class ActivityController extends AbstractController
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
} elseif ($request->query->has('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) {
throw $this->createNotFoundException('Accompanying Period not found');
@ -532,21 +543,20 @@ class ActivityController extends AbstractController
}
return [
$person, $accompanyingPeriod
$person,
$accompanyingPeriod
];
}
private function buildParamsToUrl(
?Person $person,
?AccompanyingPeriod $accompanyingPeriod
): array {
private function buildParamsToUrl(?Person $person, ?AccompanyingPeriod $accompanyingPeriod): array
{
$params = [];
if ($person) {
if (null !== $person) {
$params['person_id'] = $person->getId();
}
if ($accompanyingPeriod) {
if (null !== $accompanyingPeriod) {
$params['accompanying_period_id'] = $accompanyingPeriod->getId();
}

View File

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

View File

@ -1,27 +1,8 @@
<?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;
use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\Location;
use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface;
@ -38,7 +19,7 @@ use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasScopeInterface;
use Doctrine\Common\Collections\Collection;
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\DiscriminatorMap;
@ -202,7 +183,7 @@ class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPer
return $this->id;
}
public function setUser(User $user): self
public function setUser(UserInterface $user): self
{
$this->user = $user;

View File

@ -1,31 +1,12 @@
<?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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Class ActivityTypeCateogry
*
* @package Chill\ActivityBundle\Entity
* @ORM\Entity()
* @ORM\Table(name="activitytypecategory")
* @ORM\HasLifecycleCallbacks()
@ -37,7 +18,7 @@ class ActivityTypeCategory
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private ?int $id;
private ?int $id = null;
/**
* @ORM\Column(type="json")
@ -54,10 +35,7 @@ class ActivityTypeCategory
*/
private float $ordering = 0.0;
/**
* Get id
*/
public function getId(): int
public function getId(): ?int
{
return $this->id;
}

View File

@ -1,70 +1,39 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
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 Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityReasonAggregator implements AggregatorInterface,
ExportElementValidatedInterface
class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface
{
/**
*
* @var EntityRepository
*/
protected $categoryRepository;
protected ActivityReasonCategoryRepository $activityReasonCategoryRepository;
/**
*
* @var EntityRepository
*/
protected $reasonRepository;
protected ActivityReasonRepository $activityReasonRepository;
/**
*
* @var TranslatableStringHelper
*/
protected $stringHelper;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
EntityRepository $categoryRepository,
EntityRepository $reasonRepository,
TranslatableStringHelper $stringHelper
ActivityReasonCategoryRepository $activityReasonCategoryRepository,
ActivityReasonRepository $activityReasonRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->categoryRepository = $categoryRepository;
$this->reasonRepository = $reasonRepository;
$this->stringHelper = $stringHelper;
$this->activityReasonCategoryRepository = $activityReasonCategoryRepository;
$this->activityReasonRepository = $activityReasonRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function alterQuery(QueryBuilder $qb, $data)
@ -77,7 +46,7 @@ class ActivityReasonAggregator implements AggregatorInterface,
$elem = 'category.id';
$alias = 'activity_categories_id';
} else {
throw new \RuntimeException('the data provided are not recognized');
throw new \RuntimeException('The data provided are not recognized.');
}
$qb->addSelect($elem.' as '.$alias);
@ -93,11 +62,12 @@ class ActivityReasonAggregator implements AggregatorInterface,
(! array_key_exists('activity', $join))
) {
$qb->add(
'join',
array('activity' =>
new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')
),
true);
'join',
[
'activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')
],
true
);
}
// join category if necessary
@ -143,28 +113,33 @@ class ActivityReasonAggregator implements AggregatorInterface,
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('level', ChoiceType::class, array(
'choices' => array(
'By reason' => 'reasons',
'By category of reason' => 'categories'
),
'multiple' => false,
'expanded' => true,
'label' => 'Reason\'s level'
));
$builder->add(
'level',
ChoiceType::class,
[
'choices' => [
'By reason' => 'reasons',
'By category of reason' => 'categories'
],
'multiple' => false,
'expanded' => true,
'label' => "Reason's level"
]
);
}
public function validateForm($data, ExecutionContextInterface $context)
{
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();
}
}
public function getTitle()
public function getTitle()
{
return "Aggregate by activity reason";
return 'Aggregate by activity reason';
}
public function addRole()
@ -177,41 +152,33 @@ class ActivityReasonAggregator implements AggregatorInterface,
// for performance reason, we load data from db only once
switch ($data['level']) {
case 'reasons':
$this->reasonRepository->findBy(array('id' => $values));
$this->activityReasonRepository->findBy(['id' => $values]);
break;
case 'categories':
$this->categoryRepository->findBy(array('id' => $values));
$this->activityReasonCategoryRepository->findBy(['id' => $values]);
break;
default:
throw new \RuntimeException(sprintf("the level data '%s' is invalid",
$data['level']));
throw new \RuntimeException(sprintf("The level data '%s' is invalid.", $data['level']));
}
return function($value) use ($data) {
if ($value === '_header') {
return $data['level'] === 'reasons' ?
'Group by reasons'
:
'Group by categories of reason'
;
return $data['level'] === 'reasons' ? 'Group by reasons' : 'Group by categories of reason';
}
switch ($data['level']) {
case 'reasons':
/* @var $r \Chill\ActivityBundle\Entity\ActivityReason */
$r = $this->reasonRepository->find($value);
$r = $this->activityReasonRepository->find($value);
return $this->stringHelper->localize($r->getCategory()->getName())
." > "
. $this->stringHelper->localize($r->getName());
;
break;
return sprintf(
"%s > %s",
$this->translatableStringHelper->localize($r->getCategory()->getName()),
$this->translatableStringHelper->localize($r->getName())
);
case 'categories':
$c = $this->categoryRepository->find($value);
$c = $this->activityReasonCategoryRepository->find($value);
return $this->stringHelper->localize($c->getName());
break;
// no need for a default : the default was already set above
return $this->translatableStringHelper->localize($c->getName());
}
};
@ -222,12 +189,14 @@ class ActivityReasonAggregator implements AggregatorInterface,
// add select element
if ($data['level'] === 'reasons') {
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
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityTypeAggregator implements AggregatorInterface
{
/**
*
* @var EntityRepository
*/
protected $typeRepository;
/**
*
* @var TranslatableStringHelper
*/
protected $stringHelper;
const KEY = 'activity_type_aggregator';
protected ActivityTypeRepository $activityTypeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
public const KEY = 'activity_type_aggregator';
public function __construct(
EntityRepository $typeRepository,
TranslatableStringHelper $stringHelper
ActivityTypeRepository $activityTypeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->typeRepository = $typeRepository;
$this->stringHelper = $stringHelper;
$this->activityTypeRepository = $activityTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function alterQuery(QueryBuilder $qb, $data)
{
// add select element
// add select element
$qb->addSelect(sprintf('IDENTITY(activity.type) AS %s', self::KEY));
// add the "group by" part
$groupBy = $qb->addGroupBy(self::KEY);
$qb->addGroupBy(self::KEY);
}
/**
* Check if a join between Activity and another alias
*
*
* @param Join[] $joins
* @param string $alias the alias to search for
* @return boolean
@ -81,7 +52,7 @@ class ActivityTypeAggregator implements AggregatorInterface
return true;
}
}
return false;
}
@ -97,35 +68,33 @@ class ActivityTypeAggregator implements AggregatorInterface
public function getTitle()
{
return "Aggregate by activity type";
return 'Aggregate by activity type';
}
public function addRole()
{
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
$this->typeRepository->findBy(array('id' => $values));
return function($value) use ($data) {
$this->activityTypeRepository->findBy(['id' => $values]);
return function($value): string {
if ($value === '_header') {
return 'Activity type';
}
/* @var $r \Chill\ActivityBundle\Entity\ActivityType */
$t = $this->typeRepository->find($value);
$t = $this->activityTypeRepository->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
/*
* 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;
use Chill\MainBundle\Repository\UserRepository;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query\Expr\Join;
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
{
/**
*
* @var EntityManagerInterface
*/
protected $em;
const KEY = 'activity_user_id';
function __construct(EntityManagerInterface $em)
{
$this->em = $em;
public const KEY = 'activity_user_id';
private UserRepository $userRepository;
public function __construct(
UserRepository $userRepository
) {
$this->userRepository = $userRepository;
}
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
@ -53,9 +28,9 @@ class ActivityUserAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
// add select element
// add select element
$qb->addSelect(sprintf('IDENTITY(activity.user) AS %s', self::KEY));
// add the "group by" part
$qb->addGroupBy(self::KEY);
}
@ -73,17 +48,14 @@ class ActivityUserAggregator implements AggregatorInterface
public function getLabels($key, $values, $data): \Closure
{
// preload users at once
$this->em->getRepository(User::class)
->findBy(['id' => $values]);
$this->userRepository->findBy(['id' => $values]);
return function($value) {
switch ($value) {
case '_header':
return 'activity user';
default:
return $this->em->getRepository(User::class)->find($value)
->getUsername();
if ($value === '_header') {
return 'activity user';
}
return $this->userRepository->find($value)->getUsername();
};
}
@ -94,6 +66,6 @@ class ActivityUserAggregator implements AggregatorInterface
public function getTitle(): string
{
return "Aggregate by activity user";
return 'Aggregate by activity user';
}
}

View File

@ -1,64 +1,40 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
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 Doctrine\ORM\Query;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityManagerInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class CountActivity implements ExportInterface
{
/**
*
* @var EntityManagerInterface
*/
protected $entityManager;
protected ActivityRepository $activityRepository;
public function __construct(
EntityManagerInterface $em
)
{
$this->entityManager = $em;
ActivityRepository $activityRepository
) {
$this->activityRepository = $activityRepository;
}
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder)
{
}
public function getDescription()
{
return "Count activities by various parameters.";
return 'Count activities by various parameters.';
}
public function getTitle()
{
return "Count activities";
return 'Count activities';
}
public function getType()
@ -68,26 +44,26 @@ class CountActivity implements ExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
{
$qb = $this->entityManager->createQueryBuilder();
$centers = array_map(function($el) { return $el['center']; }, $acl);
$qb->select('COUNT(activity.id) as export_count_activity')
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.person', 'person')
;
$qb->where($qb->expr()->in('person.center', ':centers'))
->setParameter('centers', $centers)
;
$centers = array_map(static fn($el) => $el['center'], $acl);
$qb = $this
->activityRepository
->createQueryBuilder('activity')
->select('COUNT(activity.id) as export_count_activity')
->join('activity.person', 'person');
$qb
->where($qb->expr()->in('person.center', ':centers'))
->setParameter('centers', $centers);
return $qb;
}
public function supportsModifiers()
{
return array('person', 'activity');
return ['person', 'activity'];
}
public function requiredRole()
{
return new Role(ActivityStatsVoter::STATS);
@ -95,7 +71,7 @@ class CountActivity implements ExportInterface
public function getAllowedFormattersTypes()
{
return array(\Chill\MainBundle\Export\FormatterInterface::TYPE_TABULAR);
return [FormatterInterface::TYPE_TABULAR];
}
public function getLabels($key, array $values, $data)
@ -103,19 +79,13 @@ class CountActivity implements ExportInterface
if ($key !== 'export_count_activity') {
throw new \LogicException("the key $key is not used by this export");
}
return function($value) {
return $value === '_header' ?
'Number of activities'
:
$value
;
};
return static fn($value) => $value === '_header' ? 'Number of activities' : $value;
}
public function getQueryKeys($data)
{
return array('export_count_activity');
return ['export_count_activity'];
}
public function getResult($qb, $data)

View File

@ -1,31 +1,14 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Export\ListInterface;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Scope;
use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
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\Validator\Context\ExecutionContextInterface;
/**
* Create a list for all activities
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ListActivity implements ListInterface
{
private ActivityRepository $activityRepository;
/**
*
* @var EntityManagerInterface
*/
protected $entityManager;
protected EntityManagerInterface $entityManager;
/**
*
* @var TranslatorInterface
*/
protected $translator;
protected TranslatorInterface $translator;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
protected TranslatableStringHelperInterface $translatableStringHelper;
protected $fields = array(
protected array $fields = [
'id',
'date',
'durationTime',
@ -75,32 +42,28 @@ class ListActivity implements ListInterface
'person_firstname',
'person_lastname',
'person_id'
);
];
public function __construct(
EntityManagerInterface $em,
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper
)
{
EntityManagerInterface $em,
TranslatorInterface $translator,
TranslatableStringHelperInterface $translatableStringHelper,
ActivityRepository $activityRepository
) {
$this->entityManager = $em;
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
$this->activityRepository = $activityRepository;
}
/**
* {@inheritDoc}
*
* @param FormBuilderInterface $builder
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('fields', ChoiceType::class, array(
$builder->add('fields', ChoiceType::class, [
'multiple' => true,
'expanded' => true,
'choices' => array_combine($this->fields, $this->fields),
'label' => 'Fields to include in export',
'constraints' => [new Callback(array(
'constraints' => [new Callback([
'callback' => function($selected, ExecutionContextInterface $context) {
if (count($selected) === 0) {
$context->buildViolation('You must select at least one element')
@ -108,19 +71,14 @@ class ListActivity implements ListInterface
->addViolation();
}
}
))]
));
])]
]);
}
/**
* {@inheritDoc}
*
* @return type
*/
public function getAllowedFormattersTypes()
{
return array(FormatterInterface::TYPE_LIST);
return [FormatterInterface::TYPE_LIST];
}
public function getDescription()
@ -133,29 +91,32 @@ class ListActivity implements ListInterface
switch ($key)
{
case 'date' :
return function($value) {
if ($value === '_header') return 'date';
return static function($value) {
if ($value === '_header') {
return 'date';
}
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
return $date->format('d-m-Y');
};
case 'attendee':
return function($value) {
if ($value === '_header') return 'attendee';
return static function($value) {
if ($value === '_header') {
return 'attendee';
}
return $value ? 1 : 0;
};
case 'list_reasons' :
/* @var $activityReasonsRepository EntityRepository */
$activityRepository = $this->entityManager
->getRepository('ChillActivityBundle:Activity');
$activityRepository = $this->activityRepository;
return function($value) use ($activityRepository) {
if ($value === '_header') return 'activity reasons';
return function($value) use ($activityRepository): string {
if ($value === '_header') {
return 'activity reasons';
}
$activity = $activityRepository
->find($value);
$activity = $activityRepository->find($value);
return implode(", ", array_map(function(ActivityReason $r) {
@ -168,21 +129,25 @@ class ListActivity implements ListInterface
};
case 'circle_name' :
return function($value) {
if ($value === '_header') return 'circle';
if ($value === '_header') {
return 'circle';
}
return $this->translatableStringHelper
->localize(json_decode($value, true));
return $this->translatableStringHelper->localize(json_decode($value, true));
};
case 'type_name' :
return function($value) {
if ($value === '_header') return 'activity type';
if ($value === '_header') {
return 'activity type';
}
return $this->translatableStringHelper
->localize(json_decode($value, true));
return $this->translatableStringHelper->localize(json_decode($value, true));
};
default:
return function($value) use ($key) {
if ($value === '_header') return $key;
return static function($value) use ($key) {
if ($value === '_header') {
return $key;
}
return $value;
};
@ -209,14 +174,13 @@ class ListActivity implements ListInterface
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);
// throw an error if any fields are present
if (!\array_key_exists('fields', $data)) {
throw new \Doctrine\DBAL\Exception\InvalidArgumentException("any fields "
. "have been checked");
throw new InvalidArgumentException('Any fields have been checked.');
}
$qb = $this->entityManager->createQueryBuilder();
@ -227,7 +191,6 @@ class ListActivity implements ListInterface
->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers);
;
foreach ($this->fields as $f) {
if (in_array($f, $data['fields'])) {
@ -269,8 +232,6 @@ class ListActivity implements ListInterface
}
}
return $qb;
}
@ -281,7 +242,7 @@ class ListActivity implements ListInterface
public function supportsModifiers()
{
return array('activity', 'person');
return ['activity', 'person'];
}
}

View File

@ -1,89 +1,62 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
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 Doctrine\ORM\Query;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityManagerInterface;
/**
* This export allow to compute stats on activity duration.
*
*
* The desired stat must be given in constructor.
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class StatActivityDuration implements ExportInterface
{
/**
*
* @var EntityManagerInterface
*/
protected $entityManager;
const SUM = 'sum';
private ActivityRepository $activityRepository;
public const SUM = 'sum';
/**
* The action for this report.
*
* @var string
*/
protected $action;
protected string $action;
/**
* constructor
*
* @param EntityManagerInterface $em
* @param string $action the stat to perform
*/
public function __construct(
EntityManagerInterface $em,
$action = 'sum'
)
{
$this->entityManager = $em;
ActivityRepository $activityRepository,
string $action = 'sum'
) {
$this->action = $action;
$this->activityRepository = $activityRepository;
}
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder)
{
}
public function getDescription()
{
if ($this->action === self::SUM) {
return "Sum activities duration by various parameters.";
return 'Sum activities duration by various parameters.';
}
}
public function getTitle()
{
if ($this->action === self::SUM) {
return "Sum activity duration";
return 'Sum activity duration';
}
}
public function getType()
@ -91,31 +64,33 @@ class StatActivityDuration implements ExportInterface
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);
$qb = $this->entityManager->createQueryBuilder();
$centers = array_map(
static fn(array $el): string => $el['center'],
$acl
);
$qb = $this->activityRepository->createQueryBuilder('activity');
$select = null;
if ($this->action === self::SUM) {
$select = "SUM(activity.durationTime) AS export_stat_activity";
$select = 'SUM(activity.durationTime) AS export_stat_activity';
}
$qb->select($select)
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.person', 'person')
->join('person.center', 'center')
->where($qb->expr()->in('center', ':centers'))
->setParameter(':centers', $centers)
;
return $qb;
return $qb->select($select)
->join('activity.person', 'person')
->join('person.center', 'center')
->where($qb->expr()->in('center', ':centers'))
->setParameter(':centers', $centers);
}
public function supportsModifiers()
{
return array('person', 'activity');
return ['person', 'activity'];
}
public function requiredRole()
{
return new Role(ActivityStatsVoter::STATS);
@ -123,32 +98,23 @@ class StatActivityDuration implements ExportInterface
public function getAllowedFormattersTypes()
{
return array(\Chill\MainBundle\Export\FormatterInterface::TYPE_TABULAR);
return [FormatterInterface::TYPE_TABULAR];
}
public function getLabels($key, array $values, $data)
{
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) {
case self::SUM:
$header = "Sum of activities duration";
}
return function($value) use ($header) {
return $value === '_header' ?
$header
:
$value
;
};
$header = $this->action === self::SUM ? 'Sum of activities duration' : false;
return static fn(string $value) => $value === '_header' ? $header : $value;
}
public function getQueryKeys($data)
{
return array('export_stat_activity');
return ['export_stat_activity'];
}
public function getResult($qb, $data)

View File

@ -1,25 +1,12 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\MainBundle\Export\FilterInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
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 Symfony\Component\Translation\TranslatorInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityDateFilter implements FilterInterface
{
/**
*
* @var TranslatorInterface
*/
protected $translator;
protected TranslatorInterface $translator;
function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole()
{
return null;
}
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->between('activity.date', ':date_from',
$clause = $qb->expr()->between('activity.date', ':date_from',
':date_to');
if ($where instanceof Expr\Andx) {
@ -63,7 +40,7 @@ class ActivityDateFilter implements FilterInterface
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('date_from', $data['date_from']);
$qb->setParameter('date_to', $data['date_to']);
@ -74,35 +51,43 @@ class ActivityDateFilter implements FilterInterface
return 'activity';
}
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('date_from', DateType::class, array(
'label' => "Activities after this date",
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
));
$builder->add('date_to', DateType::class, array(
'label' => "Activities before this date",
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
));
$builder->add(
'date_from',
DateType::class,
[
'label' => 'Activities after this date',
'data' => new \DateTime(),
'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
]
);
$builder->add(
'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) {
/* @var $filterForm \Symfony\Component\Form\FormInterface */
$filterForm = $event->getForm()->getParent();
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
if ($enabled === true) {
// if the filter is enabled, add some validation
$form = $event->getForm();
$date_from = $form->get('date_from')->getData();
$date_to = $form->get('date_to')->getData();
// check that fields are not empty
if ($date_from === null) {
$form->get('date_from')->addError(new FormError(
@ -113,8 +98,8 @@ class ActivityDateFilter implements FilterInterface
$form->get('date_to')->addError(new FormError(
$this->translator->trans('This field '
. 'should not be empty')));
}
}
// check that date_from is before date_to
if (
($date_from !== null && $date_to !== null)
@ -132,17 +117,18 @@ class ActivityDateFilter implements FilterInterface
public function describeAction($data, $format = 'string')
{
return array(
"Filtered by date of activity: only between %date_from% and %date_to%",
array(
"%date_from%" => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y')
));
return [
'Filtered by date of activity: only between %date_from% and %date_to%',
[
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y')
]
];
}
public function getTitle()
{
return "Filtered by date activity";
return 'Filtered by date activity';
}
}

View File

@ -1,25 +1,12 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@ -28,41 +15,24 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityReasonFilter implements FilterInterface,
ExportElementValidatedInterface
class ActivityReasonFilter implements FilterInterface, ExportElementValidatedInterface
{
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
* The repository for activity reasons
*
* @var EntityRepository
*/
protected $reasonRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
protected ActivityReasonRepository $activityReasonRepository;
public function __construct(
TranslatableStringHelper $helper,
EntityRepository $reasonRepository
TranslatableStringHelper $helper,
ActivityReasonRepository $activityReasonRepository
) {
$this->translatableStringHelper = $helper;
$this->reasonRepository = $reasonRepository;
$this->activityReasonRepository = $activityReasonRepository;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
@ -75,12 +45,12 @@ class ActivityReasonFilter implements FilterInterface,
&&
!$this->checkJoinAlreadyDefined($join['activity'], 'reasons')
)
OR
||
(! array_key_exists('activity', $join))
) {
$qb->add(
'join',
array('activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')),
'join',
array('activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')),
true
);
}
@ -90,25 +60,25 @@ class ActivityReasonFilter implements FilterInterface,
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('selected_activity_reasons', $data['reasons']);
}
/**
* Check if a join between Activity and Reason is already defined
*
*
* @param Join[] $joins
* @return boolean
*/
private function checkJoinAlreadyDefined(array $joins, $alias)
private function checkJoinAlreadyDefined(array $joins, $alias): bool
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
@ -119,51 +89,47 @@ class ActivityReasonFilter implements FilterInterface,
public function buildForm(FormBuilderInterface $builder)
{
//create a local copy of translatableStringHelper
$helper = $this->translatableStringHelper;
$builder->add('reasons', EntityType::class, array(
'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());
},
$builder->add('reasons', EntityType::class, [
'class' => ActivityReason::class,
'choice_label' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getName()),
'group_by' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
'multiple' => true,
'expanded' => false
));
]);
}
public function validateForm($data, ExecutionContextInterface $context)
{
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();
}
}
public function getTitle()
public function getTitle()
{
return 'Filter by reason';
}
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
}
public function describeAction($data, $format = 'string')
{
// collect all the reasons'name used in this filter in one array
$reasonsNames = array_map(
function(ActivityReason $r) {
return "\"".$this->translatableStringHelper->localize($r->getName())."\"";
},
$this->reasonRepository->findBy(array('id' => $data['reasons']->toArray()))
);
return array("Filtered by reasons: only %list%",
["%list%" => implode(", ", $reasonsNames)]);
fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
$this->activityReasonRepository->findBy(array('id' => $data['reasons']->toArray()))
);
return [
'Filtered by reasons: only %list%',
[
'%list%' => implode(", ", $reasonsNames),
]
];
}
}

View File

@ -1,67 +1,37 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\ActivityBundle\Entity\ActivityType;
/**
*
*
*/
class ActivityTypeFilter implements FilterInterface,
ExportElementValidatedInterface
class ActivityTypeFilter implements FilterInterface, ExportElementValidatedInterface
{
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
* The repository for activity reasons
*
* @var EntityRepository
*/
protected $typeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
protected ActivityTypeRepository $activityTypeRepository;
public function __construct(
TranslatableStringHelper $helper,
EntityRepository $typeRepository
TranslatableStringHelperInterface $translatableStringHelper,
ActivityTypeRepository $activityTypeRepository
) {
$this->translatableStringHelper = $helper;
$this->typeRepository = $typeRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->activityTypeRepository = $activityTypeRepository;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
@ -72,14 +42,14 @@ class ActivityTypeFilter implements FilterInterface,
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('selected_activity_types', $data['types']);
}
/**
* Check if a join between Activity and Reason is already defined
*
*
* @param Join[] $joins
* @return boolean
*/
@ -90,7 +60,7 @@ class ActivityTypeFilter implements FilterInterface,
return true;
}
}
return false;
}
@ -101,48 +71,50 @@ class ActivityTypeFilter implements FilterInterface,
public function buildForm(FormBuilderInterface $builder)
{
//create a local copy of translatableStringHelper
$helper = $this->translatableStringHelper;
$builder->add('types', EntityType::class, array(
'class' => ActivityType::class,
'choice_label' => function (ActivityType $type) use ($helper) {
return $helper->localize($type->getName());
},
'multiple' => true,
'expanded' => false
));
$builder->add(
'types',
EntityType::class,
[
'class' => ActivityType::class,
'choice_label' => fn(ActivityType $type) => $this->translatableStringHelper->localize($type->getName()),
'multiple' => true,
'expanded' => false
]
);
}
public function validateForm($data, ExecutionContextInterface $context)
{
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();
}
}
public function getTitle()
public function getTitle()
{
return 'Filter by activity type';
}
public function addRole()
{
return new Role(ActivityStatsVoter::STATS);
}
public function describeAction($data, $format = 'string')
{
// collect all the reasons'name used in this filter in one array
$reasonsNames = array_map(
function(ActivityType $t) {
return "\"".$this->translatableStringHelper->localize($t->getName())."\"";
},
$this->typeRepository->findBy(array('id' => $data['types']->toArray()))
);
return array("Filtered by activity type: only %list%",
["%list%" => implode(", ", $reasonsNames)]);
fn(ActivityType $t): string => '"' . $this->translatableStringHelper->localize($t->getName()) . '"',
$this->activityTypeRepository->findBy(['id' => $data['types']->toArray()])
);
return [
'Filtered by activity type: only %list%',
[
'%list%' => implode(", ", $reasonsNames),
]
];
}
}

View File

@ -1,25 +1,14 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
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\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\DateType;
@ -29,78 +18,58 @@ use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\ActivityBundle\Entity\ActivityReason;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\EntityManager;
use Chill\PersonBundle\Export\Declarations;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class PersonHavingActivityBetweenDateFilter implements FilterInterface,
ExportElementValidatedInterface
class PersonHavingActivityBetweenDateFilter implements FilterInterface, ExportElementValidatedInterface
{
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/**
*
* @var EntityRepository
*/
protected $activityReasonRepository;
/**
*
* @var TranslatorInterface
*/
protected $translator;
protected TranslatableStringHelperInterface $translatableStringHelper;
protected ActivityReasonRepository $activityReasonRepository;
protected TranslatorInterface $translator;
public function __construct(
TranslatableStringHelper $translatableStringHelper,
EntityRepository $activityReasonRepository,
TranslatorInterface $translator
TranslatableStringHelper $translatableStringHelper,
ActivityReasonRepository $activityReasonRepository,
TranslatorInterface $translator
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->activityReasonRepository = $activityReasonRepository;
$this->translator = $translator;
$this->translator = $translator;
}
public function addRole()
{
return null;
}
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data)
{
// create a query for activity
$sqb = $qb->getEntityManager()->createQueryBuilder();
$sqb->select("person_person_having_activity.id")
->from("ChillActivityBundle:Activity", "activity_person_having_activity")
->join("activity_person_having_activity.person", "person_person_having_activity")
;
$sqb->select('person_person_having_activity.id')
->from('ChillActivityBundle:Activity', 'activity_person_having_activity')
->join('activity_person_having_activity.person', 'person_person_having_activity');
// add clause between date
$sqb->where("activity_person_having_activity.date BETWEEN "
. ":person_having_activity_between_date_from"
. " AND "
. ":person_having_activity_between_date_to");
$sqb->where('activity_person_having_activity.date BETWEEN '
. ':person_having_activity_between_date_from'
. ' AND '
. ':person_having_activity_between_date_to');
// add clause activity reason
$sqb->join('activity_person_having_activity.reasons',
'reasons_person_having_activity');
$sqb->join('activity_person_having_activity.reasons', 'reasons_person_having_activity');
$sqb->andWhere(
$sqb->expr()->in(
'reasons_person_having_activity',
":person_having_activity_reasons")
);
$sqb->expr()->in(
'reasons_person_having_activity', ':person_having_activity_reasons'
)
);
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('person.id', $sqb->getDQL());
@ -109,11 +78,11 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('person_having_activity_between_date_from',
$qb->setParameter('person_having_activity_between_date_from',
$data['date_from']);
$qb->setParameter('person_having_activity_between_date_to',
$qb->setParameter('person_having_activity_between_date_to',
$data['date_to']);
$qb->setParameter('person_having_activity_reasons', $data['reasons']);
}
@ -123,51 +92,45 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
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(
'label' => "Implied in an activity after this date",
$builder->add('date_from', DateType::class, [
'label' => 'Implied in an activity after this date',
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
));
$builder->add('date_to', DateType::class, array(
'label' => "Implied in an activity before this date",
]);
$builder->add('date_to', DateType::class, [
'label' => 'Implied in an activity before this date',
'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'),
'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text',
'format' => 'dd-MM-yyyy',
));
$builder->add('reasons', EntityType::class, array(
'class' => 'ChillActivityBundle:ActivityReason',
'choice_label' => function (ActivityReason $reason) {
return $this->translatableStringHelper
->localize($reason->getName());
},
'group_by' => function(ActivityReason $reason) {
return $this->translatableStringHelper
->localize($reason->getCategory()->getName());
},
]);
$builder->add('reasons', EntityType::class, [
'class' => ActivityReason::class,
'choice_label' => fn (ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getName()),
'group_by' => fn(ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
'data' => $this->activityReasonRepository->findAll(),
'multiple' => true,
'expanded' => false,
'label' => "Activity reasons for those activities"
));
'label' => 'Activity reasons for those activities'
]);
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
/* @var $filterForm \Symfony\Component\Form\FormInterface */
/* @var FormInterface $filterForm */
$filterForm = $event->getForm()->getParent();
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
if ($enabled === true) {
// if the filter is enabled, add some validation
$form = $event->getForm();
$date_from = $form->get('date_from')->getData();
$date_to = $form->get('date_to')->getData();
// check that fields are not empty
if ($date_from === null) {
$form->get('date_from')->addError(new FormError(
@ -178,8 +141,8 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
$form->get('date_to')->addError(new FormError(
$this->translator->trans('This field '
. 'should not be empty')));
}
}
// check that date_from is before date_to
if (
($date_from !== null && $date_to !== null)
@ -194,35 +157,37 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
}
});
}
public function validateForm($data, ExecutionContextInterface $context)
{
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();
}
}
public function describeAction($data, $format = 'string')
{
return array(
"Filtered by person having an activity between %date_from% and "
. "%date_to% with reasons %reasons_name%",
array(
"%date_from%" => $data['date_from']->format('d-m-Y'),
return [
'Filtered by person having an activity between %date_from% and '
. '%date_to% with reasons %reasons_name%',
[
'%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'),
"%reasons_name%" => implode(", ", array_map(
function (ActivityReason $r) {
return '"'.$this->translatableStringHelper->
localize($r->getName()).'"';
},
$data['reasons']))
));
'%reasons_name%' => implode(
", ",
array_map(
fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
$data['reasons']
)
)
]
];
}
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
/*
* 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/>.
*/
declare(strict_types=1);
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\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Chill\ActivityBundle\Entity\ActivityType;
/**
* Description of TranslatableActivityType
*
* @author Champs-Libres Coop
*/
class TranslatableActivityType extends AbstractType
{
protected TranslatableStringHelperInterface $translatableStringHelper;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
protected $activityTypeRepository;
protected ActivityTypeRepository $activityTypeRepository;
public function __construct(
TranslatableStringHelper $helper,
EntityRepository $activityTypeRepository
)
{
TranslatableStringHelperInterface $helper,
ActivityTypeRepository $activityTypeRepository
) {
$this->translatableStringHelper = $helper;
$this->activityTypeRepository = $activityTypeRepository;
}
@ -65,22 +38,21 @@ class TranslatableActivityType extends AbstractType
return EntityType::class;
}
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, array $options) {
/* @var $qb \Doctrine\ORM\QueryBuilder */
public function buildForm(FormBuilderInterface $builder, array $options) {
/* @var QueryBuilder $qb */
$qb = $options['query_builder'];
if ($options['active_only'] === true) {
$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)
{
$resolver->setDefaults(
array(
'class' => 'ChillActivityBundle:ActivityType',
'class' => ActivityType::class,
'active_only' => true,
'query_builder' => $this->activityTypeRepository
->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
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
@ -29,10 +11,10 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method AccompanyingPeriodParticipation|null find($id, $lockMode = null, $lockVersion = null)
* @method AccompanyingPeriodParticipation|null findOneBy(array $criteria, array $orderBy = null)
* @method AccompanyingPeriodParticipation[] findAll()
* @method AccompanyingPeriodParticipation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
* @method Activity|null find($id, $lockMode = null, $lockVersion = null)
* @method Activity|null findOneBy(array $criteria, array $orderBy = null)
* @method Activity[] findAll()
* @method Activity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityRepository extends ServiceEntityRepository
{
@ -42,12 +24,7 @@ class ActivityRepository extends ServiceEntityRepository
}
/**
* @param $person
* @param array $scopes
* @param string[] $orderBy
* @param int $limit
* @param int $offset
* @return array|Activity[]
* @return Activity[]
*/
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'
)
)
->setParameter('person', $person)
;
->setParameter('person', $person);
foreach ($orderBy as $k => $dir) {
$qb->addOrderBy('a.'.$k, $dir);
@ -72,17 +48,11 @@ class ActivityRepository extends ServiceEntityRepository
$qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery()
->getResult();
return $qb->getQuery()->getResult();
}
/**
* @param AccompanyingPeriod $period
* @param array $scopes
* @param int|null $limit
* @param int|null $offset
* @param array|string[] $orderBy
* @return array|Activity[]
* @return Activity[]
*/
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) {
$qb
->where($qb->expr()->in('a.scope', ':scopes'))
->setParameter('scopes', $scopes)
;
->setParameter('scopes', $scopes);
} else {
$qb
->where(
@ -102,16 +71,14 @@ class ActivityRepository extends ServiceEntityRepository
$qb->expr()->isNull('a.scope')
)
)
->setParameter('scopes', $scopes)
;
->setParameter('scopes', $scopes);
}
$qb
->andWhere(
$qb->expr()->eq('a.accompanyingPeriod', ':period')
)
->setParameter('period', $period)
;
->setParameter('period', $period);
foreach ($orderBy as $k => $dir) {
$qb->addOrderBy('a.'.$k, $dir);
@ -119,7 +86,6 @@ class ActivityRepository extends ServiceEntityRepository
$qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery()
->getResult();
return $qb->getQuery()->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 v-if="getContext === 'accompanyingCourse' && filterSuggestedPersons.length > 0">
<ul>
<ul class="list-unstyled">
<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>
</ul>
</div>

View File

@ -4,7 +4,7 @@
<span class="chill_denomination">
{{ textCutted }}
</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)">
</a>
</span>

View File

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

View File

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

View File

@ -10,7 +10,7 @@
'title' : 'Remove activity'|trans,
'confirm_question' : 'Are you sure you want to remove the activity about "%name%" ?'|trans({ '%name%' : accompanyingCourse.id } ),
'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
} ) }}
{% endblock %}

View File

@ -55,7 +55,7 @@
{% endif %}
<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>

View File

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

View File

@ -1,94 +1,53 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Timeline;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
use Chill\MainBundle\Timeline\TimelineProviderInterface;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepository;
use Doctrine\ORM\EntityManager;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Mapping\ClassMetadata;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Entity\Scope;
use Chill\ActivityBundle\Entity\Activity;
use Chill\MainBundle\Timeline\TimelineSingleQuery;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Provide activity for inclusion in timeline
*
*/
class TimelineActivityProvider implements TimelineProviderInterface
{
/**
*
* @var EntityManager
*/
protected $em;
/**
*
* @var AuthorizationHelper
*/
protected $helper;
/**
*
* @var \Chill\MainBundle\Entity\User
*/
protected $user;
protected EntityManagerInterface $em;
protected AuthorizationHelperInterface $helper;
protected UserInterface $user;
protected ActivityACLAwareRepository $aclAwareRepository;
private const SUPPORTED_CONTEXTS = [ 'center', 'person'];
/**
* TimelineActivityProvider constructor.
*
* @param EntityManager $em
* @param AuthorizationHelper $helper
* @param TokenStorageInterface $storage
*/
public function __construct(
EntityManager $em,
AuthorizationHelper $helper,
EntityManagerInterface $em,
AuthorizationHelperInterface $helper,
TokenStorageInterface $storage,
ActivityACLAwareRepository $aclAwareRepository
)
{
) {
$this->em = $em;
$this->helper = $helper;
$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 !');
}
$this->user = $storage->getToken()->getUser();
}
/**
*
*
* {@inheritDoc}
*/
public function fetchQuery($context, array $args)
@ -97,43 +56,42 @@ class TimelineActivityProvider implements TimelineProviderInterface
return TimelineSingleQuery::fromArray($this->aclAwareRepository
->queryTimelineIndexer($context, $args));
}
$metadataActivity = $this->em->getClassMetadata(Activity::class);
[$where, $parameters] = $this->getWhereClauseForPerson($args['person']);
return TimelineSingleQuery::fromArray([
'id' => $metadataActivity->getTableName()
.'.'.$metadataActivity->getColumnName('id'),
'type' => 'activity',
'date' => $metadataActivity->getTableName()
.'.'.$metadataActivity->getColumnName('date'),
'FROM' => $this->getFromClausePerson($args['person']),
'FROM' => $this->getFromClausePerson(),
'WHERE' => $where,
'parameters' => $parameters
]);
}
private function getWhereClauseForPerson(Person $person)
{
$parameters = [];
$metadataActivity = $this->em->getClassMetadata(Activity::class);
$associationMapping = $metadataActivity->getAssociationMapping('person');
$role = new Role('CHILL_ACTIVITY_SEE');
$reachableScopes = $this->helper->getReachableScopes($this->user,
$role, $person->getCenter());
$reachableScopes = $this->helper->getReachableScopes($this->user, $role->getRole(), $person->getCenter());
$whereClause = sprintf(' {activity.person_id} = ? AND {activity.scope_id} IN ({scopes_ids}) ');
$scopes_ids = [];
// first parameter: activity.person_id
// first parameter: activity.person_id
$parameters[] = $person->getId();
// loop on reachable scopes
// loop on reachable scopes
foreach ($reachableScopes as $scope) {
if (\in_array($scope->getId(), $scopes_ids)) {
continue;
}
$scopes_ids[] = '?';
$scopes_ids[] = '?';
$parameters[] = $scope->getId();
}
@ -151,47 +109,40 @@ class TimelineActivityProvider implements TimelineProviderInterface
$parameters
];
}
private function getFromClausePerson()
private function getFromClausePerson(): string
{
$metadataActivity = $this->em->getClassMetadata(Activity::class);
$metadataPerson = $this->em->getClassMetadata(Person::class);
$associationMapping = $metadataActivity->getAssociationMapping('person');
return $metadataActivity->getTableName().' JOIN '
.$metadataPerson->getTableName().' ON '
.$metadataPerson->getTableName().'.'.
$associationMapping['joinColumns'][0]['referencedColumnName']
.' = '
.$associationMapping['joinColumns'][0]['name']
;
return sprintf(
"%s JOIN %s ON %s.%s = %s",
$metadataActivity->getTableName(),
$metadataPerson->getTableName(),
$metadataPerson->getTableName(),
$associationMapping['joinColumns'][0]['referencedColumnName'],
$associationMapping['joinColumns'][0]['name']
);
}
/**
*
* {@inheritDoc}
*/
public function getEntities(array $ids)
public function getEntities(array $ids): array
{
$activities = $this->em->getRepository(Activity::class)
->findBy(array('id' => $ids));
$result = array();
foreach($activities as $activity) {
$result[$activity->getId()] = $activity;
}
return $result;
}
/**
*
* {@inheritDoc}
*/
public function getEntityTemplate($entity, $context, array $args)
public function getEntityTemplate($entity, $context, array $args): array
{
$this->checkContext($context);
return [
'template' => 'ChillActivityBundle:Timeline:activity_person_context.html.twig',
'template_data' => [
@ -201,26 +152,25 @@ class TimelineActivityProvider implements TimelineProviderInterface
];
}
/**
*
* {@inheritDoc}
*/
public function supportsType($type)
public function supportsType($type): bool
{
return $type === 'activity';
}
/**
* check if the context is supported
*
* @param string $context
* Check if the context is 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)) {
throw new \LogicException("The context '$context' is not "
. "supported. Currently only 'person' is supported");
throw new \LogicException(
sprintf(
"The context '%s' is not supported. Currently only 'person' is supported",
$context
)
);
}
}

View File

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

View File

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

View File

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

View File

@ -1,28 +1,12 @@
---
services:
chill_activity.repository.activity_type:
class: Doctrine\ORM\EntityRepository
factory: ['@doctrine.orm.entity_manager', getRepository]
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'
chill_activity.repository.activity_type: '@Chill\ActivityBundle\Repository\ActivityTypeRepository'
chill_activity.repository.reason: '@Chill\ActivityBundle\Repository\ActivityReasonRepository'
chill_activity.repository.reason_category: '@Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository'
# 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:
autowire: 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
{
private $categoryRepository;
private 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)
{
$qb = parent::buildQueryEntities($action, $request, $filterOrder);
$qb = parent::buildQueryEntities($action, $request);
if ('index' === $action) {
$qb->where($qb->expr()->eq('e.agent', ':user'));

View File

@ -19,8 +19,6 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte
{
/**
* {@inheritdoc}
*
* @phpstan-ignore-next-line
*/
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\JoinColumn(nullable=true)
*/
private $parent;
private ?AsideActivityCategory $parent = null;
/**
* @ORM\OneToMany(targetEntity=AsideActivityCategory::class, mappedBy="parent")
*/
private $children;
private AsideActivityCategory $oldParent;
public function __construct()
{
$this->children = new ArrayCollection();

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,10 @@
<?php
declare(strict_types=1);
namespace Chill\AMLI\BudgetBundle\Form;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
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 Chill\MainBundle\Form\Type\ChillDateType;
use Chill\AMLI\BudgetBundle\Config\ConfigRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
class ResourceType extends AbstractType
{
/**
*
* @var ConfigRepository
*/
protected $configRepository;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
protected ConfigRepository $configRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ConfigRepository $configRepository,
TranslatableStringHelper $translatableStringHelper
ConfigRepository $configRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->configRepository = $configRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
@ -45,17 +38,16 @@ class ResourceType extends AbstractType
'label' => 'Resource element type'
])
->add('amount', MoneyType::class)
->add('comment', TextAreaType::class, [
->add('comment', TextareaType::class, [
'required' => false
])
;
]);
if ($options['show_start_date']) {
$builder->add('startDate', ChillDateType::class, [
'label' => 'Start of validity period'
]);
}
if ($options['show_end_date']) {
$builder->add('endDate', ChillDateType::class, [
'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)
{
$resolver->setDefaults(array(
@ -89,20 +63,29 @@ class ResourceType extends AbstractType
'show_start_date' => true,
'show_end_date' => true
));
$resolver
->setAllowedTypes('show_start_date', 'boolean')
->setAllowedTypes('show_end_date', 'boolean')
;
->setAllowedTypes('show_end_date', 'boolean');
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
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
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\CalendarBundle\Controller;
@ -26,9 +8,11 @@ use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
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\Routing\Annotation\Route;
/**
* Class CalendarController
*
* @package Chill\CalendarBundle\Controller
*/
class CalendarController extends AbstractController
{
protected EventDispatcherInterface $eventDispatcher;
@ -97,9 +76,9 @@ class CalendarController extends AbstractController
'calendarItems' => $calendarItems,
'user' => $user
]);
}
} elseif ($accompanyingPeriod instanceof AccompanyingPeriod) {
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$total = $this->calendarRepository->countByAccompanyingPeriod($accompanyingPeriod);
$paginator = $this->paginator->create($total);
$calendarItems = $this->calendarRepository->findBy(
@ -117,6 +96,8 @@ class CalendarController extends AbstractController
'paginator' => $paginator
]);
}
throw new \Exception('Unable to list actions.');
}
/**
@ -125,13 +106,14 @@ class CalendarController extends AbstractController
*/
public function newAction(Request $request): Response
{
$view = null;
$em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/newByAccompanyingCourse.html.twig';
}
}
// elseif ($user instanceof User) {
// $view = '@ChillCalendar/Calendar/newUser.html.twig';
// }
@ -161,17 +143,18 @@ class CalendarController extends AbstractController
$params = $this->buildParamsToUrl($user, $accompanyingPeriod);
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) {
throw $this->createNotFoundException('Template not found');
}
$entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [
'user' => $user,
'accompanyingCourse' => $accompanyingPeriod,
@ -187,44 +170,49 @@ class CalendarController extends AbstractController
*/
public function showAction(Request $request, $id): Response
{
$view = null;
$em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/showByAccompanyingCourse.html.twig';
}
}
elseif ($user instanceof User) {
$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) {
throw $this->createNotFoundException('Template not found');
}
$personsId = [];
foreach ($entity->getPersons() as $p) {
array_push($personsId, $p->getId());
/** @var Calendar $entity */
$entity = $em->getRepository('ChillCalendarBundle:Calendar')->find($id);
if (null === $entity) {
throw $this->createNotFoundException('Unable to find Calendar entity.');
}
$professionalsId = [];
foreach ($entity->getProfessionals() as $p) {
array_push($professionalsId, $p->getId());
if (null !== $accompanyingPeriod) {
// @TODO: These properties are declared dynamically.
// 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());
$durationTimeInMinutes = $durationTime->days*1440 + $durationTime->h*60 + $durationTime->i;
@ -240,7 +228,7 @@ class CalendarController extends AbstractController
return $this->render($view, [
'accompanyingCourse' => $accompanyingPeriod,
'entity' => $entity,
'entity' => $entity,
'user' => $user,
'activityData' => $activityData
//'delete_form' => $deleteForm->createView(),
@ -255,6 +243,7 @@ class CalendarController extends AbstractController
*/
public function editAction($id, Request $request): Response
{
$view = null;
$em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request);
@ -283,8 +272,11 @@ class CalendarController extends AbstractController
$this->addFlash('success', $this->get('translator')->trans('Success : calendar item updated!'));
$params = $this->buildParamsToUrl($user, $accompanyingPeriod);
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'));
}
@ -295,7 +287,7 @@ class CalendarController extends AbstractController
}
$entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [
'entity' => $entity,
'form' => $form->createView(),
@ -312,13 +304,14 @@ class CalendarController extends AbstractController
*/
public function deleteAction(Request $request, $id)
{
$view = null;
$em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/confirm_deleteByAccompanyingCourse.html.twig';
}
}
elseif ($user instanceof User) {
$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.
*/
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['id'] = $id;
@ -414,17 +407,14 @@ class CalendarController extends AbstractController
];
}
private function buildParamsToUrl(
?User $user,
?AccompanyingPeriod $accompanyingPeriod
): array {
private function buildParamsToUrl(?User $user, ?AccompanyingPeriod $accompanyingPeriod): array {
$params = [];
if ($user) {
if (null !== $user) {
$params['user_id'] = $user->getId();
}
if ($accompanyingPeriod) {
if (null !== $accompanyingPeriod) {
$params['accompanying_period_id'] = $accompanyingPeriod->getId();
}

View File

@ -1,25 +1,30 @@
<?php
declare(strict_types=1);
namespace Chill\CalendarBundle\DataFixtures\ORM;
use Chill\CalendarBundle\Entity\CalendarRange;
use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\UserRepository;
use DateTimeImmutable;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ObjectManager;
class LoadCalendarRange extends Fixture implements FixtureGroupInterface, OrderedFixtureInterface
{
private UserRepository $userRepository;
public static array $references = [];
public function __construct(
EntityManagerInterface $em
UserRepository $userRepository
) {
$this->userRepository = $em->getRepository(User::class);
$this->userRepository = $userRepository;
}
public function getOrder(): int
@ -32,12 +37,10 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
return ['calendar'];
}
public static $references = [];
public function load(ObjectManager $manager): void
{
$arr = range(-50, 50);
print "Creating calendar range ('plage de disponibilités')\n";
$users = $this->userRepository->findAll();
@ -70,7 +73,7 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
->setUser($u)
->setStartDate($startEvent)
->setEndDate($endEvent);
$manager->persist($calendarRange);
}
@ -79,4 +82,4 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
}
$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_DELETE = 'delete';
/**
* @var CustomFieldProvider
*/
private $customFieldProvider;
/**
* @var EntityManager
*/
private $entityManager;
/**
* @var ValidatorInterface
*/
private $validator;
private $availableLanguages;
private $customizablesEntities;
/**
* CreateFieldsOnGroupCommand constructor.
*
@ -87,7 +87,7 @@ class CreateFieldsOnGroupCommand extends Command
$this->customizablesEntities = $customizablesEntities;
parent::__construct();
}
protected function configure()
{
$this->setName('chill:custom_fields:populate_group')
@ -111,7 +111,7 @@ class CreateFieldsOnGroupCommand extends Command
$em->remove($field);
}
}
/**
* @param InputInterface $input
* @param OutputInterface $output
@ -120,18 +120,18 @@ class CreateFieldsOnGroupCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output)
{
$helper = $this->getHelperSet()->get('question');
$em = $this->entityManager;
$customFieldsGroups = $em
->getRepository('ChillCustomFieldsBundle:CustomFieldsGroup')
->findAll();
if (count($customFieldsGroups) === 0) {
$output->writeln('<error>There aren\'t any CustomFieldsGroup recorded'
. ' Please create at least one.</error>');
}
}
$table = new Table($output);
$table
->setHeaders(array_merge(
@ -141,7 +141,7 @@ class CreateFieldsOnGroupCommand extends Command
->setRows($this->_prepareRows($customFieldsGroups))
->render()
;
$question = new Question(
"Enter the customfieldGroup's id on which the custom fields should be added: ");
$question->setNormalizer(
@ -151,24 +151,23 @@ class CreateFieldsOnGroupCommand extends Command
return $customFieldsGroup;
}
}
throw new \RunTimeException('The id does not match an existing '
. 'CustomFieldsGroup');
throw new \RuntimeException('The id does not match an existing CustomFieldsGroup');
}
);
$customFieldsGroup = $helper->ask($input, $output, $question);
if ($input->getOption(self::ARG_DELETE)) {
$this->deleteFieldsForCFGroup($customFieldsGroup);
}
$fieldsInput = $this->_parse($input->getArgument(self::ARG_PATH),
$fieldsInput = $this->_parse($input->getArgument(self::ARG_PATH),
$output);
$fields = $this->_addFields($customFieldsGroup, $fieldsInput, $output);
}
private function _prepareRows ($customFieldsGroups)
private function _prepareRows ($customFieldsGroups)
{
$rows = array();
$languages = $this->availableLanguages;
@ -177,8 +176,8 @@ class CreateFieldsOnGroupCommand extends Command
foreach ($this->customizablesEntities as $entry) {
$customizableEntities[$entry['class']] = $entry['name'];
}
array_walk($customFieldsGroups,
array_walk($customFieldsGroups,
function(CustomFieldsGroup $customFieldGroup, $key)
use ($languages, &$rows, $customizableEntities) {
//set id and entity
@ -186,7 +185,7 @@ class CreateFieldsOnGroupCommand extends Command
$customFieldGroup->getId(),
$customizableEntities[$customFieldGroup->getEntity()]
);
foreach ($languages as $lang) {
//todo replace with service to find lang when available
$row[] = (isset($customFieldGroup->getName()[$lang])) ?
@ -196,42 +195,42 @@ class CreateFieldsOnGroupCommand extends Command
$rows[] = $row;
}
);
return $rows;
}
private function _parse($path, OutputInterface $output)
{
$parser = new Parser();
if (!file_exists($path)) {
throw new \RunTimeException("file does not exist");
throw new \RuntimeException("file does not exist");
}
try {
$values = $parser->parse(file_get_contents($path));
} 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;
}
private function _addFields(CustomFieldsGroup $group, $values, OutputInterface $output)
{
$em = $this->entityManager;
$languages = $this->availableLanguages;
foreach($values['fields'] as $slug => $field) {
//check the cf type exists
$cfType = $this->customFieldProvider->getCustomFieldByType($field['type']);
if ($cfType === NULL) {
throw new \RunTimeException('the type '.$field['type'].' '
throw new \RuntimeException('the type '.$field['type'].' '
. 'does not exists');
}
$cf = new CustomField();
$cf->setSlug($slug)
->setName($field['name'])
@ -239,7 +238,7 @@ class CreateFieldsOnGroupCommand extends Command
->setOrdering($field['ordering'])
->setType($field['type'])
->setCustomFieldsGroup($group);
//add to table
$names = array();
foreach ($languages as $lang) {
@ -248,17 +247,17 @@ class CreateFieldsOnGroupCommand extends Command
$cf->getName()[$lang] :
'Not available in this language';
}
if ($this->validator->validate($cf)) {
$em->persist($cf);
$output->writeln("<info>Adding Custom Field of type "
.$cf->getType()."\t with slug ".$cf->getSlug().
"\t and names : ".implode($names, ', ')."</info>");
} else {
throw new \RunTimeException("Error in field ".$slug);
throw new \RuntimeException("Error in field ".$slug);
}
}
$em->flush();
}
}

View File

@ -1,21 +1,6 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\CustomFields;
@ -29,36 +14,21 @@ use Symfony\Bridge\Twig\TwigEngine;
use Chill\MainBundle\Form\Type\Select2ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class CustomFieldLongChoice extends AbstractCustomField
{
/**
*
* @var OptionRepository
*/
private $optionRepository;
private OptionRepository $optionRepository;
/**
*
* @var TranslatableStringHelper
*/
private $translatableStringHelper;
private TranslatableStringHelper $translatableStringHelper;
/**
* @var TwigEngine
*/
private $templating;
private TwigEngine $templating;
const KEY = 'key';
public const KEY = 'key';
public function __construct(OptionRepository $optionRepository,
public function __construct(
OptionRepository $optionRepository,
TranslatableStringHelper $translatableStringHelper,
TwigEngine $twigEngine)
{
TwigEngine $twigEngine
) {
$this->optionRepository = $optionRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->templating = $twigEngine;
@ -76,12 +46,7 @@ class CustomFieldLongChoice extends AbstractCustomField
'choice_label' => function(Option $option) use ($translatableStringHelper) {
return $translatableStringHelper->localize($option->getText());
},
'choice_value' => function ($key) use ($entries) {
if ($key === NULL) {
return null;
}
return $key->getId();
},
'choice_value' => static fn (Option $key): ?int => $key === null ? null : $key->getId(),
'multiple' => false,
'expanded' => false,
'required' => $customField->isRequired(),
@ -89,15 +54,16 @@ class CustomFieldLongChoice extends AbstractCustomField
'group_by' => function(Option $option) use ($translatableStringHelper) {
if ($option->hasParent()) {
return $translatableStringHelper->localize($option->getParent()->getText());
} else {
return $translatableStringHelper->localize($option->getText());
}
return $translatableStringHelper->localize($option->getText());
},
'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)

View File

@ -28,27 +28,31 @@ class ChillCustomFieldsExtension extends Extension implements PrependExtensionIn
$loader->load('services/fixtures.yaml');
$loader->load('services/controller.yaml');
$loader->load('services/command.yaml');
//add at least a blank array at 'customizable_entities' options
//$customizable_entities = (isset($config['customizables_entities'])
// && $config['customizables_entities'] !== FALSE)
//$customizable_entities = (isset($config['customizables_entities'])
// && $config['customizables_entities'] !== FALSE)
// ? $config['customizables_entities'] : array();
$container->setParameter('chill_custom_fields.customizables_entities',
$container->setParameter('chill_custom_fields.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']);
}
/* (non-PHPdoc)
* @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend()
*/
public function prepend(ContainerBuilder $container)
public function prepend(ContainerBuilder $container)
{
// 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);
//add routes for custom bundle
$container->prependExtensionConfig('chill_main', array(
'routing' => array(

View File

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

View File

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

View File

@ -1,28 +1,27 @@
<?php
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Form\DataTransformer;
use Chill\CustomFieldsBundle\Entity\CustomField;
use Symfony\Component\Form\DataTransformerInterface;
use Doctrine\Persistence\ObjectManager;
use Doctrine\Common\Collections\ArrayCollection;
class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
/**
* @var ObjectManager
*/
private $om;
private ObjectManager $om;
private array $customField;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
$customFields = $this->om
->getRepository('ChillCustomFieldsBundle:CustomField')
->getRepository(CustomField::class)
->findAll();
// @TODO: in the array_map callback, CustomField::getLabel() does not exist. What do we do here?
$customFieldsLablels = array_map(
function($e) { return $e->getLabel(); },
$customFields);
@ -36,20 +35,12 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
{
echo $customFieldsJSON;
if($customFieldsJSON === null) { // lors de la creation
$customFieldsArray = array();
if($customFieldsJSON === null) {
$customFieldsArray = [];
} 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();
foreach ($customFieldsArray as $key => $value) {
@ -62,7 +53,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
} else {
$entityClass = substr($type, 10, -1);
}
$customFieldsArrayRet[$key] = $this->om
->getRepository('ChillCustomFieldsBundle:' . $entityClass)
->findOneById($value);
@ -86,10 +77,10 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
{
/*
echo "<br> - - 7 - <br>";
var_dump(array_keys($customFieldsArray));
echo "<br> - - 8 - <br>";
var_dump(array_keys($this->customField));
@ -112,7 +103,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
//$entityClass = substr($type, 10, -1);
//echo $entityClasss;
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
@ -121,7 +112,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
// et faire le persist qd fait sur l'obj parent
// 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
// dans yml :
// dans yml :
// lifecycleCallbacks:
// prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
$this->om->flush(); // sinon l'id pose pbm
@ -142,4 +133,4 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
return json_encode($customFieldsArrayRet);
}
}
}

View File

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

View File

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

View File

@ -1,8 +1,10 @@
<?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\Persistence\ManagerRegistry;

View File

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

View File

@ -50,19 +50,19 @@ class LoadRolesACL extends AbstractFixture implements OrderedFixtureInterface
break;
case 'administrative':
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;
}
printf("Adding CHILL_EVENT_UPDATE & CHILL_EVENT_CREATE "
. "& CHILL_EVENT_PARTICIPATION_UPDATE & CHILL_EVENT_PARTICIPATION_CREATE "
. "& CHILL_EVENT_SEE & CHILL_EVENT_SEE_DETAILS "
. "to %s "
. "permission group, scope '%s' \n",
. "permission group, scope '%s' \n",
$permissionsGroup->getName(), $scope->getName()['en']);
$roleScopeUpdate = (new RoleScope())
->setRole('CHILL_EVENT_UPDATE')
->setScope($scope);
@ -71,7 +71,7 @@ class LoadRolesACL extends AbstractFixture implements OrderedFixtureInterface
->setScope($scope);
$permissionsGroup->addRoleScope($roleScopeUpdate);
$permissionsGroup->addRoleScope($roleScopeUpdate2);
$roleScopeCreate = (new RoleScope())
->setRole('CHILL_EVENT_CREATE')
->setScope($scope);
@ -80,7 +80,7 @@ class LoadRolesACL extends AbstractFixture implements OrderedFixtureInterface
->setScope($scope);
$permissionsGroup->addRoleScope($roleScopeCreate);
$permissionsGroup->addRoleScope($roleScopeCreate2);
$roleScopeSee = (new RoleScope())
->setRole('CHILL_EVENT_SEE')
->setScope($scope);
@ -89,7 +89,7 @@ class LoadRolesACL extends AbstractFixture implements OrderedFixtureInterface
->setScope($scope);
$permissionsGroup->addRoleScope($roleScopeSee);
$permissionsGroup->addRoleScope($roleScopeSee2);
$manager->persist($roleScopeUpdate);
$manager->persist($roleScopeUpdate2);
$manager->persist($roleScopeCreate);
@ -97,9 +97,9 @@ class LoadRolesACL extends AbstractFixture implements OrderedFixtureInterface
$manager->persist($roleScopeSee);
$manager->persist($roleScopeSee2);
}
}
$manager->flush();
}

View File

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

View File

@ -1,17 +1,32 @@
<?php
declare(strict_types=1);
namespace Chill\AMLI\FamilyMembersBundle\Repository;
use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember;
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
declare(strict_types=1);
namespace Chill\MainBundle\CRUD\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@ -14,94 +16,77 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Serializer\SerializerInterface;
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']`
*
* @var array
*/
protected array $crudConfig = [];
/**
* 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
*/
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())
->find($id);
if (NULL === $e) {
if (null === $e) {
throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id));
}
return $e;
}
/**
* Create an entity.
*
* @param string $action
* @param Request $request
* @return object
*/
protected function createEntity(string $action, Request $request): object
{
$type = $this->getEntityClass();
return new $type;
$class = $this->getEntityClass();
return new $class;
}
/**
* 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`.
*
* @param string $action
* @param Request $request
* @return int
*/
protected function countEntities(string $action, Request $request, $_format): int
{
return $this->buildQueryEntities($action, $request)
->select('COUNT(e)')
->getQuery()
->getSingleScalarResult()
;
->getSingleScalarResult();
}
/**
* Query the entity.
*
*
* By default, get all entities. You can customize the query by using the
* method `customizeQuery`.
*
*
* The method `orderEntity` is called internally to order entities.
*
*
* It returns, by default, a query builder.
*
*/
protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator)
{
$query = $this->buildQueryEntities($action, $request)
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
->setMaxResults($paginator->getItemsPerPage());
// allow to order queries and return the new query
return $this->orderQuery($action, $query, $request, $paginator, $_format);
}
/**
* Add ordering fields in the query build by self::queryEntities
*
*/
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.
*
* This method is used internally by `countEntities` `queryEntities`
*
*
* This base query does not contains any `WHERE` or `SELECT` clauses. You
* can add some by using the method `customizeQuery`.
*
* The alias for the entity is "e".
*
* @param string $action
* @param Request $request
*
* @return QueryBuilder
*/
protected function buildQueryEntities(string $action, Request $request)
@ -127,8 +110,7 @@ class AbstractCRUDController extends AbstractController
$qb = $this->getDoctrine()->getManager()
->createQueryBuilder()
->select('e')
->from($this->getEntityClass(), 'e')
;
->from($this->getEntityClass(), 'e');
$this->customizeQuery($action, $request, $qb);
@ -138,55 +120,54 @@ class AbstractCRUDController extends AbstractController
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();
}
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;
}
/**
* 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
{
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
{
return null;
}
/**
* Get the complete FQDN of the class
*
* @return string the complete fqdn of the class
* Get the FQDN of the class.
*
* @return class-string The FQDN of the class
*/
protected function getEntityClass(): string
{
return $this->crudConfig['class'];
}
/**
* called on post fetch entity
* Called on post fetch entity.
*/
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
{
@ -203,23 +184,23 @@ class AbstractCRUDController extends AbstractController
/**
* 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.
*
*
* Throw an \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
* if not accessible.
*
*
* @throws \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
*/
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);
}
/**
*
* @return string the crud name
* @return string The crud name.
*/
protected function getCrudName(): string
{
@ -230,7 +211,7 @@ class AbstractCRUDController extends AbstractController
{
return $this->crudConfig['actions'][$action];
}
/**
* Set the crud configuration
*
@ -241,9 +222,6 @@ class AbstractCRUDController extends AbstractController
$this->crudConfig = $config;
}
/**
* @return PaginatorFactory
*/
protected function getPaginatorFactory(): PaginatorFactory
{
return $this->container->get('chill_main.paginator_factory');
@ -254,9 +232,6 @@ class AbstractCRUDController extends AbstractController
return $this->get('validator');
}
/**
* @return array
*/
public static function getSubscribedServices(): array
{
return \array_merge(

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace Chill\MainBundle\CRUD\Controller;
use Symfony\Component\HttpFoundation\Request;
@ -17,36 +19,36 @@ class ApiController extends AbstractCRUDController
{
/**
* The view action.
*
*
* Some steps may be overriden during this process of rendering:
*
*
* This method:
*
*
* 1. fetch the entity, using `getEntity`
* 2. launch `onPostFetchEntity`. If postfetch is an instance of Response,
* this response is returned.
* 2. throw an HttpNotFoundException if entity is null
* 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 ;
* 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
{
$entity = $this->getEntity($action, $id, $request, $_format);
$entity = $this->getEntity($action, $id, $request);
$postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format);
if ($postFetch instanceof Response) {
return $postFetch;
}
$response = $this->checkACL($action, $request, $_format, $entity);
if ($response instanceof Response) {
return $response;
}
$response = $this->onPostCheckACL($action, $request, $_format, $entity);
if ($response instanceof Response) {
return $response;
@ -86,7 +88,7 @@ class ApiController extends AbstractCRUDController
case Request::METHOD_PATCH:
return $this->entityPut('_entity', $request, $id, $_format);
case Request::METHOD_POST:
return $this->entityPostAction('_entity', $request, $id, $_format);
return $this->entityPostAction('_entity', $request, $id);
case Request::METHOD_DELETE:
return $this->entityDelete('_entity', $request, $id, $_format);
default:
@ -112,9 +114,9 @@ class ApiController extends AbstractCRUDController
} catch (NotEncodableValueException $e) {
throw new BadRequestException("invalid json", 400, $e);
}
$errors = $this->validate($action, $request, $_format, $entity);
$response = $this->onAfterValidation($action, $request, $_format, $entity, $errors);
if ($response instanceof Response) {
return $response;
@ -126,12 +128,12 @@ class ApiController extends AbstractCRUDController
return $response;
}
$response = $this->checkACL($action, $request, $_format, $entity);
if ($response instanceof Response) {
return $response;
}
$response = $this->onPostCheckACL($action, $request, $_format, $entity);
if ($response instanceof Response) {
return $response;
@ -148,33 +150,33 @@ class ApiController extends AbstractCRUDController
if ($response instanceof Response) {
return $response;
}
return $this->json(
$entity,
Response::HTTP_OK,
[],
[],
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity)
);
}
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);
if ($postFetch instanceof Response) {
return $postFetch;
}
if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s "
. "is not found", $this->getCrudName(), $id));
}
$response = $this->checkACL($action, $request, $_format, $entity);
if ($response instanceof Response) {
return $response;
}
$response = $this->onPostCheckACL($action, $request, $_format, $entity);
if ($response instanceof Response) {
return $response;
@ -184,7 +186,7 @@ class ApiController extends AbstractCRUDController
if ($response instanceof Response) {
return $response;
}
try {
$entity = $this->deserialize($action, $request, $_format, $entity);
} catch (NotEncodableValueException $e) {
@ -215,13 +217,13 @@ class ApiController extends AbstractCRUDController
return $this->json(
$entity,
Response::HTTP_OK,
[],
[],
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity)
);
}
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) {
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
{
$validationGroups = $this->getValidationGroups($action, $request, $_format, $entity);
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);
}
/**
* Base action for indexing entities
@ -327,11 +329,11 @@ class ApiController extends AbstractCRUDController
/**
* Build an index page.
*
*
* Some steps may be overriden during this process of rendering.
*
*
* This method:
*
*
* 1. Launch `onPreIndex`
* x. check acl. 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. Launch `onPostIndexFetchQuery`. If it does return a response instance, return it
* 4. Serialize the entities in a Collection, using `SerializeCollection`
*
*
* @param string $action
* @param Request $request
* @return type
*/
protected function indexApiAction($action, Request $request, $_format)
{
$this->onPreIndex($action, $request, $_format);
$response = $this->checkACL($action, $request, $_format);
if ($response instanceof Response) {
return $response;
}
if (!isset($entity)) {
$entity = '';
}
$entity = '';
$response = $this->onPostCheckACL($action, $request, $_format, $entity);
if ($response instanceof Response) {
return $response;
}
$totalItems = $this->countEntities($action, $request, $_format);
$paginator = $this->getPaginatorFactory()->create($totalItems);
$response = $this->onPreIndexBuildQuery($action, $request, $_format, $totalItems,
$paginator);
$response = $this->onPreIndexBuildQuery(
$action,
$request,
$_format,
$totalItems,
$paginator
);
if ($response instanceof Response) {
return $response;
}
$query = $this->queryEntities($action, $request, $_format, $paginator);
$response = $this->onPostIndexBuildQuery($action, $request, $_format, $totalItems,
$response = $this->onPostIndexBuildQuery($action, $request, $_format, $totalItems,
$paginator, $query);
if ($response instanceof Response) {
return $response;
}
$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);
if ($response instanceof 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:
*
* 1. Fetch the base entity (throw 404 if not found)
* 2. checkACL,
* 2. checkACL,
* 3. run onPostCheckACL, return response if any,
* 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
@ -410,7 +414,7 @@ class ApiController extends AbstractCRUDController
* 7. run onAfterValidation
* 8. if errors, return a 422 response with errors
* 9. if $forcePersist === true, persist the entity
* 10. flush the data
* 10. flush the data
* 11. run onAfterFlush
* 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 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
{
$entity = $this->getEntity($action, $id, $request);
@ -500,12 +504,14 @@ class ApiController extends AbstractCRUDController
return $this->json(
$postedData,
Response::HTTP_OK,
[],
[],
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity, [$postedData])
);
}
throw new \Exception('Unable to handle such request method.');
}
/**
* Serialize collections
*
@ -518,7 +524,7 @@ class ApiController extends AbstractCRUDController
return $this->json($model, Response::HTTP_OK, [], $context);
}
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)
*
* 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 ".
"method %s nor a global role for the whole action. Add those to your ".
"configuration or override the required method", $request->getMethod()));
}
protected function getSerializer(): SerializerInterface

View File

@ -1,22 +1,4 @@
<?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;
@ -37,60 +19,40 @@ use Chill\MainBundle\Pagination\PaginatorInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Class CRUDController
*
* @package Chill\MainBundle\CRUD\Controller
*/
class CRUDController extends AbstractController
{
/**
* The crud configuration
*
* This configuration si defined by `chill_main['crud']`.
*
* @var array
*/
protected $crudConfig;
protected array $crudConfig;
/**
* @param array $config
*/
public function setCrudConfig(array $config)
public function setCrudConfig(array $config): void
{
$this->crudConfig = $config;
}
/**
* @param $parameter
* @return Response
*/
public function CRUD($parameter)
public function CRUD(?string $parameter): Response
{
return new Response($parameter);
}
/**
* @param Request $request
* @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);
}
/**
* @param string $action
* @param Request $request
* @param $id
* @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);
@ -101,8 +63,13 @@ class CRUDController extends AbstractController
}
if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s "
. "is not found"), $this->getCrudName(), $id);
throw $this->createNotFoundException(
sprintf(
'The %s with id %s is not found',
$this->getCrudName(),
$id
)
);
}
$response = $this->checkACL($action, $entity);
@ -141,7 +108,9 @@ class CRUDController extends AbstractController
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', ['id' => $entity->getId()]);
} elseif ($form->isSubmitted()) {
}
if ($form->isSubmitted()) {
$this->addFlash('error', $this->generateFormErrorMessage($action, $form));
}
@ -233,7 +202,6 @@ class CRUDController extends AbstractController
*
* @param string $action
* @param Request $request
* @return type
*/
protected function indexEntityAction($action, Request $request)
{
@ -244,9 +212,7 @@ class CRUDController extends AbstractController
return $response;
}
if (!isset($entity)) {
$entity = '';
}
$entity = '';
$response = $this->onPostCheckACL($action, $request, $entity);
if ($response instanceof Response) {
@ -342,11 +308,12 @@ class CRUDController extends AbstractController
*/
protected function buildQueryEntities(string $action, Request $request)
{
$query = $this->getDoctrine()->getManager()
$query = $this
->getDoctrine()
->getManager()
->createQueryBuilder()
->select('e')
->from($this->getEntityClass(), 'e')
;
->from($this->getEntityClass(), 'e');
$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)
{
$query = $this->buildQueryEntities($action, $request, $filterOrder)
$query = $this->buildQueryEntities($action, $request)
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
->setMaxResults($paginator->getItemsPerPage());
@ -420,7 +387,7 @@ class CRUDController extends AbstractController
*/
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)')
->getQuery()
->getSingleScalarResult()
@ -505,8 +472,13 @@ class CRUDController extends AbstractController
}
if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s "
. "is not found", $this->getCrudName(), $id));
throw $this->createNotFoundException(
sprintf(
'The %s with id %s is not found',
$this->getCrudName(),
$id
)
);
}
$response = $this->checkACL($action, $entity);
@ -598,8 +570,13 @@ class CRUDController extends AbstractController
$entity = $this->getEntity($action, $id, $request);
if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s "
. "is not found", $this->getCrudName(), $id));
throw $this->createNotFoundException(
sprintf(
'The %s with id %s is not found',
$this->getCrudName(),
$id
)
);
}
$response = $this->checkACL($action, $entity);

View File

@ -1,22 +1,6 @@
<?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/>.
*/
declare(strict_types=1);
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\CRUDController;
/**
* Class CRUDRoutesLoader
* Load the route for CRUD
*
* @package Chill\MainBundle\CRUD\Routing
*/
class CRUDRoutesLoader extends Loader
{
protected array $crudConfig = [];
protected array $apiCrudConfig = [];
/**
* @var bool
*/
private $isLoaded = false;
private bool $isLoaded = false;
private const ALL_SINGLE_METHODS = [
Request::METHOD_GET,
@ -52,19 +27,15 @@ class CRUDRoutesLoader extends Loader
];
private const ALL_INDEX_METHODS = [ Request::METHOD_GET, Request::METHOD_HEAD ];
/**
* CRUDRoutesLoader constructor.
*
* @param $crudConfig the config from cruds
* @param $apicrudConfig the config from api_crud
*/
public function __construct(array $crudConfig, array $apiConfig)
public function __construct(array $crudConfig, array $apiCrudConfig)
{
$this->crudConfig = $crudConfig;
$this->apiConfig = $apiConfig;
$this->apiCrudConfig = $apiCrudConfig;
parent::__construct();
}
/**
* @param mixed $resource
* @param null $type
@ -74,7 +45,7 @@ class CRUDRoutesLoader extends Loader
{
return 'CRUD' === $type;
}
/**
* Load routes for CRUD and CRUD Api
*/
@ -85,17 +56,17 @@ class CRUDRoutesLoader extends Loader
}
$collection = new RouteCollection();
foreach ($this->crudConfig as $crudConfig) {
$collection->addCollection($this->loadCrudConfig($crudConfig));
}
foreach ($this->apiConfig as $crudConfig) {
foreach ($this->apiCrudConfig as $crudConfig) {
$collection->addCollection($this->loadApi($crudConfig));
}
return $collection;
}
/**
* Load routes for CRUD (without api)
*
@ -112,7 +83,7 @@ class CRUDRoutesLoader extends Loader
$defaults = [
'_controller' => $controller.':'.($action['controller_action'] ?? $name)
];
if ($name === 'index') {
$path = "{_locale}".$crudConfig['base_path'];
$route = new Route($path, $defaults);
@ -126,10 +97,10 @@ class CRUDRoutesLoader extends Loader
];
$route = new Route($path, $defaults, $requirements);
}
$collection->add('chill_crud_'.$crudConfig['name'].'_'.$name, $route);
}
return $collection;
}
@ -186,14 +157,14 @@ class CRUDRoutesLoader extends Loader
if (count($methods) === 0) {
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 ".
"or allow, at least, one method");
"or allow, at least, one method");
}
if ('_entity' === $name && \in_array(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);
$collection->add("chill_api_single_{$crudConfig['name']}_{$name}_create",
$collection->add("chill_api_single_{$crudConfig['name']}_{$name}_create",
$entityPostRoute);
}
@ -205,7 +176,7 @@ class CRUDRoutesLoader extends Loader
$route = new Route($path, $defaults, $requirements);
$route->setMethods($methods);
$collection->add('chill_api_single_'.$crudConfig['name'].'_'.$name, $route);
}

View File

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

View File

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

View File

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

View File

@ -1,27 +1,13 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Command;
use Chill\MainBundle\Doctrine\Model\Point;
use Chill\MainBundle\Entity\Country;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@ -31,38 +17,19 @@ use Symfony\Component\Filesystem\Filesystem;
use Chill\MainBundle\Entity\PostalCode;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Class LoadPostalCodesCommand
*
* @package Chill\MainBundle\Command
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class LoadPostalCodesCommand extends Command
{
/**
* @var EntityManager
*/
private $entityManager;
/**
* @var ValidatorInterface
*/
private $validator;
/**
* LoadPostalCodesCommand constructor.
*
* @param EntityManager $entityManager
* @param ValidatorInterface $validator
*/
public function __construct(EntityManager $entityManager, ValidatorInterface $validator)
private EntityManagerInterface $entityManager;
private ValidatorInterface $validator;
public function __construct(EntityManagerInterface $entityManager, ValidatorInterface $validator)
{
$this->entityManager = $entityManager;
$this->validator = $validator;
parent::__construct();
}
protected function configure()
{
$this->setName('chill:main:postal-code:populate')
@ -78,10 +45,10 @@ class LoadPostalCodesCommand extends Command
->addArgument('csv_file', InputArgument::REQUIRED, "the path to "
. "the csv file. See the help for specifications.")
->addOption(
'delimiter',
'd',
InputOption::VALUE_OPTIONAL,
"The delimiter character of the csv file",
'delimiter',
'd',
InputOption::VALUE_OPTIONAL,
"The delimiter character of the csv file",
",")
->addOption(
'enclosure',
@ -99,31 +66,26 @@ class LoadPostalCodesCommand extends Command
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
try {
$csv = $this->getCSVResource($input);
} catch (\RuntimeException $e) {
$output->writeln('<error>Error during opening the csv file : '.
$e->getMessage().'</error>');
}
$csv = $this->getCSVResource($input);
if ($output->getVerbosity() === OutputInterface::VERBOSITY_VERY_VERBOSE) {
$output->writeln('The content of the file is ...');
$output->write(file_get_contents($input->getArgument('csv_file')));
}
$num = 0;
$line = 0;
while (($row = fgetcsv(
$csv,
0,
$input->getOption('delimiter'),
$input->getOption('enclosure'),
$csv,
0,
$input->getOption('delimiter'),
$input->getOption('enclosure'),
$input->getOption('escape'))) !== false) {
try{
$this->addPostalCode($row, $output);
$num++;
@ -136,31 +98,31 @@ class LoadPostalCodesCommand extends Command
}
$line ++;
}
$this->entityManager->flush();
$output->writeln('<info>'.$num.' were added !</info>');
}
private function getCSVResource(InputInterface $input)
{
$fs = new Filesystem();
$filename = $input->getArgument('csv_file');
if (!$fs->exists($filename)) {
throw new \RuntimeException("The file does not exists or you do not "
. "have the right to read it.");
}
$resource = fopen($filename, 'r');
if ($resource == FALSE) {
throw new \RuntimeException("The file '$filename' could not be opened.");
}
return $resource;
}
private function addPostalCode($row, OutputInterface $output)
{
if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
@ -170,28 +132,28 @@ class LoadPostalCodesCommand extends Command
$country = $em
->getRepository(Country::class)
->findOneBy(array('countryCode' => $row[2]));
if ($country === NULL) {
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]));
}
// try to find an existing postal code
$existingPC = $em
->getRepository(PostalCode::class)
->findBy(array('code' => $row[0], 'name' => $row[1]));
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]));
}
$postalCode = (new PostalCode())
->setCode($row[0])
->setName($row[1])
->setCountry($country)
;
if (NULL != $row[3]){
$postalCode->setRefPostalCodeId($row[3]);
}
@ -205,7 +167,7 @@ class LoadPostalCodesCommand extends Command
}
$errors = $this->validator->validate($postalCode);
if ($errors->count() == 0) {
$em->persist($postalCode);
} else {
@ -213,12 +175,12 @@ class LoadPostalCodesCommand extends Command
foreach ($errors as $error) {
$msg .= " ".$error->getMessage();
}
throw new PostalCodeNotValidException($msg);
}
if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
$output->writeln(sprintf('Creating postal code with code: %s, name: %s, countryCode: %s',
$postalCode->getCode(), $postalCode->getName(), $postalCode->getCountry()->getCountryCode()));
@ -229,15 +191,15 @@ class LoadPostalCodesCommand extends Command
class ExistingPostalCodeException extends \Exception
{
}
class CountryCodeNotFoundException extends \Exception
{
}
class PostalCodeNotValidException extends \Exception
{
}

View File

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

View File

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

View File

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

View File

@ -1,35 +1,32 @@
<?php
/*
*/
declare(strict_types=1);
namespace Chill\MainBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Chill\MainBundle\Form\PermissionsGroupType;
use Symfony\Component\DependencyInjection\Reference;
use LogicException;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ACLFlagsCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$permissionGroupType = $container->getDefinition(PermissionsGroupType::class);
foreach($container->findTaggedServiceIds('chill_main.flags') as $id => $tags) {
$reference = new Reference($id);
foreach ($tags as $tag) {
switch($tag['scope']) {
case PermissionsGroupType::FLAG_SCOPE:
$permissionGroupType->addMethodCall('addFlagProvider', [ $reference ]);
break;
default:
throw new \LogicalException(sprintf(
throw new LogicException(sprintf(
"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;
/**
*
* @var ContainerBuilder
*/
private $containerBuilder;
private ContainerBuilder $containerBuilder;
public function __construct(array $widgetFactories = array(),
public function __construct(
array $widgetFactories,
ContainerBuilder $containerBuilder)
{
$this->setWidgetFactories($widgetFactories);
@ -107,6 +104,9 @@ class Configuration implements ConfigurationInterface
->booleanNode('form_show_scopes')
->defaultTrue()
->end()
->booleanNode('form_show_centers')
->defaultTrue()
->end()
->end()
->end()
->arrayNode('redis')

View File

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

View File

@ -1,58 +1,43 @@
<?php
/*
* Chill is a software for social workers
* 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/>.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Doctrine\DQL;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
/**
* 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
*
*/
class OverlapsI extends FunctionNode
{
private $firstPeriodStart;
private $firstPeriodEnd;
private $secondPeriodStart;
private $secondPeriodEnd;
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return '('
.$this->makeCase($sqlWalker, $this->firstPeriodStart, 'start').', '
.$this->makeCase($sqlWalker, $this->firstPeriodEnd, 'end').
') OVERLAPS ('
.$this->makeCase($sqlWalker, $this->secondPeriodStart, 'start').', '
.$this->makeCase($sqlWalker, $this->secondPeriodEnd, 'end').')'
;
return sprintf(
'(%s, %s) OVERLAPS (%s, %s)',
$this->makeCase($sqlWalker, $this->firstPeriodStart, 'start'),
$this->makeCase($sqlWalker, $this->firstPeriodEnd, 'end'),
$this->makeCase($sqlWalker, $this->secondPeriodStart, 'start'),
$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) {
case 'start' :
$p = '-infinity';
@ -60,51 +45,51 @@ class OverlapsI extends FunctionNode
case 'end':
$p = 'infinity';
break;
default:
throw new \Exception('Unexpected position value.');
}
if ($part instanceof \Doctrine\ORM\Query\AST\PathExpression) {
return 'CASE WHEN '
.' '.$part->dispatch($sqlWalker).' IS NOT NULL '
. 'THEN '.
$part->dispatch($sqlWalker)
. ' ELSE '.
"'".$p."'::date "
. 'END';
} else {
return 'CASE WHEN '
.' '.$part->dispatch($sqlWalker).'::date IS NOT NULL '
. 'THEN '.
$part->dispatch($sqlWalker)
. '::date ELSE '.
"'".$p."'::date "
. 'END';
if ($part instanceof PathExpression) {
return sprintf(
"CASE WHEN %s IS NOT NULL THEN %s ELSE '%s'::date END",
$part->dispatch($sqlWalker),
$part->dispatch($sqlWalker),
$p
);
}
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_OPEN_PARENTHESIS);
$this->firstPeriodStart = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$this->firstPeriodEnd = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(Lexer::T_COMMA);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->secondPeriodStart = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$this->secondPeriodEnd = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}

View File

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

View File

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

View File

@ -1,19 +1,19 @@
<?php
/*
*
*
* 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/>.
*/
@ -23,12 +23,11 @@ namespace Chill\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Annotation as Serializer;
/**
* @ORM\Entity
* @ORM\Table(name="centers")
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class Center implements HasCenterInterface
{
@ -46,9 +45,10 @@ class Center implements HasCenterInterface
* @var string
*
* @ORM\Column(type="string", length=255)
* @Serializer\Groups({"docgen:read"})
*/
private $name;
private string $name = '';
/**
* @var Collection
*
@ -58,8 +58,8 @@ class Center implements HasCenterInterface
* )
*/
private $groupCenters;
/**
* Center constructor.
*/
@ -67,7 +67,7 @@ class Center implements HasCenterInterface
{
$this->groupCenters = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* @return string
*/
@ -75,7 +75,7 @@ class Center implements HasCenterInterface
{
return $this->name;
}
/**
* @param $name
* @return $this
@ -85,7 +85,7 @@ class Center implements HasCenterInterface
$this->name = $name;
return $this;
}
/**
* @return int
*/
@ -93,7 +93,7 @@ class Center implements HasCenterInterface
{
return $this->id;
}
/**
* @return ArrayCollection|Collection
*/
@ -101,7 +101,7 @@ class Center implements HasCenterInterface
{
return $this->groupCenters;
}
/**
* @param GroupCenter $groupCenter
* @return $this
@ -111,7 +111,7 @@ class Center implements HasCenterInterface
$this->groupCenters->add($groupCenter);
return $this;
}
/**
* @return string
*/
@ -119,7 +119,7 @@ class Center implements HasCenterInterface
{
return $this->getName();
}
/**
* @return $this|Center
*/

View File

@ -1,22 +1,6 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Entity;
@ -28,38 +12,30 @@ use Doctrine\Common\Collections\ArrayCollection;
* @ORM\Entity
* @ORM\Table(name="role_scopes")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class RoleScope
{
/**
* @var integer
*
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
private ?int $id = null;
/**
* @var string
*
* @ORM\Column(type="string", length=255)
*/
private $role;
private ?string $role = null;
/**
* @var Scope
*
* @ORM\ManyToOne(
* targetEntity="Chill\MainBundle\Entity\Scope",
* inversedBy="roleScopes")
* @ORM\JoinColumn(nullable=true, name="scope_id")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE")
*/
private $scope;
private ?Scope $scope = null;
/**
* @var Collection
*
@ -68,59 +44,37 @@ class RoleScope
* mappedBy="roleScopes")
*/
private $permissionsGroups;
/**
* RoleScope constructor.
*/
public function __construct() {
$this->new = true;
$this->permissionsGroups = new ArrayCollection();
}
/**
* @return int
*/
public function getId()
public function getId(): ?int
{
return $this->id;
}
/**
* @return string
*/
public function getRole()
public function getRole(): ?string
{
return $this->role;
}
/**
* @return Scope
*/
public function getScope()
public function getScope(): ?Scope
{
return $this->scope;
}
/**
* @param type $role
* @return RoleScope
*/
public function setRole($role)
public function setRole(?string $role = null): self
{
$this->role = $role;
return $this;
}
/**
* @param Scope $scope
* @return RoleScope
*/
public function setScope(Scope $scope = null)
public function setScope(?Scope $scope = null): self
{
$this->scope = $scope;
return $this;
}
}

View File

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

View File

@ -1,48 +1,25 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Export\Formatter;
use Chill\MainBundle\Export\ExportInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\Response;
use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Chill\MainBundle\Export\ExportManager;
use Symfony\Component\Form\Extension\Core\Type\FormType;
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
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* 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
* @deprecated this formatter is not used any more.
*/
class CSVFormatter implements FormatterInterface
{
/**
*
* @var TranslatorInterface
*/
protected $translator;
protected TranslatorInterface $translator;
protected $result;
@ -85,11 +62,7 @@ class CSVFormatter implements FormatterInterface
}
/**
*
* @uses appendAggregatorForm
* @param FormBuilderInterface $builder
* @param type $exportAlias
* @param 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
* 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;
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()
{
$line = null;
$rowKeysNb = count($this->getRowHeaders());
$columnKeysNb = count($this->getColumnHeaders());
$resultsKeysNb = count($this->export->getQueryKeys($this->exportData));
@ -196,21 +184,6 @@ class CSVFormatter implements FormatterInterface
$contentData = 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
$output = fopen('php://output', 'w');
@ -244,7 +217,7 @@ class CSVFormatter implements FormatterInterface
// add the column headers
/* @var $columns string[] the column for this row */
$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
for ($i=0; $i < $columnPosition; $i++) {

View File

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

View File

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

View File

@ -1,48 +1,25 @@
<?php
/*
* Copyright (C) 2018 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/>.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Form\Type\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class DateIntervalTransformer implements DataTransformerInterface
{
/**
*
* @param \DateInterval $value
* @throws UnexpectedTypeException
*/
public function transform($value)
{
if (empty($value)) {
return null;
}
if (!$value instanceof \DateInterval) {
throw new UnexpectedTypeException($value, \DateInterval::class);
}
if ($value->d > 0) {
// we check for weeks (weeks are converted to 7 days)
if ($value->d % 7 === 0) {
@ -50,43 +27,49 @@ class DateIntervalTransformer implements DataTransformerInterface
'n' => $value->d / 7,
'unit' => 'W'
];
} else {
return [
'n' => $value->d,
'unit' => 'D'
];
}
} elseif ($value->m > 0) {
return [
'n' => $value->d,
'unit' => 'D'
];
}
if ($value->m > 0) {
return [
'n' => $value->m,
'unit' => 'M'
];
} elseif ($value->y > 0) {
}
if ($value->y > 0) {
return [
'n' => $value->y,
'unit' => 'Y'
];
}
throw new TransformationFailedException('the date interval does not '
. 'contains any days, months or years');
throw new TransformationFailedException(
'The date interval does not contains any days, months or years.'
);
}
public function reverseTransform($value)
{
if (empty($value) or empty($value['n'])) {
if (empty($value) || empty($value['n'])) {
return null;
}
$string = 'P'.$value['n'].$value['unit'];
try {
return new \DateInterval($string);
} catch (\Exception $e) {
throw new TransformationFailedException("Could not transform value "
. "into DateInterval", 1542, $e);
throw new TransformationFailedException(
'Could not transform value into DateInterval',
1542,
$e
);
}
}
}

View File

@ -1,50 +1,23 @@
<?php
/*
* 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/>.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Pagination;
/**
* PageGenerator associated with a Paginator
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* @author Champs Libres <info@champs-libres.coop>
* PageGenerator associated with a Paginator.
*/
class PageGenerator implements \Iterator
{
/**
*
* @var Paginator
*/
protected $paginator;
/**
*
* @var int
*/
protected $current = 1;
public function __construct(Paginator $paginator)
protected Paginator $paginator;
protected int $current = 1;
public function __construct(Paginator $paginator)
{
$this->paginator = $paginator;;
}
public function current()
{
return $this->paginator->getPage($current);
@ -67,7 +40,7 @@ class PageGenerator implements \Iterator
public function valid()
{
return $this->current > 0
return $this->current > 0
&& $this->current <= $this->paginator->countPages();
}
}

View File

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

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