mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch 'upgrade-sf5' into signature-app-master
This commit is contained in:
commit
bb848746d5
@ -1,5 +0,0 @@
|
|||||||
kind: Feature
|
|
||||||
body: '[DX] move async-upload-bundle features into chill-bundles'
|
|
||||||
time: 2023-12-12T15:48:41.954970271+01:00
|
|
||||||
custom:
|
|
||||||
Issue: "221"
|
|
@ -1,6 +0,0 @@
|
|||||||
kind: Feature
|
|
||||||
body: |
|
|
||||||
Upgrade import of address list to the last version of compiled addresses of belgian-best-address
|
|
||||||
time: 2024-05-30T16:00:03.440767606+02:00
|
|
||||||
custom:
|
|
||||||
Issue: ""
|
|
@ -1,6 +0,0 @@
|
|||||||
kind: Feature
|
|
||||||
body: |
|
|
||||||
Upgrade CKEditor and refactor configuration with use of typescript
|
|
||||||
time: 2024-05-31T19:02:42.776662753+02:00
|
|
||||||
custom:
|
|
||||||
Issue: ""
|
|
@ -1,6 +0,0 @@
|
|||||||
kind: Fixed
|
|
||||||
body: Fix resolving of centers for an household, which will fix in turn the access
|
|
||||||
control
|
|
||||||
time: 2024-04-10T10:37:36.462484988+02:00
|
|
||||||
custom:
|
|
||||||
Issue: ""
|
|
6
.changes/v2.22.0.md
Normal file
6
.changes/v2.22.0.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
## v2.22.0 - 2024-06-25
|
||||||
|
### Feature
|
||||||
|
* ([#216](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/216)) [event bundle] exports added for the event module
|
||||||
|
|
||||||
|
### Traduction francophone
|
||||||
|
* Exports sont ajoutés pour la module événement.
|
5
.changes/v2.22.1.md
Normal file
5
.changes/v2.22.1.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
## v2.22.1 - 2024-07-01
|
||||||
|
### Fixed
|
||||||
|
* Remove debug word
|
||||||
|
### DX
|
||||||
|
* Add a command for reading official address DB from Luxembourg and update chill addresses
|
3
.changes/v2.22.2.md
Normal file
3
.changes/v2.22.2.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
## v2.22.2 - 2024-07-03
|
||||||
|
### Fixed
|
||||||
|
* Remove scope required for event participation stats
|
11
.changes/v2.23.0.md
Normal file
11
.changes/v2.23.0.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
## v2.23.0 - 2024-07-23
|
||||||
|
### Feature
|
||||||
|
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
|
||||||
|
* Add job bundle (module emploi)
|
||||||
|
* Upgrade import of address list to the last version of compiled addresses of belgian-best-address
|
||||||
|
|
||||||
|
* Upgrade CKEditor and refactor configuration with use of typescript
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* Fix resolving of centers for an household, which will fix in turn the access control
|
||||||
|
* Resolved type hinting error in activity list export
|
5
.changes/v3.0.0.md
Normal file
5
.changes/v3.0.0.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
## v3.0.0 - 2024-08-26
|
||||||
|
### Fixed
|
||||||
|
* Fix delete action for accompanying periods in draft state
|
||||||
|
* Fix connection to azure when making an calendar event in chill
|
||||||
|
* CollectionType js fixes for remove button and adding multiple entries
|
@ -138,4 +138,4 @@ release:
|
|||||||
- echo "running release_job"
|
- echo "running release_job"
|
||||||
release:
|
release:
|
||||||
tag_name: '$CI_COMMIT_TAG'
|
tag_name: '$CI_COMMIT_TAG'
|
||||||
description: "./.changes/v$CI_COMMIT_TAG.md"
|
description: "./.changes/$CI_COMMIT_TAG.md"
|
||||||
|
35
CHANGELOG.md
35
CHANGELOG.md
@ -6,6 +6,41 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
|||||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||||
|
|
||||||
|
|
||||||
|
## v3.0.0 - 2024-08-26
|
||||||
|
### Fixed
|
||||||
|
* Fix delete action for accompanying periods in draft state
|
||||||
|
* Fix connection to azure when making an calendar event in chill
|
||||||
|
* CollectionType js fixes for remove button and adding multiple entries
|
||||||
|
|
||||||
|
## v2.23.0 - 2024-07-23
|
||||||
|
### Feature
|
||||||
|
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
|
||||||
|
* Add job bundle (module emploi)
|
||||||
|
* Upgrade import of address list to the last version of compiled addresses of belgian-best-address
|
||||||
|
|
||||||
|
* Upgrade CKEditor and refactor configuration with use of typescript
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* Fix resolving of centers for an household, which will fix in turn the access control
|
||||||
|
* Resolved type hinting error in activity list export
|
||||||
|
|
||||||
|
## v2.22.2 - 2024-07-03
|
||||||
|
### Fixed
|
||||||
|
* Remove scope required for event participation stats
|
||||||
|
|
||||||
|
## v2.22.1 - 2024-07-01
|
||||||
|
### Fixed
|
||||||
|
* Remove debug word
|
||||||
|
### DX
|
||||||
|
* Add a command for reading official address DB from Luxembourg and update chill addresses
|
||||||
|
|
||||||
|
## v2.22.0 - 2024-06-25
|
||||||
|
### Feature
|
||||||
|
* ([#216](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/216)) [event bundle] exports added for the event module
|
||||||
|
|
||||||
|
### Traduction francophone
|
||||||
|
* Exports sont ajoutés pour la module événement.
|
||||||
|
|
||||||
## v2.21.0 - 2024-06-18
|
## v2.21.0 - 2024-06-18
|
||||||
### Feature
|
### Feature
|
||||||
* Add flash menu buttons in search results, to open directly a new calendar, or a new activity in an accompanying period
|
* Add flash menu buttons in search results, to open directly a new calendar, or a new activity in an accompanying period
|
||||||
|
@ -115,6 +115,8 @@
|
|||||||
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
|
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
|
||||||
"Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
|
"Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
|
||||||
"Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
|
"Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
|
||||||
|
"Chill\\FranceTravailApiBundle\\": "src/Bundle/ChillFranceTravailApiBundle/src",
|
||||||
|
"Chill\\JobBundle\\": "src/Bundle/ChillJobBundle/src",
|
||||||
"Chill\\MainBundle\\": "src/Bundle/ChillMainBundle",
|
"Chill\\MainBundle\\": "src/Bundle/ChillMainBundle",
|
||||||
"Chill\\PersonBundle\\": "src/Bundle/ChillPersonBundle",
|
"Chill\\PersonBundle\\": "src/Bundle/ChillPersonBundle",
|
||||||
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
|
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
|
||||||
|
@ -21,7 +21,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
|||||||
class BirthdateFilter implements ExportElementValidatedInterface, FilterInterface
|
class BirthdateFilter implements ExportElementValidatedInterface, FilterInterface
|
||||||
{
|
{
|
||||||
// add specific role for this filter
|
// add specific role for this filter
|
||||||
public function addRole()
|
public function addRole(): ?string
|
||||||
{
|
{
|
||||||
// we do not need any new role for this filter, so we return null
|
// we do not need any new role for this filter, so we return null
|
||||||
return null;
|
return null;
|
||||||
|
@ -56,7 +56,7 @@ We strongly encourage you to initialize a git repository at this step, to track
|
|||||||
cat <<< "$(jq '.extra.symfony += {"endpoint": ["flex://defaults", "https://gitlab.com/api/v4/projects/57371968/repository/files/index.json/raw?ref=main"]}' composer.json)" > composer.json
|
cat <<< "$(jq '.extra.symfony += {"endpoint": ["flex://defaults", "https://gitlab.com/api/v4/projects/57371968/repository/files/index.json/raw?ref=main"]}' composer.json)" > composer.json
|
||||||
# install chill and some dependencies
|
# install chill and some dependencies
|
||||||
# TODO fix the suffix "alpha1" and replace by ^3.0.0 when version 3.0.0 will be released
|
# TODO fix the suffix "alpha1" and replace by ^3.0.0 when version 3.0.0 will be released
|
||||||
symfony composer require chill-project/chill-bundles v3.0.0-alpha1 champs-libres/wopi-lib dev-master@dev champs-libres/wopi-bundle dev-master@dev
|
symfony composer require chill-project/chill-bundles v3.0.0-RC3 champs-libres/wopi-lib dev-master@dev champs-libres/wopi-bundle dev-master@dev
|
||||||
|
|
||||||
We encourage you to accept the inclusion of the "Docker configuration from recipes": this is the documented way to run the database.
|
We encourage you to accept the inclusion of the "Docker configuration from recipes": this is the documented way to run the database.
|
||||||
You must also accept to configure recipes from the contrib repository, unless you want to configure the bundles manually).
|
You must also accept to configure recipes from the contrib repository, unless you want to configure the bundles manually).
|
||||||
@ -110,15 +110,14 @@ you can either:
|
|||||||
.. code-block:: env
|
.. code-block:: env
|
||||||
|
|
||||||
ADMIN_PASSWORD=\$2y\$13\$iyvJLuT4YEa6iWXyQV4/N.hNHpNG8kXlYDkkt5MkYy4FXcSwYAwmm
|
ADMIN_PASSWORD=\$2y\$13\$iyvJLuT4YEa6iWXyQV4/N.hNHpNG8kXlYDkkt5MkYy4FXcSwYAwmm
|
||||||
|
# note: if you copy-paste the line above, the password will be "admin".
|
||||||
|
|
||||||
- add the generated password to the secrets manager (**note**: you must add the generated hashed password to the secrets env,
|
- add the generated password to the secrets manager (**note**: you must add the generated hashed password to the secrets env,
|
||||||
not the password in clear text).
|
not the password in clear text).
|
||||||
|
|
||||||
- set up the jwt authentication bundle
|
- set up the jwt authentication bundle
|
||||||
|
|
||||||
Some environment variables are available for the JWT authentication bundle in the :code:`.env` file. You must also run the command
|
Some environment variables are available for the JWT authentication bundle in the :code:`.env` file.
|
||||||
:code:`symfony console lexik:jwt:generate-keypair` to generate some keys that will be stored in the paths set up in the :code:`JWT_SECRET_KEY`
|
|
||||||
and the :code:`JWT_PUBLIC_KEY` env variables. This is only required for using the stored documents in Chill.
|
|
||||||
|
|
||||||
Prepare migrations and other tools
|
Prepare migrations and other tools
|
||||||
**********************************
|
**********************************
|
||||||
@ -136,6 +135,8 @@ To continue the installation process, you will have to run migrations:
|
|||||||
symfony console messenger:setup-transports
|
symfony console messenger:setup-transports
|
||||||
# prepare some views
|
# prepare some views
|
||||||
symfony console chill:db:sync-views
|
symfony console chill:db:sync-views
|
||||||
|
# generate jwt token, required for some api features (webdav access, ...)
|
||||||
|
symfony console lexik:jwt:generate-keypair
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"postcss-loader": "^7.0.2",
|
"postcss-loader": "^7.0.2",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"sass-loader": "^13.0.0",
|
"sass-loader": "^14.0.0",
|
||||||
"select2": "^4.0.13",
|
"select2": "^4.0.13",
|
||||||
"select2-bootstrap-theme": "0.1.0-beta.10",
|
"select2-bootstrap-theme": "0.1.0-beta.10",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
|
@ -1,34 +1,29 @@
|
|||||||
parameters:
|
parameters:
|
||||||
ignoreErrors:
|
ignoreErrors:
|
||||||
|
-
|
||||||
|
message: "#^Foreach overwrites \\$key with its key variable\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
|
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php
|
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
|
message: "#^Property Chill\\\\CustomFieldsBundle\\\\Entity\\\\CustomField\\:\\:\\$required \\(false\\) does not accept bool\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillPersonBundle/Form/PersonType.php
|
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
|
message: "#^Parameter \\#1 \\$user of method Chill\\\\DocStoreBundle\\\\Entity\\\\Document\\:\\:setUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User\\|null, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillMainBundle/Templating/ChillTwigRoutingHelper.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldsGroup.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
|
|
||||||
count: 2
|
count: 2
|
||||||
path: src/Bundle/ChillMainBundle/Repository/NotificationRepository.php
|
path: src/Bundle/ChillDocStoreBundle/Controller/DocumentAccompanyingCourseController.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Foreach overwrites \\$key with its key variable\\.$#"
|
message: "#^Parameter \\#1 \\$user of method Chill\\\\DocStoreBundle\\\\Entity\\\\Document\\:\\:setUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User\\|null, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
|
||||||
count: 1
|
count: 2
|
||||||
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
|
path: src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Variable \\$participation might not be defined\\.$#"
|
message: "#^Variable \\$participation might not be defined\\.$#"
|
||||||
@ -40,6 +35,106 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
|
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Comparison operation \"\\>\" between \\(bool\\|int\\|Redis\\) and 0 results in an error\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/ApiWrapper.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Variable \\$response might not be defined\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/ApiWrapper.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Function GuzzleHttp\\\\Psr7\\\\get not found\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Function GuzzleHttp\\\\Psr7\\\\str not found\\.$#"
|
||||||
|
count: 2
|
||||||
|
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$seconds of function sleep expects int, string given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Unreachable statement \\- code above always terminates\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Controller/CSPersonController.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$interval of method DateTimeImmutable\\:\\:add\\(\\) expects DateInterval, string\\|null given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Entity/Immersion.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$object of static method DateTimeImmutable\\:\\:createFromMutable\\(\\) expects DateTime, DateTimeInterface given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Entity/Immersion.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Property Chill\\\\JobBundle\\\\Entity\\\\Rome\\\\Metier\\:\\:\\$appellations is never read, only written\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Entity/Rome/Metier.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListCSPerson\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Export/ListCSPerson.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Variable \\$f might not be defined\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Export/ListCSPerson.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListFrein\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Export/ListFrein.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListProjetProfessionnel\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Export/ListProjetProfessionnel.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Property Chill\\\\JobBundle\\\\Form\\\\ChoiceLoader\\\\RomeAppellationChoiceLoader\\:\\:\\$appellationRepository \\(Chill\\\\JobBundle\\\\Repository\\\\Rome\\\\AppellationRepository\\) does not accept Doctrine\\\\ORM\\\\EntityRepository\\<Chill\\\\JobBundle\\\\Entity\\\\Rome\\\\Appellation\\>\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Result of && is always false\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Strict comparison using \\=\\=\\= between array\\{\\} and Symfony\\\\Component\\\\Validator\\\\ConstraintViolationListInterface will always evaluate to false\\.$#"
|
||||||
|
count: 2
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Strict comparison using \\=\\=\\= between null and string will always evaluate to false\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Variable \\$metier might not be defined\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$interval of method DateTimeImmutable\\:\\:add\\(\\) expects DateInterval, string\\|null given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Security/Authorization/CSConnectesVoter.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$object of static method DateTimeImmutable\\:\\:createFromMutable\\(\\) expects DateTime, DateTimeInterface given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Security/Authorization/CSConnectesVoter.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Cannot unset offset '_token' on array\\{formatter\\: mixed, export\\: mixed, centers\\: mixed, alias\\: string\\}\\.$#"
|
message: "#^Cannot unset offset '_token' on array\\{formatter\\: mixed, export\\: mixed, centers\\: mixed, alias\\: string\\}\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
@ -65,11 +160,31 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillMainBundle/Form/ChoiceLoader/PostalCodeChoiceLoader.php
|
path: src/Bundle/ChillMainBundle/Form/ChoiceLoader/PostalCodeChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
|
||||||
|
count: 2
|
||||||
|
path: src/Bundle/ChillMainBundle/Repository/NotificationRepository.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:userHasAccessForCenter\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillMainBundle/Templating/ChillTwigRoutingHelper.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
|
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
|
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillPersonBundle/Form/PersonType.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
|
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
22
rector.php
22
rector.php
@ -28,6 +28,9 @@ return static function (RectorConfig $rectorConfig): void {
|
|||||||
|
|
||||||
// register a single rule
|
// register a single rule
|
||||||
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
|
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
|
||||||
|
$rectorConfig->rule(Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromPropertyTypeRector::class);
|
||||||
|
$rectorConfig->rule(Rector\TypeDeclaration\Rector\Class_\MergeDateTimePropertyTypeDeclarationRector::class);
|
||||||
|
$rectorConfig->rule(Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationBasedOnParentClassMethodRector::class);
|
||||||
|
|
||||||
// part of the symfony 54 rules
|
// part of the symfony 54 rules
|
||||||
$rectorConfig->rule(\Rector\Symfony\Symfony53\Rector\StaticPropertyFetch\KernelTestCaseContainerPropertyDeprecationRector::class);
|
$rectorConfig->rule(\Rector\Symfony\Symfony53\Rector\StaticPropertyFetch\KernelTestCaseContainerPropertyDeprecationRector::class);
|
||||||
@ -36,14 +39,14 @@ return static function (RectorConfig $rectorConfig): void {
|
|||||||
|
|
||||||
//define sets of rules
|
//define sets of rules
|
||||||
$rectorConfig->sets([
|
$rectorConfig->sets([
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_50,
|
LevelSetList::UP_TO_PHP_82,
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_50_TYPES,
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_40,
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_51,
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_41,
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_52,
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_42,
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_53,
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_43,
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_54,
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_44,
|
||||||
\Rector\Doctrine\Set\DoctrineSetList::DOCTRINE_CODE_QUALITY,
|
\Rector\Doctrine\Set\DoctrineSetList::DOCTRINE_CODE_QUALITY,
|
||||||
\Rector\Doctrine\Set\DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES,
|
\Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_90,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$rectorConfig->ruleWithConfiguration(\Rector\Php80\Rector\Class_\AnnotationToAttributeRector::class, [
|
$rectorConfig->ruleWithConfiguration(\Rector\Php80\Rector\Class_\AnnotationToAttributeRector::class, [
|
||||||
@ -66,9 +69,8 @@ return static function (RectorConfig $rectorConfig): void {
|
|||||||
|
|
||||||
// skip some path...
|
// skip some path...
|
||||||
$rectorConfig->skip([
|
$rectorConfig->skip([
|
||||||
// we must adapt service definition
|
// waiting for fixing this bug: https://github.com/rectorphp/rector-doctrine/issues/342
|
||||||
\Rector\Symfony\Symfony28\Rector\MethodCall\GetToConstructorInjectionRector::class,
|
\Rector\Doctrine\CodeQuality\Rector\Property\ImproveDoctrineCollectionDocTypeInEntityRector::class,
|
||||||
\Rector\Symfony\Symfony34\Rector\Closure\ContainerGetNameToTypeInTestsRector::class,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [
|
$rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [
|
||||||
|
@ -99,10 +99,10 @@ final class ActivityController extends AbstractController
|
|||||||
|
|
||||||
$form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
|
$form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
|
||||||
|
|
||||||
if (Request::METHOD_DELETE === $request->getMethod()) {
|
if (Request::METHOD_POST === $request->getMethod()) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$this->logger->notice('An activity has been removed', [
|
$this->logger->notice('An activity has been removed', [
|
||||||
'by_user' => $this->getUser()->getUsername(),
|
'by_user' => $this->getUser()->getUsername(),
|
||||||
'activity_id' => $activity->getId(),
|
'activity_id' => $activity->getId(),
|
||||||
@ -640,7 +640,6 @@ final class ActivityController extends AbstractController
|
|||||||
|
|
||||||
return $this->createFormBuilder()
|
return $this->createFormBuilder()
|
||||||
->setAction($this->generateUrl('chill_activity_activity_delete', $params))
|
->setAction($this->generateUrl('chill_activity_activity_delete', $params))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
|
|||||||
private \DateTime $date;
|
private \DateTime $date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<StoredObject>
|
* @var Collection<int, StoredObject>
|
||||||
*/
|
*/
|
||||||
#[Assert\Valid(traverse: true)]
|
#[Assert\Valid(traverse: true)]
|
||||||
#[ORM\ManyToMany(targetEntity: StoredObject::class, cascade: ['persist'])]
|
#[ORM\ManyToMany(targetEntity: StoredObject::class, cascade: ['persist'])]
|
||||||
@ -107,7 +107,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
|
|||||||
private ?Person $person = null;
|
private ?Person $person = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<Person>
|
* @var Collection<int, \Chill\PersonBundle\Entity\Person>
|
||||||
*/
|
*/
|
||||||
#[Groups(['read', 'docgen:read'])]
|
#[Groups(['read', 'docgen:read'])]
|
||||||
#[ORM\ManyToMany(targetEntity: Person::class)]
|
#[ORM\ManyToMany(targetEntity: Person::class)]
|
||||||
@ -117,7 +117,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
|
|||||||
private PrivateCommentEmbeddable $privateComment;
|
private PrivateCommentEmbeddable $privateComment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<ActivityReason>
|
* @var Collection<int, ActivityReason>
|
||||||
*/
|
*/
|
||||||
#[Groups(['docgen:read'])]
|
#[Groups(['docgen:read'])]
|
||||||
#[ORM\ManyToMany(targetEntity: ActivityReason::class)]
|
#[ORM\ManyToMany(targetEntity: ActivityReason::class)]
|
||||||
@ -132,7 +132,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
|
|||||||
private string $sentReceived = '';
|
private string $sentReceived = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<SocialAction>
|
* @var Collection<int, \Chill\PersonBundle\Entity\SocialWork\SocialAction>
|
||||||
*/
|
*/
|
||||||
#[Groups(['read', 'docgen:read'])]
|
#[Groups(['read', 'docgen:read'])]
|
||||||
#[ORM\ManyToMany(targetEntity: SocialAction::class)]
|
#[ORM\ManyToMany(targetEntity: SocialAction::class)]
|
||||||
@ -140,7 +140,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
|
|||||||
private Collection $socialActions;
|
private Collection $socialActions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<SocialIssue>
|
* @var Collection<int, SocialIssue>
|
||||||
*/
|
*/
|
||||||
#[Groups(['read', 'docgen:read'])]
|
#[Groups(['read', 'docgen:read'])]
|
||||||
#[ORM\ManyToMany(targetEntity: SocialIssue::class)]
|
#[ORM\ManyToMany(targetEntity: SocialIssue::class)]
|
||||||
@ -148,7 +148,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
|
|||||||
private Collection $socialIssues;
|
private Collection $socialIssues;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<ThirdParty>
|
* @var Collection<int, ThirdParty>
|
||||||
*/
|
*/
|
||||||
#[Groups(['read', 'docgen:read'])]
|
#[Groups(['read', 'docgen:read'])]
|
||||||
#[ORM\ManyToMany(targetEntity: ThirdParty::class)]
|
#[ORM\ManyToMany(targetEntity: ThirdParty::class)]
|
||||||
@ -162,7 +162,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
|
|||||||
private ?User $user = null;
|
private ?User $user = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<User>
|
* @var Collection<int, User>
|
||||||
*/
|
*/
|
||||||
#[Groups(['read', 'docgen:read'])]
|
#[Groups(['read', 'docgen:read'])]
|
||||||
#[ORM\ManyToMany(targetEntity: User::class)]
|
#[ORM\ManyToMany(targetEntity: User::class)]
|
||||||
|
@ -79,11 +79,9 @@ class ActivityReason
|
|||||||
/**
|
/**
|
||||||
* Set active.
|
* Set active.
|
||||||
*
|
*
|
||||||
* @param bool $active
|
|
||||||
*
|
|
||||||
* @return ActivityReason
|
* @return ActivityReason
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
@ -110,11 +108,9 @@ class ActivityReason
|
|||||||
/**
|
/**
|
||||||
* Set name.
|
* Set name.
|
||||||
*
|
*
|
||||||
* @param array $name
|
|
||||||
*
|
|
||||||
* @return ActivityReason
|
* @return ActivityReason
|
||||||
*/
|
*/
|
||||||
public function setName($name)
|
public function setName(array $name)
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
|
|
||||||
|
@ -40,9 +40,9 @@ class ActivityReasonCategory implements \Stringable
|
|||||||
/**
|
/**
|
||||||
* Array of ActivityReason.
|
* Array of ActivityReason.
|
||||||
*
|
*
|
||||||
* @var Collection<ActivityReason>
|
* @var Collection<int, ActivityReason>
|
||||||
*/
|
*/
|
||||||
#[ORM\OneToMany(targetEntity: ActivityReason::class, mappedBy: 'category')]
|
#[ORM\OneToMany(mappedBy: 'category', targetEntity: ActivityReason::class)]
|
||||||
private Collection $reasons;
|
private Collection $reasons;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -152,7 +152,7 @@ class ListActivityHelper
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->translator->trans($value);
|
return $this->translator->trans((string) $value);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInt
|
|||||||
|
|
||||||
$qb->andWhere(
|
$qb->andWhere(
|
||||||
$qb->expr()->exists(
|
$qb->expr()->exists(
|
||||||
'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = acp"
|
'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = activity.accompanyingPeriod"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -15,9 +15,9 @@ use Chill\ActivityBundle\Entity\ActivityType;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
final class ActivityTypeRepository implements ActivityTypeRepositoryInterface
|
final readonly class ActivityTypeRepository implements ActivityTypeRepositoryInterface
|
||||||
{
|
{
|
||||||
private readonly EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $em)
|
public function __construct(EntityManagerInterface $em)
|
||||||
{
|
{
|
||||||
|
@ -87,7 +87,6 @@
|
|||||||
<li>
|
<li>
|
||||||
{% if bloc.type == 'user' %}
|
{% if bloc.type == 'user' %}
|
||||||
<span class="badge-user">
|
<span class="badge-user">
|
||||||
hello
|
|
||||||
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false, 'at_date': entity.date }) }}
|
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false, 'at_date': entity.date }) }}
|
||||||
</span>
|
</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -22,9 +22,9 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
|||||||
class AsideActivityCategory
|
class AsideActivityCategory
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Collection<AsideActivityCategory>
|
* @var Collection<int, AsideActivityCategory>
|
||||||
*/
|
*/
|
||||||
#[ORM\OneToMany(targetEntity: AsideActivityCategory::class, mappedBy: 'parent')]
|
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: AsideActivityCategory::class)]
|
||||||
private Collection $children;
|
private Collection $children;
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
|
@ -54,7 +54,7 @@ abstract class AbstractElementController extends AbstractController
|
|||||||
$indexPage = 'chill_budget_elements_household_index';
|
$indexPage = 'chill_budget_elements_household_index';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Request::METHOD_DELETE === $request->getMethod()) {
|
if (Request::METHOD_POST === $request->getMethod()) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
@ -198,10 +198,9 @@ abstract class AbstractElementController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Creates a form to delete a help request entity by id.
|
* Creates a form to delete a help request entity by id.
|
||||||
*/
|
*/
|
||||||
private function createDeleteForm(): Form
|
private function createDeleteForm(): \Symfony\Component\Form\FormInterface
|
||||||
{
|
{
|
||||||
return $this->createFormBuilder()
|
return $this->createFormBuilder()
|
||||||
->setMethod(Request::METHOD_DELETE)
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ class Charge extends AbstractElement implements HasCentersInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setHelp($help)
|
public function setHelp(?string $help)
|
||||||
{
|
{
|
||||||
$this->help = $help;
|
$this->help = $help;
|
||||||
|
|
||||||
|
@ -15,9 +15,9 @@ use Chill\BudgetBundle\Entity\ChargeKind;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
final class ChargeKindRepository implements ChargeKindRepositoryInterface
|
final readonly class ChargeKindRepository implements ChargeKindRepositoryInterface
|
||||||
{
|
{
|
||||||
private readonly EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $entityManager)
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
{
|
{
|
||||||
|
@ -15,9 +15,9 @@ use Chill\BudgetBundle\Entity\ResourceKind;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
final class ResourceKindRepository implements ResourceKindRepositoryInterface
|
final readonly class ResourceKindRepository implements ResourceKindRepositoryInterface
|
||||||
{
|
{
|
||||||
private readonly EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $entityManager)
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
{
|
{
|
||||||
|
@ -84,7 +84,7 @@ class CalendarController extends AbstractController
|
|||||||
|
|
||||||
$form = $this->createDeleteForm($entity);
|
$form = $this->createDeleteForm($entity);
|
||||||
|
|
||||||
if (Request::METHOD_DELETE === $request->getMethod()) {
|
if (Request::METHOD_POST === $request->getMethod()) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isValid()) {
|
if ($form->isValid()) {
|
||||||
@ -512,7 +512,6 @@ class CalendarController extends AbstractController
|
|||||||
{
|
{
|
||||||
return $this->createFormBuilder()
|
return $this->createFormBuilder()
|
||||||
->setAction($this->generateUrl('chill_calendar_calendar_delete', ['id' => $calendar->getId()]))
|
->setAction($this->generateUrl('chill_calendar_calendar_delete', ['id' => $calendar->getId()]))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
|
|||||||
private int $dateTimeVersion = 0;
|
private int $dateTimeVersion = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<CalendarDoc>
|
* @var Collection<int, \Chill\CalendarBundle\Entity\CalendarDoc>
|
||||||
*/
|
*/
|
||||||
#[ORM\OneToMany(mappedBy: 'calendar', targetEntity: CalendarDoc::class, orphanRemoval: true)]
|
#[ORM\OneToMany(mappedBy: 'calendar', targetEntity: CalendarDoc::class, orphanRemoval: true)]
|
||||||
private Collection $documents;
|
private Collection $documents;
|
||||||
@ -120,7 +120,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
|
|||||||
private ?int $id = null;
|
private ?int $id = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection&Selectable<int, Invite>
|
* @var \Doctrine\Common\Collections\Collection<int, \Chill\CalendarBundle\Entity\Invite>&Selectable
|
||||||
*/
|
*/
|
||||||
#[Serializer\Groups(['read', 'docgen:read'])]
|
#[Serializer\Groups(['read', 'docgen:read'])]
|
||||||
#[ORM\OneToMany(mappedBy: 'calendar', targetEntity: Invite::class, cascade: ['persist', 'remove', 'merge', 'detach'], orphanRemoval: true)]
|
#[ORM\OneToMany(mappedBy: 'calendar', targetEntity: Invite::class, cascade: ['persist', 'remove', 'merge', 'detach'], orphanRemoval: true)]
|
||||||
@ -143,7 +143,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
|
|||||||
private ?Person $person = null;
|
private ?Person $person = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<Person>
|
* @var Collection<int, Person>
|
||||||
*/
|
*/
|
||||||
#[Serializer\Groups(['calendar:read', 'read', 'calendar:light', 'docgen:read'])]
|
#[Serializer\Groups(['calendar:read', 'read', 'calendar:light', 'docgen:read'])]
|
||||||
#[Assert\Count(min: 1, minMessage: 'calendar.At least {{ limit }} person is required.')]
|
#[Assert\Count(min: 1, minMessage: 'calendar.At least {{ limit }} person is required.')]
|
||||||
@ -157,7 +157,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
|
|||||||
private PrivateCommentEmbeddable $privateComment;
|
private PrivateCommentEmbeddable $privateComment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<ThirdParty>
|
* @var Collection<int, ThirdParty>
|
||||||
*/
|
*/
|
||||||
#[Serializer\Groups(['calendar:read', 'read', 'calendar:light', 'docgen:read'])]
|
#[Serializer\Groups(['calendar:read', 'read', 'calendar:light', 'docgen:read'])]
|
||||||
#[ORM\ManyToMany(targetEntity: ThirdParty::class)]
|
#[ORM\ManyToMany(targetEntity: ThirdParty::class)]
|
||||||
|
@ -47,7 +47,7 @@ final class CalendarContextTest extends TestCase
|
|||||||
{
|
{
|
||||||
$expected =
|
$expected =
|
||||||
[
|
[
|
||||||
'track_datetime' => true,
|
'trackDatetime' => true,
|
||||||
'askMainPerson' => true,
|
'askMainPerson' => true,
|
||||||
'mainPersonLabel' => 'docgen.calendar.Destinee',
|
'mainPersonLabel' => 'docgen.calendar.Destinee',
|
||||||
'askThirdParty' => false,
|
'askThirdParty' => false,
|
||||||
@ -61,7 +61,7 @@ final class CalendarContextTest extends TestCase
|
|||||||
{
|
{
|
||||||
$expected =
|
$expected =
|
||||||
[
|
[
|
||||||
'track_datetime' => true,
|
'trackDatetime' => true,
|
||||||
'askMainPerson' => true,
|
'askMainPerson' => true,
|
||||||
'mainPersonLabel' => 'docgen.calendar.Destinee',
|
'mainPersonLabel' => 'docgen.calendar.Destinee',
|
||||||
'askThirdParty' => false,
|
'askThirdParty' => false,
|
||||||
|
@ -172,11 +172,9 @@ class CustomField
|
|||||||
/**
|
/**
|
||||||
* Set active.
|
* Set active.
|
||||||
*
|
*
|
||||||
* @param bool $active
|
|
||||||
*
|
|
||||||
* @return CustomField
|
* @return CustomField
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
@ -224,18 +222,16 @@ class CustomField
|
|||||||
/**
|
/**
|
||||||
* Set order.
|
* Set order.
|
||||||
*
|
*
|
||||||
* @param float $order
|
|
||||||
*
|
|
||||||
* @return CustomField
|
* @return CustomField
|
||||||
*/
|
*/
|
||||||
public function setOrdering($order)
|
public function setOrdering(?float $order)
|
||||||
{
|
{
|
||||||
$this->ordering = $order;
|
$this->ordering = $order;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRequired($required)
|
public function setRequired(bool $required)
|
||||||
{
|
{
|
||||||
$this->required = $required;
|
$this->required = $required;
|
||||||
|
|
||||||
@ -245,7 +241,7 @@ class CustomField
|
|||||||
/**
|
/**
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setSlug($slug)
|
public function setSlug(?string $slug)
|
||||||
{
|
{
|
||||||
$this->slug = $slug;
|
$this->slug = $slug;
|
||||||
|
|
||||||
@ -255,11 +251,9 @@ class CustomField
|
|||||||
/**
|
/**
|
||||||
* Set type.
|
* Set type.
|
||||||
*
|
*
|
||||||
* @param string $type
|
|
||||||
*
|
|
||||||
* @return CustomField
|
* @return CustomField
|
||||||
*/
|
*/
|
||||||
public function setType($type)
|
public function setType(?string $type)
|
||||||
{
|
{
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@ class Option
|
|||||||
private bool $active = true;
|
private bool $active = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<Option>
|
* @var Collection<int, Option>
|
||||||
*/
|
*/
|
||||||
#[ORM\OneToMany(targetEntity: Option::class, mappedBy: 'parent')]
|
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: Option::class)]
|
||||||
private Collection $children;
|
private Collection $children;
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
@ -129,7 +129,7 @@ class Option
|
|||||||
/**
|
/**
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ class Option
|
|||||||
/**
|
/**
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setInternalKey($internal_key)
|
public function setInternalKey(string $internal_key)
|
||||||
{
|
{
|
||||||
$this->internalKey = $internal_key;
|
$this->internalKey = $internal_key;
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ class Option
|
|||||||
/**
|
/**
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setKey($key)
|
public function setKey(?string $key)
|
||||||
{
|
{
|
||||||
$this->key = $key;
|
$this->key = $key;
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ class CustomFieldsDefaultGroup
|
|||||||
*
|
*
|
||||||
* @return CustomFieldsDefaultGroup
|
* @return CustomFieldsDefaultGroup
|
||||||
*/
|
*/
|
||||||
public function setCustomFieldsGroup($customFieldsGroup)
|
public function setCustomFieldsGroup(?CustomFieldsGroup $customFieldsGroup)
|
||||||
{
|
{
|
||||||
$this->customFieldsGroup = $customFieldsGroup;
|
$this->customFieldsGroup = $customFieldsGroup;
|
||||||
|
|
||||||
@ -79,11 +79,9 @@ class CustomFieldsDefaultGroup
|
|||||||
/**
|
/**
|
||||||
* Set entity.
|
* Set entity.
|
||||||
*
|
*
|
||||||
* @param string $entity
|
|
||||||
*
|
|
||||||
* @return CustomFieldsDefaultGroup
|
* @return CustomFieldsDefaultGroup
|
||||||
*/
|
*/
|
||||||
public function setEntity($entity)
|
public function setEntity(?string $entity)
|
||||||
{
|
{
|
||||||
$this->entity = $entity;
|
$this->entity = $entity;
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@ class CustomFieldsGroup
|
|||||||
* The custom fields of the group.
|
* The custom fields of the group.
|
||||||
* The custom fields are asc-ordered regarding to their property "ordering".
|
* The custom fields are asc-ordered regarding to their property "ordering".
|
||||||
*
|
*
|
||||||
* @var Collection<CustomField>
|
* @var Collection<int, CustomField>
|
||||||
*/
|
*/
|
||||||
#[ORM\OneToMany(targetEntity: CustomField::class, mappedBy: 'customFieldGroup')]
|
#[ORM\OneToMany(mappedBy: 'customFieldGroup', targetEntity: CustomField::class)]
|
||||||
#[ORM\OrderBy(['ordering' => \Doctrine\Common\Collections\Criteria::ASC])]
|
#[ORM\OrderBy(['ordering' => \Doctrine\Common\Collections\Criteria::ASC])]
|
||||||
private Collection $customFields;
|
private Collection $customFields;
|
||||||
|
|
||||||
@ -165,11 +165,9 @@ class CustomFieldsGroup
|
|||||||
/**
|
/**
|
||||||
* Set entity.
|
* Set entity.
|
||||||
*
|
*
|
||||||
* @param string $entity
|
|
||||||
*
|
|
||||||
* @return CustomFieldsGroup
|
* @return CustomFieldsGroup
|
||||||
*/
|
*/
|
||||||
public function setEntity($entity)
|
public function setEntity(?string $entity)
|
||||||
{
|
{
|
||||||
$this->entity = $entity;
|
$this->entity = $entity;
|
||||||
|
|
||||||
|
@ -21,14 +21,14 @@ use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
|
|||||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
final class RelatorioDriver implements DriverInterface
|
final readonly class RelatorioDriver implements DriverInterface
|
||||||
{
|
{
|
||||||
private readonly string $url;
|
private string $url;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly HttpClientInterface $client,
|
private HttpClientInterface $client,
|
||||||
ParameterBagInterface $parameterBag,
|
ParameterBagInterface $parameterBag,
|
||||||
private readonly LoggerInterface $logger
|
private LoggerInterface $logger
|
||||||
) {
|
) {
|
||||||
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
|
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,11 @@ use Doctrine\ORM\EntityManagerInterface;
|
|||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
final class DocGeneratorTemplateRepository implements DocGeneratorTemplateRepositoryInterface
|
final readonly class DocGeneratorTemplateRepository implements DocGeneratorTemplateRepositoryInterface
|
||||||
{
|
{
|
||||||
private readonly EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $entityManager, private readonly RequestStack $requestStack)
|
public function __construct(EntityManagerInterface $entityManager, private RequestStack $requestStack)
|
||||||
{
|
{
|
||||||
$this->repository = $entityManager->getRepository(DocGeneratorTemplate::class);
|
$this->repository = $entityManager->getRepository(DocGeneratorTemplate::class);
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ class Document implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUser($user): self
|
public function setUser(?\Chill\MainBundle\Entity\User $user): self
|
||||||
{
|
{
|
||||||
$this->user = $user;
|
$this->user = $user;
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class DocumentCategory
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setDocumentClass($documentClass): self
|
public function setDocumentClass(?string $documentClass): self
|
||||||
{
|
{
|
||||||
$this->documentClass = $documentClass;
|
$this->documentClass = $documentClass;
|
||||||
|
|
||||||
|
@ -55,14 +55,14 @@ class PersonDocument extends Document implements HasCenterInterface, HasScopeInt
|
|||||||
return $this->scope;
|
return $this->scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPerson($person): self
|
public function setPerson(Person $person): self
|
||||||
{
|
{
|
||||||
$this->person = $person;
|
$this->person = $person;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setScope($scope): self
|
public function setScope(?Scope $scope): self
|
||||||
{
|
{
|
||||||
$this->scope = $scope;
|
$this->scope = $scope;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use Chill\EventBundle\Entity\Event;
|
|||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
use Chill\EventBundle\Form\EventType;
|
use Chill\EventBundle\Form\EventType;
|
||||||
use Chill\EventBundle\Form\Type\PickEventType;
|
use Chill\EventBundle\Form\Type\PickEventType;
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Entity\Center;
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
@ -61,7 +61,7 @@ final class EventController extends AbstractController
|
|||||||
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
|
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/delete', name: 'chill_event__event_delete', requirements: ['event_id' => '\d+'], methods: ['GET', 'DELETE'])]
|
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/delete', name: 'chill_event__event_delete', requirements: ['event_id' => '\d+'], methods: ['GET', 'POST', 'DELETE'])]
|
||||||
public function deleteAction($event_id, Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|Response
|
public function deleteAction($event_id, Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|Response
|
||||||
{
|
{
|
||||||
$em = $this->managerRegistry->getManager();
|
$em = $this->managerRegistry->getManager();
|
||||||
@ -78,10 +78,10 @@ final class EventController extends AbstractController
|
|||||||
|
|
||||||
$form = $this->createDeleteForm($event_id);
|
$form = $this->createDeleteForm($event_id);
|
||||||
|
|
||||||
if (Request::METHOD_DELETE === $request->getMethod()) {
|
if (Request::METHOD_POST === $request->getMethod()) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
foreach ($participations as $participation) {
|
foreach ($participations as $participation) {
|
||||||
$em->remove($participation);
|
$em->remove($participation);
|
||||||
}
|
}
|
||||||
@ -108,28 +108,6 @@ final class EventController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays a form to edit an existing Event entity.
|
|
||||||
*/
|
|
||||||
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/edit', name: 'chill_event__event_edit')]
|
|
||||||
public function editAction($event_id): Response
|
|
||||||
{
|
|
||||||
$em = $this->managerRegistry->getManager();
|
|
||||||
|
|
||||||
$entity = $em->getRepository(Event::class)->find($event_id);
|
|
||||||
|
|
||||||
if (!$entity) {
|
|
||||||
throw $this->createNotFoundException('Unable to find Event entity.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$editForm = $this->createEditForm($entity);
|
|
||||||
|
|
||||||
return $this->render('@ChillEvent/Event/edit.html.twig', [
|
|
||||||
'entity' => $entity,
|
|
||||||
'edit_form' => $editForm->createView(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List events subscriptions for a person.
|
* List events subscriptions for a person.
|
||||||
*
|
*
|
||||||
@ -313,7 +291,7 @@ final class EventController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Edits an existing Event entity.
|
* Edits an existing Event entity.
|
||||||
*/
|
*/
|
||||||
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/update', name: 'chill_event__event_update', methods: ['POST', 'PUT'])]
|
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/edit', name: 'chill_event__event_edit', methods: ['GET', 'POST', 'PUT'])]
|
||||||
public function updateAction(Request $request, $event_id): \Symfony\Component\HttpFoundation\RedirectResponse|Response
|
public function updateAction(Request $request, $event_id): \Symfony\Component\HttpFoundation\RedirectResponse|Response
|
||||||
{
|
{
|
||||||
$em = $this->managerRegistry->getManager();
|
$em = $this->managerRegistry->getManager();
|
||||||
@ -324,14 +302,20 @@ final class EventController extends AbstractController
|
|||||||
throw $this->createNotFoundException('Unable to find Event entity.');
|
throw $this->createNotFoundException('Unable to find Event entity.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$editForm = $this->createEditForm($entity);
|
$editForm = $this->createForm(EventType::class, $entity, [
|
||||||
|
'center' => $entity->getCenter(),
|
||||||
|
'role' => EventVoter::UPDATE,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$editForm->add('submit', SubmitType::class, ['label' => 'Update']);
|
||||||
|
|
||||||
$editForm->handleRequest($request);
|
$editForm->handleRequest($request);
|
||||||
|
|
||||||
if ($editForm->isValid()) {
|
if ($editForm->isSubmitted() && $editForm->isValid()) {
|
||||||
|
$em->persist($entity);
|
||||||
$em->flush();
|
$em->flush();
|
||||||
|
|
||||||
$this->addFlash('success', $this->translator
|
$this->addFlash('success', $this->translator->trans('The event was updated'));
|
||||||
->trans('The event was updated'));
|
|
||||||
|
|
||||||
return $this->redirectToRoute('chill_event__event_show', ['event_id' => $event_id]);
|
return $this->redirectToRoute('chill_event__event_show', ['event_id' => $event_id]);
|
||||||
}
|
}
|
||||||
@ -418,7 +402,6 @@ final class EventController extends AbstractController
|
|||||||
$builder->add('event_id', HiddenType::class, [
|
$builder->add('event_id', HiddenType::class, [
|
||||||
'data' => $event->getId(),
|
'data' => $event->getId(),
|
||||||
]);
|
]);
|
||||||
dump($event->getId());
|
|
||||||
|
|
||||||
return $builder->getForm();
|
return $builder->getForm();
|
||||||
}
|
}
|
||||||
@ -600,29 +583,7 @@ final class EventController extends AbstractController
|
|||||||
->setAction($this->generateUrl('chill_event__event_delete', [
|
->setAction($this->generateUrl('chill_event__event_delete', [
|
||||||
'event_id' => $event_id,
|
'event_id' => $event_id,
|
||||||
]))
|
]))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a form to edit a Event entity.
|
|
||||||
*
|
|
||||||
* @return \Symfony\Component\Form\FormInterface
|
|
||||||
*/
|
|
||||||
private function createEditForm(Event $entity)
|
|
||||||
{
|
|
||||||
$form = $this->createForm(EventType::class, $entity, [
|
|
||||||
'action' => $this->generateUrl('chill_event__event_update', ['event_id' => $entity->getId()]),
|
|
||||||
'method' => 'PUT',
|
|
||||||
'center' => $entity->getCenter(),
|
|
||||||
'role' => 'CHILL_EVENT_CREATE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$form->remove('center');
|
|
||||||
|
|
||||||
$form->add('submit', SubmitType::class, ['label' => 'Update']);
|
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,7 @@ class EventTypeController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Creates a form to delete a EventType entity by id.
|
* Creates a form to delete a EventType entity by id.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\Form\Form The form
|
* @return \Symfony\Component\Form\FormInterface The form
|
||||||
*/
|
*/
|
||||||
private function createDeleteForm(mixed $id)
|
private function createDeleteForm(mixed $id)
|
||||||
{
|
{
|
||||||
@ -210,7 +210,6 @@ class EventTypeController extends AbstractController
|
|||||||
'chill_eventtype_admin_delete',
|
'chill_eventtype_admin_delete',
|
||||||
['id' => $id]
|
['id' => $id]
|
||||||
))
|
))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ use Chill\EventBundle\Entity\Event;
|
|||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
use Chill\EventBundle\Form\ParticipationType;
|
use Chill\EventBundle\Form\ParticipationType;
|
||||||
use Chill\EventBundle\Repository\EventRepository;
|
use Chill\EventBundle\Repository\EventRepository;
|
||||||
use Chill\EventBundle\Security\Authorization\ParticipationVoter;
|
use Chill\EventBundle\Security\ParticipationVoter;
|
||||||
use Chill\PersonBundle\Repository\PersonRepository;
|
use Chill\PersonBundle\Repository\PersonRepository;
|
||||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
@ -259,10 +259,10 @@ final class ParticipationController extends AbstractController
|
|||||||
|
|
||||||
$form = $this->createDeleteForm($participation_id);
|
$form = $this->createDeleteForm($participation_id);
|
||||||
|
|
||||||
if (Request::METHOD_DELETE === $request->getMethod()) {
|
if (Request::METHOD_POST === $request->getMethod()) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$em->remove($participation);
|
$em->remove($participation);
|
||||||
$em->flush();
|
$em->flush();
|
||||||
|
|
||||||
@ -753,7 +753,6 @@ final class ParticipationController extends AbstractController
|
|||||||
->setAction($this->generateUrl('chill_event_participation_delete', [
|
->setAction($this->generateUrl('chill_event_participation_delete', [
|
||||||
'participation_id' => $participation_id,
|
'participation_id' => $participation_id,
|
||||||
]))
|
]))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,7 @@ class RoleController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Creates a form to delete a Role entity by id.
|
* Creates a form to delete a Role entity by id.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\Form\Form The form
|
* @return \Symfony\Component\Form\FormInterface The form
|
||||||
*/
|
*/
|
||||||
private function createDeleteForm(mixed $id)
|
private function createDeleteForm(mixed $id)
|
||||||
{
|
{
|
||||||
|
@ -201,13 +201,12 @@ class StatusController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Creates a form to delete a Status entity by id.
|
* Creates a form to delete a Status entity by id.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\Form\Form The form
|
* @return \Symfony\Component\Form\FormInterface The form
|
||||||
*/
|
*/
|
||||||
private function createDeleteForm(mixed $id)
|
private function createDeleteForm(mixed $id)
|
||||||
{
|
{
|
||||||
return $this->createFormBuilder()
|
return $this->createFormBuilder()
|
||||||
->setAction($this->generateUrl('chill_event_admin_status_delete', ['id' => $id]))
|
->setAction($this->generateUrl('chill_event_admin_status_delete', ['id' => $id]))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\EventBundle\DependencyInjection;
|
namespace Chill\EventBundle\DependencyInjection;
|
||||||
|
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\EventBundle\Security\Authorization\ParticipationVoter;
|
use Chill\EventBundle\Security\ParticipationVoter;
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||||
@ -33,12 +33,13 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface
|
|||||||
|
|
||||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
||||||
$loader->load('services.yaml');
|
$loader->load('services.yaml');
|
||||||
$loader->load('services/authorization.yaml');
|
$loader->load('services/security.yaml');
|
||||||
$loader->load('services/fixtures.yaml');
|
$loader->load('services/fixtures.yaml');
|
||||||
$loader->load('services/forms.yaml');
|
$loader->load('services/forms.yaml');
|
||||||
$loader->load('services/repositories.yaml');
|
$loader->load('services/repositories.yaml');
|
||||||
$loader->load('services/search.yaml');
|
$loader->load('services/search.yaml');
|
||||||
$loader->load('services/timeline.yaml');
|
$loader->load('services/timeline.yaml');
|
||||||
|
$loader->load('services/export.yaml');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** (non-PHPdoc).
|
/** (non-PHPdoc).
|
||||||
|
@ -47,7 +47,7 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter
|
|||||||
private ?Scope $circle = null;
|
private ?Scope $circle = null;
|
||||||
|
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_MUTABLE)]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_MUTABLE)]
|
||||||
private ?\DateTime $date;
|
private ?\DateTime $date = null;
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
||||||
@ -62,9 +62,9 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter
|
|||||||
private ?string $name = null;
|
private ?string $name = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<Participation>
|
* @var Collection<int, Participation>
|
||||||
*/
|
*/
|
||||||
#[ORM\OneToMany(targetEntity: Participation::class, mappedBy: 'event')]
|
#[ORM\OneToMany(mappedBy: 'event', targetEntity: Participation::class)]
|
||||||
private Collection $participations;
|
private Collection $participations;
|
||||||
|
|
||||||
#[Assert\NotNull]
|
#[Assert\NotNull]
|
||||||
@ -79,7 +79,7 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter
|
|||||||
private ?Location $location = null;
|
private ?Location $location = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<StoredObject>
|
* @var Collection<int, StoredObject>
|
||||||
*/
|
*/
|
||||||
#[ORM\ManyToMany(targetEntity: StoredObject::class, cascade: ['persist', 'refresh'])]
|
#[ORM\ManyToMany(targetEntity: StoredObject::class, cascade: ['persist', 'refresh'])]
|
||||||
#[ORM\JoinTable('chill_event_event_documents')]
|
#[ORM\JoinTable('chill_event_event_documents')]
|
||||||
@ -192,7 +192,7 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter
|
|||||||
{
|
{
|
||||||
$iterator = iterator_to_array($this->participations->getIterator());
|
$iterator = iterator_to_array($this->participations->getIterator());
|
||||||
|
|
||||||
uasort($iterator, static fn ($first, $second) => strnatcasecmp((string) $first->getPerson()->getFirstName(), (string) $second->getPerson()->getFirstName()));
|
uasort($iterator, static fn ($first, $second) => strnatcasecmp($first->getPerson()->getFirstName(), $second->getPerson()->getFirstName()));
|
||||||
|
|
||||||
return new \ArrayIterator($iterator);
|
return new \ArrayIterator($iterator);
|
||||||
}
|
}
|
||||||
@ -265,11 +265,9 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter
|
|||||||
/**
|
/**
|
||||||
* Set label.
|
* Set label.
|
||||||
*
|
*
|
||||||
* @param string $label
|
|
||||||
*
|
|
||||||
* @return Event
|
* @return Event
|
||||||
*/
|
*/
|
||||||
public function setName($label)
|
public function setName(?string $label)
|
||||||
{
|
{
|
||||||
$this->name = $label;
|
$this->name = $label;
|
||||||
|
|
||||||
|
@ -38,13 +38,13 @@ class EventType
|
|||||||
private $name;
|
private $name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<Role>
|
* @var Collection<int, Role>
|
||||||
*/
|
*/
|
||||||
#[ORM\OneToMany(targetEntity: Role::class, mappedBy: 'type')]
|
#[ORM\OneToMany(mappedBy: 'type', targetEntity: Role::class)]
|
||||||
private Collection $roles;
|
private Collection $roles;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<Status>
|
* @var Collection<int, Status>
|
||||||
*/
|
*/
|
||||||
#[ORM\OneToMany(targetEntity: Status::class, mappedBy: 'type')]
|
#[ORM\OneToMany(targetEntity: Status::class, mappedBy: 'type')]
|
||||||
private Collection $statuses;
|
private Collection $statuses;
|
||||||
@ -146,11 +146,9 @@ class EventType
|
|||||||
/**
|
/**
|
||||||
* Set active.
|
* Set active.
|
||||||
*
|
*
|
||||||
* @param bool $active
|
|
||||||
*
|
|
||||||
* @return EventType
|
* @return EventType
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
|
@ -81,11 +81,9 @@ class Role
|
|||||||
/**
|
/**
|
||||||
* Set active.
|
* Set active.
|
||||||
*
|
*
|
||||||
* @param bool $active
|
|
||||||
*
|
|
||||||
* @return Role
|
* @return Role
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
|
@ -81,11 +81,9 @@ class Status
|
|||||||
/**
|
/**
|
||||||
* Set active.
|
* Set active.
|
||||||
*
|
*
|
||||||
* @param bool $active
|
|
||||||
*
|
|
||||||
* @return Status
|
* @return Status
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
class EventDateAggregator implements AggregatorInterface
|
||||||
|
{
|
||||||
|
private const CHOICES = [
|
||||||
|
'by month' => 'month',
|
||||||
|
'by week' => 'week',
|
||||||
|
'by year' => 'year',
|
||||||
|
];
|
||||||
|
|
||||||
|
private const DEFAULT_CHOICE = 'year';
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$order = null;
|
||||||
|
|
||||||
|
switch ($data['frequency']) {
|
||||||
|
case 'month':
|
||||||
|
$fmt = 'YYYY-MM';
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'week':
|
||||||
|
$fmt = 'YYYY-IW';
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'year':
|
||||||
|
$fmt = 'YYYY';
|
||||||
|
$order = 'DESC';
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \RuntimeException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->addSelect(sprintf("TO_CHAR(event.date, '%s') AS date_aggregator", $fmt));
|
||||||
|
$qb->addGroupBy('date_aggregator');
|
||||||
|
$qb->addOrderBy('date_aggregator', $order);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder->add('frequency', ChoiceType::class, [
|
||||||
|
'choices' => self::CHOICES,
|
||||||
|
'multiple' => false,
|
||||||
|
'expanded' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return ['frequency' => self::DEFAULT_CHOICE];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data)
|
||||||
|
{
|
||||||
|
return static function ($value) use ($data): string {
|
||||||
|
if ('_header' === $value) {
|
||||||
|
return 'by '.$data['frequency'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return match ($data['frequency']) {
|
||||||
|
default => $value,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data): array
|
||||||
|
{
|
||||||
|
return ['date_aggregator'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return 'Group event by date';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\EventTypeRepository;
|
||||||
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
class EventTypeAggregator implements AggregatorInterface
|
||||||
|
{
|
||||||
|
final public const KEY = 'event_type_aggregator';
|
||||||
|
|
||||||
|
public function __construct(protected EventTypeRepository $eventTypeRepository, protected TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
if (!\in_array('eventtype', $qb->getAllAliases(), true)) {
|
||||||
|
$qb->leftJoin('event.type', 'eventtype');
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->addSelect(sprintf('IDENTITY(event.type) AS %s', self::KEY));
|
||||||
|
$qb->addGroupBy(self::KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
// no form required for this aggregator
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data): \Closure
|
||||||
|
{
|
||||||
|
return function (int|string|null $value): string {
|
||||||
|
if ('_header' === $value) {
|
||||||
|
return 'Event type';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $value || '' === $value || null === $t = $this->eventTypeRepository->find($value)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->translatableStringHelper->localize($t->getName());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data): array
|
||||||
|
{
|
||||||
|
return [self::KEY];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Group by event type';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\RoleRepository;
|
||||||
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
class RoleAggregator implements AggregatorInterface
|
||||||
|
{
|
||||||
|
final public const KEY = 'part_role_aggregator';
|
||||||
|
|
||||||
|
public function __construct(protected RoleRepository $roleRepository, protected TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
if (!\in_array('event_part', $qb->getAllAliases(), true)) {
|
||||||
|
$qb->leftJoin('event_part.role', 'role');
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->addSelect(sprintf('IDENTITY(event_part.role) AS %s', self::KEY));
|
||||||
|
$qb->addGroupBy(self::KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT_PARTICIPANTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
// no form required for this aggregator
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data): \Closure
|
||||||
|
{
|
||||||
|
return function (int|string|null $value): string {
|
||||||
|
if ('_header' === $value) {
|
||||||
|
return 'Participant role';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $value || '' === $value || null === $r = $this->roleRepository->find($value)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->translatableStringHelper->localize($r->getName());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data): array
|
||||||
|
{
|
||||||
|
return [self::KEY];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Group by participant role';
|
||||||
|
}
|
||||||
|
}
|
22
src/Bundle/ChillEventBundle/Export/Declarations.php
Normal file
22
src/Bundle/ChillEventBundle/Export/Declarations.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class declare constants used for the export framework.
|
||||||
|
*/
|
||||||
|
abstract class Declarations
|
||||||
|
{
|
||||||
|
final public const EVENT = 'event';
|
||||||
|
|
||||||
|
final public const EVENT_PARTICIPANTS = 'event_participants';
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\ParticipationRepository;
|
||||||
|
use Chill\EventBundle\Security\ParticipationVoter;
|
||||||
|
use Chill\MainBundle\Export\ExportInterface;
|
||||||
|
use Chill\MainBundle\Export\FormatterInterface;
|
||||||
|
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||||
|
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||||
|
use Doctrine\ORM\Query;
|
||||||
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
|
||||||
|
|
||||||
|
readonly class CountEventParticipations implements ExportInterface, GroupedExportInterface
|
||||||
|
{
|
||||||
|
private bool $filterStatsByCenters;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private ParticipationRepository $participationRepository,
|
||||||
|
ParameterBagInterface $parameterBag,
|
||||||
|
) {
|
||||||
|
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder) {}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllowedFormattersTypes()
|
||||||
|
{
|
||||||
|
return [FormatterInterface::TYPE_TABULAR];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
return 'Count participants to an event by various parameters.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroup(): string
|
||||||
|
{
|
||||||
|
return 'Exports of events';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data)
|
||||||
|
{
|
||||||
|
if ('export_count_event_participants' !== $key) {
|
||||||
|
throw new \LogicException("the key {$key} is not used by this export");
|
||||||
|
}
|
||||||
|
|
||||||
|
return static fn ($value) => '_header' === $value ? 'Count event participants' : $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data)
|
||||||
|
{
|
||||||
|
return ['export_count_event_participants'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult($query, $data)
|
||||||
|
{
|
||||||
|
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Count event participants';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT_PARTICIPANTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||||
|
{
|
||||||
|
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||||
|
|
||||||
|
$qb = $this->participationRepository
|
||||||
|
->createQueryBuilder('event_part')
|
||||||
|
->join('event_part.person', 'person');
|
||||||
|
|
||||||
|
$qb->select('COUNT(event_part.id) as export_count_event_participants');
|
||||||
|
|
||||||
|
if ($this->filterStatsByCenters) {
|
||||||
|
$qb
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->exists(
|
||||||
|
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||||
|
AND acl_count_person_history.center IN (:authorized_centers)
|
||||||
|
'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->setParameter('authorized_centers', $centers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requiredRole(): string
|
||||||
|
{
|
||||||
|
return ParticipationVoter::STATS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsModifiers()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Declarations::EVENT_PARTICIPANTS,
|
||||||
|
PersonDeclarations::PERSON_TYPE,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
126
src/Bundle/ChillEventBundle/Export/Export/CountEvents.php
Normal file
126
src/Bundle/ChillEventBundle/Export/Export/CountEvents.php
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Repository\EventRepository;
|
||||||
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
|
use Chill\MainBundle\Export\ExportInterface;
|
||||||
|
use Chill\MainBundle\Export\FormatterInterface;
|
||||||
|
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||||
|
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||||
|
use Doctrine\ORM\Query;
|
||||||
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
|
||||||
|
|
||||||
|
readonly class CountEvents implements ExportInterface, GroupedExportInterface
|
||||||
|
{
|
||||||
|
private bool $filterStatsByCenters;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private EventRepository $eventRepository,
|
||||||
|
ParameterBagInterface $parameterBag,
|
||||||
|
) {
|
||||||
|
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder) {}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllowedFormattersTypes()
|
||||||
|
{
|
||||||
|
return [FormatterInterface::TYPE_TABULAR];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
return 'Count events by various parameters.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroup(): string
|
||||||
|
{
|
||||||
|
return 'Exports of events';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data)
|
||||||
|
{
|
||||||
|
if ('export_count_event' !== $key) {
|
||||||
|
throw new \LogicException("the key {$key} is not used by this export");
|
||||||
|
}
|
||||||
|
|
||||||
|
return static fn ($value) => '_header' === $value ? 'Number of events' : $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data)
|
||||||
|
{
|
||||||
|
return ['export_count_event'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult($query, $data)
|
||||||
|
{
|
||||||
|
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Count events';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||||
|
{
|
||||||
|
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||||
|
|
||||||
|
$qb = $this->eventRepository
|
||||||
|
->createQueryBuilder('event')
|
||||||
|
->leftJoin('event.participations', 'epart')
|
||||||
|
->leftJoin('epart.person', 'person');
|
||||||
|
|
||||||
|
$qb->select('COUNT(DISTINCT event.id) as export_count_event');
|
||||||
|
|
||||||
|
if ($this->filterStatsByCenters) {
|
||||||
|
$qb
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->exists(
|
||||||
|
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||||
|
AND acl_count_person_history.center IN (:authorized_centers)
|
||||||
|
'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->setParameter('authorized_centers', $centers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requiredRole(): string
|
||||||
|
{
|
||||||
|
return EventVoter::STATS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsModifiers()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Declarations::EVENT,
|
||||||
|
PersonDeclarations::PERSON_TYPE,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||||
|
use Doctrine\ORM\Query\Expr;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
class EventDateFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
public function __construct(protected TranslatorInterface $translator, private readonly RollingDateConverterInterface $rollingDateConverter) {}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$where = $qb->getDQLPart('where');
|
||||||
|
$clause = $qb->expr()->between(
|
||||||
|
'event.date',
|
||||||
|
':date_from',
|
||||||
|
':date_to'
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($where instanceof Expr\Andx) {
|
||||||
|
$where->add($clause);
|
||||||
|
} else {
|
||||||
|
$where = $qb->expr()->andX($clause);
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->add('where', $where);
|
||||||
|
$qb->setParameter(
|
||||||
|
'date_from',
|
||||||
|
$this->rollingDateConverter->convert($data['date_from'])
|
||||||
|
);
|
||||||
|
$qb->setParameter(
|
||||||
|
'date_to',
|
||||||
|
$this->rollingDateConverter->convert($data['date_to'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('date_from', PickRollingDateType::class, [
|
||||||
|
'label' => 'Events after this date',
|
||||||
|
])
|
||||||
|
->add('date_to', PickRollingDateType::class, [
|
||||||
|
'label' => 'Events before this date',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return ['date_from' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), 'date_to' => new RollingDate(RollingDate::T_TODAY)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAction($data, $format = 'string')
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Filtered by date of event: only between %date_from% and %date_to%',
|
||||||
|
[
|
||||||
|
'%date_from%' => $this->rollingDateConverter->convert($data['date_from'])->format('d-m-Y'),
|
||||||
|
'%date_to%' => $this->rollingDateConverter->convert($data['date_to'])->format('d-m-Y'),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Filtered by event date';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\EventType;
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\EventTypeRepository;
|
||||||
|
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||||
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
|
||||||
|
class EventTypeFilter implements ExportElementValidatedInterface, FilterInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
|
protected EventTypeRepository $eventTypeRepository
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$clause = $qb->expr()->in('event.type', ':selected_event_types');
|
||||||
|
|
||||||
|
$qb->andWhere($clause);
|
||||||
|
$qb->setParameter('selected_event_types', $data['types']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder->add('types', EntityType::class, [
|
||||||
|
'choices' => $this->eventTypeRepository->findAllActive(),
|
||||||
|
'class' => EventType::class,
|
||||||
|
'choice_label' => fn (EventType $ety) => $this->translatableStringHelper->localize($ety->getName()),
|
||||||
|
'multiple' => true,
|
||||||
|
'expanded' => false,
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'select2',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAction($data, $format = 'string')
|
||||||
|
{
|
||||||
|
$typeNames = array_map(
|
||||||
|
fn (EventType $t): string => $this->translatableStringHelper->localize($t->getName()),
|
||||||
|
$this->eventTypeRepository->findBy(['id' => $data['types'] instanceof \Doctrine\Common\Collections\Collection ? $data['types']->toArray() : $data['types']])
|
||||||
|
);
|
||||||
|
|
||||||
|
return ['Filtered by event type: only %list%', [
|
||||||
|
'%list%' => implode(', ', $typeNames),
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Filtered by event type';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateForm($data, ExecutionContextInterface $context)
|
||||||
|
{
|
||||||
|
if (null === $data['types'] || 0 === \count($data['types'])) {
|
||||||
|
$context
|
||||||
|
->buildViolation('At least one type must be chosen')
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
src/Bundle/ChillEventBundle/Export/Filter/RoleFilter.php
Normal file
94
src/Bundle/ChillEventBundle/Export/Filter/RoleFilter.php
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Role;
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\RoleRepository;
|
||||||
|
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||||
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
|
||||||
|
class RoleFilter implements ExportElementValidatedInterface, FilterInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
|
protected RoleRepository $roleRepository
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$clause = $qb->expr()->in('event_part.role', ':selected_part_roles');
|
||||||
|
|
||||||
|
$qb->andWhere($clause);
|
||||||
|
$qb->setParameter('selected_part_roles', $data['part_roles']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT_PARTICIPANTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder->add('part_roles', EntityType::class, [
|
||||||
|
'choices' => $this->roleRepository->findAllActive(),
|
||||||
|
'class' => Role::class,
|
||||||
|
'choice_label' => fn (Role $r) => $this->translatableStringHelper->localize($r->getName()),
|
||||||
|
'multiple' => true,
|
||||||
|
'expanded' => false,
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'select2',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAction($data, $format = 'string')
|
||||||
|
{
|
||||||
|
$roleNames = array_map(
|
||||||
|
fn (Role $r): string => $this->translatableStringHelper->localize($r->getName()),
|
||||||
|
$this->roleRepository->findBy(['id' => $data['part_roles'] instanceof \Doctrine\Common\Collections\Collection ? $data['part_roles']->toArray() : $data['part_roles']])
|
||||||
|
);
|
||||||
|
|
||||||
|
return ['Filtered by participant roles: only %list%', [
|
||||||
|
'%list%' => implode(', ', $roleNames),
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Filter by participant roles';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateForm($data, ExecutionContextInterface $context)
|
||||||
|
{
|
||||||
|
if (null === $data['part_roles'] || 0 === \count($data['part_roles'])) {
|
||||||
|
$context
|
||||||
|
->buildViolation('At least one role must be chosen')
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ namespace Chill\EventBundle\Form;
|
|||||||
|
|
||||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
use Chill\DocStoreBundle\Form\StoredObjectType;
|
use Chill\DocStoreBundle\Form\StoredObjectType;
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
use Chill\EventBundle\Form\Type\PickEventTypeType;
|
use Chill\EventBundle\Form\Type\PickEventTypeType;
|
||||||
use Chill\MainBundle\Entity\Center;
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Form\Type\ChillCollectionType;
|
use Chill\MainBundle\Form\Type\ChillCollectionType;
|
||||||
@ -23,6 +24,7 @@ use Chill\MainBundle\Form\Type\PickUserLocationType;
|
|||||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
@ -31,7 +33,9 @@ class EventType extends AbstractType
|
|||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
$builder
|
$builder
|
||||||
->add('name')
|
->add('name', TextType::class, [
|
||||||
|
'required' => true,
|
||||||
|
])
|
||||||
->add('date', ChillDateTimeType::class, [
|
->add('date', ChillDateTimeType::class, [
|
||||||
'required' => true,
|
'required' => true,
|
||||||
])
|
])
|
||||||
@ -75,7 +79,7 @@ class EventType extends AbstractType
|
|||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => \Chill\EventBundle\Entity\Event::class,
|
'data_class' => Event::class,
|
||||||
]);
|
]);
|
||||||
$resolver
|
$resolver
|
||||||
->setRequired(['center', 'role'])
|
->setRequired(['center', 'role'])
|
||||||
|
@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\EventBundle\Menu;
|
namespace Chill\EventBundle\Menu;
|
||||||
|
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
use Knp\Menu\MenuItem;
|
use Knp\Menu\MenuItem;
|
||||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||||
|
@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\EventBundle\Menu;
|
namespace Chill\EventBundle\Menu;
|
||||||
|
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
use Knp\Menu\MenuItem;
|
use Knp\Menu\MenuItem;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
@ -13,7 +13,7 @@ namespace Chill\EventBundle\Repository;
|
|||||||
|
|
||||||
use Chill\EventBundle\Entity\Event;
|
use Chill\EventBundle\Entity\Event;
|
||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
@ -12,13 +12,57 @@ declare(strict_types=1);
|
|||||||
namespace Chill\EventBundle\Repository;
|
namespace Chill\EventBundle\Repository;
|
||||||
|
|
||||||
use Chill\EventBundle\Entity\Role;
|
use Chill\EventBundle\Entity\Role;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
class RoleRepository extends ServiceEntityRepository
|
readonly class RoleRepository implements ObjectRepository
|
||||||
{
|
{
|
||||||
public function __construct(ManagerRegistry $registry)
|
private EntityRepository $repository;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $entityManager, private TranslatableStringHelper $translatableStringHelper)
|
||||||
{
|
{
|
||||||
parent::__construct($registry, Role::class);
|
$this->repository = $entityManager->getRepository(Role::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
|
||||||
|
{
|
||||||
|
return $this->repository->createQueryBuilder($alias, $indexBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($id)
|
||||||
|
{
|
||||||
|
return $this->repository->find($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll(): array
|
||||||
|
{
|
||||||
|
return $this->repository->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAllActive(): array
|
||||||
|
{
|
||||||
|
$roles = $this->repository->findBy(['active' => true]);
|
||||||
|
|
||||||
|
usort($roles, fn (Role $a, Role $b) => $this->translatableStringHelper->localize($a->getName()) <=> $this->translatableStringHelper->localize($b->getName()));
|
||||||
|
|
||||||
|
return $roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
|
||||||
|
{
|
||||||
|
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOneBy(array $criteria)
|
||||||
|
{
|
||||||
|
return $this->repository->findOneBy($criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassName(): string
|
||||||
|
{
|
||||||
|
return Role::class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% import '@ChillPerson/Person/macro.html.twig' as person_macro %}
|
{% import '@ChillPerson/Person/macro.html.twig' as person_macro %}
|
||||||
|
|
||||||
{% if ignored_participations|length > 0 %}
|
{% if ignored_participations|length > 0 %}
|
||||||
<p>{% transchoice ignored_participations|length %}The following people have been ignored because they are already participating on the event{% endtranschoice %} :</p>
|
<p>{{ 'ignored_participations'|trans({'count': ignored_participations|length}) }}:</p>
|
||||||
<ul>
|
<ul>
|
||||||
{% for p in ignored_participations %}
|
{% for p in ignored_participations %}
|
||||||
<li>{{ person_macro.render(p.person) }}</li>
|
<li>{{ person_macro.render(p.person) }}</li>
|
||||||
|
@ -9,18 +9,19 @@ declare(strict_types=1);
|
|||||||
* the LICENSE file that was distributed with this source code.
|
* the LICENSE file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Chill\EventBundle\Security\Authorization;
|
namespace Chill\EventBundle\Security;
|
||||||
|
|
||||||
use Chill\EventBundle\Entity\Event;
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description of EventVoter.
|
* Description of EventVoter.
|
||||||
@ -42,61 +43,46 @@ class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInter
|
|||||||
|
|
||||||
final public const UPDATE = 'CHILL_EVENT_UPDATE';
|
final public const UPDATE = 'CHILL_EVENT_UPDATE';
|
||||||
|
|
||||||
/**
|
final public const STATS = 'CHILL_EVENT_STATS';
|
||||||
* @var AccessDecisionManagerInterface
|
|
||||||
*/
|
|
||||||
protected $accessDecisionManager;
|
|
||||||
|
|
||||||
/**
|
private readonly VoterHelperInterface $voterHelper;
|
||||||
* @var AuthorizationHelper
|
|
||||||
*/
|
|
||||||
protected $authorizationHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var LoggerInterface
|
|
||||||
*/
|
|
||||||
protected $logger;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AccessDecisionManagerInterface $accessDecisionManager,
|
private readonly AuthorizationHelper $authorizationHelper,
|
||||||
AuthorizationHelper $authorizationHelper,
|
private readonly LoggerInterface $logger,
|
||||||
LoggerInterface $logger
|
VoterHelperFactoryInterface $voterHelperFactory
|
||||||
) {
|
) {
|
||||||
$this->accessDecisionManager = $accessDecisionManager;
|
$this->voterHelper = $voterHelperFactory
|
||||||
$this->authorizationHelper = $authorizationHelper;
|
->generate(self::class)
|
||||||
$this->logger = $logger;
|
->addCheckFor(null, [self::SEE])
|
||||||
|
->addCheckFor(Event::class, [...self::ROLES])
|
||||||
|
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||||
|
->addCheckFor(Center::class, [self::STATS])
|
||||||
|
->build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoles(): array
|
public function getRoles(): array
|
||||||
{
|
{
|
||||||
return self::ROLES;
|
return [...self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithHierarchy(): array
|
public function getRolesWithHierarchy(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'Event' => self::ROLES,
|
'Event' => $this->getRoles(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithoutScope(): array
|
public function getRolesWithoutScope(): array
|
||||||
{
|
{
|
||||||
return [];
|
return [self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supports($attribute, $subject)
|
public function supports($attribute, $subject)
|
||||||
{
|
{
|
||||||
return ($subject instanceof Event && \in_array($attribute, self::ROLES, true))
|
return $this->voterHelper->supports($attribute, $subject);
|
||||||
|| ($subject instanceof Person && \in_array($attribute, [self::CREATE, self::SEE], true))
|
|
||||||
|| (null === $subject && self::SEE === $attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $attribute
|
|
||||||
* @param Event $subject
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
||||||
{
|
{
|
||||||
$this->logger->debug(sprintf('Voting from %s class', self::class));
|
$this->logger->debug(sprintf('Voting from %s class', self::class));
|
||||||
@ -118,15 +104,5 @@ class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInter
|
|||||||
->getReachableCenters($token->getUser(), $attribute);
|
->getReachableCenters($token->getUser(), $attribute);
|
||||||
|
|
||||||
return \count($centers) > 0;
|
return \count($centers) > 0;
|
||||||
|
|
||||||
if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->authorizationHelper->userHasAccess(
|
|
||||||
$token->getUser(),
|
|
||||||
$subject,
|
|
||||||
$attribute
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,18 +9,19 @@ declare(strict_types=1);
|
|||||||
* the LICENSE file that was distributed with this source code.
|
* the LICENSE file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Chill\EventBundle\Security\Authorization;
|
namespace Chill\EventBundle\Security;
|
||||||
|
|
||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
|
||||||
|
|
||||||
class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
||||||
{
|
{
|
||||||
@ -39,58 +40,48 @@ class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierar
|
|||||||
|
|
||||||
final public const UPDATE = 'CHILL_EVENT_PARTICIPATION_UPDATE';
|
final public const UPDATE = 'CHILL_EVENT_PARTICIPATION_UPDATE';
|
||||||
|
|
||||||
/**
|
final public const STATS = 'CHILL_EVENT_PARTICIPATION_STATS';
|
||||||
* @var AccessDecisionManagerInterface
|
|
||||||
*/
|
|
||||||
protected $accessDecisionManager;
|
|
||||||
|
|
||||||
/**
|
private readonly VoterHelperInterface $voterHelper;
|
||||||
* @var AuthorizationHelper
|
|
||||||
*/
|
|
||||||
protected $authorizationHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var LoggerInterface
|
|
||||||
*/
|
|
||||||
protected $logger;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AccessDecisionManagerInterface $accessDecisionManager,
|
private readonly AuthorizationHelper $authorizationHelper,
|
||||||
AuthorizationHelper $authorizationHelper,
|
private readonly LoggerInterface $logger,
|
||||||
LoggerInterface $logger
|
VoterHelperFactoryInterface $voterHelperFactory
|
||||||
) {
|
) {
|
||||||
$this->accessDecisionManager = $accessDecisionManager;
|
$this->voterHelper = $voterHelperFactory
|
||||||
$this->authorizationHelper = $authorizationHelper;
|
->generate(self::class)
|
||||||
$this->logger = $logger;
|
->addCheckFor(null, [self::SEE])
|
||||||
|
->addCheckFor(Participation::class, [...self::ROLES])
|
||||||
|
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||||
|
->addCheckFor(Center::class, [self::STATS])
|
||||||
|
->build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoles(): array
|
public function getRoles(): array
|
||||||
{
|
{
|
||||||
return self::ROLES;
|
return [...self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithHierarchy(): array
|
public function getRolesWithHierarchy(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'Event' => self::ROLES,
|
'Participation' => $this->getRoles(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithoutScope(): array
|
public function getRolesWithoutScope(): array
|
||||||
{
|
{
|
||||||
return [];
|
return [self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supports($attribute, $subject)
|
public function supports($attribute, $subject)
|
||||||
{
|
{
|
||||||
return ($subject instanceof Participation && \in_array($attribute, self::ROLES, true))
|
return $this->voterHelper->supports($attribute, $subject);
|
||||||
|| ($subject instanceof Person && \in_array($attribute, [self::CREATE, self::SEE], true))
|
|
||||||
|| (null === $subject && self::SEE === $attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $attribute
|
* @param string $attribute
|
||||||
* @param Participation $subject
|
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
@ -115,15 +106,5 @@ class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierar
|
|||||||
->getReachableCenters($token->getUser(), $attribute);
|
->getReachableCenters($token->getUser(), $attribute);
|
||||||
|
|
||||||
return \count($centers) > 0;
|
return \count($centers) > 0;
|
||||||
|
|
||||||
if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->authorizationHelper->userHasAccess(
|
|
||||||
$token->getUser(),
|
|
||||||
$subject,
|
|
||||||
$attribute
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Tests\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Export\CountEventParticipations;
|
||||||
|
use Doctrine\ORM\AbstractQuery;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class CountEventParticipationsTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
private CountEventParticipations $countEventParticipations;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
self::bootKernel();
|
||||||
|
$this->countEventParticipations = self::getContainer()->get(CountEventParticipations::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExecuteQuery(): void
|
||||||
|
{
|
||||||
|
$qb = $this->countEventParticipations->initiateQuery([], [], [])
|
||||||
|
->setMaxResults(1);
|
||||||
|
|
||||||
|
$results = $qb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||||
|
|
||||||
|
self::assertIsArray($results, 'smoke test: test that the result is an array');
|
||||||
|
}
|
||||||
|
}
|
43
src/Bundle/ChillEventBundle/Tests/Export/CountEventTest.php
Normal file
43
src/Bundle/ChillEventBundle/Tests/Export/CountEventTest.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Tests\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Export\CountEvents;
|
||||||
|
use Doctrine\ORM\AbstractQuery;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class CountEventTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
private CountEvents $countEvents;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
self::bootKernel();
|
||||||
|
$this->countEvents = self::getContainer()->get(CountEvents::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExecuteQuery(): void
|
||||||
|
{
|
||||||
|
$qb = $this->countEvents->initiateQuery([], [], [])
|
||||||
|
->setMaxResults(1);
|
||||||
|
|
||||||
|
$results = $qb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||||
|
|
||||||
|
self::assertIsArray($results, 'smoke test: test that the result is an array');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Export\aggregators;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Export\Aggregator\EventDateAggregator;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventDateAggregatorTest extends AbstractAggregatorTest
|
||||||
|
{
|
||||||
|
private $aggregator;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->aggregator = self::getContainer()->get(EventDateAggregator::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAggregator()
|
||||||
|
{
|
||||||
|
return $this->aggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array|\Generator
|
||||||
|
{
|
||||||
|
yield ['frequency' => 'YYYY'];
|
||||||
|
yield ['frequency' => 'YYYY-MM'];
|
||||||
|
yield ['frequency' => 'YYYY-IV'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Export\aggregators;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Export\Aggregator\EventTypeAggregator;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventTypeAggregatorTest extends AbstractAggregatorTest
|
||||||
|
{
|
||||||
|
private $aggregator;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->aggregator = self::getContainer()->get(EventTypeAggregator::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAggregator()
|
||||||
|
{
|
||||||
|
return $this->aggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Export\aggregators;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Entity\Participation;
|
||||||
|
use Chill\EventBundle\Export\Aggregator\RoleAggregator;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class RoleAggregatorTest extends AbstractAggregatorTest
|
||||||
|
{
|
||||||
|
private $aggregator;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->aggregator = self::getContainer()->get(RoleAggregator::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAggregator()
|
||||||
|
{
|
||||||
|
return $this->aggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event_part')
|
||||||
|
->from(Participation::class, 'event_part'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Export\filters;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Export\Filter\EventDateFilter;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventDateFilterTest extends AbstractFilterTest
|
||||||
|
{
|
||||||
|
private RollingDateConverterInterface $rollingDateConverter;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->rollingDateConverter = self::getContainer()->get(RollingDateConverterInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilter()
|
||||||
|
{
|
||||||
|
return new EventDateFilter($this->rollingDateConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'date_from' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
|
||||||
|
'date_to' => new RollingDate(RollingDate::T_TODAY),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Export\filters;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Entity\EventType;
|
||||||
|
use Chill\EventBundle\Export\Filter\EventTypeFilter;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventTypeFilterTest extends AbstractFilterTest
|
||||||
|
{
|
||||||
|
private EventTypeFilter $filter;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$this->filter = self::getContainer()->get(EventTypeFilter::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilter(): EventTypeFilter|\Chill\MainBundle\Export\FilterInterface
|
||||||
|
{
|
||||||
|
return $this->filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
$array = $em->createQueryBuilder()
|
||||||
|
->from(EventType::class, 'et')
|
||||||
|
->select('et')
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($array as $a) {
|
||||||
|
$data[] = [
|
||||||
|
'types' => new ArrayCollection([$a]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Export\filters;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Entity\Participation;
|
||||||
|
use Chill\EventBundle\Entity\Role;
|
||||||
|
use Chill\EventBundle\Export\Filter\RoleFilter;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class RoleFilterTest extends AbstractFilterTest
|
||||||
|
{
|
||||||
|
private RoleFilter $filter;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->filter = self::getContainer()->get(RoleFilter::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilter()
|
||||||
|
{
|
||||||
|
return $this->filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
$array = $em->createQueryBuilder()
|
||||||
|
->from(Role::class, 'r')
|
||||||
|
->select('r')
|
||||||
|
->getQuery()
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($array as $a) {
|
||||||
|
$data[] = [
|
||||||
|
'roles' => new ArrayCollection([$a]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event_part')
|
||||||
|
->from(Participation::class, 'event_part'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\EventBundle\Tests\Repository;
|
namespace Chill\EventBundle\Tests\Repository;
|
||||||
|
|
||||||
use Chill\EventBundle\Repository\EventACLAwareRepository;
|
use Chill\EventBundle\Repository\EventACLAwareRepository;
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Entity\Center;
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\Scope;
|
use Chill\MainBundle\Entity\Scope;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
services:
|
|
||||||
chill_event.event_voter:
|
|
||||||
class: Chill\EventBundle\Security\Authorization\EventVoter
|
|
||||||
arguments:
|
|
||||||
- "@security.access.decision_manager"
|
|
||||||
- "@chill.main.security.authorization.helper"
|
|
||||||
- "@logger"
|
|
||||||
tags:
|
|
||||||
- { name: security.voter }
|
|
||||||
|
|
||||||
chill_event.event_participation:
|
|
||||||
class: Chill\EventBundle\Security\Authorization\ParticipationVoter
|
|
||||||
arguments:
|
|
||||||
- "@security.access.decision_manager"
|
|
||||||
- "@chill.main.security.authorization.helper"
|
|
||||||
- "@logger"
|
|
||||||
tags:
|
|
||||||
- { name: security.voter }
|
|
41
src/Bundle/ChillEventBundle/config/services/export.yaml
Normal file
41
src/Bundle/ChillEventBundle/config/services/export.yaml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
|
# indicators
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Export\CountEvents:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export, alias: 'count_events' }
|
||||||
|
Chill\EventBundle\Export\Export\CountEventParticipations:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export, alias: 'count_event_participants' }
|
||||||
|
|
||||||
|
# filters
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Filter\EventDateFilter:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_filter, alias: 'event_date_filter' }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Filter\EventTypeFilter:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_filter, alias: 'event_type_filter' }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Filter\RoleFilter:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_filter, alias: 'role_filter' }
|
||||||
|
|
||||||
|
# aggregators
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Aggregator\EventTypeAggregator:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_aggregator, alias: event_type_aggregator }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Aggregator\EventDateAggregator:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_aggregator, alias: event_date_aggregator }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Aggregator\RoleAggregator:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_aggregator, alias: role_aggregator }
|
14
src/Bundle/ChillEventBundle/config/services/security.yaml
Normal file
14
src/Bundle/ChillEventBundle/config/services/security.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
services:
|
||||||
|
Chill\EventBundle\Security\EventVoter:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
tags:
|
||||||
|
- { name: security.voter }
|
||||||
|
- { name: chill.role }
|
||||||
|
|
||||||
|
Chill\EventBundle\Security\ParticipationVoter:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
tags:
|
||||||
|
- { name: security.voter }
|
||||||
|
- { name: chill.role }
|
@ -19,3 +19,9 @@ events:
|
|||||||
one {et un autre participant}
|
one {et un autre participant}
|
||||||
other {et # autres participants}
|
other {et # autres participants}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ignored_participations: >-
|
||||||
|
{ count, plural,
|
||||||
|
one {La personne suivante a été ignorée parce qu''elle participe déjà à l''événement}
|
||||||
|
other {Les personnes suivantes ont été ignorées parce qu''elles participent déjà à l'événement}
|
||||||
|
}
|
||||||
|
@ -41,7 +41,6 @@ Back to the event: Retour à l'événement
|
|||||||
The participation was created: La participation a été créée
|
The participation was created: La participation a été créée
|
||||||
The participation was updated: La participation a été mise à jour
|
The participation was updated: La participation a été mise à jour
|
||||||
'None of the requested people may participate the event: they are maybe already participating.': 'Aucune des personnes indiquées ne peut être ajoutée à l''événement: elles sont peut-être déjà inscrites comme participantes.'
|
'None of the requested people may participate the event: they are maybe already participating.': 'Aucune des personnes indiquées ne peut être ajoutée à l''événement: elles sont peut-être déjà inscrites comme participantes.'
|
||||||
'The following people have been ignored because they are already participating on the event': '{1} La personne suivante a été ignorée parce qu''elle participe déjà à l''événement | ]1,Inf] Les personnes suivantes ont été ignorées parce qu''elles participent déjà à l''événement'
|
|
||||||
There are no participation to edit for this event: Il n'y a pas de participation pour cet événement
|
There are no participation to edit for this event: Il n'y a pas de participation pour cet événement
|
||||||
The participations have been successfully updated.: Les participations ont été mises à jour.
|
The participations have been successfully updated.: Les participations ont été mises à jour.
|
||||||
The participation has been sucessfully removed: La participation a été correctement supprimée.
|
The participation has been sucessfully removed: La participation a été correctement supprimée.
|
||||||
@ -81,9 +80,31 @@ Pick an event: Choisir un événement
|
|||||||
Pick a type of event: Choisir un type d'événement
|
Pick a type of event: Choisir un type d'événement
|
||||||
Pick a moderator: Choisir un animateur
|
Pick a moderator: Choisir un animateur
|
||||||
|
|
||||||
|
# exports
|
||||||
Select a format: Choisir un format
|
Select a format: Choisir un format
|
||||||
Export: Exporter
|
Export: Exporter
|
||||||
|
|
||||||
|
Count events: Nombre d'événements
|
||||||
|
Count events by various parameters.: Compte le nombre d'événements selon divers critères
|
||||||
|
Exports of events: Exports d'événements
|
||||||
|
|
||||||
|
Filtered by event date: Filtrer par date d'événement
|
||||||
|
'Filtered by date of event: only between %date_from% and %date_to%': "Filtré par date d'événement: uniquement entre le %date_from% et le %date_to%"
|
||||||
|
Events after this date: Événements après cette date
|
||||||
|
Events before this date: Événements avant cette date
|
||||||
|
Filtered by event type: Filtrer par type d'événement
|
||||||
|
'Filtered by event type: only %list%': "Filtré par type: uniquement %list%"
|
||||||
|
Group event by date: Grouper par date d'événement
|
||||||
|
Group by event type: Grouper par type d'événement
|
||||||
|
|
||||||
|
Count event participants: Nombre de participations
|
||||||
|
Count participants to an event by various parameters.: Compte le nombre de participations selon divers critères
|
||||||
|
Exports of event participants: Exports de participations
|
||||||
|
'Filtered by participant roles: only %list%': "Filtré par rôles de participation: uniquement %list%"
|
||||||
|
Filter by participant roles: Filtrer par rôles de participation
|
||||||
|
Part roles: Rôles de participation
|
||||||
|
Group by participant role: Grouper par rôle de participation
|
||||||
|
|
||||||
|
|
||||||
Events configuration: Configuration des événements
|
Events configuration: Configuration des événements
|
||||||
Events configuration menu: Menu des événements
|
Events configuration menu: Menu des événements
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\FranceTravailApiBundle\ApiHelper;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Redis\ChillRedis;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\ClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the pole emploi api.
|
||||||
|
*/
|
||||||
|
class ApiWrapper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Client
|
||||||
|
*/
|
||||||
|
private $client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key for the bearer for the api pole emploi.
|
||||||
|
*
|
||||||
|
* This bearer is shared across users
|
||||||
|
*/
|
||||||
|
public const UNPERSONAL_BEARER = 'api_pemploi_bear_';
|
||||||
|
|
||||||
|
public function __construct(private $clientId, private $clientSecret, private readonly ChillRedis $redis)
|
||||||
|
{
|
||||||
|
$this->client = new Client([
|
||||||
|
'base_uri' => 'https://entreprise.francetravail.fr/connexion/oauth2/access_token',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPublicBearer($scopes): string
|
||||||
|
{
|
||||||
|
$cacheKey = $this->getCacheKey($scopes);
|
||||||
|
|
||||||
|
if ($this->redis->exists($cacheKey) > 0) {
|
||||||
|
$data = \unserialize($this->redis->get($cacheKey));
|
||||||
|
|
||||||
|
return $data->access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->client->post('', [
|
||||||
|
'query' => ['realm' => '/partenaire'],
|
||||||
|
'headers' => [
|
||||||
|
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||||
|
],
|
||||||
|
'form_params' => [
|
||||||
|
'grant_type' => 'client_credentials',
|
||||||
|
'client_id' => $this->clientId,
|
||||||
|
'client_secret' => $this->clientSecret,
|
||||||
|
'scope' => \implode(' ', \array_merge($scopes, ['application_'.$this->clientId])),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
} catch (ClientException $e) {
|
||||||
|
dump($e->getResponse());
|
||||||
|
}
|
||||||
|
$data = \json_decode((string) $response->getBody());
|
||||||
|
|
||||||
|
// set the key with an expiry time
|
||||||
|
$this->redis->setex(
|
||||||
|
$cacheKey,
|
||||||
|
$data->expires_in - 2,
|
||||||
|
\serialize($data)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $data->access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCacheKey($scopes)
|
||||||
|
{
|
||||||
|
return self::UNPERSONAL_BEARER.implode('', $scopes);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\FranceTravailApiBundle\ApiHelper;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries for ROME partenaires api.
|
||||||
|
*/
|
||||||
|
class PartenaireRomeAppellation
|
||||||
|
{
|
||||||
|
use ProcessRequestTrait;
|
||||||
|
/**
|
||||||
|
* @var ApiWrapper
|
||||||
|
*/
|
||||||
|
protected $wrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Client
|
||||||
|
*/
|
||||||
|
protected $client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var LoggerInterface
|
||||||
|
*/
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
|
private const BASE = 'https://api.pole-emploi.io/partenaire/rome-metiers/v1/metiers/';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
ApiWrapper $wrapper,
|
||||||
|
LoggerInterface $logger,
|
||||||
|
private \Symfony\Contracts\HttpClient\HttpClientInterface $httpClient,
|
||||||
|
) {
|
||||||
|
$this->wrapper = $wrapper;
|
||||||
|
$this->logger = $logger;
|
||||||
|
$this->client = new Client([
|
||||||
|
'base_uri' => 'https://api.pole-emploi.io/partenaire/rome-metiers/v1/metiers/',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getBearer()
|
||||||
|
{
|
||||||
|
return $this->wrapper->getPublicBearer([
|
||||||
|
'api_rome-metiersv1',
|
||||||
|
'nomenclatureRome',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getListeAppellation(string $search): array
|
||||||
|
{
|
||||||
|
$bearer = $this->getBearer();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->httpClient->request(
|
||||||
|
'GET',
|
||||||
|
self::BASE.'appellation/requete',
|
||||||
|
[
|
||||||
|
'headers' => [
|
||||||
|
'Authorization' => 'Bearer '.$bearer,
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
],
|
||||||
|
'query' => [
|
||||||
|
'q' => $search,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return $response->toArray()['resultats'];
|
||||||
|
} catch (HttpExceptionInterface $exception) {
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAppellation(string $code): array
|
||||||
|
{
|
||||||
|
$bearer = $this->getBearer();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
$response = $this->httpClient->request('GET', sprintf(self::BASE.'appellation/%s', $code), [
|
||||||
|
'headers' => [
|
||||||
|
'Authorization' => 'Bearer '.$bearer,
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
],
|
||||||
|
'query' => [
|
||||||
|
'champs' => 'code,libelle,metier(code,libelle)',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $response->toArray();
|
||||||
|
} catch (HttpExceptionInterface $exception) {
|
||||||
|
if (429 === $exception->getResponse()->getStatusCode()) {
|
||||||
|
$retryAfter = $exception->getResponse()->getHeaders(false)['retry-after'][0] ?? 1;
|
||||||
|
sleep((int) $retryAfter);
|
||||||
|
} else {
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\FranceTravailApiBundle\ApiHelper;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Psr7\Request;
|
||||||
|
use GuzzleHttp\Exception\ClientException;
|
||||||
|
use GuzzleHttp\Exception\BadResponseException;
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methods to process request against the api, and handle the
|
||||||
|
* Exceptions.
|
||||||
|
*/
|
||||||
|
trait ProcessRequestTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle a request and 429 errors.
|
||||||
|
*
|
||||||
|
* @param Request $request the request
|
||||||
|
* @param array $parameters the requests parameters
|
||||||
|
*/
|
||||||
|
protected function handleRequest(
|
||||||
|
Request $request,
|
||||||
|
array $parameters,
|
||||||
|
Client $client,
|
||||||
|
LoggerInterface $logger
|
||||||
|
) {
|
||||||
|
return $this->handleRequestRecursive(
|
||||||
|
$request,
|
||||||
|
$parameters,
|
||||||
|
$client,
|
||||||
|
$logger
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* internal method to handle recursive requests.
|
||||||
|
*
|
||||||
|
* @throws BadResponseException
|
||||||
|
*/
|
||||||
|
private function handleRequestRecursive(
|
||||||
|
Request $request,
|
||||||
|
array $parameters,
|
||||||
|
Client $client,
|
||||||
|
LoggerInterface $logger,
|
||||||
|
$counter = 0
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return $client->send($request, $parameters);
|
||||||
|
} catch (BadResponseException $e) {
|
||||||
|
if (
|
||||||
|
// get 429 exceptions
|
||||||
|
$e instanceof ClientException
|
||||||
|
&& 429 === $e->getResponse()->getStatusCode()
|
||||||
|
&& count($e->getResponse()->getHeader('Retry-After')) > 0) {
|
||||||
|
if ($counter > 5) {
|
||||||
|
$logger->error('too much 429 response', [
|
||||||
|
'request' => Psr7\get($e->getRequest()),
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
$delays = $e->getResponse()->getHeader('Retry-After');
|
||||||
|
$delay = \end($delays);
|
||||||
|
|
||||||
|
sleep($delay);
|
||||||
|
|
||||||
|
return $this->handleRequestRecursive(
|
||||||
|
$request,
|
||||||
|
$parameters,
|
||||||
|
$client,
|
||||||
|
$logger,
|
||||||
|
$counter + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handling other errors
|
||||||
|
$logger->error('Error while querying ROME api', [
|
||||||
|
'status_code' => $e->getResponse()->getStatusCode(),
|
||||||
|
'part' => 'appellation',
|
||||||
|
'request' => $e->getRequest()->getBody()->getContents(),
|
||||||
|
'response' => $e->getResponse()->getBody()->getContents(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\FranceTravailApiBundle;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
|
||||||
|
class ChillFranceTravailApiBundle extends Bundle {}
|
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\FranceTravailApiBundle\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Chill\FranceTravailApiBundle\ApiHelper\PartenaireRomeAppellation;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
class RomeController extends AbstractController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var PartenaireRomeAppellation
|
||||||
|
*/
|
||||||
|
protected $apiAppellation;
|
||||||
|
|
||||||
|
public function __construct(PartenaireRomeAppellation $apiAppellation)
|
||||||
|
{
|
||||||
|
$this->apiAppellation = $apiAppellation;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route(path: '/{_locale}/france-travail/appellation/search.{_format}', name: 'chill_france_travail_api_appellation_search')]
|
||||||
|
public function appellationSearchAction(Request $request)
|
||||||
|
{
|
||||||
|
if (false === $request->query->has('q')) {
|
||||||
|
return new JsonResponse([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$appellations = $this->apiAppellation
|
||||||
|
->getListeAppellation($request->query->get('q'));
|
||||||
|
$results = [];
|
||||||
|
|
||||||
|
foreach ($appellations as $appellation) {
|
||||||
|
$appellation['id'] = 'original-'.$appellation['code'];
|
||||||
|
$appellation['text'] = $appellation['libelle'];
|
||||||
|
$results[] = $appellation;
|
||||||
|
}
|
||||||
|
|
||||||
|
$computed = new \stdClass();
|
||||||
|
$computed->pagination = (new \stdClass());
|
||||||
|
$computed->pagination->more = false;
|
||||||
|
$computed->results = $results;
|
||||||
|
|
||||||
|
return new JsonResponse($computed);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\FranceTravailApiBundle\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\Config\FileLocator;
|
||||||
|
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||||
|
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||||
|
use Symfony\Component\DependencyInjection\Loader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the class that loads and manages your bundle configuration.
|
||||||
|
*
|
||||||
|
* @see http://symfony.com/doc/current/cookbook/bundles/extension.html
|
||||||
|
*/
|
||||||
|
class ChillFranceTravailApiExtension extends Extension implements PrependExtensionInterface
|
||||||
|
{
|
||||||
|
public function load(array $configs, ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$configuration = new Configuration();
|
||||||
|
$config = $this->processConfiguration($configuration, $configs);
|
||||||
|
|
||||||
|
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||||
|
$loader->load('services.yml');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prepend(ContainerBuilder $container): void
|
||||||
|
{
|
||||||
|
$this->prependRoute($container);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function prependRoute(ContainerBuilder $container): void
|
||||||
|
{
|
||||||
|
// declare routes for france travail api bundle
|
||||||
|
$container->prependExtensionConfig('chill_main', [
|
||||||
|
'routing' => [
|
||||||
|
'resources' => [
|
||||||
|
'@ChillFranceTravailApiBundle/Resources/config/routing.yml',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\FranceTravailApiBundle\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||||
|
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the class that validates and merges configuration from your app/config files.
|
||||||
|
*
|
||||||
|
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/configuration.html}
|
||||||
|
*/
|
||||||
|
class Configuration implements ConfigurationInterface
|
||||||
|
{
|
||||||
|
public function getConfigTreeBuilder()
|
||||||
|
{
|
||||||
|
$treeBuilder = new TreeBuilder('chill_france_travail_api');
|
||||||
|
$rootNode = $treeBuilder->getRootNode();
|
||||||
|
// Here you should define the parameters that are allowed to
|
||||||
|
// configure your bundle. See the documentation linked above for
|
||||||
|
// more information on that topic.
|
||||||
|
|
||||||
|
return $treeBuilder;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
chill_france_travail_api_controllers:
|
||||||
|
resource: "@ChillFranceTravailApiBundle/Controller"
|
||||||
|
type: annotation
|
@ -0,0 +1,16 @@
|
|||||||
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
|
Chill\FranceTravailApiBundle\ApiHelper\ApiWrapper:
|
||||||
|
$clientId: '%env(FRANCE_TRAVAIL_CLIENT_ID)%'
|
||||||
|
$clientSecret: '%env(FRANCE_TRAVAIL_CLIENT_SECRET)%'
|
||||||
|
$redis: '@Chill\MainBundle\Redis\ChillRedis'
|
||||||
|
|
||||||
|
Chill\FranceTravailApiBundle\ApiHelper\PartenaireRomeAppellation: ~
|
||||||
|
|
||||||
|
Chill\FranceTravailApiBundle\Controller\RomeController:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
tags: ['controller.service_arguments']
|
@ -0,0 +1 @@
|
|||||||
|
|
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\FranceTravailApiBundle\Tests\ApiHelper;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Chill\FranceTravailApiBundle\ApiHelper\PartenaireRomeAppellation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class PartenaireRomeAppellationTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
self::bootKernel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetListeMetiersSimple()
|
||||||
|
{
|
||||||
|
/** @var PartenaireRomeAppellation $appellations */
|
||||||
|
$appellations = self::$kernel
|
||||||
|
->getContainer()
|
||||||
|
->get(PartenaireRomeAppellation::class)
|
||||||
|
;
|
||||||
|
|
||||||
|
$data = $appellations->getListeAppellation('arb');
|
||||||
|
|
||||||
|
$this->assertTrue(\is_array($data));
|
||||||
|
$this->assertNotNull($data[0]->libelle);
|
||||||
|
$this->assertNotNull($data[0]->code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetListeMetiersTooMuchRequests()
|
||||||
|
{
|
||||||
|
/** @var PartenaireRomeMetier $appellations */
|
||||||
|
$appellations = self::$kernel
|
||||||
|
->getContainer()
|
||||||
|
->get(PartenaireRomeAppellation::class)
|
||||||
|
;
|
||||||
|
|
||||||
|
$appellations->getListeAppellation('arb');
|
||||||
|
$appellations->getListeAppellation('ing');
|
||||||
|
$appellations->getListeAppellation('rob');
|
||||||
|
$appellations->getListeAppellation('chori');
|
||||||
|
$data = $appellations->getListeAppellation('camion');
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
$data[0] instanceof \stdClass,
|
||||||
|
'assert that first index of data is an instance of stdClass'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetAppellation()
|
||||||
|
{
|
||||||
|
/** @var PartenaireRomeMetier $appellations */
|
||||||
|
$appellations = self::$kernel
|
||||||
|
->getContainer()
|
||||||
|
->get(PartenaireRomeAppellation::class)
|
||||||
|
;
|
||||||
|
|
||||||
|
$a = $appellations->getListeAppellation('arb');
|
||||||
|
|
||||||
|
$full = $appellations->getAppellation($a[0]->code);
|
||||||
|
|
||||||
|
$this->assertNotNull(
|
||||||
|
$full->libelle,
|
||||||
|
'assert that libelle is not null'
|
||||||
|
);
|
||||||
|
$this->assertTrue(
|
||||||
|
$full->metier instanceof \stdClass,
|
||||||
|
'assert that metier is returned'
|
||||||
|
);
|
||||||
|
$this->assertNotNull(
|
||||||
|
$full->metier->libelle,
|
||||||
|
'assert that metier->libelle is not null'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\FranceTravailApiBundle\Tests\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class RomeControllerTest extends WebTestCase
|
||||||
|
{
|
||||||
|
public function testAppellationsearch()
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/{_locale}/pole-emploi/appellation/search');
|
||||||
|
}
|
||||||
|
}
|
16
src/Bundle/ChillJobBundle/src/ChillJobBundle.php
Normal file
16
src/Bundle/ChillJobBundle/src/ChillJobBundle.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\JobBundle;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
|
||||||
|
class ChillJobBundle extends Bundle {}
|
@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\JobBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\CRUD\Controller\EntityPersonCRUDController;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Chill\JobBundle\Entity\Immersion;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRUD Controller for reports (Frein, ...).
|
||||||
|
*/
|
||||||
|
class CSCrudReportController extends EntityPersonCRUDController
|
||||||
|
{
|
||||||
|
protected function onBeforeRedirectAfterSubmission(string $action, $entity, FormInterface $form, Request $request): ?Response
|
||||||
|
{
|
||||||
|
$next = $request->request->get('submit', 'save-and-close');
|
||||||
|
|
||||||
|
return match ($next) {
|
||||||
|
'save-and-close', 'delete-and-close' => $this->redirectToRoute('chill_job_report_index', [
|
||||||
|
'person' => $entity->getPerson()->getId(),
|
||||||
|
]),
|
||||||
|
default => parent::onBeforeRedirectAfterSubmission($action, $entity, $form, $request),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function duplicateEntity(string $action, Request $request)
|
||||||
|
{
|
||||||
|
if ('cscv' === $this->getCrudName()) {
|
||||||
|
$id = $request->query->get('duplicate_id', 0);
|
||||||
|
/** @var \Chill\JobBundle\Entity\CV $cv */
|
||||||
|
$cv = $this->getEntity($action, $id, $request);
|
||||||
|
$em = $this->managerRegistry->getManager();
|
||||||
|
|
||||||
|
$em->detach($cv);
|
||||||
|
|
||||||
|
foreach ($cv->getExperiences() as $experience) {
|
||||||
|
$cv->removeExperience($experience);
|
||||||
|
$em->detach($experience);
|
||||||
|
$cv->addExperience($experience);
|
||||||
|
}
|
||||||
|
foreach ($cv->getFormations() as $formation) {
|
||||||
|
$cv->removeFormation($formation);
|
||||||
|
$em->detach($formation);
|
||||||
|
$cv->addFormation($formation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cv;
|
||||||
|
}
|
||||||
|
if ('projet_prof' === $this->getCrudName()) {
|
||||||
|
$id = $request->query->get('duplicate_id', 0);
|
||||||
|
/** @var \Chill\JobBundle\Entity\ProjetProfessionnel $original */
|
||||||
|
$original = $this->getEntity($action, $id, $request);
|
||||||
|
|
||||||
|
$new = parent::duplicateEntity($action, $request);
|
||||||
|
|
||||||
|
foreach ($original->getSouhait() as $s) {
|
||||||
|
$new->addSouhait($s);
|
||||||
|
}
|
||||||
|
foreach ($original->getValide() as $s) {
|
||||||
|
$new->addValide($s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::duplicateEntity($action, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createFormFor(string $action, $entity, ?string $formClass = null, array $formOptions = []): FormInterface
|
||||||
|
{
|
||||||
|
if ($entity instanceof Immersion) {
|
||||||
|
if ('edit' === $action || 'new' === $action) {
|
||||||
|
return parent::createFormFor($action, $entity, $formClass, [
|
||||||
|
'center' => $entity->getPerson()->getCenter(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ('bilan' === $action) {
|
||||||
|
return parent::createFormFor($action, $entity, $formClass, [
|
||||||
|
'center' => $entity->getPerson()->getCenter(),
|
||||||
|
'step' => 'bilan',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ('delete' === $action) {
|
||||||
|
return parent::createFormFor($action, $entity, $formClass, $formOptions);
|
||||||
|
}
|
||||||
|
throw new \LogicException("this step {$action} is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::createFormFor($action, $entity, $formClass, $formOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function onPreFlush(string $action, $entity, FormInterface $form, Request $request)
|
||||||
|
{
|
||||||
|
// for immersion / edit-bilan action
|
||||||
|
if ('bilan' === $action) {
|
||||||
|
/* @var $entity Immersion */
|
||||||
|
$entity->setIsBilanFullfilled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::onPreFlush($action, $entity, $form, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit immersion bilan.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
*/
|
||||||
|
public function editBilan(Request $request, $id): Response
|
||||||
|
{
|
||||||
|
return $this->formEditAction('bilan', $request, $id);
|
||||||
|
}
|
||||||
|
}
|
150
src/Bundle/ChillJobBundle/src/Controller/CSPersonController.php
Normal file
150
src/Bundle/ChillJobBundle/src/Controller/CSPersonController.php
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\JobBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\CRUD\Controller\OneToOneEntityPersonCRUDController;
|
||||||
|
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Chill\JobBundle\Form\CSPersonPersonalSituationType;
|
||||||
|
use Chill\JobBundle\Form\CSPersonDispositifsType;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
class CSPersonController extends OneToOneEntityPersonCRUDController
|
||||||
|
{
|
||||||
|
#[Route(path: '{_locale}/person/job/personal_situation/{id}/edit', name: 'chill_crud_job_personal_situation_edit')]
|
||||||
|
public function personalSituationEdit(Request $request, $id): Response
|
||||||
|
{
|
||||||
|
return $this->formEditAction(
|
||||||
|
'ps_situation_edit',
|
||||||
|
$request,
|
||||||
|
$id,
|
||||||
|
CSPersonPersonalSituationType::class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route(path: '{_locale}/person/job/dispositifs/{id}/edit', name: 'chill_crud_job_dispositifs_edit')]
|
||||||
|
public function dispositifsEdit(Request $request, $id)
|
||||||
|
{
|
||||||
|
return $this->formEditAction(
|
||||||
|
'dispositifs_edit',
|
||||||
|
$request,
|
||||||
|
$id,
|
||||||
|
CSPersonDispositifsType::class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route(path: '{_locale}/person/job/{person}/personal_situation', name: 'chill_crud_job_personal_situation_view')]
|
||||||
|
public function personalSituationView(Request $request, $person): Response
|
||||||
|
{
|
||||||
|
return $this->viewAction('ps_situation_view', $request, $person);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route(path: '{_locale}/person/job/{person}/dispositifs', name: 'chill_crud_job_dispositifs_view')]
|
||||||
|
public function dispositifsView(Request $request, $person): Response
|
||||||
|
{
|
||||||
|
return $this->viewAction('dispositifs_view', $request, $person);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateRedirectOnCreateRoute($action, Request $request, $entity): string
|
||||||
|
{
|
||||||
|
$route = '';
|
||||||
|
|
||||||
|
switch ($action) {
|
||||||
|
case 'ps_situation_view':
|
||||||
|
$route = 'chill_crud_job_personal_situation_edit';
|
||||||
|
break;
|
||||||
|
case 'dispositifs_view':
|
||||||
|
$route = 'chill_crud_job_dispositifs_edit';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
parent::generateRedirectOnCreateRoute($action, $request, $entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->generateUrl($route, ['id' => $entity->getPerson()->getId()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkACL($action, $entity): void
|
||||||
|
{
|
||||||
|
match ($action) {
|
||||||
|
'ps_situation_edit', 'dispositifs_edit' => $this->denyAccessUnlessGranted(
|
||||||
|
PersonVoter::UPDATE,
|
||||||
|
$entity->getPerson()
|
||||||
|
),
|
||||||
|
'ps_situation_view', 'dispositifs_view' => $this->denyAccessUnlessGranted(
|
||||||
|
PersonVoter::SEE,
|
||||||
|
$entity->getPerson()
|
||||||
|
),
|
||||||
|
default => parent::checkACL($action, $entity),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function onBeforeRedirectAfterSubmission(string $action, $entity, FormInterface $form, Request $request): ?Response
|
||||||
|
{
|
||||||
|
return match ($action) {
|
||||||
|
'ps_situation_edit' => $this->redirectToRoute(
|
||||||
|
'chill_crud_'.$this->getCrudName().'_personal_situation_view',
|
||||||
|
['person' => $entity->getId()]
|
||||||
|
),
|
||||||
|
'dispositifs_edit' => $this->redirectToRoute(
|
||||||
|
'chill_crud_'.$this->getCrudName().'_dispositifs_view',
|
||||||
|
['person' => $entity->getId()]
|
||||||
|
),
|
||||||
|
default => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTemplateFor($action, $entity, Request $request): string
|
||||||
|
{
|
||||||
|
return match ($action) {
|
||||||
|
'ps_situation_edit' => '@ChillJob/CSPerson/personal_situation_edit.html.twig',
|
||||||
|
'dispositifs_edit' => '@ChillJob/CSPerson/dispositifs_edit.html.twig',
|
||||||
|
'ps_situation_view' => '@ChillJob/CSPerson/personal_situation_view.html.twig',
|
||||||
|
'dispositifs_view' => '@ChillJob/CSPerson/dispositifs_view.html.twig',
|
||||||
|
default => parent::getTemplateFor($action, $entity, $request),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createFormFor(string $action, $entity, ?string $formClass = null, array $formOptions = []): FormInterface
|
||||||
|
{
|
||||||
|
switch ($action) {
|
||||||
|
case 'ps_situation_edit':
|
||||||
|
case 'dispositifs_edit':
|
||||||
|
$form = $this->createForm($formClass, $entity, \array_merge(
|
||||||
|
$formOptions,
|
||||||
|
['center' => $entity->getPerson()->getCenter()]
|
||||||
|
));
|
||||||
|
$this->customizeForm($action, $form);
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return parent::createFormFor($action, $entity, $formClass, $formOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateLabelForButton($action, $formName, $form): string
|
||||||
|
{
|
||||||
|
switch ($action) {
|
||||||
|
case 'ps_situation_edit':
|
||||||
|
case 'dispositifs_edit':
|
||||||
|
if ('submit' === $formName) {
|
||||||
|
return 'Enregistrer';
|
||||||
|
}
|
||||||
|
throw new \LogicException("this formName is not supported: {$formName}");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 'Enregistrer';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\JobBundle\Controller;
|
||||||
|
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Chill\JobBundle\Entity\Frein;
|
||||||
|
use Chill\JobBundle\Entity\CV;
|
||||||
|
use Chill\JobBundle\Entity\Immersion;
|
||||||
|
use Chill\JobBundle\Entity\ProjetProfessionnel;
|
||||||
|
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||||
|
use Chill\JobBundle\Security\Authorization\JobVoter;
|
||||||
|
|
||||||
|
class CSReportController extends AbstractController
|
||||||
|
{
|
||||||
|
public function __construct(private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry) {}
|
||||||
|
|
||||||
|
#[Route(path: '{_locale}/person/job/{person}/report', name: 'chill_job_report_index')]
|
||||||
|
public function index(Person $person): Response
|
||||||
|
{
|
||||||
|
$this->denyAccessUnlessGranted(PersonVoter::SEE, $person, 'The access to '
|
||||||
|
.'person is denied');
|
||||||
|
|
||||||
|
$reports = $this->getReports($person);
|
||||||
|
|
||||||
|
return $this->render('@ChillJob/Report/index.html.twig', \array_merge([
|
||||||
|
'person' => $person,
|
||||||
|
], $reports));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getReports(Person $person): array
|
||||||
|
{
|
||||||
|
$results = [];
|
||||||
|
|
||||||
|
$kinds = [];
|
||||||
|
|
||||||
|
if ($this->isGranted(JobVoter::REPORT_CV, $person)) {
|
||||||
|
$kinds['cvs'] = CV::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isGranted(JobVoter::REPORT_NEW, $person)) {
|
||||||
|
$kinds = \array_merge($kinds, [
|
||||||
|
'cvs' => CV::class,
|
||||||
|
'freins' => Frein::class,
|
||||||
|
'immersions' => Immersion::class,
|
||||||
|
'projet_professionnels' => ProjetProfessionnel::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($kinds as $key => $className) {
|
||||||
|
$ordering = match ($key) {
|
||||||
|
'immersions' => ['debutDate' => 'DESC'],
|
||||||
|
default => ['reportDate' => 'DESC'],
|
||||||
|
};
|
||||||
|
$results[$key] = $this->managerRegistry->getManager()
|
||||||
|
->getRepository($className)
|
||||||
|
->findBy(['person' => $person], $ordering);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\JobBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\CRUD\Controller\EntityPersonCRUDController;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRUD Controller for reports (Frein, ...).
|
||||||
|
*/
|
||||||
|
class CVCrudController extends EntityPersonCRUDController
|
||||||
|
{
|
||||||
|
protected function onBeforeRedirectAfterSubmission(string $action, $entity, FormInterface $form, Request $request): ?Response
|
||||||
|
{
|
||||||
|
$next = $request->request->get('submit', 'save-and-close');
|
||||||
|
|
||||||
|
return match ($next) {
|
||||||
|
'save-and-close', 'delete-and-close' => $this->redirectToRoute('chill_job_report_index', [
|
||||||
|
'person' => $entity->getPerson()->getId(),
|
||||||
|
]),
|
||||||
|
default => parent::onBeforeRedirectAfterSubmission($action, $entity, $form, $request),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function duplicateEntity(string $action, Request $request)
|
||||||
|
{
|
||||||
|
if ('cv' === $this->getCrudName()) {
|
||||||
|
$id = $request->query->get('duplicate_id', 0);
|
||||||
|
/** @var \Chill\JobBundle\Entity\CV $cv */
|
||||||
|
$cv = $this->getEntity($action, $id, $request);
|
||||||
|
$em = $this->managerRegistry->getManager();
|
||||||
|
|
||||||
|
$em->detach($cv);
|
||||||
|
|
||||||
|
foreach ($cv->getExperiences() as $experience) {
|
||||||
|
$cv->removeExperience($experience);
|
||||||
|
$em->detach($experience);
|
||||||
|
$cv->addExperience($experience);
|
||||||
|
}
|
||||||
|
foreach ($cv->getFormations() as $formation) {
|
||||||
|
$cv->removeFormation($formation);
|
||||||
|
$em->detach($formation);
|
||||||
|
$cv->addFormation($formation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::duplicateEntity($action, $request);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user