mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-10-02 19:39:45 +00:00
Compare commits
273 Commits
signature-
...
manage-tra
Author | SHA1 | Date | |
---|---|---|---|
1961bf36e2 | |||
52f06e2142 | |||
6ded75119c | |||
89fa10cede | |||
b0b1a28f50 | |||
760f74b386 | |||
d82a3af063 | |||
7edd644963 | |||
077163a774 | |||
9c963a2122 | |||
c1c632dcb0 | |||
9732b80298 | |||
c602c27b39 | |||
41f13e29e0 | |||
611261c863 | |||
5921404712 | |||
23d882d4cd | |||
155066be13 | |||
a5329c5d69 | |||
547a9d1369 | |||
288a02f5b7 | |||
5dfd8daf3a | |||
a46e987f81 | |||
81220b5b22 | |||
5b0019cde7 | |||
b42473b01d | |||
be19d09bad | |||
c82991674e | |||
3fc3f32c5f | |||
20af766cdf | |||
681f637d13 | |||
fb8a6d960e | |||
a2310a662f | |||
dd7d126bec | |||
29f6a43288 | |||
74be6460d4 | |||
c8e87ced35 | |||
b8002d56ec | |||
a80b36bb31 | |||
116fe35ad2 | |||
5b95336bac | |||
f9d5ba7778 | |||
f76379551c | |||
15094d5a91 | |||
1079c7e394 | |||
bc2dfd159c | |||
b100792a34 | |||
00ceee1fd5 | |||
724b98e8c5 | |||
ead1abb825
|
|||
54d045f261
|
|||
702a5a27d2 | |||
|
41dd4d89f7 | ||
|
3a7ed7ef8f | ||
2c99ea17d4 | |||
18df08e8c3 | |||
db3961275b
|
|||
cd488d7576 | |||
436661d952 | |||
c3b8d42047 | |||
|
9c28df25a1 | ||
d88b5a0098
|
|||
|
c5a24e8ac5 | ||
|
d9c50cffb7 | ||
|
25ccb16308 | ||
|
ba25c181f5 | ||
e38d47ec5e | |||
36f2275a56 | |||
9a34064b23 | |||
4c3bfc90b5 | |||
1812e84c92 | |||
dfa7de4f38 | |||
145419a76b | |||
b1ba5cc608 | |||
87a6757e5e | |||
bd41308bbd
|
|||
f8fa96d836
|
|||
d28cec3786
|
|||
7cd36cd483
|
|||
d3d98cdec2
|
|||
49dd7f94fa
|
|||
916724c0c5
|
|||
|
102d0dad94 | ||
|
8d225dd68c | ||
|
61d0005be8 | ||
47f4cfddbb
|
|||
e95f9e9846 | |||
1f4bef754d
|
|||
19e34d5dc0
|
|||
fab00f679c
|
|||
791b3776c5
|
|||
6bd38f1a58
|
|||
68d21c9267
|
|||
e7ca89e0c1
|
|||
fc8bc33ba9
|
|||
cbd9489810
|
|||
90b615c5b2
|
|||
5ca222b501 | |||
3e4495dd6e
|
|||
bca0d04201
|
|||
f66ac50571 | |||
b454774836
|
|||
008f344e49
|
|||
90bfd87ec6
|
|||
cc0030c1cd
|
|||
d60ba3ecb2
|
|||
cd5001ac74 | |||
98f47ac512
|
|||
31b541d12f
|
|||
72045ce082
|
|||
0bfb3de465
|
|||
9ec4c77fb7
|
|||
77c53972c8
|
|||
350d991a85
|
|||
0ce9cdd07a
|
|||
1993fac1c4
|
|||
83883567a2
|
|||
29d57934a1
|
|||
f43d79c940
|
|||
be730679c8
|
|||
f62f1891d8
|
|||
ebb856fe85
|
|||
61877e0157
|
|||
4c3f082163
|
|||
635b1ee537 | |||
35109133f6
|
|||
a220dad83b
|
|||
9eb571549b
|
|||
db8257d230 | |||
bce93efe83 | |||
06401af801 | |||
ea1d4c48f2
|
|||
|
33cba27dd4 | ||
3fd6e52e9d | |||
f60a595ab6 | |||
436ef33dbc | |||
405aad7333 | |||
a5c2576124 | |||
33a6f9996e | |||
b96cbc5594 | |||
854d72fa42 | |||
cd6fd091dc | |||
cb5ade3d14 | |||
dddb6d66bc | |||
d34f9450b8 | |||
9ce1788a14 | |||
2895638f3b | |||
2708bafb1f | |||
7d309136b1 | |||
82d3ec4d6f | |||
cad2dea148 | |||
bff14aa700 | |||
66570cd430 | |||
53df2ec9ba | |||
068503a830 | |||
c07a728f1d | |||
b7e61c6747 | |||
97846a5877 | |||
4ed9d3d8e2 | |||
a7ec7c9f37 | |||
d82d534a4c | |||
684f28291a | |||
526882a5b6 | |||
422b6b99eb | |||
02b150b0a5 | |||
20c27c100c | |||
12a22bcc13 | |||
cba8a342d5 | |||
6f55ba15d6 | |||
454ab73303 | |||
800942bc92 | |||
c4e7683e48 | |||
28c986fddf | |||
adca4f0d6a | |||
7b25c8e390 | |||
0b5be0419b | |||
650e85c481 | |||
56d5d08ed3 | |||
d3390ca334 | |||
511c0af5fa | |||
4c354c47c9 | |||
d8b6cef7b4 | |||
e312929d86 | |||
20b38af812 | |||
2f07be0843 | |||
11c069a2ff | |||
3929602f59 | |||
38fcccfd83 | |||
deaab80270 | |||
a71573136a | |||
b1082f6a55 | |||
dcc285e976 | |||
ed3e0f889e | |||
63fe8070c4 | |||
8e3322f578 | |||
00756a3bde | |||
2c68224e9c | |||
c9e13be736 | |||
b9b342fe44 | |||
31f29f0bc5 | |||
0bc9fff825 | |||
25f93e8a89 | |||
4e0d8e4def | |||
1ecc825945 | |||
addc623add | |||
1b96deb4ee | |||
f510acd170 | |||
835409cb94 | |||
2121b3ef28 | |||
6c9101c167 | |||
b46883fe36 | |||
8d58805abd | |||
c3a799cb7d | |||
bc683b28d6 | |||
d91b1a70bf | |||
853014d8d2 | |||
ad6154a1e4 | |||
50c04382ef | |||
d62e9ce269 | |||
2149ef1cb4 | |||
d15fbadd27 | |||
fbbf421d8b | |||
fe695f1a14 | |||
d0ec6f9819 | |||
166d7fe0b0
|
|||
ab9d5439c1
|
|||
0737838dd6
|
|||
88bac5b5d8
|
|||
0b739fda34 | |||
9b8e143855 | |||
a533ab77ed | |||
087032881b | |||
82667a1c0f | |||
db6408926b | |||
f5c7ab6ef0 | |||
a13ada2937 | |||
3be8a39a1a | |||
d7eb1e01da | |||
bd62202d22 | |||
0e3de2ec8a | |||
aa2a398f9e | |||
33187448a0 | |||
a4482ad28b | |||
8ed5a023e8 | |||
cf1df462dc
|
|||
a0328b9d68
|
|||
813a80d6f9
|
|||
ab95bb157e
|
|||
18fd1dbc4a
|
|||
a35f7656cb
|
|||
ff05f9f48a
|
|||
482c494034
|
|||
653ac1d62b | |||
499009ac43 | |||
192b161e78 | |||
1b1f355123 | |||
39a863448c | |||
0c1a4a5f59 | |||
6f358ee1a9 | |||
0f36b9349b | |||
d18cc29acf | |||
4220d1a2d3 | |||
1ae27152c2 | |||
b946f8c10a | |||
62d6106801 | |||
89fb87f71f | |||
1337360690 | |||
9324c33caf | |||
c2dd9ef676 | |||
a42d7231d9 | |||
38deaf6f36 | |||
04fc5b6614 | |||
384b2be577 |
@@ -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"
|
|
@@ -1,6 +0,0 @@
|
|||||||
kind: Feature
|
|
||||||
body: |
|
|
||||||
Upgrade import of address list to the last version of compiled addresses of belgian-best-address
|
|
||||||
time: 2024-05-30T16:00:03.440767606+02:00
|
|
||||||
custom:
|
|
||||||
Issue: ""
|
|
@@ -1,6 +0,0 @@
|
|||||||
kind: Feature
|
|
||||||
body: |
|
|
||||||
Upgrade CKEditor and refactor configuration with use of typescript
|
|
||||||
time: 2024-05-31T19:02:42.776662753+02:00
|
|
||||||
custom:
|
|
||||||
Issue: ""
|
|
7
.changes/unreleased/Feature-20241118-150627.yaml
Normal file
7
.changes/unreleased/Feature-20241118-150627.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Feature
|
||||||
|
body: "Implementation of new translation management with one source of truth for both
|
||||||
|
twig and vue component templates using YAML files. \nDuplicate translation keys
|
||||||
|
can also be detected with new command."
|
||||||
|
time: 2024-11-18T15:06:27.929549251+01:00
|
||||||
|
custom:
|
||||||
|
Issue: ""
|
@@ -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: ""
|
|
21
.changes/v2.20.0.md
Normal file
21
.changes/v2.20.0.md
Normal file
@@ -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
|
||||||
|
|
||||||
|
```
|
3
.changes/v2.20.1.md
Normal file
3
.changes/v2.20.1.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
## v2.20.1 - 2024-06-05
|
||||||
|
### Fixed
|
||||||
|
* Do not allow StoredObjectCreated for edit and convert buttons
|
31
.changes/v2.21.0.md
Normal file
31
.changes/v2.21.0.md
Normal file
@@ -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,
|
||||||
|
* …
|
||||||
|
|
6
.changes/v2.22.0.md
Normal file
6
.changes/v2.22.0.md
Normal file
@@ -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.
|
5
.changes/v2.22.1.md
Normal file
5
.changes/v2.22.1.md
Normal file
@@ -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
|
3
.changes/v2.22.2.md
Normal file
3
.changes/v2.22.2.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
## v2.22.2 - 2024-07-03
|
||||||
|
### Fixed
|
||||||
|
* Remove scope required for event participation stats
|
11
.changes/v2.23.0.md
Normal file
11
.changes/v2.23.0.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
## v2.23.0 - 2024-07-23
|
||||||
|
### Feature
|
||||||
|
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
|
||||||
|
* Add job bundle (module emploi)
|
||||||
|
* Upgrade import of address list to the last version of compiled addresses of belgian-best-address
|
||||||
|
|
||||||
|
* Upgrade CKEditor and refactor configuration with use of typescript
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* Fix resolving of centers for an household, which will fix in turn the access control
|
||||||
|
* Resolved type hinting error in activity list export
|
87
CHANGELOG.md
87
CHANGELOG.md
@@ -6,6 +6,93 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
|||||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||||
|
|
||||||
|
|
||||||
|
## v2.23.0 - 2024-07-23
|
||||||
|
### Feature
|
||||||
|
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
|
||||||
|
* Add job bundle (module emploi)
|
||||||
|
* Upgrade import of address list to the last version of compiled addresses of belgian-best-address
|
||||||
|
|
||||||
|
* Upgrade CKEditor and refactor configuration with use of typescript
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* Fix resolving of centers for an household, which will fix in turn the access control
|
||||||
|
* Resolved type hinting error in activity list export
|
||||||
|
|
||||||
|
## v2.22.2 - 2024-07-03
|
||||||
|
### Fixed
|
||||||
|
* Remove scope required for event participation stats
|
||||||
|
|
||||||
|
## v2.22.1 - 2024-07-01
|
||||||
|
### Fixed
|
||||||
|
* Remove debug word
|
||||||
|
### DX
|
||||||
|
* Add a command for reading official address DB from Luxembourg and update chill addresses
|
||||||
|
|
||||||
|
## v2.22.0 - 2024-06-25
|
||||||
|
### Feature
|
||||||
|
* ([#216](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/216)) [event bundle] exports added for the event module
|
||||||
|
|
||||||
|
### Traduction francophone
|
||||||
|
* Exports sont ajoutés pour la module événement.
|
||||||
|
|
||||||
|
## v2.21.0 - 2024-06-18
|
||||||
|
### Feature
|
||||||
|
* Add flash menu buttons in search results, to open directly a new calendar, or a new activity in an accompanying period
|
||||||
|
* ([#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
|
## v2.19.0 - 2024-05-14
|
||||||
### Feature
|
### 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
|
* ([#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
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
"doctrine/doctrine-migrations-bundle": "^3.0",
|
"doctrine/doctrine-migrations-bundle": "^3.0",
|
||||||
"doctrine/orm": "^2.13.0",
|
"doctrine/orm": "^2.13.0",
|
||||||
"erusev/parsedown": "^1.7",
|
"erusev/parsedown": "^1.7",
|
||||||
"graylog2/gelf-php": "^1.5",
|
|
||||||
"knplabs/knp-menu-bundle": "^3.0",
|
"knplabs/knp-menu-bundle": "^3.0",
|
||||||
"knplabs/knp-time-bundle": "^1.12",
|
"knplabs/knp-time-bundle": "^1.12",
|
||||||
"knpuniversity/oauth2-client-bundle": "^2.10",
|
"knpuniversity/oauth2-client-bundle": "^2.10",
|
||||||
@@ -66,10 +65,12 @@
|
|||||||
"symfony/security-guard": "^5.4",
|
"symfony/security-guard": "^5.4",
|
||||||
"symfony/security-http": "^5.4",
|
"symfony/security-http": "^5.4",
|
||||||
"symfony/serializer": "^5.4",
|
"symfony/serializer": "^5.4",
|
||||||
|
"symfony/stimulus-bundle": "^2.19",
|
||||||
"symfony/string": "^5.4",
|
"symfony/string": "^5.4",
|
||||||
"symfony/templating": "^5.4",
|
"symfony/templating": "^5.4",
|
||||||
"symfony/translation": "^5.4",
|
"symfony/translation": "^5.4",
|
||||||
"symfony/twig-bundle": "^5.4",
|
"symfony/twig-bundle": "^5.4",
|
||||||
|
"symfony/ux-translator": "^2.19",
|
||||||
"symfony/validator": "^5.4",
|
"symfony/validator": "^5.4",
|
||||||
"symfony/webpack-encore-bundle": "^1.11",
|
"symfony/webpack-encore-bundle": "^1.11",
|
||||||
"symfony/workflow": "^5.4",
|
"symfony/workflow": "^5.4",
|
||||||
@@ -92,12 +93,12 @@
|
|||||||
"phpstan/phpstan": "^1.9",
|
"phpstan/phpstan": "^1.9",
|
||||||
"phpstan/phpstan-deprecation-rules": "^1.1",
|
"phpstan/phpstan-deprecation-rules": "^1.1",
|
||||||
"phpstan/phpstan-strict-rules": "^1.0",
|
"phpstan/phpstan-strict-rules": "^1.0",
|
||||||
"phpunit/phpunit": ">= 7.5",
|
"phpunit/phpunit": "^10.5.24",
|
||||||
"rector/rector": "^1.1.0",
|
"rector/rector": "^1.1.0",
|
||||||
"symfony/debug-bundle": "^5.4",
|
"symfony/debug-bundle": "^5.4",
|
||||||
"symfony/dotenv": "^5.4",
|
"symfony/dotenv": "^5.4",
|
||||||
"symfony/maker-bundle": "^1.20",
|
"symfony/maker-bundle": "^1.20",
|
||||||
"symfony/phpunit-bridge": "^5.4",
|
"symfony/phpunit-bridge": "^7.1",
|
||||||
"symfony/runtime": "^5.4",
|
"symfony/runtime": "^5.4",
|
||||||
"symfony/stopwatch": "^5.4",
|
"symfony/stopwatch": "^5.4",
|
||||||
"symfony/var-dumper": "^5.4"
|
"symfony/var-dumper": "^5.4"
|
||||||
@@ -115,6 +116,8 @@
|
|||||||
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
|
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
|
||||||
"Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
|
"Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
|
||||||
"Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
|
"Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
|
||||||
|
"Chill\\FranceTravailApiBundle\\": "src/Bundle/ChillFranceTravailApiBundle/src",
|
||||||
|
"Chill\\JobBundle\\": "src/Bundle/ChillJobBundle/src",
|
||||||
"Chill\\MainBundle\\": "src/Bundle/ChillMainBundle",
|
"Chill\\MainBundle\\": "src/Bundle/ChillMainBundle",
|
||||||
"Chill\\PersonBundle\\": "src/Bundle/ChillPersonBundle",
|
"Chill\\PersonBundle\\": "src/Bundle/ChillPersonBundle",
|
||||||
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
|
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
|
||||||
|
@@ -21,7 +21,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
|||||||
class BirthdateFilter implements ExportElementValidatedInterface, FilterInterface
|
class BirthdateFilter implements ExportElementValidatedInterface, FilterInterface
|
||||||
{
|
{
|
||||||
// add specific role for this filter
|
// add specific role for this filter
|
||||||
public function addRole()
|
public function addRole(): ?string
|
||||||
{
|
{
|
||||||
// we do not need any new role for this filter, so we return null
|
// we do not need any new role for this filter, so we return null
|
||||||
return null;
|
return null;
|
||||||
|
31
docs/source/development/translations.rst
Normal file
31
docs/source/development/translations.rst
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
Translations
|
||||||
|
*************
|
||||||
|
|
||||||
|
Translator-UX: one source of truth
|
||||||
|
==================================
|
||||||
|
|
||||||
|
The Translator-ux integration streamlines the process of managing and using translation keys dynamically in our views, whether they be in twig or vue components. The goal is to have one source of truth
|
||||||
|
for all translations and avoid having to add translation keys in the YAML files as well as in i18ns files.
|
||||||
|
|
||||||
|
To add new translation keys, you can define them in your translation YAML files. Running `symfony console cache:clear` will subsequently update the compiled translation keys which can then also be imported and
|
||||||
|
used within any vue component. For use within a twig template they can be leveraged by using the |trans function.
|
||||||
|
Within vue components you will have to import the translation keys you require and then they can be used in the template with the trans() function.
|
||||||
|
|
||||||
|
It is advisable, before adding a translation key to do a search on the existing translation keys of the translation you require. An IDE will allow you to do so easily.
|
||||||
|
However to avoid the creation of duplicate translation keys a command also exists to detect them. We also strongly advise you to use this command as explained below.
|
||||||
|
|
||||||
|
Detect duplicates command
|
||||||
|
=========================
|
||||||
|
|
||||||
|
The DetectTranslationDuplicatesCommand `chill:detect-duplicate-translations` is a Symfony console command designed to identify duplicate translations across YAML files in a project.
|
||||||
|
It checks for repeated translation values linked to different keys within a specified locale.
|
||||||
|
The command accepts two main options:
|
||||||
|
|
||||||
|
1. `--locale`: to specify the language locale to check (defaulting to 'en')
|
||||||
|
2. `--exclude-namespaces`: to list namespaces to ignore during the check.
|
||||||
|
3. [optional] `--verify-hash`: can be used to ensure that the hash of current duplicates matches a given expected value,
|
||||||
|
aiding in maintaining translation integrity.
|
||||||
|
|
||||||
|
When duplicates are detected, they are displayed in a table format, listing the repeated translations alongside the keys where they are found.
|
||||||
|
If a mismatch occurs between the computed and expected hash values, an error message is displayed to signal a potential issue in translation consistency.
|
||||||
|
This command is useful for maintaining clean and consistent translations, avoiding redundancy in your YAML files.
|
@@ -56,7 +56,7 @@ We strongly encourage you to initialize a git repository at this step, to track
|
|||||||
cat <<< "$(jq '.extra.symfony += {"endpoint": ["flex://defaults", "https://gitlab.com/api/v4/projects/57371968/repository/files/index.json/raw?ref=main"]}' composer.json)" > composer.json
|
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
|
# install chill and some dependencies
|
||||||
# TODO fix the suffix "alpha1" and replace by ^3.0.0 when version 3.0.0 will be released
|
# 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
|
symfony composer require chill-project/chill-bundles v3.0.0-RC3 champs-libres/wopi-lib dev-master@dev champs-libres/wopi-bundle dev-master@dev
|
||||||
|
|
||||||
We encourage you to accept the inclusion of the "Docker configuration from recipes": this is the documented way to run the database.
|
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).
|
You must also accept to configure recipes from the contrib repository, unless you want to configure the bundles manually).
|
||||||
@@ -95,7 +95,7 @@ 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
|
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 <https://symfony.com/doc/current/configuration/secrets.html>`_
|
in this :code:`.env` file, but instead using the `secrets management tool <https://symfony.com/doc/current/configuration/secrets.html>`_
|
||||||
or in the :code:`.env.local` file, which should not be commited to the git repository.
|
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
|
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.
|
by the symfony server, from the docker compose services.
|
||||||
@@ -110,10 +110,15 @@ you can either:
|
|||||||
.. code-block:: env
|
.. code-block:: env
|
||||||
|
|
||||||
ADMIN_PASSWORD=\$2y\$13\$iyvJLuT4YEa6iWXyQV4/N.hNHpNG8kXlYDkkt5MkYy4FXcSwYAwmm
|
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,
|
- 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).
|
not the password in clear text).
|
||||||
|
|
||||||
|
- set up the jwt authentication bundle
|
||||||
|
|
||||||
|
Some environment variables are available for the JWT authentication bundle in the :code:`.env` file.
|
||||||
|
|
||||||
Prepare migrations and other tools
|
Prepare migrations and other tools
|
||||||
**********************************
|
**********************************
|
||||||
|
|
||||||
@@ -130,6 +135,8 @@ To continue the installation process, you will have to run migrations:
|
|||||||
symfony console messenger:setup-transports
|
symfony console messenger:setup-transports
|
||||||
# prepare some views
|
# prepare some views
|
||||||
symfony console chill:db:sync-views
|
symfony console chill:db:sync-views
|
||||||
|
# generate jwt token, required for some api features (webdav access, ...)
|
||||||
|
symfony console lexik:jwt:generate-keypair
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
@@ -164,7 +171,7 @@ can rely on the whole chill framework, meaning there is no need to add them to t
|
|||||||
You will require some bundles to have the following development tools:
|
You will require some bundles to have the following development tools:
|
||||||
|
|
||||||
- add fixtures
|
- add fixtures
|
||||||
- add profiler and var-dumper to debug
|
- add profiler and debug bundle
|
||||||
|
|
||||||
Install fixtures
|
Install fixtures
|
||||||
****************
|
****************
|
||||||
@@ -179,7 +186,7 @@ Install fixtures
|
|||||||
This will generate user accounts, centers, and some basic configuration.
|
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
|
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"`.
|
visible in the "users" table: :code:`docker compose exec database psql -U app -c "SELECT username FROM users"`.
|
||||||
|
|
||||||
The password is always :code:`password`.
|
The password is always :code:`password`.
|
||||||
|
|
||||||
@@ -192,7 +199,7 @@ Add web profiler and debugger
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
symfony composer require --dev symfony/web-profiler-bundle symfony/var-dumper
|
symfony composer require --dev symfony/web-profiler-bundle symfony/debug-bundle
|
||||||
|
|
||||||
Working on chill bundles
|
Working on chill bundles
|
||||||
************************
|
************************
|
||||||
|
@@ -27,7 +27,7 @@
|
|||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"postcss-loader": "^7.0.2",
|
"postcss-loader": "^7.0.2",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"sass-loader": "^13.0.0",
|
"sass-loader": "^14.0.0",
|
||||||
"select2": "^4.0.13",
|
"select2": "^4.0.13",
|
||||||
"select2-bootstrap-theme": "0.1.0-beta.10",
|
"select2-bootstrap-theme": "0.1.0-beta.10",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
@@ -46,9 +46,11 @@
|
|||||||
"@fullcalendar/vue3": "^6.1.4",
|
"@fullcalendar/vue3": "^6.1.4",
|
||||||
"@popperjs/core": "^2.9.2",
|
"@popperjs/core": "^2.9.2",
|
||||||
"@types/leaflet": "^1.9.3",
|
"@types/leaflet": "^1.9.3",
|
||||||
|
"@types/dompurify": "^3.0.5",
|
||||||
"dropzone": "^5.7.6",
|
"dropzone": "^5.7.6",
|
||||||
"es6-promise": "^4.2.8",
|
"es6-promise": "^4.2.8",
|
||||||
"leaflet": "^1.7.1",
|
"leaflet": "^1.7.1",
|
||||||
|
"marked": "^12.0.2",
|
||||||
"masonry-layout": "^4.2.2",
|
"masonry-layout": "^4.2.2",
|
||||||
"mime": "^4.0.0",
|
"mime": "^4.0.0",
|
||||||
"swagger-ui": "^4.15.5",
|
"swagger-ui": "^4.15.5",
|
||||||
@@ -56,7 +58,7 @@
|
|||||||
"vue": "^3.2.37",
|
"vue": "^3.2.37",
|
||||||
"vue-i18n": "^9.1.6",
|
"vue-i18n": "^9.1.6",
|
||||||
"vue-multiselect": "3.0.0-alpha.2",
|
"vue-multiselect": "3.0.0-alpha.2",
|
||||||
"vue-toast-notification": "^2.0",
|
"vue-toast-notification": "^3.1.2",
|
||||||
"vuex": "^4.0.0"
|
"vuex": "^4.0.0"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
|
@@ -1,34 +1,29 @@
|
|||||||
parameters:
|
parameters:
|
||||||
ignoreErrors:
|
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\\.$#"
|
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php
|
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
|
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\\.$#"
|
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: 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\\.$#"
|
|
||||||
count: 2
|
count: 2
|
||||||
path: src/Bundle/ChillMainBundle/Repository/NotificationRepository.php
|
path: src/Bundle/ChillDocStoreBundle/Controller/DocumentAccompanyingCourseController.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Foreach overwrites \\$key with its key variable\\.$#"
|
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: 1
|
count: 2
|
||||||
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
|
path: src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Variable \\$participation might not be defined\\.$#"
|
message: "#^Variable \\$participation might not be defined\\.$#"
|
||||||
@@ -40,6 +35,106 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
|
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Comparison operation \"\\>\" between \\(bool\\|int\\|Redis\\) and 0 results in an error\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/ApiWrapper.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Variable \\$response might not be defined\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/ApiWrapper.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Function GuzzleHttp\\\\Psr7\\\\get not found\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Function GuzzleHttp\\\\Psr7\\\\str not found\\.$#"
|
||||||
|
count: 2
|
||||||
|
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$seconds of function sleep expects int, string given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Unreachable statement \\- code above always terminates\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Controller/CSPersonController.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$interval of method DateTimeImmutable\\:\\:add\\(\\) expects DateInterval, string\\|null given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Entity/Immersion.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$object of static method DateTimeImmutable\\:\\:createFromMutable\\(\\) expects DateTime, DateTimeInterface given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Entity/Immersion.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Property Chill\\\\JobBundle\\\\Entity\\\\Rome\\\\Metier\\:\\:\\$appellations is never read, only written\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Entity/Rome/Metier.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListCSPerson\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Export/ListCSPerson.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Variable \\$f might not be defined\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Export/ListCSPerson.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListFrein\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Export/ListFrein.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListProjetProfessionnel\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Export/ListProjetProfessionnel.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Property Chill\\\\JobBundle\\\\Form\\\\ChoiceLoader\\\\RomeAppellationChoiceLoader\\:\\:\\$appellationRepository \\(Chill\\\\JobBundle\\\\Repository\\\\Rome\\\\AppellationRepository\\) does not accept Doctrine\\\\ORM\\\\EntityRepository\\<Chill\\\\JobBundle\\\\Entity\\\\Rome\\\\Appellation\\>\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Result of && is always false\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Strict comparison using \\=\\=\\= between array\\{\\} and Symfony\\\\Component\\\\Validator\\\\ConstraintViolationListInterface will always evaluate to false\\.$#"
|
||||||
|
count: 2
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Strict comparison using \\=\\=\\= between null and string will always evaluate to false\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Variable \\$metier might not be defined\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$interval of method DateTimeImmutable\\:\\:add\\(\\) expects DateInterval, string\\|null given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Security/Authorization/CSConnectesVoter.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Parameter \\#1 \\$object of static method DateTimeImmutable\\:\\:createFromMutable\\(\\) expects DateTime, DateTimeInterface given\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: src/Bundle/ChillJobBundle/src/Security/Authorization/CSConnectesVoter.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Cannot unset offset '_token' on array\\{formatter\\: mixed, export\\: mixed, centers\\: mixed, alias\\: string\\}\\.$#"
|
message: "#^Cannot unset offset '_token' on array\\{formatter\\: mixed, export\\: mixed, centers\\: mixed, alias\\: string\\}\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
@@ -65,11 +160,31 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillMainBundle/Form/ChoiceLoader/PostalCodeChoiceLoader.php
|
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\\.$#"
|
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
|
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\\.$#"
|
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
17
rector.php
17
rector.php
@@ -28,6 +28,9 @@ return static function (RectorConfig $rectorConfig): void {
|
|||||||
|
|
||||||
// register a single rule
|
// register a single rule
|
||||||
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
|
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
|
||||||
|
$rectorConfig->rule(Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromPropertyTypeRector::class);
|
||||||
|
$rectorConfig->rule(Rector\TypeDeclaration\Rector\Class_\MergeDateTimePropertyTypeDeclarationRector::class);
|
||||||
|
$rectorConfig->rule(Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationBasedOnParentClassMethodRector::class);
|
||||||
|
|
||||||
// part of the symfony 54 rules
|
// part of the symfony 54 rules
|
||||||
$rectorConfig->rule(\Rector\Symfony\Symfony53\Rector\StaticPropertyFetch\KernelTestCaseContainerPropertyDeprecationRector::class);
|
$rectorConfig->rule(\Rector\Symfony\Symfony53\Rector\StaticPropertyFetch\KernelTestCaseContainerPropertyDeprecationRector::class);
|
||||||
@@ -36,14 +39,14 @@ return static function (RectorConfig $rectorConfig): void {
|
|||||||
|
|
||||||
//define sets of rules
|
//define sets of rules
|
||||||
$rectorConfig->sets([
|
$rectorConfig->sets([
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_50,
|
LevelSetList::UP_TO_PHP_82,
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_50_TYPES,
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_40,
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_51,
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_41,
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_52,
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_42,
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_53,
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_43,
|
||||||
\Rector\Symfony\Set\SymfonySetList::SYMFONY_54,
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_44,
|
||||||
\Rector\Doctrine\Set\DoctrineSetList::DOCTRINE_CODE_QUALITY,
|
\Rector\Doctrine\Set\DoctrineSetList::DOCTRINE_CODE_QUALITY,
|
||||||
\Rector\Doctrine\Set\DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES,
|
\Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_90,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$rectorConfig->ruleWithConfiguration(\Rector\Php80\Rector\Class_\AnnotationToAttributeRector::class, [
|
$rectorConfig->ruleWithConfiguration(\Rector\Php80\Rector\Class_\AnnotationToAttributeRector::class, [
|
||||||
|
@@ -99,10 +99,10 @@ final class ActivityController extends AbstractController
|
|||||||
|
|
||||||
$form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
|
$form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
|
||||||
|
|
||||||
if (Request::METHOD_DELETE === $request->getMethod()) {
|
if (Request::METHOD_POST === $request->getMethod()) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$this->logger->notice('An activity has been removed', [
|
$this->logger->notice('An activity has been removed', [
|
||||||
'by_user' => $this->getUser()->getUsername(),
|
'by_user' => $this->getUser()->getUsername(),
|
||||||
'activity_id' => $activity->getId(),
|
'activity_id' => $activity->getId(),
|
||||||
@@ -640,7 +640,6 @@ final class ActivityController extends AbstractController
|
|||||||
|
|
||||||
return $this->createFormBuilder()
|
return $this->createFormBuilder()
|
||||||
->setAction($this->generateUrl('chill_activity_activity_delete', $params))
|
->setAction($this->generateUrl('chill_activity_activity_delete', $params))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@@ -79,11 +79,9 @@ class ActivityReason
|
|||||||
/**
|
/**
|
||||||
* Set active.
|
* Set active.
|
||||||
*
|
*
|
||||||
* @param bool $active
|
|
||||||
*
|
|
||||||
* @return ActivityReason
|
* @return ActivityReason
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
@@ -110,11 +108,9 @@ class ActivityReason
|
|||||||
/**
|
/**
|
||||||
* Set name.
|
* Set name.
|
||||||
*
|
*
|
||||||
* @param array $name
|
|
||||||
*
|
|
||||||
* @return ActivityReason
|
* @return ActivityReason
|
||||||
*/
|
*/
|
||||||
public function setName($name)
|
public function setName(array $name)
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
|
|
||||||
|
@@ -152,7 +152,7 @@ class ListActivityHelper
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->translator->trans($value);
|
return $this->translator->trans((string) $value);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -73,7 +73,7 @@ final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInt
|
|||||||
|
|
||||||
$qb->andWhere(
|
$qb->andWhere(
|
||||||
$qb->expr()->exists(
|
$qb->expr()->exists(
|
||||||
'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = acp"
|
'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = activity.accompanyingPeriod"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\ActivityBundle\Menu;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
|
use Knp\Menu\MenuItem;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
final readonly class AccompanyingCourseQuickMenuBuilder implements LocalMenuBuilderInterface
|
||||||
|
{
|
||||||
|
public function __construct(private Security $security) {}
|
||||||
|
|
||||||
|
public static function getMenuIds(): array
|
||||||
|
{
|
||||||
|
return ['accompanying_course_quick_menu'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||||
|
{
|
||||||
|
/** @var \Chill\PersonBundle\Entity\AccompanyingPeriod $accompanyingCourse */
|
||||||
|
$accompanyingCourse = $parameters['accompanying-course'];
|
||||||
|
|
||||||
|
if ($this->security->isGranted(ActivityVoter::CREATE, $accompanyingCourse)) {
|
||||||
|
$menu
|
||||||
|
->addChild('Create a new activity in accompanying course', [
|
||||||
|
'route' => 'chill_activity_activity_new',
|
||||||
|
'routeParameters' => [
|
||||||
|
// 'activityType_id' => '',
|
||||||
|
'accompanying_period_id' => $accompanyingCourse->getId(),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->setExtras([
|
||||||
|
'order' => 10,
|
||||||
|
'icon' => 'plus',
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -15,9 +15,9 @@ use Chill\ActivityBundle\Entity\ActivityType;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
final class ActivityTypeRepository implements ActivityTypeRepositoryInterface
|
final readonly class ActivityTypeRepository implements ActivityTypeRepositoryInterface
|
||||||
{
|
{
|
||||||
private readonly EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $em)
|
public function __construct(EntityManagerInterface $em)
|
||||||
{
|
{
|
||||||
|
@@ -22,8 +22,8 @@
|
|||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<li class="add-persons">
|
<li class="add-persons">
|
||||||
<add-persons
|
<add-persons
|
||||||
buttonTitle="activity.add_persons"
|
:buttonTitle="trans(ACTIVITY_ADD_PERSONS)"
|
||||||
modalTitle="activity.add_persons"
|
:modalTitle="trans(ACTIVITY_ADD_PERSONS)"
|
||||||
v-bind:key="addPersons.key"
|
v-bind:key="addPersons.key"
|
||||||
v-bind:options="addPersonsOptions"
|
v-bind:options="addPersonsOptions"
|
||||||
@addNewPersons="addNewPersons"
|
@addNewPersons="addNewPersons"
|
||||||
@@ -40,6 +40,20 @@ import { mapState, mapGetters } from 'vuex';
|
|||||||
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
|
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
|
||||||
import PersonsBloc from './ConcernedGroups/PersonsBloc.vue';
|
import PersonsBloc from './ConcernedGroups/PersonsBloc.vue';
|
||||||
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
|
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
|
||||||
|
import {
|
||||||
|
ACTIVITY_BLOC_PERSONS,
|
||||||
|
ACTIVITY_BLOC_PERSONS_ASSOCIATED,
|
||||||
|
ACTIVITY_BLOC_PERSONS_NOT_ASSOCIATED,
|
||||||
|
ACTIVITY_BLOC_THIRDPARTY,
|
||||||
|
ACTIVITY_BLOC_USERS,
|
||||||
|
ACTIVITY_ADD_PERSONS,
|
||||||
|
ACTIVITY_LOCATION,
|
||||||
|
ACTIVITY_CHOOSE_LOCATION,
|
||||||
|
MULTISELECT_SELECT_LABEL,
|
||||||
|
MULTISELECT_DESELECT_LABEL,
|
||||||
|
MULTISELECT_SELECTED_LABEL,
|
||||||
|
trans,
|
||||||
|
} from "../../../../../../../../../../../assets/translator";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ConcernedGroups",
|
name: "ConcernedGroups",
|
||||||
@@ -48,16 +62,22 @@ export default {
|
|||||||
PersonsBloc,
|
PersonsBloc,
|
||||||
PersonText
|
PersonText
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
trans,
|
||||||
|
ACTIVITY_ADD_PERSONS
|
||||||
|
};
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
personsBlocs: [
|
personsBlocs: [
|
||||||
{ key: 'persons',
|
{ key: 'persons',
|
||||||
title: 'activity.bloc_persons',
|
title: trans(ACTIVITY_BLOC_PERSONS),
|
||||||
persons: [],
|
persons: [],
|
||||||
included: false
|
included: false
|
||||||
},
|
},
|
||||||
{ key: 'personsAssociated',
|
{ key: 'personsAssociated',
|
||||||
title: 'activity.bloc_persons_associated',
|
title: trans(ACTIVITY_BLOC_PERSONS_ASSOCIATED),
|
||||||
persons: [],
|
persons: [],
|
||||||
included: window.activity ? window.activity.activityType.personsVisible !== 0 : true
|
included: window.activity ? window.activity.activityType.personsVisible !== 0 : true
|
||||||
},
|
},
|
||||||
@@ -67,12 +87,12 @@ export default {
|
|||||||
included: window.activity ? window.activity.activityType.personsVisible !== 0 : true
|
included: window.activity ? window.activity.activityType.personsVisible !== 0 : true
|
||||||
},
|
},
|
||||||
{ key: 'thirdparty',
|
{ key: 'thirdparty',
|
||||||
title: 'activity.bloc_thirdparty',
|
title: trans(ACTIVITY_BLOC_THIRDPARTY),
|
||||||
persons: [],
|
persons: [],
|
||||||
included: window.activity ? window.activity.activityType.thirdPartiesVisible !== 0 : true
|
included: window.activity ? window.activity.activityType.thirdPartiesVisible !== 0 : true
|
||||||
},
|
},
|
||||||
{ key: 'users',
|
{ key: 'users',
|
||||||
title: 'activity.bloc_users',
|
title: trans(ACTIVITY_BLOC_USERS),
|
||||||
persons: [],
|
persons: [],
|
||||||
included: window.activity ? window.activity.activityType.usersVisible !== 0 : true
|
included: window.activity ? window.activity.activityType.usersVisible !== 0 : true
|
||||||
},
|
},
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<teleport to="#location">
|
<teleport to="#location">
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
<label :class="locationClassList">
|
<label :class="locationClassList">
|
||||||
{{ $t("activity.location") }}
|
{{ trans(ACTIVITY_LOCATION) }}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<VueMultiselect
|
<VueMultiselect
|
||||||
@@ -13,11 +13,11 @@
|
|||||||
open-direction="top"
|
open-direction="top"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
:searchable="true"
|
:searchable="true"
|
||||||
:placeholder="$t('activity.choose_location')"
|
:placeholder="trans(ACTIVITY_CHOOSE_LOCATION)"
|
||||||
:custom-label="customLabel"
|
:custom-label="customLabel"
|
||||||
:select-label="$t('multiselect.select_label')"
|
:select-label="trans(MULTISELECT_SELECT_LABEL)"
|
||||||
:deselect-label="$t('multiselect.deselect_label')"
|
:deselect-label="trans(MULTISELECT_DESELECT_LABEL)"
|
||||||
:selected-label="$t('multiselect.selected_label')"
|
:selected-label="trans(MULTISELECT_SELECTED_LABEL)"
|
||||||
:options="availableLocations"
|
:options="availableLocations"
|
||||||
group-values="locations"
|
group-values="locations"
|
||||||
group-label="locationGroup"
|
group-label="locationGroup"
|
||||||
@@ -34,6 +34,14 @@
|
|||||||
import { mapState, mapGetters } from "vuex";
|
import { mapState, mapGetters } from "vuex";
|
||||||
import VueMultiselect from "vue-multiselect";
|
import VueMultiselect from "vue-multiselect";
|
||||||
import NewLocation from "./Location/NewLocation.vue";
|
import NewLocation from "./Location/NewLocation.vue";
|
||||||
|
import {
|
||||||
|
trans,
|
||||||
|
ACTIVITY_LOCATION,
|
||||||
|
ACTIVITY_CHOOSE_LOCATION,
|
||||||
|
MULTISELECT_SELECT_LABEL,
|
||||||
|
MULTISELECT_DESELECT_LABEL,
|
||||||
|
MULTISELECT_SELECTED_LABEL
|
||||||
|
} from '../../../../../../../../../../../assets/translator'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Location",
|
name: "Location",
|
||||||
@@ -41,6 +49,16 @@ export default {
|
|||||||
NewLocation,
|
NewLocation,
|
||||||
VueMultiselect,
|
VueMultiselect,
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
trans,
|
||||||
|
ACTIVITY_LOCATION,
|
||||||
|
ACTIVITY_CHOOSE_LOCATION,
|
||||||
|
MULTISELECT_SELECT_LABEL,
|
||||||
|
MULTISELECT_DESELECT_LABEL,
|
||||||
|
MULTISELECT_SELECTED_LABEL
|
||||||
|
};
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
locationClassList:
|
locationClassList:
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<li>
|
<li>
|
||||||
<a class="btn btn-sm btn-create" @click="openModal">
|
<a class="btn btn-sm btn-create" @click="openModal">
|
||||||
{{ $t('activity.create_new_location') }}
|
{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
@close="modal.showModal = false">
|
@close="modal.showModal = false">
|
||||||
|
|
||||||
<template v-slot:header>
|
<template v-slot:header>
|
||||||
<h3 class="modal-title">{{ $t('activity.create_new_location') }}</h3>
|
<h3 class="modal-title">{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}</h3>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:body>
|
<template v-slot:body>
|
||||||
<form>
|
<form>
|
||||||
@@ -26,17 +26,17 @@
|
|||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<select class="form-select form-select-lg" id="type" required v-model="selectType">
|
<select class="form-select form-select-lg" id="type" required v-model="selectType">
|
||||||
<option selected disabled value="">{{ $t('activity.choose_location_type') }}</option>
|
<option selected disabled value="">{{ trans(ACTIVITY_CHOOSE_LOCATION_TYPE) }}</option>
|
||||||
<option v-for="t in locationTypes" :value="t" :key="t.id">
|
<option v-for="t in locationTypes" :value="t" :key="t.id">
|
||||||
{{ t.title.fr }}
|
{{ t.title.fr }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<label>{{ $t('activity.location_fields.type') }}</label>
|
<label>{{ trans(ACTIVITY_LOCATION_FIELDS_TYPE) }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input class="form-control form-control-lg" id="name" v-model="inputName" placeholder />
|
<input class="form-control form-control-lg" id="name" v-model="inputName" placeholder />
|
||||||
<label for="name">{{ $t('activity.location_fields.name') }}</label>
|
<label for="name">{{ trans(ACTIVITY_LOCATION_FIELDS_NAME) }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<add-address
|
<add-address
|
||||||
@@ -49,15 +49,15 @@
|
|||||||
|
|
||||||
<div class="form-floating mb-3" v-if="showContactData">
|
<div class="form-floating mb-3" v-if="showContactData">
|
||||||
<input class="form-control form-control-lg" id="phonenumber1" v-model="inputPhonenumber1" placeholder />
|
<input class="form-control form-control-lg" id="phonenumber1" v-model="inputPhonenumber1" placeholder />
|
||||||
<label for="phonenumber1">{{ $t('activity.location_fields.phonenumber1') }}</label>
|
<label for="phonenumber1">{{ trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER1) }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-floating mb-3" v-if="hasPhonenumber1">
|
<div class="form-floating mb-3" v-if="hasPhonenumber1">
|
||||||
<input class="form-control form-control-lg" id="phonenumber2" v-model="inputPhonenumber2" placeholder />
|
<input class="form-control form-control-lg" id="phonenumber2" v-model="inputPhonenumber2" placeholder />
|
||||||
<label for="phonenumber2">{{ $t('activity.location_fields.phonenumber2') }}</label>
|
<label for="phonenumber2">{{ trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER2) }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-floating mb-3" v-if="showContactData">
|
<div class="form-floating mb-3" v-if="showContactData">
|
||||||
<input class="form-control form-control-lg" id="email" v-model="inputEmail" placeholder />
|
<input class="form-control form-control-lg" id="email" v-model="inputEmail" placeholder />
|
||||||
<label for="email">{{ $t('activity.location_fields.email') }}</label>
|
<label for="email">{{ trans(ACTIVITY_LOCATION_FIELDS_EMAIL) }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
<button class="btn btn-save"
|
<button class="btn btn-save"
|
||||||
@click.prevent="saveNewLocation"
|
@click.prevent="saveNewLocation"
|
||||||
>
|
>
|
||||||
{{ $t('action.save') }}
|
{{ trans(SAVE) }}
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -81,6 +81,13 @@ import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue"
|
|||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import { getLocationTypes } from "../../api";
|
import { getLocationTypes } from "../../api";
|
||||||
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
|
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
|
||||||
|
import {
|
||||||
|
SAVE,
|
||||||
|
ACTIVITY_LOCATION_FIELDS_EMAIL, ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
|
||||||
|
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2, ACTIVITY_LOCATION_FIELDS_NAME,
|
||||||
|
ACTIVITY_LOCATION_FIELDS_TYPE, ACTIVITY_CHOOSE_LOCATION_TYPE, ACTIVITY_CREATE_NEW_LOCATION,
|
||||||
|
trans
|
||||||
|
} from "../../../../../../../../../../../../assets/translator";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "NewLocation",
|
name: "NewLocation",
|
||||||
@@ -88,6 +95,19 @@ export default {
|
|||||||
Modal,
|
Modal,
|
||||||
AddAddress,
|
AddAddress,
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
trans,
|
||||||
|
SAVE,
|
||||||
|
ACTIVITY_LOCATION_FIELDS_EMAIL,
|
||||||
|
ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
|
||||||
|
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
|
||||||
|
ACTIVITY_LOCATION_FIELDS_NAME,
|
||||||
|
ACTIVITY_LOCATION_FIELDS_TYPE,
|
||||||
|
ACTIVITY_CHOOSE_LOCATION_TYPE,
|
||||||
|
ACTIVITY_CREATE_NEW_LOCATION,
|
||||||
|
};
|
||||||
|
},
|
||||||
props: ['availableLocations'],
|
props: ['availableLocations'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label :class="socialIssuesClassList">{{ $t('activity.social_issues') }}</label>
|
<label :class="socialIssuesClassList">{{ trans(ACTIVITY_SOCIAL_ISSUES) }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
:allow-empty="true"
|
:allow-empty="true"
|
||||||
:show-labels="false"
|
:show-labels="false"
|
||||||
:loading="issueIsLoading"
|
:loading="issueIsLoading"
|
||||||
:placeholder="$t('activity.choose_other_social_issue')"
|
:placeholder="trans(ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE)"
|
||||||
:options="socialIssuesOther"
|
:options="socialIssuesOther"
|
||||||
@select="addIssueInList">
|
@select="addIssueInList">
|
||||||
</VueMultiselect>
|
</VueMultiselect>
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label :class="socialActionsClassList">{{ $t('activity.social_actions') }}</label>
|
<label :class="socialActionsClassList">{{ trans(ACTIVITY_SOCIAL_ACTIONS) }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span v-else-if="socialIssuesSelected.length === 0" class="inline-choice chill-no-data-statement mt-3">
|
<span v-else-if="socialIssuesSelected.length === 0" class="inline-choice chill-no-data-statement mt-3">
|
||||||
{{ $t('activity.select_first_a_social_issue') }}
|
{{ trans(ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE) }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<template v-else-if="socialActionsList.length > 0">
|
<template v-else-if="socialActionsList.length > 0">
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<span v-else-if="actionAreLoaded && socialActionsList.length === 0" class="inline-choice chill-no-data-statement mt-3">
|
<span v-else-if="actionAreLoaded && socialActionsList.length === 0" class="inline-choice chill-no-data-statement mt-3">
|
||||||
{{ $t('activity.social_action_list_empty') }}
|
{{ trans(ACTIVITY_SOCIAL_ACTION_LIST_EMPTY) }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
||||||
@@ -81,6 +81,11 @@ import VueMultiselect from 'vue-multiselect';
|
|||||||
import CheckSocialIssue from './SocialIssuesAcc/CheckSocialIssue.vue';
|
import CheckSocialIssue from './SocialIssuesAcc/CheckSocialIssue.vue';
|
||||||
import CheckSocialAction from './SocialIssuesAcc/CheckSocialAction.vue';
|
import CheckSocialAction from './SocialIssuesAcc/CheckSocialAction.vue';
|
||||||
import { getSocialIssues, getSocialActionByIssue } from '../api.js';
|
import { getSocialIssues, getSocialActionByIssue } from '../api.js';
|
||||||
|
import {
|
||||||
|
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
|
||||||
|
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE, ACTIVITY_SOCIAL_ACTIONS,
|
||||||
|
ACTIVITY_SOCIAL_ISSUES, ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE, trans
|
||||||
|
} from "../../../../../../../../../../../assets/translator";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "SocialIssuesAcc",
|
name: "SocialIssuesAcc",
|
||||||
@@ -89,6 +94,16 @@ export default {
|
|||||||
CheckSocialAction,
|
CheckSocialAction,
|
||||||
VueMultiselect
|
VueMultiselect
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
trans,
|
||||||
|
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
|
||||||
|
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
|
||||||
|
ACTIVITY_SOCIAL_ACTIONS,
|
||||||
|
ACTIVITY_SOCIAL_ISSUES,
|
||||||
|
ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE
|
||||||
|
};
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
issueIsLoading: false,
|
issueIsLoading: false,
|
||||||
|
@@ -68,7 +68,7 @@
|
|||||||
<div class="wl-col title"><h3>{{ 'Referrer'|trans }}</h3></div>
|
<div class="wl-col title"><h3>{{ 'Referrer'|trans }}</h3></div>
|
||||||
<div class="wl-col list">
|
<div class="wl-col list">
|
||||||
<p class="wl-item">
|
<p class="wl-item">
|
||||||
<span class="badge-user">{{ activity.user|chill_entity_render_box }}</span>
|
<span class="badge-user">{{ activity.user|chill_entity_render_box({'at_date': activity.date}) }}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -87,7 +87,7 @@
|
|||||||
<li>
|
<li>
|
||||||
{% if bloc.type == 'user' %}
|
{% if bloc.type == 'user' %}
|
||||||
<span class="badge-user">
|
<span class="badge-user">
|
||||||
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false }) }}
|
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false, 'at_date': entity.date }) }}
|
||||||
</span>
|
</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ _self.insert_onthefly(bloc.type, item) }}
|
{{ _self.insert_onthefly(bloc.type, item) }}
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
<li>
|
<li>
|
||||||
{% if bloc.type == 'user' %}
|
{% if bloc.type == 'user' %}
|
||||||
<span class="badge-user">
|
<span class="badge-user">
|
||||||
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false }) }}
|
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false, 'at_date': entity.date }) }}
|
||||||
</span>
|
</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ _self.insert_onthefly(bloc.type, item) }}
|
{{ _self.insert_onthefly(bloc.type, item) }}
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
<span class="wl-item">
|
<span class="wl-item">
|
||||||
{% if bloc.type == 'user' %}
|
{% if bloc.type == 'user' %}
|
||||||
<span class="badge-user">
|
<span class="badge-user">
|
||||||
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false }) }}
|
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false, 'at_date': entity.date }) }}
|
||||||
{%- if context == 'calendar_accompanyingCourse' or context == 'calendar_person' %}
|
{%- if context == 'calendar_accompanyingCourse' or context == 'calendar_person' %}
|
||||||
{% set invite = entity.inviteForUser(item) %}
|
{% set invite = entity.inviteForUser(item) %}
|
||||||
{% if invite is not null %}
|
{% if invite is not null %}
|
||||||
|
@@ -41,7 +41,7 @@
|
|||||||
{% if activity.user and t.userVisible %}
|
{% if activity.user and t.userVisible %}
|
||||||
<li>
|
<li>
|
||||||
<span class="item-key">{{ 'Referrer'|trans ~ ': ' }}</span>
|
<span class="item-key">{{ 'Referrer'|trans ~ ': ' }}</span>
|
||||||
<span class="badge-user">{{ activity.user|chill_entity_render_box }}</span>
|
<span class="badge-user">{{ activity.user|chill_entity_render_box({'at_date': activity.date}) }}</span>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@@ -37,7 +37,7 @@
|
|||||||
{%- if entity.user is not null %}
|
{%- if entity.user is not null %}
|
||||||
<dt class="inline">{{ 'Referrer'|trans|capitalize }}</dt>
|
<dt class="inline">{{ 'Referrer'|trans|capitalize }}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<span class="badge-user">{{ entity.user|chill_entity_render_box }}</span>
|
<span class="badge-user">{{ entity.user|chill_entity_render_box({'at_date': entity.date}) }}</span>
|
||||||
</dd>
|
</dd>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@@ -145,7 +145,7 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
|
|||||||
throw new \RuntimeException('Could not determine context of activity.');
|
throw new \RuntimeException('Could not determine context of activity.');
|
||||||
}
|
}
|
||||||
} elseif ($subject instanceof AccompanyingPeriod) {
|
} elseif ($subject instanceof AccompanyingPeriod) {
|
||||||
if (AccompanyingPeriod::STEP_CLOSED === $subject->getStep()) {
|
if (AccompanyingPeriod::STEP_CLOSED === $subject->getStep() || AccompanyingPeriod::STEP_DRAFT === $subject->getStep()) {
|
||||||
if (\in_array($attribute, [self::UPDATE, self::CREATE, self::DELETE], true)) {
|
if (\in_array($attribute, [self::UPDATE, self::CREATE, self::DELETE], true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -60,7 +60,7 @@ final class TranslatableActivityTypeTest extends KernelTestCase
|
|||||||
$this->assertInstanceOf(
|
$this->assertInstanceOf(
|
||||||
ActivityType::class,
|
ActivityType::class,
|
||||||
$form->getData()['type'],
|
$form->getData()['type'],
|
||||||
'The data is an instance of Chill\\ActivityBundle\\Entity\\ActivityType'
|
'The data is an instance of Chill\ActivityBundle\Entity\ActivityType'
|
||||||
);
|
);
|
||||||
$this->assertEquals($type->getId(), $form->getData()['type']->getId());
|
$this->assertEquals($type->getId(), $form->getData()['type']->getId());
|
||||||
|
|
||||||
|
@@ -77,6 +77,18 @@ Choose a type: Choisir un type
|
|||||||
4 hours: 4 heures
|
4 hours: 4 heures
|
||||||
4 hours 30: 4 heures 30
|
4 hours 30: 4 heures 30
|
||||||
5 hours: 5 heures
|
5 hours: 5 heures
|
||||||
|
5 hours 30: 5 heure 30
|
||||||
|
6 hours: 6 heures
|
||||||
|
6 hours 30: 6 heure 30
|
||||||
|
7 hours: 7 heures
|
||||||
|
7 hours 30: 7 heure 30
|
||||||
|
8 hours: 8 heures
|
||||||
|
8 hours 30: 8 heure 30
|
||||||
|
9 hours: 9 heures
|
||||||
|
9 hours 30: 9 heure 30
|
||||||
|
10 hours: 10 heures
|
||||||
|
11 hours: 11 heures
|
||||||
|
12 hours: 12 heures
|
||||||
Concerned groups: Parties concernées par l'échange
|
Concerned groups: Parties concernées par l'échange
|
||||||
Persons in accompanying course: Usagers du parcours
|
Persons in accompanying course: Usagers du parcours
|
||||||
Third persons: Tiers non-pro.
|
Third persons: Tiers non-pro.
|
||||||
@@ -89,6 +101,31 @@ activity:
|
|||||||
Insert a document: Insérer un document
|
Insert a document: Insérer un document
|
||||||
Remove a document: Supprimer le document
|
Remove a document: Supprimer le document
|
||||||
comment: Commentaire
|
comment: Commentaire
|
||||||
|
errors: Le formulaire contient des erreurs
|
||||||
|
social_issues: Problématiques sociales
|
||||||
|
choose_other_social_issue: Ajouter une autre problématique sociale...
|
||||||
|
social_actions: Actions d'accompagnement
|
||||||
|
select_first_a_social_issue: Sélectionnez d'abord une problématique sociale
|
||||||
|
social_action_list_empty: Aucune action sociale disponible
|
||||||
|
add_persons: Ajouter des personnes concernées
|
||||||
|
bloc_persons: Usagers
|
||||||
|
bloc_persons_associated: Usagers du parcours
|
||||||
|
bloc_persons_not_associated: Tiers non-pro.
|
||||||
|
bloc_thirdparty: Tiers professionnels
|
||||||
|
bloc_users: T(M)S
|
||||||
|
location: Localisation
|
||||||
|
choose_location: Choisissez une localisation
|
||||||
|
choose_location_type: Choisissez un type de localisation
|
||||||
|
create_new_location: Créer une nouvelle localisation
|
||||||
|
location_fields:
|
||||||
|
name: Nom
|
||||||
|
type: Type
|
||||||
|
phonenumber1: Téléphone
|
||||||
|
phonenumber2: Autre téléphone
|
||||||
|
email: Adresse courriel
|
||||||
|
create_address: Créer une adresse
|
||||||
|
edit_address: Modifier l'adresse
|
||||||
|
|
||||||
No documents: Aucun document
|
No documents: Aucun document
|
||||||
|
|
||||||
# activity filter in list page
|
# activity filter in list page
|
||||||
@@ -210,6 +247,7 @@ Documents label: Libellé du champ Documents
|
|||||||
# activity type category admin
|
# activity type category admin
|
||||||
ActivityTypeCategory list: Liste des catégories des types d'échange
|
ActivityTypeCategory list: Liste des catégories des types d'échange
|
||||||
Create a new activity type category: Créer une nouvelle catégorie de type d'échange
|
Create a new activity type category: Créer une nouvelle catégorie de type d'échange
|
||||||
|
Create a new activity in accompanying course: Créer un échange dans le parcours
|
||||||
|
|
||||||
# activity delete
|
# activity delete
|
||||||
Remove activity: Supprimer un échange
|
Remove activity: Supprimer un échange
|
||||||
|
@@ -49,13 +49,13 @@
|
|||||||
<li>
|
<li>
|
||||||
<span>
|
<span>
|
||||||
<abbr class="referrer" title={{ 'Created by'|trans }}>{{ 'By'|trans }}:</abbr>
|
<abbr class="referrer" title={{ 'Created by'|trans }}>{{ 'By'|trans }}:</abbr>
|
||||||
<b>{{ entity.createdBy|chill_entity_render_box }}</b>
|
<b>{{ entity.createdBy|chill_entity_render_box({'at_date': entity.date}) }}</b>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span>
|
<span>
|
||||||
<abbr class="referrer" title={{ 'Created for'|trans }}>{{ 'For'|trans }}:</abbr>
|
<abbr class="referrer" title={{ 'Created for'|trans }}>{{ 'For'|trans }}:</abbr>
|
||||||
<b>{{ entity.agent|chill_entity_render_box }}</b>
|
<b>{{ entity.agent|chill_entity_render_box({'at_date': entity.date}) }}</b>
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
@@ -18,11 +18,11 @@
|
|||||||
<dd>{{ entity.type|chill_entity_render_box }}</dd>
|
<dd>{{ entity.type|chill_entity_render_box }}</dd>
|
||||||
|
|
||||||
<dt class="inline">{{ 'Created by'|trans }}</dt>
|
<dt class="inline">{{ 'Created by'|trans }}</dt>
|
||||||
<dd>{{ entity.createdBy }}</dd>
|
<dd>{{ entity.createdBy|chill_entity_render_box({'at_date': entity.date}) }}</dd>
|
||||||
|
|
||||||
<dt class="inline">{{ 'Created for'|trans }}</dt>
|
<dt class="inline">{{ 'Created for'|trans }}</dt>
|
||||||
<dd>{{ entity.agent }}</dd>
|
<dd>{{ entity.agent|chill_entity_render_box({'at_date': entity.date}) }}</dd>
|
||||||
|
|
||||||
<dt class="inline">{{ 'Asideactivity location'|trans }}</dt>
|
<dt class="inline">{{ 'Asideactivity location'|trans }}</dt>
|
||||||
{%- if entity.location.name is defined -%}
|
{%- if entity.location.name is defined -%}
|
||||||
<dd>{{ entity.location.name }}</dd>
|
<dd>{{ entity.location.name }}</dd>
|
||||||
|
@@ -72,21 +72,21 @@ days: jours
|
|||||||
1 hour 30: 1 heure 30
|
1 hour 30: 1 heure 30
|
||||||
1 hour 45: 1 heure 45
|
1 hour 45: 1 heure 45
|
||||||
2 hours: 2 heures
|
2 hours: 2 heures
|
||||||
2 hours 30: 2 heure 30
|
2 hours 30: 2 heures 30
|
||||||
3 hours: 3 heures
|
3 hours: 3 heures
|
||||||
3 hours 30: 3 heure 30
|
3 hours 30: 3 heures 30
|
||||||
4 hours: 4 heures
|
4 hours: 4 heures
|
||||||
4 hours 30: 4 heure 30
|
4 hours 30: 4 heures 30
|
||||||
5 hours: 5 heures
|
5 hours: 5 heures
|
||||||
5 hours 30: 5 heure 30
|
5 hours 30: 5 heures 30
|
||||||
6 hours: 6 heures
|
6 hours: 6 heures
|
||||||
6 hours 30: 6 heure 30
|
6 hours 30: 6 heures 30
|
||||||
7 hours: 7 heures
|
7 hours: 7 heures
|
||||||
7 hours 30: 7 heure 30
|
7 hours 30: 7 heures 30
|
||||||
8 hours: 8 heures
|
8 hours: 8 heures
|
||||||
8 hours 30: 8 heure 30
|
8 hours 30: 8 heures 30
|
||||||
9 hours: 9 heures
|
9 hours: 9 heures
|
||||||
9 hours 30: 9 heure 30
|
9 hours 30: 9 heures 30
|
||||||
10 hours: 10 heures
|
10 hours: 10 heures
|
||||||
1/2 day: 1/2 jour
|
1/2 day: 1/2 jour
|
||||||
1 day: 1 jour
|
1 day: 1 jour
|
||||||
|
@@ -54,7 +54,7 @@ abstract class AbstractElementController extends AbstractController
|
|||||||
$indexPage = 'chill_budget_elements_household_index';
|
$indexPage = 'chill_budget_elements_household_index';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Request::METHOD_DELETE === $request->getMethod()) {
|
if (Request::METHOD_POST === $request->getMethod()) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
@@ -198,10 +198,9 @@ abstract class AbstractElementController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Creates a form to delete a help request entity by id.
|
* Creates a form to delete a help request entity by id.
|
||||||
*/
|
*/
|
||||||
private function createDeleteForm(): Form
|
private function createDeleteForm(): \Symfony\Component\Form\FormInterface
|
||||||
{
|
{
|
||||||
return $this->createFormBuilder()
|
return $this->createFormBuilder()
|
||||||
->setMethod(Request::METHOD_DELETE)
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@@ -100,7 +100,7 @@ class Charge extends AbstractElement implements HasCentersInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setHelp($help)
|
public function setHelp(?string $help)
|
||||||
{
|
{
|
||||||
$this->help = $help;
|
$this->help = $help;
|
||||||
|
|
||||||
|
@@ -15,9 +15,9 @@ use Chill\BudgetBundle\Entity\ChargeKind;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
final class ChargeKindRepository implements ChargeKindRepositoryInterface
|
final readonly class ChargeKindRepository implements ChargeKindRepositoryInterface
|
||||||
{
|
{
|
||||||
private readonly EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $entityManager)
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
{
|
{
|
||||||
|
@@ -15,9 +15,9 @@ use Chill\BudgetBundle\Entity\ResourceKind;
|
|||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
final class ResourceKindRepository implements ResourceKindRepositoryInterface
|
final readonly class ResourceKindRepository implements ResourceKindRepositoryInterface
|
||||||
{
|
{
|
||||||
private readonly EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $entityManager)
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
{
|
{
|
||||||
|
@@ -84,7 +84,7 @@ class CalendarController extends AbstractController
|
|||||||
|
|
||||||
$form = $this->createDeleteForm($entity);
|
$form = $this->createDeleteForm($entity);
|
||||||
|
|
||||||
if (Request::METHOD_DELETE === $request->getMethod()) {
|
if (Request::METHOD_POST === $request->getMethod()) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isValid()) {
|
if ($form->isValid()) {
|
||||||
@@ -512,7 +512,6 @@ class CalendarController extends AbstractController
|
|||||||
{
|
{
|
||||||
return $this->createFormBuilder()
|
return $this->createFormBuilder()
|
||||||
->setAction($this->generateUrl('chill_calendar_calendar_delete', ['id' => $calendar->getId()]))
|
->setAction($this->generateUrl('chill_calendar_calendar_delete', ['id' => $calendar->getId()]))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@@ -440,6 +440,16 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
|
|||||||
return $this->startDate;
|
return $this->startDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the date of the calendar.
|
||||||
|
*
|
||||||
|
* Useful for showing the date of the calendar event, required by twig in some places.
|
||||||
|
*/
|
||||||
|
public function getDate(): ?\DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->getStartDate();
|
||||||
|
}
|
||||||
|
|
||||||
public function getStatus(): ?string
|
public function getStatus(): ?string
|
||||||
{
|
{
|
||||||
return $this->status;
|
return $this->status;
|
||||||
|
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\CalendarBundle\Menu;
|
||||||
|
|
||||||
|
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
|
||||||
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
|
use Knp\Menu\MenuItem;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
final readonly class AccompanyingCourseQuickMenuBuilder implements LocalMenuBuilderInterface
|
||||||
|
{
|
||||||
|
public function __construct(private Security $security) {}
|
||||||
|
|
||||||
|
public static function getMenuIds(): array
|
||||||
|
{
|
||||||
|
return ['accompanying_course_quick_menu'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||||
|
{
|
||||||
|
/** @var \Chill\PersonBundle\Entity\AccompanyingPeriod $accompanyingCourse */
|
||||||
|
$accompanyingCourse = $parameters['accompanying-course'];
|
||||||
|
|
||||||
|
if ($this->security->isGranted(CalendarVoter::CREATE, $accompanyingCourse)) {
|
||||||
|
$menu
|
||||||
|
->addChild('Create a new calendar in accompanying course', [
|
||||||
|
'route' => 'chill_calendar_calendar_new',
|
||||||
|
'routeParameters' => [
|
||||||
|
'accompanying_period_id' => $accompanyingCourse->getId(),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->setExtras([
|
||||||
|
'order' => 20,
|
||||||
|
'icon' => 'plus',
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -37,12 +37,12 @@ class RemoteEventConverter
|
|||||||
* valid when the remote string contains also a timezone, like in
|
* valid when the remote string contains also a timezone, like in
|
||||||
* lastModifiedDate.
|
* lastModifiedDate.
|
||||||
*/
|
*/
|
||||||
final public const REMOTE_DATETIMEZONE_FORMAT = 'Y-m-d\\TH:i:s.u?P';
|
final public const REMOTE_DATETIMEZONE_FORMAT = 'Y-m-d\TH:i:s.u?P';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as above, but sometimes the date is expressed with only 6 milliseconds.
|
* Same as above, but sometimes the date is expressed with only 6 milliseconds.
|
||||||
*/
|
*/
|
||||||
final public const REMOTE_DATETIMEZONE_FORMAT_ALT = 'Y-m-d\\TH:i:s.uP';
|
final public const REMOTE_DATETIMEZONE_FORMAT_ALT = 'Y-m-d\TH:i:s.uP';
|
||||||
|
|
||||||
private const REMOTE_DATE_FORMAT = 'Y-m-d\TH:i:s.u0';
|
private const REMOTE_DATE_FORMAT = 'Y-m-d\TH:i:s.u0';
|
||||||
|
|
||||||
|
@@ -1 +1,2 @@
|
|||||||
import './scss/badge.scss';
|
import './scss/badge.scss';
|
||||||
|
import './scss/calendar-list.scss';
|
||||||
|
@@ -0,0 +1,26 @@
|
|||||||
|
ul.calendar-list {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
& > li {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
& > li:nth-child(n+2) {
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.calendar-list {
|
||||||
|
|
||||||
|
ul.calendar-list {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > a.calendar-list__global {
|
||||||
|
display: inline-block;;
|
||||||
|
padding: 0.2rem;
|
||||||
|
min-width: 2rem;
|
||||||
|
border: 1px solid var(--bs-chill-blue);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
@@ -55,7 +55,7 @@
|
|||||||
<div class="item-col">
|
<div class="item-col">
|
||||||
<ul class="list-content">
|
<ul class="list-content">
|
||||||
{% if calendar.mainUser is not empty %}
|
{% if calendar.mainUser is not empty %}
|
||||||
<span class="badge-user">{{ calendar.mainUser|chill_entity_render_box }}</span>
|
<span class="badge-user">{{ calendar.mainUser|chill_entity_render_box({'at_date': calendar.startDate}) }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
<li class="cancel">
|
<li class="cancel">
|
||||||
<span class="createdBy">
|
<span class="createdBy">
|
||||||
{{ 'Created by'|trans }}
|
{{ 'Created by'|trans }}
|
||||||
<b>{{ calendar.activity.createdBy|chill_entity_render_string }}</b>, {{ 'on'|trans }} {{ calendar.activity.createdAt|format_datetime('short', 'short') }}
|
<b>{{ calendar.activity.createdBy|chill_entity_render_string({'at_date': calendar.activity.createdAt}) }}</b>, {{ 'on'|trans }} {{ calendar.activity.createdAt|format_datetime('short', 'short') }}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
{% if is_granted('CHILL_ACTIVITY_SEE', calendar.activity) %}
|
{% if is_granted('CHILL_ACTIVITY_SEE', calendar.activity) %}
|
||||||
|
@@ -89,7 +89,7 @@ class CalendarVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
|
|||||||
switch ($attribute) {
|
switch ($attribute) {
|
||||||
case self::SEE:
|
case self::SEE:
|
||||||
case self::CREATE:
|
case self::CREATE:
|
||||||
if (AccompanyingPeriod::STEP_DRAFT === $subject->getStep()) {
|
if (AccompanyingPeriod::STEP_DRAFT === $subject->getStep() || AccompanyingPeriod::STEP_CLOSED === $subject->getStep()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,6 +26,7 @@ The calendar item has been successfully removed.: Le rendez-vous a été supprim
|
|||||||
From the day: Du
|
From the day: Du
|
||||||
to the day: au
|
to the day: au
|
||||||
Transform to activity: Transformer en échange
|
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 send SMS: Un SMS de rappel sera envoyé
|
||||||
Will not send SMS: Aucun SMS de rappel ne sera envoyé
|
Will not send SMS: Aucun SMS de rappel ne sera envoyé
|
||||||
SMS already sent: Un SMS a été envoyé
|
SMS already sent: Un SMS a été envoyé
|
||||||
|
@@ -172,11 +172,9 @@ class CustomField
|
|||||||
/**
|
/**
|
||||||
* Set active.
|
* Set active.
|
||||||
*
|
*
|
||||||
* @param bool $active
|
|
||||||
*
|
|
||||||
* @return CustomField
|
* @return CustomField
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
@@ -224,18 +222,16 @@ class CustomField
|
|||||||
/**
|
/**
|
||||||
* Set order.
|
* Set order.
|
||||||
*
|
*
|
||||||
* @param float $order
|
|
||||||
*
|
|
||||||
* @return CustomField
|
* @return CustomField
|
||||||
*/
|
*/
|
||||||
public function setOrdering($order)
|
public function setOrdering(?float $order)
|
||||||
{
|
{
|
||||||
$this->ordering = $order;
|
$this->ordering = $order;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRequired($required)
|
public function setRequired(bool $required)
|
||||||
{
|
{
|
||||||
$this->required = $required;
|
$this->required = $required;
|
||||||
|
|
||||||
@@ -245,7 +241,7 @@ class CustomField
|
|||||||
/**
|
/**
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setSlug($slug)
|
public function setSlug(?string $slug)
|
||||||
{
|
{
|
||||||
$this->slug = $slug;
|
$this->slug = $slug;
|
||||||
|
|
||||||
@@ -255,11 +251,9 @@ class CustomField
|
|||||||
/**
|
/**
|
||||||
* Set type.
|
* Set type.
|
||||||
*
|
*
|
||||||
* @param string $type
|
|
||||||
*
|
|
||||||
* @return CustomField
|
* @return CustomField
|
||||||
*/
|
*/
|
||||||
public function setType($type)
|
public function setType(?string $type)
|
||||||
{
|
{
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
|
|
||||||
|
@@ -129,7 +129,7 @@ class Option
|
|||||||
/**
|
/**
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ class Option
|
|||||||
/**
|
/**
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setInternalKey($internal_key)
|
public function setInternalKey(string $internal_key)
|
||||||
{
|
{
|
||||||
$this->internalKey = $internal_key;
|
$this->internalKey = $internal_key;
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ class Option
|
|||||||
/**
|
/**
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setKey($key)
|
public function setKey(?string $key)
|
||||||
{
|
{
|
||||||
$this->key = $key;
|
$this->key = $key;
|
||||||
|
|
||||||
|
@@ -69,7 +69,7 @@ class CustomFieldsDefaultGroup
|
|||||||
*
|
*
|
||||||
* @return CustomFieldsDefaultGroup
|
* @return CustomFieldsDefaultGroup
|
||||||
*/
|
*/
|
||||||
public function setCustomFieldsGroup($customFieldsGroup)
|
public function setCustomFieldsGroup(?CustomFieldsGroup $customFieldsGroup)
|
||||||
{
|
{
|
||||||
$this->customFieldsGroup = $customFieldsGroup;
|
$this->customFieldsGroup = $customFieldsGroup;
|
||||||
|
|
||||||
@@ -79,11 +79,9 @@ class CustomFieldsDefaultGroup
|
|||||||
/**
|
/**
|
||||||
* Set entity.
|
* Set entity.
|
||||||
*
|
*
|
||||||
* @param string $entity
|
|
||||||
*
|
|
||||||
* @return CustomFieldsDefaultGroup
|
* @return CustomFieldsDefaultGroup
|
||||||
*/
|
*/
|
||||||
public function setEntity($entity)
|
public function setEntity(?string $entity)
|
||||||
{
|
{
|
||||||
$this->entity = $entity;
|
$this->entity = $entity;
|
||||||
|
|
||||||
|
@@ -165,11 +165,9 @@ class CustomFieldsGroup
|
|||||||
/**
|
/**
|
||||||
* Set entity.
|
* Set entity.
|
||||||
*
|
*
|
||||||
* @param string $entity
|
|
||||||
*
|
|
||||||
* @return CustomFieldsGroup
|
* @return CustomFieldsGroup
|
||||||
*/
|
*/
|
||||||
public function setEntity($entity)
|
public function setEntity(?string $entity)
|
||||||
{
|
{
|
||||||
$this->entity = $entity;
|
$this->entity = $entity;
|
||||||
|
|
||||||
|
@@ -21,14 +21,14 @@ use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
|
|||||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
final class RelatorioDriver implements DriverInterface
|
final readonly class RelatorioDriver implements DriverInterface
|
||||||
{
|
{
|
||||||
private readonly string $url;
|
private string $url;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly HttpClientInterface $client,
|
private HttpClientInterface $client,
|
||||||
ParameterBagInterface $parameterBag,
|
ParameterBagInterface $parameterBag,
|
||||||
private readonly LoggerInterface $logger
|
private LoggerInterface $logger
|
||||||
) {
|
) {
|
||||||
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
|
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
|
||||||
}
|
}
|
||||||
|
@@ -16,11 +16,11 @@ use Doctrine\ORM\EntityManagerInterface;
|
|||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
final class DocGeneratorTemplateRepository implements DocGeneratorTemplateRepositoryInterface
|
final readonly class DocGeneratorTemplateRepository implements DocGeneratorTemplateRepositoryInterface
|
||||||
{
|
{
|
||||||
private readonly EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $entityManager, private readonly RequestStack $requestStack)
|
public function __construct(EntityManagerInterface $entityManager, private RequestStack $requestStack)
|
||||||
{
|
{
|
||||||
$this->repository = $entityManager->getRepository(DocGeneratorTemplate::class);
|
$this->repository = $entityManager->getRepository(DocGeneratorTemplate::class);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\DocGeneratorBundle\Test;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T of object
|
||||||
|
*/
|
||||||
|
abstract class DocGenNormalizerTestAbstract extends KernelTestCase
|
||||||
|
{
|
||||||
|
public function testNullValueHasSameKeysAsNull(): void
|
||||||
|
{
|
||||||
|
$normalizedObject = $this->getNormalizer()->normalize($this->provideNotNullObject(), 'docgen', [
|
||||||
|
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => $this->provideDocGenExpectClass(),
|
||||||
|
]);
|
||||||
|
$nullNormalizedObject = $this->getNormalizer()->normalize(null, 'docgen', [
|
||||||
|
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => $this->provideDocGenExpectClass(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertEqualsCanonicalizing(array_keys($normalizedObject), array_keys($nullNormalizedObject));
|
||||||
|
self::assertArrayHasKey('isNull', $nullNormalizedObject, 'each object must have an "isNull" key');
|
||||||
|
self::assertTrue($nullNormalizedObject['isNull'], 'isNull key must be true for null objects');
|
||||||
|
self::assertFalse($normalizedObject['isNull'], 'isNull key must be false for null objects');
|
||||||
|
|
||||||
|
foreach ($normalizedObject as $key => $value) {
|
||||||
|
if (in_array($key, ['isNull', 'type'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($value)) {
|
||||||
|
if (array_is_list($value)) {
|
||||||
|
self::assertEquals([], $nullNormalizedObject[$key], "list must be serialized as an empty array, in {$key}");
|
||||||
|
} else {
|
||||||
|
self::assertEqualsCanonicalizing(array_keys($value), array_keys($nullNormalizedObject[$key]), "sub-object must have the same keys, in {$key}");
|
||||||
|
}
|
||||||
|
} elseif (is_string($value)) {
|
||||||
|
self::assertEquals('', $nullNormalizedObject[$key], 'strings must be ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return T
|
||||||
|
*/
|
||||||
|
abstract public function provideNotNullObject(): object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return class-string<T>
|
||||||
|
*/
|
||||||
|
abstract public function provideDocGenExpectClass(): string;
|
||||||
|
|
||||||
|
abstract public function getNormalizer(): NormalizerInterface;
|
||||||
|
}
|
@@ -129,7 +129,7 @@ class Document implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUser($user): self
|
public function setUser(?\Chill\MainBundle\Entity\User $user): self
|
||||||
{
|
{
|
||||||
$this->user = $user;
|
$this->user = $user;
|
||||||
|
|
||||||
|
@@ -86,7 +86,7 @@ class DocumentCategory
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setDocumentClass($documentClass): self
|
public function setDocumentClass(?string $documentClass): self
|
||||||
{
|
{
|
||||||
$this->documentClass = $documentClass;
|
$this->documentClass = $documentClass;
|
||||||
|
|
||||||
|
@@ -55,14 +55,14 @@ class PersonDocument extends Document implements HasCenterInterface, HasScopeInt
|
|||||||
return $this->scope;
|
return $this->scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPerson($person): self
|
public function setPerson(Person $person): self
|
||||||
{
|
{
|
||||||
$this->person = $person;
|
$this->person = $person;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setScope($scope): self
|
public function setScope(?Scope $scope): self
|
||||||
{
|
{
|
||||||
$this->scope = $scope;
|
$this->scope = $scope;
|
||||||
|
|
||||||
|
@@ -313,4 +313,19 @@ class StoredObject implements Document, TrackCreationInterface
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function saveHistory(): void
|
||||||
|
{
|
||||||
|
if ('' === $this->getFilename()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->datas['history'][] = [
|
||||||
|
'filename' => $this->getFilename(),
|
||||||
|
'iv' => $this->getIv(),
|
||||||
|
'key_infos' => $this->getKeyInfos(),
|
||||||
|
'type' => $this->getType(),
|
||||||
|
'before' => (new \DateTimeImmutable('now'))->getTimestamp(),
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -57,8 +57,8 @@ class StoredObjectDataMapper implements DataMapperInterface
|
|||||||
|
|
||||||
/** @var StoredObject $viewData */
|
/** @var StoredObject $viewData */
|
||||||
if ($viewData->getFilename() !== $forms['stored_object']->getData()['filename']) {
|
if ($viewData->getFilename() !== $forms['stored_object']->getData()['filename']) {
|
||||||
// we do not want to erase the previous object
|
// we want to keep the previous history
|
||||||
$viewData = new StoredObject();
|
$viewData->saveHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
$viewData->setFilename($forms['stored_object']->getData()['filename']);
|
$viewData->setFilename($forms['stored_object']->getData()['filename']);
|
||||||
|
@@ -4,13 +4,13 @@
|
|||||||
Actions
|
Actions
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li v-if="props.canEdit && is_extension_editable(props.storedObject.type)">
|
<li v-if="props.canEdit && is_extension_editable(props.storedObject.type) && props.storedObject.status !== 'stored_object_created'">
|
||||||
<wopi-edit-button :stored-object="props.storedObject" :classes="{'dropdown-item': true}" :execute-before-leave="props.executeBeforeLeave"></wopi-edit-button>
|
<wopi-edit-button :stored-object="props.storedObject" :classes="{'dropdown-item': true}" :execute-before-leave="props.executeBeforeLeave"></wopi-edit-button>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="props.canEdit && is_extension_editable(props.storedObject.type) && props.davLink !== undefined && props.davLinkExpiration !== undefined">
|
<li v-if="props.canEdit && is_extension_editable(props.storedObject.type) && props.davLink !== undefined && props.davLinkExpiration !== undefined">
|
||||||
<desktop-edit-button :classes="{'dropdown-item': true}" :edit-link="props.davLink" :expiration-link="props.davLinkExpiration"></desktop-edit-button>
|
<desktop-edit-button :classes="{'dropdown-item': true}" :edit-link="props.davLink" :expiration-link="props.davLinkExpiration"></desktop-edit-button>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="props.storedObject.type != 'application/pdf' && is_extension_viewable(props.storedObject.type) && props.canConvertPdf">
|
<li v-if="props.storedObject.type != 'application/pdf' && is_extension_viewable(props.storedObject.type) && props.canConvertPdf && props.storedObject.status !== 'stored_object_created'">
|
||||||
<convert-button :stored-object="props.storedObject" :filename="filename" :classes="{'dropdown-item': true}"></convert-button>
|
<convert-button :stored-object="props.storedObject" :filename="filename" :classes="{'dropdown-item': true}"></convert-button>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="props.canDownload">
|
<li v-if="props.canDownload">
|
||||||
|
@@ -13,7 +13,7 @@ import {reactive} from "vue";
|
|||||||
import {StoredObject, StoredObjectCreated} from "../../types";
|
import {StoredObject, StoredObjectCreated} from "../../types";
|
||||||
|
|
||||||
interface ConvertButtonConfig {
|
interface ConvertButtonConfig {
|
||||||
storedObject: StoredObject|StoredObjectCreated,
|
storedObject: StoredObject,
|
||||||
classes: { [key: string]: boolean},
|
classes: { [key: string]: boolean},
|
||||||
filename?: string,
|
filename?: string,
|
||||||
};
|
};
|
||||||
|
@@ -11,7 +11,7 @@ import {build_wopi_editor_link} from "./helpers";
|
|||||||
import {StoredObject, StoredObjectCreated, WopiEditButtonExecutableBeforeLeaveFunction} from "../../types";
|
import {StoredObject, StoredObjectCreated, WopiEditButtonExecutableBeforeLeaveFunction} from "../../types";
|
||||||
|
|
||||||
interface WopiEditButtonConfig {
|
interface WopiEditButtonConfig {
|
||||||
storedObject: StoredObject|StoredObjectCreated,
|
storedObject: StoredObject,
|
||||||
returnPath?: string,
|
returnPath?: string,
|
||||||
classes: {[k: string] : boolean},
|
classes: {[k: string] : boolean},
|
||||||
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction,
|
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction,
|
||||||
|
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Tests\Entity;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class StoredObjectTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
public function testSaveHistory(): void
|
||||||
|
{
|
||||||
|
$storedObject = new StoredObject();
|
||||||
|
$storedObject
|
||||||
|
->setFilename('test_0')
|
||||||
|
->setIv([2, 4, 6, 8])
|
||||||
|
->setKeyInfos(['key' => ['data0' => 'data0']])
|
||||||
|
->setType('text/html');
|
||||||
|
|
||||||
|
$storedObject->saveHistory();
|
||||||
|
|
||||||
|
$storedObject
|
||||||
|
->setFilename('test_1')
|
||||||
|
->setIv([8, 10, 12])
|
||||||
|
->setKeyInfos(['key' => ['data1' => 'data1']])
|
||||||
|
->setType('text/text');
|
||||||
|
|
||||||
|
$storedObject->saveHistory();
|
||||||
|
|
||||||
|
self::assertEquals('test_0', $storedObject->getDatas()['history'][0]['filename']);
|
||||||
|
self::assertEquals([2, 4, 6, 8], $storedObject->getDatas()['history'][0]['iv']);
|
||||||
|
self::assertEquals(['key' => ['data0' => 'data0']], $storedObject->getDatas()['history'][0]['key_infos']);
|
||||||
|
self::assertEquals('text/html', $storedObject->getDatas()['history'][0]['type']);
|
||||||
|
|
||||||
|
self::assertEquals('test_1', $storedObject->getDatas()['history'][1]['filename']);
|
||||||
|
self::assertEquals([8, 10, 12], $storedObject->getDatas()['history'][1]['iv']);
|
||||||
|
self::assertEquals(['key' => ['data1' => 'data1']], $storedObject->getDatas()['history'][1]['key_infos']);
|
||||||
|
self::assertEquals('text/text', $storedObject->getDatas()['history'][1]['type']);
|
||||||
|
}
|
||||||
|
}
|
@@ -56,14 +56,14 @@ class StoredObjectTypeTest extends TypeTestCase
|
|||||||
{"filename":"abcdef","iv":[10, 15, 20, 30],"keyInfos":[],"type":"text/html","status":"object_store_created"}
|
{"filename":"abcdef","iv":[10, 15, 20, 30],"keyInfos":[],"type":"text/html","status":"object_store_created"}
|
||||||
JSON];
|
JSON];
|
||||||
$model = new StoredObject();
|
$model = new StoredObject();
|
||||||
$originalObjectId = spl_object_id($model);
|
$originalObjectId = spl_object_hash($model);
|
||||||
$form = $this->factory->create(StoredObjectType::class, $model, ['has_title' => true]);
|
$form = $this->factory->create(StoredObjectType::class, $model, ['has_title' => true]);
|
||||||
|
|
||||||
$form->submit($formData);
|
$form->submit($formData);
|
||||||
|
|
||||||
$this->assertTrue($form->isSynchronized());
|
$this->assertTrue($form->isSynchronized());
|
||||||
$model = $form->getData();
|
$model = $form->getData();
|
||||||
$this->assertNotEquals($originalObjectId, spl_object_hash($model));
|
$this->assertEquals($originalObjectId, spl_object_hash($model));
|
||||||
$this->assertEquals('abcdef', $model->getFilename());
|
$this->assertEquals('abcdef', $model->getFilename());
|
||||||
$this->assertEquals([10, 15, 20, 30], $model->getIv());
|
$this->assertEquals([10, 15, 20, 30], $model->getIv());
|
||||||
$this->assertEquals('text/html', $model->getType());
|
$this->assertEquals('text/html', $model->getType());
|
||||||
|
@@ -15,7 +15,7 @@ use Chill\EventBundle\Entity\Event;
|
|||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
use Chill\EventBundle\Form\EventType;
|
use Chill\EventBundle\Form\EventType;
|
||||||
use Chill\EventBundle\Form\Type\PickEventType;
|
use Chill\EventBundle\Form\Type\PickEventType;
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Entity\Center;
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
@@ -61,7 +61,7 @@ final class EventController extends AbstractController
|
|||||||
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
|
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/delete', name: 'chill_event__event_delete', requirements: ['event_id' => '\d+'], methods: ['GET', 'DELETE'])]
|
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/delete', name: 'chill_event__event_delete', requirements: ['event_id' => '\d+'], methods: ['GET', 'POST', 'DELETE'])]
|
||||||
public function deleteAction($event_id, Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|Response
|
public function deleteAction($event_id, Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|Response
|
||||||
{
|
{
|
||||||
$em = $this->managerRegistry->getManager();
|
$em = $this->managerRegistry->getManager();
|
||||||
@@ -78,10 +78,10 @@ final class EventController extends AbstractController
|
|||||||
|
|
||||||
$form = $this->createDeleteForm($event_id);
|
$form = $this->createDeleteForm($event_id);
|
||||||
|
|
||||||
if (Request::METHOD_DELETE === $request->getMethod()) {
|
if (Request::METHOD_POST === $request->getMethod()) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
foreach ($participations as $participation) {
|
foreach ($participations as $participation) {
|
||||||
$em->remove($participation);
|
$em->remove($participation);
|
||||||
}
|
}
|
||||||
@@ -108,28 +108,6 @@ final class EventController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays a form to edit an existing Event entity.
|
|
||||||
*/
|
|
||||||
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/edit', name: 'chill_event__event_edit')]
|
|
||||||
public function editAction($event_id): Response
|
|
||||||
{
|
|
||||||
$em = $this->managerRegistry->getManager();
|
|
||||||
|
|
||||||
$entity = $em->getRepository(Event::class)->find($event_id);
|
|
||||||
|
|
||||||
if (!$entity) {
|
|
||||||
throw $this->createNotFoundException('Unable to find Event entity.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$editForm = $this->createEditForm($entity);
|
|
||||||
|
|
||||||
return $this->render('@ChillEvent/Event/edit.html.twig', [
|
|
||||||
'entity' => $entity,
|
|
||||||
'edit_form' => $editForm->createView(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List events subscriptions for a person.
|
* List events subscriptions for a person.
|
||||||
*
|
*
|
||||||
@@ -313,7 +291,7 @@ final class EventController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Edits an existing Event entity.
|
* Edits an existing Event entity.
|
||||||
*/
|
*/
|
||||||
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/update', name: 'chill_event__event_update', methods: ['POST', 'PUT'])]
|
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/edit', name: 'chill_event__event_edit', methods: ['GET', 'POST', 'PUT'])]
|
||||||
public function updateAction(Request $request, $event_id): \Symfony\Component\HttpFoundation\RedirectResponse|Response
|
public function updateAction(Request $request, $event_id): \Symfony\Component\HttpFoundation\RedirectResponse|Response
|
||||||
{
|
{
|
||||||
$em = $this->managerRegistry->getManager();
|
$em = $this->managerRegistry->getManager();
|
||||||
@@ -324,14 +302,20 @@ final class EventController extends AbstractController
|
|||||||
throw $this->createNotFoundException('Unable to find Event entity.');
|
throw $this->createNotFoundException('Unable to find Event entity.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$editForm = $this->createEditForm($entity);
|
$editForm = $this->createForm(EventType::class, $entity, [
|
||||||
|
'center' => $entity->getCenter(),
|
||||||
|
'role' => EventVoter::UPDATE,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$editForm->add('submit', SubmitType::class, ['label' => 'Update']);
|
||||||
|
|
||||||
$editForm->handleRequest($request);
|
$editForm->handleRequest($request);
|
||||||
|
|
||||||
if ($editForm->isValid()) {
|
if ($editForm->isSubmitted() && $editForm->isValid()) {
|
||||||
|
$em->persist($entity);
|
||||||
$em->flush();
|
$em->flush();
|
||||||
|
|
||||||
$this->addFlash('success', $this->translator
|
$this->addFlash('success', $this->translator->trans('The event was updated'));
|
||||||
->trans('The event was updated'));
|
|
||||||
|
|
||||||
return $this->redirectToRoute('chill_event__event_show', ['event_id' => $event_id]);
|
return $this->redirectToRoute('chill_event__event_show', ['event_id' => $event_id]);
|
||||||
}
|
}
|
||||||
@@ -418,7 +402,6 @@ final class EventController extends AbstractController
|
|||||||
$builder->add('event_id', HiddenType::class, [
|
$builder->add('event_id', HiddenType::class, [
|
||||||
'data' => $event->getId(),
|
'data' => $event->getId(),
|
||||||
]);
|
]);
|
||||||
dump($event->getId());
|
|
||||||
|
|
||||||
return $builder->getForm();
|
return $builder->getForm();
|
||||||
}
|
}
|
||||||
@@ -600,29 +583,7 @@ final class EventController extends AbstractController
|
|||||||
->setAction($this->generateUrl('chill_event__event_delete', [
|
->setAction($this->generateUrl('chill_event__event_delete', [
|
||||||
'event_id' => $event_id,
|
'event_id' => $event_id,
|
||||||
]))
|
]))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a form to edit a Event entity.
|
|
||||||
*
|
|
||||||
* @return \Symfony\Component\Form\FormInterface
|
|
||||||
*/
|
|
||||||
private function createEditForm(Event $entity)
|
|
||||||
{
|
|
||||||
$form = $this->createForm(EventType::class, $entity, [
|
|
||||||
'action' => $this->generateUrl('chill_event__event_update', ['event_id' => $entity->getId()]),
|
|
||||||
'method' => 'PUT',
|
|
||||||
'center' => $entity->getCenter(),
|
|
||||||
'role' => 'CHILL_EVENT_CREATE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$form->remove('center');
|
|
||||||
|
|
||||||
$form->add('submit', SubmitType::class, ['label' => 'Update']);
|
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -201,7 +201,7 @@ class EventTypeController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Creates a form to delete a EventType entity by id.
|
* Creates a form to delete a EventType entity by id.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\Form\Form The form
|
* @return \Symfony\Component\Form\FormInterface The form
|
||||||
*/
|
*/
|
||||||
private function createDeleteForm(mixed $id)
|
private function createDeleteForm(mixed $id)
|
||||||
{
|
{
|
||||||
@@ -210,7 +210,6 @@ class EventTypeController extends AbstractController
|
|||||||
'chill_eventtype_admin_delete',
|
'chill_eventtype_admin_delete',
|
||||||
['id' => $id]
|
['id' => $id]
|
||||||
))
|
))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ use Chill\EventBundle\Entity\Event;
|
|||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
use Chill\EventBundle\Form\ParticipationType;
|
use Chill\EventBundle\Form\ParticipationType;
|
||||||
use Chill\EventBundle\Repository\EventRepository;
|
use Chill\EventBundle\Repository\EventRepository;
|
||||||
use Chill\EventBundle\Security\Authorization\ParticipationVoter;
|
use Chill\EventBundle\Security\ParticipationVoter;
|
||||||
use Chill\PersonBundle\Repository\PersonRepository;
|
use Chill\PersonBundle\Repository\PersonRepository;
|
||||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
@@ -259,10 +259,10 @@ final class ParticipationController extends AbstractController
|
|||||||
|
|
||||||
$form = $this->createDeleteForm($participation_id);
|
$form = $this->createDeleteForm($participation_id);
|
||||||
|
|
||||||
if (Request::METHOD_DELETE === $request->getMethod()) {
|
if (Request::METHOD_POST === $request->getMethod()) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$em->remove($participation);
|
$em->remove($participation);
|
||||||
$em->flush();
|
$em->flush();
|
||||||
|
|
||||||
@@ -753,7 +753,6 @@ final class ParticipationController extends AbstractController
|
|||||||
->setAction($this->generateUrl('chill_event_participation_delete', [
|
->setAction($this->generateUrl('chill_event_participation_delete', [
|
||||||
'participation_id' => $participation_id,
|
'participation_id' => $participation_id,
|
||||||
]))
|
]))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@@ -201,7 +201,7 @@ class RoleController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Creates a form to delete a Role entity by id.
|
* Creates a form to delete a Role entity by id.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\Form\Form The form
|
* @return \Symfony\Component\Form\FormInterface The form
|
||||||
*/
|
*/
|
||||||
private function createDeleteForm(mixed $id)
|
private function createDeleteForm(mixed $id)
|
||||||
{
|
{
|
||||||
|
@@ -201,13 +201,12 @@ class StatusController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Creates a form to delete a Status entity by id.
|
* Creates a form to delete a Status entity by id.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\Form\Form The form
|
* @return \Symfony\Component\Form\FormInterface The form
|
||||||
*/
|
*/
|
||||||
private function createDeleteForm(mixed $id)
|
private function createDeleteForm(mixed $id)
|
||||||
{
|
{
|
||||||
return $this->createFormBuilder()
|
return $this->createFormBuilder()
|
||||||
->setAction($this->generateUrl('chill_event_admin_status_delete', ['id' => $id]))
|
->setAction($this->generateUrl('chill_event_admin_status_delete', ['id' => $id]))
|
||||||
->setMethod('DELETE')
|
|
||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
@@ -11,8 +11,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\EventBundle\DependencyInjection;
|
namespace Chill\EventBundle\DependencyInjection;
|
||||||
|
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\EventBundle\Security\Authorization\ParticipationVoter;
|
use Chill\EventBundle\Security\ParticipationVoter;
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||||
@@ -33,12 +33,13 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface
|
|||||||
|
|
||||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
||||||
$loader->load('services.yaml');
|
$loader->load('services.yaml');
|
||||||
$loader->load('services/authorization.yaml');
|
$loader->load('services/security.yaml');
|
||||||
$loader->load('services/fixtures.yaml');
|
$loader->load('services/fixtures.yaml');
|
||||||
$loader->load('services/forms.yaml');
|
$loader->load('services/forms.yaml');
|
||||||
$loader->load('services/repositories.yaml');
|
$loader->load('services/repositories.yaml');
|
||||||
$loader->load('services/search.yaml');
|
$loader->load('services/search.yaml');
|
||||||
$loader->load('services/timeline.yaml');
|
$loader->load('services/timeline.yaml');
|
||||||
|
$loader->load('services/export.yaml');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** (non-PHPdoc).
|
/** (non-PHPdoc).
|
||||||
|
@@ -47,7 +47,7 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter
|
|||||||
private ?Scope $circle = null;
|
private ?Scope $circle = null;
|
||||||
|
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_MUTABLE)]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_MUTABLE)]
|
||||||
private ?\DateTime $date;
|
private ?\DateTime $date = null;
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
||||||
@@ -265,11 +265,9 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter
|
|||||||
/**
|
/**
|
||||||
* Set label.
|
* Set label.
|
||||||
*
|
*
|
||||||
* @param string $label
|
|
||||||
*
|
|
||||||
* @return Event
|
* @return Event
|
||||||
*/
|
*/
|
||||||
public function setName($label)
|
public function setName(?string $label)
|
||||||
{
|
{
|
||||||
$this->name = $label;
|
$this->name = $label;
|
||||||
|
|
||||||
|
@@ -146,11 +146,9 @@ class EventType
|
|||||||
/**
|
/**
|
||||||
* Set active.
|
* Set active.
|
||||||
*
|
*
|
||||||
* @param bool $active
|
|
||||||
*
|
|
||||||
* @return EventType
|
* @return EventType
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
|
@@ -81,11 +81,9 @@ class Role
|
|||||||
/**
|
/**
|
||||||
* Set active.
|
* Set active.
|
||||||
*
|
*
|
||||||
* @param bool $active
|
|
||||||
*
|
|
||||||
* @return Role
|
* @return Role
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
|
@@ -81,11 +81,9 @@ class Status
|
|||||||
/**
|
/**
|
||||||
* Set active.
|
* Set active.
|
||||||
*
|
*
|
||||||
* @param bool $active
|
|
||||||
*
|
|
||||||
* @return Status
|
* @return Status
|
||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive(bool $active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
|
|
||||||
|
@@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
class EventDateAggregator implements AggregatorInterface
|
||||||
|
{
|
||||||
|
private const CHOICES = [
|
||||||
|
'by month' => 'month',
|
||||||
|
'by week' => 'week',
|
||||||
|
'by year' => 'year',
|
||||||
|
];
|
||||||
|
|
||||||
|
private const DEFAULT_CHOICE = 'year';
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$order = null;
|
||||||
|
|
||||||
|
switch ($data['frequency']) {
|
||||||
|
case 'month':
|
||||||
|
$fmt = 'YYYY-MM';
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'week':
|
||||||
|
$fmt = 'YYYY-IW';
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'year':
|
||||||
|
$fmt = 'YYYY';
|
||||||
|
$order = 'DESC';
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \RuntimeException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->addSelect(sprintf("TO_CHAR(event.date, '%s') AS date_aggregator", $fmt));
|
||||||
|
$qb->addGroupBy('date_aggregator');
|
||||||
|
$qb->addOrderBy('date_aggregator', $order);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder->add('frequency', ChoiceType::class, [
|
||||||
|
'choices' => self::CHOICES,
|
||||||
|
'multiple' => false,
|
||||||
|
'expanded' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return ['frequency' => self::DEFAULT_CHOICE];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data)
|
||||||
|
{
|
||||||
|
return static function ($value) use ($data): string {
|
||||||
|
if ('_header' === $value) {
|
||||||
|
return 'by '.$data['frequency'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return match ($data['frequency']) {
|
||||||
|
default => $value,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data): array
|
||||||
|
{
|
||||||
|
return ['date_aggregator'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return 'Group event by date';
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\EventTypeRepository;
|
||||||
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
class EventTypeAggregator implements AggregatorInterface
|
||||||
|
{
|
||||||
|
final public const KEY = 'event_type_aggregator';
|
||||||
|
|
||||||
|
public function __construct(protected EventTypeRepository $eventTypeRepository, protected TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
if (!\in_array('eventtype', $qb->getAllAliases(), true)) {
|
||||||
|
$qb->leftJoin('event.type', 'eventtype');
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->addSelect(sprintf('IDENTITY(event.type) AS %s', self::KEY));
|
||||||
|
$qb->addGroupBy(self::KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
// no form required for this aggregator
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data): \Closure
|
||||||
|
{
|
||||||
|
return function (int|string|null $value): string {
|
||||||
|
if ('_header' === $value) {
|
||||||
|
return 'Event type';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $value || '' === $value || null === $t = $this->eventTypeRepository->find($value)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->translatableStringHelper->localize($t->getName());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data): array
|
||||||
|
{
|
||||||
|
return [self::KEY];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Group by event type';
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\RoleRepository;
|
||||||
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
class RoleAggregator implements AggregatorInterface
|
||||||
|
{
|
||||||
|
final public const KEY = 'part_role_aggregator';
|
||||||
|
|
||||||
|
public function __construct(protected RoleRepository $roleRepository, protected TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
if (!\in_array('event_part', $qb->getAllAliases(), true)) {
|
||||||
|
$qb->leftJoin('event_part.role', 'role');
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->addSelect(sprintf('IDENTITY(event_part.role) AS %s', self::KEY));
|
||||||
|
$qb->addGroupBy(self::KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT_PARTICIPANTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
// no form required for this aggregator
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data): \Closure
|
||||||
|
{
|
||||||
|
return function (int|string|null $value): string {
|
||||||
|
if ('_header' === $value) {
|
||||||
|
return 'Participant role';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $value || '' === $value || null === $r = $this->roleRepository->find($value)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->translatableStringHelper->localize($r->getName());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data): array
|
||||||
|
{
|
||||||
|
return [self::KEY];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Group by participant role';
|
||||||
|
}
|
||||||
|
}
|
22
src/Bundle/ChillEventBundle/Export/Declarations.php
Normal file
22
src/Bundle/ChillEventBundle/Export/Declarations.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class declare constants used for the export framework.
|
||||||
|
*/
|
||||||
|
abstract class Declarations
|
||||||
|
{
|
||||||
|
final public const EVENT = 'event';
|
||||||
|
|
||||||
|
final public const EVENT_PARTICIPANTS = 'event_participants';
|
||||||
|
}
|
@@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\ParticipationRepository;
|
||||||
|
use Chill\EventBundle\Security\ParticipationVoter;
|
||||||
|
use Chill\MainBundle\Export\ExportInterface;
|
||||||
|
use Chill\MainBundle\Export\FormatterInterface;
|
||||||
|
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||||
|
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||||
|
use Doctrine\ORM\Query;
|
||||||
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
|
||||||
|
|
||||||
|
readonly class CountEventParticipations implements ExportInterface, GroupedExportInterface
|
||||||
|
{
|
||||||
|
private bool $filterStatsByCenters;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private ParticipationRepository $participationRepository,
|
||||||
|
ParameterBagInterface $parameterBag,
|
||||||
|
) {
|
||||||
|
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder) {}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllowedFormattersTypes()
|
||||||
|
{
|
||||||
|
return [FormatterInterface::TYPE_TABULAR];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
return 'Count participants to an event by various parameters.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroup(): string
|
||||||
|
{
|
||||||
|
return 'Exports of events';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data)
|
||||||
|
{
|
||||||
|
if ('export_count_event_participants' !== $key) {
|
||||||
|
throw new \LogicException("the key {$key} is not used by this export");
|
||||||
|
}
|
||||||
|
|
||||||
|
return static fn ($value) => '_header' === $value ? 'Count event participants' : $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data)
|
||||||
|
{
|
||||||
|
return ['export_count_event_participants'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult($query, $data)
|
||||||
|
{
|
||||||
|
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Count event participants';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT_PARTICIPANTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||||
|
{
|
||||||
|
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||||
|
|
||||||
|
$qb = $this->participationRepository
|
||||||
|
->createQueryBuilder('event_part')
|
||||||
|
->join('event_part.person', 'person');
|
||||||
|
|
||||||
|
$qb->select('COUNT(event_part.id) as export_count_event_participants');
|
||||||
|
|
||||||
|
if ($this->filterStatsByCenters) {
|
||||||
|
$qb
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->exists(
|
||||||
|
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||||
|
AND acl_count_person_history.center IN (:authorized_centers)
|
||||||
|
'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->setParameter('authorized_centers', $centers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requiredRole(): string
|
||||||
|
{
|
||||||
|
return ParticipationVoter::STATS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsModifiers()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Declarations::EVENT_PARTICIPANTS,
|
||||||
|
PersonDeclarations::PERSON_TYPE,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
126
src/Bundle/ChillEventBundle/Export/Export/CountEvents.php
Normal file
126
src/Bundle/ChillEventBundle/Export/Export/CountEvents.php
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Repository\EventRepository;
|
||||||
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
|
use Chill\MainBundle\Export\ExportInterface;
|
||||||
|
use Chill\MainBundle\Export\FormatterInterface;
|
||||||
|
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||||
|
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||||
|
use Doctrine\ORM\Query;
|
||||||
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
|
||||||
|
|
||||||
|
readonly class CountEvents implements ExportInterface, GroupedExportInterface
|
||||||
|
{
|
||||||
|
private bool $filterStatsByCenters;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private EventRepository $eventRepository,
|
||||||
|
ParameterBagInterface $parameterBag,
|
||||||
|
) {
|
||||||
|
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder) {}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllowedFormattersTypes()
|
||||||
|
{
|
||||||
|
return [FormatterInterface::TYPE_TABULAR];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
return 'Count events by various parameters.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroup(): string
|
||||||
|
{
|
||||||
|
return 'Exports of events';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLabels($key, array $values, $data)
|
||||||
|
{
|
||||||
|
if ('export_count_event' !== $key) {
|
||||||
|
throw new \LogicException("the key {$key} is not used by this export");
|
||||||
|
}
|
||||||
|
|
||||||
|
return static fn ($value) => '_header' === $value ? 'Number of events' : $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data)
|
||||||
|
{
|
||||||
|
return ['export_count_event'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult($query, $data)
|
||||||
|
{
|
||||||
|
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Count events';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||||
|
{
|
||||||
|
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||||
|
|
||||||
|
$qb = $this->eventRepository
|
||||||
|
->createQueryBuilder('event')
|
||||||
|
->leftJoin('event.participations', 'epart')
|
||||||
|
->leftJoin('epart.person', 'person');
|
||||||
|
|
||||||
|
$qb->select('COUNT(DISTINCT event.id) as export_count_event');
|
||||||
|
|
||||||
|
if ($this->filterStatsByCenters) {
|
||||||
|
$qb
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->exists(
|
||||||
|
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||||
|
AND acl_count_person_history.center IN (:authorized_centers)
|
||||||
|
'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->setParameter('authorized_centers', $centers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requiredRole(): string
|
||||||
|
{
|
||||||
|
return EventVoter::STATS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsModifiers()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Declarations::EVENT,
|
||||||
|
PersonDeclarations::PERSON_TYPE,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||||
|
use Doctrine\ORM\Query\Expr;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
class EventDateFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
public function __construct(protected TranslatorInterface $translator, private readonly RollingDateConverterInterface $rollingDateConverter) {}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$where = $qb->getDQLPart('where');
|
||||||
|
$clause = $qb->expr()->between(
|
||||||
|
'event.date',
|
||||||
|
':date_from',
|
||||||
|
':date_to'
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($where instanceof Expr\Andx) {
|
||||||
|
$where->add($clause);
|
||||||
|
} else {
|
||||||
|
$where = $qb->expr()->andX($clause);
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb->add('where', $where);
|
||||||
|
$qb->setParameter(
|
||||||
|
'date_from',
|
||||||
|
$this->rollingDateConverter->convert($data['date_from'])
|
||||||
|
);
|
||||||
|
$qb->setParameter(
|
||||||
|
'date_to',
|
||||||
|
$this->rollingDateConverter->convert($data['date_to'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('date_from', PickRollingDateType::class, [
|
||||||
|
'label' => 'Events after this date',
|
||||||
|
])
|
||||||
|
->add('date_to', PickRollingDateType::class, [
|
||||||
|
'label' => 'Events before this date',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return ['date_from' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), 'date_to' => new RollingDate(RollingDate::T_TODAY)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAction($data, $format = 'string')
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Filtered by date of event: only between %date_from% and %date_to%',
|
||||||
|
[
|
||||||
|
'%date_from%' => $this->rollingDateConverter->convert($data['date_from'])->format('d-m-Y'),
|
||||||
|
'%date_to%' => $this->rollingDateConverter->convert($data['date_to'])->format('d-m-Y'),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Filtered by event date';
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\EventType;
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\EventTypeRepository;
|
||||||
|
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||||
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
|
||||||
|
class EventTypeFilter implements ExportElementValidatedInterface, FilterInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
|
protected EventTypeRepository $eventTypeRepository
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$clause = $qb->expr()->in('event.type', ':selected_event_types');
|
||||||
|
|
||||||
|
$qb->andWhere($clause);
|
||||||
|
$qb->setParameter('selected_event_types', $data['types']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder->add('types', EntityType::class, [
|
||||||
|
'choices' => $this->eventTypeRepository->findAllActive(),
|
||||||
|
'class' => EventType::class,
|
||||||
|
'choice_label' => fn (EventType $ety) => $this->translatableStringHelper->localize($ety->getName()),
|
||||||
|
'multiple' => true,
|
||||||
|
'expanded' => false,
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'select2',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAction($data, $format = 'string')
|
||||||
|
{
|
||||||
|
$typeNames = array_map(
|
||||||
|
fn (EventType $t): string => $this->translatableStringHelper->localize($t->getName()),
|
||||||
|
$this->eventTypeRepository->findBy(['id' => $data['types'] instanceof \Doctrine\Common\Collections\Collection ? $data['types']->toArray() : $data['types']])
|
||||||
|
);
|
||||||
|
|
||||||
|
return ['Filtered by event type: only %list%', [
|
||||||
|
'%list%' => implode(', ', $typeNames),
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Filtered by event type';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateForm($data, ExecutionContextInterface $context)
|
||||||
|
{
|
||||||
|
if (null === $data['types'] || 0 === \count($data['types'])) {
|
||||||
|
$context
|
||||||
|
->buildViolation('At least one type must be chosen')
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
src/Bundle/ChillEventBundle/Export/Filter/RoleFilter.php
Normal file
94
src/Bundle/ChillEventBundle/Export/Filter/RoleFilter.php
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Role;
|
||||||
|
use Chill\EventBundle\Export\Declarations;
|
||||||
|
use Chill\EventBundle\Repository\RoleRepository;
|
||||||
|
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||||
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
|
||||||
|
class RoleFilter implements ExportElementValidatedInterface, FilterInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
|
protected RoleRepository $roleRepository
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$clause = $qb->expr()->in('event_part.role', ':selected_part_roles');
|
||||||
|
|
||||||
|
$qb->andWhere($clause);
|
||||||
|
$qb->setParameter('selected_part_roles', $data['part_roles']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::EVENT_PARTICIPANTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder->add('part_roles', EntityType::class, [
|
||||||
|
'choices' => $this->roleRepository->findAllActive(),
|
||||||
|
'class' => Role::class,
|
||||||
|
'choice_label' => fn (Role $r) => $this->translatableStringHelper->localize($r->getName()),
|
||||||
|
'multiple' => true,
|
||||||
|
'expanded' => false,
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'select2',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAction($data, $format = 'string')
|
||||||
|
{
|
||||||
|
$roleNames = array_map(
|
||||||
|
fn (Role $r): string => $this->translatableStringHelper->localize($r->getName()),
|
||||||
|
$this->roleRepository->findBy(['id' => $data['part_roles'] instanceof \Doctrine\Common\Collections\Collection ? $data['part_roles']->toArray() : $data['part_roles']])
|
||||||
|
);
|
||||||
|
|
||||||
|
return ['Filtered by participant roles: only %list%', [
|
||||||
|
'%list%' => implode(', ', $roleNames),
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'Filter by participant roles';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateForm($data, ExecutionContextInterface $context)
|
||||||
|
{
|
||||||
|
if (null === $data['part_roles'] || 0 === \count($data['part_roles'])) {
|
||||||
|
$context
|
||||||
|
->buildViolation('At least one role must be chosen')
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -13,6 +13,7 @@ namespace Chill\EventBundle\Form;
|
|||||||
|
|
||||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
use Chill\DocStoreBundle\Form\StoredObjectType;
|
use Chill\DocStoreBundle\Form\StoredObjectType;
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
use Chill\EventBundle\Form\Type\PickEventTypeType;
|
use Chill\EventBundle\Form\Type\PickEventTypeType;
|
||||||
use Chill\MainBundle\Entity\Center;
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Form\Type\ChillCollectionType;
|
use Chill\MainBundle\Form\Type\ChillCollectionType;
|
||||||
@@ -23,6 +24,7 @@ use Chill\MainBundle\Form\Type\PickUserLocationType;
|
|||||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
@@ -31,7 +33,9 @@ class EventType extends AbstractType
|
|||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
$builder
|
$builder
|
||||||
->add('name')
|
->add('name', TextType::class, [
|
||||||
|
'required' => true,
|
||||||
|
])
|
||||||
->add('date', ChillDateTimeType::class, [
|
->add('date', ChillDateTimeType::class, [
|
||||||
'required' => true,
|
'required' => true,
|
||||||
])
|
])
|
||||||
@@ -75,7 +79,7 @@ class EventType extends AbstractType
|
|||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => \Chill\EventBundle\Entity\Event::class,
|
'data_class' => Event::class,
|
||||||
]);
|
]);
|
||||||
$resolver
|
$resolver
|
||||||
->setRequired(['center', 'role'])
|
->setRequired(['center', 'role'])
|
||||||
|
@@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\EventBundle\Menu;
|
namespace Chill\EventBundle\Menu;
|
||||||
|
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
use Knp\Menu\MenuItem;
|
use Knp\Menu\MenuItem;
|
||||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||||
|
@@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\EventBundle\Menu;
|
namespace Chill\EventBundle\Menu;
|
||||||
|
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
use Knp\Menu\MenuItem;
|
use Knp\Menu\MenuItem;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
@@ -13,7 +13,7 @@ namespace Chill\EventBundle\Repository;
|
|||||||
|
|
||||||
use Chill\EventBundle\Entity\Event;
|
use Chill\EventBundle\Entity\Event;
|
||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
use Chill\EventBundle\Security\Authorization\EventVoter;
|
use Chill\EventBundle\Security\EventVoter;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
@@ -12,13 +12,57 @@ declare(strict_types=1);
|
|||||||
namespace Chill\EventBundle\Repository;
|
namespace Chill\EventBundle\Repository;
|
||||||
|
|
||||||
use Chill\EventBundle\Entity\Role;
|
use Chill\EventBundle\Entity\Role;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
class RoleRepository extends ServiceEntityRepository
|
readonly class RoleRepository implements ObjectRepository
|
||||||
{
|
{
|
||||||
public function __construct(ManagerRegistry $registry)
|
private EntityRepository $repository;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $entityManager, private TranslatableStringHelper $translatableStringHelper)
|
||||||
{
|
{
|
||||||
parent::__construct($registry, Role::class);
|
$this->repository = $entityManager->getRepository(Role::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
|
||||||
|
{
|
||||||
|
return $this->repository->createQueryBuilder($alias, $indexBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($id)
|
||||||
|
{
|
||||||
|
return $this->repository->find($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll(): array
|
||||||
|
{
|
||||||
|
return $this->repository->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAllActive(): array
|
||||||
|
{
|
||||||
|
$roles = $this->repository->findBy(['active' => true]);
|
||||||
|
|
||||||
|
usort($roles, fn (Role $a, Role $b) => $this->translatableStringHelper->localize($a->getName()) <=> $this->translatableStringHelper->localize($b->getName()));
|
||||||
|
|
||||||
|
return $roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
|
||||||
|
{
|
||||||
|
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOneBy(array $criteria)
|
||||||
|
{
|
||||||
|
return $this->repository->findOneBy($criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassName(): string
|
||||||
|
{
|
||||||
|
return Role::class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{% import '@ChillPerson/Person/macro.html.twig' as person_macro %}
|
{% import '@ChillPerson/Person/macro.html.twig' as person_macro %}
|
||||||
|
|
||||||
{% if ignored_participations|length > 0 %}
|
{% if ignored_participations|length > 0 %}
|
||||||
<p>{% transchoice ignored_participations|length %}The following people have been ignored because they are already participating on the event{% endtranschoice %} :</p>
|
<p>{{ 'ignored_participations'|trans({'count': ignored_participations|length}) }}:</p>
|
||||||
<ul>
|
<ul>
|
||||||
{% for p in ignored_participations %}
|
{% for p in ignored_participations %}
|
||||||
<li>{{ person_macro.render(p.person) }}</li>
|
<li>{{ person_macro.render(p.person) }}</li>
|
||||||
|
@@ -9,18 +9,19 @@ declare(strict_types=1);
|
|||||||
* the LICENSE file that was distributed with this source code.
|
* the LICENSE file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Chill\EventBundle\Security\Authorization;
|
namespace Chill\EventBundle\Security;
|
||||||
|
|
||||||
use Chill\EventBundle\Entity\Event;
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description of EventVoter.
|
* Description of EventVoter.
|
||||||
@@ -42,61 +43,46 @@ class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInter
|
|||||||
|
|
||||||
final public const UPDATE = 'CHILL_EVENT_UPDATE';
|
final public const UPDATE = 'CHILL_EVENT_UPDATE';
|
||||||
|
|
||||||
/**
|
final public const STATS = 'CHILL_EVENT_STATS';
|
||||||
* @var AccessDecisionManagerInterface
|
|
||||||
*/
|
|
||||||
protected $accessDecisionManager;
|
|
||||||
|
|
||||||
/**
|
private readonly VoterHelperInterface $voterHelper;
|
||||||
* @var AuthorizationHelper
|
|
||||||
*/
|
|
||||||
protected $authorizationHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var LoggerInterface
|
|
||||||
*/
|
|
||||||
protected $logger;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AccessDecisionManagerInterface $accessDecisionManager,
|
private readonly AuthorizationHelper $authorizationHelper,
|
||||||
AuthorizationHelper $authorizationHelper,
|
private readonly LoggerInterface $logger,
|
||||||
LoggerInterface $logger
|
VoterHelperFactoryInterface $voterHelperFactory
|
||||||
) {
|
) {
|
||||||
$this->accessDecisionManager = $accessDecisionManager;
|
$this->voterHelper = $voterHelperFactory
|
||||||
$this->authorizationHelper = $authorizationHelper;
|
->generate(self::class)
|
||||||
$this->logger = $logger;
|
->addCheckFor(null, [self::SEE])
|
||||||
|
->addCheckFor(Event::class, [...self::ROLES])
|
||||||
|
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||||
|
->addCheckFor(Center::class, [self::STATS])
|
||||||
|
->build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoles(): array
|
public function getRoles(): array
|
||||||
{
|
{
|
||||||
return self::ROLES;
|
return [...self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithHierarchy(): array
|
public function getRolesWithHierarchy(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'Event' => self::ROLES,
|
'Event' => $this->getRoles(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithoutScope(): array
|
public function getRolesWithoutScope(): array
|
||||||
{
|
{
|
||||||
return [];
|
return [self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supports($attribute, $subject)
|
public function supports($attribute, $subject)
|
||||||
{
|
{
|
||||||
return ($subject instanceof Event && \in_array($attribute, self::ROLES, true))
|
return $this->voterHelper->supports($attribute, $subject);
|
||||||
|| ($subject instanceof Person && \in_array($attribute, [self::CREATE, self::SEE], true))
|
|
||||||
|| (null === $subject && self::SEE === $attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $attribute
|
|
||||||
* @param Event $subject
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
||||||
{
|
{
|
||||||
$this->logger->debug(sprintf('Voting from %s class', self::class));
|
$this->logger->debug(sprintf('Voting from %s class', self::class));
|
||||||
@@ -118,15 +104,5 @@ class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInter
|
|||||||
->getReachableCenters($token->getUser(), $attribute);
|
->getReachableCenters($token->getUser(), $attribute);
|
||||||
|
|
||||||
return \count($centers) > 0;
|
return \count($centers) > 0;
|
||||||
|
|
||||||
if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->authorizationHelper->userHasAccess(
|
|
||||||
$token->getUser(),
|
|
||||||
$subject,
|
|
||||||
$attribute
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -9,18 +9,19 @@ declare(strict_types=1);
|
|||||||
* the LICENSE file that was distributed with this source code.
|
* the LICENSE file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Chill\EventBundle\Security\Authorization;
|
namespace Chill\EventBundle\Security;
|
||||||
|
|
||||||
use Chill\EventBundle\Entity\Participation;
|
use Chill\EventBundle\Entity\Participation;
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
|
||||||
|
|
||||||
class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
||||||
{
|
{
|
||||||
@@ -39,58 +40,48 @@ class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierar
|
|||||||
|
|
||||||
final public const UPDATE = 'CHILL_EVENT_PARTICIPATION_UPDATE';
|
final public const UPDATE = 'CHILL_EVENT_PARTICIPATION_UPDATE';
|
||||||
|
|
||||||
/**
|
final public const STATS = 'CHILL_EVENT_PARTICIPATION_STATS';
|
||||||
* @var AccessDecisionManagerInterface
|
|
||||||
*/
|
|
||||||
protected $accessDecisionManager;
|
|
||||||
|
|
||||||
/**
|
private readonly VoterHelperInterface $voterHelper;
|
||||||
* @var AuthorizationHelper
|
|
||||||
*/
|
|
||||||
protected $authorizationHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var LoggerInterface
|
|
||||||
*/
|
|
||||||
protected $logger;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AccessDecisionManagerInterface $accessDecisionManager,
|
private readonly AuthorizationHelper $authorizationHelper,
|
||||||
AuthorizationHelper $authorizationHelper,
|
private readonly LoggerInterface $logger,
|
||||||
LoggerInterface $logger
|
VoterHelperFactoryInterface $voterHelperFactory
|
||||||
) {
|
) {
|
||||||
$this->accessDecisionManager = $accessDecisionManager;
|
$this->voterHelper = $voterHelperFactory
|
||||||
$this->authorizationHelper = $authorizationHelper;
|
->generate(self::class)
|
||||||
$this->logger = $logger;
|
->addCheckFor(null, [self::SEE])
|
||||||
|
->addCheckFor(Participation::class, [...self::ROLES])
|
||||||
|
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||||
|
->addCheckFor(Center::class, [self::STATS])
|
||||||
|
->build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoles(): array
|
public function getRoles(): array
|
||||||
{
|
{
|
||||||
return self::ROLES;
|
return [...self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithHierarchy(): array
|
public function getRolesWithHierarchy(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'Event' => self::ROLES,
|
'Participation' => $this->getRoles(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRolesWithoutScope(): array
|
public function getRolesWithoutScope(): array
|
||||||
{
|
{
|
||||||
return [];
|
return [self::ROLES, self::STATS];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supports($attribute, $subject)
|
public function supports($attribute, $subject)
|
||||||
{
|
{
|
||||||
return ($subject instanceof Participation && \in_array($attribute, self::ROLES, true))
|
return $this->voterHelper->supports($attribute, $subject);
|
||||||
|| ($subject instanceof Person && \in_array($attribute, [self::CREATE, self::SEE], true))
|
|
||||||
|| (null === $subject && self::SEE === $attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $attribute
|
* @param string $attribute
|
||||||
* @param Participation $subject
|
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
@@ -115,15 +106,5 @@ class ParticipationVoter extends AbstractChillVoter implements ProvideRoleHierar
|
|||||||
->getReachableCenters($token->getUser(), $attribute);
|
->getReachableCenters($token->getUser(), $attribute);
|
||||||
|
|
||||||
return \count($centers) > 0;
|
return \count($centers) > 0;
|
||||||
|
|
||||||
if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->authorizationHelper->userHasAccess(
|
|
||||||
$token->getUser(),
|
|
||||||
$subject,
|
|
||||||
$attribute
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Tests\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Export\CountEventParticipations;
|
||||||
|
use Doctrine\ORM\AbstractQuery;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class CountEventParticipationsTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
private CountEventParticipations $countEventParticipations;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
self::bootKernel();
|
||||||
|
$this->countEventParticipations = self::getContainer()->get(CountEventParticipations::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExecuteQuery(): void
|
||||||
|
{
|
||||||
|
$qb = $this->countEventParticipations->initiateQuery([], [], [])
|
||||||
|
->setMaxResults(1);
|
||||||
|
|
||||||
|
$results = $qb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||||
|
|
||||||
|
self::assertIsArray($results, 'smoke test: test that the result is an array');
|
||||||
|
}
|
||||||
|
}
|
43
src/Bundle/ChillEventBundle/Tests/Export/CountEventTest.php
Normal file
43
src/Bundle/ChillEventBundle/Tests/Export/CountEventTest.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\EventBundle\Tests\Export;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Export\Export\CountEvents;
|
||||||
|
use Doctrine\ORM\AbstractQuery;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class CountEventTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
private CountEvents $countEvents;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
self::bootKernel();
|
||||||
|
$this->countEvents = self::getContainer()->get(CountEvents::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExecuteQuery(): void
|
||||||
|
{
|
||||||
|
$qb = $this->countEvents->initiateQuery([], [], [])
|
||||||
|
->setMaxResults(1);
|
||||||
|
|
||||||
|
$results = $qb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||||
|
|
||||||
|
self::assertIsArray($results, 'smoke test: test that the result is an array');
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Export\aggregators;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Export\Aggregator\EventDateAggregator;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventDateAggregatorTest extends AbstractAggregatorTest
|
||||||
|
{
|
||||||
|
private $aggregator;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->aggregator = self::getContainer()->get(EventDateAggregator::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAggregator()
|
||||||
|
{
|
||||||
|
return $this->aggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array|\Generator
|
||||||
|
{
|
||||||
|
yield ['frequency' => 'YYYY'];
|
||||||
|
yield ['frequency' => 'YYYY-MM'];
|
||||||
|
yield ['frequency' => 'YYYY-IV'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user