Compare commits

..

72 Commits

Author SHA1 Message Date
d1a4891b9d upgrade phpunit config 2023-06-05 17:58:52 +02:00
5749660760 Merge branch '108-fix-acc-period-transition-notification' into 'master'
Fixed: Do not send a confirmation message when the accompanying period is back to CONFIRM state

Closes #108

See merge request Chill-Projet/chill-bundles!549
2023-05-25 13:30:15 +00:00
b679dbe26c Fixed: Do not send a confirmation message when period is mark_active back 2023-05-25 15:24:14 +02:00
6c3fa5cb98 Fixed: [UI] in designation and redispatch list, the period's statuses
were'nt shown correctly
2023-05-25 15:22:52 +02:00
6bdd1f31d3 Merge branch '98-rewrite-download-button' into 'master'
Fixed: vue downloadButton: add more log and various improvements

See merge request Chill-Projet/chill-bundles!547
2023-05-25 10:42:10 +00:00
ff3dab0934 Fixed: vue downloadButton: add more log and various improvements
- create a dedicated button for opening
- use nextTick before clicking on the "opening" button
2023-05-25 12:36:47 +02:00
977299192f Merge branch 'deselect-checkbox-exports' into 'master'
Add button to deselect all centers in export

See merge request Chill-Projet/chill-bundles!544
2023-05-24 14:34:57 +00:00
77997e2b6f FIX [review] fix review comments 2023-05-24 16:24:58 +02:00
6e618e688b DX [php-cs-fixer] 2023-05-24 15:50:25 +02:00
dad36927f3 FEATURE [export] uncheck all centers button 2023-05-24 15:34:39 +02:00
f5448f9d95 DX [phpstan] fixes 2023-05-24 14:02:49 +02:00
a31c4063a1 DX [cs-fixer] fixes 2023-05-24 14:02:05 +02:00
c8f95528c0 Merge branch '98-fix-download-document' into 'master'
Resolve "Dans certaines situations, le téléchargement et déchiffrement des documents ne fonctionne pas"

See merge request Chill-Projet/chill-bundles!541
2023-05-24 11:40:03 +00:00
20023dff67 DX: fix cs 2023-05-24 13:35:35 +02:00
d82a3e0ff6 Fixed: [download document] add a target when downloading document 2023-05-24 13:13:57 +02:00
04359f27c6 Revert "Revert "FIX [duplicates] reinstate gestion des doublons""
This reverts commit 5109490aad.
2023-05-24 13:11:40 +02:00
5109490aad Revert "FIX [duplicates] reinstate gestion des doublons"
This reverts commit 5351223d44.
2023-05-24 13:09:04 +02:00
8b82e0c535 FIX [rights] user shouldn't be allowed to see accompanyingperiods from within household 2023-05-23 18:19:31 +02:00
5351223d44 FIX [duplicates] reinstate gestion des doublons 2023-05-23 18:18:44 +02:00
674e057f67 fixes for user permission list 2023-05-20 00:54:35 +02:00
b2e79b677b Feature: [admin] add an export list of all permissions associated to
each user
2023-05-20 00:34:53 +02:00
748e566c7e Feature: [admin] list of users on csv format 2023-05-20 00:03:25 +02:00
1f4c51f3bc Merge branch '96-worflow-regression' into 'master'
Workflow regression in accompanying period work

See merge request Chill-Projet/chill-bundles!538
2023-05-19 10:17:58 +00:00
dc6eeccaab Merge branch 'permission-page/modernize' into 'master'
DX et UX: permissions page

See merge request Chill-Projet/chill-bundles!530
2023-05-19 10:03:08 +00:00
0083842509 DX: modernize controller for permissions groups and order them alphabetically 2023-05-19 11:58:11 +02:00
ca7be4ecd0 Merge branch 'export/allow-check-multiple-geographical-zones' into 'master'
Feature: [export] allow to check multiple geographical layers in geographical aggregators

See merge request Chill-Projet/chill-bundles!532
2023-05-19 09:54:25 +00:00
785c70fe92 Merge branch 'exports/filters-on-work-date' into 'master'
Feature: [exports] add filters for start and end date for accompanying period works

See merge request Chill-Projet/chill-bundles!533
2023-05-19 09:51:06 +00:00
8bbca7e61a Merge branch 'master' into export/allow-check-multiple-geographical-zones 2023-05-19 11:50:12 +02:00
5d21612c2e Merge branch '98-fix-download-document' into 'master'
Fix: [document download] better memory management and introduce delay

See merge request Chill-Projet/chill-bundles!539
2023-05-19 09:45:57 +00:00
fb9f182edd Merge branch 'docgen-add-household-composition' into 'master'
Feature: add Household composition on household in docgen

See merge request Chill-Projet/chill-bundles!537
2023-05-19 09:39:56 +00:00
bbd3d2a83f Fix: [document download] better memory management and introduce delay
before opening

Related to https://gitlab.com/Chill-Projet/chill-bundles/-/issues/98
2023-05-19 11:34:25 +02:00
e87420dc57 Merge branch '20-finalisation-cire' into 'master'
Finalisation cire

See merge request Chill-Projet/chill-bundles!529
2023-05-19 08:26:39 +00:00
f1bf02d2b4 css classes 2023-05-17 19:38:48 +02:00
8a35c2e2ee Fix workflow regression with accompanying period work (introduced by commit 6b90a7d2a7 24/01/2023) 2023-05-17 16:33:07 +02:00
fbd555e89a Feature: add Household composition on household in docgen 2023-05-17 16:05:53 +02:00
66dc027354 Fixed: fix first execution of accompanying period step change cronjob 2023-05-17 13:27:20 +02:00
8863e0a92e Fixed: force string on username 2023-05-17 13:24:44 +02:00
db9fef095a Fixed: force default values for cc users in workflow 2023-05-17 13:24:15 +02:00
1df2342c49 Merge branch 'master' into 20-finalisation-cire 2023-05-17 10:40:01 +02:00
addbdacee8 Merge branch 'feature/change-parcours-status' into 'master'
Feature/change parcours status

See merge request Chill-Projet/chill-bundles!527
2023-05-17 08:13:18 +00:00
8a684734e7 Fixed: fix docgen normalization on household with "old" members
When a household had old members, the indexes of each "current" members
should be numerical and contiguous, to be transformed in a list. If this
is not the case, the members are mapped to an associative array.

This commit alter the generic DocGenObjectNormalizer to ensure that
the ReadableCollection are normalized using the
CollectionDocGenNormalizer as default, which do not preserve keys.
2023-05-16 23:30:02 +02:00
1abaf2acb0 DX: add help and description to use ImportSocialWorkMetadata command 2023-05-10 10:28:36 +02:00
a0ae1f0d0f FIX [budget] display budget element comment if it is of kind 'autre' 2023-05-03 12:11:00 +02:00
2554da9dd8 Fix: urgent fix for EntityToJsonTransformer
The throw on error flag imposes us to propose a valid json string for decoding
2023-04-28 23:19:23 +02:00
3e3f20993d fix migration after test on real data 2023-04-28 15:55:41 +02:00
997f3cdb09 Feature: [exports] add filters for start and end date for accompanying period works 2023-04-28 12:37:28 +02:00
ab5ad7ae14 fix cs 2023-04-28 12:02:31 +02:00
36413f16c3 DX: convert closure to arrow function 2023-04-28 11:51:16 +02:00
f75b90cb26 Feature: [export] add filters regarding to accompanying period infos 2023-04-28 11:49:37 +02:00
1956836f88 add migration to fix existing period steps 2023-04-28 11:49:37 +02:00
ea4294d12d add start date's accompanying period in accompanyingperiod info 2023-04-28 11:49:36 +02:00
c73e57ad73 doc: add info about syncrhonizing views 2023-04-28 11:49:36 +02:00
5b729e1cb1 add dev docs 2023-04-28 11:49:36 +02:00
229af2e4f9 Feature: Adapt filters and aggregators with new steps 2023-04-28 11:49:36 +02:00
e80a6e417b Feature: Track update of entities if no user is associated to the request
This happens in scripts/cli (messenger, ...)
2023-04-28 11:49:35 +02:00
c5989de120 Feature: Adapt UI to show new steps 2023-04-28 11:49:35 +02:00
fcbc00d0f1 Feature: force to add updatedAt and createdAt even if no user iss associated 2023-04-28 11:49:35 +02:00
722f053f06 Feature: Change accompanying period info step in a cronjob 2023-04-28 11:49:35 +02:00
97b7ff2e43 Feature: takes activity into account for AccompanyingPeriodInfo 2023-04-28 11:49:34 +02:00
f3e0302f3f Feature: takes document and evaluation update into account for AccompanyingPeriodInfo 2023-04-28 11:49:34 +02:00
4974995ea2 Feature: add evaluation info to accompangyin preiod info 2023-04-28 11:49:34 +02:00
f2e1c73f37 Build parts to track info on accompanying period 2023-04-28 11:49:33 +02:00
cdaca533a0 Feature: [export] allow to check multiple geographical layers in
geographical aggregators

BC: saved exports which activated this layers won't work any more. This
is the query to find them:

```sql
SELECT * FROM chill_main_saved_export
WHERE
    options->'export'->'export'->'aggregators'->'accompanyingcourse_geographicalunitstat_aggregator'->>'enabled' = '1'
```
2023-04-25 17:37:27 +02:00
73c0dd0e9e Merge branch 'fix-event-bundle' into 20-finalisation-cire 2023-04-24 17:24:44 +02:00
8fd9010ea5 fix event bundle stuffs
- adapt event templates
- event bundle: fix deprecated deps injections
- fix error with n=0 not iterated into querybuilder with centers loop
2023-04-24 17:22:01 +02:00
488a0e5f0c Merge branch 'improve-budget-template' into 20-finalisation-cire 2023-04-24 16:22:30 +02:00
cb0ff88318 Fix action column width 2023-04-24 13:29:45 +02:00
be965e8698 - improve title hierarchy and ergonomie 2023-04-24 13:01:58 +02:00
241e605ea6 - style of h3 subtitle 2023-04-24 12:22:41 +02:00
fe3d437096 improve title hierarchy coherence 2023-04-24 12:12:10 +02:00
c1f5f02c41 Merge branch 'master' into improve-budget-template 2023-04-24 11:51:58 +02:00
087ada2250 UX: Better use of flex-table and tables in budget twig templates 2023-03-30 00:07:38 +02:00
1004 changed files with 14397 additions and 4225 deletions

View File

@@ -3,38 +3,3 @@
# Run tests from root to adapt your own environment
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
ADMIN_PASSWORD=admin
LOCALE=fr
REDIS_URL=redis
REDIS_PORT=6379
REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=2a30f6ba26521a2613821da35f28386e
TWILIO_SID=~
TWILIO_SECRET=~
DEFAULT_CARRIER_CODE=BE
ADD_ADDRESS_DEFAULT_COUNTRY=BE
ADD_ADDRESS_MAP_CENTER_X=50.8443
ADD_ADDRESS_MAP_CENTER_Y=4.3523
ADD_ADDRESS_MAP_CENTER_Z=15
SHORT_MESSAGE_DSN=null://null
MESSENGER_TRANSPORT_DSN=sync://
###< symfony/messenger ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
#
DATABASE_URL="postgresql://postgres:postgres@db:5432/test?serverVersion=14&charset=utf8"
ASYNC_UPLOAD_TEMP_URL_KEY=
ASYNC_UPLOAD_TEMP_URL_BASE_PATH=
ASYNC_UPLOAD_TEMP_URL_CONTAINER=

View File

@@ -3,6 +3,7 @@
# Select what we should cache between builds
cache:
paths:
- tests/app/vendor/
- .cache
# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
@@ -27,7 +28,7 @@ variables:
REDIS_PORT: 6379
REDIS_URL: redis://redis:6379
# change vendor dir to make the app install into tests/apps
#COMPOSER_VENDOR_DIR: /vendor
COMPOSER_VENDOR_DIR: tests/app/vendor
DEFAULT_CARRIER_CODE: BE
stages:
@@ -48,7 +49,7 @@ build:
expire_in: 30 min
paths:
- bin
- vendor/
- tests/app/vendor/
code_style:
stage: Tests
@@ -62,7 +63,7 @@ code_style:
expire_in: 30 min
paths:
- bin
- vendor/
- tests/app/vendor/
phpstan_tests:
stage: Tests
@@ -76,7 +77,7 @@ phpstan_tests:
expire_in: 30 min
paths:
- bin
- vendor/
- tests/app/vendor/
rector_tests:
stage: Tests
@@ -90,7 +91,7 @@ rector_tests:
expire_in: 30 min
paths:
- bin
- vendor/
- tests/app/vendor/
# psalm_tests:
# stage: Tests
@@ -110,12 +111,13 @@ unit_tests:
# until we fix testes
allow_failure: true
script:
- php tests/console doctrine:migrations:migrate -n
- php -d memory_limit=3G tests/console doctrine:fixtures:load -n
- php -d memory_limit=2G tests/console cache:clear --env=test
- php tests/app/bin/console doctrine:migrations:migrate -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=dev
- php -d memory_limit=3G tests/app/bin/console doctrine:fixtures:load -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=test
- php -d memory_limit=4G bin/phpunit --colors=never
artifacts:
expire_in: 30 min
paths:
- bin
- vendor/
- tests/app/vendor/

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "_exts/sphinx-php"]
path = _exts/sphinx-php
url = https://github.com/fabpot/sphinx-php.git
[submodule "tests/app"]
path = tests/app
url = https://gitlab.com/Chill-projet/chill-app.git

View File

@@ -34,6 +34,7 @@
"sensio/framework-extra-bundle": "^5.5",
"spomky-labs/base64url": "^2.0",
"symfony/browser-kit": "^4.4",
"symfony/clock": "^6.2",
"symfony/css-selector": "^4.4",
"symfony/expression-language": "^4.4",
"symfony/form": "^4.4",
@@ -107,7 +108,7 @@
},
"autoload-dev": {
"psr-4": {
"App\\": "tests/",
"App\\": "tests/app/src/",
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
"Chill\\WopiBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
}
@@ -123,10 +124,12 @@
},
"bin-dir": "bin",
"optimize-autoloader": true,
"sort-packages": true
"sort-packages": true,
"vendor-dir": "tests/app/vendor"
},
"scripts": {
"auto-scripts": {
"assets:install %PUBLIC_DIR%": "symfony-cmd",
"cache:clear": "symfony-cmd"
}
}

View File

@@ -50,9 +50,18 @@ class CountPerson implements ExportInterface
public function getLabels($key, array $values, $data)
{
// the Closure which will be executed by the formatter.
return fn($value) => match ($value) {
'_header' => $this->getTitle(),
default => $value,
return function ($value) {
switch ($value) {
case '_header':
// we have to process specifically the '_header' string,
// which will be used by the formatter to show a column title
return $this->getTitle();
default:
// for all value, we do not process them and return them
// immediatly
return $value;
}
};
}

View File

@@ -0,0 +1,203 @@
.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
.. _entity-info:
Stats about event on entity in php world
########################################
It is necessary to be able to gather information about events for some entities:
- when the event has been done;
- who did it;
- ...
Those "infos" are not linked with right management, like describe in :ref:`timelines`.
“infos” for some stats and info about an entity
-----------------------------------------------
Building an info means:
- create an Entity, and map this entity to a SQL view (not a regular table);
- use the framework to build this entity dynamically.
A framework api is built to be able to build multiple “infos” entities
through “union” views:
- use a command ``bin/console chill:db:sync-views`` to synchronize view (create view if it does not exists, or update
views when new SQL parts are added in the UNION query. Internally, this command call a new ``ViewEntityInfoManager``,
which iterate over available views to build the SQL;
- one can create a new “view entity info” by implementing a
``ViewEntityInfoProviderInterface``
- this implementation of the interface is free to create another
interface for building each part of the UNION query. This interface
is created for AccompanyingPeriodInfo:
``Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface``
So, converting new “events” into rows for ``AccompanyingPeriodInfo`` is
just implementing this interface!
Implementation for AccompanyingPeriod (``AccompanyingPeriod/AccompanyingPeriodInfo``)
-------------------------------------------------------------------------------------
A class is created for computing some statistical info for an
AccompanyingPeriod: ``AccompanyingPeriod/AccompanyingPeriodInfo``. This
contains information about “something happens”, who did it and when.
Having those info in table answer some questions like:
- when is the last and the first action (AccompanyingPeriodWork,
Activity, AccompanyingPeriodWorkEvaluation, …) on the period;
- who is “acting” on the period, and when is the last “action” for each
user.
The AccompanyingPeriod info is mapped to a SQL view, not a table. The
sql view is built dynamically (see below), and gather infos from
ActivityBundle, PersonBundle, CalendarBundle, … It is possible to create
custom bundle and add info on this view.
.. code:: php
/**
*
* @ORM\Entity()
* @ORM\Table(name="view_chill_person_accompanying_period_info") <==== THIS IS A VIEW, NOT A TABLE
*/
class AccompanyingPeriodInfo
{
// ...
}
Why do we need this ?
~~~~~~~~~~~~~~~~~~~~~
For multiple jobs in PHP world:
- moving the accompanying period to another steps when inactive,
automatically;
- listing all the users which are intervening on the action on a new
“Liste des intervenants” page;
- filtering on exports
Later, we will launch automatic anonymise for accompanying period and
all related entities through this information.
How is built the SQL views which is mapped to “info” entities ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The AccompanyingPeriodInfo entity is mapped by a SQL view (not a regular
table).
The sql view is built dynamically, it is a SQL view like this, for now (April 2023):
.. code:: sql
create view view_chill_person_accompanying_period_info
(accompanyingperiod_id, relatedentity, relatedentityid, user_id, infodate, discriminator, metadata) as
SELECT w.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork'::text AS relatedentity,
w.id AS relatedentityid,
cpapwr.user_id,
w.enddate AS infodate,
'accompanying_period_work_end'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work w
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON w.id = cpapwr.accompanyingperiodwork_id
WHERE w.enddate IS NOT NULL
UNION
SELECT cpapw.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
e.id AS relatedentityid,
e.updatedby_id AS user_id,
e.updatedat AS infodate,
'accompanying_period_work_evaluation_updated_at'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work_evaluation e
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
WHERE e.updatedat IS NOT NULL
UNION
SELECT cpapw.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
e.id AS relatedentityid,
cpapwr.user_id,
e.maxdate AS infodate,
'accompanying_period_work_evaluation_start'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work_evaluation e
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id
WHERE e.maxdate IS NOT NULL
UNION
SELECT cpapw.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
e.id AS relatedentityid,
cpapwr.user_id,
e.startdate AS infodate,
'accompanying_period_work_evaluation_start'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work_evaluation e
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id
UNION
SELECT cpapw.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument'::text AS relatedentity,
doc.id AS relatedentityid,
doc.updatedby_id AS user_id,
doc.updatedat AS infodate,
'accompanying_period_work_evaluation_document_updated_at'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work_evaluation_document doc
JOIN chill_person_accompanying_period_work_evaluation e ON doc.accompanyingperiodworkevaluation_id = e.id
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
WHERE doc.updatedat IS NOT NULL
UNION
SELECT cpapw.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
e.id AS relatedentityid,
cpapwr.user_id,
e.maxdate AS infodate,
'accompanying_period_work_evaluation_max'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work_evaluation e
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id
WHERE e.maxdate IS NOT NULL
UNION
SELECT w.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork'::text AS relatedentity,
w.id AS relatedentityid,
cpapwr.user_id,
w.startdate AS infodate,
'accompanying_period_work_start'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work w
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON w.id = cpapwr.accompanyingperiodwork_id
UNION
SELECT activity.accompanyingperiod_id,
'Chill\ActivityBundle\Entity\Activity'::text AS relatedentity,
activity.id AS relatedentityid,
au.user_id,
activity.date AS infodate,
'activity_date'::text AS discriminator,
'{}'::jsonb AS metadata
FROM activity
LEFT JOIN activity_user au ON activity.id = au.activity_id
WHERE activity.accompanyingperiod_id IS NOT NULL;
As you can see, the view gather multiple SELECT queries and bind them
with UNION.
Each SELECT query is built dynamically, through a class implementing an
interface: ``Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface``, `like
here <https://gitlab.com/Chill-Projet/chill-bundles/-/blob/master/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEndQueryPartForAccompanyingPeriodInfo.php>`__
To add new `SELECT` query in different `UNION` parts in the sql view, create a
service and implements this interface: ``Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface``.

View File

@@ -35,6 +35,7 @@ As Chill rely on the `symfony <http://symfony.com>`_ framework, reading the fram
manual/index.rst
Assets <assets.rst>
Cron Jobs <cronjob.rst>
Info about entities <entity-info.rst>
Layout and UI
**************

View File

@@ -6,6 +6,8 @@
A copy of the license is included in the section entitled "GNU
Free Documentation License".
.. _timelines:
Timelines
*********
@@ -18,24 +20,24 @@ Concept
From an user point of view
--------------------------
Chill has two objectives :
Chill has two objectives :
* make the administrative tasks more lightweight ;
* help social workers to have all information they need to work
To reach this second objective, Chill provides a special view: **timeline**. On a timeline view, information is gathered and shown on a single page, from the most recent event to the oldest one.
The information gathered is linked to a *context*. This *context* may be, for instance :
The information gathered is linked to a *context*. This *context* may be, for instance :
* a person : events linked to this person are shown on the page ;
* a center: events linked to a center are shown. They may concern different peoples ;
* ...
* ...
In other word, the *context* is the kind of argument that will be used in the event's query.
Let us recall that only the data the user has allowed to see should be shown.
.. seealso::
.. seealso::
`The issue where the subject was first discussed <https://redmine.champs-libres.coop/issues/224>`_
@@ -43,30 +45,30 @@ Let us recall that only the data the user has allowed to see should be shown.
For developers
--------------
The `Main` bundle provides interfaces and services to help to build timelines.
The `Main` bundle provides interfaces and services to help to build timelines.
If a bundle wants to *push* information in a timeline, it should be create a service which implements `Chill\MainBundle\Timeline\TimelineProviderInterface`, and tag is with `chill.timeline` and arguments defining the supported context (you may use multiple `chill.timeline` tags in order to support multiple context with a single service/class).
If a bundle wants to provide a new context for a timeline, the service `chill.main.timeline_builder` will helps to gather timeline's services supporting the defined context, and run queries across the models.
If a bundle wants to provide a new context for a timeline, the service `chill.main.timeline_builder` will helps to gather timeline's services supporting the defined context, and run queries across the models.
.. _understanding-queries :
Understanding queries
^^^^^^^^^^^^^^^^^^^^^
Due to the fact that timelines should show only the X last events from Y differents tables, queries for a timeline may consume a lot of resources: at first on the database, and then on the ORM part, which will have to deserialize DB data to PHP classes, which may not be used if they are not part of the "last X events".
Due to the fact that timelines should show only the X last events from Y differents tables, queries for a timeline may consume a lot of resources: at first on the database, and then on the ORM part, which will have to deserialize DB data to PHP classes, which may not be used if they are not part of the "last X events".
To avoid such load on database, the objects are queried in two steps :
To avoid such load on database, the objects are queried in two steps :
1. An UNION request which gather the last X events, ordered by date. The data retrieved are the ID, the date, and a string key: a type. This type discriminates the data type.
2. The PHP objects are queried by ID, the type helps the program to link id with the kind of objects.
2. The PHP objects are queried by ID, the type helps the program to link id with the kind of objects.
Those methods should ensure that only X PHP objects will be gathered and build by the ORM.
What does the master timeline builder service ?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When the service `chill.main.timeline_builder` is instanciated, the service is informed of each service taggued with `chill.timeline` tags. Then,
When the service `chill.main.timeline_builder` is instanciated, the service is informed of each service taggued with `chill.timeline` tags. Then,
1. The service build an UNION query by assembling column and tables names provided by the `fetchQuery` result ;
2. The UNION query is run, the result contains an id and a type for each row (see :ref:`above <understanding-queries>`)
@@ -84,7 +86,7 @@ To push events on a timeline :
Implementing the TimelineProviderInterface
------------------------------------------
The has the following signature :
The has the following signature :
.. code-block:: php
@@ -92,19 +94,19 @@ The has the following signature :
interface TimelineProviderInterface
{
/**
*
/**
*
* @param string $context
* @param mixed[] $args the argument to the context.
* @return TimelineSingleQuery
* @throw \LogicException if the context is not supported
*/
public function fetchQuery($context, array $args);
/**
* Indicate if the result type may be handled by the service
*
*
* @param string $type the key present in the SELECT query
* @return boolean
*/
@@ -113,42 +115,42 @@ The has the following signature :
/**
* fetch entities from db into an associative array. The keys **MUST BE**
* the id
*
* All ids returned by all SELECT queries
*
* All ids returned by all SELECT queries
* (@see TimeLineProviderInterface::fetchQuery) and with the type
* supported by the provider (@see TimelineProviderInterface::supportsType)
* will be passed as argument.
*
*
* @param array $ids an array of id
* @return mixed[] an associative array of entities, with id as key
*/
public function getEntities(array $ids);
/**
* return an associative array with argument to render the entity
* in an html template, which will be included in the timeline page
*
*
* The result must have the following key :
*
*
* - `template` : the template FQDN
* - `template_data`: the data required by the template
*
*
*
*
* Example:
*
*
* ```
* array(
* array(
* 'template' => 'ChillMyBundle:timeline:template.html.twig',
* 'template_data' => array(
* 'accompanyingPeriod' => $entity,
* 'person' => $args['person']
* 'accompanyingPeriod' => $entity,
* 'person' => $args['person']
* )
* );
* ```
*
*
* `$context` and `$args` are defined by the bundle which will call the timeline
* rendering.
*
* rendering.
*
* @param type $entity
* @param type $context
* @param array $args
@@ -156,7 +158,7 @@ The has the following signature :
* @throws \LogicException if the context is not supported
*/
public function getEntityTemplate($entity, $context, array $args);
}
@@ -176,7 +178,7 @@ The parameters should be replaced into the query by :code:`?`. They will be repl
`$context` and `$args` are defined by the bundle which will call the timeline rendering. You may use them to build a different query depending on this context.
For instance, if the context is `'person'`, the args will be this array :
For instance, if the context is `'person'`, the args will be this array :
.. code-block:: php
@@ -197,7 +199,7 @@ You should find in the bundle documentation which contexts are arguments the bun
.. note::
We encourage to use `ClassMetaData` to define column names arguments. If you change your column names, changes will be reflected automatically during the execution of your code.
We encourage to use `ClassMetaData` to define column names arguments. If you change your column names, changes will be reflected automatically during the execution of your code.
Example of an implementation :
@@ -215,13 +217,13 @@ Example of an implementation :
*/
class TimelineReportProvider implements TimelineProviderInterface
{
/**
*
* @var EntityManager
*/
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
@@ -230,9 +232,9 @@ Example of an implementation :
public function fetchQuery($context, array $args)
{
$this->checkContext($context);
$metadata = $this->em->getClassMetadata('ChillReportBundle:Report');
return TimelineSingleQuery::fromArray([
'id' => $metadata->getColumnName('id'),
'type' => 'report',
@@ -254,11 +256,11 @@ Example of an implementation :
The `supportsType` function
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This function indicate to the master `chill.main.timeline_builder` service (which orchestrate the build of UNION queries) that the service supports the type indicated in the result's array of the `fetchQuery` function.
This function indicate to the master `chill.main.timeline_builder` service (which orchestrate the build of UNION queries) that the service supports the type indicated in the result's array of the `fetchQuery` function.
The implementation of our previous example will be :
The implementation of our previous example will be :
.. code-block:: php
.. code-block:: php
namespace Chill\ReportBundle\Timeline;
@@ -272,7 +274,7 @@ The implementation of our previous example will be :
//...
/**
*
*
* {@inheritDoc}
*/
public function supportsType($type)
@@ -304,12 +306,12 @@ The results **must be** an array where the id given by the UNION query (remember
{
$reports = $this->em->getRepository('ChillReportBundle:Report')
->findBy(array('id' => $ids));
$result = array();
foreach($reports as $report) {
$result[$report->getId()] = $report;
}
return $result;
}
@@ -318,9 +320,9 @@ The results **must be** an array where the id given by the UNION query (remember
The `getEntityTemplate` function
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is where the master service will collect information to render the entity.
This is where the master service will collect information to render the entity.
The result must be an associative array with :
The result must be an associative array with :
- **template** is the FQDN of the template ;
- **template_data** is an associative array where keys are the variables'names for this template, and values are the values.
@@ -332,8 +334,8 @@ Example :
array(
'template' => 'ChillMyBundle:timeline:template.html.twig',
'template_data' => array(
'period' => $entity,
'person' => $args['person']
'period' => $entity,
'person' => $args['person']
)
);
@@ -349,7 +351,7 @@ Create a timeline with his own context
You have to create a Controller which will execute the service `chill.main.timeline_builder`. Using the `Chill\MainBundle\Timeline\TimelineBuilder::getTimelineHTML` function, you will get an HTML representation of the timeline, which you may include with twig `raw` filter.
Example :
Example :
.. code-block:: php

View File

@@ -23,12 +23,19 @@ class ChillMainConfiguration implements ConfigurationInterface
{
use AddWidgetConfigurationTrait;
/**
* @var ContainerBuilder
*/
private $containerBuilder;
public function __construct(
array $widgetFactories,
private readonly ContainerBuilder $containerBuilder
ContainerBuilder $containerBuilder
) {
// we register here widget factories (see below)
$this->setWidgetFactories($widgetFactories);
// we will need the container builder later...
$this->containerBuilder = $containerBuilder;
}
public function getConfigTreeBuilder()

View File

@@ -151,6 +151,7 @@ This script will :
# mount into to container
./docker-php.sh
bin/console chill:db:sync-views
# and load fixtures
bin/console doctrine:migrations:migrate
@@ -161,7 +162,7 @@ Chill will be available at ``http://localhost:8001.`` Currently, there isn't any
# mount into to container
./docker-php.sh
# and load fixtures
# and load fixtures (do not this for production)
bin/console doctrine:fixtures:load --purge-with-truncate
There are several users available:
@@ -204,8 +205,10 @@ How to create the database schema (= run migrations) ?
# if a container is running
./docker-php.sh
bin/console doctrine:migrations:migrate
bin/console chill:db:sync-views
# if not
docker-compose run --user $(id -u) php bin/console doctrine:migrations:migrate
docker-compose run --user $(id -u) php bin/console chill:db:sync-views
How to read the email sent by the program ?
@@ -236,6 +239,23 @@ How to open a terminal in the project
# if not
docker-compose run --user $(id -u) php /bin/bash
How to run cron-jobs ?
======================
Some command must be executed in :ref:`cron jobs <cronjob>`. To execute them:
.. code-block:: bash
# if a container is running
./docker-php.sh
bin/console chill:cron-job:execute
# some of them are executed only during the night. So, we have to force the execution during the day:
bin/console chill:cron-job:execute 'name-of-the-cron'
# if not
docker-compose run --user $(id -u) php bin/console chill:cron-job:execute
# some of them are executed only during the night. So, we have to force the execution during the day:
docker-compose run --user $(id -u) php bin/console chill:cron-job:execute 'name-of-the-cron'
How to run composer ?
=====================

View File

@@ -38,6 +38,14 @@ This should be adapted to your needs:
* Think about how you will backup your database. Some adminsys find easier to store database outside of docker, which might be easier to administrate or replicate.
Run migrations on each update
=============================
Every time you start a new version, you should apply update the sql schema:
- running ``bin/console doctrine:migration:migrate`` to run sql migration;
- synchonizing sql views to the last state: ``bin/console chill:db:sync-views``
Cron jobs
=========

View File

@@ -2,11 +2,20 @@
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.6/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="tests/bootstrap.php"
>
bootstrap="tests/app/tests/bootstrap.php"
cacheResultFile=".phpunit.cache/test-results"
executionOrder="depends,defects"
forceCoversAnnotation="true"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
convertDeprecationsToExceptions="true"
failOnRisky="true"
failOnWarning="true"
verbose="true">
<php>
<ini name="error_reporting" value="-1" />
<server name="APP_ENV" value="test" force="true" />

View File

@@ -12,8 +12,6 @@ return static function (RectorConfig $rectorConfig): void {
__DIR__ . '/src',
]);
$rectorConfig->symfonyContainerXml(__DIR__ . '/var/cache/dev/testsApp_KernelDevDebugContainer.xml');
//$rectorConfig->cacheClass(\Rector\Caching\ValueObject\Storage\FileCacheStorage::class);
//$rectorConfig->cacheDirectory(__DIR__ . '/.cache/rector');
@@ -23,8 +21,7 @@ return static function (RectorConfig $rectorConfig): void {
//define sets of rules
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_82,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_44,
LevelSetList::UP_TO_PHP_74
]);
// skip some path...
@@ -37,5 +34,27 @@ return static function (RectorConfig $rectorConfig): void {
// must merge MR500 and review a typing of "ArrayCollection" in entities
\Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector::class,
// remove all PHP80 rules, in order to activate them one by one
\Rector\Php80\Rector\ClassMethod\AddParamBasedOnParentClassMethodRector::class,
\Rector\Php80\Rector\Class_\AnnotationToAttributeRector::class,
\Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector::class,
\Rector\Php80\Rector\FuncCall\ClassOnObjectRector::class,
\Rector\Php80\Rector\ClassConstFetch\ClassOnThisVariableObjectRector::class,
\Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector::class,
\Rector\Php80\Rector\Class_\DoctrineAnnotationClassToAttributeRector::class,
\Rector\Php80\Rector\ClassMethod\FinalPrivateToPrivateVisibilityRector::class,
\Rector\Php80\Rector\Ternary\GetDebugTypeRector::class,
\Rector\Php80\Rector\FunctionLike\MixedTypeRector::class,
\Rector\Php80\Rector\Property\NestedAnnotationToAttributeRector::class,
\Rector\Php80\Rector\FuncCall\Php8ResourceReturnToObjectRector::class,
\Rector\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector::class,
\Rector\Php80\Rector\ClassMethod\SetStateToStaticRector::class,
\Rector\Php80\Rector\NotIdentical\StrContainsRector::class,
\Rector\Php80\Rector\Identical\StrEndsWithRector::class,
\Rector\Php80\Rector\Identical\StrStartsWithRector::class,
\Rector\Php80\Rector\Class_\StringableForToStringRector::class,
\Rector\Php80\Rector\FuncCall\TokenGetAllToObjectRector::class,
\Rector\Php80\Rector\FunctionLike\UnionTypesRector::class
]);
};

View File

@@ -47,29 +47,76 @@ use function array_key_exists;
final class ActivityController extends AbstractController
{
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
private ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
private ActivityRepository $activityRepository;
private ActivityTypeCategoryRepository $activityTypeCategoryRepository;
private ActivityTypeRepositoryInterface $activityTypeRepository;
private CenterResolverManagerInterface $centerResolver;
private EntityManagerInterface $entityManager;
private EventDispatcherInterface $eventDispatcher;
private LocationRepository $locationRepository;
private LoggerInterface $logger;
private PersonRepository $personRepository;
private SerializerInterface $serializer;
private ThirdPartyRepository $thirdPartyRepository;
private TranslatorInterface $translator;
private UserRepositoryInterface $userRepository;
public function __construct(
private readonly ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
private readonly ActivityTypeRepositoryInterface $activityTypeRepository,
private readonly ActivityTypeCategoryRepository $activityTypeCategoryRepository,
private readonly PersonRepository $personRepository,
private readonly ThirdPartyRepository $thirdPartyRepository,
private readonly LocationRepository $locationRepository,
private readonly ActivityRepository $activityRepository,
private readonly AccompanyingPeriodRepository $accompanyingPeriodRepository,
private readonly EntityManagerInterface $entityManager,
private readonly EventDispatcherInterface $eventDispatcher,
private readonly LoggerInterface $logger,
private readonly SerializerInterface $serializer,
private readonly UserRepositoryInterface $userRepository,
private readonly CenterResolverManagerInterface $centerResolver,
private readonly TranslatorInterface $translator,
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
ActivityTypeRepositoryInterface $activityTypeRepository,
ActivityTypeCategoryRepository $activityTypeCategoryRepository,
PersonRepository $personRepository,
ThirdPartyRepository $thirdPartyRepository,
LocationRepository $locationRepository,
ActivityRepository $activityRepository,
AccompanyingPeriodRepository $accompanyingPeriodRepository,
EntityManagerInterface $entityManager,
EventDispatcherInterface $eventDispatcher,
LoggerInterface $logger,
SerializerInterface $serializer,
UserRepositoryInterface $userRepository,
CenterResolverManagerInterface $centerResolver,
TranslatorInterface $translator
) {
$this->activityACLAwareRepository = $activityACLAwareRepository;
$this->activityTypeRepository = $activityTypeRepository;
$this->activityTypeCategoryRepository = $activityTypeCategoryRepository;
$this->personRepository = $personRepository;
$this->thirdPartyRepository = $thirdPartyRepository;
$this->locationRepository = $locationRepository;
$this->activityRepository = $activityRepository;
$this->accompanyingPeriodRepository = $accompanyingPeriodRepository;
$this->entityManager = $entityManager;
$this->eventDispatcher = $eventDispatcher;
$this->logger = $logger;
$this->serializer = $serializer;
$this->userRepository = $userRepository;
$this->centerResolver = $centerResolver;
$this->translator = $translator;
}
/**
* Deletes a Activity entity.
*
* @param mixed $id
*/
public function deleteAction(Request $request, mixed $id)
public function deleteAction(Request $request, $id)
{
$view = null;
@@ -603,8 +650,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

@@ -47,8 +47,10 @@ class ActivityReasonCategoryController extends AbstractController
/**
* Displays a form to edit an existing ActivityReasonCategory entity.
*
* @param mixed $id
*/
public function editAction(mixed $id)
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
@@ -96,8 +98,10 @@ class ActivityReasonCategoryController extends AbstractController
/**
* Finds and displays a ActivityReasonCategory entity.
*
* @param mixed $id
*/
public function showAction(mixed $id)
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
@@ -114,8 +118,10 @@ class ActivityReasonCategoryController extends AbstractController
/**
* Edits an existing ActivityReasonCategory entity.
*
* @param mixed $id
*/
public function updateAction(Request $request, mixed $id)
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();

View File

@@ -24,8 +24,11 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class ActivityReasonController extends AbstractController
{
public function __construct(private readonly ActivityReasonRepository $activityReasonRepository)
private ActivityReasonRepository $activityReasonRepository;
public function __construct(ActivityReasonRepository $activityReasonRepository)
{
$this->activityReasonRepository = $activityReasonRepository;
}
/**
@@ -53,8 +56,10 @@ class ActivityReasonController extends AbstractController
/**
* Displays a form to edit an existing ActivityReason entity.
*
* @param mixed $id
*/
public function editAction(mixed $id)
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
@@ -102,8 +107,10 @@ class ActivityReasonController extends AbstractController
/**
* Finds and displays a ActivityReason entity.
*
* @param mixed $id
*/
public function showAction(mixed $id)
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
@@ -120,8 +127,10 @@ class ActivityReasonController extends AbstractController
/**
* Edits an existing ActivityReason entity.
*
* @param mixed $id
*/
public function updateAction(Request $request, mixed $id)
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();

View File

@@ -25,14 +25,17 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
{
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
private EntityManagerInterface $em;
/**
* @var \Faker\Generator
*/
private $faker;
public function __construct(private readonly EntityManagerInterface $em)
public function __construct(EntityManagerInterface $em)
{
$this->faker = FakerFactory::create('fr_FR');
$this->em = $em;
}
public function getOrder()
@@ -67,7 +70,7 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
->setPerson($person)
->setDate($this->faker->dateTimeThisYear())
->setDurationTime($this->faker->dateTime(36000))
->setActivityType($this->getRandomActivityType())
->setType($this->getRandomActivityType())
->setScope($this->getRandomScope());
// ->setAttendee($this->faker->boolean())

View File

@@ -65,9 +65,9 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
use TrackUpdateTrait;
final public const SENTRECEIVED_RECEIVED = 'received';
public const SENTRECEIVED_RECEIVED = 'received';
final public const SENTRECEIVED_SENT = 'sent';
public const SENTRECEIVED_SENT = 'sent';
/**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod")
@@ -672,7 +672,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
}
/**
* @deprecated use @link{self::setActivityType} instead
* @deprecated
*/
public function setType(ActivityType $activityType): self
{

View File

@@ -23,9 +23,10 @@ use Doctrine\ORM\Mapping as ORM;
class ActivityReason
{
/**
* @var bool
* @ORM\Column(type="boolean")
*/
private bool $active = true;
private $active = true;
/**
* @var ActivityReasonCategory
@@ -33,7 +34,7 @@ class ActivityReason
* targetEntity="Chill\ActivityBundle\Entity\ActivityReasonCategory",
* inversedBy="reasons")
*/
private ?ActivityReasonCategory $category = null;
private $category;
/**
* @var int
@@ -42,13 +43,13 @@ class ActivityReason
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private ?int $id = null;
private $id;
/**
* @var array
* @ORM\Column(type="json")
*/
private array $name;
private $name;
/**
* Get active.
@@ -80,9 +81,27 @@ class ActivityReason
/**
* Get name.
*
* @param mixed|null $locale
*
* @return array | string
*/
public function getName(): array
public function getName($locale = null)
{
if ($locale) {
if (isset($this->name[$locale])) {
return $this->name[$locale];
}
foreach ($this->name as $name) {
if (!empty($name)) {
return $name;
}
}
return '';
}
return $this->name;
}

View File

@@ -21,7 +21,7 @@ use Doctrine\ORM\Mapping as ORM;
* @ORM\Table(name="activityreasoncategory")
* @ORM\HasLifecycleCallbacks
*/
class ActivityReasonCategory implements \Stringable
class ActivityReasonCategory
{
/**
* @var bool
@@ -65,7 +65,7 @@ class ActivityReasonCategory implements \Stringable
/**
* @return string
*/
public function __toString(): string
public function __toString()
{
return 'ActivityReasonCategory(' . $this->getName('x') . ')';
}

View File

@@ -27,11 +27,11 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
*/
class ActivityType
{
final public const FIELD_INVISIBLE = 0;
public const FIELD_INVISIBLE = 0;
final public const FIELD_OPTIONAL = 1;
public const FIELD_OPTIONAL = 1;
final public const FIELD_REQUIRED = 2;
public const FIELD_REQUIRED = 2;
/**
* @deprecated not in use
@@ -275,8 +275,10 @@ class ActivityType
/**
* @Assert\Callback
*
* @param mixed $payload
*/
public function checkSocialActionsVisibility(ExecutionContextInterface $context, mixed $payload)
public function checkSocialActionsVisibility(ExecutionContextInterface $context, $payload)
{
if ($this->socialIssuesVisible !== $this->socialActionsVisible) {
if (!(2 === $this->socialIssuesVisible && 1 === $this->socialActionsVisible)) {

View File

@@ -22,8 +22,14 @@ use function in_array;
class ActivityEntityListener
{
public function __construct(private readonly EntityManagerInterface $em, private readonly AccompanyingPeriodWorkRepository $workRepository)
private EntityManagerInterface $em;
private AccompanyingPeriodWorkRepository $workRepository;
public function __construct(EntityManagerInterface $em, AccompanyingPeriodWorkRepository $workRepository)
{
$this->em = $em;
$this->workRepository = $workRepository;
}
public function persistActionToCourse(Activity $activity)

View File

@@ -20,8 +20,16 @@ use Symfony\Component\Form\FormBuilderInterface;
class ByCreatorAggregator implements AggregatorInterface
{
public function __construct(private readonly UserRepositoryInterface $userRepository, private readonly UserRender $userRender)
{
private UserRender $userRender;
private UserRepositoryInterface $userRepository;
public function __construct(
UserRepositoryInterface $userRepository,
UserRender $userRender
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
}
public function addRole(): ?string

View File

@@ -21,8 +21,16 @@ use function in_array;
class BySocialActionAggregator implements AggregatorInterface
{
public function __construct(private readonly SocialActionRender $actionRender, private readonly SocialActionRepository $actionRepository)
{
private SocialActionRender $actionRender;
private SocialActionRepository $actionRepository;
public function __construct(
SocialActionRender $actionRender,
SocialActionRepository $actionRepository
) {
$this->actionRender = $actionRender;
$this->actionRepository = $actionRepository;
}
public function addRole(): ?string

View File

@@ -21,8 +21,16 @@ use function in_array;
class BySocialIssueAggregator implements AggregatorInterface
{
public function __construct(private readonly SocialIssueRepository $issueRepository, private readonly SocialIssueRender $issueRender)
{
private SocialIssueRender $issueRender;
private SocialIssueRepository $issueRepository;
public function __construct(
SocialIssueRepository $issueRepository,
SocialIssueRender $issueRender
) {
$this->issueRepository = $issueRepository;
$this->issueRender = $issueRender;
}
public function addRole(): ?string

View File

@@ -21,8 +21,16 @@ use function in_array;
class ByThirdpartyAggregator implements AggregatorInterface
{
public function __construct(private readonly ThirdPartyRepository $thirdPartyRepository, private readonly ThirdPartyRender $thirdPartyRender)
{
private ThirdPartyRender $thirdPartyRender;
private ThirdPartyRepository $thirdPartyRepository;
public function __construct(
ThirdPartyRepository $thirdPartyRepository,
ThirdPartyRender $thirdPartyRender
) {
$this->thirdPartyRepository = $thirdPartyRepository;
$this->thirdPartyRender = $thirdPartyRender;
}
public function addRole(): ?string

View File

@@ -21,8 +21,16 @@ use function in_array;
class CreatorScopeAggregator implements AggregatorInterface
{
public function __construct(private readonly ScopeRepository $scopeRepository, private readonly TranslatableStringHelper $translatableStringHelper)
{
private ScopeRepository $scopeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
ScopeRepository $scopeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -29,8 +29,12 @@ class DateAggregator implements AggregatorInterface
private const DEFAULT_CHOICE = 'year';
public function __construct(private readonly TranslatorInterface $translator)
{
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator
) {
$this->translator = $translator;
}
public function addRole(): ?string
@@ -95,9 +99,17 @@ class DateAggregator implements AggregatorInterface
return '';
}
return match ($data['frequency']) {
default => $value,
};
switch ($data['frequency']) {
case 'month':
case 'week':
//return $this->translator->trans('for week') .' '. $value ;
case 'year':
//return $this->translator->trans('in year') .' '. $value ;
default:
return $value;
}
};
}

View File

@@ -21,8 +21,16 @@ use function in_array;
class LocationTypeAggregator implements AggregatorInterface
{
public function __construct(private readonly LocationTypeRepository $locationTypeRepository, private readonly TranslatableStringHelper $translatableStringHelper)
{
private LocationTypeRepository $locationTypeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
LocationTypeRepository $locationTypeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->locationTypeRepository = $locationTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -22,10 +22,18 @@ use function in_array;
class ActivityTypeAggregator implements AggregatorInterface
{
final public const KEY = 'activity_type_aggregator';
public const KEY = 'activity_type_aggregator';
public function __construct(protected ActivityTypeRepositoryInterface $activityTypeRepository, protected TranslatableStringHelperInterface $translatableStringHelper)
{
protected ActivityTypeRepositoryInterface $activityTypeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ActivityTypeRepositoryInterface $activityTypeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->activityTypeRepository = $activityTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -21,10 +21,18 @@ use Symfony\Component\Form\FormBuilderInterface;
class ActivityUserAggregator implements AggregatorInterface
{
final public const KEY = 'activity_user_id';
public const KEY = 'activity_user_id';
public function __construct(private readonly UserRepository $userRepository, private readonly UserRender $userRender)
{
private UserRender $userRender;
private UserRepository $userRepository;
public function __construct(
UserRepository $userRepository,
UserRender $userRender
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
}
public function addRole(): ?string

View File

@@ -21,8 +21,14 @@ use function in_array;
class ActivityUsersAggregator implements AggregatorInterface
{
public function __construct(private readonly UserRepositoryInterface $userRepository, private readonly UserRender $userRender)
private UserRender $userRender;
private UserRepositoryInterface $userRepository;
public function __construct(UserRepositoryInterface $userRepository, UserRender $userRender)
{
$this->userRepository = $userRepository;
$this->userRender = $userRender;
}
public function addRole(): ?string

View File

@@ -20,8 +20,14 @@ use function in_array;
class ActivityUsersJobAggregator implements \Chill\MainBundle\Export\AggregatorInterface
{
public function __construct(private readonly UserJobRepositoryInterface $userJobRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper)
private TranslatableStringHelperInterface $translatableStringHelper;
private UserJobRepositoryInterface $userJobRepository;
public function __construct(UserJobRepositoryInterface $userJobRepository, TranslatableStringHelperInterface $translatableStringHelper)
{
$this->userJobRepository = $userJobRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -20,8 +20,14 @@ use function in_array;
class ActivityUsersScopeAggregator implements \Chill\MainBundle\Export\AggregatorInterface
{
public function __construct(private readonly ScopeRepositoryInterface $scopeRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper)
private ScopeRepositoryInterface $scopeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(ScopeRepositoryInterface $scopeRepository, TranslatableStringHelperInterface $translatableStringHelper)
{
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -30,8 +30,20 @@ use function in_array;
class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface
{
public function __construct(protected ActivityReasonCategoryRepository $activityReasonCategoryRepository, protected ActivityReasonRepository $activityReasonRepository, protected TranslatableStringHelper $translatableStringHelper)
{
protected ActivityReasonCategoryRepository $activityReasonCategoryRepository;
protected ActivityReasonRepository $activityReasonRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ActivityReasonCategoryRepository $activityReasonCategoryRepository,
ActivityReasonRepository $activityReasonRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->activityReasonCategoryRepository = $activityReasonCategoryRepository;
$this->activityReasonRepository = $activityReasonRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
@@ -101,11 +113,21 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
public function getLabels($key, array $values, $data)
{
match ($data['level']) {
'reasons' => $this->activityReasonRepository->findBy(['id' => $values]),
'categories' => $this->activityReasonCategoryRepository->findBy(['id' => $values]),
default => throw new RuntimeException(sprintf("The level data '%s' is invalid.", $data['level'])),
};
// for performance reason, we load data from db only once
switch ($data['level']) {
case 'reasons':
$this->activityReasonRepository->findBy(['id' => $values]);
break;
case 'categories':
$this->activityReasonCategoryRepository->findBy(['id' => $values]);
break;
default:
throw new RuntimeException(sprintf("The level data '%s' is invalid.", $data['level']));
}
return function ($value) use ($data) {
if ('_header' === $value) {

View File

@@ -20,8 +20,11 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class SentReceivedAggregator implements AggregatorInterface
{
public function __construct(private readonly TranslatorInterface $translator)
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole(): ?string

View File

@@ -16,9 +16,9 @@ namespace Chill\ActivityBundle\Export;
*/
abstract class Declarations
{
final public const ACTIVITY = 'activity';
public const ACTIVITY = 'activity';
final public const ACTIVITY_ACP = 'activity_linked_to_acp';
public const ACTIVITY_ACP = 'activity_linked_to_acp';
final public const ACTIVITY_PERSON = 'activity_linked_to_person';
public const ACTIVITY_PERSON = 'activity_linked_to_person';
}

View File

@@ -24,8 +24,20 @@ use Symfony\Component\Form\FormBuilderInterface;
class ListActivity implements ListInterface, GroupedExportInterface
{
public function __construct(private readonly ListActivityHelper $helper, private readonly EntityManagerInterface $entityManager, private readonly TranslatableStringExportLabelHelper $translatableStringExportLabelHelper)
{
private EntityManagerInterface $entityManager;
private ListActivityHelper $helper;
private TranslatableStringExportLabelHelper $translatableStringExportLabelHelper;
public function __construct(
ListActivityHelper $helper,
EntityManagerInterface $entityManager,
TranslatableStringExportLabelHelper $translatableStringExportLabelHelper
) {
$this->helper = $helper;
$this->entityManager = $entityManager;
$this->translatableStringExportLabelHelper = $translatableStringExportLabelHelper;
}
public function buildForm(FormBuilderInterface $builder)
@@ -50,17 +62,22 @@ class ListActivity implements ListInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
return match ($key) {
'acpId' => static function ($value) {
if ('_header' === $value) {
return ListActivityHelper::MSG_KEY . 'accompanying course id';
}
switch ($key) {
case 'acpId':
return static function ($value) {
if ('_header' === $value) {
return ListActivityHelper::MSG_KEY . 'accompanying course id';
}
return $value ?? '';
},
'scopesNames' => $this->translatableStringExportLabelHelper->getLabelMulti($key, $values, ListActivityHelper::MSG_KEY . 'course circles'),
default => $this->helper->getLabels($key, $values, $data),
};
return $value ?? '';
};
case 'scopesNames':
return $this->translatableStringExportLabelHelper->getLabelMulti($key, $values, ListActivityHelper::MSG_KEY . 'course circles');
default:
return $this->helper->getLabels($key, $values, $data);
}
}
public function getQueryKeys($data)

View File

@@ -24,8 +24,12 @@ use Symfony\Component\Form\FormBuilderInterface;
class CountActivity implements ExportInterface, GroupedExportInterface
{
public function __construct(protected ActivityRepository $activityRepository)
{
protected ActivityRepository $activityRepository;
public function __construct(
ActivityRepository $activityRepository
) {
$this->activityRepository = $activityRepository;
}
public function buildForm(FormBuilderInterface $builder)

View File

@@ -36,6 +36,8 @@ use function in_array;
class ListActivity implements ListInterface, GroupedExportInterface
{
protected EntityManagerInterface $entityManager;
protected array $fields = [
'id',
'date',
@@ -50,8 +52,22 @@ class ListActivity implements ListInterface, GroupedExportInterface
'person_id',
];
public function __construct(protected EntityManagerInterface $entityManager, protected TranslatorInterface $translator, protected TranslatableStringHelperInterface $translatableStringHelper, private readonly ActivityRepository $activityRepository)
{
protected TranslatableStringHelperInterface $translatableStringHelper;
protected TranslatorInterface $translator;
private ActivityRepository $activityRepository;
public function __construct(
EntityManagerInterface $em,
TranslatorInterface $translator,
TranslatableStringHelperInterface $translatableStringHelper,
ActivityRepository $activityRepository
) {
$this->entityManager = $em;
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
$this->activityRepository = $activityRepository;
}
public function buildForm(FormBuilderInterface $builder)

View File

@@ -30,18 +30,24 @@ use Symfony\Component\Form\FormBuilderInterface;
*/
class StatActivityDuration implements ExportInterface, GroupedExportInterface
{
final public const SUM = 'sum';
public const SUM = 'sum';
/**
* The action for this report.
*/
protected string $action;
private ActivityRepository $activityRepository;
/**
* @param string $action the stat to perform
*/
public function __construct(
private readonly ActivityRepository $activityRepository,
/**
* The action for this report.
*/
protected string $action = 'sum'
ActivityRepository $activityRepository,
string $action = 'sum'
) {
$this->action = $action;
$this->activityRepository = $activityRepository;
}
public function buildForm(FormBuilderInterface $builder)

View File

@@ -30,10 +30,46 @@ use const SORT_NUMERIC;
class ListActivityHelper
{
final public const MSG_KEY = 'export.list.activity.';
public const MSG_KEY = 'export.list.activity.';
public function __construct(private readonly ActivityPresenceRepositoryInterface $activityPresenceRepository, private readonly ActivityTypeRepositoryInterface $activityTypeRepository, private readonly DateTimeHelper $dateTimeHelper, private readonly LabelPersonHelper $labelPersonHelper, private readonly LabelThirdPartyHelper $labelThirdPartyHelper, private readonly TranslatorInterface $translator, private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly TranslatableStringExportLabelHelper $translatableStringLabelHelper, private readonly UserHelper $userHelper)
{
private ActivityPresenceRepositoryInterface $activityPresenceRepository;
private ActivityTypeRepositoryInterface $activityTypeRepository;
private DateTimeHelper $dateTimeHelper;
private LabelPersonHelper $labelPersonHelper;
private LabelThirdPartyHelper $labelThirdPartyHelper;
private TranslatableStringHelperInterface $translatableStringHelper;
private TranslatableStringExportLabelHelper $translatableStringLabelHelper;
private TranslatorInterface $translator;
private UserHelper $userHelper;
public function __construct(
ActivityPresenceRepositoryInterface $activityPresenceRepository,
ActivityTypeRepositoryInterface $activityTypeRepository,
DateTimeHelper $dateTimeHelper,
LabelPersonHelper $labelPersonHelper,
LabelThirdPartyHelper $labelThirdPartyHelper,
TranslatorInterface $translator,
TranslatableStringHelperInterface $translatableStringHelper,
TranslatableStringExportLabelHelper $translatableStringLabelHelper,
UserHelper $userHelper
) {
$this->activityPresenceRepository = $activityPresenceRepository;
$this->activityTypeRepository = $activityTypeRepository;
$this->dateTimeHelper = $dateTimeHelper;
$this->labelPersonHelper = $labelPersonHelper;
$this->labelThirdPartyHelper = $labelThirdPartyHelper;
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
$this->translatableStringLabelHelper = $translatableStringLabelHelper;
$this->userHelper = $userHelper;
}
public function addSelect(QueryBuilder $qb): void
@@ -79,78 +115,113 @@ class ListActivityHelper
public function getLabels($key, array $values, $data)
{
return match ($key) {
'createdAt', 'updatedAt' => $this->dateTimeHelper->getLabel($key),
'createdBy', 'updatedBy' => $this->userHelper->getLabel($key, $values, $key),
'date' => $this->dateTimeHelper->getLabel(self::MSG_KEY . $key),
'attendeeName' => function ($value) {
if ('_header' === $value) {
return 'Attendee';
}
switch ($key) {
case 'createdAt':
case 'updatedAt':
return $this->dateTimeHelper->getLabel($key);
if (null === $value || null === $presence = $this->activityPresenceRepository->find($value)) {
return '';
}
case 'createdBy':
case 'updatedBy':
return $this->userHelper->getLabel($key, $values, $key);
return $this->translatableStringHelper->localize($presence->getName());
},
'listReasons' => $this->translatableStringLabelHelper->getLabelMulti($key, $values, 'Activity Reasons'),
'typeName' => function ($value) {
if ('_header' === $value) {
return 'Activity type';
}
case 'date':
return $this->dateTimeHelper->getLabel(self::MSG_KEY . $key);
if (null === $value || null === $type = $this->activityTypeRepository->find($value)) {
return '';
}
case 'attendeeName':
return function ($value) {
if ('_header' === $value) {
return 'Attendee';
}
return $this->translatableStringHelper->localize($type->getName());
},
'usersNames' => $this->userHelper->getLabelMulti($key, $values, self::MSG_KEY . 'users name'),
'usersIds', 'thirdPartiesIds', 'personsIds' => static function ($value) use ($key) {
if ('_header' === $value) {
return match ($key) {
'usersIds' => self::MSG_KEY . 'users ids',
'thirdPartiesIds' => self::MSG_KEY . 'third parties ids',
'personsIds' => self::MSG_KEY . 'persons ids',
};
}
if (null === $value || null === $presence = $this->activityPresenceRepository->find($value)) {
return '';
}
$decoded = json_decode($value, null, 512, JSON_THROW_ON_ERROR);
return $this->translatableStringHelper->localize($presence->getName());
};
return implode(
'|',
array_unique(
array_filter($decoded, static fn (?int $id) => null !== $id),
SORT_NUMERIC
)
);
},
'personsNames' => $this->labelPersonHelper->getLabelMulti($key, $values, self::MSG_KEY . 'persons name'),
'thirdPartiesNames' => $this->labelThirdPartyHelper->getLabelMulti($key, $values, self::MSG_KEY . 'thirds parties'),
'sentReceived' => function ($value) {
if ('_header' === $value) {
return self::MSG_KEY . 'sent received';
}
case 'listReasons':
return $this->translatableStringLabelHelper->getLabelMulti($key, $values, 'Activity Reasons');
if (null === $value) {
return '';
}
case 'typeName':
return function ($value) {
if ('_header' === $value) {
return 'Activity type';
}
return $this->translator->trans($value);
},
default => function ($value) use ($key) {
if ('_header' === $value) {
return self::MSG_KEY . $key;
}
if (null === $value || null === $type = $this->activityTypeRepository->find($value)) {
return '';
}
if (null === $value) {
return '';
}
return $this->translatableStringHelper->localize($type->getName());
};
return $this->translator->trans($value);
},
};
case 'usersNames':
return $this->userHelper->getLabelMulti($key, $values, self::MSG_KEY . 'users name');
case 'usersIds':
case 'thirdPartiesIds':
case 'personsIds':
return static function ($value) use ($key) {
if ('_header' === $value) {
switch ($key) {
case 'usersIds':
return self::MSG_KEY . 'users ids';
case 'thirdPartiesIds':
return self::MSG_KEY . 'third parties ids';
case 'personsIds':
return self::MSG_KEY . 'persons ids';
default:
throw new LogicException('key not supported');
}
}
$decoded = json_decode($value, null, 512, JSON_THROW_ON_ERROR);
return implode(
'|',
array_unique(
array_filter($decoded, static fn (?int $id) => null !== $id),
SORT_NUMERIC
)
);
};
case 'personsNames':
return $this->labelPersonHelper->getLabelMulti($key, $values, self::MSG_KEY . 'persons name');
case 'thirdPartiesNames':
return $this->labelThirdPartyHelper->getLabelMulti($key, $values, self::MSG_KEY . 'thirds parties');
case 'sentReceived':
return function ($value) {
if ('_header' === $value) {
return self::MSG_KEY . 'sent received';
}
if (null === $value) {
return '';
}
return $this->translator->trans($value);
};
default:
return function ($value) use ($key) {
if ('_header' === $value) {
return self::MSG_KEY . $key;
}
if (null === $value) {
return '';
}
return $this->translator->trans($value);
};
}
}
public function getQueryKeys($data)

View File

@@ -23,8 +23,16 @@ use Symfony\Component\Form\FormBuilderInterface;
class ActivityTypeFilter implements FilterInterface
{
public function __construct(private readonly ActivityTypeRepositoryInterface $activityTypeRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper)
{
private ActivityTypeRepositoryInterface $activityTypeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ActivityTypeRepositoryInterface $activityTypeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->activityTypeRepository = $activityTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -20,8 +20,11 @@ use Symfony\Component\Form\FormBuilderInterface;
class ByCreatorFilter implements FilterInterface
{
public function __construct(private readonly UserRender $userRender)
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
$this->userRender = $userRender;
}
public function addRole(): ?string

View File

@@ -22,8 +22,11 @@ use function in_array;
class BySocialActionFilter implements FilterInterface
{
public function __construct(private readonly SocialActionRender $actionRender)
private SocialActionRender $actionRender;
public function __construct(SocialActionRender $actionRender)
{
$this->actionRender = $actionRender;
}
public function addRole(): ?string

View File

@@ -22,8 +22,11 @@ use function in_array;
class BySocialIssueFilter implements FilterInterface
{
public function __construct(private readonly SocialIssueRender $issueRender)
private SocialIssueRender $issueRender;
public function __construct(SocialIssueRender $issueRender)
{
$this->issueRender = $issueRender;
}
public function addRole(): ?string

View File

@@ -28,8 +28,11 @@ class EmergencyFilter implements FilterInterface
private const DEFAULT_CHOICE = false;
public function __construct(private readonly TranslatorInterface $translator)
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole(): ?string

View File

@@ -22,8 +22,11 @@ use function in_array;
class LocationTypeFilter implements FilterInterface
{
public function __construct(private readonly TranslatableStringHelper $translatableStringHelper)
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -29,8 +29,11 @@ class SentReceivedFilter implements FilterInterface
private const DEFAULT_CHOICE = Activity::SENTRECEIVED_SENT;
public function __construct(private readonly TranslatorInterface $translator)
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole(): ?string

View File

@@ -21,8 +21,11 @@ use Symfony\Component\Form\FormBuilderInterface;
class UserFilter implements FilterInterface
{
public function __construct(private readonly UserRender $userRender)
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
$this->userRender = $userRender;
}
public function addRole(): ?string

View File

@@ -23,8 +23,11 @@ use function in_array;
class UserScopeFilter implements FilterInterface
{
public function __construct(private readonly TranslatableStringHelper $translatableStringHelper)
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -27,8 +27,16 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class ActivityDateFilter implements FilterInterface
{
public function __construct(protected TranslatorInterface $translator, private readonly RollingDateConverterInterface $rollingDateConverter)
{
protected TranslatorInterface $translator;
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
TranslatorInterface $translator,
RollingDateConverterInterface $rollingDateConverter
) {
$this->translator = $translator;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string

View File

@@ -26,8 +26,16 @@ use function count;
class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInterface
{
public function __construct(protected TranslatableStringHelperInterface $translatableStringHelper, protected ActivityTypeRepositoryInterface $activityTypeRepository)
{
protected ActivityTypeRepositoryInterface $activityTypeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
TranslatableStringHelperInterface $translatableStringHelper,
ActivityTypeRepositoryInterface $activityTypeRepository
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->activityTypeRepository = $activityTypeRepository;
}
public function addRole(): ?string

View File

@@ -20,8 +20,11 @@ use Symfony\Component\Form\FormBuilderInterface;
class ActivityUsersFilter implements FilterInterface
{
public function __construct(private readonly UserRender $userRender)
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
$this->userRender = $userRender;
}
public function addRole(): ?string

View File

@@ -29,8 +29,16 @@ use function in_array;
class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInterface
{
public function __construct(protected TranslatableStringHelper $translatableStringHelper, protected ActivityReasonRepository $activityReasonRepository)
{
protected ActivityReasonRepository $activityReasonRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
TranslatableStringHelper $helper,
ActivityReasonRepository $activityReasonRepository
) {
$this->translatableStringHelper = $helper;
$this->activityReasonRepository = $activityReasonRepository;
}
public function addRole(): ?string

View File

@@ -37,8 +37,20 @@ use function count;
class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInterface, FilterInterface
{
public function __construct(protected TranslatableStringHelper $translatableStringHelper, protected ActivityReasonRepository $activityReasonRepository, protected TranslatorInterface $translator)
{
protected ActivityReasonRepository $activityReasonRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
protected TranslatorInterface $translator;
public function __construct(
TranslatableStringHelper $translatableStringHelper,
ActivityReasonRepository $activityReasonRepository,
TranslatorInterface $translator
) {
$this->translatableStringHelper = $translatableStringHelper;
$this->activityReasonRepository = $activityReasonRepository;
$this->translator = $translator;
}
public function addRole(): ?string

View File

@@ -22,8 +22,11 @@ use Symfony\Component\Form\FormBuilderInterface;
class UsersJobFilter implements FilterInterface
{
public function __construct(private readonly TranslatableStringHelperInterface $translatableStringHelper)
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(TranslatableStringHelperInterface $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -23,8 +23,16 @@ use Symfony\Component\Form\FormBuilderInterface;
class UsersScopeFilter implements FilterInterface
{
public function __construct(private readonly ScopeRepositoryInterface $scopeRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper)
{
private ScopeRepositoryInterface $scopeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ScopeRepositoryInterface $scopeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -58,22 +58,40 @@ use function in_array;
class ActivityType extends AbstractType
{
protected AuthorizationHelper $authorizationHelper;
protected ObjectManager $om;
protected SocialActionRender $socialActionRender;
protected SocialIssueRender $socialIssueRender;
protected array $timeChoices;
protected TranslatableStringHelper $translatableStringHelper;
protected User $user;
public function __construct(
TokenStorageInterface $tokenStorage,
protected AuthorizationHelper $authorizationHelper,
protected ObjectManager $om,
protected TranslatableStringHelper $translatableStringHelper,
protected array $timeChoices,
protected SocialIssueRender $socialIssueRender,
protected SocialActionRender $socialActionRender
AuthorizationHelper $authorizationHelper,
ObjectManager $om,
TranslatableStringHelper $translatableStringHelper,
array $timeChoices,
SocialIssueRender $socialIssueRender,
SocialActionRender $socialActionRender
) {
if (!$tokenStorage->getToken()->getUser() instanceof User) {
throw new RuntimeException('you should have a valid user');
}
$this->user = $tokenStorage->getToken()->getUser();
$this->authorizationHelper = $authorizationHelper;
$this->om = $om;
$this->translatableStringHelper = $translatableStringHelper;
$this->timeChoices = $timeChoices;
$this->socialIssueRender = $socialIssueRender;
$this->socialActionRender = $socialActionRender;
}
public function buildForm(FormBuilderInterface $builder, array $options): void

View File

@@ -25,8 +25,11 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class ActivityTypeType extends AbstractType
{
public function __construct(private readonly TranslatableStringHelper $translatableStringHelper)
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder, array $options)

View File

@@ -24,8 +24,20 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
*/
class PickActivityReasonType extends AbstractType
{
public function __construct(private readonly ActivityReasonRepository $activityReasonRepository, private readonly ActivityReasonRender $reasonRender, private readonly TranslatableStringHelperInterface $translatableStringHelper)
{
private ActivityReasonRepository $activityReasonRepository;
private ActivityReasonRender $reasonRender;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ActivityReasonRepository $activityReasonRepository,
ActivityReasonRender $reasonRender,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->activityReasonRepository = $activityReasonRepository;
$this->reasonRender = $reasonRender;
$this->translatableStringHelper = $translatableStringHelper;
}
public function configureOptions(OptionsResolver $resolver)

View File

@@ -23,8 +23,14 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class TranslatableActivityReasonCategoryType extends AbstractType
{
public function __construct(private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly TranslatorInterface $translator)
private TranslatableStringHelperInterface $translatableStringHelper;
private TranslatorInterface $translator;
public function __construct(TranslatableStringHelperInterface $translatableStringHelper, TranslatorInterface $translator)
{
$this->translatableStringHelper = $translatableStringHelper;
$this->translator = $translator;
}
public function configureOptions(OptionsResolver $resolver)

View File

@@ -20,8 +20,16 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class TranslatableActivityType extends AbstractType
{
public function __construct(protected TranslatableStringHelperInterface $translatableStringHelper, protected ActivityTypeRepositoryInterface $activityTypeRepository)
{
protected ActivityTypeRepositoryInterface $activityTypeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
TranslatableStringHelperInterface $helper,
ActivityTypeRepositoryInterface $activityTypeRepository
) {
$this->translatableStringHelper = $helper;
$this->activityTypeRepository = $activityTypeRepository;
}
public function configureOptions(OptionsResolver $resolver)

View File

@@ -23,8 +23,16 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
{
public function __construct(protected Security $security, protected TranslatorInterface $translator)
{
protected Security $security;
protected TranslatorInterface $translator;
public function __construct(
Security $security,
TranslatorInterface $translator
) {
$this->security = $security;
$this->translator = $translator;
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)

View File

@@ -18,10 +18,13 @@ use Symfony\Component\Security\Core\Security;
/**
* @implements LocalMenuBuilderInterface<array>
*/
final readonly class AdminMenuBuilder implements LocalMenuBuilderInterface
final class AdminMenuBuilder implements LocalMenuBuilderInterface
{
public function __construct(private Security $security)
private Security $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)

View File

@@ -23,8 +23,22 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
final class PersonMenuBuilder implements LocalMenuBuilderInterface
{
public function __construct(protected AuthorizationCheckerInterface $authorizationChecker, protected TranslatorInterface $translator)
{
/**
* @var AuthorizationCheckerInterface
*/
private $authorizationChecker;
/**
* @var TranslatorInterface
*/
private $translator;
public function __construct(
AuthorizationCheckerInterface $authorizationChecker,
TranslatorInterface $translator
) {
$this->translator = $translator;
$this->authorizationChecker = $authorizationChecker;
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)

View File

@@ -16,10 +16,13 @@ use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Notification\NotificationHandlerInterface;
final readonly class ActivityNotificationHandler implements NotificationHandlerInterface
final class ActivityNotificationHandler implements NotificationHandlerInterface
{
public function __construct(private ActivityRepository $activityRepository)
private ActivityRepository $activityRepository;
public function __construct(ActivityRepository $activityRepository)
{
$this->activityRepository = $activityRepository;
}
public function getTemplate(Notification $notification, array $options = []): string

View File

@@ -33,10 +33,34 @@ use Symfony\Component\Security\Core\Security;
use function count;
use function in_array;
final readonly class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInterface
final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInterface
{
public function __construct(private AuthorizationHelper $authorizationHelper, private CenterResolverDispatcherInterface $centerResolverDispatcher, private TokenStorageInterface $tokenStorage, private ActivityRepository $repository, private EntityManagerInterface $em, private Security $security)
{
private AuthorizationHelper $authorizationHelper;
private CenterResolverDispatcherInterface $centerResolverDispatcher;
private EntityManagerInterface $em;
private ActivityRepository $repository;
private Security $security;
private TokenStorageInterface $tokenStorage;
public function __construct(
AuthorizationHelper $authorizationHelper,
CenterResolverDispatcherInterface $centerResolverDispatcher,
TokenStorageInterface $tokenStorage,
ActivityRepository $repository,
EntityManagerInterface $em,
Security $security
) {
$this->authorizationHelper = $authorizationHelper;
$this->centerResolverDispatcher = $centerResolverDispatcher;
$this->tokenStorage = $tokenStorage;
$this->repository = $repository;
$this->em = $em;
$this->security = $security;
}
public function findByAccompanyingPeriod(AccompanyingPeriod $period, string $role, ?int $start = 0, ?int $limit = 1000, ?array $orderBy = []): array

View File

@@ -17,7 +17,7 @@ use Doctrine\ORM\EntityRepository;
class ActivityPresenceRepository implements ActivityPresenceRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -23,11 +23,15 @@ use Symfony\Component\HttpFoundation\RequestStack;
*/
class ActivityReasonRepository extends ServiceEntityRepository
{
private RequestStack $requestStack;
public function __construct(
ManagerRegistry $registry,
private readonly RequestStack $requestStack
RequestStack $requestStack
) {
parent::__construct($registry, ActivityReason::class);
$this->requestStack = $requestStack;
}
/**

View File

@@ -17,7 +17,7 @@ use Doctrine\ORM\EntityRepository;
final class ActivityTypeRepository implements ActivityTypeRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{

View File

@@ -20,9 +20,9 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class ActivityStatsVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{
final public const LISTS = 'CHILL_ACTIVITY_LIST';
public const LISTS = 'CHILL_ACTIVITY_LIST';
final public const STATS = 'CHILL_ACTIVITY_STATS';
public const STATS = 'CHILL_ACTIVITY_STATS';
protected VoterHelperInterface $helper;

View File

@@ -35,7 +35,7 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
*
* It is safe for usage in template and controller
*/
final public const CREATE = 'CHILL_ACTIVITY_CREATE';
public const CREATE = 'CHILL_ACTIVITY_CREATE';
/**
* role to allow to create an activity associated win an accompanying course.
@@ -44,7 +44,7 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
*
* @internal
*/
final public const CREATE_ACCOMPANYING_COURSE = 'CHILL_ACTIVITY_CREATE_ACCOMPANYING_COURSE';
public const CREATE_ACCOMPANYING_COURSE = 'CHILL_ACTIVITY_CREATE_ACCOMPANYING_COURSE';
/**
* role to allow to create an activity associated with a person.
@@ -53,17 +53,17 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
*
* @internal
*/
final public const CREATE_PERSON = 'CHILL_ACTIVITY_CREATE_PERSON';
public const CREATE_PERSON = 'CHILL_ACTIVITY_CREATE_PERSON';
final public const DELETE = 'CHILL_ACTIVITY_DELETE';
public const DELETE = 'CHILL_ACTIVITY_DELETE';
final public const FULL = 'CHILL_ACTIVITY_FULL';
public const FULL = 'CHILL_ACTIVITY_FULL';
final public const SEE = 'CHILL_ACTIVITY_SEE';
public const SEE = 'CHILL_ACTIVITY_SEE';
final public const SEE_DETAILS = 'CHILL_ACTIVITY_SEE_DETAILS';
public const SEE_DETAILS = 'CHILL_ACTIVITY_SEE_DETAILS';
final public const UPDATE = 'CHILL_ACTIVITY_UPDATE';
public const UPDATE = 'CHILL_ACTIVITY_UPDATE';
private const ALL = [
self::CREATE,
@@ -74,12 +74,15 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
self::FULL,
];
protected Security $security;
protected VoterHelperInterface $voterHelper;
public function __construct(
protected Security $security,
Security $security,
VoterHelperFactoryInterface $voterHelperFactory
) {
$this->security = $security;
$this->voterHelper = $voterHelperFactory->generate(self::class)
->addCheckFor(Person::class, [self::SEE, self::CREATE])
->addCheckFor(AccompanyingPeriod::class, [self::SEE, self::CREATE])

View File

@@ -39,8 +39,40 @@ class ActivityContext implements
DocGeneratorContextWithAdminFormInterface,
DocGeneratorContextWithPublicFormInterface
{
public function __construct(private readonly DocumentCategoryRepository $documentCategoryRepository, private readonly NormalizerInterface $normalizer, private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly EntityManagerInterface $em, private readonly PersonRenderInterface $personRender, private readonly PersonRepository $personRepository, private readonly TranslatorInterface $translator, private readonly BaseContextData $baseContextData)
{
private BaseContextData $baseContextData;
private DocumentCategoryRepository $documentCategoryRepository;
private EntityManagerInterface $em;
private NormalizerInterface $normalizer;
private PersonRenderInterface $personRender;
private PersonRepository $personRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
private TranslatorInterface $translator;
public function __construct(
DocumentCategoryRepository $documentCategoryRepository,
NormalizerInterface $normalizer,
TranslatableStringHelperInterface $translatableStringHelper,
EntityManagerInterface $em,
PersonRenderInterface $personRender,
PersonRepository $personRepository,
TranslatorInterface $translator,
BaseContextData $baseContextData
) {
$this->documentCategoryRepository = $documentCategoryRepository;
$this->normalizer = $normalizer;
$this->translatableStringHelper = $translatableStringHelper;
$this->em = $em;
$this->personRender = $personRender;
$this->personRepository = $personRepository;
$this->translator = $translator;
$this->baseContextData = $baseContextData;
}
public function adminFormReverseTransform(array $data): array

View File

@@ -48,8 +48,44 @@ class ListActivitiesByAccompanyingPeriodContext implements
DocGeneratorContextWithAdminFormInterface,
DocGeneratorContextWithPublicFormInterface
{
public function __construct(private readonly AccompanyingPeriodContext $accompanyingPeriodContext, private readonly ActivityACLAwareRepositoryInterface $activityACLAwareRepository, private readonly NormalizerInterface $normalizer, private readonly PersonRepository $personRepository, private readonly SocialActionRepository $socialActionRepository, private readonly SocialIssueRepository $socialIssueRepository, private readonly ThirdPartyRepository $thirdPartyRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly UserRepository $userRepository)
{
private AccompanyingPeriodContext $accompanyingPeriodContext;
private ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
private NormalizerInterface $normalizer;
private PersonRepository $personRepository;
private SocialActionRepository $socialActionRepository;
private SocialIssueRepository $socialIssueRepository;
private ThirdPartyRepository $thirdPartyRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
private UserRepository $userRepository;
public function __construct(
AccompanyingPeriodContext $accompanyingPeriodContext,
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
NormalizerInterface $normalizer,
PersonRepository $personRepository,
SocialActionRepository $socialActionRepository,
SocialIssueRepository $socialIssueRepository,
ThirdPartyRepository $thirdPartyRepository,
TranslatableStringHelperInterface $translatableStringHelper,
UserRepository $userRepository,
) {
$this->accompanyingPeriodContext = $accompanyingPeriodContext;
$this->activityACLAwareRepository = $activityACLAwareRepository;
$this->normalizer = $normalizer;
$this->personRepository = $personRepository;
$this->socialActionRepository = $socialActionRepository;
$this->socialIssueRepository = $socialIssueRepository;
$this->thirdPartyRepository = $thirdPartyRepository;
$this->translatableStringHelper = $translatableStringHelper;
$this->userRepository = $userRepository;
}
public function adminFormReverseTransform(array $data): array

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Service\EntityInfo\AccompanyingPeriodInfoQueryPart;
use Chill\ActivityBundle\Entity\Activity;
use Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface;
class ActivityUsersDateQueryPartForAccompanyingPeriodInfo implements AccompanyingPeriodInfoUnionQueryPartInterface
{
public function getAccompanyingPeriodIdColumn(): string
{
return 'activity.accompanyingperiod_id';
}
public function getRelatedEntityColumn(): string
{
return Activity::class;
}
public function getRelatedEntityIdColumn(): string
{
return 'activity.id';
}
public function getUserIdColumn(): string
{
return 'au.user_id';
}
public function getDateTimeColumn(): string
{
return 'activity.date';
}
public function getDiscriminator(): string
{
return 'activity_date';
}
public function getMetadataColumn(): string
{
return '\'{}\'::jsonb';
}
public function getFromStatement(): string
{
return 'activity
LEFT JOIN activity_user au on activity.id = au.activity_id';
}
public function getWhereClause(): string
{
return 'activity.accompanyingperiod_id IS NOT NULL';
}
}

View File

@@ -76,8 +76,10 @@ final class ActivityControllerTest extends WebTestCase
/**
* @dataProvider getSecuredPagesUnauthenticated
*
* @param mixed $url
*/
public function testAccessIsDeniedForUnauthenticated(mixed $url)
public function testAccessIsDeniedForUnauthenticated($url)
{
$client = $this->createClient();
@@ -214,7 +216,7 @@ final class ActivityControllerTest extends WebTestCase
->setName('social without activity');
//copy role scopes where ACTIVITY is not present
foreach ($socialPermissionGroup->getRoleScopes() as $roleScope) {
if (!strpos((string) $roleScope->getRole(), 'ACTIVITY')) {
if (!strpos($roleScope->getRole(), 'ACTIVITY')) {
$withoutActivityPermissionGroup->addRoleScope($roleScope);
}
}
@@ -260,9 +262,11 @@ final class ActivityControllerTest extends WebTestCase
}
/**
* @param mixed $username
*
* @return \Symfony\Component\BrowserKit\Client
*/
private function getAuthenticatedClient(mixed $username = 'center a_social')
private function getAuthenticatedClient($username = 'center a_social')
{
return self::createClient([], [
'PHP_AUTH_USER' => $username,

View File

@@ -19,7 +19,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
*/
final class ActivityReasonCategoryControllerTest extends WebTestCase
{
public function testToWrite(): never
public function testToWrite()
{
$this->markTestSkipped();
}

View File

@@ -19,7 +19,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
*/
final class ActivityReasonControllerTest extends WebTestCase
{
public function testToWrite(): never
public function testToWrite()
{
$this->markTestSkipped();
}

View File

@@ -19,7 +19,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
*/
final class ActivityTypeControllerTest extends WebTestCase
{
public function testToWrite(): never
public function testToWrite()
{
$this->markTestSkipped();
}

View File

@@ -34,7 +34,7 @@ final class TranslatableActivityReasonTest extends TypeTestCase
parent::setUp();
}
public function testSimple(): never
public function testSimple()
{
$translatableActivityReasonType = new PickActivityReasonType(
$this->getTranslatableStringHelper()

View File

@@ -89,9 +89,11 @@ final class TranslatableActivityTypeTest extends KernelTestCase
}
/**
* @param mixed $active
*
* @return \Chill\ActivityBundle\Entity\ActivityType
*/
protected function getRandomType(mixed $active = true)
protected function getRandomType($active = true)
{
$types = $this->container->get('doctrine.orm.entity_manager')
->getRepository(ActivityType::class)

View File

@@ -19,7 +19,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
*/
final class TimelineProviderTest extends WebTestCase
{
public function testAnActivityIsShownOnTimeline(): never
public function testAnActivityIsShownOnTimeline()
{
$this->markTestSkipped('we have to write fixtures before writing this tests');
}

View File

@@ -33,14 +33,24 @@ class TimelineActivityProvider implements TimelineProviderInterface
{
private const SUPPORTED_CONTEXTS = ['center', 'person'];
protected ActivityACLAwareRepository $aclAwareRepository;
protected EntityManagerInterface $em;
protected AuthorizationHelperInterface $helper;
protected UserInterface $user;
public function __construct(
protected EntityManagerInterface $em,
protected AuthorizationHelperInterface $helper,
EntityManagerInterface $em,
AuthorizationHelperInterface $helper,
TokenStorageInterface $storage,
protected ActivityACLAwareRepository $aclAwareRepository
ActivityACLAwareRepository $aclAwareRepository
) {
$this->em = $em;
$this->helper = $helper;
$this->aclAwareRepository = $aclAwareRepository;
if (!$storage->getToken()->getUser() instanceof User) {
throw new RuntimeException('A user should be authenticated !');
}

View File

@@ -18,9 +18,9 @@ use Symfony\Component\Validator\Constraint;
*/
class ActivityValidity extends Constraint
{
final public const IS_REQUIRED_MESSAGE = ' is required';
public const IS_REQUIRED_MESSAGE = ' is required';
final public const ROOT_MESSAGE = 'For this type of activity, ';
public const ROOT_MESSAGE = 'For this type of activity, ';
public $noPersonsMessage = 'For this type of activity, you must add at least one person';

View File

@@ -34,6 +34,7 @@ services:
resource: '../Validator/Constraints/'
Chill\ActivityBundle\Service\DocGenerator\:
autowire: true
autoconfigure: true
resource: '../Service/DocGenerator/'
Chill\ActivityBundle\Service\EntityInfo\:
resource: '../Service/EntityInfo/'

View File

@@ -21,8 +21,11 @@ use Symfony\Component\HttpFoundation\Request;
final class AsideActivityController extends CRUDController
{
public function __construct(private readonly AsideActivityCategoryRepository $categoryRepository)
private AsideActivityCategoryRepository $categoryRepository;
public function __construct(AsideActivityCategoryRepository $categoryRepository)
{
$this->categoryRepository = $categoryRepository;
}
public function createEntity(string $action, Request $request): object

View File

@@ -24,8 +24,11 @@ use function random_int;
class LoadAsideActivity extends Fixture implements DependentFixtureInterface
{
public function __construct(private readonly UserRepository $userRepository)
private UserRepository $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function getDependencies(): array

View File

@@ -114,8 +114,10 @@ class AsideActivityCategory
/**
* @Assert\Callback
*
* @param mixed $payload
*/
public function preventRecursiveParent(ExecutionContextInterface $context, mixed $payload)
public function preventRecursiveParent(ExecutionContextInterface $context, $payload)
{
if (!$this->hasParent()) {
return;

View File

@@ -20,8 +20,14 @@ use Symfony\Component\Form\FormBuilderInterface;
class ByActivityTypeAggregator implements AggregatorInterface
{
public function __construct(private readonly AsideActivityCategoryRepository $asideActivityCategoryRepository, private readonly TranslatableStringHelper $translatableStringHelper)
private AsideActivityCategoryRepository $asideActivityCategoryRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(AsideActivityCategoryRepository $asideActivityCategoryRepository, TranslatableStringHelper $translatableStringHelper)
{
$this->asideActivityCategoryRepository = $asideActivityCategoryRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -22,8 +22,14 @@ use function in_array;
class ByUserJobAggregator implements AggregatorInterface
{
public function __construct(private readonly UserJobRepositoryInterface $userJobRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper)
private TranslatableStringHelperInterface $translatableStringHelper;
private UserJobRepositoryInterface $userJobRepository;
public function __construct(UserJobRepositoryInterface $userJobRepository, TranslatableStringHelperInterface $translatableStringHelper)
{
$this->userJobRepository = $userJobRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -22,8 +22,14 @@ use function in_array;
class ByUserScopeAggregator implements AggregatorInterface
{
public function __construct(private readonly ScopeRepositoryInterface $scopeRepository, private readonly TranslatableStringHelperInterface $translatableStringHelper)
private ScopeRepositoryInterface $scopeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(ScopeRepositoryInterface $scopeRepository, TranslatableStringHelperInterface $translatableStringHelper)
{
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -16,5 +16,5 @@ namespace Chill\AsideActivityBundle\Export;
*/
abstract class Declarations
{
final public const ASIDE_ACTIVITY_TYPE = 'aside_activity';
public const ASIDE_ACTIVITY_TYPE = 'aside_activity';
}

View File

@@ -23,8 +23,12 @@ use Symfony\Component\Form\FormBuilderInterface;
class AvgAsideActivityDuration implements ExportInterface, GroupedExportInterface
{
public function __construct(private readonly AsideActivityRepository $repository)
{
private AsideActivityRepository $repository;
public function __construct(
AsideActivityRepository $repository
) {
$this->repository = $repository;
}
public function buildForm(FormBuilderInterface $builder)

View File

@@ -23,8 +23,12 @@ use Symfony\Component\Form\FormBuilderInterface;
class CountAsideActivity implements ExportInterface, GroupedExportInterface
{
public function __construct(private readonly AsideActivityRepository $repository)
{
private AsideActivityRepository $repository;
public function __construct(
AsideActivityRepository $repository
) {
$this->repository = $repository;
}
public function buildForm(FormBuilderInterface $builder)

View File

@@ -32,10 +32,42 @@ use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class ListAsideActivity implements ListInterface, GroupedExportInterface
final class ListAsideActivity implements ListInterface, GroupedExportInterface
{
public function __construct(private EntityManagerInterface $em, private DateTimeHelper $dateTimeHelper, private UserHelper $userHelper, private ScopeRepositoryInterface $scopeRepository, private CenterRepositoryInterface $centerRepository, private AsideActivityCategoryRepository $asideActivityCategoryRepository, private CategoryRender $categoryRender, private TranslatableStringHelperInterface $translatableStringHelper)
{
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)
@@ -59,67 +91,86 @@ final readonly class ListAsideActivity implements ListInterface, GroupedExportIn
public function getLabels($key, array $values, $data)
{
return match ($key) {
'id', 'note' => static function ($value) use ($key) {
if ('_header' === $value) {
return 'export.aside_activity.' . $key;
}
switch ($key) {
case 'id':
case 'note':
return static function ($value) use ($key) {
if ('_header' === $value) {
return 'export.aside_activity.' . $key;
}
return $value ?? '';
},
'duration' => static function ($value) use ($key) {
if ('_header' === $value) {
return 'export.aside_activity.' . $key;
}
return $value ?? '';
};
if (null === $value) {
return '';
}
case 'duration':
return static function ($value) use ($key) {
if ('_header' === $value) {
return 'export.aside_activity.' . $key;
}
if ($value instanceof DateTimeInterface) {
return $value->format('H:i:s');
}
if (null === $value) {
return '';
}
return $value;
},
'createdAt', 'updatedAt', 'date' => $this->dateTimeHelper->getLabel('export.aside_activity.' . $key),
'agent_id', 'creator_id' => $this->userHelper->getLabel($key, $values, 'export.aside_activity.' . $key),
'aside_activity_type' => function ($value) {
if ('_header' === $value) {
return 'export.aside_activity.aside_activity_type';
}
if ($value instanceof DateTimeInterface) {
return $value->format('H:i:s');
}
if (null === $value || '' === $value || null === $c = $this->asideActivityCategoryRepository->find($value)) {
return '';
}
return $value;
};
return $this->categoryRender->renderString($c, []);
},
'main_scope' => function ($value) {
if ('_header' === $value) {
return 'export.aside_activity.main_scope';
}
case 'createdAt':
case 'updatedAt':
case 'date':
return $this->dateTimeHelper->getLabel('export.aside_activity.' . $key);
if (null === $value || '' === $value || null === $c = $this->scopeRepository->find($value)) {
return '';
}
case 'agent_id':
case 'creator_id':
return $this->userHelper->getLabel($key, $values, 'export.aside_activity.' . $key);
return $this->translatableStringHelper->localize($c->getName());
},
'main_center' => function ($value) {
if ('_header' === $value) {
return 'export.aside_activity.main_center';
}
case 'aside_activity_type':
return function ($value) {
if ('_header' === $value) {
return 'export.aside_activity.aside_activity_type';
}
if (null === $value || '' === $value || null === $c = $this->centerRepository->find($value)) {
/** @var Center $c */
return '';
}
if (null === $value || '' === $value || null === $c = $this->asideActivityCategoryRepository->find($value)) {
return '';
}
return $c->getName();
},
default => throw new LogicException('this key is not supported : ' . $key),
};
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';
}
if (null === $value || '' === $value || null === $c = $this->centerRepository->find($value)) {
/** @var Center $c */
return '';
}
return $c->getName();
};
default:
throw new LogicException('this key is not supported : ' . $key);
}
}
public function getQueryKeys($data)

View File

@@ -23,8 +23,12 @@ use Symfony\Component\Form\FormBuilderInterface;
class SumAsideActivityDuration implements ExportInterface, GroupedExportInterface
{
public function __construct(private readonly AsideActivityRepository $repository)
{
private AsideActivityRepository $repository;
public function __construct(
AsideActivityRepository $repository
) {
$this->repository = $repository;
}
public function buildForm(FormBuilderInterface $builder)

View File

@@ -23,8 +23,20 @@ use Symfony\Component\Form\FormBuilderInterface;
class ByActivityTypeFilter implements FilterInterface
{
public function __construct(private readonly CategoryRender $categoryRender, private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly AsideActivityCategoryRepository $asideActivityTypeRepository)
{
private AsideActivityCategoryRepository $asideActivityTypeRepository;
private CategoryRender $categoryRender;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
CategoryRender $categoryRender,
TranslatableStringHelperInterface $translatableStringHelper,
AsideActivityCategoryRepository $asideActivityTypeRepository
) {
$this->categoryRender = $categoryRender;
$this->asideActivityTypeRepository = $asideActivityTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string

View File

@@ -26,8 +26,16 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class ByDateFilter implements FilterInterface
{
public function __construct(private readonly RollingDateConverterInterface $rollingDateConverter, protected TranslatorInterface $translator)
{
protected TranslatorInterface $translator;
private RollingDateConverterInterface $rollingDateConverter;
public function __construct(
RollingDateConverterInterface $rollingDateConverter,
TranslatorInterface $translator
) {
$this->translator = $translator;
$this->rollingDateConverter = $rollingDateConverter;
}
public function addRole(): ?string

View File

@@ -20,8 +20,11 @@ use Symfony\Component\Form\FormBuilderInterface;
class ByUserFilter implements FilterInterface
{
public function __construct(private readonly UserRender $userRender)
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
$this->userRender = $userRender;
}
public function addRole(): ?string

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