Compare commits

...

66 Commits

Author SHA1 Message Date
4d4367712e Pipeline fixes 2025-08-25 19:49:11 +02:00
3654005a74 Add missing parameter in creation of new AssignTaskEvent 2025-08-25 19:45:55 +02:00
9df423a201 Add TaskAssignEventSubscriber unit tests
- Covered `getSubscribedEvents` method behavior
- Added tests for `onTaskAssigned` with changed and unchanged assignees
- Validated notification properties in appropriate scenarios
2025-08-25 19:38:42 +02:00
6d9834de71 Improve assignee handling and task assignment logic
- Added `hasAssigneeChanged` method for detecting assignee change
- Updated `onTaskAssigned` logic to trigger only on assignee change
2025-08-25 19:38:15 +02:00
bc5f8d56fe Trans task assignment notification email
- Updated `task_assignment_notification_content.txt.twig` with dynamic translations and URLs
2025-08-25 15:35:00 +02:00
5daa83c93e Add task assignment notification system
- Introduced `AssignTaskEvent`, `TaskAssignEventSubscriber`, and `AssignTaskNotificationFlagProvider` for task assignment notifications
- WIP : Added notification templates for task assignment title and content
- Updated `SingleTaskController` to dispatch `AssignTaskEvent`
- Adjusted translations and service configurations accordingly
2025-08-14 14:54:18 +02:00
88dd7116f3 Refactor TaskNotificationHandler logic 2025-08-12 10:25:47 +02:00
ff25ab2f91 feat: add task notification handler
- WIP Implement `TaskNotificationHandler` for generating task-related notifications
- Created `showInNotification.html.twig` for rendering task notification view
- Added logic to fetch and display task data in notifications
2025-08-08 15:53:34 +02:00
aa085a1562 **fix:** add min and step attributes to integer field in DateIntervalType 2025-08-06 17:35:45 +02:00
2754251fdc Merge branch 'master' of https://gitlab.com/Chill-Projet/chill-bundles 2025-08-06 14:20:29 +02:00
2f6cef4238 - **fix:** move closing motive up to be coherent with display elsewhere 2025-08-06 14:20:09 +02:00
2309636eae - **fix:** adjust display logic for accompanying period dates, include closing date if period is closed. 2025-08-06 13:47:29 +02:00
56ec8fb516 Remove 'to_validate' as default for task filter 2025-08-06 09:05:39 +02:00
fe6e6e54c1 Show filters on list pages unfolded by default 2025-07-22 15:50:49 +02:00
2a09594b4a UI improvement: limit display of particapations in event list page 2025-07-22 13:26:44 +02:00
7c798e1f63 Merge branch '387-notification-user-group' into 'master'
Resolve "Notification: envoi à des groupes utilisateurs"

Closes #387

See merge request Chill-Projet/chill-bundles!842
2025-07-20 20:18:49 +00:00
ab8da4ab7a Resolve "Notification: envoi à des groupes utilisateurs" 2025-07-20 20:18:49 +00:00
5bdb2df929 Merge branch 'revert-5f016734' into 'master'
Revert "Merge branch 'ticket/supplementary-comments-on-motive' into 'master'"

See merge request Chill-Projet/chill-bundles!863
2025-07-20 18:51:51 +00:00
e3a6b60fa2 Revert "Merge branch 'ticket/supplementary-comments-on-motive' into 'master'"
This reverts merge request !855
2025-07-20 18:50:33 +00:00
5f01673404 Merge branch 'ticket/supplementary-comments-on-motive' into 'master'
Ajout de commentaires supplémentaires aux motifs

See merge request Chill-Projet/chill-bundles!855
2025-07-11 14:06:40 +00:00
63d0a52ea1 Ajout de commentaires supplémentaires aux motifs 2025-07-11 14:06:40 +00:00
837089ff5d Fix testMerge method in AccompanyingPeriodWorkMergeServiceTest.php 2025-07-10 11:33:23 +02:00
f383fab578 Fix styling 2025-07-09 15:30:39 +02:00
f3cc4a89af Update chill bundles to v4.0.2 2025-07-09 15:23:59 +02:00
703f5dc32d Transfer evaluations (and related documents) during merge 2025-07-09 15:21:42 +02:00
b870e71f77 Add translation for validation message in social action merger 2025-07-09 15:21:24 +02:00
a7e278204f fix changelog 2025-07-09 09:22:28 +02:00
4cfdcb2f02 Release v4.0.1 with fix in package.json file 2025-07-08 17:00:24 +02:00
3b82ab0e7f Release v4.0.0 2025-07-08 15:59:04 +02:00
ccfae1dc75 Merge branch '339-partage-d'export-enregistré' into 'master'
Partage d'export enregistré et génération asynchrone des exports

Closes #339 and #338

See merge request Chill-Projet/chill-bundles!800
2025-07-08 13:53:38 +00:00
8bc16dadb0 Partage d'export enregistré et génération asynchrone des exports 2025-07-08 13:53:25 +00:00
c4cc0baa8e Merge branch 'eslint-fix-issues-2025-07' into 'master'
Fix Eslint issues

See merge request Chill-Projet/chill-bundles!853
2025-07-08 13:38:51 +00:00
aed114c75c Fix Eslint issues 2025-07-08 13:38:51 +00:00
e592b89c94 remove ux-translator from dependencies 2025-07-07 12:36:49 +02:00
70e75adb7d Merge branch 'master' of https://gitlab.com/Chill-Projet/chill-bundles 2025-07-02 13:57:20 +02:00
6f7015b152 Fix translations of form fields in admin for social actions 2025-07-02 13:56:41 +02:00
65dde1e6a0 Merge branch '390-fix-search-results' into 'master'
Fix participant condition in list_with_period.html.twig

Closes #390

See merge request Chill-Projet/chill-bundles!832
2025-07-02 10:59:17 +00:00
d193c50922 Merge branch '359-fusion-accompanying-period-work' into 'master'
Resolve "Fusion actions d'accompagnement"

Closes #359

See merge request Chill-Projet/chill-bundles!804
2025-07-02 10:53:17 +00:00
840ef6eed8 Resolve "Fusion actions d'accompagnement" 2025-07-02 10:53:16 +00:00
b4bbb1a456 Merge branch 'improve_person_resource_form' into 'master'
Improve person resource form

See merge request Chill-Projet/chill-bundles!846
2025-07-01 14:01:44 +00:00
606435a6b3 Pipeline corrections 2025-07-01 14:47:36 +02:00
404143f8a6 Merge branch 'improve_person_resource_form' of https://gitlab.com/Chill-Projet/chill-bundles into improve_person_resource_form 2025-07-01 14:34:39 +02:00
ec957a2fe3 Improve UX with better labeling in person resource form 2025-07-01 14:34:19 +02:00
8ed5e35f1a Merge branch 'master' of https://gitlab.com/Chill-Projet/chill-bundles 2025-07-01 14:33:27 +02:00
ec37676dab release v3.12.1 2025-06-30 20:35:00 +02:00
2d8cda30b9 Add localizeString method to PickTemplate component for string localization 2025-06-30 20:32:17 +02:00
27d344c97d Release v3.12.0 2025-06-30 11:00:42 +02:00
088e5692e2 Merge branch 'improve_person_resource_form' into 'master'
Improve admin templates for event admin entities + activity reason (category)...

See merge request Chill-Projet/chill-bundles!838
2025-06-30 08:44:24 +00:00
298044bc82 Improve admin templates for event admin entities + activity reason (category)... 2025-06-30 08:44:24 +00:00
ee4e223043 Merge branch '393-fix-dump-only-document-generator' into 'master'
Send data dumps as email attachments instead of links, update translations,...

Closes #393

See merge request Chill-Projet/chill-bundles!843
2025-06-30 08:41:09 +00:00
c53377ce8d Merge branch 'workflow-do-not-remove-workflow-canceled-automatically' into 'master'
Remove unnecessary workflow deletion logic when in the initial position

See merge request Chill-Projet/chill-bundles!844
2025-06-30 08:40:46 +00:00
0b580658de Remove unnecessary workflow deletion logic when in the initial position 2025-06-26 14:38:12 +02:00
786c60a50d Send data dumps as email attachments instead of links, update translations, and add unit tests for the handler. 2025-06-26 12:21:19 +02:00
456f00566d update juni guidelines 2025-06-26 12:19:49 +02:00
a38116cca4 fix cs 2025-06-20 17:31:13 +02:00
nobohan
9158e33854 #392 php cs-fixer 2025-06-19 21:29:43 +02:00
nobohan
af74f7860b Fixed nullable content for NewsItem in setContent signature #392 2025-06-19 17:53:33 +02:00
c2842148c6 Improve UX with better labeling in person resource form 2025-06-17 19:27:06 +02:00
10e4c7da23 Merge branch 'master' of https://gitlab.com/Chill-Projet/chill-bundles 2025-06-17 18:45:25 +02:00
bdf1cf71ba Fix argument usage in localizeString method for UserRenderBoxBadge component
Adjusted `localizeString` method to accept a `label` parameter, ensuring proper localization of `user_job.label` and `main_scope.name`. Also made minor syntax adjustments to improve readability.
2025-06-17 10:55:30 +02:00
f680a35f49 Improve admin templates for event admin entities + activity reason (category) entities and remove delete and show actions for coherence 2025-06-11 17:13:19 +02:00
7d0fe06651 Fix admin entity edit actions for event admin entities and activity reason (category) entities 2025-06-11 16:46:04 +02:00
5ee8a6bc82 Merge branch 'upgrade-ci-image' into 'master'
Update CI configuration to use `chill/base-image:8.3-edge` instead of the old PHP 8.2 base image

See merge request Chill-Projet/chill-bundles!834
2025-06-05 10:10:41 +00:00
47cf83ef93 Update CI configuration to use chill/base-image:8.3-edge instead of the old PHP 8.2 base image 2025-06-05 11:30:01 +02:00
fca10ada71 Fix translation keys and participant pluralization in list_with_period.html.twig
Updated French translations for "Participants" and improved pluralization handling in accompanying_period keys. Modified list_with_period.html.twig to dynamically translate "Participants" based on the count of current participations.
2025-06-04 16:55:56 +02:00
a35d456308 Fix participant condition in list_with_period.html.twig
Updated the condition to properly handle cases where the first participation's person is not the current person.
2025-06-04 16:54:44 +02:00
654 changed files with 20410 additions and 6838 deletions

View File

@@ -1,6 +0,0 @@
kind: DX
body: Remove dead code for wopi-link module
time: 2025-04-30T14:45:50.406111606+02:00
custom:
Issue: "352"
SchemaChange: No schema change

View File

@@ -1,6 +0,0 @@
kind: DX
body: Replace library node-sass by sass, and upgrade bootstrap to version 5.3 (yarn upgrade / install is required)
time: 2025-05-28T16:58:13.226870341+02:00
custom:
Issue: ""
SchemaChange: No schema change

View File

@@ -1,7 +0,0 @@
kind: Feature
body: Add the document file name to the document title when a user upload a document,
unless there is already a document title.
time: 2025-04-24T14:22:11.800975422+02:00
custom:
Issue: "377"
SchemaChange: No schema change

View File

@@ -1,6 +0,0 @@
kind: Feature
body: Add desactivation date for social action and issue csv export
time: 2025-05-20T09:56:28.108941934+02:00
custom:
Issue: ""
SchemaChange: No schema change

View File

@@ -1,6 +0,0 @@
kind: Feature
body: Add Emoji and Fullscreen feature to ckeditor configuration
time: 2025-05-23T13:33:41.645095128+02:00
custom:
Issue: ""
SchemaChange: No schema change

View File

@@ -1,6 +0,0 @@
kind: Feature
body: Create editor which allow us to toggle between rich and simple text editor
time: 2025-05-23T13:34:34.56795603+02:00
custom:
Issue: "321"
SchemaChange: No schema change

View File

@@ -0,0 +1,6 @@
kind: Feature
body: Show filters on list pages unfolded by default
time: 2025-07-22T15:50:39.338057044+02:00
custom:
Issue: "399"
SchemaChange: No schema change

View File

@@ -1,7 +0,0 @@
kind: Fixed
body: trying to prevent bug of typeerror in doc-history + improved display of document
history
time: 2025-04-24T13:39:43.878468232+02:00
custom:
Issue: "376"
SchemaChange: No schema change

View File

@@ -1,7 +0,0 @@
kind: Fixed
body: Display previous participation in acc course work even if the person has left
the acc course
time: 2025-04-24T16:37:46.970203594+02:00
custom:
Issue: "381"
SchemaChange: No schema change

View File

@@ -1,6 +0,0 @@
kind: Fixed
body: Fix display of text in calendar events
time: 2025-05-05T10:27:15.461493066+02:00
custom:
Issue: "372"
SchemaChange: No schema change

View File

@@ -1,6 +0,0 @@
kind: Fixed
body: Add missing translation for user_group.no_user_groups
time: 2025-05-14T14:53:39.53927329+02:00
custom:
Issue: ""
SchemaChange: No schema change

View File

@@ -0,0 +1,6 @@
kind: Fixed
body: adjust display logic for accompanying period dates, include closing date if period is closed.
time: 2025-08-06T13:46:09.241584292+02:00
custom:
Issue: "382"
SchemaChange: No schema change

View File

@@ -0,0 +1,6 @@
kind: Fixed
body: add min and step attributes to integer field in DateIntervalType
time: 2025-08-06T17:35:27.413787704+02:00
custom:
Issue: "384"
SchemaChange: No schema change

View File

@@ -1,6 +0,0 @@
kind: UX
body: Remove default filter in_progress for the page 'my tasks'; Allows for new tasks to be displayed upon opening of the page
time: 2025-04-23T17:26:24.45777387+02:00
custom:
Issue: "374"
SchemaChange: No schema change

View File

@@ -0,0 +1,6 @@
kind: UX
body: Limit display of participations in event list
time: 2025-07-22T13:26:37.500656935+02:00
custom:
Issue: ""
SchemaChange: No schema change

22
.changes/v3.12.0.md Normal file
View File

@@ -0,0 +1,22 @@
## v3.12.0 - 2025-06-30
### Feature
* ([#377](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/377)) Add the document file name to the document title when a user upload a document, unless there is already a document title.
* Add desactivation date for social action and issue csv export
* Add Emoji and Fullscreen feature to ckeditor configuration
* ([#321](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/321)) Create editor which allow us to toggle between rich and simple text editor
* Do not remove workflow which are automatically canceled after staling for more than 30 days
### Fixed
* ([#376](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/376)) trying to prevent bug of typeerror in doc-history + improved display of document history
* ([#381](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/381)) Display previous participation in acc course work even if the person has left the acc course
* ([#372](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/372)) Fix display of text in calendar events
* Add missing translation for user_group.no_user_groups
* Fix admin entity edit actions for event admin entities and activity reason (category) entities
* ([#392](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/392)) Allow null and cast as string to setContent method for NewsItem
* ([#393](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/393)) Doc Generation: the "dump only" method send the document as an email attachment.
### DX
* ([#352](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/352)) Remove dead code for wopi-link module
* Replace library node-sass by sass, and upgrade bootstrap to version 5.3 (yarn upgrade / install is required)
### UX
* ([#374](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/374)) Remove default filter in_progress for the page 'my tasks'; Allows for new tasks to be displayed upon opening of the page
* Improve labeling of fields in person resource creation form

3
.changes/v3.12.1.md Normal file
View File

@@ -0,0 +1,3 @@
## v3.12.1 - 2025-06-30
### Fixed
* Fix loading of the list of documents

74
.changes/v4.0.0.md Normal file
View File

@@ -0,0 +1,74 @@
## v4.0.0 - 2025-07-08
### Feature
* ([#359](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/359)) Allow the merge of two accompanying period works
### Fixed
* ([#390](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/390)) Display the list of participant in the results, even if there is only one participant and that the search result display the requestor
* Fix admin entity edit actions for event admin entities and activity reason (category) entities
* Fix translations for social action fields in admin form: results, goals, evaluations
### DX
* Rewrite exports to run them asynchronously
**Schema Change**: Add columns or tables
* Allow TranslatableMessage in flash messages
### UX
* Improve labeling of fields in person resource creation form
**Release notes**
- Add new methods to serialize data using the rector rule
- Remove all references to the Request in filters, aggregators, filters. Actually, the most frequent occurence is `$security->getUser()`.
- Refactor manually the initializeQuery method
- Remove the injection of ExportManager into the constructor of each export element:
```diff
- class MyFormatter implements FormatterInterface
+ class MyFormatter implements FormatterInterface, \Chill\MainBundle\Export\ExportManagerAwareInterface
{
+ use \Chill\MainBundle\Export\Helper\ExportManagerAwareTrait;
- public function __construct(private ExportManager $exportmanager) {}
public function MyMethod(): void
{
- $this->exportManager->getFilter('alias');
+ $this->getExportManager()->getFilter('alias');
}
}
```
- configure messenger to handle export in a queue:
```diff
# config/packages/messenger.yaml
framework:
messenger:
routing:
+ 'Chill\MainBundle\Export\Messenger\ExportRequestGenerationMessage': priority
```
- add missing methods to exports, aggregators, filters, formatter:
```php
public function normalizeFormData(array $formData): array;
public function denormalizeFormData(array $formData, int $fromVersion): array;
```
There are rector rules to generate those methods:
- `Chill\Utils\Rector\Rector\ChillBundleAddNormalizationMethodsOnExportRector`
See:
```php
// upgrade chill exports
$rectorConfig->rules([\Chill\Utils\Rector\Rector\ChillBundleAddNormalizationMethodsOnExportRector::class]);
```
This rule will create most of the work necessary, but some manuals changes are still necessary:
- we must set manually the correct repository for method `denormalizeDoctrineEntity`;
- when the form data contains some entities, and the form type is not one of EntityType::class, PickUserDynamicType::class, PickUserLocationType::class, PickThirdpartyDynamicType::class, Select2CountryType::class, then we must handle the normalization manually (using the `\Chill\MainBundle\Export\ExportDataNormalizerTrait`)

4
.changes/v4.0.1.md Normal file
View File

@@ -0,0 +1,4 @@
## v4.0.1 - 2025-07-08
### Fixed
* Fix package.json for compilation

4
.changes/v4.0.2.md Normal file
View File

@@ -0,0 +1,4 @@
## v4.0.2 - 2025-07-09
### Fixed
* Fix add missing translation
* Fix the transfer of evaluations and documents during of accompanyingperiodwork

View File

@@ -46,7 +46,7 @@ stages:
build:
stage: Composer install
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
image: chill/base-image:8.3-edge
before_script:
- composer config -g cache-dir "$(pwd)/.cache"
script:
@@ -61,7 +61,7 @@ build:
code_style:
stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
image: chill/base-image:8.3-edge
script:
- php-cs-fixer fix --dry-run -v --show-progress=none
cache:
@@ -74,7 +74,7 @@ code_style:
phpstan_tests:
stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
image: chill/base-image:8.3-edge
variables:
COMPOSER_MEMORY_LIMIT: 3G
before_script:
@@ -91,7 +91,7 @@ phpstan_tests:
rector_tests:
stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
image: chill/base-image:8.3-edge
before_script:
- bin/console cache:clear --env=dev
script:
@@ -132,7 +132,7 @@ lint:
unit_tests:
stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
image: chill/base-image:8.3-edge
variables:
COMPOSER_MEMORY_LIMIT: 3G
before_script:

View File

@@ -22,7 +22,7 @@ Chill is a comprehensive web application built as a set of Symfony bundles. It i
- **Backend**: PHP 8.3+, Symfony 5.4
- **Frontend**: JavaScript/TypeScript, Vue.js 3, Bootstrap 5
- **Build Tools**: Webpack Encore, Yarn
- **Database**: PostgreSQL with materialized views
- **Database**: PostgreSQL with materialized views. We do not support other databases.
- **Other Services**: Redis, AMQP (RabbitMQ), SMTP
## Project Structure
@@ -149,14 +149,93 @@ Key configuration files:
- `package.json`: JavaScript dependencies and scripts
- `.env`: Default environment variables. Must usually not be updated: use `.env.local` instead.
### Database migrations
Each time a doctrine entity is created, we generate migration to adapt the database.
The migration are created using the command `symfony console doctrine:migrations:diff --no-interaction --namespace <namespace>`, where the namespace is the relevant namespace for migration. As this is a bash script, do not forget to quote the `\` (`\` must become `\\` in your command).
Each bundle has his own namespace for migration (always ask me to confirm that command, with a list of updated / created entities so that I can confirm you that it is ok):
- `Chill\Bundle\ActivityBundle` writes migrations to `Chill\Migrations\Activity`;
- `Chill\Bundle\BudgetBundle` writes migrations to `Chill\Migrations\Budget`;
- `Chill\Bundle\CustomFieldsBundle` writes migrations to `Chill\Migrations\CustomFields`;
- `Chill\Bundle\DocGeneratorBundle` writes migrations to `Chill\Migrations\DocGenerator`;
- `Chill\Bundle\DocStoreBundle` writes migrations to `Chill\Migrations\DocStore`;
- `Chill\Bundle\EventBundle` writes migrations to `Chill\Migrations\Event`;
- `Chill\Bundle\CalendarBundle` writes migrations to `Chill\Migrations\Calendar`;
- `Chill\Bundle\FamilyMembersBundle` writes migrations to `Chill\Migrations\FamilyMembers`;
- `Chill\Bundle\FranceTravailApiBundle` writes migrations to `Chill\Migrations\FranceTravailApi`;
- `Chill\Bundle\JobBundle` writes migrations to `Chill\Migrations\Job`;
- `Chill\Bundle\MainBundle` writes migrations to `Chill\Migrations\Main`;
- `Chill\Bundle\PersonBundle` writes migrations to `Chill\Migrations\Person`;
- `Chill\Bundle\ReportBundle` writes migrations to `Chill\Migrations\Report`;
- `Chill\Bundle\TaskBundle` writes migrations to `Chill\Migrations\Task`;
- `Chill\Bundle\ThirdPartyBundle` writes migrations to `Chill\Migrations\ThirdParty`;
- `Chill\Bundle\TicketBundle` writes migrations to `Chill\Migrations\Ticket`;
- `Chill\Bundle\WopiBundle` writes migrations to `Chill\Migrations\Wopi`;
Once created the, comment's classes should be removed and a description of the changes made to the entities should be added to the migrations, using the `getDescription` method. The migration should not be cleaned by any artificial intelligence, as modifying this migration is error prone.
### Guidelines related to code structure and requirements
#### Usage of clock
When we need to use a DateTime or DateTimeImmutable that need to express "now", we prefer the usage of
`Symfony\Component\Clock\ClockInterface`, where possible. This is usually not possible in doctrine entities,
where injection does not work when restoring an entity from database, but usually possible in services.
In test, we use `\Symfony\Component\Clock\MockClock` which is an implementation of `Symfony\Component\Clock\ClockInterface`
where we have full and easy control of the date.
### Testing Information
The project uses PHPUnit for testing. Each bundle has its own test suite, and there's also a global test suite at the root level.
#### Use of mock in tests
##### General mocking
For creating mock, we prefer using prophecy (library phpspec/prophecy).
##### Useful helpers and tips that avoid create a mock
Some notable implementations that are tests helper, and avoid to create a mock:
- `\Psr\Log\NullLogger`, an implementation of `\Psr\Log\LoggerInterface`;
- `\Symfony\Component\Clock\MockClock`, an implementation of `Symfony\Component\Clock\ClockInterface` (already mentioned above);
- `\Symfony\Component\HttpClient\MockHttpClient`, an implementation of `\Symfony\Contracts\HttpClient\HttpClientInterface`;
- When using `\Symfony\Component\Mailer\MailerInterface`, we can create the mock with "InMemoryTransport":
```php
use Symfony\Component\Mailer\Transport\InMemoryTransport;
use \Symfony\Component\Mailer\Mailer;
$transport = new InMemoryTransport();
$mailer = new Mailer($transport);
// After sending:
$messages = $transport->getSent(); // array of SentMessage
```
- When using `\Symfony\Contracts\EventDispatcher\EventDispatcherInterface`, we can use directly an instance of `\Symfony\Component\EventDispatcher\EventDispatcher`;
##### When we prefer not creating a mock
- When we use Doctrine Entities related to the project, we prefer not to use a mock: we instantiate them directly (unless it requires too much code to write);
##### Mocking final and readonly classes
Classes marked as final can't be mocked. To avoid that, either:
- we remove the `final` keyword from the class;
- we extract an interface from the final class.
This must be a decision made by a human, not by an AI. Every AI task must abort with an explicit message in that case.
#### Running Tests
The tests are run from the project's root (not from the bundle's root).
```bash
# Run all tests
vendor/bin/phpunit
@@ -218,7 +297,7 @@ class TicketTest extends TestCase
#### Test Database
For tests that require a database, the project uses an in-memory SQLite database by default. You can configure a different database for testing in the `.env.test` file.
For tests that require a database, the project uses postgresql database filled by fixtures (usage of doctrine-fixtures). You can configure a different database for testing in the `.env.test` file.
### Code Quality Tools

View File

@@ -6,6 +6,138 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
## v4.0.2 - 2025-07-09
### Fixed
* Fix add missing translation
* Fix the transfer of evaluations and documents during of accompanyingperiodwork
## v4.0.1 - 2025-07-08
### Fixed
* Fix package.json for compilation
## v4.0.0 - 2025-07-08
### Feature
* ([#359](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/359)) Allow the merge of two accompanying period works
### Fixed
* ([#390](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/390)) Display the list of participant in the results, even if there is only one participant and that the search result display the requestor
* Fix admin entity edit actions for event admin entities and activity reason (category) entities
* Fix translations for social action fields in admin form: results, goals, evaluations
### DX
* Rewrite exports to run them asynchronously
**Schema Change**: Add columns or tables
* Allow TranslatableMessage in flash messages
### UX
* Improve labeling of fields in person resource creation form
**Release notes**
- Add new methods to serialize data using the rector rule
- Remove all references to the Request in filters, aggregators, filters. Actually, the most frequent occurence is `$security->getUser()`.
- Refactor manually the initializeQuery method
- Remove the injection of ExportManager into the constructor of each export element:
```diff
- class MyFormatter implements FormatterInterface
+ class MyFormatter implements FormatterInterface, \Chill\MainBundle\Export\ExportManagerAwareInterface
{
+ use \Chill\MainBundle\Export\Helper\ExportManagerAwareTrait;
- public function __construct(private ExportManager $exportmanager) {}
public function MyMethod(): void
{
- $this->exportManager->getFilter('alias');
+ $this->getExportManager()->getFilter('alias');
}
}
```
- configure messenger to handle export in a queue:
```diff
# config/packages/messenger.yaml
framework:
messenger:
routing:
+ 'Chill\MainBundle\Export\Messenger\ExportRequestGenerationMessage': priority
```
- add missing methods to exports, aggregators, filters, formatter:
```php
public function normalizeFormData(array $formData): array;
public function denormalizeFormData(array $formData, int $fromVersion): array;
```
There are rector rules to generate those methods:
- `Chill\Utils\Rector\Rector\ChillBundleAddNormalizationMethodsOnExportRector`
See:
```php
// upgrade chill exports
$rectorConfig->rules([\Chill\Utils\Rector\Rector\ChillBundleAddNormalizationMethodsOnExportRector::class]);
```
This rule will create most of the work necessary, but some manuals changes are still necessary:
- we must set manually the correct repository for method `denormalizeDoctrineEntity`;
- when the form data contains some entities, and the form type is not one of EntityType::class, PickUserDynamicType::class, PickUserLocationType::class, PickThirdpartyDynamicType::class, Select2CountryType::class, then we must handle the normalization manually (using the `\Chill\MainBundle\Export\ExportDataNormalizerTrait`)
## v3.12.1 - 2025-06-30
### Fixed
* Fix loading of the list of documents
## v3.12.0 - 2025-06-30
### Feature
* ([#377](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/377)) Add the document file name to the document title when a user upload a document, unless there is already a document title.
* Add desactivation date for social action and issue csv export
* Add Emoji and Fullscreen feature to ckeditor configuration
* ([#321](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/321)) Create editor which allow us to toggle between rich and simple text editor
* Do not remove workflow which are automatically canceled after staling for more than 30 days
### Fixed
* ([#376](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/376)) trying to prevent bug of typeerror in doc-history + improved display of document history
* ([#381](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/381)) Display previous participation in acc course work even if the person has left the acc course
* ([#372](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/372)) Fix display of text in calendar events
* Add missing translation for user_group.no_user_groups
* Fix admin entity edit actions for event admin entities and activity reason (category) entities
* ([#392](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/392)) Allow null and cast as string to setContent method for NewsItem
* ([#393](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/393)) Doc Generation: the "dump only" method send the document as an email attachment.
### DX
* ([#352](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/352)) Remove dead code for wopi-link module
* Replace library node-sass by sass, and upgrade bootstrap to version 5.3 (yarn upgrade / install is required)
### UX
* ([#374](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/374)) Remove default filter in_progress for the page 'my tasks'; Allows for new tasks to be displayed upon opening of the page
* Improve labeling of fields in person resource creation form
## v3.11.0 - 2025-04-17
### Feature
* ([#365](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/365)) Add counters of actions and activities, with 2 boxes to (1) show the number of active actions on total actions and (2) show the number of activities in a accompanying period, and pills in menus for showing the number of active actions and the number of activities.
* ([#364](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/364)) Added a second phone number "telephone2" to the thirdParty entity. Adapted twig templates and vuejs apps to handle this phone number
**Schema Change**: Add columns or tables
* Signature: add a button to go directly to the signature zone, even if there is only one
### Fixed
* Fixed wrong translations in the on-the-fly for creation of thirdParty
* Fixed update of phone number in on-the-fly edition of thirdParty
* Fixed closing of modal when editing thirdParty in accompanying course works
* Shorten the delay between two execution of AccompanyingPeriodStepChangeCronjob, to ensure at least one execution in a day
* ([#102](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/102)) Fix display of title in document list
* When cleaning the old stored object versions, do not throw an error if the stored object is not found on disk
* Add consistent log prefix and key to logs when stale workflows are automatically canceled
* ([#380](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/380)) Remove the "not null" validation constraint on recently added properties on HouseholdComposition
### DX
* Add new chill-col style for displaying title and aside in a flex table
## v3.10.3 - 2025-03-18
### DX
* Eslint fixes

View File

@@ -17,6 +17,7 @@ chill_main:
acl:
form_show_scopes: true
form_show_centers: true
filter_stats_by_center: true
access_global_history: false
access_user_change_password: true
access_permissions_group_list: true

View File

@@ -5,7 +5,6 @@ framework:
# Uncomment this (and the failed transport below) to send failed messages to this transport for later handling.
failure_transport: failed
transports:
# those transports are added by chill-bundles recipes
sync: sync://
@@ -19,7 +18,9 @@ framework:
async: ~
auto_setup: true
priority: '%env(MESSENGER_TRANSPORT_DSN)%/priority'
priority:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%/priority'
# end of transports added by chill-bundles recipes
# https://symfony.com/doc/current/messenger.html#transport-configuration
failed: 'doctrine://default?queue_name=failed'
@@ -61,6 +62,10 @@ framework:
'Chill\MainBundle\Workflow\Messenger\PostSignatureStateChangeMessage': priority
'Chill\MainBundle\Workflow\Messenger\PostPublicViewMessage': async
'Chill\MainBundle\Service\Workflow\CancelStaleWorkflowMessage': async
'Chill\MainBundle\Notification\Email\NotificationEmailMessages\SendImmediateNotificationEmailMessage': async
'Chill\MainBundle\Export\Messenger\ExportRequestGenerationMessage': priority
'Chill\MainBundle\Export\Messenger\RemoveExportGenerationMessage': async
'Chill\MainBundle\Notification\Email\NotificationEmailMessages\ScheduleDailyNotificationDigestMessage': async
# end of routes added by chill-bundles recipes
# Route your messages to the transports
# 'App\Message\YourMessage': async

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use DateTime;
use Doctrine\ORM\Query\Expr;
@@ -20,6 +21,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
class BirthdateFilter implements ExportElementValidatedInterface, FilterInterface
{
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
// add specific role for this filter
public function addRole(): ?string
{
@@ -28,7 +30,7 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
}
// here, we alter the query created by Export
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$where = $qb->getDQLPart('where');
// we create the clause here
@@ -52,13 +54,13 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
}
// we give information on which type of export this filter applies
public function applyOn()
public function applyOn(): string
{
return 'person';
}
// we build a form to collect some parameters from the users
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder): void
{
$builder->add('date_from', DateType::class, [
'label' => 'Born after this date',
@@ -74,6 +76,18 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
'format' => 'dd-MM-yyyy',
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['date_from' => $this->normalizeDate($formData['date_from']), 'date_to' => $this->normalizeDate($formData['date_to'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['date_from' => $this->denormalizeDate($formData['date_from']), 'date_to' => $this->denormalizeDate($formData['date_to'])];
}
public function getFormDefaultData(): array
{
return ['date_from' => new DateTime(), 'date_to' => new DateTime()];
@@ -81,7 +95,7 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
// here, we create a simple string which will describe the action of
// the filter in the Response
public function describeAction($data, $format = 'string')
public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array
{
return ['Filtered by person\'s birtdate: '
. 'between %date_from% and %date_to%', [
@@ -90,7 +104,7 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
], ];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'Filter by person\'s birthdate';
}
@@ -99,7 +113,7 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
// is executed here. This function is added by the interface
// `ExportElementValidatedInterface`, and can be ignore if there is
// no need for a validation
public function validateForm($data, ExecutionContextInterface $context)
public function validateForm($data, ExecutionContextInterface $context): void
{
$date_from = $data['date_from'];
$date_to = $data['date_to'];

View File

@@ -36,6 +36,18 @@ class CountPerson implements ExportInterface
{
// this export does not add any form
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
@@ -60,29 +72,29 @@ class CountPerson implements ExportInterface
};
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
// this array match the result keys in the query. We have only
// one column.
return ['export_result'];
}
public function getResult($query, $data)
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'Count peoples';
}
public function getType()
public function getType(): string
{
return Declarations::PERSON_TYPE;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
// we gather all center the user choose.
$centers = array_map(static fn ($el) => $el['center'], $acl);

View File

@@ -0,0 +1,84 @@
@startuml
'https://plantuml.com/sequence-diagram
autonumber
User -> ExportController: configure export using form
activate ExportController
ExportController -> ExportForm: build form
activate ExportForm
loop for every ExportElement (Filter, Aggregator)
ExportForm -> ExportElement: `buildForm`
activate ExportElement
ExportElement -> ExportForm: add form to builders
deactivate ExportElement
end
ExportForm -> ExportController
deactivate ExportForm
ExportController -> User: show form
deactivate ExportController
note left of User: Configure the export:\ncheck filters, aggregators, …
User -> ExportController: post configuration of the export
activate ExportController
ExportController -> ExportForm: `getData`
activate ExportForm
ExportForm -> ExportController: return data: list of entities, etc.
deactivate ExportForm
loop for every ExportElement (Filter, Aggregator)
ExportController -> ExportElement: serializeData (data)
activate ExportElement
ExportElement -> ExportController: return serializedData (simple array with string, int, …)
deactivate ExportElement
end
ExportController -> Database: `INSERT INTO RequestGeneration_table` (insert new entity)
ExportController -> MessageQueue: warn about a new request
activate MessageQueue
ExportController -> User: "ok, generation is in process"
deactivate ExportController
note left of User: The user see a waiting screen
MessageQueue -> MessengerConsumer: forward the message to the MessengerConsumer
deactivate MessageQueue
activate MessengerConsumer
MessengerConsumer -> Database: `SELECT * FROM RequestGeneration_table WHERE id = %s`
activate Database
Database -> MessengerConsumer: return RequestGeneration with serializedData
deactivate Database
loop for every ExportElement (Filter, Aggregator)
MessengerConsumer -> ExportElement: deserializeData
activate ExportElement
ExportElement -> MessengerConsumer: return data (list of entities, etc.) from the serialized array
deactivate ExportElement
MessengerConsumer -> ExportElement: alter the sql query (`ExportElement::alterQuery`)
activate ExportElement
ExportElement -> MessengerConsumer: return the query with WHERE and GROUP BY clauses
deactivate ExportElement
end
MessengerConsumer -> MessengerConsumer: prepare the export
MessengerConsumer -> MessengerConsumer: save the export as a stored object
MessengerConsumer -> Database: `UPDATE RequestGeneration_table SET ready = true`
deactivate MessengerConsumer
User -> ExportController: pull every 5s to know if the export is generated
activate ExportController
ExportController -> User: warn the export is generated
deactivate ExportController
User -> ExportController: download the export from object storage
@enduml

View File

@@ -15,24 +15,31 @@ Messages to users, flashbags and buttons
Flashbags
==========
The four following levels are defined :
The four following levels are defined :
+-----------+----------------------------------------------------------------------------------------------+
|Key |Intent |
+===========+==============================================================================================+
|alert |A message not linked with the user action, but which should require an action or a |
| |correction. |
+-----------+----------------------------------------------------------------------------------------------+
|success |The user action succeeds. |
+-----------+----------------------------------------------------------------------------------------------+
|notice |A simple message to give information to the user. The message may be linked or not linked with|
| |the user action. |
+-----------+----------------------------------------------------------------------------------------------+
|warning |A message linked with an action, the user should correct. |
+-----------+----------------------------------------------------------------------------------------------+
|error |The user's action failed: he must correct something to process the action. |
+-----------+----------------------------------------------------------------------------------------------+
We can use :code:`TranslatableMessage` (and other :code:`TranslatableMessageInterface` instances) into the controller:
.. code-block:: php
// in a controller action:
if (($session = $request->getSession()) instanceof Session) {
$session->getFlashBag()->add(
'success',
new TranslatableMessage('saved_export.Saved export is saved!')
);
}
.. seealso::
`Flash Messages on Symfony documentation <http://symfony.com/doc/current/book/controller.html#flash-messages>`_
@@ -66,7 +73,7 @@ To add the action on button, use them as class along with ``sc-button`` :
| | | - Submitting this form will remove the entity |
+-----------+----------------+------------------------------------------------------------------------------+
| Edit | ``bt-edit`` or | Link to a form to edit an entity |
| | ``bt-update`` | |
| | ``bt-update`` | |
+-----------+----------------+------------------------------------------------------------------------------+
| Save | ``bt-save`` | Submitting this form will save change on the entity |
+-----------+----------------+------------------------------------------------------------------------------+

View File

@@ -2154,11 +2154,6 @@ parameters:
count: 1
path: src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php
-
message: "#^Instanceof between string and DateTimeInterface will always evaluate to false\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php
-
message: "#^PHPDoc tag @var for property Chill\\\\MainBundle\\\\Export\\\\Helper\\\\ExportAddressHelper\\:\\:\\$unitNamesKeysCache contains unresolvable type\\.$#"
count: 1

View File

@@ -3,7 +3,7 @@ parameters:
paths:
- src/
- utils/
tmpDir: .cache/
tmpDir: var/cache/phpstan
reportUnmatchedIgnoredErrors: false
excludePaths:
- .php_cs*

View File

@@ -1,13 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" bootstrap="vendor/autoload.php" executionOrder="depends,defects" beStrictAboutOutputDuringTests="true" failOnRisky="true" failOnWarning="true" colors="true" cacheDirectory="var/cache/phpunit.rector.cache" requireCoverageMetadata="true" beStrictAboutCoverageMetadata="true">
<testsuites>
<testsuite name="default">
<directory>utils/rector/tests</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">utils/rector/src</directory>
</include>
</source>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
bootstrap="vendor/autoload.php"
executionOrder="depends,defects"
beStrictAboutOutputDuringTests="true"
failOnRisky="true"
failOnWarning="true"
colors="true"
cacheDirectory="var/cache/phpunit.rector.cache"
requireCoverageMetadata="true"
beStrictAboutCoverageMetadata="true"
displayDetailsOnTestsThatTriggerWarnings="true">
<testsuites>
<testsuite name="default">
<directory>utils/rector/tests</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">utils/rector/src</directory>
</include>
</source>
</phpunit>

View File

@@ -18,14 +18,15 @@ return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/docs',
__DIR__ . '/src',
__DIR__ . '/rector.php',
]);
$rectorConfig->skip([
\Rector\Php55\Rector\String_\StringClassNameToClassConstantRector::class => __DIR__ . 'src/Bundle/ChillMainBundle/Service/Notifier/LegacyOvhCloudFactory.php'
]);
$rectorConfig->symfonyContainerXml(__DIR__ . '/var/cache/dev/test/App_KernelTestDebugContainer.xml ');
$rectorConfig->symfonyContainerPhp(__DIR__ . '/tests/symfony-container.php');
//$rectorConfig->symfonyContainerXml(__DIR__ . '/var/cache/dev/test/App_KernelTestDebugContainer.xml ');
//$rectorConfig->symfonyContainerPhp(__DIR__ . '/tests/symfony-container.php');
//$rectorConfig->cacheClass(\Rector\Caching\ValueObject\Storage\FileCacheStorage::class);
//$rectorConfig->cacheDirectory(__DIR__ . '/.cache/rector');
@@ -39,16 +40,11 @@ return static function (RectorConfig $rectorConfig): void {
// part of the symfony 54 rules
$rectorConfig->rule(\Rector\Symfony\Symfony53\Rector\StaticPropertyFetch\KernelTestCaseContainerPropertyDeprecationRector::class);
$rectorConfig->rule(\Rector\Symfony\Symfony60\Rector\MethodCall\GetHelperControllerToServiceRector::class);
$rectorConfig->disableParallel();
//$rectorConfig->disableParallel();
//define sets of rules
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_82,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_40,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_41,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_42,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_43,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_44,
\Rector\Doctrine\Set\DoctrineSetList::DOCTRINE_CODE_QUALITY,
\Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_90,
]);
@@ -66,11 +62,6 @@ return static function (RectorConfig $rectorConfig): void {
// some routes are added twice if it remains activated
// $rectorConfig->rule(\Rector\Symfony\Configs\Rector\ClassMethod\AddRouteAnnotationRector::class);
// chill rules
$rectorConfig->rules([
\Chill\Utils\Rector\Rector\ChillBundleMakeDataProviderStaticForAbstractExportTestRector::class,
]);
// skip some path...
$rectorConfig->skip([
// waiting for fixing this bug: https://github.com/rectorphp/rector-doctrine/issues/342
@@ -94,4 +85,6 @@ return static function (RectorConfig $rectorConfig): void {
new \Rector\Php80\ValueObject\AnnotationToAttribute('Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistency'),
new \Rector\Php80\ValueObject\AnnotationToAttribute('Chill\MainBundle\Workflow\Validator\EntityWorkflowCreation'),
]);
};

View File

@@ -48,28 +48,6 @@ class ActivityReasonCategoryController extends AbstractController
]);
}
/**
* Displays a form to edit an existing ActivityReasonCategory entity.
*/
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/admin/activityreasoncategory/{id}/edit', name: 'chill_activity_activityreasoncategory_edit')]
public function editAction(mixed $id)
{
$em = $this->managerRegistry->getManager();
$entity = $em->getRepository(ActivityReasonCategory::class)->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find ActivityReasonCategory entity.');
}
$editForm = $this->createEditForm($entity);
return $this->render('@ChillActivity/ActivityReasonCategory/edit.html.twig', [
'entity' => $entity,
'edit_form' => $editForm->createView(),
]);
}
/**
* Lists all ActivityReasonCategory entities.
*/
@@ -100,29 +78,10 @@ class ActivityReasonCategoryController extends AbstractController
]);
}
/**
* Finds and displays a ActivityReasonCategory entity.
*/
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/admin/activityreasoncategory/{id}/show', name: 'chill_activity_activityreasoncategory_show')]
public function showAction(mixed $id)
{
$em = $this->managerRegistry->getManager();
$entity = $em->getRepository(ActivityReasonCategory::class)->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find ActivityReasonCategory entity.');
}
return $this->render('@ChillActivity/ActivityReasonCategory/show.html.twig', [
'entity' => $entity,
]);
}
/**
* Edits an existing ActivityReasonCategory entity.
*/
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/admin/activityreasoncategory/{id}/update', name: 'chill_activity_activityreasoncategory_update', methods: ['POST', 'PUT'])]
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/admin/activityreasoncategory/{id}/update', name: 'chill_activity_activityreasoncategory_update')]
public function updateAction(Request $request, mixed $id)
{
$em = $this->managerRegistry->getManager();
@@ -139,7 +98,7 @@ class ActivityReasonCategoryController extends AbstractController
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em->flush();
return $this->redirectToRoute('chill_activity_activityreasoncategory_edit', ['id' => $id]);
return $this->redirectToRoute('chill_activity_activityreasoncategory', ['id' => $id]);
}
return $this->render('@ChillActivity/ActivityReasonCategory/edit.html.twig', [
@@ -178,7 +137,7 @@ class ActivityReasonCategoryController extends AbstractController
{
$form = $this->createForm(ActivityReasonCategoryType::class, $entity, [
'action' => $this->generateUrl('chill_activity_activityreasoncategory_update', ['id' => $entity->getId()]),
'method' => 'PUT',
'method' => 'POST',
]);
$form->add('submit', SubmitType::class, ['label' => 'Update']);

View File

@@ -17,7 +17,6 @@ use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* ActivityReason controller.
@@ -50,28 +49,6 @@ class ActivityReasonController extends AbstractController
]);
}
/**
* Displays a form to edit an existing ActivityReason entity.
*/
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/admin/activityreason/{id}/edit', name: 'chill_activity_activityreason_edit')]
public function editAction(mixed $id)
{
$em = $this->managerRegistry->getManager();
$entity = $em->getRepository(ActivityReason::class)->find($id);
if (null === $entity) {
throw new NotFoundHttpException('Unable to find ActivityReason entity.');
}
$editForm = $this->createEditForm($entity);
return $this->render('@ChillActivity/ActivityReason/edit.html.twig', [
'entity' => $entity,
'edit_form' => $editForm->createView(),
]);
}
/**
* Lists all ActivityReason entities.
*/
@@ -102,29 +79,10 @@ class ActivityReasonController extends AbstractController
]);
}
/**
* Finds and displays a ActivityReason entity.
*/
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/admin/activityreason/{id}/show', name: 'chill_activity_activityreason_show')]
public function showAction(mixed $id)
{
$em = $this->managerRegistry->getManager();
$entity = $em->getRepository(ActivityReason::class)->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find ActivityReason entity.');
}
return $this->render('@ChillActivity/ActivityReason/show.html.twig', [
'entity' => $entity,
]);
}
/**
* Edits an existing ActivityReason entity.
*/
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/admin/activityreason/{id}/update', name: 'chill_activity_activityreason_update', methods: ['POST', 'PUT'])]
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/admin/activityreason/{id}/update', name: 'chill_activity_activityreason_update')]
public function updateAction(Request $request, mixed $id)
{
$em = $this->managerRegistry->getManager();
@@ -180,7 +138,7 @@ class ActivityReasonController extends AbstractController
{
$form = $this->createForm(ActivityReasonType::class, $entity, [
'action' => $this->generateUrl('chill_activity_activityreason_update', ['id' => $entity->getId()]),
'method' => 'PUT',
'method' => 'POST',
]);
$form->add('submit', SubmitType::class, ['label' => 'Update']);

View File

@@ -24,7 +24,7 @@ class ByActivityNumberAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$qb
->addSelect('(SELECT COUNT(activity.id) FROM '.Activity::class.' activity WHERE activity.accompanyingPeriod = acp) AS activity_by_number_aggregator')
@@ -41,12 +41,27 @@ class ByActivityNumberAggregator implements AggregatorInterface
// No form needed
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return static function ($value) {
if ('_header' === $value) {

View File

@@ -33,7 +33,7 @@ final readonly class ByActivityTypeAggregator implements AggregatorInterface
private TranslatableStringHelperInterface $translatableStringHelper,
) {}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder
->add('after_date', PickRollingDateType::class, [
@@ -46,6 +46,21 @@ final readonly class ByActivityTypeAggregator implements AggregatorInterface
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['after_date' => $formData['after_date']->normalize(), 'before_date' => $formData['before_date']->normalize()];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['after_date' => \Chill\MainBundle\Service\RollingDate\RollingDate::fromNormalized($formData['after_date']), 'before_date' => \Chill\MainBundle\Service\RollingDate\RollingDate::fromNormalized($formData['before_date'])];
}
public function getFormDefaultData(): array
{
return [
@@ -54,7 +69,7 @@ final readonly class ByActivityTypeAggregator implements AggregatorInterface
];
}
public function getLabels($key, array $values, mixed $data)
public function getLabels($key, array $values, mixed $data): callable
{
return function (int|string|null $value): string {
if ('_header' === $value) {
@@ -69,12 +84,12 @@ final readonly class ByActivityTypeAggregator implements AggregatorInterface
};
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return [self::PREFIX.'_actype_id'];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'export.aggregator.acp.by_activity_type.title';
}
@@ -84,7 +99,7 @@ final readonly class ByActivityTypeAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$p = self::PREFIX;
@@ -115,7 +130,7 @@ final readonly class ByActivityTypeAggregator implements AggregatorInterface
->addGroupBy("{$p}_actype_id");
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}

View File

@@ -27,7 +27,7 @@ class BySocialActionAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
if (!\in_array('actsocialaction', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.socialActions', 'actsocialaction');
@@ -42,17 +42,32 @@ class BySocialActionAggregator implements AggregatorInterface
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// no form
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return function ($value) {
if ('_header' === $value) {

View File

@@ -27,7 +27,7 @@ class BySocialIssueAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
if (!\in_array('actsocialissue', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.socialIssues', 'actsocialissue');
@@ -42,17 +42,32 @@ class BySocialIssueAggregator implements AggregatorInterface
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// no form
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -25,7 +25,7 @@ final readonly class ActivityLocationAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
if (!\in_array('actloc', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.location', 'actloc');
@@ -39,17 +39,32 @@ final readonly class ActivityLocationAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// no form required for this aggregator
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): \Closure
public function getLabels($key, array $values, $data): callable
{
return function ($value): string {
if ('_header' === $value) {
@@ -69,7 +84,7 @@ final readonly class ActivityLocationAggregator implements AggregatorInterface
return [self::KEY];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'export.aggregator.activity.by_location.Title';
}

View File

@@ -22,14 +22,29 @@ final readonly class ActivityPresenceAggregator implements AggregatorInterface
{
public function __construct(private ActivityPresenceRepositoryInterface $activityPresenceRepository, private TranslatableStringHelperInterface $translatableStringHelper) {}
public function buildForm(FormBuilderInterface $builder) {}
public function buildForm(FormBuilderInterface $builder): void {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, mixed $data)
public function getLabels($key, array $values, mixed $data): callable
{
return function (int|string|null $value): string {
if ('_header' === $value) {
@@ -44,7 +59,7 @@ final readonly class ActivityPresenceAggregator implements AggregatorInterface
};
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return ['activity_presence_aggregator_attendee'];
}
@@ -59,13 +74,13 @@ final readonly class ActivityPresenceAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$qb->addSelect('IDENTITY(activity.attendee) AS activity_presence_aggregator_attendee');
$qb->addGroupBy('activity_presence_aggregator_attendee');
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACTIVITY;
}

View File

@@ -36,7 +36,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
// add select element
if ('reasons' === $data['level']) {
@@ -72,7 +72,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add(
'level',
@@ -89,6 +89,21 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['level' => $formData['level']];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['level' => $formData['level']];
}
public function getFormDefaultData(): array
{
return [
@@ -96,7 +111,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return function ($value) use ($data) {
if ('_header' === $value) {
@@ -125,7 +140,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
};
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
// add select element
if ('reasons' === $data['level']) {
@@ -139,12 +154,12 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
throw new \RuntimeException('The data provided are not recognised.');
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'Aggregate by activity reason';
}
public function validateForm($data, ExecutionContextInterface $context)
public function validateForm($data, ExecutionContextInterface $context): void
{
if (null === $data['level']) {
$context

View File

@@ -29,7 +29,7 @@ class ActivityTypeAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
if (!\in_array('acttype', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.activityType', 'acttype');
@@ -44,17 +44,32 @@ class ActivityTypeAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// no form required for this aggregator
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): \Closure
public function getLabels($key, array $values, $data): callable
{
return function (int|string|null $value): string {
if ('_header' === $value) {
@@ -74,7 +89,7 @@ class ActivityTypeAggregator implements AggregatorInterface
return [self::KEY];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'Aggregate by activity type';
}

View File

@@ -29,7 +29,7 @@ class ActivityUserAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
// add select element
$qb->addSelect(sprintf('IDENTITY(activity.user) AS %s', self::KEY));
@@ -43,17 +43,32 @@ class ActivityUserAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// nothing to add
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, $values, $data): \Closure
public function getLabels($key, $values, $data): callable
{
return function ($value) {
if ('_header' === $value) {
@@ -70,7 +85,7 @@ class ActivityUserAggregator implements AggregatorInterface
};
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return [self::KEY];
}

View File

@@ -27,7 +27,7 @@ class ActivityUsersAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
if (!\in_array('actusers', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.users', 'actusers');
@@ -43,17 +43,32 @@ class ActivityUsersAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// nothing to add on the form
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return function ($value) {
if ('_header' === $value) {
@@ -70,12 +85,12 @@ class ActivityUsersAggregator implements AggregatorInterface
};
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return ['activity_users_aggregator'];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'Aggregate by activity users';
}

View File

@@ -34,7 +34,7 @@ class ActivityUsersJobAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$p = self::PREFIX;
@@ -65,14 +65,29 @@ class ActivityUsersJobAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder) {}
public function buildForm(FormBuilderInterface $builder): void {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -34,7 +34,7 @@ class ActivityUsersScopeAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$p = self::PREFIX;
@@ -65,14 +65,29 @@ class ActivityUsersScopeAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder) {}
public function buildForm(FormBuilderInterface $builder): void {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -27,7 +27,7 @@ class ByCreatorAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$qb->addSelect('IDENTITY(activity.createdBy) AS creator_aggregator');
$qb->addGroupBy('creator_aggregator');
@@ -38,17 +38,32 @@ class ByCreatorAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// no form
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -27,7 +27,7 @@ class ByThirdpartyAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
if (!\in_array('acttparty', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.thirdParties', 'acttparty');
@@ -42,17 +42,32 @@ class ByThirdpartyAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// no form
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -34,7 +34,7 @@ class CreatorJobAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$p = self::PREFIX;
@@ -65,14 +65,29 @@ class CreatorJobAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder) {}
public function buildForm(FormBuilderInterface $builder): void {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -34,7 +34,7 @@ class CreatorScopeAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$p = self::PREFIX;
@@ -65,14 +65,29 @@ class CreatorScopeAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder) {}
public function buildForm(FormBuilderInterface $builder): void {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -32,7 +32,7 @@ class DateAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$order = null;
@@ -67,7 +67,7 @@ class DateAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('frequency', ChoiceType::class, [
'choices' => self::CHOICES,
@@ -76,12 +76,27 @@ class DateAggregator implements AggregatorInterface
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['frequency' => $formData['frequency']];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['frequency' => $formData['frequency']];
}
public function getFormDefaultData(): array
{
return ['frequency' => self::DEFAULT_CHOICE];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return static function ($value) use ($data): string {
if ('_header' === $value) {

View File

@@ -27,7 +27,7 @@ class LocationTypeAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
if (!\in_array('actloc', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.location', 'actloc');
@@ -42,17 +42,32 @@ class LocationTypeAggregator implements AggregatorInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// no form
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
public function getLabels($key, array $values, $data): callable
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -24,17 +24,32 @@ final readonly class HouseholdAggregator implements AggregatorInterface
{
public function __construct(private HouseholdRepository $householdRepository) {}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// nothing to add here
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, mixed $data)
public function getLabels($key, array $values, mixed $data): callable
{
return function (int|string|null $value): string|int {
if ('_header' === $value) {
@@ -49,12 +64,12 @@ final readonly class HouseholdAggregator implements AggregatorInterface
};
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return ['activity_household_agg'];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'export.aggregator.person.by_household.title';
}
@@ -64,7 +79,7 @@ final readonly class HouseholdAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$qb->join(
HouseholdMember::class,
@@ -92,7 +107,7 @@ final readonly class HouseholdAggregator implements AggregatorInterface
->addGroupBy('activity_household_agg');
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACTIVITY_PERSON;
}

View File

@@ -21,27 +21,42 @@ final readonly class PersonAggregator implements AggregatorInterface
{
public function __construct(private LabelPersonHelper $labelPersonHelper) {}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// nothing to add here
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, mixed $data)
public function getLabels($key, array $values, mixed $data): callable
{
return $this->labelPersonHelper->getLabel($key, $values, 'export.aggregator.person.by_person.person');
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return ['activity_by_person_agg'];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'export.aggregator.person.by_person.title';
}
@@ -51,14 +66,14 @@ final readonly class PersonAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$qb
->addSelect('IDENTITY(activity.person) AS activity_by_person_agg')
->addGroupBy('activity_by_person_agg');
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACTIVITY_PERSON;
}

View File

@@ -27,17 +27,32 @@ final readonly class PersonsAggregator implements AggregatorInterface
public function __construct(private LabelPersonHelper $labelPersonHelper) {}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// nothing to add here
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, mixed $data)
public function getLabels($key, array $values, mixed $data): callable
{
if ($key !== self::PREFIX.'_pid') {
throw new \UnexpectedValueException('this key should not be handled: '.$key);
@@ -46,12 +61,12 @@ final readonly class PersonsAggregator implements AggregatorInterface
return $this->labelPersonHelper->getLabel($key, $values, 'export.aggregator.activity.by_persons.Persons');
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return [self::PREFIX.'_pid'];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'export.aggregator.activity.by_persons.Group activity by persons';
}
@@ -61,7 +76,7 @@ final readonly class PersonsAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$p = self::PREFIX;
@@ -71,7 +86,7 @@ final readonly class PersonsAggregator implements AggregatorInterface
->addGroupBy("{$p}_pid");
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACTIVITY;
}

View File

@@ -26,7 +26,7 @@ class SentReceivedAggregator implements AggregatorInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$qb->addSelect('activity.sentReceived AS activity_sentreceived_aggregator')
->addGroupBy('activity_sentreceived_aggregator');
@@ -42,6 +42,21 @@ class SentReceivedAggregator implements AggregatorInterface
// No form needed
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];

View File

@@ -15,6 +15,7 @@ use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
@@ -38,6 +39,21 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
public function buildForm(FormBuilderInterface $builder) {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
@@ -72,7 +88,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
return ['export_avg_activity_duration'];
}
public function getResult($query, $data)
public function getResult($query, $data, ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
@@ -87,7 +103,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);

View File

@@ -15,6 +15,7 @@ use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
@@ -46,6 +47,21 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
// TODO: Implement buildForm() method.
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
@@ -80,7 +96,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
return ['export_avg_activity_visit_duration'];
}
public function getResult($query, $data)
public function getResult($query, $data, ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
@@ -95,7 +111,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);

View File

@@ -43,6 +43,21 @@ class CountActivity implements ExportInterface, GroupedExportInterface
public function buildForm(FormBuilderInterface $builder) {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
@@ -77,7 +92,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
return ['export_count_activity'];
}
public function getResult($query, $data)
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
@@ -92,7 +107,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);

View File

@@ -44,6 +44,21 @@ final readonly class CountHouseholdOnActivity implements ExportInterface, Groupe
public function buildForm(FormBuilderInterface $builder) {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
@@ -78,7 +93,7 @@ final readonly class CountHouseholdOnActivity implements ExportInterface, Groupe
return ['export_count_activity'];
}
public function getResult($query, $data)
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
@@ -93,7 +108,7 @@ final readonly class CountHouseholdOnActivity implements ExportInterface, Groupe
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);

View File

@@ -43,6 +43,21 @@ class CountPersonsOnActivity implements ExportInterface, GroupedExportInterface
public function buildForm(FormBuilderInterface $builder) {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
@@ -77,7 +92,7 @@ class CountPersonsOnActivity implements ExportInterface, GroupedExportInterface
return ['export_count_activity'];
}
public function getResult($query, $data)
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
@@ -92,7 +107,7 @@ class CountPersonsOnActivity implements ExportInterface, GroupedExportInterface
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);

View File

@@ -17,6 +17,7 @@ use Chill\ActivityBundle\Export\Export\ListActivityHelper;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\Helper\TranslatableStringExportLabelHelper;
use Chill\MainBundle\Export\ListInterface;
@@ -38,6 +39,21 @@ final readonly class ListActivity implements ListInterface, GroupedExportInterfa
$this->helper->buildForm($builder);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
@@ -73,7 +89,7 @@ final readonly class ListActivity implements ListInterface, GroupedExportInterfa
};
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return
array_merge(
@@ -85,25 +101,23 @@ final readonly class ListActivity implements ListInterface, GroupedExportInterfa
);
}
public function getResult($query, $data)
public function getResult($query, $data, ExportGenerationContext $context): array
{
return $this->helper->getResult($query, $data);
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return ListActivityHelper::MSG_KEY.'List activity linked to a course';
}
public function getType()
public function getType(): string
{
return $this->helper->getType();
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->entityManager->createQueryBuilder();
$qb
@@ -114,7 +128,7 @@ final readonly class ListActivity implements ListInterface, GroupedExportInterfa
->leftJoin('acppart.person', 'person')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL');
$this->filterListAccompanyingPeriodHelper->addFilterAccompanyingPeriods($qb, $requiredModifiers, $acl, $data);
$this->filterListAccompanyingPeriodHelper->addFilterAccompanyingPeriods($qb, $requiredModifiers, $acl, $context->byUser, $data);
$qb
// some grouping are necessary

View File

@@ -40,9 +40,21 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder) {}
public function getNormalizationVersion(): int
{
// TODO: Implement buildForm() method.
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
@@ -79,7 +91,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
return ['export_sum_activity_duration'];
}
public function getResult($query, $data)
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
@@ -94,7 +106,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);

View File

@@ -40,9 +40,21 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder) {}
public function getNormalizationVersion(): int
{
// TODO: Implement buildForm() method.
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
@@ -79,7 +91,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
return ['export_sum_activity_visit_duration'];
}
public function getResult($query, $data)
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
@@ -94,7 +106,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);

View File

@@ -35,6 +35,21 @@ class CountActivity implements ExportInterface, GroupedExportInterface
public function buildForm(FormBuilderInterface $builder) {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
@@ -64,17 +79,17 @@ class CountActivity implements ExportInterface, GroupedExportInterface
return static fn ($value) => '_header' === $value ? 'Number of activities linked to a person' : $value;
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return ['export_count_activity'];
}
public function getResult($query, $data)
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'Count activities linked to a person';
}
@@ -84,7 +99,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);

View File

@@ -36,6 +36,21 @@ final readonly class CountHouseholdOnActivity implements ExportInterface, Groupe
public function buildForm(FormBuilderInterface $builder) {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
@@ -65,17 +80,17 @@ final readonly class CountHouseholdOnActivity implements ExportInterface, Groupe
return static fn ($value) => '_header' === $value ? 'export.export.count_household_on_activity_person.header' : $value;
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return ['export_count_activity'];
}
public function getResult($query, $data)
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'export.export.count_household_on_activity_person.title';
}
@@ -85,7 +100,7 @@ final readonly class CountHouseholdOnActivity implements ExportInterface, Groupe
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);

View File

@@ -78,6 +78,21 @@ class ListActivity implements ListInterface, GroupedExportInterface
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['fields' => $formData['fields']];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['fields' => $formData['fields']];
}
public function getFormDefaultData(): array
{
return [];
@@ -167,17 +182,17 @@ class ListActivity implements ListInterface, GroupedExportInterface
}
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return $data['fields'];
}
public function getResult($query, $data)
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'List activity linked to a person';
}
@@ -187,7 +202,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(static fn ($el) => $el['center'], $acl);

View File

@@ -49,6 +49,21 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
public function buildForm(FormBuilderInterface $builder) {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
@@ -84,17 +99,17 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
return static fn (string $value) => '_header' === $value ? $header : $value;
}
public function getQueryKeys($data)
public function getQueryKeys($data): array
{
return ['export_stat_activity'];
}
public function getResult($query, $data)
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
if (self::SUM === $this->action) {
return 'Sum activity linked to a person duration';
@@ -108,7 +123,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$centers = array_map(
static fn (array $el): Center => $el['center'],

View File

@@ -14,6 +14,7 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
@@ -25,6 +26,7 @@ use Symfony\Component\Form\FormBuilderInterface;
final readonly class ActivityTypeFilter implements FilterInterface
{
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
private const BASE_EXISTS = 'SELECT 1 FROM '.Activity::class.' act_type_filter_activity WHERE act_type_filter_activity.accompanyingPeriod = acp';
public function __construct(
@@ -38,7 +40,7 @@ final readonly class ActivityTypeFilter implements FilterInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$exists = self::BASE_EXISTS;
@@ -62,12 +64,12 @@ final readonly class ActivityTypeFilter implements FilterInterface
}
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('accepted_activitytypes', EntityType::class, [
'class' => ActivityType::class,
@@ -92,6 +94,21 @@ final readonly class ActivityTypeFilter implements FilterInterface
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['accepted_activitytypes' => $this->normalizeDoctrineEntity($formData['accepted_activitytypes']), 'date_after' => $formData['date_after']?->normalize(), 'date_before' => $formData['date_before']?->normalize()];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['accepted_activitytypes' => $this->denormalizeDoctrineEntity($formData['accepted_activitytypes'], $this->activityTypeRepository), 'date_after' => \Chill\MainBundle\Service\RollingDate\RollingDate::fromNormalized($formData['date_after']), 'date_before' => \Chill\MainBundle\Service\RollingDate\RollingDate::fromNormalized($formData['date_before'])];
}
public function getFormDefaultData(): array
{
return [
@@ -101,7 +118,7 @@ final readonly class ActivityTypeFilter implements FilterInterface
];
}
public function describeAction($data, $format = 'string'): array
public function describeAction($data, ExportGenerationContext $context): array
{
$types = [];

View File

@@ -12,23 +12,28 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\ExportDataNormalizerTrait;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Form\Type\PickSocialActionType;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class BySocialActionFilter implements FilterInterface
{
public function __construct(private readonly SocialActionRender $actionRender) {}
use ExportDataNormalizerTrait;
public function __construct(private readonly SocialActionRender $actionRender, private readonly SocialActionRepository $socialActionRepository) {}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
if (!\in_array('actsocialaction', $qb->getAllAliases(), true)) {
$qb->join('activity.socialActions', 'actsocialaction');
@@ -48,19 +53,36 @@ class BySocialActionFilter implements FilterInterface
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('accepted_socialactions', PickSocialActionType::class, [
'multiple' => true,
]);
}
public function getFormDefaultData(): array
public function getNormalizationVersion(): int
{
return [];
return 1;
}
public function describeAction($data, $format = 'string'): array
public function normalizeFormData(array $formData): array
{
return ['accepted_socialactions' => $this->normalizeDoctrineEntity($formData['accepted_socialactions'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['accepted_socialactions' => $this->denormalizeDoctrineEntity($formData['accepted_socialactions'], $this->socialActionRepository)];
}
public function getFormDefaultData(): array
{
return [
'accepted_socialactions' => [],
];
}
public function describeAction($data, ExportGenerationContext $context): array
{
$actions = [];

View File

@@ -12,23 +12,28 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\ExportDataNormalizerTrait;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Form\Type\PickSocialIssueType;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class BySocialIssueFilter implements FilterInterface
{
public function __construct(private readonly SocialIssueRender $issueRender) {}
use ExportDataNormalizerTrait;
public function __construct(private readonly SocialIssueRender $issueRender, private readonly SocialIssueRepository $issueRepository) {}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
if (!\in_array('actsocialissue', $qb->getAllAliases(), true)) {
$qb->join('activity.socialIssues', 'actsocialissue');
@@ -48,19 +53,34 @@ class BySocialIssueFilter implements FilterInterface
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('accepted_socialissues', PickSocialIssueType::class, [
'multiple' => true,
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['accepted_socialissues' => $this->normalizeDoctrineEntity($formData['accepted_socialissues'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['accepted_socialissues' => $this->denormalizeDoctrineEntity($formData['accepted_socialissues'], $this->issueRepository)];
}
public function getFormDefaultData(): array
{
return [];
}
public function describeAction($data, $format = 'string'): array
public function describeAction($data, ExportGenerationContext $context): array
{
$issues = [];

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
@@ -27,7 +28,7 @@ class HasNoActivityFilter implements FilterInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$qb
->andWhere('
@@ -43,17 +44,32 @@ class HasNoActivityFilter implements FilterInterface
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
// no form needed
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function describeAction($data, $format = 'string'): array
public function describeAction($data, ExportGenerationContext $context): array
{
return ['Filtered acp which has no activities', []];
}

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
@@ -25,12 +26,12 @@ final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInt
private RollingDateConverterInterface $rollingDateConverter,
) {}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'export.filter.activity.course_having_activity_between_date.Title';
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder
->add('start_date', PickRollingDateType::class, [
@@ -41,6 +42,21 @@ final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInt
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['start_date' => $formData['start_date']->normalize(), 'end_date' => $formData['end_date']->normalize()];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['start_date' => RollingDate::fromNormalized($formData['start_date']), 'end_date' => RollingDate::fromNormalized($formData['end_date'])];
}
public function getFormDefaultData(): array
{
return [
@@ -49,7 +65,7 @@ final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInt
];
}
public function describeAction($data, $format = 'string')
public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array
{
return [
'export.filter.activity.course_having_activity_between_date.Only course having an activity between from and to',
@@ -65,7 +81,7 @@ final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInt
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$alias = 'act_period_having_act_betw_date_alias';
$from = 'act_period_having_act_betw_date_start';
@@ -82,7 +98,7 @@ final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInt
->setParameter($to, $this->rollingDateConverter->convert($data['end_date']));
}
public function applyOn()
public function applyOn(): string
{
return \Chill\PersonBundle\Export\Declarations::ACP_TYPE;
}

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
@@ -30,7 +31,7 @@ class ActivityDateFilter implements FilterInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->between(
@@ -61,7 +62,7 @@ class ActivityDateFilter implements FilterInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder
->add('date_from', PickRollingDateType::class, [
@@ -72,12 +73,27 @@ class ActivityDateFilter implements FilterInterface
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['date_from' => $formData['date_from']->normalize(), 'date_to' => $formData['date_to']->normalize()];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['date_from' => RollingDate::fromNormalized($formData['date_from']), 'date_to' => RollingDate::fromNormalized($formData['date_to'])];
}
public function getFormDefaultData(): array
{
return ['date_from' => new RollingDate(RollingDate::T_YEAR_PREVIOUS_START), 'date_to' => new RollingDate(RollingDate::T_TODAY)];
}
public function describeAction($data, $format = 'string')
public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array
{
return [
'Filtered by date of activity: only between %date_from% and %date_to%',
@@ -88,7 +104,7 @@ class ActivityDateFilter implements FilterInterface
];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'Filtered by date activity';
}

View File

@@ -13,6 +13,8 @@ namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\ActivityPresence;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityPresenceRepositoryInterface;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\Common\Collections\Collection;
@@ -23,17 +25,20 @@ use Symfony\Contracts\Translation\TranslatorInterface;
final readonly class ActivityPresenceFilter implements FilterInterface
{
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
public function __construct(
private TranslatableStringHelperInterface $translatableStringHelper,
private TranslatorInterface $translator,
private ActivityPresenceRepositoryInterface $activityPresenceRepository,
) {}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'export.filter.activity.by_presence.Filter activity by activity presence';
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('presences', EntityType::class, [
'class' => ActivityPresence::class,
@@ -45,12 +50,27 @@ final readonly class ActivityPresenceFilter implements FilterInterface
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['presences' => $this->normalizeDoctrineEntity($formData['presences'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['presences' => $this->denormalizeDoctrineEntity($formData['presences'], $this->activityPresenceRepository)];
}
public function getFormDefaultData(): array
{
return [];
}
public function describeAction($data, $format = 'string')
public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array
{
$presences = array_map(
fn (ActivityPresence $presence) => $this->translatableStringHelper->localize($presence->getName()),
@@ -68,14 +88,14 @@ final readonly class ActivityPresenceFilter implements FilterInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$qb
->andWhere('activity.attendee IN (:activity_presence_filter_presences)')
->setParameter('activity_presence_filter_presences', $data['presences']);
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACTIVITY;
}

View File

@@ -15,6 +15,7 @@ use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\QueryBuilder;
@@ -24,6 +25,8 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInterface
{
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
public function __construct(
protected TranslatableStringHelperInterface $translatableStringHelper,
protected ActivityTypeRepositoryInterface $activityTypeRepository,
@@ -34,7 +37,7 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$clause = $qb->expr()->in('activity.activityType', ':selected_activity_types');
@@ -47,7 +50,7 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('types', EntityType::class, [
'choices' => $this->activityTypeRepository->findAllActive(),
@@ -70,12 +73,27 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['types' => $this->normalizeDoctrineEntity($formData['types'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['types' => $this->denormalizeDoctrineEntity($formData['types'], $this->activityTypeRepository)];
}
public function getFormDefaultData(): array
{
return [];
}
public function describeAction($data, $format = 'string')
public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array
{
// collect all the reasons'name used in this filter in one array
$reasonsNames = array_map(
@@ -88,12 +106,12 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
]];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'Filter by activity type';
}
public function validateForm($data, ExecutionContextInterface $context)
public function validateForm($data, ExecutionContextInterface $context): void
{
if (null === $data['types'] || 0 === \count($data['types'])) {
$context

View File

@@ -12,26 +12,30 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Form\Type\PickUserOrMeDynamicType;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ActivityUsersFilter implements FilterInterface
final readonly class ActivityUsersFilter implements FilterInterface
{
public function __construct(private readonly UserRender $userRender) {}
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
public function __construct(private UserRender $userRender, private UserRepositoryInterface $userRepository) {}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$orX = $qb->expr()->orX();
foreach ($data['accepted_users'] as $key => $user) {
foreach ($this->userOrMe($data['accepted_users'], $exportGenerationContext) as $key => $user) {
$orX->add($qb->expr()->isMemberOf(':activity_users_filter_u'.$key, 'activity.users'));
$qb->setParameter('activity_users_filter_u'.$key, $user);
}
@@ -39,29 +43,44 @@ class ActivityUsersFilter implements FilterInterface
$qb->andWhere($orX);
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('accepted_users', PickUserDynamicType::class, [
$builder->add('accepted_users', PickUserOrMeDynamicType::class, [
'multiple' => true,
'label' => 'Users',
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['accepted_users' => $this->normalizeUserOrMe($formData['accepted_users'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['accepted_users' => $this->denormalizeUserOrMe($formData['accepted_users'], $this->userRepository)];
}
public function getFormDefaultData(): array
{
return [];
}
public function describeAction($data, $format = 'string')
public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array
{
$users = [];
foreach ($data['accepted_users'] as $u) {
foreach ($this->userOrMe($data['accepted_users'], $context) as $u) {
$users[] = $this->userRender->renderString($u, []);
}

View File

@@ -12,28 +12,32 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Form\Type\PickUserOrMeDynamicType;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ByCreatorFilter implements FilterInterface
{
public function __construct(private readonly UserRender $userRender) {}
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
public function __construct(private readonly UserRender $userRender, private readonly UserRepositoryInterface $userRepository) {}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$qb
->andWhere(
$qb->expr()->in('activity.createdBy', ':users')
)
->setParameter('users', $data['accepted_users']);
->setParameter('users', $this->userOrMe($data['accepted_users'], $exportGenerationContext));
}
public function applyOn(): string
@@ -41,23 +45,38 @@ class ByCreatorFilter implements FilterInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('accepted_users', PickUserDynamicType::class, [
$builder->add('accepted_users', PickUserOrMeDynamicType::class, [
'multiple' => true,
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['accepted_users' => $this->normalizeUserOrMe($formData['accepted_users'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['accepted_users' => $this->denormalizeUserOrMe($formData['accepted_users'], $this->userRepository)];
}
public function getFormDefaultData(): array
{
return [];
}
public function describeAction($data, $format = 'string'): array
public function describeAction($data, ExportGenerationContext $context): array
{
$users = [];
foreach ($data['accepted_users'] as $u) {
foreach ($this->userOrMe($data['accepted_users'], $context) as $u) {
$users[] = $this->userRender->renderString($u, []);
}

View File

@@ -14,6 +14,7 @@ namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\User\UserJobHistory;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
@@ -26,6 +27,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
final readonly class CreatorJobFilter implements FilterInterface
{
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
private const PREFIX = 'acp_act_filter_creator_job';
public function __construct(
@@ -39,7 +41,7 @@ final readonly class CreatorJobFilter implements FilterInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$p = self::PREFIX;
@@ -75,7 +77,7 @@ final readonly class CreatorJobFilter implements FilterInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder
->add('jobs', EntityType::class, [
@@ -90,7 +92,22 @@ final readonly class CreatorJobFilter implements FilterInterface
]);
}
public function describeAction($data, $format = 'string'): array
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['jobs' => $this->normalizeDoctrineEntity($formData['jobs'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['jobs' => $this->denormalizeDoctrineEntity($formData['jobs'], $this->userJobRepository)];
}
public function describeAction($data, ExportGenerationContext $context): array
{
$jobs = array_map(
fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),

View File

@@ -14,6 +14,7 @@ namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User\UserScopeHistory;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
@@ -24,6 +25,7 @@ use Symfony\Component\Form\FormBuilderInterface;
class CreatorScopeFilter implements FilterInterface
{
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
private const PREFIX = 'acp_act_filter_creator_scope';
public function __construct(
@@ -36,7 +38,7 @@ class CreatorScopeFilter implements FilterInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$p = self::PREFIX;
@@ -72,7 +74,7 @@ class CreatorScopeFilter implements FilterInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder
->add('scopes', EntityType::class, [
@@ -86,7 +88,22 @@ class CreatorScopeFilter implements FilterInterface
]);
}
public function describeAction($data, $format = 'string'): array
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['scopes' => $this->normalizeDoctrineEntity($formData['scopes'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['scopes' => $this->denormalizeDoctrineEntity($formData['scopes'], $this->scopeRepository)];
}
public function describeAction($data, ExportGenerationContext $context): array
{
$scopes = [];

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
@@ -35,7 +36,7 @@ class EmergencyFilter implements FilterInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$where = $qb->getDQLPart('where');
@@ -56,7 +57,7 @@ class EmergencyFilter implements FilterInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('accepted_emergency', ChoiceType::class, [
'choices' => self::CHOICES,
@@ -66,12 +67,27 @@ class EmergencyFilter implements FilterInterface
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['accepted_emergency' => $formData['accepted_emergency']];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['accepted_emergency' => $formData['accepted_emergency']];
}
public function getFormDefaultData(): array
{
return ['accepted_emergency' => self::DEFAULT_CHOICE];
}
public function describeAction($data, $format = 'string'): array
public function describeAction($data, ExportGenerationContext $context): array
{
return [
'Filtered by emergency: only %emergency%', [

View File

@@ -12,19 +12,27 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickUserLocationType;
use Chill\MainBundle\Repository\LocationRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class LocationFilter implements FilterInterface
final readonly class LocationFilter implements FilterInterface
{
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
public function __construct(
private LocationRepository $locationRepository,
) {}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$qb->andWhere(
$qb->expr()->in('activity.location', ':location')
@@ -38,7 +46,7 @@ class LocationFilter implements FilterInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('accepted_location', PickUserLocationType::class, [
'multiple' => true,
@@ -46,12 +54,27 @@ class LocationFilter implements FilterInterface
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['accepted_location' => $this->normalizeDoctrineEntity($formData['accepted_location'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['accepted_location' => $this->denormalizeDoctrineEntity($formData['accepted_location'], $this->locationRepository)];
}
public function getFormDefaultData(): array
{
return [];
}
public function describeAction($data, $format = 'string'): array
public function describeAction($data, ExportGenerationContext $context): array
{
$locations = [];

View File

@@ -12,8 +12,11 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\ExportDataNormalizerTrait;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickLocationTypeType;
use Chill\MainBundle\Repository\LocationTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
@@ -21,14 +24,16 @@ use Symfony\Component\Form\FormBuilderInterface;
class LocationTypeFilter implements FilterInterface
{
public function __construct(private readonly TranslatableStringHelper $translatableStringHelper) {}
use ExportDataNormalizerTrait;
public function __construct(private readonly TranslatableStringHelper $translatableStringHelper, private LocationTypeRepository $locationTypeRepository) {}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
if (!\in_array('actloc', $qb->getAllAliases(), true)) {
$qb->join('activity.location', 'actloc');
@@ -52,7 +57,7 @@ class LocationTypeFilter implements FilterInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('accepted_locationtype', PickLocationTypeType::class, [
'multiple' => true,
@@ -60,12 +65,27 @@ class LocationTypeFilter implements FilterInterface
]);
}
public function getFormDefaultData(): array
public function getNormalizationVersion(): int
{
return [];
return 1;
}
public function describeAction($data, $format = 'string'): array
public function normalizeFormData(array $formData): array
{
return ['accepted_locationtype' => $this->normalizeDoctrineEntity($formData['accepted_locationtype'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['accepted_locationtype' => $this->denormalizeDoctrineEntity($formData['accepted_locationtype'], $this->locationTypeRepository)];
}
public function getFormDefaultData(): array
{
return ['accepted_locationtype' => []];
}
public function describeAction($data, ExportGenerationContext $context): array
{
$types = [];

View File

@@ -15,6 +15,7 @@ use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\Common\Collections\Collection;
@@ -26,6 +27,8 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInterface
{
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
public function __construct(protected TranslatableStringHelper $translatableStringHelper, protected ActivityReasonRepository $activityReasonRepository) {}
public function addRole(): ?string
@@ -33,7 +36,7 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$where = $qb->getDQLPart('where');
$join = $qb->getDQLPart('join');
@@ -58,7 +61,7 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
return Declarations::ACTIVITY_PERSON;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('reasons', EntityType::class, [
'class' => ActivityReason::class,
@@ -70,12 +73,27 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['reasons' => $this->normalizeDoctrineEntity($formData['reasons'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['reasons' => $this->denormalizeDoctrineEntity($formData['reasons'], $this->activityReasonRepository)];
}
public function getFormDefaultData(): array
{
return [];
}
public function describeAction($data, $format = 'string')
public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array
{
// collect all the reasons'name used in this filter in one array
$reasonsNames = array_map(
@@ -91,12 +109,12 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'Filter by reason';
}
public function validateForm($data, ExecutionContextInterface $context)
public function validateForm($data, ExecutionContextInterface $context): void
{
if (null === $data['reasons'] || 0 === \count($data['reasons'])) {
$context

View File

@@ -15,6 +15,7 @@ use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
@@ -39,7 +40,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
return null;
}
public function alterQuery(QueryBuilder $qb, $data): void
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
// create a subquery for activity
$sqb = $qb->getEntityManager()->createQueryBuilder();
@@ -92,7 +93,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
return Declarations::PERSON_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('date_from_rolling', PickRollingDateType::class, [
'label' => 'export.filter.activity.person_between_dates.Implied in an activity after this date',
@@ -116,6 +117,21 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
}
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['date_from_rolling' => $formData['date_from_rolling']->normalize(), 'date_to_rolling' => $formData['date_to_rolling']->normalize()];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['date_from_rolling' => RollingDate::fromNormalized($formData['date_from_rolling']), 'date_to_rolling' => RollingDate::fromNormalized($formData['date_to_rolling'])];
}
public function getFormDefaultData(): array
{
return [
@@ -125,7 +141,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
];
}
public function describeAction($data, $format = 'string'): array
public function describeAction($data, ExportGenerationContext $context): array
{
return [
[] === $data['reasons'] ?
@@ -150,7 +166,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
return 'export.filter.activity.person_between_dates.title';
}
public function validateForm($data, ExecutionContextInterface $context)
public function validateForm($data, ExecutionContextInterface $context): void
{
if ($this->rollingDateConverter->convert($data['date_from_rolling'])
>= $this->rollingDateConverter->convert($data['date_to_rolling'])) {

View File

@@ -13,6 +13,7 @@ namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Tests\Export\Filter\PersonsFilterTest;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Form\Type\PickPersonDynamicType;
use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
@@ -33,7 +34,7 @@ final readonly class PersonsFilter implements FilterInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$p = self::PREFIX;
@@ -47,12 +48,12 @@ final readonly class PersonsFilter implements FilterInterface
$qb->andWhere($orX);
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('accepted_persons', PickPersonDynamicType::class, [
'multiple' => true,
@@ -60,6 +61,21 @@ final readonly class PersonsFilter implements FilterInterface
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['accepted_persons' => $formData['accepted_persons']];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['accepted_persons' => $formData['accepted_persons']];
}
public function getFormDefaultData(): array
{
return [
@@ -67,7 +83,7 @@ final readonly class PersonsFilter implements FilterInterface
];
}
public function describeAction($data, $format = 'string')
public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array
{
$users = [];

View File

@@ -13,6 +13,7 @@ namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
@@ -36,7 +37,7 @@ class SentReceivedFilter implements FilterInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$where = $qb->getDQLPart('where');
@@ -57,7 +58,7 @@ class SentReceivedFilter implements FilterInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('accepted_sentreceived', ChoiceType::class, [
'choices' => self::CHOICES,
@@ -68,12 +69,27 @@ class SentReceivedFilter implements FilterInterface
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['accepted_sentreceived' => $formData['accepted_sentreceived']];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['accepted_sentreceived' => $formData['accepted_sentreceived']];
}
public function getFormDefaultData(): array
{
return ['accepted_sentreceived' => self::DEFAULT_CHOICE];
}
public function describeAction($data, $format = 'string'): array
public function describeAction($data, ExportGenerationContext $context): array
{
$sentreceived = array_flip(self::CHOICES)[$data['accepted_sentreceived']];

View File

@@ -12,23 +12,27 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Form\Type\PickUserOrMeDynamicType;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class UserFilter implements FilterInterface
final readonly class UserFilter implements FilterInterface
{
public function __construct(private readonly UserRender $userRender) {}
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
public function __construct(private UserRender $userRender, private UserRepositoryInterface $userRepository) {}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$where = $qb->getDQLPart('where');
@@ -41,7 +45,7 @@ class UserFilter implements FilterInterface
}
$qb->add('where', $where);
$qb->setParameter('users', $data['accepted_users']);
$qb->setParameter('users', $this->userOrMe($data['accepted_users'], $exportGenerationContext));
}
public function applyOn(): string
@@ -49,24 +53,39 @@ class UserFilter implements FilterInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder->add('accepted_users', PickUserDynamicType::class, [
$builder->add('accepted_users', PickUserOrMeDynamicType::class, [
'multiple' => true,
'label' => 'Creators',
]);
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['accepted_users' => $this->normalizeUserOrMe($formData['accepted_users'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['accepted_users' => $this->denormalizeUserOrMe($formData['accepted_users'], $this->userRepository)];
}
public function getFormDefaultData(): array
{
return [];
}
public function describeAction($data, $format = 'string'): array
public function describeAction($data, ExportGenerationContext $context): array
{
$users = [];
foreach ($data['accepted_users'] as $u) {
foreach ($this->userOrMe($data['accepted_users'], $context) as $u) {
$users[] = $this->userRender->renderString($u, []);
}

View File

@@ -15,6 +15,7 @@ use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\User\UserJobHistory;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
@@ -25,6 +26,7 @@ use Symfony\Component\Form\FormBuilderInterface;
class UsersJobFilter implements FilterInterface
{
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
private const PREFIX = 'act_filter_user_job';
public function __construct(
@@ -37,7 +39,7 @@ class UsersJobFilter implements FilterInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$p = self::PREFIX;
@@ -60,12 +62,12 @@ class UsersJobFilter implements FilterInterface
);
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder
->add('jobs', EntityType::class, [
@@ -77,7 +79,22 @@ class UsersJobFilter implements FilterInterface
]);
}
public function describeAction($data, $format = 'string')
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['jobs' => $this->normalizeDoctrineEntity($formData['jobs'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['jobs' => $this->denormalizeDoctrineEntity($formData['jobs'], $this->userJobRepository)];
}
public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array
{
return ['export.filter.activity.by_users_job.Filtered activity by users job: only %jobs%', [
'%jobs%' => implode(
@@ -97,7 +114,7 @@ class UsersJobFilter implements FilterInterface
];
}
public function getTitle()
public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
{
return 'export.filter.activity.by_users_job.Filter by users job';
}

View File

@@ -15,6 +15,7 @@ use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User\UserScopeHistory;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
@@ -25,6 +26,7 @@ use Symfony\Component\Form\FormBuilderInterface;
class UsersScopeFilter implements FilterInterface
{
use \Chill\MainBundle\Export\ExportDataNormalizerTrait;
private const PREFIX = 'act_filter_user_scope';
public function __construct(
@@ -37,7 +39,7 @@ class UsersScopeFilter implements FilterInterface
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
$p = self::PREFIX;
@@ -65,7 +67,7 @@ class UsersScopeFilter implements FilterInterface
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder): void
{
$builder
->add('scopes', EntityType::class, [
@@ -77,7 +79,22 @@ class UsersScopeFilter implements FilterInterface
]);
}
public function describeAction($data, $format = 'string'): array
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return ['scopes' => $this->normalizeDoctrineEntity($formData['scopes'])];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['scopes' => $this->denormalizeDoctrineEntity($formData['scopes'], $this->scopeRepository)];
}
public function describeAction($data, ExportGenerationContext $context): array
{
return ['export.filter.activity.by_users_scope.Filtered activity by users scope: only %scopes%', [
'%scopes%' => implode(

View File

@@ -12,8 +12,9 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityPresence;
use Doctrine\Persistence\ObjectRepository;
interface ActivityPresenceRepositoryInterface
interface ActivityPresenceRepositoryInterface extends ObjectRepository
{
public function find($id): ?ActivityPresence;

View File

@@ -2,7 +2,7 @@ import "es6-promise/auto";
import { createStore } from "vuex";
import { postLocation } from "./api";
import prepareLocations from "./store.locations.js";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import { fetchResults, makeFetch } from "ChillMainAssets/lib/api/apiMethods";
const debug = process.env.NODE_ENV !== "production";
//console.log('window.activity', window.activity);
@@ -365,11 +365,11 @@ const store = createStore({
const accompanyingPeriodId = state.activity.accompanyingPeriod.id;
const url = `/api/1.0/person/accompanying-course/${accompanyingPeriodId}/works.json`;
try {
const works = await makeFetch("GET", url);
// console.log("works", works);
const works = await fetchResults(url);
// console.log('works', works);
commit("setAccompanyingPeriodWorks", works);
} catch (error) {
console.error("Failed to fetch accompanying period works:", error);
console.error("Failed to fetch works:", error);
}
},
getWhoAmI({ commit }) {

View File

@@ -3,7 +3,7 @@
{% block admin_content %}
<h1>{{ 'ActivityReason list'|trans }}</h1>
<table class="records_list">
<table class="table table-bordered border-dark align-middle">
<thead>
<tr>
<th>{{ 'Name'|trans }}</th>
@@ -29,10 +29,7 @@
<td>
<ul class="record_actions">
<li>
<a href="{{ path('chill_activity_activityreason_show', { 'id': entity.id }) }}" class="btn btn-show" title="{{ 'show'|trans }}"></a>
</li>
<li>
<a href="{{ path('chill_activity_activityreason_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
<a href="{{ path('chill_activity_activityreason_update', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
</li>
</ul>
</td>

View File

@@ -3,7 +3,7 @@
{% block admin_content %}
<h1>{{ 'ActivityReasonCategory list'|trans }}</h1>
<table class="records_list">
<table class="table table-bordered border-dark align-middle">
<thead>
<tr>
<th>{{ 'Name'|trans }}</th>
@@ -23,10 +23,7 @@
<td>
<ul class="record_actions">
<li>
<a href="{{ path('chill_activity_activityreasoncategory_show', { 'id': entity.id }) }}" class="btn btn-show" title="{{ 'show'|trans }}"></a>
</li>
<li>
<a href="{{ path('chill_activity_activityreasoncategory_edit', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
<a href="{{ path('chill_activity_activityreasoncategory_update', { 'id': entity.id }) }}" class="btn btn-edit" title="{{ 'edit'|trans }}"></a>
</li>
</ul>
</td>

View File

@@ -22,6 +22,52 @@ use Symfony\Component\Security\Core\Role\Role;
*/
final class ActivityControllerTest extends WebTestCase
{
/**
* @dataProvider getSecuredPagesUnauthenticated
*/
public function testAccessIsDeniedForUnauthenticated(mixed $url)
{
$client = $this->createClient();
$client->request('GET', $url);
$this->assertEquals(302, $client->getResponse()->getStatusCode());
$this->assertTrue(
$client->getResponse()->isRedirect('http://localhost/login'),
sprintf('the page "%s" does not redirect to http://localhost/login', $url)
);
}
/**
* Provide a client unauthenticated and.
*/
public function getSecuredPagesUnauthenticated()
{
self::bootKernel();
$person = $this->getPersonFromFixtures();
$activities = $this->getActivitiesForPerson($person);
return [
[sprintf('fr/person/%d/activity/', $person->getId())],
[sprintf('fr/person/%d/activity/new', $person->getId())],
[sprintf('fr/person/%d/activity/%d/show', $person->getId(), $activities[0]->getId())],
[sprintf('fr/person/%d/activity/%d/edit', $person->getId(), $activities[0]->getId())],
];
}
/**
* @dataProvider getSecuredPagesAuthenticated
*
* @param type $client
* @param type $url
*/
public function testAccessIsDeniedForUnauthorized($client, $url)
{
$client->request('GET', $url);
$this->assertEquals(403, $client->getResponse()->getStatusCode());
}
public function getSecuredPagesAuthenticated()
{
self::bootKernel();
@@ -55,52 +101,6 @@ final class ActivityControllerTest extends WebTestCase
];
}
/**
* Provide a client unauthenticated and.
*/
public function getSecuredPagesUnauthenticated()
{
self::bootKernel();
$person = $this->getPersonFromFixtures();
$activities = $this->getActivitiesForPerson($person);
return [
[sprintf('fr/person/%d/activity/', $person->getId())],
[sprintf('fr/person/%d/activity/new', $person->getId())],
[sprintf('fr/person/%d/activity/%d/show', $person->getId(), $activities[0]->getId())],
[sprintf('fr/person/%d/activity/%d/edit', $person->getId(), $activities[0]->getId())],
];
}
/**
* @dataProvider getSecuredPagesUnauthenticated
*/
public function testAccessIsDeniedForUnauthenticated(mixed $url)
{
$client = $this->createClient();
$client->request('GET', $url);
$this->assertEquals(302, $client->getResponse()->getStatusCode());
$this->assertTrue(
$client->getResponse()->isRedirect('http://localhost/login'),
sprintf('the page "%s" does not redirect to http://localhost/login', $url)
);
}
/**
* @dataProvider getSecuredPagesAuthenticated
*
* @param type $client
* @param type $url
*/
public function testAccessIsDeniedForUnauthorized($client, $url)
{
$client->request('GET', $url);
$this->assertEquals(403, $client->getResponse()->getStatusCode());
}
public function testCompleteScenario()
{
// Create a new client to browse the application

View File

@@ -45,14 +45,20 @@ final class ByCreatorFilterTest extends AbstractFilterTest
->from(User::class, 'u')
->select('u')
->getQuery()
->setMaxResults(1)
->getResult();
$data = [];
foreach ($array as $a) {
$data[] = [
'accepted_users' => $a,
];
}
$data[] = [
'accepted_users' => 'me',
];
return $data;
}

View File

@@ -55,6 +55,10 @@ final class UserFilterTest extends AbstractFilterTest
];
}
$data[] = [
'accepted_users' => 'me',
];
return $data;
}

View File

@@ -137,6 +137,64 @@ class ActivityACLAwareRepositoryTest extends KernelTestCase
self::assertIsArray($actual);
}
public function provideDataFindByAccompanyingPeriod(): iterable
{
$this->setUp();
if (null === $period = $this->entityManager
->createQueryBuilder()
->select('a')
->from(AccompanyingPeriod::class, 'a')
->setMaxResults(1)
->getQuery()
->getSingleResult()) {
throw new \RuntimeException('no period found');
}
if ([] === $types = $this->entityManager
->createQueryBuilder()
->select('t')
->from(ActivityType::class, 't')
->setMaxResults(2)
->getQuery()
->getResult()) {
throw new \RuntimeException('no types');
}
if ([] === $jobs = $this->entityManager
->createQueryBuilder()
->select('j')
->from(UserJob::class, 'j')
->setMaxResults(2)
->getQuery()
->getResult()
) {
$job = new UserJob();
$job->setLabel(['fr' => 'test']);
$this->entityManager->persist($job);
$this->entityManager->flush();
}
if (null === $user = $this->entityManager
->createQueryBuilder()
->select('u')
->from(User::class, 'u')
->setMaxResults(1)
->getQuery()
->getSingleResult()
) {
throw new \RuntimeException('no user found');
}
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], []];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['my_activities' => true]];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['types' => $types]];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['jobs' => $jobs]];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['after' => new \DateTimeImmutable('1 year ago')]];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['before' => new \DateTimeImmutable('1 year ago')]];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['after' => new \DateTimeImmutable('1 year ago'), 'before' => new \DateTimeImmutable('1 month ago')]];
}
/**
* @dataProvider provideDataFindByPerson
*/
@@ -291,62 +349,4 @@ class ActivityACLAwareRepositoryTest extends KernelTestCase
yield [$person, $user, $centers, $scopes, ActivityVoter::SEE, 0, 5, ['date' => 'DESC'], ['before' => new \DateTimeImmutable('1 year ago')]];
yield [$person, $user, $centers, $scopes, ActivityVoter::SEE, 0, 5, ['date' => 'DESC'], ['after' => new \DateTimeImmutable('1 year ago'), 'before' => new \DateTimeImmutable('1 month ago')]];
}
public function provideDataFindByAccompanyingPeriod(): iterable
{
$this->setUp();
if (null === $period = $this->entityManager
->createQueryBuilder()
->select('a')
->from(AccompanyingPeriod::class, 'a')
->setMaxResults(1)
->getQuery()
->getSingleResult()) {
throw new \RuntimeException('no period found');
}
if ([] === $types = $this->entityManager
->createQueryBuilder()
->select('t')
->from(ActivityType::class, 't')
->setMaxResults(2)
->getQuery()
->getResult()) {
throw new \RuntimeException('no types');
}
if ([] === $jobs = $this->entityManager
->createQueryBuilder()
->select('j')
->from(UserJob::class, 'j')
->setMaxResults(2)
->getQuery()
->getResult()
) {
$job = new UserJob();
$job->setLabel(['fr' => 'test']);
$this->entityManager->persist($job);
$this->entityManager->flush();
}
if (null === $user = $this->entityManager
->createQueryBuilder()
->select('u')
->from(User::class, 'u')
->setMaxResults(1)
->getQuery()
->getSingleResult()
) {
throw new \RuntimeException('no user found');
}
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], []];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['my_activities' => true]];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['types' => $types]];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['jobs' => $jobs]];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['after' => new \DateTimeImmutable('1 year ago')]];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['before' => new \DateTimeImmutable('1 year ago')]];
yield [$period, $user, ActivityVoter::SEE, 0, 10, ['date' => 'DESC'], ['after' => new \DateTimeImmutable('1 year ago'), 'before' => new \DateTimeImmutable('1 month ago')]];
}
}

View File

@@ -57,6 +57,46 @@ final class ActivityVoterTest extends KernelTestCase
$this->prophet = new \Prophecy\Prophet();
}
public function testNullUser()
{
$token = $this->prepareToken();
$center = $this->prepareCenter(1, 'center');
$person = $this->preparePerson($center);
$scope = $this->prepareScope(1, 'default');
$activity = $this->prepareActivity($scope, $person);
$this->assertEquals(
VoterInterface::ACCESS_DENIED,
$this->voter->vote($token, $activity, ['CHILL_ACTIVITY_SEE']),
'assert that a null user is not allowed to see'
);
}
/**
* @dataProvider dataProvider_testVoteAction
*
* @param type $expectedResult
* @param string $attribute
* @param string $message
*/
public function testVoteAction(
$expectedResult,
User $user,
Scope $scope,
Center $center,
$attribute,
$message,
) {
$token = $this->prepareToken($user);
$activity = $this->prepareActivity($scope, $this->preparePerson($center));
$this->assertEquals(
$expectedResult,
$this->voter->vote($token, $activity, [$attribute]),
$message
);
}
public function dataProvider_testVoteAction()
{
$centerA = $this->prepareCenter(1, 'center A');
@@ -110,46 +150,6 @@ final class ActivityVoterTest extends KernelTestCase
];
}
public function testNullUser()
{
$token = $this->prepareToken();
$center = $this->prepareCenter(1, 'center');
$person = $this->preparePerson($center);
$scope = $this->prepareScope(1, 'default');
$activity = $this->prepareActivity($scope, $person);
$this->assertEquals(
VoterInterface::ACCESS_DENIED,
$this->voter->vote($token, $activity, ['CHILL_ACTIVITY_SEE']),
'assert that a null user is not allowed to see'
);
}
/**
* @dataProvider dataProvider_testVoteAction
*
* @param type $expectedResult
* @param string $attribute
* @param string $message
*/
public function testVoteAction(
$expectedResult,
User $user,
Scope $scope,
Center $center,
$attribute,
$message,
) {
$token = $this->prepareToken($user);
$activity = $this->prepareActivity($scope, $this->preparePerson($center));
$this->assertEquals(
$expectedResult,
$this->voter->vote($token, $activity, [$attribute]),
$message
);
}
/**
* prepare a token interface with correct rights.
*

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