Compare commits

..

6 Commits

462 changed files with 11084 additions and 18015 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
.composer/*
composer
composer.phar
composer.lock
docs/build/

View File

@@ -94,7 +94,8 @@ unit_tests:
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=dev
- php -d memory_limit=3G tests/app/bin/console doctrine:fixtures:load -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=test
- php -d memory_limit=4G bin/phpunit --colors=never
- php -d memory_limit=2G tests/app/bin/console doctrine:query:dql "SELECT c.name FROM Chill\\MainBundle\\Entity\\Center c"
- php -d memory_limit=4G bin/phpunit --colors=never --testdox
artifacts:
expire_in: 30 min
paths:

View File

@@ -11,24 +11,14 @@ and this project adheres to
## Unreleased
<!-- write down unreleased development here -->
## Test releases
### 2.0.0-beta2
* [workflow]: Fixed: the notification is sent when the user is added to the first step.
* [budget] Feature: allow to desactivate some charges and resources, adding an `active` key in the configuration
* [person] Feature: on Evaluation, allow to configure an URL from the admin
### 2022-06
* [workflow]: added pagination to workflow list page
* [homepage_widget]: null error on tasks widget fixed
* [person-thirdparty]: fix quick-add of names that consist of multiple parts (eg. De Vlieger) within onthefly modal person/thirdparty
* [search]: Order of birthdate fields changed in advanced search to avoid confusion.
* [workflow]: Constraint added to workflow (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/675)
* [household]: Reposition and cut button for enfant hors menage have been deleted (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/620)
* [admin]: Add crud for composition type in admin (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/611)
* [action]: Agents traitants should be prefilled with referrer of the parcours or left empty if there is no referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/696)
## Test releases
### 2022-05-30

BIN
composer Executable file

Binary file not shown.

View File

@@ -10,8 +10,8 @@
"require": {
"php": "^7.4",
"champs-libres/async-uploader-bundle": "dev-sf4#d57134aee8e504a83c902ff0cf9f8d36ac418290",
"champs-libres/wopi-bundle": "dev-master#6dd8e0a14e00131eb4b889ecc30270ee4a0e5224",
"champs-libres/wopi-lib": "dev-master#8615f4a45a39fc2b6a98765ea835fcfd39618787",
"champs-libres/wopi-bundle": "dev-master#59b468503b9413f8d588ef9e626e7675560db3d8",
"champs-libres/wopi-lib": "dev-master#0e1da19bb6de820080b8651867a7e475be590060",
"doctrine/doctrine-bundle": "^2.1",
"doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/orm": "^2.7",
@@ -96,8 +96,7 @@
"autoload-dev": {
"psr-4": {
"App\\": "tests/app/src/",
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
"Chill\\WopiBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
}
},
"config": {

View File

@@ -10,17 +10,17 @@
Exports
*******
Export is an important issue within the Chill software : users should be able to :
Export is an important issue for the Chill software : users should be able to :
- compute statistics about their activity ;
- list "things" which are a part of their activities.
- list "things" which make part of their activities.
The `main bundle`_ provides a powerful framework to build custom queries with re-usable parts across differents bundles.
.. contents:: Table of content
:local:
.. seealso::
.. seealso::
`The issue where this framework was discussed <https://git.framasoft.org/Chill-project/Chill-Main/issues/9>`_
Provides some information about the pursued features and architecture.
@@ -32,37 +32,37 @@ Concepts
Some vocabulary: 3 "Export elements"
------------------------------------
Four terms are used for this framework :
Four terms are used for this framework :
Exports
provide some basic operation on the data. Two kinds of exports are available :
exports
provides some basic operation on the date. Two kind of exports are available :
- computed data : it may be "the number of people", "the number of activities", "the duration of activities", ...
- list data : it may be "the list of people", "the list of activities", ...
- list data : it may be "the list of people", "the list of activity", ...
Filters
The filters create a filter on the data: it removes some information the user doesn't want to introduce in the computation done by the export.
filters
The filters make a filter on the date: it removes some information the user doesn't want to introduce in the computation done by export. In other word, filters make a filter...
Example of a filter: "people under 18 years olds", "activities between the 1st of June and the 31st December", ...
Example of filter: "people under 18 years olds", "activities between the 1st of June and the 31st December", ...
Aggregators
The aggregator aggregates the data into some group (some software use the term 'bucket').
aggregators
The aggregator aggregates the data into some group (some software use the term 'bucket').
Example of an aggregator : "group people by gender", "group people by nationality", "group activity by type", ...
Example of aggregator : "group people by gender", "group people by nationality", "group activity by type", ...
Formatters
The formatters format the data into a :class:`Symfony\Component\HttpFoundation\Response`, which will be returned "as is" by the controller to the web client.
formatters
The formatters format the data into a :class:`Symfony\Component\HttpFoundation\Response`, which will be returned "as is" by the controller to the web client.
Example of a formatter: "format data as CSV", "format data as an ods spreadsheet", ...
Example of formatter: "format data as CSV", "format data as ods spreadsheet", ...
Anatomy of an export
---------------------
An export can be thought of as a sentence where each part of this sentence refers to one or multiple export elements. Examples :
An export may be explained as a sentence, where each part of this sentence refers to one or multiple exports element. Examples :
**Example 1**: Count the number of people having at least one activity in the last 12 month, and group them by nationality and gender, and format them in a CSV spreadsheet.
Here :
Here :
- *count the number of people* is the export part
- *having at least one activity* is the filter part
@@ -72,10 +72,10 @@ Here :
Note that :
- Aggregators, filters, exports and formatters are cross-bundle. Here the bundle *activity* provides a filter which is applied on an export provided by the person bundle ;
- Multiple aggregator or filter for one export may exist. Currently, only one export is allowed.
- aggregators, filters, exports and aggregators are cross-bundle. Here the bundle *activity* provides a filter which apply on an export provided by the person bundle ;
- there may exists multiple aggregator or filter for one export. Currently, only one export is allowed.
The result might be :
The result might be :
+-----------------------+----------------+---------------------------+
| Nationality | Gender | Number of people |
@@ -89,9 +89,9 @@ The result might be :
| France | Female | 150 |
+-----------------------+----------------+---------------------------+
**Example 2**: Count the average duration of an activity with type "meeting", which occurs between the 1st of June and the 31st of December, group them by week, and format the data in an OpenDocument spreadsheet.
**Example 2**: Count the average duration of an activity with type "meeting", which occurs between the 1st of June and the 31st of December, group them by week, and format the data in a OpenDocument spreadsheet.
Here :
Here :
- *count the average duration of an activity* is the export part
- *activity with type meeting* is a filter part
@@ -102,7 +102,7 @@ Here :
The result might be :
+-----------------------+----------------------+
| Week | Number of activities |
| Week | Number of activities |
+=======================+======================+
| 2015-10 | 10 |
+-----------------------+----------------------+
@@ -116,77 +116,77 @@ The result might be :
Authorization and exports
-------------------------
Exports, filters and aggregators should not show data the user is not allowed to see within the application.
Exports, filters and aggregators should not make see data the user is not allowed to see.
In other words, developers are required to take care of user authorization for each export.
There should be a specific role that grants permission to users who are allowed to build exports. For more simplicity, this role should apply on a center, and should not require special circles.
It should exists a special role that should be granted to users which are allowed to build exports. For more simplicity, this role should apply on center, and should not requires special circles.
How does the magic work ?
How does the magic works ?
===========================
To build an export, we rely on the capacity of the database to execute queries with aggregate (i.e. GROUP BY) and filter (i.e. WHERE) instructions.
An export is an SQL query which is initiated by an export, and modified by aggregators and filters.
.. note::
.. note::
**Example**: Count the number of people having at least one activity in the last 12 month, and group them by nationality and gender
1. The report initiates the query
1. The report initiate the query
.. code-block:: SQL
SELECT count(people.*) FROM people
2. The filter adds a where and join clause :
2. The filter add a where and join clause :
.. code-block:: SQL
SELECT count(people.*) FROM people
RIGHT JOIN activity
SELECT count(people.*) FROM people
RIGHT JOIN activity
WHERE activity.date IS BETWEEN now AND 6 month ago
3. The aggregator "nationality" adds a GROUP BY clause and a column in the SELECT statement:
3. The aggregator "nationality" add a GROUP BY clause and a column in the SELECT statement:
.. code-block:: sql
SELECT people.nationality, count(people.*) FROM people
RIGHT JOIN activity
WHERE activity.date IS BETWEEN now AND 6 month ago
SELECT people.nationality, count(people.*) FROM people
RIGHT JOIN activity
WHERE activity.date IS BETWEEN now AND 6 month ago
GROUP BY nationality
4. The aggregator "gender" does the same job as the nationality aggregator : it adds a GROUP BY clause and a column in the SELECT statement :
4. The aggregator "gender" do the same job as the nationality aggregator : it adds a GROUP BY clause and a column in the SELECT statement :
.. code-block:: sql
SELECT people.nationality, people.gender, count(people.*)
FROM people RIGHT JOIN activity
WHERE activity.date IS BETWEEN now AND 6 month ago
SELECT people.nationality, people.gender, count(people.*)
FROM people RIGHT JOIN activity
WHERE activity.date IS BETWEEN now AND 6 month ago
GROUP BY nationality, gender
Each filter, aggregator and filter may collect parameters from the user through a form. This form is appended to the export form. Here is an example.
Each filter, aggregator and filter may collect parameters from the user by providing a form. This form is appended to the export form. Here is an example.
.. figure:: /_static/screenshots/development/export_form-fullpage.png
The screenshot shows the export form for ``CountPeople`` (Nombre de personnes). The filter by date of birth is checked (*Filtrer par date de naissance de la personne*), which triggers a subform, which is provided by the :class:`Chill\PersonBundle\Export\Filter\BirthdateFilter`. The other unchecked filter does not show the subform.
The screenshot show the export form for ``CountPeople`` (Nombre de personnes). The filter by date of birth is checked (*Filtrer par date de naissance de la personne*), which allow to show a subform, which is provided by the :class:`Chill\PersonBundle\Export\Filter\BirthdateFilter`. The other filter, which are unchecked, does not show the subform.
Two aggregators are also checked : by Country of birth (*Aggréger les personnes par pays de naissance*, the corresponding class is :class:`Chill\PersonBundle\Export\Aggregator\CountryOfBirthAggregator`, which also triggers a subform. The aggregator by gender (*Aggréger les personnes par genre*) is also checked, but there is no corresponding subform.
Two aggregators are also checked : by Country of birth (*Aggréger les personnes par pays de naissance*, corresponding class is :class:`Chill\PersonBundle\Export\Aggregator\CountryOfBirthAggregator`, which also open a subform. The aggregator by gender (*Aggréger les personnes par genre*) is also checked, but there is no corresponding subform.
The Export Manager
------------------
The Export manager (:class:`Chill\MainBundle\Export\ExportManager` is the central class which registers all exports, aggregators, filters and formatters.
The Export manager (:class:`Chill\MainBundle\Export\ExportManager` is the central class which register all exports, aggregators, filters and formatters.
The export manager is also responsible for orchestrating the whole export process, producing a :class:`Symfony\FrameworkBundle\HttpFoundation\Request` for each export request.
The export manager is also responsible for orchestrating the whole export process, producing a :class:`Symfony\FrameworkBundle\HttpFoundation\Request` to each export request.
The export form step
--------------------
The form step allows you to build a form, combining different parts of the module.
The form step allow to build a form, aggregating different parts of the module.
The building of forms is split into different subforms, where each one is responsible for rendering their part of the form (aggregators, filters, and export).
The building of forms is separated between different subform, which are responsible for rendering their part of the form (aggregators, filters, and export).
.. figure:: /_static/puml/exports/form_steps.png
:scale: 40%
@@ -194,12 +194,12 @@ The building of forms is split into different subforms, where each one is respon
The formatter form step
-----------------------
The formatter form is processed *after* the user filled the export form. It is built the same way, but receives the data entered by the user on the previous step as parameters (i.e. export form). It may then adapt it accordingly (example: show a list of columns selected in aggregators).
The formatter form is processed *after* the user filled the export form. It is built the same way, but receive in parameters the data entered by the user on the previous step (i.e. export form). It may then adapt it accordingly (example: show a list of columns selected in aggregators).
Processing the export
---------------------
The export process can be explained by this schema :
The export process may be explained by this schema :
.. figure:: /_static/puml/exports/processing_export.png
:scale: 40%
@@ -219,20 +219,20 @@ This is an example of the ``CountPerson`` export :
:language: php
:linenos:
* **Line 36**: the ``getType`` function returns a string. This string will be used to find the aggregtors and filters which will apply to this export.
* **Line 41**: a simple description to help users understand what your export does.
* **Line 36**: the ``getType`` function return a string. This string will be used to find the aggregtors and filters which will apply to this export.
* **Line 41**: a simple description to help user to understand what your export does.
* **Line 46**: The title of the export. A summary of what your export does.
* **Line 51**: The list of roles required to execute this export.
* **Line 51**: The list of roles requires to execute this export.
* **Line 56**: We initiate the query here...
* **Line 59**: We have to filter the query with centers the users checked in the form. We process the $acl variable to get all ``Center`` objects in one array
* **Line 63**: We create the query with a query builder.
* **Line 74**: We return the result, but make sure to hydrate the results as an array.
* **Line 103**: return the list of formatter types which are allowed to be applied on this filter
* **Line 59**: We have to filter the query with centers the users checked in the form. We process the $acl variable to get all ``Center`` object in one array
* **Line 63**: We create the query, with a query builder.
* **Line 74**: We simply returns the result, but take care of hydrating the results as an array.
* **Line 103**: return the list of formatters types which are allowed to apply on this filter
Filters
-------
This is an example of the *filter by birthdate*. This filter asks some information through a form (`buildForm` is not empty), and this form must be validated. To perform this validation, we implement a new Interface: :class:`Chill\MainBundle\Export\ExportElementValidatedInterface`:
This is an example of the *filter by birthdate*. This filter ask some information in a form (`buildForm` is not empty), and this form must be validated. To performs this validations, we implement a new Interface: :class:`Chill\MainBundle\Export\ExportElementValidatedInterface`:
.. literalinclude:: /_static/code/exports/BirthdateFilter.php
:language: php

View File

@@ -65,17 +65,6 @@ This script will :
4. build assets
.. note::
In some cases it can happen that an old image (chill_base_php or chill_php) stored in the docker cache will make the script fail. To solve this problem you have to delete the image and the container, before the make init :
.. code-block:: bash
docker-compose images php
docker rmi -f chill_php:prod
docker-compose rm php
4. Start the project
====================
@@ -104,29 +93,6 @@ The password is always ``password``.
Now, read `Operations` below.
Prepare for development
***********************
Add a Gitlab token to ensure that you get always the source code:
1. generate a gitlab token there: https://gitlab.com/oauth/token
2. run this command (in php container, at the app's root): :code:`composer config gitlab-token.gitlab.com <your token>`
The auth token should appears now in the directory :code:`.composer`:
.. code-block: bash
$ cat .composer/auth.json
{
"gitlab-token": {
"gitlab.com": "<your token>"
}
}
See also "how to switch branch and get new dependencies".
Operations
**********
@@ -234,25 +200,6 @@ How to run webpack interactively
Executing :code:`bash docker-node.sh` will open a terminal in a node container, with volumes mounted.
How to switch the branch for chill-bundles, and get new dependencies
====================================================================
During development, you will switch to new branches for chill-bundles. As long as the dependencies are equals, this does not cause any problem. But sometimes, a new branch introduces a new dependency, and you must download it.
In order to do that without pain, use those steps:
0. Ensuire you have a token, set
1. at the app's root, update the `composer.json` to your current branch:
.. code-block:: json
{
"require": {
"chill-bundles": "dev-<my-branch>@dev"
}
2. mount into the php container, and run `composer update`
Build the documentation API
===========================

View File

@@ -24,7 +24,6 @@ parameters:
- "/spec/"
- "/var/"
- "/vendor/"
- "/tests/app"
# Psalm
tasks.psalm.blocking: true

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,16 @@ parameters:
count: 1
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Only booleans are allowed in &&, mixed given on the right side\\.$#"
count: 3
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillActivityBundle/Form/ActivityType.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 3

View File

@@ -40,9 +40,6 @@
<testsuite name="DocGeneratorBundle">
<directory suffix="Test.php">src/Bundle/ChillDocGeneratorBundle/tests/</directory>
</testsuite>
<testsuite name="WopiBundle">
<directory suffix="Test.php">src/Bundle/ChillWopiBundle/tests/</directory>
</testsuite>
</testsuites>
<listeners>

View File

@@ -21,7 +21,6 @@ use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Repository\LocationRepository;
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Privacy\PrivacyEvent;
@@ -57,8 +56,6 @@ final class ActivityController extends AbstractController
private ActivityTypeRepository $activityTypeRepository;
private CenterResolverManagerInterface $centerResolver;
private EntityManagerInterface $entityManager;
private EventDispatcherInterface $eventDispatcher;
@@ -85,8 +82,7 @@ final class ActivityController extends AbstractController
EntityManagerInterface $entityManager,
EventDispatcherInterface $eventDispatcher,
LoggerInterface $logger,
SerializerInterface $serializer,
CenterResolverManagerInterface $centerResolver
SerializerInterface $serializer
) {
$this->activityACLAwareRepository = $activityACLAwareRepository;
$this->activityTypeRepository = $activityTypeRepository;
@@ -100,7 +96,6 @@ final class ActivityController extends AbstractController
$this->eventDispatcher = $eventDispatcher;
$this->logger = $logger;
$this->serializer = $serializer;
$this->centerResolver = $centerResolver;
}
/**
@@ -203,7 +198,7 @@ final class ActivityController extends AbstractController
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_UPDATE', $entity);
$form = $this->createForm(ActivityType::class, $entity, [
'center' => $this->centerResolver->resolveCenters($entity)[0] ?? null,
'center' => $entity->getCenter(),
'role' => new Role('CHILL_ACTIVITY_UPDATE'),
'activityType' => $entity->getActivityType(),
'accompanyingPeriod' => $accompanyingPeriod,
@@ -421,7 +416,7 @@ final class ActivityController extends AbstractController
$this->denyAccessUnlessGranted(ActivityVoter::CREATE, $entity);
$form = $this->createForm(ActivityType::class, $entity, [
'center' => $this->centerResolver->resolveCenters($entity)[0] ?? null,
'center' => $entity->getCenter(),
'role' => new Role('CHILL_ACTIVITY_CREATE'),
'activityType' => $entity->getActivityType(),
'accompanyingPeriod' => $accompanyingPeriod,

View File

@@ -61,6 +61,8 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
ActivityVoter::DELETE => [ActivityVoter::SEE_DETAILS],
ActivityVoter::SEE_DETAILS => [ActivityVoter::SEE],
ActivityVoter::FULL => [
ActivityVoter::CREATE_PERSON,
ActivityVoter::CREATE_ACCOMPANYING_COURSE,
ActivityVoter::DELETE,
ActivityVoter::UPDATE,
],

View File

@@ -16,8 +16,8 @@ use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\Embeddable\PrivateCommentEmbeddable;
use Chill\MainBundle\Entity\HasCentersInterface;
use Chill\MainBundle\Entity\HasScopesInterface;
use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasScopeInterface;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
@@ -55,7 +55,7 @@ use Symfony\Component\Validator\Constraints as Assert;
* getUserFunction="getUser",
* path="scope")
*/
class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface, HasCentersInterface, HasScopesInterface
class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface, HasCenterInterface, HasScopeInterface
{
public const SENTRECEIVED_RECEIVED = 'received';
@@ -306,17 +306,13 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
* get the center
* center is extracted from person.
*/
public function getCenters(): iterable
public function getCenter(): ?Center
{
if ($this->person instanceof Person) {
return [$this->person->getCenter()];
return $this->person->getCenter();
}
if ($this->getAccompanyingPeriod() instanceof AccompanyingPeriod) {
return $this->getAccompanyingPeriod()->getCenters() ?? [];
}
return [];
return null;
}
public function getComment(): CommentEmbeddable
@@ -426,19 +422,6 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this->scope;
}
public function getScopes(): iterable
{
if (null !== $this->getAccompanyingPeriod()) {
return $this->getAccompanyingPeriod()->getScopes();
}
if (null !== $this->getPerson()) {
return [$this->scope];
}
return [];
}
public function getSentReceived(): string
{
return $this->sentReceived;

View File

@@ -1,83 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class BySocialActionAggregator implements AggregatorInterface
{
private SocialActionRender $actionRender;
private SocialActionRepository $actionRepository;
public function __construct(
SocialActionRender $actionRender,
SocialActionRepository $actionRepository
) {
$this->actionRender = $actionRender;
$this->actionRepository = $actionRepository;
}
public function getLabels($key, array $values, $data)
{
return function($value) {
if ('_header' === $value) {
return 'Social action';
}
$sa = $this->actionRepository->find($value);
return $this->actionRender->renderString($sa, []);
};
}
public function getQueryKeys($data): array
{
return ['socialaction_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getTitle(): string
{
return 'Group activity by linked socialaction';
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if(!in_array('socialaction', $qb->getAllAliases())) {
$qb->join('activity.socialActions', 'socialaction');
}
$qb->addSelect('socialaction.id AS socialaction_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('socialaction_aggregator');
} else {
$qb->groupBy('socialaction_aggregator');
}
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,84 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class BySocialIssueAggregator implements AggregatorInterface
{
private SocialIssueRepository $issueRepository;
private SocialIssueRender $issueRender;
public function __construct(
SocialIssueRepository $issueRepository,
SocialIssueRender $issueRender
) {
$this->issueRepository = $issueRepository;
$this->issueRender = $issueRender;
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ($value === '_header') {
return 'Social issues';
}
$i = $this->issueRepository->find($value);
return $this->issueRender->renderString($i, []);
};
}
public function getQueryKeys($data): array
{
return ['socialissue_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getTitle(): string
{
return 'Group activity by linked socialissue';
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('socialissue', $qb->getAllAliases())) {
$qb->join('activity.socialIssues', 'socialissue');
}
$qb->addSelect('socialissue.id AS socialissue_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('socialissue_aggregator');
} else {
$qb->groupBy('socialissue_aggregator');
}
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,83 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ByThirdpartyAggregator implements AggregatorInterface
{
private ThirdPartyRepository $thirdPartyRepository;
private ThirdPartyRender $thirdPartyRender;
public function __construct(
ThirdPartyRepository $thirdPartyRepository,
ThirdPartyRender $thirdPartyRender
) {
$this->thirdPartyRepository = $thirdPartyRepository;
$this->thirdPartyRender = $thirdPartyRender;
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ($value === '_header') {
return 'Accepted thirdparty';
}
$tp = $this->thirdPartyRepository->find($value);
return $this->thirdPartyRender->renderString($tp, []);
};
}
public function getQueryKeys($data): array
{
return ['thirdparty_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getTitle(): string
{
return 'Group activity by linked thirdparties';
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('thirdparty', $qb->getAllAliases())) {
$qb->join('activity.thirdParties', 'thirdparty');
}
$qb->addSelect('thirdparty.id AS thirdparty_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('thirdparty_aggregator');
} else {
$qb->groupBy('thirdparty_aggregator');
}
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,83 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Templating\Entity\UserRender;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ByUserAggregator implements AggregatorInterface
{
private UserRepository $userRepository;
private UserRender $userRender;
public function __construct(
UserRepository $userRepository,
UserRender $userRender
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ($value === '_header') {
return 'Accepted users';
}
$u = $this->userRepository->find($value);
return $this->userRender->renderString($u, []);
};
}
public function getQueryKeys($data): array
{
return ['users_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getTitle(): string
{
return 'Group activity by linked users';
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('user', $qb->getAllAliases())) {
$qb->join('activity.users', 'user');
}
$qb->addSelect('user.id AS users_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('users_aggregator');
} else {
$qb->groupBy('users_aggregator');
}
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,129 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Closure;
use Doctrine\ORM\QueryBuilder;
use RuntimeException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class DateAggregator implements AggregatorInterface
{
private const CHOICES = [
'by month' => 'month',
'by week' => 'week',
'by year' => 'year',
];
private CONST DEFAULT_CHOICE = 'year';
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator
) {
$this->translator = $translator;
}
public function getLabels($key, array $values, $data)
{
return function ($value) use ($data): string {
if ($value === '_header') {
return 'by '. $data['frequency'];
}
switch ($data['frequency']) {
case 'month':
$month = \DateTime::createFromFormat('!m', $value);
return sprintf(
"%02d (%s)",
$value,
$month->format('M')
);
case 'week':
//return $this->translator->trans('for week') .' '. $value ;
case 'year':
//return $this->translator->trans('in year') .' '. $value ;
default:
return $value;
}
};
}
public function getQueryKeys($data): array
{
return ['date_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('frequency', ChoiceType::class, [
'choices' => self::CHOICES,
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
}
public function getTitle(): string
{
return 'Group activity by date';
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$order = null;
switch ($data['frequency']) {
case 'month':
$fmt = 'MM'; break;
case 'week':
$fmt = 'IW'; break;
case 'year':
$fmt = 'YYYY'; $order = 'DESC'; break;
default:
throw new RuntimeException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
}
$qb->addSelect(sprintf("TO_CHAR(activity.date, '%s') AS date_aggregator", $fmt));
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('date_aggregator');
} else {
$qb->groupBy('date_aggregator');
}
$orderBy = $qb->getDQLPart('orderBy');
if (!empty($orderBy)) {
$qb->addOrderBy('date_aggregator', $order);
} else {
$qb->orderBy('date_aggregator', $order);
}
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,86 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\LocationTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class LocationTypeAggregator implements AggregatorInterface
{
private LocationTypeRepository $locationTypeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
LocationTypeRepository $locationTypeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->locationTypeRepository = $locationTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ($value === '_header') {
return 'Accepted locationtype';
}
$lt = $this->locationTypeRepository->find($value);
return $this->translatableStringHelper->localize(
$lt->getTitle()
);
};
}
public function getQueryKeys($data): array
{
return ['locationtype_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getTitle(): string
{
return 'Group activity by locationtype';
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('location', $qb->getAllAliases())) {
$qb->join('activity.location', 'location');
}
$qb->addSelect('IDENTITY(location.locationType) AS locationtype_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('locationtype_aggregator');
} else {
$qb->groupBy('locationtype_aggregator');
}
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,86 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class UserScopeAggregator implements AggregatorInterface
{
private ScopeRepository $scopeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
ScopeRepository $scopeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ($value === '_header') {
return 'Scope';
}
$s = $this->scopeRepository->find($value);
return $this->translatableStringHelper->localize(
$s->getName()
);
};
}
public function getQueryKeys($data): array
{
return ['userscope_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getTitle(): string
{
return 'Group activity by userscope';
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('user', $qb->getAllAliases())) {
$qb->join('activity.user', 'user');
}
$qb->addSelect('IDENTITY(user.mainScope) AS userscope_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('userscope_aggregator');
} else {
$qb->groupBy('userscope_aggregator');
}
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -9,9 +9,8 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator\PersonAggregators;
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
@@ -105,9 +104,9 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
}
}
public function applyOn(): string
public function applyOn()
{
return Declarations::ACTIVITY_PERSON;
return 'activity';
}
public function buildForm(FormBuilderInterface $builder)

View File

@@ -11,7 +11,6 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AggregatorInterface;
@@ -45,24 +44,16 @@ class ActivityTypeAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('type', $qb->getAllAliases())) {
$qb->join('activity.activityType', 'type');
}
// add select element
$qb->addSelect(sprintf('IDENTITY(activity.type) AS %s', self::KEY));
$qb->addSelect(sprintf('IDENTITY(activity.activityType) AS %s', self::KEY));
$groupby = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy(self::KEY);
} else {
$qb->groupBy(self::KEY);
}
// add the "group by" part
$qb->addGroupBy(self::KEY);
}
public function applyOn(): string
public function applyOn()
{
return Declarations::ACTIVITY;
return 'activity';
}
public function buildForm(FormBuilderInterface $builder)

View File

@@ -11,11 +11,9 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Templating\Entity\UserRender;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
@@ -27,14 +25,10 @@ class ActivityUserAggregator implements AggregatorInterface
private UserRepository $userRepository;
private UserRender $userRender;
public function __construct(
UserRepository $userRepository,
UserRender $userRender
UserRepository $userRepository
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
}
public function addRole()
@@ -53,7 +47,7 @@ class ActivityUserAggregator implements AggregatorInterface
public function applyOn(): string
{
return Declarations::ACTIVITY;
return 'activity';
}
public function buildForm(FormBuilderInterface $builder)
@@ -68,12 +62,10 @@ class ActivityUserAggregator implements AggregatorInterface
return function ($value) {
if ('_header' === $value) {
return 'Activity user';
return 'activity user';
}
$u = $this->userRepository->find($value);
return $this->userRender->renderString($u, []);
return $this->userRepository->find($value)->getUsername();
};
}

View File

@@ -1,24 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export;
/**
* This class declare constants used for the export framework.
*/
abstract class Declarations
{
public const ACTIVITY = 'activity';
public const ACTIVITY_ACP = "activity_linked_to_acp";
public const ACTIVITY_PERSON = "activity_linked_to_person";
}

View File

@@ -9,21 +9,18 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export\LinkedToPerson;
namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\ActivityBundle\Export\Declarations;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class CountActivity implements ExportInterface, GroupedExportInterface
class CountActivity implements ExportInterface
{
protected ActivityRepository $activityRepository;
@@ -44,7 +41,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
public function getDescription()
{
return 'Count activities linked to a person by various parameters.';
return 'Count activities by various parameters.';
}
public function getLabels($key, array $values, $data)
@@ -53,7 +50,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Number of activities linked to a person' : $value;
return static fn ($value) => '_header' === $value ? 'Number of activities' : $value;
}
public function getQueryKeys($data)
@@ -68,28 +65,27 @@ class CountActivity implements ExportInterface, GroupedExportInterface
public function getTitle()
{
return 'Count activities linked to a person';
return 'Count activities';
}
public function getType(): string
public function getType()
{
return Declarations::ACTIVITY;
return 'activity';
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->activityRepository->createQueryBuilder('activity')
->join('activity.person', 'person')
;
$qb->select('COUNT(activity.id) as export_count_activity');
$qb = $this
->activityRepository
->createQueryBuilder('activity')
->select('COUNT(activity.id) as export_count_activity')
->join('activity.person', 'person');
$qb
->where($qb->expr()->in('person.center', ':centers'))
->setParameter('centers', $centers)
;
->setParameter('centers', $centers);
return $qb;
}
@@ -101,15 +97,6 @@ class CountActivity implements ExportInterface, GroupedExportInterface
public function supportsModifiers()
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
//PersonDeclarations::PERSON_TYPE,
];
}
public function getGroup(): string
{
return 'Exports of activities linked to a person';
return ['person', 'activity'];
}
}

View File

@@ -1,103 +0,0 @@
<?php
namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Closure;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class AvgActivityDuration implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
}
public function getTitle(): string
{
return 'Average activity linked to an accompanying period duration';
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Average activities linked to an accompanying period duration by various parameters.';
}
public function getLabels($key, array $values, $data)
{
if ('export_avg_activity_duration' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Average activities linked to an accompanying period duration' : $value;
}
public function getQueryKeys($data): array
{
return ['export_avg_activity_duration'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getType(): string
{
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('activity')
->join('activity.accompanyingPeriod', 'acp')
;
$qb->select('AVG(activity.durationTime) as export_avg_activity_duration');
return $qb;
}
public function requiredRole(): Role
{
return new Role(ActivityStatsVoter::STATS);
}
public function supportsModifiers(): array
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
//PersonDeclarations::ACP_TYPE,
];
}
public function getGroup(): string
{
return 'Exports of activities linked to an accompanying period';
}
}

View File

@@ -1,103 +0,0 @@
<?php
namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Closure;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
}
public function getTitle(): string
{
return 'Average activity linked to an accompanying period visit duration';
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Average activities linked to an accompanying period visit duration by various parameters.';
}
public function getLabels($key, array $values, $data)
{
if ('export_avg_activity_visit_duration' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Average activities linked to an accompanying period visit duration' : $value;
}
public function getQueryKeys($data): array
{
return ['export_avg_activity_visit_duration'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getType(): string
{
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('activity')
->join('activity.accompanyingPeriod', 'acp')
;
$qb->select('AVG(activity.travelTime) as export_avg_activity_visit_duration');
return $qb;
}
public function requiredRole(): Role
{
return new Role(ActivityStatsVoter::STATS);
}
public function supportsModifiers(): array
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
//PersonDeclarations::ACP_TYPE,
];
}
public function getGroup(): string
{
return 'Exports of activities linked to an accompanying period';
}
}

View File

@@ -1,110 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class CountActivity implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
{
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Count activities linked to an accompanying period by various parameters.';
}
public function getLabels($key, array $values, $data)
{
if ('export_count_activity' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Number of activities linked to an accompanying period' : $value;
}
public function getQueryKeys($data): array
{
return ['export_count_activity'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
{
return 'Count activities linked to an accompanying period';
}
public function getType(): string
{
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('activity')
->join('activity.accompanyingPeriod', 'acp')
;
$qb->select('COUNT(activity.id) as export_count_activity');
return $qb;
}
public function requiredRole(): Role
{
return new Role(ActivityStatsVoter::STATS);
}
public function supportsModifiers(): array
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
//PersonDeclarations::ACP_TYPE,
];
}
public function getGroup(): string
{
return 'Exports of activities linked to an accompanying period';
}
}

View File

@@ -1,103 +0,0 @@
<?php
namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Closure;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class SumActivityDuration implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
}
public function getTitle(): string
{
return 'Sum activity linked to an accompanying period duration';
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Sum activities linked to an accompanying period duration by various parameters.';
}
public function getLabels($key, array $values, $data)
{
if ('export_sum_activity_duration' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Sum activities linked to an accompanying period duration' : $value;
}
public function getQueryKeys($data): array
{
return ['export_sum_activity_duration'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getType(): string
{
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('activity')
->join('activity.accompanyingPeriod', 'acp')
;
$qb->select('SUM(activity.durationTime) as export_sum_activity_duration');
return $qb;
}
public function requiredRole(): Role
{
return new Role(ActivityStatsVoter::STATS);
}
public function supportsModifiers(): array
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
//PersonDeclarations::ACP_TYPE,
];
}
public function getGroup(): string
{
return 'Exports of activities linked to an accompanying period';
}
}

View File

@@ -1,102 +0,0 @@
<?php
namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Closure;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class SumActivityVisitDuration implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
}
public function getTitle(): string
{
return 'Sum activity linked to an accompanying period visit duration';
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Sum activities linked to an accompanying period visit duration by various parameters.';
}
public function getLabels($key, array $values, $data)
{
if ('export_sum_activity_visit_duration' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Sum activities linked to an accompanying period visit duration' : $value;
}
public function getQueryKeys($data): array
{
return ['export_sum_activity_visit_duration'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getType(): string
{
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('activity')
->join('activity.accompanyingPeriod', 'acp')
;
$qb->select('SUM(activity.travelTime) as export_sum_activity_visit_duration');
return $qb;
}
public function requiredRole(): Role
{
return new Role(ActivityStatsVoter::STATS);
}
public function supportsModifiers(): array
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
//PersonDeclarations::ACP_TYPE,
];
}
public function getGroup(): string
{
return 'Exports of activities linked to an accompanying period';
}
}

View File

@@ -9,13 +9,12 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export\LinkedToPerson;
namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use DateTime;
@@ -28,14 +27,12 @@ use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Chill\ActivityBundle\Export\Declarations;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use function array_key_exists;
use function count;
use function in_array;
class ListActivity implements ListInterface, GroupedExportInterface
class ListActivity implements ListInterface
{
protected EntityManagerInterface $entityManager;
@@ -97,7 +94,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
public function getDescription()
{
return 'List activities linked to a person description';
return 'List activities';
}
public function getLabels($key, array $values, $data)
@@ -183,12 +180,12 @@ class ListActivity implements ListInterface, GroupedExportInterface
public function getTitle()
{
return 'List activity linked to a person';
return 'List activities';
}
public function getType(): string
public function getType()
{
return Declarations::ACTIVITY;
return 'activity';
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
@@ -277,15 +274,6 @@ class ListActivity implements ListInterface, GroupedExportInterface
public function supportsModifiers()
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
//PersonDeclarations::PERSON_TYPE,
];
}
public function getGroup(): string
{
return 'Exports of activities linked to a person';
return ['activity', 'person'];
}
}

View File

@@ -9,16 +9,12 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export\LinkedToPerson;
namespace Chill\ActivityBundle\Export\Export;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Chill\ActivityBundle\Export\Declarations;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
@@ -29,7 +25,7 @@ use Symfony\Component\Security\Core\Role\Role;
*
* The desired stat must be given in constructor.
*/
class StatActivityDuration implements ExportInterface, GroupedExportInterface
class StatActivityDuration implements ExportInterface
{
public const SUM = 'sum';
@@ -63,7 +59,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
public function getDescription()
{
if (self::SUM === $this->action) {
return 'Sum activities linked to a person duration by various parameters.';
return 'Sum activities duration by various parameters.';
}
}
@@ -73,7 +69,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
throw new LogicException(sprintf('The key %s is not used by this export', $key));
}
$header = self::SUM === $this->action ? 'Sum activities linked to a person duration' : false;
$header = self::SUM === $this->action ? 'Sum of activities duration' : false;
return static fn (string $value) => '_header' === $value ? $header : $value;
}
@@ -91,19 +87,19 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
public function getTitle()
{
if (self::SUM === $this->action) {
return 'Sum activity linked to a person duration';
return 'Sum activity duration';
}
}
public function getType(): string
public function getType()
{
return Declarations::ACTIVITY;
return 'activity';
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(
static fn (array $el): Center => $el['center'],
static fn (array $el): string => $el['center'],
$acl
);
@@ -129,15 +125,6 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
public function supportsModifiers()
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
//PersonDeclarations::PERSON_TYPE,
];
}
public function getGroup(): string
{
return 'Exports of activities linked to a person';
return ['person', 'activity'];
}
}

View File

@@ -1,85 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\ActivityBundle\Export\Declarations;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class BySocialActionFilter implements FilterInterface
{
private SocialActionRender $actionRender;
public function __construct(SocialActionRender $actionRender)
{
$this->actionRender = $actionRender;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_socialactions', EntityType::class, [
'class' => SocialAction::class,
'choice_label' => function (SocialAction $sa) {
return $this->actionRender->renderString($sa, []);
},
'multiple' => true,
'expanded' => true,
]);
}
public function getTitle(): string
{
return 'Filter activity by linked socialaction';
}
public function describeAction($data, $format = 'string'): array
{
$actions = [];
foreach ($data['accepted_socialactions'] as $sa) {
$actions[] = $this->actionRender->renderString($sa, []);
}
return ['Filtered activity by linked socialaction: only %actions%', [
'%actions%' => implode(", ou ", $actions)
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
if (!in_array('socialaction', $qb->getAllAliases())) {
$qb->join('activity.socialActions', 'socialaction');
}
$clause = $qb->expr()->in('socialaction.id', ':socialactions');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('socialactions', $data['accepted_socialactions']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,85 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\ActivityBundle\Export\Declarations;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class BySocialIssueFilter implements FilterInterface
{
private SocialIssueRender $issueRender;
public function __construct(SocialIssueRender $issueRender)
{
$this->issueRender = $issueRender;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_socialissues', EntityType::class, [
'class' => SocialIssue::class,
'choice_label' => function(SocialIssue $si) {
return $this->issueRender->renderString($si, []);
},
'multiple' => true,
'expanded' => true,
]);
}
public function getTitle(): string
{
return 'Filter activity by linked socialissue';
}
public function describeAction($data, $format = 'string'): array
{
$issues = [];
foreach ($data['accepted_socialissues'] as $si) {
$issues[] = $this->issueRender->renderString($si, []);
}
return ['Filtered activity by linked socialissue: only %issues%', [
'%issues%' => implode(", ou ", $issues)
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
if (!in_array('socialissue', $qb->getAllAliases())) {
$qb->join('activity.socialIssues', 'socialissue');
}
$clause = $qb->expr()->in('socialissue.id', ':socialissues');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('socialissues', $data['accepted_socialissues']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,84 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Templating\Entity\UserRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class ByUserFilter implements FilterInterface
{
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
$this->userRender = $userRender;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_users', EntityType::class, [
'class' => User::class,
'choice_label' => function (User $u) {
return $this->userRender->renderString($u, []);
},
'multiple' => true,
'expanded' => true,
]);
}
public function getTitle(): string
{
return 'Filter activity by linked users';
}
public function describeAction($data, $format = 'string'): array
{
$users = [];
foreach ($data['accepted_users'] as $u) {
$users[] = $this->userRender->renderString($u, []);
}
return ['Filtered activity by linked users: only %users%', [
'%users%' => implode(", ou ", $users)
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
if (!in_array('user', $qb->getAllAliases())) {
$qb->join('activity.users', 'user');
}
$clause = $qb->expr()->in('user.id', ':users');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('users', $data['accepted_users']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,86 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\ActivityBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class EmergencyFilter implements FilterInterface
{
private const CHOICES = [
'activity is emergency' => true,
'activity is not emergency' => false,
];
private CONST DEFAULT_CHOICE = false;
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_emergency', ChoiceType::class, [
'choices' => self::CHOICES,
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
}
public function getTitle(): string
{
return 'Filter activity by emergency';
}
public function describeAction($data, $format = 'string'): array
{
foreach (self::CHOICES as $k => $v) {
if ($v === $data['accepted_emergency']) {
$choice = $k;
}
}
return ['Filtered activity by emergency: only %emergency%', [
'%emergency%' => $this->translator->trans($choice)
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->eq('activity.emergency', ':emergency');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('emergency', $data['accepted_emergency']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,85 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\MainBundle\Entity\LocationType;
use Chill\MainBundle\Export\FilterInterface;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class LocationTypeFilter implements FilterInterface
{
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_locationtype', EntityType::class, [
'class' => LocationType::class,
'choice_label' => function(LocationType $type) {
return $this->translatableStringHelper->localize($type->getTitle());
},
'multiple' => true,
'expanded' => true,
]);
}
public function getTitle(): string
{
return 'Filter activity by locationtype';
}
public function describeAction($data, $format = 'string'): array
{
$types = [];
foreach ($data['accepted_locationtype'] as $type) {
$types[] = $this->translatableStringHelper->localize(
$type->getTitle()
);
}
return ['Filtered activity by locationtype: only %types%', [
'%types%' => implode(", ou ", $types)
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('location', $qb->getAllAliases())) {
$qb->join('activity.location', 'location');
}
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('location.locationType', ':locationtype');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('locationtype', $data['accepted_locationtype']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,82 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\MainBundle\Export\FilterInterface;
use Chill\ActivityBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class SentReceivedFilter implements FilterInterface
{
private const CHOICES = [
'is sent' => Activity::SENTRECEIVED_SENT,
'is received' => Activity::SENTRECEIVED_RECEIVED,
];
private CONST DEFAULT_CHOICE = Activity::SENTRECEIVED_SENT;
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_sentreceived', ChoiceType::class, [
'choices' => self::CHOICES,
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
}
public function getTitle(): string
{
return 'Filter activity by sentreceived';
}
public function describeAction($data, $format = 'string'): array
{
$sentreceived = array_flip(self::CHOICES)[$data['accepted_sentreceived']];
return ['Filtered activity by sentreceived: only %sentreceived%', [
'%sentreceived%' => $this->translator->trans($sentreceived)
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->eq('activity.sentReceived', ':sentreceived');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('sentreceived', $data['accepted_sentreceived']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,82 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Templating\Entity\UserRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class UserFilter implements FilterInterface
{
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
$this->userRender = $userRender;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_users', EntityType::class, [
'class' => User::class,
'choice_label' => function (User $u) {
return $this->userRender->renderString($u, []);
},
'multiple' => true,
'expanded' => true,
'label' => 'Creators'
]);
}
public function getTitle(): string
{
return 'Filter activity by user';
}
public function describeAction($data, $format = 'string'): array
{
$users = [];
foreach ($data['accepted_users'] as $u) {
$users[] = $this->userRender->renderString($u, []);
}
return ['Filtered activity by user: only %users%', [
'%users%' => implode(", ou ", $users)
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('activity.user', ':users');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('users', $data['accepted_users']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -1,89 +0,0 @@
<?php
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\ACPFilters;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Export\FilterInterface;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class UserScopeFilter implements FilterInterface
{
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_userscope', EntityType::class, [
'class' => Scope::class,
'choice_label' => function (Scope $s) {
return $this->translatableStringHelper->localize(
$s->getName()
);
},
'multiple' => true,
'expanded' => true
]);
}
public function getTitle(): string
{
return 'Filter activity by userscope';
}
public function describeAction($data, $format = 'string'): array
{
$scopes = [];
foreach ($data['accepted_userscope'] as $s) {
$scopes[] = $this->translatableStringHelper->localize(
$s->getName()
);
}
return ['Filtered activity by userscope: only %scopes%', [
'%scopes%' => implode(", ou ", $scopes)
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('user', $qb->getAllAliases())) {
$qb->join('activity.user', 'user');
}
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('user.mainScope', ':userscope');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('userscope', $data['accepted_userscope']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
}

View File

@@ -11,13 +11,12 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\Export\FilterType;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
@@ -58,23 +57,36 @@ class ActivityDateFilter implements FilterInterface
$qb->setParameter('date_to', $data['date_to']);
}
public function applyOn(): string
public function applyOn()
{
return Declarations::ACTIVITY;
return 'activity';
}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_from', ChillDateType::class, [
$builder->add(
'date_from',
DateType::class,
[
'label' => 'Activities after this date',
'data' => new DateTime(),
])
->add('date_to', ChillDateType::class, [
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
]
);
$builder->add(
'date_to',
DateType::class,
[
'label' => 'Activities before this date',
'data' => new DateTime(),
])
;
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
]
);
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
/** @var \Symfony\Component\Form\FormInterface $filterForm */

View File

@@ -9,10 +9,9 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\PersonFilters;
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
@@ -80,9 +79,9 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
$qb->setParameter('selected_activity_reasons', $data['reasons']);
}
public function applyOn(): string
public function applyOn()
{
return Declarations::ACTIVITY_PERSON;
return 'activity';
}
public function buildForm(FormBuilderInterface $builder)

View File

@@ -12,7 +12,6 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
@@ -50,7 +49,7 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('activity.activityType', ':selected_activity_types');
$clause = $qb->expr()->in('activity.type', ':selected_activity_types');
if ($where instanceof Expr\Andx) {
$where->add($clause);
@@ -62,33 +61,39 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
$qb->setParameter('selected_activity_types', $data['types']);
}
public function applyOn(): string
public function applyOn()
{
return Declarations::ACTIVITY;
return 'activity';
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('types', EntityType::class, [
'class' => ActivityType::class,
'choice_label' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getName()),
'group_by' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getCategory()->getName()),
'multiple' => true,
'expanded' => false,
]);
$builder->add(
'types',
EntityType::class,
[
'class' => ActivityType::class,
'choice_label' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getName()),
'multiple' => true,
'expanded' => false,
]
);
}
public function describeAction($data, $format = 'string')
{
// collect all the reasons'name used in this filter in one array
$reasonsNames = array_map(
fn (ActivityType $t): string => $this->translatableStringHelper->localize($t->getName()),
fn (ActivityType $t): string => '"' . $this->translatableStringHelper->localize($t->getName()) . '"',
$this->activityTypeRepository->findBy(['id' => $data['types']->toArray()])
);
return ['Filtered by activity type: only %list%', [
'%list%' => implode(', ou ', $reasonsNames),
]];
return [
'Filtered by activity type: only %list%',
[
'%list%' => implode(', ', $reasonsNames),
],
];
}
public function getTitle()

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter\PersonFilters;
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
@@ -102,7 +102,7 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
$qb->setParameter('person_having_activity_reasons', $data['reasons']);
}
public function applyOn(): string
public function applyOn()
{
return Declarations::PERSON_IMPLIED_IN;
}

View File

@@ -14,20 +14,17 @@ namespace Chill\ActivityBundle\Form;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityPresence;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\DocStoreBundle\Form\StoredObjectType;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\Type\ChillCollectionType;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\CommentType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Form\Type\PrivateCommentType;
use Chill\MainBundle\Form\Type\ScopePickerType;
use Chill\MainBundle\Form\Type\UserPickerType;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
@@ -53,7 +50,6 @@ use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\Role;
use function in_array;
class ActivityType extends AbstractType
@@ -113,18 +109,19 @@ class ActivityType extends AbstractType
$activityType = $options['activityType'];
// TODO revoir la gestion des center au niveau du form des activité.
if ($options['center'] instanceof Center && null !== $options['data']->getPerson()) {
if ($options['center']) {
$builder->add('scope', ScopePickerType::class, [
'center' => $options['center'],
'role' => ActivityVoter::CREATE === (string) $options['role'] ? ActivityVoter::CREATE_PERSON : (string) $options['role'],
'required' => true,
'role' => $options['role'],
// TODO make required again once scope and rights are fixed
'required' => false,
]);
}
/** @var ? \Chill\PersonBundle\Entity\AccompanyingPeriod $accompanyingPeriod */
$accompanyingPeriod = null;
if ($options['accompanyingPeriod'] instanceof AccompanyingPeriod) {
if ($options['accompanyingPeriod']) {
$accompanyingPeriod = $options['accompanyingPeriod'];
}
@@ -221,10 +218,12 @@ class ActivityType extends AbstractType
]);
}
if ($activityType->isVisible('user') && $options['center'] instanceof Center) {
$builder->add('user', PickUserDynamicType::class, [
if ($activityType->isVisible('user') && $options['center']) {
$builder->add('user', UserPickerType::class, [
'label' => $activityType->getLabel('user'),
'required' => $activityType->isRequired('user'),
'center' => $options['center'],
'role' => $options['role'],
]);
}
@@ -443,8 +442,8 @@ class ActivityType extends AbstractType
$resolver
->setRequired(['center', 'role', 'activityType', 'accompanyingPeriod'])
->setAllowedTypes('center', ['null', Center::class])
->setAllowedTypes('role', [Role::class, 'string'])
->setAllowedTypes('center', ['null', 'Chill\MainBundle\Entity\Center'])
->setAllowedTypes('role', 'Symfony\Component\Security\Core\Role\Role')
->setAllowedTypes('activityType', \Chill\ActivityBundle\Entity\ActivityType::class)
->setAllowedTypes('accompanyingPeriod', [\Chill\PersonBundle\Entity\AccompanyingPeriod::class, 'null']);
}

View File

@@ -120,11 +120,3 @@
{{ form_end(edit_form) }}
{# {{ form(delete_form) }} #}
{% block js %}
{{ encore_entry_script_tags('mod_pickentity_type') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %}

View File

@@ -46,7 +46,7 @@
{% include 'ChillActivityBundle:Activity:list.html.twig' with {'context': 'person'} %}
{% if is_granted('CHILL_ACTIVITY_CREATE', person) %}
{% if is_granted('CHILL_ACTIVITY_CREATE_PERSON', person) %}
<ul class="record_actions sticky-form-buttons">
<li>
<a href="{{ path('chill_activity_activity_new', {'person_id': person_id, 'accompanying_period_id': accompanying_course_id}) }}"

View File

@@ -119,11 +119,3 @@
</li>
</ul>
{{ form_end(form) }}
{% block js %}
{{ encore_entry_script_tags('mod_pickentity_type') }}
{% endblock %}
{% block css %}
{{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %}

View File

@@ -133,7 +133,7 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
// change attribute CREATE
if (self::CREATE === $attribute) {
return $this->voterHelper->voteOnAttribute(self::CREATE_PERSON, $subject->getPerson(), $token);
$attribute = self::CREATE_PERSON;
}
} elseif ($subject->getAccompanyingPeriod() instanceof AccompanyingPeriod) {
if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $subject->getAccompanyingPeriod())) {
@@ -144,8 +144,7 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
if (AccompanyingPeriod::STEP_CLOSED === $subject->getAccompanyingPeriod()->getStep()) {
return false;
}
return $this->voterHelper->voteOnAttribute(self::CREATE_ACCOMPANYING_COURSE, $subject->getAccompanyingPeriod(), $token);
$attribute = self::CREATE_ACCOMPANYING_COURSE;
}
} else {
throw new RuntimeException('Could not determine context of activity.');
@@ -159,12 +158,12 @@ class ActivityVoter extends AbstractChillVoter implements ProvideRoleHierarchyIn
// transform the attribute
if (self::CREATE === $attribute) {
return $this->voterHelper->voteOnAttribute(self::CREATE_ACCOMPANYING_COURSE, $subject, $token);
$attribute = self::CREATE_ACCOMPANYING_COURSE;
}
} elseif ($subject instanceof Person) {
// transform the attribute
if (self::CREATE === $attribute) {
return $this->voterHelper->voteOnAttribute(self::CREATE_PERSON, $subject, $token);
$attribute = self::CREATE_PERSON;
}
}

View File

@@ -130,10 +130,8 @@ class ActivityContext implements
return $this->personRender->renderString($p, []);
},
'multiple' => false,
'required' => false,
'expanded' => true,
'label' => $options[$key . 'Label'],
'placeholder' => $this->translator->trans('Any person selected'),
]);
}
}

View File

@@ -3,48 +3,26 @@ services:
autowire: true
autoconfigure: true
## Indicators
chill.activity.export.count_activity_linked_to_person:
class: Chill\ActivityBundle\Export\Export\LinkedToPerson\CountActivity
chill.activity.export.count_activity:
class: Chill\ActivityBundle\Export\Export\CountActivity
tags:
- { name: chill.export, alias: 'count_activity_linked_to_person' }
- { name: chill.export, alias: 'count_activity' }
chill.activity.export.sum_activity_duration_linked_to_person:
class: Chill\ActivityBundle\Export\Export\LinkedToPerson\StatActivityDuration
chill.activity.export.sum_activity_duration:
class: Chill\ActivityBundle\Export\Export\StatActivityDuration
tags:
- { name: chill.export, alias: 'sum_activity_duration_linked_to_person' }
- { name: chill.export, alias: 'sum_activity_duration' }
chill.activity.export.list_activity_linked_to_person:
class: Chill\ActivityBundle\Export\Export\LinkedToPerson\ListActivity
chill.activity.export.list_activity:
class: Chill\ActivityBundle\Export\Export\ListActivity
tags:
- { name: chill.export, alias: 'list_activity_linked_to_person' }
- { name: chill.export, alias: 'list_activity' }
chill.activity.export.count_activity_linked_to_acp:
class: Chill\ActivityBundle\Export\Export\LinkedToACP\CountActivity
chill.activity.export.reason_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityReasonFilter
tags:
- { name: chill.export, alias: 'count_activity_linked_to_acp' }
- { name: chill.export_filter, alias: 'activity_reason_filter' }
chill.activity.export.sum_activity_duration_linked_to_acp:
class: Chill\ActivityBundle\Export\Export\LinkedToACP\SumActivityDuration
tags:
- { name: chill.export, alias: 'sum_activity_duration_linked_to_acp' }
chill.activity.export.sum_activity_visit_duration_linked_to_acp:
class: Chill\ActivityBundle\Export\Export\LinkedToACP\SumActivityVisitDuration
tags:
- { name: chill.export, alias: 'sum_activity_visit_duration_linked_to_acp' }
chill.activity.export.avg_activity_duration_linked_to_acp:
class: Chill\ActivityBundle\Export\Export\LinkedToACP\AvgActivityDuration
tags:
- { name: chill.export, alias: 'avg_activity_duration_linked_to_acp' }
chill.activity.export.avg_activity_visit_duration_linked_to_acp:
class: Chill\ActivityBundle\Export\Export\LinkedToACP\AvgActivityVisitDuration
tags:
- { name: chill.export, alias: 'avg_activity_visit_duration_linked_to_acp' }
## Filters
chill.activity.export.type_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityTypeFilter
tags:
@@ -55,61 +33,15 @@ services:
tags:
- { name: chill.export_filter, alias: 'activity_date_filter' }
chill.activity.export.reason_filter:
class: Chill\ActivityBundle\Export\Filter\PersonFilters\ActivityReasonFilter
tags:
- { name: chill.export_filter, alias: 'activity_reason_filter' }
chill.activity.export.person_having_an_activity_between_date_filter:
class: Chill\ActivityBundle\Export\Filter\PersonFilters\PersonHavingActivityBetweenDateFilter
class: Chill\ActivityBundle\Export\Filter\PersonHavingActivityBetweenDateFilter
tags:
- #0 register as a filter
name: chill.export_filter
alias: 'activity_person_having_ac_bw_date_filter'
chill.activity.export.locationtype_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\LocationTypeFilter
tags:
- { name: chill.export_filter, alias: 'activity_locationtype_filter' }
chill.activity.export.byuser_filter: # TMS (M2M)
class: Chill\ActivityBundle\Export\Filter\ACPFilters\ByUserFilter
tags:
- { name: chill.export_filter, alias: 'activity_byuser_filter' }
chill.activity.export.emergency_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\EmergencyFilter
tags:
- { name: chill.export_filter, alias: 'activity_emergency_filter' }
chill.activity.export.sentreceived_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\SentReceivedFilter
tags:
- { name: chill.export_filter, alias: 'activity_sentreceived_filter' }
chill.activity.export.bysocialaction_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\BySocialActionFilter
tags:
- { name: chill.export_filter, alias: 'activity_bysocialaction_filter' }
chill.activity.export.bysocialissue_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\BySocialIssueFilter
tags:
- { name: chill.export_filter, alias: 'activity_bysocialissue_filter' }
chill.activity.export.user_filter: # Creator (M2O)
class: Chill\ActivityBundle\Export\Filter\ACPFilters\UserFilter
tags:
- { name: chill.export_filter, alias: 'activity_user_filter' }
chill.activity.export.userscope_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\UserScopeFilter
tags:
- { name: chill.export_filter, alias: 'activity_userscope_filter' }
## Aggregators
chill.activity.export.reason_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator
class: Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator
tags:
- { name: chill.export_aggregator, alias: activity_reason_aggregator }
@@ -122,38 +54,3 @@ services:
class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator
tags:
- { name: chill.export_aggregator, alias: activity_user_aggregator }
chill.activity.export.locationtype_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\LocationTypeAggregator
tags:
- { name: chill.export_aggregator, alias: activity_locationtype_aggregator }
chill.activity.export.date_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\DateAggregator
tags:
- { name: chill.export_aggregator, alias: activity_date_aggregator }
chill.activity.export.byuser_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByUserAggregator
tags:
- { name: chill.export_aggregator, alias: activity_byuser_aggregator }
chill.activity.export.bythirdparty_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByThirdpartyAggregator
tags:
- { name: chill.export_aggregator, alias: activity_bythirdparty_aggregator }
chill.activity.export.bysocialaction_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\BySocialActionAggregator
tags:
- { name: chill.export_aggregator, alias: activity_bysocialaction_aggregator }
chill.activity.export.bysocialissue_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\BySocialIssueAggregator
tags:
- { name: chill.export_aggregator, alias: activity_bysocialissue_aggregator }
chill.activity.export.userscope_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\UserScopeAggregator
tags:
- { name: chill.export_aggregator, alias: activity_userscope_aggregator }

View File

@@ -203,39 +203,18 @@ Are you sure you want to remove the activity about "%name%" ?: Êtes-vous sûr d
The activity has been successfully removed.: L'activité a été supprimée.
# exports
Exports of activities linked to a person: Exports des activités liées à une personne
Number of activities linked to a person: Nombre d'activités liées à une personne
Count activities linked to a person: Nombre d'activités
Count activities linked to a person by various parameters.: Compte le nombre d'activités enregistrées et liées à une personne en fonction de différents paramètres.
Sum activity linked to a person duration: Durée des activités
Sum activities linked to a person duration: Durée des activités liés à un usager
Sum activities linked to a person duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
List activity linked to a person: Liste les activités
List activities linked to a person: Liste des activités liés à un usager
List activities linked to a person description: Crée la liste des activités en fonction de différents paramètres.
Exports of activities linked to an accompanying period: Exports des activités liées à un parcours
Number of activities linked to an accompanying period: Nombre d'activités liées à un parcours
Count activities linked to an accompanying period: Nombre d'activités
Count activities linked to an accompanying period by various parameters.: Compte le nombre d'activités enregistrées et liées à un parcours en fonction de différents paramètres.
Sum activity linked to an accompanying period duration: Somme de la durée des activités
Sum activities linked to an accompanying period duration: Somme de la durée des activités liées à un parcours
Sum activities linked to an accompanying period duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
Sum activity linked to an accompanying period visit duration: Somme de la durée de déplacement des activités
Sum activities linked to an accompanying period visit duration: Somme de la durée de déplacement des activités liées à un parcours
Sum activities linked to an accompanying period visit duration by various parameters.: Additionne la durée de déplacement des activités en fonction de différents paramètres.
Average activity linked to an accompanying period duration: Moyenne de la durée des activités
Average activities linked to an accompanying period duration: Moyenne de la durée des activités liées à un parcours
Average activities linked to an accompanying period duration by various parameters.: Moyenne de la durée des activités en fonction de différents paramètres.
Average activity linked to an accompanying period visit duration: Moyenne de la durée de déplacement des activités
Average activities linked to an accompanying period visit duration: Moyenne de la durée de déplacement des activités liées à un parcours
Average activities linked to an accompanying period visit duration by various parameters.: Moyenne de la durée de déplacement des activités en fonction de différents paramètres.
Count activities: Nombre d'activités
Count activities by various parameters.: Compte le nombre d'activités enregistrées en fonction de différents paramètres.
Sum activity duration: Total de la durée des activités
Sum activities duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
List activities: Liste les activités
Number of activities: Nombre d'activités
#filters
Filter by reason: Filtrer les activités par sujet
Filter by reason: Filtrer par sujet d'activité
'Filtered by reasons: only %list%': 'Filtré par sujet: seulement %list%'
'Filtered by activity type: only %list%': "Filtré par type d'activité: uniquement %list%"
Filtered by date activity: Filtrer les activités par date
'Filtered by activity type: only %list%': "Filtré par type d'activity: seulement %list%"
Filtered by date activity: Filtrer par date d'activité
Activities after this date: Activités après cette date
Activities before this date: Activités avant cette date
"Filtered by date of activity: only between %date_from% and %date_to%": "Filtré par date de l'activité: uniquement entre %date_from% et %date_to%"
@@ -247,59 +226,18 @@ Implied in an activity before this date: Impliqué dans une activité avant cett
Filtered by person having an activity between %date_from% and %date_to% with reasons %reasons_name%: Filtré par personnes associées à une activité entre %date_from% et %date_to% avec les sujets %reasons_name%
Activity reasons for those activities: Sujets de ces activités
Filter by activity type: Filtrer les activités par type
Filter activity by locationtype: Filtrer les activités par type de localisation
'Filtered activity by locationtype: only %types%': "Filtré par type de localisation: uniquement %types%"
Accepted locationtype: Types de localisation
Filter activity by linked users: Filtrer les activités par TMS
'Filtered activity by linked users: only %users%': "Filtré par TMS: uniquement %users%"
Accepted users: TMS(s)
Filter activity by emergency: Filtrer les activités par urgence
'Filtered activity by emergency: only %emergency%': "Filtré par urgence: uniquement si %emergency%"
activity is emergency: l'activité est urgente
activity is not emergency: l'activité n'est pas urgente
Filter activity by sentreceived: Filtrer les activités par envoyé/reçu
'Filtered activity by sentreceived: only %sentreceived%': "Filtré par envoyé/reçu: uniquement %sentreceived%"
Accepted sentreceived: ''
is sent: envoyé
is received: reçu
Filter activity by linked socialaction: Filtrer les activités par action liée
'Filtered activity by linked socialaction: only %actions%': "Filtré par action liée: uniquement %actions%"
Filter activity by linked socialissue: Filtrer les activités par problématique liée
'Filtered activity by linked socialissue: only %issues%': "Filtré par problématique liée: uniquement %issues%"
Filter activity by user: Filtrer les activités par créateur
'Filtered activity by user: only %users%': "Filtré par créateur: uniquement %users%"
Creators: Créateurs
Filter activity by userscope: Filtrer les activités par service du créateur
'Filtered activity by userscope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%"
Accepted userscope: Services
Filter by activity type: Filtrer par type d'activité
#aggregators
Activity type: Type d'activité
Activity user: Utilisateur lié à l'activité
Activity user: Utilisateur lié à l'activity
By reason: Par sujet
By category of reason: Par catégorie de sujet
Reason's level: Niveau du sujet
Group by reasons: Sujet d'activité
Aggregate by activity user: Grouper les activités par utilisateur
Aggregate by activity type: Grouper les activités par type
Aggregate by activity reason: Grouper les activités par sujet
Group activity by locationtype: Grouper les activités par type de localisation
Group activity by date: Grouper les activités par date
Frequency: Fréquence
by month: Par mois
by week: Par semaine
for week: Semaine
by year: Par année
in year: En
Group activity by linked users: Grouper les activités par TMS impliqué
Group activity by linked thirdparties: Grouper les activités par tiers impliqué
Accepted thirdparty: Tiers impliqué
Group activity by linked socialaction: Grouper les activités par action liée
Group activity by linked socialissue: Grouper les activités par problématique liée
Group activity by userscope: Grouper les activités par service du créateur
Aggregate by activity user: Aggréger par utilisateur lié à l'activité
Aggregate by activity type: Aggréger par type d'activité
Aggregate by activity reason: Aggréger par sujet de l'activité
Last activities: Les dernières activités

View File

@@ -1,234 +0,0 @@
#general
Show the activity: Toon activiteit
Edit the activity: Wijzig activiteit
Activity: Activiteit
Duration time: Duur
Duration Time: Duur
durationTime: duur
Travel time: Duur van verplaatsing
Attendee: Aanwezigheden
attendee: aanwezigheden
list_reasons: Onderwerpen
user_username: gebruikersnaam
circle_name: naam kring
Remark: Opmerking
No comments: Geen opmerkingen
Add a new activity: Voeg een nieuwe activiteit toe
Activity list: Lijst van activiteiten
present: aanwezig
not present: afwezig
Delete: Verwijderen
Update: Bijwerken
Update activity: Activieit bijwerken
Scope: Werkingsgebied
Activity data: Gegevens activiteit
Activity location: Locatie activiteit
No reason associated: Geen onderwerp
No social issues associated: Geen sociaal vraagstuk
No social actions associated: Geen maatschappelijke actie
There isn't any activities.: Er zijn geen activiteiten
type_name: Soort activiteit
person_firstname: voornaam
person_lastname: familienaam
person_id: Identificatienummer persoon
Type: Soort
Invisible: Onzichtbaar
Optional: Optioneel
Required: Verplicht
Persons: Personen
Users: Gebruikers
Emergency: Dringend
Sent received: Inkomend / Uitgaand
Sent: Verzenden
Received: Ontvangen
by: 'Door '
location: Plaats
Reasons: Onderwerpen
#forms
Activity creation: Nouvelle activité
Create: Créer
Back to the list: Retour à la liste
Save activity: Sauver l'activité
Reset form: Remise à zéro du formulaire
Choose the duration: Choisir la durée
Choose a type: Choisir un type
5 minutes: 5 minutes
10 minutes: 10 minutes
15 minutes: 15 minutes
20 minutes: 20 minutes
25 minutes: 25 minutes
30 minutes: 30 minutes
45 minutes: 45 minutes
1 hour: 1 heure
1 hour 15: 1 heure 15
1 hour 30: 1 heure 30
1 hour 45: 1 heure 45
2 hours: 2 heures
Concerned groups: Parties concernées
Persons in accompanying course: Usagers du parcours
Third persons: Tiers non-pro.
Others persons: Usagers
Third parties: Tiers professionnels
Users concerned: T(M)S
activity:
Insert a document: Insérer un document
Remove a document: Supprimer le document
comment: Commentaire
No documents: Aucun document
#timeline
'%user% has done an %activity_type%': '%user% a effectué une activité de type "%activity_type%"'
#controller
'Success : activity created!': L'activité a été créée.
'The form is not valid. The activity has not been created !': Le formulaire est invalide. L'activité n'a pas été créée.
'Success : activity updated!': L'activité a été mise à jour.
'The form is not valid. The activity has not been updated !': Le formulaire est invalide. L'activité n'a pas été mise à jour.
# ROLES
CHILL_ACTIVITY_CREATE: Créer une activité
CHILL_ACTIVITY_UPDATE: Modifier une activité
CHILL_ACTIVITY_SEE: Voir une activité
CHILL_ACTIVITY_SEE_DETAILS: Voir le détail des activités
CHILL_ACTIVITY_DELETE: Supprimer une activité
CHILL_ACTIVITY_STATS: Statistique des activités
CHILL_ACTIVITY_LIST: Liste des activités
# admin
Activities: Activités
Activity configuration: Configuration des activités
Activity configuration menu: Configuration des activités
Activity types: Types d'activité
Activity type configuration: Configuration des categories d'activités
Activity Reasons: Sujets d'une activité
Activity Reasons Category: Catégories de sujet d'activités
Activity Types Categories: Catégories des types d'activité
Activity Presences: Presences des activités
# Crud
crud:
activity_type:
title_new: Nouveau type d'activité
title_edit: Edition d'un type d'activité
activity_type_category:
title_new: Nouvelle catégorie de type d'activité
title_edit: Edition d'une catégorie de type d'activité
# activity reason admin
ActivityReason list: Liste des sujets
Create a new activity reason: Créer un nouveau sujet
Active: Actif
Category: Catégorie
ActivityReason creation: Nouveau sujet
ActivityReason edit: Modification d'un sujet
ActivityReason: Sujet d'activité
The entity is inactive and won't be proposed: Le sujet est inactif et ne sera pas proposé
The entity is active and will be proposed: Le sujet est actif et sera proposé
#activity reason category admin
ActivityReasonCategory list: Catégories de sujets
Create a new activity category reason: Créer une nouvelle catégorie
ActivityReasonCategory creation: Nouvelle catégorie de sujet
ActivityReasonCategory edit: Modification d'une catégorie de sujet
ActivityReasonCategory: Catégorie de sujet d'activité
ActivityReasonCategory is active and will be proposed: La catégorie est active et sera proposée
ActivityReasonCategory is inactive and won't be proposed: La catégorie est inactive et ne sera pas proposée
# activity type type admin
ActivityType list: Types d'activités
Create a new activity type: Créer un nouveau type d'activité
Persons visible: Visibilité du champ Personnes
Persons label: Libellé du champ Personnes
User visible: Visibilité du champ Utilisateur
User label: Libellé du champ Utilisateur
Date visible: Visibilité du champ Date
Date label: Libellé du champ Date
Location visible: Visibilité du champ Lieu
Location label: Libellé du champ Lieu
Third parties visible: Visibilité du champ Tiers
Third parties label: Libellé du champ Tiers
Duration time visible: Visibilité du champ Durée
Duration time label: Libellé du champ Durée
Travel time visible: Visibilité du champ Durée de déplacement
Travel time label: Libellé du champ Durée de déplacement
Attendee visible: Visibilité du champ Présence de l'usager
Attendee label: Libellé du champ Présence de l'usager
Reasons visible: Visibilité du champ Sujet
Reasons label: Libellé du champ Sujet
Comment visible: Visibilité du champ Commentaire
Comment label: Libellé du champ Commentaire
Emergency visible: Visibilité du champ Urgent
Emergency label: Libellé du champ Urgent
Accompanying period visible: Visibilité du champ Période d'accompagnement
Accompanying period label: Libellé du champ Période d'accompagnement
Social issues visible: Visibilité du champ Problématiques sociales
Social issues label: Libellé du champ Problématiques sociales
Social actions visible: Visibilité du champ Action sociale
Social actions label: Libellé du champ Action sociale
Users visible: Visibilité du champ Utilisateurs
Users label: Libellé du champ Utilisateurs
Sent received visible: Visibilité du champ Entrant / Sortant
Sent received label: Libellé du champ Entrant / Sortant
Documents visible: Visibilité du champ Documents
Documents label: Libellé du champ Documents
# activity type category admin
ActivityTypeCategory list: Liste des catégories des types d'activité
Create a new activity type category: Créer une nouvelle catégorie de type d'activité
# activity delete
Remove activity: Supprimer une activité
Are you sure you want to remove the activity about "%name%" ?: Êtes-vous sûr de vouloir supprimer une activité qui concerne "%name%" ?
The activity has been successfully removed.: L'activité a été supprimée.
# exports
Count activities: Nombre d'activités
Count activities by various parameters.: Compte le nombre d'activités enregistrées en fonction de différents paramètres.
Sum activity duration: Total de la durée des activités
Sum activities duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
List activities: Liste les activités
Number of activities: Nombre d'activités
#filters
Filter by reason: Filtrer par sujet d'activité
'Filtered by reasons: only %list%': 'Filtré par sujet: seulement %list%'
'Filtered by activity type: only %list%': "Filtré par type d'activity: uniquement %list%"
Filtered by date activity: Filtrer par date d'activité
Activities after this date: Activités après cette date
Activities before this date: Activités avant cette date
"Filtered by date of activity: only between %date_from% and %date_to%": "Filtré par date de l'activité: uniquement entre %date_from% et %date_to%"
This date should be after the date given in "Implied in an activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités après cette date"
Filtered by person having an activity in a period: Uniquement les personnes ayant eu une activité dans la période donnée
Implied in an activity after this date: Impliqué dans une activité après cette date
Implied in an activity before this date: Impliqué dans une activité avant cette date
Filtered by person having an activity between %date_from% and %date_to% with reasons %reasons_name%: Filtré par personnes associées à une activité entre %date_from% et %date_to% avec les sujets %reasons_name%
Activity reasons for those activities: Sujets de ces activités
Filter by activity type: Filtrer par type d'activité
#aggregators
Activity type: Type d'activité
Activity user: Utilisateur lié à l'activity
By reason: Par sujet
By category of reason: Par catégorie de sujet
Reason's level: Niveau du sujet
Group by reasons: Sujet d'activité
Aggregate by activity user: Grouper par utilisateur lié à l'activité
Aggregate by activity type: Grouper par type d'activité
Aggregate by activity reason: Grouper par sujet de l'activité
Last activities: Les dernières activités
See activity in accompanying course context: Voir l'activité dans le contexte du parcours d'accompagnement
You get notified of an activity which does not exists any more: Cette notification ne correspond pas à une activité valide.
you are not allowed to see it details: La notification fait référence à une activité à laquelle vous n'avez pas accès.
This is the minimal activity data: Activité n°
docgen:
Activity basic: Echange
A basic context for activity: Contexte pour les échanges

View File

@@ -1,23 +0,0 @@
The reasons's level should not be empty: Het onderwerp niveau mag niet leeg zijn.
At least one reason must be choosen: Kies minstens één onderwerp
For this type of activity, you must add at least one person: Voor dit soort activiteit dient u minstens één persoon toe te voegen.
For this type of activity, you must add at least one user: Voor dit soort activiteit dient u minstens één gebruiker toe te voegen.
For this type of activity, you must add at least one third party: Voor dit soort activiteit dient u minstens één externe partner toe te voegen.
For this type of activity, user is required: Voor dit soort activiteit, dient u een gebruiker in te vullen.
For this type of activity, date is required: Voor dit soort activiteit, dient u een datum in te vullen.
For this type of activity, location is required: Voor dit soort activiteit, dient u een locatie in te vullen.
For this type of activity, attendee is required: Voor dit soort activiteit, dient u minstend één 'aanwezige persoon' in te vullen.
For this type of activity, duration time is required: Voor dit soort activiteit, dient u een duur in te vullen.
For this type of activity, travel time is required: Voor dit soort activiteit, dient u een verplaatsingsduur in te vullen.
For this type of activity, reasons is required: Voor dit soort activiteit, dient u een onderwerp in te vullen.
For this type of activity, comment is required: Voor dit soort activiteit, dient u een opmerking in te vullen.
For this type of activity, sent/received is required: Voor dit soort activiteit, dient u het veld 'inkomend/uitgaand' in te vullen.
For this type of activity, document is required: Voor dit soort activiteit, dient u een document toe te voegen.
For this type of activity, emergency is required: Voor dit soort activiteit, is het veld 'dringend' verplicht.
For this type of activity, accompanying period is required: Voor dit soort activiteit, dient u een begeleidingstraject in te vullen.
For this type of activity, you must add at least one social issue: Voor dit soort activiteit, dient u een sociaal vraagstuk aan te duiden.
For this type of activity, you must add at least one social action: Voor dit soort activiteit, dient u een maatschappelijke actie toe te voegen.
# admin
This parameter must be equal to social issue parameter: Deze parameter moet gelijk zijn aan de parameter "zichtbaarheid veld sociaal vraagstuk".
The socialActionsVisible value is not compatible with the socialIssuesVisible value: De waarde van de parameter "zichtbaarheid veld maatschappelijke actie" is niet compatibel met de waarde van de parameter "zichtbaarheid veld sociaal vraagstuk".

View File

@@ -1,167 +0,0 @@
#general
Show the aside activity: Toon de nevenactiviteit
Edit the aside activity: Wijzig de nevenactiviteit
Remove aside activity: Verwijder de nevenactiviteit
Aside activity: Nevenactiviteit
Aside Activity Type List: Lijst van nevenactiviteiten
Duration time: Duur
durationTime: duur
user_username: gebruikersnaam
Remark: Opmerking
No comments: Geen opmerkingen
Add a new aside activity: Nevenactiviteit toeveogen
Aside activity list: Nevenactiviteiten
present: aanwezig
not present: afwezig
Delete: Verwijderen
Update: Bijwerken
Aside activity data: Gegevens van nevenactiviteit
There aren't any aside activities.: Geen nevenactiviteiten om weer te geven
Type: Type
Invisible: Onzichtbaar
Optional: Optioneel
Required: Verplicht
Persons: Personen
Users: Gebruikers
Emergency: Dringend
by: "Door "
location: Plaats
# Crud
crud:
aside_activity:
title_view: Details nevenactiviteit
title_new: Nieuwe nevenactiviteit
title_edit: Wijzig nevenactiviteit
title_delete: Verwijder nevenactiviteit
button_delete: Verwijderen
confirm_message_delete: Bent u zeker deze nevenactiviteit te willen verwijderen?
aside_activity_category:
title_new: Nieuwe categorie nevenactiviteiten
title_edit: Wijzigen categorie nevenactiviteiten
#forms
Create a new aside activity type: Nieuwe categorie nevenactiviteiten
Back to the list: Terug naar de lijst
Choose the duration: Kies een duur
Choose a category: Kies een categorie
Is active: Actief
For agent: Voor de gebruiker
date: Datum
Duration: Duur
Note: Opmerking
Choose the agent for whom this activity is created: Kies de gebruiker voor wie deze nevenactiviteit wordt aangemaakt.
Choose the activity category: Kies een categorie
#Duration
minutes: minuten
hour: uur
hours: uren
day: dag
days: dagen
5 minutes: 5 minuten
10 minutes: 10 minuten
15 minutes: 15 minuten
20 minutes: 20 minuten
25 minutes: 25 minuten
30 minutes: 30 minuten
45 minutes: 45 minuten
1 hour: 1 uur
1 hour 15: 1 uur 15
1 hour 30: 1 uur 30
1 hour 45: 1 uur 45
2 hours: 2 uren
2 hours 30: 2 uur 30
3 hours: 3 uren
3 hours 30: 3 uur 30
4 hours: 4 uren
4 hours 30: 4 uur 30
5 hours: 5 uren
5 hours 30: 5 uur 30
6 hours: 6 uren
6 hours 30: 6 uur 30
7 hours: 7 uren
7 hours 30: 7 uur 30
8 hours: 8 uren
8 hours 30: 8 uur 30
9 hours: 9 uren
9 hours 30: 9 uur 30
10 hours: 10 uren
1/2 day: 1/2 dag
1 day: 1 dag
1 1/2 days: 1 1/2 dagen
2 days: 2 dagen
2 1/2 days: 2 1/2 dagen
3 days: 3 dagen
3 1/2 days: 3 1/2 dagen
4 days: 4 dagen
4 1/2 days: 4 1/2 dagen
5 days: 5 dagen
5 1/2 days: 5 1/2 dagen
6 days: 6 dagen
6 1/2 days: 6 1/2 dagen
7 days: 7 dagen
7 1/2 days: 7 1/2 dagen
8 days: 8 dagen
8 1/2 days: 8 1/2 dagen
9 days: 9 dagen
9 1/2 days: 9 1/2 dagen
10 days: 10 dagen
10 1/2 days: 10 1/2 dagen
11 days: 11 dagen
11 1/2 days: 11 1/2 dagen
12 days: 12 dagen
12 1/2 days: 12 1/2 dagen
13 days: 13 dagen
13 1/2 days: 13 1/2 dagen
14 days: 14 dagen
14 1/2 days: 14 1/2 dagen
15 days: 15 dagen
15 1/2 days: 15 1/2 dagen
16 days: 16 dagen
16 1/2 days: 16 1/2 dagen
17 days: 17 dagen
17 1/2 days: 17 1/2 dagen
18 days: 18 dagen
18 1/2 days: 18 1/2 dagen
19 days: 19 dagen
19 1/2 days: 19 1/2 dagen
20 days: 20 dagen
20 1/2 days: 20 1/2 dagen
21 days: 21 dagen
21 1/2 days: 21 1/2 dagen
22 days: 22 dagen
22 1/2 days: 22 1/2 dagen
23 days: 23 dagen
23 1/2 days: 23 1/2 dagen
24 days: 24 dagen
24 1/2 days: 24 1/2 dagen
25 days: 25 dagen
25 1/2 days: 25 1/2 dagen
26 days: 26 dagen
26 1/2 days: 26 1/2 dagen
27 days: 27 dagen
27 1/2 days: 27 1/2 dagen
28 days: 28 dagen
28 1/2 days: 28 1/2 dagen
29 days: 29 dagen
29 1/2 days: 29 1/2 dagen
30 days: 30 dagen
#list
My aside activities: Mijn nevenactiviteiten
#Aside activity delete
Delete aside activity: Verwijder een nevenactiviteit
Are you sure you want to remove the aside activity concerning "%name%" ?: Bent u zeker deze nevenactiviteit voor "%name%" te willen verwijderen ?
The activity has been successfully removed.: De nevenactiviteit werd verwijdert.
#Menu
Create an aside activity: "Maak een nevenactiviteit aan"
Aside activity categories: Categorieën van nevenactiviteiten
Aside activity configuration menu: "Configuratie menu voor nevenactiviteiten"
Phonecall: "Telefoon oproep"
# admin
Aside activities: Nevenactiviteiten
Aside activity types: Types nevenactiviteiten
Aside activity type configuration: Configuratie categorieën nevenactiviteiten

View File

@@ -1 +0,0 @@
You must not add twice the same category in the parent tree (previous result returned): Je mag niet tweemaal dezelfde entiteit aanduiden in de stamboom. (Het vorige resultaat werd hersteld)

View File

@@ -29,58 +29,44 @@ class ConfigRepository
$this->charges = $charges;
}
public function getChargesKeys(bool $onlyActive = false): array
public function getChargesKeys(): array
{
return array_map(static function ($element) { return $element['key']; }, $this->getCharges($onlyActive));
return array_map(static function ($element) { return $element['key']; }, $this->charges);
}
/**
* @return array where keys are the resource'key and label the ressource label
*/
public function getChargesLabels(bool $onlyActive = false)
public function getChargesLabels()
{
$charges = [];
foreach ($this->getCharges($onlyActive) as $definition) {
foreach ($this->charges as $definition) {
$charges[$definition['key']] = $this->normalizeLabel($definition['labels']);
}
return $charges;
}
public function getResourcesKeys(bool $onlyActive = false): array
public function getResourcesKeys(): array
{
return array_map(static function ($element) { return $element['key']; }, $this->getResources($onlyActive));
return array_map(static function ($element) { return $element['key']; }, $this->resources);
}
/**
* @return array where keys are the resource'key and label the ressource label
*/
public function getResourcesLabels(bool $onlyActive = false)
public function getResourcesLabels()
{
$resources = [];
foreach ($this->getResources($onlyActive) as $definition) {
foreach ($this->resources as $definition) {
$resources[$definition['key']] = $this->normalizeLabel($definition['labels']);
}
return $resources;
}
private function getCharges(bool $onlyActive = false): array
{
return $onlyActive ?
array_filter($this->charges, function ($el) { return $el['active']; })
: $this->charges;
}
private function getResources(bool $onlyActive = false): array
{
return $onlyActive ?
array_filter($this->resources, function ($el) { return $el['active']; })
: $this->resources;
}
private function normalizeLabel($labels)
{
$normalizedLabels = [];

View File

@@ -14,6 +14,11 @@ namespace Chill\BudgetBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files.
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/configuration.html}
*/
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
@@ -32,7 +37,6 @@ class Configuration implements ConfigurationInterface
->info('the key stored in database')
->example('salary')
->end()
->booleanNode('active')->defaultTrue()->end()
->arrayNode('labels')->isRequired()->requiresAtLeastOneElement()
->arrayPrototype()
->children()
@@ -55,7 +59,6 @@ class Configuration implements ConfigurationInterface
->info('the key stored in database')
->example('salary')
->end()
->booleanNode('active')->defaultTrue()->end()
->arrayNode('labels')->isRequired()->requiresAtLeastOneElement()
->arrayPrototype()
->children()

View File

@@ -103,7 +103,7 @@ class ChargeType extends AbstractType
private function getTypes()
{
$charges = $this->configRepository
->getChargesLabels(true);
->getChargesLabels();
// rewrite labels to filter in language
foreach ($charges as $key => $labels) {

View File

@@ -87,7 +87,7 @@ class ResourceType extends AbstractType
private function getTypes()
{
$resources = $this->configRepository
->getResourcesLabels(true);
->getResourcesLabels();
// rewrite labels to filter in language
foreach ($resources as $key => $labels) {

View File

@@ -1,8 +0,0 @@
budget:
number of elements: >-
{nb_items, plural,
=0 {Geen element}
one {Één element}
many {# elementen}
other {# elementen}
}

View File

@@ -37,7 +37,7 @@ No charges registered: Aucune charge enregistrée
No past resources registered: Aucune ressource passée
No past charges registered: Aucune charge passée
No future resources registered: Aucune ressource future enregistrée
No future charges registered: Aucune charge future enregistrée
No future charges registered: Aucune ressource future enregistrée
No current budget element registered: Pas des éléments de budget actuelles enregistrés
New resource: Nouvelle ressource

View File

@@ -1,76 +0,0 @@
Budget: Budget
Resource: Inkomsten
Charge: Onkosten
Budget for %name%: Budget van %name%
Budget for household %household%: Budget van gezin
Current budget household members: Actuele budget van gezinsleden
Show budget of %name%: Toon budget van %name%
See complete budget: Toon volledige budget
Hide budget: Verbergen
Hide budget of %name%: Verberg budget van %name%
Resource element type: Type inkomsten
Actual budget: Actuele elementen budget
Actual resources: Actuele inkomsten
Actual resources for %name%: Actuele inkomsten van %name%
Actual charges for %name%: Actuele onkosten van %name%
Actual charges: Actuele onkosten
Past budget: Elementen van afgelopen budget
Show past budget: Toon afgelopen budget
Show future budget: Toon toekomstig budget
Past resources: Afgelopen inkomsten
Past charges: Afgelopen onkosten
Future budget: Toekomstige elementen van budget
Future resources: Toekomstige inkomsten
Future charges: Toekomstige onkosten
Budget element type: Type
Validity period: Geldigheidsperiod
Start of validity period: Begin geldigheidsperiode
End of validity period: Eind geldigheidsperiode
Total: Totaal
Create new resource: Nieuwe inkomsten toevoegen
Create new charge: Nieuwe onkosten toevoegen
See person: Toon persoon
There isn't any element recorded: Geen budget elementen geregistreerd
No resources registered: Geen inkomsten geregistreerd
No charges registered: Geen onkosten geregistreerd
No past resources registered: Geen afgelopen inkomsten geregistreerd
No past charges registered: Geen afgelopen onkosten geregistreerd
No future resources registered: Geen toekomstige inkomsten geregistreerd
No future charges registered: Geen toekomste onkosten geregistreerd
No current budget element registered: Geen actuele budget elementen geregistreerd
New resource: Nieuwe inkomsten
New charge: Nieuwe onkosten
Edit resource: Wijzig inkomsten
Edit: Wijzigen
Edit charge: Wijzig onkosten
Remove resource: Verwijder inkomsten
Remove charge: Verwijder onkosten
Are you sure you want to remove the ressource "%type%" associated to "%name%" ?: Bent u zeker de inkomsten van het type "%type%" en geassocieerd met %name% te willen verwijderen?
Are you sure you want to remove the charge "%type%" associated to "%name%" ?: Bent u zeker de onkosten van het type "%type%" en geassocieerd met %name% te willen verwijderen?
Resource deleted: Inkomsten verwijdert
Charge deleted: Onkosten verwijdert
Charge created: Onkosten toegevoegd
Resource created: Inkomsten toegevoegd
Resource updated: Inkomsten bijgewerkt
Charge updated: Onkosten bijgewerkt
Choose a resource type: Kies een type inkomsten
Choose a charge type: Kies een type onkosten
Amount: Bedrag
Comment: Opmerking
Help to pay charges: Hulp bij afbetaling onkosten
Choose a status: Kies een status
charge.help.running: In uitvoering
charge.help.no: Niet gevraagd
charge.help.yes: Ja
charge.help.not-concerned: Niet betrokken
Budget calculator: Berekeningen en indicatoren van budget
Budget calculator result: Resultaten
The balance: Verschil tussen inkomsten en onkosten
Valid since %startDate% until %endDate%: Geldig sinds %startDate% tot %endDate%
Valid since %startDate%: Geldig sinds %startDate%

View File

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

View File

@@ -32,7 +32,6 @@ class ChillCalendarExtension extends Extension implements PrependExtensionInterf
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
$loader->load('services/exports.yaml');
$loader->load('services/controller.yml');
$loader->load('services/fixtures.yml');
$loader->load('services/form.yml');

View File

@@ -1,79 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Templating\Entity\UserRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final class AgentAggregator implements AggregatorInterface
{
private UserRepository $userRepository;
private UserRender $userRender;
public function __construct(
UserRepository $userRepository,
UserRender $userRender
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('cal.user', 'u');
$qb->addSelect('u.id AS agent_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('agent_aggregator');
} else {
$qb->groupBy('agent_aggregator');
}
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data): \Closure
{
return function ($value): string {
if ('_header' === $value) {
return 'Agent';
}
$r = $this->userRepository->find($value);
return $this->userRender->renderString($r, []);
};
}
public function getQueryKeys($data): array
{
return ['agent_aggregator'];
}
public function getTitle(): string
{
return 'Group by agent';
}
}

View File

@@ -1,81 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\CalendarBundle\Repository\CancelReasonRepository;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class CancelReasonAggregator implements AggregatorInterface
{
private CancelReasonRepository $cancelReasonRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
CancelReasonRepository $cancelReasonRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->cancelReasonRepository = $cancelReasonRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function getLabels($key, array $values, $data): \Closure
{
return function($value): string {
if ($value === '_header') {
return 'Cancel reason';
}
$j = $this->cancelReasonRepository->find($value);
return $this->translatableStringHelper->localize(
$j->getName()
);
};
}
public function getQueryKeys($data): array
{
return ['cancel_reason_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getTitle(): string
{
return 'Group by cancel reason';
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
// TODO: still needs to take into account appointments without a cancel reason somehow
$qb->join('cal.cancelReason', 'cr');
$qb->addSelect('IDENTITY(cal.cancelReason) as cancel_reason_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('cancel_reason_aggregator');
} else {
$qb->groupBy('cancel_reason_aggregator');
}
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
}

View File

@@ -1,80 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserJobRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final class JobAggregator implements AggregatorInterface
{
private UserJobRepository $jobRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
UserJobRepository $jobRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->jobRepository = $jobRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function getLabels($key, array $values, $data): \Closure
{
return function($value): string {
if ($value === '_header') {
return 'Job';
}
$j = $this->jobRepository->find($value);
return $this->translatableStringHelper->localize(
$j->getLabel()
);
};
}
public function getQueryKeys($data): array
{
return ['job_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getTitle(): string
{
return 'Group by agent job';
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('cal.user', 'u');
$qb->addSelect('IDENTITY(u.userJob) as job_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('job_aggregator');
} else {
$qb->groupBy('job_aggregator');
}
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
}

View File

@@ -1,75 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\LocationRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
final class LocationAggregator implements AggregatorInterface
{
private LocationRepository $locationRepository;
public function __construct(
LocationRepository $locationRepository
) {
$this->locationRepository = $locationRepository;
}
public function getLabels($key, array $values, $data): \Closure
{
return function($value): string {
if ($value === '_header') {
return 'Location';
}
$l = $this->locationRepository->find($value);
return $l->getName();
};
}
public function getQueryKeys($data): array
{
return ['location_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getTitle(): string
{
return 'Group by location';
}
public function addRole(): ?Role
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('cal.location', 'l');
$qb->addSelect('IDENTITY(cal.location) as location_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('location_aggregator');
} else {
$qb->groupBy('location_aggregator');
}
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
}

View File

@@ -1,81 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\LocationTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final class LocationTypeAggregator implements AggregatorInterface
{
private LocationTypeRepository $locationTypeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
LocationTypeRepository $locationTypeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->locationTypeRepository = $locationTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function getLabels($key, array $values, $data): \Closure
{
return function($value): string {
if ($value === '_header') {
return 'Location type';
}
$j = $this->locationTypeRepository->find($value);
return $this->translatableStringHelper->localize(
$j->getTitle()
);
};
}
public function getQueryKeys($data): array
{
return ['location_type_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getTitle(): string
{
return 'Group by location type';
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('cal.location', 'l');
$qb->addSelect('IDENTITY(l.locationType) as location_type_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('location_type_aggregator');
} else {
$qb->groupBy('location_type_aggregator');
}
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
}

View File

@@ -1,66 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class MonthYearAggregator implements AggregatorInterface
{
public function getQueryKeys($data): array
{
return ['month_year_aggregator'];
}
public function getLabels($key, array $values, $data): Closure
{
return function($value): string {
if ($value === '_header') {
return 'by month and year';
}
$month = substr($value,0, 2);
$year = substr($value, 3, 4);
return strftime('%B %G', mktime(0, 0, 0, $month, '1', $year));
};
}
public function buildForm(FormBuilderInterface $builder)
{
// No form needed
}
public function getTitle(): string
{
return 'Group by month and year';
}
public function addRole(): ?Role
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect("to_char(cal.startDate, 'MM-YYYY') AS month_year_aggregator");
// $qb->addSelect("extract(month from age(cal.startDate, cal.endDate)) AS month_aggregator");
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('month_year_aggregator');
} else {
$qb->groupBy('month_year_aggregator');
}
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
}

View File

@@ -1,80 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Aggregator;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final class ScopeAggregator implements AggregatorInterface
{
private ScopeRepository $scopeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
ScopeRepository $scopeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function getLabels($key, array $values, $data): \Closure
{
return function ($value): string {
if ($value === '_header') {
return 'Scope';
}
$s = $this->scopeRepository->find($value);
return $this->translatableStringHelper->localize(
$s->getName()
);
};
}
public function getQueryKeys($data): array
{
return ['scope_aggregator'];
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getTitle(): string
{
return 'Group by agent scope';
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('cal.user', 'u');
$qb->addSelect('IDENTITY(u.mainScope) as scope_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('scope_aggregator');
} else {
$qb->groupBy('scope_aggregator');
}
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
}

View File

@@ -1,20 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\CalendarBundle\Export;
/**
* This class declare constants used for the export framework.
*/
abstract class Declarations
{
public const CALENDAR_TYPE = 'calendar';
}

View File

@@ -1,111 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Export;
use Chill\CalendarBundle\Repository\CalendarRepository;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\CalendarBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Process\Exception\LogicException;
use Symfony\Component\Security\Core\Role\Role;
class CountAppointments implements ExportInterface, GroupedExportInterface
{
private CalendarRepository $calendarRepository;
public function __construct(CalendarRepository $calendarRepository)
{
$this->calendarRepository = $calendarRepository;
}
public function buildForm(FormBuilderInterface $builder)
{
// No form necessary
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Count appointments by various parameters.';
}
public function getLabels($key, array $values, $data): \Closure
{
if ('export_result' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
return static function ($value) use ($labels) {
return $labels[$value];
};
}
public function getQueryKeys($data): array
{
return ['export_result'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
}
public function getTitle(): string
{
return 'Count appointments';
}
public function getType(): string
{
return Declarations::CALENDAR_TYPE;
}
/**
* Initiate the query.
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$qb = $this->calendarRepository->createQueryBuilder('cal');
$qb->select('COUNT(cal.id) AS export_result');
return $qb;
}
public function requiredRole(): Role
{
// which role should we give here?
return new Role(PersonVoter::STATS);
}
public function supportsModifiers(): array
{
return [
Declarations::CALENDAR_TYPE,
];
}
public function getGroup(): string
{
return 'Exports of calendar';
}
}

View File

@@ -1,103 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Export;
use Chill\CalendarBundle\Export\Declarations;
use Chill\CalendarBundle\Repository\CalendarRepository;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class StatAppointmentAvgDuration implements ExportInterface, GroupedExportInterface
{
private CalendarRepository $calendarRepository;
public function __construct(
CalendarRepository $calendarRepository
) {
$this->calendarRepository = $calendarRepository;
}
public function buildForm(FormBuilderInterface $builder): void
{
// no form needed
}
public function getTitle(): string
{
return 'Average appointment duration';
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Get the average of appointment duration according to various filters';
}
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
}
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
return static function ($value) use ($labels) {
return $labels[$value];
};
}
public function getQueryKeys($data): array
{
return ['export_result'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getType(): string
{
return Declarations::CALENDAR_TYPE;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
{
$qb = $this->calendarRepository->createQueryBuilder('cal');
$qb
->select('AVG(cal.endDate - cal.startDate) AS export_result');
return $qb;
}
public function requiredRole(): Role
{
return new Role(AccompanyingPeriodVoter::STATS);
}
public function supportsModifiers(): array
{
return [
Declarations::CALENDAR_TYPE
];
}
public function getGroup(): string
{
return 'Exports of calendar';
}
}

View File

@@ -1,102 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Export;
use Chill\CalendarBundle\Export\Declarations;
use Chill\CalendarBundle\Repository\CalendarRepository;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class StatAppointmentSumDuration implements ExportInterface, GroupedExportInterface
{
private CalendarRepository $calendarRepository;
public function __construct(
CalendarRepository $calendarRepository
) {
$this->calendarRepository = $calendarRepository;
}
public function buildForm(FormBuilderInterface $builder): void
{
// no form needed
}
public function getTitle(): string
{
return 'Sum of appointment durations';
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Get the sum of appointment durations according to various filters';
}
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
}
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
return static function ($value) use ($labels) {
return $labels[$value];
};
}
public function getQueryKeys($data): array
{
return ['export_result'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getType(): string
{
return Declarations::CALENDAR_TYPE;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
{
$qb = $this->calendarRepository->createQueryBuilder('cal');
$qb
->select('SUM(cal.endDate - cal.startDate) AS export_result');
return $qb;
}
public function requiredRole(): Role
{
return new Role(AccompanyingPeriodVoter::STATS);
}
public function supportsModifiers(): array
{
return [
Declarations::CALENDAR_TYPE
];
}
public function getGroup(): string
{
return 'Exports of calendar';
}
}

View File

@@ -1,79 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Filter;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class AgentFilter implements FilterInterface
{
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
$this->userRender = $userRender;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_agents', EntityType::class, [
'class' => User::class,
'choice_label' => function (User $u) {
return $this->userRender->renderString($u, []);
},
'multiple' => true,
'expanded' => true
]);
}
public function getTitle(): string
{
return 'Filter by agent';
}
public function describeAction($data, $format = 'string'): array
{
$users = [];
foreach ($data['accepted_agents'] as $r) {
$users[] = $r;
}
return [
'Filtered by agent: only %agents%', [
'%agents' => implode(", ou ", $users)
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('cal.user', ':agents');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('agents', $data['accepted_agents']);
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
}

View File

@@ -1,70 +0,0 @@
<?php
namespace Chill\CalendarBundle\Export\Filter;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class BetweenDatesFilter implements FilterInterface
{
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('date_from', ChillDateType::class, [
'data' => new \DateTime(),
])
->add('date_to', ChillDateType::class, [
'data' => new \DateTime(),
])
;
}
public function getTitle(): string
{
return 'Filter by appointments between certain dates';
}
public function describeAction($data, $format = 'string'): array
{
return ['Filtered by appointments between %dateFrom% and %dateTo%', [
'%dateFrom%' => $data['date_from']->format('d-m-Y'),
'%dateTo%' => $data['date_to']->format('d-m-Y'),
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->andX(
$qb->expr()->gte('cal.startDate', ':dateFrom'),
$qb->expr()->lte('cal.endDate', ':dateTo')
);
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('dateFrom', $data['date_from']);
// modify dateTo so that entire day is also taken into account up until the beginning of the next day.
$qb->setParameter('dateTo', $data['date_to']->modify('+1 day'));
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
}

View File

@@ -1,99 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\CalendarBundle\Export\Filter;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class JobFilter implements FilterInterface
{
protected TranslatorInterface $translator;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper
) {
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('job', EntityType::class, [
'class' => UserJob::class,
'choice_label' => function (UserJob $j) {
return $this->translatableStringHelper->localize(
$j->getLabel()
);
},
'multiple' => true,
'expanded' => true
]);
}
public function describeAction($data, $format = 'string'): array
{
$userJobs = [];
foreach ($data['job'] as $j) {
$userJobs[] = $this->translatableStringHelper->localize(
$j->getLabel());
}
return ['Filtered by agent job: only %jobs%', [
'%jobs%' => implode(', ou ', $userJobs)
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('cal.user', 'u');
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('u.userJob', ':job');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('job', $data['job']);
}
public function applyOn(): string
{
return Declarations::CALENDAR_TYPE;
}
public function getTitle(): string
{
return 'Filter by agent job';
}
}

View File

@@ -1,99 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\CalendarBundle\Export\Filter;
use Chill\CalendarBundle\Export\Declarations;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class ScopeFilter implements FilterInterface
{
protected TranslatorInterface $translator;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper
) {
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('scope', EntityType::class, [
'class' => Scope::class,
'choice_label' => function (Scope $s) {
return $this->translatableStringHelper->localize(
$s->getName()
);
},
'multiple' => true,
'expanded' => true
]);
}
public function describeAction($data, $format = 'string')
{
$scopes = [];
foreach ($data['scope'] as $s) {
$scopes[] = $this->translatableStringHelper->localize(
$s->getName());
}
return ['Filtered by agent scope: only %scopes%', [
'%scopes%' => implode(', ou ', $scopes)
]];
}
public function addRole()
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('cal.user', 'u');
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('u.mainScope', ':scope');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('scope', $data['scope']);
}
public function applyOn()
{
return Declarations::CALENDAR_TYPE;
}
public function getTitle()
{
return 'Filter by agent scope';
}
}

View File

@@ -1,104 +0,0 @@
services:
## Indicators
chill.calendar.export.count_appointments:
class: Chill\CalendarBundle\Export\Export\CountAppointments
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: count_appointments }
chill.calendar.export.average_duration_appointments:
class: Chill\CalendarBundle\Export\Export\StatAppointmentAvgDuration
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: average_duration_appointments }
chill.calendar.export.sum_duration_appointments:
class: Chill\CalendarBundle\Export\Export\StatAppointmentSumDuration
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: sum_duration_appointments }
## Filters
chill.calendar.export.agent_filter:
class: Chill\CalendarBundle\Export\Filter\AgentFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: agent_filter }
chill.calendar.export.job_filter:
class: Chill\CalendarBundle\Export\Filter\JobFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: job_filter }
chill.calendar.export.scope_filter:
class: Chill\CalendarBundle\Export\Filter\ScopeFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: scope_filter }
chill.calendar.export.between_dates_filter:
class: Chill\CalendarBundle\Export\Filter\BetweenDatesFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: between_dates_filter }
## Aggregator
chill.calendar.export.agent_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\AgentAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: agent_aggregator }
chill.calendar.export.job_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\JobAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: job_aggregator }
chill.calendar.export.scope_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\ScopeAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: scope_aggregator }
chill.calendar.export.location_type_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\LocationTypeAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: location_type_aggregator }
chill.calendar.export.location_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\LocationAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: location_aggregator }
chill.calendar.export.cancel_reason_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\CancelReasonAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: cancel_reason_aggregator }
chill.calendar.export.month_aggregator:
class: Chill\CalendarBundle\Export\Aggregator\MonthYearAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: month_aggregator }

View File

@@ -38,36 +38,3 @@ crud:
add_new: Ajouter un nouveau
title_new: Nouveau motif d'annulation
title_edit: Modifier le motif d'annulation
# exports
Exports of calendar: Exports des rendez-vous
Count appointments: Nombre de rendez-vous
Count appointments by various parameters.: Compte le nombre de rendez-vous en fonction de différents paramètres.
Average appointment duration: Moyenne de la durée des rendez-vous
Get the average of appointment duration according to various filters: Calcule la moyenne des durées des rendez-vous en fonction de différents paramètres.
Sum of appointment durations: Somme de la durée des rendez-vous
Get the sum of appointment durations according to various filters: Calcule la somme des durées des rendez-vous en fonction de différents paramètres.
'Filtered by agent: only %agents%': "Filtré par agents: uniquement %agents%"
Filter by agent: Filtrer par agents
Filter by agent job: Filtrer par métiers des agents
'Filtered by agent job: only %jobs%': 'Filtré par métiers des agents: uniquement les %jobs%'
Filter by agent scope: Filtrer par services des agents
'Filtered by agent scope: only %scopes%': 'Filtré par services des agents: uniquement les services %scopes%'
Filter by appointments between certain dates: Filtrer par date du rendez-vous
'Filtered by appointments between %dateFrom% and %dateTo%': 'Filtré par rendez-vous entre %dateFrom% et %dateTo%'
Group by agent: Grouper par agent
Group by agent job: Grouper par métier de l'agent
Group by agent scope: Grouper par service de l'agent
Group by location type: Grouper par type de localisation
Group by location: Grouper par lieu de rendez-vous
Group by cancel reason: Grouper par motif d'annulation
Group by month and year: Grouper par mois et année
Scope: Service
Job: Métier
Location type: Type de localisation
Location: Lieu de rendez-vous
by month and year: Par mois et année

View File

@@ -1,28 +0,0 @@
Calendar: Afspraken
Calendar list: Lijst van afspraken
My calendar list: Mijn afspraken
There is no calendar items.: Er zijn geen afspraken
Remove calendar item: Verwijder afspraak
Are you sure you want to remove the calendar item?: Bent u zeker deze afspraak te willen verwijderen
Concerned groups: Betrokken partijen
Calendar data: Gegevens afspraak
Update calendar: Wijzig afspraak
main user concerned: Betrokken gebruiker
Main user: Hoofdgebruiker
Calendar item creation: Maak afspraak aan
start date: Begin afspraak
end date: Einde afspraak
cancel reason: reden van annulatie
status: Status van afspraak
calendar location: Locatie afspraak
calendar comment: Opmerkingen over afspraak
sendSMS: Verzenden sms
Send s m s: Sms verzenden?
Cancel reason: Reden van annulatie
Add a new calendar: Nieuwe afspraak toevoegen
"Success : calendar item updated!": "Afspraak bijgewerkt"
"Success : calendar item created!": "Afspraak aangemaakt"
The calendar item has been successfully removed.: De afspraak is verwijdert
From the day: Vanaf
to the day: tot
Transform to activity: In activiteit omzetten

View File

@@ -188,7 +188,7 @@ class CustomFieldChoice extends AbstractCustomField
$choices = [];
foreach ($cf->getOptions()[self::CHOICES] as $choice) {
if (false === $choice['active']) {
if (false === $choices['active']) {
continue;
}
$choices[$choice['slug']] = $this->translatableStringHelper

View File

@@ -11,7 +11,6 @@ declare(strict_types=1);
namespace Chill\DocStoreBundle\Entity;
use Chill\MainBundle\Entity\HasScopesInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\ORM\Mapping as ORM;
@@ -19,7 +18,7 @@ use Doctrine\ORM\Mapping as ORM;
* @ORM\Entity
* @ORM\Table("chill_doc.accompanyingcourse_document")
*/
class AccompanyingCourseDocument extends Document implements HasScopesInterface
class AccompanyingCourseDocument extends Document
{
/**
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
@@ -32,15 +31,6 @@ class AccompanyingCourseDocument extends Document implements HasScopesInterface
return $this->course;
}
public function getScopes(): iterable
{
if (null !== $this->course) {
return [];
}
return $this->course->getScopes();
}
public function setCourse(?AccompanyingPeriod $course): self
{
$this->course = $course;

View File

@@ -16,6 +16,8 @@ use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
use Chill\MainBundle\Entity\HasScopeInterface;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
@@ -24,7 +26,7 @@ use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\MappedSuperclass
*/
class Document implements TrackCreationInterface, TrackUpdateInterface
class Document implements HasScopeInterface, TrackCreationInterface, TrackUpdateInterface
{
use TrackCreationTrait;
@@ -68,6 +70,13 @@ class Document implements TrackCreationInterface, TrackUpdateInterface
*/
private $object;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope")
*
* @var \Chill\MainBundle\Entity\Scope The document's center
*/
private $scope;
/**
* @ORM\ManyToOne(targetEntity="Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate")
*/
@@ -113,6 +122,16 @@ class Document implements TrackCreationInterface, TrackUpdateInterface
return $this->object;
}
/**
* Get scope.
*
* @return \Chill\MainBundle\Entity\Scope
*/
public function getScope(): ?Scope
{
return $this->scope;
}
public function getTemplate(): ?DocGeneratorTemplate
{
return $this->template;
@@ -156,6 +175,13 @@ class Document implements TrackCreationInterface, TrackUpdateInterface
return $this;
}
public function setScope($scope): self
{
$this->scope = $scope;
return $this;
}
public function setTemplate(?DocGeneratorTemplate $template): self
{
$this->template = $template;

View File

@@ -13,7 +13,6 @@ namespace Chill\DocStoreBundle\Entity;
use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasScopeInterface;
use Chill\MainBundle\Entity\Scope;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\Mapping as ORM;
@@ -28,13 +27,6 @@ class PersonDocument extends Document implements HasCenterInterface, HasScopeInt
*/
private Person $person;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope")
*
* @var \Chill\MainBundle\Entity\Scope The document's center
*/
private $scope;
public function getCenter()
{
return $this->getPerson()->getCenter();
@@ -45,22 +37,10 @@ class PersonDocument extends Document implements HasCenterInterface, HasScopeInt
return $this->person;
}
public function getScope(): ?Scope
{
return $this->scope;
}
public function setPerson($person): self
{
$this->person = $person;
return $this;
}
public function setScope($scope): self
{
$this->scope = $scope;
return $this;
}
}

View File

@@ -88,5 +88,10 @@ class AccompanyingCourseDocumentType extends AbstractType
$resolver->setDefaults([
'data_class' => Document::class,
]);
// $resolver->setRequired(['role', 'center'])
// ->setAllowedTypes('role', [ \Symfony\Component\Security\Core\Role\Role::class ])
// ->setAllowedTypes('center', [ \Chill\MainBundle\Entity\Center::class ])
// ;
}
}

View File

@@ -65,7 +65,7 @@ class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareReposito
$this->addACL($qb, $person);
foreach ($orderBy as $field => $order) {
$qb->addOrderBy('d.' . $field, $order);
$qb->addOrderBy($field, $order);
}
$qb->setFirstResult($offset)->setMaxResults($limit);

View File

@@ -106,10 +106,6 @@ class AccompanyingCourseDocumentVoter extends AbstractChillVoter implements Prov
) {
return false;
}
if (self::CREATE === $attribute && null !== $subject->getCourse()) {
return $this->voterHelper->voteOnAttribute($attribute, $subject->getCourse(), $token);
}
}
return $this->voterHelper->voteOnAttribute($attribute, $subject, $token);

View File

@@ -15,18 +15,12 @@ use Base64Url\Base64Url;
use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Exception\StoredObjectManagerException;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use RuntimeException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Throwable;
use function array_key_exists;
use const OPENSSL_RAW_DATA;
final class StoredObjectManager implements StoredObjectManagerInterface
@@ -35,8 +29,6 @@ final class StoredObjectManager implements StoredObjectManagerInterface
private HttpClientInterface $client;
private array $inMemory = [];
private TempUrlGeneratorInterface $tempUrlGenerator;
public function __construct(
@@ -47,35 +39,28 @@ final class StoredObjectManager implements StoredObjectManagerInterface
$this->tempUrlGenerator = $tempUrlGenerator;
}
public function getLastModified(StoredObject $document): DateTimeInterface
{
if ($this->hasCache($document)) {
$response = $this->getResponseFromCache($document);
} else {
try {
$response = $this
->client
->request(
Request::METHOD_HEAD,
$this
->tempUrlGenerator
->generate(
Request::METHOD_PUT,
$document->getFilename()
)
->url
);
} catch (TransportExceptionInterface $exception) {
throw StoredObjectManagerException::errorDuringHttpRequest($exception);
}
}
return $this->extractLastModifiedFromResponse($response);
}
public function read(StoredObject $document): string
{
$response = $this->getResponseFromCache($document);
try {
$response = $this
->client
->request(
Request::METHOD_GET,
$this
->tempUrlGenerator
->generate(
Request::METHOD_GET,
$document->getFilename()
)
->url
);
} catch (Throwable $e) {
throw StoredObjectManagerException::errorDuringHttpRequest($e);
}
if ($response->getStatusCode() !== Response::HTTP_OK) {
throw StoredObjectManagerException::invalidStatusCode($response->getStatusCode());
}
try {
$data = $response->getContent();
@@ -105,10 +90,6 @@ final class StoredObjectManager implements StoredObjectManagerInterface
public function write(StoredObject $document, string $clearContent): void
{
if ($this->hasCache($document)) {
unset($this->inMemory[$document->getUuid()->toString()]);
}
$encryptedContent = $this->hasKeysAndIv($document)
? openssl_encrypt(
$clearContent,
@@ -145,64 +126,6 @@ final class StoredObjectManager implements StoredObjectManagerInterface
}
}
private function extractLastModifiedFromResponse(ResponseInterface $response): DateTimeImmutable
{
$lastModifiedString = (($response->getHeaders()['last-modified'] ?? [])[0] ?? '');
$date = DateTimeImmutable::createFromFormat(
DateTimeImmutable::RFC7231,
$lastModifiedString,
new DateTimeZone('GMT')
);
if (false === $date) {
throw new RuntimeException('the date from remote storage could not be parsed: '
. $lastModifiedString);
}
return $date;
}
private function fillCache(StoredObject $document): void
{
try {
$response = $this
->client
->request(
Request::METHOD_GET,
$this
->tempUrlGenerator
->generate(
Request::METHOD_GET,
$document->getFilename()
)
->url
);
} catch (Throwable $e) {
throw StoredObjectManagerException::errorDuringHttpRequest($e);
}
if ($response->getStatusCode() !== Response::HTTP_OK) {
throw StoredObjectManagerException::invalidStatusCode($response->getStatusCode());
}
$this->inMemory[$document->getUuid()->toString()] = $response;
}
private function getResponseFromCache(StoredObject $document): ResponseInterface
{
if (!$this->hasCache($document)) {
$this->fillCache($document);
}
return $this->inMemory[$document->getUuid()->toString()];
}
private function hasCache(StoredObject $document): bool
{
return array_key_exists($document->getUuid()->toString(), $this->inMemory);
}
private function hasKeysAndIv(StoredObject $storedObject): bool
{
return ([] !== $storedObject->getKeyInfos()) && ([] !== $storedObject->getIv());

View File

@@ -12,12 +12,9 @@ declare(strict_types=1);
namespace Chill\DocStoreBundle\Service;
use Chill\DocStoreBundle\Entity\StoredObject;
use DateTimeInterface;
interface StoredObjectManagerInterface
{
public function getLastModified(StoredObject $document): DateTimeInterface;
/**
* Get the content of a StoredObject.
*

View File

@@ -12,7 +12,6 @@ declare(strict_types=1);
namespace Chill\EventBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
/**
* Class AdminController
@@ -21,12 +20,18 @@ use Symfony\Component\Routing\Annotation\Route;
class AdminController extends AbstractController
{
/**
* Event admin.
*
* @Route("/{_locale}/admin/event", name="chill_event_admin_index")
* @return \Symfony\Component\HttpFoundation\Response
*/
public function indexAdminAction()
public function indexAction()
{
return $this->render('ChillEventBundle:Admin:index.html.twig');
return $this->render('ChillEventBundle:Admin:layout.html.twig');
}
/**
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function redirectToAdminIndexAction()
{
return $this->redirectToRoute('chill_main_admin_central');
}
}

View File

@@ -273,7 +273,7 @@ class EventController extends AbstractController
/**
* @var Center $centers
*/
$centers = $this->authorizationHelper->getReachableCenters($this->getUser(), (string) $role);
$centers = $this->authorizationHelper->getReachableCenters($this->getUser(), $role);
if (count($centers) === 1) {
return $this->redirectToRoute('chill_event__event_new', [

View File

@@ -151,7 +151,7 @@ class PickEventType extends AbstractType
} else {
$centers = $this->authorizationHelper->getReachableCenters(
$this->user,
(string) $options['role']
$options['role']
);
}

View File

@@ -1,61 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\EventBundle\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
class AdminMenuBuilder implements LocalMenuBuilderInterface
{
/**
* @var AuthorizationCheckerInterface
*/
protected $authorizationChecker;
public function __construct(AuthorizationCheckerInterface $authorizationChecker)
{
$this->authorizationChecker = $authorizationChecker;
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) {
return;
}
$menu->addChild('Events', [
'route' => 'chill_event_admin_index',
])
->setAttribute('class', 'list-group-item-header')
->setExtras([
'order' => 6500,
]);
$menu->addChild('Event type', [
'route' => 'chill_eventtype_admin',
])->setExtras(['order' => 6510]);
$menu->addChild('Event status', [
'route' => 'chill_event_admin_status',
])->setExtras(['order' => 6520]);
$menu->addChild('Role', [
'route' => 'chill_event_admin_role',
])->setExtras(['order' => 6530]);
}
public static function getMenuIds(): array
{
return ['admin_section', 'admin_event'];
}
}

View File

@@ -1,13 +0,0 @@
{% extends "@ChillMain/Admin/layoutWithVerticalMenu.html.twig" %}
{% block vertical_menu_content %}
{{ chill_menu('admin_event', {
'layout': '@ChillMain/Admin/menu_admin_section.html.twig',
}) }}
{% endblock %}
{% block layout_wvm_content %}
{% block admin_content %}<!-- block content empty -->
<h1>{{ 'Events configuration' |trans }}</h1>
{% endblock %}
{% endblock %}

View File

@@ -0,0 +1,31 @@
{#
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
<info@champs-libres.coop> / <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% extends "@ChillMain/Admin/layoutWithVerticalMenu.html.twig" %}
{% block vertical_menu_content %}
{{ chill_menu('admin_events', {
'layout': '@ChillEvent/Admin/menu.html.twig',
}) }}
{% endblock %}
{% block layout_wvm_content %}
{% block admin_content %}<!-- block content empty -->
<h1>{{ 'Events configuration' |trans }}</h1>
{% endblock %}
{% endblock %}

View File

@@ -1,3 +1,21 @@
{#
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
<info@champs-libres.coop> / <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% extends "@ChillMain/Menu/verticalMenu.html.twig" %}
{% block v_menu_title %}{{ 'Events configuration menu'|trans }}{% endblock %}

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