Compare commits

...

253 Commits

Author SHA1 Message Date
8423c6c99c fix banners: resolve_center is always an iterable 2021-11-22 10:55:38 +01:00
03feb78037 fix down migration 2021-11-22 10:49:56 +01:00
98760bc5e8 Merge remote-tracking branch 'origin/master' into doc/authorizaton-documentation-update 2021-11-22 10:30:05 +01:00
1194aaecaf Merge branch 'master' into 295_resume_retouches 2021-11-22 10:08:06 +01:00
f69d6edba3 accourse banner carousel: slide2 control on right, + display when ready 2021-11-22 10:05:34 +01:00
009a093b9d Merge branch 'features/docgen-normalization' into 'master'
Bootstrap encoder for documents

See merge request Chill-Projet/chill-bundles!208
2021-11-22 09:05:15 +00:00
d0bf47e0d5 Bootstrap encoder for documents 2021-11-22 09:05:15 +00:00
7758e1358d better 2021-11-22 09:47:02 +01:00
46a4afe1b3 Merge branch 'features/person-search-improve' into 'master'
refactor search for using search by pertinence

See merge request Chill-Projet/chill-bundles!224

# Description of changes

* remplacer le classement par ordre alphabétique par un classement par ordre de pertinence, qui tient compte:
  * de la présence d'une string avec le nom de la ville;
  * de la similarité;
  * du fait que la recherche commence par une partie du mot recherché
* ajouter la recherche par numéro de téléphone directement dans la barre de recherche et dans le formulaire recherche avancée;
* ajouter la recherche par date de naissance directement dans la barre de recherche;
* ajouter la recherche par ville dans la recherche avancée
* ajouter un lien vers le ménage dans les résultats de recherche
* ajouter l'id du parcours dans les résultats de recherche
* ajouter le demandeur dans les résultats de recherche
* ajout d'un bouton "recherche avancée" sur la page d'accueil
* inclure les droits de vendée dans la recherche par l'api

# Issues related

* https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/300
* https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/182
* https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/193
* https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/212
* https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/169
* https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/149
* https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/156

# Tests

* add tests for extracting phonenumber
* add tests for extracting dates
* test for search still exists
2021-11-22 08:28:22 +00:00
9fb29ec110 refactor search for using search by pertinence 2021-11-22 08:28:22 +00:00
f06f9c10ad update changelog 2021-11-22 09:17:11 +01:00
a8dbf8b986 fix view person_current_address: address must exists 2021-11-19 23:33:13 +01:00
2d536b8b53 fix bounds for view current person address 2021-11-19 23:15:41 +01:00
76179d389d carousel: fix error 2021-11-19 19:48:56 +01:00
da686ab3b5 Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2021-11-19 18:25:58 +01:00
fec852b044 carousel: fix span household position 2021-11-19 18:25:07 +01:00
039483abf7 Merge branch 'issue304_validationOnOriginAccCourse' into 'master'
accompanying course: add client-side validation if no origin

See merge request Chill-Projet/chill-bundles!225
2021-11-19 17:09:05 +00:00
31ec155070 update changelog 2021-11-19 17:27:53 +01:00
89c2d74c33 Merge branch 'master' into 295_resume_retouches 2021-11-19 17:22:00 +01:00
af339aa7f0 accourse banner: use bootstrap carousel to display vue_accourse social-issues and associated-persons as slides 2021-11-19 17:16:16 +01:00
nobohan
264b902db5 upd CHANGELOG 2021-11-19 14:50:55 +01:00
nobohan
f1113ee448 accompanying course: add client-side validation if no origin 2021-11-19 14:48:31 +01:00
Pol Dellaiera
0a522b465f fix: Update typing information. 2021-11-19 12:32:21 +01:00
Pol Dellaiera
693b59c484 fix: Fix wrong repository. 2021-11-19 12:32:05 +01:00
Pol Dellaiera
5a6a15a351 fix: Add missing repository. 2021-11-19 12:31:48 +01:00
aba47600ff hop 2021-11-18 17:56:46 +01:00
a600196151 vue_accourse referrer: style for suggestions, add and remove items 2021-11-18 16:25:36 +01:00
193d0fb94a vue_activity: style for suggestions, add and remove items 2021-11-18 16:02:40 +01:00
c7ffb7062a improve resume course styles 2021-11-18 16:02:09 +01:00
Pol Dellaiera
d1935f96e7 fix: Add more typing informations. 2021-11-18 15:23:44 +01:00
Pol Dellaiera
17ef963924 fix: Fix wrong entity class and a few other fixes. 2021-11-18 15:15:51 +01:00
Pol Dellaiera
597a12b085 fix: Get rid of obsolete import. 2021-11-18 14:50:16 +01:00
Pol Dellaiera
56b15f26b2 fix: Get rid of a single PHPStan critical issue. 2021-11-18 14:47:58 +01:00
Pol Dellaiera
7a07b8bef7 fix: Autowire and autoconfigure the new service. 2021-11-18 14:45:32 +01:00
Pol Dellaiera
1509fcc2e9 fix: Fix tests. 2021-11-18 14:33:14 +01:00
Pol Dellaiera
7fd45d5735 fix: Remove deprecations. 2021-11-18 14:33:06 +01:00
Pol Dellaiera
ae79ebc299 fix: Strict type RoleScope. 2021-11-18 14:29:11 +01:00
Pol Dellaiera
162db60f59 fix: Fix FamilyMemberController and Repository. 2021-11-18 14:28:51 +01:00
Pol Dellaiera
dfc853a876 fix: Replace tabs with spaces. 2021-11-18 14:16:02 +01:00
Pol Dellaiera
674bf614dd fix: Add a deprecation and the new service that goes with it as replacement. 2021-11-18 14:15:51 +01:00
Pol Dellaiera
370a24d3f5 fix: Fix PHPStan issues. 2021-11-18 12:36:50 +01:00
Pol Dellaiera
1e05bd31d8 fix: Fix bug reported by the tests. 2021-11-18 12:18:16 +01:00
Pol Dellaiera
11ad94ee09 fix: Fix bug reported by the tests. 2021-11-18 12:17:04 +01:00
Pol Dellaiera
c1a4454a08 fix: Fix bug reported by the tests. 2021-11-18 12:10:54 +01:00
Pol Dellaiera
f9d36cc8a9 fix: Fix bug reported by the tests. 2021-11-18 12:03:49 +01:00
Pol Dellaiera
a32692c37a fix: RoleScope might or might not have a Scope. 2021-11-18 11:48:35 +01:00
Pol Dellaiera
6ccc8d4cb8 fix: Let Symfony inject dependencies automatically. 2021-11-18 11:46:30 +01:00
Pol Dellaiera
d9b862925f fix: Let Symfony inject dependencies automatically. 2021-11-18 11:41:41 +01:00
Pol Dellaiera
7dcf0f8dcf fix: Inject newly created repository instead of having to deal with the container definitions. 2021-11-18 11:41:41 +01:00
Pol Dellaiera
97ab71b63d fix: Add missing repositories, fix typing information, autoload Repository directory. 2021-11-18 11:41:40 +01:00
Pol Dellaiera
b722106616 fix: Use TranslatableStringHelperInterface. 2021-11-18 11:41:40 +01:00
Pol Dellaiera
cc9ce4167f fix: Upgrade TranslatableStringHelper service and create its interface. 2021-11-18 11:41:40 +01:00
Pol Dellaiera
9f06bc7126 fix: Autowire and autoconfigure services in chill_main.
This commit does not remove services that are not automatically PSR loaded.
2021-11-18 11:41:40 +01:00
78615519f4 select2 field added to task form 2021-11-18 09:34:52 +01:00
Pol Dellaiera
d1e1f7faa4 Revert "fix: Autowire and autoconfigure services in chill_main."
This reverts commit d7fbbbf92c.
2021-11-18 08:34:17 +01:00
Pol Dellaiera
d7fbbbf92c fix: Autowire and autoconfigure services in chill_main. 2021-11-17 20:31:35 +01:00
Pol Dellaiera
d830b19756 fix: Enforce (and document) the type of property. 2021-11-17 20:31:35 +01:00
2492a9281f improve household button in course participation 2021-11-17 16:06:55 +01:00
0e98010d0e #101 improve translations + hide title 2021-11-17 16:06:42 +01:00
1a04973072 visgraph: refresh after post/patch/delete request, fix missing key in POST body parameter 2021-11-17 12:20:43 +01:00
Pol Dellaiera
4f56bb2464 fix: Creation of entity in createEntity().
Issue introduced in 5432242376.
2021-11-17 11:58:15 +01:00
Pol Dellaiera
afa8d7cb72 fix: Creation of entity in createEntity().
Issue introduced in 5432242376.
2021-11-17 11:46:28 +01:00
5d74b3ab0a fix errors when clearing cache 2021-11-17 10:58:52 +01:00
Pol Dellaiera
7f02130ff2 fix: Return type of getAge().
Issue highlighted by c68bda5c9b.
2021-11-16 20:58:28 +01:00
Pol Dellaiera
5432242376 fix: SA: Fix many critical rules.
SA stands for Static Analysis.
2021-11-16 17:13:39 +01:00
Pol Dellaiera
db2010082a fix: SA: Fix "...Access to an undefined property..." rule.
SA stands for Static Analysis.
2021-11-16 16:37:45 +01:00
Pol Dellaiera
6cfcf91757 fix: Follow up f8aeb08594 and fix parameters type. 2021-11-16 16:28:20 +01:00
Pol Dellaiera
f11df95f59 fix: Follow up 8879734ea2 and fix service definition. 2021-11-16 15:46:33 +01:00
Pol Dellaiera
68a21fcc0a fix: SA: Fix "...Instantiated class not found..." rules.
SA stands for Static Analysis.
2021-11-16 15:34:12 +01:00
Pol Dellaiera
8879734ea2 fix: SA: Fix "...does not call parent constructor... and ...Access to an undefined property..." rules.
SA stands for Static Analysis.
2021-11-16 15:30:53 +01:00
Pol Dellaiera
a17c22c74f fix: SA: Fix "...does not call parent constructor...." rule.
SA stands for Static Analysis.
2021-11-16 15:27:46 +01:00
Pol Dellaiera
75d2d50dd2 fix: SA: Fix "...Variable in isset() is never defined...." rule.
SA stands for Static Analysis.
2021-11-16 15:25:48 +01:00
Pol Dellaiera
c94fb2efbf fix: SA: Fix "...variable always exists and is not falsy...." rule.
SA stands for Static Analysis.
2021-11-16 15:23:56 +01:00
Pol Dellaiera
11651fdb2a fix: SA: Fix "...does not call parent constructor...." rule.
SA stands for Static Analysis.
2021-11-16 15:21:26 +01:00
Pol Dellaiera
d382cf35ba fix: SA: Fix "...Variable in isset() is never defined...." rule.
SA stands for Static Analysis.
2021-11-16 15:18:59 +01:00
Pol Dellaiera
479f0ce9ed fix: SA: Fix "...does not match parent method..." rule.
SA stands for Static Analysis.
2021-11-16 15:17:29 +01:00
Pol Dellaiera
e2a8437807 fix: SA: Fix "...Short ternary operator..." rule.
SA stands for Static Analysis.
2021-11-16 15:14:43 +01:00
Pol Dellaiera
5a85d497ab fix: SA: Fix "...Switch condition type..." rule.
Also fix a critical bug in `ReportList.php`.

SA stands for Static Analysis.
2021-11-16 15:12:31 +01:00
Pol Dellaiera
7462babbeb fix: SA: Fix "...an unused use..." rule.
Also fix a critical bug in `ListPerson.php`.

SA stands for Static Analysis.
2021-11-16 15:06:43 +01:00
Pol Dellaiera
8bebe1f904 fix: SA: Split configurations across multiple files.
SA stands for Static Analysis.
2021-11-16 14:49:42 +01:00
Pol Dellaiera
8ede116cf5 fix: SA: Split critical issues in its own file.
SA stands for Static Analysis.
2021-11-16 14:06:24 +01:00
Pol Dellaiera
c68bda5c9b fix: SA: Fix "...invoked with..." rule.
SA stands for Static Analysis.
2021-11-16 12:16:02 +01:00
Pol Dellaiera
a3eb23478a fix: SA: Fix "Call to sprintf" rule.
SA stands for Static Analysis.
2021-11-16 11:52:52 +01:00
Pol Dellaiera
f8aeb08594 fix: SA: Fix "might not be defined" rule.
SA stands for Static Analysis.
2021-11-16 11:41:12 +01:00
Pol Dellaiera
a7b96f1756 fix: SA: Fix "incorrect case" rule.
SA stands for Static Analysis.
2021-11-16 10:29:41 +01:00
196cddab09 Merge remote-tracking branch 'origin/master' into issue185_ACCent_createdBy_updatedBy 2021-11-15 14:14:12 +01:00
4a15022375 update change log 2021-11-15 13:15:32 +01:00
5fb7a6d0ae take into account form_show_centers parameters into thirdparty form type 2021-11-15 13:15:32 +01:00
77add46a70 take parameter form_show_center into account: not to ask center on person creation 2021-11-15 13:15:32 +01:00
b217fb3c39 create option config for asking (or not) center in form 2021-11-15 13:15:31 +01:00
e2d4d9c831 remove use statement 2021-11-15 13:07:44 +01:00
93e75161fc Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2021-11-15 13:05:59 +01:00
5ee67f74d9 remove toast imports for vue 2021-11-15 13:05:27 +01:00
9b03f8130b Merge remote-tracking branch 'origin/issue289_person_extraPhoneNumbers' 2021-11-15 12:30:52 +01:00
6c51d6de51 remove unnecessary space (minor, [ci-skip]) 2021-11-15 12:28:48 +01:00
0aef40f23f Merge remote-tracking branch 'origin/issue267_filter_household_of_the_person' 2021-11-15 12:24:21 +01:00
a1125cfd3a Merge branch 'fix_add_multiple_pick_address_type_in_collection' into 'master'
fix when adding multiple pick address type form in a collection

See merge request Chill-Projet/chill-bundles!216
2021-11-15 11:23:05 +00:00
9816ac8ad7 Merge branch 'master' into 'issue289_person_extraPhoneNumbers'
# Conflicts:
#   CHANGELOG.md
2021-11-15 11:22:17 +00:00
87c83dd7a0 Merge branch 'features/search-person-acl-ordering' into 'master'
add base authorization to person search + improve search ordering + fix testes

See merge request Chill-Projet/chill-bundles!217
2021-11-15 11:17:03 +00:00
8296d60cb6 add base authorization to person search + improve search ordering 2021-11-15 11:17:03 +00:00
nobohan
bfc25c50b9 upd CHANGELOG 2021-11-15 12:03:31 +01:00
nobohan
3417aa8207 person: do not suggest the current household of the person 2021-11-15 12:00:42 +01:00
4ba93bb709 failed job personControllerUpdateTest fixed 2021-11-15 11:56:29 +01:00
bf0ad88cf0 Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2021-11-15 10:22:18 +01:00
b07188eaaf changelog updated 2021-11-15 10:21:57 +01:00
5651efe44d fix phpstan errors 2021-11-13 11:42:26 +01:00
52dc89c06f Merge branch 'master' into household_filiation 2021-11-12 17:20:46 +01:00
fa7409bdf8 visgraph: export canvas as image 2021-11-12 16:58:08 +01:00
nobohan
e413a09a0f upd CHANGELOG 2021-11-12 16:05:47 +01:00
nobohan
a04c499af0 fix when adding multiple pick address type form in a collection 2021-11-12 16:01:03 +01:00
dcf5f1ed66 Merge branch 'issue162_ACCent_display_addresses' into 'master'
Display of incomplete address

See merge request Chill-Projet/chill-bundles!210
2021-11-12 12:15:03 +00:00
6dd74287a8 Merge remote-tracking branch 'origin/master' into issue162_ACCent_display_addresses 2021-11-12 13:14:04 +01:00
2b0093a351 fix typo 2021-11-12 12:08:48 +00:00
e905465e1b Merge branch 'accourse_validation' into 'master'
add validation to accompanying periods

See merge request Chill-Projet/chill-bundles!183
2021-11-12 12:07:31 +00:00
c8135e0741 add validation to accompanying periods 2021-11-12 12:07:31 +00:00
831b4f354c Merge branch 'deleteAccompanyingPeriodWork_234' into 'master'
Delete AccompanyingPeriodWork

See merge request Chill-Projet/chill-bundles!199
2021-11-12 12:06:43 +00:00
7482c709b3 Merge branch 'master' into 'deleteAccompanyingPeriodWork_234'
# Conflicts:
#   CHANGELOG.md
2021-11-12 12:06:34 +00:00
22bdf35eb0 minor fixes 2021-11-12 12:05:16 +00:00
a2e9e4cf6a Merge branch 'userInAddPerson_278' into 'master'
person: user in the modal 'search person'

See merge request Chill-Projet/chill-bundles!198
2021-11-12 11:59:03 +00:00
nobohan
28afe5228a upd CHANGELOG 2021-11-12 12:14:55 +01:00
nobohan
fcbf1bd558 accompanying course work: missing translation in App.vue 2021-11-12 12:10:27 +01:00
nobohan
32c2d96ab6 accompanying course work: add cascade remove on delete Accompanying Period Work 2021-11-12 12:02:45 +01:00
nobohan
eedf5f25bd accompanying course work: translation 2021-11-12 12:00:39 +01:00
nobohan
a7dd15a411 upd CHANGELOG 2021-11-12 11:09:02 +01:00
nobohan
24be9dbe09 person: display other phone numbers in view + translations + add message in case no others phone numbers 2021-11-12 11:05:56 +01:00
39ab7057ce Update CHANGELOG.md 2021-11-12 09:29:37 +00:00
96b44ede3c information about creation and update displayed on person detail page. Vendee person and mineur still to do 2021-11-11 16:56:26 +01:00
95610ffd34 visgraph: improve update graph mechanism
adding an updateHack in store, and a watcher in component.
* updateHack increment a value in the lpop,
* the watcher detect when value changes
* and $forceUpdate

improve layer checkbox legend refresh and rebuild
2021-11-10 20:09:18 +01:00
7230fd9c07 minor changes, and cleaning code 2021-11-10 15:13:23 +01:00
7dc9021eca visgraph: no more need for tiny emitter. remove it 2021-11-10 11:35:19 +01:00
a63e1321b0 cleaning 2021-11-09 22:48:29 +01:00
bfca6d2afc cleaning 2021-11-09 20:06:54 +01:00
b392bc9e65 visgraph: post, patch and delete relationship link works! 2021-11-09 20:00:19 +01:00
d6da6a5d9d visgraph: trying to pass value to edit relationship form fields (wip) 2021-11-09 20:00:09 +01:00
e7fcebc99e add some method for resolving scope in twig 2021-11-09 17:04:49 +01:00
7574b5bfac doc for authorization 2021-11-09 17:04:32 +01:00
nobohan
e6a9701418 upd CHANGELOG 2021-11-09 16:14:25 +01:00
nobohan
66a2009aea person search: translation User + show User in concernedGroups 2021-11-09 16:07:11 +01:00
nobohan
c8b0d62d46 person: display user in the modal 'search person' 2021-11-09 14:21:39 +01:00
16512a1ca1 changelog updated 2021-11-09 11:44:36 +01:00
a373142827 display of incomplete address changed 2021-11-09 11:38:20 +01:00
46e552d034 DELETE endpoint added 2021-11-09 10:58:08 +01:00
nobohan
66c5c4c1d6 accompanying period work: translate flash message 2021-11-08 15:52:24 +01:00
nobohan
70524c506d accompanying period work: add content to the delete page 2021-11-08 15:17:16 +01:00
nobohan
b4d21c23b2 accompanying period work: add logger interface 2021-11-08 14:50:15 +01:00
nobohan
d94c367e3d accompanying period work: can delete the work 2021-11-08 14:47:50 +01:00
nobohan
6afd8ae3cd accompanying period work: add delete twig 2021-11-08 13:52:23 +01:00
nobohan
e195434bf4 accompanyingPeriodWork: add action in controller 2021-11-08 13:52:23 +01:00
nobohan
0299500cad accompanyingPeriodWork: replace 'spinner' by a real spinner 2021-11-08 13:52:23 +01:00
nobohan
952435b36f Activity: correct accompanying_period_id param in twig 2021-11-08 13:52:22 +01:00
2c0198ef78 listen on relationship nodes event (wip) 2021-11-05 20:34:59 +01:00
e55e8e072d visgraph: prepare UI for add relationship link 2021-11-05 19:37:59 +01:00
f92b4b0ea3 visgraph, fix details 2021-11-05 17:50:19 +01:00
b904e33d9b Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-11-05 17:18:55 +01:00
f064e8f307 method added to get current accompanying periods + api adjusted 2021-11-05 17:00:07 +01:00
e911dd30f5 cleaning store 2021-11-05 16:16:24 +01:00
bfcd420cdf visgraph: improve nodes that are hidden when uncheck layer
- a new getter count links by node. before exclude person, check first if node has others visible links
- links id are rewrote to serve count links getter
- unfold and expand only folded person
2021-11-05 14:41:45 +01:00
9494bdee2a visgraph: improve splitId(node_id,'id') : return always integer
node_id value examples:
* 'accompanying_period_124' --> 124
* '124' --> 124
* 124 --> 124
2021-11-05 14:41:45 +01:00
0f8ca77105 visgraph: refactor store excludedNode action 2021-11-05 14:41:45 +01:00
9a00e13532 visgraph: adding node whitelist 2021-11-05 14:41:45 +01:00
1a00798da0 visgraph: canvas options, maximize position 2021-11-05 14:41:45 +01:00
5e2b70249e cleaning 2021-11-05 14:41:45 +01:00
55c2aed613 visgraph: fix algo problems when expanding graph 2021-11-05 14:41:45 +01:00
19badc0062 visgraph: try to remove folded person nodes when layer is unchecked (wip) 2021-11-05 14:41:35 +01:00
5d7f4bde1d minor stuffs 2021-11-03 21:17:25 +01:00
40c5322cba visgraph: an event trigger unfoldPersonsByCourse (store) 2021-11-03 21:15:37 +01:00
8ab0fd59f8 visgraph: add basic graph event mechanism when clicking on nodes 2021-11-03 21:12:42 +01:00
aa9926aa29 translations 2021-11-03 16:56:11 +01:00
adac384279 visgraph: prepare PATCH relationship when submitting (wip) 2021-11-03 16:49:07 +01:00
2099edefc5 visgraph: fix error with GET relations response 2021-11-03 16:45:37 +01:00
e2110691ff Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-11-03 16:11:05 +01:00
cab7cc4150 PATCH added to swagger 2021-11-03 16:04:35 +01:00
6c52233a5a visgraph: with reversed relationship, reverse arrow and not title 2021-11-03 15:56:25 +01:00
58f7715643 visgraph: fix logic error with relationship direction 2021-11-03 15:43:44 +01:00
e7900b8b21 visgraph: fix POST response (create link) 2021-11-03 14:54:27 +01:00
4d08f3583e Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-11-03 14:29:39 +01:00
4dbbc40473 Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-11-03 12:52:41 +01:00
577ed7b6be GET relations adapted, POST fixed 2021-11-03 12:52:21 +01:00
2d3549ee28 visgraph: give more space to graph in twig content 2021-11-03 12:39:47 +01:00
6e3cdb6f58 visgraph, disable action callback on nodes 2021-11-03 12:13:06 +01:00
7d91f3bef5 complete relations fixtures 2021-11-03 11:30:36 +01:00
71695ed793 updating changelog 2021-11-03 09:31:49 +01:00
6d5f94ab45 Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-11-03 09:28:36 +01:00
9e61c417e5 visgraph: improve relationship form UI, change physics solver, disable node edition 2021-11-03 09:20:27 +01:00
e62d026339 visgraph form: other way to display relation 2021-11-02 18:58:25 +01:00
9cffe5161a visgraph: test form without vis-network 2021-11-02 18:45:15 +01:00
cbb3ad04f0 visgraph: right align text when reverse 2021-11-02 16:39:34 +01:00
6b31bf225a visgraph: fix body in POST relationship query 2021-11-02 16:34:01 +01:00
024531fbe1 visgraph: build modal form to create/edit relationship 2021-11-02 15:39:22 +01:00
bd2ee9ddc1 visgraph: adding endpoint to get list of relations 2021-11-02 10:21:06 +01:00
84dfcb2899 tests for GET and POST: attempt 2021-11-01 16:01:21 +01:00
1d774b19e8 datafixture relationship: attempt 2021-11-01 16:00:48 +01:00
d06a4b1ca9 import multiselect (wip) 2021-11-01 15:37:32 +01:00
9cce62c294 visgraph: pass context to modals 2021-11-01 15:36:57 +01:00
d3a08149f0 visgraph: open vue modal when adding/editing edge 2021-11-01 13:31:58 +01:00
5d995115ba visgraph: only addEdge for person to person 2021-11-01 12:59:02 +01:00
41f815bbb9 tiny-emitter package is used as "centralized event hub" between vis-network and vue3 2021-11-01 11:50:36 +01:00
a0940a0c85 visgraph: init window.eventHub as external emitter called in Vue. This implementation works with vue2, but is deprecated in vue3 !!
see https://vue3-fr.netlify.app/guide/migration/events-api.html#_2-x-syntax
2021-11-01 11:48:26 +01:00
c4ba78d076 minor 2021-11-01 10:50:13 +01:00
869e442c2c visgraph: add hover interaction 2021-11-01 09:49:55 +01:00
a57a23dc49 visgraph options settings 2021-10-30 01:51:02 +02:00
851a246257 visgraph: improve label with basic markdown 2021-10-30 00:27:10 +02:00
09903c2f52 click node event (wip) 2021-10-29 19:08:52 +02:00
6719548504 Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-10-29 18:14:02 +02:00
8312955391 visgraph: hide somes persons nodes labels (folded) 2021-10-29 17:54:28 +02:00
8eedee5e91 visgraph: default uncheck layers when loading 2021-10-29 16:54:22 +02:00
b60b1cb668 Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-10-29 15:57:06 +02:00
e6183f6646 migration fixed to insert default true value for isActive 2021-10-29 15:56:57 +02:00
a9d3d2027b minor 2021-10-29 15:32:12 +02:00
80594ed186 Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-10-29 15:05:51 +02:00
1c60a5b51e vue_visgraph: improve graph 2021-10-29 15:05:34 +02:00
039c74a1e4 route annotation removed 2021-10-29 13:20:56 +02:00
50fbc7fd15 processing of review. still ACL left to do 2021-10-29 11:30:01 +02:00
1155555bb3 backend: adapt deprecated getGenderNumeric 2021-10-29 10:37:51 +02:00
76d6a9b4df vue_visgraph: makeFetch (api), add validation exception 2021-10-29 10:01:14 +02:00
e1d28289f6 Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-10-29 09:39:25 +02:00
3a7af94058 GET and POST both working. 2021-10-29 09:29:27 +02:00
998295dc5f vue_visgraph improve (physics, corrections) 2021-10-28 15:47:40 +02:00
317ba0a095 vue_visgraph: store missing persons from courses, household and relationship 2021-10-28 12:11:51 +02:00
69e3a0a6cd Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-10-27 19:56:28 +02:00
92c1bf15cc vue_visgraph: add relationship links with relations 2021-10-27 19:55:24 +02:00
f834bb3bd6 backend normalizer: add id to relationship endpoint 2021-10-27 19:54:28 +02:00
2cb1ad6afc improve household position fixture 2021-10-27 18:11:14 +02:00
83e8b117db start of Relationship fixture 2021-10-27 18:10:35 +02:00
6752a2f6d3 fixture created for Relation entity 2021-10-27 17:40:15 +02:00
1d1a54f653 vue_visgraph: improve household links (edges) 2021-10-27 17:31:04 +02:00
8ff581d5fa vue_visgraph: add links array state in store 2021-10-27 16:40:01 +02:00
7ad0e2f2c8 rename fetch api method 2021-10-27 16:39:24 +02:00
a3b203c306 fix backend error with AccompanyingCourse endpoints 2021-10-27 16:39:16 +02:00
c200e9909e i18n minor changes 2021-10-27 15:16:13 +02:00
326fe5f50b vue_visgraph: cleaning + i18n 2021-10-27 11:53:34 +02:00
2a86fd12d7 vue_visgraph: add checkbox legend to enable/disable layers 2021-10-27 11:40:15 +02:00
ccf6c7ad91 Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-10-27 08:55:51 +02:00
a20eeb6a34 vue_visgraph: add a mechanism to force update component
cfr. https://medium.com/emblatech/ways-to-force-vue-to-re-render-a-component-df866fbacf47
2021-10-26 15:18:53 +02:00
9609fcb5d3 visgraph: edit interface fr translations 2021-10-26 14:53:42 +02:00
b6dd5e92fc Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-10-26 14:49:33 +02:00
b7466c7e85 GET for relationship fixed: associated relation displayed 2021-10-26 14:49:12 +02:00
2ef70b301f visgraph: basic styles by groups, initialize legend, etc. 2021-10-26 14:44:49 +02:00
136c6f19de vue_visgraph: basically display persons, household, courses and edges between them 2021-10-25 18:47:15 +02:00
18ca01b0dc migration changed, further attempts to fix bug 2021-10-25 17:03:45 +02:00
e7f555077e vis need each edge has an id, and vuex don't like vis create it by itself ! 2021-10-25 16:42:38 +02:00
b7f3700928 loop on household members to add edges to graph 2021-10-25 15:00:01 +02:00
f4b9942f3c Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-10-25 14:59:12 +02:00
97f6110be3 prepare course and relationship dispatch 2021-10-25 14:56:23 +02:00
3196a8f785 Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-10-22 19:21:55 +02:00
05d16ec9ab relation and relationship entities created + endpoints. Still something wrong with link between Relation and Relationship 2021-10-22 19:21:32 +02:00
83d91e61cb vue_visgraph: manage vis objects with window variables to avoid conflict between vis and vue 2021-10-22 12:58:32 +02:00
6ff80be88d vue_visgraph: add vis manipulation actions and vis styles 2021-10-22 11:59:55 +02:00
8735602dd6 Merge branch 'household_filiation' of gitlab.com:Chill-Projet/chill-bundles into household_filiation 2021-10-21 10:31:46 +02:00
d7cf45885e vue_visgraph: vuex get household and update nodes array 2021-10-21 10:29:51 +02:00
8d947ea81b viewing permission checked for returned accompanying periods by-person 2021-10-20 13:37:27 +02:00
902c45f0cd endpoint created for acc periods by-person 2021-10-20 13:11:58 +02:00
1b0c19a68f prepare vue_visgraph component, with store, dataset and display basic graph 2021-10-20 09:46:26 +02:00
280 changed files with 10007 additions and 6349 deletions

View File

@@ -12,12 +12,45 @@ and this project adheres to
<!-- write down unreleased development here --> <!-- write down unreleased development here -->
* [task] Select2 field in task form to allow search for a user (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/167)
* remove "search by phone configuration option": search by phone is now executed by default
* remplacer le classement par ordre alphabétique par un classement par ordre de pertinence, qui tient compte:
* de la présence d'une string avec le nom de la ville;
* de la similarité;
* du fait que la recherche commence par une partie du mot recherché
* ajouter la recherche par numéro de téléphone directement dans la barre de recherche et dans le formulaire recherche avancée;
* ajouter la recherche par date de naissance directement dans la barre de recherche;
* ajouter la recherche par ville dans la recherche avancée
* ajouter un lien vers le ménage dans les résultats de recherche
* ajouter l'id du parcours dans les résultats de recherche
* ajouter le demandeur dans les résultats de recherche
* ajout d'un bouton "recherche avancée" sur la page d'accueil
* [person] create an accompanying course: add client-side validation if no origin (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/210)
* [person] fix bounds for computing current person address: the new address appears immediatly
* [docgen] create a normalizer and serializer for normalization on doc format
## Test releases ## Test releases
### Test release 2021-11-15
* [main] fix adding multiple AddresseDeRelais (combine PickAddressType with ChillCollection)
* [person]: do not suggest the current household of the person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/51)
* [person]: display other phone numbers in view + add message in case no others phone numbers (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/184)
* unnecessary whitespace removed from person banner after person-id + double parentheses removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/290)
* [person]: delete accompanying period work, including related objects (cascade) (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/36)
* [address]: Display of incomplete address adjusted.
* [household]: improve relationship graph
* add form to create/edit/delete relationship link,
* improve graph refresh mechanism
* add feature to export canvas as image (png)
* [person suggest] In widget "add person", improve the pertinence of persons when one of the names starts with the pattern;
* [person] do not ask for center any more on person creation
* [3party] do not ask for center any more on 3party creation
### Test release 2021-11-08 ### Test release 2021-11-08
* [person]: Display the name of a user when searching after a User (TMS)
* [person]: Add civility to the person * [person]: Add civility to the person
* [person]: Various improvements on the edit person form * [person]: Various improvements on the edit person form
* [person]: Set available_languages and available_countries as parameters for use in the edit person form * [person]: Set available_languages and available_countries as parameters for use in the edit person form
@@ -44,8 +77,6 @@ and this project adheres to
* [household]: household addresses ordered by ValidFrom date and by id to show the last created address on top. * [household]: household addresses ordered by ValidFrom date and by id to show the last created address on top.
* [socialWorkAction]: display of social issue and parent issues + banner context added. * [socialWorkAction]: display of social issue and parent issues + banner context added.
* [DBAL dependencies] Upgrade to DBAL 3.1 * [DBAL dependencies] Upgrade to DBAL 3.1
* [person]: double parentheses removed around age in banner + whitespace
### Test release 2021-10-27 ### Test release 2021-10-27
@@ -65,6 +96,9 @@ and this project adheres to
* [household members editor] finalisation of editor * [household members editor] finalisation of editor
* [AccompanyingCourse banner]: replace translation referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/70) * [AccompanyingCourse banner]: replace translation referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/70)
* [Location]: add location system in activity and RV (calendar). User can choose in location list or create a new location. * [Location]: add location system in activity and RV (calendar). User can choose in location list or create a new location.
* [household]: add relationship page with dynamic data visualisation graph
## Test releases
### Test release 2021-10-11 ### Test release 2021-10-11

View File

@@ -91,7 +91,8 @@
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"App\\": "tests/app/src/" "App\\": "tests/app/src/",
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
} }
}, },
"minimum-stability": "dev", "minimum-stability": "dev",

View File

@@ -23,15 +23,196 @@ Every time an entity is created, viewed or updated, the software check if the us
The user must be granted access to the action on this particular entity, with this scope and center. The user must be granted access to the action on this particular entity, with this scope and center.
TL;DR
=====
Resolve scope and center
------------------------
In a service, resolve the center and scope of an entity
.. code-block:: php
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
class MyService {
private ScopeResolverDispatcher $scopeResolverDispatcher;
private CenterResolverDispatcher $centerResolverDispatcher;
public function myFunction($entity) {
/** @var null|Center[]|Center $center */
$center = $this->centerResolverDispatcher->resolveCenter($entity);
// $center may be null, an array of center, or an instance of Center
if ($this->scopeResolverDispatcher->isConcerned($entity) {
/** @var null|Scope[]|Scope */
$scope = $this-scopeResolverDispatcher->resolveScope($entity);
// $scope may be null, an array of Scope, or an instance of Scope
}
}
}
In twig template, resolve the center:
.. code-block:: twig
{# resolve a center #}
{% if person|chill_resolve_center is not null%}
{% if person|chill_resolve_center is iterable %}
{% set centers = person|chill_resolve_center %}
{% else %}
{% set centers = [ person|chill_resolve_center ] %}
{% endif %}
<span class="open_sansbold">
{{ 'Center'|trans|upper}} :
</span>
{% for c in centers %}
{{ c.name|upper }}
{% if not loop.last %}, {% endif %}
{% endfor %}
{%- endif -%}
In twig template, resolve the scope:
.. code-block:: twig
{% if entity|chill_is_scope_concerned %}
{% if entity|chill_resolve_scope is iterable %}
{% set scopes = entity|chill_resolve_scope %}
{% else %}
{% set scopes = [ entity|chill_resolve_scope ] %}
{% endif %}
<span>Scopes&nbsp;:</span>
{% for s in scopes %}
{{ c.name|localize_translatable_string }}
{% if not loop.last %}, {% endif %}
{% endfor %}
{%- endif -%}
Build a ``Voter``
-----------------
.. code-block:: php
<?php
namespace Chill\DocStoreBundle\Security\Authorization;
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Core\Security;
class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{
// roles should be stored into constants:
const CREATE = 'CHILL_PERSON_DOCUMENT_CREATE';
const SEE = 'CHILL_PERSON_DOCUMENT_SEE';
const SEE_DETAILS = 'CHILL_PERSON_DOCUMENT_SEE_DETAILS';
const UPDATE = 'CHILL_PERSON_DOCUMENT_UPDATE';
const DELETE = 'CHILL_PERSON_DOCUMENT_DELETE';
protected Security $security;
protected VoterHelperInterface $voterHelper;
public function __construct(
Security $security,
VoterHelperFactoryInterface $voterHelperFactory
) {
$this->security = $security;
// we build here a voter helper. This will ease the operations below.
// when the authorization model is changed, it will be easy to make a different implementation
// of the helper, instead of writing all Voters
$this->voterHelper = $voterHelperFactory
// create a builder with some context
->generate(self::class)
// add the support of given roles for given class:
->addCheckFor(Person::class, [self::SEE, self::CREATE])
->addCheckFor(PersonDocument::class, $this->getRoles())
->build();
}
protected function supports($attribute, $subject)
{
return $this->voterHelper->supports($attribute, $subject);
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
// basic check
if (!$token->getUser() instanceof User) {
return false;
}
// we first check the acl for associated elements.
// here, we must be able to see the person associated to the document:
if ($subject instanceof PersonDocument
&& !$this->security->isGranted(PersonVoter::SEE, $subject->getPerson())) {
// not possible to see the associated person ? Then, not possible to see the document!
return false;
}
// the voter helper will implements the logic:
return $this->voterHelper->voteOnAttribute($attribute, $subject, $token);
}
// all the method below are used to register roles into the admin part
public function getRoles()
{
return [
self::CREATE,
self::SEE,
self::SEE_DETAILS,
self::UPDATE,
self::DELETE
];
}
public function getRolesWithoutScope()
{
return array();
}
public function getRolesWithHierarchy()
{
return ['PersonDocument' => $this->getRoles() ];
}
}
From an user point of view From an user point of view
-------------------------- ==========================
The software is design to allow fine tuned access rights for complicated installation and team structure. The administrators may also decide that every user has the right to see all resources, where team have a more simple structure. The software is design to allow fine tuned access rights for complicated installation and team structure. The administrators may also decide that every user has the right to see all resources, where team have a more simple structure.
Here is an overview of the model. Here is an overview of the model.
Chill can be multi-center Chill can be multi-center
^^^^^^^^^^^^^^^^^^^^^^^^^ -------------------------
Chill is designed to be installed once for social center who work with multiple teams separated, or for social services's federation who would like to share the same installation of the software for all their members. Chill is designed to be installed once for social center who work with multiple teams separated, or for social services's federation who would like to share the same installation of the software for all their members.
@@ -42,7 +223,7 @@ Otherwise, it is not required to create multiple center: Chill can also work for
Obviously, users working in the different centers are not allowed to see the entities (_persons_, _reports_, _activities_) of other centers. But users may be attached to multiple centers: consequently they will be able to see the entities of the multiple centers they are attached to. Obviously, users working in the different centers are not allowed to see the entities (_persons_, _reports_, _activities_) of other centers. But users may be attached to multiple centers: consequently they will be able to see the entities of the multiple centers they are attached to.
Inside center, scope divide team Inside center, scope divide team
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --------------------------------
Users are attached to one or more center and, inside to those center, there may exists differents scopes. The aim of those _scopes_ is to divide the whole team of social worker amongst different departement, for instance: the social team, the psychologist team, the nurse team, the administrative team, ... Each team is granted of different rights amongst scope. For instance, the social team may not see the _activities_ of the psychologist team. The administrative team may see the date & time's activities, but is not allowed to see the detail of those entities (the personal notes, ...). Users are attached to one or more center and, inside to those center, there may exists differents scopes. The aim of those _scopes_ is to divide the whole team of social worker amongst different departement, for instance: the social team, the psychologist team, the nurse team, the administrative team, ... Each team is granted of different rights amongst scope. For instance, the social team may not see the _activities_ of the psychologist team. The administrative team may see the date & time's activities, but is not allowed to see the detail of those entities (the personal notes, ...).
@@ -52,8 +233,38 @@ As entities have only one scopes, if some entities must be shared across two dif
Example: if some activities must be seen and updated between nurses and psychologists, the administrator will create a scope "nurse and psy" and add the ability for both team "nurse" and "psychologist" to "create", "see", and "update" the activities belonging to scope "nurse and psy". Example: if some activities must be seen and updated between nurses and psychologists, the administrator will create a scope "nurse and psy" and add the ability for both team "nurse" and "psychologist" to "create", "see", and "update" the activities belonging to scope "nurse and psy".
Where does the ``scope`` and ``center`` comes from ?
====================================================
Most often, scope and center comes from user's input:
* when person is created, Chill asks the associated center to the user. Then, every entity associated to the user (Activity, ...) is associated to this center;
* when an entity is created, Chill asks the associated scope.
The UI check the model before adding those input into form. If the user hae access to only one center or scope, this scope or center is filled automatically, and the UI does not ask the user. Most of the times, the user does not see "Pick a scope" and "Pick a center" inputs.
Scope and Center are associated to entities through ``ManyToOne`` properties, which are then mapped to ``FOREIGN KEY`` in tables, ...
But sometimes, this implementation does not fits the needs:
* persons are associated to center *geographically*: the address of each person contains lat/lon coordinates, and the center is resolved from this coordinated;
* some would like to associated persons to multiple center, or one center;
* entities are associated to scope through the job reached by "creator" (an user);
* some would like not to use scope at all;
*
For this reasons, associated center and scopes must be resolved programmatically. The default implementation rely on the model association, as described above. But it becomes possible to change the behaviour on different implementations.
Is my entity "concerned" by scopes ?
------------------------------------
Some entities are concerned by scope, some not.
This is also programmatically resolved.
The concepts translated into code The concepts translated into code
----------------------------------- ===================================
.. figure:: /_static/access_control_model.png .. figure:: /_static/access_control_model.png
@@ -81,7 +292,7 @@ At each step of his lifetime (creation, view of the entity and eventually of his
All those action are executed through symfony voters and helpers. All those action are executed through symfony voters and helpers.
How to check authorization ? How to check authorization ?
---------------------------- ============================
Just use the symfony way-of-doing, but do not forget to associate the entity you want to check access. For instance, in controller : Just use the symfony way-of-doing, but do not forget to associate the entity you want to check access. For instance, in controller :
@@ -100,34 +311,23 @@ Just use the symfony way-of-doing, but do not forget to associate the entity you
And in template : And in template :
.. code-block:: html+jinja .. code-block:: twig
{{ if is_granted('CHILL_ENTITY_SEE', entity) %}print something{% endif %} {{ if is_granted('CHILL_ENTITY_SEE', entity) %}print something{% endif %}
Retrieving reachable scopes and centers Retrieving reachable scopes and centers for a user
---------------------------------------- --------------------------------------------------
The class :class:`Chill\\MainBundle\\Security\\Authorization\\AuthorizationHelper` helps you to get centers and scope reachable by a user. The class :class:`Chill\\MainBundle\\Security\\Authorization\\AuthorizationHelperInterface` helps you to get centers and scope reachable by a user.
Those methods are intentionnaly build to give information about user rights: Those methods are intentionnaly build to give information about user rights:
- getReachableCenters: to get reachable centers for a user - getReachableCenters: to get reachable centers for a user
- getReachableScopes : to get reachable scopes for a user - getReachableScopes : to get reachable scopes for a user
.. note::
The service is reachable through the Depedency injection with the key `chill.main.security.authorization.helper`. Example :
.. code-block:: php
$helper = $container->get('chill.main.security.authorization.helper');
.. todo::
Waiting for a link between our api and this doc, we invite you to read the method signatures `here <https://github.com/Chill-project/Main/blob/add_acl/Security/Authorization/AuthorizationHelper.php>`_
Adding your own roles Adding your own roles
===================== ---------------------
Extending Chill will requires you to define your own roles and rules for your entities. You will have to define your own voter to do so. Extending Chill will requires you to define your own roles and rules for your entities. You will have to define your own voter to do so.
@@ -152,7 +352,7 @@ To create your own roles, you should:
Declare your role Declare your role
------------------ ^^^^^^^^^^^^^^^^^^
To declare new role, implement the class :class:`Chill\\MainBundle\\Security\\ProvideRoleInterface`. To declare new role, implement the class :class:`Chill\\MainBundle\\Security\\ProvideRoleInterface`.
@@ -212,69 +412,8 @@ Example of an implementation of :class:`Chill\\MainBundle\\Security\\ProvideRole
} }
Implement your voter
--------------------
Inside this class, you might use the :class:`Chill\\MainBundle\\Security\\Authorization\\AuthorizationHelper` to check permission (do not re-invent the wheel). This is a real-world example:
.. code-block:: php
namespace Chill\ReportBundle\Security\Authorization;
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
class ReportVoter extends AbstractChillVoter
{
const CREATE = 'CHILL_REPORT_CREATE';
const SEE = 'CHILL_REPORT_SEE';
const UPDATE = 'CHILL_REPORT_UPDATE';
/**
*
* @var AuthorizationHelper
*/
protected $helper;
public function __construct(AuthorizationHelper $helper)
{
$this->helper = $helper;
}
protected function getSupportedAttributes()
{
return array(self::CREATE, self::SEE, self::UPDATE);
}
protected function getSupportedClasses()
{
return array('Chill\ReportBundle\Entity\Report');
}
protected function isGranted($attribute, $report, $user = null)
{
if (! $user instanceof \Chill\MainBundle\Entity\User){
return false;
}
return $this->helper->userHasAccess($user, $report, $attribute);
}
}
Then, you will have to declare the service and tag it as a voter :
.. code-block:: yaml
services:
chill.report.security.authorization.report_voter:
class: Chill\ReportBundle\Security\Authorization\ReportVoter
arguments:
- "@chill.main.security.authorization.helper"
tags:
- { name: security.voter }
Adding role hierarchy Adding role hierarchy
--------------------- ^^^^^^^^^^^^^^^^^^^^^
You should prepend Symfony's security component directly from your code. You should prepend Symfony's security component directly from your code.
@@ -312,3 +451,484 @@ You should prepend Symfony's security component directly from your code.
} }
Implement your voter
^^^^^^^^^^^^^^^^^^^^
Most of the time, Voter will check that:
1. The given role is reachable (= ``$attribute``)
2. for the given center,
3. and, if any, for the given role
4. if the entity is associated to another entity, this entity should be, at least, viewable by the user.
Thats what we call the "autorization logic". But this logic may be replace by a new one, and developers should take care of it.
Then voter implementation should take care of:
* check the access to associated entities. For instance, if an ``Activity`` is associated to a ``Person``, the voter should first check that the user can show the associated ``Person``;
* as far as possible, delegates the check for associated center, scopes, and check for authorization using the authorization logic. VoterHelper will ease the most common operation of this logic.
This is an example of implementation:
.. code-block:: php
<?php
namespace Chill\DocStoreBundle\Security\Authorization;
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{
protected Security $security;
protected VoterHelperInterface $voterHelper;
public function __construct(
Security $security,
VoterHelperFactoryInterface $voterHelperFactory
) {
$this->security = $security;
// we build here a voter helper. This will ease the operations below.
// when the authorization model is changed, it will be easy to make a different implementation
// of the helper, instead of writing all Voters
$this->voterHelper = $voterHelperFactory
// create a builder with some context
->generate(self::class)
// add the support of given roles for given class:
->addCheckFor(Person::class, [self::SEE, self::CREATE])
->addCheckFor(PersonDocument::class, $this->getRoles())
->build();
}
protected function supports($attribute, $subject)
{
return $this->voterHelper->supports($attribute, $subject);
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
// basic check
if (!$token->getUser() instanceof User) {
return false;
}
// we first check the acl for associated elements.
// here, we must be able to see the person associated to the document:
if ($subject instanceof PersonDocument
&& !$this->security->isGranted(PersonVoter::SEE, $subject->getPerson())) {
// not possible to see the associated person ? Then, not possible to see the document!
return false;
}
// the voter helper will implements the logic of checking:
// 1. that the center is reachable
// 2. for this given entity
// 3. for this given scope
// 4. and for the given role
return $this->voterHelper->voteOnAttribute($attribute, $subject, $token);
}
public function getRoles()
{
// ...
}
public function getRolesWithoutScope()
{
// ...
}
public function getRolesWithHierarchy()
{
// ...
}
}
Then, you will have to declare the service and tag it as a voter :
.. code-block:: yaml
services:
chill.report.security.authorization.report_voter:
class: Chill\ReportBundle\Security\Authorization\ReportVoter
arguments:
- "@chill.main.security.authorization.helper"
tags:
- { name: security.voter }
How to resolve scope and center programmatically ?
==================================================
In a service, resolve the center and scope of an entity
.. code-block:: php
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
class MyService {
private ScopeResolverDispatcher $scopeResolverDispatcher;
private CenterResolverDispatcher $centerResolverDispatcher;
public function myFunction($entity) {
/** @var null|Center[]|Center $center */
$center = $this->centerResolverDispatcher->resolveCenter($entity);
// $center may be null, an array of center, or an instance of Center
if ($this->scopeResolverDispatcher->isConcerned($entity) {
/** @var null|Scope[]|Scope */
$scope = $this-scopeResolverDispatcher->resolveScope($entity);
// $scope may be null, an array of Scope, or an instance of Scope
}
}
}
In twig template, resolve the center:
.. code-block:: twig
{# resolve a center #}
{% if person|chill_resolve_center is not null%}
{% if person|chill_resolve_center is iterable %}
{% set centers = person|chill_resolve_center %}
{% else %}
{% set centers = [ person|chill_resolve_center ] %}
{% endif %}
<span class="open_sansbold">
{{ 'Center'|trans|upper}} :
</span>
{% for c in centers %}
{{ c.name|upper }}
{% if not loop.last %}, {% endif %}
{% endfor %}
{%- endif -%}
In twig template, resolve the scope:
.. code-block:: twig
{% if entity|chill_is_scope_concerned %}
{% if entity|chill_resolve_scope is iterable %}
{% set scopes = entity|chill_resolve_scope %}
{% else %}
{% set scopes = [ entity|chill_resolve_scope ] %}
{% endif %}
<span>Scopes&nbsp;:</span>
{% for s in scopes %}
{{ c.name|localize_translatable_string }}
{% if not loop.last %}, {% endif %}
{% endfor %}
{%- endif -%}
What is the default implementation of Scope and Center resolver ?
-----------------------------------------------------------------
By default, the implementation rely on association into entities.
* implements ``Chill\MainBundle\Entity\HasCenterInterface`` on entities which have one or any center;
* implements ``Chill\MainBundle\Entity\HasCentersInterface`` on entities which have one, multiple or any centers;
* implements ``Chill\MainBundle\Entity\HasScopeInterface`` on entities which have one or any scope;
* implements ``Chill\MainBundle\Entity\HasScopesInterface`` on entities which have one or any scopes;
Then, the default implementation will resolve the center and scope based on the implementation in your model.
How to change the default behaviour ?
-------------------------------------
Implements those interface into services:
* ``Chill\MainBundle\Security\Resolver\CenterResolverInterface``;
* ``Chill\MainBundle\Security\Resolver\ScopeResolverInterface``;
Authorization into lists and index pages
========================================
Due to the fact that authorization model may be overriden, "list" and "index" pages should not rely on center and scope from controller. This must be delegated to dedicated service, which will be aware of the authorization model. We call them ``ACLAwareRepository``. This service must implements an interface, in order to allow to change the implementation.
The controller **must not** performs any DQL or SQL query.
Example in a controller:
.. code-block:: php
namespace Chill\TaskBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface;
final class SingleTaskController extends AbstractController
{
private SingleTaskAclAwareRepositoryInterface $singleTaskAclAwareRepository;
/**
*
* @Route(
* "/{_locale}/task/single-task/list",
* name="chill_task_singletask_list"
* )
*/
public function listAction(
Request $request
) {
$this->denyAccessUnlessGranted(TaskVoter::SHOW, null);
$nb = $this->singleTaskAclAwareRepository->countByAllViewable(
'', // search pattern
[] // search flags
);
$paginator = $this->paginatorFactory->create($nb);
if (0 < $nb) {
$tasks = $this->singleTaskAclAwareRepository->findByAllViewable(
'', // search pattern
[] // search flags
$paginator->getCurrentPageFirstItemNumber(),
$paginator->getItemsPerPage(),
// ordering:
[
'startDate' => 'DESC',
'endDate' => 'DESC',
]
);
} else {
$tasks = [];
}
return $this->render('@ChillTask/SingleTask/List/index.html.twig', [
'tasks' => $tasks,
'paginator' => $paginator,
'filter_order' => $filterOrder
]);
}
}
Writing ``ACLAwareRepository``
------------------------------
The ACLAwareRepository should rely on interfaces
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
As described above, the ACLAwareRepository will perform the query for listing entities, and take care of authorization.
Those "ACLAwareRepositories" must be described into ``interfaces``.
The service must rely on this interface, and not on the default implementation.
Example: at first, we design an interface for listing ``SingleTask`` entities:
.. code-block:: php
<?php
namespace Chill\TaskBundle\Repository;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
interface SingleTaskAclAwareRepositoryInterface
{
/**
* @return SingleTask[]|array
*/
public function findByCurrentUsersTasks(?string $pattern = null, ?array $flags = [], ?int $start = 0, ?int $limit = 50, ?array $orderBy = []): array;
public function countByCurrentUsersTasks(?string $pattern = null, ?array $flags = []): int;
public function countByAllViewable(
?string $pattern = null,
?array $flags = []
): int;
/**
* @return SingleTask[]|array
*/
public function findByAllViewable(
?string $pattern = null,
?array $flags = [],
?int $start = 0,
?int $limit = 50,
?array $orderBy = []
): array;
}
Implements this interface and register the interface as an alias for the implementation.
.. code-block:: yaml
services:
Chill\TaskBundle\Repository\SingleTaskAclAwareRepository:
autowire: true
autoconfigure: true
Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface: '@Chill\TaskBundle\Repository\SingleTaskAclAwareRepository'
Write the basic implementation for re-use: separate authorization logic and search logic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The logic of such repository may be separated into two logic:
* the authorization logic (show only entities that the user is allowed to see);
* the search logic (filter entities on some criterias).
This logic should be separated into your implementation.
Considering this simple interface:
.. code-block:: php
interface MyEntityACLAwareRepositoryInterface {
public function countByAuthorized(array $criterias): int;
public function findByAuthorized(array $criteria, int $start, int $limit, array $orderBy): array;
}
The base implementation should separate the logic to allow an easy reuse. Here, the method ``buildQuery`` build a basic query without authorization logic, which can be re-used. The authorization logic is dedicated to a private method. For ease of user, the logic of adding ordering criterias and pagination parameters (``$start`` and ``$limit``) are also delegated to a public method.
.. code-block:: php
namespace Chill\MyBundle\Repository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
final class MyEntityACLAwareRepository implements MyEntityACLAwareRepositoryInterface {
private EntityManagerInterface $em;
// constructor omitted
public function countByAuthorized(array $criterias): int
{
$qb = $this->buildQuery($criterias);
return $this->addAuthorizations($qb)->select("COUNT(e)")->getQuery()->getResult()->getSingleScalarResult();
}
public function findByAuthorized(array $criteria, int $start, int $limit, array $orderBy): array
{
$qb = $this->buildQuery($criterias);
return $this->getResult($this->addAuthorizations($qb), $start, $limit, $orderBy);
}
public function getResult(QueryBuilder $qb, int $start, int $limit, array $orderBy): array
{
$qb
->setFirstResult($start)
->setMaxResults($limit)
;
// add order by logic
return $qb->getQuery()->getResult();
}
public function buildQuery(array $criterias): QueryBuilder
{
$qb = $this->em->createQueryBuilder();
// implement you logic with search criteria here
return $qb;
}
private function addAuthorizations(QueryBuilder $qb): QueryBuilder
{
// add authorization logic here
return $qb;
}
}
Once this logic is executed, it becomes easy to make a new implementation of the repository:
.. code-block:: php
namespace Chill\MyOtherBundle\Repository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use Chill\MyBundle\Repository\MyEntityACLAwareRepository
final class AnotherEntityACLAwareRepository implements MyEntityACLAwareRepositoryInterface {
private EntityManagerInterface $em;
private \Chill\MyBundle\Repository\MyEntityACLAwareRepository $initial;
public function __construct(
EntityManagerInterface $em,
\Chill\MyBundle\Repository\MyEntityACLAwareRepository $initial
) {
$this->em = $em;
$this->initial = $initial;
}
public function countByAuthorized(array $criterias): int
{
$qb = $this->initial->buildQuery($criterias);
return $this->addAuthorizations($qb)->select("COUNT(e)")->getQuery()->getResult()->getSingleScalarResult();
}
public function findByAuthorized(array $criteria, int $start, int $limit, array $orderBy): array
{
$qb = $this->initial->buildQuery($criterias);
return $this->initial->getResult($this->addAuthorizations($qb), $start, $limit, $orderBy);
}
private function addAuthorizations(QueryBuilder $qb): QueryBuilder
{
// add a different authorization logic here
return $qb;
}
}
Then, register this service and decorates the old one:
.. code-block:: yaml
services:
Chill\MyOtherBundle\Repository\AnotherEntityACLAwareRepository:
autowire: true
autoconfigure: true
decorates: Chill\MyBundle\Repository\MyEntityACLAwareRepositoryInterface:

File diff suppressed because it is too large Load Diff

131
phpstan-critical.neon Normal file
View File

@@ -0,0 +1,131 @@
parameters:
ignoreErrors:
-
message: "#^Implicit array creation is not allowed \\- variable \\$centers might not exist\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
-
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:\\$currentHouseholdParticipationAt\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Entity/Person.php
-
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Entity\\\\Household\\\\PersonHouseholdAddress\\:\\:\\$relation\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/Household/PersonHouseholdAddress.php
-
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Entity\\\\AccompanyingPeriod\\:\\:\\$work\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
-
message: "#^Undefined variable\\: \\$person$#"
count: 1
path: src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php
-
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Household\\\\MembersEditorFactory\\:\\:\\$validator\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Household/MembersEditorFactory.php
-
message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:countBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$action$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$limit$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$offset$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$orderBy$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Variable variables are not allowed\\.$#"
count: 4
path: src/Bundle/ChillPersonBundle/Search/PersonSearch.php
-
message: "#^Function Chill\\\\PersonBundle\\\\Serializer\\\\Normalizer\\\\·\\\\is_array not found\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/MembersEditorNormalizer.php
-
message: "#^Undefined variable\\: \\$value$#"
count: 1
path: src/Bundle/ChillPersonBundle/Validator/Constraints/AccompanyingPeriod/LocationValidityValidator.php
-
message: "#^Undefined variable\\: \\$choiceSlug$#"
count: 1
path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php
-
message: "#^Undefined variable\\: \\$choiceSlug$#"
count: 1
path: src/Bundle/ChillReportBundle/Export/Export/ReportList.php
-
message: "#^Undefined variable\\: \\$type$#"
count: 1
path: src/Bundle/ChillTaskBundle/Controller/TaskController.php
-
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\AbstractCRUDController\\:\\:getRoleFor\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php
-
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Controller\\\\UserController\\:\\:createEditForm\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/UserController.php
-
message: "#^Undefined variable\\: \\$current$#"
count: 1
path: src/Bundle/ChillMainBundle/Pagination/PageGenerator.php
-
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AbstractChillVoter\\:\\:getSupportedAttributes\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php
-
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AbstractChillVoter\\:\\:getSupportedClasses\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php
-
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AbstractChillVoter\\:\\:isGranted\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php
-
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Controller\\\\PersonController\\:\\:\\$security\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Controller/PersonController.php
-
message: "#^Call to an undefined method Chill\\\\ThirdPartyBundle\\\\Form\\\\Type\\\\PickThirdPartyTypeCategoryType\\:\\:transform\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Form/Type/PickThirdPartyTypeCategoryType.php

852
phpstan-types.neon Normal file
View File

@@ -0,0 +1,852 @@
parameters:
ignoreErrors:
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivitytACL.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Entity/Activity.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Entity/ActivityReason.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Entity/ActivityReasonCategory.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Export/Export/ListActivity.php
-
message: "#^Method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\StatActivityDuration\\:\\:getDescription\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php
-
message: "#^Method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\StatActivityDuration\\:\\:getTitle\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Only booleans are allowed in &&, mixed given on the right side\\.$#"
count: 3
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Menu/AdminMenuBuilder.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Security/Authorization/ActivityStatsVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillActivityBundle/Timeline/TimelineActivityProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillAsideActivityBundle/src/Menu/AdminMenuBuilder.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 3
path: src/Bundle/ChillBudgetBundle/Form/ChargeType.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillBudgetBundle/Form/ResourceType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillBudgetBundle/Security/Authorization/BudgetElementVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/Entity/Calendar.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Command/CreateFieldsOnGroupCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/AbstractCustomField.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 4
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldChoice\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldChoice.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldDate.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldDate\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldDate.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldLongChoice\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldLongChoice.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldNumber.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldNumber\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldNumber.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldText\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldText.php
-
message: "#^Method Chill\\\\CustomFieldsBundle\\\\CustomFields\\\\CustomFieldTitle\\:\\:buildForm\\(\\) should return Symfony\\\\Component\\\\Form\\\\FormTypeInterface but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldTitle.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldsGroup.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Form/CustomFieldType.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillDocStoreBundle/Entity/DocumentCategory.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Entity/Participation.php
-
message: "#^Method Chill\\\\EventBundle\\\\Entity\\\\Participation\\:\\:offsetGet\\(\\) should return mixed but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Entity/Participation.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
-
message: "#^Parameter \\$resolver of method Chill\\\\EventBundle\\\\Form\\\\EventTypeType\\:\\:setDefaultOptions\\(\\) has invalid type Symfony\\\\Component\\\\OptionsResolver\\\\OptionsResolverInterface\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/EventTypeType.php
-
message: "#^Parameter \\$resolver of method Chill\\\\EventBundle\\\\Form\\\\RoleType\\:\\:setDefaultOptions\\(\\) has invalid type Symfony\\\\Component\\\\OptionsResolver\\\\OptionsResolverInterface\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/RoleType.php
-
message: "#^Parameter \\$resolver of method Chill\\\\EventBundle\\\\Form\\\\StatusType\\:\\:setDefaultOptions\\(\\) has invalid type Symfony\\\\Component\\\\OptionsResolver\\\\OptionsResolverInterface\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/StatusType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Form/Type/PickEventType.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillEventBundle/Search/EventSearch.php
-
message: "#^Method Chill\\\\EventBundle\\\\Search\\\\EventSearch\\:\\:renderResult\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Search/EventSearch.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillEventBundle/Security/Authorization/EventVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillEventBundle/Security/Authorization/ParticipationVoter.php
-
message: "#^Casting to string something that's already string\\.$#"
count: 5
path: src/Bundle/ChillFamilyMembersBundle/Entity/AbstractFamilyMember.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillFamilyMembersBundle/Form/FamilyMemberType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillFamilyMembersBundle/Security/Voter/FamilyMemberVoter.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php
-
message: "#^Parameter \\$scope of method Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\CRUDController\\:\\:getReachableCenters\\(\\) has invalid type Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\Scope\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Resolver/Resolver.php
-
message: "#^Method Chill\\\\MainBundle\\\\CRUD\\\\Resolver\\\\Resolver\\:\\:getConfigValue\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Resolver/Resolver.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\ChillImportUsersCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Command/ChillUserSendRenewPasswordCodeCommand.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/ChillUserSendRenewPasswordCodeCommand.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\ChillUserSendRenewPasswordCodeCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/ChillUserSendRenewPasswordCodeCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Command/LoadAndUpdateLanguagesCommand.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\LoadAndUpdateLanguagesCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/LoadAndUpdateLanguagesCommand.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Command/LoadAndUpdateLanguagesCommand.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\LoadCountriesCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/LoadCountriesCommand.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\LoadPostalCodesCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/LoadPostalCodesCommand.php
-
message: "#^Method Chill\\\\MainBundle\\\\Command\\\\SetPasswordCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/SetPasswordCommand.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/PasswordController.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/PostalCodeController.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadLanguages.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 5
path: src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/ExportsCompilerPass.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/CompilerPass/SearchableServicesCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/AbstractWidgetsCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/AbstractWidgetsCompilerPass.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/AbstractWidgetsCompilerPass.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Entity/Address.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Entity/Embeddable/CommentEmbeddable.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Entity/User.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Entity/User.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 6
path: src/Bundle/ChillMainBundle/Export/ExportManager.php
-
message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/ChoiceLoader/PostalCodeChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/DataMapper/AddressDataMapper.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Form/Type/AddressType.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Form/Type/AddressType.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/ChillTextareaType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/ComposedRoleScopeType.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 3
path: src/Bundle/ChillMainBundle/Form/Type/DataTransformer/DateIntervalTransformer.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/DataTransformer/ObjectToIdTransformer.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/TranslatableStringFormType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Search/Entity/SearchUserApiProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Security/Authorization/DefaultVoterHelper.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Security/ParentRoleHelper.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverEvent.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverVoter.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/PasswordRecover/TokenManager.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 3
path: src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Timeline/TimelineBuilder.php
-
message: "#^Method Chill\\\\MainBundle\\\\Timeline\\\\TimelineBuilder\\:\\:getTemplateData\\(\\) should return array but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Timeline/TimelineBuilder.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Validation/Validator/RoleScopeScopePresence.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Validation/Validator/ValidPhonenumber.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/CRUD/Controller/OneToOneEntityPersonCRUDController.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php
-
message: "#^Method Chill\\\\PersonBundle\\\\Command\\\\ChillPersonMoveCommand\\:\\:execute\\(\\) should return int but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php
-
message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 6
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/PersonController.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/DependencyInjection/CompilerPass/AccompanyingPeriodTimelineCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/Person.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Entity/PersonPhone.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Export/Aggregator/CountryOfBirthAggregator.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Export/Aggregator/NationalityAggregator.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Export/Filter/GenderFilter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/DataMapper/PersonAltNameDataMapper.php
-
message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PersonAltNameType.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PickPersonType.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Household/MembersEditor.php
-
message: "#^Method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid return type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\QueryBuilder\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Repository/PersonRepository.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 3
path: src/Bundle/ChillPersonBundle/Search/PersonSearch.php
-
message: "#^Method Chill\\\\PersonBundle\\\\Search\\\\PersonSearch\\:\\:renderResult\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Search/PersonSearch.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Search/SearchPersonApiProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/AccompanyingPeriodWorkDenormalizer.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Templating/Entity/ClosingMotiveRender.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Timeline/AbstractTimelineAccompanyingPeriod.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReportACL.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php
-
message: "#^Method Chill\\\\ReportBundle\\\\DataFixtures\\\\ORM\\\\LoadReports\\:\\:getRandomChoice\\(\\) should return array\\<string\\>\\|string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 4
path: src/Bundle/ChillReportBundle/Export/Export/ReportList.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: src/Bundle/ChillReportBundle/Export/Export/ReportList.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Security/Authorization/ReportVoter.php
-
message: "#^Method Chill\\\\ReportBundle\\\\Security\\\\Authorization\\\\ReportVoter\\:\\:supports\\(\\) should return bool but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Security/Authorization/ReportVoter.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 4
path: src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/DataFixtures/ORM/LoadTaskACL.php
-
message: "#^Casting to string something that's already string\\.$#"
count: 3
path: src/Bundle/ChillTaskBundle/Entity/AbstractTask.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 5
path: src/Bundle/ChillTaskBundle/Repository/SingleTaskRepository.php
-
message: "#^Method Chill\\\\TaskBundle\\\\Timeline\\\\SingleTaskTaskLifeCycleEventTimelineProvider\\:\\:getTransitionByName\\(\\) should return Symfony\\\\Component\\\\Workflow\\\\Transition but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Timeline/SingleTaskTaskLifeCycleEventTimelineProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Timeline/TaskLifeCycleEventTimelineProvider.php
-
message: "#^Method Chill\\\\TaskBundle\\\\Timeline\\\\TaskLifeCycleEventTimelineProvider\\:\\:getTransitionByName\\(\\) should return Symfony\\\\Component\\\\Workflow\\\\Transition but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Timeline/TaskLifeCycleEventTimelineProvider.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/DependencyInjection/CompilerPass/ThirdPartyTypeCompilerPass.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 4
path: src/Bundle/ChillThirdPartyBundle/Entity/ThirdParty.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Form/ChoiceLoader/ThirdPartyChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Form/ChoiceLoader/ThirdPartyChoiceLoader.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Repository/ThirdPartyRepository.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Search/ThirdPartyApiSearch.php
-
message: "#^Method Chill\\\\ThirdPartyBundle\\\\Search\\\\ThirdPartySearch\\:\\:renderResult\\(\\) should return string but return statement is missing\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Search/ThirdPartySearch.php
-
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Security/Voter/ThirdPartyVoter.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Templating/Entity/ThirdPartyRender.php

View File

@@ -4,6 +4,7 @@ parameters:
- src/ - src/
excludePaths: excludePaths:
- src/Bundle/*/Tests/* - src/Bundle/*/Tests/*
- src/Bundle/*/tests/*
- src/Bundle/*/Test/* - src/Bundle/*/Test/*
- src/Bundle/*/config/* - src/Bundle/*/config/*
- src/Bundle/*/migrations/* - src/Bundle/*/migrations/*
@@ -17,5 +18,7 @@ parameters:
- src/Bundle/*/src/Resources/* - src/Bundle/*/src/Resources/*
includes: includes:
- phpstan-types.neon
- phpstan-critical.neon
- phpstan-baseline.neon - phpstan-baseline.neon

View File

@@ -37,6 +37,9 @@
<testsuite name="CalendarBundle"> <testsuite name="CalendarBundle">
<directory suffix="Test.php">src/Bundle/ChillCalendarBundle/Tests/</directory> <directory suffix="Test.php">src/Bundle/ChillCalendarBundle/Tests/</directory>
</testsuite> </testsuite>
<testsuite name="DocGeneratorBundle">
<directory suffix="Test.php">src/Bundle/ChillDocGeneratorBundle/tests/</directory>
</testsuite>
</testsuites> </testsuites>
<listeners> <listeners>

View File

@@ -1,37 +1,26 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Controller; namespace Chill\ActivityBundle\Controller;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepository; use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface; use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter; use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\Repository\LocationRepository;
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;
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Form; use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
@@ -42,33 +31,56 @@ use Chill\ActivityBundle\Form\ActivityType;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
/** final class ActivityController extends AbstractController
* Class ActivityController
*
* @package Chill\ActivityBundle\Controller
*/
class ActivityController extends AbstractController
{ {
protected EventDispatcherInterface $eventDispatcher; private EventDispatcherInterface $eventDispatcher;
protected AuthorizationHelper $authorizationHelper; private LoggerInterface $logger;
protected LoggerInterface $logger; private SerializerInterface $serializer;
protected SerializerInterface $serializer; private ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
protected ActivityACLAwareRepositoryInterface $activityACLAwareRepository; private ActivityTypeRepository $activityTypeRepository;
private ThirdPartyRepository $thirdPartyRepository;
private PersonRepository $personRepository;
private LocationRepository $locationRepository;
private EntityManagerInterface $entityManager;
private ActivityRepository $activityRepository;
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
private ActivityTypeCategoryRepository $activityTypeCategoryRepository;
public function __construct( public function __construct(
ActivityACLAwareRepositoryInterface $activityACLAwareRepository, ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
ActivityTypeRepository $activityTypeRepository,
ActivityTypeCategoryRepository $activityTypeCategoryRepository,
PersonRepository $personRepository,
ThirdPartyRepository $thirdPartyRepository,
LocationRepository $locationRepository,
ActivityRepository $activityRepository,
AccompanyingPeriodRepository $accompanyingPeriodRepository,
EntityManagerInterface $entityManager,
EventDispatcherInterface $eventDispatcher, EventDispatcherInterface $eventDispatcher,
AuthorizationHelper $authorizationHelper,
LoggerInterface $logger, LoggerInterface $logger,
SerializerInterface $serializer SerializerInterface $serializer
) { ) {
$this->activityACLAwareRepository = $activityACLAwareRepository; $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->eventDispatcher = $eventDispatcher;
$this->authorizationHelper = $authorizationHelper;
$this->logger = $logger; $this->logger = $logger;
$this->serializer = $serializer; $this->serializer = $serializer;
} }
@@ -78,8 +90,8 @@ class ActivityController extends AbstractController
*/ */
public function listAction(Request $request): Response public function listAction(Request $request): Response
{ {
$em = $this->getDoctrine()->getManager();
$view = null; $view = null;
$activities = [];
// TODO: add pagination // TODO: add pagination
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@@ -89,10 +101,10 @@ class ActivityController extends AbstractController
$activities = $this->activityACLAwareRepository $activities = $this->activityACLAwareRepository
->findByPerson($person, ActivityVoter::SEE, 0, null); ->findByPerson($person, ActivityVoter::SEE, 0, null);
$event = new PrivacyEvent($person, array( $event = new PrivacyEvent($person, [
'element_class' => Activity::class, 'element_class' => Activity::class,
'action' => 'list' 'action' => 'list'
)); ]);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
$view = 'ChillActivityBundle:Activity:listPerson.html.twig'; $view = 'ChillActivityBundle:Activity:listPerson.html.twig';
@@ -105,16 +117,18 @@ class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig'; $view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig';
} }
return $this->render($view, array( return $this->render(
$view,
[
'activities' => $activities, 'activities' => $activities,
'person' => $person, 'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
)); ]
);
} }
public function selectTypeAction(Request $request): Response public function selectTypeAction(Request $request): Response
{ {
$em = $this->getDoctrine()->getManager();
$view = null; $view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@@ -127,12 +141,17 @@ class ActivityController extends AbstractController
$data = []; $data = [];
$activityTypeCategories = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityTypeCategory::class) $activityTypeCategories = $this
->activityTypeCategoryRepository
->findBy(['active' => true], ['ordering' => 'ASC']); ->findBy(['active' => true], ['ordering' => 'ASC']);
foreach ($activityTypeCategories as $activityTypeCategory) { foreach ($activityTypeCategories as $activityTypeCategory) {
$activityTypes = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class) $activityTypes = $this
->findBy(['active' => true, 'category' => $activityTypeCategory], ['ordering' => 'ASC']); ->activityTypeRepository
->findBy(
['active' => true, 'category' => $activityTypeCategory],
['ordering' => 'ASC']
);
$data[] = [ $data[] = [
'activityTypeCategory' => $activityTypeCategory, 'activityTypeCategory' => $activityTypeCategory,
@@ -140,12 +159,6 @@ class ActivityController extends AbstractController
]; ];
} }
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
} else {
$activityData = [];
}
if ($view === null) { if ($view === null) {
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
@@ -154,13 +167,13 @@ class ActivityController extends AbstractController
'person' => $person, 'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'data' => $data, 'data' => $data,
'activityData' => $activityData 'activityData' => $request->query->get('activityData', []),
]); ]);
} }
public function newAction(Request $request): Response public function newAction(Request $request): Response
{ {
$em = $this->getDoctrine()->getManager(); $view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@@ -171,8 +184,7 @@ class ActivityController extends AbstractController
} }
$activityType_id = $request->get('activityType_id', 0); $activityType_id = $request->get('activityType_id', 0);
$activityType = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class) $activityType = $this->activityTypeRepository->find($activityType_id);
->find($activityType_id);
if (isset($activityType) && !$activityType->isActive()) { if (isset($activityType) && !$activityType->isActive()) {
throw new \InvalidArgumentException('Activity type must be active'); throw new \InvalidArgumentException('Activity type must be active');
@@ -230,20 +242,20 @@ class ActivityController extends AbstractController
if (array_key_exists('personsId', $activityData)) { if (array_key_exists('personsId', $activityData)) {
foreach($activityData['personsId'] as $personId){ foreach($activityData['personsId'] as $personId){
$concernedPerson = $em->getRepository(\Chill\PersonBundle\Entity\Person::class)->find($personId); $concernedPerson = $this->personRepository->find($personId);
$entity->addPerson($concernedPerson); $entity->addPerson($concernedPerson);
} }
} }
if (array_key_exists('professionalsId', $activityData)) { if (array_key_exists('professionalsId', $activityData)) {
foreach($activityData['professionalsId'] as $professionalsId){ foreach($activityData['professionalsId'] as $professionalsId){
$professional = $em->getRepository(\Chill\ThirdPartyBundle\Entity\ThirdParty::class)->find($professionalsId); $professional = $this->thirdPartyRepository->find($professionalsId);
$entity->addThirdParty($professional); $entity->addThirdParty($professional);
} }
} }
if (array_key_exists('location', $activityData)) { if (array_key_exists('location', $activityData)) {
$location = $em->getRepository(\Chill\MainBundle\Entity\Location::class)->find($activityData['location']); $location = $this->locationRepository->find($activityData['location']);
$entity->setLocation($location); $entity->setLocation($location);
} }
@@ -268,8 +280,8 @@ class ActivityController extends AbstractController
])->handleRequest($request); ])->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$em->persist($entity); $this->entityManager->persist($entity);
$em->flush(); $this->entityManager->flush();
$this->addFlash('success', $this->get('translator')->trans('Success : activity created!')); $this->addFlash('success', $this->get('translator')->trans('Success : activity created!'));
@@ -297,7 +309,7 @@ class ActivityController extends AbstractController
public function showAction(Request $request, $id): Response public function showAction(Request $request, $id): Response
{ {
$em = $this->getDoctrine()->getManager(); $view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@@ -307,13 +319,14 @@ class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:showPerson.html.twig'; $view = 'ChillActivityBundle:Activity:showPerson.html.twig';
} }
$entity = $em->getRepository('ChillActivityBundle:Activity')->find($id); $entity = $this->activityRepository->find($id);
if (!$entity) { if (null === $entity) {
throw $this->createNotFoundException('Unable to find Activity entity.'); throw $this->createNotFoundException('Unable to find Activity entity.');
} }
if (null !== $accompanyingPeriod) { if (null !== $accompanyingPeriod) {
// @TODO: Properties created dynamically.
$entity->personsAssociated = $entity->getPersonsAssociated(); $entity->personsAssociated = $entity->getPersonsAssociated();
$entity->personsNotAssociated = $entity->getPersonsNotAssociated(); $entity->personsNotAssociated = $entity->getPersonsNotAssociated();
} }
@@ -321,7 +334,7 @@ class ActivityController extends AbstractController
// TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période // TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_SEE', $entity); // $this->denyAccessUnlessGranted('CHILL_ACTIVITY_SEE', $entity);
$deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod); $deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
// TODO // TODO
/* /*
@@ -337,21 +350,20 @@ class ActivityController extends AbstractController
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
return $this->render($view, array( return $this->render($view, [
'person' => $person, 'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'entity' => $entity, 'entity' => $entity,
'delete_form' => $deleteForm->createView(), 'delete_form' => $deleteForm->createView(),
)); ]);
} }
/** /**
* Displays a form to edit an existing Activity entity. * Displays a form to edit an existing Activity entity.
*
*/ */
public function editAction($id, Request $request): Response public function editAction($id, Request $request): Response
{ {
$em = $this->getDoctrine()->getManager(); $view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@@ -361,9 +373,9 @@ class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:editPerson.html.twig'; $view = 'ChillActivityBundle:Activity:editPerson.html.twig';
} }
$entity = $em->getRepository('ChillActivityBundle:Activity')->find($id); $entity = $this->activityRepository->find($id);
if (!$entity) { if (null === $entity) {
throw $this->createNotFoundException('Unable to find Activity entity.'); throw $this->createNotFoundException('Unable to find Activity entity.');
} }
@@ -378,17 +390,18 @@ class ActivityController extends AbstractController
])->handleRequest($request); ])->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$em->persist($entity); $this->entityManager->persist($entity);
$em->flush(); $this->entityManager->flush();
$this->addFlash('success', $this->get('translator')->trans('Success : activity updated!')); $this->addFlash('success', $this->get('translator')->trans('Success : activity updated!'));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod); $params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $id; $params['id'] = $entity->getId();
return $this->redirectToRoute('chill_activity_activity_show', $params); return $this->redirectToRoute('chill_activity_activity_show', $params);
} }
$deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod); $deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
/* /*
* TODO * TODO
@@ -406,23 +419,22 @@ class ActivityController extends AbstractController
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']); $activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, array( return $this->render($view, [
'entity' => $entity, 'entity' => $entity,
'edit_form' => $form->createView(), 'edit_form' => $form->createView(),
'delete_form' => $deleteForm->createView(), 'delete_form' => $deleteForm->createView(),
'person' => $person, 'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
'activity_json' => $activity_array 'activity_json' => $activity_array
)); ]);
} }
/** /**
* Deletes a Activity entity. * Deletes a Activity entity.
*
*/ */
public function deleteAction(Request $request, $id) public function deleteAction(Request $request, $id)
{ {
$em = $this->getDoctrine()->getManager(); $view = null;
[$person, $accompanyingPeriod] = $this->getEntity($request); [$person, $accompanyingPeriod] = $this->getEntity($request);
@@ -432,8 +444,7 @@ class ActivityController extends AbstractController
$view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig'; $view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig';
} }
/* @var $activity Activity */ $activity = $this->activityRepository->find($id);
$activity = $em->getRepository('ChillActivityBundle:Activity')->find($id);
if (!$activity) { if (!$activity) {
throw $this->createNotFoundException('Unable to find Activity entity.'); throw $this->createNotFoundException('Unable to find Activity entity.');
@@ -442,35 +453,37 @@ class ActivityController extends AbstractController
// TODO // TODO
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_DELETE', $activity); // $this->denyAccessUnlessGranted('CHILL_ACTIVITY_DELETE', $activity);
$form = $this->createDeleteForm($id, $person, $accompanyingPeriod); $form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
if ($request->getMethod() === Request::METHOD_DELETE) { if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isValid()) { if ($form->isValid()) {
$this->logger->notice("An activity has been removed", [
$this->logger->notice("An activity has been removed", array(
'by_user' => $this->getUser()->getUsername(), 'by_user' => $this->getUser()->getUsername(),
'activity_id' => $activity->getId(), 'activity_id' => $activity->getId(),
'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null, 'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null,
'comment' => $activity->getComment()->getComment(), 'comment' => $activity->getComment()->getComment(),
'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null, 'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null,
'reasons_ids' => $activity->getReasons() 'reasons_ids' => $activity->getReasons()
->map(function ($ar) { return $ar->getId(); }) ->map(
static fn (ActivityReason $ar): int => $ar->getId()
)
->toArray(), ->toArray(),
'type_id' => $activity->getType()->getId(), 'type_id' => $activity->getType()->getId(),
'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null, 'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null,
'date' => $activity->getDate()->format('Y-m-d'), 'date' => $activity->getDate()->format('Y-m-d'),
'attendee' => $activity->getAttendee() 'attendee' => $activity->getAttendee()
)); ]);
$em->remove($activity); $this->entityManager->remove($activity);
$em->flush(); $this->entityManager->flush();
$this->addFlash('success', $this->get('translator') $this->addFlash('success', $this->get('translator')
->trans("The activity has been successfully removed.")); ->trans("The activity has been successfully removed."));
$params = $this->buildParamsToUrl($person, $accompanyingPeriod); $params = $this->buildParamsToUrl($person, $accompanyingPeriod);
return $this->redirectToRoute('chill_activity_activity_list', $params); return $this->redirectToRoute('chill_activity_activity_list', $params);
} }
} }
@@ -479,18 +492,18 @@ class ActivityController extends AbstractController
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
} }
return $this->render($view, array( return $this->render($view, [
'activity' => $activity, 'activity' => $activity,
'delete_form' => $form->createView(), 'delete_form' => $form->createView(),
'person' => $person, 'person' => $person,
'accompanyingCourse' => $accompanyingPeriod, 'accompanyingCourse' => $accompanyingPeriod,
)); ]);
} }
/** /**
* Creates a form to delete a Activity entity by id. * Creates a form to delete a Activity entity by id.
*/ */
private function createDeleteForm(int $id, ?Person $person, ?AccompanyingPeriod $accompanyingPeriod): Form private function createDeleteForm(int $id, ?Person $person, ?AccompanyingPeriod $accompanyingPeriod): FormInterface
{ {
$params = $this->buildParamsToUrl($person, $accompanyingPeriod); $params = $this->buildParamsToUrl($person, $accompanyingPeriod);
$params['id'] = $id; $params['id'] = $id;
@@ -498,19 +511,17 @@ class ActivityController extends AbstractController
return $this->createFormBuilder() return $this->createFormBuilder()
->setAction($this->generateUrl('chill_activity_activity_delete', $params)) ->setAction($this->generateUrl('chill_activity_activity_delete', $params))
->setMethod('DELETE') ->setMethod('DELETE')
->add('submit', SubmitType::class, array('label' => 'Delete')) ->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm() ->getForm();
;
} }
private function getEntity(Request $request): array private function getEntity(Request $request): array
{ {
$em = $this->getDoctrine()->getManager();
$person = $accompanyingPeriod = null; $person = $accompanyingPeriod = null;
if ($request->query->has('person_id')) { if ($request->query->has('person_id')) {
$person_id = $request->get('person_id'); $person_id = $request->get('person_id');
$person = $em->getRepository(Person::class)->find($person_id); $person = $this->personRepository->find($person_id);
if ($person === null) { if ($person === null) {
throw $this->createNotFoundException('Person not found'); throw $this->createNotFoundException('Person not found');
@@ -519,7 +530,7 @@ class ActivityController extends AbstractController
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person); $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
} elseif ($request->query->has('accompanying_period_id')) { } elseif ($request->query->has('accompanying_period_id')) {
$accompanying_period_id = $request->get('accompanying_period_id'); $accompanying_period_id = $request->get('accompanying_period_id');
$accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($accompanying_period_id); $accompanyingPeriod = $this->accompanyingPeriodRepository->find($accompanying_period_id);
if ($accompanyingPeriod === null) { if ($accompanyingPeriod === null) {
throw $this->createNotFoundException('Accompanying Period not found'); throw $this->createNotFoundException('Accompanying Period not found');
@@ -532,21 +543,20 @@ class ActivityController extends AbstractController
} }
return [ return [
$person, $accompanyingPeriod $person,
$accompanyingPeriod
]; ];
} }
private function buildParamsToUrl( private function buildParamsToUrl(?Person $person, ?AccompanyingPeriod $accompanyingPeriod): array
?Person $person, {
?AccompanyingPeriod $accompanyingPeriod
): array {
$params = []; $params = [];
if ($person) { if (null !== $person) {
$params['person_id'] = $person->getId(); $params['person_id'] = $person->getId();
} }
if ($accompanyingPeriod) { if (null !== $accompanyingPeriod) {
$params['accompanying_period_id'] = $accompanyingPeriod->getId(); $params['accompanying_period_id'] = $accompanyingPeriod->getId();
} }

View File

@@ -22,8 +22,10 @@
namespace Chill\ActivityBundle\DataFixtures\ORM; namespace Chill\ActivityBundle\DataFixtures\ORM;
use Chill\PersonBundle\Entity\Person;
use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Faker\Factory as FakerFactory; use Faker\Factory as FakerFactory;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
@@ -31,14 +33,8 @@ use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
use Chill\ActivityBundle\DataFixtures\ORM\LoadActivityReason; use Chill\ActivityBundle\DataFixtures\ORM\LoadActivityReason;
use Chill\ActivityBundle\DataFixtures\ORM\LoadActivityType; use Chill\ActivityBundle\DataFixtures\ORM\LoadActivityType;
use Chill\MainBundle\DataFixtures\ORM\LoadScopes; use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
/** class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
* Load reports into DB
*
* @author Champs-Libres Coop
*/
class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
{ {
use \Symfony\Component\DependencyInjection\ContainerAwareTrait; use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
@@ -46,10 +42,12 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, C
* @var \Faker\Generator * @var \Faker\Generator
*/ */
private $faker; private $faker;
private EntityManagerInterface $em;
public function __construct() public function __construct(EntityManagerInterface $em)
{ {
$this->faker = FakerFactory::create('fr_FR'); $this->faker = FakerFactory::create('fr_FR');
$this->em = $em;
} }
public function getOrder() public function getOrder()
@@ -84,15 +82,10 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, C
* *
* @return \Chill\ActivityBundle\Entity\ActivityReason * @return \Chill\ActivityBundle\Entity\ActivityReason
*/ */
private function getRandomActivityReason(array $excludingIds) private function getRandomActivityReason()
{ {
$reasonRef = LoadActivityReason::$references[array_rand(LoadActivityReason::$references)]; $reasonRef = LoadActivityReason::$references[array_rand(LoadActivityReason::$references)];
if (in_array($this->getReference($reasonRef)->getId(), $excludingIds)) {
// we have a reason which should be excluded. Find another...
return $this->getRandomActivityReason($excludingIds);
}
return $this->getReference($reasonRef); return $this->getReference($reasonRef);
} }
@@ -107,7 +100,7 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, C
return $this->getReference($userRef); return $this->getReference($userRef);
} }
public function newRandomActivity($person) public function newRandomActivity($person): ?Activity
{ {
$activity = (new Activity()) $activity = (new Activity())
->setUser($this->getRandomUser()) ->setUser($this->getRandomUser())
@@ -120,11 +113,13 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, C
// ->setAttendee($this->faker->boolean()) // ->setAttendee($this->faker->boolean())
$usedId = array();
for ($i = 0; $i < rand(0, 4); $i++) { for ($i = 0; $i < rand(0, 4); $i++) {
$reason = $this->getRandomActivityReason($usedId); $reason = $this->getRandomActivityReason();
$usedId[] = $reason->getId(); if (null !== $reason) {
$activity->addReason($reason); $activity->addReason($reason);
} else {
return null;
}
} }
return $activity; return $activity;
@@ -132,20 +127,19 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, C
public function load(ObjectManager $manager) public function load(ObjectManager $manager)
{ {
$persons = $this->container->get('doctrine.orm.entity_manager') $persons = $this->em
->getRepository('ChillPersonBundle:Person') ->getRepository(Person::class)
->findAll(); ->findAll();
foreach($persons as $person) { foreach ($persons as $person) {
$activityNbr = rand(0,3); $activityNbr = rand(0,3);
$ref = 'activity_'.$person->getFullnameCanonical();
for($i = 0; $i < $activityNbr; $i ++) { for ($i = 0; $i < $activityNbr; $i ++) {
$activity = $this->newRandomActivity($person); $activity = $this->newRandomActivity($person);
if (null !== $activity) {
$manager->persist($activity); $manager->persist($activity);
} }
}
$this->setReference($ref, $activity);
} }
$manager->flush(); $manager->flush();
} }

View File

@@ -1,27 +1,8 @@
<?php <?php
/*
*
* Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Entity; namespace Chill\ActivityBundle\Entity;
use Chill\DocStoreBundle\Entity\Document; use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Entity\Location;
use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface; use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface;
@@ -38,7 +19,7 @@ use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasScopeInterface; use Chill\MainBundle\Entity\HasScopeInterface;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistency; use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
@@ -202,7 +183,7 @@ class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPer
return $this->id; return $this->id;
} }
public function setUser(User $user): self public function setUser(UserInterface $user): self
{ {
$this->user = $user; $this->user = $user;

View File

@@ -1,31 +1,12 @@
<?php <?php
/* declare(strict_types=1);
*
* Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Entity; namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* Class ActivityTypeCateogry
*
* @package Chill\ActivityBundle\Entity
* @ORM\Entity() * @ORM\Entity()
* @ORM\Table(name="activitytypecategory") * @ORM\Table(name="activitytypecategory")
* @ORM\HasLifecycleCallbacks() * @ORM\HasLifecycleCallbacks()
@@ -37,7 +18,7 @@ class ActivityTypeCategory
* @ORM\Column(name="id", type="integer") * @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO") * @ORM\GeneratedValue(strategy="AUTO")
*/ */
private ?int $id; private ?int $id = null;
/** /**
* @ORM\Column(type="json") * @ORM\Column(type="json")
@@ -54,10 +35,7 @@ class ActivityTypeCategory
*/ */
private float $ordering = 0.0; private float $ordering = 0.0;
/** public function getId(): ?int
* Get id
*/
public function getId(): int
{ {
return $this->id; return $this->id;
} }

View File

@@ -1,70 +1,39 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Aggregator; namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
/** class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityReasonAggregator implements AggregatorInterface,
ExportElementValidatedInterface
{ {
/** protected ActivityReasonCategoryRepository $activityReasonCategoryRepository;
*
* @var EntityRepository
*/
protected $categoryRepository;
/** protected ActivityReasonRepository $activityReasonRepository;
*
* @var EntityRepository
*/
protected $reasonRepository;
/** protected TranslatableStringHelperInterface $translatableStringHelper;
*
* @var TranslatableStringHelper
*/
protected $stringHelper;
public function __construct( public function __construct(
EntityRepository $categoryRepository, ActivityReasonCategoryRepository $activityReasonCategoryRepository,
EntityRepository $reasonRepository, ActivityReasonRepository $activityReasonRepository,
TranslatableStringHelper $stringHelper TranslatableStringHelper $translatableStringHelper
) { ) {
$this->categoryRepository = $categoryRepository; $this->activityReasonCategoryRepository = $activityReasonCategoryRepository;
$this->reasonRepository = $reasonRepository; $this->activityReasonRepository = $activityReasonRepository;
$this->stringHelper = $stringHelper; $this->translatableStringHelper = $translatableStringHelper;
} }
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
@@ -77,7 +46,7 @@ class ActivityReasonAggregator implements AggregatorInterface,
$elem = 'category.id'; $elem = 'category.id';
$alias = 'activity_categories_id'; $alias = 'activity_categories_id';
} else { } else {
throw new \RuntimeException('the data provided are not recognized'); throw new \RuntimeException('The data provided are not recognized.');
} }
$qb->addSelect($elem.' as '.$alias); $qb->addSelect($elem.' as '.$alias);
@@ -94,10 +63,11 @@ class ActivityReasonAggregator implements AggregatorInterface,
) { ) {
$qb->add( $qb->add(
'join', 'join',
array('activity' => [
new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons') 'activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')
), ],
true); true
);
} }
// join category if necessary // join category if necessary
@@ -143,28 +113,33 @@ class ActivityReasonAggregator implements AggregatorInterface,
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('level', ChoiceType::class, array( $builder->add(
'choices' => array( 'level',
ChoiceType::class,
[
'choices' => [
'By reason' => 'reasons', 'By reason' => 'reasons',
'By category of reason' => 'categories' 'By category of reason' => 'categories'
), ],
'multiple' => false, 'multiple' => false,
'expanded' => true, 'expanded' => true,
'label' => 'Reason\'s level' 'label' => "Reason's level"
)); ]
);
} }
public function validateForm($data, ExecutionContextInterface $context) public function validateForm($data, ExecutionContextInterface $context)
{ {
if ($data['level'] === null) { if ($data['level'] === null) {
$context->buildViolation("The reasons's level should not be empty") $context
->buildViolation("The reasons's level should not be empty.")
->addViolation(); ->addViolation();
} }
} }
public function getTitle() public function getTitle()
{ {
return "Aggregate by activity reason"; return 'Aggregate by activity reason';
} }
public function addRole() public function addRole()
@@ -177,41 +152,33 @@ class ActivityReasonAggregator implements AggregatorInterface,
// for performance reason, we load data from db only once // for performance reason, we load data from db only once
switch ($data['level']) { switch ($data['level']) {
case 'reasons': case 'reasons':
$this->reasonRepository->findBy(array('id' => $values)); $this->activityReasonRepository->findBy(['id' => $values]);
break; break;
case 'categories': case 'categories':
$this->categoryRepository->findBy(array('id' => $values)); $this->activityReasonCategoryRepository->findBy(['id' => $values]);
break; break;
default: default:
throw new \RuntimeException(sprintf("the level data '%s' is invalid", throw new \RuntimeException(sprintf("The level data '%s' is invalid.", $data['level']));
$data['level']));
} }
return function($value) use ($data) { return function($value) use ($data) {
if ($value === '_header') { if ($value === '_header') {
return $data['level'] === 'reasons' ? return $data['level'] === 'reasons' ? 'Group by reasons' : 'Group by categories of reason';
'Group by reasons'
:
'Group by categories of reason'
;
} }
switch ($data['level']) { switch ($data['level']) {
case 'reasons': case 'reasons':
/* @var $r \Chill\ActivityBundle\Entity\ActivityReason */ $r = $this->activityReasonRepository->find($value);
$r = $this->reasonRepository->find($value);
return $this->stringHelper->localize($r->getCategory()->getName()) return sprintf(
." > " "%s > %s",
. $this->stringHelper->localize($r->getName()); $this->translatableStringHelper->localize($r->getCategory()->getName()),
; $this->translatableStringHelper->localize($r->getName())
break; );
case 'categories': case 'categories':
$c = $this->categoryRepository->find($value); $c = $this->activityReasonCategoryRepository->find($value);
return $this->stringHelper->localize($c->getName()); return $this->translatableStringHelper->localize($c->getName());
break;
// no need for a default : the default was already set above
} }
}; };
@@ -222,12 +189,14 @@ class ActivityReasonAggregator implements AggregatorInterface,
// add select element // add select element
if ($data['level'] === 'reasons') { if ($data['level'] === 'reasons') {
return array('activity_reasons_id'); return array('activity_reasons_id');
} elseif ($data['level'] === 'categories') {
return array ('activity_categories_id');
} else {
throw new \RuntimeException('the data provided are not recognised');
} }
if ($data['level'] === 'categories') {
return array ('activity_categories_id');
}
throw new \RuntimeException('The data provided are not recognised.');
} }
} }

View File

@@ -1,61 +1,32 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Aggregator; namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityTypeAggregator implements AggregatorInterface class ActivityTypeAggregator implements AggregatorInterface
{ {
protected ActivityTypeRepository $activityTypeRepository;
/** protected TranslatableStringHelperInterface $translatableStringHelper;
*
* @var EntityRepository
*/
protected $typeRepository;
/** public const KEY = 'activity_type_aggregator';
*
* @var TranslatableStringHelper
*/
protected $stringHelper;
const KEY = 'activity_type_aggregator';
public function __construct( public function __construct(
EntityRepository $typeRepository, ActivityTypeRepository $activityTypeRepository,
TranslatableStringHelper $stringHelper TranslatableStringHelperInterface $translatableStringHelper
) { ) {
$this->typeRepository = $typeRepository; $this->activityTypeRepository = $activityTypeRepository;
$this->stringHelper = $stringHelper; $this->translatableStringHelper = $translatableStringHelper;
} }
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
@@ -64,7 +35,7 @@ class ActivityTypeAggregator implements AggregatorInterface
$qb->addSelect(sprintf('IDENTITY(activity.type) AS %s', self::KEY)); $qb->addSelect(sprintf('IDENTITY(activity.type) AS %s', self::KEY));
// add the "group by" part // add the "group by" part
$groupBy = $qb->addGroupBy(self::KEY); $qb->addGroupBy(self::KEY);
} }
/** /**
@@ -97,7 +68,7 @@ class ActivityTypeAggregator implements AggregatorInterface
public function getTitle() public function getTitle()
{ {
return "Aggregate by activity type"; return 'Aggregate by activity type';
} }
public function addRole() public function addRole()
@@ -108,17 +79,16 @@ class ActivityTypeAggregator implements AggregatorInterface
public function getLabels($key, array $values, $data): \Closure public function getLabels($key, array $values, $data): \Closure
{ {
// for performance reason, we load data from db only once // for performance reason, we load data from db only once
$this->typeRepository->findBy(array('id' => $values)); $this->activityTypeRepository->findBy(['id' => $values]);
return function($value): string { return function($value): string {
if ($value === '_header') { if ($value === '_header') {
return 'Activity type'; return 'Activity type';
} }
/* @var $r \Chill\ActivityBundle\Entity\ActivityType */ $t = $this->activityTypeRepository->find($value);
$t = $this->typeRepository->find($value);
return $this->stringHelper->localize($t->getName()); return $this->translatableStringHelper->localize($t->getName());
}; };
} }

View File

@@ -1,49 +1,24 @@
<?php <?php
/*
* Copyright (C) 2019 Champs Libres Cooperative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Aggregator; namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\MainBundle\Repository\UserRepository;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Export\AggregatorInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query\Expr\Join;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityManagerInterface;
use Chill\MainBundle\Entity\User;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityUserAggregator implements AggregatorInterface class ActivityUserAggregator implements AggregatorInterface
{ {
/** public const KEY = 'activity_user_id';
*
* @var EntityManagerInterface
*/
protected $em;
const KEY = 'activity_user_id'; private UserRepository $userRepository;
function __construct(EntityManagerInterface $em) public function __construct(
{ UserRepository $userRepository
$this->em = $em; ) {
$this->userRepository = $userRepository;
} }
public function addRole() public function addRole()
@@ -73,17 +48,14 @@ class ActivityUserAggregator implements AggregatorInterface
public function getLabels($key, $values, $data): \Closure public function getLabels($key, $values, $data): \Closure
{ {
// preload users at once // preload users at once
$this->em->getRepository(User::class) $this->userRepository->findBy(['id' => $values]);
->findBy(['id' => $values]);
return function($value) { return function($value) {
switch ($value) { if ($value === '_header') {
case '_header':
return 'activity user'; return 'activity user';
default:
return $this->em->getRepository(User::class)->find($value)
->getUsername();
} }
return $this->userRepository->find($value)->getUsername();
}; };
} }
@@ -94,6 +66,6 @@ class ActivityUserAggregator implements AggregatorInterface
public function getTitle(): string public function getTitle(): string
{ {
return "Aggregate by activity user"; return 'Aggregate by activity user';
} }
} }

View File

@@ -1,64 +1,40 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2015 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Export; namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\ExportInterface;
use Doctrine\ORM\QueryBuilder; use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityManagerInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class CountActivity implements ExportInterface class CountActivity implements ExportInterface
{ {
/** protected ActivityRepository $activityRepository;
*
* @var EntityManagerInterface
*/
protected $entityManager;
public function __construct( public function __construct(
EntityManagerInterface $em ActivityRepository $activityRepository
) ) {
{ $this->activityRepository = $activityRepository;
$this->entityManager = $em;
} }
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
} }
public function getDescription() public function getDescription()
{ {
return "Count activities by various parameters."; return 'Count activities by various parameters.';
} }
public function getTitle() public function getTitle()
{ {
return "Count activities"; return 'Count activities';
} }
public function getType() public function getType()
@@ -68,24 +44,24 @@ class CountActivity implements ExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array()) public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
{ {
$qb = $this->entityManager->createQueryBuilder(); $centers = array_map(static fn($el) => $el['center'], $acl);
$centers = array_map(function($el) { return $el['center']; }, $acl);
$qb->select('COUNT(activity.id) as export_count_activity') $qb = $this
->from('ChillActivityBundle:Activity', 'activity') ->activityRepository
->join('activity.person', 'person') ->createQueryBuilder('activity')
; ->select('COUNT(activity.id) as export_count_activity')
->join('activity.person', 'person');
$qb->where($qb->expr()->in('person.center', ':centers')) $qb
->setParameter('centers', $centers) ->where($qb->expr()->in('person.center', ':centers'))
; ->setParameter('centers', $centers);
return $qb; return $qb;
} }
public function supportsModifiers() public function supportsModifiers()
{ {
return array('person', 'activity'); return ['person', 'activity'];
} }
public function requiredRole() public function requiredRole()
@@ -95,7 +71,7 @@ class CountActivity implements ExportInterface
public function getAllowedFormattersTypes() public function getAllowedFormattersTypes()
{ {
return array(\Chill\MainBundle\Export\FormatterInterface::TYPE_TABULAR); return [FormatterInterface::TYPE_TABULAR];
} }
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data)
@@ -104,18 +80,12 @@ class CountActivity implements ExportInterface
throw new \LogicException("the key $key is not used by this export"); throw new \LogicException("the key $key is not used by this export");
} }
return function($value) { return static fn($value) => $value === '_header' ? 'Number of activities' : $value;
return $value === '_header' ?
'Number of activities'
:
$value
;
};
} }
public function getQueryKeys($data) public function getQueryKeys($data)
{ {
return array('export_count_activity'); return ['export_count_activity'];
} }
public function getResult($qb, $data) public function getResult($qb, $data)

View File

@@ -1,31 +1,14 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Export; namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Export\ListInterface;
use Chill\ActivityBundle\Entity\ActivityReason; use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Entity\Scope; use Doctrine\DBAL\Exception\InvalidArgumentException;
use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
@@ -37,33 +20,17 @@ use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Create a list for all activities
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ListActivity implements ListInterface class ListActivity implements ListInterface
{ {
private ActivityRepository $activityRepository;
/** protected EntityManagerInterface $entityManager;
*
* @var EntityManagerInterface
*/
protected $entityManager;
/** protected TranslatorInterface $translator;
*
* @var TranslatorInterface
*/
protected $translator;
/** protected TranslatableStringHelperInterface $translatableStringHelper;
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
protected $fields = array( protected array $fields = [
'id', 'id',
'date', 'date',
'durationTime', 'durationTime',
@@ -75,32 +42,28 @@ class ListActivity implements ListInterface
'person_firstname', 'person_firstname',
'person_lastname', 'person_lastname',
'person_id' 'person_id'
); ];
public function __construct( public function __construct(
EntityManagerInterface $em, EntityManagerInterface $em,
TranslatorInterface $translator, TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper TranslatableStringHelperInterface $translatableStringHelper,
) ActivityRepository $activityRepository
{ ) {
$this->entityManager = $em; $this->entityManager = $em;
$this->translator = $translator; $this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
$this->activityRepository = $activityRepository;
} }
/**
* {@inheritDoc}
*
* @param FormBuilderInterface $builder
*/
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('fields', ChoiceType::class, array( $builder->add('fields', ChoiceType::class, [
'multiple' => true, 'multiple' => true,
'expanded' => true, 'expanded' => true,
'choices' => array_combine($this->fields, $this->fields), 'choices' => array_combine($this->fields, $this->fields),
'label' => 'Fields to include in export', 'label' => 'Fields to include in export',
'constraints' => [new Callback(array( 'constraints' => [new Callback([
'callback' => function($selected, ExecutionContextInterface $context) { 'callback' => function($selected, ExecutionContextInterface $context) {
if (count($selected) === 0) { if (count($selected) === 0) {
$context->buildViolation('You must select at least one element') $context->buildViolation('You must select at least one element')
@@ -108,19 +71,14 @@ class ListActivity implements ListInterface
->addViolation(); ->addViolation();
} }
} }
))] ])]
)); ]);
} }
/**
* {@inheritDoc}
*
* @return type
*/
public function getAllowedFormattersTypes() public function getAllowedFormattersTypes()
{ {
return array(FormatterInterface::TYPE_LIST); return [FormatterInterface::TYPE_LIST];
} }
public function getDescription() public function getDescription()
@@ -133,29 +91,32 @@ class ListActivity implements ListInterface
switch ($key) switch ($key)
{ {
case 'date' : case 'date' :
return function($value) { return static function($value) {
if ($value === '_header') return 'date'; if ($value === '_header') {
return 'date';
}
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $value); $date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
return $date->format('d-m-Y'); return $date->format('d-m-Y');
}; };
case 'attendee': case 'attendee':
return function($value) { return static function($value) {
if ($value === '_header') return 'attendee'; if ($value === '_header') {
return 'attendee';
}
return $value ? 1 : 0; return $value ? 1 : 0;
}; };
case 'list_reasons' : case 'list_reasons' :
/* @var $activityReasonsRepository EntityRepository */ $activityRepository = $this->activityRepository;
$activityRepository = $this->entityManager
->getRepository('ChillActivityBundle:Activity');
return function($value) use ($activityRepository) { return function($value) use ($activityRepository): string {
if ($value === '_header') return 'activity reasons'; if ($value === '_header') {
return 'activity reasons';
}
$activity = $activityRepository $activity = $activityRepository->find($value);
->find($value);
return implode(", ", array_map(function(ActivityReason $r) { return implode(", ", array_map(function(ActivityReason $r) {
@@ -168,21 +129,25 @@ class ListActivity implements ListInterface
}; };
case 'circle_name' : case 'circle_name' :
return function($value) { return function($value) {
if ($value === '_header') return 'circle'; if ($value === '_header') {
return 'circle';
}
return $this->translatableStringHelper return $this->translatableStringHelper->localize(json_decode($value, true));
->localize(json_decode($value, true));
}; };
case 'type_name' : case 'type_name' :
return function($value) { return function($value) {
if ($value === '_header') return 'activity type'; if ($value === '_header') {
return 'activity type';
}
return $this->translatableStringHelper return $this->translatableStringHelper->localize(json_decode($value, true));
->localize(json_decode($value, true));
}; };
default: default:
return function($value) use ($key) { return static function($value) use ($key) {
if ($value === '_header') return $key; if ($value === '_header') {
return $key;
}
return $value; return $value;
}; };
@@ -209,14 +174,13 @@ class ListActivity implements ListInterface
return 'activity'; return 'activity';
} }
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array()) public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{ {
$centers = array_map(function($el) { return $el['center']; }, $acl); $centers = array_map(function($el) { return $el['center']; }, $acl);
// throw an error if any fields are present // throw an error if any fields are present
if (!\array_key_exists('fields', $data)) { if (!\array_key_exists('fields', $data)) {
throw new \Doctrine\DBAL\Exception\InvalidArgumentException("any fields " throw new InvalidArgumentException('Any fields have been checked.');
. "have been checked");
} }
$qb = $this->entityManager->createQueryBuilder(); $qb = $this->entityManager->createQueryBuilder();
@@ -227,7 +191,6 @@ class ListActivity implements ListInterface
->join('person.center', 'center') ->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)') ->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers); ->setParameter('authorized_centers', $centers);
;
foreach ($this->fields as $f) { foreach ($this->fields as $f) {
if (in_array($f, $data['fields'])) { if (in_array($f, $data['fields'])) {
@@ -269,8 +232,6 @@ class ListActivity implements ListInterface
} }
} }
return $qb; return $qb;
} }
@@ -281,7 +242,7 @@ class ListActivity implements ListInterface
public function supportsModifiers() public function supportsModifiers()
{ {
return array('activity', 'person'); return ['activity', 'person'];
} }
} }

View File

@@ -1,72 +1,45 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2015 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Export; namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\ExportInterface;
use Doctrine\ORM\QueryBuilder; use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityManagerInterface;
/** /**
* This export allow to compute stats on activity duration. * This export allow to compute stats on activity duration.
* *
* The desired stat must be given in constructor. * The desired stat must be given in constructor.
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/ */
class StatActivityDuration implements ExportInterface class StatActivityDuration implements ExportInterface
{ {
/** private ActivityRepository $activityRepository;
*
* @var EntityManagerInterface
*/
protected $entityManager;
const SUM = 'sum'; public const SUM = 'sum';
/** /**
* The action for this report. * The action for this report.
*
* @var string
*/ */
protected $action; protected string $action;
/** /**
* constructor
*
* @param EntityManagerInterface $em
* @param string $action the stat to perform * @param string $action the stat to perform
*/ */
public function __construct( public function __construct(
EntityManagerInterface $em, ActivityRepository $activityRepository,
$action = 'sum' string $action = 'sum'
) ) {
{
$this->entityManager = $em;
$this->action = $action; $this->action = $action;
$this->activityRepository = $activityRepository;
} }
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
} }
@@ -74,14 +47,14 @@ class StatActivityDuration implements ExportInterface
public function getDescription() public function getDescription()
{ {
if ($this->action === self::SUM) { if ($this->action === self::SUM) {
return "Sum activities duration by various parameters."; return 'Sum activities duration by various parameters.';
} }
} }
public function getTitle() public function getTitle()
{ {
if ($this->action === self::SUM) { if ($this->action === self::SUM) {
return "Sum activity duration"; return 'Sum activity duration';
} }
} }
@@ -91,29 +64,31 @@ class StatActivityDuration implements ExportInterface
return 'activity'; return 'activity';
} }
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array()) public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{ {
$centers = array_map(function($el) { return $el['center']; }, $acl); $centers = array_map(
$qb = $this->entityManager->createQueryBuilder(); static fn(array $el): string => $el['center'],
$acl
);
$qb = $this->activityRepository->createQueryBuilder('activity');
$select = null;
if ($this->action === self::SUM) { if ($this->action === self::SUM) {
$select = "SUM(activity.durationTime) AS export_stat_activity"; $select = 'SUM(activity.durationTime) AS export_stat_activity';
} }
$qb->select($select) return $qb->select($select)
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.person', 'person') ->join('activity.person', 'person')
->join('person.center', 'center') ->join('person.center', 'center')
->where($qb->expr()->in('center', ':centers')) ->where($qb->expr()->in('center', ':centers'))
->setParameter(':centers', $centers) ->setParameter(':centers', $centers);
;
return $qb;
} }
public function supportsModifiers() public function supportsModifiers()
{ {
return array('person', 'activity'); return ['person', 'activity'];
} }
public function requiredRole() public function requiredRole()
@@ -123,32 +98,23 @@ class StatActivityDuration implements ExportInterface
public function getAllowedFormattersTypes() public function getAllowedFormattersTypes()
{ {
return array(\Chill\MainBundle\Export\FormatterInterface::TYPE_TABULAR); return [FormatterInterface::TYPE_TABULAR];
} }
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data)
{ {
if ($key !== 'export_stat_activity') { if ($key !== 'export_stat_activity') {
throw new \LogicException("the key $key is not used by this export"); throw new \LogicException(sprintf('The key %s is not used by this export', $key));
} }
switch ($this->action) { $header = $this->action === self::SUM ? 'Sum of activities duration' : false;
case self::SUM:
$header = "Sum of activities duration";
}
return function($value) use ($header) { return static fn(string $value) => $value === '_header' ? $header : $value;
return $value === '_header' ?
$header
:
$value
;
};
} }
public function getQueryKeys($data) public function getQueryKeys($data)
{ {
return array('export_stat_activity'); return ['export_stat_activity'];
} }
public function getResult($qb, $data) public function getResult($qb, $data)

View File

@@ -1,25 +1,12 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\DateType;
@@ -28,31 +15,21 @@ use Chill\MainBundle\Form\Type\Export\FilterType;
use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Component\Translation\TranslatorInterface;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityDateFilter implements FilterInterface class ActivityDateFilter implements FilterInterface
{ {
/** protected TranslatorInterface $translator;
*
* @var TranslatorInterface
*/
protected $translator;
function __construct(TranslatorInterface $translator) function __construct(TranslatorInterface $translator)
{ {
$this->translator = $translator; $this->translator = $translator;
} }
public function addRole() public function addRole()
{ {
return null; return null;
} }
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
$clause = $qb->expr()->between('activity.date', ':date_from', $clause = $qb->expr()->between('activity.date', ':date_from',
@@ -74,23 +51,31 @@ class ActivityDateFilter implements FilterInterface
return 'activity'; return 'activity';
} }
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('date_from', DateType::class, array( $builder->add(
'label' => "Activities after this date", 'date_from',
DateType::class,
[
'label' => 'Activities after this date',
'data' => new \DateTime(), 'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'), 'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text', 'widget'=> 'single_text',
'format' => 'dd-MM-yyyy', 'format' => 'dd-MM-yyyy',
)); ]
);
$builder->add('date_to', DateType::class, array( $builder->add(
'label' => "Activities before this date", 'date_to',
DateType::class,
[
'label' => 'Activities before this date',
'data' => new \DateTime(), 'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'), 'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text', 'widget'=> 'single_text',
'format' => 'dd-MM-yyyy', 'format' => 'dd-MM-yyyy',
)); ]
);
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) { $builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
/* @var $filterForm \Symfony\Component\Form\FormInterface */ /* @var $filterForm \Symfony\Component\Form\FormInterface */
@@ -132,17 +117,18 @@ class ActivityDateFilter implements FilterInterface
public function describeAction($data, $format = 'string') public function describeAction($data, $format = 'string')
{ {
return array( return [
"Filtered by date of activity: only between %date_from% and %date_to%", 'Filtered by date of activity: only between %date_from% and %date_to%',
array( [
"%date_from%" => $data['date_from']->format('d-m-Y'), '%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y') '%date_to%' => $data['date_to']->format('d-m-Y')
)); ]
];
} }
public function getTitle() public function getTitle()
{ {
return "Filtered by date activity"; return 'Filtered by date activity';
} }
} }

View File

@@ -1,25 +1,12 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@@ -28,41 +15,24 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface;
/** class ActivityReasonFilter implements FilterInterface, ExportElementValidatedInterface
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ActivityReasonFilter implements FilterInterface,
ExportElementValidatedInterface
{ {
/** protected TranslatableStringHelperInterface $translatableStringHelper;
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/** protected ActivityReasonRepository $activityReasonRepository;
* The repository for activity reasons
*
* @var EntityRepository
*/
protected $reasonRepository;
public function __construct( public function __construct(
TranslatableStringHelper $helper, TranslatableStringHelper $helper,
EntityRepository $reasonRepository ActivityReasonRepository $activityReasonRepository
) { ) {
$this->translatableStringHelper = $helper; $this->translatableStringHelper = $helper;
$this->reasonRepository = $reasonRepository; $this->activityReasonRepository = $activityReasonRepository;
} }
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
@@ -75,7 +45,7 @@ class ActivityReasonFilter implements FilterInterface,
&& &&
!$this->checkJoinAlreadyDefined($join['activity'], 'reasons') !$this->checkJoinAlreadyDefined($join['activity'], 'reasons')
) )
OR ||
(! array_key_exists('activity', $join)) (! array_key_exists('activity', $join))
) { ) {
$qb->add( $qb->add(
@@ -101,7 +71,7 @@ class ActivityReasonFilter implements FilterInterface,
* @param Join[] $joins * @param Join[] $joins
* @return boolean * @return boolean
*/ */
private function checkJoinAlreadyDefined(array $joins, $alias) private function checkJoinAlreadyDefined(array $joins, $alias): bool
{ {
foreach ($joins as $join) { foreach ($joins as $join) {
if ($join->getAlias() === $alias) { if ($join->getAlias() === $alias) {
@@ -119,26 +89,20 @@ class ActivityReasonFilter implements FilterInterface,
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
//create a local copy of translatableStringHelper $builder->add('reasons', EntityType::class, [
$helper = $this->translatableStringHelper; 'class' => ActivityReason::class,
'choice_label' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getName()),
$builder->add('reasons', EntityType::class, array( 'group_by' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
'class' => 'ChillActivityBundle:ActivityReason',
'choice_label' => function (ActivityReason $reason) use ($helper) {
return $helper->localize($reason->getName());
},
'group_by' => function(ActivityReason $reason) use ($helper) {
return $helper->localize($reason->getCategory()->getName());
},
'multiple' => true, 'multiple' => true,
'expanded' => false 'expanded' => false
)); ]);
} }
public function validateForm($data, ExecutionContextInterface $context) public function validateForm($data, ExecutionContextInterface $context)
{ {
if ($data['reasons'] === null || count($data['reasons']) === 0) { if ($data['reasons'] === null || count($data['reasons']) === 0) {
$context->buildViolation("At least one reason must be choosen") $context
->buildViolation('At least one reason must be chosen')
->addViolation(); ->addViolation();
} }
} }
@@ -157,13 +121,15 @@ class ActivityReasonFilter implements FilterInterface,
{ {
// collect all the reasons'name used in this filter in one array // collect all the reasons'name used in this filter in one array
$reasonsNames = array_map( $reasonsNames = array_map(
function(ActivityReason $r) { fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
return "\"".$this->translatableStringHelper->localize($r->getName())."\""; $this->activityReasonRepository->findBy(array('id' => $data['reasons']->toArray()))
},
$this->reasonRepository->findBy(array('id' => $data['reasons']->toArray()))
); );
return array("Filtered by reasons: only %list%", return [
["%list%" => implode(", ", $reasonsNames)]); 'Filtered by reasons: only %list%',
[
'%list%' => implode(", ", $reasonsNames),
]
];
} }
} }

View File

@@ -1,67 +1,37 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2018 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\Query\Expr\Join;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Entity\ActivityType;
/** class ActivityTypeFilter implements FilterInterface, ExportElementValidatedInterface
*
*
*/
class ActivityTypeFilter implements FilterInterface,
ExportElementValidatedInterface
{ {
/** protected TranslatableStringHelperInterface $translatableStringHelper;
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/** protected ActivityTypeRepository $activityTypeRepository;
* The repository for activity reasons
*
* @var EntityRepository
*/
protected $typeRepository;
public function __construct( public function __construct(
TranslatableStringHelper $helper, TranslatableStringHelperInterface $translatableStringHelper,
EntityRepository $typeRepository ActivityTypeRepository $activityTypeRepository
) { ) {
$this->translatableStringHelper = $helper; $this->translatableStringHelper = $translatableStringHelper;
$this->typeRepository = $typeRepository; $this->activityTypeRepository = $activityTypeRepository;
} }
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
@@ -101,23 +71,23 @@ class ActivityTypeFilter implements FilterInterface,
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
//create a local copy of translatableStringHelper $builder->add(
$helper = $this->translatableStringHelper; 'types',
EntityType::class,
$builder->add('types', EntityType::class, array( [
'class' => ActivityType::class, 'class' => ActivityType::class,
'choice_label' => function (ActivityType $type) use ($helper) { 'choice_label' => fn(ActivityType $type) => $this->translatableStringHelper->localize($type->getName()),
return $helper->localize($type->getName());
},
'multiple' => true, 'multiple' => true,
'expanded' => false 'expanded' => false
)); ]
);
} }
public function validateForm($data, ExecutionContextInterface $context) public function validateForm($data, ExecutionContextInterface $context)
{ {
if ($data['types'] === null || count($data['types']) === 0) { if ($data['types'] === null || count($data['types']) === 0) {
$context->buildViolation("At least one type must be choosen") $context
->buildViolation('At least one type must be chosen')
->addViolation(); ->addViolation();
} }
} }
@@ -136,13 +106,15 @@ class ActivityTypeFilter implements FilterInterface,
{ {
// collect all the reasons'name used in this filter in one array // collect all the reasons'name used in this filter in one array
$reasonsNames = array_map( $reasonsNames = array_map(
function(ActivityType $t) { fn(ActivityType $t): string => '"' . $this->translatableStringHelper->localize($t->getName()) . '"',
return "\"".$this->translatableStringHelper->localize($t->getName())."\""; $this->activityTypeRepository->findBy(['id' => $data['types']->toArray()])
},
$this->typeRepository->findBy(array('id' => $data['types']->toArray()))
); );
return array("Filtered by activity type: only %list%", return [
["%list%" => implode(", ", $reasonsNames)]); 'Filtered by activity type: only %list%',
[
'%list%' => implode(", ", $reasonsNames),
]
];
} }
} }

View File

@@ -1,25 +1,14 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Export\Filter; namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\DateType;
@@ -29,43 +18,23 @@ use Doctrine\ORM\Query\Expr;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\ActivityBundle\Entity\ActivityReason; use Chill\ActivityBundle\Entity\ActivityReason;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\EntityManager;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface;
/** class PersonHavingActivityBetweenDateFilter implements FilterInterface, ExportElementValidatedInterface
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class PersonHavingActivityBetweenDateFilter implements FilterInterface,
ExportElementValidatedInterface
{ {
protected TranslatableStringHelperInterface $translatableStringHelper;
/** protected ActivityReasonRepository $activityReasonRepository;
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/** protected TranslatorInterface $translator;
*
* @var EntityRepository
*/
protected $activityReasonRepository;
/**
*
* @var TranslatorInterface
*/
protected $translator;
public function __construct( public function __construct(
TranslatableStringHelper $translatableStringHelper, TranslatableStringHelper $translatableStringHelper,
EntityRepository $activityReasonRepository, ActivityReasonRepository $activityReasonRepository,
TranslatorInterface $translator TranslatorInterface $translator
) { ) {
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
@@ -73,32 +42,32 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
$this->translator = $translator; $this->translator = $translator;
} }
public function addRole() public function addRole()
{ {
return null; return null;
} }
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
// create a query for activity // create a query for activity
$sqb = $qb->getEntityManager()->createQueryBuilder(); $sqb = $qb->getEntityManager()->createQueryBuilder();
$sqb->select("person_person_having_activity.id") $sqb->select('person_person_having_activity.id')
->from("ChillActivityBundle:Activity", "activity_person_having_activity") ->from('ChillActivityBundle:Activity', 'activity_person_having_activity')
->join("activity_person_having_activity.person", "person_person_having_activity") ->join('activity_person_having_activity.person', 'person_person_having_activity');
;
// add clause between date // add clause between date
$sqb->where("activity_person_having_activity.date BETWEEN " $sqb->where('activity_person_having_activity.date BETWEEN '
. ":person_having_activity_between_date_from" . ':person_having_activity_between_date_from'
. " AND " . ' AND '
. ":person_having_activity_between_date_to"); . ':person_having_activity_between_date_to');
// add clause activity reason // add clause activity reason
$sqb->join('activity_person_having_activity.reasons', $sqb->join('activity_person_having_activity.reasons', 'reasons_person_having_activity');
'reasons_person_having_activity');
$sqb->andWhere( $sqb->andWhere(
$sqb->expr()->in( $sqb->expr()->in(
'reasons_person_having_activity', 'reasons_person_having_activity', ':person_having_activity_reasons'
":person_having_activity_reasons") )
); );
$where = $qb->getDQLPart('where'); $where = $qb->getDQLPart('where');
@@ -123,42 +92,36 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
return Declarations::PERSON_IMPLIED_IN; return Declarations::PERSON_IMPLIED_IN;
} }
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder->add('date_from', DateType::class, array( $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(), 'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'), 'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text', 'widget'=> 'single_text',
'format' => 'dd-MM-yyyy', 'format' => 'dd-MM-yyyy',
)); ]);
$builder->add('date_to', DateType::class, array( $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(), 'data' => new \DateTime(),
'attr' => array('class' => 'datepicker'), 'attr' => ['class' => 'datepicker'],
'widget'=> 'single_text', 'widget'=> 'single_text',
'format' => 'dd-MM-yyyy', 'format' => 'dd-MM-yyyy',
)); ]);
$builder->add('reasons', EntityType::class, array( $builder->add('reasons', EntityType::class, [
'class' => 'ChillActivityBundle:ActivityReason', 'class' => ActivityReason::class,
'choice_label' => function (ActivityReason $reason) { 'choice_label' => fn (ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getName()),
return $this->translatableStringHelper 'group_by' => fn(ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
->localize($reason->getName());
},
'group_by' => function(ActivityReason $reason) {
return $this->translatableStringHelper
->localize($reason->getCategory()->getName());
},
'data' => $this->activityReasonRepository->findAll(), 'data' => $this->activityReasonRepository->findAll(),
'multiple' => true, 'multiple' => true,
'expanded' => false, 'expanded' => false,
'label' => "Activity reasons for those activities" 'label' => 'Activity reasons for those activities'
)); ]);
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) { $builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
/* @var $filterForm \Symfony\Component\Form\FormInterface */ /* @var FormInterface $filterForm */
$filterForm = $event->getForm()->getParent(); $filterForm = $event->getForm()->getParent();
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData(); $enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
@@ -198,31 +161,33 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
public function validateForm($data, ExecutionContextInterface $context) public function validateForm($data, ExecutionContextInterface $context)
{ {
if ($data['reasons'] === null || count($data['reasons']) === 0) { if ($data['reasons'] === null || count($data['reasons']) === 0) {
$context->buildViolation("At least one reason must be choosen") $context->buildViolation('At least one reason must be chosen')
->addViolation(); ->addViolation();
} }
} }
public function describeAction($data, $format = 'string') public function describeAction($data, $format = 'string')
{ {
return array( return [
"Filtered by person having an activity between %date_from% and " 'Filtered by person having an activity between %date_from% and '
. "%date_to% with reasons %reasons_name%", . '%date_to% with reasons %reasons_name%',
array( [
"%date_from%" => $data['date_from']->format('d-m-Y'), '%date_from%' => $data['date_from']->format('d-m-Y'),
'%date_to%' => $data['date_to']->format('d-m-Y'), '%date_to%' => $data['date_to']->format('d-m-Y'),
"%reasons_name%" => implode(", ", array_map( '%reasons_name%' => implode(
function (ActivityReason $r) { ", ",
return '"'.$this->translatableStringHelper-> array_map(
localize($r->getName()).'"'; fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
}, $data['reasons']
$data['reasons'])) )
)); )
]
];
} }
public function getTitle() public function getTitle()
{ {
return "Filtered by person having an activity in a period"; return 'Filtered by person having an activity in a period';
} }
} }

View File

@@ -1,56 +1,29 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Form\Type; namespace Chill\ActivityBundle\Form\Type;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\EntityRepository;
use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Entity\ActivityType;
/**
* Description of TranslatableActivityType
*
* @author Champs-Libres Coop
*/
class TranslatableActivityType extends AbstractType class TranslatableActivityType extends AbstractType
{ {
protected TranslatableStringHelperInterface $translatableStringHelper;
/** protected ActivityTypeRepository $activityTypeRepository;
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
protected $activityTypeRepository;
public function __construct( public function __construct(
TranslatableStringHelper $helper, TranslatableStringHelperInterface $helper,
EntityRepository $activityTypeRepository ActivityTypeRepository $activityTypeRepository
) ) {
{
$this->translatableStringHelper = $helper; $this->translatableStringHelper = $helper;
$this->activityTypeRepository = $activityTypeRepository; $this->activityTypeRepository = $activityTypeRepository;
} }
@@ -65,22 +38,21 @@ class TranslatableActivityType extends AbstractType
return EntityType::class; return EntityType::class;
} }
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, array $options) { public function buildForm(FormBuilderInterface $builder, array $options) {
/* @var $qb \Doctrine\ORM\QueryBuilder */ /* @var QueryBuilder $qb */
$qb = $options['query_builder']; $qb = $options['query_builder'];
if ($options['active_only'] === true) { if ($options['active_only'] === true) {
$qb->where($qb->expr()->eq('at.active', ':active')); $qb->where($qb->expr()->eq('at.active', ':active'));
$qb->setParameter('active', true, \Doctrine\DBAL\Types\Types::BOOLEAN); $qb->setParameter('active', true, Types::BOOLEAN);
} }
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults( $resolver->setDefaults(
array( array(
'class' => 'ChillActivityBundle:ActivityType', 'class' => ActivityType::class,
'active_only' => true, 'active_only' => true,
'query_builder' => $this->activityTypeRepository 'query_builder' => $this->activityTypeRepository
->createQueryBuilder('at'), ->createQueryBuilder('at'),

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityReasonCategory;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityReasonCategory|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityReasonCategory|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityReasonCategory[] findAll()
* @method ActivityReasonCategory[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityReasonCategoryRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityReasonCategory::class);
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityReason;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityReason|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityReason|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityReason[] findAll()
* @method ActivityReason[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityReasonRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityReason::class);
}
}

View File

@@ -1,24 +1,6 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
*
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;
@@ -29,10 +11,10 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
/** /**
* @method AccompanyingPeriodParticipation|null find($id, $lockMode = null, $lockVersion = null) * @method Activity|null find($id, $lockMode = null, $lockVersion = null)
* @method AccompanyingPeriodParticipation|null findOneBy(array $criteria, array $orderBy = null) * @method Activity|null findOneBy(array $criteria, array $orderBy = null)
* @method AccompanyingPeriodParticipation[] findAll() * @method Activity[] findAll()
* @method AccompanyingPeriodParticipation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @method Activity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/ */
class ActivityRepository extends ServiceEntityRepository class ActivityRepository extends ServiceEntityRepository
{ {
@@ -42,12 +24,7 @@ class ActivityRepository extends ServiceEntityRepository
} }
/** /**
* @param $person * @return Activity[]
* @param array $scopes
* @param string[] $orderBy
* @param int $limit
* @param int $offset
* @return array|Activity[]
*/ */
public function findByPersonImplied(Person $person, array $scopes, ?array $orderBy = [ 'date' => 'DESC'], ?int $limit = 100, ?int $offset = 0): array public function findByPersonImplied(Person $person, array $scopes, ?array $orderBy = [ 'date' => 'DESC'], ?int $limit = 100, ?int $offset = 0): array
{ {
@@ -63,8 +40,7 @@ class ActivityRepository extends ServiceEntityRepository
':person MEMBER OF a.persons' ':person MEMBER OF a.persons'
) )
) )
->setParameter('person', $person) ->setParameter('person', $person);
;
foreach ($orderBy as $k => $dir) { foreach ($orderBy as $k => $dir) {
$qb->addOrderBy('a.'.$k, $dir); $qb->addOrderBy('a.'.$k, $dir);
@@ -72,17 +48,11 @@ class ActivityRepository extends ServiceEntityRepository
$qb->setMaxResults($limit)->setFirstResult($offset); $qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery() return $qb->getQuery()->getResult();
->getResult();
} }
/** /**
* @param AccompanyingPeriod $period * @return Activity[]
* @param array $scopes
* @param int|null $limit
* @param int|null $offset
* @param array|string[] $orderBy
* @return array|Activity[]
*/ */
public function findByAccompanyingPeriod(AccompanyingPeriod $period, array $scopes, ?bool $allowNullScope = false, ?int $limit = 100, ?int $offset = 0, array $orderBy = ['date' => 'desc']): array public function findByAccompanyingPeriod(AccompanyingPeriod $period, array $scopes, ?bool $allowNullScope = false, ?int $limit = 100, ?int $offset = 0, array $orderBy = ['date' => 'desc']): array
{ {
@@ -92,8 +62,7 @@ class ActivityRepository extends ServiceEntityRepository
if (!$allowNullScope) { if (!$allowNullScope) {
$qb $qb
->where($qb->expr()->in('a.scope', ':scopes')) ->where($qb->expr()->in('a.scope', ':scopes'))
->setParameter('scopes', $scopes) ->setParameter('scopes', $scopes);
;
} else { } else {
$qb $qb
->where( ->where(
@@ -102,16 +71,14 @@ class ActivityRepository extends ServiceEntityRepository
$qb->expr()->isNull('a.scope') $qb->expr()->isNull('a.scope')
) )
) )
->setParameter('scopes', $scopes) ->setParameter('scopes', $scopes);
;
} }
$qb $qb
->andWhere( ->andWhere(
$qb->expr()->eq('a.accompanyingPeriod', ':period') $qb->expr()->eq('a.accompanyingPeriod', ':period')
) )
->setParameter('period', $period) ->setParameter('period', $period);
;
foreach ($orderBy as $k => $dir) { foreach ($orderBy as $k => $dir) {
$qb->addOrderBy('a.'.$k, $dir); $qb->addOrderBy('a.'.$k, $dir);
@@ -119,7 +86,6 @@ class ActivityRepository extends ServiceEntityRepository
$qb->setMaxResults($limit)->setFirstResult($offset); $qb->setMaxResults($limit)->setFirstResult($offset);
return $qb->getQuery() return $qb->getQuery()->getResult();
->getResult();
} }
} }

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityTypeCategory;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityTypeCategory|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityTypeCategory|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityTypeCategory[] findAll()
* @method ActivityTypeCategory[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityTypeCategoryRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityTypeCategory::class);
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ActivityType|null find($id, $lockMode = null, $lockVersion = null)
* @method ActivityType|null findOneBy(array $criteria, array $orderBy = null)
* @method ActivityType[] findAll()
* @method ActivityType[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ActivityTypeRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ActivityType::class);
}
}

View File

@@ -11,9 +11,12 @@
</div> </div>
<div v-if="getContext === 'accompanyingCourse' && filterSuggestedPersons.length > 0"> <div v-if="getContext === 'accompanyingCourse' && filterSuggestedPersons.length > 0">
<ul> <ul class="list-unstyled">
<li v-for="p in filterSuggestedPersons" @click="addNewPerson(p)"> <li v-for="p in filterSuggestedPersons" @click="addNewPerson(p)">
<span class="badge bg-primary" style="cursor: pointer;">
<i class="fa fa-plus fa-fw text-success"></i>
{{ p.text }} {{ p.text }}
</span>
</li> </li>
</ul> </ul>
</div> </div>

View File

@@ -4,7 +4,7 @@
<span class="chill_denomination"> <span class="chill_denomination">
{{ textCutted }} {{ textCutted }}
</span> </span>
<a class="fa fa-fw fa-times" <a class="fa fa-fw fa-times text-danger text-decoration-none"
@click.prevent="$emit('remove', person)"> @click.prevent="$emit('remove', person)">
</a> </a>
</span> </span>

View File

@@ -27,14 +27,16 @@
{{ activity.type.name | localize_translatable_string }} {{ activity.type.name | localize_translatable_string }}
<ul class="small_in_title"> <ul class="small_in_title">
{% if activity.location and t.locationVisible %}
<li> <li>
<abbr title="{{ 'location'|trans }}">{{ 'location'|trans ~ ': ' }}</abbr> <span class="item-key">{{ 'location'|trans ~ ': ' }}</span>
{# TODO {% if activity.location %}{{ activity.location }}{% endif %} #} <span>{{ activity.location.locationType.title|localize_translatable_string }}</span>
Domicile de l'usager {{ activity.location.name }}
</li> </li>
{% endif %}
{% if activity.user and t.userVisible %} {% if activity.user and t.userVisible %}
<li> <li>
<abbr title="{{ 'Referrer'|trans }}">{{ 'Referrer'|trans ~ ': ' }}</abbr> <span class="item-key">{{ 'Referrer'|trans ~ ': ' }}</span>
{{ activity.user.usernameCanonical }} {{ activity.user.usernameCanonical }}
</li> </li>
{% endif %} {% endif %}

View File

@@ -40,7 +40,6 @@
}, },
{ 'title': 'Users concerned'|trans, { 'title': 'Users concerned'|trans,
'items': entity.users, 'items': entity.users,
'path' : 'admin_user_show',
'key' : 'id' 'key' : 'id'
}, },
] %} ] %}
@@ -58,6 +57,7 @@
<ul class="list-content"> <ul class="list-content">
{% for item in bloc.items %} {% for item in bloc.items %}
<li> <li>
{% if bloc.path is defined %}
<a href="{{ _self.href(bloc.path, bloc.key, item.id) }}"> <a href="{{ _self.href(bloc.path, bloc.key, item.id) }}">
<span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}"> <span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}">
{{ item|chill_entity_render_box({ {{ item|chill_entity_render_box({
@@ -66,6 +66,14 @@
}) }} }) }}
</span> </span>
</a> </a>
{% else %}
<span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}">
{{ item|chill_entity_render_box({
'render': 'raw',
'addAltNames': false
}) }}
</span>
{% endif %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
@@ -85,6 +93,7 @@
<ul class="list-content"> <ul class="list-content">
{% for item in bloc.items %} {% for item in bloc.items %}
<li> <li>
{% if bloc.path is defined %}
<a href="{{ _self.href(bloc.path, bloc.key, item.id) }}"> <a href="{{ _self.href(bloc.path, bloc.key, item.id) }}">
<span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}"> <span class="{% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}">
{{ item|chill_entity_render_box({ {{ item|chill_entity_render_box({
@@ -93,6 +102,12 @@
}) }} }) }}
</span> </span>
</a> </a>
{% else %}
{{ item|chill_entity_render_box({
'render': 'raw',
'addAltNames': false
}) }}
{% endif %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
@@ -114,12 +129,19 @@
{% for item in bloc.items %} {% for item in bloc.items %}
<span class="wl-item {% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}"> <span class="wl-item {% if (badge_person is defined and badge_person == true) %}badge-person{% else %}badge bg-primary{% endif %}">
{% if bloc.path is defined %}
<a href="{{ _self.href(bloc.path, bloc.key, item.id) }}"> <a href="{{ _self.href(bloc.path, bloc.key, item.id) }}">
{{ item|chill_entity_render_box({ {{ item|chill_entity_render_box({
'render': 'raw', 'render': 'raw',
'addAltNames': false 'addAltNames': false
}) }} }) }}
</a> </a>
{% else %}
{{ item|chill_entity_render_box({
'render': 'raw',
'addAltNames': false
}) }}
{% endif %}
</span> </span>
{% endfor %} {% endfor %}

View File

@@ -10,7 +10,7 @@
'title' : 'Remove activity'|trans, 'title' : 'Remove activity'|trans,
'confirm_question' : 'Are you sure you want to remove the activity about "%name%" ?'|trans({ '%name%' : accompanyingCourse.id } ), 'confirm_question' : 'Are you sure you want to remove the activity about "%name%" ?'|trans({ '%name%' : accompanyingCourse.id } ),
'cancel_route' : 'chill_activity_activity_list', 'cancel_route' : 'chill_activity_activity_list',
'cancel_parameters' : { 'accompanying_course_id' : accompanyingCourse.id, 'id' : activity.id }, 'cancel_parameters' : { 'accompanying_period_id' : accompanyingCourse.id, 'id' : activity.id },
'form' : delete_form 'form' : delete_form
} ) }} } ) }}
{% endblock %} {% endblock %}

View File

@@ -55,7 +55,7 @@
{% endif %} {% endif %}
<h2 class="chill-red">{{ 'Concerned groups'|trans }}</h2> <h2 class="chill-red">{{ 'Concerned groups'|trans }}</h2>
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': context, 'with_display': 'bloc' } %} {% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': context, 'with_display': 'bloc', 'badge_person': 'true' } %}
<h2 class="chill-red">{{ 'Activity data'|trans }}</h2> <h2 class="chill-red">{{ 'Activity data'|trans }}</h2>

View File

@@ -1,4 +1,2 @@
{{ dump(notification) }}
<a href="{{ path('chill_activity_activity_show', {'id': notification.relatedEntityId }) }}">Go to Activity</a> <a href="{{ path('chill_activity_activity_show', {'id': notification.relatedEntityId }) }}">Go to Activity</a>

View File

@@ -1,85 +1,44 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
* Copyright (C) 2015 Champs Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\ActivityBundle\Timeline; namespace Chill\ActivityBundle\Timeline;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
use Chill\MainBundle\Timeline\TimelineProviderInterface; use Chill\MainBundle\Timeline\TimelineProviderInterface;
use Chill\ActivityBundle\Repository\ActivityACLAwareRepository; use Chill\ActivityBundle\Repository\ActivityACLAwareRepository;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\Role;
use Doctrine\ORM\Mapping\ClassMetadata;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Entity\Scope;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
use Chill\MainBundle\Timeline\TimelineSingleQuery; use Chill\MainBundle\Timeline\TimelineSingleQuery;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Provide activity for inclusion in timeline
*
*/
class TimelineActivityProvider implements TimelineProviderInterface class TimelineActivityProvider implements TimelineProviderInterface
{ {
protected EntityManagerInterface $em;
/** protected AuthorizationHelperInterface $helper;
*
* @var EntityManager
*/
protected $em;
/** protected UserInterface $user;
*
* @var AuthorizationHelper
*/
protected $helper;
/**
*
* @var \Chill\MainBundle\Entity\User
*/
protected $user;
protected ActivityACLAwareRepository $aclAwareRepository; protected ActivityACLAwareRepository $aclAwareRepository;
private const SUPPORTED_CONTEXTS = [ 'center', 'person']; private const SUPPORTED_CONTEXTS = [ 'center', 'person'];
/**
* TimelineActivityProvider constructor.
*
* @param EntityManager $em
* @param AuthorizationHelper $helper
* @param TokenStorageInterface $storage
*/
public function __construct( public function __construct(
EntityManager $em, EntityManagerInterface $em,
AuthorizationHelper $helper, AuthorizationHelperInterface $helper,
TokenStorageInterface $storage, TokenStorageInterface $storage,
ActivityACLAwareRepository $aclAwareRepository ActivityACLAwareRepository $aclAwareRepository
) ) {
{
$this->em = $em; $this->em = $em;
$this->helper = $helper; $this->helper = $helper;
$this->aclAwareRepository = $aclAwareRepository; $this->aclAwareRepository = $aclAwareRepository;
if (!$storage->getToken()->getUser() instanceof \Chill\MainBundle\Entity\User) if (!$storage->getToken()->getUser() instanceof User)
{ {
throw new \RuntimeException('A user should be authenticated !'); throw new \RuntimeException('A user should be authenticated !');
} }
@@ -108,7 +67,7 @@ class TimelineActivityProvider implements TimelineProviderInterface
'type' => 'activity', 'type' => 'activity',
'date' => $metadataActivity->getTableName() 'date' => $metadataActivity->getTableName()
.'.'.$metadataActivity->getColumnName('date'), .'.'.$metadataActivity->getColumnName('date'),
'FROM' => $this->getFromClausePerson($args['person']), 'FROM' => $this->getFromClausePerson(),
'WHERE' => $where, 'WHERE' => $where,
'parameters' => $parameters 'parameters' => $parameters
]); ]);
@@ -120,8 +79,7 @@ class TimelineActivityProvider implements TimelineProviderInterface
$metadataActivity = $this->em->getClassMetadata(Activity::class); $metadataActivity = $this->em->getClassMetadata(Activity::class);
$associationMapping = $metadataActivity->getAssociationMapping('person'); $associationMapping = $metadataActivity->getAssociationMapping('person');
$role = new Role('CHILL_ACTIVITY_SEE'); $role = new Role('CHILL_ACTIVITY_SEE');
$reachableScopes = $this->helper->getReachableScopes($this->user, $reachableScopes = $this->helper->getReachableScopes($this->user, $role->getRole(), $person->getCenter());
$role, $person->getCenter());
$whereClause = sprintf(' {activity.person_id} = ? AND {activity.scope_id} IN ({scopes_ids}) '); $whereClause = sprintf(' {activity.person_id} = ? AND {activity.scope_id} IN ({scopes_ids}) ');
$scopes_ids = []; $scopes_ids = [];
@@ -152,26 +110,23 @@ class TimelineActivityProvider implements TimelineProviderInterface
]; ];
} }
private function getFromClausePerson() private function getFromClausePerson(): string
{ {
$metadataActivity = $this->em->getClassMetadata(Activity::class); $metadataActivity = $this->em->getClassMetadata(Activity::class);
$metadataPerson = $this->em->getClassMetadata(Person::class); $metadataPerson = $this->em->getClassMetadata(Person::class);
$associationMapping = $metadataActivity->getAssociationMapping('person'); $associationMapping = $metadataActivity->getAssociationMapping('person');
return $metadataActivity->getTableName().' JOIN ' return sprintf(
.$metadataPerson->getTableName().' ON ' "%s JOIN %s ON %s.%s = %s",
.$metadataPerson->getTableName().'.'. $metadataActivity->getTableName(),
$associationMapping['joinColumns'][0]['referencedColumnName'] $metadataPerson->getTableName(),
.' = ' $metadataPerson->getTableName(),
.$associationMapping['joinColumns'][0]['name'] $associationMapping['joinColumns'][0]['referencedColumnName'],
; $associationMapping['joinColumns'][0]['name']
);
} }
/** public function getEntities(array $ids): array
*
* {@inheritDoc}
*/
public function getEntities(array $ids)
{ {
$activities = $this->em->getRepository(Activity::class) $activities = $this->em->getRepository(Activity::class)
->findBy(array('id' => $ids)); ->findBy(array('id' => $ids));
@@ -184,11 +139,7 @@ class TimelineActivityProvider implements TimelineProviderInterface
return $result; return $result;
} }
/** public function getEntityTemplate($entity, $context, array $args): array
*
* {@inheritDoc}
*/
public function getEntityTemplate($entity, $context, array $args)
{ {
$this->checkContext($context); $this->checkContext($context);
@@ -201,26 +152,25 @@ class TimelineActivityProvider implements TimelineProviderInterface
]; ];
} }
/** public function supportsType($type): bool
*
* {@inheritDoc}
*/
public function supportsType($type)
{ {
return $type === 'activity'; return $type === 'activity';
} }
/** /**
* check if the context is supported * Check if the context is supported.
* *
* @param string $context
* @throws \LogicException if the context is not supported * @throws \LogicException if the context is not supported
*/ */
private function checkContext($context) private function checkContext(string $context)
{ {
if (FALSE === \in_array($context, self::SUPPORTED_CONTEXTS)) { if (FALSE === \in_array($context, self::SUPPORTED_CONTEXTS)) {
throw new \LogicException("The context '$context' is not " throw new \LogicException(
. "supported. Currently only 'person' is supported"); sprintf(
"The context '%s' is not supported. Currently only 'person' is supported",
$context
)
);
} }
} }

View File

@@ -1,4 +1,7 @@
services: services:
_defaults:
autowire: true
autoconfigure: true
chill.activity.timeline: chill.activity.timeline:
class: Chill\ActivityBundle\Timeline\TimelineActivityProvider class: Chill\ActivityBundle\Timeline\TimelineActivityProvider
@@ -13,17 +16,14 @@ services:
- { name: chill.timeline, context: 'center' } - { name: chill.timeline, context: 'center' }
Chill\ActivityBundle\Menu\: Chill\ActivityBundle\Menu\:
autowire: true
autoconfigure: true
resource: '../Menu/' resource: '../Menu/'
tags: ['chill.menu_builder'] tags: ['chill.menu_builder']
Chill\ActivityBundle\Notification\: Chill\ActivityBundle\Notification\:
autowire: true
autoconfigure: true
resource: '../Notification' resource: '../Notification'
Chill\ActivityBundle\Security\Authorization\: Chill\ActivityBundle\Security\Authorization\:
resource: '../Security/Authorization/' resource: '../Security/Authorization/'
autowire: true
autoconfigure: true Chill\ActivityBundle\Repository\:
resource: '../Repository/'

View File

@@ -1,57 +1,40 @@
services: services:
_defaults:
autowire: true
autoconfigure: true
chill.activity.export.count_activity: chill.activity.export.count_activity:
class: Chill\ActivityBundle\Export\Export\CountActivity class: Chill\ActivityBundle\Export\Export\CountActivity
arguments:
- "@doctrine.orm.entity_manager"
tags: tags:
- { name: chill.export, alias: 'count_activity' } - { name: chill.export, alias: 'count_activity' }
chill.activity.export.sum_activity_duration: chill.activity.export.sum_activity_duration:
class: Chill\ActivityBundle\Export\Export\StatActivityDuration class: Chill\ActivityBundle\Export\Export\StatActivityDuration
arguments:
- "@doctrine.orm.entity_manager"
- "sum"
tags: tags:
- { name: chill.export, alias: 'sum_activity_duration' } - { name: chill.export, alias: 'sum_activity_duration' }
chill.activity.export.list_activity: chill.activity.export.list_activity:
class: Chill\ActivityBundle\Export\Export\ListActivity class: Chill\ActivityBundle\Export\Export\ListActivity
arguments:
- "@doctrine.orm.entity_manager"
- "@translator"
- "@chill.main.helper.translatable_string"
tags: tags:
- { name: chill.export, alias: 'list_activity' } - { name: chill.export, alias: 'list_activity' }
chill.activity.export.reason_filter: chill.activity.export.reason_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityReasonFilter class: Chill\ActivityBundle\Export\Filter\ActivityReasonFilter
arguments:
- "@chill.main.helper.translatable_string"
- "@chill_activity.repository.reason"
tags: tags:
- { name: chill.export_filter, alias: 'activity_reason_filter' } - { name: chill.export_filter, alias: 'activity_reason_filter' }
chill.activity.export.type_filter: chill.activity.export.type_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityTypeFilter class: Chill\ActivityBundle\Export\Filter\ActivityTypeFilter
arguments:
- "@chill.main.helper.translatable_string"
- "@chill_activity.repository.activity_type"
tags: tags:
- { name: chill.export_filter, alias: 'activity_type_filter' } - { name: chill.export_filter, alias: 'activity_type_filter' }
chill.activity.export.date_filter: chill.activity.export.date_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityDateFilter class: Chill\ActivityBundle\Export\Filter\ActivityDateFilter
arguments:
- "@translator"
tags: tags:
- { name: chill.export_filter, alias: 'activity_date_filter' } - { name: chill.export_filter, alias: 'activity_date_filter' }
chill.activity.export.person_having_an_activity_between_date_filter: chill.activity.export.person_having_an_activity_between_date_filter:
class: Chill\ActivityBundle\Export\Filter\PersonHavingActivityBetweenDateFilter class: Chill\ActivityBundle\Export\Filter\PersonHavingActivityBetweenDateFilter
arguments:
- "@chill.main.helper.translatable_string"
- "@chill_activity.repository.reason"
- "@translator"
tags: tags:
- #0 register as a filter - #0 register as a filter
name: chill.export_filter name: chill.export_filter
@@ -59,24 +42,15 @@ services:
chill.activity.export.reason_aggregator: chill.activity.export.reason_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator class: Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator
arguments:
- "@chill_activity.repository.reason_category"
- "@chill_activity.repository.reason"
- "@chill.main.helper.translatable_string"
tags: tags:
- { name: chill.export_aggregator, alias: activity_reason_aggregator } - { name: chill.export_aggregator, alias: activity_reason_aggregator }
chill.activity.export.type_aggregator: chill.activity.export.type_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ActivityTypeAggregator class: Chill\ActivityBundle\Export\Aggregator\ActivityTypeAggregator
arguments:
- "@chill_activity.repository.activity_type"
- "@chill.main.helper.translatable_string"
tags: tags:
- { name: chill.export_aggregator, alias: activity_type_aggregator } - { name: chill.export_aggregator, alias: activity_type_aggregator }
chill.activity.export.user_aggregator: chill.activity.export.user_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator
arguments:
$em: "@doctrine.orm.entity_manager"
tags: tags:
- { name: chill.export_aggregator, alias: activity_user_aggregator } - { name: chill.export_aggregator, alias: activity_user_aggregator }

View File

@@ -1,4 +1,6 @@
services: services:
Chill\ActivityBundle\DataFixtures\ORM\: Chill\ActivityBundle\DataFixtures\ORM\:
autowire: true
autoconfigure: true
resource: ../../DataFixtures/ORM resource: ../../DataFixtures/ORM
tags: [ 'doctrine.fixture.orm' ] tags: [ 'doctrine.fixture.orm' ]

View File

@@ -1,28 +1,12 @@
--- ---
services: services:
chill_activity.repository.activity_type: chill_activity.repository.activity_type: '@Chill\ActivityBundle\Repository\ActivityTypeRepository'
class: Doctrine\ORM\EntityRepository chill_activity.repository.reason: '@Chill\ActivityBundle\Repository\ActivityReasonRepository'
factory: ['@doctrine.orm.entity_manager', getRepository] chill_activity.repository.reason_category: '@Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository'
arguments:
- 'Chill\ActivityBundle\Entity\ActivityType'
chill_activity.repository.reason:
class: Doctrine\ORM\EntityRepository
factory: ['@doctrine.orm.entity_manager', getRepository]
arguments:
- 'Chill\ActivityBundle\Entity\ActivityReason'
chill_activity.repository.reason_category:
class: Doctrine\ORM\EntityRepository
factory: ['@doctrine.orm.entity_manager', getRepository]
arguments:
- 'Chill\ActivityBundle\Entity\ActivityReasonCategory'
Chill\ActivityBundle\Repository\ActivityRepository:
tags: [doctrine.repository_service]
arguments:
- '@Doctrine\Persistence\ManagerRegistry'
# This is not a repository but merely a service. It needs to be moved out for simplicity.
# The autowire and autoconfigure should be enabled globally and removed from the definition of the service.
# Once autoloaded, there is no need to alias it to the interface here, it will be done automatically by Symfony.
Chill\ActivityBundle\Repository\ActivityACLAwareRepository: Chill\ActivityBundle\Repository\ActivityACLAwareRepository:
autowire: true autowire: true
autoconfigure: true autoconfigure: true

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Activity;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173555 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'activitytype.name',
'activitytypecategory.name'
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@@ -15,8 +15,7 @@ use Doctrine\Common\Collections\Criteria;
final class AsideActivityController extends CRUDController final class AsideActivityController extends CRUDController
{ {
private AsideActivityCategoryRepository $categoryRepository;
private $categoryRepository;
public function __construct(AsideActivityCategoryRepository $categoryRepository) public function __construct(AsideActivityCategoryRepository $categoryRepository)
{ {
@@ -25,7 +24,7 @@ final class AsideActivityController extends CRUDController
protected function buildQueryEntities(string $action, Request $request, ?FilterOrderHelper $filterOrder = null) protected function buildQueryEntities(string $action, Request $request, ?FilterOrderHelper $filterOrder = null)
{ {
$qb = parent::buildQueryEntities($action, $request, $filterOrder); $qb = parent::buildQueryEntities($action, $request);
if ('index' === $action) { if ('index' === $action) {
$qb->where($qb->expr()->eq('e.agent', ':user')); $qb->where($qb->expr()->eq('e.agent', ':user'));

View File

@@ -44,13 +44,15 @@ class AsideActivityCategory
* @ORM\ManyToOne(targetEntity=AsideActivityCategory::class, inversedBy="children") * @ORM\ManyToOne(targetEntity=AsideActivityCategory::class, inversedBy="children")
* @ORM\JoinColumn(nullable=true) * @ORM\JoinColumn(nullable=true)
*/ */
private $parent; private ?AsideActivityCategory $parent = null;
/** /**
* @ORM\OneToMany(targetEntity=AsideActivityCategory::class, mappedBy="parent") * @ORM\OneToMany(targetEntity=AsideActivityCategory::class, mappedBy="parent")
*/ */
private $children; private $children;
private AsideActivityCategory $oldParent;
public function __construct() public function __construct()
{ {
$this->children = new ArrayCollection(); $this->children = new ArrayCollection();

View File

@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\AsideActivityBundle\Form; namespace Chill\AsideActivityBundle\Form;
use Chill\AsideActivityBundle\Entity\AsideActivityCategory; use Chill\AsideActivityBundle\Entity\AsideActivityCategory;
@@ -14,12 +16,11 @@ use Symfony\Component\Form\FormBuilderInterface;
final class AsideActivityCategoryType extends AbstractType final class AsideActivityCategoryType extends AbstractType
{ {
private CategoryRender $categoryRender;
protected $translatableStringHelper; public function __construct(
CategoryRender $categoryRender
public function __construct(TranslatableStringHelper $translatableStringHelper, CategoryRender $categoryRender) ) {
{
$this->translatableStringHelper = $translatableStringHelper;
$this->categoryRender = $categoryRender; $this->categoryRender = $categoryRender;
} }

View File

@@ -25,18 +25,16 @@ use Symfony\Component\Templating\EngineInterface;
final class AsideActivityFormType extends AbstractType final class AsideActivityFormType extends AbstractType
{ {
protected array $timeChoices; private array $timeChoices;
private TokenStorageInterface $storage; private TokenStorageInterface $storage;
private CategoryRender $categoryRender; private CategoryRender $categoryRender;
public function __construct ( public function __construct (
TranslatableStringHelper $translatableStringHelper,
ParameterBagInterface $parameterBag, ParameterBagInterface $parameterBag,
TokenStorageInterface $storage, TokenStorageInterface $storage,
CategoryRender $categoryRender CategoryRender $categoryRender
){ ){
$this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration'); $this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration');
$this->translatableStringHelper = $translatableStringHelper;
$this->storage = $storage; $this->storage = $storage;
$this->categoryRender = $categoryRender; $this->categoryRender = $categoryRender;
} }

View File

@@ -1,42 +1,34 @@
<?php <?php
declare(strict_types=1);
namespace Chill\AMLI\BudgetBundle\Form; namespace Chill\AMLI\BudgetBundle\Form;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType; use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\AMLI\BudgetBundle\Config\ConfigRepository; use Chill\AMLI\BudgetBundle\Config\ConfigRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\AMLI\BudgetBundle\Entity\Charge; use Chill\AMLI\BudgetBundle\Entity\Charge;
class ChargeType extends AbstractType class ChargeType extends AbstractType
{ {
/** protected ConfigRepository $configRepository;
*
* @var ConfigRepository
*/
protected $configRepository;
/** protected TranslatableStringHelperInterface $translatableStringHelper;
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
public function __construct( public function __construct(
ConfigRepository $configRepository, ConfigRepository $configRepository,
TranslatableStringHelper $translatableStringHelper TranslatableStringHelperInterface $translatableStringHelper
) { ) {
$this->configRepository = $configRepository; $this->configRepository = $configRepository;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder $builder
@@ -45,10 +37,9 @@ class ChargeType extends AbstractType
'placeholder' => 'Choose a charge type' 'placeholder' => 'Choose a charge type'
]) ])
->add('amount', MoneyType::class) ->add('amount', MoneyType::class)
->add('comment', TextAreaType::class, [ ->add('comment', TextareaType::class, [
'required' => false 'required' => false
]) ]);
;
if ($options['show_start_date']) { if ($options['show_start_date']) {
$builder->add('startDate', ChillDateType::class, [ $builder->add('startDate', ChillDateType::class, [
@@ -93,13 +84,10 @@ class ChargeType extends AbstractType
return \array_flip($charges); return \array_flip($charges);
} }
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults(array( $resolver->setDefaults(array(
'data_class' => 'Chill\AMLI\BudgetBundle\Entity\Charge', 'data_class' => Charge::class,
'show_start_date' => true, 'show_start_date' => true,
'show_end_date' => true, 'show_end_date' => true,
'show_help' => true 'show_help' => true
@@ -108,17 +96,11 @@ class ChargeType extends AbstractType
$resolver $resolver
->setAllowedTypes('show_start_date', 'boolean') ->setAllowedTypes('show_start_date', 'boolean')
->setAllowedTypes('show_end_date', 'boolean') ->setAllowedTypes('show_end_date', 'boolean')
->setAllowedTypes('show_help', 'boolean') ->setAllowedTypes('show_help', 'boolean');
;
} }
/**
* {@inheritdoc}
*/
public function getBlockPrefix() public function getBlockPrefix()
{ {
return 'chill_amli_budgetbundle_charge'; return 'chill_amli_budgetbundle_charge';
} }
} }

View File

@@ -1,7 +1,10 @@
<?php <?php
declare(strict_types=1);
namespace Chill\AMLI\BudgetBundle\Form; namespace Chill\AMLI\BudgetBundle\Form;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -10,32 +13,22 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType; use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\AMLI\BudgetBundle\Config\ConfigRepository; use Chill\AMLI\BudgetBundle\Config\ConfigRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextareaType;
class ResourceType extends AbstractType class ResourceType extends AbstractType
{ {
/** protected ConfigRepository $configRepository;
*
* @var ConfigRepository
*/
protected $configRepository;
/** protected TranslatableStringHelperInterface $translatableStringHelper;
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
public function __construct( public function __construct(
ConfigRepository $configRepository, ConfigRepository $configRepository,
TranslatableStringHelper $translatableStringHelper TranslatableStringHelperInterface $translatableStringHelper
) { ) {
$this->configRepository = $configRepository; $this->configRepository = $configRepository;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder $builder
@@ -45,10 +38,9 @@ class ResourceType extends AbstractType
'label' => 'Resource element type' 'label' => 'Resource element type'
]) ])
->add('amount', MoneyType::class) ->add('amount', MoneyType::class)
->add('comment', TextAreaType::class, [ ->add('comment', TextareaType::class, [
'required' => false 'required' => false
]) ]);
;
if ($options['show_start_date']) { if ($options['show_start_date']) {
$builder->add('startDate', ChillDateType::class, [ $builder->add('startDate', ChillDateType::class, [
@@ -64,6 +56,24 @@ class ResourceType extends AbstractType
} }
} }
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Resource::class,
'show_start_date' => true,
'show_end_date' => true
));
$resolver
->setAllowedTypes('show_start_date', 'boolean')
->setAllowedTypes('show_end_date', 'boolean');
}
public function getBlockPrefix()
{
return 'chill_amli_budgetbundle_resource';
}
private function getTypes() private function getTypes()
{ {
$resources = $this->configRepository $resources = $this->configRepository
@@ -78,31 +88,4 @@ class ResourceType extends AbstractType
return \array_flip($resources); return \array_flip($resources);
} }
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Resource::class,
'show_start_date' => true,
'show_end_date' => true
));
$resolver
->setAllowedTypes('show_start_date', 'boolean')
->setAllowedTypes('show_end_date', 'boolean')
;
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'chill_amli_budgetbundle_resource';
}
} }

View File

@@ -1,24 +1,6 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\CalendarBundle\Controller; namespace Chill\CalendarBundle\Controller;
@@ -26,9 +8,11 @@ use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
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;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Form; use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
@@ -42,11 +26,6 @@ use Chill\MainBundle\Pagination\PaginatorFactory;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
/**
* Class CalendarController
*
* @package Chill\CalendarBundle\Controller
*/
class CalendarController extends AbstractController class CalendarController extends AbstractController
{ {
protected EventDispatcherInterface $eventDispatcher; protected EventDispatcherInterface $eventDispatcher;
@@ -127,6 +106,7 @@ class CalendarController extends AbstractController
*/ */
public function newAction(Request $request): Response public function newAction(Request $request): Response
{ {
$view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
@@ -163,10 +143,11 @@ class CalendarController extends AbstractController
$params = $this->buildParamsToUrl($user, $accompanyingPeriod); $params = $this->buildParamsToUrl($user, $accompanyingPeriod);
return $this->redirectToRoute('chill_calendar_calendar_list', $params); return $this->redirectToRoute('chill_calendar_calendar_list', $params);
} elseif ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors'));
} }
if ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors'));
}
if ($view === null) { if ($view === null) {
throw $this->createNotFoundException('Template not found'); throw $this->createNotFoundException('Template not found');
@@ -189,6 +170,7 @@ class CalendarController extends AbstractController
*/ */
public function showAction(Request $request, $id): Response public function showAction(Request $request, $id): Response
{ {
$view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
@@ -200,32 +182,36 @@ class CalendarController extends AbstractController
$view = '@ChillCalendar/Calendar/showByUser.html.twig'; $view = '@ChillCalendar/Calendar/showByUser.html.twig';
} }
if ($view === null) {
throw $this->createNotFoundException('Template not found');
}
/** @var Calendar $entity */
$entity = $em->getRepository('ChillCalendarBundle:Calendar')->find($id); $entity = $em->getRepository('ChillCalendarBundle:Calendar')->find($id);
if (!$entity) { if (null === $entity) {
throw $this->createNotFoundException('Unable to find Calendar entity.'); throw $this->createNotFoundException('Unable to find Calendar entity.');
} }
if (null !== $accompanyingPeriod) { if (null !== $accompanyingPeriod) {
// @TODO: These properties are declared dynamically.
// It must be removed.
// @See https://wiki.php.net/rfc/deprecate_dynamic_properties
$entity->personsAssociated = $entity->getPersonsAssociated(); $entity->personsAssociated = $entity->getPersonsAssociated();
$entity->personsNotAssociated = $entity->getPersonsNotAssociated(); $entity->personsNotAssociated = $entity->getPersonsNotAssociated();
} }
// $deleteForm = $this->createDeleteForm($id, $accompanyingPeriod); // $deleteForm = $this->createDeleteForm($id, $accompanyingPeriod);
if ($view === null) { $personsId = array_map(
throw $this->createNotFoundException('Template not found'); static fn (Person $p): int => $p->getId(),
} $entity->getPersons()
);
$personsId = []; $professionalsId = array_map(
foreach ($entity->getPersons() as $p) { static fn (ThirdParty $thirdParty): ?int => $thirdParty->getId(),
array_push($personsId, $p->getId()); $entity->getProfessionals()
} );
$professionalsId = [];
foreach ($entity->getProfessionals() as $p) {
array_push($professionalsId, $p->getId());
}
$durationTime = $entity->getEndDate()->diff($entity->getStartDate()); $durationTime = $entity->getEndDate()->diff($entity->getStartDate());
$durationTimeInMinutes = $durationTime->days*1440 + $durationTime->h*60 + $durationTime->i; $durationTimeInMinutes = $durationTime->days*1440 + $durationTime->h*60 + $durationTime->i;
@@ -257,6 +243,7 @@ class CalendarController extends AbstractController
*/ */
public function editAction($id, Request $request): Response public function editAction($id, Request $request): Response
{ {
$view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
@@ -285,8 +272,11 @@ class CalendarController extends AbstractController
$this->addFlash('success', $this->get('translator')->trans('Success : calendar item updated!')); $this->addFlash('success', $this->get('translator')->trans('Success : calendar item updated!'));
$params = $this->buildParamsToUrl($user, $accompanyingPeriod); $params = $this->buildParamsToUrl($user, $accompanyingPeriod);
return $this->redirectToRoute('chill_calendar_calendar_list', $params); return $this->redirectToRoute('chill_calendar_calendar_list', $params);
} elseif ($form->isSubmitted() and !$form->isValid()) { }
if ($form->isSubmitted() and !$form->isValid()) {
$this->addFlash('error', $this->get('translator')->trans('This form contains errors')); $this->addFlash('error', $this->get('translator')->trans('This form contains errors'));
} }
@@ -314,6 +304,7 @@ class CalendarController extends AbstractController
*/ */
public function deleteAction(Request $request, $id) public function deleteAction(Request $request, $id)
{ {
$view = null;
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
[$user, $accompanyingPeriod] = $this->getEntity($request); [$user, $accompanyingPeriod] = $this->getEntity($request);
@@ -369,7 +360,7 @@ class CalendarController extends AbstractController
/** /**
* Creates a form to delete a Calendar entity by id. * Creates a form to delete a Calendar entity by id.
*/ */
private function createDeleteForm(int $id, ?User $user, ?AccompanyingPeriod $accompanyingPeriod): Form private function createDeleteForm(int $id, ?User $user, ?AccompanyingPeriod $accompanyingPeriod): FormInterface
{ {
$params = $this->buildParamsToUrl($user, $accompanyingPeriod); $params = $this->buildParamsToUrl($user, $accompanyingPeriod);
$params['id'] = $id; $params['id'] = $id;
@@ -416,17 +407,14 @@ class CalendarController extends AbstractController
]; ];
} }
private function buildParamsToUrl( private function buildParamsToUrl(?User $user, ?AccompanyingPeriod $accompanyingPeriod): array {
?User $user,
?AccompanyingPeriod $accompanyingPeriod
): array {
$params = []; $params = [];
if ($user) { if (null !== $user) {
$params['user_id'] = $user->getId(); $params['user_id'] = $user->getId();
} }
if ($accompanyingPeriod) { if (null !== $accompanyingPeriod) {
$params['accompanying_period_id'] = $accompanyingPeriod->getId(); $params['accompanying_period_id'] = $accompanyingPeriod->getId();
} }

View File

@@ -1,25 +1,30 @@
<?php <?php
declare(strict_types=1);
namespace Chill\CalendarBundle\DataFixtures\ORM; namespace Chill\CalendarBundle\DataFixtures\ORM;
use Chill\CalendarBundle\Entity\CalendarRange; use Chill\CalendarBundle\Entity\CalendarRange;
use Chill\MainBundle\DataFixtures\ORM\LoadUsers; use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\UserRepository;
use DateTimeImmutable; use DateTimeImmutable;
use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface; use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
class LoadCalendarRange extends Fixture implements FixtureGroupInterface, OrderedFixtureInterface class LoadCalendarRange extends Fixture implements FixtureGroupInterface, OrderedFixtureInterface
{ {
private UserRepository $userRepository;
public static array $references = [];
public function __construct( public function __construct(
EntityManagerInterface $em UserRepository $userRepository
) { ) {
$this->userRepository = $em->getRepository(User::class); $this->userRepository = $userRepository;
} }
public function getOrder(): int public function getOrder(): int
@@ -32,8 +37,6 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
return ['calendar']; return ['calendar'];
} }
public static $references = [];
public function load(ObjectManager $manager): void public function load(ObjectManager $manager): void
{ {
$arr = range(-50, 50); $arr = range(-50, 50);

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Calendar;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173557 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'chill_calendar.cancel_reason.name',
'chill_calendar.invite.status',
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@@ -151,8 +151,7 @@ class CreateFieldsOnGroupCommand extends Command
return $customFieldsGroup; return $customFieldsGroup;
} }
} }
throw new \RunTimeException('The id does not match an existing ' throw new \RuntimeException('The id does not match an existing CustomFieldsGroup');
. 'CustomFieldsGroup');
} }
); );
$customFieldsGroup = $helper->ask($input, $output, $question); $customFieldsGroup = $helper->ask($input, $output, $question);
@@ -205,13 +204,13 @@ class CreateFieldsOnGroupCommand extends Command
$parser = new Parser(); $parser = new Parser();
if (!file_exists($path)) { if (!file_exists($path)) {
throw new \RunTimeException("file does not exist"); throw new \RuntimeException("file does not exist");
} }
try { try {
$values = $parser->parse(file_get_contents($path)); $values = $parser->parse(file_get_contents($path));
} catch (ParseException $ex) { } catch (ParseException $ex) {
throw new \RunTimeException("The yaml file is not valid", 0, $ex); throw new \RuntimeException("The yaml file is not valid", 0, $ex);
} }
return $values; return $values;
@@ -228,7 +227,7 @@ class CreateFieldsOnGroupCommand extends Command
//check the cf type exists //check the cf type exists
$cfType = $this->customFieldProvider->getCustomFieldByType($field['type']); $cfType = $this->customFieldProvider->getCustomFieldByType($field['type']);
if ($cfType === NULL) { if ($cfType === NULL) {
throw new \RunTimeException('the type '.$field['type'].' ' throw new \RuntimeException('the type '.$field['type'].' '
. 'does not exists'); . 'does not exists');
} }
@@ -255,7 +254,7 @@ class CreateFieldsOnGroupCommand extends Command
.$cf->getType()."\t with slug ".$cf->getSlug(). .$cf->getType()."\t with slug ".$cf->getSlug().
"\t and names : ".implode($names, ', ')."</info>"); "\t and names : ".implode($names, ', ')."</info>");
} else { } else {
throw new \RunTimeException("Error in field ".$slug); throw new \RuntimeException("Error in field ".$slug);
} }
} }

View File

@@ -1,21 +1,6 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\CustomFieldsBundle\CustomFields; namespace Chill\CustomFieldsBundle\CustomFields;
@@ -29,36 +14,21 @@ use Symfony\Bridge\Twig\TwigEngine;
use Chill\MainBundle\Form\Type\Select2ChoiceType; use Chill\MainBundle\Form\Type\Select2ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class CustomFieldLongChoice extends AbstractCustomField class CustomFieldLongChoice extends AbstractCustomField
{ {
/** private OptionRepository $optionRepository;
*
* @var OptionRepository
*/
private $optionRepository;
/** private TranslatableStringHelper $translatableStringHelper;
*
* @var TranslatableStringHelper
*/
private $translatableStringHelper;
/** private TwigEngine $templating;
* @var TwigEngine
*/
private $templating;
const KEY = 'key'; public const KEY = 'key';
public function __construct(OptionRepository $optionRepository, public function __construct(
OptionRepository $optionRepository,
TranslatableStringHelper $translatableStringHelper, TranslatableStringHelper $translatableStringHelper,
TwigEngine $twigEngine) TwigEngine $twigEngine
{ ) {
$this->optionRepository = $optionRepository; $this->optionRepository = $optionRepository;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
$this->templating = $twigEngine; $this->templating = $twigEngine;
@@ -76,12 +46,7 @@ class CustomFieldLongChoice extends AbstractCustomField
'choice_label' => function(Option $option) use ($translatableStringHelper) { 'choice_label' => function(Option $option) use ($translatableStringHelper) {
return $translatableStringHelper->localize($option->getText()); return $translatableStringHelper->localize($option->getText());
}, },
'choice_value' => function ($key) use ($entries) { 'choice_value' => static fn (Option $key): ?int => $key === null ? null : $key->getId(),
if ($key === NULL) {
return null;
}
return $key->getId();
},
'multiple' => false, 'multiple' => false,
'expanded' => false, 'expanded' => false,
'required' => $customField->isRequired(), 'required' => $customField->isRequired(),
@@ -89,15 +54,16 @@ class CustomFieldLongChoice extends AbstractCustomField
'group_by' => function(Option $option) use ($translatableStringHelper) { 'group_by' => function(Option $option) use ($translatableStringHelper) {
if ($option->hasParent()) { if ($option->hasParent()) {
return $translatableStringHelper->localize($option->getParent()->getText()); return $translatableStringHelper->localize($option->getParent()->getText());
} else {
return $translatableStringHelper->localize($option->getText());
} }
return $translatableStringHelper->localize($option->getText());
}, },
'label' => $translatableStringHelper->localize($customField->getName()) 'label' => $translatableStringHelper->localize($customField->getName())
)); ));
$builder->get($customField->getSlug())
->addModelTransformer(new CustomFieldDataTransformer($this, $customField));
$builder
->get($customField->getSlug())
->addModelTransformer(new CustomFieldDataTransformer($this, $customField));
} }
public function buildOptionsForm(FormBuilderInterface $builder) public function buildOptionsForm(FormBuilderInterface $builder)

View File

@@ -2,6 +2,7 @@
namespace Chill\CustomFieldsBundle\Form; namespace Chill\CustomFieldsBundle\Form;
use Chill\CustomFieldsBundle\Entity\CustomFieldsGroup;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -52,8 +53,9 @@ class CustomFieldsGroupType extends AbstractType
)) ))
; ;
$builder->addEventListener(FormEvents::POST_SET_DATA, $builder->addEventListener(
function(FormEvent $event) use ($customizableEntities, $builder){ FormEvents::POST_SET_DATA,
function(FormEvent $event) use ($customizableEntities, $builder) {
$form = $event->getForm(); $form = $event->getForm();
$group = $event->getData(); $group = $event->getData();
@@ -62,40 +64,37 @@ class CustomFieldsGroupType extends AbstractType
return; return;
} }
$optionBuilder = null;
if (count($customizableEntities[$group->getEntity()]['options']) > 0) { if (count($customizableEntities[$group->getEntity()]['options']) > 0) {
$optionBuilder = $builder $optionBuilder = $builder
->getFormFactory() ->getFormFactory()
->createBuilderForProperty( ->createBuilderForProperty(CustomFieldsGroup::class, 'options')
'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup', ->create(
'options' 'options',
) null,
->create('options', null, array( [
'compound' => true, 'compound' => true,
'auto_initialize' => false, 'auto_initialize' => false,
'required' => false) 'required' => false
]
); );
}
foreach($customizableEntities[$group->getEntity()]['options'] as $key => $option) { foreach($customizableEntities[$group->getEntity()]['options'] as $key => $option) {
$optionBuilder $optionBuilder->add($key, $option['form_type'], $option['form_options']);
->add($key, $option['form_type'], $option['form_options'])
;
} }
if (isset($optionBuilder) && $optionBuilder->count() > 0) {
$form->add($optionBuilder->getForm());
} }
if ((null !== $optionBuilder) && $optionBuilder->count() > 0) {
$form->add($optionBuilder->getForm());
}
}); });
} }
/**
* @param OptionsResolverInterface $resolver
*/
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults(array( $resolver->setDefaults([
'data_class' => 'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup' 'data_class' => CustomFieldsGroup::class,
)); ]);
} }
/** /**

View File

@@ -59,22 +59,27 @@ class CustomFieldsGroupToIdTransformer implements DataTransformerInterface
} }
if ($id instanceof CustomFieldsGroup) { if ($id instanceof CustomFieldsGroup) {
throw new TransformationFailedException(sprintf( throw new TransformationFailedException(
sprintf(
'The transformation failed: the expected argument on ' 'The transformation failed: the expected argument on '
. 'reverseTransform is an object of type int,' . 'reverseTransform is an object of type int,'
. 'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup, ' . 'Chill\CustomFieldsBundle\Entity\CustomFieldsGroup, '
. 'given', gettype($id))); . 'given'
)
);
} }
$customFieldsGroup = $this->om $customFieldsGroup = $this->om
->getRepository('ChillCustomFieldsBundle:customFieldsGroup')->find($id) ->getRepository(CustomFieldsGroup::class)->find($id)
; ;
if (null === $customFieldsGroup) { if (null === $customFieldsGroup) {
throw new TransformationFailedException(sprintf( throw new TransformationFailedException(
sprintf(
'Le group avec le numéro "%s" ne peut pas être trouvé!', 'Le group avec le numéro "%s" ne peut pas être trouvé!',
$id $id
)); )
);
} }
return $customFieldsGroup; return $customFieldsGroup;

View File

@@ -1,28 +1,27 @@
<?php <?php
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Form\DataTransformer; namespace Chill\CustomFieldsBundle\Form\DataTransformer;
use Chill\CustomFieldsBundle\Entity\CustomField;
use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\DataTransformerInterface;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Doctrine\Common\Collections\ArrayCollection;
class JsonCustomFieldToArrayTransformer implements DataTransformerInterface { class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
/** private ObjectManager $om;
* @var ObjectManager
*/ private array $customField;
private $om;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om) public function __construct(ObjectManager $om)
{ {
$this->om = $om; $this->om = $om;
$customFields = $this->om $customFields = $this->om
->getRepository('ChillCustomFieldsBundle:CustomField') ->getRepository(CustomField::class)
->findAll(); ->findAll();
// @TODO: in the array_map callback, CustomField::getLabel() does not exist. What do we do here?
$customFieldsLablels = array_map( $customFieldsLablels = array_map(
function($e) { return $e->getLabel(); }, function($e) { return $e->getLabel(); },
$customFields); $customFields);
@@ -36,20 +35,12 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
{ {
echo $customFieldsJSON; echo $customFieldsJSON;
if($customFieldsJSON === null) { // lors de la creation if($customFieldsJSON === null) {
$customFieldsArray = array(); $customFieldsArray = [];
} else { } else {
$customFieldsArray = json_decode($customFieldsJSON,true); $customFieldsArray = json_decode($customFieldsJSON, true, 512, JSON_THROW_ON_ERROR);
} }
/*
echo "<br> - 4 - <br>";
var_dump($customFieldsArray);
echo "<br> - 5 - <br>";
*/
$customFieldsArrayRet = array(); $customFieldsArrayRet = array();
foreach ($customFieldsArray as $key => $value) { foreach ($customFieldsArray as $key => $value) {

View File

@@ -0,0 +1,69 @@
<?php
namespace Chill\DocGeneratorBundle\Serializer\Encoder;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
class DocGenEncoder implements \Symfony\Component\Serializer\Encoder\EncoderInterface
{
/**
* @inheritDoc
*/
public function encode($data, string $format, array $context = [])
{
if (!$this->isAssociative($data)) {
throw new UnexpectedValueException("Only associative arrays are allowed; lists are not allowed");
}
$result = [];
$this->recusiveEncoding($data, $result, '');
return $result;
}
private function recusiveEncoding(array $data, array &$result, $path)
{
if ($this->isAssociative($data)) {
foreach ($data as $key => $value) {
if (\is_array($value)) {
$this->recusiveEncoding($value, $result, $this->canonicalizeKey($path, $key));
} else {
$result[$this->canonicalizeKey($path, $key)] = $value;
}
}
} else {
foreach ($data as $elem) {
if (!$this->isAssociative($elem)) {
throw new UnexpectedValueException(sprintf("Embedded loops are not allowed. See data under %s path", $path));
}
$sub = [];
$this->recusiveEncoding($elem, $sub, '');
$result[$path][] = $sub;
}
}
}
private function canonicalizeKey(string $path, string $key): string
{
return $path === '' ? $key : $path.'_'.$key;
}
private function isAssociative(array $data)
{
$keys = \array_keys($data);
return $keys !== \array_keys($keys);
}
/**
* @inheritDoc
*/
public function supportsEncoding(string $format)
{
return $format === 'docgen';
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Chill\DocGeneratorBundle\Serializer\Helper;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class NormalizeNullValueHelper
{
private NormalizerInterface $normalizer;
public function __construct(NormalizerInterface $normalizer)
{
$this->normalizer = $normalizer;
}
public function normalize(array $attributes, string $format = 'docgen', ?array $context = [])
{
$data = [];
foreach ($attributes as $key => $class) {
if (is_numeric($key)) {
$data[$class] = '';
} else {
switch ($class) {
case 'array':
case 'bool':
case 'double':
case 'float':
case 'int':
case 'resource':
case 'string':
case 'null':
$data[$key] = '';
break;
default:
$data[$key] = $this->normalizer->normalize(null, $format, \array_merge(
$context,
['docgen:expects' => $class]
));
break;
}
}
}
return $data;
}
}

View File

@@ -0,0 +1,177 @@
<?php
namespace Chill\DocGeneratorBundle\Serializer\Normalizer;
use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class DocGenObjectNormalizer implements NormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
private ClassMetadataFactoryInterface $classMetadataFactory;
private PropertyAccessor $propertyAccess;
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory)
{
$this->classMetadataFactory = $classMetadataFactory;
$this->propertyAccess = PropertyAccess::createPropertyAccessor();
}
/**
* @inheritDoc
*/
public function normalize($object, string $format = null, array $context = [])
{
$classMetadataKey = $object ?? $context['docgen:expects'];
if (!$this->classMetadataFactory->hasMetadataFor($classMetadataKey)) {
throw new LogicException(sprintf("This object does not have metadata: %s. Add groups on this entity to allow to serialize with the format %s and groups %s", is_object($object) ? get_class($object) : $context['docgen:expects'], $format, \implode(', ', $context['groups'])));
}
$metadata = $this->classMetadataFactory->getMetadataFor($classMetadataKey);
$expectedGroups = \array_key_exists(AbstractNormalizer::GROUPS, $context) ?
\is_array($context[AbstractNormalizer::GROUPS]) ? $context[AbstractNormalizer::GROUPS] : [$context[AbstractNormalizer::GROUPS]]
: [];
$attributes = \array_filter(
$metadata->getAttributesMetadata(),
function (AttributeMetadata $a) use ($expectedGroups) {
foreach ($a->getGroups() as $g) {
if (\in_array($g, $expectedGroups, true)) {
return true;
}
}
return false;
});
if (null === $object) {
return $this->normalizeNullData($format, $context, $metadata, $attributes);
}
return $this->normalizeObject($object, $format, $context, $expectedGroups, $metadata, $attributes);
}
/**
* @param string $format
* @param array $context
* @param array $expectedGroups
* @param ClassMetadata $metadata
* @param array|AttributeMetadata[] $attributes
*/
private function normalizeNullData(string $format, array $context, ClassMetadata $metadata, array $attributes): array
{
$keys = [];
foreach ($attributes as $attribute) {
$key = $attribute->getSerializedName() ?? $attribute->getName();
$keys[$key] = $this->getExpectedType($attribute, $metadata->getReflectionClass());
}
$normalizer = new NormalizeNullValueHelper($this->normalizer);
return $normalizer->normalize($keys, $format, $context);
}
/**
* @param $object
* @param $format
* @param array $context
* @param array $expectedGroups
* @param ClassMetadata $metadata
* @param array|AttributeMetadata[] $attributes
* @return array
* @throws ExceptionInterface
*/
private function normalizeObject($object, $format, array $context, array $expectedGroups, ClassMetadata $metadata, array $attributes)
{
$data = [];
$reflection = $metadata->getReflectionClass();
foreach ($attributes as $attribute) {
/** @var AttributeMetadata $attribute */
$value = $this->propertyAccess->getValue($object, $attribute->getName());
$key = $attribute->getSerializedName() ?? $attribute->getName();
if (is_object($value)) {
$data[$key] =
$this->normalizer->normalize($value, $format, \array_merge(
$context, $attribute->getNormalizationContextForGroups($expectedGroups)
));
} elseif (null === $value) {
$data[$key] = $this->normalizeNullOutputValue($format, $context, $attribute, $reflection);
} else {
$data[$key] = (string) $value;
}
}
return $data;
}
private function getExpectedType(AttributeMetadata $attribute, \ReflectionClass $reflection): string
{
// we have to get the expected content
if ($reflection->hasProperty($attribute->getName())) {
$type = $reflection->getProperty($attribute->getName())->getType();
} elseif ($reflection->hasMethod($attribute->getName())) {
$type = $reflection->getMethod($attribute->getName())->getReturnType();
} else {
throw new \LogicException(sprintf(
"Could not determine how the content is determined for the attribute %s. Add attribute property only on property or method", $attribute->getName()
));
}
if (null === $type) {
throw new \LogicException(sprintf(
"Could not determine the type for this attribute: %s. Add a return type to the method or property declaration", $attribute->getName()
));
}
return $type->getName();
}
/**
*/
private function normalizeNullOutputValue($format, array $context, AttributeMetadata $attribute, \ReflectionClass $reflection)
{
$type = $this->getExpectedType($attribute, $reflection);
switch ($type) {
case 'array':
case 'bool':
case 'double':
case 'float':
case 'int':
case 'resource':
case 'string':
return '';
default:
return $this->normalizer->normalize(
null,
$format,
\array_merge(
$context,
['docgen:expects' => $type]
)
);
}
}
/**
* @inheritDoc
*/
public function supportsNormalization($data, string $format = null): bool
{
return $format === 'docgen' && (is_object($data) || null === $data);
}
}

View File

@@ -8,3 +8,10 @@ services:
autowire: true autowire: true
autoconfigure: true autoconfigure: true
resource: '../Repository/' resource: '../Repository/'
Chill\DocGeneratorBundle\Serializer\Normalizer\:
autowire: true
autoconfigure: true
resource: '../Serializer/Normalizer/'
tags:
- { name: 'serializer.normalizer', priority: -152 }

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\DocGenerator;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173556 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'chill_docgen_template.name'
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace Chill\DocGeneratorBundle\Tests\Serializer\Encoder;
use Chill\DocGeneratorBundle\Serializer\Encoder\DocGenEncoder;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
class DocGenEncoderTest extends TestCase
{
private DocGenEncoder $encoder;
protected function setUp()
{
parent::setUp();
$this->encoder = new DocGenEncoder();
}
/**
* @dataProvider generateEncodeData
*/
public function testEncode($expected, $data, string $msg)
{
$generated = $this->encoder->encode($data, 'docgen');
$this->assertEquals($expected, $generated, $msg);
}
public function testEmbeddedLoopsThrowsException()
{
$this->expectException(UnexpectedValueException::class);
$data = [
'data' => [
['item' => 'one'],
[
'embedded' => [
[
['subitem' => 'two'],
['subitem' => 'three']
]
]
],
]
];
$this->encoder->encode($data, 'docgen');
}
public function generateEncodeData()
{
yield [ ['tests' => 'ok'], ['tests' => 'ok'], "A simple test with a simple array"];
yield [
// expected:
['item_subitem' => 'value'],
// data:
['item' => ['subitem' => 'value']],
"A test with multidimensional array"
];
yield [
// expected:
[ 'data' => [['item' => 'one'], ['item' => 'two']] ],
// data:
[ 'data' => [['item' => 'one'], ['item' => 'two']] ],
"a list of items"
];
yield [
// expected:
[ 'data' => [['item_subitem' => 'alpha'], ['item' => 'two']] ],
// data:
[ 'data' => [['item' => ['subitem' => 'alpha']], ['item' => 'two'] ] ],
"a list of items with multidimensional array inside item"
];
yield [
// expected:
[
'persons' => [
[
'firstname' => 'Jonathan',
'lastname' => 'Dupont',
'dateOfBirth_long' => '16 juin 1981',
'dateOfBirth_short' => '16/06/1981',
'father_firstname' => 'Marcel',
'father_lastname' => 'Dupont',
'father_dateOfBirth_long' => '10 novembre 1953',
'father_dateOfBirth_short' => '10/11/1953'
],
]
],
// data:
[
'persons' => [
[
'firstname' => 'Jonathan',
'lastname' => 'Dupont',
'dateOfBirth' => [ 'long' => '16 juin 1981', 'short' => '16/06/1981'],
'father' => [
'firstname' => 'Marcel',
'lastname' => 'Dupont',
'dateOfBirth' => ['long' => '10 novembre 1953', 'short' => '10/11/1953']
]
],
]
],
"a longer list, with near real data inside and embedded associative arrays"
];
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace Chill\DocGeneratorBundle\tests\Serializer\Normalizer;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class DocGenObjectNormalizerTest extends KernelTestCase
{
private NormalizerInterface $normalizer;
protected function setUp()
{
parent::setUp();
self::bootKernel();
$this->normalizer = self::$container->get(NormalizerInterface::class);
}
public function testNormalizationBasic()
{
$user = new User();
$user->setUsername('User Test');
$user->setMainCenter($center = new Center());
$center->setName('test');
$normalized = $this->normalizer->normalize($user, 'docgen', [ AbstractNormalizer::GROUPS => ['docgen:read']]);
$expected = [
'label' => 'User Test',
'email' => '',
'mainCenter' => [
'name' => 'test'
]
];
$this->assertEquals($expected, $normalized, "test normalization fo an user");
}
public function testNormalizeWithNullValueEmbedded()
{
$user = new User();
$user->setUsername('User Test');
$normalized = $this->normalizer->normalize($user, 'docgen', [ AbstractNormalizer::GROUPS => ['docgen:read']]);
$expected = [
'label' => 'User Test',
'email' => '',
'mainCenter' => [
'name' => ''
]
];
$this->assertEquals($expected, $normalized, "test normalization fo an user with null center");
}
public function testNormalizeNullObjectWithObjectEmbedded()
{
$normalized = $this->normalizer->normalize(null, 'docgen', [
AbstractNormalizer::GROUPS => ['docgen:read'],
'docgen:expects' => User::class,
]);
$expected = [
'label' => '',
'email' => '',
'mainCenter' => [
'name' => ''
]
];
$this->assertEquals($expected, $normalized, "test normalization for a null user");
}
}

View File

@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\DocStoreBundle\Controller; namespace Chill\DocStoreBundle\Controller;
use Chill\DocStoreBundle\Entity\DocumentCategory; use Chill\DocStoreBundle\Entity\DocumentCategory;
@@ -9,11 +11,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Chill\DocStoreBundle\ChillDocStoreBundle;
/** /**
* Class DocumentCategoryController
*
* @package Chill\DocStoreBundle\Controller
* @Route("/{_locale}/admin/document/category") * @Route("/{_locale}/admin/document/category")
*/ */
class DocumentCategoryController extends AbstractController class DocumentCategoryController extends AbstractController
@@ -24,11 +24,14 @@ class DocumentCategoryController extends AbstractController
public function index(): Response public function index(): Response
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$categories = $em->getRepository("ChillDocStoreBundle:DocumentCategory")->findAll(); $categories = $em->getRepository(DocumentCategory::class)->findAll();
return $this->render( return $this->render(
'ChillDocStoreBundle:DocumentCategory:index.html.twig', 'ChillDocStoreBundle:DocumentCategory:index.html.twig',
['document_categories' => $categories]); [
'document_categories' => $categories,
]
);
} }
/** /**
@@ -37,13 +40,10 @@ class DocumentCategoryController extends AbstractController
public function new(Request $request): Response public function new(Request $request): Response
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$documentCategory = new DocumentCategory(); $documentCategory = new DocumentCategory(
$documentCategory ChillDocStoreBundle::class,
->setBundleId('Chill\DocStoreBundle\ChillDocStoreBundle'); $em->getRepository(DocumentCategory::class)->nextIdInsideBundle()
$documentCategory );
->setIdInsideBundle(
$em->getRepository("ChillDocStoreBundle:DocumentCategory")
->nextIdInsideBundle());
$documentCategory $documentCategory
->setDocumentClass(PersonDocument::class); ->setDocumentClass(PersonDocument::class);
@@ -56,11 +56,10 @@ class DocumentCategoryController extends AbstractController
$em->flush(); $em->flush();
return $this->redirectToRoute('document_category_index'); return $this->redirectToRoute('document_category_index');
} else {
$documentCategory->setBundleId(
'Chill\DocStoreBundle\ChillDocStoreBundle');
} }
$documentCategory->setBundleId(ChillDocStoreBundle::class);
return $this->render('ChillDocStoreBundle:DocumentCategory:new.html.twig', [ return $this->render('ChillDocStoreBundle:DocumentCategory:new.html.twig', [
'document_category' => $documentCategory, 'document_category' => $documentCategory,
'form' => $form->createView(), 'form' => $form->createView(),

View File

@@ -57,7 +57,7 @@ class LoadDocumentACL extends AbstractFixture implements OrderedFixtureInterface
break; break;
case 'administrative': case 'administrative':
case 'direction': case 'direction':
if (in_array($scope->getName()['en'], array('administrative', 'social'))) { if (in_array($scope->getName()['en'], array('administrative', 'social'), true)) {
printf("denying power on %s\n", $scope->getName()['en']); printf("denying power on %s\n", $scope->getName()['en']);
break 2; // we do not want any power on social or administrative break 2; // we do not want any power on social or administrative
} }

View File

@@ -34,6 +34,6 @@ class DocumentCategoryRepository extends EntityRepository
'SELECT MAX(c.idInsideBundle) + 1 FROM ChillDocStoreBundle:DocumentCategory c') 'SELECT MAX(c.idInsideBundle) + 1 FROM ChillDocStoreBundle:DocumentCategory c')
->getSingleResult(); ->getSingleResult();
return $array_res[1] ?: 0; return reset($array_res);
} }
} }

View File

@@ -1,44 +1,19 @@
<?php <?php
/*
* Copyright (C) 2018 Champs-Libres SCRLFS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\DocStoreBundle\Security\Authorization; namespace Chill\DocStoreBundle\Security\Authorization;
use App\Security\Authorization\VoterHelperFactory;
use Chill\MainBundle\Security\Authorization\AbstractChillVoter; use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface; use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
use Chill\MainBundle\Security\Authorization\VoterHelperInterface; use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface; use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
use Chill\DocStoreBundle\Entity\PersonDocument; use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Role\Role;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
/**
*
*/
class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{ {
const CREATE = 'CHILL_PERSON_DOCUMENT_CREATE'; const CREATE = 'CHILL_PERSON_DOCUMENT_CREATE';

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\DocStore;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20211119173558 extends AbstractMigration
{
public function getDescription(): string
{
return 'remove comment on deprecated json_array type';
}
public function up(Schema $schema): void
{
$columns = [
'chill_doc.document_category.name',
'chill_doc.stored_object.key',
'chill_doc.stored_object.iv',
'chill_doc.stored_object.datas',
];
foreach ($columns as $col) {
$this->addSql("COMMENT ON COLUMN $col IS NULL");
}
}
public function down(Schema $schema): void
{
$this->throwIrreversibleMigrationException();
}
}

View File

@@ -23,6 +23,7 @@ use ArrayIterator;
use Chill\EventBundle\Entity\Event; use Chill\EventBundle\Entity\Event;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Chill\EventBundle\Entity\Participation; use Chill\EventBundle\Entity\Participation;
@@ -183,7 +184,7 @@ class ParticipationController extends AbstractController
$participations = $this->handleRequest($request, new Participation(), true); $participations = $this->handleRequest($request, new Participation(), true);
$ignoredParticipations = $newParticipations = []; $ignoredParticipations = $newParticipations = [];
foreach ($participations as $i => $participation) { foreach ($participations as $participation) {
// check for authorization // check for authorization
$this->denyAccessUnlessGranted(ParticipationVoter::CREATE, $this->denyAccessUnlessGranted(ParticipationVoter::CREATE,
$participation, 'The user is not allowed to create this participation'); $participation, 'The user is not allowed to create this participation');
@@ -218,7 +219,9 @@ class ParticipationController extends AbstractController
return $this->redirectToRoute('chill_event__event_show', array( return $this->redirectToRoute('chill_event__event_show', array(
'event_id' => $request->query->getInt('event_id', 0) 'event_id' => $request->query->getInt('event_id', 0)
)); ));
} elseif (count($newParticipations) > 1) { }
if (count($newParticipations) > 1) {
// if we have multiple participations, show a form with multiple participations // if we have multiple participations, show a form with multiple participations
$form = $this->createCreateFormMultiple($newParticipations); $form = $this->createCreateFormMultiple($newParticipations);
@@ -372,9 +375,6 @@ class ParticipationController extends AbstractController
* If the request is multiple, the $participation object is cloned. * If the request is multiple, the $participation object is cloned.
* Limitations: the $participation should not be persisted. * Limitations: the $participation should not be persisted.
* *
* @param Request $request
* @param Participation $participation
* @param boolean $multiple (default false)
* @return Participation|Participations[] return one single participation if $multiple == false * @return Participation|Participations[] return one single participation if $multiple == false
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the event/person is not found * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the event/person is not found
* @throws \Symfony\Component\Security\Core\Exception\AccessDeniedException if the user does not have access to event/person * @throws \Symfony\Component\Security\Core\Exception\AccessDeniedException if the user does not have access to event/person
@@ -382,7 +382,7 @@ class ParticipationController extends AbstractController
protected function handleRequest( protected function handleRequest(
Request $request, Request $request,
Participation $participation, Participation $participation,
$multiple = false) bool $multiple = false)
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
if ($em->contains($participation)) { if ($em->contains($participation)) {
@@ -441,11 +441,9 @@ class ParticipationController extends AbstractController
} }
/** /**
* @param Participation $participation
* @param null $return_path * @param null $return_path
* @return \Symfony\Component\Form\FormInterface
*/ */
public function createCreateForm(Participation $participation, $return_path = null) public function createCreateForm(Participation $participation, $return_path = null): FormInterface
{ {
$form = $this->createForm(ParticipationType::class, $participation, array( $form = $this->createForm(ParticipationType::class, $participation, array(
@@ -464,11 +462,7 @@ class ParticipationController extends AbstractController
return $form; return $form;
} }
/** public function createCreateFormMultiple(array $participations): FormInterface
* @param array $participations
* @return \Symfony\Component\Form\FormInterface
*/
public function createCreateFormMultiple(array $participations)
{ {
$form = $this->createForm(\Symfony\Component\Form\Extension\Core\Type\FormType::class, $form = $this->createForm(\Symfony\Component\Form\Extension\Core\Type\FormType::class,
array('participations' => $participations), array( array('participations' => $participations), array(
@@ -495,18 +489,16 @@ class ParticipationController extends AbstractController
} }
/** /**
* show an edit form for the participation with the given id. * Show an edit form for the participation with the given id.
* *
* @param int $participation_id
* @return \Symfony\Component\HttpFoundation\Response
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the participation is not found * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the participation is not found
* @throws \Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException if the user is not allowed to edit the participation * @throws \Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException if the user is not allowed to edit the participation
*/ */
public function editAction($participation_id) public function editAction(int $participation_id): Response
{ {
/* @var $participation Participation */ /* @var $participation Participation */
$participation = $this->getDoctrine()->getManager() $participation = $this->getDoctrine()->getManager()
->getRepository('ChillEventBundle:Participation') ->getRepository(Participation::class)
->find($participation_id); ->find($participation_id);
if ($participation === NULL) { if ($participation === NULL) {
@@ -518,22 +510,17 @@ class ParticipationController extends AbstractController
$form = $this->createEditForm($participation); $form = $this->createEditForm($participation);
return $this->render('ChillEventBundle:Participation:edit.html.twig', array( return $this->render('ChillEventBundle:Participation:edit.html.twig', [
'form' => $form->createView(), 'form' => $form->createView(),
'participation' => $participation 'participation' => $participation,
)); ]);
} }
/** public function updateAction(int $participation_id, Request $request): Response
* @param $participation_id
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
*/
public function updateAction($participation_id, Request $request)
{ {
/* @var $participation Participation */ /* @var $participation Participation */
$participation = $this->getDoctrine()->getManager() $participation = $this->getDoctrine()->getManager()
->getRepository('ChillEventBundle:Participation') ->getRepository(Participation::class)
->find($participation_id); ->find($participation_id);
if ($participation === NULL) { if ($participation === NULL) {
@@ -556,35 +543,40 @@ class ParticipationController extends AbstractController
'The participation was updated' 'The participation was updated'
)); ));
return $this->redirectToRoute('chill_event__event_show', array( return $this->redirectToRoute('chill_event__event_show', [
'event_id' => $participation->getEvent()->getId() 'event_id' => $participation->getEvent()->getId(),
)); ]);
} }
return $this->render('ChillEventBundle:Participation:edit.html.twig', array( return $this->render('ChillEventBundle:Participation:edit.html.twig', [
'form' => $form->createView(), 'form' => $form->createView(),
'participation' => $participation 'participation' => $participation,
)); ]);
} }
/** public function createEditForm(Participation $participation): FormInterface
*
* @param Participation $participation
* @return \Symfony\Component\Form\FormInterface
*/
public function createEditForm(Participation $participation)
{ {
$form = $this->createForm(ParticipationType::class, $participation, array( $form = $this->createForm(
ParticipationType::class,
$participation,
[
'event_type' => $participation->getEvent()->getType(), 'event_type' => $participation->getEvent()->getType(),
'action' => $this->generateUrl('chill_event_participation_update', array( 'action' => $this->generateUrl(
'participation_id' => $participation->getId() 'chill_event_participation_update',
)) [
)); 'participation_id' => $participation->getId(),
]
),
]
);
$form->add('submit', SubmitType::class, array( $form->add(
'label' => 'Edit' 'submit',
)); SubmitType::class, [
'label' => 'Edit',
]
);
return $form; return $form;
} }

View File

@@ -50,7 +50,7 @@ class LoadRolesACL extends AbstractFixture implements OrderedFixtureInterface
break; break;
case 'administrative': case 'administrative':
case 'direction': case 'direction':
if (in_array($scope->getName()['en'], array('administrative', 'social'))) { if (in_array($scope->getName()['en'], array('administrative', 'social'), true)) {
break 2; // we do not want any power on social or administrative break 2; // we do not want any power on social or administrative
} }
break; break;

View File

@@ -1,50 +1,46 @@
<?php <?php
declare(strict_types=1);
namespace Chill\AMLI\FamilyMembersBundle\Controller; namespace Chill\AMLI\FamilyMembersBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Chill\AMLI\FamilyMembersBundle\Repository\FamilyMemberRepository;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember; use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember;
use Chill\AMLI\FamilyMembersBundle\Security\Voter\FamilyMemberVoter; use Chill\AMLI\FamilyMembersBundle\Security\Voter\FamilyMemberVoter;
use Chill\AMLI\FamilyMembersBundle\Form\FamilyMemberType; use Chill\AMLI\FamilyMembersBundle\Form\FamilyMemberType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Component\Translation\TranslatorInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Annotation\Route;
class FamilyMemberController extends Controller class FamilyMemberController extends AbstractController
{ {
/** private EntityManagerInterface $em;
*
* @var EntityManagerInterface
*/
protected $em;
/** protected TranslatorInterface $translator;
*
* @var TranslatorInterface
*/
protected $translator;
/** protected LoggerInterface $chillMainLogger;
*
* @var LoggerInterface private FamilyMemberRepository $familyMemberRepository;
*/
protected $chillMainLogger;
public function __construct( public function __construct(
EntityManagerInterface $em, EntityManagerInterface $entityManager,
TranslatorInterface $translator, TranslatorInterface $translator,
LoggerInterface $chillMainLogger LoggerInterface $chillMainLogger,
FamilyMemberRepository $familyMemberRepository
) { ) {
$this->em = $em; $this->em = $entityManager;
$this->translator = $translator; $this->translator = $translator;
$this->chillMainLogger = $chillMainLogger; $this->chillMainLogger = $chillMainLogger;
$this->familyMemberRepository = $familyMemberRepository;
} }
/** /**
* @Route( * @Route(
* "{_locale}/family-members/family-members/by-person/{id}", * "{_locale}/family-members/family-members/by-person/{id}",
@@ -55,14 +51,12 @@ class FamilyMemberController extends Controller
{ {
$this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $person); $this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $person);
$familyMembers = $this->em $familyMembers = $this->familyMemberRepository->findByPerson($person);
->getRepository(FamilyMember::class)
->findByPerson($person);
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:index.html.twig', array( return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:index.html.twig', [
'person' => $person, 'person' => $person,
'familyMembers' => $familyMembers 'familyMembers' => $familyMembers
)); ]);
} }
/** /**
@@ -73,9 +67,7 @@ class FamilyMemberController extends Controller
*/ */
public function newAction(Person $person, Request $request) public function newAction(Person $person, Request $request)
{ {
$familyMember = (new FamilyMember()) $familyMember = (new FamilyMember())->setPerson($person);
->setPerson($person)
;
$this->denyAccessUnlessGranted(FamilyMemberVoter::CREATE, $familyMember); $this->denyAccessUnlessGranted(FamilyMemberVoter::CREATE, $familyMember);
@@ -84,10 +76,9 @@ class FamilyMemberController extends Controller
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager(); $this->em->persist($familyMember);
$em->persist($familyMember); $this->em->flush();
$em->flush();
$this->addFlash('success', $this->translator->trans('Family member created')); $this->addFlash('success', $this->translator->trans('Family member created'));
@@ -96,10 +87,10 @@ class FamilyMemberController extends Controller
]); ]);
} }
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:new.html.twig', array( return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:new.html.twig', [
'form' => $form->createView(), 'form' => $form->createView(),
'person' => $person 'person' => $person
)); ]);
} }
/** /**
@@ -117,9 +108,8 @@ class FamilyMemberController extends Controller
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager(); $this->em->flush();
$em->flush();
$this->addFlash('success', $this->translator->trans('Family member updated')); $this->addFlash('success', $this->translator->trans('Family member updated'));
@@ -128,11 +118,11 @@ class FamilyMemberController extends Controller
]); ]);
} }
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:edit.html.twig', array( return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:edit.html.twig', [
'familyMember' => $familyMember, 'familyMember' => $familyMember,
'form' => $form->createView(), 'form' => $form->createView(),
'person' => $familyMember->getPerson() 'person' => $familyMember->getPerson()
)); ]);
} }
/** /**
@@ -141,47 +131,42 @@ class FamilyMemberController extends Controller
* "{_locale}/family-members/family-members/{id}/delete", * "{_locale}/family-members/family-members/{id}/delete",
* name="chill_family_members_family_members_delete" * name="chill_family_members_family_members_delete"
* ) * )
*
* @param FamilyMember $familyMember
* @param Request $request
* @return \Symfony\Component\BrowserKit\Response
*/ */
public function deleteAction(FamilyMember $familyMember, Request $request) public function deleteAction(FamilyMember $familyMember, Request $request): Response
{ {
$this->denyAccessUnlessGranted(FamilyMemberVoter::DELETE, $familyMember, 'You are not ' $this->denyAccessUnlessGranted(FamilyMemberVoter::DELETE, $familyMember, 'You are not '
. 'allowed to delete this family membership'); . 'allowed to delete this family membership');
$form = $this->createDeleteForm($id); $form = $this->createDeleteForm();
if ($request->getMethod() === Request::METHOD_DELETE) { if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isValid()) { if ($form->isValid()) {
$this->chillMainLogger->notice("A family member has been removed", array( $this->chillMainLogger->notice("A family member has been removed", [
'by_user' => $this->getUser()->getUsername(), 'by_user' => $this->getUser()->getUsername(),
'family_member_id' => $familyMember->getId(), 'family_member_id' => $familyMember->getId(),
'name' => $familyMember->getFirstname()." ".$familyMember->getLastname(), 'name' => $familyMember->getFirstname()." ".$familyMember->getLastname(),
'link' => $familyMember->getLink() 'link' => $familyMember->getLink()
)); ]);
$em = $this->getDoctrine()->getManager(); $this->em->remove($familyMember);
$em->remove($familyMember); $this->em->flush();
$em->flush();
$this->addFlash('success', $this->translator $this->addFlash('success', $this->translator
->trans("The family member has been successfully removed.")); ->trans("The family member has been successfully removed."));
return $this->redirectToRoute('chill_family_members_family_members_index', array( return $this->redirectToRoute('chill_family_members_family_members_index', [
'id' => $familyMember->getPerson()->getId() 'id' => $familyMember->getPerson()->getId()
)); ]);
} }
} }
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:confirm_delete.html.twig', array( return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:confirm_delete.html.twig', [
'familyMember' => $familyMember, 'familyMember' => $familyMember,
'delete_form' => $form->createView() 'delete_form' => $form->createView()
)); ]);
} }
/** /**
@@ -194,23 +179,20 @@ class FamilyMemberController extends Controller
{ {
$this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $familyMember); $this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $familyMember);
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:view.html.twig', array( return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:view.html.twig', [
'familyMember' => $familyMember 'familyMember' => $familyMember
)); ]);
} }
/** /**
* Creates a form to delete a help request entity by id. * Creates a form to delete a help request entity by id.
*
* @param mixed $id The entity id
*
* @return \Symfony\Component\Form\Form The form
*/ */
private function createDeleteForm($id) private function createDeleteForm(): FormInterface
{ {
return $this->createFormBuilder() return $this
->createFormBuilder()
->setMethod(Request::METHOD_DELETE) ->setMethod(Request::METHOD_DELETE)
->add('submit', SubmitType::class, array('label' => 'Delete')) ->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm() ->getForm()
; ;
} }

View File

@@ -1,17 +1,32 @@
<?php <?php
declare(strict_types=1);
namespace Chill\AMLI\FamilyMembersBundle\Repository; namespace Chill\AMLI\FamilyMembersBundle\Repository;
use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/** /**
* FamilyMemberRepository * @method FamilyMember|null find($id, $lockMode = null, $lockVersion = null)
* * @method FamilyMember|null findOneBy(array $criteria, array $orderBy = null)
* @method FamilyMember[] findAll()
* @method FamilyMember[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/ */
class FamilyMemberRepository extends \Doctrine\ORM\EntityRepository class FamilyMemberRepository extends ServiceEntityRepository
{ {
public function findActiveByPerson(Person $person) public function __construct(ManagerRegistry $registry)
{ {
return $this->findBy([ 'person' => $person ]); parent::__construct($registry, FamilyMember::class);
}
/**
* @return FamilyMember[]
*/
public function findByPerson(Person $person): array
{
return $this->findBy(['person' => $person]);
} }
} }

View File

@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\CRUD\Controller; namespace Chill\MainBundle\CRUD\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -14,49 +16,39 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Component\Translation\TranslatorInterface;
class AbstractCRUDController extends AbstractController abstract class AbstractCRUDController extends AbstractController
{ {
/** /**
* The crud configuration * The crud configuration
* *
* This configuration si defined by `chill_main['crud']` or `chill_main['apis']` * This configuration si defined by `chill_main['crud']` or `chill_main['apis']`
*
* @var array
*/ */
protected array $crudConfig = []; protected array $crudConfig = [];
/** /**
* get the instance of the entity with the given id * get the instance of the entity with the given id
* *
* @param string $id
* @return object
* @throw Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the object is not found * @throw Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the object is not found
*/ */
protected function getEntity($action, $id, Request $request): object protected function getEntity($action, string $id, Request $request): object
{ {
$e = $this->getDoctrine() $e = $this
->getDoctrine()
->getRepository($this->getEntityClass()) ->getRepository($this->getEntityClass())
->find($id); ->find($id);
if (NULL === $e) { if (null === $e) {
throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id)); throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id));
} }
return $e; return $e;
} }
/**
* Create an entity.
*
* @param string $action
* @param Request $request
* @return object
*/
protected function createEntity(string $action, Request $request): object protected function createEntity(string $action, Request $request): object
{ {
$type = $this->getEntityClass(); $class = $this->getEntityClass();
return new $type; return new $class;
} }
/** /**
@@ -64,18 +56,13 @@ class AbstractCRUDController extends AbstractController
* *
* By default, count all entities. You can customize the query by * By default, count all entities. You can customize the query by
* using the method `customizeQuery`. * using the method `customizeQuery`.
*
* @param string $action
* @param Request $request
* @return int
*/ */
protected function countEntities(string $action, Request $request, $_format): int protected function countEntities(string $action, Request $request, $_format): int
{ {
return $this->buildQueryEntities($action, $request) return $this->buildQueryEntities($action, $request)
->select('COUNT(e)') ->select('COUNT(e)')
->getQuery() ->getQuery()
->getSingleScalarResult() ->getSingleScalarResult();
;
} }
/** /**
@@ -87,7 +74,6 @@ class AbstractCRUDController extends AbstractController
* The method `orderEntity` is called internally to order entities. * The method `orderEntity` is called internally to order entities.
* *
* It returns, by default, a query builder. * It returns, by default, a query builder.
*
*/ */
protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator) protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator)
{ {
@@ -101,7 +87,6 @@ class AbstractCRUDController extends AbstractController
/** /**
* Add ordering fields in the query build by self::queryEntities * Add ordering fields in the query build by self::queryEntities
*
*/ */
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format) protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
{ {
@@ -118,8 +103,6 @@ class AbstractCRUDController extends AbstractController
* *
* The alias for the entity is "e". * The alias for the entity is "e".
* *
* @param string $action
* @param Request $request
* @return QueryBuilder * @return QueryBuilder
*/ */
protected function buildQueryEntities(string $action, Request $request) protected function buildQueryEntities(string $action, Request $request)
@@ -127,8 +110,7 @@ class AbstractCRUDController extends AbstractController
$qb = $this->getDoctrine()->getManager() $qb = $this->getDoctrine()->getManager()
->createQueryBuilder() ->createQueryBuilder()
->select('e') ->select('e')
->from($this->getEntityClass(), 'e') ->from($this->getEntityClass(), 'e');
;
$this->customizeQuery($action, $request, $qb); $this->customizeQuery($action, $request, $qb);
@@ -138,7 +120,7 @@ class AbstractCRUDController extends AbstractController
protected function customizeQuery(string $action, Request $request, $query): void {} protected function customizeQuery(string $action, Request $request, $query): void {}
/** /**
* Get the result of the query * Get the result of the query.
*/ */
protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query) protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query)
{ {
@@ -151,7 +133,7 @@ class AbstractCRUDController extends AbstractController
} }
/** /**
* method used by indexAction * Method used by indexAction.
*/ */
protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response
{ {
@@ -159,7 +141,7 @@ class AbstractCRUDController extends AbstractController
} }
/** /**
* method used by indexAction * Method used by indexAction.
*/ */
protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response
{ {
@@ -167,18 +149,17 @@ class AbstractCRUDController extends AbstractController
} }
/** /**
* method used by indexAction * Method used by indexAction.
*/ */
protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response
{ {
return null; return null;
} }
/** /**
* Get the complete FQDN of the class * Get the FQDN of the class.
* *
* @return string the complete fqdn of the class * @return class-string The FQDN of the class
*/ */
protected function getEntityClass(): string protected function getEntityClass(): string
{ {
@@ -186,7 +167,7 @@ class AbstractCRUDController extends AbstractController
} }
/** /**
* called on post fetch entity * Called on post fetch entity.
*/ */
protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response
{ {
@@ -194,7 +175,7 @@ class AbstractCRUDController extends AbstractController
} }
/** /**
* Called on post check ACL * Called on post check ACL.
*/ */
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
{ {
@@ -214,12 +195,12 @@ class AbstractCRUDController extends AbstractController
*/ */
protected function checkACL(string $action, Request $request, string $_format, $entity = null) protected function checkACL(string $action, Request $request, string $_format, $entity = null)
{ {
// @TODO: Implements abstract getRoleFor method or do it in the interface.
$this->denyAccessUnlessGranted($this->getRoleFor($action, $request, $entity, $_format), $entity); $this->denyAccessUnlessGranted($this->getRoleFor($action, $request, $entity, $_format), $entity);
} }
/** /**
* * @return string The crud name.
* @return string the crud name
*/ */
protected function getCrudName(): string protected function getCrudName(): string
{ {
@@ -241,9 +222,6 @@ class AbstractCRUDController extends AbstractController
$this->crudConfig = $config; $this->crudConfig = $config;
} }
/**
* @return PaginatorFactory
*/
protected function getPaginatorFactory(): PaginatorFactory protected function getPaginatorFactory(): PaginatorFactory
{ {
return $this->container->get('chill_main.paginator_factory'); return $this->container->get('chill_main.paginator_factory');
@@ -254,9 +232,6 @@ class AbstractCRUDController extends AbstractController
return $this->get('validator'); return $this->get('validator');
} }
/**
* @return array
*/
public static function getSubscribedServices(): array public static function getSubscribedServices(): array
{ {
return \array_merge( return \array_merge(

View File

@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\CRUD\Controller; namespace Chill\MainBundle\CRUD\Controller;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -34,7 +36,7 @@ class ApiController extends AbstractCRUDController
*/ */
protected function entityGet(string $action, Request $request, $id, $_format = 'html'): Response protected function entityGet(string $action, Request $request, $id, $_format = 'html'): Response
{ {
$entity = $this->getEntity($action, $id, $request, $_format); $entity = $this->getEntity($action, $id, $request);
$postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format); $postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format);
@@ -86,7 +88,7 @@ class ApiController extends AbstractCRUDController
case Request::METHOD_PATCH: case Request::METHOD_PATCH:
return $this->entityPut('_entity', $request, $id, $_format); return $this->entityPut('_entity', $request, $id, $_format);
case Request::METHOD_POST: case Request::METHOD_POST:
return $this->entityPostAction('_entity', $request, $id, $_format); return $this->entityPostAction('_entity', $request, $id);
case Request::METHOD_DELETE: case Request::METHOD_DELETE:
return $this->entityDelete('_entity', $request, $id, $_format); return $this->entityDelete('_entity', $request, $id, $_format);
default: default:
@@ -158,7 +160,7 @@ class ApiController extends AbstractCRUDController
} }
public function entityPut($action, Request $request, $id, string $_format): Response public function entityPut($action, Request $request, $id, string $_format): Response
{ {
$entity = $this->getEntity($action, $id, $request, $_format); $entity = $this->getEntity($action, $id, $request);
$postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format); $postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format);
if ($postFetch instanceof Response) { if ($postFetch instanceof Response) {
@@ -221,7 +223,7 @@ class ApiController extends AbstractCRUDController
} }
public function entityDelete($action, Request $request, $id, string $_format): Response public function entityDelete($action, Request $request, $id, string $_format): Response
{ {
$entity = $this->getEntity($action, $id, $request, $_format); $entity = $this->getEntity($action, $id, $request);
if (NULL === $entity) { if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s " throw $this->createNotFoundException(sprintf("The %s with id %s "
@@ -345,7 +347,6 @@ class ApiController extends AbstractCRUDController
* *
* @param string $action * @param string $action
* @param Request $request * @param Request $request
* @return type
*/ */
protected function indexApiAction($action, Request $request, $_format) protected function indexApiAction($action, Request $request, $_format)
{ {
@@ -356,9 +357,7 @@ class ApiController extends AbstractCRUDController
return $response; return $response;
} }
if (!isset($entity)) {
$entity = ''; $entity = '';
}
$response = $this->onPostCheckACL($action, $request, $_format, $entity); $response = $this->onPostCheckACL($action, $request, $_format, $entity);
if ($response instanceof Response) { if ($response instanceof Response) {
@@ -368,8 +367,13 @@ class ApiController extends AbstractCRUDController
$totalItems = $this->countEntities($action, $request, $_format); $totalItems = $this->countEntities($action, $request, $_format);
$paginator = $this->getPaginatorFactory()->create($totalItems); $paginator = $this->getPaginatorFactory()->create($totalItems);
$response = $this->onPreIndexBuildQuery($action, $request, $_format, $totalItems, $response = $this->onPreIndexBuildQuery(
$paginator); $action,
$request,
$_format,
$totalItems,
$paginator
);
if ($response instanceof Response) { if ($response instanceof Response) {
return $response; return $response;

View File

@@ -1,22 +1,4 @@
<?php <?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2019, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\CRUD\Controller; namespace Chill\MainBundle\CRUD\Controller;
@@ -37,60 +19,40 @@ use Chill\MainBundle\Pagination\PaginatorInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
/**
* Class CRUDController
*
* @package Chill\MainBundle\CRUD\Controller
*/
class CRUDController extends AbstractController class CRUDController extends AbstractController
{ {
/** /**
* The crud configuration * The crud configuration
* *
* This configuration si defined by `chill_main['crud']`. * This configuration si defined by `chill_main['crud']`.
*
* @var array
*/ */
protected $crudConfig; protected array $crudConfig;
/** public function setCrudConfig(array $config): void
* @param array $config
*/
public function setCrudConfig(array $config)
{ {
$this->crudConfig = $config; $this->crudConfig = $config;
} }
/** public function CRUD(?string $parameter): Response
* @param $parameter
* @return Response
*/
public function CRUD($parameter)
{ {
return new Response($parameter); return new Response($parameter);
} }
/** /**
* @param Request $request
* @param $id * @param $id
* @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
*/ */
public function delete(Request $request, $id) public function delete(Request $request, $id): Response
{ {
return $this->deleteAction('delete', $request, $id); return $this->deleteAction('delete', $request, $id);
} }
/** /**
* @param string $action
* @param Request $request
* @param $id * @param $id
* @param null $formClass * @param null $formClass
* @return null|\Symfony\Component\HttpFoundation\RedirectResponse|Response|void
*/ */
protected function deleteAction(string $action, Request $request, $id, $formClass = null) protected function deleteAction(string $action, Request $request, $id, $formClass = null): Response
{ {
$this->onPreDelete($action, $request, $id); $this->onPreDelete($action, $request);
$entity = $this->getEntity($action, $id, $request); $entity = $this->getEntity($action, $id, $request);
@@ -101,8 +63,13 @@ class CRUDController extends AbstractController
} }
if (NULL === $entity) { if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s " throw $this->createNotFoundException(
. "is not found"), $this->getCrudName(), $id); sprintf(
'The %s with id %s is not found',
$this->getCrudName(),
$id
)
);
} }
$response = $this->checkACL($action, $entity); $response = $this->checkACL($action, $entity);
@@ -141,7 +108,9 @@ class CRUDController extends AbstractController
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', ['id' => $entity->getId()]); return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', ['id' => $entity->getId()]);
} elseif ($form->isSubmitted()) { }
if ($form->isSubmitted()) {
$this->addFlash('error', $this->generateFormErrorMessage($action, $form)); $this->addFlash('error', $this->generateFormErrorMessage($action, $form));
} }
@@ -233,7 +202,6 @@ class CRUDController extends AbstractController
* *
* @param string $action * @param string $action
* @param Request $request * @param Request $request
* @return type
*/ */
protected function indexEntityAction($action, Request $request) protected function indexEntityAction($action, Request $request)
{ {
@@ -244,9 +212,7 @@ class CRUDController extends AbstractController
return $response; return $response;
} }
if (!isset($entity)) {
$entity = ''; $entity = '';
}
$response = $this->onPostCheckACL($action, $request, $entity); $response = $this->onPostCheckACL($action, $request, $entity);
if ($response instanceof Response) { if ($response instanceof Response) {
@@ -342,11 +308,12 @@ class CRUDController extends AbstractController
*/ */
protected function buildQueryEntities(string $action, Request $request) protected function buildQueryEntities(string $action, Request $request)
{ {
$query = $this->getDoctrine()->getManager() $query = $this
->getDoctrine()
->getManager()
->createQueryBuilder() ->createQueryBuilder()
->select('e') ->select('e')
->from($this->getEntityClass(), 'e') ->from($this->getEntityClass(), 'e');
;
$this->customizeQuery($action, $request, $query); $this->customizeQuery($action, $request, $query);
@@ -371,7 +338,7 @@ class CRUDController extends AbstractController
*/ */
protected function queryEntities(string $action, Request $request, PaginatorInterface $paginator, ?FilterOrderHelper $filterOrder = null) protected function queryEntities(string $action, Request $request, PaginatorInterface $paginator, ?FilterOrderHelper $filterOrder = null)
{ {
$query = $this->buildQueryEntities($action, $request, $filterOrder) $query = $this->buildQueryEntities($action, $request)
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber()) ->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
->setMaxResults($paginator->getItemsPerPage()); ->setMaxResults($paginator->getItemsPerPage());
@@ -420,7 +387,7 @@ class CRUDController extends AbstractController
*/ */
protected function countEntities(string $action, Request $request, ?FilterOrderHelper $filterOrder = null): int protected function countEntities(string $action, Request $request, ?FilterOrderHelper $filterOrder = null): int
{ {
return $this->buildQueryEntities($action, $request, $filterOrder) return $this->buildQueryEntities($action, $request)
->select('COUNT(e)') ->select('COUNT(e)')
->getQuery() ->getQuery()
->getSingleScalarResult() ->getSingleScalarResult()
@@ -505,8 +472,13 @@ class CRUDController extends AbstractController
} }
if (NULL === $entity) { if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s " throw $this->createNotFoundException(
. "is not found", $this->getCrudName(), $id)); sprintf(
'The %s with id %s is not found',
$this->getCrudName(),
$id
)
);
} }
$response = $this->checkACL($action, $entity); $response = $this->checkACL($action, $entity);
@@ -598,8 +570,13 @@ class CRUDController extends AbstractController
$entity = $this->getEntity($action, $id, $request); $entity = $this->getEntity($action, $id, $request);
if (NULL === $entity) { if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s " throw $this->createNotFoundException(
. "is not found", $this->getCrudName(), $id)); sprintf(
'The %s with id %s is not found',
$this->getCrudName(),
$id
)
);
} }
$response = $this->checkACL($action, $entity); $response = $this->checkACL($action, $entity);

View File

@@ -1,22 +1,6 @@
<?php <?php
/*
* Chill is a software for social workers declare(strict_types=1);
*
* Copyright (C) 2019, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\CRUD\Routing; namespace Chill\MainBundle\CRUD\Routing;
@@ -27,22 +11,13 @@ use Symfony\Component\HttpFoundation\Request;
use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\CRUD\Controller\CRUDController; use Chill\MainBundle\CRUD\Controller\CRUDController;
/**
* Class CRUDRoutesLoader
* Load the route for CRUD
*
* @package Chill\MainBundle\CRUD\Routing
*/
class CRUDRoutesLoader extends Loader class CRUDRoutesLoader extends Loader
{ {
protected array $crudConfig = []; protected array $crudConfig = [];
protected array $apiCrudConfig = []; protected array $apiCrudConfig = [];
/** private bool $isLoaded = false;
* @var bool
*/
private $isLoaded = false;
private const ALL_SINGLE_METHODS = [ private const ALL_SINGLE_METHODS = [
Request::METHOD_GET, Request::METHOD_GET,
@@ -53,16 +28,12 @@ class CRUDRoutesLoader extends Loader
private const ALL_INDEX_METHODS = [ Request::METHOD_GET, Request::METHOD_HEAD ]; private const ALL_INDEX_METHODS = [ Request::METHOD_GET, Request::METHOD_HEAD ];
/** public function __construct(array $crudConfig, array $apiCrudConfig)
* CRUDRoutesLoader constructor.
*
* @param $crudConfig the config from cruds
* @param $apicrudConfig the config from api_crud
*/
public function __construct(array $crudConfig, array $apiConfig)
{ {
$this->crudConfig = $crudConfig; $this->crudConfig = $crudConfig;
$this->apiConfig = $apiConfig; $this->apiCrudConfig = $apiCrudConfig;
parent::__construct();
} }
/** /**
@@ -89,7 +60,7 @@ class CRUDRoutesLoader extends Loader
foreach ($this->crudConfig as $crudConfig) { foreach ($this->crudConfig as $crudConfig) {
$collection->addCollection($this->loadCrudConfig($crudConfig)); $collection->addCollection($this->loadCrudConfig($crudConfig));
} }
foreach ($this->apiConfig as $crudConfig) { foreach ($this->apiCrudConfig as $crudConfig) {
$collection->addCollection($this->loadApi($crudConfig)); $collection->addCollection($this->loadApi($crudConfig));
} }

View File

@@ -3,6 +3,7 @@
namespace Chill\MainBundle; namespace Chill\MainBundle;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Chill\MainBundle\Search\SearchApiInterface;
use Chill\MainBundle\Search\SearchInterface; use Chill\MainBundle\Search\SearchInterface;
use Chill\MainBundle\Security\Authorization\ChillVoterInterface; use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
use Chill\MainBundle\Security\ProvideRoleInterface; use Chill\MainBundle\Security\ProvideRoleInterface;
@@ -41,6 +42,8 @@ class ChillMainBundle extends Bundle
->addTag('chill_main.scope_resolver'); ->addTag('chill_main.scope_resolver');
$container->registerForAutoconfiguration(ChillEntityRenderInterface::class) $container->registerForAutoconfiguration(ChillEntityRenderInterface::class)
->addTag('chill.render_entity'); ->addTag('chill.render_entity');
$container->registerForAutoconfiguration(SearchApiInterface::class)
->addTag('chill.search_api_provider');
$container->addCompilerPass(new SearchableServicesCompilerPass()); $container->addCompilerPass(new SearchableServicesCompilerPass());
$container->addCompilerPass(new ConfigConsistencyCompilerPass()); $container->addCompilerPass(new ConfigConsistencyCompilerPass());

View File

@@ -1,7 +1,10 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\Command; namespace Chill\MainBundle\Command;
use Chill\MainBundle\Repository\UserRepository;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@@ -23,101 +26,51 @@ use League\Csv\Writer;
class ChillImportUsersCommand extends Command class ChillImportUsersCommand extends Command
{ {
protected EntityManagerInterface $em;
/** protected ValidatorInterface $validator;
*
* @var EntityManagerInterface
*/
protected $em;
/** protected LoggerInterface $logger;
*
* @var ValidatorInterface
*/
protected $validator;
/** protected UserPasswordEncoderInterface $passwordEncoder;
*
* @var LoggerInterface
*/
protected $logger;
/** protected UserRepository $userRepository;
*
* @var UserPasswordEncoderInterface
*/
protected $passwordEncoder;
/** protected bool $doChanges = true;
*
* @var \Chill\MainBundle\Repository\UserRepository
*/
protected $userRepository;
/** protected OutputInterface $tempOutput;
*
* @var bool
*/
protected $doChanges = true;
/** protected InputInterface $tempInput;
*
* @var OutputInterface
*/
protected $tempOutput;
/**
*
* @var InputInterface
*/
protected $tempInput;
/** /**
* Centers and aliases. * Centers and aliases.
* *
* key are aliases, values are an array of centers * key are aliases, values are an array of centers
*
* @var array
*/ */
protected $centers = []; protected array $centers;
/** protected array $permissionGroups;
*
* @var array
*/
protected $permissionGroups = [];
/** protected array $groupCenters;
*
* @var array
*/
protected $groupCenters = [];
/** protected Writer $output;
*
* @var Writer
*/
protected $output = null;
public function __construct( public function __construct(
EntityManagerInterface $em, EntityManagerInterface $em,
LoggerInterface $logger, LoggerInterface $logger,
UserPasswordEncoderInterface $passwordEncoder, UserPasswordEncoderInterface $passwordEncoder,
ValidatorInterface $validator ValidatorInterface $validator,
UserRepository $userRepository
) { ) {
$this->em = $em; $this->em = $em;
$this->passwordEncoder = $passwordEncoder; $this->passwordEncoder = $passwordEncoder;
$this->validator = $validator; $this->validator = $validator;
$this->logger = $logger; $this->logger = $logger;
$this->userRepository = $userRepository;
$this->userRepository = $em->getRepository(User::class);
parent::__construct('chill:main:import-users'); parent::__construct('chill:main:import-users');
} }
protected function configure() protected function configure()
{ {
$this $this
@@ -126,8 +79,7 @@ class ChillImportUsersCommand extends Command
->addArgument('csvfile', InputArgument::REQUIRED, 'Path to the csv file. Columns are: `username`, `email`, `center` (can contain alias), `permission group`') ->addArgument('csvfile', InputArgument::REQUIRED, 'Path to the csv file. Columns are: `username`, `email`, `center` (can contain alias), `permission group`')
->addOption('grouping-centers', null, InputOption::VALUE_OPTIONAL, 'Path to a csv file to aggregate multiple centers into a single alias') ->addOption('grouping-centers', null, InputOption::VALUE_OPTIONAL, 'Path to a csv file to aggregate multiple centers into a single alias')
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not commit the changes') ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not commit the changes')
->addOption('csv-dump', null, InputOption::VALUE_REQUIRED, 'A path to dump a summary of the created file') ->addOption('csv-dump', null, InputOption::VALUE_REQUIRED, 'A path to dump a summary of the created file');
;
} }
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
@@ -309,21 +261,14 @@ class ChillImportUsersCommand extends Command
} }
return $this->permissionGroups[$alias]; return $this->permissionGroups[$alias];
} else {
$this->logger->error("Error while responding to a a question");
$this->tempOutput("Ok, I accept, but I do not know what to do. Please try again.");
throw new \RuntimeException("Error while responding to a question");
}
} }
/** $this->logger->error('Error while responding to a a question');
* $this->tempOutput->writeln('Ok, I accept, but I do not know what to do. Please try again.');
* @param Center $center
* @param \Chill\MainBundle\Command\PermissionGroup $pg throw new \RuntimeException('Error while responding to a question');
* @return GroupCenter }
*/
protected function createOrGetGroupCenter(Center $center, PermissionsGroup $pg): GroupCenter protected function createOrGetGroupCenter(Center $center, PermissionsGroup $pg): GroupCenter
{ {
if (\array_key_exists($center->getId(), $this->groupCenters)) { if (\array_key_exists($center->getId(), $this->groupCenters)) {

View File

@@ -1,27 +1,13 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Command; namespace Chill\MainBundle\Command;
use Chill\MainBundle\Doctrine\Model\Point; use Chill\MainBundle\Doctrine\Model\Point;
use Chill\MainBundle\Entity\Country; use Chill\MainBundle\Entity\Country;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
@@ -31,32 +17,13 @@ use Symfony\Component\Filesystem\Filesystem;
use Chill\MainBundle\Entity\PostalCode; use Chill\MainBundle\Entity\PostalCode;
use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Class LoadPostalCodesCommand
*
* @package Chill\MainBundle\Command
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class LoadPostalCodesCommand extends Command class LoadPostalCodesCommand extends Command
{ {
private EntityManagerInterface $entityManager;
/** private ValidatorInterface $validator;
* @var EntityManager
*/
private $entityManager;
/** public function __construct(EntityManagerInterface $entityManager, ValidatorInterface $validator)
* @var ValidatorInterface
*/
private $validator;
/**
* LoadPostalCodesCommand constructor.
*
* @param EntityManager $entityManager
* @param ValidatorInterface $validator
*/
public function __construct(EntityManager $entityManager, ValidatorInterface $validator)
{ {
$this->entityManager = $entityManager; $this->entityManager = $entityManager;
$this->validator = $validator; $this->validator = $validator;
@@ -102,12 +69,7 @@ class LoadPostalCodesCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
try {
$csv = $this->getCSVResource($input); $csv = $this->getCSVResource($input);
} catch (\RuntimeException $e) {
$output->writeln('<error>Error during opening the csv file : '.
$e->getMessage().'</error>');
}
if ($output->getVerbosity() === OutputInterface::VERBOSITY_VERY_VERBOSE) { if ($output->getVerbosity() === OutputInterface::VERBOSITY_VERY_VERBOSE) {
$output->writeln('The content of the file is ...'); $output->writeln('The content of the file is ...');

View File

@@ -1,20 +1,12 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\Controller; namespace Chill\MainBundle\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController; use Chill\MainBundle\CRUD\Controller\CRUDController;
use Chill\MainBundle\Entity\Country;
use Chill\MainBundle\Pagination\PaginatorFactory;
/**
*
*
*/
class AdminCountryCRUDController extends CRUDController class AdminCountryCRUDController extends CRUDController
{ {
function __construct(PaginatorFactory $paginator)
{
$this->paginatorFactory = $paginator;
}
} }

View File

@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\Controller; namespace Chill\MainBundle\Controller;
use Chill\MainBundle\CRUD\Controller\AbstractCRUDController; use Chill\MainBundle\CRUD\Controller\AbstractCRUDController;
@@ -7,6 +9,7 @@ use Chill\MainBundle\CRUD\Controller\CRUDController;
use Chill\MainBundle\Pagination\PaginatorInterface; use Chill\MainBundle\Pagination\PaginatorInterface;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -16,40 +19,23 @@ use Chill\MainBundle\Form\UserType;
use Chill\MainBundle\Entity\GroupCenter; use Chill\MainBundle\Entity\GroupCenter;
use Chill\MainBundle\Form\Type\ComposedGroupCenterType; use Chill\MainBundle\Form\Type\ComposedGroupCenterType;
use Chill\MainBundle\Form\UserPasswordType; use Chill\MainBundle\Form\UserPasswordType;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
/**
* Class UserController
*
* @package Chill\MainBundle\Controller
*/
class UserController extends CRUDController class UserController extends CRUDController
{ {
const FORM_GROUP_CENTER_COMPOSED = 'composed_groupcenter'; const FORM_GROUP_CENTER_COMPOSED = 'composed_groupcenter';
/** private LoggerInterface $logger;
* @var \Psr\Log\LoggerInterface
*/
private $logger;
/** private ValidatorInterface $validator;
* @var ValidatorInterface
*/
private $validator;
private UserPasswordEncoderInterface $passwordEncoder; private UserPasswordEncoderInterface $passwordEncoder;
/**
* UserController constructor.
*
* @param LoggerInterface $logger
* @param ValidatorInterface $validator
*/
public function __construct( public function __construct(
LoggerInterface $chillLogger, LoggerInterface $chillLogger,
ValidatorInterface $validator, ValidatorInterface $validator,
@@ -121,7 +107,7 @@ class UserController extends CRUDController
*/ */
public function editPasswordAction(User $user, Request $request) public function editPasswordAction(User $user, Request $request)
{ {
$editForm = $this->createEditPasswordForm($user, $request); $editForm = $this->createEditPasswordForm($user);
$editForm->handleRequest($request); $editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) { if ($editForm->isSubmitted() && $editForm->isValid()) {
@@ -150,20 +136,17 @@ class UserController extends CRUDController
]); ]);
} }
/** private function createEditPasswordForm(User $user): FormInterface
*
*
* @param User $user
* @return \Symfony\Component\Form\Form
*/
private function createEditPasswordForm(User $user)
{ {
return $this->createForm(UserPasswordType::class, null, array( return $this->createForm(
UserPasswordType::class,
null,
[
'user' => $user 'user' => $user
)) ]
)
->add('submit', SubmitType::class, array('label' => 'Change password')) ->add('submit', SubmitType::class, array('label' => 'Change password'))
->remove('actual_password') ->remove('actual_password');
;
} }
/** /**
@@ -208,7 +191,7 @@ class UserController extends CRUDController
* @Route("/{_locale}/admin/main/user/{uid}/add_link_groupcenter", * @Route("/{_locale}/admin/main/user/{uid}/add_link_groupcenter",
* name="admin_user_add_groupcenter") * name="admin_user_add_groupcenter")
*/ */
public function addLinkGroupCenterAction(Request $request, $uid): RedirectResponse public function addLinkGroupCenterAction(Request $request, $uid): Response
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
@@ -238,23 +221,22 @@ class UserController extends CRUDController
return $this->redirect($this->generateUrl('chill_crud_admin_user_edit', return $this->redirect($this->generateUrl('chill_crud_admin_user_edit',
\array_merge(['id' => $uid], $returnPathParams))); \array_merge(['id' => $uid], $returnPathParams)));
} else { }
foreach($this->validator->validate($user) as $error)
foreach($this->validator->validate($user) as $error) {
$this->addFlash('error', $error->getMessage()); $this->addFlash('error', $error->getMessage());
} }
} }
return $this->render('@ChillMain/User/edit.html.twig', array( return $this->render('@ChillMain/User/edit.html.twig', [
'entity' => $user, 'entity' => $user,
'edit_form' => $this->createEditForm($user)->createView(), 'edit_form' => $this->createEditForm($user)->createView(),
'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($user)->createView(), 'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($user, $request)->createView(),
'delete_groupcenter_form' => array_map( 'delete_groupcenter_form' => array_map(
function(\Symfony\Component\Form\Form $form) { static fn(Form $form) => $form->createView(),
return $form->createView(); iterator_to_array($this->getDeleteLinkGroupCenterByUser($user, $request), true)
)
}, ]);
iterator_to_array($this->getDeleteLinkGroupCenterByUser($user), true))
));
} }
private function getPersistedGroupCenter(GroupCenter $groupCenter) private function getPersistedGroupCenter(GroupCenter $groupCenter)
@@ -279,10 +261,8 @@ class UserController extends CRUDController
* Creates a form to delete a link to a GroupCenter * Creates a form to delete a link to a GroupCenter
* *
* @param mixed $permissionsGroup The entity id * @param mixed $permissionsGroup The entity id
*
* @return \Symfony\Component\Form\Form The form
*/ */
private function createDeleteLinkGroupCenterForm(User $user, GroupCenter $groupCenter, $request) private function createDeleteLinkGroupCenterForm(User $user, GroupCenter $groupCenter, $request): FormInterface
{ {
$returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : []; $returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : [];
@@ -291,17 +271,13 @@ class UserController extends CRUDController
array_merge($returnPathParams, ['uid' => $user->getId(), 'gcid' => $groupCenter->getId()]))) array_merge($returnPathParams, ['uid' => $user->getId(), 'gcid' => $groupCenter->getId()])))
->setMethod('DELETE') ->setMethod('DELETE')
->add('submit', SubmitType::class, array('label' => 'Delete')) ->add('submit', SubmitType::class, array('label' => 'Delete'))
->getForm() ->getForm();
;
} }
/** /**
* create a form to add a link to a groupcenter * Create a form to add a link to a groupcenter.
*
* @param User $user
* @return \Symfony\Component\Form\Form
*/ */
private function createAddLinkGroupCenterForm(User $user, Request $request) private function createAddLinkGroupCenterForm(User $user, Request $request): FormInterface
{ {
$returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : []; $returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : [];
@@ -311,19 +287,13 @@ class UserController extends CRUDController
->setMethod('POST') ->setMethod('POST')
->add(self::FORM_GROUP_CENTER_COMPOSED, ComposedGroupCenterType::class) ->add(self::FORM_GROUP_CENTER_COMPOSED, ComposedGroupCenterType::class)
->add('submit', SubmitType::class, array('label' => 'Add a new groupCenter')) ->add('submit', SubmitType::class, array('label' => 'Add a new groupCenter'))
->getForm() ->getForm();
;
} }
/**
*
* @param User $user
*/
private function getDeleteLinkGroupCenterByUser(User $user, Request $request) private function getDeleteLinkGroupCenterByUser(User $user, Request $request)
{ {
foreach ($user->getGroupCenters() as $groupCenter) { foreach ($user->getGroupCenters() as $groupCenter) {
yield $groupCenter->getId() => $this yield $groupCenter->getId() => $this->createDeleteLinkGroupCenterForm($user, $groupCenter, $request);
->createDeleteLinkGroupCenterForm($user, $groupCenter, $request);
} }
} }
} }

View File

@@ -104,6 +104,9 @@ class Configuration implements ConfigurationInterface
->booleanNode('form_show_scopes') ->booleanNode('form_show_scopes')
->defaultTrue() ->defaultTrue()
->end() ->end()
->booleanNode('form_show_centers')
->defaultTrue()
->end()
->end() ->end()
->end() ->end()
->arrayNode('redis') ->arrayNode('redis')

View File

@@ -1,32 +1,19 @@
<?php <?php
/*
* Chill is a software for social workers declare(strict_types=1);
* Copyright (C) 2018 Champs-Libres Coopérative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Doctrine\DQL; namespace Chill\MainBundle\Doctrine\DQL;
use Doctrine\ORM\Query\AST\Functions\FunctionNode; use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\Lexer; use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
/** /**
* DQL function for OVERLAPS function in postgresql * DQL function for OVERLAPS function in postgresql
* *
* If a value is null in period start, it will be replaced by -infinity. * If a value is null in period start, it will be replaced by -infinity.
* If a value is null in period end, it will be replaced by infinity * If a value is null in period end, it will be replaced by infinity
*
*/ */
class OverlapsI extends FunctionNode class OverlapsI extends FunctionNode
{ {
@@ -40,19 +27,17 @@ class OverlapsI extends FunctionNode
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{ {
return '(' return sprintf(
.$this->makeCase($sqlWalker, $this->firstPeriodStart, 'start').', ' '(%s, %s) OVERLAPS (%s, %s)',
.$this->makeCase($sqlWalker, $this->firstPeriodEnd, 'end'). $this->makeCase($sqlWalker, $this->firstPeriodStart, 'start'),
') OVERLAPS (' $this->makeCase($sqlWalker, $this->firstPeriodEnd, 'end'),
.$this->makeCase($sqlWalker, $this->secondPeriodStart, 'start').', ' $this->makeCase($sqlWalker, $this->secondPeriodStart, 'start'),
.$this->makeCase($sqlWalker, $this->secondPeriodEnd, 'end').')' $this->makeCase($sqlWalker, $this->secondPeriodEnd, 'end')
; );
} }
protected function makeCase($sqlWalker, $part, $position) protected function makeCase($sqlWalker, $part, string $position): string
{ {
//return $part->dispatch($sqlWalker);
switch ($position) { switch ($position) {
case 'start' : case 'start' :
$p = '-infinity'; $p = '-infinity';
@@ -60,28 +45,28 @@ class OverlapsI extends FunctionNode
case 'end': case 'end':
$p = 'infinity'; $p = 'infinity';
break; break;
default:
throw new \Exception('Unexpected position value.');
} }
if ($part instanceof \Doctrine\ORM\Query\AST\PathExpression) { if ($part instanceof PathExpression) {
return 'CASE WHEN ' return sprintf(
.' '.$part->dispatch($sqlWalker).' IS NOT NULL ' "CASE WHEN %s IS NOT NULL THEN %s ELSE '%s'::date END",
. 'THEN '. $part->dispatch($sqlWalker),
$part->dispatch($sqlWalker) $part->dispatch($sqlWalker),
. ' ELSE '. $p
"'".$p."'::date " );
. 'END';
} else {
return 'CASE WHEN '
.' '.$part->dispatch($sqlWalker).'::date IS NOT NULL '
. 'THEN '.
$part->dispatch($sqlWalker)
. '::date ELSE '.
"'".$p."'::date "
. 'END';
}
} }
public function parse(\Doctrine\ORM\Query\Parser $parser) return sprintf(
"CASE WHEN %s::date IS NOT NULL THEN %s::date ELSE '%s'::date END",
$part->dispatch($sqlWalker),
$part->dispatch($sqlWalker),
$p
);
}
public function parse(Parser $parser): void
{ {
$parser->match(Lexer::T_IDENTIFIER); $parser->match(Lexer::T_IDENTIFIER);

View File

@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\Doctrine\Type; namespace Chill\MainBundle\Doctrine\Type;
use Chill\MainBundle\Doctrine\Model\Point; use Chill\MainBundle\Doctrine\Model\Point;
@@ -7,40 +9,32 @@ use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use Chill\MainBundle\Doctrine\Model\PointException; use Chill\MainBundle\Doctrine\Model\PointException;
/** /**
* A Type for Doctrine to implement the Geography Point type * A Type for Doctrine to implement the Geography Point type
* implemented by Postgis on postgis+postgresql databases * implemented by Postgis on postgis+postgresql databases
*
*/ */
class PointType extends Type { class PointType extends Type
{
const POINT = 'point'; public const POINT = 'point';
/** /**
*
* @param array $fieldDeclaration
* @param AbstractPlatform $platform
* @return string * @return string
*/ */
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform) public function getSQLDeclaration(array $column, AbstractPlatform $platform)
{ {
return 'geometry(POINT,'.Point::$SRID.')'; return 'geometry(POINT,'.Point::$SRID.')';
} }
/** /**
*
* @param type $value
* @param AbstractPlatform $platform
* @return ?Point * @return ?Point
*/ */
public function convertToPHPValue($value, AbstractPlatform $platform) public function convertToPHPValue($value, AbstractPlatform $platform)
{ {
if ($value === NULL){ if ($value === NULL){
return NULL; return NULL;
} else {
return Point::fromGeoJson($value);
} }
return Point::fromGeoJson($value);
} }
public function getName() public function getName()
@@ -52,9 +46,9 @@ class PointType extends Type {
{ {
if ($value === NULL){ if ($value === NULL){
return NULL; return NULL;
} else {
return $value->toWKT();
} }
return $value->toWKT();
} }
public function canRequireSQLConversion() public function canRequireSQLConversion()

View File

@@ -23,12 +23,11 @@ namespace Chill\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Annotation as Serializer;
/** /**
* @ORM\Entity * @ORM\Entity
* @ORM\Table(name="centers") * @ORM\Table(name="centers")
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/ */
class Center implements HasCenterInterface class Center implements HasCenterInterface
{ {
@@ -46,8 +45,9 @@ class Center implements HasCenterInterface
* @var string * @var string
* *
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
* @Serializer\Groups({"docgen:read"})
*/ */
private $name; private string $name = '';
/** /**
* @var Collection * @var Collection

View File

@@ -1,22 +1,6 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a suite of a modules, Chill is a software for social workers
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Entity; namespace Chill\MainBundle\Entity;
@@ -28,37 +12,29 @@ use Doctrine\Common\Collections\ArrayCollection;
* @ORM\Entity * @ORM\Entity
* @ORM\Table(name="role_scopes") * @ORM\Table(name="role_scopes")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region") * @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/ */
class RoleScope class RoleScope
{ {
/** /**
* @var integer
*
* @ORM\Id * @ORM\Id
* @ORM\Column(name="id", type="integer") * @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO") * @ORM\GeneratedValue(strategy="AUTO")
*/ */
private $id; private ?int $id = null;
/** /**
* @var string
*
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
*/ */
private $role; private ?string $role = null;
/** /**
* @var Scope
*
* @ORM\ManyToOne( * @ORM\ManyToOne(
* targetEntity="Chill\MainBundle\Entity\Scope", * targetEntity="Chill\MainBundle\Entity\Scope",
* inversedBy="roleScopes") * inversedBy="roleScopes")
* @ORM\JoinColumn(nullable=true, name="scope_id") * @ORM\JoinColumn(nullable=true, name="scope_id")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE") * @ORM\Cache(usage="NONSTRICT_READ_WRITE")
*/ */
private $scope; private ?Scope $scope = null;
/** /**
* @var Collection * @var Collection
@@ -69,55 +45,33 @@ class RoleScope
*/ */
private $permissionsGroups; private $permissionsGroups;
/**
* RoleScope constructor.
*/
public function __construct() { public function __construct() {
$this->new = true;
$this->permissionsGroups = new ArrayCollection(); $this->permissionsGroups = new ArrayCollection();
} }
/** public function getId(): ?int
* @return int
*/
public function getId()
{ {
return $this->id; return $this->id;
} }
/** public function getRole(): ?string
* @return string
*/
public function getRole()
{ {
return $this->role; return $this->role;
} }
/** public function getScope(): ?Scope
* @return Scope
*/
public function getScope()
{ {
return $this->scope; return $this->scope;
} }
/** public function setRole(?string $role = null): self
* @param type $role
* @return RoleScope
*/
public function setRole($role)
{ {
$this->role = $role; $this->role = $role;
return $this; return $this;
} }
/** public function setScope(?Scope $scope = null): self
* @param Scope $scope
* @return RoleScope
*/
public function setScope(Scope $scope = null)
{ {
$this->scope = $scope; $this->scope = $scope;

View File

@@ -8,7 +8,7 @@ use Doctrine\Common\Collections\ArrayCollection;
use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Entity\UserJob;
use Symfony\Component\Security\Core\User\AdvancedUserInterface; use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Serializer\Annotation as Serializer;
/** /**
* User * User
@@ -16,7 +16,7 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
* @ORM\Entity * @ORM\Entity
* @ORM\Table(name="users") * @ORM\Table(name="users")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region") * @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
* @DiscriminatorMap(typeProperty="type", mapping={ * @Serializer\DiscriminatorMap(typeProperty="type", mapping={
* "user"=User::class * "user"=User::class
* }) * })
*/ */
@@ -51,6 +51,7 @@ class User implements AdvancedUserInterface {
/** /**
* @ORM\Column(type="string", length=200) * @ORM\Column(type="string", length=200)
* @Serializer\Groups({"docgen:read"})
*/ */
private string $label = ''; private string $label = '';
@@ -58,8 +59,9 @@ class User implements AdvancedUserInterface {
* @var string * @var string
* *
* @ORM\Column(type="string", length=150, nullable=true) * @ORM\Column(type="string", length=150, nullable=true)
* @Serializer\Groups({"docgen:read"})
*/ */
private $email; private ?string $email = null;
/** /**
* @var string * @var string
@@ -123,6 +125,7 @@ class User implements AdvancedUserInterface {
/** /**
* @var Center|null * @var Center|null
* @ORM\ManyToOne(targetEntity=Center::class) * @ORM\ManyToOne(targetEntity=Center::class)
* @Serializer\Groups({"docgen:read"})
*/ */
private ?Center $mainCenter = null; private ?Center $mainCenter = null;

View File

@@ -1,48 +1,25 @@
<?php <?php
/* declare(strict_types=1);
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Export\Formatter; namespace Chill\MainBundle\Export\Formatter;
use Chill\MainBundle\Export\ExportInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Chill\MainBundle\Export\ExportManager; use Chill\MainBundle\Export\ExportManager;
use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
// command to get the report with curl : curl --user "center a_social:password" "http://localhost:8000/fr/exports/generate/count_person?export[filters][person_gender_filter][enabled]=&export[filters][person_nationality_filter][enabled]=&export[filters][person_nationality_filter][form][nationalities]=&export[aggregators][person_nationality_aggregator][order]=1&export[aggregators][person_nationality_aggregator][form][group_by_level]=country&export[submit]=&export[_token]=RHpjHl389GrK-bd6iY5NsEqrD5UKOTHH40QKE9J1edU" --globoff
/** /**
* * Command to get the report with curl:
* * curl --user "center a_social:password" "http://localhost:8000/fr/exports/generate/count_person?export[filters][person_gender_filter][enabled]=&export[filters][person_nationality_filter][enabled]=&export[filters][person_nationality_filter][form][nationalities]=&export[aggregators][person_nationality_aggregator][order]=1&export[aggregators][person_nationality_aggregator][form][group_by_level]=country&export[submit]=&export[_token]=RHpjHl389GrK-bd6iY5NsEqrD5UKOTHH40QKE9J1edU" --globoff
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* @deprecated this formatter is not used any more. * @deprecated this formatter is not used any more.
*/ */
class CSVFormatter implements FormatterInterface class CSVFormatter implements FormatterInterface
{ {
/** protected TranslatorInterface $translator;
*
* @var TranslatorInterface
*/
protected $translator;
protected $result; protected $result;
@@ -85,11 +62,7 @@ class CSVFormatter implements FormatterInterface
} }
/** /**
*
* @uses appendAggregatorForm * @uses appendAggregatorForm
* @param FormBuilderInterface $builder
* @param type $exportAlias
* @param array $aggregatorAliases
*/ */
public function buildForm(FormBuilderInterface $builder, $exportAlias, array $aggregatorAliases) public function buildForm(FormBuilderInterface $builder, $exportAlias, array $aggregatorAliases)
{ {
@@ -172,31 +145,18 @@ class CSVFormatter implements FormatterInterface
* If two aggregators have the same order, the second given will be placed * If two aggregators have the same order, the second given will be placed
* after. This is not significant for the first ordering. * after. This is not significant for the first ordering.
* *
* @param type $formatterData
* @return type
*/ */
protected function orderingHeaders($formatterData) protected function orderingHeaders(array $formatterData)
{ {
$this->formatterData = $formatterData; $this->formatterData = $formatterData;
uasort($this->formatterData, function($a, $b) { uasort(
$this->formatterData,
return ($a['order'] <= $b['order'] ? -1 : 1); static fn(array $a, array $b): int => ($a['order'] <= $b['order'] ? -1 : 1)
}); );
} }
protected function generateContent() private function findColumnPosition(&$columnHeaders, $columnToFind): int
{ {
$rowKeysNb = count($this->getRowHeaders());
$columnKeysNb = count($this->getColumnHeaders());
$resultsKeysNb = count($this->export->getQueryKeys($this->exportData));
$results = $this->getOrderedResults();
/* @var $columnHeaders string[] the column headers associations */
$columnHeaders = array();
/* @var $data string[] the data of the csv file */
$contentData = array();
$content = array();
function findColumnPosition(&$columnHeaders, $columnToFind) {
$i = 0; $i = 0;
foreach($columnHeaders as $set) { foreach($columnHeaders as $set) {
if ($set === $columnToFind) { if ($set === $columnToFind) {
@@ -211,6 +171,19 @@ class CSVFormatter implements FormatterInterface
return $i++; return $i++;
} }
protected function generateContent()
{
$line = null;
$rowKeysNb = count($this->getRowHeaders());
$columnKeysNb = count($this->getColumnHeaders());
$resultsKeysNb = count($this->export->getQueryKeys($this->exportData));
$results = $this->getOrderedResults();
/* @var $columnHeaders string[] the column headers associations */
$columnHeaders = array();
/* @var $data string[] the data of the csv file */
$contentData = array();
$content = array();
// create a file pointer connected to the output stream // create a file pointer connected to the output stream
$output = fopen('php://output', 'w'); $output = fopen('php://output', 'w');
@@ -244,7 +217,7 @@ class CSVFormatter implements FormatterInterface
// add the column headers // add the column headers
/* @var $columns string[] the column for this row */ /* @var $columns string[] the column for this row */
$columns = array_slice($row, $rowKeysNb, $columnKeysNb); $columns = array_slice($row, $rowKeysNb, $columnKeysNb);
$columnPosition = findColumnPosition($columnHeaders, $columns); $columnPosition = $this->findColumnPosition($columnHeaders, $columns);
//fill with blank at the position given by the columnPosition + nbRowHeaders //fill with blank at the position given by the columnPosition + nbRowHeaders
for ($i=0; $i < $columnPosition; $i++) { for ($i=0; $i < $columnPosition; $i++) {

View File

@@ -229,7 +229,7 @@ class SpreadSheetFormatter implements FormatterInterface
$this->getContentType($this->formatterData['format'])); $this->getContentType($this->formatterData['format']));
$this->tempfile = \tempnam(\sys_get_temp_dir(), ''); $this->tempfile = \tempnam(\sys_get_temp_dir(), '');
$this->generatecontent(); $this->generateContent();
$f = \fopen($this->tempfile, 'r'); $f = \fopen($this->tempfile, 'r');
$response->setContent(\stream_get_contents($f)); $response->setContent(\stream_get_contents($f));

View File

@@ -1,38 +1,15 @@
<?php <?php
/*
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop> declare(strict_types=1);
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Form\Type\DataTransformer; namespace Chill\MainBundle\Form\Type\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\Exception\UnexpectedTypeException;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class DateIntervalTransformer implements DataTransformerInterface class DateIntervalTransformer implements DataTransformerInterface
{ {
/**
*
* @param \DateInterval $value
* @throws UnexpectedTypeException
*/
public function transform($value) public function transform($value)
{ {
if (empty($value)) { if (empty($value)) {
@@ -50,31 +27,36 @@ class DateIntervalTransformer implements DataTransformerInterface
'n' => $value->d / 7, 'n' => $value->d / 7,
'unit' => 'W' 'unit' => 'W'
]; ];
} else { }
return [ return [
'n' => $value->d, 'n' => $value->d,
'unit' => 'D' 'unit' => 'D'
]; ];
} }
} elseif ($value->m > 0) {
if ($value->m > 0) {
return [ return [
'n' => $value->m, 'n' => $value->m,
'unit' => 'M' 'unit' => 'M'
]; ];
} elseif ($value->y > 0) { }
if ($value->y > 0) {
return [ return [
'n' => $value->y, 'n' => $value->y,
'unit' => 'Y' 'unit' => 'Y'
]; ];
} }
throw new TransformationFailedException('the date interval does not ' throw new TransformationFailedException(
. 'contains any days, months or years'); 'The date interval does not contains any days, months or years.'
);
} }
public function reverseTransform($value) public function reverseTransform($value)
{ {
if (empty($value) or empty($value['n'])) { if (empty($value) || empty($value['n'])) {
return null; return null;
} }
@@ -83,10 +65,11 @@ class DateIntervalTransformer implements DataTransformerInterface
try { try {
return new \DateInterval($string); return new \DateInterval($string);
} catch (\Exception $e) { } catch (\Exception $e) {
throw new TransformationFailedException("Could not transform value " throw new TransformationFailedException(
. "into DateInterval", 1542, $e); 'Could not transform value into DateInterval',
1542,
$e
);
} }
} }
} }

View File

@@ -1,44 +1,17 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
* Copyright (C) 2016 Champs-Libres Coopérative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Pagination; namespace Chill\MainBundle\Pagination;
/** /**
* PageGenerator associated with a Paginator * PageGenerator associated with a Paginator.
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
* @author Champs Libres <info@champs-libres.coop>
*/ */
class PageGenerator implements \Iterator class PageGenerator implements \Iterator
{ {
/** protected Paginator $paginator;
*
* @var Paginator
*/
protected $paginator;
/** protected int $current = 1;
*
* @var int
*/
protected $current = 1;
public function __construct(Paginator $paginator) public function __construct(Paginator $paginator)
{ {

View File

@@ -402,3 +402,11 @@ input.belgian_national_number {
&.daily_counter {} &.daily_counter {}
&.control_digit {} &.control_digit {}
} }
// replace abbr
span.item-key {
font-variant: all-small-caps;
font-size: 90%;
background-color: #0000000a;
//text-decoration: dotted underline;
}

View File

@@ -8,3 +8,43 @@ require('./bootstrap.scss');
import Dropdown from 'bootstrap/js/src/dropdown'; import Dropdown from 'bootstrap/js/src/dropdown';
import Modal from 'bootstrap/js/dist/modal'; import Modal from 'bootstrap/js/dist/modal';
import Collapse from 'bootstrap/js/src/collapse'; import Collapse from 'bootstrap/js/src/collapse';
import Carousel from 'bootstrap/js/src/carousel';
//
// ACHeaderSlider is a small slider used in banner of AccompanyingCourse Section
// Initialize options, and show/hide controls in first/last slides
//
let ACHeaderSlider = document.querySelector('#ACHeaderSlider');
if (ACHeaderSlider) {
let controlPrev = ACHeaderSlider.querySelector('button[data-bs-slide="prev"]'),
controlNext = ACHeaderSlider.querySelector('button[data-bs-slide="next"]'),
length = ACHeaderSlider.querySelectorAll('.carousel-item').length,
last = length-1,
carousel = new Carousel(ACHeaderSlider, {
interval: false,
wrap: false,
ride: false,
keyboard: false,
touch: true
})
;
document.addEventListener('DOMContentLoaded', (e) => {
controlNext.classList.remove('visually-hidden');
});
ACHeaderSlider.addEventListener('slid.bs.carousel', (e) => {
//console.log('from slide', e.direction, e.relatedTarget, e.from, e.to );
switch (e.to) {
case 0:
controlPrev.classList.add('visually-hidden');
controlNext.classList.remove('visually-hidden');
break;
case last:
controlPrev.classList.remove('visually-hidden');
controlNext.classList.add('visually-hidden');
break;
default:
controlPrev.classList.remove('visually-hidden');
controlNext.classList.remove('visually-hidden');
}
})
}

View File

@@ -6,12 +6,12 @@ import App from './App.vue';
const i18n = _createI18n(addressMessages); const i18n = _createI18n(addressMessages);
const addAddressInput = (inputs) => { const addAddressInput = (inputs) => {
console.log(inputs)
inputs.forEach(el => { inputs.forEach(el => {
let let
addressId = el.value, addressId = el.value,
uniqid = el.dataset.inputAddress, uniqid = el.dataset.inputAddress,
container = document.querySelector('div[data-input-address-container="' + uniqid + '"]'), container = el.parentNode.querySelector('div[data-input-address-container="' + uniqid + '"]'),
isEdit = addressId !== '', isEdit = addressId !== '',
addressIdInt = addressId !== '' ? parseInt(addressId) : null addressIdInt = addressId !== '' ? parseInt(addressId) : null
; ;

View File

@@ -45,7 +45,8 @@ const messages = {
redirect: { redirect: {
person: "Quitter la page et ouvrir la fiche de l'usager", person: "Quitter la page et ouvrir la fiche de l'usager",
thirdparty: "Quitter la page et voir le tiers", thirdparty: "Quitter la page et voir le tiers",
} },
refresh: 'Rafraîchir'
}, },
nav: { nav: {
next: "Suivant", next: "Suivant",

View File

@@ -58,6 +58,13 @@
{% macro inline(address, options) %} {% macro inline(address, options) %}
{% if options['has_no_address'] == true and address.isNoAddress == true %} {% if options['has_no_address'] == true and address.isNoAddress == true %}
{% if address.postCode is not empty %}
<p class="postcode">
<span class="code">{{ address.postCode.code }}</span>
<span class="name">{{ address.postCode.name }}</span>
</p>
<p class="country">{{ address.postCode.country.name|localize_translatable_string }}</p>
{% endif %}
<span class="noaddress"> <span class="noaddress">
{{ 'address.consider homeless'|trans }} {{ 'address.consider homeless'|trans }}
</span> </span>
@@ -108,9 +115,19 @@
{%- if render == 'bloc' -%} {%- if render == 'bloc' -%}
<div class="chill-entity entity-address"> <div class="chill-entity entity-address">
{% if options['has_no_address'] == true and address.isNoAddress == true %} {% if options['has_no_address'] == true and address.isNoAddress == true %}
{% if address.postCode is not empty %}
<div class="address{% if options['multiline'] %} multiline{% endif %}{% if options['with_delimiter'] %} delimiter{% endif %}">
<p class="postcode">
<span class="code">{{ address.postCode.code }}</span>
<span class="name">{{ address.postCode.name }}</span>
</p>
<p class="country">{{ address.postCode.country.name|localize_translatable_string }}</p>
</div>
{% endif %}
<div class="noaddress"> <div class="noaddress">
{{ 'address.consider homeless'|trans }} {{ 'address.consider homeless'|trans }}
</div> </div>
{% else %} {% else %}
<div class="address{% if options['multiline'] %} multiline{% endif %}{% if options['with_delimiter'] %} delimiter{% endif %}"> <div class="address{% if options['multiline'] %} multiline{% endif %}{% if options['with_delimiter'] %} delimiter{% endif %}">
{% if options['with_picto'] %} {% if options['with_picto'] %}

View File

@@ -10,6 +10,7 @@
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% if form.checkboxes is defined %}
{% if form.checkboxes|length > 0 %} {% if form.checkboxes|length > 0 %}
{% for checkbox_name, options in form.checkboxes %} {% for checkbox_name, options in form.checkboxes %}
<div class="row gx-0"> <div class="row gx-0">
@@ -35,5 +36,6 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endif %}
</div> </div>
{{ form_end(form) }} {{ form_end(form) }}

View File

@@ -5,7 +5,7 @@
<meta http-equiv="x-ua-compatible" content="ie=edge"> <meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ installation.name }} - {% block title %}{% endblock %}</title> <title>{{ installation.name }} - {% block title %}{{ 'Homepage'|trans }}{% endblock %}</title>
<link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon"> <link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon">
{{ encore_entry_link_tags('mod_bootstrap') }} {{ encore_entry_link_tags('mod_bootstrap') }}
@@ -68,6 +68,9 @@
<button type="submit" class="btn btn-lg btn-warning mt-3"> <button type="submit" class="btn btn-lg btn-warning mt-3">
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }} <i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
</button> </button>
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
</a>
</center> </center>
</form> </form>
</div> </div>

View File

@@ -1,22 +1,6 @@
<?php <?php
/* declare(strict_types=1);
* Chill is a software for social workers
* Copyright (C) 2015 Champs-Libres Coopérative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Routing\Loader; namespace Chill\MainBundle\Routing\Loader;
@@ -28,49 +12,31 @@ use Symfony\Component\Routing\RouteCollection;
* *
* Routes must be defined in configuration, add an entry * Routes must be defined in configuration, add an entry
* under `chill_main.routing.resources` * under `chill_main.routing.resources`
*
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/ */
class ChillRoutesLoader extends Loader class ChillRoutesLoader extends Loader
{ {
private $routes; private array $routes;
public function __construct(array $routes) public function __construct(array $routes)
{ {
$this->routes = $routes; $this->routes = $routes;
parent::__construct();
} }
/**
* {@inheritDoc}
*
* @param type $resource
* @param type $type
* @return RouteCollection
*/
public function load($resource, $type = null) public function load($resource, $type = null)
{ {
$collection = new RouteCollection(); $collection = new RouteCollection();
foreach ($this->routes as $resource) { foreach ($this->routes as $routeResource) {
$collection->addCollection( $collection->addCollection(
$this->import($resource, NULL) $this->import($routeResource, NULL)
); );
} }
return $collection; return $collection;
} }
/**
* {@inheritDoc}
*
* @param type $resource
* @param type $type
* @return boolean
*/
public function supports($resource, $type = null) public function supports($resource, $type = null)
{ {
return 'chill_routes' === $type; return 'chill_routes' === $type;

View File

@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace Chill\MainBundle\Routing; namespace Chill\MainBundle\Routing;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
@@ -8,7 +10,6 @@ use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface; use Knp\Menu\ItemInterface;
use Symfony\Component\Translation\TranslatorInterface; use Symfony\Component\Translation\TranslatorInterface;
/** /**
* This class permit to build menu from the routing information * This class permit to build menu from the routing information
* stored in each bundle. * stored in each bundle.
@@ -20,29 +21,15 @@ use Symfony\Component\Translation\TranslatorInterface;
class MenuComposer class MenuComposer
{ {
/** private RouterInterface $router;
*
* @var RouterInterface
*/
private $router;
/** private FactoryInterface $menuFactory;
*
* @var FactoryInterface
*/
private $menuFactory;
/** private TranslatorInterface $translator;
*
* @var TranslatorInterface
*/
private $translator;
/** private array $localMenuBuilders = [];
*
* @var private RouteCollection $routeCollection;
*/
private $localMenuBuilders = [];
function __construct( function __construct(

View File

@@ -78,11 +78,11 @@ abstract class AbstractSearch implements SearchInterface
$recomposed .= ' '.$term.':'; $recomposed .= ' '.$term.':';
$containsSpace = \strpos($terms[$term], " ") !== false; $containsSpace = \strpos($terms[$term], " ") !== false;
if ($containsSpace) { if ($containsSpace) {
$recomposed .= "("; $recomposed .= '"';
} }
$recomposed .= (mb_stristr(' ', $terms[$term]) === FALSE) ? $terms[$term] : '('.$terms[$term].')'; $recomposed .= (mb_stristr(' ', $terms[$term]) === FALSE) ? $terms[$term] : '('.$terms[$term].')';
if ($containsSpace) { if ($containsSpace) {
$recomposed .= ")"; $recomposed .= '"';
} }
} }
} }

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