mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-13 18:24:58 +00:00
Compare commits
172 Commits
Author | SHA1 | Date | |
---|---|---|---|
801f799e45
|
|||
98b8f3dcff
|
|||
a333a0312a
|
|||
0288fd22cf
|
|||
1dd4398c43
|
|||
2882038efc
|
|||
6065680e1e
|
|||
88114e3ba6
|
|||
bf93c1ddb2 | |||
0d365e16e5
|
|||
802ff20b5c | |||
cdfe201574 | |||
43419f9f15
|
|||
39896ea6e2
|
|||
ca62c3fd0b
|
|||
b3b84c5dc0
|
|||
6bdb3e9695
|
|||
20e64e8768 | |||
4f4b3dbb44
|
|||
1c3e6e0dba
|
|||
e7ca81e057
|
|||
197d69ef4a
|
|||
9423f4d055 | |||
99d6e9e6b8 | |||
63f9bd5548
|
|||
c8146ded17
|
|||
17d2b795b4 | |||
7f30742fc3
|
|||
56d9072abe
|
|||
7ccff61c25
|
|||
8929f4b8a3 | |||
43b7139488
|
|||
d3251075e9
|
|||
93a598b549
|
|||
9b6e6ec20f
|
|||
77d4b13c1b
|
|||
2861945a52
|
|||
5b42b85b50
|
|||
e40b1b9853
|
|||
c19232de35
|
|||
c95dc23c51
|
|||
c04fd66163
|
|||
0361743ae0 | |||
af4e7f1226
|
|||
ff1629cbb7
|
|||
779eb812b0
|
|||
a990591e0c
|
|||
145c1df313 | |||
7f9738975c | |||
a56370d851
|
|||
3e63b4abf3 | |||
dd344aed52
|
|||
1485d1ce7a | |||
a7dbdc2b9d
|
|||
b3d993165d
|
|||
9ccc57bbcb
|
|||
cc0e832cc9
|
|||
c8b62d990a
|
|||
b7df62d4f5
|
|||
5a395b160f
|
|||
393e59e22b
|
|||
4a5ac170ba
|
|||
c019fffbe7
|
|||
31745bc252
|
|||
56940d830c
|
|||
347eda05df
|
|||
90e8687799
|
|||
9687debb57
|
|||
da50fbc1fb
|
|||
5bbc50976e | |||
01dee54fab | |||
abe020f116
|
|||
4632c18d93
|
|||
7a1feaa8cb
|
|||
687ff63ce7
|
|||
a7c3089736
|
|||
90be68002a
|
|||
a93051d157
|
|||
f19b939bd4
|
|||
9f0fdb031a
|
|||
0e9597bf77
|
|||
769504c497
|
|||
811364e139
|
|||
9978b6a6e4
|
|||
0e5f1b4ab9
|
|||
f7c11d3567
|
|||
51544cfc48
|
|||
659dff3d2c
|
|||
deffc5e4db
|
|||
40ecaab5b4
|
|||
f7be53f790
|
|||
9073f118db
|
|||
21f11fb034
|
|||
50de389bc7
|
|||
960acb8c0a
|
|||
c52ba06ea0
|
|||
3fb97c3945
|
|||
5495b1cb44
|
|||
24049b9dfc
|
|||
34a333f6a3
|
|||
29140d9374
|
|||
5cbdea29e9
|
|||
909d2dfb60
|
|||
1f1ebb6adb
|
|||
4456fb3749
|
|||
727e9d0f74
|
|||
c2a734b30b | |||
68c850026b | |||
aa6479fbf9
|
|||
05822e7e4a
|
|||
b4614974c0
|
|||
c3ac382711
|
|||
ad82685c02
|
|||
7e8dbbe873
|
|||
12fdfd81c4
|
|||
cf576dca7b
|
|||
7fab411b96
|
|||
88d363fc0c
|
|||
a28740c46c
|
|||
1a03718014
|
|||
1e8fec5cbd
|
|||
fb1b28407a
|
|||
cc30c81fd7
|
|||
938027cc1e
|
|||
db14221729
|
|||
f10c50231f
|
|||
73bc95306e
|
|||
933e9f75b3
|
|||
3adf3625dc
|
|||
ea77adc640
|
|||
d5ee158caa
|
|||
02afcb30d4
|
|||
cb0a6bbd21
|
|||
fb0afc7e0a
|
|||
d1e1b1c4ce
|
|||
2aeb72811a | |||
7eb4fb4e56 | |||
5dc1cbce48 | |||
59e1e02b92 | |||
5196d26a3e | |||
9a3fcf081e | |||
ef04a04056 | |||
ba55fa349b | |||
eea5cedc5f | |||
c07e26785e | |||
4155af6686 | |||
9eb9a9a214 | |||
47a3e30ec5 | |||
20489813f0 | |||
cb718a80de
|
|||
2b57807565
|
|||
da36c59616
|
|||
40ddd1f1ee
|
|||
e9fdabf931
|
|||
40af1e64ac
|
|||
c245ffe559 | |||
bd074ebade | |||
d09e5d33db | |||
101cca8662 | |||
90a5a735aa | |||
eb107f5a15
|
|||
a3d3588b75
|
|||
08874d734e
|
|||
2b5d007fda
|
|||
e550817ded
|
|||
8dbe2d6ec2
|
|||
afcd6e0605
|
|||
6fb01b19ec
|
|||
b8ecff4f08
|
|||
4c340dd086
|
|||
8f1955c536
|
|||
c9c15cdd56
|
5
.changes/unreleased/Feature-20230707-123609.yaml
Normal file
5
.changes/unreleased/Feature-20230707-123609.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
kind: Feature
|
||||||
|
body: '[export] Add a list for people with their associated course'
|
||||||
|
time: 2023-07-07T12:36:09.596469063+02:00
|
||||||
|
custom:
|
||||||
|
Issue: "125"
|
6
.changes/unreleased/Feature-20230707-124132.yaml
Normal file
6
.changes/unreleased/Feature-20230707-124132.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Feature
|
||||||
|
body: '[export] Add ordering by person''s lastname or course opening date in list
|
||||||
|
which concerns accompanying course or peoples'
|
||||||
|
time: 2023-07-07T12:41:32.112725962+02:00
|
||||||
|
custom:
|
||||||
|
Issue: ""
|
5
.changes/unreleased/Feature-20230711-150055.yaml
Normal file
5
.changes/unreleased/Feature-20230711-150055.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
kind: Feature
|
||||||
|
body: '[Export] allow to group activities by localisation'
|
||||||
|
time: 2023-07-11T15:00:55.770070399+02:00
|
||||||
|
custom:
|
||||||
|
Issue: "128"
|
5
.changes/unreleased/Feature-20230711-155929.yaml
Normal file
5
.changes/unreleased/Feature-20230711-155929.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
kind: Feature
|
||||||
|
body: '[export] Add a filter "filter course having an activity between two dates"'
|
||||||
|
time: 2023-07-11T15:59:29.065329834+02:00
|
||||||
|
custom:
|
||||||
|
Issue: "129"
|
42
.changes/v2.3.0.md
Normal file
42
.changes/v2.3.0.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
## v2.3.0 - 2023-06-27
|
||||||
|
### Feature
|
||||||
|
* ([#110](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/110)) Edit saved exports options: the saved exports options (forms, filters, aggregators) are now editable.
|
||||||
|
* ([#103](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/103)) Get an unified list of document in person and accompanying period context
|
||||||
|
* [export] Set the default date of calculation of the accompanying period's list as "today"
|
||||||
|
* Force accompanying period user history to be unique for the same period and stardate/enddate [:warning: may encounter migration issue]
|
||||||
|
|
||||||
|
If some issue is encountered during migration, use this SQL to find the line which are in conflict, examine the problem and delete some of the concerning line
|
||||||
|
*
|
||||||
|
```sql
|
||||||
|
-- to see the line which are in conflict with another one
|
||||||
|
SELECT o.*
|
||||||
|
FROM chill_person_accompanying_period_user_history o
|
||||||
|
JOIN chill_person_accompanying_period_user_history c ON o.id < c.id AND o.accompanyingperiod_id = c.accompanyingperiod_id
|
||||||
|
WHERE tsrange(o.startdate, o.enddate, '[)') && tsrange(c.startdate, c.enddate, '[)')
|
||||||
|
ORDER BY accompanyingperiod_id;
|
||||||
|
-- to examine line in conflict for a given accompanyingperiod_id (given by the previous query)
|
||||||
|
SELECT * FROM chill_person_accompanying_period_user_history WHERE accompanyingperiod_id = IIIIDDDD order by startdate, enddate;
|
||||||
|
```
|
||||||
|
* Rename label of filter in French: "parcours actif" => "parcours ouvert", and "filtrer les parcours ouverts" => "Filtrer les parcours dont la date d'ouverture"
|
||||||
|
|
||||||
|
### Traduction francophone des principaux changements
|
||||||
|
|
||||||
|
* Les exports enregistrés sont éditables par l'utilisateur;
|
||||||
|
* L'onglet "Document" dans les parcours et les dossiers d'usager affiche désormais les documents ajoutés à différents endroits.
|
||||||
|
|
||||||
|
Pour les parcours, il s'agit de:
|
||||||
|
|
||||||
|
- documents ajoutés directement dans le parcours;
|
||||||
|
- documents des échanges;
|
||||||
|
- documents des rendez-vous;
|
||||||
|
- documents des évaluations;
|
||||||
|
- documents directement ajoutés dans le dossier des usagers concernés par le parcours;
|
||||||
|
|
||||||
|
Pour les usagers, il s'agit de:
|
||||||
|
|
||||||
|
- documents des échanges;
|
||||||
|
- documents des parcours;
|
||||||
|
- documents des rendez-vous;
|
||||||
|
- documents des actions, des échanges, des rendez-vous, des évaluations ajoutés dans les parcours.
|
||||||
|
* Dans la liste des parcours, la date de calcul des éléments associés est "aujourd'hui" par défaut.
|
||||||
|
* Dans les exports, renommage des libellés des filtres: "parcours actif" => "parcours ouvert", et "filtrer les parcours ouverts" => "Filtrer les parcours dont la date d'ouverture"
|
36
.changes/v2.4.0.md
Normal file
36
.changes/v2.4.0.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
## v2.4.0 - 2023-07-07
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] on "filter by user working" on accompanying period, add two dates to filters intervention within a period
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] Add an aggregator by user's job working on a course
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add an aggregator by user's scope working on a course
|
||||||
|
* [export] on aggregator "user working on a course"
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add a center aggregator for Person
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add a filter on "job working on a course"
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] Add a filter on "scope working on a course"
|
||||||
|
* ([#121](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/121)) Create a role "See Confidential Periods", separated from the "Reassign courses" role
|
||||||
|
* ([#124](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/124)) Sync user absence / presence through microsoft outlook / graph api.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* ([#116](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/116)) On the accompanying course page, open the action on view mode if the user does not have right to update them (i.e. if the accompanying period is closed)
|
||||||
|
* [export] Rename label for CurrentActionFilter (on accompanying period work) to make precision between "ouvert" and "sans date de fin"
|
||||||
|
* Force the db to have either a person_location or a address_location, and avoid to have both also internally in the entity
|
||||||
|
* [export] set rolling date on person age aggregator
|
||||||
|
* [export] fix list when a person locating a course is without address
|
||||||
|
* [export] remove unused condition on course about duration participation
|
||||||
|
* Command to subscribe on MS Graph users calendars: improve the loop to be more efficient
|
||||||
|
|
||||||
|
### DX
|
||||||
|
* Rolling Date: can receive a null parameter
|
||||||
|
|
||||||
|
### Traduction francophone des principaux changements
|
||||||
|
|
||||||
|
- sur le "filtre par intervenant", ajoute deux dates pour limiter la période d'intervention;
|
||||||
|
- ajout d'un regroupement par métier des intervenants sur un parcours;
|
||||||
|
- ajout d'un regroupement par service des intervenants sur un parcours;
|
||||||
|
- ajout d'un regroupement par utilisateur intervenant sur un parcours
|
||||||
|
- ajout d'un regroupement "par centre de l'usager";
|
||||||
|
- ajout d'un filtre "par métier intervenant sur un parcours";
|
||||||
|
- ajout d'un filtre "par service intervenant sur un parcours";
|
||||||
|
- création d'un rôle spécifique pour voir les parcours confidentiels (et séparer de celui de la liste qui permet de ré-assigner les parcours en lot);
|
||||||
|
- synchronisation de l'absence des utilisateurs par microsoft graph api
|
@@ -5,8 +5,11 @@ changelogPath: CHANGELOG.md
|
|||||||
versionExt: md
|
versionExt: md
|
||||||
versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}'
|
versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}'
|
||||||
kindFormat: '### {{.Kind}}'
|
kindFormat: '### {{.Kind}}'
|
||||||
|
# Note: it is possible to add a `.custom.Long` text manually into the yaml file produced by `changie new`. This will add a long description.
|
||||||
changeFormat: >-
|
changeFormat: >-
|
||||||
* {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{ end }}{{.Body}}
|
* {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{ end }}{{.Body}} {{ if not (eq .Custom.Long "") }}
|
||||||
|
|
||||||
|
{{ .Custom.Long }}{{ end }}
|
||||||
custom:
|
custom:
|
||||||
- key: Issue
|
- key: Issue
|
||||||
label: Issue number (on chill-bundles repository) (optional)
|
label: Issue number (on chill-bundles repository) (optional)
|
||||||
@@ -27,6 +30,8 @@ kinds:
|
|||||||
auto: patch
|
auto: patch
|
||||||
- label: DX
|
- label: DX
|
||||||
auto: patch
|
auto: patch
|
||||||
|
- label: UX
|
||||||
|
auto: patch
|
||||||
newlines:
|
newlines:
|
||||||
afterChangelogHeader: 1
|
afterChangelogHeader: 1
|
||||||
beforeChangelogVersion: 1
|
beforeChangelogVersion: 1
|
||||||
|
@@ -13,6 +13,7 @@ $finder = PhpCsFixer\Finder::create();
|
|||||||
|
|
||||||
$finder
|
$finder
|
||||||
->in(__DIR__.'/src')
|
->in(__DIR__.'/src')
|
||||||
|
->in(__DIR__.'/utils')
|
||||||
->append([__FILE__])
|
->append([__FILE__])
|
||||||
->exclude(['docs/', 'tests/app'])
|
->exclude(['docs/', 'tests/app'])
|
||||||
->notPath('tests/app')
|
->notPath('tests/app')
|
||||||
|
80
CHANGELOG.md
80
CHANGELOG.md
@@ -6,6 +6,86 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
|||||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||||
|
|
||||||
|
|
||||||
|
## v2.4.0 - 2023-07-07
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] on "filter by user working" on accompanying period, add two dates to filters intervention within a period
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] Add an aggregator by user's job working on a course
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add an aggregator by user's scope working on a course
|
||||||
|
* [export] on aggregator "user working on a course"
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add a center aggregator for Person
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add a filter on "job working on a course"
|
||||||
|
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] Add a filter on "scope working on a course"
|
||||||
|
* ([#121](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/121)) Create a role "See Confidential Periods", separated from the "Reassign courses" role
|
||||||
|
* ([#124](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/124)) Sync user absence / presence through microsoft outlook / graph api.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* ([#116](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/116)) On the accompanying course page, open the action on view mode if the user does not have right to update them (i.e. if the accompanying period is closed)
|
||||||
|
* [export] Rename label for CurrentActionFilter (on accompanying period work) to make precision between "ouvert" and "sans date de fin"
|
||||||
|
* Force the db to have either a person_location or a address_location, and avoid to have both also internally in the entity
|
||||||
|
* [export] set rolling date on person age aggregator
|
||||||
|
* [export] fix list when a person locating a course is without address
|
||||||
|
* [export] remove unused condition on course about duration participation
|
||||||
|
* Command to subscribe on MS Graph users calendars: improve the loop to be more efficient
|
||||||
|
|
||||||
|
### DX
|
||||||
|
* Rolling Date: can receive a null parameter
|
||||||
|
|
||||||
|
### Traduction francophone des principaux changements
|
||||||
|
|
||||||
|
- sur le "filtre par intervenant", ajoute deux dates pour limiter la période d'intervention;
|
||||||
|
- ajout d'un regroupement par métier des intervenants sur un parcours;
|
||||||
|
- ajout d'un regroupement par service des intervenants sur un parcours;
|
||||||
|
- ajout d'un regroupement par utilisateur intervenant sur un parcours
|
||||||
|
- ajout d'un regroupement "par centre de l'usager";
|
||||||
|
- ajout d'un filtre "par métier intervenant sur un parcours";
|
||||||
|
- ajout d'un filtre "par service intervenant sur un parcours";
|
||||||
|
- création d'un rôle spécifique pour voir les parcours confidentiels (et séparer de celui de la liste qui permet de ré-assigner les parcours en lot);
|
||||||
|
- synchronisation de l'absence des utilisateurs par microsoft graph api
|
||||||
|
|
||||||
|
## v2.3.0 - 2023-06-27
|
||||||
|
### Feature
|
||||||
|
* ([#110](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/110)) Edit saved exports options: the saved exports options (forms, filters, aggregators) are now editable.
|
||||||
|
* ([#103](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/103)) Get an unified list of document in person and accompanying period context
|
||||||
|
* [export] Set the default date of calculation of the accompanying period's list as "today"
|
||||||
|
* Force accompanying period user history to be unique for the same period and stardate/enddate [:warning: may encounter migration issue]
|
||||||
|
|
||||||
|
If some issue is encountered during migration, use this SQL to find the line which are in conflict, examine the problem and delete some of the concerning line
|
||||||
|
*
|
||||||
|
```sql
|
||||||
|
-- to see the line which are in conflict with another one
|
||||||
|
SELECT o.*
|
||||||
|
FROM chill_person_accompanying_period_user_history o
|
||||||
|
JOIN chill_person_accompanying_period_user_history c ON o.id < c.id AND o.accompanyingperiod_id = c.accompanyingperiod_id
|
||||||
|
WHERE tsrange(o.startdate, o.enddate, '[)') && tsrange(c.startdate, c.enddate, '[)')
|
||||||
|
ORDER BY accompanyingperiod_id;
|
||||||
|
-- to examine line in conflict for a given accompanyingperiod_id (given by the previous query)
|
||||||
|
SELECT * FROM chill_person_accompanying_period_user_history WHERE accompanyingperiod_id = IIIIDDDD order by startdate, enddate;
|
||||||
|
```
|
||||||
|
* Rename label of filter in French: "parcours actif" => "parcours ouvert", and "filtrer les parcours ouverts" => "Filtrer les parcours dont la date d'ouverture"
|
||||||
|
|
||||||
|
### Traduction francophone des principaux changements
|
||||||
|
|
||||||
|
* Les exports enregistrés sont éditables par l'utilisateur;
|
||||||
|
* L'onglet "Document" dans les parcours et les dossiers d'usager affiche désormais les documents ajoutés à différents endroits.
|
||||||
|
|
||||||
|
Pour les parcours, il s'agit de:
|
||||||
|
|
||||||
|
- documents ajoutés directement dans le parcours;
|
||||||
|
- documents des échanges;
|
||||||
|
- documents des rendez-vous;
|
||||||
|
- documents des évaluations;
|
||||||
|
- documents directement ajoutés dans le dossier des usagers concernés par le parcours;
|
||||||
|
|
||||||
|
Pour les usagers, il s'agit de:
|
||||||
|
|
||||||
|
- documents des échanges;
|
||||||
|
- documents des parcours;
|
||||||
|
- documents des rendez-vous;
|
||||||
|
- documents des actions, des échanges, des rendez-vous, des évaluations ajoutés dans les parcours.
|
||||||
|
* Dans la liste des parcours, la date de calcul des éléments associés est "aujourd'hui" par défaut.
|
||||||
|
* Dans les exports, renommage des libellés des filtres: "parcours actif" => "parcours ouvert", et "filtrer les parcours ouverts" => "Filtrer les parcours dont la date d'ouverture"
|
||||||
|
|
||||||
## v2.2.2 - 2023-06-26
|
## v2.2.2 - 2023-06-26
|
||||||
### Fixed
|
### Fixed
|
||||||
* [Accompanying period comments]: order comments from the most recent to the oldest, in the list
|
* [Accompanying period comments]: order comments from the most recent to the oldest, in the list
|
||||||
|
@@ -67,6 +67,7 @@
|
|||||||
"fakerphp/faker": "^1.13",
|
"fakerphp/faker": "^1.13",
|
||||||
"jangregor/phpstan-prophecy": "^1.0",
|
"jangregor/phpstan-prophecy": "^1.0",
|
||||||
"nelmio/alice": "^3.8",
|
"nelmio/alice": "^3.8",
|
||||||
|
"nikic/php-parser": "^4.15",
|
||||||
"phpspec/prophecy-phpunit": "^2.0",
|
"phpspec/prophecy-phpunit": "^2.0",
|
||||||
"phpstan/extension-installer": "^1.2",
|
"phpstan/extension-installer": "^1.2",
|
||||||
"phpstan/phpstan": "^1.9",
|
"phpstan/phpstan": "^1.9",
|
||||||
@@ -103,14 +104,16 @@
|
|||||||
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
|
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
|
||||||
"Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
|
"Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
|
||||||
"Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
|
"Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
|
||||||
"Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src"
|
"Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src",
|
||||||
|
"Chill\\Utils\\Rector\\": "utils/rector/src"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"App\\": "tests/app/src/",
|
"App\\": "tests/app/src/",
|
||||||
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
|
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
|
||||||
"Chill\\WopiBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
|
"Chill\\WopiBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
|
||||||
|
"Chill\\Utils\\Rector\\Tests\\": "utils/rector/tests"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
@@ -62,7 +62,6 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
|
|||||||
{
|
{
|
||||||
$builder->add('date_from', DateType::class, [
|
$builder->add('date_from', DateType::class, [
|
||||||
'label' => 'Born after this date',
|
'label' => 'Born after this date',
|
||||||
'data' => new DateTime(),
|
|
||||||
'attr' => ['class' => 'datepicker'],
|
'attr' => ['class' => 'datepicker'],
|
||||||
'widget' => 'single_text',
|
'widget' => 'single_text',
|
||||||
'format' => 'dd-MM-yyyy',
|
'format' => 'dd-MM-yyyy',
|
||||||
@@ -70,12 +69,15 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
|
|||||||
|
|
||||||
$builder->add('date_to', DateType::class, [
|
$builder->add('date_to', DateType::class, [
|
||||||
'label' => 'Born before this date',
|
'label' => 'Born before this date',
|
||||||
'data' => new DateTime(),
|
|
||||||
'attr' => ['class' => 'datepicker'],
|
'attr' => ['class' => 'datepicker'],
|
||||||
'widget' => 'single_text',
|
'widget' => 'single_text',
|
||||||
'format' => 'dd-MM-yyyy',
|
'format' => 'dd-MM-yyyy',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return ['date_from' => new DateTime(), 'date_to' => new DateTime()];
|
||||||
|
}
|
||||||
|
|
||||||
// here, we create a simple string which will describe the action of
|
// here, we create a simple string which will describe the action of
|
||||||
// the filter in the Response
|
// the filter in the Response
|
||||||
|
@@ -36,6 +36,10 @@ class CountPerson implements ExportInterface
|
|||||||
{
|
{
|
||||||
// this export does not add any form
|
// this export does not add any form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes()
|
public function getAllowedFormattersTypes()
|
||||||
{
|
{
|
||||||
|
@@ -18,6 +18,7 @@ These are alias conventions :
|
|||||||
| | SocialIssue::class | acp.socialIssues | acpsocialissue |
|
| | SocialIssue::class | acp.socialIssues | acpsocialissue |
|
||||||
| | User::class | acp.user | acpuser |
|
| | User::class | acp.user | acpuser |
|
||||||
| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories |
|
| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories |
|
||||||
|
| | AccompanyingPeriodInfo::class | not existing (using custom WITH clause) | acpinfo |
|
||||||
| AccompanyingPeriodWork::class | | | acpw |
|
| AccompanyingPeriodWork::class | | | acpw |
|
||||||
| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval |
|
| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval |
|
||||||
| | User::class | acpw.referrers | acpwuser |
|
| | User::class | acpw.referrers | acpwuser |
|
||||||
@@ -28,6 +29,8 @@ These are alias conventions :
|
|||||||
| | Person::class | acppart.person | partperson |
|
| | Person::class | acppart.person | partperson |
|
||||||
| AccompanyingPeriodWorkEvaluation::class | | | workeval |
|
| AccompanyingPeriodWorkEvaluation::class | | | workeval |
|
||||||
| | Evaluation::class | workeval.evaluation | eval |
|
| | Evaluation::class | workeval.evaluation | eval |
|
||||||
|
| AccompanyingPeriodInfo::class | | | acpinfo |
|
||||||
|
| | User::class | acpinfo.user | acpinfo_user |
|
||||||
| Goal::class | | | goal |
|
| Goal::class | | | goal |
|
||||||
| | Result::class | goal.results | goalresult |
|
| | Result::class | goal.results | goalresult |
|
||||||
| Person::class | | | person |
|
| Person::class | | | person |
|
||||||
|
@@ -2,6 +2,7 @@ parameters:
|
|||||||
level: 5
|
level: 5
|
||||||
paths:
|
paths:
|
||||||
- src/
|
- src/
|
||||||
|
- utils/
|
||||||
tmpDir: .cache/
|
tmpDir: .cache/
|
||||||
reportUnmatchedIgnoredErrors: false
|
reportUnmatchedIgnoredErrors: false
|
||||||
excludePaths:
|
excludePaths:
|
||||||
|
29
phpunit.rector.xml
Normal file
29
phpunit.rector.xml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.6/phpunit.xsd"
|
||||||
|
bootstrap="tests/app/vendor/autoload.php"
|
||||||
|
cacheResultFile=".cache/phpunit/test-results-rector"
|
||||||
|
executionOrder="depends,defects"
|
||||||
|
forceCoversAnnotation="true"
|
||||||
|
beStrictAboutCoversAnnotation="true"
|
||||||
|
beStrictAboutOutputDuringTests="true"
|
||||||
|
beStrictAboutTodoAnnotatedTests="true"
|
||||||
|
convertDeprecationsToExceptions="true"
|
||||||
|
failOnRisky="true"
|
||||||
|
failOnWarning="true"
|
||||||
|
verbose="true"
|
||||||
|
colors="true"
|
||||||
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="default">
|
||||||
|
<directory>utils/rector/tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<coverage cacheDirectory=".cache/phpunit/code-coverage-rector"
|
||||||
|
processUncoveredFiles="true">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">utils/rector/src</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
</phpunit>
|
@@ -24,6 +24,9 @@ return static function (RectorConfig $rectorConfig): void {
|
|||||||
LevelSetList::UP_TO_PHP_74
|
LevelSetList::UP_TO_PHP_74
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// chill rules
|
||||||
|
$rectorConfig->rule(\Chill\Utils\Rector\Rector\ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector::class);
|
||||||
|
|
||||||
// skip some path...
|
// skip some path...
|
||||||
$rectorConfig->skip([
|
$rectorConfig->skip([
|
||||||
// make rector stuck for some files
|
// make rector stuck for some files
|
||||||
|
@@ -18,11 +18,17 @@ use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
|
|||||||
use Chill\ActivityBundle\Repository\ActivityRepository;
|
use Chill\ActivityBundle\Repository\ActivityRepository;
|
||||||
use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository;
|
use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository;
|
||||||
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
|
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityUserJobRepository;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
||||||
|
use Chill\MainBundle\Entity\UserJob;
|
||||||
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
use Chill\MainBundle\Repository\LocationRepository;
|
use Chill\MainBundle\Repository\LocationRepository;
|
||||||
use Chill\MainBundle\Repository\UserRepositoryInterface;
|
use Chill\MainBundle\Repository\UserRepositoryInterface;
|
||||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||||
|
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
|
||||||
|
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\PersonBundle\Privacy\PrivacyEvent;
|
use Chill\PersonBundle\Privacy\PrivacyEvent;
|
||||||
@@ -47,68 +53,26 @@ use function array_key_exists;
|
|||||||
|
|
||||||
final class ActivityController extends AbstractController
|
final class ActivityController extends AbstractController
|
||||||
{
|
{
|
||||||
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
|
|
||||||
|
|
||||||
private ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
|
|
||||||
|
|
||||||
private ActivityRepository $activityRepository;
|
|
||||||
|
|
||||||
private ActivityTypeCategoryRepository $activityTypeCategoryRepository;
|
|
||||||
|
|
||||||
private ActivityTypeRepositoryInterface $activityTypeRepository;
|
|
||||||
|
|
||||||
private CenterResolverManagerInterface $centerResolver;
|
|
||||||
|
|
||||||
private EntityManagerInterface $entityManager;
|
|
||||||
|
|
||||||
private EventDispatcherInterface $eventDispatcher;
|
|
||||||
|
|
||||||
private LocationRepository $locationRepository;
|
|
||||||
|
|
||||||
private LoggerInterface $logger;
|
|
||||||
|
|
||||||
private PersonRepository $personRepository;
|
|
||||||
|
|
||||||
private SerializerInterface $serializer;
|
|
||||||
|
|
||||||
private ThirdPartyRepository $thirdPartyRepository;
|
|
||||||
|
|
||||||
private TranslatorInterface $translator;
|
|
||||||
|
|
||||||
private UserRepositoryInterface $userRepository;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
|
private readonly ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
|
||||||
ActivityTypeRepositoryInterface $activityTypeRepository,
|
private readonly ActivityTypeRepositoryInterface $activityTypeRepository,
|
||||||
ActivityTypeCategoryRepository $activityTypeCategoryRepository,
|
private readonly ActivityTypeCategoryRepository $activityTypeCategoryRepository,
|
||||||
PersonRepository $personRepository,
|
private readonly PersonRepository $personRepository,
|
||||||
ThirdPartyRepository $thirdPartyRepository,
|
private readonly ThirdPartyRepository $thirdPartyRepository,
|
||||||
LocationRepository $locationRepository,
|
private readonly LocationRepository $locationRepository,
|
||||||
ActivityRepository $activityRepository,
|
private readonly ActivityRepository $activityRepository,
|
||||||
AccompanyingPeriodRepository $accompanyingPeriodRepository,
|
private readonly AccompanyingPeriodRepository $accompanyingPeriodRepository,
|
||||||
EntityManagerInterface $entityManager,
|
private readonly EntityManagerInterface $entityManager,
|
||||||
EventDispatcherInterface $eventDispatcher,
|
private readonly EventDispatcherInterface $eventDispatcher,
|
||||||
LoggerInterface $logger,
|
private readonly LoggerInterface $logger,
|
||||||
SerializerInterface $serializer,
|
private readonly SerializerInterface $serializer,
|
||||||
UserRepositoryInterface $userRepository,
|
private readonly UserRepositoryInterface $userRepository,
|
||||||
CenterResolverManagerInterface $centerResolver,
|
private readonly CenterResolverManagerInterface $centerResolver,
|
||||||
TranslatorInterface $translator
|
private readonly TranslatorInterface $translator,
|
||||||
|
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
|
||||||
|
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
|
private readonly PaginatorFactory $paginatorFactory,
|
||||||
) {
|
) {
|
||||||
$this->activityACLAwareRepository = $activityACLAwareRepository;
|
|
||||||
$this->activityTypeRepository = $activityTypeRepository;
|
|
||||||
$this->activityTypeCategoryRepository = $activityTypeCategoryRepository;
|
|
||||||
$this->personRepository = $personRepository;
|
|
||||||
$this->thirdPartyRepository = $thirdPartyRepository;
|
|
||||||
$this->locationRepository = $locationRepository;
|
|
||||||
$this->activityRepository = $activityRepository;
|
|
||||||
$this->accompanyingPeriodRepository = $accompanyingPeriodRepository;
|
|
||||||
$this->entityManager = $entityManager;
|
|
||||||
$this->eventDispatcher = $eventDispatcher;
|
|
||||||
$this->logger = $logger;
|
|
||||||
$this->serializer = $serializer;
|
|
||||||
$this->userRepository = $userRepository;
|
|
||||||
$this->centerResolver = $centerResolver;
|
|
||||||
$this->translator = $translator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -289,14 +253,31 @@ final class ActivityController extends AbstractController
|
|||||||
{
|
{
|
||||||
$view = null;
|
$view = null;
|
||||||
$activities = [];
|
$activities = [];
|
||||||
// TODO: add pagination
|
|
||||||
|
|
||||||
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
||||||
|
$filter = $this->buildFilterOrder($person ?? $accompanyingPeriod);
|
||||||
|
|
||||||
|
$filterArgs = [
|
||||||
|
'my_activities' => $filter->getSingleCheckboxData('my_activities'),
|
||||||
|
'types' => $filter->hasEntityChoice('activity_types') ? $filter->getEntityChoiceData('activity_types') : [],
|
||||||
|
'jobs' => $filter->hasEntityChoice('jobs') ? $filter->getEntityChoiceData('jobs') : [],
|
||||||
|
'before' => $filter->getDateRangeData('activity_date')['to'],
|
||||||
|
'after' => $filter->getDateRangeData('activity_date')['from'],
|
||||||
|
];
|
||||||
|
|
||||||
if ($person instanceof Person) {
|
if ($person instanceof Person) {
|
||||||
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $person);
|
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $person);
|
||||||
|
$count = $this->activityACLAwareRepository->countByPerson($person, ActivityVoter::SEE, $filterArgs);
|
||||||
|
$paginator = $this->paginatorFactory->create($count);
|
||||||
$activities = $this->activityACLAwareRepository
|
$activities = $this->activityACLAwareRepository
|
||||||
->findByPerson($person, ActivityVoter::SEE, 0, null, ['date' => 'DESC', 'id' => 'DESC']);
|
->findByPerson(
|
||||||
|
$person,
|
||||||
|
ActivityVoter::SEE,
|
||||||
|
$paginator->getCurrentPageFirstItemNumber(),
|
||||||
|
$paginator->getItemsPerPage(),
|
||||||
|
['date' => 'DESC', 'id' => 'DESC'],
|
||||||
|
$filterArgs
|
||||||
|
);
|
||||||
|
|
||||||
$event = new PrivacyEvent($person, [
|
$event = new PrivacyEvent($person, [
|
||||||
'element_class' => Activity::class,
|
'element_class' => Activity::class,
|
||||||
@@ -308,10 +289,21 @@ final class ActivityController extends AbstractController
|
|||||||
} elseif ($accompanyingPeriod instanceof AccompanyingPeriod) {
|
} elseif ($accompanyingPeriod instanceof AccompanyingPeriod) {
|
||||||
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $accompanyingPeriod);
|
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $accompanyingPeriod);
|
||||||
|
|
||||||
|
$count = $this->activityACLAwareRepository->countByAccompanyingPeriod($accompanyingPeriod, ActivityVoter::SEE, $filterArgs);
|
||||||
|
$paginator = $this->paginatorFactory->create($count);
|
||||||
$activities = $this->activityACLAwareRepository
|
$activities = $this->activityACLAwareRepository
|
||||||
->findByAccompanyingPeriod($accompanyingPeriod, ActivityVoter::SEE, 0, null, ['date' => 'DESC', 'id' => 'DESC']);
|
->findByAccompanyingPeriod(
|
||||||
|
$accompanyingPeriod,
|
||||||
|
ActivityVoter::SEE,
|
||||||
|
$paginator->getCurrentPageFirstItemNumber(),
|
||||||
|
$paginator->getItemsPerPage(),
|
||||||
|
['date' => 'DESC', 'id' => 'DESC'],
|
||||||
|
$filterArgs
|
||||||
|
);
|
||||||
|
|
||||||
$view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig';
|
$view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig';
|
||||||
|
} else {
|
||||||
|
throw new \LogicException("Unsupported");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render(
|
return $this->render(
|
||||||
@@ -320,10 +312,47 @@ final class ActivityController extends AbstractController
|
|||||||
'activities' => $activities,
|
'activities' => $activities,
|
||||||
'person' => $person,
|
'person' => $person,
|
||||||
'accompanyingCourse' => $accompanyingPeriod,
|
'accompanyingCourse' => $accompanyingPeriod,
|
||||||
|
'filter' => $filter,
|
||||||
|
'paginator' => $paginator,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildFilterOrder(AccompanyingPeriod|Person $associated): FilterOrderHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
$filterBuilder = $this->filterOrderHelperFactory->create(self::class);
|
||||||
|
$types = $this->activityACLAwareRepository->findActivityTypeByAssociated($associated);
|
||||||
|
$jobs = $this->activityACLAwareRepository->findUserJobByAssociated($associated);
|
||||||
|
|
||||||
|
$filterBuilder
|
||||||
|
->addDateRange('activity_date', 'activity.date')
|
||||||
|
->addSingleCheckbox('my_activities', 'activity_filter.My activities');
|
||||||
|
|
||||||
|
if (1 < count($types)) {
|
||||||
|
$filterBuilder
|
||||||
|
->addEntityChoice('activity_types', 'activity_filter.Types', \Chill\ActivityBundle\Entity\ActivityType::class, $types, [
|
||||||
|
'choice_label' => function (\Chill\ActivityBundle\Entity\ActivityType $activityType) {
|
||||||
|
$text = match ($activityType->hasCategory()) {
|
||||||
|
true => $this->translatableStringHelper->localize($activityType->getCategory()->getName()) . ' > ',
|
||||||
|
false => '',
|
||||||
|
};
|
||||||
|
|
||||||
|
return $text . $this->translatableStringHelper->localize($activityType->getName());
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 < count($jobs)) {
|
||||||
|
$filterBuilder
|
||||||
|
->addEntityChoice('jobs', 'activity_filter.Jobs', UserJob::class, $jobs, [
|
||||||
|
'choice_label' => fn (UserJob $u) => $this->translatableStringHelper->localize($u->getLabel())
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filterBuilder->build();
|
||||||
|
}
|
||||||
|
|
||||||
public function newAction(Request $request): Response
|
public function newAction(Request $request): Response
|
||||||
{
|
{
|
||||||
$view = null;
|
$view = null;
|
||||||
|
@@ -40,6 +40,10 @@ class ByActivityNumberAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// No form needed
|
// No form needed
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -52,6 +52,10 @@ class ByCreatorAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form
|
// no form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -57,6 +57,10 @@ class BySocialActionAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form
|
// no form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -57,6 +57,10 @@ class BySocialIssueAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form
|
// no form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -57,6 +57,10 @@ class ByThirdpartyAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form
|
// no form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -57,6 +57,10 @@ class CreatorScopeAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form
|
// no form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -29,14 +29,6 @@ class DateAggregator implements AggregatorInterface
|
|||||||
|
|
||||||
private const DEFAULT_CHOICE = 'year';
|
private const DEFAULT_CHOICE = 'year';
|
||||||
|
|
||||||
private TranslatorInterface $translator;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
TranslatorInterface $translator
|
|
||||||
) {
|
|
||||||
$this->translator = $translator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addRole(): ?string
|
public function addRole(): ?string
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -84,9 +76,12 @@ class DateAggregator implements AggregatorInterface
|
|||||||
'multiple' => false,
|
'multiple' => false,
|
||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
'empty_data' => self::DEFAULT_CHOICE,
|
'empty_data' => self::DEFAULT_CHOICE,
|
||||||
'data' => self::DEFAULT_CHOICE,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return ['frequency' => self::DEFAULT_CHOICE];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -57,6 +57,10 @@ class LocationTypeAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form
|
// no form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\ActivityBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Export\Declarations;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
|
||||||
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
|
use Chill\MainBundle\Repository\LocationRepository;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Closure;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use function in_array;
|
||||||
|
|
||||||
|
final readonly class ActivityLocationAggregator implements AggregatorInterface
|
||||||
|
{
|
||||||
|
public const KEY = 'activity_location_aggregator';
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
if (!in_array('actloc', $qb->getAllAliases(), true)) {
|
||||||
|
$qb->leftJoin('activity.location', 'actloc');
|
||||||
|
}
|
||||||
|
$qb->addSelect(sprintf('actloc.name AS %s', self::KEY));
|
||||||
|
$qb->addGroupBy(self::KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn(): string
|
||||||
|
{
|
||||||
|
return Declarations::ACTIVITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ($value): string {
|
||||||
|
if ('_header' === $value) {
|
||||||
|
return 'export.aggregator.activity.by_location.Activity Location';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $value || '' === $value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryKeys($data): array
|
||||||
|
{
|
||||||
|
return [self::KEY];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'export.aggregator.activity.by_location.Title';
|
||||||
|
}
|
||||||
|
}
|
@@ -60,6 +60,10 @@ class ActivityTypeAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form required for this aggregator
|
// no form required for this aggregator
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data): Closure
|
public function getLabels($key, array $values, $data): Closure
|
||||||
{
|
{
|
||||||
|
@@ -58,6 +58,10 @@ class ActivityUserAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// nothing to add
|
// nothing to add
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, $values, $data): Closure
|
public function getLabels($key, $values, $data): Closure
|
||||||
{
|
{
|
||||||
|
@@ -56,6 +56,10 @@ class ActivityUsersAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// nothing to add on the form
|
// nothing to add on the form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -55,6 +55,10 @@ class ActivityUsersJobAggregator implements \Chill\MainBundle\Export\AggregatorI
|
|||||||
{
|
{
|
||||||
// nothing to add in the form
|
// nothing to add in the form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -55,6 +55,10 @@ class ActivityUsersScopeAggregator implements \Chill\MainBundle\Export\Aggregato
|
|||||||
{
|
{
|
||||||
// nothing to add in the form
|
// nothing to add in the form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -110,6 +110,10 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -47,6 +47,10 @@ class SentReceivedAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// No form needed
|
// No form needed
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data): callable
|
public function getLabels($key, array $values, $data): callable
|
||||||
{
|
{
|
||||||
|
@@ -39,6 +39,10 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
|
|||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes(): array
|
public function getAllowedFormattersTypes(): array
|
||||||
{
|
{
|
||||||
|
@@ -40,6 +40,10 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
|
|||||||
{
|
{
|
||||||
// TODO: Implement buildForm() method.
|
// TODO: Implement buildForm() method.
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes(): array
|
public function getAllowedFormattersTypes(): array
|
||||||
{
|
{
|
||||||
|
@@ -39,6 +39,10 @@ class CountActivity implements ExportInterface, GroupedExportInterface
|
|||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes(): array
|
public function getAllowedFormattersTypes(): array
|
||||||
{
|
{
|
||||||
|
@@ -44,6 +44,10 @@ class ListActivity implements ListInterface, GroupedExportInterface
|
|||||||
{
|
{
|
||||||
$this->helper->buildForm($builder);
|
$this->helper->buildForm($builder);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes()
|
public function getAllowedFormattersTypes()
|
||||||
{
|
{
|
||||||
|
@@ -40,6 +40,10 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
|
|||||||
{
|
{
|
||||||
// TODO: Implement buildForm() method.
|
// TODO: Implement buildForm() method.
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes(): array
|
public function getAllowedFormattersTypes(): array
|
||||||
{
|
{
|
||||||
|
@@ -40,6 +40,10 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
|
|||||||
{
|
{
|
||||||
// TODO: Implement buildForm() method.
|
// TODO: Implement buildForm() method.
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes(): array
|
public function getAllowedFormattersTypes(): array
|
||||||
{
|
{
|
||||||
|
@@ -35,6 +35,10 @@ class CountActivity implements ExportInterface, GroupedExportInterface
|
|||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes()
|
public function getAllowedFormattersTypes()
|
||||||
{
|
{
|
||||||
|
@@ -88,6 +88,10 @@ class ListActivity implements ListInterface, GroupedExportInterface
|
|||||||
])],
|
])],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes()
|
public function getAllowedFormattersTypes()
|
||||||
{
|
{
|
||||||
|
@@ -53,6 +53,10 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
|
|||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes()
|
public function getAllowedFormattersTypes()
|
||||||
{
|
{
|
||||||
|
@@ -68,6 +68,10 @@ class ActivityTypeFilter implements FilterInterface
|
|||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -52,6 +52,10 @@ class ByCreatorFilter implements FilterInterface
|
|||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -60,6 +60,10 @@ class BySocialActionFilter implements FilterInterface
|
|||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -60,6 +60,10 @@ class BySocialIssueFilter implements FilterInterface
|
|||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -68,9 +68,12 @@ class EmergencyFilter implements FilterInterface
|
|||||||
'multiple' => false,
|
'multiple' => false,
|
||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
'empty_data' => self::DEFAULT_CHOICE,
|
'empty_data' => self::DEFAULT_CHOICE,
|
||||||
'data' => self::DEFAULT_CHOICE,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return ['accepted_emergency' => self::DEFAULT_CHOICE];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -44,6 +44,10 @@ class HasNoActivityFilter implements FilterInterface
|
|||||||
{
|
{
|
||||||
//no form needed
|
//no form needed
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -46,6 +46,10 @@ class LocationFilter implements FilterInterface
|
|||||||
'label' => 'pick location',
|
'label' => 'pick location',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -65,6 +65,10 @@ class LocationTypeFilter implements FilterInterface
|
|||||||
//'label' => false,
|
//'label' => false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\Activity;
|
||||||
|
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\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private RollingDateConverterInterface $rollingDateConverter,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle()
|
||||||
|
{
|
||||||
|
return 'export.filter.activity.course_having_activity_between_date.Title';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('start_date', PickRollingDateType::class, [
|
||||||
|
'label' => 'export.filter.activity.course_having_activity_between_date.Receiving an activity after'
|
||||||
|
])
|
||||||
|
->add('end_date', PickRollingDateType::class, [
|
||||||
|
'label' => 'export.filter.activity.course_having_activity_between_date.Receiving an activity before'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'start_date' => new RollingDate(RollingDate::T_YEAR_CURRENT_START),
|
||||||
|
'end_date' => new RollingDate(RollingDate::T_TODAY)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAction($data, $format = 'string')
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'export.filter.activity.course_having_activity_between_date.Only course having an activity between from and to',
|
||||||
|
[
|
||||||
|
'from' => $this->rollingDateConverter->convert($data['start_date']),
|
||||||
|
'to' => $this->rollingDateConverter->convert($data['end_date']),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRole(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
|
{
|
||||||
|
$alias = 'act_period_having_act_betw_date_alias';
|
||||||
|
$from = 'act_period_having_act_betw_date_start';
|
||||||
|
$to = 'act_period_having_act_betw_date_end';
|
||||||
|
|
||||||
|
$qb->andWhere(
|
||||||
|
$qb->expr()->exists(
|
||||||
|
'SELECT 1 FROM ' . Activity::class . " {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = acp"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$qb
|
||||||
|
->setParameter($from, $this->rollingDateConverter->convert($data['start_date']))
|
||||||
|
->setParameter($to, $this->rollingDateConverter->convert($data['end_date']));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyOn()
|
||||||
|
{
|
||||||
|
return \Chill\PersonBundle\Export\Declarations::ACP_TYPE;
|
||||||
|
}
|
||||||
|
}
|
@@ -69,9 +69,12 @@ class SentReceivedFilter implements FilterInterface
|
|||||||
'multiple' => false,
|
'multiple' => false,
|
||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
'empty_data' => self::DEFAULT_CHOICE,
|
'empty_data' => self::DEFAULT_CHOICE,
|
||||||
'data' => self::DEFAULT_CHOICE,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return ['accepted_sentreceived' => self::DEFAULT_CHOICE];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -61,6 +61,10 @@ class UserFilter implements FilterInterface
|
|||||||
'label' => 'Creators',
|
'label' => 'Creators',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -71,6 +71,10 @@ class UserScopeFilter implements FilterInterface
|
|||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -80,11 +80,9 @@ class ActivityDateFilter implements FilterInterface
|
|||||||
$builder
|
$builder
|
||||||
->add('date_from', PickRollingDateType::class, [
|
->add('date_from', PickRollingDateType::class, [
|
||||||
'label' => 'Activities after this date',
|
'label' => 'Activities after this date',
|
||||||
'data' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
|
|
||||||
])
|
])
|
||||||
->add('date_to', PickRollingDateType::class, [
|
->add('date_to', PickRollingDateType::class, [
|
||||||
'label' => 'Activities before this date',
|
'label' => 'Activities before this date',
|
||||||
'data' => new RollingDate(RollingDate::T_TODAY),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
|
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
|
||||||
@@ -127,6 +125,10 @@ class ActivityDateFilter implements FilterInterface
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
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')
|
public function describeAction($data, $format = 'string')
|
||||||
{
|
{
|
||||||
|
@@ -78,6 +78,10 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string')
|
public function describeAction($data, $format = 'string')
|
||||||
{
|
{
|
||||||
|
@@ -56,6 +56,10 @@ class ActivityUsersFilter implements FilterInterface
|
|||||||
'label' => 'Users',
|
'label' => 'Users',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string')
|
public function describeAction($data, $format = 'string')
|
||||||
{
|
{
|
||||||
|
@@ -82,6 +82,10 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
|
|||||||
'expanded' => false,
|
'expanded' => false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string')
|
public function describeAction($data, $format = 'string')
|
||||||
{
|
{
|
||||||
|
@@ -112,7 +112,6 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
|
|||||||
{
|
{
|
||||||
$builder->add('date_from', DateType::class, [
|
$builder->add('date_from', DateType::class, [
|
||||||
'label' => 'Implied in an activity after this date',
|
'label' => 'Implied in an activity after this date',
|
||||||
'data' => new DateTime(),
|
|
||||||
'attr' => ['class' => 'datepicker'],
|
'attr' => ['class' => 'datepicker'],
|
||||||
'widget' => 'single_text',
|
'widget' => 'single_text',
|
||||||
'format' => 'dd-MM-yyyy',
|
'format' => 'dd-MM-yyyy',
|
||||||
@@ -120,7 +119,6 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
|
|||||||
|
|
||||||
$builder->add('date_to', DateType::class, [
|
$builder->add('date_to', DateType::class, [
|
||||||
'label' => 'Implied in an activity before this date',
|
'label' => 'Implied in an activity before this date',
|
||||||
'data' => new DateTime(),
|
|
||||||
'attr' => ['class' => 'datepicker'],
|
'attr' => ['class' => 'datepicker'],
|
||||||
'widget' => 'single_text',
|
'widget' => 'single_text',
|
||||||
'format' => 'dd-MM-yyyy',
|
'format' => 'dd-MM-yyyy',
|
||||||
@@ -130,7 +128,6 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
|
|||||||
'class' => ActivityReason::class,
|
'class' => ActivityReason::class,
|
||||||
'choice_label' => fn (ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getName()),
|
'choice_label' => fn (ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getName()),
|
||||||
'group_by' => fn (ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
|
'group_by' => fn (ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
|
||||||
'data' => $this->activityReasonRepository->findAll(),
|
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'expanded' => false,
|
'expanded' => false,
|
||||||
'label' => 'Activity reasons for those activities',
|
'label' => 'Activity reasons for those activities',
|
||||||
@@ -176,6 +173,10 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return ['date_from' => new DateTime(), 'date_to' => new DateTime(), 'reasons' => $this->activityReasonRepository->findAll()];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string')
|
public function describeAction($data, $format = 'string')
|
||||||
{
|
{
|
||||||
|
@@ -60,6 +60,10 @@ class UsersJobFilter implements FilterInterface
|
|||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string')
|
public function describeAction($data, $format = 'string')
|
||||||
{
|
{
|
||||||
|
@@ -67,6 +67,10 @@ class UsersScopeFilter implements FilterInterface
|
|||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string')
|
public function describeAction($data, $format = 'string')
|
||||||
{
|
{
|
||||||
|
@@ -18,67 +18,193 @@ use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
|||||||
use Chill\MainBundle\Entity\Location;
|
use Chill\MainBundle\Entity\Location;
|
||||||
use Chill\MainBundle\Entity\LocationType;
|
use Chill\MainBundle\Entity\LocationType;
|
||||||
use Chill\MainBundle\Entity\Scope;
|
use Chill\MainBundle\Entity\Scope;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
use Chill\MainBundle\Entity\UserJob;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\AbstractQuery;
|
use Doctrine\ORM\AbstractQuery;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\NonUniqueResultException;
|
||||||
|
use Doctrine\ORM\NoResultException;
|
||||||
|
use Doctrine\ORM\Query\Expr\Join;
|
||||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Symfony\Component\Security\Core\Role\Role;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
use function count;
|
use function count;
|
||||||
use function in_array;
|
use function in_array;
|
||||||
|
|
||||||
final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInterface
|
final readonly class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInterface
|
||||||
{
|
{
|
||||||
private AuthorizationHelper $authorizationHelper;
|
|
||||||
|
|
||||||
private CenterResolverDispatcherInterface $centerResolverDispatcher;
|
|
||||||
|
|
||||||
private EntityManagerInterface $em;
|
|
||||||
|
|
||||||
private ActivityRepository $repository;
|
|
||||||
|
|
||||||
private Security $security;
|
|
||||||
|
|
||||||
private TokenStorageInterface $tokenStorage;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AuthorizationHelper $authorizationHelper,
|
private AuthorizationHelperForCurrentUserInterface $authorizationHelper,
|
||||||
CenterResolverDispatcherInterface $centerResolverDispatcher,
|
private CenterResolverManagerInterface $centerResolverManager,
|
||||||
TokenStorageInterface $tokenStorage,
|
private ActivityRepository $repository,
|
||||||
ActivityRepository $repository,
|
private EntityManagerInterface $em,
|
||||||
EntityManagerInterface $em,
|
private Security $security,
|
||||||
Security $security
|
private RequestStack $requestStack,
|
||||||
) {
|
) {
|
||||||
$this->authorizationHelper = $authorizationHelper;
|
|
||||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
|
||||||
$this->tokenStorage = $tokenStorage;
|
|
||||||
$this->repository = $repository;
|
|
||||||
$this->em = $em;
|
|
||||||
$this->security = $security;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array
|
/**
|
||||||
|
* @throws NonUniqueResultException
|
||||||
|
* @throws NoResultException
|
||||||
|
*/
|
||||||
|
public function countByAccompanyingPeriod(AccompanyingPeriod $period, string $role, array $filters = []): int
|
||||||
{
|
{
|
||||||
$user = $this->security->getUser();
|
$qb = $this->buildBaseQuery($filters);
|
||||||
$center = $this->centerResolverDispatcher->resolveCenter($period);
|
|
||||||
|
|
||||||
if (0 === count($orderBy)) {
|
$qb
|
||||||
$orderBy = ['date' => 'DESC'];
|
->select('COUNT(a)')
|
||||||
|
->andWhere('a.accompanyingPeriod = :period')->setParameter('period', $period);
|
||||||
|
|
||||||
|
return $qb->getQuery()->getSingleScalarResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countByPerson(Person $person, string $role, array $filters = []): int
|
||||||
|
{
|
||||||
|
$qb = $this->buildBaseQuery($filters);
|
||||||
|
|
||||||
|
$qb = $this->filterBaseQueryByPerson($qb, $person, $role);
|
||||||
|
|
||||||
|
$qb->select('COUNT(a)');
|
||||||
|
|
||||||
|
return $qb->getQuery()->getSingleScalarResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, array $orderBy = ['date' => 'DESC'], array $filters = []): array
|
||||||
|
{
|
||||||
|
$qb = $this->buildBaseQuery($filters);
|
||||||
|
|
||||||
|
$qb->andWhere('a.accompanyingPeriod = :period')->setParameter('period', $period);
|
||||||
|
|
||||||
|
foreach ($orderBy as $field => $order) {
|
||||||
|
$qb->addOrderBy('a.' . $field, $order);
|
||||||
}
|
}
|
||||||
|
|
||||||
$scopes = $this->authorizationHelper
|
if (null !== $start) {
|
||||||
->getReachableCircles($user, $role, $center);
|
$qb->setFirstResult($start);
|
||||||
|
}
|
||||||
|
if (null !== $limit) {
|
||||||
|
$qb->setMaxResults($limit);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->em->getRepository(Activity::class)
|
return $qb->getQuery()->getResult();
|
||||||
->findByAccompanyingPeriod($period, $scopes, true, $limit, $start, $orderBy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function buildBaseQuery(array $filters): QueryBuilder
|
||||||
|
{
|
||||||
|
$qb = $this->repository
|
||||||
|
->createQueryBuilder('a')
|
||||||
|
;
|
||||||
|
|
||||||
|
if (($filters['my_activities'] ?? false) and ($user = $this->security->getUser()) instanceof User) {
|
||||||
|
$qb->andWhere(
|
||||||
|
$qb->expr()->orX(
|
||||||
|
'a.createdBy = :user',
|
||||||
|
'a.user = :user',
|
||||||
|
':user MEMBER OF a.users'
|
||||||
|
)
|
||||||
|
)->setParameter('user', $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] !== ($types = $filters['types'] ?? [])) {
|
||||||
|
$qb->andWhere('a.activityType IN (:types)')->setParameter('types', $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] !== ($jobs = $filters['jobs'] ?? [])) {
|
||||||
|
$qb
|
||||||
|
->leftJoin('a.createdBy', 'creator')
|
||||||
|
->leftJoin('a.user', 'activity_u')
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->orX(
|
||||||
|
'creator.userJob IN (:jobs)',
|
||||||
|
'activity_u.userJob IN (:jobs)',
|
||||||
|
'EXISTS (SELECT 1 FROM ' . User::class . ' activity_user WHERE activity_user MEMBER OF a.users AND activity_user.userJob IN (:jobs))'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->setParameter('jobs', $jobs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== ($after = $filters['after'] ?? null)) {
|
||||||
|
$qb->andWhere('a.date >= :after')->setParameter('after', $after);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== ($before = $filters['before'] ?? null)) {
|
||||||
|
$qb->andWhere('a.date <= :before')->setParameter('before', $before);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AccompanyingPeriod|Person $associated
|
||||||
|
* @return array<ActivityType>
|
||||||
|
*/
|
||||||
|
public function findActivityTypeByAssociated(AccompanyingPeriod|Person $associated): array
|
||||||
|
{
|
||||||
|
$in = $this->em->createQueryBuilder();
|
||||||
|
$in
|
||||||
|
->select('1')
|
||||||
|
->from(Activity::class, 'a');
|
||||||
|
|
||||||
|
if ($associated instanceof Person) {
|
||||||
|
$in = $this->filterBaseQueryByPerson($in, $associated, ActivityVoter::SEE);
|
||||||
|
} else {
|
||||||
|
$in->andWhere('a.accompanyingPeriod = :period')->setParameter('period', $associated);
|
||||||
|
}
|
||||||
|
|
||||||
|
// join between the embedded exist query and the main query
|
||||||
|
$in->andWhere('a.activityType = t');
|
||||||
|
|
||||||
|
$qb = $this->em->createQueryBuilder()->setParameters($in->getParameters());
|
||||||
|
$qb
|
||||||
|
->select('t')
|
||||||
|
->from(ActivityType::class, 't')
|
||||||
|
->where(
|
||||||
|
$qb->expr()->exists($in->getDQL())
|
||||||
|
);
|
||||||
|
|
||||||
|
return $qb->getQuery()->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findUserJobByAssociated(Person|AccompanyingPeriod $associated): array
|
||||||
|
{
|
||||||
|
$in = $this->em->createQueryBuilder();
|
||||||
|
$in->select('IDENTITY(u.userJob)')
|
||||||
|
->from(User::class, 'u')
|
||||||
|
->join(
|
||||||
|
Activity::class,
|
||||||
|
'a',
|
||||||
|
Join::WITH,
|
||||||
|
'a.createdBy = u OR a.user = u OR u MEMBER OF a.users'
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($associated instanceof Person) {
|
||||||
|
$in = $this->filterBaseQueryByPerson($in, $associated, ActivityVoter::SEE);
|
||||||
|
} else {
|
||||||
|
$in->andWhere('a.accompanyingPeriod = :associated');
|
||||||
|
$in->setParameter('associated', $associated);
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb = $this->em->createQueryBuilder()->setParameters($in->getParameters());
|
||||||
|
|
||||||
|
$qb->select('ub', 'JSON_EXTRACT(ub.label, :lang) AS HIDDEN lang')
|
||||||
|
->from(UserJob::class, 'ub')
|
||||||
|
->where($qb->expr()->in('ub.id', $in->getDQL()))
|
||||||
|
->setParameter('lang', $this->requestStack->getCurrentRequest()->getLocale())
|
||||||
|
->orderBy('lang')
|
||||||
|
;
|
||||||
|
|
||||||
|
return $qb->getQuery()->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function findByAccompanyingPeriodSimplified(AccompanyingPeriod $period, ?int $limit = 1000): array
|
public function findByAccompanyingPeriodSimplified(AccompanyingPeriod $period, ?int $limit = 1000): array
|
||||||
{
|
{
|
||||||
$rsm = new ResultSetMappingBuilder($this->em);
|
$rsm = new ResultSetMappingBuilder($this->em);
|
||||||
@@ -159,25 +285,73 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
|
|||||||
return $nq->getResult(AbstractQuery::HYDRATE_ARRAY);
|
return $nq->getResult(AbstractQuery::HYDRATE_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function findByPerson(Person $person, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = [], array $filters = []): array
|
||||||
* @param array $orderBy
|
|
||||||
*
|
|
||||||
* @return Activity[]|array
|
|
||||||
*/
|
|
||||||
public function findByPerson(Person $person, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array
|
|
||||||
{
|
{
|
||||||
$user = $this->security->getUser();
|
$qb = $this->buildBaseQuery($filters);
|
||||||
$center = $this->centerResolverDispatcher->resolveCenter($person);
|
|
||||||
|
|
||||||
if (0 === count($orderBy)) {
|
$qb = $this->filterBaseQueryByPerson($qb, $person, $role);
|
||||||
$orderBy = ['date' => 'DESC'];
|
|
||||||
|
foreach ($orderBy as $field => $direction) {
|
||||||
|
$qb->addOrderBy('a.' . $field, $direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
$reachableScopes = $this->authorizationHelper
|
if (null !== $start) {
|
||||||
->getReachableCircles($user, $role, $center);
|
$qb->setFirstResult($start);
|
||||||
|
}
|
||||||
|
if (null !== $limit) {
|
||||||
|
$qb->setMaxResults($limit);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->em->getRepository(Activity::class)
|
return $qb->getQuery()->getResult();
|
||||||
->findByPersonImplied($person, $reachableScopes, $orderBy, $limit, $start);
|
}
|
||||||
|
|
||||||
|
private function filterBaseQueryByPerson(QueryBuilder $qb, Person $person, string $role): QueryBuilder
|
||||||
|
{
|
||||||
|
$orX = $qb->expr()->orX();
|
||||||
|
$counter = 0;
|
||||||
|
foreach ($this->centerResolverManager->resolveCenters($person) as $center) {
|
||||||
|
$scopes = $this->authorizationHelper->getReachableScopes($role, $center);
|
||||||
|
|
||||||
|
if ([] === $scopes) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$orX->add(sprintf('a.person = :person AND a.scope IN (:scopes_%d)', $counter));
|
||||||
|
$qb->setParameter(sprintf('scopes_%d', $counter), $scopes);
|
||||||
|
$qb->setParameter('person', $person);
|
||||||
|
$counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($person->getAccompanyingPeriodParticipations() as $participation) {
|
||||||
|
if (!$this->security->isGranted(ActivityVoter::SEE, $participation->getAccompanyingPeriod())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$and = $qb->expr()->andX(
|
||||||
|
sprintf('a.accompanyingPeriod = :period_%d', $counter),
|
||||||
|
sprintf('a.date >= :participation_start_%d', $counter)
|
||||||
|
);
|
||||||
|
|
||||||
|
$qb
|
||||||
|
->setParameter(sprintf('period_%d', $counter), $participation->getAccompanyingPeriod())
|
||||||
|
->setParameter(sprintf('participation_start_%d', $counter), $participation->getStartDate());
|
||||||
|
|
||||||
|
if (null !== $participation->getEndDate()) {
|
||||||
|
$and->add(sprintf('a.date < :participation_end_%d', $counter));
|
||||||
|
$qb
|
||||||
|
->setParameter(sprintf('participation_end_%d', $counter), $participation->getEndDate());
|
||||||
|
}
|
||||||
|
$orX->add($and);
|
||||||
|
$counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === $orX->count()) {
|
||||||
|
$qb->andWhere('FALSE = TRUE');
|
||||||
|
} else {
|
||||||
|
$qb->andWhere($orX);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function queryTimelineIndexer(string $context, array $args = []): array
|
public function queryTimelineIndexer(string $context, array $args = []): array
|
||||||
@@ -226,7 +400,6 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
|
|||||||
|
|
||||||
// acls:
|
// acls:
|
||||||
$reachableCenters = $this->authorizationHelper->getReachableCenters(
|
$reachableCenters = $this->authorizationHelper->getReachableCenters(
|
||||||
$this->tokenStorage->getToken()->getUser(),
|
|
||||||
ActivityVoter::SEE
|
ActivityVoter::SEE
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -251,7 +424,7 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// we get all the reachable scopes for this center
|
// we get all the reachable scopes for this center
|
||||||
$reachableScopes = $this->authorizationHelper->getReachableScopes($this->tokenStorage->getToken()->getUser(), ActivityVoter::SEE, $center);
|
$reachableScopes = $this->authorizationHelper->getReachableScopes(ActivityVoter::SEE, $center);
|
||||||
// we get the ids for those scopes
|
// we get the ids for those scopes
|
||||||
$reachablesScopesId = array_map(
|
$reachablesScopesId = array_map(
|
||||||
static fn (Scope $scope) => $scope->getId(),
|
static fn (Scope $scope) => $scope->getId(),
|
||||||
|
@@ -11,15 +11,32 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\ActivityBundle\Repository;
|
namespace Chill\ActivityBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\Activity;
|
||||||
|
use Chill\ActivityBundle\Entity\ActivityType;
|
||||||
|
use Chill\MainBundle\Entity\UserJob;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
|
||||||
interface ActivityACLAwareRepositoryInterface
|
interface ActivityACLAwareRepositoryInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @return Activity[]|array
|
* Return all the activities associated to an accompanying period and that the user is allowed to apply the given role.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param array{my_activities?: bool, types?: array<ActivityType>, jobs?: array<UserJob>, after?: \DateTimeImmutable|null, before?: \DateTimeImmutable|null} $filters
|
||||||
|
* @return array<Activity>
|
||||||
*/
|
*/
|
||||||
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array;
|
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, array $orderBy = ['date' => 'DESC'], array $filters = []): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{my_activities?: bool, types?: array<ActivityType>, jobs?: array<UserJob>, after?: \DateTimeImmutable|null, before?: \DateTimeImmutable|null} $filters
|
||||||
|
*/
|
||||||
|
public function countByAccompanyingPeriod(AccompanyingPeriod $period, string $role, array $filters = []): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{my_activities?: bool, types?: array<ActivityType>, jobs?: array<UserJob>, after?: \DateTimeImmutable|null, before?: \DateTimeImmutable|null} $filters
|
||||||
|
*/
|
||||||
|
public function countByPerson(Person $person, string $role, array $filters = []): int;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of activities, simplified as array (not object).
|
* Return a list of activities, simplified as array (not object).
|
||||||
@@ -31,7 +48,28 @@ interface ActivityACLAwareRepositoryInterface
|
|||||||
public function findByAccompanyingPeriodSimplified(AccompanyingPeriod $period, ?int $limit = 1000): array;
|
public function findByAccompanyingPeriodSimplified(AccompanyingPeriod $period, ?int $limit = 1000): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Activity[]|array
|
* @param array{my_activities?: bool, types?: array<ActivityType>, jobs?: array<UserJob>, after?: \DateTimeImmutable|null, before?: \DateTimeImmutable|null} $filters
|
||||||
|
* @return array<Activity>
|
||||||
*/
|
*/
|
||||||
public function findByPerson(Person $person, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array;
|
public function findByPerson(Person $person, string $role, ?int $start = 0, ?int $limit = 1000, array $orderBy = ['date' => 'DESC'], array $filters = []): array;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of the type for the activities associated to person or accompanying period
|
||||||
|
*
|
||||||
|
* @return array<ActivityType>
|
||||||
|
*/
|
||||||
|
public function findActivityTypeByAssociated(AccompanyingPeriod|Person $associated): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of the user job for the activities associated to person or accompanying period
|
||||||
|
*
|
||||||
|
* Associated mean the job:
|
||||||
|
* - of the creator;
|
||||||
|
* - of the user (activity.user)
|
||||||
|
* - of all the users
|
||||||
|
*
|
||||||
|
* @return array<UserJob>
|
||||||
|
*/
|
||||||
|
public function findUserJobByAssociated(AccompanyingPeriod|Person $associated): array;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,198 @@
|
|||||||
|
<?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\Repository;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\Activity;
|
||||||
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
|
use Chill\ActivityBundle\Service\GenericDoc\Providers\AccompanyingPeriodActivityGenericDocProvider;
|
||||||
|
use Chill\ActivityBundle\Service\GenericDoc\Providers\PersonActivityGenericDocProvider;
|
||||||
|
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQuery;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\Providers\PersonDocumentGenericDocProvider;
|
||||||
|
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepositoryInterface;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\Mapping\MappingException;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\HttpKernel\HttpCache\Store;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
final readonly class ActivityDocumentACLAwareRepository implements ActivityDocumentACLAwareRepositoryInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private EntityManagerInterface $em,
|
||||||
|
private CenterResolverManagerInterface $centerResolverManager,
|
||||||
|
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser,
|
||||||
|
private Security $security
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildFetchQueryActivityDocumentLinkedToPersonFromPersonContext(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQueryInterface
|
||||||
|
{
|
||||||
|
$query = $this->buildBaseFetchQueryActivityDocumentLinkedToPersonFromPersonContext($person, $startDate, $endDate, $content);
|
||||||
|
|
||||||
|
return $this->addFetchQueryByPersonACL($query, $person);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildBaseFetchQueryActivityDocumentLinkedToPersonFromPersonContext(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery
|
||||||
|
{
|
||||||
|
$storedObjectMetadata = $this->em->getClassMetadata(StoredObject::class);
|
||||||
|
$activityMetadata = $this->em->getClassMetadata(Activity::class);
|
||||||
|
|
||||||
|
$query = new FetchQuery(
|
||||||
|
PersonActivityGenericDocProvider::KEY,
|
||||||
|
sprintf('jsonb_build_object(\'id\', stored_obj.%s, \'activity_id\', activity.%s)', $storedObjectMetadata->getSingleIdentifierColumnName(), $activityMetadata->getSingleIdentifierColumnName()),
|
||||||
|
sprintf('stored_obj.%s', $storedObjectMetadata->getColumnName('createdAt')),
|
||||||
|
sprintf('%s AS stored_obj', $storedObjectMetadata->getSchemaName().'.'.$storedObjectMetadata->getTableName())
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->addJoinClause(
|
||||||
|
'JOIN public.activity_storedobject activity_doc ON activity_doc.storedobject_id = stored_obj.id'
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->addJoinClause(
|
||||||
|
'JOIN public.activity activity ON activity.id = activity_doc.activity_id'
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->addWhereClause(
|
||||||
|
sprintf('activity.%s = ?', $activityMetadata->getSingleAssociationJoinColumnName('person')),
|
||||||
|
[$person->getId()],
|
||||||
|
[Types::INTEGER]
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->addWhereClauses($query, $startDate, $endDate, $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildFetchQueryActivityDocumentLinkedToAccompanyingPeriodFromPersonContext(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery
|
||||||
|
{
|
||||||
|
$storedObjectMetadata = $this->em->getClassMetadata(StoredObject::class);
|
||||||
|
$activityMetadata = $this->em->getClassMetadata(Activity::class);
|
||||||
|
|
||||||
|
$query = new FetchQuery(
|
||||||
|
AccompanyingPeriodActivityGenericDocProvider::KEY,
|
||||||
|
sprintf('jsonb_build_object(\'id\', stored_obj.%s, \'activity_id\', activity.%s)', $storedObjectMetadata->getSingleIdentifierColumnName(), $activityMetadata->getSingleIdentifierColumnName()),
|
||||||
|
sprintf('stored_obj.%s', $storedObjectMetadata->getColumnName('createdAt')),
|
||||||
|
sprintf('%s AS stored_obj', $storedObjectMetadata->getSchemaName().'.'.$storedObjectMetadata->getTableName())
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->addJoinClause(
|
||||||
|
'JOIN public.activity_storedobject activity_doc ON activity_doc.storedobject_id = stored_obj.id'
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->addJoinClause(
|
||||||
|
'JOIN public.activity activity ON activity.id = activity_doc.activity_id'
|
||||||
|
);
|
||||||
|
|
||||||
|
// add documents of activities from parcours context
|
||||||
|
$or = [];
|
||||||
|
$orParams = [];
|
||||||
|
$orTypes = [];
|
||||||
|
foreach ($person->getAccompanyingPeriodParticipations() as $participation) {
|
||||||
|
if (!$this->security->isGranted(ActivityVoter::SEE, $participation->getAccompanyingPeriod())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$or[] = sprintf(
|
||||||
|
'(activity.%s = ? AND stored_obj.%s BETWEEN ?::date AND COALESCE(?::date, \'infinity\'::date))',
|
||||||
|
$activityMetadata->getSingleAssociationJoinColumnName('accompanyingPeriod'),
|
||||||
|
$storedObjectMetadata->getColumnName('createdAt')
|
||||||
|
);
|
||||||
|
$orParams = [...$orParams, $participation->getAccompanyingPeriod()->getId(),
|
||||||
|
DateTimeImmutable::createFromInterface($participation->getStartDate()),
|
||||||
|
null === $participation->getEndDate() ? null : DateTimeImmutable::createFromInterface($participation->getEndDate())];
|
||||||
|
$orTypes = [...$orTypes, Types::INTEGER, Types::DATE_IMMUTABLE, Types::DATE_IMMUTABLE];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] === $or) {
|
||||||
|
$query->addWhereClause('TRUE = FALSE');
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->addWhereClause(sprintf('(%s)', implode(' OR ', $or)), $orParams, $orTypes);
|
||||||
|
|
||||||
|
return $this->addWhereClauses($query, $startDate, $endDate, $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addWhereClauses(FetchQuery $query, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery
|
||||||
|
{
|
||||||
|
$storedObjectMetadata = $this->em->getClassMetadata(StoredObject::class);
|
||||||
|
|
||||||
|
if (null !== $startDate) {
|
||||||
|
$query->addWhereClause(
|
||||||
|
sprintf('stored_obj.%s >= ?', $storedObjectMetadata->getColumnName('createdAt')),
|
||||||
|
[$startDate],
|
||||||
|
[Types::DATE_IMMUTABLE]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $endDate) {
|
||||||
|
$query->addWhereClause(
|
||||||
|
sprintf('stored_obj.%s < ?', $storedObjectMetadata->getColumnName('createdAt')),
|
||||||
|
[$endDate],
|
||||||
|
[Types::DATE_IMMUTABLE]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $content and '' !== $content) {
|
||||||
|
$query->addWhereClause(
|
||||||
|
'stored_obj.title ilike ?',
|
||||||
|
['%' . $content . '%'],
|
||||||
|
[Types::STRING]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addFetchQueryByPersonACL(FetchQuery $fetchQuery, Person $person): FetchQuery
|
||||||
|
{
|
||||||
|
$activityMetadata = $this->em->getClassMetadata(Activity::class);
|
||||||
|
|
||||||
|
$reachableScopes = [];
|
||||||
|
|
||||||
|
foreach ($this->centerResolverManager->resolveCenters($person) as $center) {
|
||||||
|
$reachableScopes = [
|
||||||
|
...$reachableScopes,
|
||||||
|
...$this->authorizationHelperForCurrentUser->getReachableScopes(ActivityVoter::SEE, $center)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] === $reachableScopes) {
|
||||||
|
$fetchQuery->addWhereClause('FALSE = TRUE');
|
||||||
|
|
||||||
|
return $fetchQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fetchQuery->addWhereClause(
|
||||||
|
sprintf(
|
||||||
|
'activity.%s IN (%s)',
|
||||||
|
$activityMetadata->getSingleAssociationJoinColumnName('scope'),
|
||||||
|
implode(', ', array_fill(0, count($reachableScopes), '?'))
|
||||||
|
),
|
||||||
|
array_map(static fn (Scope $s) => $s->getId(), $reachableScopes),
|
||||||
|
array_fill(0, count($reachableScopes), Types::INTEGER)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $fetchQuery;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,37 @@
|
|||||||
|
<?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\Repository;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQuery;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives queries usable for fetching documents, with ACL aware
|
||||||
|
*/
|
||||||
|
interface ActivityDocumentACLAwareRepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return a fetch query for querying document's activities for a person
|
||||||
|
*
|
||||||
|
* This method must check the rights to see a document: the user must be allowed to see the given activities
|
||||||
|
*/
|
||||||
|
public function buildFetchQueryActivityDocumentLinkedToPersonFromPersonContext(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQueryInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a fetch query for querying document's activities for an activity in accompanying periods, but for a given person
|
||||||
|
*
|
||||||
|
* This method must check the rights to see a document: the user must be allowed to see the given accompanying periods
|
||||||
|
*/
|
||||||
|
public function buildFetchQueryActivityDocumentLinkedToAccompanyingPeriodFromPersonContext(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery;
|
||||||
|
}
|
@@ -11,9 +11,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\ActivityBundle\Repository;
|
namespace Chill\ActivityBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\Activity;
|
||||||
use Chill\ActivityBundle\Entity\ActivityType;
|
use Chill\ActivityBundle\Entity\ActivityType;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\ORM\Query\Expr\Join;
|
||||||
|
|
||||||
final class ActivityTypeRepository implements ActivityTypeRepositoryInterface
|
final class ActivityTypeRepository implements ActivityTypeRepositoryInterface
|
||||||
{
|
{
|
||||||
|
@@ -12,12 +12,14 @@ declare(strict_types=1);
|
|||||||
namespace Chill\ActivityBundle\Repository;
|
namespace Chill\ActivityBundle\Repository;
|
||||||
|
|
||||||
use Chill\ActivityBundle\Entity\ActivityType;
|
use Chill\ActivityBundle\Entity\ActivityType;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Doctrine\Persistence\ObjectRepository;
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
interface ActivityTypeRepositoryInterface extends ObjectRepository
|
interface ActivityTypeRepositoryInterface extends ObjectRepository
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @return array|ActivityType[]
|
* @return array<ActivityType>
|
||||||
*/
|
*/
|
||||||
public function findAllActive(): array;
|
public function findAllActive(): array;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
// Access to Bootstrap variables and mixins
|
// Access to Bootstrap variables and mixins
|
||||||
@import '~ChillMainAssets/module/bootstrap/shared';
|
@import '~ChillMainAssets/module/bootstrap/shared';
|
||||||
|
@import '~ChillPersonAssets/chill/scss/mixins.scss';
|
||||||
|
@import 'bootstrap/scss/_badge.scss';
|
||||||
|
|
||||||
//// ACTIVITY CREATION
|
//// ACTIVITY CREATION
|
||||||
// first step: select type page
|
// first step: select type page
|
||||||
@@ -96,3 +98,25 @@ li.document-list-item {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 0.3rem;
|
margin-bottom: 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge-activity-type {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
|
||||||
|
.title_label {
|
||||||
|
@include chill_badge(#9acd32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title_action {
|
||||||
|
padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x);
|
||||||
|
margin-right: 1rem;
|
||||||
|
|
||||||
|
font-size: var(--bs-badge-font-size);
|
||||||
|
font-weight: var(--bs-badge-font-weight);
|
||||||
|
line-height: 1;
|
||||||
|
color: var(--bs-badge-color);
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -80,12 +80,15 @@
|
|||||||
|
|
||||||
<div class="context-{{ context }}">
|
<div class="context-{{ context }}">
|
||||||
|
|
||||||
|
{{ filter|chill_render_filter_order_helper }}
|
||||||
|
|
||||||
{% if activities|length == 0 %}
|
{% if activities|length == 0 %}
|
||||||
<p class="chill-no-data-statement">
|
<p class="chill-no-data-statement">
|
||||||
{{ "There isn't any activities."|trans }}
|
{{ "There isn't any activities."|trans }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
<div class="flex-table activity-list">
|
<div class="flex-table activity-list">
|
||||||
{% for activity in activities %}
|
{% for activity in activities %}
|
||||||
{% include 'ChillActivityBundle:Activity:_list_item.html.twig' with {
|
{% include 'ChillActivityBundle:Activity:_list_item.html.twig' with {
|
||||||
@@ -96,4 +99,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{{ chill_pagination(paginator) }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@@ -0,0 +1,83 @@
|
|||||||
|
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
|
||||||
|
{% import "@ChillDocStore/Macro/macro_mimeicon.html.twig" as mm %}
|
||||||
|
{% import '@ChillPerson/Macro/updatedBy.html.twig' as mmm %}
|
||||||
|
|
||||||
|
{% set person_id = null %}
|
||||||
|
{% if activity.person %}
|
||||||
|
{% set person_id = activity.person.id %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% set accompanying_course_id = null %}
|
||||||
|
{% if activity.accompanyingPeriod %}
|
||||||
|
{% set accompanying_course_id = activity.accompanyingPeriod.id %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="item-bloc activity-item{% if itemBlocClass is defined %} {{ itemBlocClass }}{% endif %}">
|
||||||
|
<div class="item-row">
|
||||||
|
<div class="item-col" style="width: unset">
|
||||||
|
{% if document.isPending %}
|
||||||
|
<div class="badge text-bg-info" data-docgen-is-pending="{{ document.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div>
|
||||||
|
{% elseif document.isFailure %}
|
||||||
|
<div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{% if activity.accompanyingPeriod is not null and context == 'person' %}
|
||||||
|
<span class="badge bg-primary">
|
||||||
|
<i class="fa fa-random"></i> {{ activity.accompanyingPeriod.id }}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
<div class="badge-activity-type">
|
||||||
|
<span class="title_label"></span>
|
||||||
|
<span class="title_action">
|
||||||
|
{{ activity.type.name | localize_translatable_string }}
|
||||||
|
{% if activity.emergency %}
|
||||||
|
<span class="badge bg-danger rounded-pill fs-6 float-end">{{ 'Emergency'|trans|upper }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="denomination h2">
|
||||||
|
{{ document.title|chill_print_or_message("No title") }}
|
||||||
|
</div>
|
||||||
|
{% if document.hasTemplate %}
|
||||||
|
<div>
|
||||||
|
<p>{{ document.template.name|localize_translatable_string }}</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item-col">
|
||||||
|
<div class="container">
|
||||||
|
<div class="dates row text-end">
|
||||||
|
<span>{{ document.createdAt|format_date('short') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="item-row separator">
|
||||||
|
<div class="item-col item-meta">
|
||||||
|
{{ mmm.createdBy(document) }}
|
||||||
|
</div>
|
||||||
|
<ul class="item-col record_actions flex-shrink-1">
|
||||||
|
{% if is_granted('CHILL_ACTIVITY_SEE_DETAILS', activity) %}
|
||||||
|
<li>
|
||||||
|
{{ document|chill_document_button_group(document.title, is_granted('CHILL_ACTIVITY_UPDATE', activity), {small: false}) }}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if is_granted('CHILL_ACTIVITY_SEE', activity)%}
|
||||||
|
<li>
|
||||||
|
<a href="{{ chill_path_add_return_path('chill_activity_activity_show', {'id': activity.id, 'person_id': person_id, 'accompanying_period_id': accompanying_course_id}) }}" class="btn btn-show"></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if is_granted('CHILL_ACTIVITY_UPDATE', activity) %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ chill_path_add_return_path('chill_activity_activity_edit', {'id': activity.id, 'person_id': person_id, 'accompanying_period_id': accompanying_course_id }) }}" class="btn btn-edit"></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -0,0 +1,114 @@
|
|||||||
|
<?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\Service\GenericDoc\Providers;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\Activity;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityDocumentACLAwareRepositoryInterface;
|
||||||
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQuery;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\GenericDocForAccompanyingPeriodProviderInterface;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\Mapping\MappingException;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
final class AccompanyingPeriodActivityGenericDocProvider implements GenericDocForAccompanyingPeriodProviderInterface, GenericDocForPersonProviderInterface
|
||||||
|
{
|
||||||
|
public const KEY = 'accompanying_period_activity_document';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private EntityManagerInterface $em,
|
||||||
|
private Security $security,
|
||||||
|
private ActivityDocumentACLAwareRepositoryInterface $activityDocumentACLAwareRepository,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildFetchQueryForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null, ?string $origin = null): FetchQueryInterface
|
||||||
|
{
|
||||||
|
$storedObjectMetadata = $this->em->getClassMetadata(StoredObject::class);
|
||||||
|
$activityMetadata = $this->em->getClassMetadata(Activity::class);
|
||||||
|
|
||||||
|
$query = new FetchQuery(
|
||||||
|
self::KEY,
|
||||||
|
sprintf("jsonb_build_object('id', doc_obj.%s, 'activity_id', activity.%s)", $storedObjectMetadata->getSingleIdentifierColumnName(), $activityMetadata->getSingleIdentifierColumnName()),
|
||||||
|
'doc_obj.'.$storedObjectMetadata->getColumnName('createdAt'),
|
||||||
|
$storedObjectMetadata->getSchemaName().'.'.$storedObjectMetadata->getTableName().' AS doc_obj'
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->addJoinClause(
|
||||||
|
'JOIN public.activity_storedobject activity_doc ON activity_doc.storedobject_id = doc_obj.id'
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->addJoinClause(
|
||||||
|
'JOIN public.activity activity ON activity.id = activity_doc.activity_id'
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->addWhereClause(
|
||||||
|
'activity.accompanyingperiod_id = ?',
|
||||||
|
[$accompanyingPeriod->getId()],
|
||||||
|
[Types::INTEGER]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (null !== $startDate) {
|
||||||
|
$query->addWhereClause(
|
||||||
|
sprintf('doc_obj.%s >= ?', $storedObjectMetadata->getColumnName('createdAt')),
|
||||||
|
[$startDate],
|
||||||
|
[Types::DATE_IMMUTABLE]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $endDate) {
|
||||||
|
$query->addWhereClause(
|
||||||
|
sprintf('doc_obj.%s < ?', $storedObjectMetadata->getColumnName('createdAt')),
|
||||||
|
[$endDate],
|
||||||
|
[Types::DATE_IMMUTABLE]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $content) {
|
||||||
|
$query->addWhereClause(
|
||||||
|
'doc_obj.title ilike ?',
|
||||||
|
['%' . $content . '%'],
|
||||||
|
[Types::STRING]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AccompanyingPeriod $accompanyingPeriod
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowedForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): bool
|
||||||
|
{
|
||||||
|
return $this->security->isGranted(ActivityVoter::SEE, $accompanyingPeriod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAllowedForPerson(Person $person): bool
|
||||||
|
{
|
||||||
|
return $this->security->isGranted(AccompanyingPeriodVoter::SEE, $person);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null, ?string $origin = null): FetchQueryInterface
|
||||||
|
{
|
||||||
|
return $this->activityDocumentACLAwareRepository
|
||||||
|
->buildFetchQueryActivityDocumentLinkedToAccompanyingPeriodFromPersonContext($person, $startDate, $endDate, $content);
|
||||||
|
}
|
||||||
|
}
|
@@ -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\ActivityBundle\Service\GenericDoc\Providers;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityDocumentACLAwareRepository;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityDocumentACLAwareRepositoryInterface;
|
||||||
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface;
|
||||||
|
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepositoryInterface;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
final readonly class PersonActivityGenericDocProvider implements GenericDocForPersonProviderInterface
|
||||||
|
{
|
||||||
|
public const KEY = 'person_activity_document';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private Security $security,
|
||||||
|
private ActivityDocumentACLAwareRepositoryInterface $personActivityDocumentACLAwareRepository,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null, ?string $origin = null): FetchQueryInterface
|
||||||
|
{
|
||||||
|
return $this->personActivityDocumentACLAwareRepository->buildFetchQueryActivityDocumentLinkedToPersonFromPersonContext(
|
||||||
|
$person,
|
||||||
|
$startDate,
|
||||||
|
$endDate,
|
||||||
|
$content
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Person $person
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowedForPerson(Person $person): bool
|
||||||
|
{
|
||||||
|
return $this->security->isGranted(ActivityVoter::SEE, $person);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,52 @@
|
|||||||
|
<?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\Service\GenericDoc\Renderers;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityRepository;
|
||||||
|
use Chill\ActivityBundle\Service\GenericDoc\Providers\AccompanyingPeriodActivityGenericDocProvider;
|
||||||
|
use Chill\ActivityBundle\Service\GenericDoc\Providers\PersonActivityGenericDocProvider;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\GenericDocDTO;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\Twig\GenericDocRendererInterface;
|
||||||
|
use Chill\DocStoreBundle\Repository\StoredObjectRepository;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
|
||||||
|
final class AccompanyingPeriodActivityGenericDocRenderer implements GenericDocRendererInterface
|
||||||
|
{
|
||||||
|
private StoredObjectRepository $objectRepository;
|
||||||
|
|
||||||
|
private ActivityRepository $activityRepository;
|
||||||
|
|
||||||
|
public function __construct(StoredObjectRepository $storedObjectRepository, ActivityRepository $activityRepository)
|
||||||
|
{
|
||||||
|
$this->objectRepository = $storedObjectRepository;
|
||||||
|
$this->activityRepository = $activityRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supports(GenericDocDTO $genericDocDTO, $options = []): bool
|
||||||
|
{
|
||||||
|
return $genericDocDTO->key === AccompanyingPeriodActivityGenericDocProvider::KEY || $genericDocDTO->key === PersonActivityGenericDocProvider::KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTemplate(GenericDocDTO $genericDocDTO, $options = []): string
|
||||||
|
{
|
||||||
|
return '@ChillActivity/GenericDoc/activity_document.html.twig';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTemplateData(GenericDocDTO $genericDocDTO, $options = []): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'activity' => $this->activityRepository->find($genericDocDTO->identifiers['activity_id']),
|
||||||
|
'document' => $this->objectRepository->find($genericDocDTO->identifiers['id']),
|
||||||
|
'context' => $genericDocDTO->getContext(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,325 @@
|
|||||||
|
<?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\Tests\Repository;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\ActivityType;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityACLAwareRepository;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityRepository;
|
||||||
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Entity\UserJob;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class ActivityACLAwareRepositoryTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser;
|
||||||
|
|
||||||
|
private CenterResolverManagerInterface $centerResolverManager;
|
||||||
|
|
||||||
|
private ActivityRepository $activityRepository;
|
||||||
|
|
||||||
|
private EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
|
private Security $security;
|
||||||
|
|
||||||
|
private RequestStack $requestStack;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->authorizationHelperForCurrentUser = self::$container->get(AuthorizationHelperForCurrentUserInterface::class);
|
||||||
|
$this->centerResolverManager = self::$container->get(CenterResolverManagerInterface::class);
|
||||||
|
$this->activityRepository = self::$container->get(ActivityRepository::class);
|
||||||
|
$this->entityManager = self::$container->get(EntityManagerInterface::class);
|
||||||
|
$this->security = self::$container->get(Security::class);
|
||||||
|
|
||||||
|
$this->requestStack = $requestStack = new RequestStack();
|
||||||
|
$request = $this->prophesize(Request::class);
|
||||||
|
$request->getLocale()->willReturn('fr');
|
||||||
|
$request->getDefaultLocale()->willReturn('fr');
|
||||||
|
$requestStack->push($request->reveal());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideDataFindByAccompanyingPeriod
|
||||||
|
*/
|
||||||
|
public function testFindByAccompanyingPeriod(AccompanyingPeriod $period, User $user, string $role, ?int $start = 0, ?int $limit = 1000, array $orderBy = ['date' => 'DESC'], array $filters = []): void
|
||||||
|
{
|
||||||
|
$security = $this->prophesize(Security::class);
|
||||||
|
$security->isGranted($role, $period)->willReturn(true);
|
||||||
|
$security->getUser()->willReturn($user);
|
||||||
|
|
||||||
|
$repository = new ActivityACLAwareRepository(
|
||||||
|
$this->authorizationHelperForCurrentUser,
|
||||||
|
$this->centerResolverManager,
|
||||||
|
$this->activityRepository,
|
||||||
|
$this->entityManager,
|
||||||
|
$security->reveal(),
|
||||||
|
$this->requestStack
|
||||||
|
);
|
||||||
|
|
||||||
|
$actual = $repository->findByAccompanyingPeriod($period, $role, $start, $limit, $orderBy, $filters);
|
||||||
|
|
||||||
|
self::assertIsArray($actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideDataFindByAccompanyingPeriod
|
||||||
|
*/
|
||||||
|
public function testFindActivityTypeByAccompanyingPeriod(AccompanyingPeriod $period, User $user, string $role, ?int $start = 0, ?int $limit = 1000, array $orderBy = ['date' => 'DESC'], array $filters = []): void
|
||||||
|
{
|
||||||
|
$security = $this->prophesize(Security::class);
|
||||||
|
$security->isGranted($role, $period)->willReturn(true);
|
||||||
|
$security->getUser()->willReturn($user);
|
||||||
|
|
||||||
|
$repository = new ActivityACLAwareRepository(
|
||||||
|
$this->authorizationHelperForCurrentUser,
|
||||||
|
$this->centerResolverManager,
|
||||||
|
$this->activityRepository,
|
||||||
|
$this->entityManager,
|
||||||
|
$security->reveal(),
|
||||||
|
$this->requestStack
|
||||||
|
);
|
||||||
|
|
||||||
|
$actual = $repository->findActivityTypeByAssociated($period);
|
||||||
|
|
||||||
|
self::assertIsArray($actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideDataFindByPerson
|
||||||
|
*/
|
||||||
|
public function testFindActivityTypeByPerson(Person $person, User $user, array $centers, array $scopes, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = [], array $filters = []): void
|
||||||
|
{
|
||||||
|
$role = ActivityVoter::SEE;
|
||||||
|
$centerResolver = $this->prophesize(CenterResolverManagerInterface::class);
|
||||||
|
$centerResolver->resolveCenters($person)->willReturn($centers);
|
||||||
|
|
||||||
|
$authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
|
||||||
|
$authorizationHelper->getReachableScopes($role, Argument::type(Center::class))
|
||||||
|
->willReturn($scopes);
|
||||||
|
|
||||||
|
$security = $this->prophesize(Security::class);
|
||||||
|
$security->isGranted($role, Argument::type(AccompanyingPeriod::class))->willReturn(true);
|
||||||
|
$security->getUser()->willReturn($user);
|
||||||
|
|
||||||
|
$repository = new ActivityACLAwareRepository(
|
||||||
|
$authorizationHelper->reveal(),
|
||||||
|
$centerResolver->reveal(),
|
||||||
|
$this->activityRepository,
|
||||||
|
$this->entityManager,
|
||||||
|
$security->reveal(),
|
||||||
|
$this->requestStack
|
||||||
|
);
|
||||||
|
|
||||||
|
$actual = $repository->findByPerson($person, $role, $start, $limit, $orderBy, $filters);
|
||||||
|
|
||||||
|
self::assertIsArray($actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideDataFindByPerson
|
||||||
|
*/
|
||||||
|
public function testFindByPerson(Person $person, User $user, array $centers, array $scopes, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = [], array $filters = []): void
|
||||||
|
{
|
||||||
|
$centerResolver = $this->prophesize(CenterResolverManagerInterface::class);
|
||||||
|
$centerResolver->resolveCenters($person)->willReturn($centers);
|
||||||
|
|
||||||
|
$authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
|
||||||
|
$authorizationHelper->getReachableScopes($role, Argument::type(Center::class))
|
||||||
|
->willReturn($scopes);
|
||||||
|
|
||||||
|
$security = $this->prophesize(Security::class);
|
||||||
|
$security->isGranted($role, Argument::type(AccompanyingPeriod::class))->willReturn(true);
|
||||||
|
$security->getUser()->willReturn($user);
|
||||||
|
|
||||||
|
$repository = new ActivityACLAwareRepository(
|
||||||
|
$authorizationHelper->reveal(),
|
||||||
|
$centerResolver->reveal(),
|
||||||
|
$this->activityRepository,
|
||||||
|
$this->entityManager,
|
||||||
|
$security->reveal(),
|
||||||
|
$this->requestStack
|
||||||
|
);
|
||||||
|
|
||||||
|
$actual = $repository->findByPerson($person, $role, $start, $limit, $orderBy, $filters);
|
||||||
|
|
||||||
|
self::assertIsArray($actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideDataFindByPerson(): iterable
|
||||||
|
{
|
||||||
|
$this->setUp();
|
||||||
|
|
||||||
|
/** @var Person $person */
|
||||||
|
if (null === $person = $this->entityManager->createQueryBuilder()
|
||||||
|
->select('p')->from(Person::class, 'p')->setMaxResults(1)
|
||||||
|
->getQuery()->getSingleResult()) {
|
||||||
|
throw new \RuntimeException("person not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var AccompanyingPeriod $period1 */
|
||||||
|
if (null === $period1 = $this->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('a')
|
||||||
|
->from(AccompanyingPeriod::class, 'a')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getQuery()
|
||||||
|
->getSingleResult()) {
|
||||||
|
throw new \RuntimeException("no period found");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var AccompanyingPeriod $period2 */
|
||||||
|
if (null === $period2 = $this->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('a')
|
||||||
|
->from(AccompanyingPeriod::class, 'a')
|
||||||
|
->where('a.id > :pid')
|
||||||
|
->setParameter('pid', $period1->getId())
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getQuery()
|
||||||
|
->getSingleResult()) {
|
||||||
|
throw new \RuntimeException("no second period found");
|
||||||
|
}
|
||||||
|
// add a period
|
||||||
|
$period1->addPerson($person);
|
||||||
|
$period2->addPerson($person);
|
||||||
|
$period1->getParticipationsContainsPerson($person)->first()->setEndDate(
|
||||||
|
(new \DateTime('now'))->add(new \DateInterval('P1M'))
|
||||||
|
);
|
||||||
|
|
||||||
|
if ([] === $types = $this->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('t')
|
||||||
|
->from(ActivityType::class, 't')
|
||||||
|
->setMaxResults(2)
|
||||||
|
->getQuery()
|
||||||
|
->getResult()) {
|
||||||
|
throw new \RuntimeException("no types");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] === $jobs = $this->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('j')
|
||||||
|
->from(UserJob::class, 'j')
|
||||||
|
->setMaxResults(2)
|
||||||
|
->getQuery()
|
||||||
|
->getResult()
|
||||||
|
) {
|
||||||
|
throw new \RuntimeException("no jobs found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $user = $this->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('u')
|
||||||
|
->from(User::class, 'u')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getQuery()
|
||||||
|
->getSingleResult()
|
||||||
|
) {
|
||||||
|
throw new \RuntimeException("no user found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] === $centers = $this->entityManager->createQueryBuilder()
|
||||||
|
->select('c')->from(Center::class, 'c')->setMaxResults(2)->getQuery()
|
||||||
|
->getResult()) {
|
||||||
|
throw new \RuntimeException("no centers found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] === $scopes = $this->entityManager->createQueryBuilder()
|
||||||
|
->select('s')->from(Scope::class, 's')->setMaxResults(2)->getQuery()
|
||||||
|
->getResult()) {
|
||||||
|
throw new \RuntimeException("no scopes found");
|
||||||
|
}
|
||||||
|
|
||||||
|
yield [$person, $user, $centers, $scopes, ActivityVoter::SEE, 0, 5, ['date' => 'DESC'], []];
|
||||||
|
yield [$person, $user, $centers, $scopes, ActivityVoter::SEE, 0, 5, ['date' => 'DESC'], ['my_activities' => true]];
|
||||||
|
yield [$person, $user, $centers, $scopes, ActivityVoter::SEE, 0, 5, ['date' => 'DESC'], ['types' => $types]];
|
||||||
|
yield [$person, $user, $centers, $scopes, ActivityVoter::SEE, 0, 5, ['date' => 'DESC'], ['jobs' => $jobs]];
|
||||||
|
yield [$person, $user, $centers, $scopes, ActivityVoter::SEE, 0, 5, ['date' => 'DESC'], ['after' => new \DateTimeImmutable('1 year ago')]];
|
||||||
|
yield [$person, $user, $centers, $scopes, ActivityVoter::SEE, 0, 5, ['date' => 'DESC'], ['before' => new \DateTimeImmutable('1 year ago')]];
|
||||||
|
yield [$person, $user, $centers, $scopes, ActivityVoter::SEE, 0, 5, ['date' => 'DESC'], ['after' => new \DateTimeImmutable('1 year ago'), 'before' => new \DateTimeImmutable('1 month ago')]];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideDataFindByAccompanyingPeriod(): iterable
|
||||||
|
{
|
||||||
|
$this->setUp();
|
||||||
|
|
||||||
|
if (null === $period = $this->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('a')
|
||||||
|
->from(AccompanyingPeriod::class, 'a')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getQuery()
|
||||||
|
->getSingleResult()) {
|
||||||
|
throw new \RuntimeException("no period found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] === $types = $this->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('t')
|
||||||
|
->from(ActivityType::class, 't')
|
||||||
|
->setMaxResults(2)
|
||||||
|
->getQuery()
|
||||||
|
->getResult()) {
|
||||||
|
throw new \RuntimeException("no types");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] === $jobs = $this->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('j')
|
||||||
|
->from(UserJob::class, 'j')
|
||||||
|
->setMaxResults(2)
|
||||||
|
->getQuery()
|
||||||
|
->getResult()
|
||||||
|
) {
|
||||||
|
throw new \RuntimeException("no jobs found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $user = $this->entityManager
|
||||||
|
->createQueryBuilder()
|
||||||
|
->select('u')
|
||||||
|
->from(User::class, 'u')
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getQuery()
|
||||||
|
->getSingleResult()
|
||||||
|
) {
|
||||||
|
throw new \RuntimeException("no user found");
|
||||||
|
}
|
||||||
|
|
||||||
|
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], []];
|
||||||
|
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['my_activities' => true]];
|
||||||
|
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['types' => $types]];
|
||||||
|
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['jobs' => $jobs]];
|
||||||
|
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['after' => new \DateTimeImmutable('1 year ago')]];
|
||||||
|
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['before' => new \DateTimeImmutable('1 year ago')]];
|
||||||
|
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['after' => new \DateTimeImmutable('1 year ago'), 'before' => new \DateTimeImmutable('1 month ago')]];
|
||||||
|
}
|
||||||
|
}
|
@@ -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\ActivityBundle\Tests\Repository;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityDocumentACLAwareRepository;
|
||||||
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
|
use Chill\DocStoreBundle\GenericDoc\FetchQueryToSqlBuilder;
|
||||||
|
use Chill\MainBundle\Entity\Scope;
|
||||||
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use phpseclib3\Math\BinaryField;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class ActivityDocumentACLAwareRepositoryTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
|
||||||
|
private EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
|
private CenterResolverManagerInterface $centerResolverManager;
|
||||||
|
|
||||||
|
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser;
|
||||||
|
|
||||||
|
private Security $security;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$this->entityManager = self::$container->get(EntityManagerInterface::class);
|
||||||
|
$this->centerResolverManager = self::$container->get(CenterResolverManagerInterface::class);
|
||||||
|
$this->authorizationHelperForCurrentUser = self::$container->get(AuthorizationHelperForCurrentUserInterface::class);
|
||||||
|
$this->security = self::$container->get(Security::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideDataForPerson
|
||||||
|
*/
|
||||||
|
public function testBuildFetchQueryActivityDocumentLinkedToPersonFromPersonContext(Person $person, array $reachableScopes, bool $_unused, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate, ?string $content): void
|
||||||
|
{
|
||||||
|
$authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
|
||||||
|
$authorizationHelper->getReachableScopes(ActivityVoter::SEE, Argument::any())
|
||||||
|
->willReturn($reachableScopes);
|
||||||
|
|
||||||
|
$repository = new ActivityDocumentACLAwareRepository(
|
||||||
|
$this->entityManager,
|
||||||
|
$this->centerResolverManager,
|
||||||
|
$authorizationHelper->reveal(),
|
||||||
|
$this->security
|
||||||
|
);
|
||||||
|
|
||||||
|
$query = $repository->buildFetchQueryActivityDocumentLinkedToPersonFromPersonContext($person, $startDate, $endDate, $content);
|
||||||
|
['sql' => $sql, 'params' => $params, 'types' => $types] = (new FetchQueryToSqlBuilder())->toSql($query);
|
||||||
|
|
||||||
|
$nb = $this->entityManager->getConnection()->fetchOne("SELECT COUNT(*) FROM ({$sql}) sq", $params, $types);
|
||||||
|
|
||||||
|
self::assertIsInt($nb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideDataForPerson
|
||||||
|
*/
|
||||||
|
public function testBuildFetchQueryActivityDocumentLinkedToAccompanyingPeriodFromPersonContext(Person $person, array $_unused, bool $canSeePeriod, ?\DateTimeImmutable $startDate, ?\DateTimeImmutable $endDate, ?string $content): void
|
||||||
|
{
|
||||||
|
$security = $this->prophesize(Security::class);
|
||||||
|
$security->isGranted(ActivityVoter::SEE, Argument::type(AccompanyingPeriod::class))
|
||||||
|
->willReturn($canSeePeriod);
|
||||||
|
|
||||||
|
$repository = new ActivityDocumentACLAwareRepository(
|
||||||
|
$this->entityManager,
|
||||||
|
$this->centerResolverManager,
|
||||||
|
$this->authorizationHelperForCurrentUser,
|
||||||
|
$security->reveal()
|
||||||
|
);
|
||||||
|
|
||||||
|
$query = $repository->buildFetchQueryActivityDocumentLinkedToAccompanyingPeriodFromPersonContext($person, $startDate, $endDate, $content);
|
||||||
|
|
||||||
|
['sql' => $sql, 'params' => $params, 'types' => $types] = (new FetchQueryToSqlBuilder())->toSql($query);
|
||||||
|
|
||||||
|
$nb = $this->entityManager->getConnection()->fetchOne("SELECT COUNT(*) FROM ({$sql}) sq", $params, $types);
|
||||||
|
|
||||||
|
self::assertIsInt($nb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideDataForPerson(): iterable
|
||||||
|
{
|
||||||
|
$this->setUp();
|
||||||
|
|
||||||
|
if (null === $person = $this->entityManager->createQuery("SELECT p FROM " . Person::class . " p WHERE SIZE(p.accompanyingPeriodParticipations) > 0 ")
|
||||||
|
->setMaxResults(1)
|
||||||
|
->getSingleResult()) {
|
||||||
|
throw new \RuntimeException("no person in dtabase");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] === $scopes = $this->entityManager->createQuery("SELECT s FROM " . Scope::class . " s ")->setMaxResults(5)->getResult()) {
|
||||||
|
throw new \RuntimeException("no scopes in database");
|
||||||
|
}
|
||||||
|
|
||||||
|
yield [$person, [], true, null, null, null];
|
||||||
|
yield [$person, $scopes, true, null, null, null];
|
||||||
|
yield [$person, $scopes, true, new \DateTimeImmutable("1 month ago"), null, null];
|
||||||
|
yield [$person, $scopes, true, new \DateTimeImmutable("1 month ago"), new \DateTimeImmutable("1 week ago"), null];
|
||||||
|
yield [$person, $scopes, true, new \DateTimeImmutable("1 month ago"), new \DateTimeImmutable("1 week ago"), "content"];
|
||||||
|
yield [$person, $scopes, true, null, new \DateTimeImmutable("1 week ago"), "content"];
|
||||||
|
yield [$person, [], true, new \DateTimeImmutable("1 month ago"), new \DateTimeImmutable("1 week ago"), "content"];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -161,6 +161,7 @@ class TimelineActivityProvider implements TimelineProviderInterface
|
|||||||
|
|
||||||
// loop on reachable scopes
|
// loop on reachable scopes
|
||||||
foreach ($reachableScopes as $scope) {
|
foreach ($reachableScopes as $scope) {
|
||||||
|
/** @phpstan-ignore-next-line */
|
||||||
if (in_array($scope->getId(), $scopes_ids, true)) {
|
if (in_array($scope->getId(), $scopes_ids, true)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@@ -38,3 +38,6 @@ services:
|
|||||||
|
|
||||||
Chill\ActivityBundle\Service\EntityInfo\:
|
Chill\ActivityBundle\Service\EntityInfo\:
|
||||||
resource: '../Service/EntityInfo/'
|
resource: '../Service/EntityInfo/'
|
||||||
|
|
||||||
|
Chill\ActivityBundle\Service\GenericDoc\:
|
||||||
|
resource: '../Service/GenericDoc/'
|
||||||
|
@@ -135,6 +135,10 @@ services:
|
|||||||
tags:
|
tags:
|
||||||
- { name: chill.export_filter, alias: 'accompanyingcourse_has_no_activity_filter' }
|
- { name: chill.export_filter, alias: 'accompanyingcourse_has_no_activity_filter' }
|
||||||
|
|
||||||
|
Chill\ActivityBundle\Export\Filter\ACPFilters\PeriodHavingActivityBetweenDatesFilter:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_filter, alias: 'period_having_activity_betw_dates_filter' }
|
||||||
|
|
||||||
## Aggregators
|
## Aggregators
|
||||||
Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator:
|
Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator:
|
||||||
tags:
|
tags:
|
||||||
@@ -144,6 +148,10 @@ services:
|
|||||||
tags:
|
tags:
|
||||||
- { name: chill.export_aggregator, alias: activity_common_type_aggregator }
|
- { name: chill.export_aggregator, alias: activity_common_type_aggregator }
|
||||||
|
|
||||||
|
Chill\ActivityBundle\Export\Aggregator\ActivityLocationAggregator:
|
||||||
|
tags:
|
||||||
|
- { name: chill.export_aggregator, alias: activity_common_location_aggregator }
|
||||||
|
|
||||||
chill.activity.export.user_aggregator:
|
chill.activity.export.user_aggregator:
|
||||||
class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator
|
class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator
|
||||||
tags:
|
tags:
|
||||||
|
@@ -0,0 +1,5 @@
|
|||||||
|
export:
|
||||||
|
filter:
|
||||||
|
activity:
|
||||||
|
course_having_activity_between_date:
|
||||||
|
Only course having an activity between from and to: Seulement les parcours ayant reçu au moins un échange entre le {from, date, short} et le {to, date, short}
|
@@ -83,12 +83,20 @@ Third persons: Tiers non-pro.
|
|||||||
Others persons: Usagers
|
Others persons: Usagers
|
||||||
Third parties: Tiers professionnels
|
Third parties: Tiers professionnels
|
||||||
Users concerned: T(M)S
|
Users concerned: T(M)S
|
||||||
|
|
||||||
activity:
|
activity:
|
||||||
|
date: Date de l'échange
|
||||||
Insert a document: Insérer un document
|
Insert a document: Insérer un document
|
||||||
Remove a document: Supprimer le document
|
Remove a document: Supprimer le document
|
||||||
comment: Commentaire
|
comment: Commentaire
|
||||||
No documents: Aucun document
|
No documents: Aucun document
|
||||||
|
|
||||||
|
# activity filter in list page
|
||||||
|
activity_filter:
|
||||||
|
My activities: Mes échanges (où j'interviens)
|
||||||
|
Types: Par type d'échange
|
||||||
|
Jobs: Par métier impliqué
|
||||||
|
|
||||||
#timeline
|
#timeline
|
||||||
'%user% has done an %activity_type%': '%user% a effectué un échange de type "%activity_type%"'
|
'%user% has done an %activity_type%': '%user% a effectué un échange de type "%activity_type%"'
|
||||||
|
|
||||||
@@ -365,6 +373,12 @@ export:
|
|||||||
by_usersscope:
|
by_usersscope:
|
||||||
Filter by users scope: Filtrer les échanges par services d'au moins un utilisateur participant
|
Filter by users scope: Filtrer les échanges par services d'au moins un utilisateur participant
|
||||||
'Filtered activity by users scope: only %scopes%': 'Filtré par service d''au moins un utilisateur participant: seulement %scopes%'
|
'Filtered activity by users scope: only %scopes%': 'Filtré par service d''au moins un utilisateur participant: seulement %scopes%'
|
||||||
|
course_having_activity_between_date:
|
||||||
|
Title: Filtre les parcours ayant reçu un échange entre deux dates
|
||||||
|
Receiving an activity after: Ayant reçu un échange après le
|
||||||
|
Receiving an activity before: Ayant reçu un échange avant le
|
||||||
|
|
||||||
|
|
||||||
aggregator:
|
aggregator:
|
||||||
activity:
|
activity:
|
||||||
by_sent_received:
|
by_sent_received:
|
||||||
@@ -372,3 +386,11 @@ export:
|
|||||||
is sent: envoyé
|
is sent: envoyé
|
||||||
is received: reçu
|
is received: reçu
|
||||||
Group activity by sentreceived: Grouper les échanges par envoyé / reçu
|
Group activity by sentreceived: Grouper les échanges par envoyé / reçu
|
||||||
|
by_location:
|
||||||
|
Activity Location: Localisation de l'échange
|
||||||
|
Title: Grouper les échanges par localisation de l'échange
|
||||||
|
|
||||||
|
generic_doc:
|
||||||
|
filter:
|
||||||
|
keys:
|
||||||
|
accompanying_period_activity_document: Document des échanges des parcours
|
||||||
|
@@ -50,6 +50,10 @@ class ByActivityTypeAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// No form needed
|
// No form needed
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -57,6 +57,10 @@ class ByUserJobAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// nothing to add in the form
|
// nothing to add in the form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -57,6 +57,10 @@ class ByUserScopeAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// nothing to add in the form
|
// nothing to add in the form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
{
|
{
|
||||||
|
@@ -34,6 +34,10 @@ class AvgAsideActivityDuration implements ExportInterface, GroupedExportInterfac
|
|||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes(): array
|
public function getAllowedFormattersTypes(): array
|
||||||
{
|
{
|
||||||
|
@@ -34,6 +34,10 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface
|
|||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes(): array
|
public function getAllowedFormattersTypes(): array
|
||||||
{
|
{
|
||||||
|
@@ -73,6 +73,10 @@ final class ListAsideActivity implements ListInterface, GroupedExportInterface
|
|||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes()
|
public function getAllowedFormattersTypes()
|
||||||
{
|
{
|
||||||
|
@@ -34,6 +34,10 @@ class SumAsideActivityDuration implements ExportInterface, GroupedExportInterfac
|
|||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllowedFormattersTypes(): array
|
public function getAllowedFormattersTypes(): array
|
||||||
{
|
{
|
||||||
|
@@ -76,6 +76,10 @@ class ByActivityTypeFilter implements FilterInterface
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -72,11 +72,9 @@ class ByDateFilter implements FilterInterface
|
|||||||
$builder
|
$builder
|
||||||
->add('date_from', PickRollingDateType::class, [
|
->add('date_from', PickRollingDateType::class, [
|
||||||
'label' => 'export.filter.Aside activities after this date',
|
'label' => 'export.filter.Aside activities after this date',
|
||||||
'data' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START),
|
|
||||||
])
|
])
|
||||||
->add('date_to', PickRollingDateType::class, [
|
->add('date_to', PickRollingDateType::class, [
|
||||||
'label' => 'export.filter.Aside activities before this date',
|
'label' => 'export.filter.Aside activities before this date',
|
||||||
'data' => new RollingDate(RollingDate::T_TODAY),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
|
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
|
||||||
@@ -119,6 +117,10 @@ class ByDateFilter implements FilterInterface
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
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'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -53,6 +53,10 @@ class ByUserFilter implements FilterInterface
|
|||||||
'label' => 'Creators',
|
'label' => 'Creators',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string'): array
|
public function describeAction($data, $format = 'string'): array
|
||||||
{
|
{
|
||||||
|
@@ -60,6 +60,10 @@ class ByUserJobFilter implements FilterInterface
|
|||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string')
|
public function describeAction($data, $format = 'string')
|
||||||
{
|
{
|
||||||
|
@@ -67,6 +67,10 @@ class ByUserScopeFilter implements FilterInterface
|
|||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string')
|
public function describeAction($data, $format = 'string')
|
||||||
{
|
{
|
||||||
|
@@ -176,11 +176,12 @@ export:
|
|||||||
agent_id: Utilisateur
|
agent_id: Utilisateur
|
||||||
creator_id: Créateur
|
creator_id: Créateur
|
||||||
main_scope: Service principal de l'utilisateur
|
main_scope: Service principal de l'utilisateur
|
||||||
main_center: Centre principal de l'utilisteur
|
main_center: Centre principal de l'utilisateur
|
||||||
aside_activity_type: Catégorie d'activité annexe
|
aside_activity_type: Catégorie d'activité annexe
|
||||||
date: Date
|
date: Date
|
||||||
duration: Durée
|
duration: Durée
|
||||||
note: Note
|
note: Note
|
||||||
|
id: Identifiant
|
||||||
|
|
||||||
Exports of aside activities: Exports des activités annexes
|
Exports of aside activities: Exports des activités annexes
|
||||||
Count aside activities: Nombre d'activités annexes
|
Count aside activities: Nombre d'activités annexes
|
||||||
|
@@ -18,9 +18,12 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\CalendarBundle\Command;
|
namespace Chill\CalendarBundle\Command;
|
||||||
|
|
||||||
|
use Chill\CalendarBundle\Exception\UserAbsenceSyncException;
|
||||||
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\EventsOnUserSubscriptionCreator;
|
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\EventsOnUserSubscriptionCreator;
|
||||||
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MapCalendarToUser;
|
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MapCalendarToUser;
|
||||||
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MSGraphUserRepository;
|
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MSGraphUserRepository;
|
||||||
|
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MSUserAbsenceSync;
|
||||||
|
use Chill\MainBundle\Repository\UserRepositoryInterface;
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
@@ -30,32 +33,17 @@ use Symfony\Component\Console\Input\InputInterface;
|
|||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
class MapAndSubscribeUserCalendarCommand extends Command
|
final class MapAndSubscribeUserCalendarCommand extends Command
|
||||||
{
|
{
|
||||||
private EntityManagerInterface $em;
|
|
||||||
|
|
||||||
private EventsOnUserSubscriptionCreator $eventsOnUserSubscriptionCreator;
|
|
||||||
|
|
||||||
private LoggerInterface $logger;
|
|
||||||
|
|
||||||
private MapCalendarToUser $mapCalendarToUser;
|
|
||||||
|
|
||||||
private MSGraphUserRepository $userRepository;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EntityManagerInterface $em,
|
private readonly EntityManagerInterface $em,
|
||||||
EventsOnUserSubscriptionCreator $eventsOnUserSubscriptionCreator,
|
private readonly EventsOnUserSubscriptionCreator $eventsOnUserSubscriptionCreator,
|
||||||
LoggerInterface $logger,
|
private readonly LoggerInterface $logger,
|
||||||
MapCalendarToUser $mapCalendarToUser,
|
private readonly MapCalendarToUser $mapCalendarToUser,
|
||||||
MSGraphUserRepository $userRepository
|
private readonly UserRepositoryInterface $userRepository,
|
||||||
|
private readonly MSUserAbsenceSync $userAbsenceSync,
|
||||||
) {
|
) {
|
||||||
parent::__construct('chill:calendar:msgraph-user-map-subscribe');
|
parent::__construct('chill:calendar:msgraph-user-map-subscribe');
|
||||||
|
|
||||||
$this->em = $em;
|
|
||||||
$this->eventsOnUserSubscriptionCreator = $eventsOnUserSubscriptionCreator;
|
|
||||||
$this->logger = $logger;
|
|
||||||
$this->mapCalendarToUser = $mapCalendarToUser;
|
|
||||||
$this->userRepository = $userRepository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(InputInterface $input, OutputInterface $output): int
|
public function execute(InputInterface $input, OutputInterface $output): int
|
||||||
@@ -67,83 +55,109 @@ class MapAndSubscribeUserCalendarCommand extends Command
|
|||||||
/** @var DateInterval $interval the interval before the end of the expiration */
|
/** @var DateInterval $interval the interval before the end of the expiration */
|
||||||
$interval = new DateInterval('P1D');
|
$interval = new DateInterval('P1D');
|
||||||
$expiration = (new DateTimeImmutable('now'))->add(new DateInterval($input->getOption('subscription-duration')));
|
$expiration = (new DateTimeImmutable('now'))->add(new DateInterval($input->getOption('subscription-duration')));
|
||||||
$total = $this->userRepository->countByMostOldSubscriptionOrWithoutSubscriptionOrData($interval);
|
$users = $this->userRepository->findAllAsArray('fr');
|
||||||
$created = 0;
|
$created = 0;
|
||||||
$renewed = 0;
|
$renewed = 0;
|
||||||
|
|
||||||
$this->logger->info(self::class . ' the number of user to get - renew', [
|
$this->logger->info(self::class . ' start user to get - renew', [
|
||||||
'total' => $total,
|
|
||||||
'expiration' => $expiration->format(DateTimeImmutable::ATOM),
|
'expiration' => $expiration->format(DateTimeImmutable::ATOM),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
while ($offset < $total) {
|
foreach ($users as $u) {
|
||||||
$users = $this->userRepository->findByMostOldSubscriptionOrWithoutSubscriptionOrData(
|
++$offset;
|
||||||
$interval,
|
|
||||||
$limit,
|
|
||||||
$offset
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($users as $user) {
|
if (false === $u['enabled']) {
|
||||||
if (!$this->mapCalendarToUser->hasUserId($user)) {
|
continue;
|
||||||
$this->mapCalendarToUser->writeMetadata($user);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->mapCalendarToUser->hasUserId($user)) {
|
|
||||||
// we first try to renew an existing subscription, if any.
|
|
||||||
// if not, or if it fails, we try to create a new one
|
|
||||||
if ($this->mapCalendarToUser->hasActiveSubscription($user)) {
|
|
||||||
$this->logger->debug(self::class . ' renew a subscription for', [
|
|
||||||
'userId' => $user->getId(),
|
|
||||||
'username' => $user->getUsernameCanonical(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
['secret' => $secret, 'id' => $id, 'expiration' => $expirationTs]
|
|
||||||
= $this->eventsOnUserSubscriptionCreator->renewSubscriptionForUser($user, $expiration);
|
|
||||||
$this->mapCalendarToUser->writeSubscriptionMetadata($user, $expirationTs, $id, $secret);
|
|
||||||
|
|
||||||
if (0 !== $expirationTs) {
|
|
||||||
++$renewed;
|
|
||||||
} else {
|
|
||||||
$this->logger->warning(self::class . ' could not renew subscription for a user', [
|
|
||||||
'userId' => $user->getId(),
|
|
||||||
'username' => $user->getUsernameCanonical(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->mapCalendarToUser->hasActiveSubscription($user)) {
|
|
||||||
$this->logger->debug(self::class . ' create a subscription for', [
|
|
||||||
'userId' => $user->getId(),
|
|
||||||
'username' => $user->getUsernameCanonical(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
['secret' => $secret, 'id' => $id, 'expiration' => $expirationTs]
|
|
||||||
= $this->eventsOnUserSubscriptionCreator->createSubscriptionForUser($user, $expiration);
|
|
||||||
$this->mapCalendarToUser->writeSubscriptionMetadata($user, $expirationTs, $id, $secret);
|
|
||||||
|
|
||||||
if (0 !== $expirationTs) {
|
|
||||||
++$created;
|
|
||||||
} else {
|
|
||||||
$this->logger->warning(self::class . ' could not create subscription for a user', [
|
|
||||||
'userId' => $user->getId(),
|
|
||||||
'username' => $user->getUsernameCanonical(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
++$offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->em->flush();
|
$user = $this->userRepository->find($u['id']);
|
||||||
$this->em->clear();
|
|
||||||
|
if (null === $user) {
|
||||||
|
$this->logger->error("could not find user by id", ['uid' => $u['id']]);
|
||||||
|
$output->writeln("could not find user by id : " . $u['id']);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->mapCalendarToUser->hasUserId($user)) {
|
||||||
|
$user = $this->mapCalendarToUser->writeMetadata($user);
|
||||||
|
|
||||||
|
// if user still does not have userid, continue
|
||||||
|
if (!$this->mapCalendarToUser->hasUserId($user)) {
|
||||||
|
$this->logger->warning("user does not have a counterpart on ms api", ['userId' => $user->getId(), 'email' => $user->getEmail()]);
|
||||||
|
$output->writeln(sprintf("giving up for user with email %s and id %s", $user->getEmail(), $user->getId()));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sync user absence
|
||||||
|
try {
|
||||||
|
$this->userAbsenceSync->syncUserAbsence($user);
|
||||||
|
} catch (UserAbsenceSyncException $e) {
|
||||||
|
$this->logger->error("could not sync user absence", ['userId' => $user->getId(), 'email' => $user->getEmail(), 'exception' => $e->getTraceAsString(), "message" => $e->getMessage()]);
|
||||||
|
$output->writeln(sprintf("Could not sync user absence: id: %s and email: %s", $user->getId(), $user->getEmail()));
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we first try to renew an existing subscription, if any.
|
||||||
|
// if not, or if it fails, we try to create a new one
|
||||||
|
if ($this->mapCalendarToUser->hasActiveSubscription($user)) {
|
||||||
|
$this->logger->debug(self::class . ' renew a subscription for', [
|
||||||
|
'userId' => $user->getId(),
|
||||||
|
'username' => $user->getUsernameCanonical(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
['secret' => $secret, 'id' => $id, 'expiration' => $expirationTs]
|
||||||
|
= $this->eventsOnUserSubscriptionCreator->renewSubscriptionForUser($user, $expiration);
|
||||||
|
$this->mapCalendarToUser->writeSubscriptionMetadata($user, $expirationTs, $id, $secret);
|
||||||
|
|
||||||
|
if (0 !== $expirationTs) {
|
||||||
|
++$renewed;
|
||||||
|
} else {
|
||||||
|
$this->logger->warning(self::class . ' could not renew subscription for a user', [
|
||||||
|
'userId' => $user->getId(),
|
||||||
|
'username' => $user->getUsernameCanonical(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->mapCalendarToUser->hasActiveSubscription($user)) {
|
||||||
|
$this->logger->debug(self::class . ' create a subscription for', [
|
||||||
|
'userId' => $user->getId(),
|
||||||
|
'username' => $user->getUsernameCanonical(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
['secret' => $secret, 'id' => $id, 'expiration' => $expirationTs]
|
||||||
|
= $this->eventsOnUserSubscriptionCreator->createSubscriptionForUser($user, $expiration);
|
||||||
|
$this->mapCalendarToUser->writeSubscriptionMetadata($user, $expirationTs, $id, $secret);
|
||||||
|
|
||||||
|
if (0 !== $expirationTs) {
|
||||||
|
++$created;
|
||||||
|
} else {
|
||||||
|
$this->logger->warning(self::class . ' could not create subscription for a user', [
|
||||||
|
'userId' => $user->getId(),
|
||||||
|
'username' => $user->getUsernameCanonical(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (0 === $offset % $limit) {
|
||||||
|
$this->em->flush();
|
||||||
|
$this->em->clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->em->flush();
|
||||||
|
$this->em->clear();
|
||||||
|
|
||||||
$this->logger->warning(self::class . ' process executed', [
|
$this->logger->warning(self::class . ' process executed', [
|
||||||
'created' => $created,
|
'created' => $created,
|
||||||
'renewed' => $renewed,
|
'renewed' => $renewed,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$output->writeln("users synchronized");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +166,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
|
|||||||
parent::configure();
|
parent::configure();
|
||||||
|
|
||||||
$this
|
$this
|
||||||
->setDescription('MSGraph: collect user metadata and create subscription on events for users')
|
->setDescription('MSGraph: collect user metadata and create subscription on events for users, and sync the user absence-presence')
|
||||||
->addOption(
|
->addOption(
|
||||||
'renew-before-end-interval',
|
'renew-before-end-interval',
|
||||||
'r',
|
'r',
|
||||||
|
@@ -0,0 +1,20 @@
|
|||||||
|
<?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\Exception;
|
||||||
|
|
||||||
|
class UserAbsenceSyncException extends \LogicException
|
||||||
|
{
|
||||||
|
public function __construct(string $message = "", int $code = 20_230_706, ?\Throwable $previous = null)
|
||||||
|
{
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
}
|
||||||
|
}
|
@@ -58,6 +58,10 @@ final class AgentAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form
|
// no form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data): Closure
|
public function getLabels($key, array $values, $data): Closure
|
||||||
{
|
{
|
||||||
|
@@ -59,6 +59,10 @@ class CancelReasonAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form
|
// no form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data): Closure
|
public function getLabels($key, array $values, $data): Closure
|
||||||
{
|
{
|
||||||
|
@@ -58,6 +58,10 @@ final class JobAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form
|
// no form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data): Closure
|
public function getLabels($key, array $values, $data): Closure
|
||||||
{
|
{
|
||||||
|
@@ -52,6 +52,10 @@ final class LocationAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form
|
// no form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data): Closure
|
public function getLabels($key, array $values, $data): Closure
|
||||||
{
|
{
|
||||||
|
@@ -58,6 +58,10 @@ final class LocationTypeAggregator implements AggregatorInterface
|
|||||||
{
|
{
|
||||||
// no form
|
// no form
|
||||||
}
|
}
|
||||||
|
public function getFormDefaultData(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data): Closure
|
public function getLabels($key, array $values, $data): Closure
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user