mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-31 09:18:24 +00:00 
			
		
		
		
	Compare commits
	
		
			273 Commits
		
	
	
		
			v3.0.0-RC7
			...
			295-cancel
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| fad7bdf235 | |||
| 8521cea46c | |||
| 4ead7ba761 | |||
| 9721b166eb | |||
| 1b21cd6c33 | |||
| 97860a9487 | |||
| 2fb46c65c2 | |||
| f4356ac249 | |||
| d152efe084 | |||
| ee9530d03f | |||
| b97eabf0d2 | |||
| 2e69d2df90 | |||
| cb446edd18 | |||
| 5d84e997c1 | |||
| 35199b6993 | |||
| dab68fb409 | |||
| 6001bb6447 | |||
| 29fec50515 | |||
| 34edb02cd0 | |||
| 860ae5cedf | |||
| bf056046ab | |||
| 4d73f9b81a | |||
| dd159f4379 | |||
| 49ad25b4c8 | |||
| ad94310981 | |||
| e8f09b507f | |||
| e29e1db6ed | |||
| 8c4f342ca1 | |||
| 745a29f742 | |||
| 41ffc470a0 | |||
| 46b31ae1ea | |||
| 8c5e94e295 | |||
| 9c8a84cdbd | |||
| a82b99aecc | |||
| deb4bda16e | |||
| c1e5f4a57e | |||
| 6fc5a10dc4 | |||
| 18abc84e68 | |||
| e85c31826f | |||
| d119ba49f7 | |||
| c21de777fd | |||
| 15eaf648df | |||
| 42471269db | |||
| 9475a708c3 | |||
| bf1af1aaad | |||
| 8ea87053f0 | |||
| 3c9ee41b3b | |||
| d0031e82e8 | |||
| 20f2bc6c35 | |||
| 71d3aa3969 | |||
| ce781a5b58 | |||
| 2dd275a074 | |||
| 5f5d4b8f06 | |||
| cc8214d52c | |||
| 0c797c2997 | |||
| ee6edba206 | |||
| 3e6d764b9b | |||
| 3e5a558cdf | |||
| 0e6b7d76a4 | |||
| b2042bd1e4 | |||
| 6e9f111fd9 | |||
| 313fb9ffdf | |||
| 063bc2857f | |||
| 615629d1b4 | |||
| 667e144681 | |||
| e17203ca3a | |||
| c6a6d76790 | |||
| 3d49c959e0 | |||
|  | 86896a12e6 | ||
|  | 3a959b7044 | ||
|  | f8d95384ea | ||
| b6edbb3eed | |||
| 00cc3b7806 | |||
| 7ab52ff09e | |||
| 2d82c1e105 | |||
| e477a49c92 | |||
| 0db2652f08 | |||
| c38f7c1179 | |||
| 67d24cb951 | |||
| cb90261309 | |||
| 2feea24c41 | |||
| 1b16d4fe3b | |||
| ce5659219a | |||
| 5fefe09a39 | |||
| e21db73b84 | |||
| 3978ea9a47 | |||
| 4fbb7811ac | |||
| 2b7ea4178b | |||
| 8a374864fa | |||
| bb848746d5 | |||
| 3738c110f8 | |||
| f57fdb2b4c | |||
| b57824fc7e | |||
| 6b4e1ed2d3 | |||
| b0485dbcc8 | |||
| c16219dc6d | |||
| ad47804c91 | |||
| 85e2466611 | |||
| 94d6b5eff8 | |||
| d87f380f16 | |||
| 58bf722fae | |||
| 50fb79ebbf | |||
| 58912f1d98 | |||
| 9604ba5f4b | |||
| b689a51a48 | |||
| 8c0d2f58ba | |||
| 212230448b | |||
| 2bfb8fe387 | |||
| 6362b98a00 | |||
| 6e2a08cae8 | |||
| 305105faae | |||
| 85811cc6ae | |||
| 7eee995627 | |||
| c0c448fb39 | |||
| 6445342136 | |||
| d52e54fd2a | |||
| 547a9d1369 | |||
| 288a02f5b7 | |||
| 2f9884072c | |||
| ee45ff61a6 | |||
| 5dfd8daf3a | |||
| 564813ef3d | |||
| 5fed42a623 | |||
| b19dd4fc11 | |||
| 44226d6f7f | |||
| d75607a1d2 | |||
| bf66af0f25 | |||
| 15f3e474a0 | |||
| 5623cf946e | |||
| 0a6f3a99da | |||
| 50bd9f32c3 | |||
| 1396304af5 | |||
| c33e4adeec | |||
| 7351a35c42 | |||
| 72e3325626 | |||
| 0a46b5304d | |||
|  | e57d52d00e | ||
| 64e527672d | |||
| 123168a5ee | |||
| 3836d0dc9b | |||
| 51ab4bef38 | |||
|  | 567ca8a26f | ||
|  | 111305d09c | ||
|  | 67395f52b5 | ||
|  | 421226c0dc | ||
|  | 77da2c1ac6 | ||
|  | 39d3ba2f40 | ||
|  | fb62e54d63 | ||
|  | c968d6c541 | ||
|  | c428e6665f | ||
|  | 5b7e3f0336 | ||
|  | 0c8ef37860 | ||
|  | 794c479b9e | ||
|  | 1bee3114ac | ||
|  | 1344b65dd4 | ||
|  | 68dcf4dd28 | ||
|  | b0a8fd54a8 | ||
|  | 0f589ec57e | ||
|  | 2d4fc45a0c | ||
|  | c80f23f0db | ||
|  | c950400fe2 | ||
|  | 21c1e77d36 | ||
|  | bbfd0caf10 | ||
|  | 9192883217 | ||
| 3836622d27 | |||
| cc2c4be1b0 | |||
| 873940786f | |||
| db73dcffc7 | |||
| 8aec69f0f9 | |||
| 9f88eef249 | |||
| d689ce9aef | |||
| d5e4991982 | |||
| ca68b58246 | |||
| 747a1de321 | |||
| 9e92ede16f | |||
| 31f842471a | |||
| 7d0f9175be | |||
| e83307ca6d | |||
| 215eba41b7 | |||
| 52a3d1be1b | |||
| 8d543be5cc | |||
| 0474b25859 | |||
| db94af0958 | |||
| 3e8805bdda | |||
| a887602f4f | |||
| c1cf27c42d | |||
| fe6b4848e6 | |||
| b5af9f7b63 | |||
| 7f3de62b2c | |||
| cfa51cd659 | |||
| facc4affed | |||
| f9122341d1 | |||
| 7dd5f542a6 | |||
| 3b80d9a93b | |||
| 790576863f | |||
| 25e89571f7 | |||
| 435836c7d1 | |||
| af4db22184 | |||
| 2adc8b3bf6 | |||
| 21b79c1981 | |||
| 428494ca1f | |||
| 5d57ec8a3b | |||
| 719fabc878 | |||
| e9a9a3430f | |||
| c648a560cc | |||
| 3d7c8596ee | |||
| 345f379650 | |||
| 3262a1dd02 | |||
| a9f4f8c973 | |||
| c19c597ba0 | |||
| 03800029c9 | |||
| 064dfc5a56 | |||
| ba95687f46 | |||
| a309cc0774 | |||
| 5b0babb9b0 | |||
| ac2f314395 | |||
| 8c92d11722 | |||
| 3db4fff80d | |||
| fb743b522d | |||
| d1653a074b | |||
| 254122d125 | |||
| c9d2e37cee | |||
| c9d54a5fea | |||
| 86c862e69d | |||
| 9bc6fe6aff | |||
| 18a03fd740 | |||
| e9d4b9e2ab | |||
| efaad1981d | |||
| 742f2540f6 | |||
| bab6528ed6 | |||
| a25f2c7539 | |||
| c06e76a0ee | |||
| 4607c36b57 | |||
| 7c03a25f1a | |||
| cce04ee490 | |||
| e54633d14d | |||
| d9892f6822 | |||
| f75c7a0232 | |||
| 062afd6695 | |||
| 830dace1ba | |||
| 2ce9810243 | |||
| 26b3d84d62 | |||
| 30078db841 | |||
| aaac80be84 | |||
| a0fead48e1 | |||
| 2d09efb2e0 | |||
| 3a87513a11 | |||
| d3956319ca | |||
| bd36735cb1 | |||
| 1310d53589 | |||
| 610239930b | |||
| b65e2c62c4 | |||
| 89f5231649 | |||
| 73797b98f6 | |||
| 3d40db7493 | |||
| 760d65b972 | |||
| d26fa6bde6 | |||
| 427f232ab8 | |||
| 99818c211d | |||
| a9f0059743 | |||
| 5bc542a567 | |||
| 482f279dc5 | |||
| e0828b1f0f | |||
| e015f71bb0 | |||
| 04a48f22ad | |||
| ad4fe80240 | |||
| 4b82e67952 | |||
| c8ccce83fd | |||
| e9a9262fae | |||
| d9e37d0958 | |||
| 65c41e6fa9 | |||
| 7923b5a1ef | |||
| 4a229ebf6b | 
							
								
								
									
										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: "" | ||||
							
								
								
									
										7
									
								
								.changes/unreleased/Feature-20240614-153537.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.changes/unreleased/Feature-20240614-153537.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| kind: Feature | ||||
| body: The behavoir of the voters for stored objects is adjusted so as to limit edit | ||||
|   and delete possibilities to users related to the activity, social action or workflow | ||||
|   entity. | ||||
| time: 2024-06-14T15:35:37.582159301+02:00 | ||||
| custom: | ||||
|   Issue: "286" | ||||
							
								
								
									
										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" | ||||
| @@ -1,3 +0,0 @@ | ||||
| ## v2.23.1 - 2024-07-25 | ||||
| ### Fixed | ||||
| * Fix export activities linked to accompanying period between two dates  | ||||
							
								
								
									
										5
									
								
								.changes/v3.0.0.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.changes/v3.0.0.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| ## v3.0.0 - 2024-08-26 | ||||
| ### Fixed | ||||
| * Fix delete action for accompanying periods in draft state  | ||||
| * Fix connection to azure when making an calendar event in chill  | ||||
| * CollectionType js fixes for remove button and adding multiple entries  | ||||
| @@ -122,7 +122,7 @@ unit_tests: | ||||
|         - php tests/console chill:db:sync-views --env=test | ||||
|         - php -d memory_limit=2G tests/console cache:clear --env=test | ||||
|         - php -d memory_limit=3G tests/console doctrine:fixtures:load -n --env=test | ||||
|         - php -d memory_limit=4G bin/phpunit --colors=never --exclude-group dbIntensive | ||||
|         - php -d memory_limit=4G bin/phpunit --colors=never --exclude-group dbIntensive,openstack-integration,collabora-integration | ||||
|     artifacts: | ||||
|         expire_in: 1 day | ||||
|         paths: | ||||
| @@ -138,4 +138,4 @@ release: | ||||
|         - echo "running release_job" | ||||
|     release: | ||||
|         tag_name: '$CI_COMMIT_TAG' | ||||
|         description: "./.changes/v$CI_COMMIT_TAG.md" | ||||
|         description: "./.changes/$CI_COMMIT_TAG.md" | ||||
|   | ||||
| @@ -6,6 +6,12 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), | ||||
| and is generated by [Changie](https://github.com/miniscruff/changie). | ||||
|  | ||||
|  | ||||
| ## v3.0.0 - 2024-08-26 | ||||
| ### Fixed | ||||
| * Fix delete action for accompanying periods in draft state  | ||||
| * Fix connection to azure when making an calendar event in chill  | ||||
| * CollectionType js fixes for remove button and adding multiple entries  | ||||
|  | ||||
| ## v2.23.0 - 2024-07-23 | ||||
| ### Feature | ||||
| * ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles  | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
							
								
								
									
										125
									
								
								docs/source/installation/enable-collabora-for-dev.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								docs/source/installation/enable-collabora-for-dev.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
|  | ||||
| Enable CODE for development | ||||
| =========================== | ||||
|  | ||||
| For editing a document, there must be a way to communicate between the collabora server and the symfony server, in | ||||
| both direction. The domain name should also be the same for collabora server and for the browser which access to the | ||||
| online editor. | ||||
|  | ||||
| Using ngrok (or other http tunnel) | ||||
| ---------------------------------- | ||||
|  | ||||
| One can configure a tunnel server to expose your local install to the web, and access to your local server using the | ||||
| tunnel url. | ||||
|  | ||||
| Start ngrok | ||||
| ^^^^^^^^^^^ | ||||
|  | ||||
| This can be achieve using `ngrok <https://ngrok.com/>`_. | ||||
|  | ||||
| .. note:: | ||||
|  | ||||
|    The configuration of ngrok is outside of the scope of this document. Refers to the ngrok's documentation. | ||||
|  | ||||
| .. code-block:: bash | ||||
|  | ||||
|    # ensuring that your server is running through http and port 8000 | ||||
|    ngrok http 8000 | ||||
|    # then open the link given by the ngrok utility and you should reach your app | ||||
|  | ||||
| At this step, ensure that you can reach your local app using the ngrok url. | ||||
|  | ||||
| Configure Collabora | ||||
| ^^^^^^^^^^^^^^^^^^^ | ||||
|  | ||||
| The collabora server must be executed online and configure to access to your ngrok installation. Ensure that the aliasgroup | ||||
| exists for your ngrok application (`See the CODE documentation: <https://sdk.collaboraonline.com/docs/installation/Configuration.html#multihost-configuration>`_). | ||||
|  | ||||
| Configure your app | ||||
| ^^^^^^^^^^^^^^^^^^ | ||||
|  | ||||
| Set the :code:`EDITOR_SERVER` variable to point to your collabora server, this should be done in your :code:`.env.local` file. | ||||
|  | ||||
| At this point, everything must be fine. In case of errors, watch the log from your collabora server, use the `profiler <https://symfony.com/doc/current/profiler.html>`_ | ||||
| to debug the requests. | ||||
|  | ||||
| .. note:: | ||||
|  | ||||
|    In case of error while validating proof (you'll see those message in the collabora's logs), you can temporarily disable | ||||
|    the proof validation adding this code snippet in `config/services.yaml`: | ||||
|  | ||||
|    .. code-block:: yaml | ||||
|  | ||||
|       when@dev: | ||||
|           # add only in dev environment, to avoid security problems | ||||
|           services: | ||||
|               ChampsLibres\WopiLib\Contract\Service\ProofValidatorInterface: | ||||
|                   # this class will always validate proof | ||||
|                   alias: Chill\WopiBundle\Service\Wopi\NullProofValidator | ||||
|  | ||||
| With a local CODE image | ||||
| ----------------------- | ||||
|  | ||||
| .. warning:: | ||||
|  | ||||
|    This configuration is not sure, and must be refined. The documentation does not seems to be entirely valid. | ||||
|  | ||||
| Use a local domain name and https for your app | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
|  | ||||
| Use the proxy feature from embedded symfony server to run your app. `See the dedicated doc <https://symfony.com/doc/current/setup/symfony_server.html#local-domain-names>` | ||||
|  | ||||
| Configure also the `https certificate <https://symfony.com/doc/current/setup/symfony_server.html#enabling-tls>`_ | ||||
|  | ||||
| In this example, your local domain name will be :code:`my-domain` and the url will be :code:`https://my-domain.wip`. | ||||
|  | ||||
| Ensure that the proxy is running. | ||||
|  | ||||
| Create a certificate database for collabora | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
|  | ||||
| Collabora must validate your certificate generated by symfony console. For that, you need `to create a NSS database <https://sdk.collaboraonline.com/docs/installation/Configuration.html#validating-digital-signatures>` | ||||
| and configure collabora to use it. | ||||
|  | ||||
| At first, export the certificate for symfony development. Use the graphical interface from your browser to get the | ||||
| certificate as a PEM file. | ||||
|  | ||||
| .. code-block:: bash | ||||
|  | ||||
|    # create your database in a custom directory | ||||
|    mkdir /path/to/your/directory | ||||
|    certutil -N -d /path/to/your/directory | ||||
|    cat /path/to/your/ca.crt | certutil -d . -A symfony -t -t C,P,C,u,w -a | ||||
|  | ||||
| Launch CODE properly configured | ||||
|  | ||||
| .. code-block:: yaml | ||||
|  | ||||
|      collabora: | ||||
|          image: collabora/code:latest | ||||
|          environment: | ||||
|              - SLEEPFORDEBUGGER=0 | ||||
|              - DONT_GEN_SSL_CERT="True" | ||||
|              # add path to the database | ||||
|              - extra_params=--o:ssl.enable=false --o:ssl.termination=false --o:logging.level=7 -o:certificates.database_path=/etc/custom-certificates/nss-database | ||||
|              - username=admin | ||||
|              - password=admin | ||||
|              - dictionaries=en_US | ||||
|              - aliasgroup1=https://my-domain.wip | ||||
|          ports: | ||||
|              - "127.0.0.1:9980:9980" | ||||
|          volumes: | ||||
|              - "/path/to/your/directory/nss-database:/etc/custom-certificates/nss-database" | ||||
|          extra_hosts: | ||||
|              - "my-domain.wip:host-gateway" | ||||
|  | ||||
| Configure your app | ||||
| ^^^^^^^^^^^^^^^^^^ | ||||
|  | ||||
| Into your :code:`.env.local` file: | ||||
|  | ||||
| .. code-block:: env | ||||
|  | ||||
|    EDITOR_SERVER=http://${COLLABORA_HOST}:${COLLABORA_PORT} | ||||
|  | ||||
| At this step, you should be able to edit a document through collabora. | ||||
| @@ -53,7 +53,7 @@ | ||||
|     "marked": "^12.0.2", | ||||
|     "masonry-layout": "^4.2.2", | ||||
|     "mime": "^4.0.0", | ||||
|     "swagger-ui": "^4.15.5", | ||||
|     "pdfjs-dist": "^4.3.136", | ||||
|     "vis-network": "^9.1.0", | ||||
|     "vue": "^3.2.37", | ||||
|     "vue-i18n": "^9.1.6", | ||||
|   | ||||
| @@ -69,9 +69,8 @@ return static function (RectorConfig $rectorConfig): void { | ||||
|  | ||||
|     // skip some path... | ||||
|     $rectorConfig->skip([ | ||||
|         // we must adapt service definition | ||||
|         \Rector\Symfony\Symfony28\Rector\MethodCall\GetToConstructorInjectionRector::class, | ||||
|         \Rector\Symfony\Symfony34\Rector\Closure\ContainerGetNameToTypeInTestsRector::class, | ||||
|         // waiting for fixing this bug: https://github.com/rectorphp/rector-doctrine/issues/342 | ||||
|         \Rector\Doctrine\CodeQuality\Rector\Property\ImproveDoctrineCollectionDocTypeInEntityRector::class, | ||||
|     ]); | ||||
|  | ||||
|     $rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [ | ||||
|   | ||||
| @@ -68,7 +68,7 @@ final class ActivityController extends AbstractController | ||||
|         private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|         private readonly PaginatorFactory $paginatorFactory, | ||||
|         private readonly ChillSecurity $security | ||||
|         private readonly ChillSecurity $security, | ||||
|     ) {} | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -80,7 +80,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac | ||||
|     private \DateTime $date; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<StoredObject> | ||||
|      * @var Collection<int, StoredObject> | ||||
|      */ | ||||
|     #[Assert\Valid(traverse: true)] | ||||
|     #[ORM\ManyToMany(targetEntity: StoredObject::class, cascade: ['persist'])] | ||||
| @@ -107,7 +107,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac | ||||
|     private ?Person $person = null; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<Person> | ||||
|      * @var Collection<int, \Chill\PersonBundle\Entity\Person> | ||||
|      */ | ||||
|     #[Groups(['read', 'docgen:read'])] | ||||
|     #[ORM\ManyToMany(targetEntity: Person::class)] | ||||
| @@ -117,7 +117,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac | ||||
|     private PrivateCommentEmbeddable $privateComment; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<ActivityReason> | ||||
|      * @var Collection<int, ActivityReason> | ||||
|      */ | ||||
|     #[Groups(['docgen:read'])] | ||||
|     #[ORM\ManyToMany(targetEntity: ActivityReason::class)] | ||||
| @@ -132,7 +132,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac | ||||
|     private string $sentReceived = ''; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<SocialAction> | ||||
|      * @var Collection<int, \Chill\PersonBundle\Entity\SocialWork\SocialAction> | ||||
|      */ | ||||
|     #[Groups(['read', 'docgen:read'])] | ||||
|     #[ORM\ManyToMany(targetEntity: SocialAction::class)] | ||||
| @@ -140,7 +140,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac | ||||
|     private Collection $socialActions; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<SocialIssue> | ||||
|      * @var Collection<int, SocialIssue> | ||||
|      */ | ||||
|     #[Groups(['read', 'docgen:read'])] | ||||
|     #[ORM\ManyToMany(targetEntity: SocialIssue::class)] | ||||
| @@ -148,7 +148,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac | ||||
|     private Collection $socialIssues; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<ThirdParty> | ||||
|      * @var Collection<int, ThirdParty> | ||||
|      */ | ||||
|     #[Groups(['read', 'docgen:read'])] | ||||
|     #[ORM\ManyToMany(targetEntity: ThirdParty::class)] | ||||
| @@ -162,7 +162,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac | ||||
|     private ?User $user = null; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<User> | ||||
|      * @var Collection<int, User> | ||||
|      */ | ||||
|     #[Groups(['read', 'docgen:read'])] | ||||
|     #[ORM\ManyToMany(targetEntity: User::class)] | ||||
|   | ||||
| @@ -40,9 +40,9 @@ class ActivityReasonCategory implements \Stringable | ||||
|     /** | ||||
|      * Array of ActivityReason. | ||||
|      * | ||||
|      * @var Collection<ActivityReason> | ||||
|      * @var Collection<int, ActivityReason> | ||||
|      */ | ||||
|     #[ORM\OneToMany(targetEntity: ActivityReason::class, mappedBy: 'category')] | ||||
|     #[ORM\OneToMany(mappedBy: 'category', targetEntity: ActivityReason::class)] | ||||
|     private Collection $reasons; | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -28,7 +28,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali | ||||
|     public function __construct( | ||||
|         protected ActivityReasonCategoryRepository $activityReasonCategoryRepository, | ||||
|         protected ActivityReasonRepository $activityReasonRepository, | ||||
|         protected TranslatableStringHelper $translatableStringHelper | ||||
|         protected TranslatableStringHelper $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class ActivityUsersJobAggregator implements AggregatorInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly UserJobRepositoryInterface $userJobRepository, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class ActivityUsersScopeAggregator implements AggregatorInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly ScopeRepositoryInterface $scopeRepository, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class CreatorJobAggregator implements AggregatorInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly UserJobRepositoryInterface $userJobRepository, | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class CreatorScopeAggregator implements AggregatorInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly ScopeRepository $scopeRepository, | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -42,7 +42,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface | ||||
|         /** | ||||
|          * The action for this report. | ||||
|          */ | ||||
|         protected string $action = 'sum' | ||||
|         protected string $action = 'sum', | ||||
|     ) { | ||||
|         $this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center']; | ||||
|     } | ||||
|   | ||||
| @@ -39,7 +39,7 @@ class ListActivityHelper | ||||
|         private readonly TranslatorInterface $translator, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|         private readonly TranslatableStringExportLabelHelper $translatableStringLabelHelper, | ||||
|         private readonly UserHelper $userHelper | ||||
|         private readonly UserHelper $userHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addSelect(QueryBuilder $qb): void | ||||
|   | ||||
| @@ -73,15 +73,13 @@ 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 = activity.accompanyingPeriod" | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         $qb | ||||
|             ->setParameter($from, $this->rollingDateConverter->convert($data['start_date'])) | ||||
|             ->setParameter($to, $this->rollingDateConverter->convert($data['end_date'])); | ||||
|  | ||||
|         dump($qb->getQuery()->getResult()); | ||||
|     } | ||||
|  | ||||
|     public function applyOn() | ||||
|   | ||||
| @@ -25,7 +25,7 @@ final readonly class ActivityPresenceFilter implements FilterInterface | ||||
| { | ||||
|     public function __construct( | ||||
|         private TranslatableStringHelperInterface $translatableStringHelper, | ||||
|         private TranslatorInterface $translator | ||||
|         private TranslatorInterface $translator, | ||||
|     ) {} | ||||
|  | ||||
|     public function getTitle() | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter | ||||
| { | ||||
|     public function __construct( | ||||
|         protected TranslatableStringHelperInterface $translatableStringHelper, | ||||
|         protected ActivityTypeRepositoryInterface $activityTypeRepository | ||||
|         protected ActivityTypeRepositoryInterface $activityTypeRepository, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -29,7 +29,7 @@ class UsersJobFilter implements FilterInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|         private readonly UserJobRepositoryInterface $userJobRepository | ||||
|         private readonly UserJobRepositoryInterface $userJobRepository, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -29,7 +29,7 @@ class UsersScopeFilter implements FilterInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly ScopeRepositoryInterface $scopeRepository, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -59,7 +59,7 @@ class ActivityType extends AbstractType | ||||
|         protected TranslatableStringHelper $translatableStringHelper, | ||||
|         protected array $timeChoices, | ||||
|         protected SocialIssueRender $socialIssueRender, | ||||
|         protected SocialActionRender $socialActionRender | ||||
|         protected SocialActionRender $socialActionRender, | ||||
|     ) { | ||||
|         if (!$tokenStorage->getToken()->getUser() instanceof User) { | ||||
|             throw new \RuntimeException('you should have a valid user'); | ||||
|   | ||||
| @@ -27,7 +27,7 @@ class PickActivityReasonType extends AbstractType | ||||
|     public function __construct( | ||||
|         private readonly ActivityReasonRepository $activityReasonRepository, | ||||
|         private readonly ActivityReasonRender $reasonRender, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function configureOptions(OptionsResolver $resolver) | ||||
|   | ||||
| @@ -32,7 +32,7 @@ final readonly class ActivityDocumentACLAwareRepository implements ActivityDocum | ||||
|         private EntityManagerInterface $em, | ||||
|         private CenterResolverManagerInterface $centerResolverManager, | ||||
|         private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser, | ||||
|         private Security $security | ||||
|         private Security $security, | ||||
|     ) {} | ||||
|  | ||||
|     public function buildFetchQueryActivityDocumentLinkedToPersonFromPersonContext(Person $person, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null): FetchQueryInterface | ||||
|   | ||||
| @@ -25,7 +25,7 @@ class ActivityReasonRepository extends ServiceEntityRepository | ||||
| { | ||||
|     public function __construct( | ||||
|         ManagerRegistry $registry, | ||||
|         private readonly RequestStack $requestStack | ||||
|         private readonly RequestStack $requestStack, | ||||
|     ) { | ||||
|         parent::__construct($registry, ActivityReason::class); | ||||
|     } | ||||
|   | ||||
| @@ -12,6 +12,8 @@ declare(strict_types=1); | ||||
| namespace Chill\ActivityBundle\Repository; | ||||
|  | ||||
| use Chill\ActivityBundle\Entity\Activity; | ||||
| use Chill\DocStoreBundle\Entity\StoredObject; | ||||
| use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface; | ||||
| use Chill\PersonBundle\Entity\AccompanyingPeriod; | ||||
| use Chill\PersonBundle\Entity\Person; | ||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | ||||
| @@ -23,7 +25,7 @@ use Doctrine\Persistence\ManagerRegistry; | ||||
|  * @method Activity[]    findAll() | ||||
|  * @method Activity[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) | ||||
|  */ | ||||
| class ActivityRepository extends ServiceEntityRepository | ||||
| class ActivityRepository extends ServiceEntityRepository implements AssociatedEntityToStoredObjectInterface | ||||
| { | ||||
|     public function __construct(ManagerRegistry $registry) | ||||
|     { | ||||
| @@ -97,4 +99,16 @@ class ActivityRepository extends ServiceEntityRepository | ||||
|  | ||||
|         return $qb->getQuery()->getResult(); | ||||
|     } | ||||
|  | ||||
|     public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?Activity | ||||
|     { | ||||
|         $qb = $this->createQueryBuilder('a'); | ||||
|         $query = $qb | ||||
|             ->leftJoin('a.documents', 'ad') | ||||
|             ->where('ad.id = :storedObjectId') | ||||
|             ->setParameter('storedObjectId', $storedObject->getId()) | ||||
|             ->getQuery(); | ||||
|  | ||||
|         return $query->getOneOrNullResult(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,54 @@ | ||||
| <?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\ActivityBundle\Security\Authorization; | ||||
|  | ||||
| use Chill\ActivityBundle\Entity\Activity; | ||||
| use Chill\ActivityBundle\Repository\ActivityRepository; | ||||
| use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface; | ||||
| use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum; | ||||
| use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter; | ||||
| use Chill\DocStoreBundle\Service\WorkflowStoredObjectPermissionHelper; | ||||
| use Symfony\Component\Security\Core\Security; | ||||
|  | ||||
| class ActivityStoredObjectVoter extends AbstractStoredObjectVoter | ||||
| { | ||||
|     public function __construct( | ||||
|         private readonly ActivityRepository $repository, | ||||
|         Security $security, | ||||
|         WorkflowStoredObjectPermissionHelper $workflowDocumentService, | ||||
|     ) { | ||||
|         parent::__construct($security, $workflowDocumentService); | ||||
|     } | ||||
|  | ||||
|     protected function getRepository(): AssociatedEntityToStoredObjectInterface | ||||
|     { | ||||
|         return $this->repository; | ||||
|     } | ||||
|  | ||||
|     protected function getClass(): string | ||||
|     { | ||||
|         return Activity::class; | ||||
|     } | ||||
|  | ||||
|     protected function attributeToRole(StoredObjectRoleEnum $attribute): string | ||||
|     { | ||||
|         return match ($attribute) { | ||||
|             StoredObjectRoleEnum::EDIT => ActivityVoter::UPDATE, | ||||
|             StoredObjectRoleEnum::SEE => ActivityVoter::SEE_DETAILS, | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     protected function canBeAssociatedWithWorkflow(): bool | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| @@ -75,7 +75,7 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn | ||||
|  | ||||
|     public function __construct( | ||||
|         protected Security $security, | ||||
|         VoterHelperFactoryInterface $voterHelperFactory | ||||
|         VoterHelperFactoryInterface $voterHelperFactory, | ||||
|     ) { | ||||
|         $this->voterHelper = $voterHelperFactory->generate(self::class) | ||||
|             ->addCheckFor(Person::class, [self::SEE, self::CREATE]) | ||||
|   | ||||
| @@ -50,7 +50,7 @@ class ActivityContext implements | ||||
|         private readonly TranslatorInterface $translator, | ||||
|         private readonly BaseContextData $baseContextData, | ||||
|         private readonly ThirdPartyRender $thirdPartyRender, | ||||
|         private readonly ThirdPartyRepository $thirdPartyRepository | ||||
|         private readonly ThirdPartyRepository $thirdPartyRepository, | ||||
|     ) {} | ||||
|  | ||||
|     public function adminFormReverseTransform(array $data): array | ||||
|   | ||||
| @@ -56,7 +56,7 @@ class ListActivitiesByAccompanyingPeriodContext implements | ||||
|         private readonly SocialIssueRepository $socialIssueRepository, | ||||
|         private readonly ThirdPartyRepository $thirdPartyRepository, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|         private readonly UserRepository $userRepository | ||||
|         private readonly UserRepository $userRepository, | ||||
|     ) {} | ||||
|  | ||||
|     public function adminFormReverseTransform(array $data): array | ||||
|   | ||||
| @@ -76,7 +76,7 @@ final class TranslatableActivityReasonTest extends TypeTestCase | ||||
|      */ | ||||
|     protected function getTranslatableStringHelper( | ||||
|         $locale = 'en', | ||||
|         $fallbackLocale = 'en' | ||||
|         $fallbackLocale = 'en', | ||||
|     ) { | ||||
|         $prophet = new \Prophecy\Prophet(); | ||||
|         $requestStack = $prophet->prophesize(); | ||||
|   | ||||
| @@ -138,7 +138,7 @@ final class ActivityVoterTest extends KernelTestCase | ||||
|         Scope $scope, | ||||
|         Center $center, | ||||
|         $attribute, | ||||
|         $message | ||||
|         $message, | ||||
|     ) { | ||||
|         $token = $this->prepareToken($user); | ||||
|         $activity = $this->prepareActivity($scope, $this->preparePerson($center)); | ||||
|   | ||||
| @@ -32,7 +32,7 @@ class TimelineActivityProvider implements TimelineProviderInterface | ||||
|         protected EntityManagerInterface $em, | ||||
|         protected AuthorizationHelperInterface $helper, | ||||
|         TokenStorageInterface $storage, | ||||
|         protected ActivityACLAwareRepository $aclAwareRepository | ||||
|         protected ActivityACLAwareRepository $aclAwareRepository, | ||||
|     ) { | ||||
|         if (!$storage->getToken()->getUser() instanceof User) { | ||||
|             throw new \RuntimeException('A user should be authenticated !'); | ||||
|   | ||||
| @@ -25,7 +25,7 @@ final class AsideActivityController extends CRUDController | ||||
| { | ||||
|     public function __construct( | ||||
|         private readonly AsideActivityCategoryRepository $categoryRepository, | ||||
|         private readonly Security $security | ||||
|         private readonly Security $security, | ||||
|     ) {} | ||||
|  | ||||
|     public function createEntity(string $action, Request $request): object | ||||
| @@ -76,7 +76,7 @@ final class AsideActivityController extends CRUDController | ||||
|         string $action, | ||||
|         $query, | ||||
|         Request $request, | ||||
|         PaginatorInterface $paginator | ||||
|         PaginatorInterface $paginator, | ||||
|     ) { | ||||
|         if ('index' === $action) { | ||||
|             return $query->orderBy('e.date', 'DESC'); | ||||
|   | ||||
| @@ -22,9 +22,9 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface; | ||||
| class AsideActivityCategory | ||||
| { | ||||
|     /** | ||||
|      * @var Collection<AsideActivityCategory> | ||||
|      * @var Collection<int, AsideActivityCategory> | ||||
|      */ | ||||
|     #[ORM\OneToMany(targetEntity: AsideActivityCategory::class, mappedBy: 'parent')] | ||||
|     #[ORM\OneToMany(mappedBy: 'parent', targetEntity: AsideActivityCategory::class)] | ||||
|     private Collection $children; | ||||
|  | ||||
|     #[ORM\Id] | ||||
|   | ||||
| @@ -22,7 +22,7 @@ class ByActivityTypeAggregator implements AggregatorInterface | ||||
| { | ||||
|     public function __construct( | ||||
|         private readonly AsideActivityCategoryRepository $asideActivityCategoryRepository, | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class ByUserJobAggregator implements AggregatorInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly UserJobRepositoryInterface $userJobRepository, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class ByUserScopeAggregator implements AggregatorInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly ScopeRepositoryInterface $scopeRepository, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -41,7 +41,7 @@ final readonly class ListAsideActivity implements ListInterface, GroupedExportIn | ||||
|         private AsideActivityCategoryRepository $asideActivityCategoryRepository, | ||||
|         private CategoryRender $categoryRender, | ||||
|         private LocationRepository $locationRepository, | ||||
|         private TranslatableStringHelperInterface $translatableStringHelper | ||||
|         private TranslatableStringHelperInterface $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder) {} | ||||
|   | ||||
| @@ -27,7 +27,7 @@ class ByActivityTypeFilter implements FilterInterface | ||||
|     public function __construct( | ||||
|         private readonly CategoryRender $categoryRender, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|         private readonly AsideActivityCategoryRepository $asideActivityTypeRepository | ||||
|         private readonly AsideActivityCategoryRepository $asideActivityTypeRepository, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -24,7 +24,7 @@ use Symfony\Component\Security\Core\Security; | ||||
| final readonly class ByLocationFilter implements FilterInterface | ||||
| { | ||||
|     public function __construct( | ||||
|         private Security $security | ||||
|         private Security $security, | ||||
|     ) {} | ||||
|  | ||||
|     public function getTitle(): string | ||||
|   | ||||
| @@ -29,7 +29,7 @@ class ByUserJobFilter implements FilterInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|         private readonly UserJobRepositoryInterface $userJobRepository | ||||
|         private readonly UserJobRepositoryInterface $userJobRepository, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -29,7 +29,7 @@ class ByUserScopeFilter implements FilterInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly ScopeRepositoryInterface $scopeRepository, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -44,7 +44,7 @@ class UserMenuBuilder implements LocalMenuBuilderInterface | ||||
|         CountNotificationTask $counter, | ||||
|         TokenStorageInterface $tokenStorage, | ||||
|         TranslatorInterface $translator, | ||||
|         AuthorizationCheckerInterface $authorizationChecker | ||||
|         AuthorizationCheckerInterface $authorizationChecker, | ||||
|     ) { | ||||
|         $this->counter = $counter; | ||||
|         $this->tokenStorage = $tokenStorage; | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class AsideActivityVoter extends AbstractChillVoter implements ProvideRoleHierar | ||||
|     private readonly VoterHelperInterface $voterHelper; | ||||
|  | ||||
|     public function __construct( | ||||
|         VoterHelperFactoryInterface $voterHelperFactory | ||||
|         VoterHelperFactoryInterface $voterHelperFactory, | ||||
|     ) { | ||||
|         $this->voterHelper = $voterHelperFactory | ||||
|             ->generate(self::class) | ||||
|   | ||||
| @@ -47,7 +47,7 @@ class SendTestShortMessageOnCalendarCommand extends Command | ||||
|         private readonly PhoneNumberHelperInterface $phoneNumberHelper, | ||||
|         private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder, | ||||
|         private readonly ShortMessageTransporterInterface $transporter, | ||||
|         private readonly UserRepositoryInterface $userRepository | ||||
|         private readonly UserRepositoryInterface $userRepository, | ||||
|     ) { | ||||
|         parent::__construct('chill:calendar:test-send-short-message'); | ||||
|     } | ||||
|   | ||||
| @@ -59,7 +59,7 @@ class CalendarController extends AbstractController | ||||
|         private readonly AccompanyingPeriodRepository $accompanyingPeriodRepository, | ||||
|         private readonly UserRepositoryInterface $userRepository, | ||||
|         private readonly TranslatorInterface $translator, | ||||
|         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry | ||||
|         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry, | ||||
|     ) {} | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -103,7 +103,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente | ||||
|     private int $dateTimeVersion = 0; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<CalendarDoc> | ||||
|      * @var Collection<int, \Chill\CalendarBundle\Entity\CalendarDoc> | ||||
|      */ | ||||
|     #[ORM\OneToMany(mappedBy: 'calendar', targetEntity: CalendarDoc::class, orphanRemoval: true)] | ||||
|     private Collection $documents; | ||||
| @@ -120,7 +120,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente | ||||
|     private ?int $id = null; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection&Selectable<int, Invite> | ||||
|      * @var \Doctrine\Common\Collections\Collection<int, \Chill\CalendarBundle\Entity\Invite>&Selectable | ||||
|      */ | ||||
|     #[Serializer\Groups(['read', 'docgen:read'])] | ||||
|     #[ORM\OneToMany(mappedBy: 'calendar', targetEntity: Invite::class, cascade: ['persist', 'remove', 'merge', 'detach'], orphanRemoval: true)] | ||||
| @@ -143,7 +143,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente | ||||
|     private ?Person $person = null; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<Person> | ||||
|      * @var Collection<int, Person> | ||||
|      */ | ||||
|     #[Serializer\Groups(['calendar:read', 'read', 'calendar:light', 'docgen:read'])] | ||||
|     #[Assert\Count(min: 1, minMessage: 'calendar.At least {{ limit }} person is required.')] | ||||
| @@ -157,7 +157,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente | ||||
|     private PrivateCommentEmbeddable $privateComment; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<ThirdParty> | ||||
|      * @var Collection<int, ThirdParty> | ||||
|      */ | ||||
|     #[Serializer\Groups(['calendar:read', 'read', 'calendar:light', 'docgen:read'])] | ||||
|     #[ORM\ManyToMany(targetEntity: ThirdParty::class)] | ||||
|   | ||||
| @@ -47,7 +47,7 @@ class CalendarDoc implements TrackCreationInterface, TrackUpdateInterface | ||||
|         Calendar $calendar, | ||||
|         #[ORM\ManyToOne(targetEntity: StoredObject::class, cascade: ['persist'])] | ||||
|         #[ORM\JoinColumn(nullable: false)] | ||||
|         private ?StoredObject $storedObject | ||||
|         private ?StoredObject $storedObject, | ||||
|     ) { | ||||
|         $this->setCalendar($calendar); | ||||
|         $this->datetimeVersion = $calendar->getDateTimeVersion(); | ||||
|   | ||||
| @@ -26,7 +26,7 @@ final readonly class JobAggregator implements AggregatorInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private UserJobRepository $jobRepository, | ||||
|         private TranslatableStringHelper $translatableStringHelper | ||||
|         private TranslatableStringHelper $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -26,7 +26,7 @@ final readonly class ScopeAggregator implements AggregatorInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private ScopeRepository $scopeRepository, | ||||
|         private TranslatableStringHelper $translatableStringHelper | ||||
|         private TranslatableStringHelper $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -28,7 +28,7 @@ final readonly class JobFilter implements FilterInterface | ||||
|  | ||||
|     public function __construct( | ||||
|         private TranslatableStringHelper $translatableStringHelper, | ||||
|         private UserJobRepositoryInterface $userJobRepository | ||||
|         private UserJobRepositoryInterface $userJobRepository, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -30,7 +30,7 @@ class ScopeFilter implements FilterInterface | ||||
|     public function __construct( | ||||
|         protected TranslatorInterface $translator, | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper, | ||||
|         private readonly ScopeRepositoryInterface $scopeRepository | ||||
|         private readonly ScopeRepositoryInterface $scopeRepository, | ||||
|     ) {} | ||||
|  | ||||
|     public function addRole(): ?string | ||||
|   | ||||
| @@ -37,7 +37,7 @@ class CalendarType extends AbstractType | ||||
|         private readonly IdToUsersDataTransformer $idToUsersDataTransformer, | ||||
|         private readonly IdToLocationDataTransformer $idToLocationDataTransformer, | ||||
|         private readonly ThirdPartiesToIdDataTransformer $partiesToIdDataTransformer, | ||||
|         private readonly IdToCalendarRangeDataTransformer $calendarRangeDataTransformer | ||||
|         private readonly IdToCalendarRangeDataTransformer $calendarRangeDataTransformer, | ||||
|     ) {} | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder, array $options) | ||||
|   | ||||
| @@ -46,7 +46,7 @@ class CalendarMessage | ||||
|     public function __construct( | ||||
|         Calendar $calendar, | ||||
|         private readonly string $action, | ||||
|         User $byUser | ||||
|         User $byUser, | ||||
|     ) { | ||||
|         $this->calendarId = $calendar->getId(); | ||||
|         $this->byUserId = $byUser->getId(); | ||||
|   | ||||
| @@ -59,7 +59,7 @@ final readonly class MSUserAbsenceReader implements MSUserAbsenceReaderInterface | ||||
|             'alwaysEnabled' => true, | ||||
|             'scheduled' => RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledStartDateTime']['dateTime']) < $this->clock->now() | ||||
|                 && RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledEndDateTime']['dateTime']) > $this->clock->now(), | ||||
|             default => throw new UserAbsenceSyncException('this status is not documented by Microsoft') | ||||
|             default => throw new UserAbsenceSyncException('this status is not documented by Microsoft'), | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -177,7 +177,7 @@ class MapCalendarToUser | ||||
|         User $user, | ||||
|         int $expiration, | ||||
|         ?string $id = null, | ||||
|         ?string $secret = null | ||||
|         ?string $secret = null, | ||||
|     ): void { | ||||
|         $user->setAttributeByDomain(self::METADATA_KEY, self::EXPIRATION_SUBSCRIPTION_EVENT, $expiration); | ||||
|  | ||||
|   | ||||
| @@ -57,7 +57,7 @@ class RemoteEventConverter | ||||
|         private readonly LocationConverter $locationConverter, | ||||
|         private readonly LoggerInterface $logger, | ||||
|         private readonly PersonRenderInterface $personRender, | ||||
|         private readonly TranslatorInterface $translator | ||||
|         private readonly TranslatorInterface $translator, | ||||
|     ) { | ||||
|         $this->defaultDateTimeZone = (new \DateTimeImmutable())->getTimezone(); | ||||
|         $this->remoteDateTimeZone = self::getRemoteTimeZone(); | ||||
|   | ||||
| @@ -351,7 +351,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface | ||||
|         [ | ||||
|             'id' => $id, | ||||
|             'lastModifiedDateTime' => $lastModified, | ||||
|             'changeKey' => $changeKey | ||||
|             'changeKey' => $changeKey, | ||||
|         ] = $this->createOnRemote($eventData, $calendar->getMainUser(), 'calendar_'.$calendar->getId()); | ||||
|  | ||||
|         if (null === $id) { | ||||
| @@ -427,7 +427,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface | ||||
|         [ | ||||
|             'id' => $id, | ||||
|             'lastModifiedDateTime' => $lastModified, | ||||
|             'changeKey' => $changeKey | ||||
|             'changeKey' => $changeKey, | ||||
|         ] = $this->createOnRemote( | ||||
|             $eventData, | ||||
|             $calendarRange->getUser(), | ||||
| @@ -564,7 +564,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface | ||||
|             [ | ||||
|                 'id' => $id, | ||||
|                 'lastModifiedDateTime' => $lastModified, | ||||
|                 'changeKey' => $changeKey | ||||
|                 'changeKey' => $changeKey, | ||||
|             ] = $this->patchOnRemote( | ||||
|                 $calendar->getRemoteId(), | ||||
|                 $eventData, | ||||
|   | ||||
| @@ -33,6 +33,6 @@ class RemoteEvent | ||||
|         #[Serializer\Groups(['read'])] | ||||
|         public \DateTimeImmutable $endDate, | ||||
|         #[Serializer\Groups(['read'])] | ||||
|         public bool $isAllDay = false | ||||
|         public bool $isAllDay = false, | ||||
|     ) {} | ||||
| } | ||||
|   | ||||
| @@ -65,7 +65,7 @@ class CalendarRangeRepository implements ObjectRepository | ||||
|         \DateTimeImmutable $from, | ||||
|         \DateTimeImmutable $to, | ||||
|         ?int $limit = null, | ||||
|         ?int $offset = null | ||||
|         ?int $offset = null, | ||||
|     ): array { | ||||
|         $qb = $this->buildQueryAvailableRangesForUser($user, $from, $to); | ||||
|  | ||||
|   | ||||
| @@ -40,7 +40,7 @@ final readonly class CalendarContext implements CalendarContextInterface | ||||
|         private PersonRepository $personRepository, | ||||
|         private ThirdPartyRender $thirdPartyRender, | ||||
|         private ThirdPartyRepository $thirdPartyRepository, | ||||
|         private TranslatableStringHelperInterface $translatableStringHelper | ||||
|         private TranslatableStringHelperInterface $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function adminFormReverseTransform(array $data): array | ||||
|   | ||||
| @@ -37,7 +37,7 @@ final readonly class AccompanyingPeriodCalendarGenericDocProvider implements Gen | ||||
|  | ||||
|     public function __construct( | ||||
|         private Security $security, | ||||
|         private EntityManagerInterface $em | ||||
|         private EntityManagerInterface $em, | ||||
|     ) {} | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -36,7 +36,7 @@ final readonly class PersonCalendarGenericDocProvider implements GenericDocForPe | ||||
|  | ||||
|     public function __construct( | ||||
|         private Security $security, | ||||
|         private EntityManagerInterface $em | ||||
|         private EntityManagerInterface $em, | ||||
|     ) {} | ||||
|  | ||||
|     private function addWhereClausesToQuery(FetchQuery $query, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery | ||||
|   | ||||
| @@ -156,7 +156,7 @@ final class CalendarTypeTest extends TypeTestCase | ||||
|  | ||||
|     private function buildMultiToIdDataTransformer( | ||||
|         string $classTransformer, | ||||
|         string $objClass | ||||
|         string $objClass, | ||||
|     ) { | ||||
|         $transformer = $this->prophesize($classTransformer); | ||||
|         $transformer->transform(Argument::type('array')) | ||||
| @@ -195,7 +195,7 @@ final class CalendarTypeTest extends TypeTestCase | ||||
|  | ||||
|     private function buildSingleToIdDataTransformer( | ||||
|         string $classTransformer, | ||||
|         string $class | ||||
|         string $class, | ||||
|     ) { | ||||
|         $transformer = $this->prophesize($classTransformer); | ||||
|         $transformer->transform(Argument::type('object')) | ||||
|   | ||||
| @@ -47,7 +47,7 @@ final class CalendarContextTest extends TestCase | ||||
|     { | ||||
|         $expected = | ||||
|             [ | ||||
|                 'track_datetime' => true, | ||||
|                 'trackDatetime' => true, | ||||
|                 'askMainPerson' => true, | ||||
|                 'mainPersonLabel' => 'docgen.calendar.Destinee', | ||||
|                 'askThirdParty' => false, | ||||
| @@ -61,7 +61,7 @@ final class CalendarContextTest extends TestCase | ||||
|     { | ||||
|         $expected = | ||||
|             [ | ||||
|                 'track_datetime' => true, | ||||
|                 'trackDatetime' => true, | ||||
|                 'askMainPerson' => true, | ||||
|                 'mainPersonLabel' => 'docgen.calendar.Destinee', | ||||
|                 'askThirdParty' => false, | ||||
| @@ -203,7 +203,7 @@ final class CalendarContextTest extends TestCase | ||||
|  | ||||
|     private function buildCalendarContext( | ||||
|         ?EntityManagerInterface $entityManager = null, | ||||
|         ?NormalizerInterface $normalizer = null | ||||
|         ?NormalizerInterface $normalizer = null, | ||||
|     ): CalendarContext { | ||||
|         $baseContext = $this->prophesize(BaseContextData::class); | ||||
|         $baseContext->getData(null)->willReturn(['base_context' => 'data']); | ||||
|   | ||||
| @@ -44,7 +44,7 @@ class CreateFieldsOnGroupCommand extends Command | ||||
|         private readonly EntityManager $entityManager, | ||||
|         private readonly ValidatorInterface $validator, | ||||
|         private $availableLanguages, | ||||
|         private $customizablesEntities | ||||
|         private $customizablesEntities, | ||||
|     ) { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|   | ||||
| @@ -39,7 +39,7 @@ class CustomFieldsGroupController extends AbstractController | ||||
|     public function __construct( | ||||
|         private readonly CustomFieldProvider $customFieldProvider, | ||||
|         private readonly TranslatorInterface $translator, | ||||
|         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry | ||||
|         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry, | ||||
|     ) {} | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -42,7 +42,7 @@ class CustomFieldChoice extends AbstractCustomField | ||||
|         /** | ||||
|          * @var TranslatableStringHelper Helper that find the string in current locale from an array of translation | ||||
|          */ | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function allowOtherChoice(CustomField $cf) | ||||
|   | ||||
| @@ -44,7 +44,7 @@ class CustomFieldDate extends AbstractCustomField | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly Environment $templating, | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder, CustomField $customField) | ||||
|   | ||||
| @@ -41,7 +41,7 @@ class CustomFieldNumber extends AbstractCustomField | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly Environment $templating, | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder, CustomField $customField) | ||||
|   | ||||
| @@ -28,7 +28,7 @@ class CustomFieldText extends AbstractCustomField | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly Environment $templating, | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -31,7 +31,7 @@ class CustomFieldTitle extends AbstractCustomField | ||||
|         /** | ||||
|          * @var TranslatableStringHelper Helper that find the string in current locale from an array of translation | ||||
|          */ | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper | ||||
|         private readonly TranslatableStringHelper $translatableStringHelper, | ||||
|     ) {} | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder, CustomField $customField) | ||||
|   | ||||
| @@ -23,9 +23,9 @@ class Option | ||||
|     private bool $active = true; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<Option> | ||||
|      * @var Collection<int, Option> | ||||
|      */ | ||||
|     #[ORM\OneToMany(targetEntity: Option::class, mappedBy: 'parent')] | ||||
|     #[ORM\OneToMany(mappedBy: 'parent', targetEntity: Option::class)] | ||||
|     private Collection $children; | ||||
|  | ||||
|     #[ORM\Id] | ||||
|   | ||||
| @@ -32,9 +32,9 @@ class CustomFieldsGroup | ||||
|      * The custom fields of the group. | ||||
|      * The custom fields are asc-ordered regarding to their property "ordering". | ||||
|      * | ||||
|      * @var Collection<CustomField> | ||||
|      * @var Collection<int, CustomField> | ||||
|      */ | ||||
|     #[ORM\OneToMany(targetEntity: CustomField::class, mappedBy: 'customFieldGroup')] | ||||
|     #[ORM\OneToMany(mappedBy: 'customFieldGroup', targetEntity: CustomField::class)] | ||||
|     #[ORM\OrderBy(['ordering' => \Doctrine\Common\Collections\Criteria::ASC])] | ||||
|     private Collection $customFields; | ||||
|  | ||||
|   | ||||
| @@ -26,7 +26,7 @@ class CustomFieldsGroupType extends AbstractType | ||||
|     public function __construct( | ||||
|         private readonly array $customizableEntities, | ||||
|         // TODO : add comment about this variable | ||||
|         private readonly TranslatorInterface $translator | ||||
|         private readonly TranslatorInterface $translator, | ||||
|     ) {} | ||||
|  | ||||
|     // TODO : details about the function | ||||
|   | ||||
| @@ -48,7 +48,7 @@ final class DocGeneratorTemplateController extends AbstractController | ||||
|         private readonly PaginatorFactory $paginatorFactory, | ||||
|         private readonly EntityManagerInterface $entityManager, | ||||
|         private readonly ClockInterface $clock, | ||||
|         private readonly ChillSecurity $security | ||||
|         private readonly ChillSecurity $security, | ||||
|     ) {} | ||||
|  | ||||
|     #[Route(path: '{_locale}/admin/doc/gen/generate/test/from/{template}/for/{entityClassName}/{entityId}', name: 'chill_docgenerator_test_generate_from_template')] | ||||
| @@ -56,7 +56,7 @@ final class DocGeneratorTemplateController extends AbstractController | ||||
|         DocGeneratorTemplate $template, | ||||
|         string $entityClassName, | ||||
|         int $entityId, | ||||
|         Request $request | ||||
|         Request $request, | ||||
|     ): Response { | ||||
|         return $this->generateDocFromTemplate( | ||||
|             $template, | ||||
| @@ -71,7 +71,7 @@ final class DocGeneratorTemplateController extends AbstractController | ||||
|         DocGeneratorTemplate $template, | ||||
|         string $entityClassName, | ||||
|         int $entityId, | ||||
|         Request $request | ||||
|         Request $request, | ||||
|     ): Response { | ||||
|         return $this->generateDocFromTemplate( | ||||
|             $template, | ||||
| @@ -137,7 +137,7 @@ final class DocGeneratorTemplateController extends AbstractController | ||||
|         DocGeneratorTemplate $template, | ||||
|         int $entityId, | ||||
|         Request $request, | ||||
|         bool $isTest | ||||
|         bool $isTest, | ||||
|     ): Response { | ||||
|         try { | ||||
|             $context = $this->contextManager->getContextByDocGeneratorTemplate($template); | ||||
|   | ||||
| @@ -54,12 +54,15 @@ class LoadDocGeneratorTemplate extends AbstractFixture | ||||
|         ]; | ||||
|  | ||||
|         foreach ($templates as $template) { | ||||
|             $newStoredObj = (new StoredObject()) | ||||
|                 ->setFilename($template['file']['filename']) | ||||
|                 ->setKeyInfos(json_decode($template['file']['key'], true)) | ||||
|                 ->setIv(json_decode($template['file']['iv'], true)) | ||||
|             $newStoredObj = (new StoredObject()); | ||||
|  | ||||
|             $newStoredObj | ||||
|                 ->setCreatedAt(new \DateTime('today')) | ||||
|                 ->setType($template['file']['type']); | ||||
|                 ->registerVersion( | ||||
|                     json_decode($template['file']['key'], true), | ||||
|                     json_decode($template['file']['iv'], true), | ||||
|                     $template['file']['type'], | ||||
|                 ); | ||||
|  | ||||
|             $manager->persist($newStoredObj); | ||||
|  | ||||
|   | ||||
| @@ -28,7 +28,7 @@ final readonly class RelatorioDriver implements DriverInterface | ||||
|     public function __construct( | ||||
|         private HttpClientInterface $client, | ||||
|         ParameterBagInterface $parameterBag, | ||||
|         private LoggerInterface $logger | ||||
|         private LoggerInterface $logger, | ||||
|     ) { | ||||
|         $this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url']; | ||||
|     } | ||||
|   | ||||
| @@ -35,7 +35,7 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly ClassMetadataFactoryInterface $classMetadataFactory, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|     ) { | ||||
|         $this->propertyAccess = PropertyAccess::createPropertyAccessor(); | ||||
|     } | ||||
|   | ||||
| @@ -33,7 +33,7 @@ class Generator implements GeneratorInterface | ||||
|         private readonly DriverInterface $driver, | ||||
|         private readonly ManagerRegistry $objectManagerRegistry, | ||||
|         private readonly LoggerInterface $logger, | ||||
|         private readonly StoredObjectManagerInterface $storedObjectManager | ||||
|         private readonly StoredObjectManagerInterface $storedObjectManager, | ||||
|     ) {} | ||||
|  | ||||
|     public function generateDataDump( | ||||
| @@ -134,13 +134,11 @@ class Generator implements GeneratorInterface | ||||
|             $content = Yaml::dump($data, 6); | ||||
|             /* @var StoredObject $destinationStoredObject */ | ||||
|             $destinationStoredObject | ||||
|                 ->setType('application/yaml') | ||||
|                 ->setFilename(sprintf('%s_yaml', uniqid('doc_', true))) | ||||
|                 ->setStatus(StoredObject::STATUS_READY) | ||||
|             ; | ||||
|  | ||||
|             try { | ||||
|                 $this->storedObjectManager->write($destinationStoredObject, $content); | ||||
|                 $this->storedObjectManager->write($destinationStoredObject, $content, 'application/yaml'); | ||||
|             } catch (StoredObjectManagerException $e) { | ||||
|                 $destinationStoredObject->addGenerationErrors($e->getMessage()); | ||||
|  | ||||
| @@ -174,13 +172,11 @@ class Generator implements GeneratorInterface | ||||
|  | ||||
|         /* @var StoredObject $destinationStoredObject */ | ||||
|         $destinationStoredObject | ||||
|             ->setType($template->getFile()->getType()) | ||||
|             ->setFilename(sprintf('%s_odt', uniqid('doc_', true))) | ||||
|             ->setStatus(StoredObject::STATUS_READY) | ||||
|         ; | ||||
|  | ||||
|         try { | ||||
|             $this->storedObjectManager->write($destinationStoredObject, $generatedResource); | ||||
|             $this->storedObjectManager->write($destinationStoredObject, $generatedResource, $template->getFile()->getType()); | ||||
|         } catch (StoredObjectManagerException $e) { | ||||
|             $destinationStoredObject->addGenerationErrors($e->getMessage()); | ||||
|  | ||||
|   | ||||
| @@ -39,7 +39,7 @@ final readonly class OnGenerationFails implements EventSubscriberInterface | ||||
|         private MailerInterface $mailer, | ||||
|         private StoredObjectRepositoryInterface $storedObjectRepository, | ||||
|         private TranslatorInterface $translator, | ||||
|         private UserRepositoryInterface $userRepository | ||||
|         private UserRepositoryInterface $userRepository, | ||||
|     ) {} | ||||
|  | ||||
|     public static function getSubscribedEvents() | ||||
|   | ||||
| @@ -56,7 +56,7 @@ final class BaseContextDataTest extends KernelTestCase | ||||
|     } | ||||
|  | ||||
|     private function buildBaseContext( | ||||
|         ?NormalizerInterface $normalizer = null | ||||
|         ?NormalizerInterface $normalizer = null, | ||||
|     ): BaseContextData { | ||||
|         return new BaseContextData( | ||||
|             $normalizer ?? self::getContainer()->get(NormalizerInterface::class) | ||||
|   | ||||
| @@ -19,6 +19,7 @@ use Chill\DocGeneratorBundle\Service\Generator\Generator; | ||||
| use Chill\DocGeneratorBundle\Service\Generator\ObjectReadyException; | ||||
| use Chill\DocGeneratorBundle\Service\Generator\RelatedEntityNotFoundException; | ||||
| use Chill\DocStoreBundle\Entity\StoredObject; | ||||
| use Chill\DocStoreBundle\Entity\StoredObjectVersion; | ||||
| use Chill\DocStoreBundle\Service\StoredObjectManagerInterface; | ||||
| use Chill\MainBundle\Entity\User; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| @@ -39,11 +40,11 @@ class GeneratorTest extends TestCase | ||||
|  | ||||
|     public function testSuccessfulGeneration(): void | ||||
|     { | ||||
|         $template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject()) | ||||
|             ->setType('application/test')); | ||||
|         $templateStoredObject = new StoredObject(); | ||||
|         $templateStoredObject->registerVersion(type: 'application/test'); | ||||
|         $template = (new DocGeneratorTemplate())->setFile($templateStoredObject); | ||||
|         $destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING); | ||||
|         $reflection = new \ReflectionClass($destinationStoredObject); | ||||
|         $reflection->getProperty('id')->setAccessible(true); | ||||
|         $reflection->getProperty('id')->setValue($destinationStoredObject, 1); | ||||
|         $entity = new class () {}; | ||||
|         $data = []; | ||||
| @@ -76,7 +77,14 @@ class GeneratorTest extends TestCase | ||||
|  | ||||
|         $storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class); | ||||
|         $storedObjectManager->read($templateStoredObject)->willReturn('template'); | ||||
|         $storedObjectManager->write($destinationStoredObject, 'generated')->shouldBeCalled(); | ||||
|         $storedObjectManager->write($destinationStoredObject, 'generated', 'application/test') | ||||
|             ->will(function ($args): StoredObjectVersion { | ||||
|                 /** @var StoredObject $storedObject */ | ||||
|                 $storedObject = $args[0]; | ||||
|  | ||||
|                 return $storedObject->registerVersion(type: $args[2]); | ||||
|             }) | ||||
|             ->shouldBeCalled(); | ||||
|  | ||||
|         $generator = new Generator( | ||||
|             $contextManagerInterface->reveal(), | ||||
| @@ -107,8 +115,9 @@ class GeneratorTest extends TestCase | ||||
|             $this->prophesize(StoredObjectManagerInterface::class)->reveal() | ||||
|         ); | ||||
|  | ||||
|         $template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject()) | ||||
|             ->setType('application/test')); | ||||
|         $templateStoredObject = new StoredObject(); | ||||
|         $templateStoredObject->registerVersion(type: 'application/test'); | ||||
|         $template = (new DocGeneratorTemplate())->setFile($templateStoredObject); | ||||
|         $destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_READY); | ||||
|  | ||||
|         $generator->generateDocFromTemplate( | ||||
| @@ -124,11 +133,11 @@ class GeneratorTest extends TestCase | ||||
|     { | ||||
|         $this->expectException(RelatedEntityNotFoundException::class); | ||||
|  | ||||
|         $template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject()) | ||||
|             ->setType('application/test')); | ||||
|         $templateStoredObject = new StoredObject(); | ||||
|         $templateStoredObject->registerVersion(type: 'application/test'); | ||||
|         $template = (new DocGeneratorTemplate())->setFile($templateStoredObject); | ||||
|         $destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING); | ||||
|         $reflection = new \ReflectionClass($destinationStoredObject); | ||||
|         $reflection->getProperty('id')->setAccessible(true); | ||||
|         $reflection->getProperty('id')->setValue($destinationStoredObject, 1); | ||||
|  | ||||
|         $context = $this->prophesize(DocGeneratorContextInterface::class); | ||||
|   | ||||
| @@ -58,6 +58,7 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf | ||||
|         ?int $expire_delay = null, | ||||
|         ?int $submit_delay = null, | ||||
|         int $max_file_count = 1, | ||||
|         ?string $object_name = null, | ||||
|     ): SignedUrlPost { | ||||
|         $delay = $expire_delay ?? $this->max_expire_delay; | ||||
|         $submit_delay ??= $this->max_submit_delay; | ||||
| @@ -84,11 +85,14 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf | ||||
|  | ||||
|         $expires = $this->clock->now()->add(new \DateInterval('PT'.(string) $delay.'S')); | ||||
|  | ||||
|         if (null === $object_name) { | ||||
|             $object_name = $this->generateObjectName(); | ||||
|         } | ||||
|  | ||||
|         $g = new SignedUrlPost( | ||||
|             $url = $this->generateUrl($object_name), | ||||
|             $expires, | ||||
|             $object_name, | ||||
|             $this->max_post_file_size, | ||||
|             $max_file_count, | ||||
|             $submit_delay, | ||||
| @@ -127,7 +131,7 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf | ||||
|         ]; | ||||
|         $url = $url.'?'.\http_build_query($args); | ||||
|  | ||||
|         $signature = new SignedUrl(strtoupper($method), $url, $expires); | ||||
|         $signature = new SignedUrl(strtoupper($method), $url, $expires, $object_name); | ||||
|  | ||||
|         $this->event_dispatcher->dispatch( | ||||
|             new TempUrlGenerateEvent($signature) | ||||
| @@ -140,7 +144,7 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf | ||||
|     { | ||||
|         return match (str_ends_with($this->base_url, '/')) { | ||||
|             true => $this->base_url.$relative_path, | ||||
|             false => $this->base_url.'/'.$relative_path | ||||
|             false => $this->base_url.'/'.$relative_path, | ||||
|         }; | ||||
|     } | ||||
|  | ||||
| @@ -178,21 +182,19 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf | ||||
|         return \hash_hmac('sha512', $body, $this->key, false); | ||||
|     } | ||||
|  | ||||
|     private function generateSignature($method, $url, \DateTimeImmutable $expires) | ||||
|     private function generateSignature(string $method, $url, \DateTimeImmutable $expires) | ||||
|     { | ||||
|         if ('POST' === $method) { | ||||
|             return $this->generateSignaturePost($url, $expires); | ||||
|         } | ||||
|  | ||||
|         $path = \parse_url((string) $url, PHP_URL_PATH); | ||||
|  | ||||
|         $body = sprintf( | ||||
|             "%s\n%s\n%s", | ||||
|             $method, | ||||
|             strtoupper($method), | ||||
|             $expires->format('U'), | ||||
|             $path | ||||
|         ) | ||||
|         ; | ||||
|         ); | ||||
|  | ||||
|         $this->logger->debug( | ||||
|             'generate signature GET', | ||||
|   | ||||
| @@ -21,6 +21,8 @@ readonly class SignedUrl | ||||
|         #[Serializer\Groups(['read'])] | ||||
|         public string $url, | ||||
|         public \DateTimeImmutable $expires, | ||||
|         #[Serializer\Groups(['read'])] | ||||
|         public string $object_name, | ||||
|     ) {} | ||||
|  | ||||
|     #[Serializer\Groups(['read'])] | ||||
|   | ||||
| @@ -18,6 +18,7 @@ readonly class SignedUrlPost extends SignedUrl | ||||
|     public function __construct( | ||||
|         string $url, | ||||
|         \DateTimeImmutable $expires, | ||||
|         string $object_name, | ||||
|         #[Serializer\Groups(['read'])] | ||||
|         public int $max_file_size, | ||||
|         #[Serializer\Groups(['read'])] | ||||
| @@ -31,6 +32,6 @@ readonly class SignedUrlPost extends SignedUrl | ||||
|         #[Serializer\Groups(['read'])] | ||||
|         public string $signature, | ||||
|     ) { | ||||
|         parent::__construct('POST', $url, $expires); | ||||
|         parent::__construct('POST', $url, $expires, $object_name); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -16,7 +16,8 @@ interface TempUrlGeneratorInterface | ||||
|     public function generatePost( | ||||
|         ?int $expire_delay = null, | ||||
|         ?int $submit_delay = null, | ||||
|         int $max_file_count = 1 | ||||
|         int $max_file_count = 1, | ||||
|         ?string $object_name = null, | ||||
|     ): SignedUrlPost; | ||||
|  | ||||
|     public function generate(string $method, string $object_name, ?int $expire_delay = null): SignedUrl; | ||||
|   | ||||
| @@ -25,7 +25,7 @@ class AsyncUploadExtension extends AbstractExtension | ||||
| { | ||||
|     public function __construct( | ||||
|         private readonly TempUrlGeneratorInterface $tempUrlGenerator, | ||||
|         private readonly UrlGeneratorInterface $routingUrlGenerator | ||||
|         private readonly UrlGeneratorInterface $routingUrlGenerator, | ||||
|     ) {} | ||||
|  | ||||
|     public function getFilters() | ||||
|   | ||||
| @@ -11,9 +11,11 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\DocStoreBundle\Controller; | ||||
|  | ||||
| use Chill\DocStoreBundle\AsyncUpload\Exception\TempUrlGeneratorException; | ||||
| use Chill\DocStoreBundle\AsyncUpload\TempUrlGeneratorInterface; | ||||
| use Chill\DocStoreBundle\Security\Authorization\AsyncUploadVoter; | ||||
| use Chill\DocStoreBundle\Entity\StoredObject; | ||||
| use Chill\DocStoreBundle\Entity\StoredObjectVersion; | ||||
| use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum; | ||||
| use Chill\MainBundle\Entity\User; | ||||
| use Psr\Log\LoggerInterface; | ||||
| use Symfony\Component\HttpFoundation\JsonResponse; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| @@ -30,63 +32,85 @@ final readonly class AsyncUploadController | ||||
|         private TempUrlGeneratorInterface $tempUrlGenerator, | ||||
|         private SerializerInterface $serializer, | ||||
|         private Security $security, | ||||
|         private LoggerInterface $logger, | ||||
|         private LoggerInterface $chillLogger, | ||||
|     ) {} | ||||
|  | ||||
|     #[Route(path: '/asyncupload/temp_url/generate/{method}', name: 'async_upload.generate_url')] | ||||
|     public function getSignedUrl(string $method, Request $request): JsonResponse | ||||
|     #[Route(path: '/api/1.0/doc-store/async-upload/temp_url/{uuid}/generate/post', name: 'chill_docstore_asyncupload_getsignedurlpost')] | ||||
|     public function getSignedUrlPost(Request $request, StoredObject $storedObject): JsonResponse | ||||
|     { | ||||
|         try { | ||||
|             switch (strtolower($method)) { | ||||
|                 case 'post': | ||||
|         if (!$this->security->isGranted(StoredObjectRoleEnum::EDIT->value, $storedObject)) { | ||||
|             throw new AccessDeniedHttpException('not able to edit the given stored object'); | ||||
|         } | ||||
|  | ||||
|         // we create a dummy version, to generate a filename | ||||
|         $version = $storedObject->registerVersion(); | ||||
|  | ||||
|         $p = $this->tempUrlGenerator | ||||
|             ->generatePost( | ||||
|                 $request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null, | ||||
|                             $request->query->has('submit_delay') ? $request->query->getInt('submit_delay') : null | ||||
|                         ) | ||||
|                     ; | ||||
|                     break; | ||||
|                 case 'get': | ||||
|                 case 'head': | ||||
|                     $object_name = $request->query->get('object_name', null); | ||||
|  | ||||
|                     if (null === $object_name) { | ||||
|                         return (new JsonResponse((object) [ | ||||
|                             'message' => 'the object_name is null', | ||||
|                         ])) | ||||
|                             ->setStatusCode(JsonResponse::HTTP_BAD_REQUEST); | ||||
|                     } | ||||
|                     $p = $this->tempUrlGenerator->generate( | ||||
|                         $method, | ||||
|                         $object_name, | ||||
|                         $request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null | ||||
|                 $request->query->has('submit_delay') ? $request->query->getInt('submit_delay') : null, | ||||
|                 object_name: $version->getFilename() | ||||
|             ); | ||||
|                     break; | ||||
|                 default: | ||||
|                     return (new JsonResponse((object) ['message' => 'the method ' | ||||
|                         ."{$method} is not valid"])) | ||||
|                         ->setStatusCode(JsonResponse::HTTP_BAD_REQUEST); | ||||
|             } | ||||
|         } catch (TempUrlGeneratorException $e) { | ||||
|             $this->logger->warning('The client requested a temp url' | ||||
|                 .' which sparkle an error.', [ | ||||
|                     'message' => $e->getMessage(), | ||||
|                     'expire_delay' => $request->query->getInt('expire_delay', 0), | ||||
|                     'file_count' => $request->query->getInt('file_count', 1), | ||||
|                     'method' => $method, | ||||
|  | ||||
|         $this->chillLogger->notice('[Privacy Event] a request to upload a document has been generated', [ | ||||
|             'doc_uuid' => $storedObject->getUuid(), | ||||
|         ]); | ||||
|  | ||||
|             $p = new \stdClass(); | ||||
|             $p->message = $e->getMessage(); | ||||
|             $p->status = JsonResponse::HTTP_BAD_REQUEST; | ||||
|  | ||||
|             return new JsonResponse($p, JsonResponse::HTTP_BAD_REQUEST); | ||||
|         return new JsonResponse( | ||||
|             $this->serializer->serialize($p, 'json', [AbstractNormalizer::GROUPS => ['read']]), | ||||
|             Response::HTTP_OK, | ||||
|             [], | ||||
|             true | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|         if (!$this->security->isGranted(AsyncUploadVoter::GENERATE_SIGNATURE, $p)) { | ||||
|             throw new AccessDeniedHttpException('not allowed to generate this signature'); | ||||
|     #[Route(path: '/api/1.0/doc-store/async-upload/temp_url/{uuid}/generate/{method}', name: 'chill_docstore_asyncupload_getsignedurlget', requirements: ['method' => 'get|head'])] | ||||
|     public function getSignedUrlGet(Request $request, StoredObject $storedObject, string $method): JsonResponse | ||||
|     { | ||||
|         if (!$this->security->isGranted(StoredObjectRoleEnum::SEE->value, $storedObject)) { | ||||
|             throw new AccessDeniedHttpException('not able to read the given stored object'); | ||||
|         } | ||||
|  | ||||
|         // we really want to be sure that there are no other method than get or head: | ||||
|         if (!in_array($method, ['get', 'head'], true)) { | ||||
|             throw new AccessDeniedHttpException('Only methods get and head are allowed'); | ||||
|         } | ||||
|  | ||||
|         if ($request->query->has('version')) { | ||||
|             $filename = $request->query->get('version'); | ||||
|  | ||||
|             $storedObjectVersion = $storedObject->getVersions()->findFirst(fn (int $index, StoredObjectVersion $version): bool => $version->getFilename() === $filename); | ||||
|  | ||||
|             if (null === $storedObjectVersion) { | ||||
|                 // we are here in the case where the version is not stored into the database | ||||
|                 // as the version is prefixed by the stored object prefix, we just have to check that this prefix | ||||
|                 // is the same. It means that the user had previously the permission to "SEE_AND_EDIT" this stored | ||||
|                 // object with same prefix that we checked before | ||||
|                 if (!str_starts_with($filename, $storedObject->getPrefix())) { | ||||
|                     throw new AccessDeniedHttpException('not able to match the version with the same filename'); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             $filename = $storedObject->getCurrentVersion()->getFilename(); | ||||
|         } | ||||
|  | ||||
|         $p = $this->tempUrlGenerator->generate( | ||||
|             $method, | ||||
|             $filename, | ||||
|             $request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null | ||||
|         ); | ||||
|  | ||||
|         $user = $this->security->getUser(); | ||||
|         $userId = match ($user instanceof User) { | ||||
|             true => $user->getId(), | ||||
|             false => $user->getUserIdentifier(), | ||||
|         }; | ||||
|  | ||||
|         $this->chillLogger->notice('[Privacy Event] a request to see a document has been granted', [ | ||||
|             'doc_uuid' => $storedObject->getUuid()->toString(), | ||||
|             'user_id' => $userId, | ||||
|         ]); | ||||
|  | ||||
|         return new JsonResponse( | ||||
|             $this->serializer->serialize($p, 'json', [AbstractNormalizer::GROUPS => ['read']]), | ||||
|             Response::HTTP_OK, | ||||
|   | ||||
| @@ -35,7 +35,7 @@ class DocumentAccompanyingCourseController extends AbstractController | ||||
|         protected TranslatorInterface $translator, | ||||
|         protected EventDispatcherInterface $eventDispatcher, | ||||
|         protected AuthorizationHelper $authorizationHelper, | ||||
|         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry | ||||
|         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry, | ||||
|     ) {} | ||||
|  | ||||
|     #[Route(path: '/{id}/delete', name: 'chill_docstore_accompanying_course_document_delete')] | ||||
|   | ||||
| @@ -26,6 +26,8 @@ use Symfony\Component\HttpFoundation\Request; | ||||
| use Symfony\Component\HttpFoundation\Response; | ||||
| use Symfony\Component\Routing\Annotation\Route; | ||||
| use Symfony\Contracts\Translation\TranslatorInterface; | ||||
| use Chill\DocStoreBundle\Service\Signature\PDFSignatureZoneParser; | ||||
| use Chill\DocStoreBundle\Service\StoredObjectManagerInterface; | ||||
|  | ||||
| /** | ||||
|  * Class DocumentPersonController. | ||||
| @@ -40,7 +42,9 @@ class DocumentPersonController extends AbstractController | ||||
|         protected TranslatorInterface $translator, | ||||
|         protected EventDispatcherInterface $eventDispatcher, | ||||
|         protected AuthorizationHelper $authorizationHelper, | ||||
|         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry | ||||
|         protected PDFSignatureZoneParser $PDFSignatureZoneParser, | ||||
|         protected StoredObjectManagerInterface $storedObjectManagerInterface, | ||||
|         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry, | ||||
|     ) {} | ||||
|  | ||||
|     #[Route(path: '/{id}/delete', name: 'chill_docstore_person_document_delete')] | ||||
| @@ -197,4 +201,36 @@ class DocumentPersonController extends AbstractController | ||||
|             ['document' => $document, 'person' => $person] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     #[Route(path: '/{id}/signature', name: 'person_document_signature', methods: 'GET')] | ||||
|     public function signature(Person $person, PersonDocument $document): Response | ||||
|     { | ||||
|         $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person); | ||||
|         $this->denyAccessUnlessGranted('CHILL_PERSON_DOCUMENT_SEE', $document); | ||||
|  | ||||
|         $event = new PrivacyEvent($person, [ | ||||
|             'element_class' => PersonDocument::class, | ||||
|             'element_id' => $document->getId(), | ||||
|             'action' => 'show', | ||||
|         ]); | ||||
|         $this->eventDispatcher->dispatch($event, PrivacyEvent::PERSON_PRIVACY_EVENT); | ||||
|  | ||||
|         $storedObject = $document->getObject(); | ||||
|         $content = $this->storedObjectManagerInterface->read($storedObject); | ||||
|         $zones = $this->PDFSignatureZoneParser->findSignatureZones($content); | ||||
|  | ||||
|         $signature = []; | ||||
|         $signature['id'] = 1; | ||||
|         $signature['storedObject'] = [ // TEMP | ||||
|             'filename' => $storedObject->getFilename(), | ||||
|             'iv' => $storedObject->getIv(), | ||||
|             'keyInfos' => $storedObject->getKeyInfos(), | ||||
|         ]; | ||||
|         $signature['zones'] = $zones; | ||||
|  | ||||
|         return $this->render( | ||||
|             '@ChillDocStore/PersonDocument/signature.html.twig', | ||||
|             ['document' => $document, 'person' => $person, 'signature' => $signature] | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,67 @@ | ||||
| <?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\Service\Signature\Driver\BaseSigner\RequestPdfSignMessage; | ||||
| use Chill\DocStoreBundle\Service\Signature\PDFPage; | ||||
| use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone; | ||||
| use Chill\DocStoreBundle\Service\StoredObjectManagerInterface; | ||||
| use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature; | ||||
| use Chill\MainBundle\Workflow\EntityWorkflowManager; | ||||
| use Symfony\Component\HttpFoundation\JsonResponse; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| use Symfony\Component\Messenger\MessageBusInterface; | ||||
| use Symfony\Component\Routing\Annotation\Route; | ||||
|  | ||||
| class SignatureRequestController | ||||
| { | ||||
|     public function __construct( | ||||
|         private readonly MessageBusInterface $messageBus, | ||||
|         private readonly StoredObjectManagerInterface $storedObjectManager, | ||||
|         private readonly EntityWorkflowManager $entityWorkflowManager, | ||||
|     ) {} | ||||
|  | ||||
|     #[Route('/api/1.0/document/workflow/{id}/signature-request', name: 'chill_docstore_signature_request')] | ||||
|     public function processSignature(EntityWorkflowStepSignature $signature, Request $request): JsonResponse | ||||
|     { | ||||
|         $entityWorkflow = $signature->getStep()->getEntityWorkflow(); | ||||
|         $storedObject = $this->entityWorkflowManager->getAssociatedStoredObject($entityWorkflow); | ||||
|         $content = $this->storedObjectManager->read($storedObject); | ||||
|  | ||||
|         $data = \json_decode((string) $request->getContent(), true, 512, JSON_THROW_ON_ERROR); // TODO parse payload: json_decode ou, mieux, dataTransfertObject | ||||
|         $zone = new PDFSignatureZone( | ||||
|             $data['zone']['index'], | ||||
|             $data['zone']['x'], | ||||
|             $data['zone']['y'], | ||||
|             $data['zone']['height'], | ||||
|             $data['zone']['width'], | ||||
|             new PDFPage($data['zone']['PDFPage']['index'], $data['zone']['PDFPage']['width'], $data['zone']['PDFPage']['height']) | ||||
|         ); | ||||
|  | ||||
|         $this->messageBus->dispatch(new RequestPdfSignMessage( | ||||
|             $signature->getId(), | ||||
|             $zone, | ||||
|             $data['zone']['index'], | ||||
|             'test signature', // reason (string) | ||||
|             'Mme Caroline Diallo', // signerText (string) | ||||
|             $content | ||||
|         )); | ||||
|  | ||||
|         return new JsonResponse(null, JsonResponse::HTTP_OK, []); | ||||
|     } | ||||
|  | ||||
|     #[Route('/api/1.0/document/workflow/{id}/check-signature', name: 'chill_docstore_check_signature')] | ||||
|     public function checkSignature(EntityWorkflowStepSignature $signature): JsonResponse | ||||
|     { | ||||
|         return new JsonResponse($signature->getState(), JsonResponse::HTTP_OK, []); | ||||
|     } | ||||
| } | ||||
| @@ -11,6 +11,46 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\DocStoreBundle\Controller; | ||||
|  | ||||
| use Chill\DocStoreBundle\Entity\StoredObject; | ||||
| use Chill\MainBundle\CRUD\Controller\ApiController; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Symfony\Component\HttpFoundation\JsonResponse; | ||||
| use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||||
| use Symfony\Component\Routing\Annotation\Route; | ||||
| use Symfony\Component\Security\Core\Security; | ||||
| use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; | ||||
| use Symfony\Component\Serializer\SerializerInterface; | ||||
|  | ||||
| class StoredObjectApiController extends ApiController {} | ||||
| class StoredObjectApiController extends ApiController | ||||
| { | ||||
|     public function __construct( | ||||
|         private readonly Security $security, | ||||
|         private readonly SerializerInterface $serializer, | ||||
|         private readonly EntityManagerInterface $entityManager, | ||||
|     ) {} | ||||
|  | ||||
|     /** | ||||
|      * Creates a new stored object. | ||||
|      * | ||||
|      * @return JsonResponse the response containing the serialized object in JSON format | ||||
|      * | ||||
|      * @throws AccessDeniedHttpException if the user does not have the necessary role to create a stored object | ||||
|      */ | ||||
|     #[Route('/api/1.0/doc-store/stored-object/create', methods: ['POST'])] | ||||
|     public function createStoredObject(): JsonResponse | ||||
|     { | ||||
|         if (!($this->security->isGranted('ROLE_ADMIN') || $this->security->isGranted('ROLE_USER'))) { | ||||
|             throw new AccessDeniedHttpException('Must be user or admin to create a stored object'); | ||||
|         } | ||||
|  | ||||
|         $object = new StoredObject(); | ||||
|  | ||||
|         $this->entityManager->persist($object); | ||||
|         $this->entityManager->flush(); | ||||
|  | ||||
|         return new JsonResponse( | ||||
|             $this->serializer->serialize($object, 'json', [AbstractNormalizer::GROUPS => ['read']]), | ||||
|             json: true | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -16,6 +16,7 @@ use Chill\DocStoreBundle\Dav\Response\DavResponse; | ||||
| use Chill\DocStoreBundle\Entity\StoredObject; | ||||
| use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum; | ||||
| use Chill\DocStoreBundle\Service\StoredObjectManagerInterface; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| use Symfony\Component\HttpFoundation\Response; | ||||
| use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||||
| @@ -42,6 +43,7 @@ final readonly class WebdavController | ||||
|         private \Twig\Environment $engine, | ||||
|         private StoredObjectManagerInterface $storedObjectManager, | ||||
|         private Security $security, | ||||
|         private EntityManagerInterface $entityManager, | ||||
|     ) { | ||||
|         $this->requestAnalyzer = new PropfindRequestAnalyzer(); | ||||
|     } | ||||
| @@ -201,6 +203,8 @@ final readonly class WebdavController | ||||
|  | ||||
|         $this->storedObjectManager->write($storedObject, $request->getContent()); | ||||
|  | ||||
|         $this->entityManager->flush(); | ||||
|  | ||||
|         return new DavResponse('', Response::HTTP_NO_CONTENT); | ||||
|     } | ||||
|  | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user