diff --git a/.changes/unreleased/DX-20250430-144550.yaml b/.changes/unreleased/DX-20250430-144550.yaml new file mode 100644 index 000000000..7d9f0c32b --- /dev/null +++ b/.changes/unreleased/DX-20250430-144550.yaml @@ -0,0 +1,6 @@ +kind: DX +body: Remove dead code for wopi-link module +time: 2025-04-30T14:45:50.406111606+02:00 +custom: + Issue: "352" + SchemaChange: No schema change diff --git a/.changes/unreleased/Feature-20231212-154841.yaml b/.changes/unreleased/Feature-20231212-154841.yaml deleted file mode 100644 index 14e19345b..000000000 --- a/.changes/unreleased/Feature-20231212-154841.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: Feature -body: '[DX] move async-upload-bundle features into chill-bundles' -time: 2023-12-12T15:48:41.954970271+01:00 -custom: - Issue: "221" diff --git a/.changes/unreleased/Feature-20250424-142211.yaml b/.changes/unreleased/Feature-20250424-142211.yaml new file mode 100644 index 000000000..e1f5297c3 --- /dev/null +++ b/.changes/unreleased/Feature-20250424-142211.yaml @@ -0,0 +1,7 @@ +kind: Feature +body: Add the document file name to the document title when a user upload a document, + unless there is already a document title. +time: 2025-04-24T14:22:11.800975422+02:00 +custom: + Issue: "377" + SchemaChange: No schema change diff --git a/.changes/unreleased/Feature-20250520-095628.yaml b/.changes/unreleased/Feature-20250520-095628.yaml new file mode 100644 index 000000000..4b1fba30c --- /dev/null +++ b/.changes/unreleased/Feature-20250520-095628.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: Add desactivation date for social action and issue csv export +time: 2025-05-20T09:56:28.108941934+02:00 +custom: + Issue: "" + SchemaChange: No schema change diff --git a/.changes/unreleased/Fixed-20240410-103736.yaml b/.changes/unreleased/Fixed-20240410-103736.yaml deleted file mode 100644 index 355d24b1e..000000000 --- a/.changes/unreleased/Fixed-20240410-103736.yaml +++ /dev/null @@ -1,6 +0,0 @@ -kind: Fixed -body: Fix resolving of centers for an household, which will fix in turn the access - control -time: 2024-04-10T10:37:36.462484988+02:00 -custom: - Issue: "" diff --git a/.changes/unreleased/Fixed-20250424-133943.yaml b/.changes/unreleased/Fixed-20250424-133943.yaml new file mode 100644 index 000000000..c11c42ac0 --- /dev/null +++ b/.changes/unreleased/Fixed-20250424-133943.yaml @@ -0,0 +1,7 @@ +kind: Fixed +body: trying to prevent bug of typeerror in doc-history + improved display of document + history +time: 2025-04-24T13:39:43.878468232+02:00 +custom: + Issue: "376" + SchemaChange: No schema change diff --git a/.changes/unreleased/Fixed-20250424-163746.yaml b/.changes/unreleased/Fixed-20250424-163746.yaml new file mode 100644 index 000000000..79c07b080 --- /dev/null +++ b/.changes/unreleased/Fixed-20250424-163746.yaml @@ -0,0 +1,7 @@ +kind: Fixed +body: Display previous participation in acc course work even if the person has left + the acc course +time: 2025-04-24T16:37:46.970203594+02:00 +custom: + Issue: "381" + SchemaChange: No schema change diff --git a/.changes/unreleased/Fixed-20250505-102715.yaml b/.changes/unreleased/Fixed-20250505-102715.yaml new file mode 100644 index 000000000..5d47c25af --- /dev/null +++ b/.changes/unreleased/Fixed-20250505-102715.yaml @@ -0,0 +1,6 @@ +kind: Fixed +body: Fix display of text in calendar events +time: 2025-05-05T10:27:15.461493066+02:00 +custom: + Issue: "372" + SchemaChange: No schema change diff --git a/.changes/unreleased/Fixed-20250514-145339.yaml b/.changes/unreleased/Fixed-20250514-145339.yaml new file mode 100644 index 000000000..862ea764b --- /dev/null +++ b/.changes/unreleased/Fixed-20250514-145339.yaml @@ -0,0 +1,6 @@ +kind: Fixed +body: Add missing translation for user_group.no_user_groups +time: 2025-05-14T14:53:39.53927329+02:00 +custom: + Issue: "" + SchemaChange: No schema change diff --git a/.changes/unreleased/UX-20250423-172624.yaml b/.changes/unreleased/UX-20250423-172624.yaml new file mode 100644 index 000000000..2a17e4195 --- /dev/null +++ b/.changes/unreleased/UX-20250423-172624.yaml @@ -0,0 +1,6 @@ +kind: UX +body: Remove default filter in_progress for the page 'my tasks'; Allows for new tasks to be displayed upon opening of the page +time: 2025-04-23T17:26:24.45777387+02:00 +custom: + Issue: "374" + SchemaChange: No schema change diff --git a/.changes/v2.20.0.md b/.changes/v2.20.0.md new file mode 100644 index 000000000..7cfb4809c --- /dev/null +++ b/.changes/v2.20.0.md @@ -0,0 +1,21 @@ +## v2.20.0 - 2024-06-05 +### Fixed +* ([#170](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/170)) Display agents traitants instead of accompanying period referrer in export list social actions. +* Added translations for choices of durations (> 5 hours) +### Feature +* ([#145](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/145)) Allow to open documents in LibreOffice locally (need configuration within security); + + This endpoint should be added to make the endpoint works properly: + + ```yaml + security: + firewalls: + dav: + pattern: ^/dav + provider: chain_provider + stateless: true + guard: + authenticators: + - Chill\DocStoreBundle\Security\Guard\JWTOnDavUrlAuthenticator + + ``` diff --git a/.changes/v2.20.1.md b/.changes/v2.20.1.md new file mode 100644 index 000000000..5493c2c17 --- /dev/null +++ b/.changes/v2.20.1.md @@ -0,0 +1,3 @@ +## v2.20.1 - 2024-06-05 +### Fixed +* Do not allow StoredObjectCreated for edit and convert buttons diff --git a/.changes/v2.21.0.md b/.changes/v2.21.0.md new file mode 100644 index 000000000..6398c4a67 --- /dev/null +++ b/.changes/v2.21.0.md @@ -0,0 +1,31 @@ +## v2.21.0 - 2024-06-18 +### Feature +* Add flash menu buttons in search results, to open directly a new calendar, or a new activity in an accompanying period +* ([#122](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/122)) Improve the list of calendar in the search results: make all calendar clicable, and display a list of calendars +* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add start date and end date on filters "filter course by referrer job" and "filter course by referrer scope" +* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] the aggregator "Group by referrer" now accept a date range. +* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add date range on "group course by referrer's scope" +* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add date range on "group course by referrer's jobs" +* ([#168](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/168) In the UX, display user job and service at the time when he performs an action: + now, the job and service is shown: + * at the activity's date, + * at the appointment's date, + * when the user is marked as referrer for an accompanying period work, + * when the user apply a transition in a workflow, + * when the user updates or creates "something" ("created/updated by ... at ..."), + * or when he wrote a comment, + * … + +### Traduction francophone +* Ajout d'un menu "flash" dans les résultats de recherche, pour créer un rendez-vous ou un échange dans un parcours depuis les résultats de recherche; +* Améliore la liste des rendez-vous dans les résultats de recherche: les rendez-vous sont cliquables; +* [exports] Ajout d'intervalles de dates pour des filtres et regroupements des parcours par référent, métier du référent, service du référent; +* Affiche le métier et le service des utilisateurs à la date à laquelle il a exécuté une action. Le métier et le service est affiché: + * à la date d'un échange, + * au jour d'un rendez-vous, + * quand l'utilisateur est devenu référent d'un parcours d'accompagnement, + * quand il a appliqué une transition sur un workflow, + * quand il a mise à jour ou créé une fiche, dans les mentions "créé / mise à jour par ..., le ...", + * quand il a mis à jour un commentaire, + * … + diff --git a/.changes/v2.22.0.md b/.changes/v2.22.0.md new file mode 100644 index 000000000..fef006fd0 --- /dev/null +++ b/.changes/v2.22.0.md @@ -0,0 +1,6 @@ +## v2.22.0 - 2024-06-25 +### Feature +* ([#216](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/216)) [event bundle] exports added for the event module + +### Traduction francophone +* Exports sont ajoutés pour la module événement. diff --git a/.changes/v2.22.1.md b/.changes/v2.22.1.md new file mode 100644 index 000000000..b856cc95b --- /dev/null +++ b/.changes/v2.22.1.md @@ -0,0 +1,5 @@ +## v2.22.1 - 2024-07-01 +### Fixed +* Remove debug word +### DX +* Add a command for reading official address DB from Luxembourg and update chill addresses diff --git a/.changes/v2.22.2.md b/.changes/v2.22.2.md new file mode 100644 index 000000000..51b3c75da --- /dev/null +++ b/.changes/v2.22.2.md @@ -0,0 +1,3 @@ +## v2.22.2 - 2024-07-03 +### Fixed +* Remove scope required for event participation stats diff --git a/.changes/v2.23.0.md b/.changes/v2.23.0.md new file mode 100644 index 000000000..5987b1ca8 --- /dev/null +++ b/.changes/v2.23.0.md @@ -0,0 +1,30 @@ +## v2.23.0 - 2024-07-23 & 2024-07-19 +### Feature +* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles +* Add job bundle (module emploi) +* Upgrade import of address list to the last version of compiled addresses of belgian-best-address + +* Upgrade CKEditor and refactor configuration with use of typescript + +* ([#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. diff --git a/.changes/v2.24.0.md b/.changes/v2.24.0.md new file mode 100644 index 000000000..b1ec2a194 --- /dev/null +++ b/.changes/v2.24.0.md @@ -0,0 +1,3 @@ +## 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. diff --git a/.changes/v3.0.0.md b/.changes/v3.0.0.md new file mode 100644 index 000000000..586e648da --- /dev/null +++ b/.changes/v3.0.0.md @@ -0,0 +1,5 @@ +## v3.0.0 - 2024-08-26 +### Fixed +* Fix delete action for accompanying periods in draft state +* Fix connection to azure when making an calendar event in chill +* CollectionType js fixes for remove button and adding multiple entries diff --git a/.changes/v3.1.0.md b/.changes/v3.1.0.md new file mode 100644 index 000000000..cb05f47fb --- /dev/null +++ b/.changes/v3.1.0.md @@ -0,0 +1,3 @@ +## 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. diff --git a/.changes/v3.1.1.md b/.changes/v3.1.1.md new file mode 100644 index 000000000..06bf33077 --- /dev/null +++ b/.changes/v3.1.1.md @@ -0,0 +1,6 @@ +## v3.1.1 - 2024-10-01 +### Fixed +* ([#308](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/308)) Show only the current referrer in the page "show" for an accompanying period workf +* ([#309](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/309)) Correctly compute the grouping by referrer aggregator + +* Fixed typing of custom field long choice and custom field group diff --git a/.changes/v3.10.0.md b/.changes/v3.10.0.md new file mode 100644 index 000000000..5ed6d62ce --- /dev/null +++ b/.changes/v3.10.0.md @@ -0,0 +1,6 @@ +## v3.10.0 - 2025-03-17 +### Feature +* ([#363](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/363)) Display social actions grouped per social issue within activity form +### Fixed +* ([#362](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/362)) Fix Dependency Injection, which prevented to save the CalendarRange +* ([#368](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/368)) fix search query for user groups diff --git a/.changes/v3.10.1.md b/.changes/v3.10.1.md new file mode 100644 index 000000000..ae6cd6d89 --- /dev/null +++ b/.changes/v3.10.1.md @@ -0,0 +1,3 @@ +## v3.10.1 - 2025-03-17 +### DX +* Remove yarn dependency to symfony/ux-translator, to ease the build process diff --git a/.changes/v3.10.2.md b/.changes/v3.10.2.md new file mode 100644 index 000000000..3c24add88 --- /dev/null +++ b/.changes/v3.10.2.md @@ -0,0 +1,3 @@ +## v3.10.2 - 2025-03-17 +### Fixed +* Replace a ts-expect-error with a ts-ignore diff --git a/.changes/v3.10.3.md b/.changes/v3.10.3.md new file mode 100644 index 000000000..3d402ca34 --- /dev/null +++ b/.changes/v3.10.3.md @@ -0,0 +1,3 @@ +## v3.10.3 - 2025-03-18 +### DX +* Eslint fixes diff --git a/.changes/v3.11.0.md b/.changes/v3.11.0.md new file mode 100644 index 000000000..07f4d4a5f --- /dev/null +++ b/.changes/v3.11.0.md @@ -0,0 +1,19 @@ +## v3.11.0 - 2025-04-17 +### Feature +* ([#365](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/365)) Add counters of actions and activities, with 2 boxes to (1) show the number of active actions on total actions and (2) show the number of activities in a accompanying period, and pills in menus for showing the number of active actions and the number of activities. +* ([#364](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/364)) Added a second phone number "telephone2" to the thirdParty entity. Adapted twig templates and vuejs apps to handle this phone number + + **Schema Change**: Add columns or tables +* Signature: add a button to go directly to the signature zone, even if there is only one +### Fixed +* Fixed wrong translations in the on-the-fly for creation of thirdParty +* Fixed update of phone number in on-the-fly edition of thirdParty +* Fixed closing of modal when editing thirdParty in accompanying course works +* Shorten the delay between two execution of AccompanyingPeriodStepChangeCronjob, to ensure at least one execution in a day +* ([#102](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/102)) Fix display of title in document list +* When cleaning the old stored object versions, do not throw an error if the stored object is not found on disk +* Add consistent log prefix and key to logs when stale workflows are automatically canceled +* ([#380](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/380)) Remove the "not null" validation constraint on recently added properties on HouseholdComposition + +### DX +* Add new chill-col style for displaying title and aside in a flex table diff --git a/.changes/v3.2.0.md b/.changes/v3.2.0.md new file mode 100644 index 000000000..25dd295c8 --- /dev/null +++ b/.changes/v3.2.0.md @@ -0,0 +1,3 @@ +## v3.2.0 - 2024-10-30 +### Feature +* Introduce a gender entity diff --git a/.changes/v3.2.1.md b/.changes/v3.2.1.md new file mode 100644 index 000000000..dd1466922 --- /dev/null +++ b/.changes/v3.2.1.md @@ -0,0 +1,4 @@ +## v3.2.1 - 2024-10-31 +### Fixed +* Add the possibility of unknown to the gender entity +* Fix the fusion of person doubles by excluding accompanyingPeriod work entities to be deleted. They are moved instead. diff --git a/.changes/v3.2.2.md b/.changes/v3.2.2.md new file mode 100644 index 000000000..7ab01bf62 --- /dev/null +++ b/.changes/v3.2.2.md @@ -0,0 +1,3 @@ +## v3.2.2 - 2024-10-31 +### Fixed +* Fix gender translation for unknown diff --git a/.changes/v3.2.3.md b/.changes/v3.2.3.md new file mode 100644 index 000000000..b7064350a --- /dev/null +++ b/.changes/v3.2.3.md @@ -0,0 +1,4 @@ +## v3.2.3 - 2024-11-05 +### Fixed +* ([#315](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/315)) Fix display of accompanying period work referrers. Only current referrers should be displayed. +Fix color of Chill footer diff --git a/.changes/v3.2.4.md b/.changes/v3.2.4.md new file mode 100644 index 000000000..f89cf37eb --- /dev/null +++ b/.changes/v3.2.4.md @@ -0,0 +1,3 @@ +## v3.2.4 - 2024-11-06 +### Fixed +* Fix compilation of chill assets diff --git a/.changes/v3.3.0.md b/.changes/v3.3.0.md new file mode 100644 index 000000000..bb56973b7 --- /dev/null +++ b/.changes/v3.3.0.md @@ -0,0 +1,13 @@ +## v3.3.0 - 2024-11-20 +### Feature +* Electronic signature + +Implementation of the electronic signature for documents within chill. +* ([#286](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/286)) The behavoir of the voters for stored objects is adjusted so as to limit edit and delete possibilities to users related to the activity, social action or workflow entity. +* ([#288](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/288)) Metadata form added for person signatures +* Add a signature step in workflow, which allow to apply an electronic signature on documents +* Keep an history of each version of a stored object. +* Add a "send external" step in workflow, which allow to send stored objects and other elements to remote people, by sending them a public url +### Fixed +* Adjust household list export to include households even if their address is NULL +* Remove validation of date string on deathDate diff --git a/.changes/v3.4.0.md b/.changes/v3.4.0.md new file mode 100644 index 000000000..3fc4dd5d6 --- /dev/null +++ b/.changes/v3.4.0.md @@ -0,0 +1,4 @@ +## v3.4.0 - 2024-11-20 +### Feature +* ([#314](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/314)) Admin: improve document type admin form with a select field for related class. +Admin: Allow administrator to assign multiple group centers in one go to a user. diff --git a/.changes/v3.4.1.md b/.changes/v3.4.1.md new file mode 100644 index 000000000..261023d15 --- /dev/null +++ b/.changes/v3.4.1.md @@ -0,0 +1,3 @@ +## v3.4.1 - 2024-11-22 +### Fixed +* Set the workflow's title to notification content and subject diff --git a/.changes/v3.4.2.md b/.changes/v3.4.2.md new file mode 100644 index 000000000..18b0759e0 --- /dev/null +++ b/.changes/v3.4.2.md @@ -0,0 +1,6 @@ +## v3.4.2 - 2024-12-05 +### Fixed +* ([#329](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/329)) Fix the serialization of gender for the generation of documents +* ([#337](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/337)) Enforce unique contraint on activity storedobject +### DX +* ([#310](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/310)) Clean migrations, to reduce the number of bloated migration when running diff on schema diff --git a/.changes/v3.4.3.md b/.changes/v3.4.3.md new file mode 100644 index 000000000..00acd4577 --- /dev/null +++ b/.changes/v3.4.3.md @@ -0,0 +1,4 @@ +## v3.4.3 - 2024-12-05 +### Fixed +* Remove the "not null" constraint on person supplementary phones +* Remove doctrine annotation that prevent from adding documents to activities diff --git a/.changes/v3.5.0.md b/.changes/v3.5.0.md new file mode 100644 index 000000000..566b883a0 --- /dev/null +++ b/.changes/v3.5.0.md @@ -0,0 +1,6 @@ +## v3.5.0 - 2024-12-09 +### Feature +* ([#318](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/318)) Show all the pages of the documents in the signature app +### Fixed +* Wrap the signature's change state into a transaction, to avoid race conditions +* Fix display of gender label diff --git a/.changes/v3.5.1.md b/.changes/v3.5.1.md new file mode 100644 index 000000000..2463e3d5b --- /dev/null +++ b/.changes/v3.5.1.md @@ -0,0 +1,4 @@ +## v3.5.1 - 2024-12-16 +### Fixed +* Filiation: fix the display of the gender label in the graph +* Wrap handling of PdfSignedMessage into transactions diff --git a/.changes/v3.5.2.md b/.changes/v3.5.2.md new file mode 100644 index 000000000..bcd09f6eb --- /dev/null +++ b/.changes/v3.5.2.md @@ -0,0 +1,3 @@ +## v3.5.2 - 2024-12-19 +### Fixed +* ([#345](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/345)) Export: activity filtering of users that were associated to an activity between certain dates. Results contained activities that were not within the specified date range" diff --git a/.changes/v3.5.3.md b/.changes/v3.5.3.md new file mode 100644 index 000000000..adedf15ca --- /dev/null +++ b/.changes/v3.5.3.md @@ -0,0 +1,3 @@ +## v3.5.3 - 2025-01-07 +### Fixed +* Fix the EntityToJsonTransformer to return an empty array if the value is "" diff --git a/.changes/v3.6.0.md b/.changes/v3.6.0.md new file mode 100644 index 000000000..1b74c3f65 --- /dev/null +++ b/.changes/v3.6.0.md @@ -0,0 +1,9 @@ +## v3.6.0 - 2025-01-16 +### Feature +* Importer for addresses does not fails when the postal code is not found with some addresses, and compute a recap list of all addresses that could not be imported. This recap list can be send by email. +* ([#346](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/346)) Create a driver for storing documents on disk (instead of openstack object store) + +* Add address importer from french Base d'Adresse Nationale (BAN) +* ([#343](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/343)) Add csv export for social issues and social actions +### Fixed +* Export: fix missing alias in activity between certain dates filter. Condition added for alias. diff --git a/.changes/v3.7.0.md b/.changes/v3.7.0.md new file mode 100644 index 000000000..34e4ca4ed --- /dev/null +++ b/.changes/v3.7.0.md @@ -0,0 +1,62 @@ +## v3.7.0 - 2025-01-21 +### Feature +* Use the Notifier component from Symfony to sens short messages (SMS). This allow to use more provider. +### Fixed +* ([#348](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/348)) [export] Fix aggregation of referrer's scope and job: fix the date range comparison + +### Warning on configuration of Notifier component + +If installed in an symfony app where the recipes are activated, this configuration should be added automatically: + +```yaml +framework: + notifier: + chatter_transports: + texter_transports: + ovhcloud: '%env(OVHCLOUD_DSN)%' + channel_policy: + # use chat/slack, chat/telegram, sms/twilio or sms/nexmo + urgent: ['email'] + high: ['email'] + medium: ['email'] + low: ['email'] + admin_recipients: + - { email: admin@example.com } +``` + +Actually, you should either: + +- remove the configuration of ovhcloud added by the recipe +- or remove the previous configuration of chill, to avoid keeping legacy configuration + +#### Remove the added configuration and keep the legacy configuration + +To remove the configuration: + +```diff +framework: + notifier: + chatter_transports: + texter_transports: +- ovhcloud: '%env(OVHCLOUD_DSN)%' +``` + +In that case, the previous configuration, which was stored under the `chill_main.short_messages.dsn` will be reconfigured into the Notifier component's configuration. + +#### Properly configure SMS + +You can also properly configure it, as [described in the OVH cloud provider repository](https://github.com/symfony/ovh-cloud-notifier/tree/5.4?tab=readme-ov-file#dsn-example) (where the scheme is `ovhcloud`): + +**NOTE**: You have access to all notifier available with the [Notifier component](https://symfony.com/doc/current/notifier.html#notifier-sms-channel). You are not restricted to use OVH as a provider. + +```diff +framework: + notifier: + chatter_transports: + texter_transports: ++ ovhcloud: '%env(OVHCLOUD_DSN)%' # this value should be located in a variable, and have `ovhcloud://` as a scheme + +chill_main: +- short_messages: +- dsn: '%env(string:SHORT_MESSAGE_DSN)%' +``` diff --git a/.changes/v3.7.1.md b/.changes/v3.7.1.md new file mode 100644 index 000000000..ee2fabfd1 --- /dev/null +++ b/.changes/v3.7.1.md @@ -0,0 +1,3 @@ +## v3.7.1 - 2025-01-21 +### Fixed +* Fix legacy configuration processor for notifier component diff --git a/.changes/v3.8.0.md b/.changes/v3.8.0.md new file mode 100644 index 000000000..1c6b60efd --- /dev/null +++ b/.changes/v3.8.0.md @@ -0,0 +1,11 @@ +## v3.8.0 - 2025-02-03 +### Feature +* Improve the UX of the news item admin form to prevent wrong usage +* ([#319](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/319)) Notification list: display the concerned person's badges in the list +* ([#320](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/320)) Show the first 3 persons directly in the accompanying period's banner +* ([#334](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/334)) Suggest current user when creating an activity +* ([#331](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/331)) Add attachments to workflows +### Fixed +* ([#350](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/350)) Add validation error to manual selection of person in PersonDuplicateController +* ([#354](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/354)) Fix document category creation +* ([#351](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/351)) Add definitive whitespace between span elements in vue PersonText component diff --git a/.changes/v3.8.1.md b/.changes/v3.8.1.md new file mode 100644 index 000000000..088a9e969 --- /dev/null +++ b/.changes/v3.8.1.md @@ -0,0 +1,3 @@ +## v3.8.1 - 2025-02-05 +### Fixed +* Fix household link in the parcours banner diff --git a/.changes/v3.8.2.md b/.changes/v3.8.2.md new file mode 100644 index 000000000..467ad4ac3 --- /dev/null +++ b/.changes/v3.8.2.md @@ -0,0 +1,3 @@ +## v3.8.2 - 2025-02-10 +### Fixed +* ([#358](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/358)) Remove "filter" button on list of documents in the workflow's "add attachement" modal diff --git a/.changes/v3.9.0.md b/.changes/v3.9.0.md new file mode 100644 index 000000000..7cc6a7f9f --- /dev/null +++ b/.changes/v3.9.0.md @@ -0,0 +1,10 @@ +## v3.9.0 - 2025-02-27 +### Feature +* ([#349](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/349)) Suggest all referrers within actions of the accompanying period when creating an activity +* ([#343](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/343)) Add possibility to export a csv with all social issues and social actions +* ([#360](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/360)) Restore document to previous kept version when a workflow is canceled +* ([#341](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/341)) Add a list of third parties from within the admin (csv download) +### Fixed +* fix generation of document with accompanying period context, and list of activities and works +### DX +* ([#333](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/333)) Create an unique source of trust for translations diff --git a/.changes/v3.9.1.md b/.changes/v3.9.1.md new file mode 100644 index 000000000..546f55b74 --- /dev/null +++ b/.changes/v3.9.1.md @@ -0,0 +1,3 @@ +## v3.9.1 - 2025-02-27 +### Fixed +* Fix post/patch request with missing 'type' property for gender diff --git a/.changes/v3.9.2.md b/.changes/v3.9.2.md new file mode 100644 index 000000000..e7f573526 --- /dev/null +++ b/.changes/v3.9.2.md @@ -0,0 +1,3 @@ +## v3.9.2 - 2025-02-27 +### Fixed +* Use fetchResults method to fetch all social issues instead of only the first page diff --git a/.changie.yaml b/.changie.yaml index 0145062f8..49a885a2e 100644 --- a/.changie.yaml +++ b/.changie.yaml @@ -7,15 +7,29 @@ versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}' kindFormat: '### {{.Kind}}' # Note: it is possible to add a `.custom.Long` text manually into the yaml file produced by `changie new`. This will add a long description. changeFormat: >- - * {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{ end }}{{.Body}} {{ if and (.Custom.Long) (not (eq .Custom.Long "")) }} + * {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{ end }}{{ .Body }} {{ if and .Custom.SchemaChange (ne .Custom.SchemaChange "No schema change") }} + + **Schema Change**: {{ .Custom.SchemaChange }} + {{- end -}} + + {{ if and (.Custom.Long) (not (eq .Custom.Long "")) }}{{ .Custom.Long }}{{ end }} - {{ .Custom.Long }}{{ end }} custom: + - key: SchemaChange + label: Is a schema change required? + optional: false + type: enum + enumOptions: + - "No schema change" + - "Add columns or tables" + - "Drop or rename table or columns, or enforce new constraint that must be manually fixed" + - key: Issue label: Issue number (on chill-bundles repository) (optional) optional: true type: int minInt: 1 + body: # allow multiline messages block: true diff --git a/.env b/.env index 1714966d4..cc23653bd 100644 --- a/.env +++ b/.env @@ -11,19 +11,13 @@ # Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). # https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration -## Locale -LOCALE=fr - ###> symfony/framework-bundle ### -# this should be set in docker-compose.yml file APP_ENV=prod -APP_SECRET=ChangeItf2b58287ef7f9976409d3f6c72529e99ChangeIt -TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -TRUSTED_HOSTS='^(localhost|example\.com|nginx)$' +APP_SECRET=!ChangeMeInAppEnv! ###< symfony/framework-bundle ### ## Wopi server for editing documents online -WOPI_SERVER=http://collabora:9980 +EDITOR_SERVER=http://collabora:9980 # must be manually set in .env.local # ADMIN_PASSWORD= @@ -32,52 +26,14 @@ WOPI_SERVER=http://collabora:9980 # MAILER_DSN=null://null ###< symfony/mailer ### -## Notifications -NOTIFICATION_HOST=localhost:8001 -NOTIFICATION_FROM_EMAIL=admin@chill.social -NOTIFICATION_FROM_NAME="Chill " - -## Pgadmin credential -PGADMIN_DEFAULT_EMAIL= -PGADMIN_DEFAULT_PASSWORD= - -## OVH OpenStack Storage Container -ASYNC_UPLOAD_TEMP_URL_KEY= -ASYNC_UPLOAD_TEMP_URL_BASE_PATH= -ASYNC_UPLOAD_TEMP_URL_CONTAINER= - -## Redis Cache -REDIS_HOST=redis -REDIS_PORT=6379 -REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT} - -## Twilio -TWILIO_SID=~ -TWILIO_SECRET=~ -DEFAULT_CARRIER_CODE=BE - -ADD_ADDRESS_DEFAULT_COUNTRY=BE - -ADD_ADDRESS_MAP_CENTER_X=50.8443 -ADD_ADDRESS_MAP_CENTER_Y=4.3523 -ADD_ADDRESS_MAP_CENTER_Z=15 - -SHORT_MESSAGE_DSN=null://null - -## DOCKER IMAGES REGISTRY -#IMAGE_PHP= -#IMAGE_NGINX= - -## DOCKER IMAGES TAG -#VERSION=test -#VERSION=prod ###> symfony/messenger ### # Choose one of the transports below # MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages # MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages -MESSENGER_TRANSPORT_DSN=sync:// -MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0 +# MESSENGER_TRANSPORT_DSN=sync:// +# MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0 +MESSENGER_TRANSPORT_DSN=${RABBITMQ_URL}/%2f ###< symfony/messenger ### ###> doctrine/doctrine-bundle ### @@ -92,3 +48,47 @@ JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem JWT_PASSPHRASE=2a30f6ba26521a2613821da35f28386e ###< lexik/jwt-authentication-bundle ### + +###> chill-project/chill-bundles ### +# Generate a password using `symfony console security:hash-password --empty-salt 'Symfony\Component\Security\Core\User\User'` and paste it into .env.local file +# ADMIN_PASSWORD= +# Add a configuration for sending SMS (before calendar appointments, aka "Rendez-vous"). See https://symfony.com/doc/current/notifier.html#sms-channel +# Configuration for using ovh custom notifier +# SHORT_MESSAGE_DSN="ovh://user:password@ovh-eu?consumer_key=123456&sender=%2B32475123456&service_name=sms-xx123456-1" +SHORT_MESSAGE_DSN=null://null +# Default locale for the project +LOCALE=fr +# Those keys are required to configure the access to store documents +ASYNC_UPLOAD_TEMP_URL_KEY= +ASYNC_UPLOAD_TEMP_URL_BASE_PATH= +ASYNC_UPLOAD_TEMP_URL_CONTAINER= +# Twilio configuration, to check for the existence of phonenumber in a directory (may be deprecated in a near future) +TWILIO_SID= +TWILIO_SECRET= +# Default carrier code will replace all leading zero by an international code. Set here the country as two letters +DEFAULT_CARRIER_CODE=FR +# Configuration for the notification emails +NOTIFICATION_FROM_EMAIL=chill@instance.com +NOTIFICATION_FROM_NAME=Chill +# Will set the default host when sending email from command or services (see https://symfony.com/doc/5.x/routing.html#generating-urls-in-commands) +NOTIFICATION_HOST=my.chill.social +# Variables for relatorio host, which generates documents +RELATORIO_HOST= +RELATORIO_PORT= +# Address for your collabora server +#EDITOR_SERVER=https://code.example.com +EDITOR_SERVER=https://collabora.champs-libres.be +# center address widget when empty +ADD_ADDRESS_DEFAULT_COUNTRY=BE +ADD_ADDRESS_MAP_CENTER_X=50.8443 +ADD_ADDRESS_MAP_CENTER_Y=4.3523 +ADD_ADDRESS_MAP_CENTER_Z=15 +## Redis Cache & redis database +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT} +###< chill-project/chill-bundles ### + +###> symfony/ovh-cloud-notifier ### +# OVHCLOUD_DSN=ovhcloud://APPLICATION_KEY:APPLICATION_SECRET@default?consumer_key=CONSUMER_KEY&service_name=SERVICE_NAME +###< symfony/ovh-cloud-notifier ### diff --git a/.env.test b/.env.test index f84920e54..9cd2ae81b 100644 --- a/.env.test +++ b/.env.test @@ -4,15 +4,8 @@ KERNEL_CLASS='App\Kernel' APP_SECRET='$ecretf0rt3st' -TRUSTED_HOSTS= - ADMIN_PASSWORD=password -LOCALE=fr -REDIS_URL=redis -REDIS_PORT=6379 -REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT} - JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem JWT_PASSPHRASE=2a30f6ba26521a2613821da35f28386e @@ -22,22 +15,25 @@ TWILIO_SECRET=~ DEFAULT_CARRIER_CODE=BE ADD_ADDRESS_DEFAULT_COUNTRY=BE - ADD_ADDRESS_MAP_CENTER_X=50.8443 ADD_ADDRESS_MAP_CENTER_Y=4.3523 ADD_ADDRESS_MAP_CENTER_Z=15 SHORT_MESSAGE_DSN=null://null MESSENGER_TRANSPORT_DSN=sync:// -###< symfony/messenger ### ###> doctrine/doctrine-bundle ### # Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml # -DATABASE_URL="postgresql://postgres:postgres@db:5432/test?serverVersion=14&charset=utf8" +DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5454/test?serverVersion=14&charset=utf8" ###< doctrine/doctrine-bundle ### ASYNC_UPLOAD_TEMP_URL_KEY= ASYNC_UPLOAD_TEMP_URL_BASE_PATH= ASYNC_UPLOAD_TEMP_URL_CONTAINER= + +MAILER_DSN=null://null + +REDIS_HOST=127.0.0.1 +REDIS_PORT=6363 diff --git a/.eslint-baseline.json b/.eslint-baseline.json new file mode 100644 index 000000000..8a8daa2fe --- /dev/null +++ b/.eslint-baseline.json @@ -0,0 +1,2386 @@ +[ + { + "path": "assets/translator.js", + "line": 4, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'getLocale' is defined but never used.", + "hash": "4de7bfba61e745c3cf5323aad048092c7c17ccf8" + }, + { + "path": "assets/translator.js", + "line": 5, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'setLocale' is defined but never used.", + "hash": "a7eabf4524aa4b2a7fd30de6d1ce61a3623c00db" + }, + { + "path": "docs/source/development/user-interface/js-functions/show_hide.js", + "line": 12, + "column": 26, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "6d66a79b37a676e44177f5e22c83250315d23c96" + }, + { + "path": "docs/source/development/user-interface/js-functions/show_hide.js", + "line": 29, + "column": 26, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "ecfe1d9da55e96316b21de60db959be5f9e20f59" + }, + { + "path": "src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/SocialIssuesAcc.vue", + "line": 136, + "column": 39, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "4faa92668b8548e3b747568ef9a3671708c0fbf9" + }, + { + "path": "src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/SocialIssuesAcc.vue", + "line": 211, + "column": 47, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "d1fd4d809f01ecad48418336edca270335fccff9" + }, + { + "path": "src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/index.js", + "line": 18, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "f8c2979921289906e3baabae31ba101ead91504f" + }, + { + "path": "src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/index.js", + "line": 57, + "column": 23, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "cf0cf378f71403f62a6425f384ccbbdec433d1f2" + }, + { + "path": "src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js", + "line": 90, + "column": 13, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'resources' is assigned a value but never used.", + "hash": "342d8aad3e080ba262239e5f5c6fc419f388ca33" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/module/Invite/answer.js", + "line": 7, + "column": 57, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "7930b2e3b44140e5058b66710ecdccdb81871c1a" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/Components/CalendarActive.vue", + "line": 19, + "column": 30, + "ruleId": "vue/valid-v-else", + "message": "'v-else' directives require no attribute value.", + "hash": "505f99b24500a684eec3c6bf870426fcd841c20b" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/Components/CalendarActive.vue", + "line": 54, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'mapGetters' is defined but never used.", + "hash": "01e3928f7d9be0accb6db3894b158dd683a685ff" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/index.js", + "line": 10, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "4dae6f8a4aa0fabbbd88aabc983cf03da6f3b7fd" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/actions.js", + "line": 29, + "column": 32, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'getters' is defined but never used.", + "hash": "5ac92a47d5c8de02a33eef97b27a75e68841ab04" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/actions.js", + "line": 238, + "column": 35, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'state' is defined but never used.", + "hash": "8d8cdf4931f8b803539d67ef603fc5011a439ad0" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/getters.js", + "line": 30, + "column": 26, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'getters' is defined but never used.", + "hash": "790488f2c65040f92bb02cb31c35e9a9fff907ec" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/getters.js", + "line": 279, + "column": 11, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'resources' is assigned a value but never used.", + "hash": "950151f3e4a25489310590e6137cfe2c155bd579" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/index.js", + "line": 3, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'postLocation' is defined but never used.", + "hash": "c4fc4177d62914f827e7a5c2cd0cb974b947e3f7" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.ts", + "line": 4, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'DateTime' is defined but never used.", + "hash": "fdf38ad15813c4a931b93f4a85db985ca71105fc" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.ts", + "line": 14, + "column": 27, + "ruleId": "@typescript-eslint/no-empty-object-type", + "message": "The `{}` (\"empty object\") type allows any non-nullish value, including literals like `0` and `\"\"`.\n- If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option.\n- If you want a type meaning \"any object\", you probably want `object` instead.\n- If you want a type meaning \"any value\", you probably want `unknown` instead.", + "hash": "9602ec11eda8f46ff5712d69dcd87419babcd6f0" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.ts", + "line": 16, + "column": 20, + "ruleId": "@typescript-eslint/no-empty-object-type", + "message": "The `{}` (\"empty object\") type allows any non-nullish value, including literals like `0` and `\"\"`.\n- If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option.\n- If you want a type meaning \"any object\", you probably want `object` instead.\n- If you want a type meaning \"any value\", you probably want `unknown` instead.", + "hash": "23f7a8a1c3cba955077ddbd604bad6805ce30ad2" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.ts", + "line": 18, + "column": 19, + "ruleId": "@typescript-eslint/no-empty-object-type", + "message": "The `{}` (\"empty object\") type allows any non-nullish value, including literals like `0` and `\"\"`.\n- If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option.\n- If you want a type meaning \"any object\", you probably want `object` instead.\n- If you want a type meaning \"any value\", you probably want `unknown` instead.", + "hash": "abbc89e186c74d0f71f45cfda31c490339e39078" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Invite/Answer.vue", + "line": 95, + "column": 13, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'payload' is defined but never used.", + "hash": "66c545917093ba30f1d6ca10ddaa676140e749bd" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue", + "line": 224, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reactive' is defined but never used.", + "hash": "96ed76a9828138fb125fc36c4b55e900bbfe87c2" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue", + "line": 230, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'DropArg' is defined but never used.", + "hash": "bd405399a4091d65e8391404bfb0c4611816c8e0" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue", + "line": 251, + "column": 9, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'t' is assigned a value but never used.", + "hash": "bc09207a496405f7a71c178e522b89aeb1f7ebd3" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue", + "line": 356, + "column": 32, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'arg' is defined but never used.", + "hash": "aeae152f0669b946a1ad681dd52b0ef03393ae79" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue", + "line": 434, + "column": 11, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'changedEvent' is assigned a value but never used.", + "hash": "a7a81a6bf09d00c0364e3aa8207ffad853f0547b" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue", + "line": 444, + "column": 5, + "ruleId": "@typescript-eslint/ban-ts-comment", + "message": "Use \"@ts-expect-error\" instead of \"@ts-ignore\", as \"@ts-ignore\" will do nothing if the following line is error-free.", + "hash": "1b8a1b6b081d3f14ed319619e6031039668cd736" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue", + "line": 77, + "column": 16, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_' is defined but never used.", + "hash": "d29fb2fc9299c48082167b2fa4c427c569716bd6" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue", + "line": 82, + "column": 30, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_' is defined but never used.", + "hash": "bd8f0e9bb24ec4486579bfac87c52db7ebb9f182" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue", + "line": 82, + "column": 33, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "991397a66bbda97bd596a67f11e8caa98b53e517" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/index2.ts", + "line": 7, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'useI18n' is defined but never used.", + "hash": "24cadd85d1ba9f16b2f68d2b2b14ef1db72ac1a6" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/index2.ts", + "line": 12, + "column": 11, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "6422c0876b992a3c5174faa99ef06d8339b74b4e" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/index.ts", + "line": 46, + "column": 20, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_' is defined but never used.", + "hash": "5bf7ba0b0200fb6788d50df669e6b7701f8a87bb" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarRanges.ts", + "line": 259, + "column": 62, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_' is defined but never used.", + "hash": "a06407a3b44a8e5865525e37e7620d20100c1e32" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarRanges.ts", + "line": 350, + "column": 48, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_' is defined but never used.", + "hash": "fabe51c847e3842b4a4fda0b581f10b466cbd098" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarRanges.ts", + "line": 374, + "column": 48, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_' is defined but never used.", + "hash": "7547f6801ac63196c32ffc61d2be7ad5bf113cc8" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts", + "line": 57, + "column": 32, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_' is defined but never used.", + "hash": "a733a5edfe4c434ed63682bcbd5fe65f175a1b85" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts", + "line": 64, + "column": 32, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_' is defined but never used.", + "hash": "d83b1879b5fc6179c045ad1981421f46a7cbbd56" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts", + "line": 71, + "column": 32, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_' is defined but never used.", + "hash": "222eed84495212735a60fa05e0c7f298c7bc33e1" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts", + "line": 72, + "column": 26, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_' is defined but never used.", + "hash": "0cf85eccce08b65a8590f7318cace58cbb21ea08" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/me.ts", + "line": 9, + "column": 6, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'Context' is defined but never used.", + "hash": "be22e3a53da2ea78944f0e3e8409b8eba957ffa9" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 108, + "column": 47, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "1a771f9f65375cac2002ed7d706184355685d378" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 148, + "column": 29, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"users\" prop.", + "hash": "d9f7e517892b6588be17516fd077c3149ccbefd9" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 151, + "column": 29, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"calendarEvents\" prop.", + "hash": "47c270d8c0bcda67ea04a83ba267eca3e70fa7fb" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 154, + "column": 59, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "7cf30f0d2dc567ea0dfb44dadfc9ecf4977e7b95" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 155, + "column": 41, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"users\" prop.", + "hash": "7876348a3eee16be7effd3e992a6aaa2334cfd24" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 164, + "column": 63, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "0f308d7417bfc294b28eea6890666a2d91e31ecd" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 185, + "column": 57, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"calendarEvents\" prop.", + "hash": "5a021a581a33d3aeea21c65e023902f2123bb562" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 218, + "column": 17, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "655811d4dad8de23b3d37dfbf02f7fca18b6d8f2" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 235, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"calendarEvents\" prop.", + "hash": "19317d9a564b8b10355cac1eeb9308c7d49c0dd1" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 240, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"users\" prop.", + "hash": "95e478eca53a2acfa8edd0aa5747e73b0da01d0c" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 246, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"users\" prop.", + "hash": "0b66e38f0c0bbe17aa7c548dc127be033133ce06" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/js/async-upload/uploader.ts", + "line": 6, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'URL_POST' is assigned a value but never used.", + "hash": "9216fdae92adfabcddd8f915195337eb06ead048" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/downloader.js", + "line": 8, + "column": 3, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "bc5721039a242f9d275fcd6879ebfcdfea2a5eb1" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/downloader.js", + "line": 30, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'decryptError' is assigned a value but never used.", + "hash": "fed5b83c3c3d5d1fc6912801b7fbf33659284552" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/downloader.js", + "line": 31, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'fetchError' is assigned a value but never used.", + "hash": "b5719b526776f763e4c3edc948400c6e41d6b299" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/downloader.js", + "line": 32, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'key' is defined but never used.", + "hash": "369b13ffbe97104d3b2c984678c8f00fbfc31a35" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/downloader.js", + "line": 33, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'url' is defined but never used.", + "hash": "6b5067617eb3c8a563d70dca46e756314a2f81d5" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/downloader.js", + "line": 87, + "column": 13, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'error' is defined but never used.", + "hash": "e26e5e101e90d2b7ee84d6f5de8c819e52129c17" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/index.ts", + "line": 29, + "column": 14, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'vm' is defined but never used.", + "hash": "8e7f5e89dd72c54459cf82156389b88988f97d63" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", + "line": 39, + "column": 3, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "7cbec23eb581a6e7090acffbcb3efc0bf4de260e" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", + "line": 44, + "column": 40, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'files' is defined but never used.", + "hash": "6907608aafd5b95a7beda9647b214705545569ba" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", + "line": 103, + "column": 34, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'zoneData' is defined but never used.", + "hash": "aabe2ba3b3f0aff817c64f5e5002dd18e16f77da" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", + "line": 141, + "column": 27, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'file' is defined but never used.", + "hash": "fa4c56a1614fa1342a10c54d8587b6ce8ee32ca8" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", + "line": 154, + "column": 43, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'response' is defined but never used.", + "hash": "341a9ef745481a5cc1fa94db48c8f16cbbe836c4" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", + "line": 159, + "column": 39, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'file' is defined but never used.", + "hash": "c222ff822f41535c2ec8d0c1e504caa65259066e" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", + "line": 165, + "column": 41, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'file' is defined but never used.", + "hash": "1ac09121d7d8725c70d7d7f58010212c031759dc" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", + "line": 239, + "column": 30, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'zoneData' is defined but never used.", + "hash": "6e72d62f9f6031a09d5d39f751bd2fcbc500ee55" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", + "line": 301, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'removeDownloadButton' is assigned a value but never used.", + "hash": "a91a8e7065fec4c015bfc155ebfcb2820db71242" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", + "line": 301, + "column": 37, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'zoneData' is defined but never used.", + "hash": "60bfad4fe297c635f2f2c1b132754946480f4454" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/uploader.js", + "line": 348, + "column": 43, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "27fd905f7750f1ee727f8c8fa5bbc12493a37c54" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/button_download/index.ts", + "line": 3, + "column": 8, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'DocumentActionButtonsGroup' is defined but never used.", + "hash": "385b162a454092801fdc98df75354475eeeedc67" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/button_download/index.ts", + "line": 4, + "column": 24, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'StoredObjectStatusChange' is defined but never used.", + "hash": "59372c9c257957b6ad6edc6db6d6a6a276f66145" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/button_download/index.ts", + "line": 5, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'defineComponent' is defined but never used.", + "hash": "5ab301967629c4f1f1d451233f4378ab87255220" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/button_download/index.ts", + "line": 11, + "column": 55, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "ab73a104ee8d027dd16a433ef33bfd379ce59827" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts", + "line": 5, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'is_object_ready' is defined but never used.", + "hash": "2b2312921b3a9ced3b6307301ea2777edf17280b" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts", + "line": 10, + "column": 55, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "c5322ffbb276330255aed8133207cb4d6c810990" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue", + "line": 80, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'StoredObjectVersion' is defined but never used.", + "hash": "2fadbe9e331a66f44b54782acc1420c4b487e1f6" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/App.vue", + "line": 336, + "column": 20, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reactive' is defined but never used.", + "hash": "433bed446eb37784c380d5c82bb364221af80969" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/App.vue", + "line": 354, + "column": 1, + "ruleId": "@typescript-eslint/ban-ts-comment", + "message": "Use \"@ts-expect-error\" instead of \"@ts-ignore\", as \"@ts-ignore\" will do nothing if the following line is error-free.", + "hash": "b104a4e3ccf06c4a16fd83076e2b3f2dc27b2d91" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/App.vue", + "line": 363, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'download_and_decrypt_doc' is defined but never used.", + "hash": "77340aab22ce01c7ed6518a96586f4219299dd36" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/App.vue", + "line": 757, + "column": 16, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'r' is defined but never used.", + "hash": "c4cc95fa4538bfd3a61bdc2d5ffcae8c96178af6" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/App.vue", + "line": 779, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-expressions", + "message": "Expected an assignment or function call and instead saw an expression.", + "hash": "c5f5d0e73bd179d79b60b53a3dc1c6d676bee287" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/index.ts", + "line": 2, + "column": 1, + "ruleId": "@typescript-eslint/ban-ts-comment", + "message": "Use \"@ts-expect-error\" instead of \"@ts-ignore\", as \"@ts-ignore\" will do nothing if the following line is error-free.", + "hash": "b60ce5d41e38f02a016f25170e065e86fd7863ec" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/index.ts", + "line": 34, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "63c8d2e65a7103f786ce1619a447eee56d2e06ee" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileWidget.vue", + "line": 3, + "column": 20, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'ref' is defined but never used.", + "hash": "11fba46b19890b0af973dc69d26d0c1101ca67e6" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileWidget.vue", + "line": 3, + "column": 25, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'Ref' is defined but never used.", + "hash": "51180c9c7d852650d0b930900cee3d9aca10c4c3" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue", + "line": 11, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'download_and_decrypt_doc' is defined but never used.", + "hash": "9a803f1fe608ec60ab065aa8f76e50e2ef1fa4c2" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue", + "line": 14, + "column": 8, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'mime' is defined but never used.", + "hash": "13520522261b138600d6ec9028ee02eb9cd06180" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue", + "line": 50, + "column": 11, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reset_pending' is assigned a value but never used.", + "hash": "96fde3634a150f1253fac2f2f2bdcfe6e0daf82a" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton.vue", + "line": 7, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'computed' is defined but never used.", + "hash": "a3a0749af7945b37eccd14da930d3426ab51d199" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton.vue", + "line": 7, + "column": 30, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'ref' is defined but never used.", + "hash": "2a27cd6d06a26e1326654c929068e3704137e24b" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue", + "line": 57, + "column": 17, + "ruleId": "vue/valid-v-for", + "message": "Custom elements in iteration require 'v-bind:key' directives.", + "hash": "cce787939524e83dd135869e13738ef332d7156c" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/WopiEditButton.vue", + "line": 15, + "column": 8, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'WopiEditButton' is defined but never used.", + "hash": "80cb06a18ac4db47e8d120103704ec6988696b79" + }, + { + "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts", + "line": 3, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'StoredObjectStatus' is defined but never used.", + "hash": "e868cee022cd1499bb54f7b5503a8a822773e097" + }, + { + "path": "src/Bundle/ChillJobBundle/src/Resources/public/module/cv_edit/index.js", + "line": 7, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'a' is assigned a value but never used.", + "hash": "46d2235305e750a202849fb47836fb72efb23a05" + }, + { + "path": "src/Bundle/ChillJobBundle/src/Resources/public/module/cv_edit/index.js", + "line": 11, + "column": 28, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "d10364cc7a78ac507fcdc72faad1e6c53b33d756" + }, + { + "path": "src/Bundle/ChillJobBundle/src/Resources/public/module/cv_edit/index.js", + "line": 32, + "column": 43, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_e' is defined but never used.", + "hash": "464451b9cf747ada3d7b960bdbec52fa83470ac0" + }, + { + "path": "src/Bundle/ChillJobBundle/src/Resources/public/module/dispositif_edit/index.js", + "line": 15, + "column": 26, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "a502c4bb96318eab29957a19c21ca2dae860a736" + }, + { + "path": "src/Bundle/ChillJobBundle/src/Resources/public/module/dispositif_edit/index.js", + "line": 30, + "column": 26, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "a18e483f334f58f4202744bb7690131e79accbe3" + }, + { + "path": "src/Bundle/ChillJobBundle/src/Resources/public/module/dispositif_edit/index.js", + "line": 47, + "column": 26, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "7f049438577bc91505d4f53f96117401ed09685a" + }, + { + "path": "src/Bundle/ChillJobBundle/src/Resources/public/module/dispositif_edit/index.js", + "line": 59, + "column": 26, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "ba32878c1dd4f851b70b128aaa81910d398002cb" + }, + { + "path": "src/Bundle/ChillJobBundle/src/Resources/public/module/immersion_edit/index.js", + "line": 8, + "column": 26, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "3e8d716f29c9281b10dd453dd4a72eadd4a58525" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/chill.js", + "line": 3, + "column": 11, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'moment' is defined but never used.", + "hash": "50b0cc2383baff6bba176e52d2516605b1cac04a" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/chill.js", + "line": 241, + "column": 17, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "1a01f297aa8e16086c60374ec293ff2f1ccd4f62" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/chill.js", + "line": 282, + "column": 21, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "b194f742a3a8c4af5919ecd77c10f52c61537425" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/chill.js", + "line": 315, + "column": 7, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "5b6badc602a6175fa4dccc529b3a45239461eb2e" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/chill.js", + "line": 319, + "column": 7, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "6bda13d299b7f93955b9197391c071e16ac28b0c" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/chill.js", + "line": 352, + "column": 5, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "ccc46215d423bf1815b495bdb4dc2e10b7834017" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/counter.js", + "line": 28, + "column": 55, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "a78b1ecd6fe7ca192d7f00aee8b270d435a9042a" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts", + "line": 73, + "column": 5, + "ruleId": "prefer-const", + "message": "'cal' is never reassigned. Use 'const' instead.", + "hash": "53586ea7ec719f07750a8874db79f5e76583d5e0" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts", + "line": 79, + "column": 5, + "ruleId": "prefer-const", + "message": "'time' is never reassigned. Use 'const' instead.", + "hash": "19821279c0b2d7e69418db6877c317f8c6e2dacc" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts", + "line": 85, + "column": 5, + "ruleId": "prefer-const", + "message": "'offset' is never reassigned. Use 'const' instead.", + "hash": "244b65de0ab0791ec89219058c5cb4f2e11622c7" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts", + "line": 176, + "column": 25, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "78974307acb7b584f0c2196fbb47fc914b5c7a32" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts", + "line": 186, + "column": 9, + "ruleId": "prefer-const", + "message": "'promises' is never reassigned. Use 'const' instead.", + "hash": "2e178f566f5da9822a5dd46b29738829fbd1adf7" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts", + "line": 232, + "column": 26, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'response' is defined but never used.", + "hash": "0a036a266bd7bba398df10163e9704e6ea6167f3" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts", + "line": 240, + "column": 28, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'response' is defined but never used.", + "hash": "86c054fe1edcb6b24e6d0aefd64cd445e7614a52" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts", + "line": 260, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'response' is defined but never used.", + "hash": "a45a603d1877899ba33744991bc21cfa4bedb17d" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/download-report/download-report.js", + "line": 18, + "column": 8, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'mime' is defined but never used.", + "hash": "c8ef5b1aa8983aec00f188234be22c77a9a77425" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/select_interactive_loading/index.js", + "line": 1, + "column": 43, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "a3317069459ea66c96501bd9cfb77ea42813ca94" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/select_interactive_loading/index.js", + "line": 5, + "column": 3, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "d43728b51f59dc4bea30de30b1090d76ccdd5faf" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/tabs/tabs.js", + "line": 19, + "column": 3, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "d62839e33722008a8888ec8397584c9e15ef4974" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/tabs/tabs.js", + "line": 51, + "column": 30, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'pane' is defined but never used.", + "hash": "2c53f99e3ff670ca2d695b7da3106e49c0d0ae0d" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/tabs/tabs.js", + "line": 77, + "column": 9, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'tabs' is assigned a value but never used.", + "hash": "750c361cd41b0d42baa186d7e505dde80db12dd3" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/address-details/index.ts", + "line": 3, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'createI18n' is defined but never used.", + "hash": "921ae29ccf48f99622b40b8d8eb047ca4dffe35b" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/blur/index.js", + "line": 9, + "column": 32, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "2a0ad2a99642951ded06c75a14b668fb6c56129d" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/index.js", + "line": 8, + "column": 8, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'Dropdown' is defined but never used.", + "hash": "03f2e6cddba6e332840742e3826b59018087c97c" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/index.js", + "line": 9, + "column": 8, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'Modal' is defined but never used.", + "hash": "b68c78d1b28da96ca9eade0b083ef986a82bc81f" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/index.js", + "line": 10, + "column": 8, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'Collapse' is defined but never used.", + "hash": "daa0e8fded9a2a5fc6288a0fcf09509e2e13a661" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/index.js", + "line": 27, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'carousel' is assigned a value but never used.", + "hash": "7c700d60868641fee4b9b1925a70bf1aea1c59c3" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/index.js", + "line": 34, + "column": 50, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "e534f5b90d9d78f819b788579c6dc97cc96ab4f4" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/bootstrap/index.js", + "line": 62, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'popoverList' is assigned a value but never used.", + "hash": "2fd1da06ffc0a1025552cd02b7838cb5ee70006c" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/index.ts", + "line": 7, + "column": 16, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'editor' is defined but never used.", + "hash": "a1ee37171b35396754af104e4815484e1bd2a5ec" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts", + "line": 53, + "column": 35, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "3d7c37e1a8d8c13d427dfa5861ac8e3ca0fad093" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts", + "line": 133, + "column": 30, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'target' is defined but never used.", + "hash": "7db7b2f692341951595ec738e8b45562465e7bf7" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts", + "line": 141, + "column": 5, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "cd5fc994be294fde5fef27ba841c9af454b81dc0" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts", + "line": 153, + "column": 5, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "381830dd078845aeab5ea366757541cd7c77ca3f" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts", + "line": 162, + "column": 9, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "768b1976fb935d4a4b0890f00be2299b7ff60170" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/disable-buttons/index.js", + "line": 9, + "column": 43, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "1c04bebef0448105f6d455da4eb89bbbc299f7fc" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/disable-buttons/index.js", + "line": 10, + "column": 3, + "ruleId": "@typescript-eslint/prefer-for-of", + "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", + "hash": "aac391029a9094c0024acd4748728d0345d43d62" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/notification/toggle_read.js", + "line": 4, + "column": 8, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'NotificationReadAllToggle' is defined but never used.", + "hash": "85b91b57af8f355c4da6175f0cd10543e8c21298" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/notification/toggle_read.js", + "line": 8, + "column": 55, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "07f4feb1fc536257245672cadc5e4a6eff3dfd11" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/pick-entity/index.js", + "line": 26, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-expressions", + "message": "Expected an assignment or function call and instead saw an expression.", + "hash": "ade0f5ef31462024600ac17130a26e0429e27039" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/pick-entity/index.js", + "line": 176, + "column": 57, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "733b101d5fc60b4b4d0539d772cec69590baae19" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/pick-postal-code/index.js", + "line": 10, + "column": 9, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "b12c35e669c0ad8af41fe1dc3ffe994cf53198fa" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/pick-postal-code/index.js", + "line": 26, + "column": 21, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'city' is defined but never used.", + "hash": "e892975b07863cec84647ba00d93ba913776e433" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/pick-postal-code/index.js", + "line": 55, + "column": 57, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "4f3f46e4b736f4325e869cffbecbe1427939eae4" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/pick-rolling-date/index.js", + "line": 3, + "column": 57, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_e' is defined but never used.", + "hash": "1d6448401778e8c56554020fe5abd47851ed33f3" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/module/wopi-link/index.js", + "line": 21, + "column": 55, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "eae499e4f6e9f43a9d17f9cd917cb6d3d97be25c" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/page/export/download-export.js", + "line": 3, + "column": 55, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "088fd383e7807e484aefc9825209bc7c8942bd22" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.js", + "line": 9, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "2b4f1b7b1ec87616e72a89871f91845c16200435" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/page/location/index.js", + "line": 63, + "column": 48, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_e' is defined but never used.", + "hash": "2cafe722e433827869f1d0c381e2da4f9433d6a7" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js", + "line": 25, + "column": 28, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "18c8d3c84a2d185f0ccacdbf9369dae0a873af82" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js", + "line": 61, + "column": 28, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "8c9f34e3dc525681be29e2d7f4fbc5f9625707ac" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js", + "line": 81, + "column": 28, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "621b61792a977fd46a013dbb78632897ca097d3c" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js", + "line": 103, + "column": 28, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "7778a0a6f7163fe5fd1875faf20d77a9261172ee" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js", + "line": 115, + "column": 28, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "fd85d6b471a593de3170bc227db6c33ad3408a11" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js", + "line": 128, + "column": 29, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'arg2' is defined but never used.", + "hash": "c58527d970320b92295ffdc435ffdc9466bfbd60" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/page/workflow-show/index.js", + "line": 128, + "column": 35, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'arg3' is defined but never used.", + "hash": "f4587bddf906e9d3e7cfdc6476d6d41baf906087" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/App.vue", + "line": 79, + "column": 55, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "29ea9ef3f1b2cb970e8103383bcde12a4ab66c25" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/App.vue", + "line": 101, + "column": 55, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "b81ee6947049c6876f81410cfbb499dc43da4374" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", + "line": 247, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'postAddressToPerson' is defined but never used.", + "hash": "8a41c437cf2b5554cbbe1704cd51f3102b3d5994" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", + "line": 248, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'postAddressToHousehold' is defined but never used.", + "hash": "66dec84b2ece299daf21308e5e60d497ba442b27" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", + "line": 490, + "column": 21, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"context\" prop.", + "hash": "0d3f40c47974a4371072b3b9ee04b197c830162d" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", + "line": 491, + "column": 21, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"context\" prop.", + "hash": "8e877b7e588c30e182f7b572bdb9685360f9cf99" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", + "line": 508, + "column": 47, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "5a3e3401bc3c765d91faaf4cfde57697af1262b7" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", + "line": 525, + "column": 47, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "35a741d90379574b9323279f5802193d0c98a9dc" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", + "line": 553, + "column": 47, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "c23d1ddf6c0d10ae97948e74aee9c14b9320b86c" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", + "line": 572, + "column": 47, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "4322e81c6ea9d9734c680633a724d5bd4fabacb2" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", + "line": 803, + "column": 47, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "7928a6461b9d394c7d97f048933553936f7d8963" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", + "line": 852, + "column": 47, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "e5afdb8efccb5470a08dde48f755b1268fa947b5" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", + "line": 93, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "68f5e1cf5c03f9ada59c9e0afca0b74c7f3fca4b" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", + "line": 101, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "50d730f6109092baff2db66adc44dc1315e2bda2" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", + "line": 109, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "573e4c041ce663f28b933d7a675c2a525aba644c" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", + "line": 117, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "293f845eeab515b1df4649d136c2d8219ed59c4d" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", + "line": 125, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "792310bc5def2c7b45f50da97cd8818d82862e8a" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", + "line": 133, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "a69e5335393f67a83d89720af34a4385a7d7e665" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", + "line": 141, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "2d5a5e680ff207ad97c7e7b7d999064b561dfd8a" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 106, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "d52356f2af31d0167c02330ec22d09fbfa6b2b9f" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 114, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "c8e8e06f370f93bf05867e93b5f037dfa46937b1" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 128, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "9abaf71ca4b4f292b3b01e724d0a7733365e71f1" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 129, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "0b0743959778a9e3d93089b132608816ee4e6646" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 132, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "9759da7b7859b8ee8efaf74876430658ac6b6fe2" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 133, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "dba8be9a27ab74ec743b7d9e07c05d857b407dd3" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 134, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "9b1f5bce779aafc46b19d7a5d266eaa29f8f9be9" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 139, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "fe6fc4aea0994ba9da15b7c09d308842b67958cb" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 153, + "column": 55, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "bd0e024fcad2e3f4566f15293e3c25c840f6dd3e" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 154, + "column": 37, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "596c4b180b926b7829f987384328bf5636cd367a" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 171, + "column": 59, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "5b41d5f9b45da074fb7bbbbd45e0da501da72071" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 172, + "column": 41, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "d92b92a25043244cca809bd129633b7e024e26b4" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 190, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "dd9a85ea740742d620e864796f67c5bff834486d" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 191, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "e3e59960d0d50709a57b336f66b586710b774892" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 192, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "fe11b0e54396511e7b3b08615a78d22fc27e2fad" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 222, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "63c14c2150c33ec701bc4a0ff94efde69537d490" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 96, + "column": 20, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "d2a9fdaeef0e2810f480022d4c6f99e4f76a818e" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 96, + "column": 20, + "ruleId": "vue/no-side-effects-in-computed-properties", + "message": "Unexpected side effect in \"cities\" computed property.", + "hash": "dd92a60a9b1ebefeb9a90941d45326fbfa483733" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 102, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "04be01ab638ce01f568fb0216929e65e1175ca23" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 110, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "8619c8e0b63e87d09268832f90e4fba06b87e41f" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 124, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "281f918da00635079501418b1e6b2c05b62eb4a7" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 125, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "c131b09fa67ab1d069f1d04a54582d6b0f206153" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 126, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "3d3a2a4add64c291b8f5f1cddd90a173cd6a819d" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 131, + "column": 21, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "ed48f4988914d7897018a2e06830a97e6740b3e8" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 145, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "744f3a7610d4d6015e50e25149bceffd6c6e2763" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 146, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "eaaaaee5fb2e324ffe0a68eefe340dabdf162324" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 147, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "2de47b4a4ddbe546b3fce9898af48b72853364bf" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 149, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "1e7b1ad55866f708baaca72dfa4ff26d6f8e5d21" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 152, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "84779331536ffceec8d4a8c5ca4307310b882549" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 161, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "0789999841be671a4d8ab080d6fdb679f843eb52" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 170, + "column": 51, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "bbb17afa114f016e2058d90aa32d2a625804f0d1" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 171, + "column": 33, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "5fbe407ceceb37bff2ac800ceddd7942540132f1" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 190, + "column": 55, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "e2af91def877befbabef8e93deba4c58a3ee2ded" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 191, + "column": 37, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "ee8544ee45681a650ed7d4918ae979685cdd8f0f" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 210, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "5d9d2217c8c7e6571bc9f72a98ea5b370edb4968" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 211, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "6e04619b373c23c91f6c36c2aad314ac16cdb697" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 212, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "39df045639a62f64ccdb03a80e286bc3ad772587" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 213, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "c399a43fa797a8ce61c9d96a644a39cc84a387b7" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 245, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "04337a07944caaa4819cfebcf29e1a7cbfdf248b" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue", + "line": 75, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "f2e288dcddf773ed3a79b238327af8a48961e861" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue", + "line": 80, + "column": 13, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "836965dc8fa085f98d31f3ce261dcdf27d1ef6a4" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/DatePane.vue", + "line": 95, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "84e13d1fdc79f4568634a78df281adbe81739cbd" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/DatePane.vue", + "line": 103, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "1eed832462e52537402a2825655733f0f2d391d9" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue", + "line": 155, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "b3a822914fcb5e2fcf28efc331a45b9205002eeb" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue", + "line": 164, + "column": 17, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "72c7d850f6cdeaf65b373a33234222f9766ee30b" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/index.js", + "line": 10, + "column": 9, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "e39c8efb64c15a67efdd894e74c51129ca30cf09" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/mod_input_address_index.js", + "line": 22, + "column": 11, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "c0178faf1e0af01e6445c761e083df5f19e314c8" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/mod_input_address_index.js", + "line": 86, + "column": 48, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_e' is defined but never used.", + "hash": "52a2051aee948783e053f102a066d4c51155cc54" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/NewsItem.vue", + "line": 142, + "column": 57, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "a68eeba7b2e1e603d83da0946c94cd221134aa99" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/OnTheFly.vue", + "line": 204, + "column": 22, + "ruleId": "vue/return-in-computed-property", + "message": "Expected to return a value in \"buttonMessage\" computed property.", + "hash": "b101c861dc11bc7024857fa2977118cb9f99c02c" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/index.js", + "line": 12, + "column": 9, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "9e6125f4fc387dc362c69cc6e3ce360eb2851f1b" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/PickEntity/PickEntity.vue", + "line": 60, + "column": 22, + "ruleId": "vue/require-valid-default-prop", + "message": "Type of the default value for 'suggested' prop must be a function.", + "hash": "d30212820bc2e97fa02d75dbc3a014558693f169" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsMap.vue", + "line": 24, + "column": 13, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'LatLngExpression' is defined but never used.", + "hash": "78f5a83dddf05b38aa9472ab93871e976719ef30" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/GenderIconRenderBox.vue", + "line": 6, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'props' is assigned a value but never used.", + "hash": "29fe0a5d52e46c479aa2e7bb23fb55c53df7b22e" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflow.vue", + "line": 142, + "column": 32, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'w' is defined but never used.", + "hash": "ad12fc3b08dbfc9c24ae65d2a59c7bd5cecb39f2" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflow.vue", + "line": 146, + "column": 33, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'w' is defined but never used.", + "hash": "d28435fe2f60f5d3c22ab2b4cb59a6bd2cd0f947" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflow.vue", + "line": 155, + "column": 15, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'popoverList' is assigned a value but never used.", + "hash": "933d1a8c7c3d9321347218a08588cdf4c352f422" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue", + "line": 114, + "column": 34, + "ruleId": "vue/require-valid-default-prop", + "message": "Type of the default value for 'goToGenerateWorkflowPayload' prop must be a function.", + "hash": "d686fa87cfdc801aaaa08b24e827e503e81e86be" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue", + "line": 170, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'goToDuplicateRelatedEntity' is assigned a value but never used.", + "hash": "224ddf3abcff96e3e20a0facc7493883958d5a80" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue", + "line": 171, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "aa87fd5511528b5a45713fe1eaeda9ae0a8c0975" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue", + "line": 172, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'workflowName' is defined but never used.", + "hash": "e34bbcf245552e9329efdf7bd64ea3a56f0d4538" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue", + "line": 173, + "column": 12, + "ruleId": "@typescript-eslint/no-empty-function", + "message": "Unexpected empty arrow function.", + "hash": "8bdff7a5b3a7ac1506966a6066a1deb556d30efe" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue", + "line": 3, + "column": 9, + "ruleId": "vue/require-toggle-inside-transition", + "message": "The element inside `` is expected to have a `v-if` or `v-show` directive.", + "hash": "0594fb9d0984f4dd1612671aca21b571087ab8ee" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue", + "line": 59, + "column": 22, + "ruleId": "vue/require-valid-default-prop", + "message": "Type of the default value for 'modalDialogClass' prop must be a function.", + "hash": "51033a0b54bc839ce532e609bc58a2686818c895" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.ts", + "line": 60, + "column": 35, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "61fa9887fddae8f7c4fd60cd21827fb034d5bb7f" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.ts", + "line": 66, + "column": 9, + "ruleId": "@typescript-eslint/ban-ts-comment", + "message": "Use \"@ts-expect-error\" instead of \"@ts-ignore\", as \"@ts-ignore\" will do nothing if the following line is error-free.", + "hash": "370ce59576610d7a292501effe428366011585f4" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/page/accompanying_course_index/masonry.js", + "line": 4, + "column": 5, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'msnry' is assigned a value but never used.", + "hash": "37c52c5d1360a9339ba377f65e7f5671bcecb82a" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/page/person/suggest-names.js", + "line": 47, + "column": 9, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'tags' is assigned a value but never used.", + "hash": "ae9bb2e0651c118ed9efd227e88b86cc83f5d80d" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/StickyNav.vue", + "line": 116, + "column": 18, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "201f182769c6dfb87148b841e7d9b592be429669" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/index.js", + "line": 19, + "column": 11, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "45a24a5a25333d674e2e8de8715e011c44d520fc" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/index.js", + "line": 42, + "column": 11, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "80ace30483979a840c9f6cbe0e8481bafe77708b" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkCreate/index.js", + "line": 12, + "column": 7, + "ruleId": "no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "812c02cd516013d189322ca62677793aadb99edf" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkCreate/index.js", + "line": 12, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "aaaaa63e7a60443b8cbf8191feb9142852ebdf1c" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue", + "line": 436, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'dateToISO' is defined but never used.", + "hash": "2ec2e571f3e30aa4c2be2d27bb2e8e0911f2b73f" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue", + "line": 437, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'ISOToDate' is defined but never used.", + "hash": "e798ef13db37e7acb223e3df29da4ce8dbbd3ae6" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue", + "line": 438, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'ISOToDatetime' is defined but never used.", + "hash": "76ca9c82007f9040643d2a8cf2689ebe1110823e" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue", + "line": 451, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'buildLinkCreate' is defined but never used.", + "hash": "0dbad8bda4c2da8b4489288fdaf8e4e42583c399" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue", + "line": 512, + "column": 5, + "ruleId": "vue/no-unused-components", + "message": "The \"AddressRenderBox\" component has been registered but not used.", + "hash": "1fa5e8a5a2e917188fd4b5f5d215601e1be5ea9a" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue", + "line": 514, + "column": 5, + "ruleId": "vue/no-unused-components", + "message": "The \"PickTemplate\" component has been registered but not used.", + "hash": "b68269999f017d4125854ca86a09f355f046c5fb" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue", + "line": 699, + "column": 25, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'data' is defined but never used.", + "hash": "66ec68e084b27a818062ebf5d612bdd4598f1cc6" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue", + "line": 710, + "column": 25, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'data' is defined but never used.", + "hash": "fed92eac022439396e7c1a7dd5f00cf9b958c1e2" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/AddEvaluation.vue", + "line": 136, + "column": 26, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "d762b8ac0404258c3f69a602e838c141e7260af0" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/AddEvaluation.vue", + "line": 142, + "column": 28, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "49bc6a911e9dd1170d487d095920c519784a84bc" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/AddEvaluation.vue", + "line": 142, + "column": 35, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'link' is defined but never used.", + "hash": "537ba2743fe9b6894692e6a481d82557f533b75f" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue", + "line": 79, + "column": 13, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "422f53925922e59655d0f71624c19af75d41628c" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue", + "line": 275, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'dateToISO' is defined but never used.", + "hash": "1fb4dee6397e578ac9e4587fcf81b35ce1bfe3d9" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue", + "line": 276, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'ISOToDate' is defined but never used.", + "hash": "ea360950c17604fe63003333c7a5a9c303887ca6" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue", + "line": 281, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'mapGetters' is defined but never used.", + "hash": "ec02e9573e9ee7cc13e25fda951b748ba8ae8f93" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue", + "line": 600, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'event' is defined but never used.", + "hash": "b7f25f6178570c873ffb8eefd349b967bb9afb17" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue", + "line": 601, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'link' is defined but never used.", + "hash": "b60571c99af4afc2933bedef97fa6f745ce45a0f" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/index.js", + "line": 12, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "138abd46734ddf8fe1ebdb530a2119b9052143b6" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js", + "line": 6, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'ISOToDatetime' is defined but never used.", + "hash": "04da431c5998791e27a587762af6efb87382dd35" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js", + "line": 10, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'findSocialActionsBySocialIssue' is defined but never used.", + "hash": "e332ac8d07d5801f0591fc5197504f88300199dc" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js", + "line": 11, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'create' is defined but never used.", + "hash": "49a7ec9b72de87036e05882d92bea39a7d6595f4" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js", + "line": 17, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'evalFQDN' is assigned a value but never used.", + "hash": "7fc32caafa23addddf44f3acbc5045b4523a0271" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js", + "line": 611, + "column": 9, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'errors' is assigned a value but never used.", + "hash": "c41cf979fc1626c38328dbf1028800c3395496bd" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/App.vue", + "line": 282, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-expressions", + "message": "Expected an assignment or function call and instead saw an expression.", + "hash": "de3a6e2bb10a80a2bacba665be74266c7efc7d64" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/index.js", + "line": 16, + "column": 9, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "b025959acb471750c92952656e7a60ba28f5da0a" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Positioning.vue", + "line": 82, + "column": 31, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is defined but never used.", + "hash": "aad0b0d7e4bc2ea9f0a0dd7b6c696a82da14f25a" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/index.js", + "line": 13, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "2f161e663689e3e4dfe2c53b0d64c91a4d2b1a60" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue", + "line": 263, + "column": 19, + "ruleId": "vue/return-in-computed-property", + "message": "Expected to return a value in \"refreshNetwork\" computed property.", + "hash": "2c1b08a49098c83b09058cedc0a962126e91e544" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue", + "line": 270, + "column": 7, + "ruleId": "vue/no-side-effects-in-computed-properties", + "message": "Unexpected side effect in \"legendLayers\" computed property.", + "hash": "760948d2187c853f17ac9a1bd7107e883092d4f4" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue", + "line": 281, + "column": 5, + "ruleId": "vue/no-dupe-keys", + "message": "Duplicate key 'checkedLayers'. May cause name collision in script or template tag.", + "hash": "447edb461e15e3ff5c60c8ecba88131e442539aa" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue", + "line": 353, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-expressions", + "message": "Expected an assignment or function call and instead saw an expression.", + "hash": "9cf656cbf1eb3d7cc0082e63adcd320b6093d14f" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/index.js", + "line": 20, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'app' is assigned a value but never used.", + "hash": "9e94e6412b8a44e47bfe8e66218cad09cff5bed4" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriod/SetReferrer.vue", + "line": 42, + "column": 16, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'response' is defined but never used.", + "hash": "62de07b13c662e32332bb062038acee23978ea70" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue", + "line": 356, + "column": 28, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'_response' is defined but never used.", + "hash": "097e7788a2b5dea500b80b8a3cf968e57063a66a" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeUserGroup.vue", + "line": 6, + "column": 8, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'BadgeEntity' is defined but never used.", + "hash": "951a1b012bdec10c4b859af8b34dd894f63add23" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeUserGroup.vue", + "line": 7, + "column": 8, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'UserRenderBoxBadge' is defined but never used.", + "hash": "99eba0d8633b2c9497417f4f61ec4194dbb2a96b" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/Person.vue", + "line": 430, + "column": 17, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "e48c11f9011de90dab15b426baf77030ffae7e22" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/Person.vue", + "line": 448, + "column": 33, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'reject' is defined but never used.", + "hash": "dfd37ec320e99059374135a72bfd53978e7e66ae" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdParty.vue", + "line": 371, + "column": 21, + "ruleId": "@typescript-eslint/no-unused-expressions", + "message": "Expected an assignment or function call and instead saw an expression.", + "hash": "ed6f5ae90514bf9fc967f2eb7a3ce06344a5f8af" + }, + { + "path": "src/Bundle/ChillWopiBundle/src/Resources/public/module/pending/index.ts", + "line": 4, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'StoredObjectStatus' is defined but never used.", + "hash": "63f8c4572293916850d6165647774b27d4b732c6" + }, + { + "path": "src/Bundle/ChillWopiBundle/src/Resources/public/module/pending/index.ts", + "line": 5, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'StoredObjectStatusChange' is defined but never used.", + "hash": "a87c178e3eb5999bf0f46b3fa1c6da77e1be08b9" + }, + { + "path": "src/Bundle/ChillWopiBundle/src/Resources/public/module/pending/index.ts", + "line": 30, + "column": 61, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'e' is defined but never used.", + "hash": "02953121583f4f73742a19adab099ab63df9076e" + }, + { + "path": "src/Bundle/ChillWopiBundle/src/Resources/public/module/pending/index.ts", + "line": 31, + "column": 32, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "af48e21a1651b6017ede882dab249c00a818a44d" + }, + { + "path": "src/Bundle/ChillWopiBundle/src/Resources/public/module/pending/index.ts", + "line": 37, + "column": 16, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "7513ea552a0a649ce4ab93b6cf9d40bfef4f68d9" + }, + { + "path": "webpack.config.js", + "line": 77, + "column": 28, + "ruleId": "@typescript-eslint/no-empty-function", + "message": "Unexpected empty arrow function.", + "hash": "a8e433351f1c8bc89a95c8570dd0781d59e26103" + } +] \ No newline at end of file diff --git a/.gitignore b/.gitignore index 26802dca0..88458b64e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,20 @@ composer composer.phar composer.lock docs/build/ -node_modules/* .php_cs.cache .cache/* +yarn.lock + +docker/db/data +docker/rabbitmq/data + +# in this development bundle, we want to ignore directories related to a real app +assets/* +!assets/translator.ts +!assets/ux-translator +migrations/* +templates/* +translations/* ###> symfony/framework-bundle ### /.env.local @@ -15,7 +26,6 @@ node_modules/* /public/bundles/ /var/ /vendor/ -/bin/ ###< symfony/framework-bundle ### ###> phpunit/phpunit ### @@ -27,4 +37,25 @@ node_modules/* /.idea/ /.psalm/ -node_modules/* +###> phpstan/phpstan ### +phpstan.neon +###< phpstan/phpstan ### + +###> lexik/jwt-authentication-bundle ### +/config/jwt/*.pem +###< lexik/jwt-authentication-bundle ### + +###> symfony/phpunit-bridge ### +###< symfony/phpunit-bridge ### + +###> symfony/webpack-encore-bundle ### +/node_modules/ +/public/build/ +npm-debug.log +yarn-error.log +###< symfony/webpack-encore-bundle ### + +###> friendsofphp/php-cs-fixer ### +/.php-cs-fixer.php +/.php-cs-fixer.cache +###< friendsofphp/php-cs-fixer ### diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e355d95de..02c01640c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,11 +5,12 @@ cache: paths: - /vendor/ - .cache + - node_modules/ # Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service # See http://docs.gitlab.com/ee/ci/services/README.html for examples. services: - - name: postgis/postgis:14-3.3-alpine + - name: postgis/postgis:17-3.5-alpine alias: db command: - postgres @@ -26,7 +27,7 @@ variables: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres # configure database access - DATABASE_URL: postgresql://postgres:postgres@db:5432/postgres?serverVersion=14&charset=utf8 + DATABASE_URL: postgresql://postgres:postgres@db:5432/postgres?serverVersion=17&charset=utf8 # fetch the chill-app using git submodules # GIT_SUBMODULE_STRATEGY: recursive REDIS_HOST: redis @@ -56,7 +57,6 @@ build: artifacts: expire_in: 1 day paths: - - bin - vendor/ code_style: @@ -70,38 +70,54 @@ code_style: artifacts: expire_in: 1 day paths: - - bin - vendor/ phpstan_tests: stage: Tests image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82 + variables: + COMPOSER_MEMORY_LIMIT: 3G + before_script: + - bin/console cache:clear --env=dev script: - - bin/phpstan analyze --memory-limit=2G + - composer exec phpstan -- analyze --memory-limit=3G cache: paths: - .cache/ artifacts: expire_in: 1 day paths: - - bin - vendor/ rector_tests: stage: Tests image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82 + before_script: + - bin/console cache:clear --env=dev script: - - tests/console cache:clear - - bin/rector process --dry-run + - composer exec rector -- process --dry-run cache: paths: - .cache/ artifacts: expire_in: 1 day paths: - - bin - vendor/ +lint: + stage: Tests + image: node:20-alpine + before_script: + - apk add --no-cache python3 make g++ py3-setuptools + - export PYTHON="$(which python3)" + - export PATH="./node_modules/.bin:$PATH" + script: + - yarn install --ignore-optional + - npx eslint-baseline "src/**/*.{js,ts,vue}" + cache: + paths: + - node_modules/ + # psalm_tests: # stage: Tests # image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82 @@ -117,16 +133,19 @@ rector_tests: unit_tests: stage: Tests image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82 + variables: + COMPOSER_MEMORY_LIMIT: 3G + before_script: + - php bin/console doctrine:database:create -n --env=test + - php bin/console doctrine:migrations:migrate -n --env=test + - php bin/console chill:db:sync-views --env=test + - php bin/console cache:clear --env=test + - php bin/console doctrine:fixtures:load -n --env=test script: - - php tests/console doctrine:migrations:migrate -n --env=test - - 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 + - composer exec phpunit -- --colors=never --exclude-group dbIntensive,openstack-integration artifacts: expire_in: 1 day paths: - - bin - vendor/ release: @@ -138,4 +157,4 @@ release: - echo "running release_job" release: tag_name: '$CI_COMMIT_TAG' - description: "./.changes/v$CI_COMMIT_TAG.md" + description: "./.changes/$CI_COMMIT_TAG.md" diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..2edeafb09 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 \ No newline at end of file diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 4a70e81e7..3360abe85 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -25,7 +25,7 @@ $config = new PhpCsFixer\Config(); $config ->setFinder($finder) ->setRiskyAllowed(true) - ->setCacheFile('.cache/php-cs-fixer.cache') + ->setCacheFile('var/php-cs-fixer.cache') ->setUsingCache(true) ->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect()) ; @@ -120,6 +120,6 @@ $rules = array_merge( $untilFullSwitchToPhp8, ); -$rules['header_comment']['header'] = trim(file_get_contents(__DIR__.'/resource/header.txt')); +$rules['header_comment']['header'] = trim(file_get_contents(__DIR__.'/resources/header.txt')); return $config->setRules($rules); diff --git a/.symfony.local.yaml b/.symfony.local.yaml new file mode 100644 index 000000000..fad7fc396 --- /dev/null +++ b/.symfony.local.yaml @@ -0,0 +1,2 @@ +workers: + docker_compose: ~ diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b2f5e2e1..b1b9aa894 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,345 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), and is generated by [Changie](https://github.com/miniscruff/changie). +## v3.10.3 - 2025-03-18 +### DX +* Eslint fixes + +## v3.10.2 - 2025-03-17 +### Fixed +* Replace a ts-expect-error with a ts-ignore + +## v3.10.1 - 2025-03-17 +### DX +* Remove yarn dependency to symfony/ux-translator, to ease the build process + +## v3.10.0 - 2025-03-17 +### Feature +* ([#363](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/363)) Display social actions grouped per social issue within activity form +### Fixed +* ([#362](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/362)) Fix Dependency Injection, which prevented to save the CalendarRange +* ([#368](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/368)) fix search query for user groups + +## v3.9.2 - 2025-02-27 +### Fixed +* Use fetchResults method to fetch all social issues instead of only the first page + +## v3.9.1 - 2025-02-27 +### Fixed +* Fix post/patch request with missing 'type' property for gender + +## v3.9.0 - 2025-02-27 +### Feature +* ([#349](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/349)) Suggest all referrers within actions of the accompanying period when creating an activity +* ([#343](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/343)) Add possibility to export a csv with all social issues and social actions +* ([#360](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/360)) Restore document to previous kept version when a workflow is canceled +* ([#341](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/341)) Add a list of third parties from within the admin (csv download) +### Fixed +* fix generation of document with accompanying period context, and list of activities and works +### DX +* ([#333](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/333)) Create an unique source of trust for translations + +## v3.8.2 - 2025-02-10 +### Fixed +* ([#358](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/358)) Remove "filter" button on list of documents in the workflow's "add attachement" modal + +## v3.8.1 - 2025-02-05 +### Fixed +* Fix household link in the parcours banner + +## v3.8.0 - 2025-02-03 +### Feature +* Improve the UX of the news item admin form to prevent wrong usage +* ([#319](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/319)) Notification list: display the concerned person's badges in the list +* ([#320](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/320)) Show the first 3 persons directly in the accompanying period's banner +* ([#334](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/334)) Suggest current user when creating an activity +* ([#331](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/331)) Add attachments to workflows +### Fixed +* ([#350](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/350)) Add validation error to manual selection of person in PersonDuplicateController +* ([#354](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/354)) Fix document category creation +* ([#351](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/351)) Add definitive whitespace between span elements in vue PersonText component + +## v3.7.1 - 2025-01-21 +### Fixed +* Fix legacy configuration processor for notifier component + +## v3.7.0 - 2025-01-21 +### Feature +* Use the Notifier component from Symfony to sens short messages (SMS). This allow to use more provider. +### Fixed +* ([#348](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/348)) [export] Fix aggregation of referrer's scope and job: fix the date range comparison + +### Warning on configuration of Notifier component + +If installed in an symfony app where the recipes are activated, this configuration should be added automatically: + +```yaml +framework: + notifier: + chatter_transports: + texter_transports: + ovhcloud: '%env(OVHCLOUD_DSN)%' + channel_policy: + # use chat/slack, chat/telegram, sms/twilio or sms/nexmo + urgent: ['email'] + high: ['email'] + medium: ['email'] + low: ['email'] + admin_recipients: + - { email: admin@example.com } +``` + +Actually, you should either: + +- remove the configuration of ovhcloud added by the recipe +- or remove the previous configuration of chill, to avoid keeping legacy configuration + +#### Remove the added configuration and keep the legacy configuration + +To remove the configuration: + +```diff +framework: + notifier: + chatter_transports: + texter_transports: +- ovhcloud: '%env(OVHCLOUD_DSN)%' +``` + +In that case, the previous configuration, which was stored under the `chill_main.short_messages.dsn` will be reconfigured into the Notifier component's configuration. + +#### Properly configure SMS + +You can also properly configure it, as [described in the OVH cloud provider repository](https://github.com/symfony/ovh-cloud-notifier/tree/5.4?tab=readme-ov-file#dsn-example) (where the scheme is `ovhcloud`): + +**NOTE**: You have access to all notifier available with the [Notifier component](https://symfony.com/doc/current/notifier.html#notifier-sms-channel). You are not restricted to use OVH as a provider. + +```diff +framework: + notifier: + chatter_transports: + texter_transports: ++ ovhcloud: '%env(OVHCLOUD_DSN)%' # this value should be located in a variable, and have `ovhcloud://` as a scheme + +chill_main: +- short_messages: +- dsn: '%env(string:SHORT_MESSAGE_DSN)%' +``` + +## v3.6.0 - 2025-01-16 +### Feature +* Importer for addresses does not fails when the postal code is not found with some addresses, and compute a recap list of all addresses that could not be imported. This recap list can be send by email. +* ([#346](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/346)) Create a driver for storing documents on disk (instead of openstack object store) + +* Add address importer from french Base d'Adresse Nationale (BAN) +* ([#343](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/343)) Add csv export for social issues and social actions +### Fixed +* Export: fix missing alias in activity between certain dates filter. Condition added for alias. + +## v3.5.3 - 2025-01-07 +### Fixed +* Fix the EntityToJsonTransformer to return an empty array if the value is "" + +## v3.5.2 - 2024-12-19 +### Fixed +* ([#345](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/345)) Export: activity filtering of users that were associated to an activity between certain dates. Results contained activities that were not within the specified date range" + +## v3.5.1 - 2024-12-16 +### Fixed +* Filiation: fix the display of the gender label in the graph +* Wrap handling of PdfSignedMessage into transactions + +## v3.5.0 - 2024-12-09 +### Feature +* ([#318](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/318)) Show all the pages of the documents in the signature app +### Fixed +* Wrap the signature's change state into a transaction, to avoid race conditions +* Fix display of gender label + +## v3.4.3 - 2024-12-05 +### Fixed +* Remove the "not null" constraint on person supplementary phones +* Remove doctrine annotation that prevent from adding documents to activities + +## v3.4.2 - 2024-12-05 +### Fixed +* ([#329](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/329)) Fix the serialization of gender for the generation of documents +* ([#337](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/337)) Enforce unique contraint on activity storedobject +### DX +* ([#310](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/310)) Clean migrations, to reduce the number of bloated migration when running diff on schema + +## v3.4.1 - 2024-11-22 +### Fixed +* Set the workflow's title to notification content and subject + +## v3.4.0 - 2024-11-20 +### Feature +* ([#314](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/314)) Admin: improve document type admin form with a select field for related class. +Admin: Allow administrator to assign multiple group centers in one go to a user. + +## v3.3.0 - 2024-11-20 +### Feature +* Electronic signature + +Implementation of the electronic signature for documents within chill. +* ([#286](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/286)) The behavoir of the voters for stored objects is adjusted so as to limit edit and delete possibilities to users related to the activity, social action or workflow entity. +* ([#288](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/288)) Metadata form added for person signatures +* Add a signature step in workflow, which allow to apply an electronic signature on documents +* Keep an history of each version of a stored object. +* Add a "send external" step in workflow, which allow to send stored objects and other elements to remote people, by sending them a public url +### Fixed +* Adjust household list export to include households even if their address is NULL +* Remove validation of date string on deathDate + +## v3.2.4 - 2024-11-06 +### Fixed +* Fix compilation of chill assets + +## v3.2.3 - 2024-11-05 +### Fixed +* ([#315](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/315)) Fix display of accompanying period work referrers. Only current referrers should be displayed. +Fix color of Chill footer + +## v3.2.2 - 2024-10-31 +### Fixed +* Fix gender translation for unknown + +## v3.2.1 - 2024-10-31 +### Fixed +* Add the possibility of unknown to the gender entity +* Fix the fusion of person doubles by excluding accompanyingPeriod work entities to be deleted. They are moved instead. + +## v3.2.0 - 2024-10-30 +### Feature +* Introduce a gender entity + +## v3.1.1 - 2024-10-01 +### Fixed +* ([#308](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/308)) Show only the current referrer in the page "show" for an accompanying period workf +* ([#309](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/309)) Correctly compute the grouping by referrer aggregator + +* Fixed typing of custom field long choice and custom field group + +## 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 + +## 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. + +## v2.23.0 - 2024-07-23 & 2024-07-19 +### Feature +* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles +* Add job bundle (module emploi) +* Upgrade import of address list to the last version of compiled addresses of belgian-best-address + +* Upgrade CKEditor and refactor configuration with use of typescript + +* ([#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. + +## v2.22.2 - 2024-07-03 +### Fixed +* Remove scope required for event participation stats + +## v2.22.1 - 2024-07-01 +### Fixed +* Remove debug word +### DX +* Add a command for reading official address DB from Luxembourg and update chill addresses + +## v2.22.0 - 2024-06-25 +### Feature +* ([#216](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/216)) [event bundle] exports added for the event module + +### Traduction francophone +* Exports sont ajoutés pour la module événement. + +## v2.21.0 - 2024-06-18 +### Feature +* Add flash menu buttons in search results, to open directly a new calendar, or a new activity in an accompanying period +* ([#122](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/122)) Improve the list of calendar in the search results: make all calendar clicable, and display a list of calendars +* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add start date and end date on filters "filter course by referrer job" and "filter course by referrer scope" +* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] the aggregator "Group by referrer" now accept a date range. +* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add date range on "group course by referrer's scope" +* ([#282](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/282)) [export] add date range on "group course by referrer's jobs" +* ([#168](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/168) In the UX, display user job and service at the time when he performs an action: + now, the job and service is shown: + * at the activity's date, + * at the appointment's date, + * when the user is marked as referrer for an accompanying period work, + * when the user apply a transition in a workflow, + * when the user updates or creates "something" ("created/updated by ... at ..."), + * or when he wrote a comment, + * … + +### Traduction francophone +* Ajout d'un menu "flash" dans les résultats de recherche, pour créer un rendez-vous ou un échange dans un parcours depuis les résultats de recherche; +* Améliore la liste des rendez-vous dans les résultats de recherche: les rendez-vous sont cliquables; +* [exports] Ajout d'intervalles de dates pour des filtres et regroupements des parcours par référent, métier du référent, service du référent; +* Affiche le métier et le service des utilisateurs à la date à laquelle il a exécuté une action. Le métier et le service est affiché: + * à la date d'un échange, + * au jour d'un rendez-vous, + * quand l'utilisateur est devenu référent d'un parcours d'accompagnement, + * quand il a appliqué une transition sur un workflow, + * quand il a mise à jour ou créé une fiche, dans les mentions "créé / mise à jour par ..., le ...", + * quand il a mis à jour un commentaire, + * … + + +## v2.20.1 - 2024-06-05 +### Fixed +* Do not allow StoredObjectCreated for edit and convert buttons + +## v2.20.0 - 2024-06-05 +### Fixed +* ([#170](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/170)) Display agents traitants instead of accompanying period referrer in export list social actions. +* Added translations for choices of durations (> 5 hours) +### Feature +* ([#145](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/145)) Allow to open documents in LibreOffice locally (need configuration within security); + + This endpoint should be added to make the endpoint works properly: + + ```yaml + security: + firewalls: + dav: + pattern: ^/dav + provider: chain_provider + stateless: true + guard: + authenticators: + - Chill\DocStoreBundle\Security\Guard\JWTOnDavUrlAuthenticator + + ``` + ## v2.19.0 - 2024-05-14 ### Feature * ([#197](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/197)) Make the script which subscribe to microsoft calendars changes more tolerant to errors or missing configuration on the microsoft side diff --git a/CONVENTIONS-en.md b/CONVENTIONS-en.md new file mode 100644 index 000000000..45ec38f89 --- /dev/null +++ b/CONVENTIONS-en.md @@ -0,0 +1,386 @@ +# Chill Conventions + +In Progress + +## Translations + +Per bundle, all translations for Twig pages are located in a single file: translations/messages.fr.yaml. + +## File Locations + + Controllers, form types, and Twig templates are placed in the root folders Controller, Form, and Resources/views, respectively. + Admin pages are no longer placed in subfolders under Admin. + +## Assets: Entrypoint Naming + +There are three types of entry points: + +* Vue application-specific (often for a single page): Prefixed with vue_. + * Reusable JavaScript/CSS code: + Examples include: + * ckeditor + * async_upload (used for forms) + * bootstrap + * chill.js + * ... + => We prefix with `mod_` + + * Page-specific CSS/JS: + * Often reuses functionalities like ShowHide. + => We prefix with `page_`. + +## Folder Structure + +```` +# Under Resources/public: + + - chill/ => Contains the theme (Chill). + - chillmain.scss: Compiled into the chill entrypoint. + - lib/ => Libraries never used as entrypoints but reused elsewhere. + - Examples: ShowHide, Collection, Select2. + - module/: Ends up in reusable entrypoints (mod_). + - bootstrap + - custom.scss + - custom/ + - variable.scss + - ... + - AsyncUpload. + - vue/ => Vue applications only (vue_). + - Examples: _components, app. + - page/ => Page-specific assets (page_). + - login + - person + - personvendee + - household_edit_metadata + - index.js +```` + +# Stylesheet Organization + +1. The mod_bootstrap (module bootstrap) entry point is the first level. All parts (modules) of bootstrap are included in the bootstrap.js file located in ChillMainBundle/Resources/public/module/bootstrap. + + * At the beginning, this file imports the variables.scss file, which determines most of the bootstrap settings as customized. This file overrides the original, and many variables are adjusted for Chill. + * Care must be taken to ensure this file can always be compared to the original bootstrap file. In the event of a bootstrap update, a diff must be generated and this diff applied to the variable file of the new version. + * At the end, it imports the custom.scss file, which includes bootstrap adaptations to prepare it for our Chill theme. + * This `custom.scss` file can be split into smaller files using `@import 'custom/...'`. + * The idea is that this first bootstrap layer sets an important part of the application’s styles, particularly those related to layout positioning, responsive breakpoints, and the default margins and spacing applied to elements being manipulated. + +2. The chill entry point is the second level. It contains the Chill theme, which is recognizable in the application. + + * Each bundle has a `Resources/public/chill` folder containing a main sass file, which can optionally be split using `@imports`. All these files are compiled into a single Chill entry point, which serves as the application’s theme and overrides bootstrap. + * The chillmain.scss file should contain the most general style cascades, those applied to many areas of the application. + * The chillperson.scss file also includes styles specific to different contexts related to people: person, household, and accompanying course. + * Some smaller bundles contain only styles specific to their functionality. + +3. The vue_ entry points are used for Vue components. Vue files can contain an scss style block. These styles are specific to the component and its inheritance, with the scoped tag precisely defining their scope (see the documentation). + +4. The page_ entry points are used to add assets specific to certain pages, most often scripts and styles. + +## HTML Tagging and Style Cascades + +The following example shows how to tag a code element without overdoing it. Note that: + +* It is not necessary to tag all inner classes. +* The parent class should not be repeated in all child classes. Sass cascading allows for flexible HTML structuring without overloading the tag hierarchy. +* Often, the first class will have variations created with additional classes that start in the same way: bloc-dark simply adds the dark version of bloc. We do not use bloc dark because we don’t want the dark class of bloc to interact with the same dark class of table. As a result, we will have an element bloc bloc-dark and another element table table-dark. + +```html +
+

My Title

+
    +
  • + +
  • +
+
+``` + +Finally, it is important to define what a block, an action zone, and a button are. These three elements exist independently and are the only ones we tag. + +For example, to style the title, we simply specify h3 within the block cascade. + +```scss +div.bloc { + // un bloc générique, utilisé à plusieurs endroits + &.bloc-dark { + // la version sombre du bloc + } + h3 {} + ul { + // une liste standard dans bloc + li { + // des items de liste standard dans bloc + } + } +} +div.mon-bloc { + // des exceptions spécifiques à mon-bloc, + // qui sont des adaptations de bloc +} + +ul.record_actions { + // va uniformiser tous les record_actions de l'application + li { + //... + } +} + +.btn { + // les boutons de bootstrap + .btn-edit { + // chill étends les boutons bootstrap pour ses propres besoins + } +} + +``` +## Render box + +## URL + +## Route Naming Conventions + +:::warning +These rules have not always been followed in the past. They are desired for the future. +::: + +Routes follow this structure: + +`chill_(api|crud)_bundle_(api)_entity_action` + +1. First, chill_ (for all Chill modules). +2. Then, crud or api, optional, automatically added if the route is generated by the configuration. +3. Then, a string indicating the bundle (`main`, `person`, `activity`, ...). +4. Then, api, if the route is an API route. +5. Then, a string indicating the entity the route targets, and possibly the sub-entities. +6. Then, an action (`list`, `view`, `edit`, `new`, ...). + +Indicating `api` in the fourth position allows distinguishing API routes generated by the configuration (which are all prefixed with `chill_api`) from those generated manually. (For example: `chill_api_household__index` and `chill_person_api_household_members_move`). + +If points 4 and 5 are missing, they are replaced by other elements to ensure the uniqueness of the route and its clear understanding. + +#### HTML pages + +:::warning +These rules have not always been followed in the past. They are desired for the future. +::: + +Syntaxe: + +``` +/{_locale}/bundle/entity/{id}/action +/{_locale}/bundle/entity/sub-entity/{id}/action +``` + +The following elements should be included in the list: + +1. The locale; +2. An identifier for the bundle; +3. The entity it relates to; +4. Any sub-entities that the URL refers to; +5. The action. + +``` +# list of activities for a person +/fr/activity/person/25/activity/list + +# new activity +/fr/activity/activity/new?person_id=25 + +``` + +#### Pour les API + +:::info +Automatically generated routes are prefixed with `chill_api` +::: + +Syntaxe: + +``` +/api/1.0/bundle/entity/{id}/action +/api/1.0/bundle/entity/sub-entity/{id}/action +``` + +The following elements should be included in the list: + +1. The string `/api/` followed by the version (e.g., 1.0); +2. An identifier for the bundle; +3. The entity it relates to; +4. Any sub-entities that the URL refers to; +5. The action. + +These elements may be interspersed with the entity identifier. In this case, the identifier should be placed immediately after the entity it relates to. + +#### URLS for admin pages +Same conventions as for other html pages, **but `admin` is added in second position**. Soit: + +`/{_locale}/admin/bundle/entity/{id}/action` + +## Database table naming convention + +When creating a new entity and the corresponding database table, we follow the following naming convention for the database table: + +`chill_{bundle_identifier}_{entity_name}`. +Eg. chill_person_spoken_languages + +## UI Rules + +### Page Titles +#### Every page must have a title (in the head tag and page header). + +Each page contains a title in the tag. This title is typically the same as the header title on the page. + +Tip: It is possible to use the block function in Twig for this. + +Example: + +```htmlmixed= +{% block title "Titre de la page" %} + +{% block content %} +

+ {{ block('title')}} +

+{% endblock %} +``` + +### entity_render usage + +#### in Twig +Always use chill_entity_render_box for rendering entities like: +* User +* Person +* SocialAction +* SocialIssue +* Address +* Thirdparty +* ... + +Example: + +``` +address|chill_entity_render_box +``` +Justification: + +1. Customization by installation: Some elements are sometimes customized during installation (for example, the name of each user will be followed by the name of the service). +2. To streamline and make displays consistent: Ensures uniformity in the way information is displayed across different pages or sections. +3. To simplify Twig code: By using blocks and centralizing title logic, it reduces repetition and makes the Twig code easier to maintain. + +* Three rendering modes: + * inline + * block + * item. + +#### In Vue + +There is always a renderbox equivalent in vue. + +#### HTML Links to Sections + +Always include links/icons for accessing records, such as person or household details, if the user has access. + +### Form Guidelines + +#### Vocabulary: +* `Create` in a `bt bt-create` for links to the form to create an entity (to access the form). +* `Save` in a `bt bt-save` for "Save" buttons (in either an edit or create form). +* `Save and new` +* `Save and view` +* `Edit` in a `bt bt-edit` for links to the edit form. +* `Duplicate` (specify where it can be seen). +* `Cancel` for leaving an edit page with a link to the list, or the `returnPath`. + +#### After Saving: + +Redirect to the returnPath if available; otherwise, redirect to the view page. + +### Sticky Form Buttons: + +Buttons like "Cancel" and "Save" must be within a "sticky-form" bar at the bottom of the form. + +If relevant: + +* The banner contains a "Cancel" button that returns to the previous page. It is mandatory for forms, but optional for lists or "summary" pages. +* This "Cancel" button is always on the left. + +``` + +``` + +### Flash Messages + +#### Display a flash message upon entity creation: + +Everytime an entity has been created + +> "The entity has been created." + +The name of the element can be replaced with something more relevant: + +> * The activity has been created +> * The appointment has been created +> * ... + +#### On Saving an Entity + +Each time an entity is saved, a flash message should appear: + +> The data has been modified + +#### Form Error (Validation Error) + +At the top of the form, a flash message should indicate that validations have failed: + +> This form contains errors + +Errors should appear attached to the field they concern. However, it is acceptable to display errors at the root of the form if it is technically difficult to attach errors. + +### Return Links + +Each time a link is provided, check whether the function chill_return_path, chill_forward_return_path, or chill_return_path_or should be used. +* From the list page, to the opening of an element, or the creation button => use chill_path_add_return_path +* In these edit pages: + * use chill_return_path_or in the "Cancel" button; + * for the "Save and view" and "Save and close" buttons => ? + +### Assets for Suggestion Lists + +Create a list of suggestions to add (the entire item is clickable) + +```html +
    +
  • + item +
  • +
+``` + +Create a list of suggestions to remove (with a clickable red cross, the anchor is empty) + +```html +
    +
  • + + item + +
  • +
+``` + +Create a removable title (with a clickable red cross, the anchor
is empty) + +```html +
+ title +
+``` + +The classes `cols` or `inline` can be added alongside `list-suggest` to modify the layout of the list. In the last example, add a `removable` class to the `` if you want to make the item removable. diff --git a/CONVENTIONS.md b/CONVENTIONS-fr.md similarity index 98% rename from CONVENTIONS.md rename to CONVENTIONS-fr.md index 0fe76a135..4462f4e08 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS-fr.md @@ -4,7 +4,6 @@ en cours de rédaction ## Translations - Par bundle, toutes les traductions des pages twig se trouvent dans un seul fichier `translations/messages.fr.yaml`. ## Emplacement des fichiers @@ -142,7 +141,6 @@ ul.record_actions { ## Render box - ## URL ### Nommage des routes @@ -234,6 +232,13 @@ Même conventions que dans les autres pages html de l'application, **mais `admin `/{_locale}/admin/bundle/entity/{id}/action` +### Nommage des tables de base de donnée + +Lors de la création d'une nouvelle entité et de la table de base de données correspondante, nous suivons la convention d'appellation suivante pour la table de base de données : + +`chill_{bundle_identifier}_{nom_de_l'entité}`. + +Par exemple : chill_person_spoken_languages ## Règles UI chill @@ -293,8 +298,6 @@ A prevoir: > quand on passe l’option render: bloc, on peut placer le render_box dans une boucle for plus large qui fonctionne avec la classe flex-table ou la classe flex-bloc, ce qui donnera un affichage en rangée (table) ou en blocs. [name=Mathieu] - - #### En vue Il existe systématiquement une "box" équivalente en vue. diff --git a/README.md b/README.md index bf3e28f41..6a5c7d603 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,15 @@ -# Chill framework +# Main Chill Bundles and Chill framework -Documentation of the Chill software. +Chill is a software for social workers. It allows them to keep track of the social work they do. -The online documentation can be found at http://docs.chill.social +See our website for more information https://www.chill.social -See the [`docs`][1] directory for more. +## Installation -[1]: docs/README.md +Chill-bundles is a set of bundles that should be used within a Symfony application. + +A symfony application will help you to customize all the configuration options, change the behaviour of some parts of the usual-way that chill works, … without to have to fork this repository ! + +See [the instructions in the docs](./docs/source/installation/index.rst). + +Those instructions are also built [online](https://docs.chill.social). diff --git a/assets/translator.ts b/assets/translator.ts new file mode 100644 index 000000000..fe4e14ffa --- /dev/null +++ b/assets/translator.ts @@ -0,0 +1,7 @@ +import { trans, setLocale, setLocaleFallbacks } from "./ux-translator"; + +setLocaleFallbacks({"en": "fr", "nl": "fr", "fr": "en"}); +setLocale('fr'); + +export { trans }; +export * from '../var/translations'; diff --git a/assets/ux-translator/README.md b/assets/ux-translator/README.md new file mode 100644 index 000000000..e6cad5f1a --- /dev/null +++ b/assets/ux-translator/README.md @@ -0,0 +1,3 @@ +This directory import the symfony ux-translator files directly into chill-bundles. + +This remove the yarn dependencies from the real package, which breaks our installation. diff --git a/assets/ux-translator/dist/formatters/formatter.d.ts b/assets/ux-translator/dist/formatters/formatter.d.ts new file mode 100644 index 000000000..c3403c041 --- /dev/null +++ b/assets/ux-translator/dist/formatters/formatter.d.ts @@ -0,0 +1 @@ +export declare function format(id: string, parameters: Record, locale: string): string; diff --git a/assets/ux-translator/dist/formatters/intl-formatter.d.ts b/assets/ux-translator/dist/formatters/intl-formatter.d.ts new file mode 100644 index 000000000..e22d7481a --- /dev/null +++ b/assets/ux-translator/dist/formatters/intl-formatter.d.ts @@ -0,0 +1 @@ +export declare function formatIntl(id: string, parameters: Record, locale: string): string; diff --git a/assets/ux-translator/dist/translator.d.ts b/assets/ux-translator/dist/translator.d.ts new file mode 100644 index 000000000..8d9f73e02 --- /dev/null +++ b/assets/ux-translator/dist/translator.d.ts @@ -0,0 +1,27 @@ +export type DomainType = string; +export type LocaleType = string; +export type TranslationsType = Record; +export type NoParametersType = Record; +export type ParametersType = Record | NoParametersType; +export type RemoveIntlIcuSuffix = T extends `${infer U}+intl-icu` ? U : T; +export type DomainsOf = M extends Message ? keyof Translations : never; +export type LocaleOf = M extends Message ? Locale : never; +export type ParametersOf = M extends Message ? Translations[D] extends { + parameters: infer Parameters; +} ? Parameters : never : never; +export interface Message { + id: string; + translations: { + [domain in DomainType]: { + [locale in Locale]: string; + }; + }; +} +export declare function setLocale(locale: LocaleType | null): void; +export declare function getLocale(): LocaleType; +export declare function throwWhenNotFound(enabled: boolean): void; +export declare function setLocaleFallbacks(localeFallbacks: Record): void; +export declare function getLocaleFallbacks(): Record; +export declare function trans, D extends DomainsOf, P extends ParametersOf>(...args: P extends NoParametersType ? [message: M, parameters?: P, domain?: RemoveIntlIcuSuffix, locale?: LocaleOf] : [message: M, parameters: P, domain?: RemoveIntlIcuSuffix, locale?: LocaleOf]): string; diff --git a/assets/ux-translator/dist/translator_controller.d.ts b/assets/ux-translator/dist/translator_controller.d.ts new file mode 100644 index 000000000..aff1f5870 --- /dev/null +++ b/assets/ux-translator/dist/translator_controller.d.ts @@ -0,0 +1 @@ +export * from './translator'; diff --git a/assets/ux-translator/dist/translator_controller.js b/assets/ux-translator/dist/translator_controller.js new file mode 100644 index 000000000..c4504b75e --- /dev/null +++ b/assets/ux-translator/dist/translator_controller.js @@ -0,0 +1,283 @@ +import { IntlMessageFormat } from 'intl-messageformat'; + +function strtr(string, replacePairs) { + const regex = Object.entries(replacePairs).map(([from]) => { + return from.replace(/([-[\]{}()*+?.\\^$|#,])/g, '\\$1'); + }); + if (regex.length === 0) { + return string; + } + return string.replace(new RegExp(regex.join('|'), 'g'), (matched) => replacePairs[matched].toString()); +} + +function format(id, parameters, locale) { + if (null === id || '' === id) { + return ''; + } + if (typeof parameters['%count%'] === 'undefined' || Number.isNaN(parameters['%count%'])) { + return strtr(id, parameters); + } + const number = Number(parameters['%count%']); + let parts = []; + if (/^\|+$/.test(id)) { + parts = id.split('|'); + } + else { + parts = id.match(/(?:\|\||[^|])+/g) || []; + } + const intervalRegex = /^(?({\s*(-?\d+(\.\d+)?[\s*,\s*\-?\d+(.\d+)?]*)\s*})|(?[[\]])\s*(?-Inf|-?\d+(\.\d+)?)\s*,\s*(?\+?Inf|-?\d+(\.\d+)?)\s*(?[[\]]))\s*(?.*?)$/s; + const standardRules = []; + for (let part of parts) { + part = part.trim().replace(/\|\|/g, '|'); + const matches = part.match(intervalRegex); + if (matches) { + const matchGroups = matches.groups || {}; + if (matches[2]) { + for (const n of matches[3].split(',')) { + if (number === Number(n)) { + return strtr(matchGroups.message, parameters); + } + } + } + else { + const leftNumber = '-Inf' === matchGroups.left ? Number.NEGATIVE_INFINITY : Number(matchGroups.left); + const rightNumber = ['Inf', '+Inf'].includes(matchGroups.right) + ? Number.POSITIVE_INFINITY + : Number(matchGroups.right); + if (('[' === matchGroups.left_delimiter ? number >= leftNumber : number > leftNumber) && + (']' === matchGroups.right_delimiter ? number <= rightNumber : number < rightNumber)) { + return strtr(matchGroups.message, parameters); + } + } + } + else { + const ruleMatch = part.match(/^\w+:\s*(.*?)$/); + standardRules.push(ruleMatch ? ruleMatch[1] : part); + } + } + const position = getPluralizationRule(number, locale); + if (typeof standardRules[position] === 'undefined') { + if (1 === parts.length && typeof standardRules[0] !== 'undefined') { + return strtr(standardRules[0], parameters); + } + throw new Error(`Unable to choose a translation for "${id}" with locale "${locale}" for value "${number}". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %count% apples").`); + } + return strtr(standardRules[position], parameters); +} +function getPluralizationRule(number, locale) { + number = Math.abs(number); + let _locale = locale; + if (locale === 'pt_BR' || locale === 'en_US_POSIX') { + return 0; + } + _locale = _locale.length > 3 ? _locale.substring(0, _locale.indexOf('_')) : _locale; + switch (_locale) { + case 'af': + case 'bn': + case 'bg': + case 'ca': + case 'da': + case 'de': + case 'el': + case 'en': + case 'en_US_POSIX': + case 'eo': + case 'es': + case 'et': + case 'eu': + case 'fa': + case 'fi': + case 'fo': + case 'fur': + case 'fy': + case 'gl': + case 'gu': + case 'ha': + case 'he': + case 'hu': + case 'is': + case 'it': + case 'ku': + case 'lb': + case 'ml': + case 'mn': + case 'mr': + case 'nah': + case 'nb': + case 'ne': + case 'nl': + case 'nn': + case 'no': + case 'oc': + case 'om': + case 'or': + case 'pa': + case 'pap': + case 'ps': + case 'pt': + case 'so': + case 'sq': + case 'sv': + case 'sw': + case 'ta': + case 'te': + case 'tk': + case 'ur': + case 'zu': + return 1 === number ? 0 : 1; + case 'am': + case 'bh': + case 'fil': + case 'fr': + case 'gun': + case 'hi': + case 'hy': + case 'ln': + case 'mg': + case 'nso': + case 'pt_BR': + case 'ti': + case 'wa': + return number < 2 ? 0 : 1; + case 'be': + case 'bs': + case 'hr': + case 'ru': + case 'sh': + case 'sr': + case 'uk': + return 1 === number % 10 && 11 !== number % 100 + ? 0 + : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20) + ? 1 + : 2; + case 'cs': + case 'sk': + return 1 === number ? 0 : number >= 2 && number <= 4 ? 1 : 2; + case 'ga': + return 1 === number ? 0 : 2 === number ? 1 : 2; + case 'lt': + return 1 === number % 10 && 11 !== number % 100 + ? 0 + : number % 10 >= 2 && (number % 100 < 10 || number % 100 >= 20) + ? 1 + : 2; + case 'sl': + return 1 === number % 100 ? 0 : 2 === number % 100 ? 1 : 3 === number % 100 || 4 === number % 100 ? 2 : 3; + case 'mk': + return 1 === number % 10 ? 0 : 1; + case 'mt': + return 1 === number + ? 0 + : 0 === number || (number % 100 > 1 && number % 100 < 11) + ? 1 + : number % 100 > 10 && number % 100 < 20 + ? 2 + : 3; + case 'lv': + return 0 === number ? 0 : 1 === number % 10 && 11 !== number % 100 ? 1 : 2; + case 'pl': + return 1 === number + ? 0 + : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 12 || number % 100 > 14) + ? 1 + : 2; + case 'cy': + return 1 === number ? 0 : 2 === number ? 1 : 8 === number || 11 === number ? 2 : 3; + case 'ro': + return 1 === number ? 0 : 0 === number || (number % 100 > 0 && number % 100 < 20) ? 1 : 2; + case 'ar': + return 0 === number + ? 0 + : 1 === number + ? 1 + : 2 === number + ? 2 + : number % 100 >= 3 && number % 100 <= 10 + ? 3 + : number % 100 >= 11 && number % 100 <= 99 + ? 4 + : 5; + default: + return 0; + } +} + +function formatIntl(id, parameters, locale) { + if (id === '') { + return ''; + } + const intlMessage = new IntlMessageFormat(id, [locale.replace('_', '-')], undefined, { ignoreTag: true }); + parameters = { ...parameters }; + Object.entries(parameters).forEach(([key, value]) => { + if (key.includes('%') || key.includes('{')) { + delete parameters[key]; + parameters[key.replace(/[%{} ]/g, '').trim()] = value; + } + }); + return intlMessage.format(parameters); +} + +let _locale = null; +let _localeFallbacks = {}; +let _throwWhenNotFound = false; +function setLocale(locale) { + _locale = locale; +} +function getLocale() { + return (_locale || + document.documentElement.getAttribute('data-symfony-ux-translator-locale') || + (document.documentElement.lang ? document.documentElement.lang.replace('-', '_') : null) || + 'en'); +} +function throwWhenNotFound(enabled) { + _throwWhenNotFound = enabled; +} +function setLocaleFallbacks(localeFallbacks) { + _localeFallbacks = localeFallbacks; +} +function getLocaleFallbacks() { + return _localeFallbacks; +} +function trans(message, parameters = {}, domain = 'messages', locale = null) { + if (typeof domain === 'undefined') { + domain = 'messages'; + } + if (typeof locale === 'undefined' || null === locale) { + locale = getLocale(); + } + if (typeof message.translations === 'undefined') { + return message.id; + } + const localesFallbacks = getLocaleFallbacks(); + const translationsIntl = message.translations[`${domain}+intl-icu`]; + if (typeof translationsIntl !== 'undefined') { + while (typeof translationsIntl[locale] === 'undefined') { + locale = localesFallbacks[locale]; + if (!locale) { + break; + } + } + if (locale) { + return formatIntl(translationsIntl[locale], parameters, locale); + } + } + const translations = message.translations[domain]; + if (typeof translations !== 'undefined') { + while (typeof translations[locale] === 'undefined') { + locale = localesFallbacks[locale]; + if (!locale) { + break; + } + } + if (locale) { + return format(translations[locale], parameters, locale); + } + } + if (_throwWhenNotFound) { + throw new Error(`No translation message found with id "${message.id}".`); + } + return message.id; +} + +export { getLocale, getLocaleFallbacks, setLocale, setLocaleFallbacks, throwWhenNotFound, trans }; diff --git a/assets/ux-translator/dist/utils.d.ts b/assets/ux-translator/dist/utils.d.ts new file mode 100644 index 000000000..b6621dfd6 --- /dev/null +++ b/assets/ux-translator/dist/utils.d.ts @@ -0,0 +1 @@ +export declare function strtr(string: string, replacePairs: Record): string; diff --git a/assets/ux-translator/package.json b/assets/ux-translator/package.json new file mode 100644 index 000000000..8fa315582 --- /dev/null +++ b/assets/ux-translator/package.json @@ -0,0 +1,34 @@ +{ + "name": "@symfony/ux-translator", + "description": "Symfony Translator for JavaScript", + "license": "MIT", + "version": "1.0.0", + "main": "dist/translator_controller.js", + "types": "dist/translator_controller.d.ts", + "scripts": { + "build": "node ../../../bin/build_package.js .", + "watch": "node ../../../bin/build_package.js . --watch", + "test": "../../../bin/test_package.sh .", + "check": "biome check", + "ci": "biome ci" + }, + "symfony": { + "importmap": { + "intl-messageformat": "^10.5.11", + "@symfony/ux-translator": "path:%PACKAGE%/dist/translator_controller.js", + "@app/translations": "path:var/translations/index.js", + "@app/translations/configuration": "path:var/translations/configuration.js" + } + }, + "peerDependencies": { + "intl-messageformat": "^10.5.11" + }, + "peerDependenciesMeta": { + "intl-messageformat": { + "optional": false + } + }, + "devDependencies": { + "intl-messageformat": "^10.5.11" + } +} diff --git a/tests/console b/bin/console similarity index 78% rename from tests/console rename to bin/console index c933dc535..d8d530e2c 100755 --- a/tests/console +++ b/bin/console @@ -4,6 +4,10 @@ use App\Kernel; use Symfony\Bundle\FrameworkBundle\Console\Application; +if (!is_dir(dirname(__DIR__).'/vendor')) { + throw new LogicException('Dependencies are missing. Try running "composer install".'); +} + if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) { throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".'); } diff --git a/compose.override.yaml b/compose.override.yaml new file mode 100644 index 000000000..01169fbf0 --- /dev/null +++ b/compose.override.yaml @@ -0,0 +1,34 @@ +services: + ###> doctrine/doctrine-bundle ### + database: + ports: + - "127.0.0.1:5454:5432" + ###< doctrine/doctrine-bundle ### + + ###> symfony/mailer ### + mailer: + image: axllent/mailpit + ports: + - "1025" + - "8025" + environment: + MP_SMTP_AUTH_ACCEPT_ANY: 1 + MP_SMTP_AUTH_ALLOW_INSECURE: 1 + ###< symfony/mailer ### + + + redis: + ports: + - 127.0.0.1:6363:6379 + relatorio: + ports: + - 8888 + + rabbitmq: + ports: + - 127.0.0.1:5689:5672 + - 127.0.0.1:15689:15672 + # required to make data persistent + hostname: my-rabbit + volumes: + - ./docker/rabbitmq/data:/var/lib/rabbitmq diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 000000000..920f2e1b9 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,56 @@ +services: + ###> doctrine/doctrine-bundle ### + database: + image: postgis/postgis:${POSTGRES_VERSION:-16}-3.4-alpine + environment: + POSTGRES_DB: ${POSTGRES_DB:-app} + # You should definitely change the password in production + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!} + POSTGRES_USER: ${POSTGRES_USER:-app} + volumes: + # - database_data:/var/lib/postgresql/data:rw + # You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data! + - ./docker/db/data:/var/lib/postgresql/data:rw + ###< doctrine/doctrine-bundle ### + + redis: + image: redis + relatorio: + image: registry.gitlab.com/champs-libres/public/relatorio-tornado/app:latest + + sign-worker: + image: h3m6q87t.gra7.container-registry.ovh.net/sign-pdf-worker/worker:latest + environment: + AMQP_URL: amqp://guest:guest@rabbitmq:5672/%2f/to_python_sign + LOG_LEVEL: INFO + PKCS12_PATH: /etc/sign-pdf/dummy.p12 + TIMESTAMP_URL: http://freetsa.org/tsr + QUEUE_IN: to_python_sign + EXCHANGE_OUT: signed_docs + OUT_ROUTING_KEY: signed_doc + TSA_CERT_CHAIN: /etc/sign-pdf/tsa/tsa-chain.pem + TSA_CONFIG_PATH: /etc/sign-pdf/rootca.conf + TSA_KEY_PASSWORD: "5678" + volumes: + - "./resources/dev-certificate/dummy.p12:/etc/sign-pdf/dummy.p12:ro" + - "./resources/dev-certificate/rootca.conf:/etc/sign-pdf/rootca.conf:ro" + - "./resources/dev-certificate/tsa:/etc/sign-pdf/tsa:ro" + - "./resources/dev-certificate/tsa_serial:/var/lib/tsa/tsa_serial:rw" + links: + - rabbitmq + depends_on: + rabbitmq: + condition: service_healthy + + rabbitmq: + image: rabbitmq:3-management-alpine + healthcheck: + test: rabbitmq-diagnostics -q ping + interval: 30s + timeout: 30s + retries: 3 + +volumes: + ###> doctrine/doctrine-bundle ### + database_data: +###< doctrine/doctrine-bundle ### diff --git a/composer.json b/composer.json index 25b5cda97..8d36d4d07 100644 --- a/composer.json +++ b/composer.json @@ -13,13 +13,14 @@ "ext-json": "*", "ext-openssl": "*", "ext-redis": "*", + "ext-zlib": "*", "champs-libres/wopi-bundle": "dev-master@dev", "champs-libres/wopi-lib": "dev-master@dev", + "doctrine/data-fixtures": "^1.8", "doctrine/doctrine-bundle": "^2.1", "doctrine/doctrine-migrations-bundle": "^3.0", "doctrine/orm": "^2.13.0", "erusev/parsedown": "^1.7", - "graylog2/gelf-php": "^1.5", "knplabs/knp-menu-bundle": "^3.0", "knplabs/knp-time-bundle": "^1.12", "knpuniversity/oauth2-client-bundle": "^2.10", @@ -32,6 +33,7 @@ "phpoffice/phpspreadsheet": "^1.16", "ramsey/uuid-doctrine": "^1.7", "sensio/framework-extra-bundle": "^5.5", + "smalot/pdfparser": "^2.10", "spomky-labs/base64url": "^2.0", "symfony/asset": "^5.4", "symfony/browser-kit": "^5.4", @@ -43,6 +45,7 @@ "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", @@ -55,7 +58,9 @@ "symfony/messenger": "^5.4", "symfony/mime": "^5.4", "symfony/monolog-bundle": "^3.5", + "symfony/notifier": "^5.4", "symfony/options-resolver": "^5.4", + "symfony/ovh-cloud-notifier": "^5.4", "symfony/process": "^5.4", "symfony/property-access": "^5.4", "symfony/property-info": "^5.4", @@ -70,6 +75,7 @@ "symfony/templating": "^5.4", "symfony/translation": "^5.4", "symfony/twig-bundle": "^5.4", + "symfony/ux-translator": "^2.22", "symfony/validator": "^5.4", "symfony/webpack-encore-bundle": "^1.11", "symfony/workflow": "^5.4", @@ -84,6 +90,7 @@ "require-dev": { "doctrine/doctrine-fixtures-bundle": "^3.3", "fakerphp/faker": "^1.13", + "friendsofphp/php-cs-fixer": "3.65.0", "jangregor/phpstan-prophecy": "^1.0", "nelmio/alice": "^3.8", "nikic/php-parser": "^4.15", @@ -92,15 +99,18 @@ "phpstan/phpstan": "^1.9", "phpstan/phpstan-deprecation-rules": "^1.1", "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": ">= 7.5", + "phpunit/phpunit": "^10.5.24", "rector/rector": "^1.1.0", + "symfony/amqp-messenger": "^5.4.45", "symfony/debug-bundle": "^5.4", "symfony/dotenv": "^5.4", + "symfony/flex": "^2.4", "symfony/maker-bundle": "^1.20", - "symfony/phpunit-bridge": "^5.4", + "symfony/phpunit-bridge": "^7.1", "symfony/runtime": "^5.4", "symfony/stopwatch": "^5.4", - "symfony/var-dumper": "^5.4" + "symfony/var-dumper": "^5.4", + "symfony/web-profiler-bundle": "^5.4" }, "conflict": { "symfony/symfony": "*" @@ -115,11 +125,12 @@ "Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle", "Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle", "Chill\\EventBundle\\": "src/Bundle/ChillEventBundle", + "Chill\\FranceTravailApiBundle\\": "src/Bundle/ChillFranceTravailApiBundle/src", + "Chill\\JobBundle\\": "src/Bundle/ChillJobBundle/src", "Chill\\MainBundle\\": "src/Bundle/ChillMainBundle", "Chill\\PersonBundle\\": "src/Bundle/ChillPersonBundle", "Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle", "Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle", - "Chill\\TicketBundle\\": "src/Bundle/ChillTicketBundle/src", "Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle", "Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src", "Chill\\Utils\\Rector\\": "utils/rector/src" @@ -127,9 +138,8 @@ }, "autoload-dev": { "psr-4": { - "App\\": "tests", + "App\\": "src/app", "Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests", - "Chill\\TicketBundle\\Tests\\": "src/Bundle/ChillTicketBundle/tests", "Chill\\WopiBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests", "Chill\\Utils\\Rector\\Tests\\": "utils/rector/tests" } @@ -142,16 +152,24 @@ "phpro/grumphp": true, "phpstan/extension-installer": true, "roave/you-are-using-it-wrong": true, - "symfony/runtime": true + "symfony/runtime": true, + "symfony/flex": true }, - "bin-dir": "bin", "optimize-autoloader": true, "sort-packages": true }, "scripts": { "auto-scripts": { - "cache:clear": "symfony-cmd" + "cache:clear": "symfony-cmd", + "assets:install %PUBLIC_DIR%": "symfony-cmd" }, - "php-cs-fixer": "php-cs-fixer fix --config=./.php-cs-fixer.dist.php --show-progress=none" + "php-cs-fixer": "php-cs-fixer fix --config=./.php-cs-fixer.dist.php --show-progress=none", + "phpstan": "phpstan --no-progress", + "rector": "rector --no-progress-bar" + }, + "extra": { + "symfony": { + "docker": true + } } } diff --git a/tests/app/config/bundles.php b/config/bundles.php similarity index 79% rename from tests/app/config/bundles.php rename to config/bundles.php index 38be80676..ec11bc0b6 100644 --- a/tests/app/config/bundles.php +++ b/config/bundles.php @@ -1,15 +1,26 @@ ['all' => true], + loophp\PsrHttpMessageBridgeBundle\PsrHttpMessageBridgeBundle::class => ['all' => true], + ChampsLibres\WopiBundle\WopiBundle::class => ['all' => true], + Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], + Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], + Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], + Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true], + Knp\Bundle\TimeBundle\KnpTimeBundle::class => ['all' => true], + KnpU\OAuth2ClientBundle\KnpUOAuth2ClientBundle::class => ['all' => true], + Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], + Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], + Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true, 'test' => true], + Misd\PhoneNumberBundle\MisdPhoneNumberBundle::class => ['all' => true], + Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], + Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], + Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], + Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], + Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], + Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], + Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], Chill\ActivityBundle\ChillActivityBundle::class => ['all' => true], Chill\AsideActivityBundle\ChillAsideActivityBundle::class => ['all' => true], Chill\CalendarBundle\ChillCalendarBundle::class => ['all' => true], @@ -23,25 +34,7 @@ return [ Chill\TaskBundle\ChillTaskBundle::class => ['all' => true], Chill\ThirdPartyBundle\ChillThirdPartyBundle::class => ['all' => true], Chill\BudgetBundle\ChillBudgetBundle::class => ['all' => true], - Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], - Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], - Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], - Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], - Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true], - Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], - Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], - Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], - Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], - Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], - //Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], - Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], - Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true], - Knp\Bundle\TimeBundle\KnpTimeBundle::class => ['all' => true], - Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], - loophp\PsrHttpMessageBridgeBundle\PsrHttpMessageBridgeBundle::class => ['all' => true], - \Misd\PhoneNumberBundle\MisdPhoneNumberBundle::class => ['all' => true], - ChampsLibres\WopiBundle\WopiBundle::class => ['all' => true], Chill\WopiBundle\ChillWopiBundle::class => ['all' => true], - \Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], - Chill\TicketBundle\ChillTicketBundle::class => ['all' => true], + Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], + Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true], ]; diff --git a/tests/app/config/packages/cache.yaml b/config/packages/cache.yaml similarity index 61% rename from tests/app/config/packages/cache.yaml rename to config/packages/cache.yaml index c7a5f169d..6899b7200 100644 --- a/tests/app/config/packages/cache.yaml +++ b/config/packages/cache.yaml @@ -17,17 +17,3 @@ framework: # Namespaced pools use the above "app" backend by default #pools: #my.dedicated.cache: null - - default_redis_provider: '%env(resolve:REDIS_URL)%' - - pools: - cache.user_data: - adapter: cache.adapter.redis - public: true - default_lifetime: 300 # 5 minutes - - # will be used in chill_main.tag_aware_cache service - cache.tags: - adapter: cache.adapter.redis - public: false - default_lifetime: 300 diff --git a/config/packages/cache_chill.yaml b/config/packages/cache_chill.yaml new file mode 100644 index 000000000..71877b6dd --- /dev/null +++ b/config/packages/cache_chill.yaml @@ -0,0 +1,14 @@ +framework: + cache: + default_redis_provider: '%env(resolve:REDIS_URL)%' + pools: + cache.user_data: + adapter: cache.adapter.redis + public: true + default_lifetime: 300 # 5 minutes + + # will be used in chill_main.tag_aware_cache service + cache.tags: + adapter: cache.adapter.redis + public: false + default_lifetime: 300 diff --git a/config/packages/chill.yaml b/config/packages/chill.yaml new file mode 100644 index 000000000..76578a2c7 --- /dev/null +++ b/config/packages/chill.yaml @@ -0,0 +1,121 @@ +chill_main: + available_languages: [ '%env(resolve:LOCALE)%', 'en' ] + available_countries: ['BE', 'FR'] + notifications: + from_email: '%env(resolve:NOTIFICATION_FROM_EMAIL)%' + from_name: '%env(resolve:NOTIFICATION_FROM_NAME)%' + host: '%env(resolve:NOTIFICATION_HOST)%' + redis: + host: '%env(resolve:REDIS_HOST)%' + port: '%env(resolve:REDIS_PORT)%' + phone_helper: + twilio_sid: '%env(resolve:TWILIO_SID)%' + twilio_secret: '%env(resolve:TWILIO_SECRET)%' + default_carrier_code: '%env(resolve:DEFAULT_CARRIER_CODE)%' + short_messages: + dsn: '%env(string:SHORT_MESSAGE_DSN)%' + acl: + form_show_scopes: true + form_show_centers: true + access_global_history: false + access_user_change_password: true + access_permissions_group_list: true + add_address: + default_country: '%env(string:ADD_ADDRESS_DEFAULT_COUNTRY)%' + map_center: + x: '%env(float:ADD_ADDRESS_MAP_CENTER_X)%' + y: '%env(float:ADD_ADDRESS_MAP_CENTER_Y)%' + z: '%env(float:ADD_ADDRESS_MAP_CENTER_Z)%' + +when@test: + chill_main: + available_languages: + - 'fr' + - 'en' + +chill_custom_fields: + show_empty_values_in_views: false + +chill_person: + create_person_allowed: false + create_parcours_allowed: false + allow_multiple_simultaneous_accompanying_periods: true + accompanying_periods_fields: + user: visible + # createdBy, step, origin, intensity, scopes, requestor, anonymous, emergency, confidential : visible(default) | hidden + person_fields: + acceptEmail: hidden + alt_names: + - key: jeune_fille + labels: + lang: fr + label: Nom de naissance + marital_status: visible + civility: visible + deathdate: visible + validation: + center_required: true + +chill_activity: + form: + time_duration: + - + label: '5 minutes' + seconds: 300 + - + label: '10 minutes' + seconds: 600 + - + label: '15 minutes' + seconds: 900 + - + label: '20 minutes' + seconds: 1200 + - + label: '25 minutes' + seconds: 1500 + - + label: '30 minutes' + seconds: 1800 + - + label: '45 minutes' + seconds: 2700 + - + label: '1 hour' + seconds: 3600 + - + label: '1 hour 15' + seconds: 4500 + - + label: '1 hour 30' + seconds: 5400 + - + label: '1 hour 45' + seconds: 6300 + - + label: '2 hours' + seconds: 7200 + - + label: '2 hours 15' + seconds: 8100 + - + label: '2 hours 30' + seconds: 9000 + - + label: '2 hours 45' + seconds: 9900 + - + label: '3 hours' + seconds: 10800 + - + label: '3 hours 30' + seconds: 12600 + - + label: '4 hours' + seconds: 14400 + - + label: '4 hours 30' + seconds: 16200 + - + label: '5 hours' + seconds: 18000 diff --git a/config/packages/chill_budget.yaml b/config/packages/chill_budget.yaml new file mode 100644 index 000000000..853dc197b --- /dev/null +++ b/config/packages/chill_budget.yaml @@ -0,0 +1,85 @@ +chill_budget: + resources: + - { key: fixed_contract, labels: [{ lang: fr, label: "Salarié en CDI" }]} + - { key: temporary_contract, labels: [{ lang: fr, label: "Salarié en CDD"}]} + - { key: interim, labels: [{ lang: fr, label: "Salarié en intérim"}]} + - { key: other_revenues, labels: [{ lang: fr, label: "Revenus non salariés, commerçant, artisan,..."}]} + - { key: agricultural_activity, labels: [{ lang: fr, label: "Exploitant agricole"}]} + - { key: chomage_ass, labels: [{ lang: fr, label: "Allocations chômage ASS"}]} + - { key: chomage_are, labels: [{ lang: fr, label: "Allocations chômage ARE"}]} + - { key: remuneration_stage, labels: [{ lang: fr, label: "Rémunération de stage, d’apprentissage,..."}]} + - { key: daily_allowences, labels: [{ lang: fr, label: "Indemnités journalières"}]} + - { key: others, labels: [{ lang: fr, label: "Autres"}]} + - { key: pension, labels: [{ lang: fr, label: "Pension de retraite"}]} + - { key: invalidity, labels: [{ lang: fr, label: "Pension d'invalidité"}]} + - { key: reversion, labels: [{ lang: fr, label: "Pension de réversion"}]} + - { key: widowhood, labels: [{ lang: fr, label: "Pension de veuvage"}]} + - { key: military, labels: [{ lang: fr, label: "Pension militaire"}]} + - { key: food, labels: [{ lang: fr, label: "Pension alimentaire"}]} + - { key: compensation, labels: [{ lang: fr, label: "Pension de prestation compensatoire"}]} + - { key: allocation_handicap_adult, labels: [{ lang: fr, label: "Allocation aux adultes handicapés"}]} + - { key: rsa, labels: [{ lang: fr, label: "RSA"}]} + - { key: annuity_accident, labels: [{ lang: fr, label: "Rente accident"}]} + - { key: premium_al_pl, labels: [{ lang: fr, label: "Prime d’activité AL/APL au bénéficiaire"}]} + - { key: premium_thirdparty, labels: [{ lang: fr, label: "Prime d’activité versé au tiers"}]} + - { key: other_income, labels: [{ lang: fr, label: "Autres ressources (ARS, ...)"}]} + - { key: allocation_family, labels: [{ lang: fr, label: "Allocations familiales"}]} + - { key: allocation_basic, labels: [{ lang: fr, label: "Allocation de base"}]} + - { key: free_choice_saving, labels: [{ lang: fr, label: "Complément de libre choix du mode de garde"}]} + - { key: shared_benefits_child, labels: [{ lang: fr, label: "Prestation partagée éducation de l’enfant"}]} + - { key: complimentary_family, labels: [{ lang: fr, label: "Complément familial"}]} + - { key: allocation_family_support, labels: [{ lang: fr, label: "Allocation de soutien familial"}]} + - { key: allocation_education_child_handicap, labels: [{ lang: fr, label: "Allocation d’éducation de l’enfant handicapé"}]} + + charges: + - { key: rent, labels: [{ lang: fr, label: "Loyer" }]} + - { key: home_ownership, labels: [{ lang: fr, label: "Accession à la propriété" }]} + - { key: costs_accomodation, labels: [{ lang: fr, label: "Frais d’hébergement" }]} + - { key: home_insurance, labels: [{ lang: fr, label: "Assurance habitation" }]} + - { key: taxes, labels: [{ lang: fr, label: "Impôts (taxe habitation, taxe foncière, ordures ménagères, redevances incitatives)" }]} + - { key: impots, labels: [{lang: fr, label: "Impôts"}]} + - { key: electricity, labels: [{ lang: fr, label: "Electricité" }]} + - { key: gas, labels: [{ lang: fr, label: "Gaz en bouteille" }]} + - { key: heating, labels: [{ lang: fr, label: "Autre moyen de chauffage" }]} + - { key: water, labels: [{ lang: fr, label: "Eau" }]} + - { key: school_fees, labels: [ { lang: fr, label: "Frais de scolarité"}]} + - { key: alimony, labels: [ { lang: fr, label: "Pension alimentaire à reverser"}]} + - { key: child_care, labels: [ { lang: fr, label: "Frais de garde (en totalité)"}]} + - { key: telecom, labels: [ { lang: fr, label: "Frais de communication fixe"}]} + - { key: mobilephone, labels: [ { lang: fr, label: "Frais de communication mobile"}]} + - { key: internet, labels: [ { lang: fr, label: "Frais de communication internet"}]} + - { key: insurance, labels: [{ lang: fr, label: "Assurances"}]} + - { key: debt_commission, labels: [{ lang: fr, label: "Saisine de la commission surendettement"}]} + - { key: recovery_plan, labels: [{ lang: fr, label: "Plan de redressement"}]} + - { key: rpr, labels: [{ lang: fr, label: "PRP"}]} + - { key: moratoire, labels: [{lang: fr, label: "Moratoire"}]} + - { key: mutuelle, labels: [{lang: fr, label: "Mutuelle"}]} + - { key: transport, labels: [{lang: fr, label: "Frais de transport"}]} + - { key: decouvbank, labels: [{lang: fr, label: "Découvert bancaire utilisé"}]} + - { key: procsaisie, labels: [{lang: fr, label: "Procédure de saisie"}]} + - { key: indus, labels: [{lang: fr, label: "Indus"}]} + - { key: apurement, labels: [{lang: fr, label: "Plan d'apurement"}]} + - { key: debt_rent, labels: [{lang: fr, label: "Dette de loyer (hors APL)"}]} + - { key: debt_property, labels: [{lang: fr, label: "Dette d'accession à la propriété"}]} + - { key: debt_lodging, labels: [{lang: fr, label: "Dette de frais d’hébergement"}]} + - { key: debt_electricity, labels: [{lang: fr, label: "Dette d'électricité"}]} + - { key: debt_water, labels: [{lang: fr, label: "Dette d'eau"}]} + - { key: debt_heating, labels: [{lang: fr, label: "Dette d'autre moyen de chauffage"}]} + - { key: debt_gas, labels: [{lang: fr, label: "Dette de gaz en bouteille"}]} + - { key: debt_house_insurance, labels: [{lang: fr, label: "Dette d'assurance habitation"}]} + - { key: debt_housing_taxes, labels: [{lang: fr, label: "Dette d'impôts liées au logement"}]} + - { key: debt_taxes, labels: [{lang: fr, label: "Dette d'impôts autres"}]} + - { key: debt_school, labels: [{lang: fr, label: "Dette de frais de scolarité (cantine, transport, frais de scolarité, frais de garde)"}]} + - { key: debt_alimentation, labels: [{lang: fr, label: "Dette de pension alimentaire à reverser"}]} + - { key: debt_childcare, labels: [{lang: fr, label: "Dette de frais de garde (en totalité)"}]} + - { key: debt_phone, labels: [{lang: fr, label: "Dette de communication fixe"}]} + - { key: debt_mobile, labels: [{lang: fr, label: "Dette de communication portable"}]} + - { key: debt_internet, labels: [{lang: fr, label: "Dette de communication internet"}]} + - { key: debt_car_insurance, labels: [{lang: fr, label: "Dette d'assurance auto"}]} + - { key: debt_mutual, labels: [{lang: fr, label: "Dette de mutuelle"}]} + - { key: debt_transport, labels: [{lang: fr, label: "Dette de frais de transport"}]} + - { key: debt_bank, labels: [{lang: fr, label: "Dette de découvert bancaire utilisé"}]} + - { key: debt_garbage, labels: [{lang: fr, label: "Dette de taxe d’ordures ménagère"}]} + - { key: debt_other, labels: [{lang: fr, label: "Dette autre"}]} + - { key: autre, labels: [{lang: fr, label: "Autre"}]} + diff --git a/config/packages/chill_calendar.yaml b/config/packages/chill_calendar.yaml new file mode 100644 index 000000000..f7eedf748 --- /dev/null +++ b/config/packages/chill_calendar.yaml @@ -0,0 +1,3 @@ +chill_calendar: + remote_calendars_sync: + enabled: false diff --git a/config/packages/chill_doc_generator.yaml b/config/packages/chill_doc_generator.yaml new file mode 100644 index 000000000..1e5bf8686 --- /dev/null +++ b/config/packages/chill_doc_generator.yaml @@ -0,0 +1,5 @@ +chill_doc_generator: + driver: + type: relatorio + relatorio: + url: 'http://%env(RELATORIO_HOST)%:%env(RELATORIO_PORT)%/' \ No newline at end of file diff --git a/tests/app/config/packages/chill_doc_store.yaml b/config/packages/chill_doc_store.yaml similarity index 76% rename from tests/app/config/packages/chill_doc_store.yaml rename to config/packages/chill_doc_store.yaml index 994e19240..eb7f6be01 100644 --- a/tests/app/config/packages/chill_doc_store.yaml +++ b/config/packages/chill_doc_store.yaml @@ -1,4 +1,7 @@ chill_doc_store: + use_driver: openstack + local_storage: + storage_path: '%kernel.project_dir%/var/storage' openstack: temp_url: temp_url_key: '%env(resolve:ASYNC_UPLOAD_TEMP_URL_KEY)%' # Required diff --git a/config/packages/chill_workflow_signature_documents.yaml b/config/packages/chill_workflow_signature_documents.yaml new file mode 100644 index 000000000..5784f55e7 --- /dev/null +++ b/config/packages/chill_workflow_signature_documents.yaml @@ -0,0 +1,11 @@ +chill_main: + workflow_signature: + base_signer: + document_kinds: + - { key: id_card, labels: [ { lang: fr, label: "Carte d'identité" } ] } + - { key: passport, labels: [ { lang: fr, label: "Passeport" } ] } + - { key: drivers_license, labels: [ { lang: fr, label: "Permis de conduire" } ] } + - { key: visa_long_stay, labels: [ { lang: fr, label: "Visa de long séjour" } ] } + - { key: resident_permit, labels: [ { lang: fr, label: "Carte de séjour" } ] } + - { key: residency_card, labels: [ { lang: fr, label: "Carte de résident" } ] } + - { key: provisionary_residency_permit, labels: [ { lang: fr, label: "Autorisation provisoire de séjour" } ] } diff --git a/config/packages/debug.yaml b/config/packages/debug.yaml new file mode 100644 index 000000000..ad874afdd --- /dev/null +++ b/config/packages/debug.yaml @@ -0,0 +1,5 @@ +when@dev: + debug: + # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. + # See the "server:dump" command to start a new server. + dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml new file mode 100644 index 000000000..d421158a0 --- /dev/null +++ b/config/packages/doctrine.yaml @@ -0,0 +1,38 @@ +doctrine: + dbal: + url: '%env(resolve:DATABASE_URL)%' + + # IMPORTANT: You MUST configure your server version, + # either here or in the DATABASE_URL env var (see .env file) + #server_version: '16' + use_savepoints: true + orm: + auto_generate_proxy_classes: true + naming_strategy: doctrine.orm.naming_strategy.default + auto_mapping: true + +when@test: + doctrine: + dbal: + # "TEST_TOKEN" is typically set by ParaTest + dbname_suffix: '_test%env(default::TEST_TOKEN)%' + +when@prod: + doctrine: + orm: + auto_generate_proxy_classes: false + proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' + query_cache_driver: + type: pool + pool: doctrine.system_cache_pool + result_cache_driver: + type: pool + pool: doctrine.result_cache_pool + + framework: + cache: + pools: + doctrine.result_cache_pool: + adapter: cache.app + doctrine.system_cache_pool: + adapter: cache.system diff --git a/config/packages/doctrine_migrations.yaml b/config/packages/doctrine_migrations.yaml new file mode 100644 index 000000000..6eac03d9f --- /dev/null +++ b/config/packages/doctrine_migrations.yaml @@ -0,0 +1,2 @@ +doctrine_migrations: + enable_profiler: false diff --git a/tests/app/config/packages/doctrine_migrations.yaml b/config/packages/doctrine_migrations_chill.yaml similarity index 77% rename from tests/app/config/packages/doctrine_migrations.yaml rename to config/packages/doctrine_migrations_chill.yaml index a2afd0f01..8b8bf539b 100644 --- a/tests/app/config/packages/doctrine_migrations.yaml +++ b/config/packages/doctrine_migrations_chill.yaml @@ -7,25 +7,15 @@ doctrine_migrations: 'Chill\Migrations\CustomFields': '@ChillCustomFieldsBundle/migrations' 'Chill\Migrations\Event': '@ChillEventBundle/migrations' 'Chill\Migrations\Person': '@ChillPersonBundle/migrations' - 'Chill\Migrations\Report': '@ChillReportBundle/migrations' 'Chill\Migrations\Task': '@ChillTaskBundle/migrations' 'Chill\Migrations\ThirdParty': '@ChillThirdPartyBundle/migrations' - 'Chill\Migrations\AsideActivity': '@ChillAsideActivityBundle/migrations' 'Chill\Migrations\DocGenerator': '@ChillDocGeneratorBundle/migrations' + 'Chill\Migrations\AsideActivity': '@ChillAsideActivityBundle/migrations' 'Chill\Migrations\Calendar': '@ChillCalendarBundle/migrations' 'Chill\Migrations\Budget': '@ChillBudgetBundle/migrations' - 'Chill\Migrations\Ticket': '@ChillTicketBundle/migrations' - + 'Chill\Migrations\Report': '@ChillReportBundle/migrations' all_or_nothing: true services: 'Doctrine\Migrations\Version\Comparator': 'Chill\MainBundle\Doctrine\Migrations\VersionComparator' - - storage: - table_storage: - table_name: 'migration_versions' - version_column_name: 'version' - version_column_length: 1024 - executed_at_column_name: 'executed_at' - execution_time_column_name: 'execution_time' diff --git a/tests/app/config/packages/framework.yaml b/config/packages/framework.yaml similarity index 65% rename from tests/app/config/packages/framework.yaml rename to config/packages/framework.yaml index a5ec738f2..33a27cd5c 100644 --- a/tests/app/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -1,16 +1,6 @@ # see https://symfony.com/doc/current/reference/configuration/framework.html framework: secret: '%env(APP_SECRET)%' - - http_client: - default_options: - verify_peer: false - verify_host: false - - trusted_hosts: - - '^(localhost|127.0.0.1|web)$' - - '%env(resolve:TRUSTED_HOSTS)%' - #csrf_protection: true http_method_override: false @@ -27,12 +17,6 @@ framework: php_errors: log: true - #error_controller: App\Controller\ErrorController::show - - ## sf4 check: ou à déplacer dans un chill.yaml - assets: - json_manifest_path: '%kernel.project_dir%/public/build/manifest.json' - when@test: framework: test: true diff --git a/tests/app/config/packages/assets.yaml b/config/packages/framework_chill.yaml similarity index 55% rename from tests/app/config/packages/assets.yaml rename to config/packages/framework_chill.yaml index 051d36dce..d67a9d570 100644 --- a/tests/app/config/packages/assets.yaml +++ b/config/packages/framework_chill.yaml @@ -1,3 +1,8 @@ framework: assets: json_manifest_path: '%kernel.project_dir%/public/build/manifest.json' + +when@test: + framework: + assets: + json_manifest_path: null diff --git a/config/packages/knpu_oauth2_client.yaml b/config/packages/knpu_oauth2_client.yaml new file mode 100644 index 000000000..05e853399 --- /dev/null +++ b/config/packages/knpu_oauth2_client.yaml @@ -0,0 +1,3 @@ +knpu_oauth2_client: + clients: + # configure your clients as described here: https://github.com/knpuniversity/oauth2-client-bundle#configuration diff --git a/config/packages/lexik_jwt_authentication.yaml b/config/packages/lexik_jwt_authentication.yaml new file mode 100644 index 000000000..edfb69dc8 --- /dev/null +++ b/config/packages/lexik_jwt_authentication.yaml @@ -0,0 +1,4 @@ +lexik_jwt_authentication: + secret_key: '%env(resolve:JWT_SECRET_KEY)%' + public_key: '%env(resolve:JWT_PUBLIC_KEY)%' + pass_phrase: '%env(JWT_PASSPHRASE)%' diff --git a/config/packages/lexik_jwt_authentication_chill.yaml b/config/packages/lexik_jwt_authentication_chill.yaml new file mode 100644 index 000000000..0f1f9c130 --- /dev/null +++ b/config/packages/lexik_jwt_authentication_chill.yaml @@ -0,0 +1,9 @@ +lexik_jwt_authentication: + # required for wopi - recommended duration + token_ttl: 36000 + + # required for wopi - token in parameter access_token + token_extractors: + query_parameter: + enabled: true + name: access_token \ No newline at end of file diff --git a/tests/app/config/packages/loophp_psr17.yaml b/config/packages/loophp_psr17.yaml similarity index 100% rename from tests/app/config/packages/loophp_psr17.yaml rename to config/packages/loophp_psr17.yaml diff --git a/config/packages/mailer.yaml b/config/packages/mailer.yaml new file mode 100644 index 000000000..56a650d89 --- /dev/null +++ b/config/packages/mailer.yaml @@ -0,0 +1,3 @@ +framework: + mailer: + dsn: '%env(MAILER_DSN)%' diff --git a/config/packages/mailer_chill.yaml b/config/packages/mailer_chill.yaml new file mode 100644 index 000000000..041ea7978 --- /dev/null +++ b/config/packages/mailer_chill.yaml @@ -0,0 +1,4 @@ +framework: + mailer: + envelope: + sender: '%env(NOTIFICATION_FROM_EMAIL)%' diff --git a/config/packages/messenger.yaml b/config/packages/messenger.yaml new file mode 100644 index 000000000..b460fd60c --- /dev/null +++ b/config/packages/messenger.yaml @@ -0,0 +1,75 @@ +framework: + messenger: + # reset services after consuming messages + reset_on_message: true + + # Uncomment this (and the failed transport below) to send failed messages to this transport for later handling. + failure_transport: failed + + transports: + # those transports are added by chill-bundles recipes + sync: sync:// + async: + dsn: '%env(MESSENGER_TRANSPORT_DSN)%/async' + options: + exchange: + name: async + type: fanout + queues: + async: ~ + auto_setup: true + + priority: '%env(MESSENGER_TRANSPORT_DSN)%/priority' + # end of transports added by chill-bundles recipes + # https://symfony.com/doc/current/messenger.html#transport-configuration + failed: 'doctrine://default?queue_name=failed' + to_sign_worker: + dsn: '%env(MESSENGER_TRANSPORT_DSN)%/to_python_sign' + serializer: Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\RequestPdfSignMessageSerializer + options: + exchange: + name: to_python_sign + type: direct + queues: + to_python_sign: ~ + signed_docs: + dsn: '%env(MESSENGER_TRANSPORT_DSN)%/signed_docs' + serializer: Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\PdfSignedMessageSerializer + options: + exchange: + name: signed_docs + type: direct + queues: + signed_docs: + binding_keys: [signed_doc] + + auto_setup: false + + routing: + # routes added by chill-bundles recipes + 'Chill\CalendarBundle\Messenger\Message\CalendarRangeMessage': async + 'Chill\CalendarBundle\Messenger\Message\CalendarRangeRemovedMessage': async + 'Chill\CalendarBundle\Messenger\Message\CalendarRemovedMessage': async + 'Chill\CalendarBundle\Messenger\Message\CalendarMessage': async + 'Chill\CalendarBundle\Messenger\Message\InviteUpdateMessage': async + 'Chill\CalendarBundle\Messenger\Message\MSGraphChangeNotificationMessage': async + 'Chill\MainBundle\Service\ShortMessage\ShortMessage': async + 'Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationMessage': priority + 'Chill\PersonBundle\AccompanyingPeriod\Lifecycle\AccompanyingPeriodStepChangeRequestMessage': async + 'Chill\DocStoreBundle\Service\Signature\Driver\BaseSigner\RequestPdfSignMessage': to_sign_worker + 'Chill\DocStoreBundle\Service\StoredObjectCleaner\RemoveOldVersionMessage': async + 'Chill\MainBundle\Workflow\Messenger\PostSignatureStateChangeMessage': priority + 'Chill\MainBundle\Workflow\Messenger\PostPublicViewMessage': async + 'Chill\MainBundle\Service\Workflow\CancelStaleWorkflowMessage': async + # end of routes added by chill-bundles recipes + # Route your messages to the transports + # 'App\Message\YourMessage': async + # 'Symfony\Component\Mailer\Messenger\SendEmailMessage': async + +when@test: + framework: + messenger: + transports: + async: 'in-memory://' + priority: 'in-memory://' + signed_docs: 'in-memory://' diff --git a/config/packages/misd_phone_number.yaml b/config/packages/misd_phone_number.yaml new file mode 100644 index 000000000..20e0144ae --- /dev/null +++ b/config/packages/misd_phone_number.yaml @@ -0,0 +1,13 @@ +# To persist libphonenumber\PhoneNumber objects, add the Misd\PhoneNumberBundle\Doctrine\DBAL\Types\PhoneNumberType mapping to your application's config. +# This requires: doctrine/doctrine-bundle +#doctrine: +# dbal: +# types: +# phone_number: Misd\PhoneNumberBundle\Doctrine\DBAL\Types\PhoneNumberType + +#misd_phone_number: +# twig: false +# form: false +# serializer: false +# validator: +# default_region: GB diff --git a/config/packages/monolog.yaml b/config/packages/monolog.yaml new file mode 100644 index 000000000..9db7d8a7f --- /dev/null +++ b/config/packages/monolog.yaml @@ -0,0 +1,62 @@ +monolog: + channels: + - deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists + +when@dev: + monolog: + handlers: + main: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: debug + channels: ["!event"] + # uncomment to get logging in your browser + # you may have to allow bigger header sizes in your Web server configuration + #firephp: + # type: firephp + # level: info + #chromephp: + # type: chromephp + # level: info + console: + type: console + process_psr_3_messages: false + channels: ["!event", "!doctrine", "!console"] + +when@test: + monolog: + handlers: + main: + type: fingers_crossed + action_level: error + handler: nested + excluded_http_codes: [404, 405] + channels: ["!event"] + nested: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: debug + +when@prod: + monolog: + handlers: + main: + type: fingers_crossed + action_level: error + handler: nested + excluded_http_codes: [404, 405] + buffer_size: 50 # How many messages should be saved? Prevent memory leaks + nested: + type: stream + path: php://stderr + level: debug + formatter: monolog.formatter.json + console: + type: console + process_psr_3_messages: false + channels: ["!event", "!doctrine"] + deprecation: + type: stream + channels: [deprecation] + path: php://stderr + formatter: monolog.formatter.json diff --git a/config/packages/nelmio_alice.yaml b/config/packages/nelmio_alice.yaml new file mode 100644 index 000000000..e82c32982 --- /dev/null +++ b/config/packages/nelmio_alice.yaml @@ -0,0 +1,12 @@ +when@dev: &dev + nelmio_alice: + functions_blacklist: + - 'current' + - 'shuffle' + - 'date' + - 'time' + - 'file' + - 'md5' + - 'sha1' + +when@test: *dev diff --git a/config/packages/notifier.yaml b/config/packages/notifier.yaml new file mode 100644 index 000000000..1b474455a --- /dev/null +++ b/config/packages/notifier.yaml @@ -0,0 +1,13 @@ +framework: + notifier: + texter_transports: + #ovhcloud: '%env(OVHCLOUD_DSN)%' + #ovhcloud: '%env(SHORT_MESSAGE_DSN)%' + channel_policy: + # use chat/slack, chat/telegram, sms/twilio or sms/nexmo + urgent: ['email'] + high: ['email'] + medium: ['email'] + low: ['email'] + admin_recipients: + - { email: admin@example.com } diff --git a/tests/app/config/packages/nyholm_psr7.yaml b/config/packages/nyholm_psr7.yaml similarity index 56% rename from tests/app/config/packages/nyholm_psr7.yaml rename to config/packages/nyholm_psr7.yaml index f1357233b..ade831249 100644 --- a/tests/app/config/packages/nyholm_psr7.yaml +++ b/config/packages/nyholm_psr7.yaml @@ -7,15 +7,5 @@ services: Psr\Http\Message\UploadedFileFactoryInterface: '@nyholm.psr7.psr17_factory' Psr\Http\Message\UriFactoryInterface: '@nyholm.psr7.psr17_factory' - # Register nyholm/psr7 services for autowiring with HTTPlug factories - Http\Message\MessageFactory: '@nyholm.psr7.httplug_factory' - Http\Message\RequestFactory: '@nyholm.psr7.httplug_factory' - Http\Message\ResponseFactory: '@nyholm.psr7.httplug_factory' - Http\Message\StreamFactory: '@nyholm.psr7.httplug_factory' - Http\Message\UriFactory: '@nyholm.psr7.httplug_factory' - nyholm.psr7.psr17_factory: class: Nyholm\Psr7\Factory\Psr17Factory - - nyholm.psr7.httplug_factory: - class: Nyholm\Psr7\Factory\HttplugFactory diff --git a/tests/app/config/packages/ramsey_uuid_doctrine.yaml b/config/packages/ramsey_uuid_doctrine.yaml similarity index 100% rename from tests/app/config/packages/ramsey_uuid_doctrine.yaml rename to config/packages/ramsey_uuid_doctrine.yaml diff --git a/tests/app/config/packages/routing.yaml b/config/packages/routing.yaml similarity index 75% rename from tests/app/config/packages/routing.yaml rename to config/packages/routing.yaml index b45c1cec7..4b766ce57 100644 --- a/tests/app/config/packages/routing.yaml +++ b/config/packages/routing.yaml @@ -5,3 +5,8 @@ framework: # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands #default_uri: http://localhost + +when@prod: + framework: + router: + strict_requirements: null diff --git a/config/packages/routing_chill.yaml b/config/packages/routing_chill.yaml new file mode 100644 index 000000000..ca2bc8d8d --- /dev/null +++ b/config/packages/routing_chill.yaml @@ -0,0 +1,8 @@ +parameters: + composed_uri: 'https://%env(resolve:NOTIFICATION_HOST)%' + +framework: + router: + # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. + # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands + default_uri: '%composed_uri%' \ No newline at end of file diff --git a/tests/app/config/packages/security.yaml b/config/packages/security.yaml similarity index 65% rename from tests/app/config/packages/security.yaml rename to config/packages/security.yaml index a1a257736..af2385ba9 100644 --- a/tests/app/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -1,68 +1,68 @@ security: - - access_decision_manager: - strategy: unanimous - allow_if_all_abstain: false - enable_authenticator_manager: true # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords password_hashers: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider providers: - - chain_provider: - chain : - providers: [in_memory, users] - in_memory: + # providers added by chill-bundles recipes\n + # those providers are required to make chill working + chill_chain_provider: + chain: + providers: [chill_in_memory, chill_users] + chill_in_memory: memory: users: admin: { password: '%env(resolve:ADMIN_PASSWORD)%', roles: ['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH', 'ROLE_USER'] } - users: + chill_users: id: chill.main.user_provider - - encoders: - Chill\MainBundle\Entity\User: - algorithm: bcrypt - Symfony\Component\Security\Core\User\User: plaintext - + # end of providers added by chill-bundles recipes\n + # all other providers might be removed, unless you have specific needs\n firewalls: - dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false - + # those lines are added by chill-bundles recipe, and are requires to make chill-bundles working + # this firewall is in use for wopi endpoint, which requires wopi: pattern: ^/wopi - provider: chain_provider + provider: chill_chain_provider stateless: true guard: authenticators: - lexik_jwt_authentication.jwt_token_authenticator - - main: - # remove during upgrade from symfony 4 to symfony 5 TODO check this - #anonymous: ~ - provider: chain_provider + dav: + pattern: ^/dav + provider: chill_chain_provider + stateless: true + guard: + authenticators: + - Chill\DocStoreBundle\Security\Guard\JWTOnDavUrlAuthenticator + # this firewall is the main firewall for chill. It should be the last one in the stack, + # unless you have specific needs + chill_main: + provider: chill_chain_provider + entry_point: form_login form_login: csrf_parameter: _csrf_token csrf_token_id: authenticate #csrf_provider: security.csrf.token_manager - # remove during upgrade from symfony 4 to symfony 5 TODO check this - # logout_on_user_change: true logout: path: /logout - # uncomment to enable impersonate mode in Chill # https://symfony.com/doc/current/security/impersonating_user.html switch_user: true - + # end of lines added by chill-bundles recipe # activate different ways to authenticate - # https://symfony.com/doc/current/security.html#firewalls-authentication + # https://symfony.com/doc/current/security.html#the-firewall + + # https://symfony.com/doc/current/security/impersonating_user.html + # switch_user: true # Easy way to control access for large sections of your site # Note: Only the *first* access control that matches will be used access_control: + # those lines are added by chill-bundles recipes, and are requires to make chill-bundles working - { path: ^/(login|logout), roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/public, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/wopi, roles: IS_AUTHENTICATED_FULLY } @@ -75,6 +75,7 @@ security: - { path: ^/([a-z]+/)?admin, roles: ROLE_ADMIN } # other pages, only for regular user (no admin) - { path: ^/, roles: ROLE_USER } + # this is the end of line added by chill-project/chill-bundles recipes when@test: security: diff --git a/config/packages/security_chill.yaml b/config/packages/security_chill.yaml new file mode 100644 index 000000000..1de2a5ff6 --- /dev/null +++ b/config/packages/security_chill.yaml @@ -0,0 +1,9 @@ +security: + access_decision_manager: + strategy: unanimous + allow_if_all_abstain: false + +when@test: + security: + role_hierarchy: + CHILL_MASTER_ROLE: [ CHILL_INHERITED_ROLE_1 ] diff --git a/tests/app/config/packages/sensio_framework_extra.yaml b/config/packages/sensio_framework_extra.yaml similarity index 100% rename from tests/app/config/packages/sensio_framework_extra.yaml rename to config/packages/sensio_framework_extra.yaml diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml new file mode 100644 index 000000000..b3f8f9cfe --- /dev/null +++ b/config/packages/translation.yaml @@ -0,0 +1,7 @@ +framework: + default_locale: en + translator: + default_path: '%kernel.project_dir%/translations' + fallbacks: + - en + providers: diff --git a/config/packages/translation_chill.yaml b/config/packages/translation_chill.yaml new file mode 100644 index 000000000..a0371ff10 --- /dev/null +++ b/config/packages/translation_chill.yaml @@ -0,0 +1,4 @@ +framework: + default_locale: '%env(resolve:LOCALE)%' + translator: + fallbacks: [ '%env(resolve:LOCALE)%' ] \ No newline at end of file diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml new file mode 100644 index 000000000..f9f4cc539 --- /dev/null +++ b/config/packages/twig.yaml @@ -0,0 +1,6 @@ +twig: + default_path: '%kernel.project_dir%/templates' + +when@test: + twig: + strict_variables: true diff --git a/tests/app/config/packages/twig.yaml b/config/packages/twig_chill.yaml similarity index 72% rename from tests/app/config/packages/twig.yaml rename to config/packages/twig_chill.yaml index 02d862922..15a3066fd 100644 --- a/tests/app/config/packages/twig.yaml +++ b/config/packages/twig_chill.yaml @@ -1,9 +1,4 @@ twig: - default_path: '%kernel.project_dir%/templates' - debug: '%kernel.debug%' - strict_variables: '%kernel.debug%' - exception_controller: null - ## In Symfony 5, bootstrap_5 theme is supported. But not yet in sf4 !! # see sf5 https://symfony.com/doc/current/form/form_themes.html # see sf4 https://symfony.com/doc/4.4/form/form_themes.html @@ -14,4 +9,8 @@ twig: # and adapt it lightly. # form_themes: ['@ChillMain/Form/bootstrap5/bootstrap_5_horizontal_layout.html.twig'] - #form_themes: ['bootstrap_5_horizontal_layout.html.twig'] + +when@dev: + twig: + globals: + responsive_debug: false diff --git a/config/packages/ux_translator.yaml b/config/packages/ux_translator.yaml new file mode 100644 index 000000000..1c1c70608 --- /dev/null +++ b/config/packages/ux_translator.yaml @@ -0,0 +1,3 @@ +ux_translator: + # The directory where the JavaScript translations are dumped + dump_directory: '%kernel.project_dir%/var/translations' diff --git a/tests/app/config/packages/validator.yaml b/config/packages/validator.yaml similarity index 74% rename from tests/app/config/packages/validator.yaml rename to config/packages/validator.yaml index 350786a13..0201281d3 100644 --- a/tests/app/config/packages/validator.yaml +++ b/config/packages/validator.yaml @@ -6,3 +6,8 @@ framework: # For instance, basic validation constraints will be inferred from Doctrine's metadata. #auto_mapping: # App\Entity\: [] + +when@test: + framework: + validation: + not_compromised_password: false diff --git a/config/packages/web_profiler.yaml b/config/packages/web_profiler.yaml new file mode 100644 index 000000000..17893da18 --- /dev/null +++ b/config/packages/web_profiler.yaml @@ -0,0 +1,15 @@ +when@dev: + web_profiler: + toolbar: true + intercept_redirects: false + + framework: + profiler: { only_exceptions: false } + +when@test: + web_profiler: + toolbar: false + intercept_redirects: false + + framework: + profiler: { collect: false } diff --git a/tests/app/config/packages/webpack_encore.yaml b/config/packages/webpack_encore.yaml similarity index 63% rename from tests/app/config/packages/webpack_encore.yaml rename to config/packages/webpack_encore.yaml index 709531205..476246aec 100644 --- a/tests/app/config/packages/webpack_encore.yaml +++ b/config/packages/webpack_encore.yaml @@ -1,16 +1,18 @@ ---- webpack_encore: # The path where Encore is building the assets - i.e. Encore.setOutputPath() output_path: '%kernel.project_dir%/public/build' - # If multiple builds are defined (as shown below), you can disable the default build: # output_path: false # Set attributes that will be rendered on all script and link tags script_attributes: defer: true + # Uncomment (also under link_attributes) if using Turbo Drive + # https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change + # 'data-turbo-track': reload # link_attributes: - # + # Uncomment if using Turbo Drive + # 'data-turbo-track': reload # If using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials') # crossorigin: 'anonymous' @@ -23,11 +25,17 @@ webpack_encore: # If you have multiple builds: # builds: - # pass "frontend" as the 3rg arg to the Twig functions - # {{ encore_entry_script_tags('entry1', null, 'frontend') }} - # frontend: '%kernel.project_dir%/public/frontend/build' - # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes) - # Put in config/packages/prod/webpack_encore.yaml - # cache: true + # pass the build name as the 3rd argument to the Twig functions + # {{ encore_entry_script_tags('entry1', null, 'frontend') }} + +#when@prod: +# webpack_encore: +# # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes) +# # Available in version 1.2 +# cache: true + +when@test: + webpack_encore: + strict_mode: false diff --git a/config/packages/wopi.yaml b/config/packages/wopi.yaml new file mode 100644 index 000000000..fc93a858e --- /dev/null +++ b/config/packages/wopi.yaml @@ -0,0 +1,2 @@ +wopi: + server: "%env(resolve:EDITOR_SERVER)%" \ No newline at end of file diff --git a/tests/app/config/packages/workflow.yaml b/config/packages/workflow.yaml similarity index 100% rename from tests/app/config/packages/workflow.yaml rename to config/packages/workflow.yaml diff --git a/config/packages/workflow_chill.yaml b/config/packages/workflow_chill.yaml new file mode 100644 index 000000000..2e1506da5 --- /dev/null +++ b/config/packages/workflow_chill.yaml @@ -0,0 +1,347 @@ +framework: + workflows: + vendee_internal: + type: state_machine + metadata: + related_entity: + - Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument + - Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork + - Chill\DocStoreBundle\Entity\AccompanyingCourseDocument + label: + fr: 'Suivi' + support_strategy: Chill\MainBundle\Workflow\RelatedEntityWorkflowSupportsStrategy + marking_store: + service: Chill\MainBundle\Workflow\EntityWorkflowMarkingStore + initial_marking: 'initial' + places: + initial: + metadata: + label: + fr: Étape initiale + attenteModification: + metadata: + label: + fr: En attente de modification du document + validationFilterInputLabels: + forward: {fr: Modification effectuée} + backward: {fr: Pas de modification effectuée} + neutral: {fr: Autre} + attenteMiseEnForme: + metadata: + label: + fr: En attente de mise en forme + validationFilterInputLabels: + forward: {fr: Mise en forme terminée} + backward: {fr: Pas de mise en forme effectuée} + neutral: {fr: Autre} + attenteVisa: + metadata: + label: + fr: En attente de visa + validationFilterInputLabels: + forward: {fr: Visa accordé} + backward: {fr: Visa refusé} + neutral: {fr: Autre} + attenteSignature: + metadata: + isSignature: ['user', 'person'] + onSignatureCompleted: + transitionName: signatureApplied + label: + fr: En attente de signature + validationFilterInputLabels: + forward: {fr: Signature accordée} + backward: {fr: Signature refusée} + neutral: {fr: Autre} + postSignature: + metadata: + label: + fr: Signatures traitées + validationFilterInputLabels: + forward: {fr: Poursuite du traitement} + backward: {fr: Annulation ou refus de signature} + neutral: {fr: Autre} + attenteTraitement: + metadata: + label: + fr: En attente de traitement + validationFilterInputLabels: + forward: {fr: Traitement terminé favorablement} + backward: {fr: Traitement terminé défavorablement} + neutral: {fr: Autre} + attenteEnvoi: + metadata: + label: + fr: En attente d'envoi + validationFilterInputLabels: + forward: {fr: Document envoyé} + backward: {fr: Document non envoyé} + neutral: {fr: Autre} + attenteValidationMiseEnForme: + metadata: + label: + fr: En attente de validation de la mise en forme + validationFilterInputLabels: + forward: {fr: Validation de la mise en forme} + backward: {fr: Refus de validation de la mise en forme} + neutral: {fr: Autre} + attenteReceptionExternal: + metadata: + isSentExternal: true + onExternalView: clotureApresLectureEnvoiExterne + label: + fr: En attente d'ouverture par un destinataire externe + validationFilterInputLabels: + forward: {fr: Document reçu par un destinataire externe} + backward: {fr: Document non reçu par un destinataire externe} + neutral: {fr: Autre} + annule: + metadata: + isFinal: true + isFinalPositive: false + label: + fr: Annulé + final: + metadata: + isFinal: true + isFinalPositive: true + label: + fr: Finalisé + transitions: + # transition qui avancent + demandeModificationDocument: + from: + - initial + to: attenteModification + metadata: + label: + fr: Demande de modification du document + isForward: true + demandeMiseEnForme: + from: + - initial + - attenteModification + to: attenteMiseEnForme + metadata: + label: + fr: Demande de mise en forme + isForward: true + demandeValidationMiseEnForme: + from: + - attenteMiseEnForme + to: attenteValidationMiseEnForme + metadata: + label: + fr: Demande de validation de la mise en forme + isForward: true + demandeVisa: + from: + - initial + - attenteModification + - attenteMiseEnForme + - attenteValidationMiseEnForme + - postSignature + - attenteTraitement + to: attenteVisa + metadata: + label: + fr: Demande de visa + isForward: true + demandeSignature: + from: + - initial + - attenteModification + - attenteMiseEnForme + - attenteValidationMiseEnForme + - attenteVisa + - attenteTraitement + to: attenteSignature + metadata: + label: {fr: Demande de signature} + isForward: true + signatureApplied: + from: + - attenteSignature + to: postSignature + metadata: + label: {fr: Signatures appliquées} + isForward: true + transitionGuard: 'system' # can be 'system+only-dest' or 'only-dest' (only-dest is default) + demandeTraitement: + from: + - initial + - attenteModification + - attenteMiseEnForme + - attenteValidationMiseEnForme + - attenteVisa + - postSignature + to: attenteTraitement + metadata: + label: {fr: Demande de traitement} + isForward: true + demandeEnvoi: + from: + - initial + - attenteModification + - attenteMiseEnForme + - attenteValidationMiseEnForme + - attenteVisa + - postSignature + - attenteTraitement + to: attenteEnvoi + metadata: + label: {fr: Demande d'envoi} + isForward: true + demandeEnvoiExterne: + from: + - initial + - attenteModification + - attenteMiseEnForme + - attenteValidationMiseEnForme + - attenteVisa + - postSignature + - attenteTraitement + to: attenteReceptionExternal + metadata: + label: {fr: Envoi sécurisé par courrier électronique} + isForward: true + clotureApresLectureEnvoiExterne: + from: + - attenteReceptionExternal + to: + - final + metadata: + transitionGuard: system + isForward: true + label: {fr: Consultation de l'envoi sécurisé} + annulation: + from: + - initial + - attenteModification + - attenteMiseEnForme + - attenteValidationMiseEnForme + - attenteSignature + - attenteVisa + - postSignature + - attenteTraitement + - attenteEnvoi + to: annule + metadata: + label: {fr: Annulation} + isForward: false + transitionGuard: 'system+only-dest' # can be 'system+only-dest' or 'only-dest' (only-dest is default) + # transitions qui répètent l'étape + demandeMiseEnFormeSupplementaire: + from: + - attenteMiseEnForme + - attenteValidationMiseEnForme + to: attenteMiseEnForme + metadata: + label: {fr: Demande de mise en forme supplémentaire} + demandeVisaSupplementaire: + from: + - attenteVisa + to: attenteVisa + metadata: + label: {fr: Demande de visa supplémentaire} + isForward: true + demandeSignatureSupplementaire: + from: + - postSignature + to: attenteSignature + metadata: + label: {fr: Demande de signature supplémentaire} + isForward: true + demandeTraitementSupplementaire: + from: + - attenteTraitement + to: attenteTraitement + metadata: + label: {fr: Demande de traitement supplémentaire} + # transitions qui renvoient vers une étape précédente + refusEtModificationDocument: + from: + - attenteVisa + - postSignature + - attenteTraitement + - attenteEnvoi + to: attenteModification + metadata: + label: + fr: Refus et demande de modification du document + isForward: false + refusEtDemandeMiseEnForme: + from: + - attenteVisa + - attenteTraitement + - attenteEnvoi + to: attenteMiseEnForme + metadata: + label: {fr: Refus et demande de mise en forme} + isForward: false + refusEtDemandeVisa: + from: + - postSignature + - attenteEnvoi + to: attenteVisa + metadata: + label: {fr: Refus et demande de visa} + isForward: false + refusEtDemandeSignature: + from: + - attenteEnvoi + to: attenteSignature + metadata: + label: {fr: Refus et demande de signature} + isForward: false + refusEtDemandeTraitement: + from: + - attenteEnvoi + to: attenteTraitement + metadata: + label: {fr: Refus et demande de traitement} + isForward: false + # transition vers final + initialToFinal: + from: + - initial + to: final + metadata: + label: {fr: Clotûre immédiate et cloture positive} + isForward: true + attenteMiseEnFormeToFinal: + from: + - attenteMiseEnForme + - attenteValidationMiseEnForme + to: final + metadata: + label: {fr: Mise en forme terminée et cloture positive} + isForward: true + attenteVisaToFinal: + from: + - attenteVisa + to: final + metadata: + label: {fr: Accorde le visa et cloture positive} + isForward: true + postSignatureToFinal: + from: + - postSignature + to: final + metadata: + label: {fr: Cloture positive} + isForward: true + attenteTraitementToFinal: + from: + - attenteTraitement + to: final + metadata: + label: {fr: Traitement terminé et cloture positive} + isForward: true + attenteEnvoiToFinal: + from: + - attenteEnvoi + to: final + metadata: + label: {fr: Envoyé et cloture positive} + isForward: true diff --git a/config/preload.php b/config/preload.php new file mode 100644 index 000000000..5ebcdb215 --- /dev/null +++ b/config/preload.php @@ -0,0 +1,5 @@ +`_ + +We also encourage you to use tools like `phpstan `_ and `rector `_. + +For running php-cs-fixer: + +.. code-block:: bash + + symfony composer php-cs-fixer + +Execute tests +============= + +.. code-block:: bash + + symfony composer exec phpunit -- /path/to_your_test.php + +Note that IDE like PhpStorm should be able to run tests, even KernelTestcase or WebTestCase, `from within their interfaces `_. + +Execute rector +============== + +.. code-block:: bash + + symfony composer exec rector -- process + diff --git a/docs/source/development/cronjob.rst b/docs/source/development/cronjob.rst index df72fa922..bda32b5c8 100644 --- a/docs/source/development/cronjob.rst +++ b/docs/source/development/cronjob.rst @@ -39,9 +39,12 @@ 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 @@ -56,7 +59,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 = new DateTimeImmutable('now'); + $now = $clock->now(); return $cronJobExecution->getLastStart() < $now->sub(new DateInterval('P1D')) && in_array($now->format('H'), self::ACCEPTED_HOURS, true) @@ -69,10 +72,15 @@ Implements a :code:`Chill\MainBundle\Cron\CronJobInterface`. Here is an example: return 'arbitrary-and-unique-key'; } - public function run(): void + public function run(array $lastExecutionData): 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 ? diff --git a/docs/source/development/es-lint.rst b/docs/source/development/es-lint.rst new file mode 100644 index 000000000..f8504e082 --- /dev/null +++ b/docs/source/development/es-lint.rst @@ -0,0 +1,71 @@ +ESLint +====== + +To improve the quality of our JS and VueJS code, ESLint and eslint-plugin-vue are implemented within the chill-bundles project. + +Commands +-------- + +To run ESLint, you can simply use the ``eslint`` command within the chill-bundles directory. +This runs eslint **not** taking the baseline into account, thus showing all existing errors in the project. + +A script was also added to package.json allowing you to execute ``yarn run eslint``. +This will run eslint, but **taking the baseline into account**, thus only alerting to newly created errors. + +The eslint command is configured to also run ``prettier`` which will simply format the code to look more uniform (takes care indentation for example). + +Interesting options that can be used in combination with eslint are: + +- ``--quiet`` to only get errors and silence the warnings +- ``--fix`` to have ESLint fix what it can, automatically. This will not fix everything. + +Baseline +-------- + +To allow us the time to fix linting errors/warnings a baseline has been created using the following command +- ``npx eslint-baseline "**/*.{js,vue}"`` + +The baseline has been commited and the gitlab CI setup to only fail upon new errors/warnings being created. +When fixing errors/warnings manually, please update the baseline. + +1. Delete the current baseline file +2. Run the above command locally, this will automatically create a new baseline that should be commited + +Rules +----- + +We use Vue 3, so the rules can be configured as follows within the ``eslint.config.mjs`` file: + +- ``.configs["flat/base"]`` ... Settings and rules to enable correct ESLint parsing. + +Configurations for using Vue.js 3.x: + +- ``.configs["flat/essential"]`` : Base rules plus rules to prevent errors or unintended behavior. +- ``.configs["flat/strongly-recommended"]`` ... Above, plus rules to considerably improve code readability and/or dev experience. +- ``.configs["flat/recommended"]`` ... Above, plus rules to enforce subjective community defaults to ensure consistency. + +Detailed information about which rules each set includes can be found here: +`https://eslint.vuejs.org/rules/ `_ + +Manual Rule Configuration +------------------------- + +We can also manually configure certain rules or override rules that are part of the ruleset specified above. + +For example, if we want to turn off a certain rule, we can do so as follows: + +.. code-block:: javascript + + rules: { + 'vue/multi-word-component': 'off' + } + +We could also change the severity of a certain rule from 'error' to 'warning', for example. + +Within specific ``.js`` or ``.vue`` files, we can also override a certain rule only for that specific file by adding a comment: + +.. code-block:: javascript + + /* eslint multi-word-component: "off", no-child-content: "error" + -------- + Here's a description about why this configuration is necessary. */ diff --git a/docs/source/development/index.rst b/docs/source/development/index.rst index d48f92890..d3aefc1b0 100644 --- a/docs/source/development/index.rst +++ b/docs/source/development/index.rst @@ -31,6 +31,7 @@ As Chill relies on the `symfony `_ framework, reading the fr Exports Embeddable comments Run tests + ESLint Useful snippets manual/index.rst Assets diff --git a/docs/source/development/user-interface/js-functions/show_hide.js b/docs/source/development/user-interface/js-functions/show_hide.js index 5e6b66eb8..63845a90e 100644 --- a/docs/source/development/user-interface/js-functions/show_hide.js +++ b/docs/source/development/user-interface/js-functions/show_hide.js @@ -1,39 +1,37 @@ -import { ShowHide } from 'ShowHide/show_hide.js'; - -var - div_accompagnement = document.getElementById("form_accompagnement"), - div_accompagnement_comment = document.getElementById("form_accompagnement_comment"), - div_caf_id = document.getElementById("cafId"), - div_caf_inscription_date = document.getElementById("cafInscriptionDate"), - ; +import { ShowHide } from "ShowHide/show_hide.js"; +var div_accompagnement = document.getElementById("form_accompagnement"), + div_accompagnement_comment = document.getElementById( + "form_accompagnement_comment", + ), + div_caf_id = document.getElementById("cafId"), + div_caf_inscription_date = document.getElementById("cafInscriptionDate"); // let show/hide the div_accompagnement_comment if the input with value `'autre'` is checked new ShowHide({ - "froms": [div_accompagnement], - "test": function(froms, event) { - for (let el of froms.values()) { - for (let input of el.querySelectorAll('input').values()) { - if (input.value === 'autre') { - return input.checked; - } - } + froms: [div_accompagnement], + test: function (froms, event) { + for (let el of froms.values()) { + for (let input of el.querySelectorAll("input").values()) { + if (input.value === "autre") { + return input.checked; } - - return false; - }, - "container": [div_accompagnement_comment] + } + } + + return false; + }, + container: [div_accompagnement_comment], }); -// let show the date input only if the the id is filled +// let show the date input only if the the id is filled new ShowHide({ - froms: [ div_caf_id ], - test: function(froms, event) { - for (let el of froms.values()) { - return el.querySelector("input").value !== ""; - } - }, - container: [ div_caf_inscription_date ], - // using this option, we use the event `input` instead of `change` - event_name: 'input' + froms: [div_caf_id], + test: function (froms, event) { + for (let el of froms.values()) { + return el.querySelector("input").value !== ""; + } + }, + container: [div_caf_inscription_date], + // using this option, we use the event `input` instead of `change` + event_name: "input", }); - diff --git a/docs/source/index.rst b/docs/source/index.rst index 4ac09908f..ddfa23e3b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -16,7 +16,7 @@ Welcome to Chill documentation! Chill is a free software for social workers. -Chill rely on the php framework `Symfony `_. +Chill rely on the php framework `Symfony `_. Contents of this documentation: @@ -42,7 +42,7 @@ Contribute User manual =========== -An user manual exists in French and currently focuses on describing the main concept of the software. +An user manual exists in French and currently focuses on describing the main concept of the software. `Read (and contribute) to the manual `_ @@ -55,12 +55,11 @@ Available bundles * Chill Person, to deal with persons, * chill custom fields, to add custom fields to some entities, * chill activity: to add activities to people, - * chill report: to add report to people, + * chill report: to add report to people, * chill event: to gather people into events, * chill docs store: to store documents to people, but also entities, * chill task: to register task with people, * chill third party: to register third parties, - * chill family members: to register family members You will also found the following projects : diff --git a/docs/source/installation/document-storage.rst b/docs/source/installation/document-storage.rst new file mode 100644 index 000000000..0b87d9eb2 --- /dev/null +++ b/docs/source/installation/document-storage.rst @@ -0,0 +1,84 @@ +Document storage +################ + +You can store document on two different ways: + +- on disk +- in the cloud, using object storage: currently only `openstack swift `_ is supported. + +Comparison +========== + +Storing documents within the cloud is particularily suitable for "portable" deployments, like in kubernetes, or within container +without having to manage volumes to store documents. But you'll have to subscribe on a commercial offer. + +Storing documents on disk is more easy to configure, but more difficult to manage: if you use container, you will have to +manager volumes to attach documents on disk. You'll have to do some backup of the directory. If chill is load-balanced (and +multiple instances of chill are run), you will have to find a way to share the directories in read-write mode for every instance. + +On Disk +======= + +Configure Chill like this: + +.. code-block:: yaml + + # file config/packages/chill_doc_store.yaml + chill_doc_store: + use_driver: local_storage + local_storage: + storage_path: '%kernel.project_dir%/var/storage' + +In this configuration, documents will be stored in :code:`var/storage` within your app directory. But this path can be +elsewhere on the disk. Be aware that the directory must be writable by the user executing the chill app (php-fpm or www-data). + +Documents will be stored in subpathes within that directory. The files will be encrypted, the key is stored in the database. + +In the cloud, using openstack object store +########################################## + +You must subscribe to a commercial offer for object store. + +Chill use some features to allow documents to be stored in the cloud without being uploaded first to the chill server: + +- `Form POST Middelware `_; +- `Temporary URL Middelware `_. + +A secret key must be generated and configured, and CORS must be configured depending on the domain you will use to serve Chill. + +At first, create a container and get the base path to the container. For instance, on OVH, if you create a container named "mychill", +you will be able to retrieve the base path of the container within the OVH interface, like this: + +- base_path: :code:`https://storage.gra.cloud.ovh.net/v1/AUTH_123456789/mychill/` => will be variable :code:`ASYNC_UPLOAD_TEMP_URL_BASE_PATH` +- container: :code:`mychill` => will be variable :code:`ASYNC_UPLOAD_TEMP_URL_CONTAINER` + +You can also generate a key, which should have at least 20 characters. This key will go in the variable :code:`ASYNC_UPLOAD_TEMP_URL_KEY`. + +.. note:: + + See the `documentation of symfony `_ on how to store variables, and how to encrypt them if needed. + +Configure the storage like this: + +.. code-block:: yaml + + # file config/packages/chill_doc_store.yaml + chill_doc_store: + use_driver: openstack + openstack: + temp_url: + temp_url_key: '%env(resolve:ASYNC_UPLOAD_TEMP_URL_KEY)%' # Required + container: '%env(resolve:ASYNC_UPLOAD_TEMP_URL_CONTAINER)%' # Required + temp_url_base_path: '%env(resolve:ASYNC_UPLOAD_TEMP_URL_BASE_PATH)%' # Required + +Chill is able to configure the container in order to store document. Grab an Openstack Token (for instance, using :code:`openstack token issue` or +the web interface of your openstack provider), and run this command: + +.. code-block:: bash + + symfony console async-upload:configure --os_token=OPENSTACK_TOKEN -d https://mychill.mydomain.example + + # or, without symfony-cli + bin/console async-upload:configure --os_token=OPENSTACK_TOKEN -d https://mychill.mydomain.example + + diff --git a/docs/source/installation/enable-collabora-for-dev.rst b/docs/source/installation/enable-collabora-for-dev.rst new file mode 100644 index 000000000..17a9ae1cc --- /dev/null +++ b/docs/source/installation/enable-collabora-for-dev.rst @@ -0,0 +1,125 @@ + +Enable CODE for development +=========================== + +For editing a document, there must be a way to communicate between the collabora server and the symfony server, in +both direction. The domain name should also be the same for collabora server and for the browser which access to the +online editor. + +Using ngrok (or other http tunnel) +---------------------------------- + +One can configure a tunnel server to expose your local install to the web, and access to your local server using the +tunnel url. + +Start ngrok +^^^^^^^^^^^ + +This can be achieve using `ngrok `_. + +.. 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: `_). + +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 `_ +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 ` + +Configure also the `https certificate `_ + +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 ` +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. diff --git a/docs/source/installation/index.rst b/docs/source/installation/index.rst index 0328322e4..fb4cb3cac 100644 --- a/docs/source/installation/index.rst +++ b/docs/source/installation/index.rst @@ -17,6 +17,27 @@ Installation & Usage You will learn here how to install a new symfony project with chill, and configure it. +Which can of installation do I need ? +===================================== + +I want to run chill in production +--------------------------------- + +See the :ref:`instructions about installing Chill for production `. + +I want to add features to the main chill bundles +------------------------------------------------ + +If you want to add features to chill bundles itself, **and** you want those features to be merged into the chill bundles, +you can use the "development" installation mode. + +See the :ref:`instruction for installation for development `. + +I want to add features to Chill, but keep those features for my instance +------------------------------------------------------------------------- + +Follow the same instruction than for production, until the end. + Requirements ============ @@ -36,334 +57,11 @@ Chill needs a redis server and a postgresql database, and a few other things lik generate documents from templates. **All these things are available through docker using the plugin compose**. We do not provide information on how to run this without docker compose. - -Install a new project -===================== - -Initialize project and dependencies -*********************************** - -.. code-block:: bash - - symfony new --version=5.4 my_chill_project - cd my_chill_project - -We strongly encourage you to initialize a git repository at this step, to track further changes. - -.. code-block:: bash - - # add the flex endpoints required for custom recipes - cat <<< "$(jq '.extra.symfony += {"endpoint": ["flex://defaults", "https://gitlab.com/api/v4/projects/57371968/repository/files/index.json/raw?ref=main"]}' composer.json)" > composer.json - # install chill and some dependencies - # TODO fix the suffix "alpha1" and replace by ^3.0.0 when version 3.0.0 will be released - symfony composer require chill-project/chill-bundles v3.0.0-alpha1 champs-libres/wopi-lib dev-master@dev champs-libres/wopi-bundle dev-master@dev - -We encourage you to accept the inclusion of the "Docker configuration from recipes": this is the documented way to run the database. -You must also accept to configure recipes from the contrib repository, unless you want to configure the bundles manually). - -.. code-block:: bash - - # fix some configuration - ./post-install-chill.sh - # install node dependencies - yarn install - # and compile assets - yarn run encore production - -.. note:: - - If you encounter this error during assets compilation (:code:`yarn run encore production`) (repeated multiple times): - - .. code-block:: txt - - [tsl] ERROR in /tmp/chill/v1/public/bundles/chillcalendar/types.ts(2,65) - TS2307: Cannot find module '../../../ChillMainBundle/Resources/public/types' or its corresponding type declarations. - - run: - - .. code-block:: bash - - rm -rf public/bundles/* - - Then restart the compilation of assets (:code:```yarn run encore production```) - -Configure your project -********************** - -You should read the configuration files in :code:`chill/config/packages` carefully, especially if you have -custom developments. But most of the time, this should be fine. - -You have to configure some local variables, which are described in the :code:`.env` file. The secrets should not be stored -in this :code:`.env` file, but instead using the `secrets management tool `_ -or in the :code:`.env.local` file, which should not be commited to the git repository. - -You do not need to set variables for the smtp server, redis server and relatorio server, as they are generated automatically -by the symfony server, from the docker compose services. - -The only required variable is the :code:`ADMIN_PASSWORD`. You can generate a hashed and salted admin password using the command -:code:`symfony console security:hash-password 'Symfony\Component\Security\Core\User\User'`. Then, -you can either: - -- add this password to the :code:`.env.local` file, you must escape the character :code:`$`: if the generated password - is :code:`$2y$13$iyvJLuT4YEa6iWXyQV4/N.hNHpNG8kXlYDkkt5MkYy4FXcSwYAwmm`, your :code:`.env.local` file will be: - - .. code-block:: env - - ADMIN_PASSWORD=\$2y\$13\$iyvJLuT4YEa6iWXyQV4/N.hNHpNG8kXlYDkkt5MkYy4FXcSwYAwmm - -- add the generated password to the secrets manager (**note**: you must add the generated hashed password to the secrets env, - not the password in clear text). - -Prepare migrations and other tools -********************************** - -To continue the installation process, you will have to run migrations: - -.. code-block:: bash - - # start databases and other services - docker compose up -d - # the first start, it may last some seconds, you can check with docker compose ps - # run migrations - symfony console doctrine:migrations:migrate - # setup messenger - symfony console messenger:setup-transports - # prepare some views - symfony console chill:db:sync-views - -.. warning:: - - If you encounter an error while running :code:`symfony console messenger:setup-transports`, you can set up the messenger - transport to redis, by adding this in the :code:`.env.local` or :code:`.env` file: - - .. code-block:: env - - MESSENGER_TRANSPORT_DSN=redis://${REDIS_HOST}:${REDIS_PORT}/messages - -Start your web server locally -***************************** - -At this step, Chill will be ready to be served locally, but without any configuration. You can run the project -locally using the `local symfony server `_: - -.. code-block:: bash - - # see the whole possibilities at https://symfony.com/doc/current/setup/symfony_server.html - symfony server:start -d - - -If you need to test the instance with accounts and some basic configuration, please install the fixtures (see below). - - -Add capabilities for dev -======================== - -If you need to add custom bundles, you can develop them in the `src/` directory, like for any other symfony project. You -can rely on the whole chill framework, meaning there is no need to add them to the original `chill-bundles`. - -You will require some bundles to have the following development tools: - -- add fixtures -- add profiler and var-dumper to debug - -Install fixtures -**************** - -.. code-block:: bash - - # generate fixtures for chill - symfony composer require --dev doctrine/doctrine-fixtures-bundle nelmio/alice - # now, you can generate fixtures (this will reset your database) - symfony console doctrine:fixtures:load - -This will generate user accounts, centers, and some basic configuration. - -The accounts created are: :code:`center a_social`, :code:`center b_social`, :code:`center a_direction`, ... The full list is -visibile in the "users" table: :code:`docker compose exec database psql -U app -c "SELECT username FROM users"`. - -The password is always :code:`password`. - -.. warning:: - - The fixtures are not fully functional. See the `corresponding issue `_. - -Add web profiler and debugger -***************************** - -.. code-block:: bash - - symfony composer require --dev symfony/web-profiler-bundle symfony/var-dumper - -Working on chill bundles -************************ - -If you plan to improve the chill-bundles repository, that's great! - -You will have to download chill-bundles as a git repository (and not as an archive, which is barely editable). - -In your :code:`composer.json` file, add these lines: - -.. code-block:: diff - - { - "config": { - + "preferred-install": { - + "chill-project/chill-bundles": "source", - "*": "dist" - + } - } - -Then, run :code:`symfony composer reinstall chill-project/chill-bundles` to re-install the package from source. - -Code style, code quality and other tools -**************************************** - -For development, you will also have to install: - -- `php-cs-fixer `_ - -We also encourage you to use tools like `phpstan `_ and `rector `_. - -Commit and share your project -============================= - -If multiple developers work on a project, you can commit your symfony project and share it with other people. - -When another developer clones your project, they will have to: - -- run :code:`symfony composer install` and :code:`yarn install` to install the same dependencies as the initial developer; -- run :code:`yarn run encore production` to compile assets; -- copy any possible variables from the :code:`.env.local` files; -- start the docker compose stack, using :code:`docker compose`, and run migrations, set up transports, and prepare chill db views - (see the corresponding command above) - -Update -====== - -In order to update your app, you must update dependencies: - -- for chill-bundles, you can `set the last version `_ manually - in the :code:`composer.json` file, or set the version to `^3.0.0` and run :code:`symfony composer update` regularly -- run :code:`composer update` and :code:`yarn update` to maintain your dependencies up-to-date. - -After each update, you must update your database schema: - -.. code-block:: bash - - symfony console doctrine:migrations:migrate - symfony console chill:db:sync-views - -Operations -========== - -Build assets -************ - -run those commands: - -.. code-block:: bash - - # for production (or in dev, when you don't need to work on your assets and need some speed) - yarn run encore production - # in dev, when you wan't to reload the assets on each changes - yarn run encore dev --watch - -How to execute the console ? -**************************** - -.. code-block:: bash - - # start the console with all required variables - symfony console - # you can add your command after that: - symfony console list - -How to generate documents -************************* - -Documents are generated asynchronously by `"consuming messages" `_. - -You must generate them using a dedicated process: - -.. code-block:: bash - - symfony console messenger:consume async priority - -To avoid memory issues, we encourage you to also use the :code:`--limit` parameter of the command. - -How to read emails sent by the program ? -******************************************* - -In development, there is a built-in "mail catcher". Open it with :code:`symfony open:local:webmail` - -How to run cron-jobs ? -********************** - -Some commands must be executed in :ref:`cron jobs `. To execute them: - -.. code-block:: bash - - symfony console chill:cron-job:execute - -What about materialized views ? -******************************* - -There are some materialized views in chill, to speed up some complex computations in the database. - -In order to refresh them, run a cron job or refresh them manually in your database. - -How to run tests for chill-bundles -********************************** - -Tests reside inside the installed bundles. You must `cd` into that directory, download the required packages, and execute them from this place. - -**Note**: some bundles require the fixtures to be executed. See the dedicated _how-tos_. - -Example, for running a unit test inside `main` bundle: - -.. code-block:: bash - - # cd into main directory - cd vendor/chill-project/chill-bundles - composer install - # run tests - bin/phpunit src/Bundle/path/to/your/test - -Or for running tests to check code style and php conventions with csfixer and phpstan: - -Troubleshooting -=============== - -Error `An exception has been thrown during the rendering of a template ("Asset manifest file "/var/www/app/web/build/manifest.json" does not exist.").` on first run -******************************************************************************************************************************************************************** - -Build assets, see above. - -Running in production -===================== - -Currently, to run this software in production, the *state of the art* is the following : - -1. Run the software locally and tweak the configuration to your needs ; -2. Build the image and store it in a private container registry. - -.. warning:: - - In production, you **must** set these variables: - - * ``APP_ENV`` to ``prod`` - * ``APP_DEBUG`` to ``false`` - - There are security issues if you keep the same variables as for production. - - -Going further -============= +Instructions +============ .. toctree:: :maxdepth: 2 - prod.rst - load-addresses.rst - prod-calendar-sms-sending.rst - msgraph-configure.rst + installation-development.rst + installation-production.rst diff --git a/docs/source/installation/installation-development.rst b/docs/source/installation/installation-development.rst new file mode 100644 index 000000000..5977747c6 --- /dev/null +++ b/docs/source/installation/installation-development.rst @@ -0,0 +1,101 @@ +.. _installation-for-dev: + +Installation for development or testing purpose only +==================================================== + +⚠️ Use this method for development only. ⚠️ + +You will need: + +- `Composer `__; +- `Symfony-cli tool `__; +- `docker `__ and + `docker-compose `__ +- node > 20 and yarn 1.22 + +First initialization +-------------------- + +1. clone the repository and move to the cloned directory: + +.. code:: bash + + git clone https://gitlab.com/Chill-Projet/chill-bundles.git + cd chill-bundles + +2. install dependencies using composer + +.. code:: bash + + composer install + +3. Install and compile assets: + +.. code:: bash + + yarn install + yarn run encore production + +**note** double check that you have the node version > 20 using the +``node --version`` command. + +4. configure your project: create a ``.env.local`` file at the root, and + add the admin password: + +.. code:: dotenv + + # for this installation mode, the environment should always be "dev" + APP_ENV=dev + ADMIN_PASSWORD=\$2y\$13\$iyvJLuT4YEa6iWXyQV4/N.hNHpNG8kXlYDkkt5MkYy4FXcSwYAwmm + # note: if you copy-paste the line above, the password will be "admin". + +5. start the stack using ``docker compose up -d``, check the status of + the start with ``docker compose ps`` + +6. configure all the needed third-party tools + + .. code:: bash + + # the first start, it may last some seconds, you can check with docker compose ps + # run migrations + symfony console doctrine:migrations:migrate + # setup messenger + symfony console messenger:setup-transports + # prepare some views + symfony console chill:db:sync-views + # generate jwt token, required for some api features (webdav access, ...) + symfony console lexik:jwt:generate-keypair + +7. add some fixtures + +This will truncate all the existing data of the database. But remember, +we are in dev mode ! + +.. code:: bash + + symfony console doctrine:fixtures:load + +8. launch symfony dev-server + +.. code:: bash + + symfony server:start -d + +And visit the web page it suggest. You can login with user +``center a_social`` and password ``password``, or login ``admin`` with +the password you set. + +Stopping the server +------------------- + +.. code:: bash + + symfony server:stop + +Restart the webserver for subsequent start +------------------------------------------ + +.. code:: bash + + symfony server:start -d + # this will automatically starts the full docker compose services diff --git a/docs/source/installation/installation-production.rst b/docs/source/installation/installation-production.rst new file mode 100644 index 000000000..08ede3d40 --- /dev/null +++ b/docs/source/installation/installation-production.rst @@ -0,0 +1,353 @@ +.. _installation-production: + +Install Chill for production with or without adding personal features +##################################################################### + +Chill is a set of "bundles" for a symfony app. + +To run Chill in production or add new features to it (without merging those features to the chill core), you must create +a symfony app, and eventually add those features into your app. + +Once you are happy with the configuration, `you should follow the dedicated instructions of how to go into production for +Symfony apps `_. + +Install a new app +================= + +Initialize project and dependencies +----------------------------------- + +.. code-block:: bash + + symfony new --version=5.4 my_chill_project + cd my_chill_project + +We strongly encourage you to initialize a git repository at this step, to track further changes. + +.. code-block:: bash + + # add the flex endpoints required for custom recipes + cat <<< "$(jq '.extra.symfony += {"endpoint": ["flex://defaults", "https://gitlab.com/api/v4/projects/57371968/repository/files/index.json/raw?ref=main"]}' composer.json)" > composer.json + # install chill and some dependencies + symfony composer require chill-project/chill-bundles ^3.7.1 champs-libres/wopi-lib dev-master@dev champs-libres/wopi-bundle dev-master@dev symfony/amqp-messenger + +We encourage you to accept the inclusion of the "Docker configuration from recipes": this is the documented way to run the database. +You must also accept to configure recipes from the contrib repository, unless you want to configure the bundles manually). + +.. code-block:: bash + + # fix some configuration + ./post-install-chill.sh + # install node dependencies + yarn install + # and compile assets + yarn run encore production + +.. note:: + + If you encounter this error during assets compilation (:code:`yarn run encore production`) (repeated multiple times): + + .. code-block:: + + [tsl] ERROR in /tmp/chill/v1/public/bundles/chillcalendar/types.ts(2,65) + TS2307: Cannot find module '../../../ChillMainBundle/Resources/public/types' or its corresponding type declarations. + + run: + + .. code-block:: bash + + rm -rf public/bundles/* + + Then restart the compilation of assets (:code:```yarn run encore production```) + +Configure your project +---------------------- + +You should read the configuration files in :code:`chill/config/packages` carefully, especially if you have +custom developments. But most of the time, this should be fine. + +You have to configure some local variables, which are described in the :code:`.env` file. The secrets should not be stored +in this :code:`.env` file, but instead using the `secrets management tool `_ +or in the :code:`.env.local` file, which should not be committed to the git repository. + +You do not need to set variables for the smtp server, redis server and relatorio server, as they are generated automatically +by the symfony server, from the docker compose services. + +The required variables are: + +- the :code:`ADMIN_PASSWORD`; +- the :code:`OVHCLOUD_DSN` variable; + +:code:`ADMIN_PASSWORD` +^^^^^^^^^^^^^^^^^^^^^^ + +You can generate a hashed and salted admin password using the command +:code:`symfony console security:hash-password 'Symfony\Component\Security\Core\User\User'`.Then, +you can either: + +- add this password to the :code:`.env.local` file, you must escape the character :code:`$`: if the generated password + is :code:`$2y$13$iyvJLuT4YEa6iWXyQV4/N.hNHpNG8kXlYDkkt5MkYy4FXcSwYAwmm`, your :code:`.env.local` file will be: + + .. code-block:: bash + + ADMIN_PASSWORD=\$2y\$13\$iyvJLuT4YEa6iWXyQV4/N.hNHpNG8kXlYDkkt5MkYy4FXcSwYAwmm + # note: if you copy-paste the line above, the password will be "admin". + +- add the generated password to the secrets manager (**note**: you must add the generated hashed password to the secrets env, + not the password in clear text). + +:code:`OVHCLOUD_DSN` and sending SMS messages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is a temporary dependency, for ensuring compatibility for previous behaviour. + +You can set it to :code:`null://null` if you do not plan to use sending SMS. + +.. code-block:: bash + + OVHCLOUD_DSN=null://null + +If you plan to do it, you can configure the notifier component `as described in the symfony documentation `_. + + +Some environment variables are available for the JWT authentication bundle in the :code:`.env` file. + +Prepare database, messenger queue, and other configuration +---------------------------------------------------------- + +To continue the installation process, you will have to run migrations: + +.. code-block:: bash + + # start databases and other services + docker compose up -d + # the first start, it may last some seconds, you can check with docker compose ps + # run migrations + symfony console doctrine:migrations:migrate + # setup messenger + symfony console messenger:setup-transports + # prepare some views + symfony console chill:db:sync-views + # load languages data + symfony console chill:main:languages:populate + # generate jwt token, required for some api features (webdav access, ...) + symfony console lexik:jwt:generate-keypair + +.. note:: + + If you encounter this error: + + .. code-block:: + + No transport supports the given Messenger DSN. + + Please check that you installed the package `symfony/amqp-messenger`. + + + +Start your web server locally +----------------------------- + +At this step, Chill will be ready to be served locally, but without any configuration. You can run the project +locally using the `local symfony server `_: + +.. code-block:: bash + + # see the whole possibilities at https://symfony.com/doc/current/setup/symfony_server.html + symfony server:start -d + + +If you need to test the instance with accounts and some basic configuration, please install the fixtures (see below). + + +Add capabilities for dev +======================== + +If you need to add custom bundles, you can develop them in the `src/` directory, like for any other symfony project. You +can rely on the whole chill framework, meaning there is no need to add them to the original `chill-bundles`. + +You will require some bundles to have the following development tools: + +- add fixtures +- add profiler and debug bundle + +Install fixtures +---------------- + +.. code-block:: bash + + # generate fixtures for chill + symfony composer require --dev doctrine/doctrine-fixtures-bundle nelmio/alice + # now, you can generate fixtures (this will reset your database) + symfony console doctrine:fixtures:load + +This will generate user accounts, centers, and some basic configuration. + +The accounts created are: :code:`center a_social`, :code:`center b_social`, :code:`center a_direction`, ... The full list is +visible in the "users" table: :code:`docker compose exec database psql -U app -c "SELECT username FROM users"`. + +The password is always :code:`password`. + +.. warning:: + + The fixtures are not fully functional. See the `corresponding issue `_. + +Add web profiler and debugger +----------------------------- + +.. code-block:: bash + + symfony composer require --dev symfony/web-profiler-bundle symfony/debug-bundle + +Working on chill bundles +------------------------ + +If you plan to improve the chill-bundles repository, that's great! + +It would be better :ref:`to follow the instruction about development `. But if those features are +deeply linked to some dev you made in the app, it can be easier to develop within the :code:`vendor/` directory. + +You will have to download chill-bundles as a git repository (and not as an archive, which is barely editable). + +In your :code:`composer.json` file, add these lines: + +.. code-block:: diff + + { + "config": { + + "preferred-install": { + + "chill-project/chill-bundles": "source", + "*": "dist" + + } + } + +Then, run :code:`symfony composer reinstall chill-project/chill-bundles` to re-install the package from source. + + +Update +====== + +In order to update your app, you must update dependencies: + +- for chill-bundles, you can `set the last version `_ manually + in the :code:`composer.json` file, or set the version to `^3.0.0` and run :code:`symfony composer update` regularly +- run :code:`composer update` and :code:`yarn update` to maintain your dependencies up-to-date. + +After each update, you must update your database schema: + +.. code-block:: bash + + symfony console doctrine:migrations:migrate + symfony console chill:db:sync-views + + +Commit and share your project +============================= + +If multiple developers work on a project, you can commit your symfony project and share it with other people. + +When another developer clones your project, they will have to: + +- run :code:`symfony composer install` and :code:`yarn install` to install the same dependencies as the initial developer; +- run :code:`yarn run encore production` to compile assets; +- copy any possible variables from the :code:`.env.local` files; +- start the docker compose stack, using :code:`docker compose`, and run migrations, set up transports, and prepare chill db views + (see the corresponding command above) + +Operations +========== + +Build assets +------------ + +run those commands: + +.. code-block:: bash + + # for production (or in dev, when you don't need to work on your assets and need some speed) + yarn run encore production + # in dev, when you wan't to reload the assets on each changes + yarn run encore dev --watch + +How to execute the console ? +---------------------------- + +.. code-block:: bash + + # start the console with all required variables + symfony console + # you can add your command after that: + symfony console list + +How to generate documents +------------------------- + +Documents are generated asynchronously by `"consuming messages" `_. + +You must generate them using a dedicated process: + +.. code-block:: bash + + symfony console messenger:consume async priority + +To avoid memory issues, we encourage you to also use the :code:`--limit` parameter of the command. + +How to read emails sent by the program ? +------------------------------------------- + +In development, there is a built-in "mail catcher". Open it with :code:`symfony open:local:webmail` + +How to run cron-jobs ? +---------------------- + +Some commands must be executed in :ref:`cron jobs `. To execute them: + +.. code-block:: bash + + symfony console chill:cron-job:execute + +What about materialized views ? +------------------------------- + +There are some materialized views in chill, to speed up some complex computations in the database. + +In order to refresh them, run a cron job or refresh them manually in your database. + + +Troubleshooting +=============== + +Error `An exception has been thrown during the rendering of a template ("Asset manifest file "/var/www/app/web/build/manifest.json" does not exist.").` on first run +-------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Build assets, see above. + +Go to production +================ + +Currently, to run this software in production, the *state of the art* is the following : + +1. Run the software locally and tweak the configuration to your needs ; +2. Build the image and store it in a private container registry. + +.. warning:: + + In production, you **must** set these variables: + + * ``APP_ENV`` to ``prod`` + * ``APP_DEBUG`` to ``false`` + + There are security issues if you keep the same variables as for production. + + +Going further +============= + +.. toctree:: + :maxdepth: 2 + + prod.rst + document-storage.rst + load-addresses.rst + prod-calendar-sms-sending.rst + msgraph-configure.rst diff --git a/docs/source/installation/load-addresses.rst b/docs/source/installation/load-addresses.rst index 85c29d618..641325dee 100644 --- a/docs/source/installation/load-addresses.rst +++ b/docs/source/installation/load-addresses.rst @@ -41,16 +41,18 @@ Postal code are loaded from this database. There is no need to load postal codes The data are prepared for Chill (`See this repository `_). One can select postal code by his first number (:code:`1xxx` for postal codes from 1000 to 1999), or a limited list for development purpose. +The command expects a language code as first argument. + .. code-block:: bash # load postal code from 1000 to 3999: - bin/console chill:main:address-ref-from-best-addresse 1xxx 2xxx 3xxx + bin/console chill:main:address-ref-from-best-addresse fr 1xxx 2xxx 3xxx # load only an extract (for dev purposes) - bin/console chill:main:address-ref-from-best-addresse extract + bin/console chill:main:address-ref-from-best-addresse fr extract # load full addresses (discouraged) - bin/console chill:main:address-ref-from-best-addresse full + bin/console chill:main:address-ref-from-best-addresse fr full .. note:: diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 000000000..dbcafea0a --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,40 @@ +import eslintPluginVue from "eslint-plugin-vue"; +import ts from "typescript-eslint"; +import eslintPluginPrettier from "eslint-plugin-prettier"; + +export default ts.config( + ...ts.configs.recommended, + ...ts.configs.stylistic, + ...eslintPluginVue.configs["flat/essential"], + { + files: ["**/*.vue"], + languageOptions: { + parserOptions: { + parser: "@typescript-eslint/parser", + }, + }, + }, + { + ignores: [ + "**/vendor/*", + "**/import-png.d.ts", + "**/chill.webpack.config.js", + "**/var/*", + "**/docker/*", + "**/node_modules/*", + "**/public/build/*" + ], + }, + { + plugins: { + prettier: eslintPluginPrettier, + }, + rules: { + "prettier/prettier": "error", + // override/add rules settings here, such as: + "vue/multi-word-component-names": "off", + "@typescript-eslint/no-require-imports": "off", + "@typescript-eslint/ban-ts-comment": "off" + }, + }, +); diff --git a/package.json b/package.json index 3b9d4c70a..15697def7 100644 --- a/package.json +++ b/package.json @@ -6,33 +6,41 @@ "@apidevtools/swagger-cli": "^4.0.4", "@babel/core": "^7.20.5", "@babel/preset-env": "^7.20.2", - "@ckeditor/ckeditor5-build-classic": "^41.4.2", - "@ckeditor/ckeditor5-dev-utils": "^40.2.0", - "@ckeditor/ckeditor5-dev-webpack-plugin": "^31.1.13", - "@ckeditor/ckeditor5-dev-translations": "^40.2.0", - "@ckeditor/ckeditor5-markdown-gfm": "^41.4.2", - "@ckeditor/ckeditor5-theme-lark": "^41.4.2", - "@ckeditor/ckeditor5-vue": "^5.1.0", + "@ckeditor/ckeditor5-vue": "^7.3.0", + "@eslint/js": "^9.14.0", + "@hotwired/stimulus": "^3.0.0", + "@luminateone/eslint-baseline": "^1.0.9", + "@symfony/stimulus-bridge": "^3.2.0", + "@symfony/ux-translator": "file:vendor/symfony/ux-translator/assets", "@symfony/webpack-encore": "^4.1.0", - "@tsconfig/node14": "^1.0.1", + "@tsconfig/node20": "^20.1.4", "@types/dompurify": "^3.0.5", + "@types/eslint__js": "^8.42.3", + "@typescript-eslint/parser": "^8.12.2", "bindings": "^1.5.0", "bootstrap": "5.2.3", "chokidar": "^3.5.1", + "ckeditor5": "^44.1.0", "dompurify": "^3.1.0", + "eslint": "^9.14.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-vue": "^9.30.0", "fork-awesome": "^1.1.7", + "intl-messageformat": "^10.5.11", "jquery": "^3.6.0", "node-sass": "^8.0.0", - "marked": "^12.0.1", "popper.js": "^1.16.1", "postcss-loader": "^7.0.2", + "prettier": "^3.3.3", "raw-loader": "^4.0.2", - "sass-loader": "^13.0.0", + "sass-loader": "^14.0.0", "select2": "^4.0.13", "select2-bootstrap-theme": "0.1.0-beta.10", "style-loader": "^3.3.1", "ts-loader": "^9.3.1", - "typescript": "^5.4.5", + "typescript": "^5.6.3", + "typescript-eslint": "^8.13.0", "vue-loader": "^17.0.0", "webpack": "^5.75.0", "webpack-cli": "^5.0.1" @@ -45,28 +53,39 @@ "@fullcalendar/timegrid": "^6.1.4", "@fullcalendar/vue3": "^6.1.4", "@popperjs/core": "^2.9.2", + "@tsconfig/node20": "^20.1.4", + "@types/dompurify": "^3.0.5", "@types/leaflet": "^1.9.3", + "bootstrap-icons": "^1.11.3", "dropzone": "^5.7.6", "es6-promise": "^4.2.8", + "intl-messageformat": "^10.5.11", "leaflet": "^1.7.1", + "marked": "^12.0.2", "masonry-layout": "^4.2.2", "mime": "^4.0.0", - "swagger-ui": "^4.15.5", + "pdfjs-dist": "^4.3.136", "vis-network": "^9.1.0", - "vue": "^3.2.37", + "vue": "^3.5.6", "vue-i18n": "^9.1.6", "vue-multiselect": "3.0.0-alpha.2", - "vue-toast-notification": "^2.0", + "vue-toast-notification": "^3.1.2", "vuex": "^4.0.0" }, "browserslist": [ - "Firefox ESR" + "defaults and fully supports es6-module and not dead" ], "scripts": { "dev-server": "encore dev-server", "dev": "encore dev", "watch": "encore dev --watch", - "build": "encore production --progress" + "build": "encore production --progress", + "specs-build": "yaml-merge src/Bundle/ChillMainBundle/chill.api.specs.yaml src/Bundle/ChillPersonBundle/chill.api.specs.yaml src/Bundle/ChillCalendarBundle/chill.api.specs.yaml src/Bundle/ChillThirdPartyBundle/chill.api.specs.yaml src/Bundle/ChillDocStoreBundle/chill.api.specs.yaml> templates/api/specs.yaml", + "specs-validate": "swagger-cli validate templates/api/specs.yaml", + "specs-create-dir": "mkdir -p templates/api", + "specs": "yarn run specs-create-dir && yarn run specs-build && yarn run specs-validate", + "version": "node --version", + "eslint": "npx eslint-baseline --fix \"src/**/*.{js,ts,vue}\"" }, "private": true } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 8af07174d..641bb5dcd 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,34 +1,29 @@ parameters: ignoreErrors: + - + message: "#^Foreach overwrites \\$key with its key variable\\.$#" + count: 1 + path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php + - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" count: 1 path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" + message: "#^Property Chill\\\\CustomFieldsBundle\\\\Entity\\\\CustomField\\:\\:\\$required \\(false\\) does not accept bool\\.$#" count: 1 - path: src/Bundle/ChillPersonBundle/Form/PersonType.php + path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 1 - path: src/Bundle/ChillMainBundle/Templating/ChillTwigRoutingHelper.php - - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 1 - path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldsGroup.php - - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" + message: "#^Parameter \\#1 \\$user of method Chill\\\\DocStoreBundle\\\\Entity\\\\Document\\:\\:setUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User\\|null, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#" count: 2 - path: src/Bundle/ChillMainBundle/Repository/NotificationRepository.php + path: src/Bundle/ChillDocStoreBundle/Controller/DocumentAccompanyingCourseController.php - - message: "#^Foreach overwrites \\$key with its key variable\\.$#" - count: 1 - path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php + message: "#^Parameter \\#1 \\$user of method Chill\\\\DocStoreBundle\\\\Entity\\\\Document\\:\\:setUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User\\|null, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#" + count: 2 + path: src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php - message: "#^Variable \\$participation might not be defined\\.$#" @@ -40,6 +35,106 @@ parameters: count: 1 path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php + - + message: "#^Comparison operation \"\\>\" between \\(bool\\|int\\|Redis\\) and 0 results in an error\\.$#" + count: 1 + path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/ApiWrapper.php + + - + message: "#^Variable \\$response might not be defined\\.$#" + count: 1 + path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/ApiWrapper.php + + - + message: "#^Function GuzzleHttp\\\\Psr7\\\\get not found\\.$#" + count: 1 + path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php + + - + message: "#^Function GuzzleHttp\\\\Psr7\\\\str not found\\.$#" + count: 2 + path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php + + - + message: "#^Parameter \\#1 \\$seconds of function sleep expects int, string given\\.$#" + count: 1 + path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Controller/CSPersonController.php + + - + message: "#^Parameter \\#1 \\$interval of method DateTimeImmutable\\:\\:add\\(\\) expects DateInterval, string\\|null given\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Entity/Immersion.php + + - + message: "#^Parameter \\#1 \\$object of static method DateTimeImmutable\\:\\:createFromMutable\\(\\) expects DateTime, DateTimeInterface given\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Entity/Immersion.php + + - + message: "#^Property Chill\\\\JobBundle\\\\Entity\\\\Rome\\\\Metier\\:\\:\\$appellations is never read, only written\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Entity/Rome/Metier.php + + - + message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListCSPerson\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Export/ListCSPerson.php + + - + message: "#^Variable \\$f might not be defined\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Export/ListCSPerson.php + + - + message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListFrein\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Export/ListFrein.php + + - + message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListProjetProfessionnel\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Export/ListProjetProfessionnel.php + + - + message: "#^Property Chill\\\\JobBundle\\\\Form\\\\ChoiceLoader\\\\RomeAppellationChoiceLoader\\:\\:\\$appellationRepository \\(Chill\\\\JobBundle\\\\Repository\\\\Rome\\\\AppellationRepository\\) does not accept Doctrine\\\\ORM\\\\EntityRepository\\\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php + + - + message: "#^Result of && is always false\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php + + - + message: "#^Strict comparison using \\=\\=\\= between array\\{\\} and Symfony\\\\Component\\\\Validator\\\\ConstraintViolationListInterface will always evaluate to false\\.$#" + count: 2 + path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php + + - + message: "#^Strict comparison using \\=\\=\\= between null and string will always evaluate to false\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php + + - + message: "#^Variable \\$metier might not be defined\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php + + - + message: "#^Parameter \\#1 \\$interval of method DateTimeImmutable\\:\\:add\\(\\) expects DateInterval, string\\|null given\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Security/Authorization/CSConnectesVoter.php + + - + message: "#^Parameter \\#1 \\$object of static method DateTimeImmutable\\:\\:createFromMutable\\(\\) expects DateTime, DateTimeInterface given\\.$#" + count: 1 + path: src/Bundle/ChillJobBundle/src/Security/Authorization/CSConnectesVoter.php + - message: "#^Cannot unset offset '_token' on array\\{formatter\\: mixed, export\\: mixed, centers\\: mixed, alias\\: string\\}\\.$#" count: 1 @@ -65,11 +160,31 @@ parameters: count: 1 path: src/Bundle/ChillMainBundle/Form/ChoiceLoader/PostalCodeChoiceLoader.php + - + message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" + count: 2 + path: src/Bundle/ChillMainBundle/Repository/NotificationRepository.php + + - + message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:userHasAccessForCenter\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface given\\.$#" + count: 1 + path: src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php + + - + message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" + count: 1 + path: src/Bundle/ChillMainBundle/Templating/ChillTwigRoutingHelper.php + - message: "#^Foreach overwrites \\$value with its value variable\\.$#" count: 1 path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php + - + message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" + count: 1 + path: src/Bundle/ChillPersonBundle/Form/PersonType.php + - message: "#^Foreach overwrites \\$value with its value variable\\.$#" count: 1 diff --git a/phpstan.neon.dist b/phpstan.dist.neon similarity index 100% rename from phpstan.neon.dist rename to phpstan.dist.neon diff --git a/phpunit.xml.dist b/phpunit.xml.dist index fa8f479c5..d122aeb4c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,12 +1,21 @@ - + + - + + + - + + diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue index 279650ffd..86d4a6c5a 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue @@ -1,51 +1,46 @@ - + diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/i18n.ts b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/i18n.ts index 2950c16da..e9e31dd25 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/i18n.ts +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/i18n.ts @@ -1,30 +1,27 @@ const appMessages = { - fr: { - created_availabilities: "Lieu des plages de disponibilités créées", - edit_your_calendar_range: "Planifiez vos plages de disponibilités", - show_my_calendar: "Afficher mon calendrier", - show_weekends: "Afficher les week-ends", - copy_range: "Copier", - 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", - delete_range_to_save: "Plages à supprimer", - by: "Par", - main_user_concerned: "Utilisateur concerné", - dateFrom: "De", - dateTo: "à", - day: "Jour", - week: "Semaine", - month: "Mois", - today: "Aujourd'hui", - } -} - -export { - appMessages + fr: { + created_availabilities: "Lieu des plages de disponibilités créées", + edit_your_calendar_range: "Planifiez vos plages de disponibilités", + 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_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", + delete_range_to_save: "Plages à supprimer", + by: "Par", + main_user_concerned: "Utilisateur concerné", + dateFrom: "De", + dateTo: "à", + day: "Jour", + week: "Semaine", + month: "Mois", + today: "Aujourd'hui", + }, }; + +export { appMessages }; diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/index2.ts b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/index2.ts index 52dcd318d..cb87b0b67 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/index2.ts +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/index2.ts @@ -1,19 +1,19 @@ -import { createApp } from 'vue'; -import { _createI18n } from '../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n' -import { appMessages } from './i18n' -import futureStore, {key} from './store/index' +import { createApp } from "vue"; +import { _createI18n } from "../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n"; +import { appMessages } from "./i18n"; +import futureStore, { key } from "./store/index"; -import App2 from './App2.vue'; -import {useI18n} from "vue-i18n"; +import App2 from "./App2.vue"; +import { useI18n } from "vue-i18n"; futureStore().then((store) => { - const i18n = _createI18n(appMessages, false); + const i18n = _createI18n(appMessages, false); - const app = createApp({ - template: ``, - }) - .use(store, key) - .use(i18n) - .component('app', App2) - .mount('#myCalendar'); + const app = createApp({ + template: ``, + }) + .use(store, key) + .use(i18n) + .component("app", App2) + .mount("#myCalendar"); }); diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/index.ts b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/index.ts index f87832cb5..e5a863245 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/index.ts +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/index.ts @@ -1,50 +1,56 @@ -import 'es6-promise/auto'; -import {Store, createStore} from 'vuex'; -import {InjectionKey} from "vue"; -import me, {MeState} from './modules/me'; -import fullCalendar, {FullCalendarState} from './modules/fullcalendar'; -import calendarRanges, {CalendarRangesState} from './modules/calendarRanges'; -import calendarRemotes, {CalendarRemotesState} from './modules/calendarRemotes'; -import {whoami} from "../../../../../../ChillMainBundle/Resources/public/lib/api/user"; -import {User} from '../../../../../../ChillMainBundle/Resources/public/types'; -import locations, {LocationState} from "./modules/location"; -import calendarLocals, {CalendarLocalsState} from "./modules/calendarLocals"; +import "es6-promise/auto"; +import { Store, createStore } from "vuex"; +import { InjectionKey } from "vue"; +import me, { MeState } from "./modules/me"; +import fullCalendar, { FullCalendarState } from "./modules/fullcalendar"; +import calendarRanges, { CalendarRangesState } from "./modules/calendarRanges"; +import calendarRemotes, { + CalendarRemotesState, +} from "./modules/calendarRemotes"; +import { whoami } from "../../../../../../ChillMainBundle/Resources/public/lib/api/user"; +import { User } from "../../../../../../ChillMainBundle/Resources/public/types"; +import locations, { LocationState } from "./modules/location"; +import calendarLocals, { CalendarLocalsState } from "./modules/calendarLocals"; -const debug = process.env.NODE_ENV !== 'production'; +const debug = process.env.NODE_ENV !== "production"; export interface State { - calendarRanges: CalendarRangesState, - calendarRemotes: CalendarRemotesState, - calendarLocals: CalendarLocalsState, - fullCalendar: FullCalendarState, - me: MeState, - locations: LocationState + calendarRanges: CalendarRangesState; + calendarRemotes: CalendarRemotesState; + calendarLocals: CalendarLocalsState; + fullCalendar: FullCalendarState; + me: MeState; + locations: LocationState; } export const key: InjectionKey> = Symbol(); -const futureStore = function(): Promise> { - return whoami().then((user: User) => { - const store = createStore({ - strict: debug, - modules: { - me, - fullCalendar, - calendarRanges, - calendarRemotes, - calendarLocals, - locations, - }, - mutations: {} - }); +const futureStore = function (): Promise> { + return whoami().then((user: User) => { + const store = createStore({ + strict: debug, + modules: { + me, + fullCalendar, + calendarRanges, + calendarRemotes, + calendarLocals, + locations, + }, + mutations: {}, + }); - store.commit('me/setWhoAmi', user, {root: true}); - store.dispatch('locations/getLocations', null, {root: true}).then(_ => { - return store.dispatch('locations/getCurrentLocation', null, {root: true}); - }); + store.commit("me/setWhoAmi", user, { root: true }); + store + .dispatch("locations/getLocations", null, { root: true }) + .then((_) => { + return store.dispatch("locations/getCurrentLocation", null, { + root: true, + }); + }); - return Promise.resolve(store); - }); -} + return Promise.resolve(store); + }); +}; export default futureStore; diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarLocals.ts b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarLocals.ts index 893c0413c..07ce477dd 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarLocals.ts +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarLocals.ts @@ -1,95 +1,116 @@ -import {State} from './../index'; -import {ActionContext, Module} from 'vuex'; -import {CalendarLight} from '../../../../types'; -import {fetchCalendarLocalForUser} from '../../../Calendar/api'; -import {EventInput} from '@fullcalendar/core'; -import {localsToFullCalendarEvent} from "../../../Calendar/store/utils"; -import {TransportExceptionInterface} from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods"; -import {COLORS} from "../../../Calendar/const"; +import { State } from "./../index"; +import { ActionContext, Module } from "vuex"; +import { CalendarLight } from "../../../../types"; +import { fetchCalendarLocalForUser } from "../../../Calendar/api"; +import { EventInput } from "@fullcalendar/core"; +import { localsToFullCalendarEvent } from "../../../Calendar/store/utils"; +import { TransportExceptionInterface } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods"; +import { COLORS } from "../../../Calendar/const"; export interface CalendarLocalsState { - locals: EventInput[], - localsLoaded: {start: number, end: number}[], - localsIndex: Set, - key: number + locals: EventInput[]; + localsLoaded: { start: number; end: number }[]; + localsIndex: Set; + key: number; } type Context = ActionContext; -export default > { - namespaced: true, - state: (): CalendarLocalsState => ({ - locals: [], - localsLoaded: [], - localsIndex: new Set(), - key: 0 - }), - getters: { - isLocalsLoaded: (state: CalendarLocalsState) => ({start, end}: {start: Date, end: Date}): boolean => { - for (let range of state.localsLoaded) { - if (start.getTime() === range.start && end.getTime() === range.end) { - return true; - } - } +export default { + namespaced: true, + state: (): CalendarLocalsState => ({ + locals: [], + localsLoaded: [], + localsIndex: new Set(), + key: 0, + }), + getters: { + isLocalsLoaded: + (state: CalendarLocalsState) => + ({ start, end }: { start: Date; end: Date }): boolean => { + for (const range of state.localsLoaded) { + if ( + start.getTime() === range.start && + end.getTime() === range.end + ) { + return true; + } + } - return false; + return false; + }, }, - }, - mutations: { - addLocals(state: CalendarLocalsState, ranges: CalendarLight[]) { - console.log('addLocals', ranges); + mutations: { + addLocals(state: CalendarLocalsState, ranges: CalendarLight[]) { + console.log("addLocals", ranges); - const toAdd = ranges - .map(cr => localsToFullCalendarEvent(cr)) - .filter(r => !state.localsIndex.has(r.id)); + const toAdd = ranges + .map((cr) => localsToFullCalendarEvent(cr)) + .filter((r) => !state.localsIndex.has(r.id)); - toAdd.forEach((r) => { - state.localsIndex.add(r.id); - state.locals.push(r); - }); - state.key = state.key + toAdd.length; + toAdd.forEach((r) => { + state.localsIndex.add(r.id); + state.locals.push(r); + }); + state.key = state.key + toAdd.length; + }, + addLoaded( + state: CalendarLocalsState, + payload: { start: Date; end: Date }, + ) { + state.localsLoaded.push({ + start: payload.start.getTime(), + end: payload.end.getTime(), + }); + }, }, - addLoaded(state: CalendarLocalsState, payload: {start: Date, end: Date}) { - state.localsLoaded.push({start: payload.start.getTime(), end: payload.end.getTime()}); + actions: { + fetchLocals( + ctx: Context, + payload: { start: Date; end: Date }, + ): Promise { + const start = payload.start; + const end = payload.end; + + if (ctx.rootGetters["me/getMe"] === null) { + return Promise.resolve(null); + } + + if (ctx.getters.isLocalsLoaded({ start, end })) { + return Promise.resolve(ctx.getters.getRangeSource); + } + + ctx.commit("addLoaded", { + start: start, + end: end, + }); + + return fetchCalendarLocalForUser( + ctx.rootGetters["me/getMe"], + start, + end, + ) + .then((remotes: CalendarLight[]) => { + // to be add when reactivity problem will be solve ? + //ctx.commit('addRemotes', remotes); + const inputs = remotes + .map((cr) => localsToFullCalendarEvent(cr)) + .map((cr) => ({ + ...cr, + backgroundColor: COLORS[0], + textColor: "black", + editable: false, + })); + ctx.commit("calendarRanges/addExternals", inputs, { + root: true, + }); + return Promise.resolve(null); + }) + .catch((e: TransportExceptionInterface) => { + console.error(e); + + return Promise.resolve(null); + }); + }, }, - }, - actions: { - fetchLocals(ctx: Context, payload: {start: Date, end: Date}): Promise { - const start = payload.start; - const end = payload.end; - - if (ctx.rootGetters['me/getMe'] === null) { - return Promise.resolve(null); - } - - if (ctx.getters.isLocalsLoaded({start, end})) { - return Promise.resolve(ctx.getters.getRangeSource); - } - - ctx.commit('addLoaded', { - start: start, - end: end, - }); - - return fetchCalendarLocalForUser( - ctx.rootGetters['me/getMe'], - start, - end - ) - .then((remotes: CalendarLight[]) => { - // to be add when reactivity problem will be solve ? - //ctx.commit('addRemotes', remotes); - const inputs = remotes - .map(cr => localsToFullCalendarEvent(cr)) - .map(cr => ({...cr, backgroundColor: COLORS[0], textColor: 'black', editable: false})) - ctx.commit('calendarRanges/addExternals', inputs, {root: true}); - return Promise.resolve(null); - }) - .catch((e: TransportExceptionInterface) => { - console.error(e); - - return Promise.resolve(null); - }); - } - } -}; +} as Module; diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarRanges.ts b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarRanges.ts index aaa530909..26984c051 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarRanges.ts +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarRanges.ts @@ -1,252 +1,380 @@ -import {State} from './../index'; -import {ActionContext, Module} from 'vuex'; -import {CalendarRange, CalendarRangeCreate, CalendarRangeEdit, isEventInputCalendarRange} from "../../../../types"; -import {Location} from "../../../../../../../ChillMainBundle/Resources/public/types"; -import {fetchCalendarRangeForUser} from '../../../Calendar/api'; -import {calendarRangeToFullCalendarEvent} from '../../../Calendar/store/utils'; -import {EventInput} from '@fullcalendar/core'; -import {makeFetch} from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods"; +import { State } from "./../index"; +import { ActionContext, Module } from "vuex"; import { - datetimeToISO, - dateToISO, - ISOToDatetime + CalendarRange, + CalendarRangeCreate, + CalendarRangeEdit, + isEventInputCalendarRange, +} from "../../../../types"; +import { Location } from "../../../../../../../ChillMainBundle/Resources/public/types"; +import { fetchCalendarRangeForUser } from "../../../Calendar/api"; +import { calendarRangeToFullCalendarEvent } from "../../../Calendar/store/utils"; +import { EventInput } from "@fullcalendar/core"; +import { makeFetch } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods"; +import { + datetimeToISO, + dateToISO, + ISOToDatetime, } from "../../../../../../../ChillMainBundle/Resources/public/chill/js/date"; -import type {EventInputCalendarRange} from "../../../../types"; +import type { EventInputCalendarRange } from "../../../../types"; export interface CalendarRangesState { - ranges: (EventInput | EventInputCalendarRange) [], - rangesLoaded: { start: number, end: number }[], - rangesIndex: Set, - key: number + ranges: (EventInput | EventInputCalendarRange)[]; + rangesLoaded: { start: number; end: number }[]; + rangesIndex: Set; + key: number; } type Context = ActionContext; -export default >{ - namespaced: true, - state: (): CalendarRangesState => ({ - ranges: [], - rangesLoaded: [], - rangesIndex: new Set(), - key: 0 - }), - getters: { - isRangeLoaded: (state: CalendarRangesState) => ({start, end}: { start: Date, end: Date }): boolean => { - for (let range of state.rangesLoaded) { - if (start.getTime() === range.start && end.getTime() === range.end) { - return true; - } - } +export default { + namespaced: true, + state: (): CalendarRangesState => ({ + ranges: [], + rangesLoaded: [], + rangesIndex: new Set(), + key: 0, + }), + getters: { + isRangeLoaded: + (state: CalendarRangesState) => + ({ start, end }: { start: Date; end: Date }): boolean => { + for (const range of state.rangesLoaded) { + if ( + start.getTime() === range.start && + end.getTime() === range.end + ) { + return true; + } + } - return false; + return false; + }, + getRangesOnDate: + (state: CalendarRangesState) => + (date: Date): EventInputCalendarRange[] => { + const founds = []; + const dateStr = dateToISO(date) as string; + + for (const range of state.ranges) { + if ( + isEventInputCalendarRange(range) && + range.start.startsWith(dateStr) + ) { + founds.push(range); + } + } + + return founds; + }, + getRangesOnWeek: + (state: CalendarRangesState) => + (mondayDate: Date): EventInputCalendarRange[] => { + const founds = []; + for (const d of Array.from(Array(7).keys())) { + const dateOfWeek = new Date(mondayDate); + dateOfWeek.setDate(mondayDate.getDate() + d); + const dateStr = dateToISO(dateOfWeek) as string; + for (const range of state.ranges) { + if ( + isEventInputCalendarRange(range) && + range.start.startsWith(dateStr) + ) { + founds.push(range); + } + } + } + + return founds; + }, }, - getRangesOnDate: (state: CalendarRangesState) => (date: Date): EventInputCalendarRange[] => { - const founds = []; - const dateStr = dateToISO(date); + mutations: { + addRanges(state: CalendarRangesState, ranges: CalendarRange[]) { + const toAdd = ranges + .map((cr) => calendarRangeToFullCalendarEvent(cr)) + .map((cr) => ({ + ...cr, + backgroundColor: "white", + borderColor: "#3788d8", + textColor: "black", + })) + .filter((r) => !state.rangesIndex.has(r.id)); - for (let range of state.ranges) { - if (isEventInputCalendarRange(range) - && range.start.startsWith(dateStr) + toAdd.forEach((r) => { + state.rangesIndex.add(r.id); + state.ranges.push(r); + }); + state.key = state.key + toAdd.length; + }, + addExternals( + state: CalendarRangesState, + externalEvents: (EventInput & { id: string })[], ) { - founds.push(range); - } - } + const toAdd = externalEvents.filter( + (r) => !state.rangesIndex.has(r.id), + ); - return founds; - }, - }, - mutations: { - addRanges(state: CalendarRangesState, ranges: CalendarRange[]) { - const toAdd = ranges - .map(cr => calendarRangeToFullCalendarEvent(cr)) - .map(cr => ({ - ...cr, backgroundColor: 'white', borderColor: '#3788d8', - textColor: 'black' - })) - .filter(r => !state.rangesIndex.has(r.id)); - - toAdd.forEach((r) => { - state.rangesIndex.add(r.id); - state.ranges.push(r); - }); - state.key = state.key + toAdd.length; - }, - addExternals(state, externalEvents: (EventInput & { id: string })[]) { - const toAdd = externalEvents - .filter(r => !state.rangesIndex.has(r.id)); - - toAdd.forEach((r) => { - state.rangesIndex.add(r.id); - state.ranges.push(r); - }); - state.key = state.key + toAdd.length; - }, - addLoaded(state: CalendarRangesState, payload: { start: Date, end: Date }) { - state.rangesLoaded.push({start: payload.start.getTime(), end: payload.end.getTime()}); - }, - addRange(state: CalendarRangesState, payload: CalendarRange) { - const asEvent = calendarRangeToFullCalendarEvent(payload); - state.ranges.push({...asEvent, backgroundColor: 'white', borderColor: '#3788d8', textColor: 'black'}); - state.rangesIndex.add(asEvent.id); - state.key = state.key + 1; - }, - removeRange(state: CalendarRangesState, calendarRangeId: number) { - const found = state.ranges.find(r => r.calendarRangeId === calendarRangeId && r.is === "range"); - - if (found !== undefined) { - state.ranges = state.ranges.filter( - (r) => !(r.calendarRangeId === calendarRangeId && r.is === "range") - ); - - if (typeof found.id === "string") { // should always be true - state.rangesIndex.delete(found.id); - } - - state.key = state.key + 1; - } - }, - updateRange(state, range: CalendarRange) { - const found = state.ranges.find(r => r.calendarRangeId === range.id && r.is === "range"); - const newEvent = calendarRangeToFullCalendarEvent(range); - - if (found !== undefined) { - found.start = newEvent.start; - found.end = newEvent.end; - found.locationId = range.location.id; - found.locationName = range.location.name; - } - - state.key = state.key + 1; - } - }, - actions: { - fetchRanges(ctx: Context, payload: { start: Date, end: Date }): Promise { - const start = payload.start; - const end = payload.end; - - if (ctx.rootGetters['me/getMe'] === null) { - return Promise.resolve(ctx.getters.getRangeSource); - } - - if (ctx.getters.isRangeLoaded({start, end})) { - return Promise.resolve(ctx.getters.getRangeSource); - } - - ctx.commit('addLoaded', { - start: start, - end: end, - }); - - return fetchCalendarRangeForUser( - ctx.rootGetters['me/getMe'], - start, - end - ) - .then((ranges: CalendarRange[]) => { - ctx.commit('addRanges', ranges); - return Promise.resolve(null); - }); - }, - createRange(ctx, {start, end, location}: { start: Date, end: Date, location: Location }): Promise { - const url = `/api/1.0/calendar/calendar-range.json?`; - - if (ctx.rootState.me.me === null) { - throw new Error('user is currently null'); - } - - const body = { - user: { - id: ctx.rootState.me.me.id, - type: "user" + toAdd.forEach((r) => { + state.rangesIndex.add(r.id); + state.ranges.push(r); + }); + state.key = state.key + toAdd.length; }, - startDate: { - datetime: datetimeToISO(start), + addLoaded( + state: CalendarRangesState, + payload: { start: Date; end: Date }, + ) { + state.rangesLoaded.push({ + start: payload.start.getTime(), + end: payload.end.getTime(), + }); }, - endDate: { - datetime: datetimeToISO(end) + addRange(state: CalendarRangesState, payload: CalendarRange) { + const asEvent = calendarRangeToFullCalendarEvent(payload); + state.ranges.push({ + ...asEvent, + backgroundColor: "white", + borderColor: "#3788d8", + textColor: "black", + }); + state.rangesIndex.add(asEvent.id); + state.key = state.key + 1; }, - location: { - id: location.id, - type: "location" - } - } as CalendarRangeCreate; + removeRange(state: CalendarRangesState, calendarRangeId: number) { + const found = state.ranges.find( + (r) => + r.calendarRangeId === calendarRangeId && r.is === "range", + ); - return makeFetch('POST', url, body) - .then((newRange) => { + if (found !== undefined) { + state.ranges = state.ranges.filter( + (r) => + !( + r.calendarRangeId === calendarRangeId && + r.is === "range" + ), + ); - ctx.commit('addRange', newRange); + if (typeof found.id === "string") { + // should always be true + state.rangesIndex.delete(found.id); + } - return Promise.resolve(null); - }) - .catch((error) => { - console.error(error); + state.key = state.key + 1; + } + }, + updateRange(state: CalendarRangesState, range: CalendarRange) { + const found = state.ranges.find( + (r) => r.calendarRangeId === range.id && r.is === "range", + ); + const newEvent = calendarRangeToFullCalendarEvent(range); - throw error; - }) + if (found !== undefined) { + found.start = newEvent.start; + found.end = newEvent.end; + found.locationId = range.location.id; + found.locationName = range.location.name; + } + + state.key = state.key + 1; + }, }, - deleteRange(ctx, calendarRangeId: number) { - const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`; + actions: { + fetchRanges( + ctx: Context, + payload: { start: Date; end: Date }, + ): Promise { + const start = payload.start; + const end = payload.end; - makeFetch('DELETE', url) - .then((_) => { - ctx.commit('removeRange', calendarRangeId); - }); - }, - patchRangeTime(ctx, {calendarRangeId, start, end}: {calendarRangeId: number, start: Date, end: Date}): Promise { - const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`; - const body = { - startDate: { - datetime: datetimeToISO(start) + if (ctx.rootGetters["me/getMe"] === null) { + return Promise.resolve(ctx.getters.getRangeSource); + } + + if (ctx.getters.isRangeLoaded({ start, end })) { + return Promise.resolve(ctx.getters.getRangeSource); + } + + ctx.commit("addLoaded", { + start: start, + end: end, + }); + + return fetchCalendarRangeForUser( + ctx.rootGetters["me/getMe"], + start, + end, + ).then((ranges: CalendarRange[]) => { + ctx.commit("addRanges", ranges); + return Promise.resolve(null); + }); }, - endDate: { - datetime: datetimeToISO(end) + createRange( + ctx: Context, + { + start, + end, + location, + }: { start: Date; end: Date; location: Location }, + ): Promise { + const url = `/api/1.0/calendar/calendar-range.json?`; + + if (ctx.rootState.me.me === null) { + throw new Error("user is currently null"); + } + + const body = { + user: { + id: ctx.rootState.me.me.id, + type: "user", + }, + startDate: { + datetime: datetimeToISO(start), + }, + endDate: { + datetime: datetimeToISO(end), + }, + location: { + id: location.id, + type: "location", + }, + } as CalendarRangeCreate; + + return makeFetch( + "POST", + url, + body, + ) + .then((newRange) => { + ctx.commit("addRange", newRange); + + return Promise.resolve(null); + }) + .catch((error) => { + console.error(error); + + throw error; + }); }, - } as CalendarRangeEdit; + deleteRange(ctx: Context, calendarRangeId: number) { + const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`; - return makeFetch('PATCH', url, body) - .then((range) => { - ctx.commit('updateRange', range); - return Promise.resolve(null); - }) - .catch((error) => { - console.error(error); - return Promise.resolve(null); - }) - }, - patchRangeLocation(ctx, {location, calendarRangeId}: {location: Location, calendarRangeId: number}): Promise { - const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`; - const body = { - location: { - id: location.id, - type: "location" + makeFetch("DELETE", url).then(() => { + ctx.commit("removeRange", calendarRangeId); + }); }, - } as CalendarRangeEdit; + patchRangeTime( + ctx, + { + calendarRangeId, + start, + end, + }: { calendarRangeId: number; start: Date; end: Date }, + ): Promise { + const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`; + const body = { + startDate: { + datetime: datetimeToISO(start), + }, + endDate: { + datetime: datetimeToISO(end), + }, + } as CalendarRangeEdit; - return makeFetch('PATCH', url, body) - .then((range) => { - ctx.commit('updateRange', range); - return Promise.resolve(null); - }) - .catch((error) => { - console.error(error); - return Promise.resolve(null); - }) + return makeFetch( + "PATCH", + url, + body, + ) + .then((range) => { + ctx.commit("updateRange", range); + return Promise.resolve(null); + }) + .catch((error) => { + console.error(error); + return Promise.resolve(null); + }); + }, + patchRangeLocation( + ctx, + { + location, + calendarRangeId, + }: { location: Location; calendarRangeId: number }, + ): Promise { + const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`; + const body = { + location: { + id: location.id, + type: "location", + }, + } as CalendarRangeEdit; + + return makeFetch( + "PATCH", + url, + body, + ) + .then((range) => { + ctx.commit("updateRange", range); + return Promise.resolve(null); + }) + .catch((error) => { + console.error(error); + return Promise.resolve(null); + }); + }, + copyFromDayToAnotherDay( + ctx, + { from, to }: { from: Date; to: Date }, + ): Promise { + const rangesToCopy: EventInputCalendarRange[] = + ctx.getters["getRangesOnDate"](from); + const promises = []; + + for (const r of rangesToCopy) { + const start = new Date(ISOToDatetime(r.start) as Date); + start.setFullYear( + to.getFullYear(), + to.getMonth(), + to.getDate(), + ); + const end = new Date(ISOToDatetime(r.end) as Date); + end.setFullYear(to.getFullYear(), to.getMonth(), to.getDate()); + const location = ctx.rootGetters["locations/getLocationById"]( + r.locationId, + ); + + promises.push( + ctx.dispatch("createRange", { start, end, location }), + ); + } + + return Promise.all(promises).then(() => Promise.resolve(null)); + }, + copyFromWeekToAnotherWeek( + ctx: Context, + { fromMonday, toMonday }: { fromMonday: Date; toMonday: Date }, + ): Promise { + const rangesToCopy: EventInputCalendarRange[] = + ctx.getters["getRangesOnWeek"](fromMonday); + const promises = []; + const diffTime = toMonday.getTime() - fromMonday.getTime(); + for (const r of rangesToCopy) { + const start = new Date(ISOToDatetime(r.start) as Date); + const end = new Date(ISOToDatetime(r.end) as Date); + start.setTime(start.getTime() + diffTime); + end.setTime(end.getTime() + diffTime); + const location = ctx.rootGetters["locations/getLocationById"]( + r.locationId, + ); + + promises.push( + ctx.dispatch("createRange", { start, end, location }), + ); + } + + return Promise.all(promises).then(() => Promise.resolve(null)); + }, }, - copyFromDayToAnotherDay(ctx, {from, to}: {from: Date, to: Date}): Promise { - const rangesToCopy: EventInputCalendarRange[] = ctx.getters['getRangesOnDate'](from); - const promises = []; - - for (let r of rangesToCopy) { - let start = new Date(ISOToDatetime(r.start)); - start.setFullYear(to.getFullYear(), to.getMonth(), to.getDate()) - let end = new Date(ISOToDatetime(r.end)); - end.setFullYear(to.getFullYear(), to.getMonth(), to.getDate()); - let location = ctx.rootGetters['locations/getLocationById'](r.locationId); - - promises.push(ctx.dispatch('createRange', {start, end, location})); - } - - return Promise.all(promises).then(_ => Promise.resolve(null)); - } - } -}; +} as Module; diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarRemotes.ts b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarRemotes.ts index a92e1f9b3..bd2bf20f8 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarRemotes.ts +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/calendarRemotes.ts @@ -1,95 +1,116 @@ -import {State} from './../index'; -import {ActionContext, Module} from 'vuex'; -import {CalendarRemote} from '../../../../types'; -import {fetchCalendarRemoteForUser} from '../../../Calendar/api'; -import {EventInput} from '@fullcalendar/core'; -import {remoteToFullCalendarEvent} from "../../../Calendar/store/utils"; -import {TransportExceptionInterface} from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods"; -import {COLORS} from "../../../Calendar/const"; +import { State } from "./../index"; +import { ActionContext, Module } from "vuex"; +import { CalendarRemote } from "../../../../types"; +import { fetchCalendarRemoteForUser } from "../../../Calendar/api"; +import { EventInput } from "@fullcalendar/core"; +import { remoteToFullCalendarEvent } from "../../../Calendar/store/utils"; +import { TransportExceptionInterface } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods"; +import { COLORS } from "../../../Calendar/const"; export interface CalendarRemotesState { - remotes: EventInput[], - remotesLoaded: {start: number, end: number}[], - remotesIndex: Set, - key: number + remotes: EventInput[]; + remotesLoaded: { start: number; end: number }[]; + remotesIndex: Set; + key: number; } type Context = ActionContext; -export default > { - namespaced: true, - state: (): CalendarRemotesState => ({ - remotes: [], - remotesLoaded: [], - remotesIndex: new Set(), - key: 0 - }), - getters: { - isRemotesLoaded: (state: CalendarRemotesState) => ({start, end}: {start: Date, end: Date}): boolean => { - for (let range of state.remotesLoaded) { - if (start.getTime() === range.start && end.getTime() === range.end) { - return true; - } - } +export default { + namespaced: true, + state: (): CalendarRemotesState => ({ + remotes: [], + remotesLoaded: [], + remotesIndex: new Set(), + key: 0, + }), + getters: { + isRemotesLoaded: + (state: CalendarRemotesState) => + ({ start, end }: { start: Date; end: Date }): boolean => { + for (const range of state.remotesLoaded) { + if ( + start.getTime() === range.start && + end.getTime() === range.end + ) { + return true; + } + } - return false; + return false; + }, }, - }, - mutations: { - addRemotes(state: CalendarRemotesState, ranges: CalendarRemote[]) { - console.log('addRemotes', ranges); + mutations: { + addRemotes(state: CalendarRemotesState, ranges: CalendarRemote[]) { + console.log("addRemotes", ranges); - const toAdd = ranges - .map(cr => remoteToFullCalendarEvent(cr)) - .filter(r => !state.remotesIndex.has(r.id)); + const toAdd = ranges + .map((cr) => remoteToFullCalendarEvent(cr)) + .filter((r) => !state.remotesIndex.has(r.id)); - toAdd.forEach((r) => { - state.remotesIndex.add(r.id); - state.remotes.push(r); - }); - state.key = state.key + toAdd.length; + toAdd.forEach((r) => { + state.remotesIndex.add(r.id); + state.remotes.push(r); + }); + state.key = state.key + toAdd.length; + }, + addLoaded( + state: CalendarRemotesState, + payload: { start: Date; end: Date }, + ) { + state.remotesLoaded.push({ + start: payload.start.getTime(), + end: payload.end.getTime(), + }); + }, }, - addLoaded(state: CalendarRemotesState, payload: {start: Date, end: Date}) { - state.remotesLoaded.push({start: payload.start.getTime(), end: payload.end.getTime()}); + actions: { + fetchRemotes( + ctx: Context, + payload: { start: Date; end: Date }, + ): Promise { + const start = payload.start; + const end = payload.end; + + if (ctx.rootGetters["me/getMe"] === null) { + return Promise.resolve(null); + } + + if (ctx.getters.isRemotesLoaded({ start, end })) { + return Promise.resolve(ctx.getters.getRangeSource); + } + + ctx.commit("addLoaded", { + start: start, + end: end, + }); + + return fetchCalendarRemoteForUser( + ctx.rootGetters["me/getMe"], + start, + end, + ) + .then((remotes: CalendarRemote[]) => { + // to be add when reactivity problem will be solve ? + //ctx.commit('addRemotes', remotes); + const inputs = remotes + .map((cr) => remoteToFullCalendarEvent(cr)) + .map((cr) => ({ + ...cr, + backgroundColor: COLORS[0], + textColor: "black", + editable: false, + })); + ctx.commit("calendarRanges/addExternals", inputs, { + root: true, + }); + return Promise.resolve(null); + }) + .catch((e: TransportExceptionInterface) => { + console.error(e); + + return Promise.resolve(null); + }); + }, }, - }, - actions: { - fetchRemotes(ctx: Context, payload: {start: Date, end: Date}): Promise { - const start = payload.start; - const end = payload.end; - - if (ctx.rootGetters['me/getMe'] === null) { - return Promise.resolve(null); - } - - if (ctx.getters.isRemotesLoaded({start, end})) { - return Promise.resolve(ctx.getters.getRangeSource); - } - - ctx.commit('addLoaded', { - start: start, - end: end, - }); - - return fetchCalendarRemoteForUser( - ctx.rootGetters['me/getMe'], - start, - end - ) - .then((remotes: CalendarRemote[]) => { - // to be add when reactivity problem will be solve ? - //ctx.commit('addRemotes', remotes); - const inputs = remotes - .map(cr => remoteToFullCalendarEvent(cr)) - .map(cr => ({...cr, backgroundColor: COLORS[0], textColor: 'black', editable: false})) - ctx.commit('calendarRanges/addExternals', inputs, {root: true}); - return Promise.resolve(null); - }) - .catch((e: TransportExceptionInterface) => { - console.error(e); - - return Promise.resolve(null); - }); - } - } -}; +} as Module; diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts index de379753f..27ef8b8d5 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts @@ -1,56 +1,78 @@ -import {State} from './../index'; -import {ActionContext} from 'vuex'; +import { State } from "./../index"; +import { ActionContext } from "vuex"; export interface FullCalendarState { - currentView: { - start: Date|null, - end: Date|null, - }, - key: number + currentView: { + start: Date | null; + end: Date | null; + }; + key: number; } type Context = ActionContext; export default { - namespaced: true, - state: (): FullCalendarState => ({ - currentView: { - start: null, - end: null, + namespaced: true, + state: (): FullCalendarState => ({ + currentView: { + start: null, + end: null, + }, + key: 0, + }), + mutations: { + setCurrentDatesView: function ( + state: FullCalendarState, + payload: { start: Date; end: Date }, + ): void { + state.currentView.start = payload.start; + state.currentView.end = payload.end; + }, + increaseKey: function (state: FullCalendarState): void { + state.key = state.key + 1; + }, }, - key: 0, - }), - mutations: { - setCurrentDatesView: function(state: FullCalendarState, payload: {start: Date, end: Date}): void { - state.currentView.start = payload.start; - state.currentView.end = payload.end; + actions: { + setCurrentDatesView( + ctx: Context, + { start, end }: { start: Date | null; end: Date | null }, + ): Promise { + console.log("dispatch setCurrentDatesView", { start, end }); + + if ( + ctx.state.currentView.start !== start || + ctx.state.currentView.end !== end + ) { + ctx.commit("setCurrentDatesView", { start, end }); + } + + if (start !== null && end !== null) { + return Promise.all([ + ctx + .dispatch( + "calendarRanges/fetchRanges", + { start, end }, + { root: true }, + ) + .then((_) => Promise.resolve(null)), + ctx + .dispatch( + "calendarRemotes/fetchRemotes", + { start, end }, + { root: true }, + ) + .then((_) => Promise.resolve(null)), + ctx + .dispatch( + "calendarLocals/fetchLocals", + { start, end }, + { root: true }, + ) + .then((_) => Promise.resolve(null)), + ]).then((_) => Promise.resolve(null)); + } else { + return Promise.resolve(null); + } + }, }, - increaseKey: function(state: FullCalendarState): void { - state.key = state.key + 1; - } - }, - actions: { - setCurrentDatesView(ctx: Context, {start, end}: {start: Date|null, end: Date|null}): Promise { - console.log('dispatch setCurrentDatesView', {start, end}); - - if (ctx.state.currentView.start !== start || ctx.state.currentView.end !== end) { - ctx.commit('setCurrentDatesView', {start, end}); - } - - if (start !== null && end !== null) { - - return Promise.all([ - ctx.dispatch('calendarRanges/fetchRanges', {start, end}, {root: true}).then(_ => Promise.resolve(null)), - ctx.dispatch('calendarRemotes/fetchRemotes', {start, end}, {root: true}).then(_ => Promise.resolve(null)), - ctx.dispatch('calendarLocals/fetchLocals', {start, end}, {root: true}).then(_ => Promise.resolve(null)) - ] - ).then(_ => Promise.resolve(null)); - - } else { - return Promise.resolve(null); - } - }, - } -} - - +}; diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/location.ts b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/location.ts index 7fbfe7fce..91c435810 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/location.ts +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/location.ts @@ -1,62 +1,65 @@ -import {Location} from "../../../../../../../ChillMainBundle/Resources/public/types"; -import {State} from '../index'; -import {Module} from 'vuex'; -import {getLocations} from "../../../../../../../ChillMainBundle/Resources/public/lib/api/locations"; -import {whereami} from "../../../../../../../ChillMainBundle/Resources/public/lib/api/user"; +import { Location } from "../../../../../../../ChillMainBundle/Resources/public/types"; +import { State } from "../index"; +import { Module } from "vuex"; +import { getLocations } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/locations"; +import { whereami } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/user"; export interface LocationState { - locations: Location[]; - locationPicked: Location | null; - currentLocation: Location | null; + locations: Location[]; + locationPicked: Location | null; + currentLocation: Location | null; } -export default >{ - namespaced: true, - state: (): LocationState => { - return { - locations: [], - locationPicked: null, - currentLocation: null, - } - }, - getters: { - getLocationById: (state) => (id: number): Location|undefined => { - return state.locations.find(l => l.id === id); +export default { + namespaced: true, + state: (): LocationState => { + return { + locations: [], + locationPicked: null, + currentLocation: null, + }; }, - }, - mutations: { - setLocations(state, locations): void { - state.locations = locations; + getters: { + getLocationById: + (state) => + (id: number): Location | undefined => { + return state.locations.find((l) => l.id === id); + }, }, - setLocationPicked(state, location: Location | null): void { - if (null === location) { - state.locationPicked = null; - return; - } + mutations: { + setLocations(state, locations): void { + state.locations = locations; + }, + setLocationPicked(state, location: Location | null): void { + if (null === location) { + state.locationPicked = null; + return; + } - state.locationPicked = state.locations.find(l => l.id === location.id) || null; - }, - setCurrentLocation(state, location: Location | null): void { - if (null === location) { - state.currentLocation = null; - return; - } + state.locationPicked = + state.locations.find((l) => l.id === location.id) || null; + }, + setCurrentLocation(state, location: Location | null): void { + if (null === location) { + state.currentLocation = null; + return; + } - state.currentLocation = state.locations.find(l => l.id === location.id) || null; - } - }, - actions: { - getLocations(ctx): Promise { - return getLocations().then(locations => { - ctx.commit('setLocations', locations); - return Promise.resolve(); - }); + state.currentLocation = + state.locations.find((l) => l.id === location.id) || null; + }, }, - getCurrentLocation(ctx): Promise { - return whereami().then(location => { - ctx.commit('setCurrentLocation', location); - }) - } - } -} - + actions: { + getLocations(ctx): Promise { + return getLocations().then((locations) => { + ctx.commit("setLocations", locations); + return Promise.resolve(); + }); + }, + getCurrentLocation(ctx): Promise { + return whereami().then((location) => { + ctx.commit("setCurrentLocation", location); + }); + }, + }, +} as Module; diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/me.ts b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/me.ts index 8610b76d3..4f83d0185 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/me.ts +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/me.ts @@ -1,29 +1,26 @@ -import {State} from './../index'; -import {User} from '../../../../../../../ChillMainBundle/Resources/public/types'; -import {ActionContext} from 'vuex'; +import { State } from "./../index"; +import { User } from "../../../../../../../ChillMainBundle/Resources/public/types"; +import { ActionContext } from "vuex"; export interface MeState { - me: User|null, + me: User | null; } type Context = ActionContext; export default { - namespaced: true, - state: (): MeState => ({ - me: null, - }), - getters: { - getMe: function(state: MeState): User|null { - return state.me; + namespaced: true, + state: (): MeState => ({ + me: null, + }), + getters: { + getMe: function (state: MeState): User | null { + return state.me; + }, }, - }, - mutations: { - setWhoAmi(state: MeState, me: User) { - state.me = me; + mutations: { + setWhoAmi(state: MeState, me: User) { + state.me = me; + }, }, - } }; - - - diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_api/api.js b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_api/api.js index b2bee8afe..5b54044fe 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_api/api.js +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_api/api.js @@ -1,103 +1,111 @@ /* -* Endpoint chill_api_single_calendar_range -* method GET, get Calendar ranges -* @returns {Promise} a promise containing all Calendar ranges objects -*/ + * Endpoint chill_api_single_calendar_range + * method GET, get Calendar ranges + * @returns {Promise} a promise containing all Calendar ranges objects + */ const fetchCalendarRanges = () => { return Promise.resolve([]); - const url = `/api/1.0/calendar/calendar-range-available.json`; - return fetch(url) - .then(response => { - if (response.ok) { return response.json(); } - throw Error('Error with request resource response'); - }); + const url = `/api/1.0/calendar/calendar-range-available.json`; + return fetch(url).then((response) => { + if (response.ok) { + return response.json(); + } + throw Error("Error with request resource response"); + }); }; const fetchCalendarRangesByUser = (userId) => { return Promise.resolve([]); - const url = `/api/1.0/calendar/calendar-range-available.json?user=${userId}`; - return fetch(url) - .then(response => { - if (response.ok) { return response.json(); } - throw Error('Error with request resource response'); - }); + const url = `/api/1.0/calendar/calendar-range-available.json?user=${userId}`; + return fetch(url).then((response) => { + if (response.ok) { + return response.json(); + } + throw Error("Error with request resource response"); + }); }; /* -* Endpoint chill_api_single_calendar -* method GET, get Calendar events, can be filtered by mainUser -* @returns {Promise} a promise containing all Calendar objects -*/ + * Endpoint chill_api_single_calendar + * method GET, get Calendar events, can be filtered by mainUser + * @returns {Promise} a promise containing all Calendar objects + */ const fetchCalendar = (mainUserId) => { return Promise.resolve([]); - const url = `/api/1.0/calendar/calendar.json?main_user=${mainUserId}&item_per_page=1000`; - return fetch(url) - .then(response => { - if (response.ok) { return response.json(); } - throw Error('Error with request resource response'); - }); + const url = `/api/1.0/calendar/calendar.json?main_user=${mainUserId}&item_per_page=1000`; + return fetch(url).then((response) => { + if (response.ok) { + return response.json(); + } + throw Error("Error with request resource response"); + }); }; - /* -* Endpoint chill_api_single_calendar_range__entity_create -* method POST, post CalendarRange entity -*/ + * Endpoint chill_api_single_calendar_range__entity_create + * method POST, post CalendarRange entity + */ const postCalendarRange = (body) => { - const url = `/api/1.0/calendar/calendar-range.json?`; - return fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - body: JSON.stringify(body) - }).then(response => { - if (response.ok) { return response.json(); } - throw Error('Error with request resource response'); - }); + const url = `/api/1.0/calendar/calendar-range.json?`; + return fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json;charset=utf-8", + }, + body: JSON.stringify(body), + }).then((response) => { + if (response.ok) { + return response.json(); + } + throw Error("Error with request resource response"); + }); }; /* -* Endpoint chill_api_single_calendar_range__entity -* method PATCH, patch CalendarRange entity -*/ + * Endpoint chill_api_single_calendar_range__entity + * method PATCH, patch CalendarRange entity + */ const patchCalendarRange = (id, body) => { - console.log(body) - const url = `/api/1.0/calendar/calendar-range/${id}.json`; - return fetch(url, { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - body: JSON.stringify(body) - }).then(response => { - if (response.ok) { return response.json(); } - throw Error('Error with request resource response'); - }); + console.log(body); + const url = `/api/1.0/calendar/calendar-range/${id}.json`; + return fetch(url, { + method: "PATCH", + headers: { + "Content-Type": "application/json;charset=utf-8", + }, + body: JSON.stringify(body), + }).then((response) => { + if (response.ok) { + return response.json(); + } + throw Error("Error with request resource response"); + }); }; /* -* Endpoint chill_api_single_calendar_range__entity -* method DELETE, delete CalendarRange entity -*/ + * Endpoint chill_api_single_calendar_range__entity + * method DELETE, delete CalendarRange entity + */ const deleteCalendarRange = (id) => { - const url = `/api/1.0/calendar/calendar-range/${id}.json`; - return fetch(url, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - }).then(response => { - if (response.ok) { return response.json(); } - throw Error('Error with request resource response'); - }); + const url = `/api/1.0/calendar/calendar-range/${id}.json`; + return fetch(url, { + method: "DELETE", + headers: { + "Content-Type": "application/json;charset=utf-8", + }, + }).then((response) => { + if (response.ok) { + return response.json(); + } + throw Error("Error with request resource response"); + }); }; export { - fetchCalendarRanges, - fetchCalendar, - fetchCalendarRangesByUser, - postCalendarRange, - patchCalendarRange, - deleteCalendarRange + fetchCalendarRanges, + fetchCalendar, + fetchCalendarRangesByUser, + postCalendarRange, + patchCalendarRange, + deleteCalendarRange, }; diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue index 494fde96c..97d4c6f53 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue @@ -1,206 +1,259 @@ - diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/js/i18n.js b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/js/i18n.js index 2f7d15662..38e06c15c 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/js/i18n.js +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/js/i18n.js @@ -1,17 +1,14 @@ -import { multiSelectMessages } from 'ChillMainAssets/vuejs/_js/i18n' +import { multiSelectMessages } from "ChillMainAssets/vuejs/_js/i18n"; const calendarUserSelectorMessages = { fr: { choose_your_calendar_user: "Afficher les plages de disponibilités", select_user: "Sélectionnez des calendriers", show_my_calendar: "Afficher mon calendrier", - show_weekends: "Afficher les week-ends" - } + show_weekends: "Afficher les week-ends", + }, }; - + Object.assign(calendarUserSelectorMessages.fr, multiSelectMessages.fr); -export { - calendarUserSelectorMessages -}; - \ No newline at end of file +export { calendarUserSelectorMessages }; diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig index 0ca2f6b73..cff5c00cc 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig +++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig @@ -55,7 +55,7 @@
    {% if calendar.mainUser is not empty %} - {{ calendar.mainUser|chill_entity_render_box }} + {{ calendar.mainUser|chill_entity_render_box({'at_date': calendar.startDate}) }} {% endif %}
@@ -132,7 +132,7 @@
  • {{ 'Created by'|trans }} - {{ calendar.activity.createdBy|chill_entity_render_string }}, {{ 'on'|trans }} {{ calendar.activity.createdAt|format_datetime('short', 'short') }} + {{ calendar.activity.createdBy|chill_entity_render_string({'at_date': calendar.activity.createdAt}) }}, {{ 'on'|trans }} {{ calendar.activity.createdAt|format_datetime('short', 'short') }}
  • {% if is_granted('CHILL_ACTIVITY_SEE', calendar.activity) %} diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/GenericDoc/calendar_document.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/GenericDoc/calendar_document.html.twig index facf5be50..f35a25a73 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/views/GenericDoc/calendar_document.html.twig +++ b/src/Bundle/ChillCalendarBundle/Resources/views/GenericDoc/calendar_document.html.twig @@ -5,71 +5,5 @@ {% set c = document.calendar %}
    diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/GenericDoc/calendar_document_row.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/GenericDoc/calendar_document_row.html.twig new file mode 100644 index 000000000..8015ac600 --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Resources/views/GenericDoc/calendar_document_row.html.twig @@ -0,0 +1,73 @@ +{% import "@ChillDocStore/Macro/macro.html.twig" as m %} +{% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %} +{% import '@ChillPerson/Macro/updatedBy.html.twig' as mmm %} + +{% set c = document.calendar %} + + +
    +
    +
    + {% if document.storedObject.isPending %} +
    {{ 'docgen.Doc generation is pending'|trans }}
    + {% elseif document.storedObject.isFailure %} +
    {{ 'docgen.Doc generation failed'|trans }}
    + {% endif %} + +
    + + + {{ 'Calendar'|trans }} + {% if c.endDate.diff(c.startDate).days >= 1 %} + {{ c.startDate|format_datetime('short', 'short') }} + - {{ c.endDate|format_datetime('short', 'short') }} + {% else %} + {{ c.startDate|format_datetime('short', 'short') }} + - {{ c.endDate|format_datetime('none', 'short') }} + {% endif %} + +
    + +
    + {{ document.storedObject.title|chill_print_or_message("No title") }} +
    + {% if document.storedObject.hasTemplate %} +
    +

    {{ document.storedObject.template.name|localize_translatable_string }}

    +
    + {% endif %} +
    +
    +
    + {{ document.storedObject.createdAt|format_date('short') }} +
    + {% if c.accompanyingPeriod is not null and context == 'person' %} +
    + + {{ c.accompanyingPeriod.id }} +   +
    + {% endif %} +
    +
    +
    + +{% if show_actions %} +
    +
    + {{ mmm.createdBy(document) }} +
    +
      + {% if is_granted('CHILL_CALENDAR_DOC_SEE', document) %} +
    • + {{ document.storedObject|chill_document_button_group(document.storedObject.title, is_granted('CHILL_CALENDAR_CALENDAR_EDIT', c)) }} +
    • + {% endif %} + {% if is_granted('CHILL_CALENDAR_CALENDAR_EDIT', c) %} +
    • + +
    • + {% endif %} +
    +
    +{% endif %} diff --git a/src/Bundle/ChillCalendarBundle/Security/Voter/CalendarVoter.php b/src/Bundle/ChillCalendarBundle/Security/Voter/CalendarVoter.php index 8f4c3a065..b7aae9349 100644 --- a/src/Bundle/ChillCalendarBundle/Security/Voter/CalendarVoter.php +++ b/src/Bundle/ChillCalendarBundle/Security/Voter/CalendarVoter.php @@ -89,7 +89,7 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn switch ($attribute) { case self::SEE: case self::CREATE: - if (AccompanyingPeriod::STEP_DRAFT === $subject->getStep()) { + if (AccompanyingPeriod::STEP_DRAFT === $subject->getStep() || AccompanyingPeriod::STEP_CLOSED === $subject->getStep()) { return false; } diff --git a/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php b/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php index 087f5ea86..c45acd41f 100644 --- a/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php +++ b/src/Bundle/ChillCalendarBundle/Service/DocGenerator/CalendarContext.php @@ -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 diff --git a/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Normalizer/AccompanyingPeriodCalendarGenericDocNormalizer.php b/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Normalizer/AccompanyingPeriodCalendarGenericDocNormalizer.php new file mode 100644 index 000000000..61a6e1370 --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Normalizer/AccompanyingPeriodCalendarGenericDocNormalizer.php @@ -0,0 +1,51 @@ +key && 'json' === $format; + } + + public function normalize(GenericDocDTO $genericDocDTO, string $format, array $context = []): array + { + if (null === $calendarDoc = $this->calendarDocRepository->find($genericDocDTO->identifiers['id'])) { + return ['title' => $this->translator->trans('generic_doc.document removed'), 'isPresent' => false]; + } + + return [ + 'isPresent' => true, + 'title' => $calendarDoc->getStoredObject()->getTitle(), + 'html' => $this->twig->render( + $this->renderer->getTemplate($genericDocDTO, ['show-actions' => false, 'row-only' => true]), + $this->renderer->getTemplateData($genericDocDTO, ['show-actions' => false, 'row-only' => true]) + ), + ]; + } +} diff --git a/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Providers/AccompanyingPeriodCalendarGenericDocProvider.php b/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Providers/AccompanyingPeriodCalendarGenericDocProvider.php index 7d4544b37..fe71f18a2 100644 --- a/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Providers/AccompanyingPeriodCalendarGenericDocProvider.php +++ b/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Providers/AccompanyingPeriodCalendarGenericDocProvider.php @@ -13,10 +13,12 @@ namespace Chill\CalendarBundle\Service\GenericDoc\Providers; use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Entity\CalendarDoc; +use Chill\CalendarBundle\Repository\CalendarDocRepositoryInterface; use Chill\CalendarBundle\Security\Voter\CalendarVoter; use Chill\DocStoreBundle\Entity\StoredObject; use Chill\DocStoreBundle\GenericDoc\FetchQuery; use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface; +use Chill\DocStoreBundle\GenericDoc\GenericDocDTO; use Chill\DocStoreBundle\GenericDoc\GenericDocForAccompanyingPeriodProviderInterface; use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; @@ -37,9 +39,39 @@ final readonly class AccompanyingPeriodCalendarGenericDocProvider implements Gen public function __construct( private Security $security, - private EntityManagerInterface $em + private EntityManagerInterface $em, + private CalendarDocRepositoryInterface $calendarRepository, ) {} + public function fetchAssociatedStoredObject(GenericDocDTO $genericDocDTO): ?StoredObject + { + return $this->calendarRepository->find($genericDocDTO->identifiers['id'])?->getStoredObject(); + } + + public function supportsGenericDoc(GenericDocDTO $genericDocDTO): bool + { + return $this->supportsKeyAndIdentifiers($genericDocDTO->key, $genericDocDTO->identifiers); + } + + public function supportsKeyAndIdentifiers(string $key, array $identifiers): bool + { + return self::KEY === $key && array_key_exists('id', $identifiers); + } + + public function buildOneGenericDoc(string $key, array $identifiers): ?GenericDocDTO + { + if (null === $calendarDoc = $this->calendarRepository->find($identifiers['id'])) { + return null; + } + + return new GenericDocDTO( + self::KEY, + $identifiers, + \DateTimeImmutable::createFromInterface($calendarDoc->getCreatedAt() ?? new \DateTimeImmutable('now')), + $calendarDoc->getCalendar()->getAccompanyingPeriod() ?? $calendarDoc->getCalendar()->getPerson() + ); + } + /** * @throws MappingException */ @@ -82,7 +114,7 @@ final readonly class AccompanyingPeriodCalendarGenericDocProvider implements Gen [Types::INTEGER] ); - return $query; + return $this->addWhereClausesToQuery($query, $startDate, $endDate, $content); } public function isAllowedForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): bool diff --git a/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Providers/PersonCalendarGenericDocProvider.php b/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Providers/PersonCalendarGenericDocProvider.php index 5ca52744b..176f7978b 100644 --- a/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Providers/PersonCalendarGenericDocProvider.php +++ b/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Providers/PersonCalendarGenericDocProvider.php @@ -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 diff --git a/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Renderers/AccompanyingPeriodCalendarGenericDocRenderer.php b/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Renderers/AccompanyingPeriodCalendarGenericDocRenderer.php index 123afc164..217578be4 100644 --- a/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Renderers/AccompanyingPeriodCalendarGenericDocRenderer.php +++ b/src/Bundle/ChillCalendarBundle/Service/GenericDoc/Renderers/AccompanyingPeriodCalendarGenericDocRenderer.php @@ -17,6 +17,9 @@ use Chill\CalendarBundle\Service\GenericDoc\Providers\PersonCalendarGenericDocPr use Chill\DocStoreBundle\GenericDoc\GenericDocDTO; use Chill\DocStoreBundle\GenericDoc\Twig\GenericDocRendererInterface; +/** + * @implements GenericDocRendererInterface + */ final readonly class AccompanyingPeriodCalendarGenericDocRenderer implements GenericDocRendererInterface { public function __construct(private CalendarDocRepository $repository) {} @@ -28,7 +31,8 @@ final readonly class AccompanyingPeriodCalendarGenericDocRenderer implements Gen public function getTemplate(GenericDocDTO $genericDocDTO, $options = []): string { - return '@ChillCalendar/GenericDoc/calendar_document.html.twig'; + return $options['row-only'] ?? false ? '@ChillCalendar/GenericDoc/calendar_document_row.html.twig' + : '@ChillCalendar/GenericDoc/calendar_document.html.twig'; } public function getTemplateData(GenericDocDTO $genericDocDTO, $options = []): array @@ -36,6 +40,7 @@ final readonly class AccompanyingPeriodCalendarGenericDocRenderer implements Gen return [ 'document' => $this->repository->find($genericDocDTO->identifiers['id']), 'context' => $genericDocDTO->getContext(), + 'show_actions' => $options['show-actions'] ?? true, ]; } } diff --git a/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/BulkCalendarShortMessageSender.php b/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/BulkCalendarShortMessageSender.php index 9a4a92a94..7053d905a 100644 --- a/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/BulkCalendarShortMessageSender.php +++ b/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/BulkCalendarShortMessageSender.php @@ -21,11 +21,17 @@ namespace Chill\CalendarBundle\Service\ShortMessageNotification; use Chill\CalendarBundle\Entity\Calendar; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; -use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Notifier\TexterInterface; class BulkCalendarShortMessageSender { - public function __construct(private readonly CalendarForShortMessageProvider $provider, private readonly EntityManagerInterface $em, private readonly LoggerInterface $logger, private readonly MessageBusInterface $messageBus, private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder) {} + public function __construct( + private readonly CalendarForShortMessageProvider $provider, + private readonly EntityManagerInterface $em, + private readonly LoggerInterface $logger, + private readonly TexterInterface $texter, + private readonly ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder, + ) {} public function sendBulkMessageToEligibleCalendars() { @@ -36,7 +42,7 @@ class BulkCalendarShortMessageSender $smses = $this->messageForCalendarBuilder->buildMessageForCalendar($calendar); foreach ($smses as $sms) { - $this->messageBus->dispatch($sms); + $this->texter->send($sms); ++$countSms; } diff --git a/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/DefaultRangeGenerator.php b/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/DefaultRangeGenerator.php index b79118cf0..11e43139c 100644 --- a/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/DefaultRangeGenerator.php +++ b/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/DefaultRangeGenerator.php @@ -18,8 +18,6 @@ declare(strict_types=1); namespace Chill\CalendarBundle\Service\ShortMessageNotification; -use Monolog\DateTimeImmutable; - /** * * Lundi => Envoi des rdv du mardi et mercredi. * * Mardi => Envoi des rdv du jeudi. @@ -31,7 +29,7 @@ class DefaultRangeGenerator implements RangeGeneratorInterface { public function generateRange(\DateTimeImmutable $date): ?array { - $onMidnight = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $date->format('Y-m-d').' 00:00:00'); + $onMidnight = \DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $date->format('Y-m-d').' 00:00:00'); switch ($dow = (int) $onMidnight->format('w')) { case 6: // Saturday diff --git a/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/DefaultShortMessageForCalendarBuilder.php b/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/DefaultShortMessageForCalendarBuilder.php index 880c9950f..dfce0548c 100644 --- a/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/DefaultShortMessageForCalendarBuilder.php +++ b/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/DefaultShortMessageForCalendarBuilder.php @@ -19,12 +19,26 @@ declare(strict_types=1); namespace Chill\CalendarBundle\Service\ShortMessageNotification; use Chill\CalendarBundle\Entity\Calendar; -use Chill\MainBundle\Service\ShortMessage\ShortMessage; +use libphonenumber\PhoneNumberFormat; +use libphonenumber\PhoneNumberUtil; +use Symfony\Component\Notifier\Message\SmsMessage; class DefaultShortMessageForCalendarBuilder implements ShortMessageForCalendarBuilderInterface { - public function __construct(private readonly \Twig\Environment $engine) {} + private readonly PhoneNumberUtil $phoneUtil; + public function __construct(private readonly \Twig\Environment $engine) + { + $this->phoneUtil = PhoneNumberUtil::getInstance(); + } + + /** + * @return list + * + * @throws \Twig\Error\LoaderError + * @throws \Twig\Error\RuntimeError + * @throws \Twig\Error\SyntaxError + */ public function buildMessageForCalendar(Calendar $calendar): array { if (true !== $calendar->getSendSMS()) { @@ -39,16 +53,14 @@ class DefaultShortMessageForCalendarBuilder implements ShortMessageForCalendarBu } if (Calendar::SMS_PENDING === $calendar->getSmsStatus()) { - $toUsers[] = new ShortMessage( + $toUsers[] = new SmsMessage( + $this->phoneUtil->format($person->getMobilenumber(), PhoneNumberFormat::E164), $this->engine->render('@ChillCalendar/CalendarShortMessage/short_message.txt.twig', ['calendar' => $calendar]), - $person->getMobilenumber(), - ShortMessage::PRIORITY_LOW ); } elseif (Calendar::SMS_CANCEL_PENDING === $calendar->getSmsStatus()) { - $toUsers[] = new ShortMessage( + $toUsers[] = new SmsMessage( + $this->phoneUtil->format($person->getMobilenumber(), PhoneNumberFormat::E164), $this->engine->render('@ChillCalendar/CalendarShortMessage/short_message_canceled.txt.twig', ['calendar' => $calendar]), - $person->getMobilenumber(), - ShortMessage::PRIORITY_LOW ); } } diff --git a/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/ShortMessageForCalendarBuilderInterface.php b/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/ShortMessageForCalendarBuilderInterface.php index 65856a437..6aa21247d 100644 --- a/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/ShortMessageForCalendarBuilderInterface.php +++ b/src/Bundle/ChillCalendarBundle/Service/ShortMessageNotification/ShortMessageForCalendarBuilderInterface.php @@ -19,12 +19,12 @@ declare(strict_types=1); namespace Chill\CalendarBundle\Service\ShortMessageNotification; use Chill\CalendarBundle\Entity\Calendar; -use Chill\MainBundle\Service\ShortMessage\ShortMessage; +use Symfony\Component\Notifier\Message\SmsMessage; interface ShortMessageForCalendarBuilderInterface { /** - * @return array|ShortMessage[] + * @return list */ public function buildMessageForCalendar(Calendar $calendar): array; } diff --git a/src/Bundle/ChillCalendarBundle/Tests/Form/CalendarTypeTest.php b/src/Bundle/ChillCalendarBundle/Tests/Form/CalendarTypeTest.php index 0d1202b67..83366c848 100644 --- a/src/Bundle/ChillCalendarBundle/Tests/Form/CalendarTypeTest.php +++ b/src/Bundle/ChillCalendarBundle/Tests/Form/CalendarTypeTest.php @@ -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')) diff --git a/src/Bundle/ChillCalendarBundle/Tests/Service/DocGenerator/CalendarContextTest.php b/src/Bundle/ChillCalendarBundle/Tests/Service/DocGenerator/CalendarContextTest.php index 4a4b4f7d4..8674a6b44 100644 --- a/src/Bundle/ChillCalendarBundle/Tests/Service/DocGenerator/CalendarContextTest.php +++ b/src/Bundle/ChillCalendarBundle/Tests/Service/DocGenerator/CalendarContextTest.php @@ -47,7 +47,7 @@ final class CalendarContextTest extends TestCase { $expected = [ - 'track_datetime' => true, + 'trackDatetime' => true, 'askMainPerson' => true, 'mainPersonLabel' => 'docgen.calendar.Destinee', 'askThirdParty' => false, @@ -61,7 +61,7 @@ final class CalendarContextTest extends TestCase { $expected = [ - 'track_datetime' => true, + 'trackDatetime' => true, 'askMainPerson' => true, 'mainPersonLabel' => 'docgen.calendar.Destinee', 'askThirdParty' => false, @@ -203,7 +203,7 @@ final class CalendarContextTest extends TestCase private function buildCalendarContext( ?EntityManagerInterface $entityManager = null, - ?NormalizerInterface $normalizer = null + ?NormalizerInterface $normalizer = null, ): CalendarContext { $baseContext = $this->prophesize(BaseContextData::class); $baseContext->getData(null)->willReturn(['base_context' => 'data']); diff --git a/src/Bundle/ChillCalendarBundle/Tests/Service/ShortMessageNotification/BulkCalendarShortMessageSenderTest.php b/src/Bundle/ChillCalendarBundle/Tests/Service/ShortMessageNotification/BulkCalendarShortMessageSenderTest.php index 09c9ddf96..dd86ab16b 100644 --- a/src/Bundle/ChillCalendarBundle/Tests/Service/ShortMessageNotification/BulkCalendarShortMessageSenderTest.php +++ b/src/Bundle/ChillCalendarBundle/Tests/Service/ShortMessageNotification/BulkCalendarShortMessageSenderTest.php @@ -23,17 +23,16 @@ use Chill\CalendarBundle\Service\ShortMessageNotification\BulkCalendarShortMessa use Chill\CalendarBundle\Service\ShortMessageNotification\CalendarForShortMessageProvider; use Chill\CalendarBundle\Service\ShortMessageNotification\ShortMessageForCalendarBuilderInterface; use Chill\MainBundle\Entity\User; -use Chill\MainBundle\Service\ShortMessage\ShortMessage; use Chill\MainBundle\Test\PrepareUserTrait; use Chill\PersonBundle\DataFixtures\Helper\PersonRandomHelper; use Doctrine\ORM\EntityManagerInterface; -use libphonenumber\PhoneNumberUtil; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Psr\Log\NullLogger; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; -use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Notifier\Message\SentMessage; +use Symfony\Component\Notifier\Message\SmsMessage; +use Symfony\Component\Notifier\TexterInterface; /** * @internal @@ -101,24 +100,23 @@ final class BulkCalendarShortMessageSenderTest extends KernelTestCase $messageBuilder->buildMessageForCalendar(Argument::type(Calendar::class)) ->willReturn( [ - new ShortMessage( + new SmsMessage( + '+32470123456', 'content', - PhoneNumberUtil::getInstance()->parse('+32470123456', 'BE'), - ShortMessage::PRIORITY_MEDIUM ), ] ); - $bus = $this->prophesize(MessageBusInterface::class); - $bus->dispatch(Argument::type(ShortMessage::class)) - ->willReturn(new Envelope(new \stdClass())) + $texter = $this->prophesize(TexterInterface::class); + $texter->send(Argument::type(SmsMessage::class)) + ->will(fn ($args): SentMessage => new SentMessage($args[0], 'sms')) ->shouldBeCalledTimes(1); $bulk = new BulkCalendarShortMessageSender( $provider->reveal(), $em, new NullLogger(), - $bus->reveal(), + $texter->reveal(), $messageBuilder->reveal() ); diff --git a/src/Bundle/ChillCalendarBundle/Tests/Service/ShortMessageNotification/DefaultShortMessageForCalendarBuilderTest.php b/src/Bundle/ChillCalendarBundle/Tests/Service/ShortMessageNotification/DefaultShortMessageForCalendarBuilderTest.php index 222d3a451..f5b9c8fdd 100644 --- a/src/Bundle/ChillCalendarBundle/Tests/Service/ShortMessageNotification/DefaultShortMessageForCalendarBuilderTest.php +++ b/src/Bundle/ChillCalendarBundle/Tests/Service/ShortMessageNotification/DefaultShortMessageForCalendarBuilderTest.php @@ -23,7 +23,6 @@ use Chill\CalendarBundle\Service\ShortMessageNotification\DefaultShortMessageFor use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\User; use Chill\PersonBundle\Entity\Person; -use libphonenumber\PhoneNumberFormat; use libphonenumber\PhoneNumberUtil; use PHPUnit\Framework\TestCase; use Prophecy\Argument; @@ -90,10 +89,9 @@ final class DefaultShortMessageForCalendarBuilderTest extends TestCase $this->assertCount(1, $sms); $this->assertEquals( '+32470123456', - $this->phoneNumberUtil->format($sms[0]->getPhoneNumber(), PhoneNumberFormat::E164) + $sms[0]->getPhone() ); - $this->assertEquals('message content', $sms[0]->getContent()); - $this->assertEquals('low', $sms[0]->getPriority()); + $this->assertEquals('message content', $sms[0]->getSubject()); // if the calendar is canceled $calendar @@ -105,9 +103,8 @@ final class DefaultShortMessageForCalendarBuilderTest extends TestCase $this->assertCount(1, $sms); $this->assertEquals( '+32470123456', - $this->phoneNumberUtil->format($sms[0]->getPhoneNumber(), PhoneNumberFormat::E164) + $sms[0]->getRecipientId(), ); - $this->assertEquals('message canceled', $sms[0]->getContent()); - $this->assertEquals('low', $sms[0]->getPriority()); + $this->assertEquals('message canceled', $sms[0]->getSubject()); } } diff --git a/src/Bundle/ChillCalendarBundle/chill.webpack.config.js b/src/Bundle/ChillCalendarBundle/chill.webpack.config.js index 9d45a3142..50db64135 100644 --- a/src/Bundle/ChillCalendarBundle/chill.webpack.config.js +++ b/src/Bundle/ChillCalendarBundle/chill.webpack.config.js @@ -1,14 +1,25 @@ // this file loads all assets from the Chill calendar bundle -module.exports = function(encore, entries) { +module.exports = function (encore, entries) { + entries.push(__dirname + "/Resources/public/chill/chill.js"); - entries.push(__dirname + '/Resources/public/chill/chill.js'); + encore.addAliases({ + ChillCalendarAssets: __dirname + "/Resources/public", + }); - encore.addAliases({ - ChillCalendarAssets: __dirname + '/Resources/public' - }); - - encore.addEntry('vue_calendar', __dirname + '/Resources/public/vuejs/Calendar/index.js'); - encore.addEntry('vue_mycalendarrange', __dirname + '/Resources/public/vuejs/MyCalendarRange/index2.ts'); - encore.addEntry('page_calendar', __dirname + '/Resources/public/chill/index.js'); - encore.addEntry('mod_answer', __dirname + '/Resources/public/module/Invite/answer.js'); + encore.addEntry( + "vue_calendar", + __dirname + "/Resources/public/vuejs/Calendar/index.js", + ); + encore.addEntry( + "vue_mycalendarrange", + __dirname + "/Resources/public/vuejs/MyCalendarRange/index2.ts", + ); + encore.addEntry( + "page_calendar", + __dirname + "/Resources/public/chill/index.js", + ); + encore.addEntry( + "mod_answer", + __dirname + "/Resources/public/module/Invite/answer.js", + ); }; diff --git a/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml b/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml index d157683e0..cfb2fe057 100644 --- a/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml @@ -26,6 +26,7 @@ The calendar item has been successfully removed.: Le rendez-vous a été supprim From the day: Du to the day: au Transform to activity: Transformer en échange +Create a new calendar in accompanying course: Créer un rendez-vous dans le parcours Will send SMS: Un SMS de rappel sera envoyé Will not send SMS: Aucun SMS de rappel ne sera envoyé SMS already sent: Un SMS a été envoyé diff --git a/src/Bundle/ChillCustomFieldsBundle/Command/CreateFieldsOnGroupCommand.php b/src/Bundle/ChillCustomFieldsBundle/Command/CreateFieldsOnGroupCommand.php index 24c531860..9fd474e24 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Command/CreateFieldsOnGroupCommand.php +++ b/src/Bundle/ChillCustomFieldsBundle/Command/CreateFieldsOnGroupCommand.php @@ -44,7 +44,7 @@ class CreateFieldsOnGroupCommand extends Command private readonly EntityManager $entityManager, private readonly ValidatorInterface $validator, private $availableLanguages, - private $customizablesEntities + private $customizablesEntities, ) { parent::__construct(); } diff --git a/src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php b/src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php index 5d16269e6..acf223888 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php +++ b/src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php @@ -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, ) {} /** @@ -298,7 +298,7 @@ class CustomFieldsGroupController extends AbstractController ->setCustomFieldsGroup($customFieldsGroup); $builder = $this->get('form.factory') - ->createNamedBuilder(null, FormType::class, $customfield, [ + ->createNamedBuilder('', FormType::class, $customfield, [ 'method' => 'GET', 'action' => $this->generateUrl('customfield_new'), 'csrf_protection' => false, diff --git a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php index a54e4c21f..e803742fe 100644 --- a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php +++ b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php @@ -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) diff --git a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldDate.php b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldDate.php index dac447930..4eee4e829 100644 --- a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldDate.php +++ b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldDate.php @@ -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) diff --git a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldLongChoice.php b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldLongChoice.php index 54d63b978..3a0adbdcc 100644 --- a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldLongChoice.php +++ b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldLongChoice.php @@ -42,8 +42,8 @@ class CustomFieldLongChoice extends AbstractCustomField $translatableStringHelper = $this->translatableStringHelper; $builder->add($customField->getSlug(), Select2ChoiceType::class, [ 'choices' => $entries, - 'choice_label' => static fn (Option $option) => $translatableStringHelper->localize($option->getText()), - 'choice_value' => static fn (Option $key): ?int => null === $key ? null : $key->getId(), + 'choice_label' => static fn (?Option $option) => $translatableStringHelper->localize($option->getText()), + 'choice_value' => static fn (?Option $key): ?int => $key?->getId(), 'multiple' => false, 'expanded' => false, 'required' => $customField->isRequired(), diff --git a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldNumber.php b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldNumber.php index e083b9b80..0f303fed6 100644 --- a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldNumber.php +++ b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldNumber.php @@ -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) diff --git a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldText.php b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldText.php index 17d8e95e7..00daee1c8 100644 --- a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldText.php +++ b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldText.php @@ -28,7 +28,7 @@ class CustomFieldText extends AbstractCustomField public function __construct( private readonly Environment $templating, - private readonly TranslatableStringHelper $translatableStringHelper + private readonly TranslatableStringHelper $translatableStringHelper, ) {} /** diff --git a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldTitle.php b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldTitle.php index 302e04350..75ecc0368 100644 --- a/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldTitle.php +++ b/src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldTitle.php @@ -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) diff --git a/src/Bundle/ChillCustomFieldsBundle/DataFixtures/ORM/LoadOption.php b/src/Bundle/ChillCustomFieldsBundle/DataFixtures/ORM/LoadOption.php index f95991b7c..4da28baac 100644 --- a/src/Bundle/ChillCustomFieldsBundle/DataFixtures/ORM/LoadOption.php +++ b/src/Bundle/ChillCustomFieldsBundle/DataFixtures/ORM/LoadOption.php @@ -46,12 +46,12 @@ class LoadOption extends AbstractFixture implements OrderedFixtureInterface $this->fakerNl = \Faker\Factory::create('nl_NL'); } - public function getOrder() + public function getOrder(): int { return 1000; } - public function load(ObjectManager $manager) + public function load(ObjectManager $manager): void { echo "Loading Options \n"; diff --git a/src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php b/src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php index 9fddc10b3..04cdd2308 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php +++ b/src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php @@ -48,7 +48,7 @@ class CustomField #[ORM\Column(type: \Doctrine\DBAL\Types\Types::FLOAT)] private ?float $ordering = null; - #[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN)] + #[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN, options: ['default' => false])] private false $required = false; #[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 255)] @@ -172,11 +172,9 @@ class CustomField /** * Set active. * - * @param bool $active - * * @return CustomField */ - public function setActive($active) + public function setActive(bool $active) { $this->active = $active; @@ -224,18 +222,16 @@ class CustomField /** * Set order. * - * @param float $order - * * @return CustomField */ - public function setOrdering($order) + public function setOrdering(?float $order) { $this->ordering = $order; return $this; } - public function setRequired($required) + public function setRequired(bool $required) { $this->required = $required; @@ -245,7 +241,7 @@ class CustomField /** * @return $this */ - public function setSlug($slug) + public function setSlug(?string $slug) { $this->slug = $slug; @@ -255,11 +251,9 @@ class CustomField /** * Set type. * - * @param string $type - * * @return CustomField */ - public function setType($type) + public function setType(?string $type) { $this->type = $type; diff --git a/src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldLongChoice/Option.php b/src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldLongChoice/Option.php index 497cf7150..bd98cec68 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldLongChoice/Option.php +++ b/src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldLongChoice/Option.php @@ -23,9 +23,9 @@ class Option private bool $active = true; /** - * @var Collection