mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-11-04 03:08:25 +00:00 
			
		
		
		
	Compare commits
	
		
			34 Commits
		
	
	
		
			v3.0.0-RC8
			...
			288-signat
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d33dcacc46 | |||
| 8d97df9f96 | |||
| 2822800c76 | |||
| 8973b7c20b | |||
| 7f144da1a7 | |||
| ab4193938d | |||
| e2426ba1d8 | |||
| 8209990437 | |||
| b1885de3e2 | |||
| 218280304c | |||
| 8a7b48b201 | |||
| 52a9aab73f | |||
| 8f358112b1 | |||
| 57a07af3db | |||
| fd216ff66e | |||
| 689c2c574a | |||
| a8de18beac | |||
| babca5fc0f | |||
| f2c5663b05 | |||
| ba95687f46 | |||
| 
						
						
							
						
						a309cc0774
	
				 | 
					
					
						|||
| 
						
						
							
						
						3db4fff80d
	
				 | 
					
					
						|||
| 
						
						
							
						
						c9d54a5fea
	
				 | 
					
					
						|||
| 
						
						
							
						
						86c862e69d
	
				 | 
					
					
						|||
| 
						
						
							
						
						9bc6fe6aff
	
				 | 
					
					
						|||
| 
						
						
							
						
						18a03fd740
	
				 | 
					
					
						|||
| 
						
						
							
						
						610239930b
	
				 | 
					
					
						|||
| b65e2c62c4 | |||
| 
						
						
							
						
						89f5231649
	
				 | 
					
					
						|||
| 
						
						
							
						
						99818c211d
	
				 | 
					
					
						|||
| 
						
						
							
						
						a9f0059743
	
				 | 
					
					
						|||
| 
						
						
							
						
						5bc542a567
	
				 | 
					
					
						|||
| 
						
						
							
						
						c8ccce83fd
	
				 | 
					
					
						|||
| 4a229ebf6b | 
							
								
								
									
										5
									
								
								.changes/unreleased/Feature-20231212-154841.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.changes/unreleased/Feature-20231212-154841.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
kind: Feature
 | 
			
		||||
body: '[DX] move async-upload-bundle features into chill-bundles'
 | 
			
		||||
time: 2023-12-12T15:48:41.954970271+01:00
 | 
			
		||||
custom:
 | 
			
		||||
  Issue: "221"
 | 
			
		||||
							
								
								
									
										6
									
								
								.changes/unreleased/Feature-20240530-160003.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Feature-20240530-160003.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
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: ""
 | 
			
		||||
							
								
								
									
										6
									
								
								.changes/unreleased/Feature-20240531-190242.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Feature-20240531-190242.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
kind: Feature
 | 
			
		||||
body: |
 | 
			
		||||
  Upgrade CKEditor and refactor configuration with use of typescript
 | 
			
		||||
time: 2024-05-31T19:02:42.776662753+02:00
 | 
			
		||||
custom:
 | 
			
		||||
  Issue: ""
 | 
			
		||||
							
								
								
									
										8
									
								
								.changes/unreleased/Feature-20240614-153236.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.changes/unreleased/Feature-20240614-153236.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
kind: Feature
 | 
			
		||||
body: |-
 | 
			
		||||
  Electronic signature
 | 
			
		||||
 | 
			
		||||
  Implementation of the electronic signature for documents within chill.
 | 
			
		||||
time: 2024-06-14T15:32:36.875891692+02:00
 | 
			
		||||
custom:
 | 
			
		||||
  Issue: ""
 | 
			
		||||
							
								
								
									
										5
									
								
								.changes/unreleased/Feature-20240718-151233.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.changes/unreleased/Feature-20240718-151233.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
kind: Feature
 | 
			
		||||
body: Metadata form added for person signatures
 | 
			
		||||
time: 2024-07-18T15:12:33.8134266+02:00
 | 
			
		||||
custom:
 | 
			
		||||
  Issue: "288"
 | 
			
		||||
							
								
								
									
										6
									
								
								.changes/unreleased/Fixed-20240410-103736.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Fixed-20240410-103736.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
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: ""
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
## 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.
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
## 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 
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
## v2.22.2 - 2024-07-03
 | 
			
		||||
### Fixed
 | 
			
		||||
* Remove scope required for event participation stats 
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
## 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  
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
## v2.23.1 - 2024-07-25
 | 
			
		||||
### Fixed
 | 
			
		||||
* Fix export activities linked to accompanying period between two dates 
 | 
			
		||||
							
								
								
									
										29
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -6,35 +6,6 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
 | 
			
		||||
and is generated by [Changie](https://github.com/miniscruff/changie).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 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
 | 
			
		||||
### Feature
 | 
			
		||||
* Add flash menu buttons in search results, to open directly a new calendar, or a new activity in an accompanying period
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@
 | 
			
		||||
        "phpoffice/phpspreadsheet": "^1.16",
 | 
			
		||||
        "ramsey/uuid-doctrine": "^1.7",
 | 
			
		||||
        "sensio/framework-extra-bundle": "^5.5",
 | 
			
		||||
        "smalot/pdfparser": "^2.10",
 | 
			
		||||
        "spomky-labs/base64url": "^2.0",
 | 
			
		||||
        "symfony/asset": "^5.4",
 | 
			
		||||
        "symfony/browser-kit": "^5.4",
 | 
			
		||||
@@ -114,8 +115,6 @@
 | 
			
		||||
            "Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
 | 
			
		||||
            "Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
 | 
			
		||||
            "Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
 | 
			
		||||
            "Chill\\FranceTravailApiBundle\\": "src/Bundle/ChillFranceTravailApiBundle/src",
 | 
			
		||||
            "Chill\\JobBundle\\": "src/Bundle/ChillJobBundle/src",
 | 
			
		||||
            "Chill\\MainBundle\\": "src/Bundle/ChillMainBundle",
 | 
			
		||||
            "Chill\\PersonBundle\\": "src/Bundle/ChillPersonBundle",
 | 
			
		||||
            "Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
 | 
			
		||||
class BirthdateFilter implements ExportElementValidatedInterface, FilterInterface
 | 
			
		||||
{
 | 
			
		||||
    // add specific role for this filter
 | 
			
		||||
    public function addRole(): ?string
 | 
			
		||||
    public function addRole()
 | 
			
		||||
    {
 | 
			
		||||
        // we do not need any new role for this filter, so we 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
 | 
			
		||||
   # install chill and some dependencies
 | 
			
		||||
   # 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-RC3 champs-libres/wopi-lib dev-master@dev champs-libres/wopi-bundle dev-master@dev
 | 
			
		||||
   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
 | 
			
		||||
 | 
			
		||||
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).
 | 
			
		||||
@@ -110,14 +110,15 @@ you can either:
 | 
			
		||||
  .. code-block:: env
 | 
			
		||||
 | 
			
		||||
     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,
 | 
			
		||||
  not the password in clear text).
 | 
			
		||||
 | 
			
		||||
- set up the jwt authentication bundle
 | 
			
		||||
 | 
			
		||||
Some environment variables are available for the JWT authentication bundle in the :code:`.env` file. 
 | 
			
		||||
Some environment variables are available for the JWT authentication bundle in the :code:`.env` file. You must also run the command
 | 
			
		||||
: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
 | 
			
		||||
**********************************
 | 
			
		||||
@@ -135,8 +136,6 @@ To continue the installation process, you will have to run migrations:
 | 
			
		||||
   symfony console messenger:setup-transports
 | 
			
		||||
   # prepare some views
 | 
			
		||||
   symfony console chill:db:sync-views
 | 
			
		||||
   # generate jwt token, required for some api features (webdav access, ...)
 | 
			
		||||
   symfony console lexik:jwt:generate-keypair
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
    "popper.js": "^1.16.1",
 | 
			
		||||
    "postcss-loader": "^7.0.2",
 | 
			
		||||
    "raw-loader": "^4.0.2",
 | 
			
		||||
    "sass-loader": "^14.0.0",
 | 
			
		||||
    "sass-loader": "^13.0.0",
 | 
			
		||||
    "select2": "^4.0.13",
 | 
			
		||||
    "select2-bootstrap-theme": "0.1.0-beta.10",
 | 
			
		||||
    "style-loader": "^3.3.1",
 | 
			
		||||
@@ -58,7 +58,7 @@
 | 
			
		||||
    "vue": "^3.2.37",
 | 
			
		||||
    "vue-i18n": "^9.1.6",
 | 
			
		||||
    "vue-multiselect": "3.0.0-alpha.2",
 | 
			
		||||
    "vue-toast-notification": "^3.1.2",
 | 
			
		||||
    "vue-toast-notification": "^2.0",
 | 
			
		||||
    "vuex": "^4.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "browserslist": [
 | 
			
		||||
 
 | 
			
		||||
@@ -1,29 +1,34 @@
 | 
			
		||||
parameters:
 | 
			
		||||
	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\\.$#"
 | 
			
		||||
			count: 1
 | 
			
		||||
			path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php
 | 
			
		||||
 | 
			
		||||
		-
 | 
			
		||||
			message: "#^Property Chill\\\\CustomFieldsBundle\\\\Entity\\\\CustomField\\:\\:\\$required \\(false\\) does not accept bool\\.$#"
 | 
			
		||||
			message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
 | 
			
		||||
			count: 1
 | 
			
		||||
			path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php
 | 
			
		||||
			path: src/Bundle/ChillPersonBundle/Form/PersonType.php
 | 
			
		||||
 | 
			
		||||
		-
 | 
			
		||||
			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: 2
 | 
			
		||||
			path: src/Bundle/ChillDocStoreBundle/Controller/DocumentAccompanyingCourseController.php
 | 
			
		||||
			message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
 | 
			
		||||
			count: 1
 | 
			
		||||
			path: src/Bundle/ChillMainBundle/Templating/ChillTwigRoutingHelper.php
 | 
			
		||||
 | 
			
		||||
		-
 | 
			
		||||
			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\\.$#"
 | 
			
		||||
			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
 | 
			
		||||
			path: src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php
 | 
			
		||||
			path: src/Bundle/ChillMainBundle/Repository/NotificationRepository.php
 | 
			
		||||
 | 
			
		||||
		-
 | 
			
		||||
			message: "#^Foreach overwrites \\$key with its key variable\\.$#"
 | 
			
		||||
			count: 1
 | 
			
		||||
			path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
 | 
			
		||||
 | 
			
		||||
		-
 | 
			
		||||
			message: "#^Variable \\$participation might not be defined\\.$#"
 | 
			
		||||
@@ -35,106 +40,6 @@ parameters:
 | 
			
		||||
			count: 1
 | 
			
		||||
			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\\}\\.$#"
 | 
			
		||||
			count: 1
 | 
			
		||||
@@ -160,31 +65,11 @@ parameters:
 | 
			
		||||
			count: 1
 | 
			
		||||
			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\\.$#"
 | 
			
		||||
			count: 1
 | 
			
		||||
			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\\.$#"
 | 
			
		||||
			count: 1
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								rector.php
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								rector.php
									
									
									
									
									
								
							@@ -28,9 +28,6 @@ return static function (RectorConfig $rectorConfig): void {
 | 
			
		||||
 | 
			
		||||
    // register a single rule
 | 
			
		||||
    $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
 | 
			
		||||
    $rectorConfig->rule(\Rector\Symfony\Symfony53\Rector\StaticPropertyFetch\KernelTestCaseContainerPropertyDeprecationRector::class);
 | 
			
		||||
@@ -39,14 +36,14 @@ return static function (RectorConfig $rectorConfig): void {
 | 
			
		||||
 | 
			
		||||
    //define sets of rules
 | 
			
		||||
    $rectorConfig->sets([
 | 
			
		||||
        LevelSetList::UP_TO_PHP_82,
 | 
			
		||||
        \Rector\Symfony\Set\SymfonySetList::SYMFONY_40,
 | 
			
		||||
        \Rector\Symfony\Set\SymfonySetList::SYMFONY_41,
 | 
			
		||||
        \Rector\Symfony\Set\SymfonySetList::SYMFONY_42,
 | 
			
		||||
        \Rector\Symfony\Set\SymfonySetList::SYMFONY_43,
 | 
			
		||||
        \Rector\Symfony\Set\SymfonySetList::SYMFONY_44,
 | 
			
		||||
        \Rector\Symfony\Set\SymfonySetList::SYMFONY_50,
 | 
			
		||||
        \Rector\Symfony\Set\SymfonySetList::SYMFONY_50_TYPES,
 | 
			
		||||
        \Rector\Symfony\Set\SymfonySetList::SYMFONY_51,
 | 
			
		||||
        \Rector\Symfony\Set\SymfonySetList::SYMFONY_52,
 | 
			
		||||
        \Rector\Symfony\Set\SymfonySetList::SYMFONY_53,
 | 
			
		||||
        \Rector\Symfony\Set\SymfonySetList::SYMFONY_54,
 | 
			
		||||
        \Rector\Doctrine\Set\DoctrineSetList::DOCTRINE_CODE_QUALITY,
 | 
			
		||||
        \Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_90,
 | 
			
		||||
        \Rector\Doctrine\Set\DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES,
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    $rectorConfig->ruleWithConfiguration(\Rector\Php80\Rector\Class_\AnnotationToAttributeRector::class, [
 | 
			
		||||
 
 | 
			
		||||
@@ -99,10 +99,10 @@ final class ActivityController extends AbstractController
 | 
			
		||||
 | 
			
		||||
        $form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
 | 
			
		||||
 | 
			
		||||
        if (Request::METHOD_POST === $request->getMethod()) {
 | 
			
		||||
        if (Request::METHOD_DELETE === $request->getMethod()) {
 | 
			
		||||
            $form->handleRequest($request);
 | 
			
		||||
 | 
			
		||||
            if ($form->isSubmitted() && $form->isValid()) {
 | 
			
		||||
            if ($form->isValid()) {
 | 
			
		||||
                $this->logger->notice('An activity has been removed', [
 | 
			
		||||
                    'by_user' => $this->getUser()->getUsername(),
 | 
			
		||||
                    'activity_id' => $activity->getId(),
 | 
			
		||||
@@ -640,6 +640,7 @@ final class ActivityController extends AbstractController
 | 
			
		||||
 | 
			
		||||
        return $this->createFormBuilder()
 | 
			
		||||
            ->setAction($this->generateUrl('chill_activity_activity_delete', $params))
 | 
			
		||||
            ->setMethod('DELETE')
 | 
			
		||||
            ->add('submit', SubmitType::class, ['label' => 'Delete'])
 | 
			
		||||
            ->getForm();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -79,9 +79,11 @@ class ActivityReason
 | 
			
		||||
    /**
 | 
			
		||||
     * Set active.
 | 
			
		||||
     *
 | 
			
		||||
     * @param bool $active
 | 
			
		||||
     *
 | 
			
		||||
     * @return ActivityReason
 | 
			
		||||
     */
 | 
			
		||||
    public function setActive(bool $active)
 | 
			
		||||
    public function setActive($active)
 | 
			
		||||
    {
 | 
			
		||||
        $this->active = $active;
 | 
			
		||||
 | 
			
		||||
@@ -108,9 +110,11 @@ class ActivityReason
 | 
			
		||||
    /**
 | 
			
		||||
     * Set name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $name
 | 
			
		||||
     *
 | 
			
		||||
     * @return ActivityReason
 | 
			
		||||
     */
 | 
			
		||||
    public function setName(array $name)
 | 
			
		||||
    public function setName($name)
 | 
			
		||||
    {
 | 
			
		||||
        $this->name = $name;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -152,7 +152,7 @@ class ListActivityHelper
 | 
			
		||||
                    return '';
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return $this->translator->trans((string) $value);
 | 
			
		||||
                return $this->translator->trans($value);
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,7 @@ final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInt
 | 
			
		||||
 | 
			
		||||
        $qb->andWhere(
 | 
			
		||||
            $qb->expr()->exists(
 | 
			
		||||
                'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = activity.accompanyingPeriod AND {$alias}.id = activity.id"
 | 
			
		||||
                'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = acp"
 | 
			
		||||
            )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,9 +15,9 @@ use Chill\ActivityBundle\Entity\ActivityType;
 | 
			
		||||
use Doctrine\ORM\EntityManagerInterface;
 | 
			
		||||
use Doctrine\ORM\EntityRepository;
 | 
			
		||||
 | 
			
		||||
final readonly class ActivityTypeRepository implements ActivityTypeRepositoryInterface
 | 
			
		||||
final class ActivityTypeRepository implements ActivityTypeRepositoryInterface
 | 
			
		||||
{
 | 
			
		||||
    private EntityRepository $repository;
 | 
			
		||||
    private readonly EntityRepository $repository;
 | 
			
		||||
 | 
			
		||||
    public function __construct(EntityManagerInterface $em)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@
 | 
			
		||||
                            <li>
 | 
			
		||||
                                {% if bloc.type == 'user' %}
 | 
			
		||||
                                    <span class="badge-user">
 | 
			
		||||
                                        hello
 | 
			
		||||
                                        {{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false, 'at_date': entity.date }) }}
 | 
			
		||||
                                    </span>
 | 
			
		||||
                                {% else %}
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ abstract class AbstractElementController extends AbstractController
 | 
			
		||||
            $indexPage = 'chill_budget_elements_household_index';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (Request::METHOD_POST === $request->getMethod()) {
 | 
			
		||||
        if (Request::METHOD_DELETE === $request->getMethod()) {
 | 
			
		||||
            $form->handleRequest($request);
 | 
			
		||||
 | 
			
		||||
            if ($form->isSubmitted() && $form->isValid()) {
 | 
			
		||||
@@ -198,9 +198,10 @@ abstract class AbstractElementController extends AbstractController
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a form to delete a help request entity by id.
 | 
			
		||||
     */
 | 
			
		||||
    private function createDeleteForm(): \Symfony\Component\Form\FormInterface
 | 
			
		||||
    private function createDeleteForm(): Form
 | 
			
		||||
    {
 | 
			
		||||
        return $this->createFormBuilder()
 | 
			
		||||
            ->setMethod(Request::METHOD_DELETE)
 | 
			
		||||
            ->add('submit', SubmitType::class, ['label' => 'Delete'])
 | 
			
		||||
            ->getForm();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,7 @@ class Charge extends AbstractElement implements HasCentersInterface
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setHelp(?string $help)
 | 
			
		||||
    public function setHelp($help)
 | 
			
		||||
    {
 | 
			
		||||
        $this->help = $help;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,9 +15,9 @@ use Chill\BudgetBundle\Entity\ChargeKind;
 | 
			
		||||
use Doctrine\ORM\EntityManagerInterface;
 | 
			
		||||
use Doctrine\ORM\EntityRepository;
 | 
			
		||||
 | 
			
		||||
final readonly class ChargeKindRepository implements ChargeKindRepositoryInterface
 | 
			
		||||
final class ChargeKindRepository implements ChargeKindRepositoryInterface
 | 
			
		||||
{
 | 
			
		||||
    private EntityRepository $repository;
 | 
			
		||||
    private readonly EntityRepository $repository;
 | 
			
		||||
 | 
			
		||||
    public function __construct(EntityManagerInterface $entityManager)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,9 +15,9 @@ use Chill\BudgetBundle\Entity\ResourceKind;
 | 
			
		||||
use Doctrine\ORM\EntityManagerInterface;
 | 
			
		||||
use Doctrine\ORM\EntityRepository;
 | 
			
		||||
 | 
			
		||||
final readonly class ResourceKindRepository implements ResourceKindRepositoryInterface
 | 
			
		||||
final class ResourceKindRepository implements ResourceKindRepositoryInterface
 | 
			
		||||
{
 | 
			
		||||
    private EntityRepository $repository;
 | 
			
		||||
    private readonly EntityRepository $repository;
 | 
			
		||||
 | 
			
		||||
    public function __construct(EntityManagerInterface $entityManager)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,7 @@ class CalendarController extends AbstractController
 | 
			
		||||
 | 
			
		||||
        $form = $this->createDeleteForm($entity);
 | 
			
		||||
 | 
			
		||||
        if (Request::METHOD_POST === $request->getMethod()) {
 | 
			
		||||
        if (Request::METHOD_DELETE === $request->getMethod()) {
 | 
			
		||||
            $form->handleRequest($request);
 | 
			
		||||
 | 
			
		||||
            if ($form->isValid()) {
 | 
			
		||||
@@ -512,6 +512,7 @@ class CalendarController extends AbstractController
 | 
			
		||||
    {
 | 
			
		||||
        return $this->createFormBuilder()
 | 
			
		||||
            ->setAction($this->generateUrl('chill_calendar_calendar_delete', ['id' => $calendar->getId()]))
 | 
			
		||||
            ->setMethod('DELETE')
 | 
			
		||||
            ->add('submit', SubmitType::class, ['label' => 'Delete'])
 | 
			
		||||
            ->getForm();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -172,9 +172,11 @@ class CustomField
 | 
			
		||||
    /**
 | 
			
		||||
     * Set active.
 | 
			
		||||
     *
 | 
			
		||||
     * @param bool $active
 | 
			
		||||
     *
 | 
			
		||||
     * @return CustomField
 | 
			
		||||
     */
 | 
			
		||||
    public function setActive(bool $active)
 | 
			
		||||
    public function setActive($active)
 | 
			
		||||
    {
 | 
			
		||||
        $this->active = $active;
 | 
			
		||||
 | 
			
		||||
@@ -222,16 +224,18 @@ class CustomField
 | 
			
		||||
    /**
 | 
			
		||||
     * Set order.
 | 
			
		||||
     *
 | 
			
		||||
     * @param float $order
 | 
			
		||||
     *
 | 
			
		||||
     * @return CustomField
 | 
			
		||||
     */
 | 
			
		||||
    public function setOrdering(?float $order)
 | 
			
		||||
    public function setOrdering($order)
 | 
			
		||||
    {
 | 
			
		||||
        $this->ordering = $order;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setRequired(bool $required)
 | 
			
		||||
    public function setRequired($required)
 | 
			
		||||
    {
 | 
			
		||||
        $this->required = $required;
 | 
			
		||||
 | 
			
		||||
@@ -241,7 +245,7 @@ class CustomField
 | 
			
		||||
    /**
 | 
			
		||||
     * @return $this
 | 
			
		||||
     */
 | 
			
		||||
    public function setSlug(?string $slug)
 | 
			
		||||
    public function setSlug($slug)
 | 
			
		||||
    {
 | 
			
		||||
        $this->slug = $slug;
 | 
			
		||||
 | 
			
		||||
@@ -251,9 +255,11 @@ class CustomField
 | 
			
		||||
    /**
 | 
			
		||||
     * Set type.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $type
 | 
			
		||||
     *
 | 
			
		||||
     * @return CustomField
 | 
			
		||||
     */
 | 
			
		||||
    public function setType(?string $type)
 | 
			
		||||
    public function setType($type)
 | 
			
		||||
    {
 | 
			
		||||
        $this->type = $type;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -129,7 +129,7 @@ class Option
 | 
			
		||||
    /**
 | 
			
		||||
     * @return $this
 | 
			
		||||
     */
 | 
			
		||||
    public function setActive(bool $active)
 | 
			
		||||
    public function setActive($active)
 | 
			
		||||
    {
 | 
			
		||||
        $this->active = $active;
 | 
			
		||||
 | 
			
		||||
@@ -139,7 +139,7 @@ class Option
 | 
			
		||||
    /**
 | 
			
		||||
     * @return $this
 | 
			
		||||
     */
 | 
			
		||||
    public function setInternalKey(string $internal_key)
 | 
			
		||||
    public function setInternalKey($internal_key)
 | 
			
		||||
    {
 | 
			
		||||
        $this->internalKey = $internal_key;
 | 
			
		||||
 | 
			
		||||
@@ -149,7 +149,7 @@ class Option
 | 
			
		||||
    /**
 | 
			
		||||
     * @return $this
 | 
			
		||||
     */
 | 
			
		||||
    public function setKey(?string $key)
 | 
			
		||||
    public function setKey($key)
 | 
			
		||||
    {
 | 
			
		||||
        $this->key = $key;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ class CustomFieldsDefaultGroup
 | 
			
		||||
     *
 | 
			
		||||
     * @return CustomFieldsDefaultGroup
 | 
			
		||||
     */
 | 
			
		||||
    public function setCustomFieldsGroup(?CustomFieldsGroup $customFieldsGroup)
 | 
			
		||||
    public function setCustomFieldsGroup($customFieldsGroup)
 | 
			
		||||
    {
 | 
			
		||||
        $this->customFieldsGroup = $customFieldsGroup;
 | 
			
		||||
 | 
			
		||||
@@ -79,9 +79,11 @@ class CustomFieldsDefaultGroup
 | 
			
		||||
    /**
 | 
			
		||||
     * Set entity.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $entity
 | 
			
		||||
     *
 | 
			
		||||
     * @return CustomFieldsDefaultGroup
 | 
			
		||||
     */
 | 
			
		||||
    public function setEntity(?string $entity)
 | 
			
		||||
    public function setEntity($entity)
 | 
			
		||||
    {
 | 
			
		||||
        $this->entity = $entity;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -165,9 +165,11 @@ class CustomFieldsGroup
 | 
			
		||||
    /**
 | 
			
		||||
     * Set entity.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $entity
 | 
			
		||||
     *
 | 
			
		||||
     * @return CustomFieldsGroup
 | 
			
		||||
     */
 | 
			
		||||
    public function setEntity(?string $entity)
 | 
			
		||||
    public function setEntity($entity)
 | 
			
		||||
    {
 | 
			
		||||
        $this->entity = $entity;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,14 +21,14 @@ use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
 | 
			
		||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
 | 
			
		||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
 | 
			
		||||
 | 
			
		||||
final readonly class RelatorioDriver implements DriverInterface
 | 
			
		||||
final class RelatorioDriver implements DriverInterface
 | 
			
		||||
{
 | 
			
		||||
    private string $url;
 | 
			
		||||
    private readonly string $url;
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private HttpClientInterface $client,
 | 
			
		||||
        private readonly HttpClientInterface $client,
 | 
			
		||||
        ParameterBagInterface $parameterBag,
 | 
			
		||||
        private LoggerInterface $logger
 | 
			
		||||
        private readonly LoggerInterface $logger
 | 
			
		||||
    ) {
 | 
			
		||||
        $this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -16,11 +16,11 @@ use Doctrine\ORM\EntityManagerInterface;
 | 
			
		||||
use Doctrine\ORM\EntityRepository;
 | 
			
		||||
use Symfony\Component\HttpFoundation\RequestStack;
 | 
			
		||||
 | 
			
		||||
final readonly class DocGeneratorTemplateRepository implements DocGeneratorTemplateRepositoryInterface
 | 
			
		||||
final class DocGeneratorTemplateRepository implements DocGeneratorTemplateRepositoryInterface
 | 
			
		||||
{
 | 
			
		||||
    private EntityRepository $repository;
 | 
			
		||||
    private readonly EntityRepository $repository;
 | 
			
		||||
 | 
			
		||||
    public function __construct(EntityManagerInterface $entityManager, private RequestStack $requestStack)
 | 
			
		||||
    public function __construct(EntityManagerInterface $entityManager, private readonly RequestStack $requestStack)
 | 
			
		||||
    {
 | 
			
		||||
        $this->repository = $entityManager->getRepository(DocGeneratorTemplate::class);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,46 @@
 | 
			
		||||
<?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\DocStoreBundle\Controller;
 | 
			
		||||
 | 
			
		||||
use Chill\DocStoreBundle\Entity\StoredObject;
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\RequestPdfSignMessage;
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\PDFPage;
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone;
 | 
			
		||||
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
 | 
			
		||||
use Symfony\Component\HttpFoundation\Response;
 | 
			
		||||
use Symfony\Component\Messenger\MessageBusInterface;
 | 
			
		||||
use Symfony\Component\Routing\Annotation\Route;
 | 
			
		||||
 | 
			
		||||
class SignatureRequestController
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private MessageBusInterface $messageBus,
 | 
			
		||||
        private StoredObjectManagerInterface $storedObjectManager,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    #[Route('/api/1.0/document/workflow/{id}/signature-request', name: 'chill_docstore_signature_request')]
 | 
			
		||||
    public function processSignature(StoredObject $storedObject): Response
 | 
			
		||||
    {
 | 
			
		||||
        $content = $this->storedObjectManager->read($storedObject);
 | 
			
		||||
 | 
			
		||||
        $this->messageBus->dispatch(new RequestPdfSignMessage(
 | 
			
		||||
            0,
 | 
			
		||||
            new PDFSignatureZone(10.0, 10.0, 180.0, 180.0, new PDFPage(0, 500.0, 800.0)),
 | 
			
		||||
            0,
 | 
			
		||||
            'test signature',
 | 
			
		||||
            'Mme Caroline Diallo',
 | 
			
		||||
            $content
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        return new Response('<html><head><title>test</title></head><body><p>ok</p></body></html>');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -129,7 +129,7 @@ class Document implements TrackCreationInterface, TrackUpdateInterface
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setUser(?\Chill\MainBundle\Entity\User $user): self
 | 
			
		||||
    public function setUser($user): self
 | 
			
		||||
    {
 | 
			
		||||
        $this->user = $user;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -86,7 +86,7 @@ class DocumentCategory
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setDocumentClass(?string $documentClass): self
 | 
			
		||||
    public function setDocumentClass($documentClass): self
 | 
			
		||||
    {
 | 
			
		||||
        $this->documentClass = $documentClass;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -55,14 +55,14 @@ class PersonDocument extends Document implements HasCenterInterface, HasScopeInt
 | 
			
		||||
        return $this->scope;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setPerson(Person $person): self
 | 
			
		||||
    public function setPerson($person): self
 | 
			
		||||
    {
 | 
			
		||||
        $this->person = $person;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setScope(?Scope $scope): self
 | 
			
		||||
    public function setScope($scope): self
 | 
			
		||||
    {
 | 
			
		||||
        $this->scope = $scope;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,23 @@
 | 
			
		||||
<?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\DocStoreBundle\Service\Signature\Driver\BaseSigner;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Message which is received when a pdf is signed.
 | 
			
		||||
 */
 | 
			
		||||
final readonly class PdfSignedMessage
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        public readonly int $signatureId,
 | 
			
		||||
        public readonly string $content
 | 
			
		||||
    ) {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
<?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\DocStoreBundle\Service\Signature\Driver\BaseSigner;
 | 
			
		||||
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
 | 
			
		||||
 | 
			
		||||
final readonly class PdfSignedMessageHandler implements MessageHandlerInterface
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * log prefix.
 | 
			
		||||
     */
 | 
			
		||||
    private const P = '[pdf signed message] ';
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private LoggerInterface $logger,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public function __invoke(PdfSignedMessage $message): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->logger->info(self::P.'a message is received', ['signaturedId' => $message->signatureId]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,66 @@
 | 
			
		||||
<?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\DocStoreBundle\Service\Signature\Driver\BaseSigner;
 | 
			
		||||
 | 
			
		||||
use Symfony\Component\Messenger\Envelope;
 | 
			
		||||
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
 | 
			
		||||
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Decode (and requeue) @see{PdfSignedMessage}, which comes from an external producer.
 | 
			
		||||
 */
 | 
			
		||||
final readonly class PdfSignedMessageSerializer implements SerializerInterface
 | 
			
		||||
{
 | 
			
		||||
    public function decode(array $encodedEnvelope): Envelope
 | 
			
		||||
    {
 | 
			
		||||
        $body = $encodedEnvelope['body'];
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $decoded = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
 | 
			
		||||
        } catch (\JsonException $e) {
 | 
			
		||||
            throw new MessageDecodingFailedException('Could not deserialize message', previous: $e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!array_key_exists('signatureId', $decoded) || !array_key_exists('content', $decoded)) {
 | 
			
		||||
            throw new MessageDecodingFailedException('Could not find expected keys: signatureId or content');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $content = base64_decode($decoded['content'], true);
 | 
			
		||||
 | 
			
		||||
        if (false === $content) {
 | 
			
		||||
            throw new MessageDecodingFailedException('Invalid character found in the base64 encoded content');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $message = new PdfSignedMessage($decoded['signatureId'], $content);
 | 
			
		||||
 | 
			
		||||
        return new Envelope($message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function encode(Envelope $envelope): array
 | 
			
		||||
    {
 | 
			
		||||
        $message = $envelope->getMessage();
 | 
			
		||||
 | 
			
		||||
        if (!$message instanceof PdfSignedMessage) {
 | 
			
		||||
            throw new MessageDecodingFailedException('Expected a PdfSignedMessage');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'signatureId' => $message->signatureId,
 | 
			
		||||
            'content' => base64_encode($message->content),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'body' => json_encode($data, JSON_THROW_ON_ERROR),
 | 
			
		||||
            'headers' => [],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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\DocStoreBundle\Service\Signature\Driver\BaseSigner;
 | 
			
		||||
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Message which is sent when we request a signature on a pdf.
 | 
			
		||||
 */
 | 
			
		||||
final readonly class RequestPdfSignMessage
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        public int $signatureId,
 | 
			
		||||
        public PDFSignatureZone $PDFSignatureZone,
 | 
			
		||||
        public int $signatureZoneIndex,
 | 
			
		||||
        public string $reason,
 | 
			
		||||
        public string $signerText,
 | 
			
		||||
        public string $content,
 | 
			
		||||
    ) {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,105 @@
 | 
			
		||||
<?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\DocStoreBundle\Service\Signature\Driver\BaseSigner;
 | 
			
		||||
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone;
 | 
			
		||||
use Symfony\Component\Messenger\Envelope;
 | 
			
		||||
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
 | 
			
		||||
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
 | 
			
		||||
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
 | 
			
		||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
 | 
			
		||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
 | 
			
		||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Serialize a RequestPdfSignMessage, for external consumer.
 | 
			
		||||
 */
 | 
			
		||||
final readonly class RequestPdfSignMessageSerializer implements SerializerInterface
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private NormalizerInterface $normalizer,
 | 
			
		||||
        private DenormalizerInterface $denormalizer,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public function decode(array $encodedEnvelope): Envelope
 | 
			
		||||
    {
 | 
			
		||||
        $body = $encodedEnvelope['body'];
 | 
			
		||||
        $headers = $encodedEnvelope['headers'];
 | 
			
		||||
 | 
			
		||||
        if (RequestPdfSignMessage::class !== ($headers['Message'] ?? null)) {
 | 
			
		||||
            throw new MessageDecodingFailedException('serializer does not support this message');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $data = json_decode($body, true);
 | 
			
		||||
 | 
			
		||||
        $zoneSignature = $this->denormalizer->denormalize($data['signatureZone'], PDFSignatureZone::class, 'json', [
 | 
			
		||||
            AbstractNormalizer::GROUPS => ['write'],
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $content = base64_decode($data['content'], true);
 | 
			
		||||
 | 
			
		||||
        if (false === $content) {
 | 
			
		||||
            throw new MessageDecodingFailedException('the content could not be converted from base64 encoding');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $message = new RequestPdfSignMessage(
 | 
			
		||||
            $data['signatureId'],
 | 
			
		||||
            $zoneSignature,
 | 
			
		||||
            $data['signatureZoneIndex'],
 | 
			
		||||
            $data['reason'],
 | 
			
		||||
            $data['signerText'],
 | 
			
		||||
            $content,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // in case of redelivery, unserialize any stamps
 | 
			
		||||
        $stamps = [];
 | 
			
		||||
        if (isset($headers['stamps'])) {
 | 
			
		||||
            $stamps = unserialize($headers['stamps']);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new Envelope($message, $stamps);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function encode(Envelope $envelope): array
 | 
			
		||||
    {
 | 
			
		||||
        $message = $envelope->getMessage();
 | 
			
		||||
 | 
			
		||||
        if (!$message instanceof RequestPdfSignMessage) {
 | 
			
		||||
            throw new MessageDecodingFailedException('Message is not a RequestPdfSignMessage');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'signatureId' => $message->signatureId,
 | 
			
		||||
            'signatureZoneIndex' => $message->signatureZoneIndex,
 | 
			
		||||
            'signatureZone' => $this->normalizer->normalize($message->PDFSignatureZone, 'json', [AbstractNormalizer::GROUPS => ['read']]),
 | 
			
		||||
            'reason' => $message->reason,
 | 
			
		||||
            'signerText' => $message->signerText,
 | 
			
		||||
            'content' => base64_encode($message->content),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $allStamps = [];
 | 
			
		||||
        foreach ($envelope->all() as $stamp) {
 | 
			
		||||
            if ($stamp instanceof NonSendableStampInterface) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            $allStamps = [...$allStamps, ...$stamp];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'body' => json_encode($data, JSON_THROW_ON_ERROR, 512),
 | 
			
		||||
            'headers' => [
 | 
			
		||||
                'stamps' => serialize($allStamps),
 | 
			
		||||
                'Message' => RequestPdfSignMessage::class,
 | 
			
		||||
            ],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								src/Bundle/ChillDocStoreBundle/Service/Signature/PDFPage.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/Bundle/ChillDocStoreBundle/Service/Signature/PDFPage.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
<?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\DocStoreBundle\Service\Signature;
 | 
			
		||||
 | 
			
		||||
use Symfony\Component\Serializer\Annotation\Groups;
 | 
			
		||||
 | 
			
		||||
final readonly class PDFPage
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        #[Groups(['read'])]
 | 
			
		||||
        public int $index,
 | 
			
		||||
        #[Groups(['read'])]
 | 
			
		||||
        public float $width,
 | 
			
		||||
        #[Groups(['read'])]
 | 
			
		||||
        public float $height,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public function equals(self $page): bool
 | 
			
		||||
    {
 | 
			
		||||
        return $page->index === $this->index
 | 
			
		||||
            && round($page->width, 2) === round($this->width, 2)
 | 
			
		||||
            && round($page->height, 2) === round($this->height, 2);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
<?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\DocStoreBundle\Service\Signature;
 | 
			
		||||
 | 
			
		||||
use Symfony\Component\Serializer\Annotation\Groups;
 | 
			
		||||
 | 
			
		||||
final readonly class PDFSignatureZone
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        #[Groups(['read'])]
 | 
			
		||||
        public float $x,
 | 
			
		||||
        #[Groups(['read'])]
 | 
			
		||||
        public float $y,
 | 
			
		||||
        #[Groups(['read'])]
 | 
			
		||||
        public float $height,
 | 
			
		||||
        #[Groups(['read'])]
 | 
			
		||||
        public float $width,
 | 
			
		||||
        #[Groups(['read'])]
 | 
			
		||||
        public PDFPage $PDFPage,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public function equals(self $other): bool
 | 
			
		||||
    {
 | 
			
		||||
        return
 | 
			
		||||
            $this->x == $other->x
 | 
			
		||||
            && $this->y == $other->y
 | 
			
		||||
            && $this->height == $other->height
 | 
			
		||||
            && $this->width == $other->width
 | 
			
		||||
            && $this->PDFPage->equals($other->PDFPage);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
<?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\DocStoreBundle\Service\Signature;
 | 
			
		||||
 | 
			
		||||
use Smalot\PdfParser\Parser;
 | 
			
		||||
 | 
			
		||||
class PDFSignatureZoneParser
 | 
			
		||||
{
 | 
			
		||||
    public const ZONE_SIGNATURE_START = 'signature_zone';
 | 
			
		||||
 | 
			
		||||
    private Parser $parser;
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        public float $defaultHeight = 180.0,
 | 
			
		||||
        public float $defaultWidth = 180.0,
 | 
			
		||||
    ) {
 | 
			
		||||
        $this->parser = new Parser();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return list<PDFSignatureZone>
 | 
			
		||||
     */
 | 
			
		||||
    public function findSignatureZones(string $fileContent): array
 | 
			
		||||
    {
 | 
			
		||||
        $pdf = $this->parser->parseContent($fileContent);
 | 
			
		||||
        $zones = [];
 | 
			
		||||
 | 
			
		||||
        $defaults = $pdf->getObjectsByType('Pages');
 | 
			
		||||
        $defaultPage = reset($defaults);
 | 
			
		||||
        $defaultPageDetails = $defaultPage->getDetails();
 | 
			
		||||
 | 
			
		||||
        foreach ($pdf->getPages() as $index => $page) {
 | 
			
		||||
            $details = $page->getDetails();
 | 
			
		||||
            $pdfPage = new PDFPage(
 | 
			
		||||
                $index,
 | 
			
		||||
                (float) ($details['MediaBox'][2] ?? $defaultPageDetails['MediaBox'][2]),
 | 
			
		||||
                (float) ($details['MediaBox'][3] ?? $defaultPageDetails['MediaBox'][3]),
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            foreach ($page->getDataTm() as $dataTm) {
 | 
			
		||||
                if (str_starts_with($dataTm[1], self::ZONE_SIGNATURE_START)) {
 | 
			
		||||
                    $zones[] = new PDFSignatureZone((float) $dataTm[0][4], (float) $dataTm[0][5], $this->defaultHeight, $this->defaultWidth, $pdfPage);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $zones;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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 Chill\DocStoreBundle\Tests\Service\Signature\Driver\BaseSigner;
 | 
			
		||||
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\PdfSignedMessage;
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\PdfSignedMessageSerializer;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Symfony\Component\Messenger\Envelope;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @internal
 | 
			
		||||
 *
 | 
			
		||||
 * @coversNothing
 | 
			
		||||
 */
 | 
			
		||||
class PdfSignedMessageSerializerTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    public function testDecode(): void
 | 
			
		||||
    {
 | 
			
		||||
        $asString = <<<'JSON'
 | 
			
		||||
            {"signatureId": 0, "content": "dGVzdAo="}
 | 
			
		||||
            JSON;
 | 
			
		||||
 | 
			
		||||
        $actual = $this->buildSerializer()->decode(['body' => $asString]);
 | 
			
		||||
 | 
			
		||||
        self::assertInstanceOf(Envelope::class, $actual);
 | 
			
		||||
        $message = $actual->getMessage();
 | 
			
		||||
        self::assertInstanceOf(PdfSignedMessage::class, $message);
 | 
			
		||||
        self::assertEquals("test\n", $message->content);
 | 
			
		||||
        self::assertEquals(0, $message->signatureId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testEncode(): void
 | 
			
		||||
    {
 | 
			
		||||
        $envelope = new Envelope(
 | 
			
		||||
            new PdfSignedMessage(0, "test\n")
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $actual = $this->buildSerializer()->encode($envelope);
 | 
			
		||||
 | 
			
		||||
        self::assertIsArray($actual);
 | 
			
		||||
        self::assertArrayHasKey('body', $actual);
 | 
			
		||||
        self::assertArrayHasKey('headers', $actual);
 | 
			
		||||
        self::assertEquals([], $actual['headers']);
 | 
			
		||||
 | 
			
		||||
        self::assertEquals(<<<'JSON'
 | 
			
		||||
                {"signatureId":0,"content":"dGVzdAo="}
 | 
			
		||||
                JSON, $actual['body']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function buildSerializer(): PdfSignedMessageSerializer
 | 
			
		||||
    {
 | 
			
		||||
        return new PdfSignedMessageSerializer();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,137 @@
 | 
			
		||||
<?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\DocStoreBundle\Tests\Service\Signature\Driver\BaseSigner;
 | 
			
		||||
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\PDFPage;
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone;
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\RequestPdfSignMessage;
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\RequestPdfSignMessageSerializer;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Symfony\Component\Messenger\Envelope;
 | 
			
		||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
 | 
			
		||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
 | 
			
		||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
 | 
			
		||||
use Symfony\Component\Serializer\Serializer;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @internal
 | 
			
		||||
 *
 | 
			
		||||
 * @coversNothing
 | 
			
		||||
 */
 | 
			
		||||
class RequestPdfSignMessageSerializerTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    public function testEncode(): void
 | 
			
		||||
    {
 | 
			
		||||
        $serializer = $this->buildSerializer();
 | 
			
		||||
 | 
			
		||||
        $envelope = new Envelope(
 | 
			
		||||
            $request = new RequestPdfSignMessage(
 | 
			
		||||
                0,
 | 
			
		||||
                new PDFSignatureZone(10.0, 10.0, 180.0, 180.0, new PDFPage(0, 500.0, 800.0)),
 | 
			
		||||
                0,
 | 
			
		||||
                'metadata to add to the signature',
 | 
			
		||||
                'Mme Caroline Diallo',
 | 
			
		||||
                'abc'
 | 
			
		||||
            ),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $actual = $serializer->encode($envelope);
 | 
			
		||||
        $expectedBody = json_encode([
 | 
			
		||||
            'signatureId' => $request->signatureId,
 | 
			
		||||
            'signatureZoneIndex' => $request->signatureZoneIndex,
 | 
			
		||||
            'signatureZone' => ['x' => 10.0],
 | 
			
		||||
            'reason' => $request->reason,
 | 
			
		||||
            'signerText' => $request->signerText,
 | 
			
		||||
            'content' => base64_encode($request->content),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        self::assertIsArray($actual);
 | 
			
		||||
        self::assertArrayHasKey('body', $actual);
 | 
			
		||||
        self::assertArrayHasKey('headers', $actual);
 | 
			
		||||
        self::assertEquals($expectedBody, $actual['body']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testDecode(): void
 | 
			
		||||
    {
 | 
			
		||||
        $serializer = $this->buildSerializer();
 | 
			
		||||
 | 
			
		||||
        $request = new RequestPdfSignMessage(
 | 
			
		||||
            0,
 | 
			
		||||
            new PDFSignatureZone(10.0, 10.0, 180.0, 180.0, new PDFPage(0, 500.0, 800.0)),
 | 
			
		||||
            0,
 | 
			
		||||
            'metadata to add to the signature',
 | 
			
		||||
            'Mme Caroline Diallo',
 | 
			
		||||
            'abc'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $bodyAsString = json_encode([
 | 
			
		||||
            'signatureId' => $request->signatureId,
 | 
			
		||||
            'signatureZoneIndex' => $request->signatureZoneIndex,
 | 
			
		||||
            'signatureZone' => ['x' => 10.0],
 | 
			
		||||
            'reason' => $request->reason,
 | 
			
		||||
            'signerText' => $request->signerText,
 | 
			
		||||
            'content' => base64_encode($request->content),
 | 
			
		||||
        ], JSON_THROW_ON_ERROR);
 | 
			
		||||
 | 
			
		||||
        $actual = $serializer->decode([
 | 
			
		||||
            'body' => $bodyAsString,
 | 
			
		||||
            'headers' => [
 | 
			
		||||
                'Message' => RequestPdfSignMessage::class,
 | 
			
		||||
            ],
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        self::assertInstanceOf(RequestPdfSignMessage::class, $actual->getMessage());
 | 
			
		||||
        self::assertEquals($request->signatureId, $actual->getMessage()->signatureId);
 | 
			
		||||
        self::assertEquals($request->signatureZoneIndex, $actual->getMessage()->signatureZoneIndex);
 | 
			
		||||
        self::assertEquals($request->reason, $actual->getMessage()->reason);
 | 
			
		||||
        self::assertEquals($request->signerText, $actual->getMessage()->signerText);
 | 
			
		||||
        self::assertEquals($request->content, $actual->getMessage()->content);
 | 
			
		||||
        self::assertNotNull($actual->getMessage()->PDFSignatureZone);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function buildSerializer(): RequestPdfSignMessageSerializer
 | 
			
		||||
    {
 | 
			
		||||
        $normalizer =
 | 
			
		||||
            new class () implements NormalizerInterface {
 | 
			
		||||
                public function normalize($object, ?string $format = null, array $context = []): array
 | 
			
		||||
                {
 | 
			
		||||
                    if (!$object instanceof PDFSignatureZone) {
 | 
			
		||||
                        throw new UnexpectedValueException('expected RequestPdfSignMessage');
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return [
 | 
			
		||||
                        'x' => $object->x,
 | 
			
		||||
                    ];
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                public function supportsNormalization($data, ?string $format = null): bool
 | 
			
		||||
                {
 | 
			
		||||
                    return $data instanceof PDFSignatureZone;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        $denormalizer = new class () implements DenormalizerInterface {
 | 
			
		||||
            public function denormalize($data, string $type, ?string $format = null, array $context = [])
 | 
			
		||||
            {
 | 
			
		||||
                return new PDFSignatureZone(10.0, 10.0, 180.0, 180.0, new PDFPage(0, 500.0, 800.0));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public function supportsDenormalization($data, string $type, ?string $format = null)
 | 
			
		||||
            {
 | 
			
		||||
                return PDFSignatureZone::class === $type;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        $serializer = new Serializer([$normalizer, $denormalizer]);
 | 
			
		||||
 | 
			
		||||
        return new RequestPdfSignMessageSerializer($serializer, $serializer);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,77 @@
 | 
			
		||||
<?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 Tests\Service\Signature;
 | 
			
		||||
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\PDFPage;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone;
 | 
			
		||||
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZoneParser;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @internal
 | 
			
		||||
 *
 | 
			
		||||
 * @coversNothing
 | 
			
		||||
 */
 | 
			
		||||
class PDFSignatureZoneParserTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    private static PDFSignatureZoneParser $parser;
 | 
			
		||||
 | 
			
		||||
    public static function setUpBeforeClass(): void
 | 
			
		||||
    {
 | 
			
		||||
        self::$parser = new PDFSignatureZoneParser();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @dataProvider provideFiles
 | 
			
		||||
     *
 | 
			
		||||
     * @param list<PDFSignatureZone> $expected
 | 
			
		||||
     */
 | 
			
		||||
    public function testFindSignatureZones(string $filePath, array $expected): void
 | 
			
		||||
    {
 | 
			
		||||
        $content = file_get_contents($filePath);
 | 
			
		||||
 | 
			
		||||
        if (false === $content) {
 | 
			
		||||
            throw new \LogicException("Unable to read file {$filePath}");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $actual = self::$parser->findSignatureZones($content);
 | 
			
		||||
 | 
			
		||||
        self::assertEquals(count($expected), count($actual));
 | 
			
		||||
 | 
			
		||||
        foreach ($actual as $index => $signatureZone) {
 | 
			
		||||
            self::assertObjectEquals($expected[$index], $signatureZone);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function provideFiles(): iterable
 | 
			
		||||
    {
 | 
			
		||||
        yield [
 | 
			
		||||
            __DIR__.'/data/signature_2_signature_page_1.pdf',
 | 
			
		||||
            [
 | 
			
		||||
                new PDFSignatureZone(
 | 
			
		||||
                    127.7,
 | 
			
		||||
                    95.289,
 | 
			
		||||
                    180.0,
 | 
			
		||||
                    180.0,
 | 
			
		||||
                    $page = new PDFPage(0, 595.30393, 841.8897)
 | 
			
		||||
                ),
 | 
			
		||||
                new PDFSignatureZone(
 | 
			
		||||
                    269.5,
 | 
			
		||||
                    95.289,
 | 
			
		||||
                    180.0,
 | 
			
		||||
                    180.0,
 | 
			
		||||
                    $page,
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -121,9 +121,4 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
 | 
			
		||||
    {
 | 
			
		||||
        return AccompanyingCourseDocument::class === $entityWorkflow->getRelatedEntityClass();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool
 | 
			
		||||
    {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ use Chill\EventBundle\Entity\Event;
 | 
			
		||||
use Chill\EventBundle\Entity\Participation;
 | 
			
		||||
use Chill\EventBundle\Form\EventType;
 | 
			
		||||
use Chill\EventBundle\Form\Type\PickEventType;
 | 
			
		||||
use Chill\EventBundle\Security\EventVoter;
 | 
			
		||||
use Chill\EventBundle\Security\Authorization\EventVoter;
 | 
			
		||||
use Chill\MainBundle\Entity\Center;
 | 
			
		||||
use Chill\MainBundle\Entity\User;
 | 
			
		||||
use Chill\MainBundle\Pagination\PaginatorFactory;
 | 
			
		||||
@@ -61,7 +61,7 @@ final class EventController extends AbstractController
 | 
			
		||||
        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', 'POST', 'DELETE'])]
 | 
			
		||||
    #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/delete', name: 'chill_event__event_delete', requirements: ['event_id' => '\d+'], methods: ['GET', 'DELETE'])]
 | 
			
		||||
    public function deleteAction($event_id, Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|Response
 | 
			
		||||
    {
 | 
			
		||||
        $em = $this->managerRegistry->getManager();
 | 
			
		||||
@@ -78,10 +78,10 @@ final class EventController extends AbstractController
 | 
			
		||||
 | 
			
		||||
        $form = $this->createDeleteForm($event_id);
 | 
			
		||||
 | 
			
		||||
        if (Request::METHOD_POST === $request->getMethod()) {
 | 
			
		||||
        if (Request::METHOD_DELETE === $request->getMethod()) {
 | 
			
		||||
            $form->handleRequest($request);
 | 
			
		||||
 | 
			
		||||
            if ($form->isSubmitted() && $form->isValid()) {
 | 
			
		||||
            if ($form->isValid()) {
 | 
			
		||||
                foreach ($participations as $participation) {
 | 
			
		||||
                    $em->remove($participation);
 | 
			
		||||
                }
 | 
			
		||||
@@ -108,6 +108,28 @@ 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.
 | 
			
		||||
     *
 | 
			
		||||
@@ -291,7 +313,7 @@ final class EventController extends AbstractController
 | 
			
		||||
    /**
 | 
			
		||||
     * Edits an existing Event entity.
 | 
			
		||||
     */
 | 
			
		||||
    #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/edit', name: 'chill_event__event_edit', methods: ['GET', 'POST', 'PUT'])]
 | 
			
		||||
    #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/update', name: 'chill_event__event_update', methods: ['POST', 'PUT'])]
 | 
			
		||||
    public function updateAction(Request $request, $event_id): \Symfony\Component\HttpFoundation\RedirectResponse|Response
 | 
			
		||||
    {
 | 
			
		||||
        $em = $this->managerRegistry->getManager();
 | 
			
		||||
@@ -302,20 +324,14 @@ final class EventController extends AbstractController
 | 
			
		||||
            throw $this->createNotFoundException('Unable to find Event entity.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $editForm = $this->createForm(EventType::class, $entity, [
 | 
			
		||||
            'center' => $entity->getCenter(),
 | 
			
		||||
            'role' => EventVoter::UPDATE,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $editForm->add('submit', SubmitType::class, ['label' => 'Update']);
 | 
			
		||||
 | 
			
		||||
        $editForm = $this->createEditForm($entity);
 | 
			
		||||
        $editForm->handleRequest($request);
 | 
			
		||||
 | 
			
		||||
        if ($editForm->isSubmitted() && $editForm->isValid()) {
 | 
			
		||||
            $em->persist($entity);
 | 
			
		||||
        if ($editForm->isValid()) {
 | 
			
		||||
            $em->flush();
 | 
			
		||||
 | 
			
		||||
            $this->addFlash('success', $this->translator->trans('The event was updated'));
 | 
			
		||||
            $this->addFlash('success', $this->translator
 | 
			
		||||
                ->trans('The event was updated'));
 | 
			
		||||
 | 
			
		||||
            return $this->redirectToRoute('chill_event__event_show', ['event_id' => $event_id]);
 | 
			
		||||
        }
 | 
			
		||||
@@ -402,6 +418,7 @@ final class EventController extends AbstractController
 | 
			
		||||
        $builder->add('event_id', HiddenType::class, [
 | 
			
		||||
            'data' => $event->getId(),
 | 
			
		||||
        ]);
 | 
			
		||||
        dump($event->getId());
 | 
			
		||||
 | 
			
		||||
        return $builder->getForm();
 | 
			
		||||
    }
 | 
			
		||||
@@ -583,7 +600,29 @@ final class EventController extends AbstractController
 | 
			
		||||
            ->setAction($this->generateUrl('chill_event__event_delete', [
 | 
			
		||||
                'event_id' => $event_id,
 | 
			
		||||
            ]))
 | 
			
		||||
            ->setMethod('DELETE')
 | 
			
		||||
            ->add('submit', SubmitType::class, ['label' => 'Delete'])
 | 
			
		||||
            ->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.
 | 
			
		||||
     *
 | 
			
		||||
     * @return \Symfony\Component\Form\FormInterface The form
 | 
			
		||||
     * @return \Symfony\Component\Form\Form The form
 | 
			
		||||
     */
 | 
			
		||||
    private function createDeleteForm(mixed $id)
 | 
			
		||||
    {
 | 
			
		||||
@@ -210,6 +210,7 @@ class EventTypeController extends AbstractController
 | 
			
		||||
                'chill_eventtype_admin_delete',
 | 
			
		||||
                ['id' => $id]
 | 
			
		||||
            ))
 | 
			
		||||
            ->setMethod('DELETE')
 | 
			
		||||
            ->add('submit', SubmitType::class, ['label' => 'Delete'])
 | 
			
		||||
            ->getForm();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ use Chill\EventBundle\Entity\Event;
 | 
			
		||||
use Chill\EventBundle\Entity\Participation;
 | 
			
		||||
use Chill\EventBundle\Form\ParticipationType;
 | 
			
		||||
use Chill\EventBundle\Repository\EventRepository;
 | 
			
		||||
use Chill\EventBundle\Security\ParticipationVoter;
 | 
			
		||||
use Chill\EventBundle\Security\Authorization\ParticipationVoter;
 | 
			
		||||
use Chill\PersonBundle\Repository\PersonRepository;
 | 
			
		||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
 | 
			
		||||
use Doctrine\Common\Collections\Collection;
 | 
			
		||||
@@ -259,10 +259,10 @@ final class ParticipationController extends AbstractController
 | 
			
		||||
 | 
			
		||||
        $form = $this->createDeleteForm($participation_id);
 | 
			
		||||
 | 
			
		||||
        if (Request::METHOD_POST === $request->getMethod()) {
 | 
			
		||||
        if (Request::METHOD_DELETE === $request->getMethod()) {
 | 
			
		||||
            $form->handleRequest($request);
 | 
			
		||||
 | 
			
		||||
            if ($form->isSubmitted() && $form->isValid()) {
 | 
			
		||||
            if ($form->isValid()) {
 | 
			
		||||
                $em->remove($participation);
 | 
			
		||||
                $em->flush();
 | 
			
		||||
 | 
			
		||||
@@ -753,6 +753,7 @@ final class ParticipationController extends AbstractController
 | 
			
		||||
            ->setAction($this->generateUrl('chill_event_participation_delete', [
 | 
			
		||||
                'participation_id' => $participation_id,
 | 
			
		||||
            ]))
 | 
			
		||||
            ->setMethod('DELETE')
 | 
			
		||||
            ->add('submit', SubmitType::class, ['label' => 'Delete'])
 | 
			
		||||
            ->getForm();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -201,7 +201,7 @@ class RoleController extends AbstractController
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a form to delete a Role entity by id.
 | 
			
		||||
     *
 | 
			
		||||
     * @return \Symfony\Component\Form\FormInterface The form
 | 
			
		||||
     * @return \Symfony\Component\Form\Form The form
 | 
			
		||||
     */
 | 
			
		||||
    private function createDeleteForm(mixed $id)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -201,12 +201,13 @@ class StatusController extends AbstractController
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a form to delete a Status entity by id.
 | 
			
		||||
     *
 | 
			
		||||
     * @return \Symfony\Component\Form\FormInterface The form
 | 
			
		||||
     * @return \Symfony\Component\Form\Form The form
 | 
			
		||||
     */
 | 
			
		||||
    private function createDeleteForm(mixed $id)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->createFormBuilder()
 | 
			
		||||
            ->setAction($this->generateUrl('chill_event_admin_status_delete', ['id' => $id]))
 | 
			
		||||
            ->setMethod('DELETE')
 | 
			
		||||
            ->add('submit', SubmitType::class, ['label' => 'Delete'])
 | 
			
		||||
            ->getForm();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,8 +11,8 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Chill\EventBundle\DependencyInjection;
 | 
			
		||||
 | 
			
		||||
use Chill\EventBundle\Security\EventVoter;
 | 
			
		||||
use Chill\EventBundle\Security\ParticipationVoter;
 | 
			
		||||
use Chill\EventBundle\Security\Authorization\EventVoter;
 | 
			
		||||
use Chill\EventBundle\Security\Authorization\ParticipationVoter;
 | 
			
		||||
use Symfony\Component\Config\FileLocator;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
 | 
			
		||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
 | 
			
		||||
@@ -33,13 +33,12 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface
 | 
			
		||||
 | 
			
		||||
        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
 | 
			
		||||
        $loader->load('services.yaml');
 | 
			
		||||
        $loader->load('services/security.yaml');
 | 
			
		||||
        $loader->load('services/authorization.yaml');
 | 
			
		||||
        $loader->load('services/fixtures.yaml');
 | 
			
		||||
        $loader->load('services/forms.yaml');
 | 
			
		||||
        $loader->load('services/repositories.yaml');
 | 
			
		||||
        $loader->load('services/search.yaml');
 | 
			
		||||
        $loader->load('services/timeline.yaml');
 | 
			
		||||
        $loader->load('services/export.yaml');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** (non-PHPdoc).
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter
 | 
			
		||||
    private ?Scope $circle = null;
 | 
			
		||||
 | 
			
		||||
    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_MUTABLE)]
 | 
			
		||||
    private ?\DateTime $date = null;
 | 
			
		||||
    private ?\DateTime $date;
 | 
			
		||||
 | 
			
		||||
    #[ORM\Id]
 | 
			
		||||
    #[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
 | 
			
		||||
@@ -265,9 +265,11 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter
 | 
			
		||||
    /**
 | 
			
		||||
     * Set label.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $label
 | 
			
		||||
     *
 | 
			
		||||
     * @return Event
 | 
			
		||||
     */
 | 
			
		||||
    public function setName(?string $label)
 | 
			
		||||
    public function setName($label)
 | 
			
		||||
    {
 | 
			
		||||
        $this->name = $label;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -146,9 +146,11 @@ class EventType
 | 
			
		||||
    /**
 | 
			
		||||
     * Set active.
 | 
			
		||||
     *
 | 
			
		||||
     * @param bool $active
 | 
			
		||||
     *
 | 
			
		||||
     * @return EventType
 | 
			
		||||
     */
 | 
			
		||||
    public function setActive(bool $active)
 | 
			
		||||
    public function setActive($active)
 | 
			
		||||
    {
 | 
			
		||||
        $this->active = $active;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -81,9 +81,11 @@ class Role
 | 
			
		||||
    /**
 | 
			
		||||
     * Set active.
 | 
			
		||||
     *
 | 
			
		||||
     * @param bool $active
 | 
			
		||||
     *
 | 
			
		||||
     * @return Role
 | 
			
		||||
     */
 | 
			
		||||
    public function setActive(bool $active)
 | 
			
		||||
    public function setActive($active)
 | 
			
		||||
    {
 | 
			
		||||
        $this->active = $active;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -81,9 +81,11 @@ class Status
 | 
			
		||||
    /**
 | 
			
		||||
     * Set active.
 | 
			
		||||
     *
 | 
			
		||||
     * @param bool $active
 | 
			
		||||
     *
 | 
			
		||||
     * @return Status
 | 
			
		||||
     */
 | 
			
		||||
    public function setActive(bool $active)
 | 
			
		||||
    public function setActive($active)
 | 
			
		||||
    {
 | 
			
		||||
        $this->active = $active;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,110 +0,0 @@
 | 
			
		||||
<?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';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,81 +0,0 @@
 | 
			
		||||
<?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';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,81 +0,0 @@
 | 
			
		||||
<?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';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
<?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';
 | 
			
		||||
}
 | 
			
		||||
@@ -1,125 +0,0 @@
 | 
			
		||||
<?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,
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,126 +0,0 @@
 | 
			
		||||
<?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,
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,95 +0,0 @@
 | 
			
		||||
<?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';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,94 +0,0 @@
 | 
			
		||||
<?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();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,94 +0,0 @@
 | 
			
		||||
<?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,7 +13,6 @@ namespace Chill\EventBundle\Form;
 | 
			
		||||
 | 
			
		||||
use Chill\DocStoreBundle\Entity\StoredObject;
 | 
			
		||||
use Chill\DocStoreBundle\Form\StoredObjectType;
 | 
			
		||||
use Chill\EventBundle\Entity\Event;
 | 
			
		||||
use Chill\EventBundle\Form\Type\PickEventTypeType;
 | 
			
		||||
use Chill\MainBundle\Entity\Center;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillCollectionType;
 | 
			
		||||
@@ -24,7 +23,6 @@ use Chill\MainBundle\Form\Type\PickUserLocationType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ScopePickerType;
 | 
			
		||||
use Symfony\Component\Form\AbstractType;
 | 
			
		||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
 | 
			
		||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
 | 
			
		||||
use Symfony\Component\Form\FormBuilderInterface;
 | 
			
		||||
use Symfony\Component\OptionsResolver\OptionsResolver;
 | 
			
		||||
 | 
			
		||||
@@ -33,9 +31,7 @@ class EventType extends AbstractType
 | 
			
		||||
    public function buildForm(FormBuilderInterface $builder, array $options)
 | 
			
		||||
    {
 | 
			
		||||
        $builder
 | 
			
		||||
            ->add('name', TextType::class, [
 | 
			
		||||
                'required' => true,
 | 
			
		||||
            ])
 | 
			
		||||
            ->add('name')
 | 
			
		||||
            ->add('date', ChillDateTimeType::class, [
 | 
			
		||||
                'required' => true,
 | 
			
		||||
            ])
 | 
			
		||||
@@ -79,7 +75,7 @@ class EventType extends AbstractType
 | 
			
		||||
    public function configureOptions(OptionsResolver $resolver)
 | 
			
		||||
    {
 | 
			
		||||
        $resolver->setDefaults([
 | 
			
		||||
            'data_class' => Event::class,
 | 
			
		||||
            'data_class' => \Chill\EventBundle\Entity\Event::class,
 | 
			
		||||
        ]);
 | 
			
		||||
        $resolver
 | 
			
		||||
            ->setRequired(['center', 'role'])
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Chill\EventBundle\Menu;
 | 
			
		||||
 | 
			
		||||
use Chill\EventBundle\Security\EventVoter;
 | 
			
		||||
use Chill\EventBundle\Security\Authorization\EventVoter;
 | 
			
		||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
 | 
			
		||||
use Knp\Menu\MenuItem;
 | 
			
		||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Chill\EventBundle\Menu;
 | 
			
		||||
 | 
			
		||||
use Chill\EventBundle\Security\EventVoter;
 | 
			
		||||
use Chill\EventBundle\Security\Authorization\EventVoter;
 | 
			
		||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
 | 
			
		||||
use Knp\Menu\MenuItem;
 | 
			
		||||
use Symfony\Component\Security\Core\Security;
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ namespace Chill\EventBundle\Repository;
 | 
			
		||||
 | 
			
		||||
use Chill\EventBundle\Entity\Event;
 | 
			
		||||
use Chill\EventBundle\Entity\Participation;
 | 
			
		||||
use Chill\EventBundle\Security\EventVoter;
 | 
			
		||||
use Chill\EventBundle\Security\Authorization\EventVoter;
 | 
			
		||||
use Chill\MainBundle\Entity\User;
 | 
			
		||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
 | 
			
		||||
use Chill\PersonBundle\Entity\Person;
 | 
			
		||||
 
 | 
			
		||||
@@ -12,57 +12,13 @@ declare(strict_types=1);
 | 
			
		||||
namespace Chill\EventBundle\Repository;
 | 
			
		||||
 | 
			
		||||
use Chill\EventBundle\Entity\Role;
 | 
			
		||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
 | 
			
		||||
use Doctrine\ORM\EntityManagerInterface;
 | 
			
		||||
use Doctrine\ORM\EntityRepository;
 | 
			
		||||
use Doctrine\ORM\QueryBuilder;
 | 
			
		||||
use Doctrine\Persistence\ObjectRepository;
 | 
			
		||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
 | 
			
		||||
use Doctrine\Persistence\ManagerRegistry;
 | 
			
		||||
 | 
			
		||||
readonly class RoleRepository implements ObjectRepository
 | 
			
		||||
class RoleRepository extends ServiceEntityRepository
 | 
			
		||||
{
 | 
			
		||||
    private EntityRepository $repository;
 | 
			
		||||
 | 
			
		||||
    public function __construct(EntityManagerInterface $entityManager, private TranslatableStringHelper $translatableStringHelper)
 | 
			
		||||
    public function __construct(ManagerRegistry $registry)
 | 
			
		||||
    {
 | 
			
		||||
        $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;
 | 
			
		||||
        parent::__construct($registry, Role::class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
{% import '@ChillPerson/Person/macro.html.twig' as person_macro %}
 | 
			
		||||
 | 
			
		||||
{% if ignored_participations|length > 0 %}
 | 
			
		||||
    <p>{{ 'ignored_participations'|trans({'count': ignored_participations|length}) }}:</p>
 | 
			
		||||
    <p>{% transchoice ignored_participations|length %}The following people have been ignored because they are already participating on the event{% endtranschoice %} :</p>
 | 
			
		||||
    <ul>
 | 
			
		||||
        {% for p in ignored_participations %}
 | 
			
		||||
            <li>{{ person_macro.render(p.person) }}</li>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,19 +9,18 @@ declare(strict_types=1);
 | 
			
		||||
 * the LICENSE file that was distributed with this source code.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Chill\EventBundle\Security;
 | 
			
		||||
namespace Chill\EventBundle\Security\Authorization;
 | 
			
		||||
 | 
			
		||||
use Chill\EventBundle\Entity\Event;
 | 
			
		||||
use Chill\MainBundle\Entity\Center;
 | 
			
		||||
use Chill\MainBundle\Entity\User;
 | 
			
		||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
 | 
			
		||||
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\PersonBundle\Entity\Person;
 | 
			
		||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
 | 
			
		||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Description of EventVoter.
 | 
			
		||||
@@ -43,46 +42,61 @@ class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInter
 | 
			
		||||
 | 
			
		||||
    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(
 | 
			
		||||
        private readonly AuthorizationHelper $authorizationHelper,
 | 
			
		||||
        private readonly LoggerInterface $logger,
 | 
			
		||||
        VoterHelperFactoryInterface $voterHelperFactory
 | 
			
		||||
        AccessDecisionManagerInterface $accessDecisionManager,
 | 
			
		||||
        AuthorizationHelper $authorizationHelper,
 | 
			
		||||
        LoggerInterface $logger
 | 
			
		||||
    ) {
 | 
			
		||||
        $this->voterHelper = $voterHelperFactory
 | 
			
		||||
            ->generate(self::class)
 | 
			
		||||
            ->addCheckFor(null, [self::SEE])
 | 
			
		||||
            ->addCheckFor(Event::class, [...self::ROLES])
 | 
			
		||||
            ->addCheckFor(Person::class, [self::SEE, self::CREATE])
 | 
			
		||||
            ->addCheckFor(Center::class, [self::STATS])
 | 
			
		||||
            ->build();
 | 
			
		||||
        $this->accessDecisionManager = $accessDecisionManager;
 | 
			
		||||
        $this->authorizationHelper = $authorizationHelper;
 | 
			
		||||
        $this->logger = $logger;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getRoles(): array
 | 
			
		||||
    {
 | 
			
		||||
        return [...self::ROLES, self::STATS];
 | 
			
		||||
        return self::ROLES;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getRolesWithHierarchy(): array
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            'Event' => $this->getRoles(),
 | 
			
		||||
            'Event' => self::ROLES,
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getRolesWithoutScope(): array
 | 
			
		||||
    {
 | 
			
		||||
        return [self::ROLES, self::STATS];
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function supports($attribute, $subject)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->voterHelper->supports($attribute, $subject);
 | 
			
		||||
        return ($subject instanceof Event && \in_array($attribute, self::ROLES, true))
 | 
			
		||||
            || ($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)
 | 
			
		||||
    {
 | 
			
		||||
        $this->logger->debug(sprintf('Voting from %s class', self::class));
 | 
			
		||||
@@ -104,5 +118,15 @@ class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInter
 | 
			
		||||
            ->getReachableCenters($token->getUser(), $attribute);
 | 
			
		||||
 | 
			
		||||
        return \count($centers) > 0;
 | 
			
		||||
 | 
			
		||||
        if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->authorizationHelper->userHasAccess(
 | 
			
		||||
            $token->getUser(),
 | 
			
		||||
            $subject,
 | 
			
		||||
            $attribute
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -9,19 +9,18 @@ declare(strict_types=1);
 | 
			
		||||
 * the LICENSE file that was distributed with this source code.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Chill\EventBundle\Security;
 | 
			
		||||
namespace Chill\EventBundle\Security\Authorization;
 | 
			
		||||
 | 
			
		||||
use Chill\EventBundle\Entity\Participation;
 | 
			
		||||
use Chill\MainBundle\Entity\Center;
 | 
			
		||||
use Chill\MainBundle\Entity\User;
 | 
			
		||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
 | 
			
		||||
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\PersonBundle\Entity\Person;
 | 
			
		||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
 | 
			
		||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
 | 
			
		||||
 | 
			
		||||
class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
 | 
			
		||||
{
 | 
			
		||||
@@ -40,48 +39,58 @@ class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierar
 | 
			
		||||
 | 
			
		||||
    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(
 | 
			
		||||
        private readonly AuthorizationHelper $authorizationHelper,
 | 
			
		||||
        private readonly LoggerInterface $logger,
 | 
			
		||||
        VoterHelperFactoryInterface $voterHelperFactory
 | 
			
		||||
        AccessDecisionManagerInterface $accessDecisionManager,
 | 
			
		||||
        AuthorizationHelper $authorizationHelper,
 | 
			
		||||
        LoggerInterface $logger
 | 
			
		||||
    ) {
 | 
			
		||||
        $this->voterHelper = $voterHelperFactory
 | 
			
		||||
            ->generate(self::class)
 | 
			
		||||
            ->addCheckFor(null, [self::SEE])
 | 
			
		||||
            ->addCheckFor(Participation::class, [...self::ROLES])
 | 
			
		||||
            ->addCheckFor(Person::class, [self::SEE, self::CREATE])
 | 
			
		||||
            ->addCheckFor(Center::class, [self::STATS])
 | 
			
		||||
            ->build();
 | 
			
		||||
        $this->accessDecisionManager = $accessDecisionManager;
 | 
			
		||||
        $this->authorizationHelper = $authorizationHelper;
 | 
			
		||||
        $this->logger = $logger;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getRoles(): array
 | 
			
		||||
    {
 | 
			
		||||
        return [...self::ROLES, self::STATS];
 | 
			
		||||
        return self::ROLES;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getRolesWithHierarchy(): array
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            'Participation' => $this->getRoles(),
 | 
			
		||||
            'Event' => self::ROLES,
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getRolesWithoutScope(): array
 | 
			
		||||
    {
 | 
			
		||||
        return [self::ROLES, self::STATS];
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function supports($attribute, $subject)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->voterHelper->supports($attribute, $subject);
 | 
			
		||||
        return ($subject instanceof Participation && \in_array($attribute, self::ROLES, true))
 | 
			
		||||
            || ($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
 | 
			
		||||
     */
 | 
			
		||||
@@ -106,5 +115,15 @@ class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierar
 | 
			
		||||
            ->getReachableCenters($token->getUser(), $attribute);
 | 
			
		||||
 | 
			
		||||
        return \count($centers) > 0;
 | 
			
		||||
 | 
			
		||||
        if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->authorizationHelper->userHasAccess(
 | 
			
		||||
            $token->getUser(),
 | 
			
		||||
            $subject,
 | 
			
		||||
            $attribute
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
<?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');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
<?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');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,59 +0,0 @@
 | 
			
		||||
<?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'),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,59 +0,0 @@
 | 
			
		||||
<?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'),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,63 +0,0 @@
 | 
			
		||||
<?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'),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,65 +0,0 @@
 | 
			
		||||
<?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'),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,76 +0,0 @@
 | 
			
		||||
<?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'),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,81 +0,0 @@
 | 
			
		||||
<?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;
 | 
			
		||||
 | 
			
		||||
use Chill\EventBundle\Repository\EventACLAwareRepository;
 | 
			
		||||
use Chill\EventBundle\Security\EventVoter;
 | 
			
		||||
use Chill\EventBundle\Security\Authorization\EventVoter;
 | 
			
		||||
use Chill\MainBundle\Entity\Center;
 | 
			
		||||
use Chill\MainBundle\Entity\Scope;
 | 
			
		||||
use Chill\MainBundle\Entity\User;
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
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 }
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
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 }
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
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,9 +19,3 @@ events:
 | 
			
		||||
            one {et un autre participant}
 | 
			
		||||
            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,6 +41,7 @@ Back to the event: Retour à l'événement
 | 
			
		||||
The participation was created: La participation a été créée
 | 
			
		||||
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.'
 | 
			
		||||
'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
 | 
			
		||||
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.
 | 
			
		||||
@@ -80,31 +81,9 @@ Pick an event: Choisir un événement
 | 
			
		||||
Pick a type of event: Choisir un type d'événement
 | 
			
		||||
Pick a moderator: Choisir un animateur
 | 
			
		||||
 | 
			
		||||
# exports
 | 
			
		||||
Select a format: Choisir un format
 | 
			
		||||
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 menu: Menu des événements
 | 
			
		||||
 
 | 
			
		||||
@@ -1,84 +0,0 @@
 | 
			
		||||
<?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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,113 +0,0 @@
 | 
			
		||||
<?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;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,100 +0,0 @@
 | 
			
		||||
<?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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
<?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 {}
 | 
			
		||||
@@ -1,56 +0,0 @@
 | 
			
		||||
<?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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,52 +0,0 @@
 | 
			
		||||
<?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',
 | 
			
		||||
                ],
 | 
			
		||||
            ],
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
<?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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user