mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-31 09:18:24 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			452-workfl
			...
			fix_calend
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 90d3bf32e6 | |||
| ebc2921696 | 
| @@ -1,7 +0,0 @@ | ||||
| kind: DX | ||||
| body: | | ||||
|     Send notifications log to dedicated channel, if it exists | ||||
| time: 2025-10-27T15:00:53.309372316+01:00 | ||||
| custom: | ||||
|     Issue: "" | ||||
|     SchemaChange: No schema change | ||||
							
								
								
									
										6
									
								
								.changes/unreleased/Feature-20250722-155039.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Feature-20250722-155039.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| kind: Feature | ||||
| body: Show filters on list pages unfolded by default | ||||
| time: 2025-07-22T15:50:39.338057044+02:00 | ||||
| custom: | ||||
|     Issue: "399" | ||||
|     SchemaChange: No schema change | ||||
							
								
								
									
										6
									
								
								.changes/unreleased/Feature-20250811-152154.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Feature-20250811-152154.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| kind: Feature | ||||
| body: Add 45 and 60 min calendar ranges | ||||
| time: 2025-08-11T15:21:54.209009751+02:00 | ||||
| custom: | ||||
|     Issue: "409" | ||||
|     SchemaChange: No schema change | ||||
							
								
								
									
										6
									
								
								.changes/unreleased/Fixed-20250806-134609.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Fixed-20250806-134609.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| kind: Fixed | ||||
| body: adjust display logic for accompanying period dates, include closing date if period is closed. | ||||
| time: 2025-08-06T13:46:09.241584292+02:00 | ||||
| custom: | ||||
|     Issue: "382" | ||||
|     SchemaChange: No schema change | ||||
							
								
								
									
										6
									
								
								.changes/unreleased/Fixed-20250806-173527.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Fixed-20250806-173527.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| kind: Fixed | ||||
| body: add min and step attributes to integer field in DateIntervalType | ||||
| time: 2025-08-06T17:35:27.413787704+02:00 | ||||
| custom: | ||||
|     Issue: "384" | ||||
|     SchemaChange: No schema change | ||||
							
								
								
									
										6
									
								
								.changes/unreleased/Fixed-20250811-155212.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Fixed-20250811-155212.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| kind: Fixed | ||||
| body: fix date formatting in calendar range display | ||||
| time: 2025-08-11T15:52:12.949078671+02:00 | ||||
| custom: | ||||
|     Issue: "" | ||||
|     SchemaChange: No schema change | ||||
							
								
								
									
										6
									
								
								.changes/unreleased/UX-20250722-132637.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/UX-20250722-132637.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| kind: UX | ||||
| body: Limit display of participations in event list | ||||
| time: 2025-07-22T13:26:37.500656935+02:00 | ||||
| custom: | ||||
|     Issue: "" | ||||
|     SchemaChange: No schema change | ||||
| @@ -1,6 +0,0 @@ | ||||
| kind: UX | ||||
| body: Improve the ux for selecting whether user wants to be notified of the final step of a workflow or all steps | ||||
| time: 2025-10-29T11:08:04.077020411+01:00 | ||||
| custom: | ||||
|     Issue: "542" | ||||
|     SchemaChange: No schema change | ||||
| @@ -1,12 +0,0 @@ | ||||
| ## v4.1.0 - 2025-08-26 | ||||
| ### Feature | ||||
| * ([#400](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/400)) Add filter to social actions list to filter out actions where current user intervenes    | ||||
| * ([#399](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/399)) Show filters on list pages unfolded by default    | ||||
| * Expansion of event module with new fields in the creation form: thematic, internal/external animator, responsable, and budget elements. Filtering options in the event list + adapted exports    | ||||
|  | ||||
|   **Schema Change**: Add columns or tables | ||||
| ### Fixed | ||||
| * ([#382](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/382)) adjust display logic for accompanying period dates, include closing date if period is closed.    | ||||
| * ([#384](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/384)) add min and step attributes to integer field in DateIntervalType    | ||||
| ### UX | ||||
| * Limit display of participations in event list    | ||||
| @@ -1,10 +0,0 @@ | ||||
| ## v4.2.0 - 2025-09-02 | ||||
| ### Feature | ||||
| * ([#64](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/64)) Add external identifier for a Person | ||||
|  | ||||
|   **Schema Change**: Add columns or tables | ||||
| * ([#330](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/330) Allow users to choose for which notifications they want to receive an email | ||||
| ### Fixed | ||||
| * ([#422](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/422)) Fixed html layout of pages for recovering password | ||||
| * Fix typo in 'uncheckAll' script for centers selection | ||||
| * Fix incorrect parameter name in event details link | ||||
| @@ -1,6 +0,0 @@ | ||||
| ## v4.2.1 - 2025-09-03 | ||||
| ### Fixed | ||||
| * Fix exports to work with DirectExportInterface    | ||||
| ### DX | ||||
| * Improve error message when a stored object cannot be written on local disk | ||||
|     | ||||
| @@ -1,10 +0,0 @@ | ||||
| ## v4.3.0 - 2025-09-08 | ||||
| ### Feature | ||||
| * ([#409](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/409)) Add 45 and 60 min calendar ranges    | ||||
| * Add a command to generate a list of permissions    | ||||
| * ([#412](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/412)) Add an absence end date    | ||||
|  | ||||
|   **Schema Change**: Add columns or tables | ||||
| ### Fixed | ||||
| * fix date formatting in calendar range display    | ||||
| * Change route URL to avoid clash with person duplicate controller method    | ||||
| @@ -1,8 +0,0 @@ | ||||
| ## v4.4.0 - 2025-09-11 | ||||
| ### Feature | ||||
| * ([#359](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/359)) Allow the merge of two accompanying period works    | ||||
| * ([#369](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/369)) Duplication of a document to another accompanying period work evaluation    | ||||
| * ([#359](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/359)) Fusion of two accompanying period works    | ||||
| ### Fixed | ||||
| * Fix display of 'duplicate' and 'merge' buttons in CRUD templates    | ||||
| * Fix saving notification preferences in user's profile    | ||||
| @@ -1,3 +0,0 @@ | ||||
| ## v4.4.1 - 2025-09-11 | ||||
| ### Fixed | ||||
| * fix translations in duplicate evaluation document modal and realign close modal button    | ||||
| @@ -1,3 +0,0 @@ | ||||
| ## v4.4.2 - 2025-09-12 | ||||
| ### Fixed | ||||
| * Fix document generation and workflow generation do not work on accompanying period work documents    | ||||
| @@ -1,13 +0,0 @@ | ||||
| ## v4.5.0 - 2025-10-03 | ||||
| ### Feature | ||||
| * Only allow delete of attachment on workflows that are not final    | ||||
| * Move up signature buttons on index workflow page for easier access    | ||||
| * Filter out document from attachment list if it is the same as the workflow document    | ||||
| * Block edition on attached document on workflow, if the workflow is finalized or sent external    | ||||
| * Convert workflow's attached document to pdf while sending them external    | ||||
| * After a signature is canceled or rejected, going to a waiting page until the post-process routines apply a workflow transition    | ||||
| ### Fixed | ||||
| * ([#426](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/426)) Increased the number of required characters when setting a new password in Chill from 9 to 14 - GDPR compliance    | ||||
| * Fix permissions on storedObject which are subject by a workflow    | ||||
| ### DX | ||||
| * Introduce a WaitingScreen component to display a waiting screen    | ||||
| @@ -1,4 +0,0 @@ | ||||
| ## v4.5.1 - 2025-10-03 | ||||
| ### Fixed | ||||
| * Add missing javascript dependency    | ||||
| * Add exception handling for conversion of attachment on sending external, when documens are already in pdf    | ||||
| @@ -1,14 +0,0 @@ | ||||
| ## v4.6.0 - 2025-10-15 | ||||
| ### Feature | ||||
| * ([#423](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/423)) Create environment banner that can be activated and configured depending on the image deployed    | ||||
| * ([#394](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/394)) Only show active workflow on the page "my tracked workflow"    | ||||
| ### Fixed | ||||
| * Fix loading of classLists in SocialIssuesAcc.vue, ensure elements are present    | ||||
| * Fix the rendering of list of StoredObjectVersions, where there are kept version (before converting to pdf) and intermediate versions deleted    | ||||
| * ([#434](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/434)) Notification: fix editing of sent notification by removing form.addressesEmails, a field that no longer exists    | ||||
| * Fix loading of social issues and social actions within vue component    | ||||
| * ([#446](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/446)) Add unique condition on stored object filename, with cleaning step on existing duplicate filenames    | ||||
|  | ||||
|   **Schema Change**: Drop or rename table or columns, or enforce new constraint that must be manually fixed | ||||
| * [workflow] take permissions into account to delete the workflow attachment    | ||||
| * ([#448](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/448)) Fix the execution of daily cronjob notification, when the previous last execution storage was invalid    | ||||
| @@ -1,3 +0,0 @@ | ||||
| ## v4.6.1 - 2025-10-27 | ||||
| ### Fixed | ||||
| * Fix export case where no 'reason' is picked within the PersonHavingActivityBetweenDateFilter.php    | ||||
| @@ -7,6 +7,14 @@ | ||||
|         "message": "'app' is assigned a value but never used.", | ||||
|         "hash": "f8c2979921289906e3baabae31ba101ead91504f" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/index.js", | ||||
|         "line": 57, | ||||
|         "column": 23, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'event' is defined but never used.", | ||||
|         "hash": "cf0cf378f71403f62a6425f384ccbbdec433d1f2" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillCalendarBundle/Resources/public/module/Invite/answer.js", | ||||
|         "line": 7, | ||||
| @@ -119,6 +127,46 @@ | ||||
|         "message": "'payload' is defined but never used.", | ||||
|         "hash": "66c545917093ba30f1d6ca10ddaa676140e749bd" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue", | ||||
|         "line": 224, | ||||
|         "column": 10, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'reactive' is defined but never used.", | ||||
|         "hash": "96ed76a9828138fb125fc36c4b55e900bbfe87c2" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue", | ||||
|         "line": 230, | ||||
|         "column": 5, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'DropArg' is defined but never used.", | ||||
|         "hash": "bd405399a4091d65e8391404bfb0c4611816c8e0" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue", | ||||
|         "line": 251, | ||||
|         "column": 9, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'t' is assigned a value but never used.", | ||||
|         "hash": "bc09207a496405f7a71c178e522b89aeb1f7ebd3" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue", | ||||
|         "line": 356, | ||||
|         "column": 32, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'arg' is defined but never used.", | ||||
|         "hash": "aeae152f0669b946a1ad681dd52b0ef03393ae79" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue", | ||||
|         "line": 434, | ||||
|         "column": 11, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'changedEvent' is assigned a value but never used.", | ||||
|         "hash": "a7a81a6bf09d00c0364e3aa8207ffad853f0547b" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue", | ||||
|         "line": 77, | ||||
| @@ -351,6 +399,14 @@ | ||||
|         "message": "'error' is defined but never used.", | ||||
|         "hash": "e26e5e101e90d2b7ee84d6f5de8c819e52129c17" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/index.ts", | ||||
|         "line": 29, | ||||
|         "column": 14, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'vm' is defined but never used.", | ||||
|         "hash": "8e7f5e89dd72c54459cf82156389b88988f97d63" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", | ||||
|         "line": 39, | ||||
| @@ -559,6 +615,14 @@ | ||||
|         "message": "'ref' is defined but never used.", | ||||
|         "hash": "2a27cd6d06a26e1326654c929068e3704137e24b" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue", | ||||
|         "line": 57, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/valid-v-for", | ||||
|         "message": "Custom elements in iteration require 'v-bind:key' directives.", | ||||
|         "hash": "cce787939524e83dd135869e13738ef332d7156c" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/WopiEditButton.vue", | ||||
|         "line": 15, | ||||
| @@ -919,6 +983,22 @@ | ||||
|         "message": "'_e' is defined but never used.", | ||||
|         "hash": "1d6448401778e8c56554020fe5abd47851ed33f3" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/module/wopi-link/index.js", | ||||
|         "line": 21, | ||||
|         "column": 55, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'e' is defined but never used.", | ||||
|         "hash": "eae499e4f6e9f43a9d17f9cd917cb6d3d97be25c" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/page/export/download-export.js", | ||||
|         "line": 3, | ||||
|         "column": 55, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'e' is defined but never used.", | ||||
|         "hash": "088fd383e7807e484aefc9825209bc7c8942bd22" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.js", | ||||
|         "line": 9, | ||||
| @@ -1009,19 +1089,115 @@ | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", | ||||
|         "line": 516, | ||||
|         "column": 21, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"context\" prop.", | ||||
|         "hash": "984c4203f2ac1e1bb65f9ce76ecd03b763cfaa83" | ||||
|         "line": 247, | ||||
|         "column": 5, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'postAddressToPerson' is defined but never used.", | ||||
|         "hash": "8a41c437cf2b5554cbbe1704cd51f3102b3d5994" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", | ||||
|         "line": 517, | ||||
|         "line": 248, | ||||
|         "column": 5, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'postAddressToHousehold' is defined but never used.", | ||||
|         "hash": "66dec84b2ece299daf21308e5e60d497ba442b27" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", | ||||
|         "line": 490, | ||||
|         "column": 21, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"context\" prop.", | ||||
|         "hash": "c9fb019bc21bfa77d989ed596913b99dd653c594" | ||||
|         "hash": "0d3f40c47974a4371072b3b9ee04b197c830162d" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", | ||||
|         "line": 491, | ||||
|         "column": 21, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"context\" prop.", | ||||
|         "hash": "8e877b7e588c30e182f7b572bdb9685360f9cf99" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", | ||||
|         "line": 508, | ||||
|         "column": 47, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'reject' is defined but never used.", | ||||
|         "hash": "5a3e3401bc3c765d91faaf4cfde57697af1262b7" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", | ||||
|         "line": 525, | ||||
|         "column": 47, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'reject' is defined but never used.", | ||||
|         "hash": "35a741d90379574b9323279f5802193d0c98a9dc" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", | ||||
|         "line": 553, | ||||
|         "column": 47, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'reject' is defined but never used.", | ||||
|         "hash": "c23d1ddf6c0d10ae97948e74aee9c14b9320b86c" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", | ||||
|         "line": 572, | ||||
|         "column": 47, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'reject' is defined but never used.", | ||||
|         "hash": "4322e81c6ea9d9734c680633a724d5bd4fabacb2" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", | ||||
|         "line": 803, | ||||
|         "column": 47, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'reject' is defined but never used.", | ||||
|         "hash": "7928a6461b9d394c7d97f048933553936f7d8963" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", | ||||
|         "line": 852, | ||||
|         "column": 47, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'reject' is defined but never used.", | ||||
|         "hash": "e5afdb8efccb5470a08dde48f755b1268fa947b5" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", | ||||
|         "line": 93, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "68f5e1cf5c03f9ada59c9e0afca0b74c7f3fca4b" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", | ||||
|         "line": 101, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "50d730f6109092baff2db66adc44dc1315e2bda2" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", | ||||
|         "line": 109, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "573e4c041ce663f28b933d7a675c2a525aba644c" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", | ||||
|         "line": 117, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "293f845eeab515b1df4649d136c2d8219ed59c4d" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", | ||||
| @@ -1048,180 +1224,204 @@ | ||||
|         "hash": "2d5a5e680ff207ad97c7e7b7d999064b561dfd8a" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", | ||||
|         "line": 149, | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 106, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "e4c1ecd7ae77d46ac3625c5bbe92a24d6a964db9" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", | ||||
|         "line": 157, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "4dece2db87c6ce1c04ae06c088ddfe916c1c0c61" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", | ||||
|         "line": 165, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "facc7a0f17bdf19396fae3d0de3da82e60503c0d" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", | ||||
|         "line": 173, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "19de32c76518387218264d7c4dab914d143a9cca" | ||||
|         "hash": "d52356f2af31d0167c02330ec22d09fbfa6b2b9f" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 130, | ||||
|         "line": 114, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "239ac02a02694d5b20ab30d4c7ce5838c51d1515" | ||||
|         "hash": "c8e8e06f370f93bf05867e93b5f037dfa46937b1" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 138, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "a54f9bc6d1edfa4df93c7dd7d409cfef3fccf99e" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 152, | ||||
|         "line": 128, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "74a5f664d18f3916ea908897fcd0291cb0128f29" | ||||
|         "hash": "9abaf71ca4b4f292b3b01e724d0a7733365e71f1" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 129, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "0b0743959778a9e3d93089b132608816ee4e6646" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 132, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "9759da7b7859b8ee8efaf74876430658ac6b6fe2" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 133, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "dba8be9a27ab74ec743b7d9e07c05d857b407dd3" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 134, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "9b1f5bce779aafc46b19d7a5d266eaa29f8f9be9" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 139, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "fe6fc4aea0994ba9da15b7c09d308842b67958cb" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 153, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "740ea5d793c7a34c9f352d8b333f3aa04cc80ee8" | ||||
|         "column": 55, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'reject' is defined but never used.", | ||||
|         "hash": "bd0e024fcad2e3f4566f15293e3c25c840f6dd3e" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 156, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "af8aca18f0226a5988ed90d44d95e2d607bfb5e6" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 157, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "7bc2453017793ae20cd6c10005f941d384b59d84" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 158, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "571b4ee5f22358dd165ec59696bb3439b7c9ff6c" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 163, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "cfcb5946c86e289fc61623a794284a5a272d02e8" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 178, | ||||
|         "line": 154, | ||||
|         "column": 37, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "0ec402e43cb08bf129e0737c0d2c4f6d0c7af8bd" | ||||
|         "hash": "596c4b180b926b7829f987384328bf5636cd367a" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 196, | ||||
|         "line": 171, | ||||
|         "column": 59, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'reject' is defined but never used.", | ||||
|         "hash": "5b41d5f9b45da074fb7bbbbd45e0da501da72071" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 172, | ||||
|         "column": 41, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "ec178d33e067aac892e015002afb6f3a2ff98762" | ||||
|         "hash": "d92b92a25043244cca809bd129633b7e024e26b4" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 214, | ||||
|         "line": 190, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "c0f4e5454e672b6064eb9cf6c235c6810f7bfa80" | ||||
|         "hash": "dd9a85ea740742d620e864796f67c5bff834486d" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 215, | ||||
|         "line": 191, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "e3dd840d2474f9865a45822872bf9ecfb15961d7" | ||||
|         "hash": "e3e59960d0d50709a57b336f66b586710b774892" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 216, | ||||
|         "line": 192, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "a32a60382b145cc7a4a7ebe01ec435b8e3103320" | ||||
|         "hash": "fe11b0e54396511e7b3b08615a78d22fc27e2fad" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", | ||||
|         "line": 246, | ||||
|         "line": 222, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "082447e5c731012f3acc282943502775dfd24797" | ||||
|         "hash": "63c14c2150c33ec701bc4a0ff94efde69537d490" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 118, | ||||
|         "line": 96, | ||||
|         "column": 20, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "d4fba4fe09af3c0937c0dd164928c8930c1591b5" | ||||
|         "hash": "d2a9fdaeef0e2810f480022d4c6f99e4f76a818e" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 118, | ||||
|         "line": 96, | ||||
|         "column": 20, | ||||
|         "ruleId": "vue/no-side-effects-in-computed-properties", | ||||
|         "message": "Unexpected side effect in \"cities\" computed property.", | ||||
|         "hash": "1113a114d5aaf9f32f442916d25458541c5af35c" | ||||
|         "hash": "dd92a60a9b1ebefeb9a90941d45326fbfa483733" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 102, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "04be01ab638ce01f568fb0216929e65e1175ca23" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 110, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "8619c8e0b63e87d09268832f90e4fba06b87e41f" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 124, | ||||
|         "column": 17, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "fa56a7c93583f0a9d0c2ecac10228c4f4fc1bc3a" | ||||
|         "hash": "281f918da00635079501418b1e6b2c05b62eb4a7" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 132, | ||||
|         "column": 17, | ||||
|         "line": 125, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "9fe87937ea67d1dae95fb3d44d4be0da2eba0905" | ||||
|         "hash": "c131b09fa67ab1d069f1d04a54582d6b0f206153" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 126, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "3d3a2a4add64c291b8f5f1cddd90a173cd6a819d" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 131, | ||||
|         "column": 21, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "ed48f4988914d7897018a2e06830a97e6740b3e8" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 145, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "744f3a7610d4d6015e50e25149bceffd6c6e2763" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
| @@ -1241,139 +1441,115 @@ | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 148, | ||||
|         "column": 13, | ||||
|         "line": 149, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "ab4f478fbfbc954b8dff75176dcd432f9ff28cfc" | ||||
|         "hash": "1e7b1ad55866f708baaca72dfa4ff26d6f8e5d21" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 153, | ||||
|         "column": 21, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "1d907d149f9ddb62e32140a90efe9a74b3e71fef" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 167, | ||||
|         "line": 152, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "8aa37d2d4f011773e68838a2c88017875de563b5" | ||||
|         "hash": "84779331536ffceec8d4a8c5ca4307310b882549" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 168, | ||||
|         "line": 161, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "a4827a357e52a51fa9262319114d81a130296acf" | ||||
|         "hash": "0789999841be671a4d8ab080d6fdb679f843eb52" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 169, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "a4c9715664202949e3242b8d4aa4098288b46dc4" | ||||
|         "line": 170, | ||||
|         "column": 51, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'reject' is defined but never used.", | ||||
|         "hash": "bbb17afa114f016e2058d90aa32d2a625804f0d1" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 171, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "f3e9e21e433e90ec7b615b8940d43c4177372b66" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 174, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "770b7a24cc24b380e88db47d62422c8e1ece2571" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 183, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "2aef3c519a9ec6abcfe7573989d3de19d5c4c752" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 193, | ||||
|         "column": 33, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "5d1f97e4d7d9f47399d312e8b9f95ef9e3843b8c" | ||||
|         "hash": "5fbe407ceceb37bff2ac800ceddd7942540132f1" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 190, | ||||
|         "column": 55, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'reject' is defined but never used.", | ||||
|         "hash": "e2af91def877befbabef8e93deba4c58a3ee2ded" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 191, | ||||
|         "column": 37, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "ee8544ee45681a650ed7d4918ae979685cdd8f0f" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 210, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "5d9d2217c8c7e6571bc9f72a98ea5b370edb4968" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 211, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "6e04619b373c23c91f6c36c2aad314ac16cdb697" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 212, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "39df045639a62f64ccdb03a80e286bc3ad772587" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 213, | ||||
|         "column": 37, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "c1df874f790ef0c036bf58ae8a8db1ee173685d4" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 232, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "476e6588a28ac9382e8b9d2e63a8babecd23bad8" | ||||
|         "hash": "c399a43fa797a8ce61c9d96a644a39cc84a387b7" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 233, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "6a0c82ba72d6d87217bf33a6ad8e40a4b81bc802" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 234, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "741d5af6c7d90041c0dc1c1df2e8699b80fca69a" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 235, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "c3ffd141f58d532663875cc5c7d338ed00db2a6d" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", | ||||
|         "line": 267, | ||||
|         "line": 245, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "2700f258396516a2fe971618fafbcdf72cdda3ab" | ||||
|         "hash": "04337a07944caaa4819cfebcf29e1a7cbfdf248b" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue", | ||||
|         "line": 94, | ||||
|         "line": 76, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "4be1b0592efa775092a91a1d744e16ce98bd216e" | ||||
|         "hash": "373a2e31f110d138c66d77f1faf5dc61545c55af" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue", | ||||
|         "line": 99, | ||||
|         "line": 81, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "19b54b6d76c30249d520a296f826eda9d6eb0668" | ||||
|         "hash": "421eb6a63224b4b1d81b216677a710c5c99ddee3" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/DatePane.vue", | ||||
| @@ -1393,19 +1569,19 @@ | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue", | ||||
|         "line": 169, | ||||
|         "line": 155, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "dcb7b34098062760ddbb849655a5bb3ca65c36d3" | ||||
|         "hash": "b3a822914fcb5e2fcf28efc331a45b9205002eeb" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue", | ||||
|         "line": 178, | ||||
|         "line": 164, | ||||
|         "column": 17, | ||||
|         "ruleId": "vue/no-mutating-props", | ||||
|         "message": "Unexpected mutation of \"entity\" prop.", | ||||
|         "hash": "86b3ecf201025cac36878c5e4bf8850fb9d58cb5" | ||||
|         "hash": "72c7d850f6cdeaf65b373a33234222f9766ee30b" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/index.js", | ||||
| @@ -1455,6 +1631,14 @@ | ||||
|         "message": "'app' is assigned a value but never used.", | ||||
|         "hash": "9e6125f4fc387dc362c69cc6e3ce360eb2851f1b" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/PickEntity/PickEntity.vue", | ||||
|         "line": 60, | ||||
|         "column": 22, | ||||
|         "ruleId": "vue/require-valid-default-prop", | ||||
|         "message": "Type of the default value for 'suggested' prop must be a function.", | ||||
|         "hash": "d30212820bc2e97fa02d75dbc3a014558693f169" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsMap.vue", | ||||
|         "line": 24, | ||||
| @@ -1543,6 +1727,14 @@ | ||||
|         "message": "'tags' is assigned a value but never used.", | ||||
|         "hash": "ae9bb2e0651c118ed9efd227e88b86cc83f5d80d" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/StickyNav.vue", | ||||
|         "line": 116, | ||||
|         "column": 18, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'event' is defined but never used.", | ||||
|         "hash": "201f182769c6dfb87148b841e7d9b592be429669" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/index.js", | ||||
|         "line": 19, | ||||
| @@ -1575,6 +1767,14 @@ | ||||
|         "message": "'app' is assigned a value but never used.", | ||||
|         "hash": "aaaaa63e7a60443b8cbf8191feb9142852ebdf1c" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue", | ||||
|         "line": 79, | ||||
|         "column": 13, | ||||
|         "ruleId": "vue/require-v-for-key", | ||||
|         "message": "Elements in iteration expect to have 'v-bind:key' directives.", | ||||
|         "hash": "422f53925922e59655d0f71624c19af75d41628c" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/index.js", | ||||
|         "line": 12, | ||||
| @@ -1615,6 +1815,22 @@ | ||||
|         "message": "'evalFQDN' is assigned a value but never used.", | ||||
|         "hash": "7fc32caafa23addddf44f3acbc5045b4523a0271" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js", | ||||
|         "line": 611, | ||||
|         "column": 9, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'errors' is assigned a value but never used.", | ||||
|         "hash": "c41cf979fc1626c38328dbf1028800c3395496bd" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/App.vue", | ||||
|         "line": 282, | ||||
|         "column": 7, | ||||
|         "ruleId": "@typescript-eslint/no-unused-expressions", | ||||
|         "message": "Expected an assignment or function call and instead saw an expression.", | ||||
|         "hash": "de3a6e2bb10a80a2bacba665be74266c7efc7d64" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/index.js", | ||||
|         "line": 16, | ||||
| @@ -1631,6 +1847,38 @@ | ||||
|         "message": "'app' is assigned a value but never used.", | ||||
|         "hash": "2f161e663689e3e4dfe2c53b0d64c91a4d2b1a60" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue", | ||||
|         "line": 263, | ||||
|         "column": 19, | ||||
|         "ruleId": "vue/return-in-computed-property", | ||||
|         "message": "Expected to return a value in \"refreshNetwork\" computed property.", | ||||
|         "hash": "2c1b08a49098c83b09058cedc0a962126e91e544" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue", | ||||
|         "line": 270, | ||||
|         "column": 7, | ||||
|         "ruleId": "vue/no-side-effects-in-computed-properties", | ||||
|         "message": "Unexpected side effect in \"legendLayers\" computed property.", | ||||
|         "hash": "760948d2187c853f17ac9a1bd7107e883092d4f4" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue", | ||||
|         "line": 281, | ||||
|         "column": 5, | ||||
|         "ruleId": "vue/no-dupe-keys", | ||||
|         "message": "Duplicate key 'checkedLayers'. May cause name collision in script or template tag.", | ||||
|         "hash": "447edb461e15e3ff5c60c8ecba88131e442539aa" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue", | ||||
|         "line": 353, | ||||
|         "column": 7, | ||||
|         "ruleId": "@typescript-eslint/no-unused-expressions", | ||||
|         "message": "Expected an assignment or function call and instead saw an expression.", | ||||
|         "hash": "9cf656cbf1eb3d7cc0082e63adcd320b6093d14f" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/index.js", | ||||
|         "line": 20, | ||||
| @@ -1639,6 +1887,22 @@ | ||||
|         "message": "'app' is assigned a value but never used.", | ||||
|         "hash": "9e94e6412b8a44e47bfe8e66218cad09cff5bed4" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriod/SetReferrer.vue", | ||||
|         "line": 42, | ||||
|         "column": 16, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'response' is defined but never used.", | ||||
|         "hash": "62de07b13c662e32332bb062038acee23978ea70" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue", | ||||
|         "line": 356, | ||||
|         "column": 28, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'_response' is defined but never used.", | ||||
|         "hash": "097e7788a2b5dea500b80b8a3cf968e57063a66a" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeUserGroup.vue", | ||||
|         "line": 6, | ||||
| @@ -1654,5 +1918,45 @@ | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'UserRenderBoxBadge' is defined but never used.", | ||||
|         "hash": "99eba0d8633b2c9497417f4f61ec4194dbb2a96b" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillWopiBundle/src/Resources/public/module/pending/index.ts", | ||||
|         "line": 4, | ||||
|         "column": 3, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'StoredObjectStatus' is defined but never used.", | ||||
|         "hash": "63f8c4572293916850d6165647774b27d4b732c6" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillWopiBundle/src/Resources/public/module/pending/index.ts", | ||||
|         "line": 5, | ||||
|         "column": 3, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'StoredObjectStatusChange' is defined but never used.", | ||||
|         "hash": "a87c178e3eb5999bf0f46b3fa1c6da77e1be08b9" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillWopiBundle/src/Resources/public/module/pending/index.ts", | ||||
|         "line": 30, | ||||
|         "column": 61, | ||||
|         "ruleId": "@typescript-eslint/no-unused-vars", | ||||
|         "message": "'e' is defined but never used.", | ||||
|         "hash": "02953121583f4f73742a19adab099ab63df9076e" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillWopiBundle/src/Resources/public/module/pending/index.ts", | ||||
|         "line": 31, | ||||
|         "column": 32, | ||||
|         "ruleId": "@typescript-eslint/no-explicit-any", | ||||
|         "message": "Unexpected any. Specify a different type.", | ||||
|         "hash": "af48e21a1651b6017ede882dab249c00a818a44d" | ||||
|     }, | ||||
|     { | ||||
|         "path": "src/Bundle/ChillWopiBundle/src/Resources/public/module/pending/index.ts", | ||||
|         "line": 37, | ||||
|         "column": 16, | ||||
|         "ruleId": "@typescript-eslint/no-explicit-any", | ||||
|         "message": "Unexpected any. Specify a different type.", | ||||
|         "hash": "7513ea552a0a649ce4ab93b6cf9d40bfef4f68d9" | ||||
|     } | ||||
| ] | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -18,9 +18,6 @@ migrations/* | ||||
| templates/* | ||||
| translations/* | ||||
|  | ||||
| # we allow developers to add customization on their installation, without commiting it | ||||
| config/packages/dev/* | ||||
|  | ||||
| ###> symfony/framework-bundle ### | ||||
| /.env.local | ||||
| /.env.local.php | ||||
|   | ||||
| @@ -27,11 +27,11 @@ Chill is a comprehensive web application built as a set of Symfony bundles. It i | ||||
|  | ||||
| ## Project Structure | ||||
|  | ||||
| Note: This is a project that's existed for a long time, and throughout the years we've used multiple structures inside each bundle. When having the choice, the developers should choose the new structure. | ||||
| Note: This is a project which exists from a long time ago, and we found multiple structure inside each bundle. When having the choice, the developers should choose the new structure. | ||||
|  | ||||
| The project follows a standard Symfony bundle structure: | ||||
| - `/src/Bundle/`: Contains all the Chill bundles. The code is either at the root of the bundle directory, or within a `src/` directory (preferred). See psr4 mapping at the root's `composer.json`. | ||||
| - each bundle comes with its own tests, either in the `Tests` directory (when the code is directly within the bundle directory (for instance `src/Bundle/ChillMainBundle/Tests`, `src/Bundle/ChillPersonBundle/Tests`)), or inside the `tests` directory, alongside the `src/` sub-directory (example: `src/Bundle/ChillWopiBundle/tests`) (this is the preferred way). | ||||
| - each bundle come with his own tests, either in the `Tests` directory (when the code is directly within the bundle directory (for instance `src/Bundle/ChillMainBundle/Tests`, `src/Bundle/ChillPersonBundle/Tests`)), or inside the `tests` directory, alongside to the `src/` sub-directory (example: `src/Bundle/ChillWopiBundle/tests`) (this is the preferred way). | ||||
| - `/docs/`: Contains project documentation | ||||
|  | ||||
| Each bundle typically has the following structure: | ||||
| @@ -46,13 +46,13 @@ Each bundle typically has the following structure: | ||||
|  | ||||
| ### A special word about TicketBundle | ||||
|  | ||||
| The ticket bundle is developed using a kind of "Command" pattern. The controller fills a "Command," and a "CommandHandler" handles this command. They are saved in the `src/Bundle/ChillTicketBundle/src/Action` directory. | ||||
| The ticket bundle is developed using a kind of "Command" pattern. The controller fill a "Command", and a "CommandHandler" handle this command. They are savec in the `src/Bundle/ChillTicketBundle/src/Action` directory. | ||||
|  | ||||
| ## Development Guidelines | ||||
|  | ||||
| ### Building and Configuration Instructions | ||||
|  | ||||
| All the commands should be run through the `symfony` command, which will configure the required variables. | ||||
| All the command should be run through the `symfony` command, which will configure the required variables. | ||||
|  | ||||
| For assets, we must ensure that we use node at version `^20.0.0`. This is done using `nvm use 20`. | ||||
|  | ||||
| @@ -87,7 +87,7 @@ For assets, we must ensure that we use node at version `^20.0.0`. This is done u | ||||
|    docker compose up -d | ||||
|    ``` | ||||
|  | ||||
| 6. **Set Up the Database**: | ||||
| 5. **Set Up the Database**: | ||||
|    ```bash | ||||
|    # Create the database | ||||
|    symfony console doctrine:database:create | ||||
| @@ -99,20 +99,20 @@ For assets, we must ensure that we use node at version `^20.0.0`. This is done u | ||||
|    symfony console doctrine:fixtures:load | ||||
|    ``` | ||||
|  | ||||
| 7. **Build Assets**: | ||||
| 6. **Build Assets**: | ||||
|    ```bash | ||||
|    nvm use 20 | ||||
|    yarn run encore dev | ||||
|    ``` | ||||
|  | ||||
| 8. **Start the Development Server**: | ||||
| 7. **Start the Development Server**: | ||||
|    ```bash | ||||
|    symfony server:start -d | ||||
|    ``` | ||||
|  | ||||
| #### Docker Setup | ||||
|  | ||||
| The project includes a Docker configuration for easier development: | ||||
| The project includes Docker configuration for easier development: | ||||
|  | ||||
| 1. **Start Docker Services**: | ||||
|    ```bash | ||||
| @@ -153,9 +153,9 @@ Key configuration files: | ||||
|  | ||||
| Each time a doctrine entity is created, we generate migration to adapt the database. | ||||
|  | ||||
| The migration is created using the command `symfony console doctrine:migrations:diff --no-interaction --namespace <namespace>`, where the namespace is the relevant namespace for migration. As this is a bash script, remember to quote the `\` (`\` must become `\\` in your command). | ||||
| The migration are created using the command `symfony console doctrine:migrations:diff --no-interaction --namespace <namespace>`, where the namespace is the relevant namespace for migration. As this is a bash script, do not forget to quote the `\` (`\` must become `\\` in your command). | ||||
|  | ||||
| Each bundle has his own namespace for migration (always ask me to confirm that command with a list of updated / created entities so that I can confirm to you that it is ok): | ||||
| Each bundle has his own namespace for migration (always ask me to confirm that command, with a list of updated / created entities so that I can confirm you that it is ok): | ||||
|  | ||||
| - `Chill\Bundle\ActivityBundle` writes migrations to `Chill\Migrations\Activity`; | ||||
| - `Chill\Bundle\BudgetBundle` writes migrations to `Chill\Migrations\Budget`; | ||||
| @@ -183,7 +183,7 @@ Once created the, comment's classes should be removed and a description of the c | ||||
|  | ||||
| When we need to use a DateTime or DateTimeImmutable that need to express "now", we prefer the usage of | ||||
| `Symfony\Component\Clock\ClockInterface`, where possible. This is usually not possible in doctrine entities, | ||||
| where injection does not work when restoring an entity from a database, but usually possible in services. | ||||
| where injection does not work when restoring an entity from database, but usually possible in services. | ||||
|  | ||||
| In test, we use `\Symfony\Component\Clock\MockClock` which is an implementation of `Symfony\Component\Clock\ClockInterface` | ||||
| where we have full and easy control of the date. | ||||
| @@ -198,9 +198,9 @@ The project uses PHPUnit for testing. Each bundle has its own test suite, and th | ||||
|  | ||||
| For creating mock, we prefer using prophecy (library phpspec/prophecy). | ||||
|  | ||||
| ##### Useful helpers and tips that avoid creating a mock | ||||
| ##### Useful helpers and tips that avoid create a mock | ||||
|  | ||||
| Some notable implementations that are test helpers and avoid creating a mock: | ||||
| Some notable implementations that are tests helper, and avoid to create a mock: | ||||
|  | ||||
| - `\Psr\Log\NullLogger`, an implementation of `\Psr\Log\LoggerInterface`; | ||||
| - `\Symfony\Component\Clock\MockClock`, an implementation of `Symfony\Component\Clock\ClockInterface` (already mentioned above); | ||||
| @@ -240,6 +240,9 @@ The tests are run from the project's root (not from the bundle's root). | ||||
| # Run all tests | ||||
| vendor/bin/phpunit | ||||
|  | ||||
| # Run tests for a specific bundle | ||||
| vendor/bin/phpunit --testsuite NameBundle | ||||
|  | ||||
| # Run a specific test file | ||||
| vendor/bin/phpunit path/to/TestFile.php | ||||
|  | ||||
| @@ -247,9 +250,6 @@ vendor/bin/phpunit path/to/TestFile.php | ||||
| vendor/bin/phpunit --filter methodName path/to/TestFile.php | ||||
| ``` | ||||
|  | ||||
| When writing tests, only test specific files. Do not run all tests or the full | ||||
| test suite. | ||||
|  | ||||
| #### Test Structure | ||||
|  | ||||
| Tests are organized by bundle and follow the same structure as the bundle itself: | ||||
| @@ -297,7 +297,7 @@ class TicketTest extends TestCase | ||||
|  | ||||
| #### Test Database | ||||
|  | ||||
| For tests that require a database, the project uses a postgresql database filled with fixtures (usage of doctrine-fixtures). You can configure a different database for testing in the `.env.test` file. | ||||
| For tests that require a database, the project uses postgresql database filled by fixtures (usage of doctrine-fixtures). You can configure a different database for testing in the `.env.test` file. | ||||
|  | ||||
| ### Code Quality Tools | ||||
|  | ||||
|   | ||||
							
								
								
									
										97
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										97
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -6,103 +6,6 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), | ||||
| and is generated by [Changie](https://github.com/miniscruff/changie). | ||||
|  | ||||
|  | ||||
| ## v4.6.1 - 2025-10-27 | ||||
| ### Fixed | ||||
| * Fix export case where no 'reason' is picked within the PersonHavingActivityBetweenDateFilter.php    | ||||
|  | ||||
| ## v4.6.0 - 2025-10-15 | ||||
| ### Feature | ||||
| * ([#423](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/423)) Create environment banner that can be activated and configured depending on the image deployed    | ||||
| * ([#394](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/394)) Only show active workflow on the page "my tracked workflow"    | ||||
| ### Fixed | ||||
| * Fix loading of classLists in SocialIssuesAcc.vue, ensure elements are present    | ||||
| * Fix the rendering of list of StoredObjectVersions, where there are kept version (before converting to pdf) and intermediate versions deleted    | ||||
| * ([#434](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/434)) Notification: fix editing of sent notification by removing form.addressesEmails, a field that no longer exists    | ||||
| * Fix loading of social issues and social actions within vue component    | ||||
| * ([#446](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/446)) Add unique condition on stored object filename, with cleaning step on existing duplicate filenames    | ||||
|  | ||||
|   **Schema Change**: Drop or rename table or columns, or enforce new constraint that must be manually fixed | ||||
| * [workflow] take permissions into account to delete the workflow attachment    | ||||
| * ([#448](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/448)) Fix the execution of daily cronjob notification, when the previous last execution storage was invalid    | ||||
|  | ||||
| ## v4.5.1 - 2025-10-03 | ||||
| ### Fixed | ||||
| * Add missing javascript dependency    | ||||
| * Add exception handling for conversion of attachment on sending external, when documens are already in pdf    | ||||
|  | ||||
| ## v4.5.0 - 2025-10-03 | ||||
| ### Feature | ||||
| * Only allow delete of attachment on workflows that are not final    | ||||
| * Move up signature buttons on index workflow page for easier access    | ||||
| * Filter out document from attachment list if it is the same as the workflow document    | ||||
| * Block edition on attached document on workflow, if the workflow is finalized or sent external    | ||||
| * Convert workflow's attached document to pdf while sending them external    | ||||
| * After a signature is canceled or rejected, going to a waiting page until the post-process routines apply a workflow transition    | ||||
| ### Fixed | ||||
| * ([#426](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/426)) Increased the number of required characters when setting a new password in Chill from 9 to 14 - GDPR compliance    | ||||
| * Fix permissions on storedObject which are subject by a workflow    | ||||
| ### DX | ||||
| * Introduce a WaitingScreen component to display a waiting screen    | ||||
|  | ||||
| ## v4.4.2 - 2025-09-12 | ||||
| ### Fixed | ||||
| * Fix document generation and workflow generation do not work on accompanying period work documents    | ||||
|  | ||||
| ## v4.4.1 - 2025-09-11 | ||||
| ### Fixed | ||||
| * fix translations in duplicate evaluation document modal and realign close modal button    | ||||
|  | ||||
| ## v4.4.0 - 2025-09-11 | ||||
| ### Feature | ||||
| * ([#359](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/359)) Allow the merge of two accompanying period works    | ||||
| * ([#369](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/369)) Duplication of a document to another accompanying period work evaluation    | ||||
| * ([#359](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/359)) Fusion of two accompanying period works    | ||||
| ### Fixed | ||||
| * Fix display of 'duplicate' and 'merge' buttons in CRUD templates    | ||||
| * Fix saving notification preferences in user's profile    | ||||
|  | ||||
| ## v4.3.0 - 2025-09-08 | ||||
| ### Feature | ||||
| * ([#409](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/409)) Add 45 and 60 min calendar ranges    | ||||
| * Add a command to generate a list of permissions    | ||||
| * ([#412](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/412)) Add an absence end date    | ||||
|  | ||||
|   **Schema Change**: Add columns or tables | ||||
| ### Fixed | ||||
| * fix date formatting in calendar range display    | ||||
| * Change route URL to avoid clash with person duplicate controller method    | ||||
|  | ||||
| ## v4.2.1 - 2025-09-03 | ||||
| ### Fixed | ||||
| * Fix exports to work with DirectExportInterface    | ||||
| ### DX | ||||
| * Improve error message when a stored object cannot be written on local disk | ||||
|     | ||||
|  | ||||
| ## v4.2.0 - 2025-09-02 | ||||
| ### Feature | ||||
| * ([#64](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/64)) Add external identifier for a Person | ||||
|  | ||||
|   **Schema Change**: Add columns or tables | ||||
| * ([#330](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/330) Allow users to choose for which notifications they want to receive an email | ||||
| ### Fixed | ||||
| * ([#422](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/422)) Fixed html layout of pages for recovering password | ||||
| * Fix typo in 'uncheckAll' script for centers selection | ||||
| * Fix incorrect parameter name in event details link | ||||
|  | ||||
| ## v4.1.0 - 2025-08-26 | ||||
| ### Feature | ||||
| * ([#400](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/400)) Add filter to social actions list to filter out actions where current user intervenes    | ||||
| * ([#399](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/399)) Show filters on list pages unfolded by default    | ||||
| * Expansion of event module with new fields in the creation form: thematic, internal/external animator, responsable, and budget elements. Filtering options in the event list + adapted exports    | ||||
|  | ||||
|   **Schema Change**: Add columns or tables | ||||
| ### Fixed | ||||
| * ([#382](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/382)) adjust display logic for accompanying period dates, include closing date if period is closed.    | ||||
| * ([#384](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/384)) add min and step attributes to integer field in DateIntervalType    | ||||
| ### UX | ||||
| * Limit display of participations in event list    | ||||
|  | ||||
| ## v4.0.2 - 2025-07-09 | ||||
| ### Fixed | ||||
| * Fix add missing translation    | ||||
|   | ||||
| @@ -1,13 +1,6 @@ | ||||
| chill_main: | ||||
|     available_languages: [ '%env(resolve:LOCALE)%', 'en' ] | ||||
|     available_countries: ['BE', 'FR'] | ||||
|     top_banner: | ||||
|         visible: false | ||||
|         text: | ||||
|             fr: 'Vous travaillez actuellement avec la version de PRÉ-PRODUCTION.' | ||||
|             nl: 'Je werkt momenteel in de PRE-PRODUCTIE versie' | ||||
|         color: '#353535' | ||||
|         background_color: '#d8bb48' | ||||
|     notifications: | ||||
|         from_email: '%env(resolve:NOTIFICATION_FROM_EMAIL)%' | ||||
|         from_name: '%env(resolve:NOTIFICATION_FROM_NAME)%' | ||||
|   | ||||
| @@ -55,7 +55,6 @@ | ||||
|     "@tsconfig/node20": "^20.1.4", | ||||
|     "@types/dompurify": "^3.0.5", | ||||
|     "@types/leaflet": "^1.9.3", | ||||
|     "@vueuse/core": "^13.9.0", | ||||
|     "bootstrap-icons": "^1.11.3", | ||||
|     "dropzone": "^5.7.6", | ||||
|     "es6-promise": "^4.2.8", | ||||
|   | ||||
| @@ -90,9 +90,7 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt | ||||
|  | ||||
|     public function getFormDefaultData(): array | ||||
|     { | ||||
|         return [ | ||||
|             'reasons' => [], | ||||
|         ]; | ||||
|         return []; | ||||
|     } | ||||
|  | ||||
|     public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array | ||||
|   | ||||
| @@ -42,8 +42,6 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem | ||||
|  | ||||
|     public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void | ||||
|     { | ||||
|         error_log('alterQuery called with data: '.json_encode(array_keys($data))); | ||||
|  | ||||
|         // create a subquery for activity | ||||
|         $sqb = $qb->getEntityManager()->createQueryBuilder(); | ||||
|         $sqb->select('1') | ||||
| @@ -61,6 +59,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem | ||||
|         if (\in_array('activity', $qb->getAllAliases(), true)) { | ||||
|             $sqb->andWhere('activity_person_having_activity.id = activity.id'); | ||||
|         } | ||||
|  | ||||
|         if (isset($data['reasons']) && [] !== $data['reasons']) { | ||||
|             // add clause activity reason | ||||
|             $sqb->join('activity_person_having_activity.reasons', 'reasons_person_having_activity'); | ||||
| @@ -125,38 +124,12 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem | ||||
|  | ||||
|     public function normalizeFormData(array $formData): array | ||||
|     { | ||||
|         $normalized = [ | ||||
|             'date_from_rolling' => $formData['date_from_rolling']->normalize(), | ||||
|             'date_to_rolling' => $formData['date_to_rolling']->normalize(), | ||||
|             'reasons' => [], | ||||
|         ]; | ||||
|  | ||||
|         if (isset($formData['reasons']) && [] !== $formData['reasons']) { | ||||
|             $normalized['reasons'] = array_map( | ||||
|                 fn (ActivityReason $reason) => $reason->getId(), | ||||
|                 $formData['reasons'] | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return $normalized; | ||||
|         return ['date_from_rolling' => $formData['date_from_rolling']->normalize(), 'date_to_rolling' => $formData['date_to_rolling']->normalize()]; | ||||
|     } | ||||
|  | ||||
|     public function denormalizeFormData(array $formData, int $fromVersion): array | ||||
|     { | ||||
|         $denormalized = [ | ||||
|             'date_from_rolling' => RollingDate::fromNormalized($formData['date_from_rolling']), | ||||
|             'date_to_rolling' => RollingDate::fromNormalized($formData['date_to_rolling']), | ||||
|             'reasons' => [], | ||||
|         ]; | ||||
|  | ||||
|         if (isset($formData['reasons']) && [] !== $formData['reasons']) { | ||||
|             $denormalized['reasons'] = array_map( | ||||
|                 fn ($id) => $this->activityReasonRepository->find($id), | ||||
|                 $formData['reasons'] | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return $denormalized; | ||||
|         return ['date_from_rolling' => RollingDate::fromNormalized($formData['date_from_rolling']), 'date_to_rolling' => RollingDate::fromNormalized($formData['date_to_rolling'])]; | ||||
|     } | ||||
|  | ||||
|     public function getFormDefaultData(): array | ||||
| @@ -170,12 +143,10 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem | ||||
|  | ||||
|     public function describeAction($data, ExportGenerationContext $context): array | ||||
|     { | ||||
|         $reasons = $data['reasons'] ?? []; | ||||
|  | ||||
|         return [ | ||||
|             [] === $reasons ? | ||||
|                 'export.filter.activity.describe_action_with_no_subject' | ||||
|                 : 'export.filter.activity.describe_action_with_subject', | ||||
|             [] === $data['reasons'] ? | ||||
|                 'export.filter.person_between_dates.describe_action_with_no_subject' | ||||
|                 : 'export.filter.person_between_dates.describe_action_with_subject', | ||||
|             [ | ||||
|                 'date_from' => $this->rollingDateConverter->convert($data['date_from_rolling']), | ||||
|                 'date_to' => $this->rollingDateConverter->convert($data['date_to_rolling']), | ||||
| @@ -183,7 +154,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem | ||||
|                     ', ', | ||||
|                     array_map( | ||||
|                         fn (ActivityReason $r): string => '"'.$this->translatableStringHelper->localize($r->getName()).'"', | ||||
|                         $reasons | ||||
|                         $data['reasons'] | ||||
|                     ) | ||||
|                 ), | ||||
|             ], | ||||
| @@ -197,7 +168,6 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem | ||||
|  | ||||
|     public function validateForm($data, ExecutionContextInterface $context): void | ||||
|     { | ||||
|         error_log('validateForm called with data: '.json_encode(array_keys($data))); | ||||
|         if ($this->rollingDateConverter->convert($data['date_from_rolling']) | ||||
|             >= $this->rollingDateConverter->convert($data['date_to_rolling'])) { | ||||
|             $context->buildViolation('export.filter.activity.person_between_dates.date mismatch') | ||||
|   | ||||
| @@ -136,14 +136,8 @@ export default { | ||||
|             issueIsLoading: false, | ||||
|             actionIsLoading: false, | ||||
|             actionAreLoaded: false, | ||||
|             socialIssuesClassList: { | ||||
|                 "col-form-label": true, | ||||
|                 required: false, | ||||
|             }, | ||||
|             socialActionsClassList: { | ||||
|                 "col-form-label": true, | ||||
|                 required: false, | ||||
|             }, | ||||
|             socialIssuesClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialIssues").getAttribute("required") ? "required" : ""}`, | ||||
|             socialActionsClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialActions").getAttribute("required") ? "required" : ""}`, | ||||
|         }; | ||||
|     }, | ||||
|     computed: { | ||||
| @@ -164,21 +158,6 @@ export default { | ||||
|         }, | ||||
|     }, | ||||
|     mounted() { | ||||
|         /* Load classNames after element is present */ | ||||
|         const socialActionsEl = document.querySelector( | ||||
|             "input#chill_activitybundle_activity_socialActions", | ||||
|         ); | ||||
|         if (socialActionsEl && socialActionsEl.hasAttribute("required")) { | ||||
|             this.socialActionsClassList.required = true; | ||||
|         } | ||||
|  | ||||
|         const socialIssuesEl = document.querySelector( | ||||
|             "input#chill_activitybundle_activity_socialIssues", | ||||
|         ); | ||||
|         if (socialIssuesEl && socialIssuesEl.hasAttribute("required")) { | ||||
|             this.socialIssuesClassList.required = true; | ||||
|         } | ||||
|  | ||||
|         /* Load other issues in multiselect */ | ||||
|         this.issueIsLoading = true; | ||||
|         this.actionAreLoaded = false; | ||||
|   | ||||
| @@ -55,6 +55,5 @@ | ||||
| 			</dl> | ||||
|  | ||||
| 		{% endblock %} | ||||
|         {% block content_view_actions_duplicate_link %}{% endblock %} | ||||
| 	{% endembed %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -104,8 +104,7 @@ | ||||
|                     event.title | ||||
|                 }}</b> | ||||
|                 <b v-else-if="event.extendedProps.is === 'range'" | ||||
|                     >{{ formatDate(event.startStr, "time") }} - | ||||
|                     {{ formatDate(event.endStr, "time") }}: | ||||
|                     >{{ formatDate(event.startStr) }} - {{ formatDate(event.endStr, 'time') }}: | ||||
|                     {{ event.extendedProps.locationName }}</b | ||||
|                 > | ||||
|                 <b v-else-if="event.extendedProps.is === 'local'">{{ | ||||
| @@ -297,25 +296,25 @@ const nextWeeks = computed((): Weeks[] => | ||||
|     }), | ||||
| ); | ||||
|  | ||||
| const formatDate = (datetime: string, format: null | "time" = null) => { | ||||
| const formatDate = (datetime: string, format: null | 'time' = null) => { | ||||
|     const date = ISOToDate(datetime); | ||||
|     if (!date) return ""; | ||||
|     if (!date) return ''; | ||||
|  | ||||
|     if (format === "time") { | ||||
|         return date.toLocaleTimeString("fr-FR", { | ||||
|             hour: "2-digit", | ||||
|             minute: "2-digit", | ||||
|     if (format === 'time') { | ||||
|         return date.toLocaleTimeString('fr-FR', { | ||||
|             hour: '2-digit', | ||||
|             minute: '2-digit' | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     // French date formatting | ||||
|     return date.toLocaleDateString("fr-FR", { | ||||
|         weekday: "short", | ||||
|         year: "numeric", | ||||
|         month: "short", | ||||
|         day: "numeric", | ||||
|         hour: "2-digit", | ||||
|         minute: "2-digit", | ||||
|     return date.toLocaleDateString('fr-FR', { | ||||
|         weekday: 'short', | ||||
|         year: 'numeric', | ||||
|         month: 'short', | ||||
|         day: 'numeric', | ||||
|         hour: '2-digit', | ||||
|         minute: '2-digit' | ||||
|     }); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,29 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\CustomFieldsBundle\EntityRepository; | ||||
|  | ||||
| use Chill\CustomFieldsBundle\Entity\CustomFieldsDefaultGroup; | ||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | ||||
| use Doctrine\Persistence\ManagerRegistry; | ||||
|  | ||||
| class CustomFieldsDefaultGroupRepository extends ServiceEntityRepository | ||||
| { | ||||
|     public function __construct(ManagerRegistry $registry) | ||||
|     { | ||||
|         parent::__construct($registry, CustomFieldsDefaultGroup::class); | ||||
|     } | ||||
|  | ||||
|     public function findOneByEntity(string $className): ?CustomFieldsDefaultGroup | ||||
|     { | ||||
|         return $this->findOneBy(['entity' => $className]); | ||||
|     } | ||||
| } | ||||
| @@ -127,7 +127,3 @@ services: | ||||
|         factory: ["@doctrine", getRepository] | ||||
|         arguments: | ||||
|             - "Chill\\CustomFieldsBundle\\Entity\\CustomFieldLongChoice\\Option" | ||||
|  | ||||
|     Chill\CustomFieldsBundle\EntityRepository\CustomFieldsDefaultGroupRepository: | ||||
|         autowire: true | ||||
|         autoconfigure: true | ||||
|   | ||||
| @@ -18,7 +18,6 @@ use Chill\DocStoreBundle\Exception\StoredObjectManagerException; | ||||
| use Chill\DocStoreBundle\Service\Cryptography\KeyGenerator; | ||||
| use Chill\DocStoreBundle\Service\StoredObjectManagerInterface; | ||||
| use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; | ||||
| use Symfony\Component\Filesystem\Exception\IOExceptionInterface; | ||||
| use Symfony\Component\Filesystem\Filesystem; | ||||
| use Symfony\Component\Filesystem\Path; | ||||
|  | ||||
| @@ -148,11 +147,16 @@ class StoredObjectManager implements StoredObjectManagerInterface | ||||
|     public function writeContent(string $filename, string $encryptedContent): void | ||||
|     { | ||||
|         $fullPath = $this->buildPath($filename); | ||||
|         $dir = Path::getDirectory($fullPath); | ||||
|  | ||||
|         try { | ||||
|             $this->filesystem->dumpFile($fullPath, $encryptedContent); | ||||
|         } catch (IOExceptionInterface $exception) { | ||||
|             throw StoredObjectManagerException::unableToStoreDocumentOnDisk($exception); | ||||
|         if (!$this->filesystem->exists($dir)) { | ||||
|             $this->filesystem->mkdir($dir); | ||||
|         } | ||||
|  | ||||
|         $result = file_put_contents($fullPath, $encryptedContent); | ||||
|  | ||||
|         if (false === $result) { | ||||
|             throw StoredObjectManagerException::unableToStoreDocumentOnDisk(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -59,7 +59,7 @@ final readonly class StoredObjectVersionApiController | ||||
|  | ||||
|         return new JsonResponse( | ||||
|             $this->serializer->serialize( | ||||
|                 new Collection(array_values($items->toArray()), $paginator), | ||||
|                 new Collection($items, $paginator), | ||||
|                 'json', | ||||
|                 [AbstractNormalizer::GROUPS => ['read', StoredObjectVersionNormalizer::WITH_POINT_IN_TIMES_CONTEXT, StoredObjectVersionNormalizer::WITH_RESTORED_CONTEXT]] | ||||
|             ), | ||||
|   | ||||
| @@ -23,14 +23,10 @@ use Random\RandomException; | ||||
|  * Store each version of StoredObject's. | ||||
|  * | ||||
|  * A version should not be created manually: use the method @see{StoredObject::registerVersion} instead. | ||||
|  * | ||||
|  * Each filename must be unique within the same StoredObject. We add a condition on id to apply this condition only for | ||||
|  * newly created versions when this new index is applied. | ||||
|  */ | ||||
| #[ORM\Entity] | ||||
| #[ORM\Table('chill_doc.stored_object_version')] | ||||
| #[ORM\UniqueConstraint(name: 'chill_doc_stored_object_version_unique_by_object', columns: ['stored_object_id', 'version'])] | ||||
| #[ORM\UniqueConstraint(name: 'chill_doc_stored_object_version_unique_by_filename', columns: ['filename'], options: ['where' => '(id > 0)'])] | ||||
| class StoredObjectVersion implements TrackCreationInterface | ||||
| { | ||||
|     use TrackCreationTrait; | ||||
|   | ||||
| @@ -1,20 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\DocStoreBundle\Exception; | ||||
|  | ||||
| class ConversionWithSameMimeTypeException extends \RuntimeException | ||||
| { | ||||
|     public function __construct(string $mimeType, ?\Throwable $previous = null) | ||||
|     { | ||||
|         parent::__construct("Conversion to same MIME type '{$mimeType}' is not allowed: already at the same MIME type", 0, $previous); | ||||
|     } | ||||
| } | ||||
| @@ -25,7 +25,7 @@ export interface GenericDoc { | ||||
|     type: "doc_store_generic_doc"; | ||||
|     uniqueKey: string; | ||||
|     key: string; | ||||
|     identifiers: { id: number }; | ||||
|     identifiers: object; | ||||
|     context: "person" | "accompanying-period"; | ||||
|     doc_date: DateTime; | ||||
|     metadata: GenericDocMetadata; | ||||
| @@ -36,18 +36,6 @@ export interface GenericDocForAccompanyingPeriod extends GenericDoc { | ||||
|     context: "accompanying-period"; | ||||
| } | ||||
|  | ||||
| export function isGenericDocForAccompanyingPeriod( | ||||
|     doc: GenericDoc, | ||||
| ): doc is GenericDocForAccompanyingPeriod { | ||||
|     return doc.context === "accompanying-period"; | ||||
| } | ||||
|  | ||||
| export function isGenericDocWithStoredObject( | ||||
|     doc: GenericDoc, | ||||
| ): doc is GenericDoc & { storedObject: StoredObject } { | ||||
|     return doc.storedObject !== null; | ||||
| } | ||||
|  | ||||
| interface BaseMetadataWithHtml extends BaseMetadata { | ||||
|     html: string; | ||||
| } | ||||
| @@ -56,33 +44,28 @@ export interface GenericDocForAccompanyingCourseDocument | ||||
|     extends GenericDocForAccompanyingPeriod { | ||||
|     key: "accompanying_course_document"; | ||||
|     metadata: BaseMetadataWithHtml; | ||||
|     storedObject: StoredObject; | ||||
| } | ||||
|  | ||||
| export interface GenericDocForAccompanyingCourseActivityDocument | ||||
|     extends GenericDocForAccompanyingPeriod { | ||||
|     key: "accompanying_course_activity_document"; | ||||
|     metadata: BaseMetadataWithHtml; | ||||
|     storedObject: StoredObject; | ||||
| } | ||||
|  | ||||
| export interface GenericDocForAccompanyingCourseCalendarDocument | ||||
|     extends GenericDocForAccompanyingPeriod { | ||||
|     key: "accompanying_course_calendar_document"; | ||||
|     metadata: BaseMetadataWithHtml; | ||||
|     storedObject: StoredObject; | ||||
| } | ||||
|  | ||||
| export interface GenericDocForAccompanyingCoursePersonDocument | ||||
|     extends GenericDocForAccompanyingPeriod { | ||||
|     key: "person_document"; | ||||
|     metadata: BaseMetadataWithHtml; | ||||
|     storedObject: StoredObject; | ||||
| } | ||||
|  | ||||
| export interface GenericDocForAccompanyingCourseWorkEvaluationDocument | ||||
|     extends GenericDocForAccompanyingPeriod { | ||||
|     key: "accompanying_period_work_evaluation_document"; | ||||
|     metadata: BaseMetadataWithHtml; | ||||
|     storedObject: StoredObject; | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,6 @@ import { StoredObject, StoredObjectVersion } from "../../types"; | ||||
| import DropFileWidget from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFileWidget.vue"; | ||||
| import { computed, reactive } from "vue"; | ||||
| import { useToast } from "vue-toast-notification"; | ||||
| import { DOCUMENT_ADD, trans } from "translator"; | ||||
|  | ||||
| interface DropFileConfig { | ||||
|     allowRemove: boolean; | ||||
| @@ -76,9 +75,11 @@ function closeModal(): void { | ||||
|         @click="openModal" | ||||
|         class="btn btn-create" | ||||
|     > | ||||
|         {{ trans(DOCUMENT_ADD) }} | ||||
|         Ajouter un document | ||||
|     </button> | ||||
|     <button v-else @click="openModal" class="btn btn-edit"> | ||||
|         Remplacer le document | ||||
|     </button> | ||||
|     <button v-else @click="openModal" class="btn btn-edit"></button> | ||||
|     <modal | ||||
|         v-if="state.showModal" | ||||
|         :modal-dialog-class="modalClasses" | ||||
|   | ||||
| @@ -3,9 +3,9 @@ import { | ||||
|     StoredObject, | ||||
|     StoredObjectPointInTime, | ||||
|     StoredObjectVersionWithPointInTime, | ||||
| } from "ChillDocStoreAssets/types"; | ||||
| } from "./../../../types"; | ||||
| import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue"; | ||||
| import { ISOToDatetime } from "ChillMainAssets/chill/js/date"; | ||||
| import { ISOToDatetime } from "./../../../../../../ChillMainBundle/Resources/public/chill/js/date"; | ||||
| import FileIcon from "ChillDocStoreAssets/vuejs/FileIcon.vue"; | ||||
| import RestoreVersionButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/RestoreVersionButton.vue"; | ||||
| import DownloadButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/DownloadButton.vue"; | ||||
|   | ||||
| @@ -46,16 +46,6 @@ abstract class AbstractStoredObjectVoter implements StoredObjectVoterInterface | ||||
|  | ||||
|     public function voteOnAttribute(StoredObjectRoleEnum $attribute, StoredObject $subject, TokenInterface $token): bool | ||||
|     { | ||||
|         // we first try to get the permission from the workflow, as attachement (this is the less intensive query) | ||||
|         $workflowPermissionAsAttachment = match ($attribute) { | ||||
|             StoredObjectRoleEnum::SEE => $this->workflowDocumentService->isAllowedByWorkflowForReadOperation($subject), | ||||
|             StoredObjectRoleEnum::EDIT => $this->workflowDocumentService->isAllowedByWorkflowForWriteOperation($subject), | ||||
|         }; | ||||
|  | ||||
|         if (WorkflowRelatedEntityPermissionHelper::FORCE_DENIED === $workflowPermissionAsAttachment) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Retrieve the related entity | ||||
|         $entity = $this->getRepository()->findAssociatedEntityToStoredObject($subject); | ||||
|  | ||||
| @@ -76,7 +66,7 @@ abstract class AbstractStoredObjectVoter implements StoredObjectVoterInterface | ||||
|         return match ($workflowPermission) { | ||||
|             WorkflowRelatedEntityPermissionHelper::FORCE_GRANT => true, | ||||
|             WorkflowRelatedEntityPermissionHelper::FORCE_DENIED => false, | ||||
|             WorkflowRelatedEntityPermissionHelper::ABSTAIN => WorkflowRelatedEntityPermissionHelper::FORCE_GRANT === $workflowPermissionAsAttachment || $regularPermission, | ||||
|             WorkflowRelatedEntityPermissionHelper::ABSTAIN => $regularPermission, | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,12 +14,6 @@ namespace Chill\DocStoreBundle\Security\Authorization; | ||||
| use Chill\DocStoreBundle\Entity\StoredObject; | ||||
| use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; | ||||
|  | ||||
| /** | ||||
|  * Interface for voting on stored object permissions. | ||||
|  * | ||||
|  * Each time a stored object is attached to a document, the voter is responsible for determining | ||||
|  * whether the user has the necessary permissions to access or modify the stored object. | ||||
|  */ | ||||
| interface StoredObjectVoterInterface | ||||
| { | ||||
|     public function supports(StoredObjectRoleEnum $attribute, StoredObject $subject): bool; | ||||
|   | ||||
| @@ -15,7 +15,6 @@ use Chill\DocStoreBundle\Entity\StoredObject; | ||||
| use Chill\DocStoreBundle\Entity\StoredObjectPointInTime; | ||||
| use Chill\DocStoreBundle\Entity\StoredObjectPointInTimeReasonEnum; | ||||
| use Chill\DocStoreBundle\Entity\StoredObjectVersion; | ||||
| use Chill\DocStoreBundle\Exception\ConversionWithSameMimeTypeException; | ||||
| use Chill\DocStoreBundle\Exception\StoredObjectManagerException; | ||||
| use Chill\WopiBundle\Service\WopiConverter; | ||||
| use Symfony\Component\Mime\MimeTypesInterface; | ||||
| @@ -42,10 +41,9 @@ class StoredObjectToPdfConverter | ||||
|      * | ||||
|      * @return array{0: StoredObjectPointInTime, 1: StoredObjectVersion, 2?: string} contains the point in time before conversion and the new version of the stored object. The converted content is included in the response if $includeConvertedContent is true | ||||
|      * | ||||
|      * @throws \UnexpectedValueException           if the preferred mime type for the conversion is not found | ||||
|      * @throws \RuntimeException                   if the conversion or storage of the new version fails | ||||
|      * @throws \UnexpectedValueException    if the preferred mime type for the conversion is not found | ||||
|      * @throws \RuntimeException            if the conversion or storage of the new version fails | ||||
|      * @throws StoredObjectManagerException | ||||
|      * @throws ConversionWithSameMimeTypeException if the document has already the same mime type79* | ||||
|      */ | ||||
|     public function addConvertedVersion(StoredObject $storedObject, string $lang, $convertTo = 'pdf', bool $includeConvertedContent = false): array | ||||
|     { | ||||
| @@ -58,7 +56,7 @@ class StoredObjectToPdfConverter | ||||
|         $currentVersion = $storedObject->getCurrentVersion(); | ||||
|  | ||||
|         if ($currentVersion->getType() === $newMimeType) { | ||||
|             throw new ConversionWithSameMimeTypeException($newMimeType); | ||||
|             throw new \UnexpectedValueException('Already at the same mime type'); | ||||
|         } | ||||
|  | ||||
|         $content = $this->storedObjectManager->read($currentVersion); | ||||
|   | ||||
| @@ -40,10 +40,6 @@ class StoredObjectVersionApiControllerTest extends \PHPUnit\Framework\TestCase | ||||
|             $storedObject->registerVersion(); | ||||
|         } | ||||
|  | ||||
|         // remove one version in the history | ||||
|         $v5 = $storedObject->getVersions()->get(5); | ||||
|         $storedObject->removeVersion($v5); | ||||
|  | ||||
|         $security = $this->prophesize(Security::class); | ||||
|         $security->isGranted(StoredObjectRoleEnum::SEE->value, $storedObject) | ||||
|             ->willReturn(true) | ||||
| @@ -57,7 +53,6 @@ class StoredObjectVersionApiControllerTest extends \PHPUnit\Framework\TestCase | ||||
|         self::assertEquals($response->getStatusCode(), 200); | ||||
|         self::assertIsArray($body); | ||||
|         self::assertArrayHasKey('results', $body); | ||||
|         self::assertIsList($body['results']); | ||||
|         self::assertCount(10, $body['results']); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -86,165 +86,9 @@ class AbstractStoredObjectVoterTest extends TestCase | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @dataProvider dataProviderVoteOnAttributeWithStoredObjectPermission | ||||
|      * @dataProvider dataProviderVoteOnAttribute | ||||
|      */ | ||||
|     public function testVoteOnAttributeWithStoredObjectPermission( | ||||
|         StoredObjectRoleEnum $attribute, | ||||
|         bool $expected, | ||||
|         bool $isGrantedRegularPermission, | ||||
|         string $isGrantedWorkflowPermission, | ||||
|         string $isGrantedStoredObjectAttachment, | ||||
|     ): void { | ||||
|         $storedObject = new StoredObject(); | ||||
|         $repository = new DummyRepository($related = new \stdClass()); | ||||
|         $token = new UsernamePasswordToken(new User(), 'dummy'); | ||||
|  | ||||
|         $security = $this->prophesize(Security::class); | ||||
|         $security->isGranted('SOME_ROLE', $related)->willReturn($isGrantedRegularPermission); | ||||
|  | ||||
|         $workflowRelatedEntityPermissionHelper = $this->prophesize(WorkflowRelatedEntityPermissionHelper::class); | ||||
|  | ||||
|         $security = $this->prophesize(Security::class); | ||||
|         $security->isGranted('SOME_ROLE', $related)->willReturn($isGrantedRegularPermission); | ||||
|  | ||||
|         if (StoredObjectRoleEnum::SEE === $attribute) { | ||||
|             $workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($storedObject) | ||||
|                 ->shouldBeCalled() | ||||
|                 ->willReturn($isGrantedStoredObjectAttachment); | ||||
|             $workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($related) | ||||
|                 ->willReturn($isGrantedWorkflowPermission); | ||||
|         } elseif (StoredObjectRoleEnum::EDIT === $attribute) { | ||||
|             $workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForWriteOperation($storedObject) | ||||
|                 ->shouldBeCalled() | ||||
|                 ->willReturn($isGrantedStoredObjectAttachment); | ||||
|             $workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForWriteOperation($related) | ||||
|                 ->willReturn($isGrantedWorkflowPermission); | ||||
|         } else { | ||||
|             throw new \LogicException('Invalid attribute for StoredObjectVoter'); | ||||
|         } | ||||
|  | ||||
|         $storedObjectVoter = new class ($repository, $workflowRelatedEntityPermissionHelper->reveal(), $security->reveal()) extends AbstractStoredObjectVoter { | ||||
|             public function __construct(private $repository, $helper, $security) | ||||
|             { | ||||
|                 parent::__construct($security, $helper); | ||||
|             } | ||||
|  | ||||
|             protected function getRepository(): AssociatedEntityToStoredObjectInterface | ||||
|             { | ||||
|                 return $this->repository; | ||||
|             } | ||||
|  | ||||
|             protected function getClass(): string | ||||
|             { | ||||
|                 return \stdClass::class; | ||||
|             } | ||||
|  | ||||
|             protected function attributeToRole(StoredObjectRoleEnum $attribute): string | ||||
|             { | ||||
|                 return 'SOME_ROLE'; | ||||
|             } | ||||
|  | ||||
|             protected function canBeAssociatedWithWorkflow(): bool | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         $actual = $storedObjectVoter->voteOnAttribute($attribute, $storedObject, $token); | ||||
|  | ||||
|         self::assertEquals($expected, $actual); | ||||
|     } | ||||
|  | ||||
|     public static function dataProviderVoteOnAttributeWithStoredObjectPermission(): iterable | ||||
|     { | ||||
|         foreach (['read' => StoredObjectRoleEnum::SEE, 'write' => StoredObjectRoleEnum::EDIT] as $action => $attribute) { | ||||
|             yield 'Not related to any workflow nor attachment ('.$action.')' => [ | ||||
|                 $attribute, | ||||
|                 true, | ||||
|                 true, | ||||
|                 WorkflowRelatedEntityPermissionHelper::ABSTAIN, | ||||
|                 WorkflowRelatedEntityPermissionHelper::ABSTAIN, | ||||
|             ]; | ||||
|  | ||||
|             yield 'Not related to any workflow nor attachment (refuse) ('.$action.')' => [ | ||||
|                 $attribute, | ||||
|                 false, | ||||
|                 false, | ||||
|                 WorkflowRelatedEntityPermissionHelper::ABSTAIN, | ||||
|                 WorkflowRelatedEntityPermissionHelper::ABSTAIN, | ||||
|             ]; | ||||
|  | ||||
|             yield 'Is granted by a workflow takes precedence (workflow) ('.$action.')' => [ | ||||
|                 $attribute, | ||||
|                 false, | ||||
|                 true, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, | ||||
|                 WorkflowRelatedEntityPermissionHelper::ABSTAIN, | ||||
|             ]; | ||||
|  | ||||
|             yield 'Is granted by a workflow takes precedence (stored object) ('.$action.')' => [ | ||||
|                 $attribute, | ||||
|                 false, | ||||
|                 true, | ||||
|                 WorkflowRelatedEntityPermissionHelper::ABSTAIN, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, | ||||
|             ]; | ||||
|  | ||||
|             yield 'Is granted by a workflow takes precedence (workflow) although grant ('.$action.')' => [ | ||||
|                 $attribute, | ||||
|                 false, | ||||
|                 true, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, | ||||
|             ]; | ||||
|  | ||||
|             yield 'Is granted by a workflow takes precedence (stored object) although grant ('.$action.')' => [ | ||||
|                 $attribute, | ||||
|                 false, | ||||
|                 true, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, | ||||
|             ]; | ||||
|  | ||||
|             yield 'Is granted by a workflow takes precedence (initially refused) (workflow) although grant ('.$action.')' => [ | ||||
|                 $attribute, | ||||
|                 false, | ||||
|                 false, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, | ||||
|             ]; | ||||
|  | ||||
|             yield 'Is granted by a workflow takes precedence (initially refused) (stored object) although grant ('.$action.')' => [ | ||||
|                 $attribute, | ||||
|                 false, | ||||
|                 false, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, | ||||
|             ]; | ||||
|  | ||||
|             yield 'Force grant inverse the regular permission (workflow) ('.$action.')' => [ | ||||
|                 $attribute, | ||||
|                 true, | ||||
|                 false, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, | ||||
|                 WorkflowRelatedEntityPermissionHelper::ABSTAIN, | ||||
|             ]; | ||||
|  | ||||
|             yield 'Force grant inverse the regular permission (so) ('.$action.')' => [ | ||||
|                 $attribute, | ||||
|                 true, | ||||
|                 false, | ||||
|                 WorkflowRelatedEntityPermissionHelper::ABSTAIN, | ||||
|                 WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, | ||||
|             ]; | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @dataProvider dataProviderVoteOnAttributeWithoutStoredObjectPermission | ||||
|      */ | ||||
|     public function testVoteOnAttributeWithoutStoredObjectPermission( | ||||
|     public function testVoteOnAttribute( | ||||
|         StoredObjectRoleEnum $attribute, | ||||
|         bool $expected, | ||||
|         bool $canBeAssociatedWithWorkflow, | ||||
| @@ -261,10 +105,6 @@ class AbstractStoredObjectVoterTest extends TestCase | ||||
|         $security->isGranted('SOME_ROLE', $related)->willReturn($isGrantedRegularPermission); | ||||
|  | ||||
|         $workflowRelatedEntityPermissionHelper = $this->prophesize(WorkflowRelatedEntityPermissionHelper::class); | ||||
|  | ||||
|         $workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($storedObject)->willReturn(WorkflowRelatedEntityPermissionHelper::ABSTAIN); | ||||
|         $workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForWriteOperation($storedObject)->willReturn(WorkflowRelatedEntityPermissionHelper::ABSTAIN); | ||||
|  | ||||
|         if (null !== $isGrantedWorkflowPermissionRead) { | ||||
|             $workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($related) | ||||
|                 ->willReturn($isGrantedWorkflowPermissionRead)->shouldBeCalled(); | ||||
| @@ -283,7 +123,7 @@ class AbstractStoredObjectVoterTest extends TestCase | ||||
|         self::assertEquals($expected, $voter->voteOnAttribute($attribute, $storedObject, $token), $message); | ||||
|     } | ||||
|  | ||||
|     public static function dataProviderVoteOnAttributeWithoutStoredObjectPermission(): iterable | ||||
|     public static function dataProviderVoteOnAttribute(): iterable | ||||
|     { | ||||
|         // not associated on a workflow | ||||
|         yield [StoredObjectRoleEnum::SEE, true, false, true, null, null, 'not associated on a workflow, granted by regular access, must not rely on helper']; | ||||
|   | ||||
| @@ -1,63 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\Migrations\DocStore; | ||||
|  | ||||
| use Doctrine\DBAL\Schema\Schema; | ||||
| use Doctrine\Migrations\AbstractMigration; | ||||
|  | ||||
| final class Version20251013094414 extends AbstractMigration | ||||
| { | ||||
|     public function getDescription(): string | ||||
|     { | ||||
|         return 'DocStore: Enforce filename uniqueness on chill_doc.stored_object_version; clean duplicates and add partial unique index on filename (for new rows only).'; | ||||
|     } | ||||
|  | ||||
|     public function up(Schema $schema): void | ||||
|     { | ||||
|         // 1) Clean duplicates: for each (stored_object_id, filename, key, iv), keep only the last inserted row | ||||
|         //    and delete all others. Use ROW_NUMBER over id DESC to define the last one. | ||||
|         $this->addSql(<<<'SQL' | ||||
|             WITH ranked AS ( | ||||
|                 SELECT id, | ||||
|                        rank() OVER ( | ||||
|                            PARTITION BY stored_object_id, filename, "key"::jsonb, iv::jsonb | ||||
|                            ORDER BY id DESC | ||||
|                        ) AS rn | ||||
|                 FROM chill_doc.stored_object_version | ||||
|             ) | ||||
|             DELETE FROM chill_doc.stored_object_version sov | ||||
|             USING ranked r | ||||
|             WHERE sov.id = r.id | ||||
|               AND r.rn > 1 | ||||
|         SQL); | ||||
|  | ||||
|         // 2) Create a partial unique index on filename that applies only to subsequently inserted rows. | ||||
|         //    Per user's instruction, compute the cutoff using the stored_object_id sequence value. | ||||
|         $nextVal = (int) $this->connection->fetchOne("SELECT nextval('chill_doc.stored_object_version_id_seq')"); | ||||
|  | ||||
|         // Safety: if somehow sequence is not available, fallback to current max id from the table | ||||
|         if ($nextVal <= 0) { | ||||
|             $nextVal = (int) $this->connection->fetchOne('SELECT COALESCE(MAX(id), 0) FROM chill_doc.stored_object_version'); | ||||
|         } | ||||
|  | ||||
|         $this->addSql(sprintf( | ||||
|             'CREATE UNIQUE INDEX chill_doc_stored_object_version_unique_by_filename ON chill_doc.stored_object_version (filename) WHERE id > %d', | ||||
|             $nextVal | ||||
|         )); | ||||
|     } | ||||
|  | ||||
|     public function down(Schema $schema): void | ||||
|     { | ||||
|         // Drop the partial unique index; data cleanup is irreversible. | ||||
|         $this->addSql('DROP INDEX IF EXISTS chill_doc_stored_object_version_unique_by_filename'); | ||||
|     } | ||||
| } | ||||
| @@ -23,8 +23,6 @@ See the document: Voir le document | ||||
|  | ||||
| document: | ||||
|     Any title: Aucun titre | ||||
|     replace: Remplacer | ||||
|     Add: Ajouter un document | ||||
|  | ||||
| generic_doc: | ||||
|     filter: | ||||
|   | ||||
| @@ -1,28 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Controller\Admin; | ||||
|  | ||||
| use Chill\MainBundle\CRUD\Controller\CRUDController; | ||||
| use Chill\MainBundle\Pagination\PaginatorInterface; | ||||
| use Doctrine\ORM\QueryBuilder; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
|  | ||||
| class EventBudgetKindController extends CRUDController | ||||
| { | ||||
|     protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator) | ||||
|     { | ||||
|         /* @var QueryBuilder $query */ | ||||
|         $query->addOrderBy('e.type', 'ASC'); | ||||
|  | ||||
|         return parent::orderQuery($action, $query, $request, $paginator); | ||||
|     } | ||||
| } | ||||
| @@ -23,11 +23,11 @@ use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; | ||||
| use Chill\PersonBundle\Entity\Person; | ||||
| use Chill\PersonBundle\Form\Type\PickPersonDynamicType; | ||||
| use Chill\PersonBundle\Privacy\PrivacyEvent; | ||||
| use Doctrine\Persistence\ManagerRegistry; | ||||
| use PhpOffice\PhpSpreadsheet\Spreadsheet; | ||||
| use PhpOffice\PhpSpreadsheet\Writer\Csv; | ||||
| use PhpOffice\PhpSpreadsheet\Writer\Ods; | ||||
| use PhpOffice\PhpSpreadsheet\Writer\Xlsx; | ||||
| use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | ||||
| use Symfony\Bridge\Doctrine\Form\Type\EntityType; | ||||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||||
| use Symfony\Component\EventDispatcher\EventDispatcherInterface; | ||||
| @@ -41,8 +41,6 @@ use Symfony\Component\HttpFoundation\Response; | ||||
| use Symfony\Component\HttpFoundation\StreamedResponse; | ||||
| use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||||
| use Symfony\Component\Security\Core\Security; | ||||
| use Symfony\Component\Serializer\Exception\ExceptionInterface; | ||||
| use Symfony\Component\Serializer\Normalizer\NormalizerInterface; | ||||
| use Symfony\Contracts\Translation\TranslatorInterface; | ||||
|  | ||||
| /** | ||||
| @@ -60,8 +58,7 @@ final class EventController extends AbstractController | ||||
|         private readonly TranslatorInterface $translator, | ||||
|         private readonly PaginatorFactory $paginator, | ||||
|         private readonly Security $security, | ||||
|         private readonly ManagerRegistry $managerRegistry, | ||||
|         private readonly NormalizerInterface $normalizer, | ||||
|         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry, | ||||
|     ) {} | ||||
|  | ||||
|     #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/delete', name: 'chill_event__event_delete', requirements: ['event_id' => '\d+'], methods: ['GET', 'POST', 'DELETE'])] | ||||
| @@ -78,7 +75,6 @@ final class EventController extends AbstractController | ||||
|  | ||||
|         /** @var array $participations */ | ||||
|         $participations = $event->getParticipations(); | ||||
|         $budgetElements = $event->getBudgetElements(); | ||||
|  | ||||
|         $form = $this->createDeleteForm($event_id); | ||||
|  | ||||
| @@ -90,10 +86,6 @@ final class EventController extends AbstractController | ||||
|                     $em->remove($participation); | ||||
|                 } | ||||
|  | ||||
|                 foreach ($budgetElements as $e) { | ||||
|                     $em->remove($e); | ||||
|                 } | ||||
|  | ||||
|                 $em->remove($event); | ||||
|                 $em->flush(); | ||||
|  | ||||
| @@ -111,7 +103,7 @@ final class EventController extends AbstractController | ||||
|         } | ||||
|  | ||||
|         return $this->render('@ChillEvent/Event/confirm_delete.html.twig', [ | ||||
|             'id' => $event->getId(), | ||||
|             'event_id' => $event->getId(), | ||||
|             'delete_form' => $form->createView(), | ||||
|         ]); | ||||
|     } | ||||
| @@ -177,8 +169,6 @@ final class EventController extends AbstractController | ||||
|  | ||||
|     /** | ||||
|      * Displays a form to create a new Event entity. | ||||
|      * | ||||
|      * @throws ExceptionInterface | ||||
|      */ | ||||
|     #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/new', name: 'chill_event__event_new', methods: ['GET', 'POST'])] | ||||
|     public function newAction(?Center $center, Request $request): Response | ||||
| @@ -209,23 +199,26 @@ final class EventController extends AbstractController | ||||
|             $this->addFlash('success', $this->translator | ||||
|                 ->trans('The event was created')); | ||||
|  | ||||
|             return $this->redirectToRoute('chill_event__event_show', ['id' => $entity->getId()]); | ||||
|             return $this->redirectToRoute('chill_event__event_show', ['event_id' => $entity->getId()]); | ||||
|         } | ||||
|  | ||||
|         $entity_array = $this->normalizer->normalize($entity, 'json', ['groups' => 'read']); | ||||
|  | ||||
|         return $this->render('@ChillEvent/Event/new.html.twig', [ | ||||
|             'entity' => $entity, | ||||
|             'form' => $form->createView(), | ||||
|             'entity_json' => $entity_array, | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * First step of new Event form. | ||||
|      */ | ||||
|     #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/new/pick-center', name: 'chill_event__event_new_pickcenter', options: [null])] | ||||
|     public function newPickCenterAction(): Response | ||||
|     { | ||||
|         $role = 'CHILL_EVENT_CREATE'; | ||||
|  | ||||
|         /** | ||||
|          * @var Center $centers | ||||
|          */ | ||||
|         $centers = $this->authorizationHelper->getReachableCenters($this->getUser(), $role); | ||||
|  | ||||
|         if (1 === \count($centers)) { | ||||
| @@ -245,7 +238,7 @@ final class EventController extends AbstractController | ||||
|             ->add('center_id', EntityType::class, [ | ||||
|                 'class' => Center::class, | ||||
|                 'choices' => $centers, | ||||
|                 'placeholder' => $this->translator->trans('Pick a center'), | ||||
|                 'placeholder' => '', | ||||
|                 'label' => 'To which centre should the event be associated ?', | ||||
|             ]) | ||||
|             ->add('submit', SubmitType::class, [ | ||||
| @@ -258,7 +251,16 @@ final class EventController extends AbstractController | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{id}/show', name: 'chill_event__event_show')] | ||||
|     /** | ||||
|      * Finds and displays a Event entity. | ||||
|      * | ||||
|      * @ParamConverter("event", options={"id": "event_id"}) | ||||
|      * | ||||
|      * @return Response | ||||
|      * | ||||
|      * @throws \PhpOffice\PhpSpreadsheet\Exception | ||||
|      */ | ||||
|     #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/show', name: 'chill_event__event_show')] | ||||
|     public function showAction(Event $event, Request $request) | ||||
|     { | ||||
|         if (!$event) { | ||||
| @@ -315,7 +317,7 @@ final class EventController extends AbstractController | ||||
|  | ||||
|             $this->addFlash('success', $this->translator->trans('The event was updated')); | ||||
|  | ||||
|             return $this->redirectToRoute('chill_event__event_show', ['id' => $event_id]); | ||||
|             return $this->redirectToRoute('chill_event__event_show', ['event_id' => $event_id]); | ||||
|         } | ||||
|  | ||||
|         return $this->render('@ChillEvent/Event/edit.html.twig', [ | ||||
|   | ||||
| @@ -15,15 +15,11 @@ use Chill\EventBundle\Entity\Event; | ||||
| use Chill\EventBundle\Entity\EventType; | ||||
| use Chill\EventBundle\Repository\EventACLAwareRepositoryInterface; | ||||
| use Chill\EventBundle\Repository\EventTypeRepository; | ||||
| use Chill\EventBundle\Security\EventVoter; | ||||
| use Chill\MainBundle\Entity\Center; | ||||
| use Chill\MainBundle\Pagination\PaginatorFactoryInterface; | ||||
| use Chill\MainBundle\Security\Authorization\AuthorizationHelper; | ||||
| use Chill\MainBundle\Templating\Listing\FilterOrderHelper; | ||||
| use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactory; | ||||
| use Chill\MainBundle\Templating\TranslatableStringHelperInterface; | ||||
| use Chill\PersonBundle\Form\Type\PickPersonDynamicType; | ||||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||||
| use Symfony\Component\Form\Extension\Core\Type\FormType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\HiddenType; | ||||
| use Symfony\Component\Form\FormFactoryInterface; | ||||
| @@ -33,18 +29,17 @@ use Symfony\Component\Routing\Annotation\Route; | ||||
| use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||||
| use Twig\Environment; | ||||
|  | ||||
| final class EventListController extends AbstractController | ||||
| final readonly class EventListController | ||||
| { | ||||
|     public function __construct( | ||||
|         private readonly Environment $environment, | ||||
|         private readonly EventACLAwareRepositoryInterface $eventACLAwareRepository, | ||||
|         private readonly EventTypeRepository $eventTypeRepository, | ||||
|         private readonly FilterOrderHelperFactory $filterOrderHelperFactory, | ||||
|         private readonly FormFactoryInterface $formFactory, | ||||
|         private readonly PaginatorFactoryInterface $paginatorFactory, | ||||
|         private readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|         private readonly UrlGeneratorInterface $urlGenerator, | ||||
|         private readonly AuthorizationHelper $authorizationHelper, | ||||
|         private Environment $environment, | ||||
|         private EventACLAwareRepositoryInterface $eventACLAwareRepository, | ||||
|         private EventTypeRepository $eventTypeRepository, | ||||
|         private FilterOrderHelperFactory $filterOrderHelperFactory, | ||||
|         private FormFactoryInterface $formFactory, | ||||
|         private PaginatorFactoryInterface $paginatorFactory, | ||||
|         private TranslatableStringHelperInterface $translatableStringHelper, | ||||
|         private UrlGeneratorInterface $urlGenerator, | ||||
|     ) {} | ||||
|  | ||||
|     #[Route(path: '{_locale}/event/event/list', name: 'chill_event_event_list')] | ||||
| @@ -55,8 +50,6 @@ final class EventListController extends AbstractController | ||||
|             'q' => (string) $filter->getQueryString(), | ||||
|             'dates' => $filter->getDateRangeData('dates'), | ||||
|             'event_types' => $filter->getEntityChoiceData('event_types'), | ||||
|             'responsables' => $filter->getUserPickerData('responsables'), | ||||
|             'centers' => $filter->getEntityChoiceData('centers'), | ||||
|         ]; | ||||
|         $total = $this->eventACLAwareRepository->countAllViewable($filterData); | ||||
|         $pagination = $this->paginatorFactory->create($total); | ||||
| @@ -80,7 +73,6 @@ final class EventListController extends AbstractController | ||||
|     private function buildFilterOrder(): FilterOrderHelper | ||||
|     { | ||||
|         $types = $this->eventTypeRepository->findAllActive(); | ||||
|         $centers = $this->authorizationHelper->getReachableCenters($this->getUser(), EventVoter::SEE); | ||||
|  | ||||
|         $builder = $this->filterOrderHelperFactory->create(__METHOD__); | ||||
|         $builder | ||||
| @@ -88,16 +80,6 @@ final class EventListController extends AbstractController | ||||
|             ->addSearchBox(['name']) | ||||
|             ->addEntityChoice('event_types', 'event.filter.event_types', EventType::class, $types, [ | ||||
|                 'choice_label' => fn (EventType $e) => $this->translatableStringHelper->localize($e->getName()), | ||||
|                 'expanded' => false, | ||||
|                 'required' => false, | ||||
|                 'attr' => ['class' => 'select2'], | ||||
|             ]) | ||||
|             ->addUserPicker('responsables', 'event.filter.pick_responsable', ['multiple' => true, 'required' => false]) | ||||
|             ->addEntityChoice('centers', 'event.filter.center', Center::class, $centers, [ | ||||
|                 'choice_label' => fn (Center $c) => $c->getName(), | ||||
|                 'expanded' => false, | ||||
|                 'required' => false, | ||||
|                 'attr' => ['class' => 'select2'], | ||||
|             ]); | ||||
|  | ||||
|         return $builder->build(); | ||||
|   | ||||
| @@ -1,44 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Controller; | ||||
|  | ||||
| use Chill\MainBundle\CRUD\Controller\CRUDController; | ||||
| use Chill\MainBundle\Pagination\PaginatorInterface; | ||||
| use Doctrine\ORM\QueryBuilder; | ||||
| use Symfony\Component\Form\FormInterface; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
|  | ||||
| class EventThemeController extends CRUDController | ||||
| { | ||||
|     protected function createFormFor(string $action, $entity, ?string $formClass = null, array $formOptions = []): FormInterface | ||||
|     { | ||||
|         if ('new' === $action) { | ||||
|             return parent::createFormFor($action, $entity, $formClass, ['step' => 'create']); | ||||
|         } | ||||
|  | ||||
|         if ('edit' === $action) { | ||||
|             return parent::createFormFor($action, $entity, $formClass, ['step' => 'edit']); | ||||
|         } | ||||
|  | ||||
|         throw new \LogicException('action is not supported: '.$action); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param QueryBuilder|mixed $query | ||||
|      */ | ||||
|     protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator): QueryBuilder | ||||
|     { | ||||
|         /* @var QueryBuilder $query */ | ||||
|         return $query->orderBy('e.ordering', 'ASC') | ||||
|             ->addOrderBy('e.id', 'ASC'); | ||||
|     } | ||||
| } | ||||
| @@ -228,7 +228,7 @@ final class ParticipationController extends AbstractController | ||||
|             } | ||||
|  | ||||
|             return $this->redirectToRoute('chill_event__event_show', [ | ||||
|                 'id' => $participation->getEvent()->getId(), | ||||
|                 'event_id' => $participation->getEvent()->getId(), | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
| @@ -242,7 +242,7 @@ final class ParticipationController extends AbstractController | ||||
|     /** | ||||
|      * @param int $participation_id | ||||
|      */ | ||||
|     #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/participation/{participation_id}/delete', name: 'chill_event_participation_delete', requirements: ['participation_id' => '\d+'])] | ||||
|     #[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/participation/{participation_id}/delete', name: 'chill_event_participation_delete', requirements: ['participation_id' => '\d+'], methods: ['GET', 'DELETE'])] | ||||
|     public function deleteAction($participation_id, Request $request): Response|\Symfony\Component\HttpFoundation\RedirectResponse | ||||
|     { | ||||
|         $em = $this->managerRegistry->getManager(); | ||||
| @@ -273,7 +273,7 @@ final class ParticipationController extends AbstractController | ||||
|                 ); | ||||
|  | ||||
|                 return $this->redirectToRoute('chill_event__event_show', [ | ||||
|                     'id' => $event->getId(), | ||||
|                     'event_id' => $event->getId(), | ||||
|                 ]); | ||||
|             } | ||||
|         } | ||||
| @@ -442,7 +442,7 @@ final class ParticipationController extends AbstractController | ||||
|             )); | ||||
|  | ||||
|             return $this->redirectToRoute('chill_event__event_show', [ | ||||
|                 'id' => $participation->getEvent()->getId(), | ||||
|                 'event_id' => $participation->getEvent()->getId(), | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -11,12 +11,6 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\EventBundle\DependencyInjection; | ||||
|  | ||||
| use Chill\EventBundle\Controller\Admin\EventBudgetKindController; | ||||
| use Chill\EventBundle\Controller\EventThemeController; | ||||
| use Chill\EventBundle\Entity\EventBudgetKind; | ||||
| use Chill\EventBundle\Entity\EventTheme; | ||||
| use Chill\EventBundle\Form\EventBudgetKindType; | ||||
| use Chill\EventBundle\Form\EventThemeType; | ||||
| use Chill\EventBundle\Security\EventVoter; | ||||
| use Chill\EventBundle\Security\ParticipationVoter; | ||||
| use Symfony\Component\Config\FileLocator; | ||||
| @@ -32,10 +26,7 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension; | ||||
|  */ | ||||
| class ChillEventExtension extends Extension implements PrependExtensionInterface | ||||
| { | ||||
|     /** | ||||
|      * @throws \Exception | ||||
|      */ | ||||
|     public function load(array $configs, ContainerBuilder $container): void | ||||
|     public function load(array $configs, ContainerBuilder $container) | ||||
|     { | ||||
|         $configuration = new Configuration(); | ||||
|         $config = $this->processConfiguration($configuration, $configs); | ||||
| @@ -54,17 +45,16 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface | ||||
|     /** (non-PHPdoc). | ||||
|      * @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend() | ||||
|      */ | ||||
|     public function prepend(ContainerBuilder $container): void | ||||
|     public function prepend(ContainerBuilder $container) | ||||
|     { | ||||
|         $this->prependAuthorization($container); | ||||
|         $this->prependCruds($container); | ||||
|         $this->prependRoute($container); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * add authorization hierarchy. | ||||
|      */ | ||||
|     protected function prependAuthorization(ContainerBuilder $container): void | ||||
|     protected function prependAuthorization(ContainerBuilder $container) | ||||
|     { | ||||
|         $container->prependExtensionConfig('security', [ | ||||
|             'role_hierarchy' => [ | ||||
| @@ -80,7 +70,7 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface | ||||
|     /** | ||||
|      * add route to route loader for chill. | ||||
|      */ | ||||
|     protected function prependRoute(ContainerBuilder $container): void | ||||
|     protected function prependRoute(ContainerBuilder $container) | ||||
|     { | ||||
|         // add routes for custom bundle | ||||
|         $container->prependExtensionConfig('chill_main', [ | ||||
| @@ -91,54 +81,4 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface | ||||
|             ], | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     protected function prependCruds(ContainerBuilder $container): void | ||||
|     { | ||||
|         $container->prependExtensionConfig('chill_main', [ | ||||
|             'cruds' => [ | ||||
|                 [ | ||||
|                     'class' => EventTheme::class, | ||||
|                     'name' => 'event_theme', | ||||
|                     'base_path' => '/admin/event/theme', | ||||
|                     'form_class' => EventThemeType::class, | ||||
|                     'controller' => EventThemeController::class, | ||||
|                     'actions' => [ | ||||
|                         'index' => [ | ||||
|                             'template' => '@ChillEvent/Admin/EventTheme/index.html.twig', | ||||
|                             'role' => 'ROLE_ADMIN', | ||||
|                         ], | ||||
|                         'new' => [ | ||||
|                             'role' => 'ROLE_ADMIN', | ||||
|                             'template' => '@ChillEvent/Admin/EventTheme/new.html.twig', | ||||
|                         ], | ||||
|                         'edit' => [ | ||||
|                             'role' => 'ROLE_ADMIN', | ||||
|                             'template' => '@ChillEvent/Admin/EventTheme/edit.html.twig', | ||||
|                         ], | ||||
|                     ], | ||||
|                 ], | ||||
|                 [ | ||||
|                     'class' => EventBudgetKind::class, | ||||
|                     'name' => 'event_budget_kind', | ||||
|                     'base_path' => '/admin/event/budget', | ||||
|                     'form_class' => EventBudgetKindType::class, | ||||
|                     'controller' => EventBudgetKindController::class, | ||||
|                     'actions' => [ | ||||
|                         'index' => [ | ||||
|                             'template' => '@ChillEvent/Admin/BudgetKind/index.html.twig', | ||||
|                             'role' => 'ROLE_ADMIN', | ||||
|                         ], | ||||
|                         'new' => [ | ||||
|                             'role' => 'ROLE_ADMIN', | ||||
|                             'template' => '@ChillEvent/Admin/BudgetKind/new.html.twig', | ||||
|                         ], | ||||
|                         'edit' => [ | ||||
|                             'role' => 'ROLE_ADMIN', | ||||
|                             'template' => '@ChillEvent/Admin/BudgetKind/edit.html.twig', | ||||
|                         ], | ||||
|                     ], | ||||
|                 ], | ||||
|             ], | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,18 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Entity; | ||||
|  | ||||
| enum BudgetTypeEnum: string | ||||
| { | ||||
|     case CHARGE = 'Charge'; | ||||
|     case RESOURCE = 'Resource'; | ||||
| } | ||||
| @@ -23,13 +23,10 @@ use Chill\MainBundle\Entity\HasScopeInterface; | ||||
| use Chill\MainBundle\Entity\Location; | ||||
| use Chill\MainBundle\Entity\Scope; | ||||
| use Chill\MainBundle\Entity\User; | ||||
| use Chill\ThirdPartyBundle\Entity\ThirdParty; | ||||
| use Doctrine\Common\Collections\ArrayCollection; | ||||
| use Doctrine\Common\Collections\Collection; | ||||
| use Doctrine\DBAL\Types\Types; | ||||
| use Doctrine\ORM\Mapping as ORM; | ||||
| use Symfony\Component\Validator\Constraints as Assert; | ||||
| use Symfony\Component\Serializer\Annotation as Serializer; | ||||
|  | ||||
| /** | ||||
|  * Class Event. | ||||
| @@ -49,63 +46,35 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter | ||||
|     #[ORM\ManyToOne(targetEntity: Scope::class)] | ||||
|     private ?Scope $circle = null; | ||||
|  | ||||
|     #[ORM\Column(type: Types::DATETIME_MUTABLE)] | ||||
|     #[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_MUTABLE)] | ||||
|     private ?\DateTime $date = null; | ||||
|  | ||||
|     #[ORM\Id] | ||||
|     #[ORM\Column(name: 'id', type: Types::INTEGER)] | ||||
|     #[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)] | ||||
|     #[ORM\GeneratedValue(strategy: 'AUTO')] | ||||
|     private ?int $id = null; | ||||
|  | ||||
|     #[ORM\ManyToOne(targetEntity: User::class)] | ||||
|     private ?User $moderator = null; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<int, User> | ||||
|      */ | ||||
|     #[ORM\ManyToMany(targetEntity: User::class)] | ||||
|     #[Serializer\Groups(['read'])] | ||||
|     #[ORM\JoinTable('chill_event_animatorsintern')] | ||||
|     private Collection $animatorsIntern; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<int, ThirdParty> | ||||
|      */ | ||||
|     #[ORM\ManyToMany(targetEntity: ThirdParty::class)] | ||||
|     #[Serializer\Groups(['read'])] | ||||
|     #[ORM\JoinTable('chill_event_animatorsextern')] | ||||
|     private Collection $animatorsExtern; | ||||
|  | ||||
|     #[Assert\NotBlank] | ||||
|     #[Serializer\Groups(['read'])] | ||||
|     #[ORM\Column(type: Types::STRING, length: 150)] | ||||
|     #[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 150)] | ||||
|     private ?string $name = null; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<int, Participation> | ||||
|      */ | ||||
|     #[ORM\OneToMany(mappedBy: 'event', targetEntity: Participation::class)] | ||||
|     #[Serializer\Groups(['read'])] | ||||
|     private Collection $participations; | ||||
|  | ||||
|     #[Assert\NotNull] | ||||
|     #[Serializer\Groups(['read'])] | ||||
|     #[ORM\ManyToOne(targetEntity: EventType::class)] | ||||
|     private ?EventType $type = null; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<int, EventTheme> | ||||
|      */ | ||||
|     #[ORM\ManyToMany(targetEntity: EventTheme::class)] | ||||
|     #[Serializer\Groups(['read'])] | ||||
|     #[ORM\JoinTable('chill_event_eventtheme')] | ||||
|     private Collection $themes; | ||||
|  | ||||
|     #[ORM\Embedded(class: CommentEmbeddable::class, columnPrefix: 'comment_')] | ||||
|     private CommentEmbeddable $comment; | ||||
|  | ||||
|     #[ORM\ManyToOne(targetEntity: Location::class)] | ||||
|     #[Serializer\Groups(['read'])] | ||||
|     #[ORM\JoinColumn(nullable: true)] | ||||
|     private ?Location $location = null; | ||||
|  | ||||
| @@ -116,17 +85,7 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter | ||||
|     #[ORM\JoinTable('chill_event_event_documents')] | ||||
|     private Collection $documents; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<int, EventBudgetElement> | ||||
|      */ | ||||
|     #[ORM\OneToMany(mappedBy: 'event', targetEntity: EventBudgetElement::class, cascade: ['persist'])] | ||||
|     #[Serializer\Groups(['read'])] | ||||
|     private Collection $budgetElements; | ||||
|  | ||||
|     /** | ||||
|      * @deprecated use budgetElements instead | ||||
|      */ | ||||
|     #[ORM\Column(type: Types::DECIMAL, precision: 10, scale: 4, nullable: true, options: ['default' => '0.0'])] | ||||
|     #[ORM\Column(type: \Doctrine\DBAL\Types\Types::DECIMAL, precision: 10, scale: 4, nullable: true, options: ['default' => '0.0'])] | ||||
|     private string $organizationCost = '0.0'; | ||||
|  | ||||
|     /** | ||||
| @@ -137,20 +96,6 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter | ||||
|         $this->participations = new ArrayCollection(); | ||||
|         $this->documents = new ArrayCollection(); | ||||
|         $this->comment = new CommentEmbeddable(); | ||||
|         $this->themes = new ArrayCollection(); | ||||
|         $this->budgetElements = new ArrayCollection(); | ||||
|         $this->animatorsIntern = new ArrayCollection(); | ||||
|         $this->animatorsExtern = new ArrayCollection(); | ||||
|     } | ||||
|  | ||||
|     public function addBudgetElement(EventBudgetElement $budgetElement) | ||||
|     { | ||||
|         if (!$this->budgetElements->contains($budgetElement)) { | ||||
|             $this->budgetElements[] = $budgetElement; | ||||
|             $budgetElement->setEvent($this); | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -181,79 +126,38 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function getThemes(): Collection | ||||
|     { | ||||
|         return $this->themes; | ||||
|     } | ||||
|  | ||||
|     public function addTheme(EventTheme $theme): self | ||||
|     { | ||||
|         $this->themes->add($theme); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function removeTheme(EventTheme $theme): void | ||||
|     { | ||||
|         $this->themes->removeElement($theme); | ||||
|     } | ||||
|  | ||||
|     public function getAnimatorsIntern(): Collection | ||||
|     { | ||||
|         return $this->animatorsIntern; | ||||
|     } | ||||
|  | ||||
|     public function getAnimatorsExtern(): Collection | ||||
|     { | ||||
|         return $this->animatorsExtern; | ||||
|     } | ||||
|  | ||||
|     public function addAnimatorsIntern(User $ai): self | ||||
|     { | ||||
|         $this->animatorsIntern->add($ai); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function removeAnimatorsIntern(User $ai): void | ||||
|     { | ||||
|         $this->animatorsIntern->removeElement($ai); | ||||
|     } | ||||
|  | ||||
|     public function addAnimatorsExtern(ThirdParty $ae): self | ||||
|     { | ||||
|         $this->animatorsExtern->add($ae); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function removeAnimatorsExtern(ThirdParty $ae): void | ||||
|     { | ||||
|         $this->animatorsExtern->removeElement($ae); | ||||
|     } | ||||
|  | ||||
|     public function getCenter(): Center | ||||
|     /** | ||||
|      * @return Center | ||||
|      */ | ||||
|     public function getCenter() | ||||
|     { | ||||
|         return $this->center; | ||||
|     } | ||||
|  | ||||
|     public function getCircle(): ?Scope | ||||
|     /** | ||||
|      * @return Scope | ||||
|      */ | ||||
|     public function getCircle() | ||||
|     { | ||||
|         return $this->circle; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get date. | ||||
|      * | ||||
|      * @return \DateTime | ||||
|      */ | ||||
|     public function getDate(): ?\DateTime | ||||
|     public function getDate() | ||||
|     { | ||||
|         return $this->date; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get id. | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getId(): ?int | ||||
|     public function getId() | ||||
|     { | ||||
|         return $this->id; | ||||
|     } | ||||
| @@ -265,20 +169,14 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter | ||||
|  | ||||
|     /** | ||||
|      * Get label. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getName(): ?string | ||||
|     public function getName() | ||||
|     { | ||||
|         return $this->name; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection<int, EventBudgetElement> | ||||
|      */ | ||||
|     public function getBudgetElements(): Collection | ||||
|     { | ||||
|         return $this->budgetElements; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection<int, Participation> | ||||
|      */ | ||||
| @@ -301,26 +199,26 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter | ||||
|  | ||||
|     /** | ||||
|      * @deprecated | ||||
|      * | ||||
|      * @return Scope | ||||
|      */ | ||||
|     public function getScope(): Scope | ||||
|     public function getScope() | ||||
|     { | ||||
|         return $this->getCircle(); | ||||
|     } | ||||
|  | ||||
|     public function getType(): ?EventType | ||||
|     /** | ||||
|      * @return EventType | ||||
|      */ | ||||
|     public function getType() | ||||
|     { | ||||
|         return $this->type; | ||||
|     } | ||||
|  | ||||
|     public function removeBudgetElement(EventBudgetElement $budgetElement): void | ||||
|     { | ||||
|         $this->budgetElements->removeElement($budgetElement); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove participation. | ||||
|      */ | ||||
|     public function removeParticipation(Participation $participation): void | ||||
|     public function removeParticipation(Participation $participation) | ||||
|     { | ||||
|         $this->participations->removeElement($participation); | ||||
|     } | ||||
| @@ -416,17 +314,11 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter | ||||
|         $this->documents = $documents; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @deprecated | ||||
|      */ | ||||
|     public function getOrganizationCost(): string | ||||
|     { | ||||
|         return $this->organizationCost; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @deprecated | ||||
|      */ | ||||
|     public function setOrganizationCost(string $organizationCost): void | ||||
|     { | ||||
|         $this->organizationCost = $organizationCost; | ||||
|   | ||||
| @@ -1,103 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Entity; | ||||
|  | ||||
| use Chill\EventBundle\Repository\EventThemeRepository; | ||||
| use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; | ||||
| use Doctrine\DBAL\Types\Types; | ||||
| use Doctrine\ORM\Mapping as ORM; | ||||
| use Symfony\Component\Validator\Constraints as Assert; | ||||
|  | ||||
| #[ORM\Entity(repositoryClass: EventThemeRepository::class)] | ||||
| #[ORM\Table(name: 'chill_event_budget_element')] | ||||
| class EventBudgetElement | ||||
| { | ||||
|     #[ORM\Column(name: 'id', type: Types::INTEGER)] | ||||
|     #[ORM\Id] | ||||
|     #[ORM\GeneratedValue(strategy: 'AUTO')] | ||||
|     private ?int $id = null; | ||||
|  | ||||
|     #[Assert\GreaterThan(value: 0)] | ||||
|     #[Assert\NotNull(message: 'The amount cannot be empty')] | ||||
|     #[ORM\Column(name: 'amount', type: Types::DECIMAL, precision: 10, scale: 2)] | ||||
|     private string $amount; | ||||
|  | ||||
|     #[ORM\Embedded(class: CommentEmbeddable::class, columnPrefix: 'comment_budget_element_')] | ||||
|     private ?CommentEmbeddable $comment = null; | ||||
|  | ||||
|     #[ORM\ManyToOne(targetEntity: Event::class)] | ||||
|     private Event $event; | ||||
|  | ||||
|     #[ORM\ManyToOne(targetEntity: EventBudgetKind::class, inversedBy: 'EventBudgetElement')] | ||||
|     #[ORM\JoinColumn] | ||||
|     private EventBudgetKind $kind; | ||||
|  | ||||
|     /* Getters and Setters */ | ||||
|  | ||||
|     public function getId(): ?int | ||||
|     { | ||||
|         return $this->id; | ||||
|     } | ||||
|  | ||||
|     public function setId(?int $id): void | ||||
|     { | ||||
|         $this->id = $id; | ||||
|     } | ||||
|  | ||||
|     public function getAmount(): float | ||||
|     { | ||||
|         return (float) $this->amount; | ||||
|     } | ||||
|  | ||||
|     public function getComment(): ?CommentEmbeddable | ||||
|     { | ||||
|         return $this->comment; | ||||
|     } | ||||
|  | ||||
|     public function getEvent(): Event | ||||
|     { | ||||
|         return $this->event; | ||||
|     } | ||||
|  | ||||
|     public function getKind(): EventBudgetKind | ||||
|     { | ||||
|         return $this->kind; | ||||
|     } | ||||
|  | ||||
|     public function setAmount(string $amount): self | ||||
|     { | ||||
|         $this->amount = $amount; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function setComment(?CommentEmbeddable $comment = null): self | ||||
|     { | ||||
|         $this->comment = $comment; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function setEvent(Event $event): self | ||||
|     { | ||||
|         $this->event = $event; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function setKind(EventBudgetKind $kind): self | ||||
|     { | ||||
|         $this->kind = $kind; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| @@ -1,78 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Entity; | ||||
|  | ||||
| use Doctrine\DBAL\Types\Types; | ||||
| use Doctrine\ORM\Mapping as ORM; | ||||
|  | ||||
| /** | ||||
|  * Type of event budget element. | ||||
|  */ | ||||
| #[ORM\Entity] | ||||
| #[ORM\Table(name: 'chill_event_budget_kind')] | ||||
| class EventBudgetKind | ||||
| { | ||||
|     #[ORM\Id] | ||||
|     #[ORM\GeneratedValue] | ||||
|     #[ORM\Column(type: Types::INTEGER)] | ||||
|     private ?int $id = null; | ||||
|  | ||||
|     #[ORM\Column(type: Types::BOOLEAN, options: ['default' => true])] | ||||
|     private bool $isActive = true; | ||||
|  | ||||
|     #[ORM\Column(enumType: BudgetTypeEnum::class)] | ||||
|     private BudgetTypeEnum $type; | ||||
|  | ||||
|     #[ORM\Column(type: Types::JSON, length: 255, options: ['default' => '{}', 'jsonb' => true])] | ||||
|     private array $name = []; | ||||
|  | ||||
|     public function getId(): ?int | ||||
|     { | ||||
|         return $this->id; | ||||
|     } | ||||
|  | ||||
|     public function getIsActive(): bool | ||||
|     { | ||||
|         return $this->isActive; | ||||
|     } | ||||
|  | ||||
|     public function getType(): BudgetTypeEnum | ||||
|     { | ||||
|         return $this->type; | ||||
|     } | ||||
|  | ||||
|     public function getName(): ?array | ||||
|     { | ||||
|         return $this->name; | ||||
|     } | ||||
|  | ||||
|     public function setIsActive(bool $isActive): self | ||||
|     { | ||||
|         $this->isActive = $isActive; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function setType(BudgetTypeEnum $type): self | ||||
|     { | ||||
|         $this->type = $type; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function setName(array $name): self | ||||
|     { | ||||
|         $this->name = $name; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| @@ -1,158 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Entity; | ||||
|  | ||||
| use Chill\EventBundle\Repository\EventThemeRepository; | ||||
| use Doctrine\Common\Collections\ArrayCollection; | ||||
| use Doctrine\Common\Collections\Collection; | ||||
| use Doctrine\DBAL\Types\Types; | ||||
| use Doctrine\ORM\Mapping as ORM; | ||||
|  | ||||
| /** | ||||
|  * Class EventTheme. | ||||
|  */ | ||||
| #[ORM\HasLifecycleCallbacks] | ||||
| #[ORM\Entity(repositoryClass: EventThemeRepository::class)] | ||||
| #[ORM\Table(name: 'chill_event_event_theme')] | ||||
| class EventTheme | ||||
| { | ||||
|     #[ORM\Column(type: Types::BOOLEAN, nullable: false)] | ||||
|     private bool $isActive = true; | ||||
|  | ||||
|     #[ORM\Id] | ||||
|     #[ORM\Column(name: 'id', type: Types::INTEGER)] | ||||
|     #[ORM\GeneratedValue(strategy: 'AUTO')] | ||||
|     private ?int $id = null; | ||||
|  | ||||
|     #[ORM\Column(type: Types::JSON)] | ||||
|     private array $name; | ||||
|  | ||||
|     /** | ||||
|      * @var Collection<int, EventTheme> | ||||
|      */ | ||||
|     #[ORM\OneToMany(mappedBy: 'parent', targetEntity: EventTheme::class)] | ||||
|     private Collection $children; | ||||
|  | ||||
|     #[ORM\ManyToOne(targetEntity: EventTheme::class, inversedBy: 'children')] | ||||
|     private ?EventTheme $parent = null; | ||||
|  | ||||
|     #[ORM\Column(name: 'ordering', type: Types::FLOAT, options: ['default' => '0.0'])] | ||||
|     private float $ordering = 0.0; | ||||
|  | ||||
|     /** | ||||
|      * Constructor. | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->children = new ArrayCollection(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get active. | ||||
|      */ | ||||
|     public function getIsActive(): bool | ||||
|     { | ||||
|         return $this->isActive; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get id. | ||||
|      */ | ||||
|     public function getId(): ?int | ||||
|     { | ||||
|         return $this->id; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get label. | ||||
|      */ | ||||
|     public function getName(): array | ||||
|     { | ||||
|         return $this->name; | ||||
|     } | ||||
|  | ||||
|     public function setIsActive(bool $active): self | ||||
|     { | ||||
|         $this->isActive = $active; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function setName(array $label): self | ||||
|     { | ||||
|         $this->name = $label; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function addChild(self $child): self | ||||
|     { | ||||
|         if (!$this->children->contains($child)) { | ||||
|             $this->children[] = $child; | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function removeChild(self $child): self | ||||
|     { | ||||
|         if ($this->children->removeElement($child)) { | ||||
|             // set the owning side to null (unless already changed) | ||||
|             if ($child->getParent() === $this) { | ||||
|                 $child->setParent(null); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function getChildren(): Collection | ||||
|     { | ||||
|         return $this->children; | ||||
|     } | ||||
|  | ||||
|     public function hasChildren(): bool | ||||
|     { | ||||
|         return 0 < $this->getChildren()->count(); | ||||
|     } | ||||
|  | ||||
|     public function hasParent(): bool | ||||
|     { | ||||
|         return null !== $this->parent; | ||||
|     } | ||||
|  | ||||
|     public function getOrdering(): float | ||||
|     { | ||||
|         return $this->ordering; | ||||
|     } | ||||
|  | ||||
|     public function setOrdering(float $ordering): EventTheme | ||||
|     { | ||||
|         $this->ordering = $ordering; | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function getParent(): ?self | ||||
|     { | ||||
|         return $this->parent; | ||||
|     } | ||||
|  | ||||
|     public function setParent(?self $parent): self | ||||
|     { | ||||
|         $this->parent = $parent; | ||||
|  | ||||
|         $parent?->addChild($this); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| @@ -1,377 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Export\Export; | ||||
|  | ||||
| use Chill\EventBundle\Entity\BudgetTypeEnum; | ||||
| use Chill\EventBundle\Entity\Event; | ||||
| use Chill\EventBundle\Export\Declarations; | ||||
| use Chill\EventBundle\Repository\EventBudgetElementRepository; | ||||
| use Chill\EventBundle\Repository\EventThemeRepository; | ||||
| use Chill\EventBundle\Security\EventVoter; | ||||
| use Chill\EventBundle\Templating\Entity\EventThemeRender; | ||||
| use Chill\MainBundle\Export\ExportGenerationContext; | ||||
| use Chill\MainBundle\Export\FormatterInterface; | ||||
| use Chill\MainBundle\Export\GroupedExportInterface; | ||||
| use Chill\MainBundle\Export\ListInterface; | ||||
| use Chill\MainBundle\Templating\TranslatableStringHelperInterface; | ||||
| use Chill\ThirdPartyBundle\Export\Helper\LabelThirdPartyHelper; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Doctrine\ORM\NativeQuery; | ||||
| use Doctrine\ORM\Query; | ||||
| use Doctrine\ORM\QueryBuilder; | ||||
| use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; | ||||
| use Symfony\Component\Form\Extension\Core\Type\ChoiceType; | ||||
| use Symfony\Component\Form\FormBuilderInterface; | ||||
| use Symfony\Component\Validator\Constraints\Callback; | ||||
| use Symfony\Component\Validator\Context\ExecutionContextInterface; | ||||
| use Symfony\Contracts\Translation\TranslatableInterface; | ||||
|  | ||||
| /** | ||||
|  * Render a list of events. | ||||
|  */ | ||||
| class ListEvents implements ListInterface, GroupedExportInterface | ||||
| { | ||||
|     protected array $fields = [ | ||||
|         'event_id', | ||||
|         'event_center', | ||||
|         'event_name', | ||||
|         'event_date', | ||||
|         'event_location', | ||||
|         'event_type', | ||||
|         'event_themes', | ||||
|         'event_moderator', | ||||
|         'event_animators', | ||||
|         'event_participants_count', | ||||
|         'event_budget_resources', | ||||
|         'event_budget_charges', | ||||
|     ]; | ||||
|  | ||||
|     private readonly bool $filterStatsByCenters; | ||||
|  | ||||
|     public function __construct( | ||||
|         protected readonly EntityManagerInterface $entityManager, | ||||
|         ParameterBagInterface $parameterBag, | ||||
|         protected readonly TranslatableStringHelperInterface $translatableStringHelper, | ||||
|         protected readonly EventThemeRender $eventThemeRender, | ||||
|         protected readonly EventThemeRepository $eventThemeRepository, | ||||
|         protected readonly LabelThirdPartyHelper $labelThirdPartyHelper, | ||||
|         protected readonly EventBudgetElementRepository $eventBudgetElementRepository, | ||||
|     ) { | ||||
|         $this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center']; | ||||
|     } | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder): void | ||||
|     { | ||||
|         $builder | ||||
|             ->add('fields', ChoiceType::class, [ | ||||
|                 'multiple' => true, | ||||
|                 'expanded' => true, | ||||
|                 'choices' => array_combine($this->fields, $this->fields), | ||||
|                 'label' => 'Fields to include in export', | ||||
|                 'constraints' => [new Callback([ | ||||
|                     'callback' => static function ($selected, ExecutionContextInterface $context) { | ||||
|                         if (0 === \count($selected)) { | ||||
|                             $context->buildViolation('You must select at least one element') | ||||
|                                 ->atPath('fields') | ||||
|                                 ->addViolation(); | ||||
|                         } | ||||
|                     }, | ||||
|                 ])], | ||||
|             ]); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public function getFormDefaultData(): array | ||||
|     { | ||||
|         return [ | ||||
|             'fields' => $this->fields, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function getAllowedFormattersTypes(): array | ||||
|     { | ||||
|         return [FormatterInterface::TYPE_LIST]; | ||||
|     } | ||||
|  | ||||
|     public function getDescription(): string | ||||
|     { | ||||
|         return 'export.event.list.description'; | ||||
|     } | ||||
|  | ||||
|     public function getGroup(): string | ||||
|     { | ||||
|         return 'Exports of events'; | ||||
|     } | ||||
|  | ||||
|     public function getLabels($key, array $values, $data) | ||||
|     { | ||||
|         return match ($key) { | ||||
|             'event_id' => fn ($value) => '_header' === $value ? $key : $value, | ||||
|             'event_name' => fn ($value) => '_header' === $value ? $key : $value, | ||||
|             'event_date' => function ($value) use ($key) { | ||||
|                 if ('_header' === $value) { | ||||
|                     return $key; | ||||
|                 } | ||||
|  | ||||
|                 if ($value instanceof \DateTime) { | ||||
|                     return $value->format('Y-m-d'); | ||||
|                 } | ||||
|  | ||||
|                 $date = \DateTime::createFromFormat('Y-m-d H:i:s', $value); | ||||
|  | ||||
|                 return $date ? $date->format('Y-m-d') : $value; | ||||
|             }, | ||||
|             'event_type' => function ($value) use ($key) { | ||||
|                 if ('_header' === $value) { | ||||
|                     return 'export.event.list.'.$key; | ||||
|                 } | ||||
|  | ||||
|                 return $this->translatableStringHelper->localize(json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR)); | ||||
|             }, | ||||
|             'event_center' => fn ($value) => '_header' === $value ? $key : $value, | ||||
|             'event_moderator' => fn ($value) => '_header' === $value ? $key : $value, | ||||
|             'event_participants_count' => fn ($value) => '_header' === $value ? $key : $value, | ||||
|             'event_location' => fn ($value) => '_header' === $value ? $key : $value, | ||||
|             'event_animators' => $this->labelThirdPartyHelper->getLabelMulti($key, $values, $key), | ||||
|             'event_themes' => function ($value) use ($key) { | ||||
|                 if ('_header' === $value) { | ||||
|                     return $key; | ||||
|                 } | ||||
|  | ||||
|                 if (null === $value) { | ||||
|                     return ''; | ||||
|                 } | ||||
|  | ||||
|                 return implode( | ||||
|                     '|', | ||||
|                     array_map( | ||||
|                         fn ($t) => $this->eventThemeRender->renderString($this->eventThemeRepository->find($t), []), | ||||
|                         json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR) | ||||
|                     ) | ||||
|                 ); | ||||
|             }, | ||||
|             'event_budget_resources' => function ($value) use ($key) { | ||||
|                 if ('_header' === $value) { | ||||
|                     return $key; | ||||
|                 } | ||||
|  | ||||
|                 if (!$value) { | ||||
|                     return ''; | ||||
|                 } | ||||
|  | ||||
|                 $ids = explode(',', $value); | ||||
|                 $ids = json_decode($value, true, 512, JSON_THROW_ON_ERROR); | ||||
|                 $elements = $this->eventBudgetElementRepository->findBy(['id' => $ids]); | ||||
|  | ||||
|                 return implode('|', array_map(function ($element) { | ||||
|                     $name = $this->translatableStringHelper->localize($element->getKind()->getName()); | ||||
|                     $amount = number_format($element->getAmount(), 2, '.', ''); | ||||
|  | ||||
|                     return $name.': '.$amount; | ||||
|                 }, $elements)); | ||||
|             }, | ||||
|  | ||||
|             'event_budget_charges' => function ($value) use ($key) { | ||||
|                 if ('_header' === $value) { | ||||
|                     return $key; | ||||
|                 } | ||||
|  | ||||
|                 if (!$value) { | ||||
|                     return ''; | ||||
|                 } | ||||
|  | ||||
|                 $ids = explode(',', $value); | ||||
|                 $ids = json_decode($value, true, 512, JSON_THROW_ON_ERROR); | ||||
|  | ||||
|                 $elements = $this->eventBudgetElementRepository->findBy(['id' => $ids]); | ||||
|  | ||||
|                 return implode('|', array_map(function ($element) { | ||||
|                     $name = $this->translatableStringHelper->localize($element->getKind()->getName()); | ||||
|                     $amount = number_format($element->getAmount(), 2, '.', ''); | ||||
|  | ||||
|                     return $name.': '.$amount; | ||||
|                 }, $elements)); | ||||
|             }, | ||||
|  | ||||
|  | ||||
|             default => fn ($value) => '_header' === $value ? $key : $value, | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     public function getQueryKeys(array $data): array | ||||
|     { | ||||
|         return $data['fields']; | ||||
|     } | ||||
|  | ||||
|     public function getResult($query, $data, ExportGenerationContext $context): array | ||||
|     { | ||||
|         return $query->getQuery()->getResult(Query::HYDRATE_SCALAR); | ||||
|     } | ||||
|  | ||||
|     public function getTitle(): string|TranslatableInterface | ||||
|     { | ||||
|         return 'export.event.list.title'; | ||||
|     } | ||||
|  | ||||
|     public function getType(): string | ||||
|     { | ||||
|         return Declarations::EVENT; | ||||
|     } | ||||
|  | ||||
|     public function initiateQuery(array $requiredModifiers, array $acl, array $data, ExportGenerationContext $context): NativeQuery|QueryBuilder | ||||
|     { | ||||
|         $centers = array_map(static fn ($el) => $el['center'], $acl); | ||||
|  | ||||
|         // Throw an error if no fields are present | ||||
|         if (!\array_key_exists('fields', $data)) { | ||||
|             throw new \InvalidArgumentException('No fields have been checked.'); | ||||
|         } | ||||
|  | ||||
|         $qb = $this->entityManager->createQueryBuilder() | ||||
|             ->from(Event::class, 'event'); | ||||
|  | ||||
|         if ($this->filterStatsByCenters) { | ||||
|             $qb | ||||
|                 ->andWhere('event.center IN (:authorized_centers)') | ||||
|                 ->setParameter('authorized_centers', $centers); | ||||
|         } | ||||
|  | ||||
|         // Add fields based on selection | ||||
|         foreach ($this->fields as $field) { | ||||
|             if (\in_array($field, $data['fields'], true)) { | ||||
|                 switch ($field) { | ||||
|                     case 'event_id': | ||||
|                         $qb->addSelect('event.id AS event_id'); | ||||
|                         break; | ||||
|  | ||||
|                     case 'event_name': | ||||
|                         $qb->addSelect('event.name AS event_name'); | ||||
|                         break; | ||||
|  | ||||
|                     case 'event_date': | ||||
|                         $qb->addSelect('event.date AS event_date'); | ||||
|                         break; | ||||
|  | ||||
|                     case 'event_type': | ||||
|                         if (!$this->hasJoin($qb, 'event.type')) { | ||||
|                             $qb->leftJoin('event.type', 'type'); | ||||
|                         } | ||||
|                         $qb->addSelect('type.name AS event_type'); | ||||
|                         break; | ||||
|  | ||||
|                     case 'event_center': | ||||
|                         if (!$this->hasJoin($qb, 'event.center')) { | ||||
|                             $qb->leftJoin('event.center', 'center'); | ||||
|                         } | ||||
|                         $qb->addSelect('center.name AS event_center'); | ||||
|                         break; | ||||
|  | ||||
|                     case 'event_moderator': | ||||
|                         if (!$this->hasJoin($qb, 'event.moderator')) { | ||||
|                             $qb->leftJoin('event.moderator', 'user'); | ||||
|                         } | ||||
|                         $qb->addSelect('user.username AS event_moderator'); | ||||
|                         break; | ||||
|  | ||||
|                     case 'event_participants_count': | ||||
|                         $qb->addSelect('(SELECT COUNT(p.id) FROM Chill\EventBundle\Entity\Participation p WHERE p.event = event.id) AS event_participants_count'); | ||||
|                         break; | ||||
|  | ||||
|                     case 'event_location': | ||||
|                         if (!$this->hasJoin($qb, 'event.location')) { | ||||
|                             $qb->leftJoin('event.location', 'location'); | ||||
|                         } | ||||
|                         $qb->addSelect('location.name AS event_location'); | ||||
|                         break; | ||||
|  | ||||
|                     case 'event_animators': | ||||
|                         $qb->addSelect( | ||||
|                             '(SELECT AGGREGATE(tp.id) FROM Chill\ThirdPartyBundle\Entity\ThirdParty tp WHERE tp MEMBER OF event.animators) AS event_animators' | ||||
|                         ); | ||||
|                         break; | ||||
|  | ||||
|                     case 'event_themes': | ||||
|                         $qb->addSelect( | ||||
|                             '(SELECT AGGREGATE(t.id) FROM Chill\EventBundle\Entity\EventTheme t WHERE t MEMBER OF event.themes) AS event_themes' | ||||
|                         ); | ||||
|                         break; | ||||
|  | ||||
|                     case 'event_budget_resources': | ||||
|                         $qb->addSelect( | ||||
|                             '(SELECT AGGREGATE(ebr.id) | ||||
|                           FROM Chill\EventBundle\Entity\EventBudgetElement ebr | ||||
|                           JOIN ebr.kind kr | ||||
|                           WHERE ebr.event = event.id AND kr.type = :resource_type) AS event_budget_resources' | ||||
|                         ); | ||||
|                         $qb->setParameter('resource_type', BudgetTypeEnum::RESOURCE->value); | ||||
|                         break; | ||||
|  | ||||
|                     case 'event_budget_charges': | ||||
|                         $qb->addSelect( | ||||
|                             '(SELECT AGGREGATE(ebc.id) | ||||
|                           FROM Chill\EventBundle\Entity\EventBudgetElement ebc | ||||
|                           JOIN ebc.kind kc | ||||
|                           WHERE ebc.event = event.id AND kc.type = :charge_type) AS event_budget_charges' | ||||
|                         ); | ||||
|                         $qb->setParameter('charge_type', BudgetTypeEnum::CHARGE->value); | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $qb; | ||||
|     } | ||||
|  | ||||
|     public function requiredRole(): string | ||||
|     { | ||||
|         return EventVoter::STATS; | ||||
|     } | ||||
|  | ||||
|     public function supportsModifiers() | ||||
|     { | ||||
|         return [Declarations::EVENT]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Helper method to check if a join already exists in the QueryBuilder. | ||||
|      */ | ||||
|     private function hasJoin($queryBuilder, $joinPath): bool | ||||
|     { | ||||
|         $joins = $queryBuilder->getDQLPart('join'); | ||||
|         if (!isset($joins['event'])) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         foreach ($joins['event'] as $join) { | ||||
|             if ($join->getJoin() === $joinPath) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public function normalizeFormData(array $formData): array | ||||
|     { | ||||
|         return ['fields' => $formData['fields']]; | ||||
|     } | ||||
|  | ||||
|     public function denormalizeFormData(array $formData, int $fromVersion): array | ||||
|     { | ||||
|         return ['fields' => $formData['fields']]; | ||||
|     } | ||||
|  | ||||
|     public function getNormalizationVersion(): int | ||||
|     { | ||||
|         return 1; | ||||
|     } | ||||
| } | ||||
| @@ -1,59 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Form; | ||||
|  | ||||
| use Chill\EventBundle\Entity\BudgetTypeEnum; | ||||
| use Chill\EventBundle\Entity\EventBudgetElement; | ||||
| use Chill\EventBundle\Entity\EventBudgetKind; | ||||
| use Chill\EventBundle\Repository\EventBudgetKindRepository; | ||||
| use Chill\MainBundle\Form\Type\CommentType; | ||||
| use Chill\MainBundle\Templating\TranslatableStringHelperInterface; | ||||
| use Symfony\Component\Form\AbstractType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\ChoiceType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\NumberType; | ||||
| use Symfony\Component\Form\FormBuilderInterface; | ||||
| use Symfony\Component\OptionsResolver\OptionsResolver; | ||||
|  | ||||
| class AddEventBudgetElementType extends AbstractType | ||||
| { | ||||
|     public function __construct(private readonly EventBudgetKindRepository $kindRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper) {} | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder, array $options): void | ||||
|     { | ||||
|         $charges = $this->kindRepository->findByType(BudgetTypeEnum::CHARGE->value); | ||||
|         $resources = $this->kindRepository->findByType(BudgetTypeEnum::RESOURCE->value); | ||||
|  | ||||
|         $builder->add('kind', ChoiceType::class, [ | ||||
|             'choices' => [ | ||||
|                 'event.budget.charges' => $charges, | ||||
|                 'event.budget.resources' => $resources, | ||||
|             ], | ||||
|             'choice_label' => fn (EventBudgetKind $kind) => $this->translatableStringHelper->localize($kind->getName()), | ||||
|             'choice_value' => fn (?EventBudgetKind $kind) => $kind?->getId(), | ||||
|             'placeholder' => 'event.budget.Select a budget element kind', | ||||
|         ]) | ||||
|             ->add('amount', NumberType::class, [ | ||||
|                 'required' => true, | ||||
|             ]) | ||||
|             ->add('comment', CommentType::class, [ | ||||
|                 'label' => 'Comment', | ||||
|                 'required' => false, | ||||
|             ]); | ||||
|     } | ||||
|  | ||||
|     public function configureOptions(OptionsResolver $resolver): void | ||||
|     { | ||||
|         $resolver->setDefaults([ | ||||
|             'data_class' => EventBudgetElement::class, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @@ -1,53 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Form; | ||||
|  | ||||
| use Chill\EventBundle\Entity\BudgetTypeEnum; | ||||
| use Chill\EventBundle\Entity\EventBudgetKind; | ||||
| use Chill\MainBundle\Form\Type\TranslatableStringFormType; | ||||
| use Symfony\Component\Form\AbstractType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\CheckboxType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\EnumType; | ||||
| use Symfony\Component\Form\FormBuilderInterface; | ||||
| use Symfony\Component\OptionsResolver\OptionsResolver; | ||||
| use Symfony\Contracts\Translation\TranslatorInterface; | ||||
|  | ||||
| class EventBudgetKindType extends AbstractType | ||||
| { | ||||
|     public function __construct(private readonly TranslatorInterface $translator) {} | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder, array $options) | ||||
|     { | ||||
|         $builder | ||||
|             ->add('name', TranslatableStringFormType::class, [ | ||||
|                 'label' => 'Title', | ||||
|             ]) | ||||
|             ->add('type', EnumType::class, [ | ||||
|                 'class' => BudgetTypeEnum::class, | ||||
|                 'choice_label' => fn (BudgetTypeEnum $type): string => $this->translator->trans($type->value), | ||||
|                 'expanded' => true, | ||||
|                 'multiple' => false, | ||||
|                 'mapped' => true, | ||||
|                 'label' => 'event.admin.Select budget type', | ||||
|             ]) | ||||
|             ->add('isActive', CheckboxType::class, [ | ||||
|                 'label' => 'Actif ?', | ||||
|                 'required' => false, | ||||
|             ]); | ||||
|     } | ||||
|  | ||||
|     public function configureOptions(OptionsResolver $resolver) | ||||
|     { | ||||
|         $resolver | ||||
|             ->setDefault('class', EventBudgetKind::class); | ||||
|     } | ||||
| } | ||||
| @@ -1,67 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Form; | ||||
|  | ||||
| use Chill\EventBundle\Entity\EventTheme; | ||||
| use Chill\MainBundle\Form\Type\TranslatableStringFormType; | ||||
| use Chill\MainBundle\Templating\TranslatableStringHelperInterface; | ||||
| use Symfony\Bridge\Doctrine\Form\Type\EntityType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\ChoiceType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\NumberType; | ||||
| use Symfony\Component\Form\FormBuilderInterface; | ||||
| use Symfony\Component\OptionsResolver\OptionsResolver; | ||||
| use Symfony\Component\Form\AbstractType; | ||||
|  | ||||
| class EventThemeType extends AbstractType | ||||
| { | ||||
|     public function __construct(protected TranslatableStringHelperInterface $translatableStringHelper) {} | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder, array $options): void | ||||
|     { | ||||
|         $builder | ||||
|             ->add('name', TranslatableStringFormType::class, [ | ||||
|                 'label' => 'Nom', | ||||
|             ]); | ||||
|  | ||||
|         if ('create' === $options['step']) { | ||||
|             $builder | ||||
|                 ->add('parent', EntityType::class, [ | ||||
|                     'class' => EventTheme::class, | ||||
|                     'required' => false, | ||||
|                     'choice_label' => fn (EventTheme $theme): ?string => $this->translatableStringHelper->localize($theme->getName()), | ||||
|                     'mapped' => 'create' == $options['step'], | ||||
|                 ]); | ||||
|         } | ||||
|  | ||||
|         $builder | ||||
|             ->add('ordering', NumberType::class, [ | ||||
|                 'required' => true, | ||||
|                 'scale' => 6, | ||||
|             ]) | ||||
|             ->add('isActive', ChoiceType::class, [ | ||||
|                 'choices' => [ | ||||
|                     'Yes' => true, | ||||
|                     'No' => false, | ||||
|                 ], | ||||
|                 'expanded' => true, | ||||
|             ]); | ||||
|     } | ||||
|  | ||||
|     public function configureOptions(OptionsResolver $resolver): void | ||||
|     { | ||||
|         $resolver->setDefaults([ | ||||
|             'data_class' => EventTheme::class, | ||||
|         ]); | ||||
|         $resolver->setRequired('step') | ||||
|             ->setAllowedValues('step', ['create', 'edit']); | ||||
|     } | ||||
| } | ||||
| @@ -13,39 +13,25 @@ namespace Chill\EventBundle\Form; | ||||
|  | ||||
| use Chill\DocStoreBundle\Entity\StoredObject; | ||||
| use Chill\DocStoreBundle\Form\StoredObjectType; | ||||
| use Chill\EventBundle\Entity\BudgetTypeEnum; | ||||
| use Chill\EventBundle\Entity\Event; | ||||
| use Chill\EventBundle\Form\Type\PickEventThemeType; | ||||
| use Chill\EventBundle\Form\Type\PickEventTypeType; | ||||
| use Chill\EventBundle\Repository\EventBudgetKindRepository; | ||||
| use Chill\MainBundle\Entity\Center; | ||||
| use Chill\MainBundle\Form\DataTransformer\IdToLocationDataTransformer; | ||||
| use Chill\MainBundle\Form\Type\ChillCollectionType; | ||||
| use Chill\MainBundle\Form\Type\ChillDateTimeType; | ||||
| use Chill\MainBundle\Form\Type\CommentType; | ||||
| use Chill\MainBundle\Form\Type\PickUserDynamicType; | ||||
| use Chill\MainBundle\Form\Type\PickUserLocationType; | ||||
| use Chill\MainBundle\Form\Type\ScopePickerType; | ||||
| use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType; | ||||
| use Symfony\Component\Form\AbstractType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\HiddenType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\MoneyType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\TextType; | ||||
| use Symfony\Component\Form\FormBuilderInterface; | ||||
| use Symfony\Component\OptionsResolver\OptionsResolver; | ||||
| use Symfony\Contracts\Translation\TranslatorInterface; | ||||
|  | ||||
| class EventType extends AbstractType | ||||
| { | ||||
|     public function __construct( | ||||
|         private readonly IdToLocationDataTransformer $idToLocationDataTransformer, | ||||
|         private readonly EventBudgetKindRepository $eventBudgetKindRepository, | ||||
|         private readonly TranslatorInterface $translator, | ||||
|     ) {} | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder, array $options): void | ||||
|     public function buildForm(FormBuilderInterface $builder, array $options) | ||||
|     { | ||||
|         $chargeKinds = $this->eventBudgetKindRepository->findByType(BudgetTypeEnum::CHARGE->value); | ||||
|         $resourceKinds = $this->eventBudgetKindRepository->findByType(BudgetTypeEnum::RESOURCE->value); | ||||
|  | ||||
|         $builder | ||||
|             ->add('name', TextType::class, [ | ||||
|                 'required' => true, | ||||
| @@ -63,28 +49,11 @@ class EventType extends AbstractType | ||||
|                     'class' => '', | ||||
|                 ], | ||||
|             ]) | ||||
|             ->add('themes', PickEventThemeType::class, [ | ||||
|                 'multiple' => true, | ||||
|             ]) | ||||
|             ->add('moderator', PickUserDynamicType::class, [ | ||||
|                 'label' => 'Pick a moderator', | ||||
|             ]) | ||||
|             ->add('animatorsIntern', PickUserDynamicType::class, [ | ||||
|                 'multiple' => true, | ||||
|                 'label' => $this->translator->trans('event.fields.internal animators'), | ||||
|                 'required' => false, | ||||
|             ]) | ||||
|             ->add('animatorsExtern', PickThirdpartyDynamicType::class, [ | ||||
|                 'multiple' => true, | ||||
|                 'label' => $this->translator->trans('event.fields.external animators'), | ||||
|                 'required' => false, | ||||
|             ]) | ||||
|             ->add('budgetElements', ChillCollectionType::class, [ | ||||
|                 'entry_type' => AddEventBudgetElementType::class, | ||||
|                 'entry_options' => ['label' => false], | ||||
|                 'allow_add' => true, | ||||
|                 'allow_delete' => true, | ||||
|                 'by_reference' => false, | ||||
|             ->add('location', PickUserLocationType::class, [ | ||||
|                 'label' => 'event.fields.location', | ||||
|             ]) | ||||
|             ->add('comment', CommentType::class, [ | ||||
|                 'label' => 'Comment', | ||||
| @@ -100,11 +69,11 @@ class EventType extends AbstractType | ||||
|                 'delete_empty' => fn (StoredObject $storedObject): bool => '' === $storedObject->getFilename(), | ||||
|                 'button_remove_label' => 'event.form.remove_document', | ||||
|                 'button_add_label' => 'event.form.add_document', | ||||
|             ]) | ||||
|             ->add('organizationCost', MoneyType::class, [ | ||||
|                 'label' => 'event.fields.organizationCost', | ||||
|                 'help' => 'event.form.organisationCost_help', | ||||
|             ]); | ||||
|  | ||||
|         $builder->add('location', HiddenType::class) | ||||
|             ->get('location') | ||||
|             ->addModelTransformer($this->idToLocationDataTransformer); | ||||
|     } | ||||
|  | ||||
|     public function configureOptions(OptionsResolver $resolver) | ||||
| @@ -118,9 +87,11 @@ class EventType extends AbstractType | ||||
|             ->setAllowedTypes('role', 'string'); | ||||
|     } | ||||
|  | ||||
|     public function getBlockPrefix(): string | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getBlockPrefix() | ||||
|     { | ||||
|         // as the js shares some hardcoded items from the activity bundle, we have to rewrite block prefix | ||||
|         return 'chill_activitybundle_activity'; | ||||
|         return 'chill_eventbundle_event'; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,45 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Form\Type; | ||||
|  | ||||
| use Chill\EventBundle\Entity\EventTheme; | ||||
| use Chill\EventBundle\Repository\EventThemeRepository; | ||||
| use Chill\EventBundle\Templating\Entity\EventThemeRender; | ||||
| use Symfony\Bridge\Doctrine\Form\Type\EntityType; | ||||
| use Symfony\Component\Form\AbstractType; | ||||
| use Symfony\Component\OptionsResolver\OptionsResolver; | ||||
|  | ||||
| class PickEventThemeType extends AbstractType | ||||
| { | ||||
|     public function __construct(private readonly EventThemeRender $eventThemeRender, private readonly EventThemeRepository $eventThemeRepository) {} | ||||
|  | ||||
|     public function configureOptions(OptionsResolver $resolver) | ||||
|     { | ||||
|         $resolver | ||||
|             ->setDefaults([ | ||||
|                 'class' => EventTheme::class, | ||||
|                 'choices' => $this->eventThemeRepository->findByActiveOrdered(), | ||||
|                 'choice_label' => fn (EventTheme $et) => $this->eventThemeRender->renderString($et, []), | ||||
|                 'placeholder' => 'event.form.Select one or more themes', | ||||
|                 'required' => true, | ||||
|                 'attr' => ['class' => 'select2'], | ||||
|                 'label' => 'event.theme.label', | ||||
|                 'multiple' => false, | ||||
|             ]) | ||||
|             ->setAllowedTypes('multiple', ['bool']); | ||||
|     } | ||||
|  | ||||
|     public function getParent(): string | ||||
|     { | ||||
|         return EntityType::class; | ||||
|     } | ||||
| } | ||||
| @@ -23,7 +23,15 @@ use Symfony\Component\OptionsResolver\OptionsResolver; | ||||
|  */ | ||||
| class PickEventTypeType extends AbstractType | ||||
| { | ||||
|     public function __construct(protected TranslatableStringHelper $translatableStringHelper) {} | ||||
|     /** | ||||
|      * @var TranslatableStringHelper | ||||
|      */ | ||||
|     protected $translatableStringHelper; | ||||
|  | ||||
|     public function __construct(TranslatableStringHelper $helper) | ||||
|     { | ||||
|         $this->translatableStringHelper = $helper; | ||||
|     } | ||||
|  | ||||
|     public function configureOptions(OptionsResolver $resolver) | ||||
|     { | ||||
|   | ||||
| @@ -17,9 +17,17 @@ use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; | ||||
|  | ||||
| class AdminMenuBuilder implements LocalMenuBuilderInterface | ||||
| { | ||||
|     public function __construct(protected AuthorizationCheckerInterface $authorizationChecker) {} | ||||
|     /** | ||||
|      * @var AuthorizationCheckerInterface | ||||
|      */ | ||||
|     protected $authorizationChecker; | ||||
|  | ||||
|     public function buildMenu($menuId, MenuItem $menu, array $parameters): void | ||||
|     public function __construct(AuthorizationCheckerInterface $authorizationChecker) | ||||
|     { | ||||
|         $this->authorizationChecker = $authorizationChecker; | ||||
|     } | ||||
|  | ||||
|     public function buildMenu($menuId, MenuItem $menu, array $parameters) | ||||
|     { | ||||
|         if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) { | ||||
|             return; | ||||
| @@ -44,14 +52,6 @@ class AdminMenuBuilder implements LocalMenuBuilderInterface | ||||
|         $menu->addChild('Role', [ | ||||
|             'route' => 'chill_event_admin_role', | ||||
|         ])->setExtras(['order' => 6530]); | ||||
|  | ||||
|         $menu->addChild('event.theme.label', [ | ||||
|             'route' => 'chill_crud_event_theme_index', | ||||
|         ])->setExtras(['order' => 6540]); | ||||
|  | ||||
|         $menu->addChild('event.budget.label', [ | ||||
|             'route' => 'chill_crud_event_budget_kind_index', | ||||
|         ])->setExtras(['order' => 6550]); | ||||
|     } | ||||
|  | ||||
|     public static function getMenuIds(): array | ||||
|   | ||||
| @@ -88,16 +88,6 @@ final readonly class EventACLAwareRepository implements EventACLAwareRepositoryI | ||||
|             $qb->andWhere('event.type IN (:event_types)'); | ||||
|             $qb->setParameter('event_types', $filters['event_types']); | ||||
|         } | ||||
|  | ||||
|         if (0 < count($filters['centers'] ?? [])) { | ||||
|             $qb->andWhere('event.center IN (:centers)'); | ||||
|             $qb->setParameter('centers', $filters['centers']); | ||||
|         } | ||||
|  | ||||
|         if (0 < count($filters['responsables'] ?? [])) { | ||||
|             $qb->andWhere('event.moderator IN (:responsables)'); | ||||
|             $qb->setParameter('responsables', $filters['responsables']); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function buildQueryByAllViewable(array $filters): QueryBuilder | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Repository; | ||||
|  | ||||
| use Chill\EventBundle\Entity\EventBudgetElement; | ||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | ||||
| use Doctrine\Persistence\ManagerRegistry; | ||||
|  | ||||
| /** | ||||
|  * @extends ServiceEntityRepository<EventBudgetElement> | ||||
|  */ | ||||
| class EventBudgetElementRepository extends ServiceEntityRepository | ||||
| { | ||||
|     public function __construct(ManagerRegistry $registry) | ||||
|     { | ||||
|         parent::__construct($registry, EventBudgetElement::class); | ||||
|     } | ||||
| } | ||||
| @@ -1,46 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Repository; | ||||
|  | ||||
| use Chill\EventBundle\Entity\EventBudgetKind; | ||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | ||||
| use Doctrine\Persistence\ManagerRegistry; | ||||
|  | ||||
| /** | ||||
|  * @extends ServiceEntityRepository<EventBudgetKind> | ||||
|  */ | ||||
| class EventBudgetKindRepository extends ServiceEntityRepository | ||||
| { | ||||
|     public function __construct(ManagerRegistry $registry) | ||||
|     { | ||||
|         parent::__construct($registry, EventBudgetKind::class); | ||||
|     } | ||||
|  | ||||
|     public function findByActive(): array | ||||
|     { | ||||
|         return $this->createQueryBuilder('e') | ||||
|             ->select('e') | ||||
|             ->where('e.active = True') | ||||
|             ->getQuery() | ||||
|             ->getResult(); | ||||
|     } | ||||
|  | ||||
|     public function findByType(string $type): array | ||||
|     { | ||||
|         return $this->createQueryBuilder('e') | ||||
|             ->select('e') | ||||
|             ->where('e.type = :type') | ||||
|             ->setParameter('type', $type) | ||||
|             ->getQuery() | ||||
|             ->getResult(); | ||||
|     } | ||||
| } | ||||
| @@ -1,37 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Repository; | ||||
|  | ||||
| use Chill\EventBundle\Entity\EventTheme; | ||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | ||||
| use Doctrine\Persistence\ManagerRegistry; | ||||
|  | ||||
| /** | ||||
|  * @extends ServiceEntityRepository<EventTheme> | ||||
|  */ | ||||
| class EventThemeRepository extends ServiceEntityRepository | ||||
| { | ||||
|     public function __construct(ManagerRegistry $registry) | ||||
|     { | ||||
|         parent::__construct($registry, EventTheme::class); | ||||
|     } | ||||
|  | ||||
|     public function findByActiveOrdered(): array | ||||
|     { | ||||
|         return $this->createQueryBuilder('t') | ||||
|             ->select('t') | ||||
|             ->where('t.isActive = True') | ||||
|             ->orderBy('t.ordering', 'ASC') | ||||
|             ->getQuery() | ||||
|             ->getResult(); | ||||
|     } | ||||
| } | ||||
| @@ -55,13 +55,3 @@ form#export_tableur { | ||||
|     -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; | ||||
|     transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; | ||||
| } | ||||
|  | ||||
| .participation-list { | ||||
|     flex-wrap: wrap; | ||||
| } | ||||
|  | ||||
| .participations-wrapper { | ||||
|     background-color: whitesmoke; | ||||
|     padding: 1rem; | ||||
|     margin-bottom: .5rem; | ||||
| } | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| <template> | ||||
|     <location /> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import Location from "ChillActivityAssets/vuejs/Activity/components/Location.vue"; | ||||
|  | ||||
| export default { | ||||
|     name: "App", | ||||
|     components: { | ||||
|         Location, | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
| @@ -1,6 +0,0 @@ | ||||
| import { createApp } from "vue"; | ||||
|  | ||||
| import App from "./App.vue"; | ||||
| import store from "./store"; | ||||
|  | ||||
| createApp(App).use(store).mount("#event"); | ||||
| @@ -1,76 +0,0 @@ | ||||
| import "es6-promise/auto"; | ||||
| import { createStore } from "vuex"; | ||||
|  | ||||
| import prepareLocations from "ChillActivityAssets/vuejs/Activity/store.locations"; | ||||
| import { whoami } from "ChillMainAssets/lib/api/user"; | ||||
| import { postLocation } from "ChillActivityAssets/vuejs/Activity/api"; | ||||
|  | ||||
| const debug = process.env.NODE_ENV !== "production"; | ||||
|  | ||||
| const store = createStore({ | ||||
|   strict: debug, | ||||
|   state: { | ||||
|     activity: window.entity, // activity is the event entity in this case (re-using component from activity bundle) | ||||
|     currentEvent: null, | ||||
|     availableLocations: [], | ||||
|     me: null, | ||||
|   }, | ||||
|   getters: {}, | ||||
|   actions: { | ||||
|     addAvailableLocationGroup({ commit }, payload) { | ||||
|       commit("addAvailableLocationGroup", payload); | ||||
|     }, | ||||
|     updateLocation({ commit }, value) { | ||||
|       // console.log("### action: updateLocation", value); | ||||
|       let hiddenLocation = document.getElementById( | ||||
|         "chill_activitybundle_activity_location", | ||||
|       ); | ||||
|       if (value.onthefly) { | ||||
|         const body = { | ||||
|           type: "location", | ||||
|           name: | ||||
|             value.name === "__AccompanyingCourseLocation__" ? null : value.name, | ||||
|           locationType: { | ||||
|             id: value.locationType.id, | ||||
|             type: "location-type", | ||||
|           }, | ||||
|         }; | ||||
|         if (value.address.id) { | ||||
|           Object.assign(body, { | ||||
|             address: { | ||||
|               id: value.address.id, | ||||
|             }, | ||||
|           }); | ||||
|         } | ||||
|         postLocation(body) | ||||
|           .then((location) => (hiddenLocation.value = location.id)) | ||||
|           .catch((err) => { | ||||
|             console.log(err.message); | ||||
|           }); | ||||
|       } else { | ||||
|         hiddenLocation.value = value.id; | ||||
|       } | ||||
|       commit("updateLocation", value); | ||||
|     }, | ||||
|   }, | ||||
|   mutations: { | ||||
|     setWhoAmiI(state, me) { | ||||
|       state.me = me; | ||||
|     }, | ||||
|     addAvailableLocationGroup(state, group) { | ||||
|       state.availableLocations.push(group); | ||||
|     }, | ||||
|     updateLocation(state, value) { | ||||
|       // console.log("### mutation: updateLocation", value); | ||||
|       state.activity.location = value; | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| whoami().then((me) => { | ||||
|   store.commit("setWhoAmiI", me); | ||||
| }); | ||||
|  | ||||
| prepareLocations(store); | ||||
|  | ||||
| export default store; | ||||
| @@ -1,12 +0,0 @@ | ||||
| {% extends '@ChillMain/CRUD/Admin/index.html.twig' %} | ||||
|  | ||||
| {% block title %} | ||||
|     {% include('@ChillMain/CRUD/_edit_title.html.twig') %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block admin_content %} | ||||
|     {% embed '@ChillMain/CRUD/_edit_content.html.twig' %} | ||||
|         {% block content_form_actions_view %}{% endblock %} | ||||
|         {% block content_form_actions_save_and_show %}{% endblock %} | ||||
|     {% endembed %} | ||||
| {% endblock %} | ||||
| @@ -1,51 +0,0 @@ | ||||
| {% extends '@ChillMain/Admin/layoutWithVerticalMenu.html.twig' %} | ||||
|  | ||||
| {% block title %}{{ 'event.admin.title.Event budget element list'|trans }}{% endblock title %} | ||||
|  | ||||
| {% block admin_content %} | ||||
|  | ||||
|     <h1>{{ 'event.admin.title.Event budget element list'|trans }}</h1> | ||||
|  | ||||
| 	<table class="records_list table table-bordered border-dark"> | ||||
| 		<thead> | ||||
| 			<tr> | ||||
| 				<th>{{ 'Name'|trans }}</th> | ||||
|                 <th>{{ 'Type'|trans }}</th> | ||||
| 				<th>{{ 'Active'|trans }}</th> | ||||
|                 <th> </th> | ||||
| 			</tr> | ||||
| 		</thead> | ||||
| 		<tbody> | ||||
| 			{% for entity in entities %} | ||||
| 				<tr> | ||||
| 					<td>{{ entity.name|localize_translatable_string }}</td> | ||||
|                     <td>{{ entity.type.value|trans }}</td> | ||||
| 					<td style="text-align:center;"> | ||||
| 						{%- if entity.isActive -%} | ||||
| 							<i class="fa fa-check-square-o"></i> | ||||
| 						{%- else -%} | ||||
| 							<i class="fa fa-square-o"></i> | ||||
| 						{%- endif -%} | ||||
| 					</td> | ||||
|                     <td> | ||||
| 						<ul class="record_actions"> | ||||
| 							<li> | ||||
| 								<a href="{{ path('chill_crud_event_budget_kind_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a> | ||||
| 							</li> | ||||
| 						</ul> | ||||
| 					</td> | ||||
| 				</tr> | ||||
| 			{% endfor %} | ||||
| 		</tbody> | ||||
| 	</table> | ||||
|  | ||||
|     {{ chill_pagination(paginator) }} | ||||
|  | ||||
| 	<ul class="record_actions sticky-form-buttons"> | ||||
| 		<li> | ||||
| 			<a href="{{ path('chill_crud_event_budget_kind_new') }}" class="btn btn-create"> | ||||
| 				{{ 'event.admin.new.Create a new budget kind'|trans }} | ||||
| 			</a> | ||||
| 		</li> | ||||
| 	</ul> | ||||
| {% endblock %} | ||||
| @@ -1,11 +0,0 @@ | ||||
| {% extends '@ChillMain/CRUD/Admin/index.html.twig' %} | ||||
|  | ||||
| {% block title %} | ||||
|     {% include('@ChillMain/CRUD/_new_title.html.twig') %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block admin_content %} | ||||
|     {% embed '@ChillMain/CRUD/_new_content.html.twig' %} | ||||
|         {% block content_form_actions_save_and_show %}{% endblock %} | ||||
|     {% endembed %} | ||||
| {% endblock %} | ||||
| @@ -1,26 +0,0 @@ | ||||
| {% extends '@ChillMain/CRUD/Admin/index.html.twig' %} | ||||
|  | ||||
| {% block title %} | ||||
|     {% include('@ChillMain/CRUD/_edit_title.html.twig') %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block admin_content %} | ||||
|     {% embed '@ChillMain/CRUD/_edit_content.html.twig' %} | ||||
|  | ||||
|         {% block crud_content_form_rows %} | ||||
|             {{ form_row(form.name) }} | ||||
|             <div class="mb-3 row"> | ||||
|                 <label class="col-form-label col-sm-4"> | ||||
|                     {{ 'Parent'|trans }} | ||||
|                 </label> | ||||
|                 <div class="col-sm-8"> | ||||
|                     {{ entity.parent|chill_entity_render_box }} | ||||
|                 </div> | ||||
|             </div> | ||||
|             {{ form_row(form.ordering) }} | ||||
|             {{ form_row(form.isActive) }} | ||||
|         {% endblock crud_content_form_rows %} | ||||
|  | ||||
|         {% block content_form_actions_save_and_show %}{% endblock %} | ||||
|     {% endembed %} | ||||
| {% endblock admin_content %} | ||||
| @@ -1,45 +0,0 @@ | ||||
| {% extends '@ChillMain/CRUD/Admin/index.html.twig' %} | ||||
|  | ||||
| {% block admin_content %} | ||||
|     {% embed '@ChillMain/CRUD/_index.html.twig' %} | ||||
|         {% block table_entities_thead_tr %} | ||||
|             <th>{{ 'Id'|trans }}</th> | ||||
|             <th>{{ 'Title'|trans }}</th> | ||||
|             <th>{{ 'Ordering'|trans }}</th> | ||||
|             <th>{{ 'active'|trans }}</th> | ||||
|             <th> </th> | ||||
|         {% endblock %} | ||||
|  | ||||
|         {% block table_entities_tbody %} | ||||
|             {% for entity in entities %} | ||||
|                 <tr> | ||||
|                     <td>{{ entity.id }}</td> | ||||
|                     <td> | ||||
|                         {{ entity|chill_entity_render_box }} | ||||
|                     </td> | ||||
|                     <td>{{ entity.ordering }}</td> | ||||
|                     <td style="text-align:center;"> | ||||
|                         {%- if entity.isActive -%} | ||||
|                             <i class="fa fa-check-square-o"></i> | ||||
|                         {%- else -%} | ||||
|                             <i class="fa fa-square-o"></i> | ||||
|                         {%- endif -%} | ||||
|                     </td> | ||||
|                     <td> | ||||
|                         <ul class="record_actions"> | ||||
|                             <li> | ||||
|                                 <a href="{{ chill_path_add_return_path('chill_crud_event_theme_edit', { 'id': entity.id }) }}" class="btn btn-edit"></a> | ||||
|                             </li> | ||||
|                         </ul> | ||||
|                     </td> | ||||
|                 </tr> | ||||
|             {% endfor %} | ||||
|         {% endblock %} | ||||
|  | ||||
|         {% block actions_before %} | ||||
|             <li class='cancel'> | ||||
|                 <a href="{{ path('chill_main_admin_central') }}" class="btn btn-cancel">{{'Back to the admin'|trans}}</a> | ||||
|             </li> | ||||
|         {% endblock %} | ||||
|     {% endembed %} | ||||
| {% endblock %} | ||||
| @@ -1,11 +0,0 @@ | ||||
| {% extends '@ChillMain/CRUD/Admin/index.html.twig' %} | ||||
|  | ||||
| {% block title %} | ||||
|     {% include('@ChillMain/CRUD/_new_title.html.twig') %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block admin_content %} | ||||
|     {% embed '@ChillMain/CRUD/_new_content.html.twig' %} | ||||
|         {% block content_form_actions_save_and_show %}{% endblock %} | ||||
|     {% endembed %} | ||||
| {% endblock admin_content %} | ||||
| @@ -1,13 +0,0 @@ | ||||
| {% set reversed_parents = parents|reverse %} | ||||
| <span class="chill-entity entity-event-theme"> | ||||
|   <span class="badge bg-chill-l-gray text-dark"> | ||||
|     {%- for p in reversed_parents %} | ||||
|         <span class="parent-{{ loop.revindex0 }}"> | ||||
|       {{ p.name|localize_translatable_string }}{{ options['default.separator'] }} | ||||
|     </span> | ||||
|     {%- endfor -%} | ||||
|     <span class="child"> | ||||
|       {{ eventTheme.name|localize_translatable_string }} | ||||
|     </span> | ||||
|   </span> | ||||
| </span> | ||||
| @@ -12,7 +12,7 @@ | ||||
|             'title'             : 'Delete event'|trans, | ||||
|             'confirm_question'  : 'Are you sure you want to remove that event ?'|trans, | ||||
|             'cancel_route'      : activeRouteKey, | ||||
|             'cancel_parameters' : { 'id' : id }, | ||||
|             'cancel_parameters' : { 'event_id' : event_id }, | ||||
|             'form'              : delete_form | ||||
|         } | ||||
|     ) }} | ||||
|   | ||||
| @@ -17,12 +17,10 @@ | ||||
|     {{ form_row(edit_form.date) }} | ||||
|  | ||||
|     {{ form_row(edit_form.type, { label: "Event type" }) }} | ||||
|     {{ form_row(edit_form.themes) }} | ||||
|     {{ form_row(edit_form.moderator) }} | ||||
|     {{ form_row(edit_form.animatorsIntern) }} | ||||
|     {{ form_row(edit_form.animatorsExtern) }} | ||||
|     {{ form_row(edit_form.location) }} | ||||
|     {{ form_row(edit_form.budgetElements) }} | ||||
|     {{ form_row(edit_form.organizationCost) }} | ||||
|  | ||||
|     {{ form_row(edit_form.comment) }} | ||||
|     {{ form_row(edit_form.documents) }} | ||||
|  | ||||
|   | ||||
| @@ -34,7 +34,7 @@ | ||||
|                     <ul class="record_actions"> | ||||
|                         <li> | ||||
|                             {# {% if is_granted('CHILL_EVENT_SEE_DETAILS', event) %} #} | ||||
|                             <a href="{{ path('chill_event__event_show', { 'id' : event.id } ) }}" class="btn btn-view"> | ||||
|                             <a href="{{ path('chill_event__event_show', { 'event_id' : event.id } ) }}" class="btn btn-view"> | ||||
|                                 {{ 'See'|trans }} | ||||
|                             </a> | ||||
|                             {# {% endif %} #} | ||||
|   | ||||
| @@ -53,7 +53,7 @@ | ||||
|                                 {% set returnLabel = 'Back to %person% events'|trans({ '%person%' : currentPerson } ) %} | ||||
|  | ||||
|                                 {% if is_granted('CHILL_EVENT_SEE_DETAILS', participation.event) %} | ||||
|                                     <a  href="{{ path('chill_event__event_show', { 'id' : participation.event.id, 'return_path' : currentPath, 'return_label' : returnLabel } ) }}" | ||||
|                                     <a  href="{{ path('chill_event__event_show', { 'event_id' : participation.event.id, 'return_path' : currentPath, 'return_label' : returnLabel } ) }}" | ||||
|                                     class="btn btn-primary btn-sm" title="{{ 'See details of the event'|trans }}"> | ||||
|                                     <i class="fa fa-fw fa-eye"></i> | ||||
|                                 </a> | ||||
|   | ||||
| @@ -1,30 +1,25 @@ | ||||
| {% extends '@ChillEvent/layout.html.twig' %} | ||||
| {% extends '@ChillEvent/layout.html.twig' %} {% block js %} | ||||
| {{ encore_entry_script_tags("mod_async_upload") }} | ||||
| {{ encore_entry_script_tags("mod_pickentity_type") }} | ||||
|  | ||||
| {% block css %} | ||||
| {% endblock %} {% block css %} | ||||
| {{ encore_entry_link_tags("mod_async_upload") }} | ||||
| {{ encore_entry_link_tags("mod_pickentity_type") }} | ||||
| {{ encore_entry_link_tags('vue_event') }} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block title 'Event creation'|trans %} | ||||
|  | ||||
| {% block event_content -%} | ||||
| {% endblock %} {% block title 'Event creation'|trans %} {% block event_content | ||||
| -%} | ||||
| <div class="col-10"> | ||||
|     <h1>{{ "Event creation" | trans }}</h1> | ||||
|  | ||||
|     {{ form_start(form) }} | ||||
|     {{ form_errors(form) }} | ||||
|     {{ form_row(form.name) }} | ||||
|     {{ form_row(form.circle) }} | ||||
|     {{ form_row(form.name) }} | ||||
|     {{ form_row(form.date) }} | ||||
|     {{ form_row(form.type, { label: "Event type" }) }} | ||||
|     {{ form_row(form.themes) }} | ||||
|     {{ form_row(form.moderator) }} | ||||
|     {{ form_row(form.animatorsIntern) }} | ||||
|     {{ form_row(form.animatorsExtern) }} | ||||
|     {{ form_row(form.location) }} | ||||
|     <div id="location"></div> | ||||
|     {{ form_row(form.budgetElements) }} | ||||
|     {{ form_row(form.organizationCost) }} | ||||
|     {{ form_row(form.comment) }} | ||||
|     {{ form_row(form.documents) }} | ||||
|  | ||||
| @@ -45,18 +40,5 @@ | ||||
|     </ul> | ||||
|  | ||||
|     {{ form_end(form) }} | ||||
|  | ||||
|     <div id="event"></div> | ||||
|  | ||||
| </div> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
|     {{ encore_entry_script_tags("mod_async_upload") }} | ||||
|     {{ encore_entry_script_tags("mod_pickentity_type") }} | ||||
|     {{ encore_entry_script_tags('vue_event') }} | ||||
|     <script type="text/javascript"> | ||||
|         window.entity = {{ entity_json|json_encode|raw }}; | ||||
|         {% if app.user.currentLocation is not null %}window.default_location_id = {{ app.user.currentLocation.id }};{% endif %} | ||||
|     </script> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -11,7 +11,7 @@ block js %} | ||||
|  | ||||
|     {{ filter | chill_render_filter_order_helper }} | ||||
|  | ||||
|     {% if is_granted('CHILL_EVENT_CREATE') %} | ||||
|     {# {% if is_granted('CHILL_EVENT_CREATE') %} #} | ||||
|     <ul class="record_actions"> | ||||
|         <li> | ||||
|             <a | ||||
| @@ -25,9 +25,7 @@ block js %} | ||||
|             > | ||||
|         </li> | ||||
|     </ul> | ||||
|     {% endif %} | ||||
|  | ||||
|     {% if events|length > 0 %} | ||||
|     {# {% endif %} #} {% if events|length > 0 %} | ||||
|     <div class="flex-table"> | ||||
|         {% for e in events %} | ||||
|         <div class="item-bloc"> | ||||
| @@ -43,12 +41,6 @@ block js %} | ||||
|                         {{ e.moderator | chill_entity_render_box }} | ||||
|                     </p> | ||||
|                     {% endif %} | ||||
|  | ||||
|                     <div> | ||||
|                         {% for t in e.themes %} | ||||
|                             <span>{{ t|chill_entity_render_box }}</span> | ||||
|                         {% endfor %} | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <div class="item-col"> | ||||
|                     <div class="container" style="text-align: right"> | ||||
| @@ -56,12 +48,11 @@ block js %} | ||||
|                         <p> | ||||
|                             {{ 'count participations to this event'|trans({'count': e.participations|length}) }} | ||||
|                         </p> | ||||
|                         <p>{{ "center"|trans }}: {{ e.center.name }}</p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|             {% if e.participations|length > 0 %} | ||||
|             <div class="participation-list item-row separator"> | ||||
|             <div class="item-row separator"> | ||||
|                 <strong>{{ "Participations" | trans }} : </strong> | ||||
|                 {% for part in e.participations|slice(0, 5) %} {% include | ||||
|                 '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { | ||||
| @@ -115,7 +106,7 @@ block js %} | ||||
|                                 href="{{ | ||||
|                                     chill_path_add_return_path( | ||||
|                                         'chill_event__event_show', | ||||
|                                         { id: e.id } | ||||
|                                         { event_id: e.id } | ||||
|                                     ) | ||||
|                                 }}" | ||||
|                                 class="btn btn-show" | ||||
|   | ||||
| @@ -16,16 +16,6 @@ | ||||
|     {{ encore_entry_link_tags('mod_document_action_buttons_group') }} | ||||
| {% endblock %} | ||||
|  | ||||
| {% macro insert_onthefly(type, entity, parent = null) %} | ||||
|     {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { | ||||
|         action: 'show', displayBadge: true, | ||||
|         targetEntity: { name: type, id: entity.id }, | ||||
|         buttonText: entity|chill_entity_render_string, | ||||
|         isDead: entity.deathdate is defined and entity.deathdate is not null, | ||||
|         parent: parent | ||||
|     } %} | ||||
| {% endmacro %} | ||||
|  | ||||
| {% block event_content -%} | ||||
| <div class="col-10"> | ||||
|     <h1>{{ 'Details of an event'|trans }}</h1> | ||||
| @@ -36,10 +26,6 @@ | ||||
|                 <th>{{ 'Circle'|trans }}</th> | ||||
|                 <td>{{ event.circle.name|localize_translatable_string }}</td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|                 <th>{{ 'Center'|trans }}</th> | ||||
|                 <td>{{ event.center.name }}</td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|                 <th>{{ 'Name'|trans }}</th> | ||||
|                 <td>{{ event.name }}</td> | ||||
| @@ -52,33 +38,13 @@ | ||||
|                 <th>{{ 'Event type'|trans }}</th> | ||||
|                 <td>{{ event.type.name|localize_translatable_string }}</td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|                 <th>{{ 'event.theme.label'|trans }} | ||||
|                 <td> | ||||
|                 {% for t in event.themes %} | ||||
|                     {{ t|chill_entity_render_box }} | ||||
|                 {% endfor %} | ||||
|                 </td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|                 <th>{{ 'Moderator'|trans }}</th> | ||||
|                 <td>{{ event.moderator|chill_entity_render_string }}</td> | ||||
|                 <td>{{ event.moderator|trans|default('-') }}</td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|                 <th>{{ 'event.animators.intern'|trans }}</th> | ||||
|                 <td> | ||||
|                     {% for a in event.animatorsIntern %} | ||||
|                         {{ _self.insert_onthefly('user', a) }} | ||||
|                     {% endfor %} | ||||
|                 </td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|                 <th>{{ 'event.animators.extern'|trans }}</th> | ||||
|                 <td> | ||||
|                     {% for a in event.animatorsExtern %} | ||||
|                         {{ _self.insert_onthefly('thirdparty', a) }} | ||||
|                     {% endfor %} | ||||
|                 </td> | ||||
|                 <th>{{ 'event.fields.organizationCost'|trans }}</th> | ||||
|                 <td>{{ event.organizationCost|format_currency('EUR') }}</td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|                 <th>{{ 'event.fields.location'|trans }}</th> | ||||
| @@ -94,77 +60,6 @@ | ||||
|         </tbody> | ||||
|     </table> | ||||
|  | ||||
|     <div class="budget-wrapper" style="background-color: whitesmoke; padding: 1rem; margin-bottom: .5rem;"> | ||||
|         {% set resources = event.budgetElements|filter(e => e.kind.type.value == 'Resource') %} | ||||
|         {% set charges = event.budgetElements|filter(e => e.kind.type.value == 'Charge') %} | ||||
|  | ||||
|         <h2>Budget de l'événement</h2> | ||||
|  | ||||
|         <h3>Ressources</h3> | ||||
|         {% if resources is not empty %} | ||||
|             <table class="table table-bordered border-dark align-middle"> | ||||
|                 <thead> | ||||
|                 <tr> | ||||
|                     <th>{{ 'event.budget.resource type'|trans }}</th> | ||||
|                     <th>{{ 'event.budget.amount'|trans }}</th> | ||||
|                     <th>{{ 'event.budget.comment'|trans }}</th> | ||||
|                 </tr> | ||||
|                 </thead> | ||||
|                 <tbody> | ||||
|                 {% set totalResources = 0 %} | ||||
|                 {% for res in resources %} | ||||
|                     <tr> | ||||
|                         <td>{{ res.kind.name|localize_translatable_string }}</td> | ||||
|                         <td>{{ res.amount|format_currency('EUR') }}</td> | ||||
|                         <td>{{ res.comment.comment|chill_print_or_message(null, 'blockquote') }}</td> | ||||
|                     </tr> | ||||
|                     {% set totalResources = totalResources + res.amount %} | ||||
|                 {% endfor %} | ||||
|                 </tbody> | ||||
|                 <tfoot> | ||||
|                 <tr> | ||||
|                     <th>Total</th> | ||||
|                     <td>{{ totalResources|format_currency('EUR') }}</td> | ||||
|                 </tr> | ||||
|                 </tfoot> | ||||
|             </table> | ||||
|         {% else %} | ||||
|             <p class="chill-no-data-statement">{{ 'event.budget.no elements'|trans }}</p> | ||||
|         {% endif %} | ||||
|  | ||||
|         <h3>Charges</h3> | ||||
|         {% if charges is not empty %} | ||||
|             <table class="table table-bordered border-dark align-middle"> | ||||
|                 <thead> | ||||
|                 <tr> | ||||
|                     <th>{{ 'event.budget.charge type'|trans }}</th> | ||||
|                     <th>{{ 'event.budget.amount'|trans }}</th> | ||||
|                     <th>{{ 'event.budget.comment'|trans }}</th> | ||||
|                 </tr> | ||||
|                 </thead> | ||||
|                 <tbody> | ||||
|                 {% set totalCharges = 0 %} | ||||
|                 {% for chg in charges %} | ||||
|                     <tr> | ||||
|                         <td>{{ chg.kind.name|localize_translatable_string }}</td> | ||||
|                         <td>{{ chg.amount|format_currency('EUR') }}</td> | ||||
|                         <td>{{ chg.comment.comment|chill_print_or_message(null, 'blockquote') }}</td> | ||||
|                     </tr> | ||||
|                     {% set totalCharges = totalCharges + chg.amount %} | ||||
|                 {% endfor %} | ||||
|                 </tbody> | ||||
|                 <tfoot> | ||||
|                 <tr> | ||||
|                     <th>Total</th> | ||||
|                     <td>{{ totalCharges|format_currency('EUR') }}</td> | ||||
|                 </tr> | ||||
|                 </tfoot> | ||||
|             </table> | ||||
|         {% else %} | ||||
|             <p class="chill-no-data-statement">{{ 'event.budget.no elements'|trans }}</p> | ||||
|         {% endif %} | ||||
|     </div> | ||||
|  | ||||
|     {% if event.documents|length > 0 %} | ||||
|         <div> | ||||
|             <p><strong>{{ 'event.fields.documents'|trans }}</strong></p> | ||||
| @@ -185,97 +80,6 @@ | ||||
|         </div> | ||||
|     {% endif %} | ||||
|  | ||||
|     <div class="participations-wrapper"> | ||||
|         <h2>{{ 'Participations'|trans }}</h2> | ||||
|         {% set count = event.participations|length %} | ||||
|         <p class="chill-no-data-statement">{{ 'count participations to this event'|trans({'count': count}) }}</p> | ||||
|  | ||||
|         {% if count > 0 %} | ||||
|             <table class="table table-bordered border-dark align-middle"> | ||||
|                 <thead> | ||||
|                     <tr> | ||||
|                         <th>{{ 'Person'|trans }}</th> | ||||
|                         <th>{{ 'Role'|trans }}</th> | ||||
|                         <th>{{ 'Status'|trans }}</th> | ||||
|                         <th>{{ 'Last update'|trans }}</th> | ||||
|                         <th> </th> | ||||
|                     </tr> | ||||
|                 </thead> | ||||
|                 <tbody> | ||||
|                 {% for participation in event.participations %} | ||||
|                     <tr> | ||||
|                         <td> | ||||
|                             {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { | ||||
|                                 targetEntity: { name: 'person', id: participation.person.id }, | ||||
|                                 action: 'show', | ||||
|                                 displayBadge: true, | ||||
|                                 buttonText: participation.person|chill_entity_render_string, | ||||
|                                 isDead: participation.person.deathdate is not null | ||||
|                             } %} | ||||
|                         </td> | ||||
|                         <td>{{ participation.role.name|localize_translatable_string }}</td> | ||||
|                         <td>{{ participation.status.name|localize_translatable_string }}</td> | ||||
|                         <td>{{ participation.lastUpdate|ago }} {# sf4 check: filter 'time_diff' is abandoned, | ||||
|                              alternative: knplabs/knp-time-bundle provide filter 'ago' #} | ||||
|                             <i class="fa fa-info-circle" title="{{ participation.lastUpdate|format_date("long")|escape('html_attr') }}"></i> | ||||
|                         </td> | ||||
|                         <td> | ||||
|                             <ul class="record_actions"> | ||||
|                                 {% if is_granted('CHILL_EVENT_PARTICIPATION_UPDATE', participation) %} | ||||
|                                 <li> | ||||
|                                     <a href="{{ chill_path_add_return_path('chill_event_participation_edit', { 'participation_id' : participation.id }, false, 'See'|trans ) }}" | ||||
|                                        class="btn btn-edit" title="{{ 'Edit'|trans }}"></a> | ||||
|                                 </li> | ||||
|                                 <li> | ||||
|                                     <a href="{{ path('chill_event_participation_delete', {'participation_id' : participation.id } ) }}" | ||||
|                                         class="btn btn-delete" title="{{ 'Delete'|trans }}"></a> | ||||
|                                 </li> | ||||
|                                 {% endif %} | ||||
|                             </ul> | ||||
|                         </td> | ||||
|                     </tr> | ||||
|                 {% endfor %} | ||||
|                 </tbody> | ||||
|             </table> | ||||
|  | ||||
|  | ||||
|         {% endif %} | ||||
|  | ||||
|         <div class="row"> | ||||
|             <div class="col-8"> | ||||
|                 {{ form_start(form_add_participation_by_person) }} | ||||
|                 <div class="input-group"> | ||||
|                     {{ form_widget(form_add_participation_by_person.person_id, { 'attr' : { | ||||
|                         'class' : 'custom-select', | ||||
|                         'style': 'min-width: 15em; max-width: 18em; display: inline-block;' | ||||
|                     }} ) }} | ||||
|                 </div> | ||||
|                 <input type="hidden" name="returnPath" value="{{ app.request.requestUri }}" /> | ||||
|                 {{ form_end(form_add_participation_by_person) }} | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|         <ul class="record_actions"> | ||||
|             {% if count > 0 %} | ||||
|                 <li> | ||||
|                     {{ form_start(form_export, {'attr': {'id': 'export_tableur'}}) }} | ||||
|                     <div class="input-group"> | ||||
|                         {{ form_widget(form_export.format, { 'attr' : { 'class': 'custom-select' } }) }} | ||||
|                         <div class="input-group-append"> | ||||
|                             {{ form_widget(form_export.submit, { 'attr' : { 'class': 'btn btn-save' } }) }} | ||||
|                         </div> | ||||
|                         <a class="bt-"></a> | ||||
|                     </div> | ||||
|                     {{ form_rest(form_export) }} | ||||
|                     {{ form_end(form_export) }} | ||||
|                 </li> | ||||
|                 <li><a href="{{ path('chill_event_participation_edit_multiple', { 'event_id' : event.id } ) }}" class="btn btn-edit">{{ 'Edit all the participations'|trans }}</a></li> | ||||
|             {% endif %} | ||||
|         </ul> | ||||
|     </div> | ||||
|     <div class="post_show"> | ||||
|         {{ chill_delegated_block('block_footer_show', { 'event': event }) }} | ||||
|     </div> | ||||
|  | ||||
|     <ul class="record_actions"> | ||||
|  | ||||
| @@ -296,5 +100,97 @@ | ||||
|         </li> | ||||
|  | ||||
|     </ul> | ||||
|  | ||||
|     <h2>{{ 'Participations'|trans }}</h2> | ||||
|     {% set count = event.participations|length %} | ||||
|     <p>{{ 'count participations to this event'|trans({'count': count}) }}</p> | ||||
|  | ||||
|     {% if count > 0 %} | ||||
|         <table class="table table-bordered border-dark align-middle"> | ||||
|             <thead> | ||||
|                 <tr> | ||||
|                     <th>{{ 'Person'|trans }}</th> | ||||
|                     <th>{{ 'Role'|trans }}</th> | ||||
|                     <th>{{ 'Status'|trans }}</th> | ||||
|                     <th>{{ 'Last update'|trans }}</th> | ||||
|                     <th> </th> | ||||
|                 </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|             {% for participation in event.participations %} | ||||
|                 <tr> | ||||
|                     <td> | ||||
|                         {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { | ||||
|                             targetEntity: { name: 'person', id: participation.person.id }, | ||||
|                             action: 'show', | ||||
|                             displayBadge: true, | ||||
|                             buttonText: participation.person|chill_entity_render_string, | ||||
|                             isDead: participation.person.deathdate is not null | ||||
|                         } %} | ||||
|                     </td> | ||||
|                     <td>{{ participation.role.name|localize_translatable_string }}</td> | ||||
|                     <td>{{ participation.status.name|localize_translatable_string }}</td> | ||||
|                     <td>{{ participation.lastUpdate|ago }} {# sf4 check: filter 'time_diff' is abandoned, | ||||
|                          alternative: knplabs/knp-time-bundle provide filter 'ago' #} | ||||
|                         <i class="fa fa-info-circle" title="{{ participation.lastUpdate|format_date("long")|escape('html_attr') }}"></i> | ||||
|                     </td> | ||||
|                     <td> | ||||
|                         <ul class="record_actions"> | ||||
|                             {% if is_granted('CHILL_EVENT_PARTICIPATION_UPDATE', participation) %} | ||||
|                             <li> | ||||
|                                 <a href="{{ chill_path_add_return_path('chill_event_participation_edit', { 'participation_id' : participation.id }, false, 'See'|trans ) }}" | ||||
|                                    class="btn btn-edit" title="{{ 'Edit'|trans }}"></a> | ||||
|                             </li> | ||||
|                             <li> | ||||
|                                 <a href="{{ path('chill_event_participation_delete', {'participation_id' : participation.id } ) }}" | ||||
|                                     class="btn btn-delete" title="{{ 'Delete'|trans }}"></a> | ||||
|                             </li> | ||||
|                             {% endif %} | ||||
|                         </ul> | ||||
|                     </td> | ||||
|                 </tr> | ||||
|             {% endfor %} | ||||
|             </tbody> | ||||
|         </table> | ||||
|  | ||||
|  | ||||
|     {% endif %} | ||||
|  | ||||
|     <ul class="record_actions"> | ||||
|         {% if count > 0 %} | ||||
|             <li><a href="{{ path('chill_event_participation_edit_multiple', { 'event_id' : event.id } ) }}" class="btn btn-edit">{{ 'Edit all the participations'|trans }}</a></li> | ||||
|         {% endif %} | ||||
|     </ul> | ||||
|  | ||||
|     <div class="row" style="margin-bottom: 10em;"> | ||||
|         <div class="col-8"> | ||||
|             {{ form_start(form_add_participation_by_person) }} | ||||
|             <div class="input-group"> | ||||
|                 {{ form_widget(form_add_participation_by_person.person_id, { 'attr' : { | ||||
|                     'class' : 'custom-select', | ||||
|                     'style': 'min-width: 15em; max-width: 18em; display: inline-block;' | ||||
|                 }} ) }} | ||||
|             </div> | ||||
|             <input type="hidden" name="returnPath" value="{{ app.request.requestUri }}" /> | ||||
|             {{ form_end(form_add_participation_by_person) }} | ||||
|         </div> | ||||
|  | ||||
|         <div class="col-4"> | ||||
|             {{ form_start(form_export, {'attr': {'id': 'export_tableur'}}) }} | ||||
|             <div class="input-group"> | ||||
|                 {{ form_widget(form_export.format, { 'attr' : { 'class': 'custom-select' } }) }} | ||||
|                 <div class="input-group-append"> | ||||
|                     {{ form_widget(form_export.submit, { 'attr' : { 'class': 'btn btn-save' } }) }} | ||||
|                 </div> | ||||
|                 <a class="bt-"></a> | ||||
|             </div> | ||||
|             {{ form_rest(form_export) }} | ||||
|             {{ form_end(form_export) }} | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="post_show"> | ||||
|         {{ chill_delegated_block('block_footer_show', { 'event': event }) }} | ||||
|     </div> | ||||
| </div> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -11,7 +11,7 @@ | ||||
|             'title'             : 'Remove participation'|trans, | ||||
|             'confirm_question'  : 'Are you sure you want to remove that participation ?'|trans, | ||||
|             'cancel_route'      : activeRouteKey, | ||||
|             'cancel_parameters' : { 'id' : event_id }, | ||||
|             'cancel_parameters' : { 'event_id' : event_id }, | ||||
|             'form'              : delete_form | ||||
|         } | ||||
|     ) }} | ||||
|   | ||||
| @@ -33,7 +33,7 @@ | ||||
|             {% set returnPath = app.request.get('return_path') %} | ||||
|             {% set returnLabel = app.request.get('return_label') %} | ||||
|  | ||||
|             <a href="{{ returnPath |default( path('chill_event__event_show', { 'id' : participation.event.id } )) }}" class="btn btn-cancel"> | ||||
|             <a href="{{ returnPath |default( path('chill_event__event_show', { 'event_id' : participation.event.id } )) }}" class="btn btn-cancel"> | ||||
|                 {{ returnLabel |default('Back to the event'|trans) }} | ||||
|             </a> | ||||
|         </li> | ||||
|   | ||||
| @@ -34,7 +34,7 @@ | ||||
|  | ||||
|     <ul class="record_actions sticky-form-buttons"> | ||||
|         <li class="cancel"> | ||||
|             <a href="{{ path('chill_event__event_show', { 'id' : participation.event.id } ) }}" class="btn btn-cancel"> | ||||
|             <a href="{{ path('chill_event__event_show', { 'event_id' : participation.event.id } ) }}" class="btn btn-cancel"> | ||||
|                 {{ 'Back to the event'|trans }} | ||||
|             </a> | ||||
|         </li> | ||||
|   | ||||
| @@ -54,9 +54,9 @@ class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInter | ||||
|     ) { | ||||
|         $this->voterHelper = $voterHelperFactory | ||||
|             ->generate(self::class) | ||||
|             ->addCheckFor(null, [self::SEE, self::CREATE]) | ||||
|             ->addCheckFor(null, [self::SEE]) | ||||
|             ->addCheckFor(Event::class, [...self::ROLES]) | ||||
|             ->addCheckFor(Person::class, [self::SEE]) | ||||
|             ->addCheckFor(Person::class, [self::SEE, self::CREATE]) | ||||
|             ->addCheckFor(Center::class, [self::STATS]) | ||||
|             ->build(); | ||||
|     } | ||||
|   | ||||
| @@ -1,109 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\EventBundle\Templating\Entity; | ||||
|  | ||||
| use Chill\EventBundle\Entity\EventTheme; | ||||
| use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface; | ||||
| use Chill\MainBundle\Templating\TranslatableStringHelper; | ||||
| use Symfony\Contracts\Translation\TranslatorInterface; | ||||
| use Twig\Error\LoaderError; | ||||
| use Twig\Error\RuntimeError; | ||||
| use Twig\Error\SyntaxError; | ||||
|  | ||||
| /** | ||||
|  * @implements ChillEntityRenderInterface<EventTheme> | ||||
|  */ | ||||
| class EventThemeRender implements ChillEntityRenderInterface | ||||
| { | ||||
|     public const AND_CHILDREN_MENTION = 'show_and_children_mention'; | ||||
|  | ||||
|     public const DEFAULT_ARGS = [ | ||||
|         self::SEPARATOR_KEY => ' > ', | ||||
|         self::SHOW_AND_CHILDREN => false, | ||||
|         self::AND_CHILDREN_MENTION => 'event_theme.and children', | ||||
|     ]; | ||||
|  | ||||
|     public const SEPARATOR_KEY = 'default.separator'; | ||||
|  | ||||
|     /** | ||||
|      * Show a mention "and children" on each EventTheme, if the event theme | ||||
|      * has at least one child. | ||||
|      */ | ||||
|     public const SHOW_AND_CHILDREN = 'show_and_children'; | ||||
|  | ||||
|     public function __construct(private readonly TranslatableStringHelper $translatableStringHelper, private readonly \Twig\Environment $engine, private readonly TranslatorInterface $translator) {} | ||||
|  | ||||
|     /** | ||||
|      * @throws RuntimeError | ||||
|      * @throws SyntaxError | ||||
|      * @throws LoaderError | ||||
|      */ | ||||
|     public function renderBox($entity, array $options): string | ||||
|     { | ||||
|         $options = array_merge(self::DEFAULT_ARGS, $options); | ||||
|         // give some help to twig: an array of parents | ||||
|         $parents = $this->buildParents($entity); | ||||
|  | ||||
|         return $this | ||||
|             ->engine | ||||
|             ->render( | ||||
|                 '@ChillEvent/Entity/event_theme.html.twig', | ||||
|                 [ | ||||
|                     'eventTheme' => $entity, | ||||
|                     'parents' => $parents, | ||||
|                     'options' => $options, | ||||
|                 ] | ||||
|             ); | ||||
|     } | ||||
|  | ||||
|     public function renderString($entity, array $options): string | ||||
|     { | ||||
|         /** @var EventTheme $entity */ | ||||
|         $options = array_merge(self::DEFAULT_ARGS, $options); | ||||
|  | ||||
|         $titles = [$this->translatableStringHelper->localize($entity->getName())]; | ||||
|  | ||||
|         // loop to parent, until root | ||||
|         while ($entity->hasParent()) { | ||||
|             $entity = $entity->getParent(); | ||||
|             $titles[] = $this->translatableStringHelper->localize( | ||||
|                 $entity->getName() | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $titles = \array_reverse($titles); | ||||
|  | ||||
|         $title = \implode($options[self::SEPARATOR_KEY], $titles); | ||||
|  | ||||
|         if ($options[self::SHOW_AND_CHILDREN] && $entity->hasChildren()) { | ||||
|             $title .= ' ('.$this->translator->trans($options[self::AND_CHILDREN_MENTION]).')'; | ||||
|         } | ||||
|  | ||||
|         return $title; | ||||
|     } | ||||
|  | ||||
|     public function supports($entity, array $options): bool | ||||
|     { | ||||
|         return $entity instanceof EventTheme; | ||||
|     } | ||||
|  | ||||
|     private function buildParents(EventTheme $entity): array | ||||
|     { | ||||
|         $parents = []; | ||||
|  | ||||
|         while ($entity->hasParent()) { | ||||
|             $entity = $parents[] = $entity->getParent(); | ||||
|         } | ||||
|  | ||||
|         return $parents; | ||||
|     } | ||||
| } | ||||
| @@ -1,10 +1,3 @@ | ||||
| module.exports = function (encore, entries) { | ||||
|   entries.push(__dirname + "/Resources/public/chill/index.js"); | ||||
|  | ||||
|   encore.addEntry( | ||||
|     "vue_event", | ||||
|     __dirname + "/Resources/public/vuejs/index.js", | ||||
|   ); | ||||
| }; | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| services: | ||||
|     Chill\EventBundle\Controller\: | ||||
|         autowire: true | ||||
|         autoconfigure: true | ||||
|         resource: '../Controller' | ||||
|         tags: ['controller.service_arguments'] | ||||
|  | ||||
| @@ -9,11 +8,4 @@ services: | ||||
|         autowire: true | ||||
|         autoconfigure: true | ||||
|         resource: '../Menu/' | ||||
|         tags: ['chill.menu_builder'] | ||||
|  | ||||
|     Chill\EventBundle\Templating\Entity\: | ||||
|         autowire: true | ||||
|         autoconfigure: true | ||||
|         resource: '../Templating/Entity' | ||||
|         tags: | ||||
|             - 'chill.render_entity' | ||||
|         tags: ['chill.menu_builder'] | ||||
| @@ -1,4 +0,0 @@ | ||||
| services: | ||||
|     _defaults: | ||||
|         autowire: true | ||||
|         autoconfigure: true | ||||
|   | ||||
| @@ -8,9 +8,6 @@ services: | ||||
|     Chill\EventBundle\Export\Export\CountEvents: | ||||
|         tags: | ||||
|             - { name: chill.export, alias: 'count_events' } | ||||
|     Chill\EventBundle\Export\Export\ListEvents: | ||||
|         tags: | ||||
|             - { name: chill.export, alias: 'list_events' } | ||||
|     Chill\EventBundle\Export\Export\CountEventParticipations: | ||||
|         tags: | ||||
|             - { name: chill.export, alias: 'count_event_participants' } | ||||
|   | ||||
| @@ -31,29 +31,3 @@ services: | ||||
|     Chill\EventBundle\Form\Type\PickEventType: | ||||
|         tags: | ||||
|             - { name: form.type } | ||||
|  | ||||
|     Chill\EventBundle\Form\EventThemeType: | ||||
|         tags: | ||||
|             - { name: form.type } | ||||
|  | ||||
|     Chill\EventBundle\Form\Type\PickEventThemeType: | ||||
|         tags: | ||||
|             - { name: form.type } | ||||
|  | ||||
|     Chill\EventBundle\Form\EventType: | ||||
|         tags: | ||||
|             - { name: form.type } | ||||
|  | ||||
|     Chill\EventBundle\Form\EventBudgetKindType: | ||||
|         tags: | ||||
|             - { name: form.type } | ||||
|  | ||||
|     Chill\EventBundle\Form\AddEventBudgetElementType: | ||||
|         tags: | ||||
|             - { name: form.type } | ||||
|  | ||||
|     Chill\EventBundle\Form\Type\PickAnimatorType: | ||||
|         tags: | ||||
|             - { name: form.type } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,40 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\Migrations\Event; | ||||
|  | ||||
| use Doctrine\DBAL\Schema\Schema; | ||||
| use Doctrine\Migrations\AbstractMigration; | ||||
|  | ||||
| /** | ||||
|  * Add event theme entity. | ||||
|  */ | ||||
| final class Version20250428092611 extends AbstractMigration | ||||
| { | ||||
|     public function getDescription(): string | ||||
|     { | ||||
|         return 'Add an event theme entity'; | ||||
|     } | ||||
|  | ||||
|     public function up(Schema $schema): void | ||||
|     { | ||||
|         $this->addSql('CREATE SEQUENCE chill_event_event_theme_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); | ||||
|         $this->addSql('CREATE TABLE chill_event_event_theme (id INT NOT NULL, parent_id INT DEFAULT NULL, isActive BOOLEAN NOT NULL, name JSON NOT NULL, ordering DOUBLE PRECISION DEFAULT \'0.0\' NOT NULL, PRIMARY KEY(id))'); | ||||
|         $this->addSql('CREATE INDEX IDX_80D7C6B0727ACA70 ON chill_event_event_theme (parent_id)'); | ||||
|         $this->addSql('ALTER TABLE chill_event_event_theme ADD CONSTRAINT FK_80D7C6B0727ACA70 FOREIGN KEY (parent_id) REFERENCES chill_event_event_theme (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); | ||||
|     } | ||||
|  | ||||
|     public function down(Schema $schema): void | ||||
|     { | ||||
|         $this->addSql('ALTER TABLE chill_event_event_theme DROP CONSTRAINT FK_80D7C6B0727ACA70'); | ||||
|         $this->addSql('DROP TABLE chill_event_event_theme'); | ||||
|     } | ||||
| } | ||||
| @@ -1,42 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\Migrations\Event; | ||||
|  | ||||
| use Doctrine\DBAL\Schema\Schema; | ||||
| use Doctrine\Migrations\AbstractMigration; | ||||
|  | ||||
| /** | ||||
|  * Auto-generated Migration: Please modify to your needs! | ||||
|  */ | ||||
| final class Version20250429062911 extends AbstractMigration | ||||
| { | ||||
|     public function getDescription(): string | ||||
|     { | ||||
|         return 'Add themes property to event'; | ||||
|     } | ||||
|  | ||||
|     public function up(Schema $schema): void | ||||
|     { | ||||
|         $this->addSql('CREATE TABLE chill_event_eventtheme (event_id INT NOT NULL, eventtheme_id INT NOT NULL, PRIMARY KEY(event_id, eventtheme_id))'); | ||||
|         $this->addSql('CREATE INDEX IDX_8D75029771F7E88B ON chill_event_eventtheme (event_id)'); | ||||
|         $this->addSql('CREATE INDEX IDX_8D750297A81D3C55 ON chill_event_eventtheme (eventtheme_id)'); | ||||
|         $this->addSql('ALTER TABLE chill_event_eventtheme ADD CONSTRAINT FK_8D75029771F7E88B FOREIGN KEY (event_id) REFERENCES chill_event_event (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); | ||||
|         $this->addSql('ALTER TABLE chill_event_eventtheme ADD CONSTRAINT FK_8D750297A81D3C55 FOREIGN KEY (eventtheme_id) REFERENCES chill_event_event_theme (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); | ||||
|     } | ||||
|  | ||||
|     public function down(Schema $schema): void | ||||
|     { | ||||
|         $this->addSql('ALTER TABLE chill_event_eventtheme DROP CONSTRAINT FK_8D75029771F7E88B'); | ||||
|         $this->addSql('ALTER TABLE chill_event_eventtheme DROP CONSTRAINT FK_8D750297A81D3C55'); | ||||
|         $this->addSql('DROP TABLE chill_event_eventtheme'); | ||||
|     } | ||||
| } | ||||
| @@ -1,43 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * Chill is a software for social workers | ||||
|  * | ||||
|  * For the full copyright and license information, please view | ||||
|  * the LICENSE file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace Chill\Migrations\Event; | ||||
|  | ||||
| use Doctrine\DBAL\Schema\Schema; | ||||
| use Doctrine\Migrations\AbstractMigration; | ||||
|  | ||||
| final class Version20250505120818 extends AbstractMigration | ||||
| { | ||||
|     public function getDescription(): string | ||||
|     { | ||||
|         return 'Add budget kind admin entity'; | ||||
|     } | ||||
|  | ||||
|     public function up(Schema $schema): void | ||||
|     { | ||||
|         $this->addSql(<<<'SQL' | ||||
|             CREATE SEQUENCE chill_event_budget_kind_id_seq INCREMENT BY 1 MINVALUE 1 START 1 | ||||
|         SQL); | ||||
|         $this->addSql(<<<'SQL' | ||||
|             CREATE TABLE chill_event_budget_kind (id INT NOT NULL, isActive BOOLEAN DEFAULT true NOT NULL, type VARCHAR(255) NOT NULL, name JSONB DEFAULT '{}' NOT NULL, PRIMARY KEY(id)) | ||||
|         SQL); | ||||
|     } | ||||
|  | ||||
|     public function down(Schema $schema): void | ||||
|     { | ||||
|         $this->addSql(<<<'SQL' | ||||
|             DROP SEQUENCE chill_event_budget_kind_id_seq CASCADE | ||||
|         SQL); | ||||
|         $this->addSql(<<<'SQL' | ||||
|             DROP TABLE chill_event_budget_kind | ||||
|         SQL); | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user