mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-12 17:55:00 +00:00
Compare commits
198 Commits
testing-20
...
v3.0.0
Author | SHA1 | Date | |
---|---|---|---|
94d6b5eff8 | |||
d87f380f16 | |||
58bf722fae | |||
50fb79ebbf | |||
58912f1d98 | |||
9604ba5f4b | |||
b689a51a48 | |||
8c0d2f58ba | |||
212230448b | |||
2bfb8fe387 | |||
6362b98a00 | |||
6e2a08cae8 | |||
305105faae | |||
85811cc6ae | |||
7eee995627 | |||
c0c448fb39 | |||
6445342136 | |||
d52e54fd2a | |||
547a9d1369 | |||
288a02f5b7 | |||
2f9884072c | |||
ee45ff61a6 | |||
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
|
|||
35109133f6
|
|||
a220dad83b
|
|||
9eb571549b
|
|||
db8257d230 | |||
bce93efe83 | |||
06401af801 | |||
ea1d4c48f2
|
|||
|
33cba27dd4 | ||
a7ec7c9f37 | |||
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 | |||
0b739fda34 | |||
9b8e143855 | |||
a533ab77ed | |||
087032881b | |||
82667a1c0f | |||
db6408926b | |||
f5c7ab6ef0 | |||
a13ada2937 | |||
3be8a39a1a | |||
d7eb1e01da | |||
bd62202d22 | |||
0e3de2ec8a | |||
aa2a398f9e | |||
33187448a0 | |||
a4482ad28b | |||
8ed5a023e8 | |||
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,5 +0,0 @@
|
|||||||
kind: Feature
|
|
||||||
body: Add job bundle (module emploi)
|
|
||||||
time: 2024-05-22T16:49:33.730465146+02:00
|
|
||||||
custom:
|
|
||||||
Issue: ""
|
|
@@ -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: ""
|
|
@@ -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
|
5
.changes/v3.0.0.md
Normal file
5
.changes/v3.0.0.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
## v3.0.0 - 2024-08-26
|
||||||
|
### Fixed
|
||||||
|
* Fix delete action for accompanying periods in draft state
|
||||||
|
* Fix connection to azure when making an calendar event in chill
|
||||||
|
* CollectionType js fixes for remove button and adding multiple entries
|
@@ -138,4 +138,4 @@ release:
|
|||||||
- echo "running release_job"
|
- echo "running release_job"
|
||||||
release:
|
release:
|
||||||
tag_name: '$CI_COMMIT_TAG'
|
tag_name: '$CI_COMMIT_TAG'
|
||||||
description: "./.changes/v$CI_COMMIT_TAG.md"
|
description: "./.changes/$CI_COMMIT_TAG.md"
|
||||||
|
93
CHANGELOG.md
93
CHANGELOG.md
@@ -6,6 +6,99 @@ 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).
|
||||||
|
|
||||||
|
|
||||||
|
## v3.0.0 - 2024-08-26
|
||||||
|
### Fixed
|
||||||
|
* Fix delete action for accompanying periods in draft state
|
||||||
|
* Fix connection to azure when making an calendar event in chill
|
||||||
|
* CollectionType js fixes for remove button and adding multiple entries
|
||||||
|
|
||||||
|
## v2.23.0 - 2024-07-23
|
||||||
|
### Feature
|
||||||
|
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
|
||||||
|
* 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",
|
||||||
@@ -92,12 +91,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"
|
||||||
|
@@ -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
|
||||||
************************
|
************************
|
||||||
|
@@ -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",
|
||||||
|
10
rector.php
10
rector.php
@@ -39,10 +39,14 @@ return static function (RectorConfig $rectorConfig): void {
|
|||||||
|
|
||||||
//define sets of rules
|
//define sets of rules
|
||||||
$rectorConfig->sets([
|
$rectorConfig->sets([
|
||||||
\Rector\Set\ValueObject\LevelSetList::UP_TO_PHP_82,
|
LevelSetList::UP_TO_PHP_82,
|
||||||
\Rector\Symfony\Set\SymfonyLevelSetList::UP_TO_SYMFONY_54,
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_40,
|
||||||
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_41,
|
||||||
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_42,
|
||||||
|
\Rector\Symfony\Set\SymfonySetList::SYMFONY_43,
|
||||||
|
\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();
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
|
@@ -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.
|
||||||
@@ -210,6 +222,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,10 +18,10 @@
|
|||||||
<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 -%}
|
||||||
|
@@ -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();
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -47,7 +47,7 @@ final class CalendarContextTest extends TestCase
|
|||||||
{
|
{
|
||||||
$expected =
|
$expected =
|
||||||
[
|
[
|
||||||
'track_datetime' => true,
|
'trackDatetime' => true,
|
||||||
'askMainPerson' => true,
|
'askMainPerson' => true,
|
||||||
'mainPersonLabel' => 'docgen.calendar.Destinee',
|
'mainPersonLabel' => 'docgen.calendar.Destinee',
|
||||||
'askThirdParty' => false,
|
'askThirdParty' => false,
|
||||||
@@ -61,7 +61,7 @@ final class CalendarContextTest extends TestCase
|
|||||||
{
|
{
|
||||||
$expected =
|
$expected =
|
||||||
[
|
[
|
||||||
'track_datetime' => true,
|
'trackDatetime' => true,
|
||||||
'askMainPerson' => true,
|
'askMainPerson' => true,
|
||||||
'mainPersonLabel' => 'docgen.calendar.Destinee',
|
'mainPersonLabel' => 'docgen.calendar.Destinee',
|
||||||
'askThirdParty' => false,
|
'askThirdParty' => 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é
|
||||||
|
@@ -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;
|
||||||
|
}
|
@@ -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]);
|
||||||
}
|
}
|
||||||
@@ -599,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).
|
||||||
|
@@ -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'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -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\EventTypeAggregator;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventTypeAggregatorTest extends AbstractAggregatorTest
|
||||||
|
{
|
||||||
|
private $aggregator;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->aggregator = self::getContainer()->get(EventTypeAggregator::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAggregator()
|
||||||
|
{
|
||||||
|
return $this->aggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,63 @@
|
|||||||
|
<?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\Entity\Participation;
|
||||||
|
use Chill\EventBundle\Export\Aggregator\RoleAggregator;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class RoleAggregatorTest extends AbstractAggregatorTest
|
||||||
|
{
|
||||||
|
private $aggregator;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->aggregator = self::getContainer()->get(RoleAggregator::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAggregator()
|
||||||
|
{
|
||||||
|
return $this->aggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event_part')
|
||||||
|
->from(Participation::class, 'event_part'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -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 Export\filters;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Export\Filter\EventDateFilter;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||||
|
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventDateFilterTest extends AbstractFilterTest
|
||||||
|
{
|
||||||
|
private RollingDateConverterInterface $rollingDateConverter;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->rollingDateConverter = self::getContainer()->get(RollingDateConverterInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilter()
|
||||||
|
{
|
||||||
|
return new EventDateFilter($this->rollingDateConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'date_from' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
|
||||||
|
'date_to' => new RollingDate(RollingDate::T_TODAY),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,76 @@
|
|||||||
|
<?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\filters;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Entity\EventType;
|
||||||
|
use Chill\EventBundle\Export\Filter\EventTypeFilter;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class EventTypeFilterTest extends AbstractFilterTest
|
||||||
|
{
|
||||||
|
private EventTypeFilter $filter;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$this->filter = self::getContainer()->get(EventTypeFilter::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilter(): EventTypeFilter|\Chill\MainBundle\Export\FilterInterface
|
||||||
|
{
|
||||||
|
return $this->filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
$array = $em->createQueryBuilder()
|
||||||
|
->from(EventType::class, 'et')
|
||||||
|
->select('et')
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($array as $a) {
|
||||||
|
$data[] = [
|
||||||
|
'types' => new ArrayCollection([$a]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -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 Export\filters;
|
||||||
|
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Entity\Participation;
|
||||||
|
use Chill\EventBundle\Entity\Role;
|
||||||
|
use Chill\EventBundle\Export\Filter\RoleFilter;
|
||||||
|
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class RoleFilterTest extends AbstractFilterTest
|
||||||
|
{
|
||||||
|
private RoleFilter $filter;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->filter = self::getContainer()->get(RoleFilter::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilter()
|
||||||
|
{
|
||||||
|
return $this->filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormData(): array
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
$array = $em->createQueryBuilder()
|
||||||
|
->from(Role::class, 'r')
|
||||||
|
->select('r')
|
||||||
|
->getQuery()
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getResult();
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($array as $a) {
|
||||||
|
$data[] = [
|
||||||
|
'roles' => new ArrayCollection([$a]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryBuilders()
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event.id')
|
||||||
|
->from(Event::class, 'event'),
|
||||||
|
$em->createQueryBuilder()
|
||||||
|
->select('event_part')
|
||||||
|
->from(Participation::class, 'event_part'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -12,7 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\EventBundle\Tests\Repository;
|
namespace Chill\EventBundle\Tests\Repository;
|
||||||
|
|
||||||
use Chill\EventBundle\Repository\EventACLAwareRepository;
|
use Chill\EventBundle\Repository\EventACLAwareRepository;
|
||||||
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\Scope;
|
use Chill\MainBundle\Entity\Scope;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
|
@@ -1,18 +0,0 @@
|
|||||||
services:
|
|
||||||
chill_event.event_voter:
|
|
||||||
class: Chill\EventBundle\Security\Authorization\EventVoter
|
|
||||||
arguments:
|
|
||||||
- "@security.access.decision_manager"
|
|
||||||
- "@chill.main.security.authorization.helper"
|
|
||||||
- "@logger"
|
|
||||||
tags:
|
|
||||||
- { name: security.voter }
|
|
||||||
|
|
||||||
chill_event.event_participation:
|
|
||||||
class: Chill\EventBundle\Security\Authorization\ParticipationVoter
|
|
||||||
arguments:
|
|
||||||
- "@security.access.decision_manager"
|
|
||||||
- "@chill.main.security.authorization.helper"
|
|
||||||
- "@logger"
|
|
||||||
tags:
|
|
||||||
- { name: security.voter }
|
|
41
src/Bundle/ChillEventBundle/config/services/export.yaml
Normal file
41
src/Bundle/ChillEventBundle/config/services/export.yaml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
|
# indicators
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Export\CountEvents:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export, alias: 'count_events' }
|
||||||
|
Chill\EventBundle\Export\Export\CountEventParticipations:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export, alias: 'count_event_participants' }
|
||||||
|
|
||||||
|
# filters
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Filter\EventDateFilter:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_filter, alias: 'event_date_filter' }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Filter\EventTypeFilter:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_filter, alias: 'event_type_filter' }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Filter\RoleFilter:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_filter, alias: 'role_filter' }
|
||||||
|
|
||||||
|
# aggregators
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Aggregator\EventTypeAggregator:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_aggregator, alias: event_type_aggregator }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Aggregator\EventDateAggregator:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_aggregator, alias: event_date_aggregator }
|
||||||
|
|
||||||
|
Chill\EventBundle\Export\Aggregator\RoleAggregator:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_aggregator, alias: role_aggregator }
|
14
src/Bundle/ChillEventBundle/config/services/security.yaml
Normal file
14
src/Bundle/ChillEventBundle/config/services/security.yaml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
services:
|
||||||
|
Chill\EventBundle\Security\EventVoter:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
tags:
|
||||||
|
- { name: security.voter }
|
||||||
|
- { name: chill.role }
|
||||||
|
|
||||||
|
Chill\EventBundle\Security\ParticipationVoter:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
tags:
|
||||||
|
- { name: security.voter }
|
||||||
|
- { name: chill.role }
|
@@ -19,3 +19,9 @@ events:
|
|||||||
one {et un autre participant}
|
one {et un autre participant}
|
||||||
other {et # autres participants}
|
other {et # autres participants}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ignored_participations: >-
|
||||||
|
{ count, plural,
|
||||||
|
one {La personne suivante a été ignorée parce qu''elle participe déjà à l''événement}
|
||||||
|
other {Les personnes suivantes ont été ignorées parce qu''elles participent déjà à l'événement}
|
||||||
|
}
|
||||||
|
@@ -41,7 +41,6 @@ Back to the event: Retour à l'événement
|
|||||||
The participation was created: La participation a été créée
|
The participation was created: La participation a été créée
|
||||||
The participation was updated: La participation a été mise à jour
|
The participation was updated: La participation a été mise à jour
|
||||||
'None of the requested people may participate the event: they are maybe already participating.': 'Aucune des personnes indiquées ne peut être ajoutée à l''événement: elles sont peut-être déjà inscrites comme participantes.'
|
'None of the requested people may participate the event: they are maybe already participating.': 'Aucune des personnes indiquées ne peut être ajoutée à l''événement: elles sont peut-être déjà inscrites comme participantes.'
|
||||||
'The following people have been ignored because they are already participating on the event': '{1} La personne suivante a été ignorée parce qu''elle participe déjà à l''événement | ]1,Inf] Les personnes suivantes ont été ignorées parce qu''elles participent déjà à l''événement'
|
|
||||||
There are no participation to edit for this event: Il n'y a pas de participation pour cet événement
|
There are no participation to edit for this event: Il n'y a pas de participation pour cet événement
|
||||||
The participations have been successfully updated.: Les participations ont été mises à jour.
|
The participations have been successfully updated.: Les participations ont été mises à jour.
|
||||||
The participation has been sucessfully removed: La participation a été correctement supprimée.
|
The participation has been sucessfully removed: La participation a été correctement supprimée.
|
||||||
@@ -81,9 +80,31 @@ Pick an event: Choisir un événement
|
|||||||
Pick a type of event: Choisir un type d'événement
|
Pick a type of event: Choisir un type d'événement
|
||||||
Pick a moderator: Choisir un animateur
|
Pick a moderator: Choisir un animateur
|
||||||
|
|
||||||
|
# exports
|
||||||
Select a format: Choisir un format
|
Select a format: Choisir un format
|
||||||
Export: Exporter
|
Export: Exporter
|
||||||
|
|
||||||
|
Count events: Nombre d'événements
|
||||||
|
Count events by various parameters.: Compte le nombre d'événements selon divers critères
|
||||||
|
Exports of events: Exports d'événements
|
||||||
|
|
||||||
|
Filtered by event date: Filtrer par date d'événement
|
||||||
|
'Filtered by date of event: only between %date_from% and %date_to%': "Filtré par date d'événement: uniquement entre le %date_from% et le %date_to%"
|
||||||
|
Events after this date: Événements après cette date
|
||||||
|
Events before this date: Événements avant cette date
|
||||||
|
Filtered by event type: Filtrer par type d'événement
|
||||||
|
'Filtered by event type: only %list%': "Filtré par type: uniquement %list%"
|
||||||
|
Group event by date: Grouper par date d'événement
|
||||||
|
Group by event type: Grouper par type d'événement
|
||||||
|
|
||||||
|
Count event participants: Nombre de participations
|
||||||
|
Count participants to an event by various parameters.: Compte le nombre de participations selon divers critères
|
||||||
|
Exports of event participants: Exports de participations
|
||||||
|
'Filtered by participant roles: only %list%': "Filtré par rôles de participation: uniquement %list%"
|
||||||
|
Filter by participant roles: Filtrer par rôles de participation
|
||||||
|
Part roles: Rôles de participation
|
||||||
|
Group by participant role: Grouper par rôle de participation
|
||||||
|
|
||||||
|
|
||||||
Events configuration: Configuration des événements
|
Events configuration: Configuration des événements
|
||||||
Events configuration menu: Menu des événements
|
Events configuration menu: Menu des événements
|
||||||
|
@@ -87,20 +87,27 @@ class PartenaireRomeAppellation
|
|||||||
{
|
{
|
||||||
$bearer = $this->getBearer();
|
$bearer = $this->getBearer();
|
||||||
|
|
||||||
try {
|
while (true) {
|
||||||
$response = $this->httpClient->request('GET', sprintf(self::BASE.'appellation/%s', $code), [
|
try {
|
||||||
'headers' => [
|
$response = $this->httpClient->request('GET', sprintf(self::BASE.'appellation/%s', $code), [
|
||||||
'Authorization' => 'Bearer '.$bearer,
|
'headers' => [
|
||||||
'Accept' => 'application/json',
|
'Authorization' => 'Bearer '.$bearer,
|
||||||
],
|
'Accept' => 'application/json',
|
||||||
'query' => [
|
],
|
||||||
'champs' => 'code,libelle,metier(code,libelle)',
|
'query' => [
|
||||||
],
|
'champs' => 'code,libelle,metier(code,libelle)',
|
||||||
]);
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
return $response->toArray();
|
return $response->toArray();
|
||||||
} catch (HttpExceptionInterface $exception) {
|
} catch (HttpExceptionInterface $exception) {
|
||||||
throw $exception;
|
if (429 === $exception->getResponse()->getStatusCode()) {
|
||||||
|
$retryAfter = $exception->getResponse()->getHeaders(false)['retry-after'][0] ?? 1;
|
||||||
|
sleep((int) $retryAfter);
|
||||||
|
} else {
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -254,11 +254,12 @@
|
|||||||
] %}
|
] %}
|
||||||
|
|
||||||
<dt>{{ r[1] }}</dt>
|
<dt>{{ r[1] }}</dt>
|
||||||
{% set document = attribute(entity, r[0]) %}
|
{% set d = attribute(entity, r[0]) %}
|
||||||
{% if document is null %}
|
{% if d is null %}
|
||||||
<dd>{{ null|chill_print_or_message("Aucun document") }}</dd>
|
<dd>{{ null|chill_print_or_message("Aucun document") }}</dd>
|
||||||
{% else %}
|
{% else %}
|
||||||
<dd>{{ doc.download_button(document, r[1] ~ " de " ~ person.firstName ~ " " ~ person.lastName) }}</dd>
|
{% set title = person.lastname ~ ' ' ~ person.firstname ~ ', ' ~ r[1]|replace({"Document ": ""}) %}
|
||||||
|
<dd>{{ d|chill_document_button_group(title, is_granted('CHILL_PERSON_UPDATE', person), {small: true}) }}</dd>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -273,10 +274,12 @@
|
|||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
<link rel="stylesheet" type="text/css" href="{{ asset('build/async_upload.css') }}" />
|
{{ encore_entry_link_tags('mod_async_upload') }}
|
||||||
|
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||||
{% endblock css %}
|
{% endblock css %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
<script type="text/javascript" src="{{ asset('build/async_upload.js') }}"></script>
|
{{ encore_entry_script_tags('mod_async_upload') }}
|
||||||
|
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||||
{% endblock js %}
|
{% endblock js %}
|
||||||
|
@@ -0,0 +1,40 @@
|
|||||||
|
<?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\MainBundle\Command;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Service\Import\AddressReferenceLU;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class LoadAddressesLUFromBDAddressCommand extends Command
|
||||||
|
{
|
||||||
|
protected static $defaultDescription = 'Import LUX addresses from BD addresses (see https://data.public.lu/fr/datasets/adresses-georeferencees-bd-adresses/)';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly AddressReferenceLU $addressImporter,
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->setName('chill:main:address-ref-lux');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$this->addressImporter->import();
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
@@ -632,7 +632,7 @@ class ExportController extends AbstractController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function rebuildRawData(string $key): array
|
private function rebuildRawData(?string $key): array
|
||||||
{
|
{
|
||||||
if (null === $key) {
|
if (null === $key) {
|
||||||
throw $this->createNotFoundException('key does not exists');
|
throw $this->createNotFoundException('key does not exists');
|
||||||
|
@@ -43,7 +43,7 @@ class ShortMessageCompilerPass implements CompilerPassInterface
|
|||||||
$defaultTransporter = new Reference(NullShortMessageSender::class);
|
$defaultTransporter = new Reference(NullShortMessageSender::class);
|
||||||
} elseif ('ovh' === $dsn['scheme']) {
|
} elseif ('ovh' === $dsn['scheme']) {
|
||||||
if (!class_exists('\\'.\Ovh\Api::class)) {
|
if (!class_exists('\\'.\Ovh\Api::class)) {
|
||||||
throw new RuntimeException('Class \\Ovh\\Api not found');
|
throw new RuntimeException('Class \Ovh\Api not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (['user', 'host', 'pass'] as $component) {
|
foreach (['user', 'host', 'pass'] as $component) {
|
||||||
|
@@ -216,13 +216,13 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
|||||||
return $this->mainLocation;
|
return $this->mainLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMainScope(?\DateTimeImmutable $at = null): ?Scope
|
public function getMainScope(?\DateTimeImmutable $atDate = null): ?Scope
|
||||||
{
|
{
|
||||||
$at ??= new \DateTimeImmutable('now');
|
$atDate ??= new \DateTimeImmutable('now');
|
||||||
|
|
||||||
foreach ($this->scopeHistories as $scopeHistory) {
|
foreach ($this->scopeHistories as $scopeHistory) {
|
||||||
if ($at >= $scopeHistory->getStartDate() && (
|
if ($atDate >= $scopeHistory->getStartDate() && (
|
||||||
null === $scopeHistory->getEndDate() || $at < $scopeHistory->getEndDate()
|
null === $scopeHistory->getEndDate() || $atDate < $scopeHistory->getEndDate()
|
||||||
)) {
|
)) {
|
||||||
return $scopeHistory->getScope();
|
return $scopeHistory->getScope();
|
||||||
}
|
}
|
||||||
@@ -265,13 +265,13 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
|||||||
return $this->salt;
|
return $this->salt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUserJob(?\DateTimeImmutable $at = null): ?UserJob
|
public function getUserJob(?\DateTimeImmutable $atDate = null): ?UserJob
|
||||||
{
|
{
|
||||||
$at ??= new \DateTimeImmutable('now');
|
$atDate ??= new \DateTimeImmutable('now');
|
||||||
|
|
||||||
foreach ($this->jobHistories as $jobHistory) {
|
foreach ($this->jobHistories as $jobHistory) {
|
||||||
if ($at >= $jobHistory->getStartDate() && (
|
if ($atDate >= $jobHistory->getStartDate() && (
|
||||||
null === $jobHistory->getEndDate() || $at < $jobHistory->getEndDate()
|
null === $jobHistory->getEndDate() || $atDate < $jobHistory->getEndDate()
|
||||||
)) {
|
)) {
|
||||||
return $jobHistory->getJob();
|
return $jobHistory->getJob();
|
||||||
}
|
}
|
||||||
@@ -285,6 +285,11 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
|||||||
return $this->jobHistories;
|
return $this->jobHistories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUserScopeHistories(): Collection
|
||||||
|
{
|
||||||
|
return $this->scopeHistories;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ArrayCollection|UserJobHistory[]
|
* @return ArrayCollection|UserJobHistory[]
|
||||||
*/
|
*/
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user