Compare commits

..

145 Commits

Author SHA1 Message Date
2dcc65d172 unresolved! temporary disable deprecated appendScopeChoices function in reportType form 2023-03-29 13:49:10 +02:00
2d6a0d14eb Fix syntax in ReportController 2023-03-29 13:48:24 +02:00
fcb057c55b resolve center in Report Controller 2023-03-29 13:47:34 +02:00
96ddc73e45 Feature: [calendar sync msgraph] Allow to show the calendar details if the user does not have msgraph mapping
We first check that there are "msgraph" attributes before forcing redirection to MSGraph authorization. If not, we let's the user see the calendar edit/create page.
2023-03-23 12:48:37 +01:00
4eb7d10e45 Fixed: [calendar ms synchro] do not throw an error if we are not allowed
to get the default calendar
2023-03-23 11:23:14 +01:00
efa475df0f Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2023-03-22 09:06:12 +01:00
a8977729fe Fixed: [similar person matcher] properly takes person center into account
Center comes from the table person_center_history, not person.center_id
2023-03-21 16:39:31 +01:00
ecac409586 fixed: fix 3party syntax 2023-03-20 21:38:42 +01:00
df2480c47c Fixed: transform null value into emtpy string into ThirdParty::setProfession 2023-03-20 21:36:46 +01:00
c729a14304 FIX [minor] remove dump, use statement, ... 2023-03-20 07:54:16 +01:00
06b7e84270 FIX [thirdparty][profession] set default value for profession in symfony form 2023-03-16 13:48:01 +01:00
1cc80c8e6a Fixed: takes all activity into account to check social issue consistency 2023-03-16 00:12:41 +01:00
c3558beee1 Fixed: add a social issue on an activity to an accompanying period 2023-03-16 00:12:09 +01:00
44ecad2bca Fixed: re-introduce creator in async doc generation 2023-03-15 13:38:19 +01:00
d1bdf41c4c Feature: Force language when converting documents 2023-03-15 13:36:41 +01:00
4a30f310b8 FIX [referrer][deselect] allow referrer to be deselected in parcours edit form 2023-03-13 16:41:43 +01:00
896b4cdfe3 FIX [regroupment][datamapper] add condition to check if key exists in forms array 2023-03-10 15:36:18 +01:00
a272dabcaf Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2023-03-10 12:04:10 +01:00
3901fe2d32 FIX [translations][dutch] change some dutch translations 2023-03-10 12:03:33 +01:00
78858e84f2 Fixed: force a name when downloading a document without filename
see https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/1005
2023-03-09 18:01:39 +01:00
cc98f64be5 Merge branch 'issue702_document_title_activity_listing' into 'master'
Show document titles in activity listing

See merge request Chill-Projet/chill-bundles!498
2023-03-09 16:49:39 +00:00
6e812b54e1 Fixed: add small button and ACL on document edit 2023-03-09 17:47:52 +01:00
83e0a50b57 Fixed: do not alter layout for bootstrap 2023-03-09 17:47:32 +01:00
3aac4d5d35 FEATURE add download button to document listing 2023-03-09 17:41:31 +01:00
23cee274a5 FEATURE [documents][listing] show document titles in activity listing if there are any and user has see_detail rights 2023-03-09 17:41:31 +01:00
aacb54037b Merge branch '56-user-badges' into 'master'
Fixed: uniformize user badge #56

Closes #56

See merge request Chill-Projet/chill-bundles!503
2023-03-09 16:09:28 +00:00
57cb96320c Merge branch '54-display-action-button' into 'master'
Fixed: graphical bug: the download action button is overflowed #54

See merge request Chill-Projet/chill-bundles!501
2023-03-09 15:54:59 +00:00
5a2d80cb4d Merge branch '721-bug-location' into 'master'
Bug on Location admin page

See merge request Chill-Projet/chill-bundles!505
2023-03-09 14:26:32 +00:00
2e822a9486 Fix bug on admin Location Page with availableForUsers value 2023-03-09 12:35:29 +01:00
f376b1af49 UX: [badges] uniformize user badges in lists, shows and dashboards 2023-03-07 16:23:41 +01:00
dd621186e8 improve 2023-03-06 18:07:06 +01:00
1b15abe635 oops.. with condition 2023-03-06 17:59:55 +01:00
1965fc55f4 Fixed button DocumentActionsButtonsGroup display bug
- improve document table
- add a smallfont css classe to handle table style
when called from index or from show page
2023-03-06 17:47:33 +01:00
a9290eb3fe Merge branch 'issue704_private_comment_listing' into 'master'
Show private comment in activity listing

See merge request Chill-Projet/chill-bundles!497
2023-03-06 12:41:37 +00:00
e78eb8789d FIX [private comment] show private comment in activity listings 2023-03-06 13:39:34 +01:00
9911112e08 Merge branch 'issue711_confirm_referrer' into 'master'
Confirmation popup when referrer is changed for parcours

See merge request Chill-Projet/chill-bundles!496
2023-03-06 12:30:30 +00:00
c0675aee9b Fixed: set referrer from suggestion 2023-03-06 13:28:47 +01:00
03ee04978c FEATURE [confirm][popup] Add a popup asking for confirmation when referrer is changed to prevent mistakes and unecessary notifications 2023-03-06 13:20:10 +01:00
15d68df8c6 Merge branch 'issue716_change_field_thirdparty' into 'master'
Change profession field into string type

See merge request Chill-Projet/chill-bundles!492
2023-03-06 12:12:29 +00:00
1b2c0ecc87 fixed: use TEXT instead of VARCHAR to store profession 2023-03-06 13:01:22 +01:00
f15017ebd7 FIX don't delete profession_id column yet 2023-03-06 13:01:22 +01:00
9a56a1b115 FEATURE [migration] migrate the old profession_id to inserting the corresponding string into new profession column string type 2023-03-06 13:01:22 +01:00
11e7f2179c FIX [obsolete] remove obsolete code since thirdparty entity will be removed 2023-03-06 13:01:22 +01:00
e982e81900 FIX [post] adjust vue components to allow thirdparty to be posted with new profession type as string 2023-03-06 13:01:21 +01:00
e50b02a8c7 FIX [rendering] adjust vue components to correctly render profession string 2023-03-06 13:01:21 +01:00
bea839663f FEATURE [form] adjust symfony form to use text type 2023-03-06 13:01:21 +01:00
ac4c821290 FEATURE [profession] change field type of profession into string 2023-03-06 13:01:20 +01:00
c953da3fd0 Merge branch 'issue715_household_move_email' into 'master'
Issue715 household move email

See merge request Chill-Projet/chill-bundles!495
2023-03-06 11:38:13 +00:00
f5d17eb38c Merge remote-tracking branch 'origin/master' into issue715_household_move_email 2023-03-06 12:20:20 +01:00
73f332927d FIX [personMatcher] wrong syntax was used to setParameter for query in similar person matcher 2023-03-02 11:45:41 +01:00
ef75deda26 FIX [regroupment] remove CenterCompilerPass - no longer in use 2023-03-02 11:23:29 +01:00
6264a95d62 Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2023-03-02 11:12:12 +01:00
26a6169b95 FIX [budget][calculator] fix method call getAlias() 2023-03-02 11:11:40 +01:00
678defdee7 FIX [regroupment][form] add check to display regroupment field in the form or not. Only if regroupments exist 2023-03-02 10:52:48 +01:00
88ccbd450a Merge branch 'integrate_regroupment_entity' into 'master'
Feature: ask for center regroupment in exports

See merge request Chill-Projet/chill-bundles!486
2023-03-01 16:13:32 +00:00
62532e0a90 Fixes for feature "Regroupment of center"
* allow more than 15 character in regroupment name
* remove unused methods in PickCenterType
* show only active Regroupment in form
* remove dead code and commented code
2023-03-01 17:11:29 +01:00
73fa585707 Merge remote-tracking branch 'origin/master' into integrate_regroupment_entity 2023-03-01 16:50:53 +01:00
21a16dcbe2 Merge branch 'user_absences' into 'master'
Feature: allow users to say they are out of office

See merge request Chill-Projet/chill-bundles!476
2023-03-01 15:40:22 +00:00
b30e966316 DX: [absence] Remove unneccessary $em->persist() 2023-03-01 16:32:20 +01:00
9696a8194c DX: use the UserRenderBoxBadge to display user in AddPerson 2023-03-01 16:10:51 +01:00
6749758b46 Fixes for features "allow to set user absences" 2023-03-01 16:08:49 +01:00
f1ebc089c3 Merge remote-tracking branch 'origin/master' into user_absences 2023-03-01 14:59:43 +01:00
813adc70f4 Merge branch '52-repair-fixtures' into 'master'
Repair fixtures

See merge request Chill-Projet/chill-bundles!494
2023-03-01 13:57:21 +00:00
95984eff6d Apply 1 suggestion(s) to 1 file(s) 2023-03-01 13:55:35 +00:00
3db5b62d57 Feature: [UI] use download button group in calendar list 2023-02-28 22:37:46 +01:00
cf1cc937ca DX: ignore file in .cache directory (in use with php82 branch) 2023-02-28 22:37:00 +01:00
14df8fe9ad DX: remove implicit creation of a variable 2023-02-28 22:09:07 +01:00
fe4388c884 Feature: [docgen] implements normalization of data on CalendarContext 2023-02-28 22:01:52 +01:00
dbcc425f5f DX: improve performance for counting feature linked to person 2023-02-28 17:22:54 +01:00
77c545344c DX: adapt to php 7.4 2023-02-28 16:44:37 +01:00
c13d672db2 Merge branch '49-decouple-docgen' into 'master'
Resolve "Génération de document: mémoire disponible dépassée"

Closes #49

See merge request Chill-Projet/chill-bundles!493
2023-02-28 15:25:47 +00:00
a16244a3f5 Feature: [docgen] generate documents in an async queue
The documents are now generated in a queue, using symfony messenger. This queue should be configured:

```yaml
# app/config/messenger.yaml
framework:
    messenger:
        # reset services after consuming messages
        # reset_on_message: true

        failure_transport: failed

        transports:
            # https://symfony.com/doc/current/messenger.html#transport-configuration
            async: '%env(MESSENGER_TRANSPORT_DSN)%'
            priority:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
            failed: 'doctrine://default?queue_name=failed'

        routing:
            # ... other messages
            'Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationMessage': priority
```

`StoredObject`s now have additionnal properties:

* status (pending, failure, ready (by default) ), which explain if the document is generated;
* a generationTrialCounter, which is incremented on each generation trial, which prevent each generation more than 5 times;

The generator computation is moved from the `DocGenTemplateController` to a `Generator` (implementing `GeneratorInterface`. 

There are new methods to `Context` which allow to normalize/denormalize context data to/from a messenger's `Message`.
2023-02-28 15:25:47 +00:00
27f13e0dd1 FEATURE [menu][counters] show counter icon for person resources and residential address if there are any 2023-02-24 13:54:44 +01:00
f07ea3259e php cs fixes 2023-02-22 11:54:03 +01:00
1f4438690e FEATURE [email] add a line notifying referrer which user has moved a person or household 2023-02-22 11:36:57 +01:00
744b62184a fix LoadHousehold fixture 2023-02-17 14:48:02 +01:00
5ee0ab5ab8 fix LoadInvite fixture (CalendarBundle) 2023-02-17 13:32:33 +01:00
4370349f10 fix LoadCalendarRange fixture 2023-02-17 13:32:05 +01:00
6254303392 Revert "Feature: [docgen][stored object] update model to store status, template_id and fix defaults"
This reverts commit 55918bcafb.
2023-02-16 14:08:16 +01:00
9676975cd8 Revert "Feature: [docgen][stored object] handler for request generator and required fixes"
This reverts commit 91d21ba939.
2023-02-16 14:08:11 +01:00
7c4bc8f46a Revert commits linked to decoupling of docgen (changes not reviewed)
This reverts commit 55a845fcd6.
2023-02-16 14:07:47 +01:00
55a845fcd6 Sanitize DocGeneratorTemplateController constructor 2023-02-14 23:34:21 +01:00
91d21ba939 Feature: [docgen][stored object] handler for request generator and required fixes 2023-02-14 23:26:00 +01:00
55918bcafb Feature: [docgen][stored object] update model to store status, template_id and fix defaults 2023-02-14 20:57:34 +01:00
bb05ba0f17 Feature: [docgen] create a service to generate a document from a template 2023-02-14 19:35:28 +01:00
eac3471cbb FIX [person][contactinfo] place the contactinfo fields underneath birthdate for easier access 2023-02-10 11:37:32 +01:00
f653f8fd7a FIX [translations][wording] make distinction between parties concernees for an activity and an appointment in the translations 2023-02-10 11:30:26 +01:00
6d2c6fb6e1 FIX [renaming][translation] changing a translation for concerned groups in activities 2023-02-10 10:51:16 +01:00
aea6272c4d Merge improvements for admin's budget bundle 2023-02-09 18:20:33 +01:00
25cbb528ec Fixed: [budget] in admin list, show pagination and kind in resource_kind and charge_kind pages 2023-02-09 18:18:30 +01:00
2d013e110a Fixed: [budget] force budget kind and resource kind to have a unique kind field in database 2023-02-09 18:08:44 +01:00
d6df16973a Merge branch '693-filter-acp-by-user-job' into 'master'
693 on doit utiliser le métier du référent, et pas le métier du parcours

See merge request Chill-Projet/chill-bundles!482
2023-02-08 15:59:43 +00:00
80835dd7c3 Fixed: [export][filter by user's job] take into account the job associated to the user instead of the one associated to the course 2023-02-08 15:59:42 +00:00
9e63480c70 Merge remote-tracking branch 'origin/master' into 693-filter-acp-by-user-job 2023-02-08 16:44:50 +01:00
988495df27 Merge branch '697-add-filter-location' into 'master'
697 Filtrer les échanges par localisation

See merge request Chill-Projet/chill-bundles!481
2023-02-08 15:42:44 +00:00
cc62c9cc4a Feature: [export][activity] Add filter on localisation for activities 2023-02-08 15:42:44 +00:00
40924d9d39 Merge branch '698-filter-correction' into 'master'
698 Filtrer les actions par agent traitant: utiliser le formulaire avec AddPersons, et pas un EntityType

See merge request Chill-Projet/chill-bundles!480
2023-02-08 15:39:29 +00:00
8b505410ca Fixed: [export] filter referrer using dynamic picker in filter "agent traitant" for social works 2023-02-08 15:39:29 +00:00
d535ec6cfb Merge branch '676-docgen-center' into 'master'
676 fix docgen person center

See merge request Chill-Projet/chill-bundles!479
2023-02-08 15:22:09 +00:00
9029426d03 Feature: [docgen] add center when normalizing person 2023-02-08 15:22:08 +00:00
9ae2e51819 Merge branch '669-no-limit-for-location-endpoint' into 'master'
669 no limit for location endpoint

See merge request Chill-Projet/chill-bundles!478
2023-02-08 14:42:16 +00:00
68f7a832b4 Fixed: [activity] fetch all the available location, beyond the first page 2023-02-08 14:42:16 +00:00
af5f27ff49 Merge branch 'exports_activite_annexe' into 'master'
Feature: exports for aside activities

See merge request Chill-Projet/chill-bundles!485
2023-02-08 14:28:21 +00:00
5e58d36e79 Feature: exports for aside activities 2023-02-08 14:28:21 +00:00
b0ab591cbd Feature: [Document action buttons] do now show "Editer en ligne" for document which are not editable 2023-02-07 16:50:07 +01:00
d8af7d455e Fixed: [doc generation] fix summary budget 2023-02-07 16:22:26 +01:00
5830c3e177 Feature: [doc generation] show all the deps of the tree for debug information 2023-02-07 15:49:57 +01:00
1285100801 Merge branch 'integrate_regroupment_entity' of gitlab.com:Chill-Projet/chill-bundles into integrate_regroupment_entity 2023-02-07 15:49:08 +01:00
a4e21b7834 FEATURE [datamapper][regroupment] minor fix 2023-02-07 15:42:58 +01:00
1a44a516c2 FEATURE [datamapper][regroupment] make the datamapper work 2023-02-07 15:42:58 +01:00
fb9b9b9226 FEATURE [datamapper][regroupment] moved datamapper to seperate class. Still not working 2023-02-07 15:42:58 +01:00
0ace1c1f6a FEATURE [regroupment][form] integrate in regroupment admin entity into PickCenterType, datamapping not working 2023-02-07 15:42:58 +01:00
7d80507517 FEATURE [regroupment][exports] first commit to implement regroupment entity in exports 2023-02-07 15:42:58 +01:00
b5ec0919e7 FEATURE [datamapper][regroupment] minor fix 2023-02-07 15:41:28 +01:00
068311d071 FEATURE [datamapper][regroupment] make the datamapper work 2023-02-07 15:36:57 +01:00
88eefa698b Fixed: add string key for summary of charges and resources into doc generation 2023-02-07 15:09:40 +01:00
5756a37178 FEATURE [datamapper][regroupment] moved datamapper to seperate class. Still not working 2023-02-07 10:23:19 +01:00
c83e8ad9a4 FEATURE [regroupment][form] integrate in regroupment admin entity into PickCenterType, datamapping not working 2023-02-03 12:29:38 +01:00
5bfd2aefe6 DX: [html] change deprecated tag 2023-01-30 15:20:13 +01:00
2165e04ec3 Fixed: [template] integration of absence alert box 2023-01-30 15:16:38 +01:00
1a8e21a77f Fixed: [vue] title hover translation for absent pill 2023-01-30 15:01:24 +01:00
50bb8f10cf FEATURE [admin] add absence in user index admin 2023-01-27 12:25:05 +01:00
1c673db628 FIX [phpcsfixer][phpstan] 2023-01-27 12:18:19 +01:00
fa8a2c5cc5 FIX remove some whitespaces or empty lines + add btn in absence message homepage (still commented out) 2023-01-27 12:04:11 +01:00
d46304e229 FEATURE [banner][render] display the A icon within accourse banner if user is absent instead of string 2023-01-27 12:03:12 +01:00
4dd81da1ef FIX [routes][redirect] fix route naming and redirection after delete action 2023-01-27 11:23:29 +01:00
882e72b609 FIX fix condition to show form or not + sticky button for delete 2023-01-27 11:15:59 +01:00
bb7d072cc8 FIX [render] use isAbsent method to render user as absent or not + place homepage msg above searchbar 2023-01-27 11:10:17 +01:00
f76c031ff3 FIX [translations][types] remove redundant translator and change datetime field to date field 2023-01-27 10:51:59 +01:00
86b5f4dfac FIX [template][translations] fixes to template and translations 2023-01-27 10:44:25 +01:00
ded71c5997 Fixed: [migration] fix the required comment for doctrine on new column 2023-01-26 17:11:50 +01:00
5ae4eb1bf7 Merge branch 'master' into user_absences 2023-01-26 17:09:54 +01:00
2f091a639b remove unused private function 2023-01-26 11:52:45 +01:00
7f9e045d5d FEATURE [regroupment][exports] first commit to implement regroupment entity in exports 2023-01-26 11:28:13 +01:00
8e3a83de85 DX: [export] Rename JobAggregator to more logical UserJobAggregator 2023-01-25 12:03:39 +01:00
050a4feab5 Fix: [export][aggregator] group by user job, not by job 2023-01-25 11:55:13 +01:00
de9d53936f FEATURE [styling][absence] give styling 2023-01-12 19:51:43 +01:00
6c1108b8aa FEATURE: [homepage][absence] display message to let user know they are still marked as absent 2023-01-12 13:50:29 +01:00
5bbe5af124 FEATURE [absence][render] add absence tag to renderbox and renderstring 2023-01-12 13:30:28 +01:00
2c5c815f68 FEATURE [admin][absence] add possibility for admin to set absence of a user 2023-01-12 12:02:52 +01:00
44ef21f940 FEATURE [delete][absence] add functionality to unset absence 2023-01-12 11:51:42 +01:00
68998c9156 FEATURE [translations][absence] add translations 2023-01-12 11:50:06 +01:00
b93b78615b FIX [migration][absence] fix the typing in db for absence datetime immuatable 2023-01-12 11:12:32 +01:00
b2924ede70 FEAUTURE [routing][absence] use routing annotation instead of config file 2023-01-12 11:10:41 +01:00
fb51e44e45 FEATURE [absence][in_progress] add absence property to user, create form, controller, template, migration and menu entry 2023-01-11 17:16:37 +01:00
251 changed files with 4611 additions and 8340 deletions

1
.gitignore vendored
View File

@@ -5,6 +5,7 @@ composer.lock
docs/build/
node_modules/*
.php_cs.cache
.cache/*
###> symfony/framework-bundle ###
/.env.local

View File

@@ -9,24 +9,13 @@ declare(strict_types=1);
* the LICENSE file that was distributed with this source code.
*/
$finder = PhpCsFixer\Finder::create();
$config = require __DIR__ . '/tests/app/vendor/drupol/php-conventions/config/php73/php_cs_fixer.config.php';
$finder
->in(__DIR__.'/src')
->append([__FILE__])
->exclude(['docs/', 'tests/app'])
->notPath('tests/app')
->ignoreDotFiles(true)
->name('**.php')
;
$config = new PhpCsFixer\Config();
$config
->setFinder($finder)
->setRiskyAllowed(true)
->setCacheFile('.php_cs.cache')
->setUsingCache(true)
;
->getFinder()
->ignoreDotFiles(false)
->notPath('tests/app')
->name(['.php_cs.dist.php']);
$rules = $config->getRules();
@@ -79,15 +68,9 @@ $riskyRules = [
// 'psr_autoloading' => false,
];
$untilFullSwitchToPhp8 = [
'blank_line_between_import_groups' => false,
'declare_strict_types' => true,
];
$rules = array_merge(
$rules,
$riskyRules,
$untilFullSwitchToPhp8,
$riskyRules
);
$rules['header_comment']['header'] = trim(file_get_contents(__DIR__ . '/resource/header.txt'));

View File

@@ -8,7 +8,7 @@
"social worker"
],
"require": {
"php": "^7.4|^8.2",
"php": "^7.4",
"ext-json": "*",
"ext-openssl": "*",
"ext-redis": "*",
@@ -64,24 +64,18 @@
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3",
"drupol/php-conventions": "^5",
"fakerphp/faker": "^1.13",
"jangregor/phpstan-prophecy": "^1.0",
"nelmio/alice": "^3.8",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.9",
"phpstan/phpstan-deprecation-rules": "^1.1",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": ">= 7.5",
"psalm/plugin-phpunit": "^0.18.4",
"psalm/plugin-symfony": "^4.0.2",
"symfony/debug-bundle": "^5.1",
"symfony/dotenv": "^4.4",
"symfony/maker-bundle": "^1.20",
"symfony/phpunit-bridge": "^4.4",
"symfony/stopwatch": "^4.4",
"symfony/var-dumper": "^4.4",
"vimeo/psalm": "^4.30.0"
"symfony/var-dumper": "^4.4"
},
"conflict": {
"symfony/symfony": "*"

40
grumphp.yml Normal file
View File

@@ -0,0 +1,40 @@
imports:
- {
resource: tests/app/vendor/drupol/php-conventions/config/php73/grumphp.yml,
}
parameters:
tasks.phpcsfixer.config: .php_cs.dist.php
tasks.license.name: AGPL-3.0
tasks.license.holder: Champs-Libres
tasks.license.date_from: 2001
tasks.phpcsfixer.allow_risky: true
tasks.phpcsfixer.diff: true
tasks.phpstan.level: 1
tasks.phpstan.blocking: true
tasks.phpstan.ignore_patterns:
- "/.github/"
- "/.idea/"
- "/build/"
- "/benchmarks/"
- "/docs/"
- "/node_modules/"
- "/resource/"
- "/spec/"
- "/var/"
- "/vendor/"
- "/tests/app"
# Psalm
tasks.psalm.blocking: true
tasks.psalm.ignore_patterns:
- "/.github/"
- "/.idea/"
- "/build/"
- "/benchmarks/"
- "/node_modules/"
- "/resource/"
- "/spec/"
- "/var/"
- "/vendor/"

View File

@@ -1,306 +0,0 @@
parameters:
ignoreErrors:
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\ActivityBundle\\\\Entity\\\\Activity\\|null given\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Controller/ActivityController.php
-
message: "#^Only booleans are allowed in a ternary operator condition, Chill\\\\MainBundle\\\\Entity\\\\Scope\\|null given\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Controller/ActivityController.php
-
message: "#^Only booleans are allowed in a ternary operator condition, Chill\\\\PersonBundle\\\\Entity\\\\Person\\|null given\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Controller/ActivityController.php
-
message: "#^Only booleans are allowed in a ternary operator condition, DateTime\\|null given\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Controller/ActivityController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\ActivityBundle\\\\Entity\\\\ActivityReasonCategory\\|null given\\.$#"
count: 3
path: src/Bundle/ChillActivityBundle/Controller/ActivityReasonCategoryController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\ActivityBundle\\\\Entity\\\\ActivityReason\\|null given\\.$#"
count: 2
path: src/Bundle/ChillActivityBundle/Controller/ActivityReasonController.php
-
message: "#^Call to method DateTime\\:\\:setTimezone\\(\\) with incorrect case\\: setTimeZone$#"
count: 1
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Only booleans are allowed in &&, Chill\\\\PersonBundle\\\\Entity\\\\AccompanyingPeriod\\|null given on the right side\\.$#"
count: 2
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Only booleans are allowed in an if condition, Chill\\\\AsideActivityBundle\\\\Entity\\\\AsideActivityCategory\\|null given\\.$#"
count: 1
path: src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivityCategory.php
-
message: "#^Call to method DateTimeImmutable\\:\\:setTimezone\\(\\) with incorrect case\\: setTimeZone$#"
count: 1
path: src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php
-
message: "#^Only booleans are allowed in a ternary operator condition, Chill\\\\MainBundle\\\\Entity\\\\Location\\|null given\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/Controller/CalendarController.php
-
message: "#^Only booleans are allowed in an if condition, Symfony\\\\Component\\\\Validator\\\\ConstraintViolationListInterface given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Command/CreateFieldsOnGroupCommand.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\CustomFieldsBundle\\\\Entity\\\\CustomFieldsGroup\\|null given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\CustomFieldsBundle\\\\Entity\\\\CustomField\\|null given\\.$#"
count: 3
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldController.php
-
message: "#^Only booleans are allowed in a ternary operator condition, Chill\\\\CustomFieldsBundle\\\\Entity\\\\CustomFieldsGroup given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\CustomFieldsBundle\\\\Entity\\\\CustomFieldsGroup\\|null given\\.$#"
count: 4
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
-
message: "#^Only booleans are allowed in a ternary operator condition, string given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/CustomFields/CustomFieldLongChoice.php
-
message: "#^Only booleans are allowed in a negated boolean, string given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Form/DataTransformer/CustomFieldsGroupToIdTransformer.php
-
message: "#^Call to method Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:getFirstName\\(\\) with incorrect case\\: getFirstname$#"
count: 1
path: src/Bundle/ChillEventBundle/Controller/EventController.php
-
message: "#^Call to method Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:getLastName\\(\\) with incorrect case\\: getLastname$#"
count: 1
path: src/Bundle/ChillEventBundle/Controller/EventController.php
-
message: "#^Call to method Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:getMobilenumber\\(\\) with incorrect case\\: getMobileNumber$#"
count: 1
path: src/Bundle/ChillEventBundle/Controller/EventController.php
-
message: "#^Call to method Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:getPhonenumber\\(\\) with incorrect case\\: getPhoneNumber$#"
count: 1
path: src/Bundle/ChillEventBundle/Controller/EventController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\EventBundle\\\\Entity\\\\Event given\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Controller/EventController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\EventBundle\\\\Entity\\\\Event\\|null given\\.$#"
count: 3
path: src/Bundle/ChillEventBundle/Controller/EventController.php
-
message: "#^Only booleans are allowed in a ternary operator condition, int given\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Controller/EventController.php
-
message: "#^Only numeric types are allowed in pre\\-increment, string given\\.$#"
count: 2
path: src/Bundle/ChillEventBundle/Controller/EventController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\EventBundle\\\\Entity\\\\EventType\\|null given\\.$#"
count: 4
path: src/Bundle/ChillEventBundle/Controller/EventTypeController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\EventBundle\\\\Entity\\\\Participation\\|null given\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Controller/ParticipationController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\EventBundle\\\\Entity\\\\Role\\|null given\\.$#"
count: 4
path: src/Bundle/ChillEventBundle/Controller/RoleController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\EventBundle\\\\Entity\\\\Status\\|null given\\.$#"
count: 4
path: src/Bundle/ChillEventBundle/Controller/StatusController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\MainBundle\\\\Entity\\\\Language\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/LoadAndUpdateLanguagesCommand.php
-
message: "#^Only booleans are allowed in an if condition, Chill\\\\MainBundle\\\\Entity\\\\Language\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/LoadAndUpdateLanguagesCommand.php
-
message: "#^Only booleans are allowed in an if condition, int given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/LoadPostalCodesCommand.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\MainBundle\\\\Entity\\\\Center\\|null given\\.$#"
count: 3
path: src/Bundle/ChillMainBundle/Controller/CenterController.php
-
message: "#^Call to method Redis\\:\\:setex\\(\\) with incorrect case\\: setEx$#"
count: 2
path: src/Bundle/ChillMainBundle/Controller/ExportController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\MainBundle\\\\Entity\\\\PermissionsGroup\\|null given\\.$#"
count: 5
path: src/Bundle/ChillMainBundle/Controller/PermissionsGroupController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\MainBundle\\\\Entity\\\\RoleScope\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/PermissionsGroupController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\MainBundle\\\\Entity\\\\Scope\\|null given\\.$#"
count: 3
path: src/Bundle/ChillMainBundle/Controller/ScopeController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\MainBundle\\\\Entity\\\\GroupCenter\\|null given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Controller/UserController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\MainBundle\\\\Entity\\\\User\\|null given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Controller/UserController.php
-
message: "#^Only booleans are allowed in an if condition, int given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadPostalCodes.php
-
message: "#^Only numeric types are allowed in pre\\-increment, string given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php
-
message: "#^Only booleans are allowed in an if condition, int given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Form/DataTransformer/IdToEntityDataTransformer.php
-
message: "#^Only booleans are allowed in a ternary operator condition, string\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/PickUserLocationType.php
-
message: "#^Only booleans are allowed in a ternary operator condition, int\\<0, max\\> given\\.$#"
count: 4
path: src/Bundle/ChillMainBundle/Search/SearchApiQuery.php
-
message: "#^Call to method Chill\\\\MainBundle\\\\Entity\\\\Address\\:\\:getPostcode\\(\\) with incorrect case\\: getPostCode$#"
count: 6
path: src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php
-
message: "#^Only booleans are allowed in an if condition, Symfony\\\\Component\\\\Serializer\\\\Mapping\\\\ClassDiscriminatorMapping\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Serializer/Normalizer/DoctrineExistingEntityNormalizer.php
-
message: "#^Call to method Chill\\\\MainBundle\\\\Entity\\\\Address\\:\\:getPostcode\\(\\) with incorrect case\\: getPostCode$#"
count: 6
path: src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php
-
message: "#^Only booleans are allowed in a negated boolean, null given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/Person.php
-
message: "#^Only booleans are allowed in a ternary operator condition, Chill\\\\PersonBundle\\\\Entity\\\\AccompanyingPeriodParticipation\\|null given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/Person.php
-
message: "#^Call to method Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:getFirstName\\(\\) with incorrect case\\: getFirstname$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PickPersonType.php
-
message: "#^Call to method Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:getLastName\\(\\) with incorrect case\\: getLastname$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PickPersonType.php
-
message: "#^Only booleans are allowed in an if condition, int\\<0, max\\> given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Search/SimilarPersonMatcher.php
-
message: "#^Only booleans are allowed in &&, null given on the left side\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/MembersEditorNormalizer.php
-
message: "#^Only booleans are allowed in a ternary operator condition, Chill\\\\PersonBundle\\\\Entity\\\\Household\\\\Household\\|null given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonNormalizer.php
-
message: "#^Only booleans are allowed in a ternary operator condition, array\\<Chill\\\\PersonBundle\\\\Entity\\\\Person\\\\ResidentialAddress\\> given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonNormalizer.php
-
message: "#^Call to method Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:getDeathdate\\(\\) with incorrect case\\: getDeathDate$#"
count: 2
path: src/Bundle/ChillPersonBundle/Templating/Entity/PersonRender.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\PersonBundle\\\\Entity\\\\Person\\|null given\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Controller/ReportController.php
-
message: "#^Only booleans are allowed in a negated boolean, Chill\\\\ReportBundle\\\\Entity\\\\Report given\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Controller/ReportController.php
-
message: "#^Call to method Chill\\\\ThirdPartyBundle\\\\Entity\\\\ThirdParty\\:\\:setFirstname\\(\\) with incorrect case\\: setFirstName$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/EventListener/ThirdPartyEventListener.php
-
message: "#^Dynamic call to static method Chill\\\\ThirdPartyBundle\\\\ThirdPartyType\\\\ThirdPartyTypeProviderInterface\\:\\:getKey\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/ThirdPartyType/ThirdPartyTypeManager.php

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,721 +0,0 @@
parameters:
ignoreErrors:
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Controller/ActivityController.php
-
message: "#^Parameter \\#1 \\$context of method Chill\\\\ActivityBundle\\\\Timeline\\\\TimelineActivityProvider\\:\\:checkContext\\(\\) expects string, Chill\\\\MainBundle\\\\Timeline\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillActivityBundle/Timeline/TimelineActivityProvider.php
-
message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(Chill\\\\BudgetBundle\\\\Repository\\\\ChargeType\\)\\: mixed\\)\\|null, Closure\\(Chill\\\\BudgetBundle\\\\Entity\\\\ChargeKind\\)\\: int\\|null given\\.$#"
count: 1
path: src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php
-
message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(Chill\\\\BudgetBundle\\\\Repository\\\\ResourceType\\)\\: mixed\\)\\|null, Closure\\(Chill\\\\BudgetBundle\\\\Entity\\\\ResourceKind\\)\\: int\\|null given\\.$#"
count: 1
path: src/Bundle/ChillBudgetBundle/Service/Summary/SummaryBudget.php
-
message: "#^Parameter \\#2 \\$byUser of class Chill\\\\CalendarBundle\\\\Messenger\\\\Message\\\\InviteUpdateMessage constructor expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/Controller/InviteApiController.php
-
message: "#^Parameter \\#1 \\$name of method Chill\\\\MainBundle\\\\Entity\\\\Country\\:\\:setName\\(\\) expects string, array\\<string, string\\> given\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadCalendarRange.php
-
message: "#^Parameter \\#1 \\$title of method Chill\\\\MainBundle\\\\Entity\\\\LocationType\\:\\:setTitle\\(\\) expects array, string given\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadCalendarRange.php
-
message: "#^Parameter \\#1 \\$status of method Chill\\\\CalendarBundle\\\\Entity\\\\Invite\\:\\:setStatus\\(\\) expects string, array\\<string, string\\> given\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadInvite.php
-
message: "#^Parameter \\#2 \\$byUser of class Chill\\\\CalendarBundle\\\\Messenger\\\\Message\\\\CalendarRemovedMessage constructor expects Chill\\\\MainBundle\\\\Entity\\\\User\\|null, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/Messenger/Doctrine/CalendarEntityListener.php
-
message: "#^Parameter \\#3 \\$byUser of class Chill\\\\CalendarBundle\\\\Messenger\\\\Message\\\\CalendarMessage constructor expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 2
path: src/Bundle/ChillCalendarBundle/Messenger/Doctrine/CalendarEntityListener.php
-
message: "#^Parameter \\#2 \\$byUser of class Chill\\\\CalendarBundle\\\\Messenger\\\\Message\\\\CalendarRangeRemovedMessage constructor expects Chill\\\\MainBundle\\\\Entity\\\\User\\|null, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/Messenger/Doctrine/CalendarRangeEntityListener.php
-
message: "#^Parameter \\#3 \\$byUser of class Chill\\\\CalendarBundle\\\\Messenger\\\\Message\\\\CalendarRangeMessage constructor expects Chill\\\\MainBundle\\\\Entity\\\\User\\|null, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 2
path: src/Bundle/ChillCalendarBundle/Messenger/Doctrine/CalendarRangeEntityListener.php
-
message: "#^Parameter \\#1 \\$token of method Chill\\\\CalendarBundle\\\\RemoteCalendar\\\\Connector\\\\MSGraph\\\\OnBehalfOfUserTokenStorage\\:\\:setToken\\(\\) expects TheNetworg\\\\OAuth2\\\\Client\\\\Token\\\\AccessToken, League\\\\OAuth2\\\\Client\\\\Token\\\\AccessTokenInterface given\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/OnBehalfOfUserTokenStorage.php
-
message: "#^Parameter \\#2 \\$code of class Exception constructor expects int, array\\<string, int\\|string\\|null\\> given\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php
-
message: "#^Parameter \\#4 \\$offset of method Chill\\\\CalendarBundle\\\\Repository\\\\CalendarRepository\\:\\:findBy\\(\\) expects int\\|null, array\\|null given\\.$#"
count: 1
path: src/Bundle/ChillCalendarBundle/Repository/CalendarRepository.php
-
message: "#^Parameter \\#1 \\$prefix of function uniqid expects string, int\\<0, max\\> given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Form/Type/ChoicesListType.php
-
message: "#^Parameter \\#2 \\$callback of function array_filter expects callable\\(Symfony\\\\Component\\\\Serializer\\\\Mapping\\\\AttributeMetadataInterface\\)\\: mixed, Closure\\(Symfony\\\\Component\\\\Serializer\\\\Mapping\\\\AttributeMetadata\\)\\: bool given\\.$#"
count: 1
path: src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php
-
message: "#^Parameter \\#3 \\$metadata of method Chill\\\\DocGeneratorBundle\\\\Serializer\\\\Normalizer\\\\DocGenObjectNormalizer\\:\\:normalizeNullData\\(\\) expects Symfony\\\\Component\\\\Serializer\\\\Mapping\\\\ClassMetadata, Symfony\\\\Component\\\\Serializer\\\\Mapping\\\\ClassMetadataInterface given\\.$#"
count: 1
path: src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php
-
message: "#^Parameter \\#4 \\$attributes of method Chill\\\\DocGeneratorBundle\\\\Serializer\\\\Normalizer\\\\DocGenObjectNormalizer\\:\\:normalizeNullData\\(\\) expects array\\<Symfony\\\\Component\\\\Serializer\\\\Mapping\\\\AttributeMetadata\\>, array\\<Symfony\\\\Component\\\\Serializer\\\\Mapping\\\\AttributeMetadataInterface\\> given\\.$#"
count: 1
path: src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php
-
message: "#^Parameter \\#5 \\$metadata of method Chill\\\\DocGeneratorBundle\\\\Serializer\\\\Normalizer\\\\DocGenObjectNormalizer\\:\\:normalizeObject\\(\\) expects Symfony\\\\Component\\\\Serializer\\\\Mapping\\\\ClassMetadata, Symfony\\\\Component\\\\Serializer\\\\Mapping\\\\ClassMetadataInterface given\\.$#"
count: 1
path: src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php
-
message: "#^Parameter \\#6 \\$attributes of method Chill\\\\DocGeneratorBundle\\\\Serializer\\\\Normalizer\\\\DocGenObjectNormalizer\\:\\:normalizeObject\\(\\) expects array\\<Symfony\\\\Component\\\\Serializer\\\\Mapping\\\\AttributeMetadata\\>, array\\<Symfony\\\\Component\\\\Serializer\\\\Mapping\\\\AttributeMetadataInterface\\> given\\.$#"
count: 1
path: src/Bundle/ChillDocGeneratorBundle/Serializer/Normalizer/DocGenObjectNormalizer.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 4
path: src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Controller/EventController.php
-
message: "#^Parameter \\#1 \\$name of method Symfony\\\\Component\\\\Form\\\\FormFactoryInterface\\:\\:createNamedBuilder\\(\\) expects string, null given\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Controller/EventController.php
-
message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, Chill\\\\MainBundle\\\\Entity\\\\Center given\\.$#"
count: 1
path: src/Bundle/ChillEventBundle/Controller/EventController.php
-
message: "#^Parameter \\#1 \\$participations of method Chill\\\\EventBundle\\\\Controller\\\\ParticipationController\\:\\:createEditFormMultiple\\(\\) expects ArrayIterator, Traversable given\\.$#"
count: 2
path: src/Bundle/ChillEventBundle/Controller/ParticipationController.php
-
message: "#^Parameter \\#1 \\$callback of function call_user_func expects callable\\(\\)\\: mixed, null given\\.$#"
count: 2
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
-
message: "#^Parameter \\#2 \\$previous of class Symfony\\\\Component\\\\HttpKernel\\\\Exception\\\\BadRequestHttpException constructor expects Throwable\\|null, int given\\.$#"
count: 3
path: src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php
-
message: "#^Parameter \\#3 \\$code of class Symfony\\\\Component\\\\HttpKernel\\\\Exception\\\\BadRequestHttpException constructor expects int, Symfony\\\\Component\\\\Serializer\\\\Exception\\\\NotEncodableValueException given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php
-
message: "#^Parameter \\#3 \\$code of class Symfony\\\\Component\\\\HttpKernel\\\\Exception\\\\BadRequestHttpException constructor expects int, Symfony\\\\Component\\\\Serializer\\\\Exception\\\\UnexpectedValueException given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php
-
message: "#^Parameter \\#2 \\$role of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:getReachableCenters\\(\\) expects string, Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php
-
message: "#^Parameter \\#3 \\$formClass of method Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\CRUDController\\:\\:createFormFor\\(\\) expects string\\|null, Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\type\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php
-
message: "#^Parameter \\#3 \\$scope of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:getReachableCenters\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\Scope\\|null, Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\Scope\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php
-
message: "#^Parameter \\#1 \\$name of method Chill\\\\MainBundle\\\\Entity\\\\Country\\:\\:setName\\(\\) expects string, array\\<int\\|string, string\\> given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/LoadCountriesCommand.php
-
message: "#^Parameter \\#4 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, Chill\\\\MainBundle\\\\Entity\\\\the given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Command/LoadPostalCodesCommand.php
-
message: "#^Parameter \\#1 \\$alias of method Chill\\\\MainBundle\\\\Export\\\\ExportManager\\:\\:getExport\\(\\) expects string, Symfony\\\\Component\\\\HttpFoundation\\\\Request given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/ExportController.php
-
message: "#^Parameter \\#1 \\$name of method Symfony\\\\Component\\\\Form\\\\FormFactoryInterface\\:\\:createNamedBuilder\\(\\) expects string, null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/ExportController.php
-
message: "#^Parameter \\#3 \\$alias of method Chill\\\\MainBundle\\\\Controller\\\\ExportController\\:\\:exportFormStep\\(\\) expects string, Symfony\\\\Component\\\\HttpFoundation\\\\Request given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/ExportController.php
-
message: "#^Parameter \\#3 \\$alias of method Chill\\\\MainBundle\\\\Controller\\\\ExportController\\:\\:formatterFormStep\\(\\) expects string, Symfony\\\\Component\\\\HttpFoundation\\\\Request given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/ExportController.php
-
message: "#^Parameter \\#3 \\$alias of method Chill\\\\MainBundle\\\\Controller\\\\ExportController\\:\\:forwardToGenerate\\(\\) expects string, Symfony\\\\Component\\\\HttpFoundation\\\\Request given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/ExportController.php
-
message: "#^Parameter \\#3 \\$alias of method Chill\\\\MainBundle\\\\Controller\\\\ExportController\\:\\:selectCentersStep\\(\\) expects string, Symfony\\\\Component\\\\HttpFoundation\\\\Request given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/ExportController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Repository\\\\NotificationRepository\\:\\:countUnreadByUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/NotificationApiController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Repository\\\\NotificationRepository\\:\\:findUnreadByUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/NotificationApiController.php
-
message: "#^Parameter \\#1 \\$addressee of method Chill\\\\MainBundle\\\\Repository\\\\NotificationRepository\\:\\:countAllForAttendee\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/NotificationController.php
-
message: "#^Parameter \\#1 \\$addressee of method Chill\\\\MainBundle\\\\Repository\\\\NotificationRepository\\:\\:findAllForAttendee\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/NotificationController.php
-
message: "#^Parameter \\#1 \\$sender of method Chill\\\\MainBundle\\\\Repository\\\\NotificationRepository\\:\\:countAllForSender\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/NotificationController.php
-
message: "#^Parameter \\#1 \\$sender of method Chill\\\\MainBundle\\\\Repository\\\\NotificationRepository\\:\\:findAllForSender\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/NotificationController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Repository\\\\NotificationRepository\\:\\:countUnreadByUserWhereAddressee\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/NotificationController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Repository\\\\NotificationRepository\\:\\:countUnreadByUserWhereSender\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/NotificationController.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 4
path: src/Bundle/ChillMainBundle/Controller/PasswordController.php
-
message: "#^Parameter \\#1 \\$token of class Chill\\\\MainBundle\\\\Security\\\\PasswordRecover\\\\PasswordRecoverEvent constructor expects Chill\\\\MainBundle\\\\Security\\\\PasswordRecover\\\\type\\|null, string given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Controller/PasswordController.php
-
message: "#^Parameter \\#3 \\$ip of class Chill\\\\MainBundle\\\\Security\\\\PasswordRecover\\\\PasswordRecoverEvent constructor expects Chill\\\\MainBundle\\\\Security\\\\PasswordRecover\\\\type\\|null, string\\|null given\\.$#"
count: 4
path: src/Bundle/ChillMainBundle/Controller/PasswordController.php
-
message: "#^Parameter \\#1 \\$context of method Chill\\\\MainBundle\\\\Timeline\\\\TimelineBuilder\\:\\:countItems\\(\\) expects Chill\\\\MainBundle\\\\Timeline\\\\unknown, string given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/TimelineCenterController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Entity\\\\Workflow\\\\EntityWorkflow\\:\\:addSubscriberToFinal\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Entity\\\\Workflow\\\\EntityWorkflow\\:\\:addSubscriberToStep\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Entity\\\\Workflow\\\\EntityWorkflow\\:\\:isUserSubscribedToFinal\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Entity\\\\Workflow\\\\EntityWorkflow\\:\\:isUserSubscribedToStep\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Entity\\\\Workflow\\\\EntityWorkflow\\:\\:removeSubscriberToFinal\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Entity\\\\Workflow\\\\EntityWorkflow\\:\\:removeSubscriberToStep\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php
-
message: "#^Parameter \\#1 \\$place of method Chill\\\\MainBundle\\\\DependencyInjection\\\\Configuration\\:\\:filterWidgetByPlace\\(\\) expects string, Chill\\\\MainBundle\\\\DependencyInjection\\\\Widget\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php
-
message: "#^Parameter \\#1 \\$place of method Chill\\\\MainBundle\\\\DependencyInjection\\\\Configuration\\:\\:getWidgetAliasesbyPlace\\(\\) expects Chill\\\\MainBundle\\\\DependencyInjection\\\\Widget\\\\type, string given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php
-
message: "#^Parameter \\#2 \\$array of function implode expects array\\|null, Chill\\\\MainBundle\\\\DependencyInjection\\\\Widget\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php
-
message: "#^Parameter \\#2 \\$place of method Chill\\\\MainBundle\\\\DependencyInjection\\\\Widget\\\\Factory\\\\WidgetFactoryInterface\\:\\:createDefinition\\(\\) expects Chill\\\\MainBundle\\\\DependencyInjection\\\\Widget\\\\Factory\\\\type, string given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/AbstractWidgetsCompilerPass.php
-
message: "#^Parameter \\#3 \\$order of method Chill\\\\MainBundle\\\\DependencyInjection\\\\Widget\\\\Factory\\\\WidgetFactoryInterface\\:\\:createDefinition\\(\\) expects Chill\\\\MainBundle\\\\DependencyInjection\\\\Widget\\\\Factory\\\\type, float given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/AbstractWidgetsCompilerPass.php
-
message: "#^Parameter \\#2 \\$place of method Chill\\\\MainBundle\\\\DependencyInjection\\\\Widget\\\\Factory\\\\WidgetFactoryInterface\\:\\:getServiceId\\(\\) expects string, Chill\\\\MainBundle\\\\DependencyInjection\\\\Widget\\\\Factory\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/Factory/AbstractWidgetFactory.php
-
message: "#^Parameter \\#3 \\$order of method Chill\\\\MainBundle\\\\DependencyInjection\\\\Widget\\\\Factory\\\\WidgetFactoryInterface\\:\\:getServiceId\\(\\) expects float, Chill\\\\MainBundle\\\\DependencyInjection\\\\Widget\\\\Factory\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/DependencyInjection/Widget/Factory/AbstractWidgetFactory.php
-
message: "#^Parameter \\#1 \\$iterator of function iterator_to_array expects Traversable, array\\<Chill\\\\MainBundle\\\\Export\\\\AggregatorInterface\\> given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Export/ExportManager.php
-
message: "#^Parameter \\#2 \\$nbAggregators of method Chill\\\\MainBundle\\\\Export\\\\Formatter\\\\CSVFormatter\\:\\:appendAggregatorForm\\(\\) expects string, int\\<0, max\\> given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Export/Formatter/CSVFormatter.php
-
message: "#^Parameter \\#2 \\$array of function array_map expects array, Chill\\\\MainBundle\\\\Export\\\\Formatter\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php
-
message: "#^Parameter \\#2 \\$nbAggregators of method Chill\\\\MainBundle\\\\Export\\\\Formatter\\\\SpreadSheetFormatter\\:\\:appendAggregatorForm\\(\\) expects string, int\\<0, max\\> given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php
-
message: "#^Parameter \\#1 \\$callback of function call_user_func expects callable\\(\\)\\: mixed, null given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Form/ChoiceLoader/PostalCodeChoiceLoader.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Entity\\\\Embeddable\\\\PrivateCommentEmbeddable\\:\\:getCommentForUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/DataMapper/PrivateCommentDataMapper.php
-
message: "#^Parameter \\#1 \\$em of class Chill\\\\MainBundle\\\\Form\\\\Type\\\\DataTransformer\\\\ObjectToIdTransformer constructor expects Doctrine\\\\ORM\\\\EntityManagerInterface, Doctrine\\\\Persistence\\\\ObjectManager given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/Select2CountryType.php
-
message: "#^Parameter \\#1 \\$em of class Chill\\\\MainBundle\\\\Form\\\\Type\\\\DataTransformer\\\\MultipleObjectsToIdTransformer constructor expects Doctrine\\\\ORM\\\\EntityManagerInterface, Doctrine\\\\Persistence\\\\ObjectManager given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/Select2LanguageType.php
-
message: "#^Parameter \\#1 \\$translatableStrings of method Chill\\\\MainBundle\\\\Templating\\\\TranslatableStringHelper\\:\\:localize\\(\\) expects array, string given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Form/Type/Select2LanguageType.php
-
message: "#^Parameter \\#1 \\$datetime of class DateTime constructor expects string, Chill\\\\MainBundle\\\\Search\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Search/AbstractSearch.php
-
message: "#^Parameter \\#4 \\$paginator of method Chill\\\\MainBundle\\\\Search\\\\SearchApi\\:\\:fetchRawResult\\(\\) expects Chill\\\\MainBundle\\\\Pagination\\\\Paginator, Chill\\\\MainBundle\\\\Pagination\\\\PaginatorInterface given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Search/SearchApi.php
-
message: "#^Parameter \\#2 \\$callback of function uasort expects callable\\(Chill\\\\MainBundle\\\\Search\\\\HasAdvancedSearchForm, Chill\\\\MainBundle\\\\Search\\\\HasAdvancedSearchForm\\)\\: int, Closure\\(Chill\\\\MainBundle\\\\Search\\\\SearchInterface, Chill\\\\MainBundle\\\\Search\\\\SearchInterface\\)\\: \\-1\\|0\\|1 given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Search/SearchProvider.php
-
message: "#^Parameter \\#2 \\$subject of function preg_match_all expects string, Chill\\\\MainBundle\\\\Search\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Search/SearchProvider.php
-
message: "#^Parameter \\#1 \\$object of function get_class expects object, array\\<Chill\\\\MainBundle\\\\Entity\\\\Center\\> given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Entity\\\\Notification\\:\\:isReadBy\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Serializer/Normalizer/NotificationNormalizer.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Entity\\\\Embeddable\\\\PrivateCommentEmbeddable\\:\\:setCommentForUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Serializer/Normalizer/PrivateCommentEmbeddableNormalizer.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Templating/Widget/WidgetRenderingTwig.php
-
message: "#^Parameter \\#1 \\$context of method Chill\\\\MainBundle\\\\Timeline\\\\TimelineBuilder\\:\\:buildUnionQuery\\(\\) expects string, Chill\\\\MainBundle\\\\Timeline\\\\unknown given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Timeline/TimelineBuilder.php
-
message: "#^Parameter \\#2 \\$context of method Chill\\\\MainBundle\\\\Timeline\\\\TimelineProviderInterface\\:\\:getEntityTemplate\\(\\) expects Chill\\\\MainBundle\\\\Timeline\\\\type, string given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Timeline/TimelineBuilder.php
-
message: "#^Parameter \\#1 \\$phoneNumber of method Chill\\\\MainBundle\\\\Phonenumber\\\\PhoneNumberHelperInterface\\:\\:format\\(\\) expects libphonenumber\\\\PhoneNumber\\|null, string given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Validation/Validator/ValidPhonenumber.php
-
message: "#^Parameter \\#1 \\$transitionBy of method Chill\\\\MainBundle\\\\Entity\\\\Workflow\\\\EntityWorkflowStep\\:\\:setTransitionBy\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User\\|null, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Parameter \\#1 \\$mobilenumber of method Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:setMobilenumber\\(\\) expects libphonenumber\\\\PhoneNumber\\|null, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Parameter \\#1 \\$phonenumber of method Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:setPhonenumber\\(\\) expects libphonenumber\\\\PhoneNumber\\|null, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodController.php
-
message: "#^Parameter \\#2 \\$callback of function usort expects callable\\(mixed, mixed\\)\\: int, Closure\\(mixed, mixed\\)\\: bool given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkEvaluationRepository\\:\\:countNearMaxDateByUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationApiController.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkEvaluationRepository\\:\\:findNearMaxDateByUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationApiController.php
-
message: "#^Parameter \\#1 \\$object of static method DateTimeImmutable\\:\\:createFromMutable\\(\\) expects DateTime, DateTimeInterface given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/HouseholdApiController.php
-
message: "#^Parameter \\#2 \\$callback of function usort expects callable\\(mixed, mixed\\)\\: int, Closure\\(mixed, mixed\\)\\: bool given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/HouseholdController.php
-
message: "#^Parameter \\#2 \\$previous of class Symfony\\\\Component\\\\HttpKernel\\\\Exception\\\\BadRequestHttpException constructor expects Throwable\\|null, int given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php
-
message: "#^Parameter \\#3 \\$code of class Symfony\\\\Component\\\\HttpKernel\\\\Exception\\\\BadRequestHttpException constructor expects int, Symfony\\\\Component\\\\Serializer\\\\Exception\\\\InvalidArgumentException\\|Symfony\\\\Component\\\\Serializer\\\\Exception\\\\UnexpectedValueException given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Controller/PersonController.php
-
message: "#^Parameter \\#1 \\$form of method Chill\\\\PersonBundle\\\\Controller\\\\PersonController\\:\\:isLastPostDataChanges\\(\\) expects Symfony\\\\Component\\\\Form\\\\Form, Symfony\\\\Component\\\\Form\\\\FormInterface given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/PersonController.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php
-
message: "#^Parameter \\#2 \\$precision of method Chill\\\\PersonBundle\\\\Search\\\\SimilarPersonMatcher\\:\\:matchPerson\\(\\) expects float, Chill\\\\PersonBundle\\\\Repository\\\\PersonNotDuplicateRepository given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php
-
message: "#^Parameter \\#3 \\$orderBy of method Chill\\\\PersonBundle\\\\Search\\\\SimilarPersonMatcher\\:\\:matchPerson\\(\\) expects string, float given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php
-
message: "#^Parameter \\#4 \\$addYearComparison of method Chill\\\\PersonBundle\\\\Search\\\\SimilarPersonMatcher\\:\\:matchPerson\\(\\) expects bool, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php
-
message: "#^Parameter \\#1 \\$context of method Chill\\\\MainBundle\\\\Timeline\\\\TimelineBuilder\\:\\:countItems\\(\\) expects Chill\\\\MainBundle\\\\Timeline\\\\unknown, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/TimelinePersonController.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Controller/TimelinePersonController.php
-
message: "#^Parameter \\#1 \\$period of method Chill\\\\PersonBundle\\\\Entity\\\\AccompanyingPeriod\\\\AccompanyingPeriodLocationHistory\\:\\:setPeriod\\(\\) expects Chill\\\\PersonBundle\\\\Entity\\\\AccompanyingPeriod, null given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
-
message: "#^Parameter \\#1 \\$now of method Chill\\\\PersonBundle\\\\Entity\\\\Household\\\\Household\\:\\:getCurrentMembers\\(\\) expects DateTimeImmutable\\|null, DateTimeInterface\\|null given\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Entity/Household/Household.php
-
message: "#^Parameter \\#1 \\$now of method Chill\\\\PersonBundle\\\\Entity\\\\Household\\\\Household\\:\\:getNonCurrentMembers\\(\\) expects DateTimeImmutable\\|null, DateTimeInterface\\|null given\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Entity/Household/Household.php
-
message: "#^Parameter \\#1 \\$key of method Doctrine\\\\Common\\\\Collections\\\\Collection\\<\\(int\\|string\\),mixed\\>\\:\\:remove\\(\\) expects \\(int\\|string\\), Chill\\\\PersonBundle\\\\Entity\\\\SocialWork\\\\SocialAction given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php
-
message: "#^Parameter \\#1 \\$translatableStrings of method Chill\\\\MainBundle\\\\Templating\\\\TranslatableStringHelper\\:\\:localize\\(\\) expects array, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php
-
message: "#^Parameter \\#1 \\$callback of function call_user_func expects callable\\(\\)\\: mixed, null given\\.$#"
count: 2
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
-
message: "#^Parameter \\#1 \\$entity of method Chill\\\\PersonBundle\\\\Templating\\\\Entity\\\\ClosingMotiveRender\\:\\:renderString\\(\\) expects Chill\\\\PersonBundle\\\\Entity\\\\AccompanyingPeriod\\\\ClosingMotive, Chill\\\\MainBundle\\\\Templating\\\\Entity\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Templating/Entity/ClosingMotiveRender.php
-
message: "#^Parameter \\#1 \\$entityName of method Doctrine\\\\ORM\\\\EntityManager\\:\\:getRepository\\(\\) expects class\\-string\\<ChillPersonBundle\\:AccompanyingPeriod\\>, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Timeline/AbstractTimelineAccompanyingPeriod.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:filterReachableCenters\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Timeline/AbstractTimelineAccompanyingPeriod.php
-
message: "#^Parameter \\#3 \\$context of method Chill\\\\PersonBundle\\\\Timeline\\\\AbstractTimelineAccompanyingPeriod\\:\\:getBasicEntityTemplate\\(\\) expects string, Chill\\\\MainBundle\\\\Timeline\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Timeline/TimelineAccompanyingPeriodClosing.php
-
message: "#^Parameter \\#3 \\$context of method Chill\\\\PersonBundle\\\\Timeline\\\\AbstractTimelineAccompanyingPeriod\\:\\:getBasicEntityTemplate\\(\\) expects string, Chill\\\\MainBundle\\\\Timeline\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Timeline/TimelineAccompanyingPeriodOpening.php
-
message: "#^Parameter \\#2 \\$role of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelperInterface\\:\\:getReachableCenters\\(\\) expects string, Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Widget/PersonListWidget.php
-
message: "#^Parameter \\#1 \\$className of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getRepository\\(\\) expects class\\-string\\<ChillReportBundle\\:Report\\>, string given\\.$#"
count: 4
path: src/Bundle/ChillReportBundle/Controller/ReportController.php
-
message: "#^Parameter \\#1 \\$entity of method Chill\\\\ReportBundle\\\\Controller\\\\ReportController\\:\\:createEditForm\\(\\) expects Chill\\\\ReportBundle\\\\Entity\\\\Report, ChillReportBundle\\:Report given\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Controller/ReportController.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 4
path: src/Bundle/ChillReportBundle/Controller/ReportController.php
-
message: "#^Parameter \\#2 \\$role of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:getReachableScopes\\(\\) expects string, Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role given\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Controller/ReportController.php
-
message: "#^Parameter \\#1 \\$em of class Chill\\\\MainBundle\\\\Form\\\\Type\\\\DataTransformer\\\\ScopeTransformer constructor expects Doctrine\\\\ORM\\\\EntityManagerInterface, Doctrine\\\\Persistence\\\\ObjectManager given\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Form/ReportType.php
-
message: "#^Parameter \\#2 \\$role of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:getReachableScopes\\(\\) expects string, Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role given\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Form/ReportType.php
-
message: "#^Parameter \\#2 \\$role of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:getReachableCenters\\(\\) expects string, Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role given\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Search/ReportSearch.php
-
message: "#^Parameter \\#2 \\$role of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:getReachableScopes\\(\\) expects string, Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role given\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Search/ReportSearch.php
-
message: "#^Parameter \\#1 \\$context of method Chill\\\\ReportBundle\\\\Timeline\\\\TimelineReportProvider\\:\\:checkContext\\(\\) expects string, Chill\\\\MainBundle\\\\Timeline\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php
-
message: "#^Parameter \\#1 \\$entity of method Chill\\\\ReportBundle\\\\Timeline\\\\TimelineReportProvider\\:\\:getFieldsToRender\\(\\) expects Chill\\\\ReportBundle\\\\Entity\\\\Report, Chill\\\\MainBundle\\\\Timeline\\\\type given\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php
-
message: "#^Parameter \\#1 \\$entityName of method Doctrine\\\\ORM\\\\EntityManager\\:\\:getRepository\\(\\) expects class\\-string\\<ChillReportBundle\\:Report\\>, string given\\.$#"
count: 1
path: src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 6
path: src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Controller/TaskController.php
-
message: "#^Parameter \\#1 \\$keys of function array_fill_keys expects array, Chill\\\\TaskBundle\\\\Entity\\\\json given\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Entity/AbstractTask.php
-
message: "#^Parameter \\#1 \\$task of method Chill\\\\TaskBundle\\\\Entity\\\\Task\\\\SingleTaskPlaceEvent\\:\\:setTask\\(\\) expects Chill\\\\TaskBundle\\\\Entity\\\\SingleTask, Chill\\\\TaskBundle\\\\Entity\\\\AbstractTask given\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Event/Lifecycle/TaskLifecycleEvent.php
-
message: "#^Parameter \\#1 \\$repository of class Chill\\\\PersonBundle\\\\Form\\\\DataTransformer\\\\PersonToIdTransformer constructor expects Chill\\\\PersonBundle\\\\Repository\\\\PersonRepository, Doctrine\\\\ORM\\\\EntityManagerInterface given\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php
-
message: "#^Parameter \\#2 \\$role of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:getReachableCenters\\(\\) expects string, Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role given\\.$#"
count: 4
path: src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php
-
message: "#^Parameter \\#1 \\$extras of method Knp\\\\Menu\\\\MenuItem\\:\\:setExtras\\(\\) expects array\\<string, mixed\\>, string given\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php
-
message: "#^Parameter \\#2 \\$role of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:getReachableCenters\\(\\) expects string, Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role given\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Repository/SingleTaskRepository.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php
-
message: "#^Parameter \\#2 \\$role of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:getReachableCenters\\(\\) expects string, Symfony\\\\Component\\\\Security\\\\Core\\\\Role\\\\Role given\\.$#"
count: 1
path: src/Bundle/ChillTaskBundle/Timeline/TaskLifeCycleEventTimelineProvider.php
-
message: "#^Parameter \\#1 \\$storedObject of method Chill\\\\WopiBundle\\\\Service\\\\Wopi\\\\ChillDocumentManager\\:\\:getContent\\(\\) expects Chill\\\\DocStoreBundle\\\\Entity\\\\StoredObject, ChampsLibres\\\\WopiLib\\\\Contract\\\\Entity\\\\Document given\\.$#"
count: 3
path: src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillDocumentManager.php
-
message: "#^Parameter \\#1 \\$storedObject of method Chill\\\\WopiBundle\\\\Service\\\\Wopi\\\\ChillDocumentManager\\:\\:setContent\\(\\) expects Chill\\\\DocStoreBundle\\\\Entity\\\\StoredObject, ChampsLibres\\\\WopiLib\\\\Contract\\\\Entity\\\\Document given\\.$#"
count: 1
path: src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillDocumentManager.php
-
message: "#^Parameter \\#1 \\$type of method Chill\\\\DocStoreBundle\\\\Entity\\\\StoredObject\\:\\:setType\\(\\) expects string\\|null, false given\\.$#"
count: 1
path: src/Bundle/ChillWopiBundle/src/Service/Wopi/ChillDocumentManager.php

View File

@@ -1,6 +1,76 @@
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\\\\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: "#^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\\: \\$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

View File

@@ -340,11 +340,6 @@ parameters:
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.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
@@ -400,6 +395,16 @@ parameters:
count: 1
path: src/Bundle/ChillTaskBundle/Timeline/TaskLifeCycleEventTimelineProvider.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: "#^Method Chill\\\\ThirdPartyBundle\\\\Search\\\\ThirdPartySearch\\:\\:renderResult\\(\\) should return string but return statement is missing\\.$#"
count: 1

View File

@@ -1,8 +1,7 @@
parameters:
level: 5
level: 1
paths:
- src/
reportUnmatchedIgnoredErrors: false
excludePaths:
- .php_cs*
- docs/
@@ -25,8 +24,4 @@ includes:
- phpstan-critical.neon
- phpstan-deprecations.neon
- phpstan-types.neon
- phpstan-baseline-level-2.neon
- phpstan-baseline-level-3.neon
- phpstan-baseline-level-4.neon
- phpstan-baseline-level-5.neon

View File

@@ -9,10 +9,9 @@
cacheDirectory="./.psalm"
>
<projectFiles>
<directory name="src"/>
<directory name="src" />
<ignoreFiles>
<directory name="./tests/"/>
<directory name="./node_modules/"/>
<directory name="./tests/" />
</ignoreFiles>
</projectFiles>
@@ -21,27 +20,9 @@
<issueHandlers>
<UndefinedDocblockClass>
<errorLevel type="suppress">
<referencedClass name="UnitEnum"/>
<referencedClass name="UnitEnum" />
</errorLevel>
</UndefinedDocblockClass>
</issueHandlers>
<!--
<fileExtensions>
<extension name=".php"/>
<extension name=".twig" checker="tests/app/vendor/psalm/plugin-symfony/src/Twig/TemplateFileAnalyzer.php" />
</fileExtensions>
-->
<plugins>
<pluginClass class="Psalm\PhpUnitPlugin\Plugin"/>
<!--
<pluginClass class="Psalm\SymfonyPsalmPlugin\Plugin">
<containerXml>tests/app/var/cache/dev/srcApp_KernelDevDebugContainer.xml</containerXml>
<stubs>
<file name="vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php" />
</stubs>
</pluginClass>
-->
</plugins>
</psalm>

View File

@@ -651,8 +651,8 @@ final class ActivityController extends AbstractController
throw $this->createNotFoundException('Accompanying Period not found');
}
// TODO Add permission
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
// TODO Add permission
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
} else {
throw $this->createNotFoundException('Person or Accompanying Period not found');
}

View File

@@ -257,12 +257,10 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* Add a social issue.
*
* Note: the social issue consistency (the fact that only yougest social issues
* Note: the social issue consistency (the fact that only youngest social issues
* are kept) is processed by an entity listener:
*
* @see{\Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodSocialIssueConsistencyEntityListener}
*
* @return $this
*/
public function addSocialIssue(SocialIssue $socialIssue): self
{
@@ -270,6 +268,10 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
$this->socialIssues[] = $socialIssue;
}
if ($this->getAccompanyingPeriod() !== null) {
$this->getAccompanyingPeriod()->addSocialIssue($socialIssue);
}
return $this;
}
@@ -550,6 +552,10 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
{
$this->accompanyingPeriod = $accompanyingPeriod;
foreach ($this->getSocialIssues() as $issue) {
$this->accompanyingPeriod->addSocialIssue($issue);
}
return $this;
}

View File

@@ -58,8 +58,7 @@ class DateAggregator implements AggregatorInterface
break;
case 'year':
$fmt = 'YYYY';
$order = 'DESC';
$fmt = 'YYYY'; $order = 'DESC';
break; // order DESC does not works !

View File

@@ -73,7 +73,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
};
case 'scopesNames':
return $this->translatableStringExportLabelHelper->getLabelMulti($key, $values, ListActivityHelper::MSG_KEY . 'course circles');
return $this->translatableStringExportLabelHelper->getLabelMulti($key, $values, ListActivityHelper::MSG_KEY . 'course circles');
default:
return $this->helper->getLabels($key, $values, $data);

View File

@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickUserLocationType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class LocationFilter implements FilterInterface
{
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->andWhere(
$qb->expr()->in('activity.location', ':location')
);
$qb->setParameter('location', $data['accepted_location']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_location', PickUserLocationType::class, [
'multiple' => true,
'label' => 'pick location',
]);
}
public function describeAction($data, $format = 'string'): array
{
$locations = [];
foreach ($data['accepted_location'] as $location) {
$locations[] = $location->getName();
}
return ['Filtered activity by location: only %locations%', [
'%locations%' => implode(', ', $locations),
]];
}
public function getTitle(): string
{
return 'Filter activity by location';
}
}

View File

@@ -225,9 +225,10 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
$personToCenter = $metadataPerson->getAssociationMapping('center')['joinColumns'][0]['name'];
// acls:
$role = new Role(ActivityVoter::SEE);
$reachableCenters = $this->authorizationHelper->getReachableCenters(
$this->tokenStorage->getToken()->getUser(),
ActivityVoter::SEE
$role
);
if (count($reachableCenters) === 0) {
@@ -238,7 +239,7 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
if ('person' === $context) {
// we start with activities having the person_id linked to person
$where .= sprintf('%s = ? AND ', $activityToPerson);
$parameters[] = $args['context']->getId();
$parameters[] = $person->getId();
}
// we add acl (reachable center and scopes)
@@ -251,7 +252,7 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
continue;
}
// we get all the reachable scopes for this center
$reachableScopes = $this->authorizationHelper->getReachableScopes($this->tokenStorage->getToken()->getUser(), ActivityVoter::SEE, $center);
$reachableScopes = $this->authorizationHelper->getReachableScopes($this->tokenStorage->getToken()->getUser(), $role, $center);
// we get the ids for those scopes
$reachablesScopesId = array_map(
static function (Scope $scope) {

View File

@@ -88,3 +88,11 @@ div.flex-bloc.concerned-groups {
font-size: 120%;
}
}
/// DOCUMENT LIST IN ACTIVITY ITEM
li.document-list-item {
display: flex;
width: 100%;
justify-content: space-between;
margin-bottom: 0.3rem;
}

View File

@@ -68,7 +68,7 @@
<div class="wl-col title"><h3>{{ 'Referrer'|trans }}</h3></div>
<div class="wl-col list">
<p class="wl-item">
{{ activity.user|chill_entity_render_box }}
<span class="badge-user">{{ activity.user|chill_entity_render_box }}</span>
</p>
</div>
</div>
@@ -137,19 +137,42 @@
{{ activity.comment|chill_entity_render_box({
'disable_markdown': false,
'limit_lines': 3,
'metadata': false
'metadata': false,
}) }}
</div>
</div>
{% endif %}
{# Only if ACL SEE_DETAILS AND/OR only on template SHOW ??
durationTime
travelTime
comment
documents
attendee
#}
{% if is_granted('CHILL_ACTIVITY_SEE_DETAILS', activity) and activity.privateComment.hasCommentForUser(app.user) %}
<div class="wl-row">
<div class="wl-col title">
<h3>{{ 'Private comment'|trans }}</h3>
</div>
<div class="wl-col list">
<section class="chill-entity entity-comment-embeddable">
<blockquote class="chill-user-quote private-quote">
{{ activity.privateComment.comments[app.user.id]|chill_markdown_to_html }}
</blockquote>
</section>
</div>
</div>
{% endif %}
{% if is_granted('CHILL_ACTIVITY_SEE_DETAILS', activity) and activity.documents|length > 0 %}
<div class="wl-row">
<div class="wl-col title">
<h3>{{ 'Documents'|trans }}</h3>
</div>
<div class="wl-col list">
<ul>
{% for d in activity.documents %}
<li class="document-list-item">{{ d.title|chill_print_or_message('document.Any title') }} {{ d|chill_document_button_group(d.title, is_granted('CHILL_ACTIVITY_UPDATE', activity), {small: true}) }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
</div>

View File

@@ -8,11 +8,13 @@
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_notification_toggle_read_status') }}
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block content %}

View File

@@ -23,11 +23,13 @@
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_notification_toggle_read_status') }}
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block content %}

View File

@@ -41,7 +41,7 @@
{% if activity.user and t.userVisible %}
<li>
<span class="item-key">{{ 'Referrer'|trans ~ ': ' }}</span>
<b>{{ activity.user|chill_entity_render_box}}</b>
<span class="badge-user">{{ activity.user|chill_entity_render_box }}</span>
</li>
{% endif %}

View File

@@ -35,7 +35,9 @@
<div class="item-row separator">
<dl class="chill_view_data">
<dt class="inline">{{ 'Referrer'|trans|capitalize }}</dt>
<dd>{{ entity.user|chill_entity_render_box }}</dd>
<dd>
<span class="badge-user">{{ entity.user|chill_entity_render_box }}</span>
</dd>
{%- if entity.scope -%}
<dt class="inline">{{ 'Scope'|trans }}</dt>
@@ -168,7 +170,7 @@
{% if entity.documents|length > 0 %}
<ul>
{% for d in entity.documents %}
<li>{{ d.title }} {{ d|chill_document_button_group() }}</li>
<li class="document-list-item">{{ d.title|chill_print_or_message('document.Any title') }} {{ d|chill_document_button_group(d.title, is_granted('CHILL_ACTIVITY_UPDATE', entity), {small: true}) }}</li>
{% endfor %}
</ul>
{% else %}

View File

@@ -22,6 +22,7 @@ use Chill\DocStoreBundle\Repository\DocumentCategoryRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@@ -45,6 +46,8 @@ class ActivityContext implements
private PersonRenderInterface $personRender;
private PersonRepository $personRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
private TranslatorInterface $translator;
@@ -55,6 +58,7 @@ class ActivityContext implements
TranslatableStringHelperInterface $translatableStringHelper,
EntityManagerInterface $em,
PersonRenderInterface $personRender,
PersonRepository $personRepository,
TranslatorInterface $translator,
BaseContextData $baseContextData
) {
@@ -63,6 +67,7 @@ class ActivityContext implements
$this->translatableStringHelper = $translatableStringHelper;
$this->em = $em;
$this->personRender = $personRender;
$this->personRepository = $personRepository;
$this->translator = $translator;
$this->baseContextData = $baseContextData;
}
@@ -147,7 +152,7 @@ class ActivityContext implements
$options = $template->getOptions();
$data = [];
$data = array_merge($data, $this->baseContextData->getData());
$data = array_merge($data, $this->baseContextData->getData($contextGenerationData['creator'] ?? null));
$data['activity'] = $this->normalizer->normalize($entity, 'docgen', ['docgen:expects' => Activity::class, 'groups' => 'docgen:read']);
$data['course'] = $this->normalizer->normalize($entity->getAccompanyingPeriod(), 'docgen', ['docgen:expects' => AccompanyingPeriod::class, 'groups' => 'docgen:read']);
@@ -206,6 +211,32 @@ class ActivityContext implements
return $options['mainPerson'] || $options['person1'] || $options['person2'];
}
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
$normalized = [];
foreach (['mainPerson', 'person1', 'person2'] as $k) {
$normalized[$k] = null === $data[$k] ? null : $data[$k]->getId();
}
return $normalized;
}
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
$denormalized = [];
foreach (['mainPerson', 'person1', 'person2'] as $k) {
if (null !== ($id = ($data[$k] ?? null))) {
$denormalized[$k] = $this->personRepository->find($id);
} else {
$denormalized[$k] = null;
}
}
return $denormalized;
}
/**
* @param Activity $entity
*/

View File

@@ -146,6 +146,16 @@ class ListActivitiesByAccompanyingPeriodContext implements
return $this->accompanyingPeriodContext->hasPublicForm($template, $entity);
}
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
return $this->accompanyingPeriodContext->contextGenerationDataNormalize($template, $entity, $data);
}
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
return $this->accompanyingPeriodContext->contextGenerationDataDenormalize($template, $entity, $data);
}
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{
$this->accompanyingPeriodContext->storeGenerated($template, $storedObject, $entity, $contextGenerationData);

View File

@@ -79,6 +79,11 @@ services:
tags:
- { name: chill.export_filter, alias: 'accompanyingcourse_activitytype_filter' }
chill.activity.export.location_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\LocationFilter
tags:
- { name: chill.export_filter, alias: 'activity_location_filter' }
chill.activity.export.locationtype_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\LocationTypeFilter
tags:

View File

@@ -77,7 +77,7 @@ Choose a type: Choisir un type
4 hours: 4 heures
4 hours 30: 4 heures 30
5 hours: 5 heures
Concerned groups: Parties concernées
Concerned groups: Parties concernées par l'échange
Persons in accompanying course: Usagers du parcours
Third persons: Tiers non-pro.
Others persons: Usagers
@@ -252,6 +252,8 @@ Activity reasons for those activities: Sujets de ces activités
Filter by activity type: Filtrer les activités par type
Filter activity by location: Filtrer les activités par localisation
'Filtered activity by location: only %locations%': "Filtré par localisation: uniquement %locations%"
Filter activity by locationtype: Filtrer les activités par type de localisation
'Filtered activity by locationtype: only %types%': "Filtré par type de localisation: uniquement %types%"
Accepted locationtype: Types de localisation

View File

@@ -53,19 +53,15 @@ class ByActivityTypeAggregator implements AggregatorInterface
public function getLabels($key, array $values, $data)
{
$this->asideActivityCategoryRepository->findBy(['id' => $values]);
return function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.Aside activity type';
}
if (null === $value) {
if (null === $value || null === $t = $this->asideActivityCategoryRepository->find($value)) {
return '';
}
$t = $this->asideActivityCategoryRepository->find($value);
return $this->translatableStringHelper->localize($t->getTitle());
};
}

View File

@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\AsideActivityBundle\Export\Aggregator;
use Chill\AsideActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class ByUserJobAggregator implements AggregatorInterface
{
private TranslatableStringHelperInterface $translatableStringHelper;
private UserJobRepositoryInterface $userJobRepository;
public function __construct(UserJobRepositoryInterface $userJobRepository, TranslatableStringHelperInterface $translatableStringHelper)
{
$this->userJobRepository = $userJobRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('aside_user', $qb->getAllAliases(), true)) {
$qb->leftJoin('aside.agent', 'aside_user');
}
$qb
->addSelect('IDENTITY(aside_user.userJob) AS aside_activity_user_job_aggregator')
->addGroupBy('aside_activity_user_job_aggregator');
}
public function applyOn()
{
return Declarations::ASIDE_ACTIVITY_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
// nothing to add in the form
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Users \'s job';
}
if (null === $value || '' === $value) {
return '';
}
$j = $this->userJobRepository->find($value);
return $this->translatableStringHelper->localize(
$j->getLabel()
);
};
}
public function getQueryKeys($data): array
{
return ['aside_activity_user_job_aggregator'];
}
public function getTitle()
{
return 'export.aggregator.Aggregate by user job';
}
}

View File

@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\AsideActivityBundle\Export\Aggregator;
use Chill\AsideActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class ByUserScopeAggregator implements AggregatorInterface
{
private ScopeRepositoryInterface $scopeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(ScopeRepositoryInterface $scopeRepository, TranslatableStringHelperInterface $translatableStringHelper)
{
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('aside_user', $qb->getAllAliases(), true)) {
$qb->leftJoin('aside.agent', 'aside_user');
}
$qb
->addSelect('IDENTITY(aside_user.mainScope) AS aside_activity_user_scope_aggregator')
->addGroupBy('aside_activity_user_scope_aggregator');
}
public function applyOn()
{
return Declarations::ASIDE_ACTIVITY_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
// nothing to add in the form
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Users \'s scope';
}
if (null === $value || '' === $value) {
return '';
}
$s = $this->scopeRepository->find($value);
return $this->translatableStringHelper->localize(
$s->getName()
);
};
}
public function getQueryKeys($data): array
{
return ['aside_activity_user_scope_aggregator'];
}
public function getTitle()
{
return 'export.aggregator.Aggregate by user scope';
}
}

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\AsideActivityBundle\Export\Export;
use Chill\AsideActivityBundle\Export\Declarations;
use Chill\AsideActivityBundle\Repository\AsideActivityRepository;
use Chill\AsideActivityBundle\Security\AsideActivityVoter;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class AvgAsideActivityDuration implements ExportInterface, GroupedExportInterface
{
private AsideActivityRepository $repository;
public function __construct(
AsideActivityRepository $repository
) {
$this->repository = $repository;
}
public function buildForm(FormBuilderInterface $builder)
{
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'export.Average aside activities duration';
}
public function getGroup(): string
{
return 'export.Exports of aside activities';
}
public function getLabels($key, array $values, $data)
{
if ('export_avg_aside_activity_duration' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Average duration aside activities' : $value;
}
public function getQueryKeys($data): array
{
return ['export_avg_aside_activity_duration'];
}
public function getResult($query, $data)
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
{
return 'export.Average aside activities duration';
}
public function getType(): string
{
return Declarations::ASIDE_ACTIVITY_TYPE;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('aside');
$qb
->select('AVG(aside.duration) as export_avg_aside_activity_duration')
->andWhere($qb->expr()->isNotNull('aside.duration'));
return $qb;
}
public function requiredRole(): string
{
return AsideActivityVoter::STATS;
}
public function supportsModifiers(): array
{
return [Declarations::ASIDE_ACTIVITY_TYPE];
}
}

View File

@@ -100,6 +100,8 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface
public function supportsModifiers(): array
{
return [];
return [
Declarations::ASIDE_ACTIVITY_TYPE,
];
}
}

View File

@@ -0,0 +1,243 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\AsideActivityBundle\Export\Export;
use Chill\AsideActivityBundle\Entity\AsideActivity;
use Chill\AsideActivityBundle\Export\Declarations;
use Chill\AsideActivityBundle\Repository\AsideActivityCategoryRepository;
use Chill\AsideActivityBundle\Security\AsideActivityVoter;
use Chill\AsideActivityBundle\Templating\Entity\CategoryRender;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\Helper\DateTimeHelper;
use Chill\MainBundle\Export\Helper\UserHelper;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use DateTimeInterface;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
final class ListAsideActivity implements ListInterface, GroupedExportInterface
{
private AsideActivityCategoryRepository $asideActivityCategoryRepository;
private CategoryRender $categoryRender;
private CenterRepositoryInterface $centerRepository;
private DateTimeHelper $dateTimeHelper;
private EntityManagerInterface $em;
private ScopeRepositoryInterface $scopeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
private UserHelper $userHelper;
public function __construct(
EntityManagerInterface $em,
DateTimeHelper $dateTimeHelper,
UserHelper $userHelper,
ScopeRepositoryInterface $scopeRepository,
CenterRepositoryInterface $centerRepository,
AsideActivityCategoryRepository $asideActivityCategoryRepository,
CategoryRender $categoryRender,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->em = $em;
$this->dateTimeHelper = $dateTimeHelper;
$this->userHelper = $userHelper;
$this->scopeRepository = $scopeRepository;
$this->centerRepository = $centerRepository;
$this->asideActivityCategoryRepository = $asideActivityCategoryRepository;
$this->categoryRender = $categoryRender;
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder)
{
}
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_LIST];
}
public function getDescription()
{
return 'export.aside_activity.List of aside activities';
}
public function getGroup(): string
{
return 'export.Exports of aside activities';
}
public function getLabels($key, array $values, $data)
{
switch ($key) {
case 'id':
case 'note':
return static function ($value) use ($key) {
if ('_header' === $value) {
return 'export.aside_activity.' . $key;
}
return $value ?? '';
};
case 'duration':
return static function ($value) use ($key) {
if ('_header' === $value) {
return 'export.aside_activity.' . $key;
}
if (null === $value) {
return '';
}
if ($value instanceof DateTimeInterface) {
return $value->format('H:i:s');
}
return $value;
};
case 'createdAt':
case 'updatedAt':
case 'date':
return $this->dateTimeHelper->getLabel('export.aside_activity.' . $key);
case 'agent_id':
case 'creator_id':
return $this->userHelper->getLabel($key, $values, 'export.aside_activity.' . $key);
case 'aside_activity_type':
return function ($value) {
if ('_header' === $value) {
return 'export.aside_activity.aside_activity_type';
}
if (null === $value || '' === $value || null === $c = $this->asideActivityCategoryRepository->find($value)) {
return '';
}
return $this->categoryRender->renderString($c, []);
};
case 'main_scope':
return function ($value) {
if ('_header' === $value) {
return 'export.aside_activity.main_scope';
}
if (null === $value || '' === $value || null === $c = $this->scopeRepository->find($value)) {
return '';
}
return $this->translatableStringHelper->localize($c->getName());
};
case 'main_center':
return function ($value) {
if ('_header' === $value) {
return 'export.aside_activity.main_center';
}
/** @var Center $c */
if (null === $value || '' === $value || null === $c = $this->centerRepository->find($value)) {
return '';
}
return $c->getName();
};
default:
throw new LogicException('this key is not supported : ' . $key);
}
}
public function getQueryKeys($data)
{
return [
'id',
'createdAt',
'updatedAt',
'agent_id',
'creator_id',
'main_scope',
'main_center',
'aside_activity_type',
'date',
'duration',
'note',
];
}
/**
* @param QueryBuilder $query
* @param array $data
*/
public function getResult($query, $data): array
{
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
}
public function getTitle()
{
return 'export.aside_activity.List of aside activities';
}
public function getType(): string
{
return Declarations::ASIDE_ACTIVITY_TYPE;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->em->createQueryBuilder()
->from(AsideActivity::class, 'aside')
->leftJoin('aside.agent', 'agent');
$qb
->addSelect('aside.id AS id')
->addSelect('aside.createdAt AS createdAt')
->addSelect('aside.updatedAt AS updatedAt')
->addSelect('IDENTITY(aside.agent) AS agent_id')
->addSelect('IDENTITY(aside.createdBy) AS creator_id')
->addSelect('IDENTITY(agent.mainScope) AS main_scope')
->addSelect('IDENTITY(agent.mainCenter) AS main_center')
->addSelect('IDENTITY(aside.type) AS aside_activity_type')
->addSelect('aside.date')
->addSelect('aside.duration')
->addSelect('aside.note');
return $qb;
}
public function requiredRole(): string
{
return AsideActivityVoter::STATS;
}
public function supportsModifiers()
{
return [Declarations::ASIDE_ACTIVITY_TYPE];
}
}

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\AsideActivityBundle\Export\Export;
use Chill\AsideActivityBundle\Export\Declarations;
use Chill\AsideActivityBundle\Repository\AsideActivityRepository;
use Chill\AsideActivityBundle\Security\AsideActivityVoter;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class SumAsideActivityDuration implements ExportInterface, GroupedExportInterface
{
private AsideActivityRepository $repository;
public function __construct(
AsideActivityRepository $repository
) {
$this->repository = $repository;
}
public function buildForm(FormBuilderInterface $builder)
{
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'export.Sum aside activities duration';
}
public function getGroup(): string
{
return 'export.Exports of aside activities';
}
public function getLabels($key, array $values, $data)
{
if ('export_sum_aside_activity_duration' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Sum duration aside activities' : $value;
}
public function getQueryKeys($data): array
{
return ['export_sum_aside_activity_duration'];
}
public function getResult($query, $data)
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
{
return 'export.Sum aside activities duration';
}
public function getType(): string
{
return Declarations::ASIDE_ACTIVITY_TYPE;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository
->createQueryBuilder('aside');
$qb->select('SUM(aside.duration) as export_sum_aside_activity_duration')
->andWhere($qb->expr()->isNotNull('aside.duration'));
return $qb;
}
public function requiredRole(): string
{
return AsideActivityVoter::STATS;
}
public function supportsModifiers(): array
{
return [Declarations::ASIDE_ACTIVITY_TYPE];
}
}

View File

@@ -80,8 +80,8 @@ class ByActivityTypeFilter implements FilterInterface
public function describeAction($data, $format = 'string'): array
{
$types = array_map(
fn (AsideActivityCategory $t): string => $this->translatableStringHelper->localize($t->getName()),
$this->asideActivityTypeRepository->findBy(['id' => $data['types']->toArray()])
fn (AsideActivityCategory $t): string => $this->translatableStringHelper->localize($t->getTitle()),
$data['types']->toArray()
);
return ['export.filter.Filtered by aside activity type: only %type%', [

View File

@@ -17,7 +17,6 @@ use Chill\MainBundle\Form\Type\Export\FilterType;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
@@ -46,25 +45,18 @@ class ByDateFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->between(
'aside.date',
':date_from',
':date_to'
);
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->andWhere($clause);
$qb->add('where', $where);
$qb->setParameter(
'date_from',
$this->rollingDateConverter->convert($data['date_from'])
);
$qb->setParameter(
)->setParameter(
'date_to',
$this->rollingDateConverter->convert($data['date_to'])
);

View File

@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\AsideActivityBundle\Export\Filter;
use Chill\AsideActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Templating\Entity\UserRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ByUserFilter implements FilterInterface
{
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
$this->userRender = $userRender;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$clause = $qb->expr()->in('aside.agent', ':users');
$qb
->andWhere($clause)
->setParameter('users', $data['accepted_users']);
}
public function applyOn(): string
{
return Declarations::ASIDE_ACTIVITY_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_users', PickUserDynamicType::class, [
'multiple' => true,
'label' => 'Creators',
]);
}
public function describeAction($data, $format = 'string'): array
{
$users = [];
foreach ($data['accepted_users'] as $u) {
$users[] = $this->userRender->renderString($u, []);
}
return ['export.filter.Filtered aside activity by user: only %users%', [
'%users%' => implode(', ', $users),
]];
}
public function getTitle(): string
{
return 'export.filter.Filter aside activity by user';
}
}

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\AsideActivityBundle\Export\Filter;
use Chill\AsideActivityBundle\Entity\AsideActivity;
use Chill\AsideActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class ByUserJobFilter implements FilterInterface
{
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(TranslatableStringHelperInterface $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . AsideActivity::class . ' aside_activity_user_job_filter_act
JOIN aside_activity_user_job_filter_act.agent aside_activity_user_job_filter_user WHERE aside_activity_user_job_filter_user.userJob IN (:aside_activity_user_job_filter_jobs) AND aside_activity_user_job_filter_act = aside'
)
)
->setParameter('aside_activity_user_job_filter_jobs', $data['jobs']);
}
public function applyOn()
{
return Declarations::ASIDE_ACTIVITY_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('jobs', EntityType::class, [
'class' => UserJob::class,
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize($j->getLabel()),
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string')
{
return ['export.filter.Filtered aside activities by user jobs: only %jobs%', [
'%jobs%' => implode(
', ',
array_map(
fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
$data['jobs']->toArray()
)
),
]];
}
public function getTitle()
{
return 'export.filter.Filter by user jobs';
}
}

View File

@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\AsideActivityBundle\Export\Filter;
use Chill\AsideActivityBundle\Entity\AsideActivity;
use Chill\AsideActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class ByUserScopeFilter implements FilterInterface
{
private ScopeRepositoryInterface $scopeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ScopeRepositoryInterface $scopeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb
->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM ' . AsideActivity::class . ' aside_activity_user_scope_filter_act
JOIN aside_activity_user_scope_filter_act.agent aside_activity_user_scope_filter_user WHERE aside_activity_user_scope_filter_user.mainScope IN (:aside_activity_user_scope_filter_scopes) AND aside_activity_user_scope_filter_act = aside '
)
)
->setParameter('aside_activity_user_scope_filter_scopes', $data['scopes']);
}
public function applyOn()
{
return Declarations::ASIDE_ACTIVITY_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('scopes', EntityType::class, [
'class' => Scope::class,
'choices' => $this->scopeRepository->findAllActive(),
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
'multiple' => true,
'expanded' => true,
]);
}
public function describeAction($data, $format = 'string')
{
return ['export.filter.Filtered aside activities by user scope: only %scopes%', [
'%scopes%' => implode(
', ',
array_map(
fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
$data['scopes']->toArray()
)
),
]];
}
public function getTitle()
{
return 'export.filter.Filter by user scope';
}
}

View File

@@ -20,33 +20,3 @@ services:
resource: "../Controller"
autowire: true
autoconfigure: true
## Exports
# indicators
Chill\AsideActivityBundle\Export\Export\CountAsideActivity:
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: count_asideactivity }
# filters
Chill\AsideActivityBundle\Export\Filter\ByDateFilter:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: asideactivity_bydate_filter }
Chill\AsideActivityBundle\Export\Filter\ByActivityTypeFilter:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: asideactivity_activitytype_filter }
# aggregators
Chill\AsideActivityBundle\Export\Aggregator\ByActivityTypeAggregator:
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: asideactivity_activitytype_aggregator }

View File

@@ -3,11 +3,23 @@ services:
autowire: true
autoconfigure: true
Chill\AsideActivityBundle\Export\Export\ListAsideActivity:
tags:
- { name: chill.export, alias: 'list_aside_activity' }
## Indicators
Chill\AsideActivityBundle\Export\Export\CountAsideActivity:
tags:
- { name: chill.export, alias: 'count_aside_activity' }
Chill\AsideActivityBundle\Export\Export\SumAsideActivityDuration:
tags:
- { name: chill.export, alias: 'sum_aside_activity_duration' }
Chill\AsideActivityBundle\Export\Export\AvgAsideActivityDuration:
tags:
- { name: chill.export, alias: 'avg_aside_activity_duration' }
## Filters
chill.aside_activity.export.date_filter:
class: Chill\AsideActivityBundle\Export\Filter\ByDateFilter
@@ -19,9 +31,34 @@ services:
tags:
- { name: chill.export_filter, alias: 'aside_activity_type_filter' }
chill.aside_activity.export.user_job_filter:
class: Chill\AsideActivityBundle\Export\Filter\ByUserJobFilter
tags:
- { name: chill.export_filter, alias: 'aside_activity_user_job_filter' }
chill.aside_activity.export.user_scope_filter:
class: Chill\AsideActivityBundle\Export\Filter\ByUserScopeFilter
tags:
- { name: chill.export_filter, alias: 'aside_activity_user_scope_filter' }
chill.aside_activity.export.user_filter:
class: Chill\AsideActivityBundle\Export\Filter\ByUserFilter
tags:
- { name: chill.export_filter, alias: 'aside_activity_user_filter' }
## Aggregators
chill.aside_activity.export.type_aggregator:
class: Chill\AsideActivityBundle\Export\Aggregator\ByActivityTypeAggregator
tags:
- { name: chill.export_aggregator, alias: activity_type_aggregator }
- { name: chill.export_aggregator, alias: activity_type_aggregator }
chill.aside_activity.export.user_job_aggregator:
class: Chill\AsideActivityBundle\Export\Aggregator\ByUserJobAggregator
tags:
- { name: chill.export_aggregator, alias: aside_activity_user_job_aggregator }
chill.aside_activity.export.user_scope_aggregator:
class: Chill\AsideActivityBundle\Export\Aggregator\ByUserScopeAggregator
tags:
- { name: chill.export_aggregator, alias: aside_activity_user_scope_aggregator }

View File

@@ -29,16 +29,16 @@ location: Lieu
# Crud
crud:
aside_activity:
title_view: Détail de l'activité annexe
title_new: Nouvelle activité annexe
title_edit: Édition d'une activité annexe
title_delete: Supprimer une activité annexe
button_delete: Supprimer
confirm_message_delete: Êtes-vous sûr de vouloir supprimer cette activité annexe?
aside_activity_category:
title_new: Nouvelle catégorie d'activité annexe
title_edit: Édition d'une catégorie de type d'activité
aside_activity:
title_view: Détail de l'activité annexe
title_new: Nouvelle activité annexe
title_edit: Édition d'une activité annexe
title_delete: Supprimer une activité annexe
button_delete: Supprimer
confirm_message_delete: Êtes-vous sûr de vouloir supprimer cette activité annexe?
aside_activity_category:
title_new: Nouvelle catégorie d'activité annexe
title_edit: Édition d'une catégorie de type d'activité
#forms
Create a new aside activity type: Nouvelle categorie d'activité annexe
@@ -169,19 +169,43 @@ Aside activity configuration: Configuration des activités annexes
# exports
export:
Exports of aside activities: Exports des activités annexes
Count aside activities: Nombre d'activités annexes
Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères
filter:
Filter by aside activity date: Filtrer les activités annexes par date
Filter by aside activity type: Filtrer les activités annexes par type d'activité
'Filtered by aside activity type: only %type%': "Filtré par type d'activité annexe: uniquement %type%"
This date should be after the date given in "Implied in an aside activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités annexes après cette date"
Aside activities after this date: Actvitités annexes après cette date
Aside activities before this date: Actvitités annexes avant cette date
aggregator:
Group by aside activity type: Grouper les activités annexes par type d'activité
Aside activity type: Type d'activité annexe
aside_activity:
List of aside activities: Liste des activités annexes
createdAt: Création
updatedAt: Dernière mise à jour
agent_id: Utilisateur
creator_id: Créateur
main_scope: Service principal de l'utilisateur
main_center: Centre principal de l'utilisteur
aside_activity_type: Catégorie d'activité annexe
date: Date
duration: Durée
note: Note
Exports of aside activities: Exports des activités annexes
Count aside activities: Nombre d'activités annexes
Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères
Average aside activities duration: Durée moyenne des activités annexes
Sum aside activities duration: Durée des activités annexes
filter:
Filter by aside activity date: Filtrer les activités annexes par date
Filter by aside activity type: Filtrer les activités annexes par type d'activité
'Filtered by aside activity type: only %type%': "Filtré par type d'activité annexe: uniquement %type%"
Filtered by aside activities between %dateFrom% and %dateTo%: Filtré par date d'activité annexe, entre %dateFrom% et %dateTo%
This date should be after the date given in "Implied in an aside activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités annexes après cette date"
Aside activities after this date: Actvitités annexes après cette date
Aside activities before this date: Actvitités annexes avant cette date
'Filtered aside activity by user: only %users%': "Filtré par utilisateur: uniquement %users%"
Filter aside activity by user: Filtrer par utilisateur
'Filtered aside activities by user jobs: only %jobs%': "Filtré par métier des utilisateurs: uniquement %jobs%"
Filter by user jobs: Filtrer les activités annexes par métier des utilisateurs
'Filtered aside activities by user scope: only %scopes%': "Filtré par service des utilisateur: uniquement %scopes%"
Filter by user scope: Filtrer les activités annexes par service d'utilisateur
aggregator:
Group by aside activity type: Grouper les activités annexes par type d'activité
Aside activity type: Type d'activité annexe
Aggregate by user job: Grouper les activités annexes par métier des utilisateurs
Aggregate by user scope: Grouper les activités annexes par service des utilisateurs
# ROLES
CHILL_ASIDE_ACTIVITY_STATS: Statistiques pour les activités annexes

View File

@@ -29,10 +29,10 @@ class CalculatorManager
public function addCalculator(CalculatorInterface $calculator, bool $default)
{
$this->calculators[$calculator::getAlias()] = $calculator;
$this->calculators[$calculator->getAlias()] = $calculator;
if ($default) {
$this->defaultCalculator[] = $calculator::getAlias();
$this->defaultCalculator[] = $calculator->getAlias();
}
}
@@ -50,7 +50,7 @@ class CalculatorManager
$result = $calculator->calculate($elements);
if (null !== $result) {
$results[$calculator::getAlias()] = $result;
$results[$calculator->getAlias()] = $result;
}
}

View File

@@ -12,12 +12,17 @@ declare(strict_types=1);
namespace Chill\BudgetBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Type of charge.
*
* @ORM\Table(name="chill_budget.charge_type")
* @ORM\Table(name="chill_budget.charge_type",
* uniqueConstraints={@ORM\UniqueConstraint(name="charge_kind_unique_type_idx", fields={"kind"})}
* )
* @ORM\Entity
* @UniqueEntity(fields={"kind"})
*/
class ChargeKind
{
@@ -35,6 +40,8 @@ class ChargeKind
/**
* @ORM\Column(type="string", length=255, options={"default": ""}, nullable=false)
* @Assert\Regex(pattern="/^[a-z0-9\-_]{1,}$/", message="budget.admin.form.kind.only_alphanumeric")
* @Assert\Length(min=3)
*/
private string $kind = '';

View File

@@ -12,12 +12,17 @@ declare(strict_types=1);
namespace Chill\BudgetBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Type of resource.
*
* @ORM\Table(name="chill_budget.resource_type")
* @ORM\Table(name="chill_budget.resource_type", uniqueConstraints={
* @ORM\UniqueConstraint(name="resource_kind_unique_type_idx", fields={"kind"})
* })
* @ORM\Entity
* @UniqueEntity(fields={"kind"})
*/
class ResourceKind
{
@@ -35,6 +40,8 @@ class ResourceKind
/**
* @ORM\Column(type="string", length=255, nullable=false, options={"default": ""})
* @Assert\Regex(pattern="/^[a-z0-9\-_]{1,}$/", message="budget.admin.form.kind.only_alphanumeric")
* @Assert\Length(min=3)
*/
private string $kind = '';

View File

@@ -16,6 +16,7 @@ use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -25,7 +26,11 @@ class ChargeKindType extends AbstractType
{
$builder
->add('name', TranslatableStringFormType::class, [
'label' => 'Nom',
'label' => 'Title',
])
->add('kind', TextType::class, [
'label' => 'budget.admin.form.Charge_kind_key',
'help' => 'budget.admin.form.This kind must contains only alphabeticals characters, and dashes. This string is in use during document generation. Changes may have side effect on document',
])
->add('ordering', NumberType::class)
->add('isActive', CheckboxType::class, [

View File

@@ -16,6 +16,7 @@ use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -25,7 +26,11 @@ class ResourceKindType extends AbstractType
{
$builder
->add('name', TranslatableStringFormType::class, [
'label' => 'Nom',
'label' => 'Title',
])
->add('kind', TextType::class, [
'label' => 'budget.admin.form.Resource_kind_key',
'help' => 'budget.admin.form.This kind must contains only alphabeticals characters, and dashes. This string is in use during document generation. Changes may have side effect on document',
])
->add('ordering', NumberType::class)
->add('isActive', CheckboxType::class, [

View File

@@ -14,9 +14,8 @@ namespace Chill\BudgetBundle\Repository;
use Chill\BudgetBundle\Entity\ChargeKind;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
class ChargeKindRepository implements ObjectRepository
final class ChargeKindRepository implements ChargeKindRepositoryInterface
{
private EntityRepository $repository;
@@ -77,6 +76,11 @@ class ChargeKindRepository implements ObjectRepository
return $this->repository->findOneBy($criteria);
}
public function findOneByKind(string $kind): ?ChargeKind
{
return $this->repository->findOneBy(['kind' => $kind]);
}
public function getClassName(): string
{
return ChargeKind::class;

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\BudgetBundle\Repository;
use Chill\BudgetBundle\Entity\ChargeKind;
use Doctrine\Persistence\ObjectRepository;
interface ChargeKindRepositoryInterface extends ObjectRepository
{
public function find($id): ?ChargeKind;
/**
* @return ChargeType[]
*/
public function findAll(): array;
/**
* @return ChargeType[]
*/
public function findAllActive(): array;
/**
* @return ChargeType[]
*/
public function findAllByType(string $type): array;
/**
* @param mixed|null $limit
* @param mixed|null $offset
*
* @return ChargeType[]
*/
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array;
public function findOneBy(array $criteria): ?ChargeKind;
public function findOneByKind(string $kind): ?ChargeKind;
public function getClassName(): string;
}

View File

@@ -14,9 +14,8 @@ namespace Chill\BudgetBundle\Repository;
use Chill\BudgetBundle\Entity\ResourceKind;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
class ResourceKindRepository implements ObjectRepository
final class ResourceKindRepository implements ResourceKindRepositoryInterface
{
private EntityRepository $repository;
@@ -53,11 +52,6 @@ class ResourceKindRepository implements ObjectRepository
->getResult();
}
public function findOneByKind(string $kind): ?ResourceKind
{
return $this->repository->findOneBy(['kind' => $kind]) ;
}
/**
* @return ResourceType[]
*/
@@ -82,6 +76,11 @@ class ResourceKindRepository implements ObjectRepository
return $this->repository->findOneBy($criteria);
}
public function findOneByKind(string $kind): ?ResourceKind
{
return $this->repository->findOneBy(['kind' => $kind]);
}
public function getClassName(): string
{
return ResourceKind::class;

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\BudgetBundle\Repository;
use Chill\BudgetBundle\Entity\ResourceKind;
use Doctrine\Persistence\ObjectRepository;
interface ResourceKindRepositoryInterface extends ObjectRepository
{
public function find($id): ?ResourceKind;
/**
* @return ResourceType[]
*/
public function findAll(): array;
/**
* @return ResourceType[]
*/
public function findAllActive(): array;
/**
* @return ResourceType[]
*/
public function findAllByType(string $type): array;
/**
* @param mixed|null $limit
* @param mixed|null $offset
*
* @return ResourceType[]
*/
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array;
public function findOneBy(array $criteria): ?ResourceKind;
public function findOneByKind(string $kind): ?ResourceKind;
public function getClassName(): string;
}

View File

@@ -19,7 +19,10 @@
{% for entity in entities %}
<tr>
<td>{{ entity.ordering }}</td>
<td>{{ entity|chill_entity_render_box }}</td>
<td>
{{ entity|chill_entity_render_box }}<br/>
<strong>{{ 'budget.admin.form.Charge_kind_key'|trans }}&nbsp;:</strong> <code>{{ entity.kind }}</code>
</td>
<td style="text-align:center;">
{%- if entity.isActive -%}
<i class="fa fa-check-square-o"></i>
@@ -39,6 +42,8 @@
</tbody>
</table>
{{ chill_pagination(paginator) }}
<ul class="record_actions sticky-form-buttons">
<li>
<a href="{{ path('chill_crud_charge_kind_new') }}" class="btn btn-create">

View File

@@ -19,7 +19,10 @@
{% for entity in entities %}
<tr>
<td>{{ entity.ordering }}</td>
<td>{{ entity|chill_entity_render_box }}</td>
<td>
{{ entity|chill_entity_render_box }}<br/>
<strong>{{ 'budget.admin.form.Resource_kind_key'|trans }}&nbsp;:</strong> <code>{{ entity.kind }}</code>
</td>
<td style="text-align:center;">
{%- if entity.isActive -%}
<i class="fa fa-check-square-o"></i>
@@ -39,6 +42,8 @@
</tbody>
</table>
{{ chill_pagination(paginator) }}
<ul class="record_actions sticky-form-buttons">
<li>
<a href="{{ path('chill_crud_resource_kind_new') }}" class="btn btn-create">

View File

@@ -13,8 +13,8 @@ namespace Chill\BudgetBundle\Service\Summary;
use Chill\BudgetBundle\Entity\ChargeKind;
use Chill\BudgetBundle\Entity\ResourceKind;
use Chill\BudgetBundle\Repository\ChargeKindRepository;
use Chill\BudgetBundle\Repository\ResourceKindRepository;
use Chill\BudgetBundle\Repository\ChargeKindRepositoryInterface;
use Chill\BudgetBundle\Repository\ResourceKindRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Person;
@@ -27,7 +27,7 @@ use function count;
/**
* Helps to find a summary of the budget: the sum of resources and charges.
*/
class SummaryBudget implements SummaryBudgetInterface
final class SummaryBudget implements SummaryBudgetInterface
{
private const QUERY_CHARGE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, charge_id AS kind_id FROM chill_budget.charge WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY charge_id';
@@ -37,23 +37,19 @@ class SummaryBudget implements SummaryBudgetInterface
private const QUERY_RESOURCE_BY_PERSON = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, resource_id AS kind_id FROM chill_budget.resource WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY resource_id';
private ChargeKindRepository $chargeKindRepository;
private array $chargeLabels;
private ChargeKindRepositoryInterface $chargeKindRepository;
private EntityManagerInterface $em;
private ResourceKindRepository $resourceKindRepository;
private array $resourcesLabels;
private ResourceKindRepositoryInterface $resourceKindRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
EntityManagerInterface $em,
TranslatableStringHelperInterface $translatableStringHelper,
ResourceKindRepository $resourceKindRepository,
ChargeKindRepository $chargeKindRepository
ResourceKindRepositoryInterface $resourceKindRepository,
ChargeKindRepositoryInterface $chargeKindRepository
) {
$this->em = $em;
$this->translatableStringHelper = $translatableStringHelper;
@@ -129,19 +125,19 @@ class SummaryBudget implements SummaryBudgetInterface
private function getEmptyChargeArray(): array
{
$keys = array_map(static fn (ChargeKind $kind) => $kind->getId(), $this->chargeKindRepository->findAll());
$keys = array_map(static fn (ChargeKind $kind) => $kind->getKind(), $this->chargeKindRepository->findAll());
return array_combine($keys, array_map(function ($id) {
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->chargeKindRepository->find($id)->getName()), 'comment' => ''];
return array_combine($keys, array_map(function ($kind) {
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->chargeKindRepository->findOneByKind($kind)->getName()), 'comment' => ''];
}, $keys));
}
private function getEmptyResourceArray(): array
{
$keys = array_map(static fn (ResourceKind $kind) => $kind->getId(), $this->resourceKindRepository->findAll());
$keys = array_map(static fn (ResourceKind $kind) => $kind->getKind(), $this->resourceKindRepository->findAll());
return array_combine($keys, array_map(function ($id) {
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->resourceKindRepository->find($id)->getName()), 'comment' => ''];
return array_combine($keys, array_map(function ($kind) {
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->resourceKindRepository->findOneByKind($kind)->getName()), 'comment' => ''];
}, $keys));
}
@@ -155,7 +151,7 @@ class SummaryBudget implements SummaryBudgetInterface
$chargeKind = $this->chargeKindRepository->find($row['kind_id']);
if (null === $chargeKind) {
throw new RuntimeException('charge kind not found');
throw new RuntimeException('charge kind not found: ' . $row['kind_id']);
}
$result[$chargeKind->getKind()] = [
'sum' => (float) $row['sum'],
@@ -171,7 +167,7 @@ class SummaryBudget implements SummaryBudgetInterface
$resourceKind = $this->resourceKindRepository->find($row['kind_id']);
if (null === $resourceKind) {
throw new RuntimeException('charge kind not found');
throw new RuntimeException('charge kind not found: ' . $row['kind_id']);
}
$result[$resourceKind->getKind()] = [

View File

@@ -0,0 +1,159 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\BudgetBundle\Tests\Service\Summary;
use Chill\BudgetBundle\Entity\ChargeKind;
use Chill\BudgetBundle\Entity\ResourceKind;
use Chill\BudgetBundle\Repository\ChargeKindRepositoryInterface;
use Chill\BudgetBundle\Repository\ResourceKindRepositoryInterface;
use Chill\BudgetBundle\Service\Summary\SummaryBudget;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use ReflectionClass;
use RuntimeException;
/**
* @internal
*
* @coversNothing
*/
final class SummaryBudgetTest extends TestCase
{
use ProphecyTrait;
public function testGenerateSummaryForPerson(): void
{
$queryCharges = $this->prophesize(AbstractQuery::class);
$queryCharges->getResult()->willReturn([
[
'sum' => 250.0,
'comment' => '',
'kind_id' => 1, // kind: rental
],
]);
$queryCharges->setParameters(Argument::type('array'))
->will(static function ($args, $query) {
return $query;
});
$queryResources = $this->prophesize(AbstractQuery::class);
$queryResources->getResult()->willReturn([
[
'sum' => 1500.0,
'comment' => '',
'kind_id' => 2, // kind: 'salary',
],
]);
$queryResources->setParameters(Argument::type('array'))
->will(static function ($args, $query) {
return $query;
});
$em = $this->prophesize(EntityManagerInterface::class);
$em->createNativeQuery(Argument::type('string'), Argument::type(Query\ResultSetMapping::class))
->will(static function ($args) use ($queryResources, $queryCharges) {
if (false !== strpos($args[0], 'chill_budget.resource')) {
return $queryResources->reveal();
}
if (false !== strpos($args[0], 'chill_budget.charge')) {
return $queryCharges->reveal();
}
throw new RuntimeException('this query does not have a stub counterpart: ' . $args[0]);
});
$chargeRepository = $this->prophesize(ChargeKindRepositoryInterface::class);
$chargeRepository->findAll()->willReturn([
$rental = (new ChargeKind())->setKind('rental')->setName(['fr' => 'Rental']),
$other = (new ChargeKind())->setKind('other')->setName(['fr' => 'Other']),
]);
$chargeRepository->find(1)->willReturn($rental);
$chargeRepository->findOneByKind('rental')->willReturn($rental);
$chargeRepository->findOneByKind('other')->willReturn($other);
$resourceRepository = $this->prophesize(ResourceKindRepositoryInterface::class);
$resourceRepository->findAll()->willReturn([
$salary = (new ResourceKind())->setKind('salary')->setName(['fr' => 'Salary']),
$misc = (new ResourceKind())->setKind('misc')->setName(['fr' => 'Misc']),
]);
$resourceRepository->find(2)->willReturn($salary);
$resourceRepository->findOneByKind('salary')->willReturn($salary);
$resourceRepository->findOneByKind('misc')->willReturn($misc);
$translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
$translatableStringHelper->localize(Argument::type('array'))->will(static function ($arg) {
return $arg[0]['fr'];
});
$person = new Person();
$personReflection = new ReflectionClass($person);
$personIdReflection = $personReflection->getProperty('id');
$personIdReflection->setAccessible(true);
$personIdReflection->setValue($person, 1);
$household = new Household();
$householdReflection = new ReflectionClass($household);
$householdId = $householdReflection->getProperty('id');
$householdId->setAccessible(true);
$householdId->setValue($household, 1);
$householdMember = (new HouseholdMember())->setPerson($person)
->setStartDate(new DateTimeImmutable('1 month ago'));
$household->addMember($householdMember);
$summaryBudget = new SummaryBudget(
$em->reveal(),
$translatableStringHelper->reveal(),
$resourceRepository->reveal(),
$chargeRepository->reveal()
);
$summary = $summaryBudget->getSummaryForPerson($person);
$summaryForHousehold = $summaryBudget->getSummaryForHousehold($household);
// we check the structure for the summary. The structure is the same for household
// and persons
$expected = [
'charges' => [
'rental' => ['sum' => 250.0, 'comment' => '', 'label' => 'Rental'],
'other' => ['sum' => 0.0, 'comment' => '', 'label' => 'Other'],
],
'resources' => [
'salary' => ['sum' => 1500.0, 'comment' => '', 'label' => 'Salary'],
'misc' => ['sum' => 0.0, 'comment' => '', 'label' => 'Misc'],
],
];
foreach ([$summaryForHousehold, $summary] as $summary) {
$this->assertIsArray($summary);
$this->assertEqualsCanonicalizing(['charges', 'resources'], array_keys($summary));
$this->assertEqualsCanonicalizing(['rental', 'other'], array_keys($summary['charges']));
$this->assertEqualsCanonicalizing(['salary', 'misc'], array_keys($summary['resources']));
foreach ($expected as $resCha => $contains) {
foreach ($contains as $kind => $row) {
$this->assertEqualsCanonicalizing($row, $summary[$resCha][$kind]);
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\Migrations\Budget;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20230209161546 extends AbstractMigration
{
public function down(Schema $schema): void
{
$this->addSql('DROP INDEX resource_kind_unique_type_idx');
$this->addSql('DROP INDEX charge_kind_unique_type_idx');
}
public function getDescription(): string
{
return 'Budget: add unique constraint on kind for charge_kind and resource_kind';
}
public function up(Schema $schema): void
{
$this->addSql("UPDATE chill_budget.resource_type SET kind=md5(random()::text) WHERE kind = ''");
$this->addSql("UPDATE chill_budget.charge_type SET kind=md5(random()::text) WHERE kind = ''");
$this->addSql('CREATE UNIQUE INDEX resource_kind_unique_type_idx ON chill_budget.resource_type (kind);');
$this->addSql('CREATE UNIQUE INDEX charge_kind_unique_type_idx ON chill_budget.charge_type (kind);');
}
}

View File

@@ -77,6 +77,13 @@ The balance: Différence entre ressources et charges
Valid since %startDate% until %endDate%: Valide depuis le %startDate% jusqu'au %endDate%
Valid since %startDate%: Valide depuis le %startDate%
budget:
admin:
form:
Charge_kind_key: Clé d'identification
Resource_kind_key: Clé d'identification
This kind must contains only alphabeticals characters, and dashes. This string is in use during document generation. Changes may have side effect on document: Cette clé sert à identifier le type de charge ou de revenu lors de la génération de document. Seuls les caractères alpha-numériques sont autorisés. Modifier cette clé peut avoir un effet lors de la génération de nouveaux documents.
# ROLES
Budget elements: Budget
CHILL_BUDGET_ELEMENT_CREATE: Créer une ressource/charge

View File

@@ -2,8 +2,8 @@ Budget: Budget
Resource: Inkomsten
Charge: Onkosten
Budget for %name%: Budget van %name%
Budget for household %household%: Budget van gezin
Current budget household members: Actuele budget van gezinsleden
Budget for household %household%: Budget van huishouden
Current budget household members: Actuele budget van leden huishouden
Show budget of %name%: Toon budget van %name%
See complete budget: Toon volledige budget
Hide budget: Verbergen

View File

@@ -1,2 +1,8 @@
The amount cannot be empty: Le montant ne peut pas être vide ou égal à zéro
The budget element's end date must be after the start date: La date de fin doit être après la date de début
The budget element's end date must be after the start date: La date de fin doit être après la date de début
budget:
admin:
form:
kind:
only_alphanumeric

View File

@@ -145,9 +145,6 @@ class CalendarDocController
$returnParams = ['id' => $calendarDoc->getCalendar()->getPerson()->getId()];
break;
default:
throw new \LogicException(sprintf("This context '%s' is not supported", $calendarDoc->getCalendar()->getContext()));
}
$form = $this->formFactory->createBuilder()

View File

@@ -61,7 +61,7 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
->setEmail('centreA@test.chill.social')
->setLocationType($type = new LocationType())
->setPhonenumber1(PhoneNumberUtil::getInstance()->parse('+3287653812'));
$type->setTitle('Service');
$type->setTitle(['fr' => 'Service']);
$address->setStreet('Rue des Épaules')->setStreetNumber('14')
->setPostcode($postCode = new PostalCode());
$postCode->setCode('4145')->setName('Houte-Si-Plout')->setCountry(

View File

@@ -12,6 +12,8 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\DataFixtures\ORM;
use Chill\CalendarBundle\Entity\Invite;
use Chill\MainBundle\DataFixtures\ORM\LoadUsers;
use Chill\MainBundle\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
use Doctrine\Persistence\ObjectManager;
@@ -33,14 +35,21 @@ class LoadInvite extends Fixture implements FixtureGroupInterface
public function load(ObjectManager $manager): void
{
$arr = [
['name' => ['fr' => 'Rendez-vous décliné']],
['name' => ['fr' => 'Rendez-vous accepté']],
[
'name' => ['fr' => 'Rendez-vous décliné'],
'status' => Invite::DECLINED,
],
[
'name' => ['fr' => 'Rendez-vous accepté'],
'status' => Invite::ACCEPTED,
],
];
foreach ($arr as $a) {
echo 'Creating calendar invite : ' . $a['name']['fr'] . "\n";
$invite = (new Invite())
->setStatus($a['name']);
->setStatus($a['status'])
->setUser($this->getRandomUser());
$manager->persist($invite);
$reference = 'Invite_' . $a['name']['fr'];
$this->addReference($reference, $invite);
@@ -49,4 +58,11 @@ class LoadInvite extends Fixture implements FixtureGroupInterface
$manager->flush();
}
private function getRandomUser(): User
{
$userRef = array_rand(LoadUsers::$refs);
return $this->getReference($userRef);
}
}

View File

@@ -512,9 +512,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
*/
public function getUsers(): Collection
{
return $this->getInvites()->map(static function (Invite $i) {
return $i->getUser();
});
return $this->getInvites()->map(static function (Invite $i) { return $i->getUser(); });
}
public function hasCalendarRange(): bool
@@ -599,9 +597,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
}
$invite = $this->invites
->filter(static function (Invite $invite) use ($user) {
return $invite->getUser() === $user;
})
->filter(static function (Invite $invite) use ($user) { return $invite->getUser() === $user; })
->first();
$this->removeInvite($invite);

View File

@@ -89,14 +89,10 @@ class CalendarToRemoteHandler implements MessageHandlerInterface
$newInvites = array_filter(
array_map(
function ($id) {
return $this->inviteRepository->find($id);
},
function ($id) { return $this->inviteRepository->find($id); },
$calendarMessage->getNewInvitesIds(),
),
static function (?Invite $invite) {
return null !== $invite;
}
static function (?Invite $invite) { return null !== $invite; }
);
$this->calendarConnector->syncCalendar(

View File

@@ -22,6 +22,7 @@ use Chill\MainBundle\Entity\User;
use DateTimeImmutable;
use LogicException;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use function array_key_exists;
@@ -74,9 +75,18 @@ class MapCalendarToUser
public function getDefaultUserCalendar(string $idOrUserPrincipalName): ?array
{
$value = $this->machineHttpClient->request('GET', "users/{$idOrUserPrincipalName}/calendars", [
'query' => ['$filter' => 'isDefaultCalendar eq true'],
])->toArray()['value'];
try {
$value = $this->machineHttpClient->request('GET', "users/{$idOrUserPrincipalName}/calendars", [
'query' => ['$filter' => 'isDefaultCalendar eq true'],
])->toArray()['value'];
} catch (ClientExceptionInterface $e) {
$this->logger->error('[MapCalendarToUser] Error while listing calendars for a user', [
'http_status_code' => $e->getResponse()->getStatusCode(),
'id_user' => $idOrUserPrincipalName,
]);
return null;
}
return $value[0] ?? null;
}

View File

@@ -34,6 +34,7 @@ use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -64,6 +65,8 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
private OnBehalfOfUserHttpClient $userHttpClient;
private Security $security;
public function __construct(
CalendarRepository $calendarRepository,
CalendarRangeRepository $calendarRangeRepository,
@@ -74,7 +77,8 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
OnBehalfOfUserHttpClient $userHttpClient,
RemoteEventConverter $remoteEventConverter,
TranslatorInterface $translator,
UrlGeneratorInterface $urlGenerator
UrlGeneratorInterface $urlGenerator,
Security $security
) {
$this->calendarRepository = $calendarRepository;
$this->calendarRangeRepository = $calendarRangeRepository;
@@ -86,6 +90,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
$this->translator = $translator;
$this->urlGenerator = $urlGenerator;
$this->userHttpClient = $userHttpClient;
$this->security = $security;
}
public function countEventsForUser(User $user, DateTimeImmutable $startDate, DateTimeImmutable $endDate): int
@@ -133,6 +138,24 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
public function isReady(): bool
{
$user = $this->security->getUser();
if (!$user instanceof User) {
// this is not a user from chill. This is not the role of this class to
// restrict access, so we will just say that we do not have to do anything more
// here...
return true;
}
if (null === $this->mapCalendarToUser->getUserId($user)) {
// this user is not mapped with remote calendar. The user will have to wait for
// the next calendar subscription iteration
$this->logger->debug('mark user ready for msgraph calendar as he does not have any mapping', [
'userId' => $user->getId(),
]);
return true;
}
return $this->tokenStorage->hasToken();
}
@@ -167,9 +190,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
]
)->toArray();
$ids = array_map(static function ($item) {
return $item['id'];
}, $bareEvents['value']);
$ids = array_map(static function ($item) { return $item['id']; }, $bareEvents['value']);
$existingIdsInRange = $this->calendarRangeRepository->findRemoteIdsPresent($ids);
$existingIdsInCalendar = $this->calendarRepository->findRemoteIdsPresent($ids);

View File

@@ -215,7 +215,7 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface
$qb
->where(
$qb->expr()->orX(
// the calendar where the person is the main person:
// the calendar where the person is the main person:
$qb->expr()->eq('c.person', ':person'),
// when the calendar is in a reachable period, and contains person
$qb->expr()->andX(

View File

@@ -17,30 +17,20 @@
<td class="eval">
<ul class="eval_title">
<li>
{{ mm.mimeIcon(d.storedObject.type) }}
{{ d.storedObject.title }}
{% if d.dateTimeVersion < d.calendar.dateTimeVersion %}
<span class="badge bg-danger">{{ 'chill_calendar.Document outdated'|trans }}</span>
{% endif %}
<ul class="record_actions small inline">
{% if chill_document_is_editable(d.storedObject) and is_granted('CHILL_CALENDAR_DOC_EDIT', d) %}
<li>
<a href="{{ chill_path_add_return_path('chill_calendar_calendardoc_delete', {'id': d.id})}}" class="btn btn-delete"></a>
</li>
<li>
{{ d.storedObject|chill_document_edit_button }}
</li>
{% endif %}
{% if is_granted('CHILL_CALENDAR_DOC_EDIT', d) %}
<li>
<a href="{{ chill_path_add_return_path('chill_calendar_calendardoc_edit', {'id': d.id})}}" class="btn btn-edit"></a>
</li>
{% endif %}
<li>
{{ m.download_button(d.storedObject, d.storedObject.title) }}
</li>
</ul>
<div class="row">
<div class="col text-start">
{{ d.storedObject.title }}
{% if d.dateTimeVersion < d.calendar.dateTimeVersion %}
<span class="badge bg-danger">{{ 'chill_calendar.Document outdated'|trans }}</span>
{% endif %}
</div>
<div class="col-md-auto text-center">
{{ mm.mimeIcon(d.storedObject.type) }}
</div>
<div class="col col-lg-4 text-end">
{{ d.storedObject|chill_document_button_group(d.storedObject.title, is_granted('CHILL_CALENDAR_DOC_EDIT', d), {'small': true}) }}
</div>
</div>
</li>
</ul>
</td>

View File

@@ -7,7 +7,7 @@
<div id="mainUser"></div> {# <=== vue component: mainUser #}
<h2 class="chill-red">{{ 'Concerned groups'|trans }}</h2>
<h2 class="chill-red">{{ 'Concerned groups calendar'|trans }}</h2>
{%- if form.persons is defined -%}
{{ form_widget(form.persons) }}

View File

@@ -10,13 +10,13 @@
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_answer') }}
{{ encore_entry_script_tags('mod_async_upload') }}
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_answer') }}
{{ encore_entry_link_tags('mod_async_upload') }}
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
{% endblock %}
{% block content %}

View File

@@ -7,7 +7,7 @@
<div id="mainUser"></div> {# <=== vue component: mainUser #}
<h2 class="chill-red">{{ 'Concerned groups'|trans }}</h2>
<h2 class="chill-red">{{ 'Concerned groups calendar'|trans }}</h2>
{%- if form.mainUser is defined -%}
{{ form_row(form.mainUser) }}

View File

@@ -14,7 +14,7 @@
<dd>{{ entity.mainUser }}</dd>
</dl>
<h2 class="chill-red">{{ 'Concerned groups'|trans }}</h2>
<h2 class="chill-red">{{ 'Concerned groups calendar'|trans }}</h2>
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': 'calendar_' ~ context, 'render': 'bloc' } %}

View File

@@ -18,8 +18,10 @@ use Chill\DocGeneratorBundle\Service\Context\BaseContextData;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Templating\Entity\PersonRender;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@@ -39,6 +41,10 @@ final class CalendarContext implements CalendarContextInterface
private PersonRender $personRender;
private PersonRepository $personRepository;
private ThirdPartyRepository $thirdPartyRepository;
private ThirdPartyRender $thirdPartyRender;
private TranslatableStringHelperInterface $translatableStringHelper;
@@ -48,14 +54,18 @@ final class CalendarContext implements CalendarContextInterface
EntityManagerInterface $entityManager,
NormalizerInterface $normalizer,
PersonRender $personRender,
PersonRepository $personRepository,
ThirdPartyRender $thirdPartyRender,
ThirdPartyRepository $thirdPartyRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->baseContextData = $baseContextData;
$this->entityManager = $entityManager;
$this->normalizer = $normalizer;
$this->personRender = $personRender;
$this->personRepository = $personRepository;
$this->thirdPartyRender = $thirdPartyRender;
$this->thirdPartyRepository = $thirdPartyRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
@@ -146,7 +156,7 @@ final class CalendarContext implements CalendarContextInterface
$options = $this->getOptions($template);
$data = array_merge(
$this->baseContextData->getData(),
$this->baseContextData->getData($contextGenerationData['creator'] ?? null),
[
'calendar' => $this->normalizer->normalize($entity, 'docgen', ['docgen:expects' => Calendar::class, 'groups' => ['docgen:read']]),
]
@@ -226,8 +236,44 @@ final class CalendarContext implements CalendarContextInterface
return true;
}
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
$normalized = [];
$normalized['title'] = $data['title'] ?? '';
foreach (['mainPerson', 'thirdParty'] as $k) {
if (isset($data[$k])) {
$normalized[$k] = $data[$k]->getId();
}
}
return $normalized;
}
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
$denormalized = [];
$denormalized['title'] = $data['title'];
if (null !== ($data['mainPerson'] ?? null)) {
if (null === $person = $this->personRepository->find($data['mainPerson'])) {
throw new \RuntimeException('person not found');
}
$denormalized['mainPerson'] = $person;
}
if (null !== ($data['thirdParty'] ?? null)) {
if (null === $thirdParty = $this->thirdPartyRepository->find($data['thirdParty'])) {
throw new \RuntimeException('third party not found');
}
$denormalized['thirdParty'] = $thirdParty;
}
return $denormalized;
}
/**
* @param array{mainPerson?: Person, thirdParty?: ThirdParty, title: string} $contextGenerationData
* param array{mainPerson?: Person, thirdParty?: ThirdParty, title: string} $contextGenerationData
*/
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{

View File

@@ -56,6 +56,10 @@ interface CalendarContextInterface extends DocGeneratorContextWithPublicFormInte
*/
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool;
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array;
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array;
/**
* @param Calendar $entity
*/

View File

@@ -96,7 +96,7 @@ final class CalendarTypeTest extends TypeTestCase
];
$calendar = new Calendar();
$calendar->setMainUser(new class () extends User {
$calendar->setMainUser(new class() extends User {
public function getId()
{
return '1';
@@ -114,12 +114,8 @@ final class CalendarTypeTest extends TypeTestCase
$this->assertEquals(8, $calendar->getCalendarRange()->getId());
$this->assertEquals(9, $calendar->getLocation()->getId());
$this->assertEquals(true, $calendar->getSendSMS());
$this->assertContains(2, $calendar->getUsers()->map(static function (User $u) {
return $u->getId();
}));
$this->assertContains(3, $calendar->getUsers()->map(static function (User $u) {
return $u->getId();
}));
$this->assertContains(2, $calendar->getUsers()->map(static function (User $u) { return $u->getId(); }));
$this->assertContains(3, $calendar->getUsers()->map(static function (User $u) { return $u->getId(); }));
}
protected function getExtensions()
@@ -151,9 +147,7 @@ final class CalendarTypeTest extends TypeTestCase
->will(static function ($args) {
return implode(
',',
array_map(static function ($p) {
return $p->getId();
}, $args[0])
array_map(static function ($p) { return $p->getId(); }, $args[0])
);
});
$transformer->transform(Argument::exact(null))
@@ -162,9 +156,7 @@ final class CalendarTypeTest extends TypeTestCase
->will(static function ($args) {
return implode(
',',
array_map(static function ($p) {
return $p->getId();
}, $args[0]->toArray())
array_map(static function ($p) { return $p->getId(); }, $args[0]->toArray())
);
});
$transformer->reverseTransform(Argument::type('string'))

View File

@@ -205,7 +205,7 @@ final class CalendarContextTest extends TestCase
?NormalizerInterface $normalizer = null
): CalendarContext {
$baseContext = $this->prophesize(BaseContextData::class);
$baseContext->getData()->willReturn(['base_context' => 'data']);
$baseContext->getData(null)->willReturn(['base_context' => 'data']);
$personRender = $this->prophesize(PersonRender::class);
$personRender->renderString(Argument::type(Person::class), [])->willReturn('person name');

View File

@@ -4,7 +4,7 @@ My calendar list: Mes rendez-vous
There is no calendar items.: Il n'y a pas de rendez-vous
Remove calendar item: Supprimer le rendez-vous
Are you sure you want to remove the calendar item?: Êtes-vous sûr de vouloir supprimer le rendez-vous?
Concerned groups: Parties concernées
Concerned groups calendar: Parties concernées
Calendar data: Données du rendez-vous
Update calendar: Modifier le rendez-vous
main user concerned: Utilisateur concerné

View File

@@ -23,6 +23,9 @@ interface DocGeneratorContextWithPublicFormInterface extends DocGeneratorContext
*/
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void;
/**
* Fill the form with initial data
*/
public function getFormData(DocGeneratorTemplate $template, $entity): array;
/**
@@ -31,4 +34,14 @@ interface DocGeneratorContextWithPublicFormInterface extends DocGeneratorContext
* @param mixed $entity
*/
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool;
/**
* Transform the data from the form into serializable data, storable into messenger's message
*/
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array;
/**
* Reverse the data from the messenger's message into data usable for doc's generation
*/
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array;
}

View File

@@ -16,67 +16,58 @@ use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
use Chill\DocGeneratorBundle\Context\Exception\ContextNotFoundException;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\GeneratorDriver\DriverInterface;
use Chill\DocGeneratorBundle\GeneratorDriver\Exception\TemplateException;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
use Chill\DocGeneratorBundle\Service\Generator\GeneratorInterface;
use Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationMessage;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Serializer\Model\Collection;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
// TODO à mettre dans services
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Throwable;
use function strlen;
use const JSON_PRETTY_PRINT;
final class DocGeneratorTemplateController extends AbstractController
{
private HttpClientInterface $client;
private ContextManager $contextManager;
private DocGeneratorTemplateRepository $docGeneratorTemplateRepository;
private DriverInterface $driver;
private EntityManagerInterface $entityManager;
private LoggerInterface $logger;
private GeneratorInterface $generator;
private MessageBusInterface $messageBus;
private PaginatorFactory $paginatorFactory;
private StoredObjectManagerInterface $storedObjectManager;
public function __construct(
ContextManager $contextManager,
DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
DriverInterface $driver,
LoggerInterface $logger,
GeneratorInterface $generator,
MessageBusInterface $messageBus,
PaginatorFactory $paginatorFactory,
HttpClientInterface $client,
StoredObjectManagerInterface $storedObjectManager,
EntityManagerInterface $entityManager
) {
$this->contextManager = $contextManager;
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->driver = $driver;
$this->logger = $logger;
$this->generator = $generator;
$this->messageBus = $messageBus;
$this->paginatorFactory = $paginatorFactory;
$this->client = $client;
$this->storedObjectManager = $storedObjectManager;
$this->entityManager = $entityManager;
}
@@ -94,7 +85,6 @@ final class DocGeneratorTemplateController extends AbstractController
): Response {
return $this->generateDocFromTemplate(
$template,
$entityClassName,
$entityId,
$request,
true
@@ -115,7 +105,6 @@ final class DocGeneratorTemplateController extends AbstractController
): Response {
return $this->generateDocFromTemplate(
$template,
$entityClassName,
$entityId,
$request,
false
@@ -185,7 +174,6 @@ final class DocGeneratorTemplateController extends AbstractController
private function generateDocFromTemplate(
DocGeneratorTemplate $template,
string $entityClassName,
int $entityId,
Request $request,
bool $isTest
@@ -206,7 +194,7 @@ final class DocGeneratorTemplateController extends AbstractController
if (null === $entity) {
throw new NotFoundHttpException(
sprintf('Entity with classname %s and id %s is not found', $entityClassName, $entityId)
sprintf('Entity with classname %s and id %s is not found', $context->getEntityClass(), $entityId)
);
}
@@ -259,98 +247,68 @@ final class DocGeneratorTemplateController extends AbstractController
}
}
$document = $template->getFile();
if ($isTest && ($contextGenerationData['test_file'] instanceof File)) {
$dataDecrypted = file_get_contents($contextGenerationData['test_file']->getPathname());
} else {
try {
$dataDecrypted = $this->storedObjectManager->read($document);
} catch (Throwable $exception) {
throw $exception;
}
}
// transform context generation data
$contextGenerationDataSanitized =
$context instanceof DocGeneratorContextWithPublicFormInterface ?
$context->contextGenerationDataNormalize($template, $entity, $contextGenerationData)
: [];
// if is test, render the data or generate the doc
if ($isTest && isset($form) && $form['show_data']->getData()) {
// very ugly hack...
dd($context->getData($template, $entity, $contextGenerationData));
}
try {
$generatedResource = $this
->driver
->generateFromString(
$dataDecrypted,
$template->getFile()->getType(),
$context->getData($template, $entity, $contextGenerationData),
$template->getFile()->getFilename()
);
} catch (TemplateException $e) {
return new Response(
implode("\n", $e->getErrors()),
400,
[
'Content-Type' => 'text/plain',
]
return $this->render('@ChillDocGenerator/Generator/debug_value.html.twig', [
'datas' => json_encode($context->getData($template, $entity, $contextGenerationData), JSON_PRETTY_PRINT),
]);
} elseif ($isTest) {
$generated = $this->generator->generateDocFromTemplate(
$template,
$entityId,
$contextGenerationDataSanitized,
null,
true,
isset($form) ? $form['test_file']->getData() : null
);
}
if ($isTest) {
return new Response(
$generatedResource,
$generated,
Response::HTTP_OK,
[
'Content-Transfer-Encoding', 'binary',
'Content-Type' => 'application/vnd.oasis.opendocument.text',
'Content-Disposition' => 'attachment; filename="generated.odt"',
'Content-Length' => strlen($generatedResource),
'Content-Length' => strlen($generated),
],
);
}
/** @var StoredObject $storedObject */
$storedObject = (new ObjectNormalizer())
->denormalize(
[
'type' => $template->getFile()->getType(),
'filename' => sprintf('%s_odt', uniqid('doc_', true)),
],
StoredObject::class
);
try {
$this->storedObjectManager->write($storedObject, $generatedResource);
} catch (Throwable $exception) {
throw $exception;
}
// this is not a test
// we prepare the object to store the document
$storedObject = (new StoredObject())
->setStatus(StoredObject::STATUS_PENDING)
;
$this->entityManager->persist($storedObject);
try {
$context
->storeGenerated(
$template,
$storedObject,
$entity,
$contextGenerationData
);
} catch (Exception $e) {
$this
->logger
->error(
'Unable to store the associated document to entity',
[
'entityClassName' => $entityClassName,
'entityId' => $entityId,
'contextKey' => $context->getName(),
]
);
throw $e;
}
// we store the generated document
$context
->storeGenerated(
$template,
$storedObject,
$entity,
$contextGenerationData
);
$this->entityManager->flush();
$this->messageBus->dispatch(
new RequestGenerationMessage(
$this->getUser(),
$template,
$entityId,
$storedObject,
$contextGenerationDataSanitized,
)
);
return $this
->redirectToRoute(
'chill_wopi_file_edit',

View File

@@ -53,6 +53,7 @@ final class RelatorioDriver implements DriverInterface
$response = $this->client->request('POST', $this->url, [
'headers' => $form->getPreparedHeaders()->toArray(),
'body' => $form->bodyToIterable(),
'timeout' => '300',
]);
return $response->getContent();

View File

@@ -0,0 +1,16 @@
{{ creator.label }},
{{ 'docgen.failure_email.The generation of the document {template_name} failed'|trans({'{template_name}': template.name|localize_translatable_string}) }}
{{ 'docgen.failure_email.Forward this email to your administrator for solving'|trans }}
{{ 'docgen.failure_email.References'|trans }}:
{% if errors|length > 0 %}
{{ 'docgen.failure_email.The following errors were encoutered'|trans }}:
{% for error in errors %}
- {{ error }}
{% endfor %}
{% endif %}
- template_id: {{ template.id }}
- stored_object_destination_id: {{ stored_object_id }}

View File

@@ -0,0 +1,8 @@
<html>
<head>
<title>{{ 'Doc generator debug'|trans }}</title>
</head>
<body>
<pre>{{ datas }}</pre>
</body>
</html>

View File

@@ -21,18 +21,14 @@ class BaseContextData
{
private NormalizerInterface $normalizer;
private Security $security;
public function __construct(Security $security, NormalizerInterface $normalizer)
public function __construct(NormalizerInterface $normalizer)
{
$this->security = $security;
$this->normalizer = $normalizer;
}
public function getData(): array
public function getData(?User $user = null): array
{
$data = [];
$user = $this->security->getUser();
$data['creator'] = $this->normalizer->normalize(
$user instanceof User ? $user : null,

View File

@@ -0,0 +1,146 @@
<?php
namespace Chill\DocGeneratorBundle\Service\Generator;
use Chill\DocGeneratorBundle\Context\ContextManagerInterface;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\GeneratorDriver\DriverInterface;
use Chill\DocGeneratorBundle\GeneratorDriver\Exception\TemplateException;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\MainBundle\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\File\File;
class Generator implements GeneratorInterface
{
private ContextManagerInterface $contextManager;
private DriverInterface $driver;
private EntityManagerInterface $entityManager;
private LoggerInterface $logger;
private StoredObjectManagerInterface $storedObjectManager;
private const LOG_PREFIX = '[docgen generator] ';
public function __construct(
ContextManagerInterface $contextManager,
DriverInterface $driver,
EntityManagerInterface $entityManager,
LoggerInterface $logger,
StoredObjectManagerInterface $storedObjectManager
) {
$this->contextManager = $contextManager;
$this->driver = $driver;
$this->entityManager = $entityManager;
$this->logger = $logger;
$this->storedObjectManager = $storedObjectManager;
}
/**
* @template T of File|null
* @template B of bool
* @param B $isTest
* @param (B is true ? T : null) $testFile
* @psalm-return (B is true ? string : null)
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface|\Throwable
*/
public function generateDocFromTemplate(
DocGeneratorTemplate $template,
int $entityId,
array $contextGenerationDataNormalized,
?StoredObject $destinationStoredObject = null,
bool $isTest = false,
?File $testFile = null,
?User $creator = null
): ?string {
if ($destinationStoredObject instanceof StoredObject && StoredObject::STATUS_PENDING !== $destinationStoredObject->getStatus()) {
$this->logger->info(self::LOG_PREFIX.'Aborting generation of an already generated document');
throw new ObjectReadyException();
}
$this->logger->info(self::LOG_PREFIX.'Starting generation of a document', [
'entity_id' => $entityId,
'destination_stored_object' => $destinationStoredObject === null ? null : $destinationStoredObject->getId()
]);
$context = $this->contextManager->getContextByDocGeneratorTemplate($template);
$entity = $this
->entityManager
->find($context->getEntityClass(), $entityId)
;
if (null === $entity) {
throw new RelatedEntityNotFoundException($template->getEntity(), $entityId);
}
$contextGenerationDataNormalized = array_merge(
$contextGenerationDataNormalized,
['creator' => $creator],
$context instanceof DocGeneratorContextWithPublicFormInterface ?
$context->contextGenerationDataDenormalize($template, $entity, $contextGenerationDataNormalized)
: []
);
$data = $context->getData($template, $entity, $contextGenerationDataNormalized);
$destinationStoredObjectId = $destinationStoredObject instanceof StoredObject ? $destinationStoredObject->getId() : null;
$this->entityManager->clear();
gc_collect_cycles();
if (null !== $destinationStoredObjectId) {
$destinationStoredObject = $this->entityManager->find(StoredObject::class, $destinationStoredObjectId);
}
if ($isTest && ($testFile instanceof File)) {
$templateDecrypted = file_get_contents($testFile->getPathname());
} else {
$templateDecrypted = $this->storedObjectManager->read($template->getFile());
}
try {
$generatedResource = $this
->driver
->generateFromString(
$templateDecrypted,
$template->getFile()->getType(),
$data,
$template->getFile()->getFilename()
);
} catch (TemplateException $e) {
throw new GeneratorException($e->getErrors(), $e);
}
if ($isTest) {
$this->logger->info(self::LOG_PREFIX.'Finished generation of a document', [
'is_test' => true,
'entity_id' => $entityId,
'destination_stored_object' => $destinationStoredObject === null ? null : $destinationStoredObject->getId()
]);
return $generatedResource;
}
/** @var StoredObject $storedObject */
$destinationStoredObject
->setType($template->getFile()->getType())
->setFilename(sprintf('%s_odt', uniqid('doc_', true)))
->setStatus(StoredObject::STATUS_READY)
;
$this->storedObjectManager->write($destinationStoredObject, $generatedResource);
$this->entityManager->flush();
$this->logger->info(self::LOG_PREFIX.'Finished generation of a document', [
'entity_id' => $entityId,
'destination_stored_object' => $destinationStoredObject->getId(),
]);
return null;
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\DocGeneratorBundle\Service\Generator;
use RuntimeException;
use Throwable;
class GeneratorException extends RuntimeException
{
/**
* @var list<string>
*/
private array $errors;
public function __construct(array $errors = [], ?Throwable $previous = null)
{
$this->errors = $errors;
parent::__construct(
'Could not generate the document',
15252,
$previous
);
}
/**
* @return array
*/
public function getErrors(): array
{
return $this->errors;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Chill\DocGeneratorBundle\Service\Generator;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\User;
use Symfony\Component\HttpFoundation\File\File;
interface GeneratorInterface
{
/**
* @template T of File|null
* @template B of bool
* @param B $isTest
* @param (B is true ? T : null) $testFile
* @psalm-return (B is true ? string : null)
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface|\Throwable
*/
public function generateDocFromTemplate(
DocGeneratorTemplate $template,
int $entityId,
array $contextGenerationDataNormalized,
?StoredObject $destinationStoredObject = null,
bool $isTest = false,
?File $testFile = null,
?User $creator = null
): ?string;
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\DocGeneratorBundle\Service\Generator;
use RuntimeException;
class ObjectReadyException extends RuntimeException
{
public function __construct()
{
parent::__construct('object is already ready', 6698856);
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\DocGeneratorBundle\Service\Generator;
use RuntimeException;
class RelatedEntityNotFoundException extends RuntimeException
{
public function __construct(string $relatedEntityClass, int $relatedEntityId, ?Throwable $previous = null)
{
parent::__construct(
sprintf('Related entity not found: %s, %s', $relatedEntityClass, $relatedEntityId),
99876652,
$previous
);
}
}

View File

@@ -0,0 +1,156 @@
<?php
namespace Chill\DocGeneratorBundle\Service\Messenger;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
use Chill\DocGeneratorBundle\Service\Generator\GeneratorException;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\StoredObjectRepository;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Contracts\Translation\TranslatorInterface;
final class OnGenerationFails implements EventSubscriberInterface
{
private DocGeneratorTemplateRepository $docGeneratorTemplateRepository;
private EntityManagerInterface $entityManager;
private LoggerInterface $logger;
private MailerInterface $mailer;
private StoredObjectRepository $storedObjectRepository;
private TranslatorInterface $translator;
private UserRepositoryInterface $userRepository;
const LOG_PREFIX = '[docgen failed] ';
/**
* @param DocGeneratorTemplateRepository $docGeneratorTemplateRepository
* @param EntityManagerInterface $entityManager
* @param LoggerInterface $logger
* @param MailerInterface $mailer
* @param StoredObjectRepository $storedObjectRepository
* @param TranslatorInterface $translator
* @param UserRepositoryInterface $userRepository
*/
public function __construct(
DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
EntityManagerInterface $entityManager,
LoggerInterface $logger,
MailerInterface $mailer,
StoredObjectRepository $storedObjectRepository,
TranslatorInterface $translator,
UserRepositoryInterface $userRepository
) {
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->entityManager = $entityManager;
$this->logger = $logger;
$this->mailer = $mailer;
$this->storedObjectRepository = $storedObjectRepository;
$this->translator = $translator;
$this->userRepository = $userRepository;
}
public static function getSubscribedEvents()
{
return [
WorkerMessageFailedEvent::class => 'onMessageFailed'
];
}
public function onMessageFailed(WorkerMessageFailedEvent $event): void
{
if ($event->willRetry()) {
return;
}
if (!$event->getEnvelope()->getMessage() instanceof RequestGenerationMessage) {
return;
}
/** @var \Chill\DocGeneratorBundle\Service\Messenger\RequestGenerationMessage $message */
$message = $event->getEnvelope()->getMessage();
$this->logger->error(self::LOG_PREFIX.'Docgen failed', [
'stored_object_id' => $message->getDestinationStoredObjectId(),
'entity_id' => $message->getEntityId(),
'template_id' => $message->getTemplateId(),
'creator_id' => $message->getCreatorId(),
'throwable_class' => get_class($event->getThrowable()),
]);
$this->markObjectAsFailed($message);
$this->warnCreator($message, $event);
}
private function markObjectAsFailed(RequestGenerationMessage $message): void
{
$object = $this->storedObjectRepository->find($message->getDestinationStoredObjectId());
if (null === $object) {
$this->logger->error(self::LOG_PREFIX.'Stored object not found', ['stored_object_id', $message->getDestinationStoredObjectId()]);
}
$object->setStatus(StoredObject::STATUS_FAILURE);
$this->entityManager->flush();
}
private function warnCreator(RequestGenerationMessage $message, WorkerMessageFailedEvent $event): void
{
if (null === $creatorId = $message->getCreatorId()) {
$this->logger->info(self::LOG_PREFIX.'creator id is null');
return;
}
if (null === $creator = $this->userRepository->find($creatorId)) {
$this->logger->error(self::LOG_PREFIX.'Creator not found with given id', ['creator_id', $creatorId]);
return;
}
if (null === $creator->getEmail() || '' === $creator->getEmail()) {
$this->logger->info(self::LOG_PREFIX.'Creator does not have any email', ['user' => $creator->getUsernameCanonical()]);
return;
}
// if the exception is not a GeneratorException, we try the previous one...
$throwable = $event->getThrowable();
if (!$throwable instanceof GeneratorException) {
$throwable = $throwable->getPrevious();
}
if ($throwable instanceof GeneratorException) {
$errors = $throwable->getErrors();
} else {
$errors = [$throwable->getTraceAsString()];
}
if (null === $template = $this->docGeneratorTemplateRepository->find($message->getTemplateId())) {
$this->logger->info(self::LOG_PREFIX.'Template not found', ['template_id' => $message->getTemplateId()]);
return;
}
$email = (new TemplatedEmail())
->to($creator->getEmail())
->subject($this->translator->trans('docgen.failure_email.The generation of a document failed'))
->textTemplate('@ChillDocGenerator/Email/on_generation_failed_email.txt.twig')
->context([
'errors' => $errors,
'template' => $template,
'creator' => $creator,
'stored_object_id' => $message->getDestinationStoredObjectId(),
]);
$this->mailer->send($email);
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace Chill\DocGeneratorBundle\Service\Messenger;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
use Chill\DocGeneratorBundle\Service\Generator\Generator;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\StoredObjectRepository;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
/**
* Handle the request of document generation
*/
class RequestGenerationHandler implements MessageHandlerInterface
{
private StoredObjectRepository $storedObjectRepository;
private DocGeneratorTemplateRepository $docGeneratorTemplateRepository;
private EntityManagerInterface $entityManager;
private Generator $generator;
private LoggerInterface $logger;
private UserRepositoryInterface $userRepository;
public const AUTHORIZED_TRIALS = 5;
private const LOG_PREFIX = '[docgen message handler] ';
public function __construct(
DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
EntityManagerInterface $entityManager,
Generator $generator,
LoggerInterface $logger,
StoredObjectRepository $storedObjectRepository,
UserRepositoryInterface $userRepository
) {
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->entityManager = $entityManager;
$this->generator = $generator;
$this->logger = $logger;
$this->storedObjectRepository = $storedObjectRepository;
$this->userRepository = $userRepository;
}
public function __invoke(RequestGenerationMessage $message)
{
if (null === $template = $this->docGeneratorTemplateRepository->find($message->getTemplateId())) {
throw new \RuntimeException('template not found: ' . $message->getTemplateId());
}
if (null === $destinationStoredObject = $this->storedObjectRepository->find($message->getDestinationStoredObjectId())) {
throw new \RuntimeException('destination stored object not found : ' . $message->getDestinationStoredObjectId());
}
if ($destinationStoredObject->getGenerationTrialsCounter() >= self::AUTHORIZED_TRIALS) {
throw new UnrecoverableMessageHandlingException('maximum number of retry reached');
}
$creator = $this->userRepository->find($message->getCreatorId());
$destinationStoredObject->addGenerationTrial();
$this->entityManager->createQuery('UPDATE '.StoredObject::class.' s SET s.generationTrialsCounter = s.generationTrialsCounter + 1 WHERE s.id = :id')
->setParameter('id', $destinationStoredObject->getId())
->execute();
$this->generator->generateDocFromTemplate(
$template,
$message->getEntityId(),
$message->getContextGenerationData(),
$destinationStoredObject,
false,
null,
$creator
);
$this->logger->info(self::LOG_PREFIX.'Request generation finished', [
'template_id' => $message->getTemplateId(),
'destination_stored_object' => $message->getDestinationStoredObjectId(),
'duration_int' => (new \DateTimeImmutable('now'))->getTimestamp() - $message->getCreatedAt()->getTimestamp(),
]);
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace Chill\DocGeneratorBundle\Service\Messenger;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\User;
class RequestGenerationMessage
{
private int $creatorId;
private int $templateId;
private int $entityId;
private int $destinationStoredObjectId;
private array $contextGenerationData;
private \DateTimeImmutable $createdAt;
public function __construct(
User $creator,
DocGeneratorTemplate $template,
int $entityId,
StoredObject $destinationStoredObject,
array $contextGenerationData
) {
$this->creatorId = $creator->getId();
$this->templateId = $template->getId();
$this->entityId = $entityId;
$this->destinationStoredObjectId = $destinationStoredObject->getId();
$this->contextGenerationData = $contextGenerationData;
$this->createdAt = new \DateTimeImmutable('now');
}
public function getCreatorId(): int
{
return $this->creatorId;
}
public function getDestinationStoredObjectId(): int
{
return $this->destinationStoredObjectId;
}
public function getTemplateId(): int
{
return $this->templateId;
}
public function getEntityId(): int
{
return $this->entityId;
}
public function getContextGenerationData(): array
{
return $this->contextGenerationData;
}
public function getCreatedAt(): \DateTimeImmutable
{
return $this->createdAt;
}
}

View File

@@ -20,10 +20,14 @@ services:
resource: '../Serializer/Normalizer/'
tags:
- { name: 'serializer.normalizer', priority: -152 }
Chill\DocGeneratorBundle\Serializer\Normalizer\CollectionDocGenNormalizer:
tags:
- { name: 'serializer.normalizer', priority: -126 }
Chill\DocGeneratorBundle\Service\Context\:
resource: "../Service/Context"
Chill\DocGeneratorBundle\Controller\:
resource: "../Controller"
autowire: true
@@ -34,18 +38,20 @@ services:
autowire: true
autoconfigure: true
Chill\DocGeneratorBundle\Service\Context\:
resource: "../Service/Context/"
autowire: true
autoconfigure: true
Chill\DocGeneratorBundle\GeneratorDriver\:
resource: "../GeneratorDriver/"
autowire: true
autoconfigure: true
Chill\DocGeneratorBundle\Service\Messenger\:
resource: "../Service/Messenger/"
Chill\DocGeneratorBundle\Service\Generator\Generator: ~
Chill\DocGeneratorBundle\Service\Generator\GeneratorInterface: '@Chill\DocGeneratorBundle\Service\Generator\Generator'
Chill\DocGeneratorBundle\Driver\RelatorioDriver: '@Chill\DocGeneratorBundle\Driver\DriverInterface'
Chill\DocGeneratorBundle\Context\ContextManager:
arguments:
$contexts: !tagged_iterator { tag: chill_docgen.context, default_index_method: getKey }
Chill\DocGeneratorBundle\Context\ContextManagerInterface: '@Chill\DocGeneratorBundle\Context\ContextManager'

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\DocGenerator;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20230214192558 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add status, template_id and fix defaults on chill_doc.stored_object';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_doc.stored_object ADD template_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE chill_doc.stored_object ADD status TEXT DEFAULT \'ready\' NOT NULL');
$this->addSql('ALTER TABLE chill_doc.stored_object ADD createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
$this->addSql('UPDATE chill_doc.stored_object SET createdAt = creation_date');
$this->addSql('ALTER TABLE chill_doc.stored_object ADD createdBy_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE chill_doc.stored_object DROP creation_date;');
$this->addSql('ALTER TABLE chill_doc.stored_object ALTER type SET DEFAULT \'\'');
$this->addSql('ALTER TABLE chill_doc.stored_object ALTER title DROP DEFAULT');
$this->addSql('COMMENT ON COLUMN chill_doc.stored_object.createdAt IS \'(DC2Type:datetime_immutable)\'');
$this->addSql('ALTER TABLE chill_doc.stored_object ADD CONSTRAINT FK_49604E365DA0FB8 FOREIGN KEY (template_id) REFERENCES chill_docgen_template (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE chill_doc.stored_object ADD CONSTRAINT FK_49604E363174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('CREATE INDEX IDX_49604E365DA0FB8 ON chill_doc.stored_object (template_id)');
$this->addSql('CREATE INDEX IDX_49604E363174800F ON chill_doc.stored_object (createdBy_id)');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_doc.stored_object DROP CONSTRAINT FK_49604E365DA0FB8');
$this->addSql('ALTER TABLE chill_doc.stored_object DROP CONSTRAINT FK_49604E363174800F');
$this->addSql('ALTER TABLE chill_doc.stored_object DROP template_id');
$this->addSql('ALTER TABLE chill_doc.stored_object DROP status');
$this->addSql('ALTER TABLE chill_doc.stored_object ADD creation_date TIMESTAMP(0) DEFAULT NOW()');
$this->addSql('UPDATE chill_doc.stored_object SET creation_date = createdAt');
$this->addSql('ALTER TABLE chill_doc.stored_object DROP createdAt');
$this->addSql('ALTER TABLE chill_doc.stored_object DROP createdBy_id');
$this->addSql('ALTER TABLE chill_doc.stored_object ALTER title SET DEFAULT \'\'');
$this->addSql('ALTER TABLE chill_doc.stored_object ALTER type DROP DEFAULT');
}
}

View File

@@ -0,0 +1,141 @@
<?php
namespace Chill\DocGeneratorBundle\tests\Service\Context\Generator;
use Chill\DocGeneratorBundle\Context\ContextManagerInterface;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextInterface;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\GeneratorDriver\DriverInterface;
use Chill\DocGeneratorBundle\Service\Generator\Generator;
use Chill\DocGeneratorBundle\Service\Generator\ObjectReadyException;
use Chill\DocGeneratorBundle\Service\Generator\RelatedEntityNotFoundException;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Log\NullLogger;
class GeneratorTest extends TestCase
{
use ProphecyTrait;
public function testSuccessfulGeneration(): void
{
$template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject())
->setType('application/test'));
$destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING);
$reflection = new \ReflectionClass($destinationStoredObject);
$reflection->getProperty('id')->setAccessible(true);
$reflection->getProperty('id')->setValue($destinationStoredObject, 1);
$entity = new class {};
$data = [];
$context = $this->prophesize(DocGeneratorContextInterface::class);
$context->getData($template, $entity, Argument::type('array'))->willReturn($data);
$context->getName()->willReturn('dummy_context');
$context->getEntityClass()->willReturn('DummyClass');
$context = $context->reveal();
$contextManagerInterface = $this->prophesize(ContextManagerInterface::class);
$contextManagerInterface->getContextByDocGeneratorTemplate($template)
->willReturn($context);
$driver = $this->prophesize(DriverInterface::class);
$driver->generateFromString('template', 'application/test', $data, Argument::any())
->willReturn('generated');
$entityManager = $this->prophesize(EntityManagerInterface::class);
$entityManager->find(StoredObject::class, 1)
->willReturn($destinationStoredObject);
$entityManager->find('DummyClass', Argument::type('int'))
->willReturn($entity);
$entityManager->clear()->shouldBeCalled();
$entityManager->flush()->shouldBeCalled();
$storedObjectManager = $this->prophesize(StoredObjectManagerInterface::class);
$storedObjectManager->read($templateStoredObject)->willReturn('template');
$storedObjectManager->write($destinationStoredObject, 'generated')->shouldBeCalled();
$generator = new Generator(
$contextManagerInterface->reveal(),
$driver->reveal(),
$entityManager->reveal(),
new NullLogger(),
$storedObjectManager->reveal()
);
$generator->generateDocFromTemplate(
$template,
1,
[],
$destinationStoredObject
);
}
public function testPreventRegenerateDocument(): void
{
$this->expectException(ObjectReadyException::class);
$generator = new Generator(
$this->prophesize(ContextManagerInterface::class)->reveal(),
$this->prophesize(DriverInterface::class)->reveal(),
$this->prophesize(EntityManagerInterface::class)->reveal(),
new NullLogger(),
$this->prophesize(StoredObjectManagerInterface::class)->reveal()
);
$template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject())
->setType('application/test'));
$destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_READY);
$generator->generateDocFromTemplate(
$template,
1,
[],
$destinationStoredObject
);
}
public function testRelatedEntityNotFound(): void
{
$this->expectException(RelatedEntityNotFoundException::class);
$template = (new DocGeneratorTemplate())->setFile($templateStoredObject = (new StoredObject())
->setType('application/test'));
$destinationStoredObject = (new StoredObject())->setStatus(StoredObject::STATUS_PENDING);
$reflection = new \ReflectionClass($destinationStoredObject);
$reflection->getProperty('id')->setAccessible(true);
$reflection->getProperty('id')->setValue($destinationStoredObject, 1);
$context = $this->prophesize(DocGeneratorContextInterface::class);
$context->getName()->willReturn('dummy_context');
$context->getEntityClass()->willReturn('DummyClass');
$context = $context->reveal();
$contextManagerInterface = $this->prophesize(ContextManagerInterface::class);
$contextManagerInterface->getContextByDocGeneratorTemplate($template)
->willReturn($context);
$entityManager = $this->prophesize(EntityManagerInterface::class);
$entityManager->find(Argument::type('string'), Argument::type('int'))
->willReturn(null);
$generator = new Generator(
$contextManagerInterface->reveal(),
$this->prophesize(DriverInterface::class)->reveal(),
$entityManager->reveal(),
new NullLogger(),
$this->prophesize(StoredObjectManagerInterface::class)->reveal()
);
$generator->generateDocFromTemplate(
$template,
1,
[],
$destinationStoredObject
);
}
}

View File

@@ -10,6 +10,16 @@ docgen:
test generate: Tester la génération
With context %name%: 'Avec le contexte "%name%"'
Doc generation failed: La génération de ce document a échoué
Doc generation is pending: La génération de ce document est en cours
Come back later: Revenir plus tard
failure_email:
The generation of a document failed: La génération d'un document a échoué
The generation of the document {template_name} failed: La génération d'un document à partir du modèle {{ template_name }} a échoué.
The following errors were encoutered: Les erreurs suivantes ont été rencontrées
Forward this email to your administrator for solving: Faites suivre ce message vers votre administrateur pour la résolution du problème.
References: Références
crud:
docgen_template:
@@ -19,4 +29,4 @@ crud:
Show data instead of generating: Montrer les données au lieu de générer le document
Template file: Fichier modèle
Template file: Fichier modèle

View File

@@ -0,0 +1,39 @@
<?php
namespace Chill\DocStoreBundle\Controller;
use Chill\DocStoreBundle\Entity\StoredObject;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
class StoredObjectApiController
{
private Security $security;
public function __construct(Security $security)
{
$this->security = $security;
}
/**
* @Route("/api/1.0/doc-store/stored-object/{uuid}/is-ready")
*/
public function isDocumentReady(StoredObject $storedObject): Response
{
if (!$this->security->isGranted('ROLE_USER')) {
throw new AccessDeniedHttpException();
}
return new JsonResponse(
[
'id' => $storedObject->getId(),
'filename' => $storedObject->getFilename(),
'status' => $storedObject->getStatus(),
'type' => $storedObject->getType(),
]
);
}
}

View File

@@ -14,6 +14,9 @@ namespace Chill\DocStoreBundle\Entity;
use ChampsLibres\AsyncUploaderBundle\Model\AsyncFileInterface;
use ChampsLibres\AsyncUploaderBundle\Validator\Constraints\AsyncFileExists;
use ChampsLibres\WopiLib\Contract\Entity\Document;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
@@ -30,13 +33,13 @@ use Symfony\Component\Serializer\Annotation as Serializer;
* message="The file is not stored properly"
* )
*/
class StoredObject implements AsyncFileInterface, Document
class StoredObject implements AsyncFileInterface, Document, TrackCreationInterface
{
/**
* @ORM\Column(type="datetime", name="creation_date")
* @Serializer\Groups({"read", "write"})
*/
private DateTimeInterface $creationDate;
public const STATUS_READY = "ready";
public const STATUS_PENDING = "pending";
public const STATUS_FAILURE = "failure";
use TrackCreationTrait;
/**
* @ORM\Column(type="json", name="datas")
@@ -48,7 +51,7 @@ class StoredObject implements AsyncFileInterface, Document
* @ORM\Column(type="text")
* @Serializer\Groups({"read", "write"})
*/
private $filename;
private string $filename = '';
/**
* @ORM\Id
@@ -56,7 +59,7 @@ class StoredObject implements AsyncFileInterface, Document
* @ORM\Column(type="integer")
* @Serializer\Groups({"read", "write"})
*/
private $id;
private ?int $id;
/**
* @var int[]
@@ -78,7 +81,7 @@ class StoredObject implements AsyncFileInterface, Document
private string $title = '';
/**
* @ORM\Column(type="text", name="type")
* @ORM\Column(type="text", name="type", options={"default": ""})
* @Serializer\Groups({"read", "write"})
*/
private string $type = '';
@@ -89,28 +92,68 @@ class StoredObject implements AsyncFileInterface, Document
*/
private UuidInterface $uuid;
public function __construct()
/**
* @ORM\ManyToOne(targetEntity=DocGeneratorTemplate::class)
*/
private ?DocGeneratorTemplate $template;
/**
* @ORM\Column(type="text", options={"default": "ready"})
* @Serializer\Groups({"read"})
*/
private string $status;
/**
* Store the number of times a generation has been tryied for this StoredObject.
*
* This is a workaround, as generation consume lot of memory, and out-of-memory errors
* are not handled by messenger.
*
* @ORM\Column(type="integer", options={"default": 0})
*/
private int $generationTrialsCounter = 0;
/**
* @param StoredObject::STATUS_* $status
*/
public function __construct(string $status = "ready")
{
$this->creationDate = new DateTime();
$this->uuid = Uuid::uuid4();
$this->status = $status;
}
public function addGenerationTrial(): self
{
$this->generationTrialsCounter++;
return $this;
}
/**
* @Serializer\Groups({"read", "write"})
* @deprecated
*/
public function getCreationDate(): DateTime
{
return $this->creationDate;
return DateTime::createFromImmutable($this->createdAt);
}
public function getDatas()
public function getDatas(): array
{
return $this->datas;
}
public function getFilename()
public function getFilename(): string
{
return $this->filename;
}
public function getId()
public function getGenerationTrialsCounter(): int
{
return $this->generationTrialsCounter;
}
public function getId(): ?int
{
return $this->id;
}
@@ -133,6 +176,14 @@ class StoredObject implements AsyncFileInterface, Document
return $this->getFilename();
}
/**
* @return StoredObject::STATUS_*
*/
public function getStatus(): string
{
return $this->status;
}
public function getTitle()
{
return $this->title;
@@ -153,52 +204,92 @@ class StoredObject implements AsyncFileInterface, Document
return (string) $this->uuid;
}
public function setCreationDate(DateTime $creationDate)
/**
* @Serializer\Groups({"write"})
* @deprecated
*/
public function setCreationDate(DateTime $creationDate): self
{
$this->creationDate = $creationDate;
$this->createdAt = \DateTimeImmutable::createFromMutable($creationDate);
return $this;
}
public function setDatas(?array $datas)
public function setDatas(?array $datas): self
{
$this->datas = (array) $datas;
return $this;
}
public function setFilename(?string $filename)
public function setFilename(?string $filename): self
{
$this->filename = (string) $filename;
return $this;
}
public function setIv(?array $iv)
public function setIv(?array $iv): self
{
$this->iv = (array) $iv;
return $this;
}
public function setKeyInfos(?array $keyInfos)
public function setKeyInfos(?array $keyInfos): self
{
$this->keyInfos = (array) $keyInfos;
return $this;
}
public function setTitle(?string $title)
/**
* @param StoredObject::STATUS_* $status
*/
public function setStatus(string $status): self
{
$this->status = $status;
return $this;
}
public function setTitle(?string $title): self
{
$this->title = (string) $title;
return $this;
}
public function setType(?string $type)
public function setType(?string $type): self
{
$this->type = (string) $type;
return $this;
}
public function getTemplate(): ?DocGeneratorTemplate
{
return $this->template;
}
public function hasTemplate(): bool
{
return null !== $this->template;
}
public function setTemplate(?DocGeneratorTemplate $template): StoredObject
{
$this->template = $template;
return $this;
}
public function isPending(): bool
{
return self::STATUS_PENDING === $this->getStatus();
}
public function isFailure(): bool
{
return self::STATUS_FAILURE === $this->getStatus();
}
}

View File

@@ -1,7 +1,8 @@
import {_createI18n} from "../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n";
import DocumentActionButtonsGroup from "../../vuejs/DocumentActionButtonsGroup.vue";
import {createApp} from "vue";
import {StoredObject} from "../../types";
import {StoredObject, StoredObjectStatusChange} from "../../types";
import {is_object_ready} from "../../vuejs/StoredObjectButton/helpers";
const i18n = _createI18n({});
@@ -15,19 +16,32 @@ window.addEventListener('DOMContentLoaded', function (e) {
filename: string,
canEdit: string,
storedObject: string,
small: string,
buttonSmall: string,
};
const
storedObject = JSON.parse(datasets.storedObject),
storedObject = JSON.parse(datasets.storedObject) as StoredObject,
filename = datasets.filename,
canEdit = datasets.canEdit === '1',
small = datasets.small === '1'
small = datasets.buttonSmall === '1'
;
return { storedObject, filename, canEdit, small };
},
template: '<document-action-buttons-group :can-edit="canEdit" :filename="filename" :stored-object="storedObject" :small="small"></document-action-buttons-group>',
template: '<document-action-buttons-group :can-edit="canEdit" :filename="filename" :stored-object="storedObject" :small="small" @on-stored-object-status-change="onStoredObjectStatusChange"></document-action-buttons-group>',
methods: {
onStoredObjectStatusChange: function(newStatus: StoredObjectStatusChange): void {
this.$data.storedObject.status = newStatus.status;
this.$data.storedObject.filename = newStatus.filename;
this.$data.storedObject.type = newStatus.type;
// remove eventual div which inform pending status
document.querySelectorAll(`[data-docgen-is-pending="${this.$data.storedObject.id}"]`)
.forEach(function(el) {
el.remove();
});
}
}
});
app.use(i18n).mount(el);

View File

@@ -1,5 +1,7 @@
import {DateTime} from "../../../ChillMainBundle/Resources/public/types";
export type StoredObjectStatus = "ready"|"failure"|"pending";
export interface StoredObject {
id: number,
@@ -13,7 +15,15 @@ export interface StoredObject {
keyInfos: object,
title: string,
type: string,
uuid: string
uuid: string,
status: StoredObjectStatus,
}
export interface StoredObjectStatusChange {
id: number,
filename: string,
status: StoredObjectStatus,
type: string,
}
/**

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