Compare commits

..

2 Commits

Author SHA1 Message Date
nobohan
bf61324c1d Signature: multi page viewer - WIP 2024-09-04 11:26:08 +02:00
nobohan
abf20b0cf2 Signature: add a signature zone manually 2024-09-04 10:14:08 +02:00
531 changed files with 2557 additions and 10995 deletions

View File

@@ -1,6 +0,0 @@
kind: Fixed
body: Show only the current referrer in the page "show" for an accompanying period
workf
time: 2024-09-16T15:18:43.017401122+02:00
custom:
Issue: "308"

View File

@@ -1,30 +1,11 @@
## v2.23.0 - 2024-07-23 & 2024-07-19
## v2.23.0 - 2024-07-23
### Feature
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
* Add job bundle (module emploi)
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
* Add job bundle (module emploi)
* Upgrade import of address list to the last version of compiled addresses of belgian-best-address
* Upgrade CKEditor and refactor configuration with use of typescript
* ([#123](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/123)) Add a button to duplicate calendar ranges from a week to another one
* [admin] filter users by active / inactive in the admin user's list
* ([#273](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/273)) Add the possibility to mark all notifications as read
* Handle duplicate reference id in the import of reference addresses
* Do not update the "createdAt" column when importing postal code which does not change
* Display filename on file upload within the UI interface
### Fixed
* Fix resolving of centers for an household, which will fix in turn the access control
* Resolved type hinting error in activity list export
* ([#271](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/271)) Take into account the acp closing date in the acp works date filter
### Traduction française des principaux changements
- Ajout d'un bouton pour dupliquer les périodes de disponibilités d'une semaine à une autre;
- dans l'interface d'administration, filtre sur les utilisateurs actifs. Par défaut, seul les utilisateurs
actifs sont affichés;
- Nouveau bouton pour indiquer toutes les notifications comme lues;
- Améliorations sur l'import des adresses et des codes postaux;
- Affiche le nom du fichier déposé quand on téléverse un fichier depuis le poste de travail local;
- Agrandit l'icône du type de fichier dans l'interface de dépôt de fichier;
- correction: tient compte de la date de fermeture du parcours dans les filtres sur les actions d'accompagnement.
* Fix resolving of centers for an household, which will fix in turn the access control
* Resolved type hinting error in activity list export

View File

@@ -1,3 +0,0 @@
## v2.24.0 - 2024-09-11
### Feature
* ([#306](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/306)) When a document is converted or downloaded in the browser, this document is removed from the browser memory after 45s. Future click on the button re-download the document.

View File

@@ -1,3 +0,0 @@
## v3.1.0 - 2024-08-30
### Feature
* Add export aggregator to aggregate activities by household + filter persons that are not part of an accompanyingperiod during a certain timeframe.

2
.env
View File

@@ -23,7 +23,7 @@ TRUSTED_HOSTS='^(localhost|example\.com|nginx)$'
###< symfony/framework-bundle ###
## Wopi server for editing documents online
EDITOR_SERVER=http://collabora:9980
WOPI_SERVER=http://collabora:9980
# must be manually set in .env.local
# ADMIN_PASSWORD=

View File

@@ -41,5 +41,3 @@ DATABASE_URL="postgresql://postgres:postgres@db:5432/test?serverVersion=14&chars
ASYNC_UPLOAD_TEMP_URL_KEY=
ASYNC_UPLOAD_TEMP_URL_BASE_PATH=
ASYNC_UPLOAD_TEMP_URL_CONTAINER=
EDITOR_SERVER=https://localhost:9980

View File

@@ -122,7 +122,7 @@ unit_tests:
- php tests/console chill:db:sync-views --env=test
- php -d memory_limit=2G tests/console cache:clear --env=test
- php -d memory_limit=3G tests/console doctrine:fixtures:load -n --env=test
- php -d memory_limit=4G bin/phpunit --colors=never --exclude-group dbIntensive,openstack-integration,collabora-integration
- php -d memory_limit=4G bin/phpunit --colors=never --exclude-group dbIntensive
artifacts:
expire_in: 1 day
paths:

View File

@@ -6,62 +6,33 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
## v3.1.0 - 2024-08-30
### Feature
* Add export aggregator to aggregate activities by household + filter persons that are not part of an accompanyingperiod during a certain timeframe.
## v3.0.0 - 2024-08-26
### Fixed
* Fix delete action for accompanying periods in draft state
* Fix connection to azure when making an calendar event in chill
* CollectionType js fixes for remove button and adding multiple entries
* Fix delete action for accompanying periods in draft state
* Fix connection to azure when making an calendar event in chill
* CollectionType js fixes for remove button and adding multiple entries
## v2.24.0 - 2024-09-11
## v2.23.0 - 2024-07-23
### Feature
* ([#306](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/306)) When a document is converted or downloaded in the browser, this document is removed from the browser memory after 45s. Future click on the button re-download the document.
## v2.23.0 - 2024-07-19 & 2024-07-23
### Feature
* ([#123](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/123)) Add a button to duplicate calendar ranges from a week to another one
* [admin] filter users by active / inactive in the admin user's list
* ([#273](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/273)) Add the possibility to mark all notifications as read
* Handle duplicate reference id in the import of reference addresses
* Do not update the "createdAt" column when importing postal code which does not change
* Display filename on file upload within the UI interface
### Fixed
* ([#271](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/271)) Take into account the acp closing date in the acp works date filter
### Traduction française des principaux changements
- Ajout d'un bouton pour dupliquer les périodes de disponibilités d'une semaine à une autre;
- dans l'interface d'administration, filtre sur les utilisateurs actifs. Par défaut, seul les utilisateurs
actifs sont affichés;
- Nouveau bouton pour indiquer toutes les notifications comme lues;
- Améliorations sur l'import des adresses et des codes postaux;
- Affiche le nom du fichier déposé quand on téléverse un fichier depuis le poste de travail local;
- Agrandit l'icône du type de fichier dans l'interface de dépôt de fichier;
- correction: tient compte de la date de fermeture du parcours dans les filtres sur les actions d'accompagnement.
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
* Add job bundle (module emploi)
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
* Add job bundle (module emploi)
* Upgrade import of address list to the last version of compiled addresses of belgian-best-address
* Upgrade CKEditor and refactor configuration with use of typescript
### Fixed
* Fix resolving of centers for an household, which will fix in turn the access control
* Resolved type hinting error in activity list export
* Fix resolving of centers for an household, which will fix in turn the access control
* Resolved type hinting error in activity list export
## v2.22.2 - 2024-07-03
### Fixed
* Remove scope required for event participation stats
* Remove scope required for event participation stats
## v2.22.1 - 2024-07-01
### Fixed
* Remove debug word
* Remove debug word
### DX
* Add a command for reading official address DB from Luxembourg and update chill addresses
* Add a command for reading official address DB from Luxembourg and update chill addresses
## v2.22.0 - 2024-06-25
### Feature
@@ -104,7 +75,7 @@ and is generated by [Changie](https://github.com/miniscruff/changie).
## v2.20.1 - 2024-06-05
### Fixed
* Do not allow StoredObjectCreated for edit and convert buttons
* Do not allow StoredObjectCreated for edit and convert buttons
## v2.20.0 - 2024-06-05
### Fixed
@@ -151,96 +122,96 @@ and is generated by [Changie](https://github.com/miniscruff/changie).
## v2.18.2 - 2024-04-12
### Fixed
* ([#250](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/250)) Postal codes import : fix the source URL and the keys to handle each record
* ([#250](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/250)) Postal codes import : fix the source URL and the keys to handle each record
## v2.18.1 - 2024-03-26
### Fixed
* Fix layout issue in document generation for admin (minor)
* Fix layout issue in document generation for admin (minor)
## v2.18.0 - 2024-03-26
### Feature
* ([#268](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/268)) Improve admin UX to configure document templates for document generation
* ([#268](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/268)) Improve admin UX to configure document templates for document generation
### Fixed
* ([#267](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/267)) Fix the join between job and user in the user list (admin): show only the current user job
* ([#267](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/267)) Fix the join between job and user in the user list (admin): show only the current user job
## v2.17.0 - 2024-03-19
### Feature
* ([#237](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/237)) New export filter for social actions with an evaluation created between two dates
* ([#258](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/258)) In the list of accompangying period, add the list of person's centers and the duration of the course
* ([#238](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/238)) Allow to customize list person with new fields
* ([#237](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/237)) New export filter for social actions with an evaluation created between two dates
* ([#258](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/258)) In the list of accompangying period, add the list of person's centers and the duration of the course
* ([#238](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/238)) Allow to customize list person with new fields
* ([#159](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/159)) Admin can publish news on the homepage
### Fixed
* ([#264](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/264)) Fix languages: load the languages in all availables languages configured for Chill
* ([#259](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/259)) Keep a consistent behaviour between the filtering of activities within the document generation (model "accompanying period with activities"), and the same filter in the list of activities for an accompanying period
* ([#264](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/264)) Fix languages: load the languages in all availables languages configured for Chill
* ([#259](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/259)) Keep a consistent behaviour between the filtering of activities within the document generation (model "accompanying period with activities"), and the same filter in the list of activities for an accompanying period
## v2.16.3 - 2024-02-26
### Fixed
* ([#236](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/236)) Fix translation of user job -> 'service' must be 'métier'
* ([#236](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/236)) Fix translation of user job -> 'service' must be 'métier'
### UX
* ([#232](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/232)) Order user jobs and services alphabetically in export filters
* ([#232](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/232)) Order user jobs and services alphabetically in export filters
## v2.16.2 - 2024-02-21
### Fixed
* Check for null values in closing motive of parcours d'accompagnement for correct rendering of template
* Check for null values in closing motive of parcours d'accompagnement for correct rendering of template
## v2.16.1 - 2024-02-09
### Fixed
* Force bootstrap version to avoid error in builds with newer version
* Force bootstrap version to avoid error in builds with newer version
## v2.16.0 - 2024-02-08
### Feature
* ([#231](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/231)) Create new filter for persons having a participation in an accompanying period during a certain time span
* ([#241](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/241)) [Export][List of accompanyign period] Add two columns: the list of persons participating to the period, and their ids
* ([#244](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/244)) Add capability to generate export about change of steps of accompanying period, and generate exports for this
* ([#253](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/253)) Export: group accompanying period by person participating
* ([#243](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/243)) Export: add filter for courses not linked to a reference address
* ([#229](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/229)) Allow to group activities linked with accompanying period by reason
* ([#115](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/115)) Prevent social work to be saved when another user edited conccurently the social work
* Modernize the event bundle, with some new fields and multiple improvements
* ([#231](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/231)) Create new filter for persons having a participation in an accompanying period during a certain time span
* ([#241](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/241)) [Export][List of accompanyign period] Add two columns: the list of persons participating to the period, and their ids
* ([#244](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/244)) Add capability to generate export about change of steps of accompanying period, and generate exports for this
* ([#253](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/253)) Export: group accompanying period by person participating
* ([#243](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/243)) Export: add filter for courses not linked to a reference address
* ([#229](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/229)) Allow to group activities linked with accompanying period by reason
* ([#115](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/115)) Prevent social work to be saved when another user edited conccurently the social work
* Modernize the event bundle, with some new fields and multiple improvements
### Fixed
* ([#220](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/220)) Fix error in logs about wrong typing of eventArgs in onEditNotificationComment method
* ([#256](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/256)) Fix the conditions upon which social actions should be optional or required in relation to social issues within the activity creation form
* ([#220](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/220)) Fix error in logs about wrong typing of eventArgs in onEditNotificationComment method
* ([#256](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/256)) Fix the conditions upon which social actions should be optional or required in relation to social issues within the activity creation form
### UX
* ([#260](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/260)) Order list of centers alphabetically in dropdown 'user' section admin.
* ([#260](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/260)) Order list of centers alphabetically in dropdown 'user' section admin.
## v2.15.2 - 2024-01-11
### Fixed
* Fix the id_seq used when creating a new accompanying period participation during fusion of two person files
* Fix the id_seq used when creating a new accompanying period participation during fusion of two person files
### DX
* Set placeholder to False for expanded EntityType form fields where required is set to False.
* Set placeholder to False for expanded EntityType form fields where required is set to False.
## v2.15.1 - 2023-12-20
### Fixed
* Fix the household export query to exclude accompanying periods that are in draft state.
* Fix the household export query to exclude accompanying periods that are in draft state.
### DX
* ([#167](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/167)) Fixed readthedocs compilation by updating readthedocs config file and requirements for Sphinx
* ([#167](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/167)) Fixed readthedocs compilation by updating readthedocs config file and requirements for Sphinx
## v2.15.0 - 2023-12-11
### Feature
* ([#191](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/191)) Add export "number of household associate with an exchange"
* ([#235](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/235)) Export: add dates on the filter "filter course by activity type"
* ([#191](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/191)) Add export "number of household associate with an exchange"
* ([#235](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/235)) Export: add dates on the filter "filter course by activity type"
### Fixed
* ([#214](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/214)) Fix error when posting an empty comment on an accompanying period.
* ([#233](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/233)) Fix "filter evaluation by evaluation type" (and add select2 to the list of evaluation types to pick)
* ([#214](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/214)) Fix error when posting an empty comment on an accompanying period.
* ([#233](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/233)) Fix "filter evaluation by evaluation type" (and add select2 to the list of evaluation types to pick)
* ([#234](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/234)) Fix "filter aside activity by date"
* ([#228](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/228)) Fix export of activity for people created before the introduction of the createdAt column on person (during v1)
* ([#246](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/246)) Do not show activities, evaluations and social work when associated to a confidential accompanying period, except for the users which are allowed to see them
* ([#228](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/228)) Fix export of activity for people created before the introduction of the createdAt column on person (during v1)
* ([#246](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/246)) Do not show activities, evaluations and social work when associated to a confidential accompanying period, except for the users which are allowed to see them
## v2.14.1 - 2023-11-29
### Fixed
* Export: fix list person with custom fields
* ([#100](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/100)) Add a paginator to budget elements (resource and charge types) in the admin
* Fix error in ListEvaluation when "handling agents" are alone
* Export: fix list person with custom fields
* ([#100](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/100)) Add a paginator to budget elements (resource and charge types) in the admin
* Fix error in ListEvaluation when "handling agents" are alone
## v2.14.0 - 2023-11-24
### Feature
* ([#161](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/161)) Export: in filter "Filter accompanying period work (social action) by type, goal and result", order the items alphabetically or with the defined order
* ([#161](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/161)) Export: in filter "Filter accompanying period work (social action) by type, goal and result", order the items alphabetically or with the defined order
### Fixed
* ([#141](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/141)) Export: on filter "action by type goals, and results", restore the fields when editing a saved export
* ([#219](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/219)) Export: fix the list of accompanying period work, when the "calc date" is null
* ([#222](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/222)) Fix rendering of custom fields
* Fix various errors in custom fields administration
* ([#141](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/141)) Export: on filter "action by type goals, and results", restore the fields when editing a saved export
* ([#219](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/219)) Export: fix the list of accompanying period work, when the "calc date" is null
* ([#222](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/222)) Fix rendering of custom fields
* Fix various errors in custom fields administration
## v2.13.0 - 2023-11-21
### Feature
@@ -254,7 +225,7 @@ and is generated by [Changie](https://github.com/miniscruff/changie).
## v2.12.1 - 2023-11-16
### Fixed
* ([#208](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/208)) Export: fix loading of form for "filter action by type, goal and result"
* ([#208](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/208)) Export: fix loading of form for "filter action by type, goal and result"
## v2.12.0 - 2023-11-15
### Feature
@@ -285,36 +256,36 @@ and is generated by [Changie](https://github.com/miniscruff/changie).
## v2.11.0 - 2023-11-07
### Feature
* ([#194](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/194)) Export: add a filter "filter activity by creator job"
* ([#194](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/194)) Export: add a filter "filter activity by creator job"
### Fixed
* ([#185](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/185)) Export: fix "group accompanying period by geographical unit": take into account the accompanying periods when the period is not located within an unit
* Fix "group activity by creator job" aggregator
* ([#185](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/185)) Export: fix "group accompanying period by geographical unit": take into account the accompanying periods when the period is not located within an unit
* Fix "group activity by creator job" aggregator
## v2.10.6 - 2023-11-07
### Fixed
* ([#182](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/182)) Fix merging of double person files. Adjustement relationship sql statement
* ([#185](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/185)) Export: fix aggregator by geographical unit on person: avoid inconsistencies
* ([#182](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/182)) Fix merging of double person files. Adjustement relationship sql statement
* ([#185](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/185)) Export: fix aggregator by geographical unit on person: avoid inconsistencies
## v2.10.5 - 2023-11-05
### Fixed
* ([#183](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/183)) Fix "problem during download" on some filters, which used a wrong data type
* ([#184](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/184)) Fix filter "activity by date"
* ([#183](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/183)) Fix "problem during download" on some filters, which used a wrong data type
* ([#184](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/184)) Fix filter "activity by date"
## v2.10.4 - 2023-10-26
### Fixed
* Fix null value constraint errors when merging relationships in doubles
* Fix null value constraint errors when merging relationships in doubles
## v2.10.3 - 2023-10-26
### Fixed
* ([#175](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/175)) Replace old method of getting translator with injection of translatorInterface
* ([#175](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/175)) Replace old method of getting translator with injection of translatorInterface
## v2.10.2 - 2023-10-26
### Fixed
* ([#175](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/175)) Use injection of translator instead of ->get().
* ([#175](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/175)) Use injection of translator instead of ->get().
## v2.10.1 - 2023-10-24
### Fixed
* Fix export controller when generating an export without any data in session
* Fix export controller when generating an export without any data in session
## v2.10.0 - 2023-10-24
### Feature
@@ -339,11 +310,11 @@ and is generated by [Changie](https://github.com/miniscruff/changie).
## v2.9.2 - 2023-10-17
### Fixed
* Fix possible null values in string's entities
* Fix possible null values in string's entities
## v2.9.1 - 2023-10-17
### Fixed
* Fix the handling of activity form when editing or creating an activity in an accompanying period with multiple centers
* Fix the handling of activity form when editing or creating an activity in an accompanying period with multiple centers
## v2.9.0 - 2023-10-17
### Feature
@@ -391,57 +362,57 @@ But if you do not need this any more, you must ensure that the configuration key
## v2.7.0 - 2023-09-27
### Feature
* ([#155](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/155)) The regulation list load accompanying periods by exact postal code (address associated with postal code), and not by the content of the postal code (postal code with same code's string)
* ([#155](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/155)) The regulation list load accompanying periods by exact postal code (address associated with postal code), and not by the content of the postal code (postal code with same code's string)
### Fixed
* ([#142](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/142)) Fix the label of filter ActivityTypeFilter to a more obvious one
* ([#140](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/140)) [export] Fix association of filter "filter location by type" which did not appears on "list of activities"
* ([#142](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/142)) Fix the label of filter ActivityTypeFilter to a more obvious one
* ([#140](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/140)) [export] Fix association of filter "filter location by type" which did not appears on "list of activities"
## v2.6.3 - 2023-09-19
### Fixed
* Remove id property from document
mappedsuperclass
* Remove id property from document
mappedsuperclass
## v2.6.2 - 2023-09-18
### Fixed
* Fix doctrine mapping of AbstractTaskPlaceEvent and SingleTaskPlaceEvent: id property moved.
* Fix doctrine mapping of AbstractTaskPlaceEvent and SingleTaskPlaceEvent: id property moved.
## v2.6.1 - 2023-09-14
### Fixed
* Filter out active centers in exports, which uses a different PickCenterType.
* Filter out active centers in exports, which uses a different PickCenterType.
## v2.6.0 - 2023-09-14
### Feature
* ([#133](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/133)) Add locations in Aside Activity. By default, suggest user location, otherwise a select with all locations.
* ([#133](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/133)) Adapt Aside Activity exports: display location, filter by location, group by location
* Use the CRUD controller for center entity + add the isActive property to be able to mask instances of Center that are no longer in use.
* ([#133](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/133)) Add locations in Aside Activity. By default, suggest user location, otherwise a select with all locations.
* ([#133](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/133)) Adapt Aside Activity exports: display location, filter by location, group by location
* Use the CRUD controller for center entity + add the isActive property to be able to mask instances of Center that are no longer in use.
### Fixed
* ([#107](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/107)) reinstate the fusion of duplicate persons
* Missing translation in Work Actions exports
* Reimplement the mission type filter on tasks, only for instances that have a config parameter indicating true for this.
* ([#135](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/135)) Corrects a typing error in 2 filters, which caused an
* ([#107](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/107)) reinstate the fusion of duplicate persons
* Missing translation in Work Actions exports
* Reimplement the mission type filter on tasks, only for instances that have a config parameter indicating true for this.
* ([#135](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/135)) Corrects a typing error in 2 filters, which caused an
error when trying to reedit a saved export
* ([#136](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/136)) [household] when moving a person to a sharing position to a not-sharing position on the same household on the same date, remove the previous household membership on the same household. This fix duplicate member.
* ([#136](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/136)) [household] when moving a person to a sharing position to a not-sharing position on the same household on the same date, remove the previous household membership on the same household. This fix duplicate member.
* Add missing translation for comment field placeholder in repositionning household editor.
* Do not send an email to creator twice when adding a comment to a notification
* ([#107](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/107)) Fix gestion doublon functionality to work with chill bundles v2
* Do not send an email to creator twice when adding a comment to a notification
* ([#107](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/107)) Fix gestion doublon functionality to work with chill bundles v2
### UX
* Uniformize badge-person in household banner (background, size)
## v2.5.3 - 2023-07-20
### Fixed
* ([#132](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/132)) Rendez-vous documents created would appear in all documents lists of all persons with an accompanying period. Or statements are now added to the where clause to filter out documents that come from unrelated accompanying period/ or person rendez-vous.
* ([#132](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/132)) Rendez-vous documents created would appear in all documents lists of all persons with an accompanying period. Or statements are now added to the where clause to filter out documents that come from unrelated accompanying period/ or person rendez-vous.
## v2.5.2 - 2023-07-15
### Fixed
* [Collate Address] when updating address point, do not use the point's address reference if the similarity is below the requirement for associating the address reference and the address (it uses the postcode's center instead)
* [Collate Address] when updating address point, do not use the point's address reference if the similarity is below the requirement for associating the address reference and the address (it uses the postcode's center instead)
## v2.5.1 - 2023-07-14
### Fixed
* [collate addresses] block collating addresses to another address reference where the address reference is already the best match
* [collate addresses] block collating addresses to another address reference where the address reference is already the best match
## v2.5.0 - 2023-07-14
### Feature

View File

@@ -43,7 +43,6 @@
"symfony/dom-crawler": "^5.4",
"symfony/error-handler": "^5.4",
"symfony/event-dispatcher": "^5.4",
"symfony/event-dispatcher-contracts": "^2.4",
"symfony/expression-language": "^5.4",
"symfony/filesystem": "^5.4",
"symfony/finder": "^5.4",

View File

@@ -39,12 +39,9 @@ Implements a :code:`Chill\MainBundle\Cron\CronJobInterface`. Here is an example:
use Chill\MainBundle\Entity\CronJobExecution;
use DateInterval;
use DateTimeImmutable;
use Symfony\Component\Clock\ClockInterface;
class MyCronJob implements CronJobInterface
{
function __construct(private ClockInterface $clock) {}
public function canRun(?CronJobExecution $cronJobExecution): bool
{
// the parameter $cronJobExecution contains data about the last execution of the cronjob
@@ -59,7 +56,7 @@ Implements a :code:`Chill\MainBundle\Cron\CronJobInterface`. Here is an example:
// this cron job should be executed if the last execution is greater than one day, but only during the night
$now = $clock->now();
$now = new DateTimeImmutable('now');
return $cronJobExecution->getLastStart() < $now->sub(new DateInterval('P1D'))
&& in_array($now->format('H'), self::ACCEPTED_HOURS, true)
@@ -72,15 +69,10 @@ Implements a :code:`Chill\MainBundle\Cron\CronJobInterface`. Here is an example:
return 'arbitrary-and-unique-key';
}
public function run(array $lastExecutionData): void
public function run(): void
{
// here, we execute the command
// we return execution data, which will be served for next execution
// this data should be easily serializable in a json column: it should contains
// only int, string, etc. Avoid storing object
return ['last-execution-id' => 0];
}
}
}
How are cron job scheduled ?

View File

@@ -1,125 +0,0 @@
Enable CODE for development
===========================
For editing a document, there must be a way to communicate between the collabora server and the symfony server, in
both direction. The domain name should also be the same for collabora server and for the browser which access to the
online editor.
Using ngrok (or other http tunnel)
----------------------------------
One can configure a tunnel server to expose your local install to the web, and access to your local server using the
tunnel url.
Start ngrok
^^^^^^^^^^^
This can be achieve using `ngrok <https://ngrok.com/>`_.
.. note::
The configuration of ngrok is outside of the scope of this document. Refers to the ngrok's documentation.
.. code-block:: bash
# ensuring that your server is running through http and port 8000
ngrok http 8000
# then open the link given by the ngrok utility and you should reach your app
At this step, ensure that you can reach your local app using the ngrok url.
Configure Collabora
^^^^^^^^^^^^^^^^^^^
The collabora server must be executed online and configure to access to your ngrok installation. Ensure that the aliasgroup
exists for your ngrok application (`See the CODE documentation: <https://sdk.collaboraonline.com/docs/installation/Configuration.html#multihost-configuration>`_).
Configure your app
^^^^^^^^^^^^^^^^^^
Set the :code:`EDITOR_SERVER` variable to point to your collabora server, this should be done in your :code:`.env.local` file.
At this point, everything must be fine. In case of errors, watch the log from your collabora server, use the `profiler <https://symfony.com/doc/current/profiler.html>`_
to debug the requests.
.. note::
In case of error while validating proof (you'll see those message in the collabora's logs), you can temporarily disable
the proof validation adding this code snippet in `config/services.yaml`:
.. code-block:: yaml
when@dev:
# add only in dev environment, to avoid security problems
services:
ChampsLibres\WopiLib\Contract\Service\ProofValidatorInterface:
# this class will always validate proof
alias: Chill\WopiBundle\Service\Wopi\NullProofValidator
With a local CODE image
-----------------------
.. warning::
This configuration is not sure, and must be refined. The documentation does not seems to be entirely valid.
Use a local domain name and https for your app
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use the proxy feature from embedded symfony server to run your app. `See the dedicated doc <https://symfony.com/doc/current/setup/symfony_server.html#local-domain-names>`
Configure also the `https certificate <https://symfony.com/doc/current/setup/symfony_server.html#enabling-tls>`_
In this example, your local domain name will be :code:`my-domain` and the url will be :code:`https://my-domain.wip`.
Ensure that the proxy is running.
Create a certificate database for collabora
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Collabora must validate your certificate generated by symfony console. For that, you need `to create a NSS database <https://sdk.collaboraonline.com/docs/installation/Configuration.html#validating-digital-signatures>`
and configure collabora to use it.
At first, export the certificate for symfony development. Use the graphical interface from your browser to get the
certificate as a PEM file.
.. code-block:: bash
# create your database in a custom directory
mkdir /path/to/your/directory
certutil -N -d /path/to/your/directory
cat /path/to/your/ca.crt | certutil -d . -A symfony -t -t C,P,C,u,w -a
Launch CODE properly configured
.. code-block:: yaml
collabora:
image: collabora/code:latest
environment:
- SLEEPFORDEBUGGER=0
- DONT_GEN_SSL_CERT="True"
# add path to the database
- extra_params=--o:ssl.enable=false --o:ssl.termination=false --o:logging.level=7 -o:certificates.database_path=/etc/custom-certificates/nss-database
- username=admin
- password=admin
- dictionaries=en_US
- aliasgroup1=https://my-domain.wip
ports:
- "127.0.0.1:9980:9980"
volumes:
- "/path/to/your/directory/nss-database:/etc/custom-certificates/nss-database"
extra_hosts:
- "my-domain.wip:host-gateway"
Configure your app
^^^^^^^^^^^^^^^^^^
Into your :code:`.env.local` file:
.. code-block:: env
EDITOR_SERVER=http://${COLLABORA_HOST}:${COLLABORA_PORT}
At this step, you should be able to edit a document through collabora.

View File

@@ -54,8 +54,9 @@
"masonry-layout": "^4.2.2",
"mime": "^4.0.0",
"pdfjs-dist": "^4.3.136",
"swagger-ui": "^4.15.5",
"vis-network": "^9.1.0",
"vue": "^3.5.6",
"vue": "^3.2.37",
"vue-i18n": "^9.1.6",
"vue-multiselect": "3.0.0-alpha.2",
"vue-toast-notification": "^3.1.2",

View File

@@ -68,7 +68,7 @@ final class ActivityController extends AbstractController
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly PaginatorFactory $paginatorFactory,
private readonly ChillSecurity $security,
private readonly ChillSecurity $security
) {}
/**

View File

@@ -28,7 +28,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
public function __construct(
protected ActivityReasonCategoryRepository $activityReasonCategoryRepository,
protected ActivityReasonRepository $activityReasonRepository,
protected TranslatableStringHelper $translatableStringHelper,
protected TranslatableStringHelper $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -26,7 +26,7 @@ class ActivityUsersJobAggregator implements AggregatorInterface
public function __construct(
private readonly UserJobRepositoryInterface $userJobRepository,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly TranslatableStringHelperInterface $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -26,7 +26,7 @@ class ActivityUsersScopeAggregator implements AggregatorInterface
public function __construct(
private readonly ScopeRepositoryInterface $scopeRepository,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly TranslatableStringHelperInterface $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -26,7 +26,7 @@ class CreatorJobAggregator implements AggregatorInterface
public function __construct(
private readonly UserJobRepositoryInterface $userJobRepository,
private readonly TranslatableStringHelper $translatableStringHelper,
private readonly TranslatableStringHelper $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -26,7 +26,7 @@ class CreatorScopeAggregator implements AggregatorInterface
public function __construct(
private readonly ScopeRepository $scopeRepository,
private readonly TranslatableStringHelper $translatableStringHelper,
private readonly TranslatableStringHelper $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -1,99 +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\ActivityBundle\Export\Aggregator\PersonAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Repository\Household\HouseholdRepository;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class HouseholdAggregator implements AggregatorInterface
{
public function __construct(private HouseholdRepository $householdRepository) {}
public function buildForm(FormBuilderInterface $builder)
{
// nothing to add here
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, mixed $data)
{
return function (int|string|null $value): string|int {
if ('_header' === $value) {
return 'export.aggregator.person.by_household.household';
}
if ('' === $value || null === $value || null === $household = $this->householdRepository->find($value)) {
return '';
}
return $household->getId();
};
}
public function getQueryKeys($data)
{
return ['activity_household_agg'];
}
public function getTitle()
{
return 'export.aggregator.person.by_household.title';
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join(
HouseholdMember::class,
'activity_household_agg_household_member',
Join::WITH,
$qb->expr()->andX(
$qb->expr()->eq('activity_household_agg_household_member.person', 'activity.person'),
$qb->expr()->lte('activity_household_agg_household_member.startDate', 'activity.date'),
$qb->expr()->orX(
$qb->expr()->gte('activity_household_agg_household_member.endDate', 'activity.date'),
$qb->expr()->isNull('activity_household_agg_household_member.endDate')
)
)
);
$qb->join(
Household::class,
'activity_household_agg_household',
Join::WITH,
$qb->expr()->eq('activity_household_agg_household_member.household', 'activity_household_agg_household')
);
$qb
->addSelect('activity_household_agg_household.id AS activity_household_agg')
->addGroupBy('activity_household_agg');
}
public function applyOn()
{
return Declarations::ACTIVITY_PERSON;
}
}

View File

@@ -19,7 +19,6 @@ use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\ORM\EntityManagerInterface;
@@ -45,7 +44,6 @@ class ListActivity implements ListInterface, GroupedExportInterface
'person_firstname',
'person_lastname',
'person_id',
'household_id',
];
private readonly bool $filterStatsByCenters;
@@ -191,26 +189,19 @@ class ListActivity implements ListInterface, GroupedExportInterface
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
// throw an error if no fields are present
// throw an error if any fields are present
if (!\array_key_exists('fields', $data)) {
throw new InvalidArgumentException('No fields have been checked.');
throw new InvalidArgumentException('Any fields have been checked.');
}
$qb = $this->entityManager->createQueryBuilder();
$qb
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.person', 'person')
->join(
HouseholdMember::class,
'householdmember',
Query\Expr\Join::WITH,
'person = householdmember.person AND householdmember.startDate <= activity.date AND (householdmember.endDate IS NULL OR householdmember.endDate > activity.date)'
)
->join('householdmember.household', 'household');
->join('activity.person', 'actperson');
if ($this->filterStatsByCenters) {
$qb->join('person.centerHistory', 'centerHistory');
$qb->join('actperson.centerHistory', 'centerHistory');
$qb->where(
$qb->expr()->andX(
$qb->expr()->lte('centerHistory.startDate', 'activity.date'),
@@ -233,22 +224,17 @@ class ListActivity implements ListInterface, GroupedExportInterface
break;
case 'person_firstname':
$qb->addSelect('person.firstName AS person_firstname');
$qb->addSelect('actperson.firstName AS person_firstname');
break;
case 'person_lastname':
$qb->addSelect('person.lastName AS person_lastname');
$qb->addSelect('actperson.lastName AS person_lastname');
break;
case 'person_id':
$qb->addSelect('person.id AS person_id');
break;
case 'household_id':
$qb->addSelect('household.id AS household_id');
$qb->addSelect('actperson.id AS person_id');
break;
@@ -298,7 +284,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return ActivityStatsVoter::LISTS;
}
public function supportsModifiers(): array
public function supportsModifiers()
{
return [
Declarations::ACTIVITY,

View File

@@ -42,7 +42,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
/**
* The action for this report.
*/
protected string $action = 'sum',
protected string $action = 'sum'
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}

View File

@@ -39,7 +39,7 @@ class ListActivityHelper
private readonly TranslatorInterface $translator,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly TranslatableStringExportLabelHelper $translatableStringLabelHelper,
private readonly UserHelper $userHelper,
private readonly UserHelper $userHelper
) {}
public function addSelect(QueryBuilder $qb): void

View File

@@ -73,7 +73,7 @@ final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInt
$qb->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = acp"
'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = activity.accompanyingPeriod"
)
);

View File

@@ -25,7 +25,7 @@ final readonly class ActivityPresenceFilter implements FilterInterface
{
public function __construct(
private TranslatableStringHelperInterface $translatableStringHelper,
private TranslatorInterface $translator,
private TranslatorInterface $translator
) {}
public function getTitle()

View File

@@ -26,7 +26,7 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
{
public function __construct(
protected TranslatableStringHelperInterface $translatableStringHelper,
protected ActivityTypeRepositoryInterface $activityTypeRepository,
protected ActivityTypeRepositoryInterface $activityTypeRepository
) {}
public function addRole(): ?string

View File

@@ -39,7 +39,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
public function alterQuery(QueryBuilder $qb, $data)
{
// create a subquery for activity
$sqb = $qb->getEntityManager()->createQueryBuilder();
@@ -121,7 +121,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
];
}
public function describeAction($data, $format = 'string'): array
public function describeAction($data, $format = 'string')
{
return [
[] === $data['reasons'] ?
@@ -141,7 +141,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
];
}
public function getTitle(): string
public function getTitle()
{
return 'export.filter.activity.person_between_dates.title';
}

View File

@@ -29,7 +29,7 @@ class UsersJobFilter implements FilterInterface
public function __construct(
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly UserJobRepositoryInterface $userJobRepository,
private readonly UserJobRepositoryInterface $userJobRepository
) {}
public function addRole(): ?string

View File

@@ -29,7 +29,7 @@ class UsersScopeFilter implements FilterInterface
public function __construct(
private readonly ScopeRepositoryInterface $scopeRepository,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly TranslatableStringHelperInterface $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -59,7 +59,7 @@ class ActivityType extends AbstractType
protected TranslatableStringHelper $translatableStringHelper,
protected array $timeChoices,
protected SocialIssueRender $socialIssueRender,
protected SocialActionRender $socialActionRender,
protected SocialActionRender $socialActionRender
) {
if (!$tokenStorage->getToken()->getUser() instanceof User) {
throw new \RuntimeException('you should have a valid user');

View File

@@ -27,7 +27,7 @@ class PickActivityReasonType extends AbstractType
public function __construct(
private readonly ActivityReasonRepository $activityReasonRepository,
private readonly ActivityReasonRender $reasonRender,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly TranslatableStringHelperInterface $translatableStringHelper
) {}
public function configureOptions(OptionsResolver $resolver)

View File

@@ -32,7 +32,7 @@ final readonly class ActivityDocumentACLAwareRepository implements ActivityDocum
private EntityManagerInterface $em,
private CenterResolverManagerInterface $centerResolverManager,
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser,
private Security $security,
private Security $security
) {}
public function buildFetchQueryActivityDocumentLinkedToPersonFromPersonContext(Person $person, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null): FetchQueryInterface

View File

@@ -25,7 +25,7 @@ class ActivityReasonRepository extends ServiceEntityRepository
{
public function __construct(
ManagerRegistry $registry,
private readonly RequestStack $requestStack,
private readonly RequestStack $requestStack
) {
parent::__construct($registry, ActivityReason::class);
}

View File

@@ -24,7 +24,7 @@ class ActivityStoredObjectVoter extends AbstractStoredObjectVoter
public function __construct(
private readonly ActivityRepository $repository,
Security $security,
WorkflowStoredObjectPermissionHelper $workflowDocumentService,
WorkflowStoredObjectPermissionHelper $workflowDocumentService
) {
parent::__construct($security, $workflowDocumentService);
}

View File

@@ -75,7 +75,7 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
public function __construct(
protected Security $security,
VoterHelperFactoryInterface $voterHelperFactory,
VoterHelperFactoryInterface $voterHelperFactory
) {
$this->voterHelper = $voterHelperFactory->generate(self::class)
->addCheckFor(Person::class, [self::SEE, self::CREATE])

View File

@@ -50,7 +50,7 @@ class ActivityContext implements
private readonly TranslatorInterface $translator,
private readonly BaseContextData $baseContextData,
private readonly ThirdPartyRender $thirdPartyRender,
private readonly ThirdPartyRepository $thirdPartyRepository,
private readonly ThirdPartyRepository $thirdPartyRepository
) {}
public function adminFormReverseTransform(array $data): array

View File

@@ -56,7 +56,7 @@ class ListActivitiesByAccompanyingPeriodContext implements
private readonly SocialIssueRepository $socialIssueRepository,
private readonly ThirdPartyRepository $thirdPartyRepository,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly UserRepository $userRepository,
private readonly UserRepository $userRepository
) {}
public function adminFormReverseTransform(array $data): array

View File

@@ -76,7 +76,7 @@ final class TranslatableActivityReasonTest extends TypeTestCase
*/
protected function getTranslatableStringHelper(
$locale = 'en',
$fallbackLocale = 'en',
$fallbackLocale = 'en'
) {
$prophet = new \Prophecy\Prophet();
$requestStack = $prophet->prophesize();

View File

@@ -138,7 +138,7 @@ final class ActivityVoterTest extends KernelTestCase
Scope $scope,
Center $center,
$attribute,
$message,
$message
) {
$token = $this->prepareToken($user);
$activity = $this->prepareActivity($scope, $this->preparePerson($center));

View File

@@ -32,7 +32,7 @@ class TimelineActivityProvider implements TimelineProviderInterface
protected EntityManagerInterface $em,
protected AuthorizationHelperInterface $helper,
TokenStorageInterface $storage,
protected ActivityACLAwareRepository $aclAwareRepository,
protected ActivityACLAwareRepository $aclAwareRepository
) {
if (!$storage->getToken()->getUser() instanceof User) {
throw new \RuntimeException('A user should be authenticated !');

View File

@@ -243,7 +243,3 @@ services:
Chill\ActivityBundle\Export\Aggregator\PersonAggregators\PersonAggregator:
tags:
- { name: chill.export_aggregator, alias: activity_person_agg }
Chill\ActivityBundle\Export\Aggregator\PersonAggregators\HouseholdAggregator:
tags:
- { name: chill.export_aggregator, alias: activity_household_agg }

View File

@@ -428,9 +428,6 @@ export:
by_person:
title: Grouper les échanges par usager (dossier d'usager dans lequel l'échange est enregistré)
person: Usager
by_household:
title: Grouper les échanges par ménage
household: Identifiant ménage
acp:
by_activity_type:
title: Grouper les parcours par type d'échange

View File

@@ -25,7 +25,7 @@ final class AsideActivityController extends CRUDController
{
public function __construct(
private readonly AsideActivityCategoryRepository $categoryRepository,
private readonly Security $security,
private readonly Security $security
) {}
public function createEntity(string $action, Request $request): object
@@ -76,7 +76,7 @@ final class AsideActivityController extends CRUDController
string $action,
$query,
Request $request,
PaginatorInterface $paginator,
PaginatorInterface $paginator
) {
if ('index' === $action) {
return $query->orderBy('e.date', 'DESC');

View File

@@ -22,7 +22,7 @@ class ByActivityTypeAggregator implements AggregatorInterface
{
public function __construct(
private readonly AsideActivityCategoryRepository $asideActivityCategoryRepository,
private readonly TranslatableStringHelper $translatableStringHelper,
private readonly TranslatableStringHelper $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -26,7 +26,7 @@ class ByUserJobAggregator implements AggregatorInterface
public function __construct(
private readonly UserJobRepositoryInterface $userJobRepository,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly TranslatableStringHelperInterface $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -26,7 +26,7 @@ class ByUserScopeAggregator implements AggregatorInterface
public function __construct(
private readonly ScopeRepositoryInterface $scopeRepository,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly TranslatableStringHelperInterface $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -41,7 +41,7 @@ final readonly class ListAsideActivity implements ListInterface, GroupedExportIn
private AsideActivityCategoryRepository $asideActivityCategoryRepository,
private CategoryRender $categoryRender,
private LocationRepository $locationRepository,
private TranslatableStringHelperInterface $translatableStringHelper,
private TranslatableStringHelperInterface $translatableStringHelper
) {}
public function buildForm(FormBuilderInterface $builder) {}

View File

@@ -27,7 +27,7 @@ class ByActivityTypeFilter implements FilterInterface
public function __construct(
private readonly CategoryRender $categoryRender,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly AsideActivityCategoryRepository $asideActivityTypeRepository,
private readonly AsideActivityCategoryRepository $asideActivityTypeRepository
) {}
public function addRole(): ?string

View File

@@ -24,7 +24,7 @@ use Symfony\Component\Security\Core\Security;
final readonly class ByLocationFilter implements FilterInterface
{
public function __construct(
private Security $security,
private Security $security
) {}
public function getTitle(): string

View File

@@ -29,7 +29,7 @@ class ByUserJobFilter implements FilterInterface
public function __construct(
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly UserJobRepositoryInterface $userJobRepository,
private readonly UserJobRepositoryInterface $userJobRepository
) {}
public function addRole(): ?string

View File

@@ -29,7 +29,7 @@ class ByUserScopeFilter implements FilterInterface
public function __construct(
private readonly ScopeRepositoryInterface $scopeRepository,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly TranslatableStringHelperInterface $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -44,7 +44,7 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
CountNotificationTask $counter,
TokenStorageInterface $tokenStorage,
TranslatorInterface $translator,
AuthorizationCheckerInterface $authorizationChecker,
AuthorizationCheckerInterface $authorizationChecker
) {
$this->counter = $counter;
$this->tokenStorage = $tokenStorage;

View File

@@ -26,7 +26,7 @@ class AsideActivityVoter extends AbstractChillVoter implements ProvideRoleHierar
private readonly VoterHelperInterface $voterHelper;
public function __construct(
VoterHelperFactoryInterface $voterHelperFactory,
VoterHelperFactoryInterface $voterHelperFactory
) {
$this->voterHelper = $voterHelperFactory
->generate(self::class)

View File

@@ -47,7 +47,7 @@ class SendTestShortMessageOnCalendarCommand extends Command
private readonly PhoneNumberHelperInterface $phoneNumberHelper,
private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder,
private readonly ShortMessageTransporterInterface $transporter,
private readonly UserRepositoryInterface $userRepository,
private readonly UserRepositoryInterface $userRepository
) {
parent::__construct('chill:calendar:test-send-short-message');
}

View File

@@ -59,7 +59,7 @@ class CalendarController extends AbstractController
private readonly AccompanyingPeriodRepository $accompanyingPeriodRepository,
private readonly UserRepositoryInterface $userRepository,
private readonly TranslatorInterface $translator,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
) {}
/**

View File

@@ -47,7 +47,7 @@ class CalendarDoc implements TrackCreationInterface, TrackUpdateInterface
Calendar $calendar,
#[ORM\ManyToOne(targetEntity: StoredObject::class, cascade: ['persist'])]
#[ORM\JoinColumn(nullable: false)]
private ?StoredObject $storedObject,
private ?StoredObject $storedObject
) {
$this->setCalendar($calendar);
$this->datetimeVersion = $calendar->getDateTimeVersion();

View File

@@ -26,7 +26,7 @@ final readonly class JobAggregator implements AggregatorInterface
public function __construct(
private UserJobRepository $jobRepository,
private TranslatableStringHelper $translatableStringHelper,
private TranslatableStringHelper $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -26,7 +26,7 @@ final readonly class ScopeAggregator implements AggregatorInterface
public function __construct(
private ScopeRepository $scopeRepository,
private TranslatableStringHelper $translatableStringHelper,
private TranslatableStringHelper $translatableStringHelper
) {}
public function addRole(): ?string

View File

@@ -28,7 +28,7 @@ final readonly class JobFilter implements FilterInterface
public function __construct(
private TranslatableStringHelper $translatableStringHelper,
private UserJobRepositoryInterface $userJobRepository,
private UserJobRepositoryInterface $userJobRepository
) {}
public function addRole(): ?string

View File

@@ -30,7 +30,7 @@ class ScopeFilter implements FilterInterface
public function __construct(
protected TranslatorInterface $translator,
private readonly TranslatableStringHelper $translatableStringHelper,
private readonly ScopeRepositoryInterface $scopeRepository,
private readonly ScopeRepositoryInterface $scopeRepository
) {}
public function addRole(): ?string

View File

@@ -37,7 +37,7 @@ class CalendarType extends AbstractType
private readonly IdToUsersDataTransformer $idToUsersDataTransformer,
private readonly IdToLocationDataTransformer $idToLocationDataTransformer,
private readonly ThirdPartiesToIdDataTransformer $partiesToIdDataTransformer,
private readonly IdToCalendarRangeDataTransformer $calendarRangeDataTransformer,
private readonly IdToCalendarRangeDataTransformer $calendarRangeDataTransformer
) {}
public function buildForm(FormBuilderInterface $builder, array $options)

View File

@@ -46,7 +46,7 @@ class CalendarMessage
public function __construct(
Calendar $calendar,
private readonly string $action,
User $byUser,
User $byUser
) {
$this->calendarId = $calendar->getId();
$this->byUserId = $byUser->getId();

View File

@@ -59,7 +59,7 @@ final readonly class MSUserAbsenceReader implements MSUserAbsenceReaderInterface
'alwaysEnabled' => true,
'scheduled' => RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledStartDateTime']['dateTime']) < $this->clock->now()
&& RemoteEventConverter::convertStringDateWithoutTimezone($automaticRepliesSettings['scheduledEndDateTime']['dateTime']) > $this->clock->now(),
default => throw new UserAbsenceSyncException('this status is not documented by Microsoft'),
default => throw new UserAbsenceSyncException('this status is not documented by Microsoft')
};
}
}

View File

@@ -177,7 +177,7 @@ class MapCalendarToUser
User $user,
int $expiration,
?string $id = null,
?string $secret = null,
?string $secret = null
): void {
$user->setAttributeByDomain(self::METADATA_KEY, self::EXPIRATION_SUBSCRIPTION_EVENT, $expiration);

View File

@@ -57,7 +57,7 @@ class RemoteEventConverter
private readonly LocationConverter $locationConverter,
private readonly LoggerInterface $logger,
private readonly PersonRenderInterface $personRender,
private readonly TranslatorInterface $translator,
private readonly TranslatorInterface $translator
) {
$this->defaultDateTimeZone = (new \DateTimeImmutable())->getTimezone();
$this->remoteDateTimeZone = self::getRemoteTimeZone();

View File

@@ -351,7 +351,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
[
'id' => $id,
'lastModifiedDateTime' => $lastModified,
'changeKey' => $changeKey,
'changeKey' => $changeKey
] = $this->createOnRemote($eventData, $calendar->getMainUser(), 'calendar_'.$calendar->getId());
if (null === $id) {
@@ -427,7 +427,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
[
'id' => $id,
'lastModifiedDateTime' => $lastModified,
'changeKey' => $changeKey,
'changeKey' => $changeKey
] = $this->createOnRemote(
$eventData,
$calendarRange->getUser(),
@@ -564,7 +564,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
[
'id' => $id,
'lastModifiedDateTime' => $lastModified,
'changeKey' => $changeKey,
'changeKey' => $changeKey
] = $this->patchOnRemote(
$calendar->getRemoteId(),
$eventData,

View File

@@ -33,6 +33,6 @@ class RemoteEvent
#[Serializer\Groups(['read'])]
public \DateTimeImmutable $endDate,
#[Serializer\Groups(['read'])]
public bool $isAllDay = false,
public bool $isAllDay = false
) {}
}

View File

@@ -65,7 +65,7 @@ class CalendarRangeRepository implements ObjectRepository
\DateTimeImmutable $from,
\DateTimeImmutable $to,
?int $limit = null,
?int $offset = null,
?int $offset = null
): array {
$qb = $this->buildQueryAvailableRangesForUser($user, $from, $to);

View File

@@ -1,7 +1,7 @@
<template>
<div class="row">
<div class="col-sm">
<label class="form-label">{{ $t("created_availabilities") }}</label>
<label class="form-label">{{ $t('created_availabilities') }}</label>
<vue-multiselect
v-model="pickedLocation"
:options="locations"
@@ -14,15 +14,10 @@
></vue-multiselect>
</div>
</div>
<div
class="display-options row justify-content-between"
style="margin-top: 1rem"
>
<div class="display-options row justify-content-between" style="margin-top: 1rem;">
<div class="col-sm-9 col-xs-12">
<div class="input-group mb-3">
<label class="input-group-text" for="slotDuration"
>Durée des créneaux</label
>
<label class="input-group-text" for="slotDuration">Durée des créneaux</label>
<select v-model="slotDuration" id="slotDuration" class="form-select">
<option value="00:05:00">5 minutes</option>
<option value="00:10:00">10 minutes</option>
@@ -63,20 +58,13 @@
</select>
</div>
</div>
<div class="col-xs-12 col-sm-3">
<div class="col-sm-3 col-xs-12">
<div class="float-end">
<div class="form-check input-group">
<span class="input-group-text">
<input
id="showHideWE"
class="mt-0"
type="checkbox"
v-model="showWeekends"
/>
<input id="showHideWE" class="mt-0" type="checkbox" v-model="showWeekends">
</span>
<label for="showHideWE" class="form-check-label input-group-text"
>Week-ends</label
>
<label for="showHideWE" class="form-check-label input-group-text">Week-ends</label>
</div>
</div>
</div>
@@ -84,86 +72,39 @@
<FullCalendar :options="calendarOptions" ref="calendarRef">
<template v-slot:eventContent="arg: EventApi">
<span :class="eventClasses(arg.event)">
<b v-if="arg.event.extendedProps.is === 'remote'">{{
arg.event.title
}}</b>
<b v-else-if="arg.event.extendedProps.is === 'range'"
>{{ arg.timeText }} - {{ arg.event.extendedProps.locationName }}</b
>
<b v-else-if="arg.event.extendedProps.is === 'local'">{{
arg.event.title
}}</b>
<b v-else>no 'is'</b>
<a
v-if="arg.event.extendedProps.is === 'range'"
class="fa fa-fw fa-times delete"
@click.prevent="onClickDelete(arg.event)"
>
</a>
</span>
<b v-if="arg.event.extendedProps.is === 'remote'">{{ arg.event.title}}</b>
<b v-else-if="arg.event.extendedProps.is === 'range'">{{ arg.timeText }} - {{ arg.event.extendedProps.locationName }}</b>
<b v-else-if="arg.event.extendedProps.is === 'local'">{{ arg.event.title}}</b>
<b v-else >no 'is'</b>
<a v-if="arg.event.extendedProps.is === 'range'" class="fa fa-fw fa-times delete"
@click.prevent="onClickDelete(arg.event)">
</a>
</span>
</template>
</FullCalendar>
<div id="copy-widget">
<div class="container mt-2 mb-2">
<div class="row justify-content-between align-items-center mb-4">
<div class="col-xs-12 col-sm-3 col-md-2">
<h6 class="chill-red">{{ $t("copy_range_from_to") }}</h6>
</div>
<div class="col-xs-12 col-sm-9 col-md-2">
<select v-model="dayOrWeek" id="dayOrWeek" class="form-select">
<option value="day">{{ $t("from_day_to_day") }}</option>
<option value="week">{{ $t("from_week_to_week") }}</option>
</select>
</div>
<template v-if="dayOrWeek === 'day'">
<div class="col-xs-12 col-sm-3 col-md-3">
<input class="form-control" type="date" v-model="copyFrom" />
</div>
<div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
<i class="fa fa-angle-double-right"></i>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<input class="form-control" type="date" v-model="copyTo" />
</div>
<div class="col-xs-12 col-sm-5 col-md-1">
<button class="btn btn-action float-end" @click="copyDay">
{{ $t("copy_range") }}
</button>
</div>
</template>
<template v-else>
<div class="col-xs-12 col-sm-3 col-md-3">
<select
v-model="copyFromWeek"
id="copyFromWeek"
class="form-select"
>
<option v-for="w in lastWeeks" :value="w.value" :key="w.value">
{{ w.text }}
</option>
</select>
</div>
<div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
<i class="fa fa-angle-double-right"></i>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<select v-model="copyToWeek" id="copyToWeek" class="form-select">
<option v-for="w in nextWeeks" :value="w.value" :key="w.value">
{{ w.text }}
</option>
</select>
</div>
<div class="col-xs-12 col-sm-5 col-md-1">
<button class="btn btn-action float-end" @click="copyWeek">
{{ $t("copy_range") }}
</button>
</div>
</template>
<div class="container">
<div class="row align-items-center">
<div class="col-sm-4 col-xs-12">
<h6 class="chill-red">{{ $t('copy_range_from_to') }}</h6>
</div>
<div class="col-sm-3 col-xs-12">
<input class="form-control" type="date" v-model="copyFrom" />
</div>
<div class="col-sm-1 col-xs-12" style="text-align: center; font-size: x-large;">
<i class="fa fa-angle-double-right"></i>
</div>
<div class="col-sm-3 col-xs-12" >
<input class="form-control" type="date" v-model="copyTo" />
</div>
<div class="col-sm-1">
<button class="btn btn-action" @click="copyDay">
{{ $t('copy_range') }}
</button>
</div>
</div>
</div>
</div>
<!-- not directly seen, but include in a modal -->
@@ -171,95 +112,42 @@
</template>
<script setup lang="ts">
import type {
CalendarOptions,
DatesSetArg,
EventInput,
} from "@fullcalendar/core";
import { reactive, computed, ref, onMounted } from "vue";
import { useStore } from "vuex";
import { key } from "./store";
import FullCalendar from "@fullcalendar/vue3";
import frLocale from "@fullcalendar/core/locales/fr";
import interactionPlugin, {
DropArg,
EventResizeDoneArg,
} from "@fullcalendar/interaction";
EventInput
} from '@fullcalendar/core';
import {reactive, computed, ref} from "vue";
import {useStore} from "vuex";
import {key} from './store';
import FullCalendar from '@fullcalendar/vue3';
import frLocale from '@fullcalendar/core/locales/fr';
import interactionPlugin, {DropArg, EventResizeDoneArg} from "@fullcalendar/interaction";
import timeGridPlugin from "@fullcalendar/timegrid";
import {
EventApi,
DateSelectArg,
EventDropArg,
EventClickArg,
} from "@fullcalendar/core";
import {
dateToISO,
ISOToDate,
} from "../../../../../ChillMainBundle/Resources/public/chill/js/date";
import {EventApi, DateSelectArg, EventDropArg, EventClickArg} from "@fullcalendar/core";
import {ISOToDate} from "../../../../../ChillMainBundle/Resources/public/chill/js/date";
import VueMultiselect from "vue-multiselect";
import { Location } from "../../../../../ChillMainBundle/Resources/public/types";
import {Location} from "../../../../../ChillMainBundle/Resources/public/types";
import EditLocation from "./Components/EditLocation.vue";
import { useI18n } from "vue-i18n";
import {useI18n} from "vue-i18n";
const store = useStore(key);
const { t } = useI18n();
const {t} = useI18n();
const showWeekends = ref(false);
const slotDuration = ref("00:15:00");
const slotMinTime = ref("09:00:00");
const slotMaxTime = ref("18:00:00");
const slotDuration = ref('00:05:00');
const slotMinTime = ref('09:00:00');
const slotMaxTime = ref('18:00:00');
const copyFrom = ref<string | null>(null);
const copyTo = ref<string | null>(null);
const editLocation = ref<InstanceType<typeof EditLocation> | null>(null);
const dayOrWeek = ref("day");
const copyFromWeek = ref<string | null>(null);
const copyToWeek = ref<string | null>(null);
interface Weeks {
value: string | null;
text: string;
}
const getMonday = (week: number): Date => {
const lastMonday = new Date();
lastMonday.setDate(
lastMonday.getDate() - ((lastMonday.getDay() + 6) % 7) + week * 7
);
return lastMonday;
};
const dateOptions: Intl.DateTimeFormatOptions = {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
};
const lastWeeks = computed((): Weeks[] =>
Array.from(Array(30).keys()).map((w) => {
const lastMonday = getMonday(15-w);
return {
value: dateToISO(lastMonday),
text: `Semaine du ${lastMonday.toLocaleDateString("fr-FR", dateOptions)}`,
};
})
);
const nextWeeks = computed((): Weeks[] =>
Array.from(Array(52).keys()).map((w) => {
const nextMonday = getMonday(w + 1);
return {
value: dateToISO(nextMonday),
text: `Semaine du ${nextMonday.toLocaleDateString("fr-FR", dateOptions)}`,
};
})
);
const editLocation = ref<InstanceType<typeof EditLocation> | null>(null)
const baseOptions = ref<CalendarOptions>({
locale: frLocale,
plugins: [interactionPlugin, timeGridPlugin],
initialView: "timeGridWeek",
initialView: 'timeGridWeek',
initialDate: new Date(),
scrollTimeReset: false,
selectable: true,
@@ -276,9 +164,9 @@ const baseOptions = ref<CalendarOptions>({
selectMirror: false,
editable: true,
headerToolbar: {
left: "prev,next today",
center: "title",
right: "timeGridWeek,timeGridDay",
left: 'prev,next today',
center: 'title',
right: 'timeGridWeek,timeGridDay'
},
});
@@ -292,23 +180,20 @@ const locations = computed<Location[]>(() => {
const pickedLocation = computed<Location | null>({
get(): Location | null {
return (
store.state.locations.locationPicked ||
store.state.locations.currentLocation
);
return store.state.locations.locationPicked || store.state.locations.currentLocation;
},
set(newLocation: Location | null): void {
store.commit("locations/setLocationPicked", newLocation, { root: true });
},
});
store.commit('locations/setLocationPicked', newLocation, {root: true});
}
})
/**
* return the show classes for the event
* @param arg
*/
const eventClasses = function (arg: EventApi): object {
return { calendarRangeItems: true };
};
const eventClasses = function(arg: EventApi): object {
return {'calendarRangeItems': true};
}
/*
// currently, all events are stored into calendarRanges, due to reactivity bug
@@ -345,60 +230,51 @@ const calendarOptions = computed((): CalendarOptions => {
* launched when the calendar range date change
*/
function onDatesSet(event: DatesSetArg): void {
store.dispatch("fullCalendar/setCurrentDatesView", {
start: event.start,
end: event.end,
});
store.dispatch('fullCalendar/setCurrentDatesView', {start: event.start, end: event.end});
}
function onDateSelect(event: DateSelectArg): void {
if (null === pickedLocation.value) {
window.alert(
"Indiquez une localisation avant de créer une période de disponibilité."
);
window.alert("Indiquez une localisation avant de créer une période de disponibilité.");
return;
}
store.dispatch("calendarRanges/createRange", {
start: event.start,
end: event.end,
location: pickedLocation.value,
});
store.dispatch('calendarRanges/createRange', {start: event.start, end: event.end, location: pickedLocation.value});
}
/**
* When a calendar range is deleted
*/
function onClickDelete(event: EventApi): void {
if (event.extendedProps.is !== "range") {
console.log('onClickDelete', event);
if (event.extendedProps.is !== 'range') {
return;
}
store.dispatch(
"calendarRanges/deleteRange",
event.extendedProps.calendarRangeId
);
store.dispatch('calendarRanges/deleteRange', event.extendedProps.calendarRangeId);
}
function onEventDropOrResize(payload: EventDropArg | EventResizeDoneArg) {
if (payload.event.extendedProps.is !== "range") {
if (payload.event.extendedProps.is !== 'range') {
return;
}
const changedEvent = payload.event;
store.dispatch("calendarRanges/patchRangeTime", {
store.dispatch('calendarRanges/patchRangeTime', {
calendarRangeId: payload.event.extendedProps.calendarRangeId,
start: payload.event.start,
end: payload.event.end,
});
}
};
function onEventClick(payload: EventClickArg): void {
// @ts-ignore TS does not recognize the target. But it does exists.
if (payload.jsEvent.target.classList.contains("delete")) {
if (payload.jsEvent.target.classList.contains('delete')) {
return;
}
if (payload.event.extendedProps.is !== "range") {
if (payload.event.extendedProps.is !== 'range') {
return;
}
@@ -409,26 +285,10 @@ function copyDay() {
if (null === copyFrom.value || null === copyTo.value) {
return;
}
store.dispatch("calendarRanges/copyFromDayToAnotherDay", {
from: ISOToDate(copyFrom.value),
to: ISOToDate(copyTo.value),
});
store.dispatch('calendarRanges/copyFromDayToAnotherDay', {from: ISOToDate(copyFrom.value), to: ISOToDate(copyTo.value)})
}
function copyWeek() {
if (null === copyFromWeek.value || null === copyToWeek.value) {
return;
}
store.dispatch("calendarRanges/copyFromWeekToAnotherWeek", {
fromMonday: ISOToDate(copyFromWeek.value),
toMonday: ISOToDate(copyToWeek.value),
});
}
onMounted(() => {
copyFromWeek.value = dateToISO(getMonday(0));
copyToWeek.value = dateToISO(getMonday(1));
});
</script>
<style scoped>
@@ -439,9 +299,4 @@ onMounted(() => {
z-index: 9999999999;
padding: 0.25rem 0 0.25rem;
}
div.copy-chevron {
text-align: center;
font-size: x-large;
width: 2rem;
}
</style>

View File

@@ -5,9 +5,11 @@ const appMessages = {
show_my_calendar: "Afficher mon calendrier",
show_weekends: "Afficher les week-ends",
copy_range: "Copier",
copy_range_from_to: "Copier les plages",
from_day_to_day: "d'un jour à l'autre",
from_week_to_week: "d'une semaine à l'autre",
copy_range_from_to: "Copier les plages d'un jour à l'autre",
copy_range_to_next_day: "Copier les plages du jour au jour suivant",
copy_range_from_day: "Copier les plages du ",
to_the_next_day: " au jour suivant",
copy_range_to_next_week: "Copier les plages de la semaine à la semaine suivante",
copy_range_how_to: "Créez les plages de disponibilités durant une journée et copiez-les facilement au jour suivant avec ce bouton. Si les week-ends sont cachés, le jour suivant un vendredi sera le lundi.",
new_range_to_save: "Nouvelles plages à enregistrer",
update_range_to_save: "Plages à modifier",

View File

@@ -52,23 +52,6 @@ export default <Module<CalendarRangesState, State>>{
}
}
return founds;
},
getRangesOnWeek: (state: CalendarRangesState) => (mondayDate: Date): EventInputCalendarRange[] => {
const founds = [];
for (let d of Array.from(Array(7).keys())) {
const dateOfWeek = new Date(mondayDate);
dateOfWeek.setDate(mondayDate.getDate() + d);
const dateStr = <string>dateToISO(dateOfWeek);
for (let range of state.ranges) {
if (isEventInputCalendarRange(range)
&& range.start.startsWith(dateStr)
) {
founds.push(range);
}
}
}
return founds;
},
},
@@ -255,7 +238,7 @@ export default <Module<CalendarRangesState, State>>{
for (let r of rangesToCopy) {
let start = new Date(<Date>ISOToDatetime(r.start));
start.setFullYear(to.getFullYear(), to.getMonth(), to.getDate());
start.setFullYear(to.getFullYear(), to.getMonth(), to.getDate())
let end = new Date(<Date>ISOToDatetime(r.end));
end.setFullYear(to.getFullYear(), to.getMonth(), to.getDate());
let location = ctx.rootGetters['locations/getLocationById'](r.locationId);
@@ -263,23 +246,6 @@ export default <Module<CalendarRangesState, State>>{
promises.push(ctx.dispatch('createRange', {start, end, location}));
}
return Promise.all(promises).then(_ => Promise.resolve(null));
},
copyFromWeekToAnotherWeek(ctx, {fromMonday, toMonday}: {fromMonday: Date, toMonday: Date}): Promise<null> {
const rangesToCopy: EventInputCalendarRange[] = ctx.getters['getRangesOnWeek'](fromMonday);
const promises = [];
const diffTime = toMonday.getTime() - fromMonday.getTime();
for (let r of rangesToCopy) {
let start = new Date(<Date>ISOToDatetime(r.start));
let end = new Date(<Date>ISOToDatetime(r.end));
start.setTime(start.getTime() + diffTime);
end.setTime(end.getTime() + diffTime);
let location = ctx.rootGetters['locations/getLocationById'](r.locationId);
promises.push(ctx.dispatch('createRange', {start, end, location}));
}
return Promise.all(promises).then(_ => Promise.resolve(null));
}
}

View File

@@ -40,7 +40,7 @@ final readonly class CalendarContext implements CalendarContextInterface
private PersonRepository $personRepository,
private ThirdPartyRender $thirdPartyRender,
private ThirdPartyRepository $thirdPartyRepository,
private TranslatableStringHelperInterface $translatableStringHelper,
private TranslatableStringHelperInterface $translatableStringHelper
) {}
public function adminFormReverseTransform(array $data): array

View File

@@ -37,7 +37,7 @@ final readonly class AccompanyingPeriodCalendarGenericDocProvider implements Gen
public function __construct(
private Security $security,
private EntityManagerInterface $em,
private EntityManagerInterface $em
) {}
/**

View File

@@ -36,7 +36,7 @@ final readonly class PersonCalendarGenericDocProvider implements GenericDocForPe
public function __construct(
private Security $security,
private EntityManagerInterface $em,
private EntityManagerInterface $em
) {}
private function addWhereClausesToQuery(FetchQuery $query, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery

View File

@@ -156,7 +156,7 @@ final class CalendarTypeTest extends TypeTestCase
private function buildMultiToIdDataTransformer(
string $classTransformer,
string $objClass,
string $objClass
) {
$transformer = $this->prophesize($classTransformer);
$transformer->transform(Argument::type('array'))
@@ -195,7 +195,7 @@ final class CalendarTypeTest extends TypeTestCase
private function buildSingleToIdDataTransformer(
string $classTransformer,
string $class,
string $class
) {
$transformer = $this->prophesize($classTransformer);
$transformer->transform(Argument::type('object'))

View File

@@ -203,7 +203,7 @@ final class CalendarContextTest extends TestCase
private function buildCalendarContext(
?EntityManagerInterface $entityManager = null,
?NormalizerInterface $normalizer = null,
?NormalizerInterface $normalizer = null
): CalendarContext {
$baseContext = $this->prophesize(BaseContextData::class);
$baseContext->getData(null)->willReturn(['base_context' => 'data']);

View File

@@ -44,7 +44,7 @@ class CreateFieldsOnGroupCommand extends Command
private readonly EntityManager $entityManager,
private readonly ValidatorInterface $validator,
private $availableLanguages,
private $customizablesEntities,
private $customizablesEntities
) {
parent::__construct();
}

View File

@@ -39,7 +39,7 @@ class CustomFieldsGroupController extends AbstractController
public function __construct(
private readonly CustomFieldProvider $customFieldProvider,
private readonly TranslatorInterface $translator,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
) {}
/**

View File

@@ -42,7 +42,7 @@ class CustomFieldChoice extends AbstractCustomField
/**
* @var TranslatableStringHelper Helper that find the string in current locale from an array of translation
*/
private readonly TranslatableStringHelper $translatableStringHelper,
private readonly TranslatableStringHelper $translatableStringHelper
) {}
public function allowOtherChoice(CustomField $cf)

View File

@@ -44,7 +44,7 @@ class CustomFieldDate extends AbstractCustomField
public function __construct(
private readonly Environment $templating,
private readonly TranslatableStringHelper $translatableStringHelper,
private readonly TranslatableStringHelper $translatableStringHelper
) {}
public function buildForm(FormBuilderInterface $builder, CustomField $customField)

View File

@@ -41,7 +41,7 @@ class CustomFieldNumber extends AbstractCustomField
public function __construct(
private readonly Environment $templating,
private readonly TranslatableStringHelper $translatableStringHelper,
private readonly TranslatableStringHelper $translatableStringHelper
) {}
public function buildForm(FormBuilderInterface $builder, CustomField $customField)

View File

@@ -28,7 +28,7 @@ class CustomFieldText extends AbstractCustomField
public function __construct(
private readonly Environment $templating,
private readonly TranslatableStringHelper $translatableStringHelper,
private readonly TranslatableStringHelper $translatableStringHelper
) {}
/**

View File

@@ -31,7 +31,7 @@ class CustomFieldTitle extends AbstractCustomField
/**
* @var TranslatableStringHelper Helper that find the string in current locale from an array of translation
*/
private readonly TranslatableStringHelper $translatableStringHelper,
private readonly TranslatableStringHelper $translatableStringHelper
) {}
public function buildForm(FormBuilderInterface $builder, CustomField $customField)

View File

@@ -26,7 +26,7 @@ class CustomFieldsGroupType extends AbstractType
public function __construct(
private readonly array $customizableEntities,
// TODO : add comment about this variable
private readonly TranslatorInterface $translator,
private readonly TranslatorInterface $translator
) {}
// TODO : details about the function

View File

@@ -48,7 +48,7 @@ final class DocGeneratorTemplateController extends AbstractController
private readonly PaginatorFactory $paginatorFactory,
private readonly EntityManagerInterface $entityManager,
private readonly ClockInterface $clock,
private readonly ChillSecurity $security,
private readonly ChillSecurity $security
) {}
#[Route(path: '{_locale}/admin/doc/gen/generate/test/from/{template}/for/{entityClassName}/{entityId}', name: 'chill_docgenerator_test_generate_from_template')]
@@ -56,7 +56,7 @@ final class DocGeneratorTemplateController extends AbstractController
DocGeneratorTemplate $template,
string $entityClassName,
int $entityId,
Request $request,
Request $request
): Response {
return $this->generateDocFromTemplate(
$template,
@@ -71,7 +71,7 @@ final class DocGeneratorTemplateController extends AbstractController
DocGeneratorTemplate $template,
string $entityClassName,
int $entityId,
Request $request,
Request $request
): Response {
return $this->generateDocFromTemplate(
$template,
@@ -137,7 +137,7 @@ final class DocGeneratorTemplateController extends AbstractController
DocGeneratorTemplate $template,
int $entityId,
Request $request,
bool $isTest,
bool $isTest
): Response {
try {
$context = $this->contextManager->getContextByDocGeneratorTemplate($template);

View File

@@ -54,15 +54,12 @@ class LoadDocGeneratorTemplate extends AbstractFixture
];
foreach ($templates as $template) {
$newStoredObj = (new StoredObject());
$newStoredObj
$newStoredObj = (new StoredObject())
->setFilename($template['file']['filename'])
->setKeyInfos(json_decode($template['file']['key'], true))
->setIv(json_decode($template['file']['iv'], true))
->setCreatedAt(new \DateTime('today'))
->registerVersion(
json_decode($template['file']['key'], true),
json_decode($template['file']['iv'], true),
$template['file']['type'],
);
->setType($template['file']['type']);
$manager->persist($newStoredObj);

View File

@@ -28,7 +28,7 @@ final readonly class RelatorioDriver implements DriverInterface
public function __construct(
private HttpClientInterface $client,
ParameterBagInterface $parameterBag,
private LoggerInterface $logger,
private LoggerInterface $logger
) {
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
}

View File

@@ -35,7 +35,7 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte
public function __construct(
private readonly ClassMetadataFactoryInterface $classMetadataFactory,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly TranslatableStringHelperInterface $translatableStringHelper
) {
$this->propertyAccess = PropertyAccess::createPropertyAccessor();
}

View File

@@ -33,7 +33,7 @@ class Generator implements GeneratorInterface
private readonly DriverInterface $driver,
private readonly ManagerRegistry $objectManagerRegistry,
private readonly LoggerInterface $logger,
private readonly StoredObjectManagerInterface $storedObjectManager,
private readonly StoredObjectManagerInterface $storedObjectManager
) {}
public function generateDataDump(
@@ -134,11 +134,13 @@ class Generator implements GeneratorInterface
$content = Yaml::dump($data, 6);
/* @var StoredObject $destinationStoredObject */
$destinationStoredObject
->setType('application/yaml')
->setFilename(sprintf('%s_yaml', uniqid('doc_', true)))
->setStatus(StoredObject::STATUS_READY)
;
try {
$this->storedObjectManager->write($destinationStoredObject, $content, 'application/yaml');
$this->storedObjectManager->write($destinationStoredObject, $content);
} catch (StoredObjectManagerException $e) {
$destinationStoredObject->addGenerationErrors($e->getMessage());
@@ -172,11 +174,13 @@ class Generator implements GeneratorInterface
/* @var StoredObject $destinationStoredObject */
$destinationStoredObject
->setType($template->getFile()->getType())
->setFilename(sprintf('%s_odt', uniqid('doc_', true)))
->setStatus(StoredObject::STATUS_READY)
;
try {
$this->storedObjectManager->write($destinationStoredObject, $generatedResource, $template->getFile()->getType());
$this->storedObjectManager->write($destinationStoredObject, $generatedResource);
} catch (StoredObjectManagerException $e) {
$destinationStoredObject->addGenerationErrors($e->getMessage());

View File

@@ -39,7 +39,7 @@ final readonly class OnGenerationFails implements EventSubscriberInterface
private MailerInterface $mailer,
private StoredObjectRepositoryInterface $storedObjectRepository,
private TranslatorInterface $translator,
private UserRepositoryInterface $userRepository,
private UserRepositoryInterface $userRepository
) {}
public static function getSubscribedEvents()

View File

@@ -56,7 +56,7 @@ final class BaseContextDataTest extends KernelTestCase
}
private function buildBaseContext(
?NormalizerInterface $normalizer = null,
?NormalizerInterface $normalizer = null
): BaseContextData {
return new BaseContextData(
$normalizer ?? self::getContainer()->get(NormalizerInterface::class)

View File

@@ -19,7 +19,6 @@ use Chill\DocGeneratorBundle\Service\Generator\Generator;
use Chill\DocGeneratorBundle\Service\Generator\ObjectReadyException;
use Chill\DocGeneratorBundle\Service\Generator\RelatedEntityNotFoundException;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Entity\StoredObjectVersion;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\MainBundle\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
@@ -40,11 +39,11 @@ class GeneratorTest extends TestCase
public function testSuccessfulGeneration(): void
{
$templateStoredObject = new StoredObject();
$templateStoredObject->registerVersion(type: 'application/test');
$template = (new DocGeneratorTemplate())->setFile($templateStoredObject);
$template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject())
->setType('application/test'));
$destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING);
$reflection = new \ReflectionClass($destinationStoredObject);
$reflection->getProperty('id')->setAccessible(true);
$reflection->getProperty('id')->setValue($destinationStoredObject, 1);
$entity = new class () {};
$data = [];
@@ -77,14 +76,7 @@ class GeneratorTest extends TestCase
$storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
$storedObjectManager->read($templateStoredObject)->willReturn('template');
$storedObjectManager->write($destinationStoredObject, 'generated', 'application/test')
->will(function ($args): StoredObjectVersion {
/** @var StoredObject $storedObject */
$storedObject = $args[0];
return $storedObject->registerVersion(type: $args[2]);
})
->shouldBeCalled();
$storedObjectManager->write($destinationStoredObject, 'generated')->shouldBeCalled();
$generator = new Generator(
$contextManagerInterface->reveal(),
@@ -115,9 +107,8 @@ class GeneratorTest extends TestCase
$this->prophesize(StoredObjectManagerInterface::class)->reveal()
);
$templateStoredObject = new StoredObject();
$templateStoredObject->registerVersion(type: 'application/test');
$template = (new DocGeneratorTemplate())->setFile($templateStoredObject);
$template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject())
->setType('application/test'));
$destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_READY);
$generator->generateDocFromTemplate(
@@ -133,11 +124,11 @@ class GeneratorTest extends TestCase
{
$this->expectException(RelatedEntityNotFoundException::class);
$templateStoredObject = new StoredObject();
$templateStoredObject->registerVersion(type: 'application/test');
$template = (new DocGeneratorTemplate())->setFile($templateStoredObject);
$template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject())
->setType('application/test'));
$destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING);
$reflection = new \ReflectionClass($destinationStoredObject);
$reflection->getProperty('id')->setAccessible(true);
$reflection->getProperty('id')->setValue($destinationStoredObject, 1);
$context = $this->prophesize(DocGeneratorContextInterface::class);

View File

@@ -58,7 +58,6 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf
?int $expire_delay = null,
?int $submit_delay = null,
int $max_file_count = 1,
?string $object_name = null,
): SignedUrlPost {
$delay = $expire_delay ?? $this->max_expire_delay;
$submit_delay ??= $this->max_submit_delay;
@@ -85,9 +84,7 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf
$expires = $this->clock->now()->add(new \DateInterval('PT'.(string) $delay.'S'));
if (null === $object_name) {
$object_name = $this->generateObjectName();
}
$object_name = $this->generateObjectName();
$g = new SignedUrlPost(
$url = $this->generateUrl($object_name),
@@ -144,7 +141,7 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf
{
return match (str_ends_with($this->base_url, '/')) {
true => $this->base_url.$relative_path,
false => $this->base_url.'/'.$relative_path,
false => $this->base_url.'/'.$relative_path
};
}
@@ -182,19 +179,21 @@ final readonly class TempUrlOpenstackGenerator implements TempUrlGeneratorInterf
return \hash_hmac('sha512', $body, $this->key, false);
}
private function generateSignature(string $method, $url, \DateTimeImmutable $expires)
private function generateSignature($method, $url, \DateTimeImmutable $expires)
{
if ('POST' === $method) {
return $this->generateSignaturePost($url, $expires);
}
$path = \parse_url((string) $url, PHP_URL_PATH);
$body = sprintf(
"%s\n%s\n%s",
strtoupper($method),
$method,
$expires->format('U'),
$path
);
)
;
$this->logger->debug(
'generate signature GET',

View File

@@ -16,8 +16,7 @@ interface TempUrlGeneratorInterface
public function generatePost(
?int $expire_delay = null,
?int $submit_delay = null,
int $max_file_count = 1,
?string $object_name = null,
int $max_file_count = 1
): SignedUrlPost;
public function generate(string $method, string $object_name, ?int $expire_delay = null): SignedUrl;

View File

@@ -25,7 +25,7 @@ class AsyncUploadExtension extends AbstractExtension
{
public function __construct(
private readonly TempUrlGeneratorInterface $tempUrlGenerator,
private readonly UrlGeneratorInterface $routingUrlGenerator,
private readonly UrlGeneratorInterface $routingUrlGenerator
) {}
public function getFilters()

View File

@@ -11,11 +11,9 @@ declare(strict_types=1);
namespace Chill\DocStoreBundle\Controller;
use Chill\DocStoreBundle\AsyncUpload\Exception\TempUrlGeneratorException;
use Chill\DocStoreBundle\AsyncUpload\TempUrlGeneratorInterface;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Entity\StoredObjectVersion;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\MainBundle\Entity\User;
use Chill\DocStoreBundle\Security\Authorization\AsyncUploadVoter;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
@@ -32,84 +30,62 @@ final readonly class AsyncUploadController
private TempUrlGeneratorInterface $tempUrlGenerator,
private SerializerInterface $serializer,
private Security $security,
private LoggerInterface $chillLogger,
private LoggerInterface $logger,
) {}
#[Route(path: '/api/1.0/doc-store/async-upload/temp_url/{uuid}/generate/post', name: 'chill_docstore_asyncupload_getsignedurlpost')]
public function getSignedUrlPost(Request $request, StoredObject $storedObject): JsonResponse
#[Route(path: '/asyncupload/temp_url/generate/{method}', name: 'async_upload.generate_url')]
public function getSignedUrl(string $method, Request $request): JsonResponse
{
if (!$this->security->isGranted(StoredObjectRoleEnum::EDIT->value, $storedObject)) {
throw new AccessDeniedHttpException('not able to edit the given stored object');
}
try {
switch (strtolower($method)) {
case 'post':
$p = $this->tempUrlGenerator
->generatePost(
$request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null,
$request->query->has('submit_delay') ? $request->query->getInt('submit_delay') : null
)
;
break;
case 'get':
case 'head':
$object_name = $request->query->get('object_name', null);
// we create a dummy version, to generate a filename
$version = $storedObject->registerVersion();
$p = $this->tempUrlGenerator
->generatePost(
$request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null,
$request->query->has('submit_delay') ? $request->query->getInt('submit_delay') : null,
object_name: $version->getFilename()
);
$this->chillLogger->notice('[Privacy Event] a request to upload a document has been generated', [
'doc_uuid' => $storedObject->getUuid(),
]);
return new JsonResponse(
$this->serializer->serialize($p, 'json', [AbstractNormalizer::GROUPS => ['read']]),
Response::HTTP_OK,
[],
true
);
}
#[Route(path: '/api/1.0/doc-store/async-upload/temp_url/{uuid}/generate/{method}', name: 'chill_docstore_asyncupload_getsignedurlget', requirements: ['method' => 'get|head'])]
public function getSignedUrlGet(Request $request, StoredObject $storedObject, string $method): JsonResponse
{
if (!$this->security->isGranted(StoredObjectRoleEnum::SEE->value, $storedObject)) {
throw new AccessDeniedHttpException('not able to read the given stored object');
}
// we really want to be sure that there are no other method than get or head:
if (!in_array($method, ['get', 'head'], true)) {
throw new AccessDeniedHttpException('Only methods get and head are allowed');
}
if ($request->query->has('version')) {
$filename = $request->query->get('version');
$storedObjectVersion = $storedObject->getVersions()->findFirst(fn (int $index, StoredObjectVersion $version): bool => $version->getFilename() === $filename);
if (null === $storedObjectVersion) {
// we are here in the case where the version is not stored into the database
// as the version is prefixed by the stored object prefix, we just have to check that this prefix
// is the same. It means that the user had previously the permission to "SEE_AND_EDIT" this stored
// object with same prefix that we checked before
if (!str_starts_with($filename, $storedObject->getPrefix())) {
throw new AccessDeniedHttpException('not able to match the version with the same filename');
}
if (null === $object_name) {
return (new JsonResponse((object) [
'message' => 'the object_name is null',
]))
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
$p = $this->tempUrlGenerator->generate(
$method,
$object_name,
$request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null
);
break;
default:
return (new JsonResponse((object) ['message' => 'the method '
."{$method} is not valid"]))
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
} else {
$filename = $storedObject->getCurrentVersion()->getFilename();
} catch (TempUrlGeneratorException $e) {
$this->logger->warning('The client requested a temp url'
.' which sparkle an error.', [
'message' => $e->getMessage(),
'expire_delay' => $request->query->getInt('expire_delay', 0),
'file_count' => $request->query->getInt('file_count', 1),
'method' => $method,
]);
$p = new \stdClass();
$p->message = $e->getMessage();
$p->status = JsonResponse::HTTP_BAD_REQUEST;
return new JsonResponse($p, JsonResponse::HTTP_BAD_REQUEST);
}
$p = $this->tempUrlGenerator->generate(
$method,
$filename,
$request->query->has('expires_delay') ? $request->query->getInt('expires_delay') : null
);
$user = $this->security->getUser();
$userId = match ($user instanceof User) {
true => $user->getId(),
false => $user->getUserIdentifier(),
};
$this->chillLogger->notice('[Privacy Event] a request to see a document has been granted', [
'doc_uuid' => $storedObject->getUuid()->toString(),
'user_id' => $userId,
]);
if (!$this->security->isGranted(AsyncUploadVoter::GENERATE_SIGNATURE, $p)) {
throw new AccessDeniedHttpException('not allowed to generate this signature');
}
return new JsonResponse(
$this->serializer->serialize($p, 'json', [AbstractNormalizer::GROUPS => ['read']]),

View File

@@ -35,7 +35,7 @@ class DocumentAccompanyingCourseController extends AbstractController
protected TranslatorInterface $translator,
protected EventDispatcherInterface $eventDispatcher,
protected AuthorizationHelper $authorizationHelper,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
) {}
#[Route(path: '/{id}/delete', name: 'chill_docstore_accompanying_course_document_delete')]

View File

@@ -1,55 +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\Controller;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
use Chill\DocStoreBundle\Workflow\AccompanyingCourseDocumentDuplicator;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
final readonly class DocumentAccompanyingCourseDuplicateController
{
public function __construct(
private Security $security,
private AccompanyingCourseDocumentDuplicator $documentWorkflowDuplicator,
private EntityManagerInterface $entityManager,
private UrlGeneratorInterface $urlGenerator,
) {}
#[Route('/{_locale}/doc-store/accompanying-course-document/{id}/duplicate', name: 'chill_doc_store_accompanying_course_document_duplicate')]
public function __invoke(AccompanyingCourseDocument $document, Request $request, Session $session): Response
{
if (!$this->security->isGranted(AccompanyingCourseDocumentVoter::SEE, $document)) {
throw new AccessDeniedHttpException('not allowed to see this document');
}
if (!$this->security->isGranted(AccompanyingCourseDocumentVoter::CREATE, $document->getCourse())) {
throw new AccessDeniedHttpException('not allowed to create this document');
}
$duplicated = $this->documentWorkflowDuplicator->duplicate($document);
$this->entityManager->persist($duplicated);
$this->entityManager->flush();
return new RedirectResponse(
$this->urlGenerator->generate('accompanying_course_document_edit', ['id' => $duplicated->getId(), 'course' => $duplicated->getCourse()->getId()])
);
}
}

View File

@@ -44,7 +44,7 @@ class DocumentPersonController extends AbstractController
protected AuthorizationHelper $authorizationHelper,
protected PDFSignatureZoneParser $PDFSignatureZoneParser,
protected StoredObjectManagerInterface $storedObjectManagerInterface,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
) {}
#[Route(path: '/{id}/delete', name: 'chill_docstore_person_document_delete')]
@@ -201,4 +201,36 @@ class DocumentPersonController extends AbstractController
['document' => $document, 'person' => $person]
);
}
#[Route(path: '/{id}/signature', name: 'person_document_signature', methods: 'GET')]
public function signature(Person $person, PersonDocument $document): Response
{
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
$this->denyAccessUnlessGranted('CHILL_PERSON_DOCUMENT_SEE', $document);
$event = new PrivacyEvent($person, [
'element_class' => PersonDocument::class,
'element_id' => $document->getId(),
'action' => 'show',
]);
$this->eventDispatcher->dispatch($event, PrivacyEvent::PERSON_PRIVACY_EVENT);
$storedObject = $document->getObject();
$content = $this->storedObjectManagerInterface->read($storedObject);
$zones = $this->PDFSignatureZoneParser->findSignatureZones($content);
$signature = [];
$signature['id'] = 1;
$signature['storedObject'] = [ // TEMP
'filename' => $storedObject->getFilename(),
'iv' => $storedObject->getIv(),
'keyInfos' => $storedObject->getKeyInfos(),
];
$signature['zones'] = $zones;
return $this->render(
'@ChillDocStore/PersonDocument/signature.html.twig',
['document' => $document, 'person' => $person, 'signature' => $signature]
);
}
}

View File

@@ -15,19 +15,12 @@ use Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\RequestPdfSignMessa
use Chill\DocStoreBundle\Service\Signature\PDFPage;
use Chill\DocStoreBundle\Service\Signature\PDFSignatureZone;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderManagerInterface;
use Chill\MainBundle\Security\Authorization\EntityWorkflowStepSignatureVoter;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class SignatureRequestController
{
@@ -35,24 +28,12 @@ class SignatureRequestController
private readonly MessageBusInterface $messageBus,
private readonly StoredObjectManagerInterface $storedObjectManager,
private readonly EntityWorkflowManager $entityWorkflowManager,
private readonly ChillEntityRenderManagerInterface $entityRender,
private readonly NormalizerInterface $normalizer,
private readonly Security $security,
) {}
#[Route('/api/1.0/document/workflow/{id}/signature-request', name: 'chill_docstore_signature_request')]
public function processSignature(EntityWorkflowStepSignature $signature, Request $request): JsonResponse
{
if (!$this->security->isGranted(EntityWorkflowStepSignatureVoter::SIGN, $signature)) {
throw new AccessDeniedHttpException('not authorized to sign this step');
}
$entityWorkflow = $signature->getStep()->getEntityWorkflow();
if (EntityWorkflowSignatureStateEnum::PENDING !== $signature->getState()) {
return new JsonResponse([], status: Response::HTTP_CONFLICT);
}
$storedObject = $this->entityWorkflowManager->getAssociatedStoredObject($entityWorkflow);
$content = $this->storedObjectManager->read($storedObject);
@@ -70,14 +51,8 @@ class SignatureRequestController
$signature->getId(),
$zone,
$data['zone']['index'],
'Signed by IP: '.(string) $request->getClientIp().', authenticated user: '.$this->entityRender->renderString($this->security->getUser(), []),
$this->entityRender->renderString($signature->getSigner(), [
// options for user render
'absence' => false,
'main_scope' => false,
// options for person render
'addAge' => false,
]),
'test signature', // reason (string)
'Mme Caroline Diallo', // signerText (string)
$content
));
@@ -87,16 +62,6 @@ class SignatureRequestController
#[Route('/api/1.0/document/workflow/{id}/check-signature', name: 'chill_docstore_check_signature')]
public function checkSignature(EntityWorkflowStepSignature $signature): JsonResponse
{
$entityWorkflow = $signature->getStep()->getEntityWorkflow();
$storedObject = $this->entityWorkflowManager->getAssociatedStoredObject($entityWorkflow);
return new JsonResponse(
[
'state' => $signature->getState(),
'storedObject' => $this->normalizer->normalize($storedObject, 'json'),
],
JsonResponse::HTTP_OK,
[]
);
return new JsonResponse($signature->getState(), JsonResponse::HTTP_OK, []);
}
}

View File

@@ -11,46 +11,6 @@ declare(strict_types=1);
namespace Chill\DocStoreBundle\Controller;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\SerializerInterface;
class StoredObjectApiController extends ApiController
{
public function __construct(
private readonly Security $security,
private readonly SerializerInterface $serializer,
private readonly EntityManagerInterface $entityManager,
) {}
/**
* Creates a new stored object.
*
* @return JsonResponse the response containing the serialized object in JSON format
*
* @throws AccessDeniedHttpException if the user does not have the necessary role to create a stored object
*/
#[Route('/api/1.0/doc-store/stored-object/create', methods: ['POST'])]
public function createStoredObject(): JsonResponse
{
if (!($this->security->isGranted('ROLE_ADMIN') || $this->security->isGranted('ROLE_USER'))) {
throw new AccessDeniedHttpException('Must be user or admin to create a stored object');
}
$object = new StoredObject();
$this->entityManager->persist($object);
$this->entityManager->flush();
return new JsonResponse(
$this->serializer->serialize($object, 'json', [AbstractNormalizer::GROUPS => ['read']]),
json: true
);
}
}
class StoredObjectApiController extends ApiController {}

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