mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-20 22:53:49 +00:00
Merge remote-tracking branch 'origin/master' into issue133_user_current_location
This commit is contained in:
25
CHANGELOG.md
25
CHANGELOG.md
@@ -14,6 +14,28 @@ and this project adheres to
|
|||||||
|
|
||||||
* [main] Add currentLocation to the User entity + add a page for selecting this location + add in the user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/133)
|
* [main] Add currentLocation to the User entity + add a page for selecting this location + add in the user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/133)
|
||||||
* [activity] add user current location as default location for a new activity (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/133)
|
* [activity] add user current location as default location for a new activity (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/133)
|
||||||
|
* [task] Select2 field in task form to allow search for a user (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/167)
|
||||||
|
* remove "search by phone configuration option": search by phone is now executed by default
|
||||||
|
* remplacer le classement par ordre alphabétique par un classement par ordre de pertinence, qui tient compte:
|
||||||
|
* de la présence d'une string avec le nom de la ville;
|
||||||
|
* de la similarité;
|
||||||
|
* du fait que la recherche commence par une partie du mot recherché
|
||||||
|
* ajouter la recherche par numéro de téléphone directement dans la barre de recherche et dans le formulaire recherche avancée;
|
||||||
|
* ajouter la recherche par date de naissance directement dans la barre de recherche;
|
||||||
|
* ajouter la recherche par ville dans la recherche avancée
|
||||||
|
* ajouter un lien vers le ménage dans les résultats de recherche
|
||||||
|
* ajouter l'id du parcours dans les résultats de recherche
|
||||||
|
* ajouter le demandeur dans les résultats de recherche
|
||||||
|
* ajout d'un bouton "recherche avancée" sur la page d'accueil
|
||||||
|
* [person] create an accompanying course: add client-side validation if no origin (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/210)
|
||||||
|
* [person] fix bounds for computing current person address: the new address appears immediatly
|
||||||
|
* [docgen] create a normalizer and serializer for normalization on doc format
|
||||||
|
* [person normalization] the key center is now "centers" and is an array. Empty array if no center
|
||||||
|
|
||||||
|
|
||||||
|
## Test releases
|
||||||
|
|
||||||
|
### Test release 2021-11-15
|
||||||
|
|
||||||
* [main] fix adding multiple AddresseDeRelais (combine PickAddressType with ChillCollection)
|
* [main] fix adding multiple AddresseDeRelais (combine PickAddressType with ChillCollection)
|
||||||
* [person]: do not suggest the current household of the person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/51)
|
* [person]: do not suggest the current household of the person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/51)
|
||||||
@@ -29,9 +51,6 @@ and this project adheres to
|
|||||||
* [person] do not ask for center any more on person creation
|
* [person] do not ask for center any more on person creation
|
||||||
* [3party] do not ask for center any more on 3party creation
|
* [3party] do not ask for center any more on 3party creation
|
||||||
|
|
||||||
|
|
||||||
## Test releases
|
|
||||||
|
|
||||||
### Test release 2021-11-08
|
### Test release 2021-11-08
|
||||||
|
|
||||||
* [person]: Display the name of a user when searching after a User (TMS)
|
* [person]: Display the name of a user when searching after a User (TMS)
|
||||||
|
@@ -91,7 +91,8 @@
|
|||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"App\\": "tests/app/src/"
|
"App\\": "tests/app/src/",
|
||||||
|
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
|
@@ -23,15 +23,196 @@ Every time an entity is created, viewed or updated, the software check if the us
|
|||||||
|
|
||||||
The user must be granted access to the action on this particular entity, with this scope and center.
|
The user must be granted access to the action on this particular entity, with this scope and center.
|
||||||
|
|
||||||
|
TL;DR
|
||||||
|
=====
|
||||||
|
|
||||||
|
Resolve scope and center
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
In a service, resolve the center and scope of an entity
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||||
|
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
||||||
|
|
||||||
|
|
||||||
|
class MyService {
|
||||||
|
private ScopeResolverDispatcher $scopeResolverDispatcher;
|
||||||
|
private CenterResolverDispatcher $centerResolverDispatcher;
|
||||||
|
|
||||||
|
public function myFunction($entity) {
|
||||||
|
/** @var null|Center[]|Center $center */
|
||||||
|
$center = $this->centerResolverDispatcher->resolveCenter($entity);
|
||||||
|
// $center may be null, an array of center, or an instance of Center
|
||||||
|
|
||||||
|
if ($this->scopeResolverDispatcher->isConcerned($entity) {
|
||||||
|
/** @var null|Scope[]|Scope */
|
||||||
|
$scope = $this-scopeResolverDispatcher->resolveScope($entity);
|
||||||
|
// $scope may be null, an array of Scope, or an instance of Scope
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
In twig template, resolve the center:
|
||||||
|
|
||||||
|
.. code-block:: twig
|
||||||
|
|
||||||
|
{# resolve a center #}
|
||||||
|
|
||||||
|
{% if person|chill_resolve_center is not null%}
|
||||||
|
|
||||||
|
{% if person|chill_resolve_center is iterable %}
|
||||||
|
{% set centers = person|chill_resolve_center %}
|
||||||
|
{% else %}
|
||||||
|
{% set centers = [ person|chill_resolve_center ] %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<span class="open_sansbold">
|
||||||
|
{{ 'Center'|trans|upper}} :
|
||||||
|
</span>
|
||||||
|
{% for c in centers %}
|
||||||
|
{{ c.name|upper }}
|
||||||
|
{% if not loop.last %}, {% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
In twig template, resolve the scope:
|
||||||
|
|
||||||
|
.. code-block:: twig
|
||||||
|
|
||||||
|
{% if entity|chill_is_scope_concerned %}
|
||||||
|
|
||||||
|
{% if entity|chill_resolve_scope is iterable %}
|
||||||
|
{% set scopes = entity|chill_resolve_scope %}
|
||||||
|
{% else %}
|
||||||
|
{% set scopes = [ entity|chill_resolve_scope ] %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<span>Scopes :</span>
|
||||||
|
{% for s in scopes %}
|
||||||
|
{{ c.name|localize_translatable_string }}
|
||||||
|
{% if not loop.last %}, {% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
Build a ``Voter``
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Security\Authorization;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||||
|
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||||
|
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
||||||
|
{
|
||||||
|
// roles should be stored into constants:
|
||||||
|
|
||||||
|
const CREATE = 'CHILL_PERSON_DOCUMENT_CREATE';
|
||||||
|
const SEE = 'CHILL_PERSON_DOCUMENT_SEE';
|
||||||
|
const SEE_DETAILS = 'CHILL_PERSON_DOCUMENT_SEE_DETAILS';
|
||||||
|
const UPDATE = 'CHILL_PERSON_DOCUMENT_UPDATE';
|
||||||
|
const DELETE = 'CHILL_PERSON_DOCUMENT_DELETE';
|
||||||
|
|
||||||
|
protected Security $security;
|
||||||
|
protected VoterHelperInterface $voterHelper;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
Security $security,
|
||||||
|
VoterHelperFactoryInterface $voterHelperFactory
|
||||||
|
) {
|
||||||
|
$this->security = $security;
|
||||||
|
|
||||||
|
// we build here a voter helper. This will ease the operations below.
|
||||||
|
// when the authorization model is changed, it will be easy to make a different implementation
|
||||||
|
// of the helper, instead of writing all Voters
|
||||||
|
|
||||||
|
$this->voterHelper = $voterHelperFactory
|
||||||
|
// create a builder with some context
|
||||||
|
->generate(self::class)
|
||||||
|
// add the support of given roles for given class:
|
||||||
|
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||||
|
->addCheckFor(PersonDocument::class, $this->getRoles())
|
||||||
|
->build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function supports($attribute, $subject)
|
||||||
|
{
|
||||||
|
return $this->voterHelper->supports($attribute, $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
||||||
|
{
|
||||||
|
// basic check
|
||||||
|
if (!$token->getUser() instanceof User) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we first check the acl for associated elements.
|
||||||
|
// here, we must be able to see the person associated to the document:
|
||||||
|
if ($subject instanceof PersonDocument
|
||||||
|
&& !$this->security->isGranted(PersonVoter::SEE, $subject->getPerson())) {
|
||||||
|
|
||||||
|
// not possible to see the associated person ? Then, not possible to see the document!
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the voter helper will implements the logic:
|
||||||
|
return $this->voterHelper->voteOnAttribute($attribute, $subject, $token);
|
||||||
|
}
|
||||||
|
|
||||||
|
// all the method below are used to register roles into the admin part
|
||||||
|
public function getRoles()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::CREATE,
|
||||||
|
self::SEE,
|
||||||
|
self::SEE_DETAILS,
|
||||||
|
self::UPDATE,
|
||||||
|
self::DELETE
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRolesWithoutScope()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getRolesWithHierarchy()
|
||||||
|
{
|
||||||
|
return ['PersonDocument' => $this->getRoles() ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
From an user point of view
|
From an user point of view
|
||||||
--------------------------
|
==========================
|
||||||
|
|
||||||
The software is design to allow fine tuned access rights for complicated installation and team structure. The administrators may also decide that every user has the right to see all resources, where team have a more simple structure.
|
The software is design to allow fine tuned access rights for complicated installation and team structure. The administrators may also decide that every user has the right to see all resources, where team have a more simple structure.
|
||||||
|
|
||||||
Here is an overview of the model.
|
Here is an overview of the model.
|
||||||
|
|
||||||
Chill can be multi-center
|
Chill can be multi-center
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
-------------------------
|
||||||
|
|
||||||
Chill is designed to be installed once for social center who work with multiple teams separated, or for social services's federation who would like to share the same installation of the software for all their members.
|
Chill is designed to be installed once for social center who work with multiple teams separated, or for social services's federation who would like to share the same installation of the software for all their members.
|
||||||
|
|
||||||
@@ -42,7 +223,7 @@ Otherwise, it is not required to create multiple center: Chill can also work for
|
|||||||
Obviously, users working in the different centers are not allowed to see the entities (_persons_, _reports_, _activities_) of other centers. But users may be attached to multiple centers: consequently they will be able to see the entities of the multiple centers they are attached to.
|
Obviously, users working in the different centers are not allowed to see the entities (_persons_, _reports_, _activities_) of other centers. But users may be attached to multiple centers: consequently they will be able to see the entities of the multiple centers they are attached to.
|
||||||
|
|
||||||
Inside center, scope divide team
|
Inside center, scope divide team
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
--------------------------------
|
||||||
|
|
||||||
Users are attached to one or more center and, inside to those center, there may exists differents scopes. The aim of those _scopes_ is to divide the whole team of social worker amongst different departement, for instance: the social team, the psychologist team, the nurse team, the administrative team, ... Each team is granted of different rights amongst scope. For instance, the social team may not see the _activities_ of the psychologist team. The administrative team may see the date & time's activities, but is not allowed to see the detail of those entities (the personal notes, ...).
|
Users are attached to one or more center and, inside to those center, there may exists differents scopes. The aim of those _scopes_ is to divide the whole team of social worker amongst different departement, for instance: the social team, the psychologist team, the nurse team, the administrative team, ... Each team is granted of different rights amongst scope. For instance, the social team may not see the _activities_ of the psychologist team. The administrative team may see the date & time's activities, but is not allowed to see the detail of those entities (the personal notes, ...).
|
||||||
|
|
||||||
@@ -52,8 +233,38 @@ As entities have only one scopes, if some entities must be shared across two dif
|
|||||||
|
|
||||||
Example: if some activities must be seen and updated between nurses and psychologists, the administrator will create a scope "nurse and psy" and add the ability for both team "nurse" and "psychologist" to "create", "see", and "update" the activities belonging to scope "nurse and psy".
|
Example: if some activities must be seen and updated between nurses and psychologists, the administrator will create a scope "nurse and psy" and add the ability for both team "nurse" and "psychologist" to "create", "see", and "update" the activities belonging to scope "nurse and psy".
|
||||||
|
|
||||||
|
|
||||||
|
Where does the ``scope`` and ``center`` comes from ?
|
||||||
|
====================================================
|
||||||
|
|
||||||
|
Most often, scope and center comes from user's input:
|
||||||
|
|
||||||
|
* when person is created, Chill asks the associated center to the user. Then, every entity associated to the user (Activity, ...) is associated to this center;
|
||||||
|
* when an entity is created, Chill asks the associated scope.
|
||||||
|
|
||||||
|
The UI check the model before adding those input into form. If the user hae access to only one center or scope, this scope or center is filled automatically, and the UI does not ask the user. Most of the times, the user does not see "Pick a scope" and "Pick a center" inputs.
|
||||||
|
|
||||||
|
Scope and Center are associated to entities through ``ManyToOne`` properties, which are then mapped to ``FOREIGN KEY`` in tables, ...
|
||||||
|
|
||||||
|
But sometimes, this implementation does not fits the needs:
|
||||||
|
|
||||||
|
* persons are associated to center *geographically*: the address of each person contains lat/lon coordinates, and the center is resolved from this coordinated;
|
||||||
|
* some would like to associated persons to multiple center, or one center;
|
||||||
|
* entities are associated to scope through the job reached by "creator" (an user);
|
||||||
|
* some would like not to use scope at all;
|
||||||
|
* …
|
||||||
|
|
||||||
|
For this reasons, associated center and scopes must be resolved programmatically. The default implementation rely on the model association, as described above. But it becomes possible to change the behaviour on different implementations.
|
||||||
|
|
||||||
|
Is my entity "concerned" by scopes ?
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
Some entities are concerned by scope, some not.
|
||||||
|
|
||||||
|
This is also programmatically resolved.
|
||||||
|
|
||||||
The concepts translated into code
|
The concepts translated into code
|
||||||
-----------------------------------
|
===================================
|
||||||
|
|
||||||
.. figure:: /_static/access_control_model.png
|
.. figure:: /_static/access_control_model.png
|
||||||
|
|
||||||
@@ -81,7 +292,7 @@ At each step of his lifetime (creation, view of the entity and eventually of his
|
|||||||
All those action are executed through symfony voters and helpers.
|
All those action are executed through symfony voters and helpers.
|
||||||
|
|
||||||
How to check authorization ?
|
How to check authorization ?
|
||||||
----------------------------
|
============================
|
||||||
|
|
||||||
Just use the symfony way-of-doing, but do not forget to associate the entity you want to check access. For instance, in controller :
|
Just use the symfony way-of-doing, but do not forget to associate the entity you want to check access. For instance, in controller :
|
||||||
|
|
||||||
@@ -100,34 +311,23 @@ Just use the symfony way-of-doing, but do not forget to associate the entity you
|
|||||||
|
|
||||||
And in template :
|
And in template :
|
||||||
|
|
||||||
.. code-block:: html+jinja
|
.. code-block:: twig
|
||||||
|
|
||||||
{{ if is_granted('CHILL_ENTITY_SEE', entity) %}print something{% endif %}
|
{{ if is_granted('CHILL_ENTITY_SEE', entity) %}print something{% endif %}
|
||||||
|
|
||||||
Retrieving reachable scopes and centers
|
Retrieving reachable scopes and centers for a user
|
||||||
----------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
The class :class:`Chill\\MainBundle\\Security\\Authorization\\AuthorizationHelper` helps you to get centers and scope reachable by a user.
|
The class :class:`Chill\\MainBundle\\Security\\Authorization\\AuthorizationHelperInterface` helps you to get centers and scope reachable by a user.
|
||||||
|
|
||||||
Those methods are intentionnaly build to give information about user rights:
|
Those methods are intentionnaly build to give information about user rights:
|
||||||
|
|
||||||
- getReachableCenters: to get reachable centers for a user
|
- getReachableCenters: to get reachable centers for a user
|
||||||
- getReachableScopes : to get reachable scopes for a user
|
- getReachableScopes : to get reachable scopes for a user
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The service is reachable through the Depedency injection with the key `chill.main.security.authorization.helper`. Example :
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
$helper = $container->get('chill.main.security.authorization.helper');
|
|
||||||
|
|
||||||
.. todo::
|
|
||||||
|
|
||||||
Waiting for a link between our api and this doc, we invite you to read the method signatures `here <https://github.com/Chill-project/Main/blob/add_acl/Security/Authorization/AuthorizationHelper.php>`_
|
|
||||||
|
|
||||||
Adding your own roles
|
Adding your own roles
|
||||||
=====================
|
---------------------
|
||||||
|
|
||||||
Extending Chill will requires you to define your own roles and rules for your entities. You will have to define your own voter to do so.
|
Extending Chill will requires you to define your own roles and rules for your entities. You will have to define your own voter to do so.
|
||||||
|
|
||||||
@@ -152,7 +352,7 @@ To create your own roles, you should:
|
|||||||
|
|
||||||
|
|
||||||
Declare your role
|
Declare your role
|
||||||
------------------
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
To declare new role, implement the class :class:`Chill\\MainBundle\\Security\\ProvideRoleInterface`.
|
To declare new role, implement the class :class:`Chill\\MainBundle\\Security\\ProvideRoleInterface`.
|
||||||
|
|
||||||
@@ -212,69 +412,8 @@ Example of an implementation of :class:`Chill\\MainBundle\\Security\\ProvideRole
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Implement your voter
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Inside this class, you might use the :class:`Chill\\MainBundle\\Security\\Authorization\\AuthorizationHelper` to check permission (do not re-invent the wheel). This is a real-world example:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
namespace Chill\ReportBundle\Security\Authorization;
|
|
||||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
|
||||||
|
|
||||||
|
|
||||||
class ReportVoter extends AbstractChillVoter
|
|
||||||
{
|
|
||||||
const CREATE = 'CHILL_REPORT_CREATE';
|
|
||||||
const SEE = 'CHILL_REPORT_SEE';
|
|
||||||
const UPDATE = 'CHILL_REPORT_UPDATE';
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @var AuthorizationHelper
|
|
||||||
*/
|
|
||||||
protected $helper;
|
|
||||||
|
|
||||||
public function __construct(AuthorizationHelper $helper)
|
|
||||||
{
|
|
||||||
$this->helper = $helper;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getSupportedAttributes()
|
|
||||||
{
|
|
||||||
return array(self::CREATE, self::SEE, self::UPDATE);
|
|
||||||
}
|
|
||||||
protected function getSupportedClasses()
|
|
||||||
{
|
|
||||||
return array('Chill\ReportBundle\Entity\Report');
|
|
||||||
}
|
|
||||||
protected function isGranted($attribute, $report, $user = null)
|
|
||||||
{
|
|
||||||
if (! $user instanceof \Chill\MainBundle\Entity\User){
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->helper->userHasAccess($user, $report, $attribute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Then, you will have to declare the service and tag it as a voter :
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
services:
|
|
||||||
chill.report.security.authorization.report_voter:
|
|
||||||
class: Chill\ReportBundle\Security\Authorization\ReportVoter
|
|
||||||
arguments:
|
|
||||||
- "@chill.main.security.authorization.helper"
|
|
||||||
tags:
|
|
||||||
- { name: security.voter }
|
|
||||||
|
|
||||||
|
|
||||||
Adding role hierarchy
|
Adding role hierarchy
|
||||||
---------------------
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
You should prepend Symfony's security component directly from your code.
|
You should prepend Symfony's security component directly from your code.
|
||||||
|
|
||||||
@@ -312,3 +451,484 @@ You should prepend Symfony's security component directly from your code.
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Implement your voter
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Most of the time, Voter will check that:
|
||||||
|
|
||||||
|
1. The given role is reachable (= ``$attribute``)
|
||||||
|
2. for the given center,
|
||||||
|
3. and, if any, for the given role
|
||||||
|
4. if the entity is associated to another entity, this entity should be, at least, viewable by the user.
|
||||||
|
|
||||||
|
Thats what we call the "autorization logic". But this logic may be replace by a new one, and developers should take care of it.
|
||||||
|
|
||||||
|
|
||||||
|
Then voter implementation should take care of:
|
||||||
|
|
||||||
|
* check the access to associated entities. For instance, if an ``Activity`` is associated to a ``Person``, the voter should first check that the user can show the associated ``Person``;
|
||||||
|
* as far as possible, delegates the check for associated center, scopes, and check for authorization using the authorization logic. VoterHelper will ease the most common operation of this logic.
|
||||||
|
|
||||||
|
This is an example of implementation:
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Security\Authorization;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||||
|
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||||
|
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
||||||
|
{
|
||||||
|
protected Security $security;
|
||||||
|
protected VoterHelperInterface $voterHelper;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
Security $security,
|
||||||
|
VoterHelperFactoryInterface $voterHelperFactory
|
||||||
|
) {
|
||||||
|
$this->security = $security;
|
||||||
|
|
||||||
|
// we build here a voter helper. This will ease the operations below.
|
||||||
|
// when the authorization model is changed, it will be easy to make a different implementation
|
||||||
|
// of the helper, instead of writing all Voters
|
||||||
|
|
||||||
|
$this->voterHelper = $voterHelperFactory
|
||||||
|
// create a builder with some context
|
||||||
|
->generate(self::class)
|
||||||
|
// add the support of given roles for given class:
|
||||||
|
->addCheckFor(Person::class, [self::SEE, self::CREATE])
|
||||||
|
->addCheckFor(PersonDocument::class, $this->getRoles())
|
||||||
|
->build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function supports($attribute, $subject)
|
||||||
|
{
|
||||||
|
return $this->voterHelper->supports($attribute, $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
||||||
|
{
|
||||||
|
// basic check
|
||||||
|
if (!$token->getUser() instanceof User) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we first check the acl for associated elements.
|
||||||
|
// here, we must be able to see the person associated to the document:
|
||||||
|
if ($subject instanceof PersonDocument
|
||||||
|
&& !$this->security->isGranted(PersonVoter::SEE, $subject->getPerson())) {
|
||||||
|
|
||||||
|
// not possible to see the associated person ? Then, not possible to see the document!
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the voter helper will implements the logic of checking:
|
||||||
|
// 1. that the center is reachable
|
||||||
|
// 2. for this given entity
|
||||||
|
// 3. for this given scope
|
||||||
|
// 4. and for the given role
|
||||||
|
return $this->voterHelper->voteOnAttribute($attribute, $subject, $token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRoles()
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRolesWithoutScope()
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getRolesWithHierarchy()
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Then, you will have to declare the service and tag it as a voter :
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
services:
|
||||||
|
chill.report.security.authorization.report_voter:
|
||||||
|
class: Chill\ReportBundle\Security\Authorization\ReportVoter
|
||||||
|
arguments:
|
||||||
|
- "@chill.main.security.authorization.helper"
|
||||||
|
tags:
|
||||||
|
- { name: security.voter }
|
||||||
|
|
||||||
|
|
||||||
|
How to resolve scope and center programmatically ?
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
In a service, resolve the center and scope of an entity
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||||
|
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
||||||
|
|
||||||
|
|
||||||
|
class MyService {
|
||||||
|
private ScopeResolverDispatcher $scopeResolverDispatcher;
|
||||||
|
private CenterResolverDispatcher $centerResolverDispatcher;
|
||||||
|
|
||||||
|
public function myFunction($entity) {
|
||||||
|
/** @var null|Center[]|Center $center */
|
||||||
|
$center = $this->centerResolverDispatcher->resolveCenter($entity);
|
||||||
|
// $center may be null, an array of center, or an instance of Center
|
||||||
|
|
||||||
|
if ($this->scopeResolverDispatcher->isConcerned($entity) {
|
||||||
|
/** @var null|Scope[]|Scope */
|
||||||
|
$scope = $this-scopeResolverDispatcher->resolveScope($entity);
|
||||||
|
// $scope may be null, an array of Scope, or an instance of Scope
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
In twig template, resolve the center:
|
||||||
|
|
||||||
|
.. code-block:: twig
|
||||||
|
|
||||||
|
{# resolve a center #}
|
||||||
|
|
||||||
|
{% if person|chill_resolve_center is not null%}
|
||||||
|
|
||||||
|
{% if person|chill_resolve_center is iterable %}
|
||||||
|
{% set centers = person|chill_resolve_center %}
|
||||||
|
{% else %}
|
||||||
|
{% set centers = [ person|chill_resolve_center ] %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<span class="open_sansbold">
|
||||||
|
{{ 'Center'|trans|upper}} :
|
||||||
|
</span>
|
||||||
|
{% for c in centers %}
|
||||||
|
{{ c.name|upper }}
|
||||||
|
{% if not loop.last %}, {% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
In twig template, resolve the scope:
|
||||||
|
|
||||||
|
.. code-block:: twig
|
||||||
|
|
||||||
|
{% if entity|chill_is_scope_concerned %}
|
||||||
|
|
||||||
|
{% if entity|chill_resolve_scope is iterable %}
|
||||||
|
{% set scopes = entity|chill_resolve_scope %}
|
||||||
|
{% else %}
|
||||||
|
{% set scopes = [ entity|chill_resolve_scope ] %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<span>Scopes :</span>
|
||||||
|
{% for s in scopes %}
|
||||||
|
{{ c.name|localize_translatable_string }}
|
||||||
|
{% if not loop.last %}, {% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
What is the default implementation of Scope and Center resolver ?
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
By default, the implementation rely on association into entities.
|
||||||
|
|
||||||
|
* implements ``Chill\MainBundle\Entity\HasCenterInterface`` on entities which have one or any center;
|
||||||
|
* implements ``Chill\MainBundle\Entity\HasCentersInterface`` on entities which have one, multiple or any centers;
|
||||||
|
* implements ``Chill\MainBundle\Entity\HasScopeInterface`` on entities which have one or any scope;
|
||||||
|
* implements ``Chill\MainBundle\Entity\HasScopesInterface`` on entities which have one or any scopes;
|
||||||
|
|
||||||
|
Then, the default implementation will resolve the center and scope based on the implementation in your model.
|
||||||
|
|
||||||
|
How to change the default behaviour ?
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
Implements those interface into services:
|
||||||
|
|
||||||
|
* ``Chill\MainBundle\Security\Resolver\CenterResolverInterface``;
|
||||||
|
* ``Chill\MainBundle\Security\Resolver\ScopeResolverInterface``;
|
||||||
|
|
||||||
|
Authorization into lists and index pages
|
||||||
|
========================================
|
||||||
|
|
||||||
|
Due to the fact that authorization model may be overriden, "list" and "index" pages should not rely on center and scope from controller. This must be delegated to dedicated service, which will be aware of the authorization model. We call them ``ACLAwareRepository``. This service must implements an interface, in order to allow to change the implementation.
|
||||||
|
|
||||||
|
The controller **must not** performs any DQL or SQL query.
|
||||||
|
|
||||||
|
Example in a controller:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
namespace Chill\TaskBundle\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface;
|
||||||
|
|
||||||
|
|
||||||
|
final class SingleTaskController extends AbstractController
|
||||||
|
{
|
||||||
|
|
||||||
|
private SingleTaskAclAwareRepositoryInterface $singleTaskAclAwareRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @Route(
|
||||||
|
* "/{_locale}/task/single-task/list",
|
||||||
|
* name="chill_task_singletask_list"
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function listAction(
|
||||||
|
Request $request
|
||||||
|
) {
|
||||||
|
$this->denyAccessUnlessGranted(TaskVoter::SHOW, null);
|
||||||
|
|
||||||
|
$nb = $this->singleTaskAclAwareRepository->countByAllViewable(
|
||||||
|
'', // search pattern
|
||||||
|
[] // search flags
|
||||||
|
);
|
||||||
|
$paginator = $this->paginatorFactory->create($nb);
|
||||||
|
|
||||||
|
if (0 < $nb) {
|
||||||
|
$tasks = $this->singleTaskAclAwareRepository->findByAllViewable(
|
||||||
|
'', // search pattern
|
||||||
|
[] // search flags
|
||||||
|
$paginator->getCurrentPageFirstItemNumber(),
|
||||||
|
$paginator->getItemsPerPage(),
|
||||||
|
// ordering:
|
||||||
|
[
|
||||||
|
'startDate' => 'DESC',
|
||||||
|
'endDate' => 'DESC',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$tasks = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('@ChillTask/SingleTask/List/index.html.twig', [
|
||||||
|
'tasks' => $tasks,
|
||||||
|
'paginator' => $paginator,
|
||||||
|
'filter_order' => $filterOrder
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Writing ``ACLAwareRepository``
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
The ACLAwareRepository should rely on interfaces
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
As described above, the ACLAwareRepository will perform the query for listing entities, and take care of authorization.
|
||||||
|
|
||||||
|
Those "ACLAwareRepositories" must be described into ``interfaces``.
|
||||||
|
|
||||||
|
The service must rely on this interface, and not on the default implementation.
|
||||||
|
|
||||||
|
Example: at first, we design an interface for listing ``SingleTask`` entities:
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\TaskBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
|
||||||
|
interface SingleTaskAclAwareRepositoryInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return SingleTask[]|array
|
||||||
|
*/
|
||||||
|
public function findByCurrentUsersTasks(?string $pattern = null, ?array $flags = [], ?int $start = 0, ?int $limit = 50, ?array $orderBy = []): array;
|
||||||
|
|
||||||
|
public function countByCurrentUsersTasks(?string $pattern = null, ?array $flags = []): int;
|
||||||
|
|
||||||
|
public function countByAllViewable(
|
||||||
|
?string $pattern = null,
|
||||||
|
?array $flags = []
|
||||||
|
): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return SingleTask[]|array
|
||||||
|
*/
|
||||||
|
public function findByAllViewable(
|
||||||
|
?string $pattern = null,
|
||||||
|
?array $flags = [],
|
||||||
|
?int $start = 0,
|
||||||
|
?int $limit = 50,
|
||||||
|
?array $orderBy = []
|
||||||
|
): array;
|
||||||
|
}
|
||||||
|
|
||||||
|
Implements this interface and register the interface as an alias for the implementation.
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
services:
|
||||||
|
Chill\TaskBundle\Repository\SingleTaskAclAwareRepository:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
|
Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface: '@Chill\TaskBundle\Repository\SingleTaskAclAwareRepository'
|
||||||
|
|
||||||
|
Write the basic implementation for re-use: separate authorization logic and search logic
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The logic of such repository may be separated into two logic:
|
||||||
|
|
||||||
|
* the authorization logic (show only entities that the user is allowed to see);
|
||||||
|
* the search logic (filter entities on some criterias).
|
||||||
|
|
||||||
|
This logic should be separated into your implementation.
|
||||||
|
|
||||||
|
Considering this simple interface:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
interface MyEntityACLAwareRepositoryInterface {
|
||||||
|
|
||||||
|
public function countByAuthorized(array $criterias): int;
|
||||||
|
|
||||||
|
public function findByAuthorized(array $criteria, int $start, int $limit, array $orderBy): array;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
The base implementation should separate the logic to allow an easy reuse. Here, the method ``buildQuery`` build a basic query without authorization logic, which can be re-used. The authorization logic is dedicated to a private method. For ease of user, the logic of adding ordering criterias and pagination parameters (``$start`` and ``$limit``) are also delegated to a public method.
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
namespace Chill\MyBundle\Repository;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
|
||||||
|
|
||||||
|
final class MyEntityACLAwareRepository implements MyEntityACLAwareRepositoryInterface {
|
||||||
|
|
||||||
|
private EntityManagerInterface $em;
|
||||||
|
|
||||||
|
// constructor omitted
|
||||||
|
|
||||||
|
public function countByAuthorized(array $criterias): int
|
||||||
|
{
|
||||||
|
$qb = $this->buildQuery($criterias);
|
||||||
|
|
||||||
|
return $this->addAuthorizations($qb)->select("COUNT(e)")->getQuery()->getResult()->getSingleScalarResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByAuthorized(array $criteria, int $start, int $limit, array $orderBy): array
|
||||||
|
{
|
||||||
|
$qb = $this->buildQuery($criterias);
|
||||||
|
|
||||||
|
return $this->getResult($this->addAuthorizations($qb), $start, $limit, $orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult(QueryBuilder $qb, int $start, int $limit, array $orderBy): array
|
||||||
|
{
|
||||||
|
$qb
|
||||||
|
->setFirstResult($start)
|
||||||
|
->setMaxResults($limit)
|
||||||
|
;
|
||||||
|
|
||||||
|
// add order by logic
|
||||||
|
|
||||||
|
return $qb->getQuery()->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildQuery(array $criterias): QueryBuilder
|
||||||
|
{
|
||||||
|
$qb = $this->em->createQueryBuilder();
|
||||||
|
|
||||||
|
// implement you logic with search criteria here
|
||||||
|
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addAuthorizations(QueryBuilder $qb): QueryBuilder
|
||||||
|
{
|
||||||
|
// add authorization logic here
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Once this logic is executed, it becomes easy to make a new implementation of the repository:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
namespace Chill\MyOtherBundle\Repository;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Chill\MyBundle\Repository\MyEntityACLAwareRepository
|
||||||
|
|
||||||
|
|
||||||
|
final class AnotherEntityACLAwareRepository implements MyEntityACLAwareRepositoryInterface {
|
||||||
|
|
||||||
|
private EntityManagerInterface $em;
|
||||||
|
private \Chill\MyBundle\Repository\MyEntityACLAwareRepository $initial;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
EntityManagerInterface $em,
|
||||||
|
\Chill\MyBundle\Repository\MyEntityACLAwareRepository $initial
|
||||||
|
) {
|
||||||
|
$this->em = $em;
|
||||||
|
$this->initial = $initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countByAuthorized(array $criterias): int
|
||||||
|
{
|
||||||
|
$qb = $this->initial->buildQuery($criterias);
|
||||||
|
|
||||||
|
return $this->addAuthorizations($qb)->select("COUNT(e)")->getQuery()->getResult()->getSingleScalarResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByAuthorized(array $criteria, int $start, int $limit, array $orderBy): array
|
||||||
|
{
|
||||||
|
$qb = $this->initial->buildQuery($criterias);
|
||||||
|
|
||||||
|
return $this->initial->getResult($this->addAuthorizations($qb), $start, $limit, $orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addAuthorizations(QueryBuilder $qb): QueryBuilder
|
||||||
|
{
|
||||||
|
// add a different authorization logic here
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Then, register this service and decorates the old one:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
services:
|
||||||
|
Chill\MyOtherBundle\Repository\AnotherEntityACLAwareRepository:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
decorates: Chill\MyBundle\Repository\MyEntityACLAwareRepositoryInterface:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -90,51 +90,21 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillTaskBundle/Controller/TaskController.php
|
path: src/Bundle/ChillTaskBundle/Controller/TaskController.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Undefined variable\\: \\$id$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillFamilyMembersBundle/Controller/FamilyMemberController.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\AbstractCRUDController\\:\\:getRoleFor\\(\\)\\.$#"
|
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\AbstractCRUDController\\:\\:getRoleFor\\(\\)\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php
|
path: src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Command\\\\ChillImportUsersCommand\\:\\:tempOutput\\(\\)\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\MainBundle\\\\Controller\\\\AdminCountryCRUDController\\:\\:\\$paginatorFactory\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillMainBundle/Controller/AdminCountryCRUDController.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Controller\\\\UserController\\:\\:createEditForm\\(\\)\\.$#"
|
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Controller\\\\UserController\\:\\:createEditForm\\(\\)\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillMainBundle/Controller/UserController.php
|
path: src/Bundle/ChillMainBundle/Controller/UserController.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\MainBundle\\\\Entity\\\\RoleScope\\:\\:\\$new\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillMainBundle/Entity/RoleScope.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Undefined variable\\: \\$current$#"
|
message: "#^Undefined variable\\: \\$current$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillMainBundle/Pagination/PageGenerator.php
|
path: src/Bundle/ChillMainBundle/Pagination/PageGenerator.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\MainBundle\\\\Routing\\\\MenuComposer\\:\\:\\$routeCollection\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillMainBundle/Routing/MenuComposer.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\MainBundle\\\\Search\\\\SearchApiResult\\:\\:\\$relevance\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: src/Bundle/ChillMainBundle/Search/SearchApiResult.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AbstractChillVoter\\:\\:getSupportedAttributes\\(\\)\\.$#"
|
message: "#^Call to an undefined method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AbstractChillVoter\\:\\:getSupportedAttributes\\(\\)\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
@@ -155,53 +125,7 @@ parameters:
|
|||||||
count: 3
|
count: 3
|
||||||
path: src/Bundle/ChillPersonBundle/Controller/PersonController.php
|
path: src/Bundle/ChillPersonBundle/Controller/PersonController.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Controller\\\\TimelinePersonController\\:\\:\\$authorizationHelper\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillPersonBundle/Controller/TimelinePersonController.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\DataFixtures\\\\ORM\\\\LoadHousehold\\:\\:\\$personIds\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Form\\\\CreationPersonType\\:\\:\\$centerTransformer\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillPersonBundle/Form/CreationPersonType.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\ReportBundle\\\\Timeline\\\\TimelineReportProvider\\:\\:\\$security\\.$#"
|
|
||||||
count: 4
|
|
||||||
path: src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\AsideActivityBundle\\\\Entity\\\\AsideActivityCategory\\:\\:\\$oldParent\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivityCategory.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\AsideActivityBundle\\\\Form\\\\AsideActivityCategoryType\\:\\:\\$categoryRender\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityCategoryType.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\AsideActivityBundle\\\\Form\\\\AsideActivityFormType\\:\\:\\$translatableStringHelper\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\CalendarBundle\\\\DataFixtures\\\\ORM\\\\LoadCalendarRange\\:\\:\\$userRepository\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadCalendarRange.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Access to an undefined property Chill\\\\CustomFieldsBundle\\\\Form\\\\DataTransformer\\\\JsonCustomFieldToArrayTransformer\\:\\:\\$customField\\.$#"
|
|
||||||
count: 3
|
|
||||||
path: src/Bundle/ChillCustomFieldsBundle/Form/DataTransformer/JsonCustomFieldToArrayTransformer.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Call to an undefined method Chill\\\\ThirdPartyBundle\\\\Form\\\\Type\\\\PickThirdPartyTypeCategoryType\\:\\:transform\\(\\)\\.$#"
|
message: "#^Call to an undefined method Chill\\\\ThirdPartyBundle\\\\Form\\\\Type\\\\PickThirdPartyTypeCategoryType\\:\\:transform\\(\\)\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillThirdPartyBundle/Form/Type/PickThirdPartyTypeCategoryType.php
|
path: src/Bundle/ChillThirdPartyBundle/Form/Type/PickThirdPartyTypeCategoryType.php
|
||||||
|
|
||||||
|
@@ -705,11 +705,6 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: src/Bundle/ChillPersonBundle/Search/SearchPersonApiProvider.php
|
path: src/Bundle/ChillPersonBundle/Search/SearchPersonApiProvider.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method Chill\\\\PersonBundle\\\\Search\\\\SimilarityPersonSearch\\:\\:renderResult\\(\\) should return string but return statement is missing\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/Bundle/ChillPersonBundle/Search/SimilarityPersonSearch.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
|
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
@@ -4,6 +4,7 @@ parameters:
|
|||||||
- src/
|
- src/
|
||||||
excludePaths:
|
excludePaths:
|
||||||
- src/Bundle/*/Tests/*
|
- src/Bundle/*/Tests/*
|
||||||
|
- src/Bundle/*/tests/*
|
||||||
- src/Bundle/*/Test/*
|
- src/Bundle/*/Test/*
|
||||||
- src/Bundle/*/config/*
|
- src/Bundle/*/config/*
|
||||||
- src/Bundle/*/migrations/*
|
- src/Bundle/*/migrations/*
|
||||||
|
@@ -37,6 +37,9 @@
|
|||||||
<testsuite name="CalendarBundle">
|
<testsuite name="CalendarBundle">
|
||||||
<directory suffix="Test.php">src/Bundle/ChillCalendarBundle/Tests/</directory>
|
<directory suffix="Test.php">src/Bundle/ChillCalendarBundle/Tests/</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
|
<testsuite name="DocGeneratorBundle">
|
||||||
|
<directory suffix="Test.php">src/Bundle/ChillDocGeneratorBundle/tests/</directory>
|
||||||
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
|
|
||||||
<listeners>
|
<listeners>
|
||||||
|
@@ -4,15 +4,23 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\ActivityBundle\Controller;
|
namespace Chill\ActivityBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\ActivityReason;
|
||||||
use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
|
use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityRepository;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
use Chill\MainBundle\Repository\LocationRepository;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\PersonBundle\Privacy\PrivacyEvent;
|
use Chill\PersonBundle\Privacy\PrivacyEvent;
|
||||||
|
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
|
||||||
|
use Chill\PersonBundle\Repository\PersonRepository;
|
||||||
|
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
use Symfony\Component\Form\Form;
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
@@ -25,26 +33,54 @@ use Symfony\Component\Serializer\SerializerInterface;
|
|||||||
|
|
||||||
final class ActivityController extends AbstractController
|
final class ActivityController extends AbstractController
|
||||||
{
|
{
|
||||||
protected EventDispatcherInterface $eventDispatcher;
|
private EventDispatcherInterface $eventDispatcher;
|
||||||
|
|
||||||
protected AuthorizationHelper $authorizationHelper;
|
private LoggerInterface $logger;
|
||||||
|
|
||||||
protected LoggerInterface $logger;
|
private SerializerInterface $serializer;
|
||||||
|
|
||||||
protected SerializerInterface $serializer;
|
private ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
|
||||||
|
|
||||||
protected ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
|
private ActivityTypeRepository $activityTypeRepository;
|
||||||
|
|
||||||
|
private ThirdPartyRepository $thirdPartyRepository;
|
||||||
|
|
||||||
|
private PersonRepository $personRepository;
|
||||||
|
|
||||||
|
private LocationRepository $locationRepository;
|
||||||
|
|
||||||
|
private EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
|
private ActivityRepository $activityRepository;
|
||||||
|
|
||||||
|
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
|
||||||
|
|
||||||
|
private ActivityTypeCategoryRepository $activityTypeCategoryRepository;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
|
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
|
||||||
|
ActivityTypeRepository $activityTypeRepository,
|
||||||
|
ActivityTypeCategoryRepository $activityTypeCategoryRepository,
|
||||||
|
PersonRepository $personRepository,
|
||||||
|
ThirdPartyRepository $thirdPartyRepository,
|
||||||
|
LocationRepository $locationRepository,
|
||||||
|
ActivityRepository $activityRepository,
|
||||||
|
AccompanyingPeriodRepository $accompanyingPeriodRepository,
|
||||||
|
EntityManagerInterface $entityManager,
|
||||||
EventDispatcherInterface $eventDispatcher,
|
EventDispatcherInterface $eventDispatcher,
|
||||||
AuthorizationHelper $authorizationHelper,
|
|
||||||
LoggerInterface $logger,
|
LoggerInterface $logger,
|
||||||
SerializerInterface $serializer
|
SerializerInterface $serializer
|
||||||
) {
|
) {
|
||||||
$this->activityACLAwareRepository = $activityACLAwareRepository;
|
$this->activityACLAwareRepository = $activityACLAwareRepository;
|
||||||
|
$this->activityTypeRepository = $activityTypeRepository;
|
||||||
|
$this->activityTypeCategoryRepository = $activityTypeCategoryRepository;
|
||||||
|
$this->personRepository = $personRepository;
|
||||||
|
$this->thirdPartyRepository = $thirdPartyRepository;
|
||||||
|
$this->locationRepository = $locationRepository;
|
||||||
|
$this->activityRepository = $activityRepository;
|
||||||
|
$this->accompanyingPeriodRepository = $accompanyingPeriodRepository;
|
||||||
|
$this->entityManager = $entityManager;
|
||||||
$this->eventDispatcher = $eventDispatcher;
|
$this->eventDispatcher = $eventDispatcher;
|
||||||
$this->authorizationHelper = $authorizationHelper;
|
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
$this->serializer = $serializer;
|
$this->serializer = $serializer;
|
||||||
}
|
}
|
||||||
@@ -65,10 +101,10 @@ final class ActivityController extends AbstractController
|
|||||||
$activities = $this->activityACLAwareRepository
|
$activities = $this->activityACLAwareRepository
|
||||||
->findByPerson($person, ActivityVoter::SEE, 0, null);
|
->findByPerson($person, ActivityVoter::SEE, 0, null);
|
||||||
|
|
||||||
$event = new PrivacyEvent($person, array(
|
$event = new PrivacyEvent($person, [
|
||||||
'element_class' => Activity::class,
|
'element_class' => Activity::class,
|
||||||
'action' => 'list'
|
'action' => 'list'
|
||||||
));
|
]);
|
||||||
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||||
|
|
||||||
$view = 'ChillActivityBundle:Activity:listPerson.html.twig';
|
$view = 'ChillActivityBundle:Activity:listPerson.html.twig';
|
||||||
@@ -93,7 +129,6 @@ final class ActivityController extends AbstractController
|
|||||||
|
|
||||||
public function selectTypeAction(Request $request): Response
|
public function selectTypeAction(Request $request): Response
|
||||||
{
|
{
|
||||||
$em = $this->getDoctrine()->getManager();
|
|
||||||
$view = null;
|
$view = null;
|
||||||
|
|
||||||
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
||||||
@@ -106,12 +141,17 @@ final class ActivityController extends AbstractController
|
|||||||
|
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
$activityTypeCategories = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityTypeCategory::class)
|
$activityTypeCategories = $this
|
||||||
|
->activityTypeCategoryRepository
|
||||||
->findBy(['active' => true], ['ordering' => 'ASC']);
|
->findBy(['active' => true], ['ordering' => 'ASC']);
|
||||||
|
|
||||||
foreach ($activityTypeCategories as $activityTypeCategory) {
|
foreach ($activityTypeCategories as $activityTypeCategory) {
|
||||||
$activityTypes = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class)
|
$activityTypes = $this
|
||||||
->findBy(['active' => true, 'category' => $activityTypeCategory], ['ordering' => 'ASC']);
|
->activityTypeRepository
|
||||||
|
->findBy(
|
||||||
|
['active' => true, 'category' => $activityTypeCategory],
|
||||||
|
['ordering' => 'ASC']
|
||||||
|
);
|
||||||
|
|
||||||
$data[] = [
|
$data[] = [
|
||||||
'activityTypeCategory' => $activityTypeCategory,
|
'activityTypeCategory' => $activityTypeCategory,
|
||||||
@@ -119,12 +159,6 @@ final class ActivityController extends AbstractController
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->query->has('activityData')) {
|
|
||||||
$activityData = $request->query->get('activityData');
|
|
||||||
} else {
|
|
||||||
$activityData = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($view === null) {
|
if ($view === null) {
|
||||||
throw $this->createNotFoundException('Template not found');
|
throw $this->createNotFoundException('Template not found');
|
||||||
}
|
}
|
||||||
@@ -133,14 +167,13 @@ final class ActivityController extends AbstractController
|
|||||||
'person' => $person,
|
'person' => $person,
|
||||||
'accompanyingCourse' => $accompanyingPeriod,
|
'accompanyingCourse' => $accompanyingPeriod,
|
||||||
'data' => $data,
|
'data' => $data,
|
||||||
'activityData' => $activityData
|
'activityData' => $request->query->get('activityData', []),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function newAction(Request $request): Response
|
public function newAction(Request $request): Response
|
||||||
{
|
{
|
||||||
$view = null;
|
$view = null;
|
||||||
$em = $this->getDoctrine()->getManager();
|
|
||||||
|
|
||||||
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
||||||
|
|
||||||
@@ -151,8 +184,7 @@ final class ActivityController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$activityType_id = $request->get('activityType_id', 0);
|
$activityType_id = $request->get('activityType_id', 0);
|
||||||
$activityType = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class)
|
$activityType = $this->activityTypeRepository->find($activityType_id);
|
||||||
->find($activityType_id);
|
|
||||||
|
|
||||||
if (isset($activityType) && !$activityType->isActive()) {
|
if (isset($activityType) && !$activityType->isActive()) {
|
||||||
throw new \InvalidArgumentException('Activity type must be active');
|
throw new \InvalidArgumentException('Activity type must be active');
|
||||||
@@ -210,20 +242,20 @@ final class ActivityController extends AbstractController
|
|||||||
|
|
||||||
if (array_key_exists('personsId', $activityData)) {
|
if (array_key_exists('personsId', $activityData)) {
|
||||||
foreach($activityData['personsId'] as $personId){
|
foreach($activityData['personsId'] as $personId){
|
||||||
$concernedPerson = $em->getRepository(\Chill\PersonBundle\Entity\Person::class)->find($personId);
|
$concernedPerson = $this->personRepository->find($personId);
|
||||||
$entity->addPerson($concernedPerson);
|
$entity->addPerson($concernedPerson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('professionalsId', $activityData)) {
|
if (array_key_exists('professionalsId', $activityData)) {
|
||||||
foreach($activityData['professionalsId'] as $professionalsId){
|
foreach($activityData['professionalsId'] as $professionalsId){
|
||||||
$professional = $em->getRepository(\Chill\ThirdPartyBundle\Entity\ThirdParty::class)->find($professionalsId);
|
$professional = $this->thirdPartyRepository->find($professionalsId);
|
||||||
$entity->addThirdParty($professional);
|
$entity->addThirdParty($professional);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('location', $activityData)) {
|
if (array_key_exists('location', $activityData)) {
|
||||||
$location = $em->getRepository(\Chill\MainBundle\Entity\Location::class)->find($activityData['location']);
|
$location = $this->locationRepository->find($activityData['location']);
|
||||||
$entity->setLocation($location);
|
$entity->setLocation($location);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,8 +280,8 @@ final class ActivityController extends AbstractController
|
|||||||
])->handleRequest($request);
|
])->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$em->persist($entity);
|
$this->entityManager->persist($entity);
|
||||||
$em->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
$this->addFlash('success', $this->get('translator')->trans('Success : activity created!'));
|
$this->addFlash('success', $this->get('translator')->trans('Success : activity created!'));
|
||||||
|
|
||||||
@@ -281,7 +313,6 @@ final class ActivityController extends AbstractController
|
|||||||
public function showAction(Request $request, int $id): Response
|
public function showAction(Request $request, int $id): Response
|
||||||
{
|
{
|
||||||
$view = null;
|
$view = null;
|
||||||
$em = $this->getDoctrine()->getManager();
|
|
||||||
|
|
||||||
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
||||||
|
|
||||||
@@ -291,13 +322,14 @@ final class ActivityController extends AbstractController
|
|||||||
$view = 'ChillActivityBundle:Activity:showPerson.html.twig';
|
$view = 'ChillActivityBundle:Activity:showPerson.html.twig';
|
||||||
}
|
}
|
||||||
|
|
||||||
$entity = $em->getRepository('ChillActivityBundle:Activity')->find($id);
|
$entity = $this->activityRepository->find($id);
|
||||||
|
|
||||||
if (!$entity) {
|
if (null === $entity) {
|
||||||
throw $this->createNotFoundException('Unable to find Activity entity.');
|
throw $this->createNotFoundException('Unable to find Activity entity.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $accompanyingPeriod) {
|
if (null !== $accompanyingPeriod) {
|
||||||
|
// @TODO: Properties created dynamically.
|
||||||
$entity->personsAssociated = $entity->getPersonsAssociated();
|
$entity->personsAssociated = $entity->getPersonsAssociated();
|
||||||
$entity->personsNotAssociated = $entity->getPersonsNotAssociated();
|
$entity->personsNotAssociated = $entity->getPersonsNotAssociated();
|
||||||
}
|
}
|
||||||
@@ -305,7 +337,7 @@ final class ActivityController extends AbstractController
|
|||||||
// TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période
|
// TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période
|
||||||
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_SEE', $entity);
|
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_SEE', $entity);
|
||||||
|
|
||||||
$deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod);
|
$deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
/*
|
/*
|
||||||
@@ -321,22 +353,20 @@ final class ActivityController extends AbstractController
|
|||||||
throw $this->createNotFoundException('Template not found');
|
throw $this->createNotFoundException('Template not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render($view, array(
|
return $this->render($view, [
|
||||||
'person' => $person,
|
'person' => $person,
|
||||||
'accompanyingCourse' => $accompanyingPeriod,
|
'accompanyingCourse' => $accompanyingPeriod,
|
||||||
'entity' => $entity,
|
'entity' => $entity,
|
||||||
'delete_form' => $deleteForm->createView(),
|
'delete_form' => $deleteForm->createView(),
|
||||||
));
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a form to edit an existing Activity entity.
|
* Displays a form to edit an existing Activity entity.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function editAction(int $id, Request $request): Response
|
public function editAction(int $id, Request $request): Response
|
||||||
{
|
{
|
||||||
$view = null;
|
$view = null;
|
||||||
$em = $this->getDoctrine()->getManager();
|
|
||||||
|
|
||||||
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
||||||
|
|
||||||
@@ -346,9 +376,9 @@ final class ActivityController extends AbstractController
|
|||||||
$view = 'ChillActivityBundle:Activity:editPerson.html.twig';
|
$view = 'ChillActivityBundle:Activity:editPerson.html.twig';
|
||||||
}
|
}
|
||||||
|
|
||||||
$entity = $em->getRepository('ChillActivityBundle:Activity')->find($id);
|
$entity = $this->activityRepository->find($id);
|
||||||
|
|
||||||
if (!$entity) {
|
if (null === $entity) {
|
||||||
throw $this->createNotFoundException('Unable to find Activity entity.');
|
throw $this->createNotFoundException('Unable to find Activity entity.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,17 +393,18 @@ final class ActivityController extends AbstractController
|
|||||||
])->handleRequest($request);
|
])->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$em->persist($entity);
|
$this->entityManager->persist($entity);
|
||||||
$em->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
$this->addFlash('success', $this->get('translator')->trans('Success : activity updated!'));
|
$this->addFlash('success', $this->get('translator')->trans('Success : activity updated!'));
|
||||||
|
|
||||||
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
|
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
|
||||||
$params['id'] = $id;
|
$params['id'] = $entity->getId();
|
||||||
|
|
||||||
return $this->redirectToRoute('chill_activity_activity_show', $params);
|
return $this->redirectToRoute('chill_activity_activity_show', $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
$deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod);
|
$deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO
|
* TODO
|
||||||
@@ -391,24 +422,22 @@ final class ActivityController extends AbstractController
|
|||||||
|
|
||||||
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
|
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
|
||||||
|
|
||||||
return $this->render($view, array(
|
return $this->render($view, [
|
||||||
'entity' => $entity,
|
'entity' => $entity,
|
||||||
'edit_form' => $form->createView(),
|
'edit_form' => $form->createView(),
|
||||||
'delete_form' => $deleteForm->createView(),
|
'delete_form' => $deleteForm->createView(),
|
||||||
'person' => $person,
|
'person' => $person,
|
||||||
'accompanyingCourse' => $accompanyingPeriod,
|
'accompanyingCourse' => $accompanyingPeriod,
|
||||||
'activity_json' => $activity_array
|
'activity_json' => $activity_array
|
||||||
));
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a Activity entity.
|
* Deletes a Activity entity.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function deleteAction(Request $request, $id)
|
public function deleteAction(Request $request, $id)
|
||||||
{
|
{
|
||||||
$view = null;
|
$view = null;
|
||||||
$em = $this->getDoctrine()->getManager();
|
|
||||||
|
|
||||||
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
[$person, $accompanyingPeriod] = $this->getEntity($request);
|
||||||
|
|
||||||
@@ -418,8 +447,7 @@ final class ActivityController extends AbstractController
|
|||||||
$view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig';
|
$view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @var $activity Activity */
|
$activity = $this->activityRepository->find($id);
|
||||||
$activity = $em->getRepository('ChillActivityBundle:Activity')->find($id);
|
|
||||||
|
|
||||||
if (!$activity) {
|
if (!$activity) {
|
||||||
throw $this->createNotFoundException('Unable to find Activity entity.');
|
throw $this->createNotFoundException('Unable to find Activity entity.');
|
||||||
@@ -428,35 +456,37 @@ final class ActivityController extends AbstractController
|
|||||||
// TODO
|
// TODO
|
||||||
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_DELETE', $activity);
|
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_DELETE', $activity);
|
||||||
|
|
||||||
$form = $this->createDeleteForm($id, $person, $accompanyingPeriod);
|
$form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
|
||||||
|
|
||||||
if ($request->getMethod() === Request::METHOD_DELETE) {
|
if ($request->getMethod() === Request::METHOD_DELETE) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isValid()) {
|
if ($form->isValid()) {
|
||||||
|
$this->logger->notice("An activity has been removed", [
|
||||||
$this->logger->notice("An activity has been removed", array(
|
|
||||||
'by_user' => $this->getUser()->getUsername(),
|
'by_user' => $this->getUser()->getUsername(),
|
||||||
'activity_id' => $activity->getId(),
|
'activity_id' => $activity->getId(),
|
||||||
'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null,
|
'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null,
|
||||||
'comment' => $activity->getComment()->getComment(),
|
'comment' => $activity->getComment()->getComment(),
|
||||||
'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null,
|
'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null,
|
||||||
'reasons_ids' => $activity->getReasons()
|
'reasons_ids' => $activity->getReasons()
|
||||||
->map(function ($ar) { return $ar->getId(); })
|
->map(
|
||||||
|
static fn (ActivityReason $ar): int => $ar->getId()
|
||||||
|
)
|
||||||
->toArray(),
|
->toArray(),
|
||||||
'type_id' => $activity->getType()->getId(),
|
'type_id' => $activity->getType()->getId(),
|
||||||
'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null,
|
'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null,
|
||||||
'date' => $activity->getDate()->format('Y-m-d'),
|
'date' => $activity->getDate()->format('Y-m-d'),
|
||||||
'attendee' => $activity->getAttendee()
|
'attendee' => $activity->getAttendee()
|
||||||
));
|
]);
|
||||||
|
|
||||||
$em->remove($activity);
|
$this->entityManager->remove($activity);
|
||||||
$em->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
$this->addFlash('success', $this->get('translator')
|
$this->addFlash('success', $this->get('translator')
|
||||||
->trans("The activity has been successfully removed."));
|
->trans("The activity has been successfully removed."));
|
||||||
|
|
||||||
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
|
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
|
||||||
|
|
||||||
return $this->redirectToRoute('chill_activity_activity_list', $params);
|
return $this->redirectToRoute('chill_activity_activity_list', $params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -465,18 +495,18 @@ final class ActivityController extends AbstractController
|
|||||||
throw $this->createNotFoundException('Template not found');
|
throw $this->createNotFoundException('Template not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render($view, array(
|
return $this->render($view, [
|
||||||
'activity' => $activity,
|
'activity' => $activity,
|
||||||
'delete_form' => $form->createView(),
|
'delete_form' => $form->createView(),
|
||||||
'person' => $person,
|
'person' => $person,
|
||||||
'accompanyingCourse' => $accompanyingPeriod,
|
'accompanyingCourse' => $accompanyingPeriod,
|
||||||
));
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a form to delete a Activity entity by id.
|
* Creates a form to delete a Activity entity by id.
|
||||||
*/
|
*/
|
||||||
private function createDeleteForm(int $id, ?Person $person, ?AccompanyingPeriod $accompanyingPeriod): Form
|
private function createDeleteForm(int $id, ?Person $person, ?AccompanyingPeriod $accompanyingPeriod): FormInterface
|
||||||
{
|
{
|
||||||
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
|
$params = $this->buildParamsToUrl($person, $accompanyingPeriod);
|
||||||
$params['id'] = $id;
|
$params['id'] = $id;
|
||||||
@@ -484,19 +514,17 @@ final class ActivityController extends AbstractController
|
|||||||
return $this->createFormBuilder()
|
return $this->createFormBuilder()
|
||||||
->setAction($this->generateUrl('chill_activity_activity_delete', $params))
|
->setAction($this->generateUrl('chill_activity_activity_delete', $params))
|
||||||
->setMethod('DELETE')
|
->setMethod('DELETE')
|
||||||
->add('submit', SubmitType::class, array('label' => 'Delete'))
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm()
|
->getForm();
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getEntity(Request $request): array
|
private function getEntity(Request $request): array
|
||||||
{
|
{
|
||||||
$em = $this->getDoctrine()->getManager();
|
|
||||||
$person = $accompanyingPeriod = null;
|
$person = $accompanyingPeriod = null;
|
||||||
|
|
||||||
if ($request->query->has('person_id')) {
|
if ($request->query->has('person_id')) {
|
||||||
$person_id = $request->get('person_id');
|
$person_id = $request->get('person_id');
|
||||||
$person = $em->getRepository(Person::class)->find($person_id);
|
$person = $this->personRepository->find($person_id);
|
||||||
|
|
||||||
if ($person === null) {
|
if ($person === null) {
|
||||||
throw $this->createNotFoundException('Person not found');
|
throw $this->createNotFoundException('Person not found');
|
||||||
@@ -505,7 +533,7 @@ final class ActivityController extends AbstractController
|
|||||||
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
|
$this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
|
||||||
} elseif ($request->query->has('accompanying_period_id')) {
|
} elseif ($request->query->has('accompanying_period_id')) {
|
||||||
$accompanying_period_id = $request->get('accompanying_period_id');
|
$accompanying_period_id = $request->get('accompanying_period_id');
|
||||||
$accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($accompanying_period_id);
|
$accompanyingPeriod = $this->accompanyingPeriodRepository->find($accompanying_period_id);
|
||||||
|
|
||||||
if ($accompanyingPeriod === null) {
|
if ($accompanyingPeriod === null) {
|
||||||
throw $this->createNotFoundException('Accompanying Period not found');
|
throw $this->createNotFoundException('Accompanying Period not found');
|
||||||
@@ -518,21 +546,20 @@ final class ActivityController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
$person, $accompanyingPeriod
|
$person,
|
||||||
|
$accompanyingPeriod
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildParamsToUrl(
|
private function buildParamsToUrl(?Person $person, ?AccompanyingPeriod $accompanyingPeriod): array
|
||||||
?Person $person,
|
{
|
||||||
?AccompanyingPeriod $accompanyingPeriod
|
|
||||||
): array {
|
|
||||||
$params = [];
|
$params = [];
|
||||||
|
|
||||||
if ($person) {
|
if (null !== $person) {
|
||||||
$params['person_id'] = $person->getId();
|
$params['person_id'] = $person->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($accompanyingPeriod) {
|
if (null !== $accompanyingPeriod) {
|
||||||
$params['accompanying_period_id'] = $accompanyingPeriod->getId();
|
$params['accompanying_period_id'] = $accompanyingPeriod->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -36,6 +36,8 @@ use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
|
|||||||
|
|
||||||
class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
|
class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
|
||||||
{
|
{
|
||||||
|
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Faker\Generator
|
* @var \Faker\Generator
|
||||||
*/
|
*/
|
||||||
@@ -80,15 +82,10 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
|
|||||||
*
|
*
|
||||||
* @return \Chill\ActivityBundle\Entity\ActivityReason
|
* @return \Chill\ActivityBundle\Entity\ActivityReason
|
||||||
*/
|
*/
|
||||||
private function getRandomActivityReason(array $excludingIds)
|
private function getRandomActivityReason()
|
||||||
{
|
{
|
||||||
$reasonRef = LoadActivityReason::$references[array_rand(LoadActivityReason::$references)];
|
$reasonRef = LoadActivityReason::$references[array_rand(LoadActivityReason::$references)];
|
||||||
|
|
||||||
if (in_array($this->getReference($reasonRef)->getId(), $excludingIds, true)) {
|
|
||||||
// we have a reason which should be excluded. Find another...
|
|
||||||
return $this->getRandomActivityReason($excludingIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->getReference($reasonRef);
|
return $this->getReference($reasonRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +100,7 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
|
|||||||
return $this->getReference($userRef);
|
return $this->getReference($userRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function newRandomActivity($person)
|
public function newRandomActivity($person): ?Activity
|
||||||
{
|
{
|
||||||
$activity = (new Activity())
|
$activity = (new Activity())
|
||||||
->setUser($this->getRandomUser())
|
->setUser($this->getRandomUser())
|
||||||
@@ -116,11 +113,13 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
|
|||||||
|
|
||||||
// ->setAttendee($this->faker->boolean())
|
// ->setAttendee($this->faker->boolean())
|
||||||
|
|
||||||
$usedId = array();
|
|
||||||
for ($i = 0; $i < rand(0, 4); $i++) {
|
for ($i = 0; $i < rand(0, 4); $i++) {
|
||||||
$reason = $this->getRandomActivityReason($usedId);
|
$reason = $this->getRandomActivityReason();
|
||||||
$usedId[] = $reason->getId();
|
if (null !== $reason) {
|
||||||
$activity->addReason($reason);
|
$activity->addReason($reason);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $activity;
|
return $activity;
|
||||||
@@ -137,9 +136,11 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
|
|||||||
|
|
||||||
for ($i = 0; $i < $activityNbr; $i ++) {
|
for ($i = 0; $i < $activityNbr; $i ++) {
|
||||||
$activity = $this->newRandomActivity($person);
|
$activity = $this->newRandomActivity($person);
|
||||||
|
if (null !== $activity) {
|
||||||
$manager->persist($activity);
|
$manager->persist($activity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
$manager->flush();
|
$manager->flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,27 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Entity;
|
namespace Chill\ActivityBundle\Entity;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\Entity\Document;
|
use Chill\DocStoreBundle\Entity\Document;
|
||||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
|
||||||
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
||||||
use Chill\MainBundle\Entity\Location;
|
use Chill\MainBundle\Entity\Location;
|
||||||
use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface;
|
use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface;
|
||||||
@@ -38,7 +19,7 @@ use Chill\MainBundle\Entity\HasCenterInterface;
|
|||||||
use Chill\MainBundle\Entity\HasScopeInterface;
|
use Chill\MainBundle\Entity\HasScopeInterface;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistency;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Serializer\Annotation\Groups;
|
use Symfony\Component\Serializer\Annotation\Groups;
|
||||||
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||||
|
|
||||||
@@ -202,7 +183,7 @@ class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPer
|
|||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUser(User $user): self
|
public function setUser(UserInterface $user): self
|
||||||
{
|
{
|
||||||
$this->user = $user;
|
$this->user = $user;
|
||||||
|
|
||||||
|
@@ -1,31 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
*
|
|
||||||
* Copyright (C) 2015, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Entity;
|
namespace Chill\ActivityBundle\Entity;
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ActivityTypeCateogry
|
|
||||||
*
|
|
||||||
* @package Chill\ActivityBundle\Entity
|
|
||||||
* @ORM\Entity()
|
* @ORM\Entity()
|
||||||
* @ORM\Table(name="activitytypecategory")
|
* @ORM\Table(name="activitytypecategory")
|
||||||
* @ORM\HasLifecycleCallbacks()
|
* @ORM\HasLifecycleCallbacks()
|
||||||
@@ -37,7 +18,7 @@ class ActivityTypeCategory
|
|||||||
* @ORM\Column(name="id", type="integer")
|
* @ORM\Column(name="id", type="integer")
|
||||||
* @ORM\GeneratedValue(strategy="AUTO")
|
* @ORM\GeneratedValue(strategy="AUTO")
|
||||||
*/
|
*/
|
||||||
private ?int $id;
|
private ?int $id = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="json")
|
* @ORM\Column(type="json")
|
||||||
@@ -54,10 +35,7 @@ class ActivityTypeCategory
|
|||||||
*/
|
*/
|
||||||
private float $ordering = 0.0;
|
private float $ordering = 0.0;
|
||||||
|
|
||||||
/**
|
public function getId(): ?int
|
||||||
* Get id
|
|
||||||
*/
|
|
||||||
public function getId(): int
|
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
@@ -1,70 +1,39 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Export\Aggregator;
|
namespace Chill\ActivityBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Chill\MainBundle\Export\AggregatorInterface;
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
use Symfony\Component\Security\Core\Role\Role;
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
||||||
use Doctrine\ORM\EntityRepository;
|
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||||
use Doctrine\ORM\Query\Expr\Join;
|
use Doctrine\ORM\Query\Expr\Join;
|
||||||
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
|
||||||
/**
|
class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
|
||||||
class ActivityReasonAggregator implements AggregatorInterface,
|
|
||||||
ExportElementValidatedInterface
|
|
||||||
{
|
{
|
||||||
/**
|
protected ActivityReasonCategoryRepository $activityReasonCategoryRepository;
|
||||||
*
|
|
||||||
* @var EntityRepository
|
|
||||||
*/
|
|
||||||
protected $categoryRepository;
|
|
||||||
|
|
||||||
/**
|
protected ActivityReasonRepository $activityReasonRepository;
|
||||||
*
|
|
||||||
* @var EntityRepository
|
|
||||||
*/
|
|
||||||
protected $reasonRepository;
|
|
||||||
|
|
||||||
/**
|
protected TranslatableStringHelperInterface $translatableStringHelper;
|
||||||
*
|
|
||||||
* @var TranslatableStringHelper
|
|
||||||
*/
|
|
||||||
protected $stringHelper;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EntityRepository $categoryRepository,
|
ActivityReasonCategoryRepository $activityReasonCategoryRepository,
|
||||||
EntityRepository $reasonRepository,
|
ActivityReasonRepository $activityReasonRepository,
|
||||||
TranslatableStringHelper $stringHelper
|
TranslatableStringHelper $translatableStringHelper
|
||||||
) {
|
) {
|
||||||
$this->categoryRepository = $categoryRepository;
|
$this->activityReasonCategoryRepository = $activityReasonCategoryRepository;
|
||||||
$this->reasonRepository = $reasonRepository;
|
$this->activityReasonRepository = $activityReasonRepository;
|
||||||
$this->stringHelper = $stringHelper;
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function alterQuery(QueryBuilder $qb, $data)
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
@@ -77,7 +46,7 @@ class ActivityReasonAggregator implements AggregatorInterface,
|
|||||||
$elem = 'category.id';
|
$elem = 'category.id';
|
||||||
$alias = 'activity_categories_id';
|
$alias = 'activity_categories_id';
|
||||||
} else {
|
} else {
|
||||||
throw new \RuntimeException('the data provided are not recognized');
|
throw new \RuntimeException('The data provided are not recognized.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$qb->addSelect($elem.' as '.$alias);
|
$qb->addSelect($elem.' as '.$alias);
|
||||||
@@ -94,10 +63,11 @@ class ActivityReasonAggregator implements AggregatorInterface,
|
|||||||
) {
|
) {
|
||||||
$qb->add(
|
$qb->add(
|
||||||
'join',
|
'join',
|
||||||
array('activity' =>
|
[
|
||||||
new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')
|
'activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')
|
||||||
),
|
],
|
||||||
true);
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// join category if necessary
|
// join category if necessary
|
||||||
@@ -143,28 +113,33 @@ class ActivityReasonAggregator implements AggregatorInterface,
|
|||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
$builder->add('level', ChoiceType::class, array(
|
$builder->add(
|
||||||
'choices' => array(
|
'level',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'choices' => [
|
||||||
'By reason' => 'reasons',
|
'By reason' => 'reasons',
|
||||||
'By category of reason' => 'categories'
|
'By category of reason' => 'categories'
|
||||||
),
|
],
|
||||||
'multiple' => false,
|
'multiple' => false,
|
||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
'label' => 'Reason\'s level'
|
'label' => "Reason's level"
|
||||||
));
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validateForm($data, ExecutionContextInterface $context)
|
public function validateForm($data, ExecutionContextInterface $context)
|
||||||
{
|
{
|
||||||
if ($data['level'] === null) {
|
if ($data['level'] === null) {
|
||||||
$context->buildViolation("The reasons's level should not be empty")
|
$context
|
||||||
|
->buildViolation("The reasons's level should not be empty.")
|
||||||
->addViolation();
|
->addViolation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTitle()
|
public function getTitle()
|
||||||
{
|
{
|
||||||
return "Aggregate by activity reason";
|
return 'Aggregate by activity reason';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addRole()
|
public function addRole()
|
||||||
@@ -177,41 +152,33 @@ class ActivityReasonAggregator implements AggregatorInterface,
|
|||||||
// for performance reason, we load data from db only once
|
// for performance reason, we load data from db only once
|
||||||
switch ($data['level']) {
|
switch ($data['level']) {
|
||||||
case 'reasons':
|
case 'reasons':
|
||||||
$this->reasonRepository->findBy(array('id' => $values));
|
$this->activityReasonRepository->findBy(['id' => $values]);
|
||||||
break;
|
break;
|
||||||
case 'categories':
|
case 'categories':
|
||||||
$this->categoryRepository->findBy(array('id' => $values));
|
$this->activityReasonCategoryRepository->findBy(['id' => $values]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \RuntimeException(sprintf("the level data '%s' is invalid",
|
throw new \RuntimeException(sprintf("The level data '%s' is invalid.", $data['level']));
|
||||||
$data['level']));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return function($value) use ($data) {
|
return function($value) use ($data) {
|
||||||
if ($value === '_header') {
|
if ($value === '_header') {
|
||||||
return $data['level'] === 'reasons' ?
|
return $data['level'] === 'reasons' ? 'Group by reasons' : 'Group by categories of reason';
|
||||||
'Group by reasons'
|
|
||||||
:
|
|
||||||
'Group by categories of reason'
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($data['level']) {
|
switch ($data['level']) {
|
||||||
case 'reasons':
|
case 'reasons':
|
||||||
/* @var $r \Chill\ActivityBundle\Entity\ActivityReason */
|
$r = $this->activityReasonRepository->find($value);
|
||||||
$r = $this->reasonRepository->find($value);
|
|
||||||
|
|
||||||
return $this->stringHelper->localize($r->getCategory()->getName())
|
return sprintf(
|
||||||
." > "
|
"%s > %s",
|
||||||
. $this->stringHelper->localize($r->getName());
|
$this->translatableStringHelper->localize($r->getCategory()->getName()),
|
||||||
;
|
$this->translatableStringHelper->localize($r->getName())
|
||||||
break;
|
);
|
||||||
case 'categories':
|
case 'categories':
|
||||||
$c = $this->categoryRepository->find($value);
|
$c = $this->activityReasonCategoryRepository->find($value);
|
||||||
|
|
||||||
return $this->stringHelper->localize($c->getName());
|
return $this->translatableStringHelper->localize($c->getName());
|
||||||
break;
|
|
||||||
// no need for a default : the default was already set above
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -222,12 +189,14 @@ class ActivityReasonAggregator implements AggregatorInterface,
|
|||||||
// add select element
|
// add select element
|
||||||
if ($data['level'] === 'reasons') {
|
if ($data['level'] === 'reasons') {
|
||||||
return array('activity_reasons_id');
|
return array('activity_reasons_id');
|
||||||
} elseif ($data['level'] === 'categories') {
|
}
|
||||||
|
|
||||||
|
if ($data['level'] === 'categories') {
|
||||||
return array ('activity_categories_id');
|
return array ('activity_categories_id');
|
||||||
} else {
|
|
||||||
throw new \RuntimeException('the data provided are not recognised');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException('The data provided are not recognised.');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,61 +1,32 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Export\Aggregator;
|
namespace Chill\ActivityBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Chill\MainBundle\Export\AggregatorInterface;
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
use Symfony\Component\Security\Core\Role\Role;
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
||||||
use Doctrine\ORM\EntityRepository;
|
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
|
||||||
use Doctrine\ORM\Query\Expr\Join;
|
use Doctrine\ORM\Query\Expr\Join;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
|
||||||
class ActivityTypeAggregator implements AggregatorInterface
|
class ActivityTypeAggregator implements AggregatorInterface
|
||||||
{
|
{
|
||||||
|
protected ActivityTypeRepository $activityTypeRepository;
|
||||||
|
|
||||||
/**
|
protected TranslatableStringHelperInterface $translatableStringHelper;
|
||||||
*
|
|
||||||
* @var EntityRepository
|
|
||||||
*/
|
|
||||||
protected $typeRepository;
|
|
||||||
|
|
||||||
/**
|
public const KEY = 'activity_type_aggregator';
|
||||||
*
|
|
||||||
* @var TranslatableStringHelper
|
|
||||||
*/
|
|
||||||
protected $stringHelper;
|
|
||||||
|
|
||||||
const KEY = 'activity_type_aggregator';
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EntityRepository $typeRepository,
|
ActivityTypeRepository $activityTypeRepository,
|
||||||
TranslatableStringHelper $stringHelper
|
TranslatableStringHelperInterface $translatableStringHelper
|
||||||
) {
|
) {
|
||||||
$this->typeRepository = $typeRepository;
|
$this->activityTypeRepository = $activityTypeRepository;
|
||||||
$this->stringHelper = $stringHelper;
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function alterQuery(QueryBuilder $qb, $data)
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
@@ -64,7 +35,7 @@ class ActivityTypeAggregator implements AggregatorInterface
|
|||||||
$qb->addSelect(sprintf('IDENTITY(activity.type) AS %s', self::KEY));
|
$qb->addSelect(sprintf('IDENTITY(activity.type) AS %s', self::KEY));
|
||||||
|
|
||||||
// add the "group by" part
|
// add the "group by" part
|
||||||
$groupBy = $qb->addGroupBy(self::KEY);
|
$qb->addGroupBy(self::KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,7 +68,7 @@ class ActivityTypeAggregator implements AggregatorInterface
|
|||||||
|
|
||||||
public function getTitle()
|
public function getTitle()
|
||||||
{
|
{
|
||||||
return "Aggregate by activity type";
|
return 'Aggregate by activity type';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addRole()
|
public function addRole()
|
||||||
@@ -108,17 +79,16 @@ class ActivityTypeAggregator implements AggregatorInterface
|
|||||||
public function getLabels($key, array $values, $data): \Closure
|
public function getLabels($key, array $values, $data): \Closure
|
||||||
{
|
{
|
||||||
// for performance reason, we load data from db only once
|
// for performance reason, we load data from db only once
|
||||||
$this->typeRepository->findBy(array('id' => $values));
|
$this->activityTypeRepository->findBy(['id' => $values]);
|
||||||
|
|
||||||
return function($value): string {
|
return function($value): string {
|
||||||
if ($value === '_header') {
|
if ($value === '_header') {
|
||||||
return 'Activity type';
|
return 'Activity type';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @var $r \Chill\ActivityBundle\Entity\ActivityType */
|
$t = $this->activityTypeRepository->find($value);
|
||||||
$t = $this->typeRepository->find($value);
|
|
||||||
|
|
||||||
return $this->stringHelper->localize($t->getName());
|
return $this->translatableStringHelper->localize($t->getName());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,49 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
/*
|
|
||||||
* Copyright (C) 2019 Champs Libres Cooperative <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
namespace Chill\ActivityBundle\Export\Aggregator;
|
namespace Chill\ActivityBundle\Export\Aggregator;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Repository\UserRepository;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Chill\MainBundle\Export\AggregatorInterface;
|
use Chill\MainBundle\Export\AggregatorInterface;
|
||||||
use Symfony\Component\Security\Core\Role\Role;
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
use Doctrine\ORM\Query\Expr\Join;
|
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Chill\MainBundle\Entity\User;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
|
||||||
class ActivityUserAggregator implements AggregatorInterface
|
class ActivityUserAggregator implements AggregatorInterface
|
||||||
{
|
{
|
||||||
/**
|
public const KEY = 'activity_user_id';
|
||||||
*
|
|
||||||
* @var EntityManagerInterface
|
|
||||||
*/
|
|
||||||
protected $em;
|
|
||||||
|
|
||||||
const KEY = 'activity_user_id';
|
private UserRepository $userRepository;
|
||||||
|
|
||||||
function __construct(EntityManagerInterface $em)
|
public function __construct(
|
||||||
{
|
UserRepository $userRepository
|
||||||
$this->em = $em;
|
) {
|
||||||
|
$this->userRepository = $userRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addRole()
|
public function addRole()
|
||||||
@@ -73,17 +48,14 @@ class ActivityUserAggregator implements AggregatorInterface
|
|||||||
public function getLabels($key, $values, $data): \Closure
|
public function getLabels($key, $values, $data): \Closure
|
||||||
{
|
{
|
||||||
// preload users at once
|
// preload users at once
|
||||||
$this->em->getRepository(User::class)
|
$this->userRepository->findBy(['id' => $values]);
|
||||||
->findBy(['id' => $values]);
|
|
||||||
|
|
||||||
return function($value) {
|
return function($value) {
|
||||||
switch ($value) {
|
if ($value === '_header') {
|
||||||
case '_header':
|
|
||||||
return 'activity user';
|
return 'activity user';
|
||||||
default:
|
|
||||||
return $this->em->getRepository(User::class)->find($value)
|
|
||||||
->getUsername();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this->userRepository->find($value)->getUsername();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +66,6 @@ class ActivityUserAggregator implements AggregatorInterface
|
|||||||
|
|
||||||
public function getTitle(): string
|
public function getTitle(): string
|
||||||
{
|
{
|
||||||
return "Aggregate by activity user";
|
return 'Aggregate by activity user';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,64 +1,40 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Copyright (C) 2015 Champs-Libres <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Export\Export;
|
namespace Chill\ActivityBundle\Export\Export;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityRepository;
|
||||||
use Chill\MainBundle\Export\ExportInterface;
|
use Chill\MainBundle\Export\ExportInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Chill\MainBundle\Export\FormatterInterface;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Security\Core\Role\Role;
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
use Doctrine\ORM\Query;
|
use Doctrine\ORM\Query;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
|
||||||
class CountActivity implements ExportInterface
|
class CountActivity implements ExportInterface
|
||||||
{
|
{
|
||||||
/**
|
protected ActivityRepository $activityRepository;
|
||||||
*
|
|
||||||
* @var EntityManagerInterface
|
|
||||||
*/
|
|
||||||
protected $entityManager;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EntityManagerInterface $em
|
ActivityRepository $activityRepository
|
||||||
)
|
) {
|
||||||
{
|
$this->activityRepository = $activityRepository;
|
||||||
$this->entityManager = $em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDescription()
|
public function getDescription()
|
||||||
{
|
{
|
||||||
return "Count activities by various parameters.";
|
return 'Count activities by various parameters.';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTitle()
|
public function getTitle()
|
||||||
{
|
{
|
||||||
return "Count activities";
|
return 'Count activities';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getType()
|
public function getType()
|
||||||
@@ -68,24 +44,24 @@ class CountActivity implements ExportInterface
|
|||||||
|
|
||||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
|
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
|
||||||
{
|
{
|
||||||
$qb = $this->entityManager->createQueryBuilder();
|
$centers = array_map(static fn($el) => $el['center'], $acl);
|
||||||
$centers = array_map(function($el) { return $el['center']; }, $acl);
|
|
||||||
|
|
||||||
$qb->select('COUNT(activity.id) as export_count_activity')
|
$qb = $this
|
||||||
->from('ChillActivityBundle:Activity', 'activity')
|
->activityRepository
|
||||||
->join('activity.person', 'person')
|
->createQueryBuilder('activity')
|
||||||
;
|
->select('COUNT(activity.id) as export_count_activity')
|
||||||
|
->join('activity.person', 'person');
|
||||||
|
|
||||||
$qb->where($qb->expr()->in('person.center', ':centers'))
|
$qb
|
||||||
->setParameter('centers', $centers)
|
->where($qb->expr()->in('person.center', ':centers'))
|
||||||
;
|
->setParameter('centers', $centers);
|
||||||
|
|
||||||
return $qb;
|
return $qb;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supportsModifiers()
|
public function supportsModifiers()
|
||||||
{
|
{
|
||||||
return array('person', 'activity');
|
return ['person', 'activity'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function requiredRole()
|
public function requiredRole()
|
||||||
@@ -95,7 +71,7 @@ class CountActivity implements ExportInterface
|
|||||||
|
|
||||||
public function getAllowedFormattersTypes()
|
public function getAllowedFormattersTypes()
|
||||||
{
|
{
|
||||||
return array(\Chill\MainBundle\Export\FormatterInterface::TYPE_TABULAR);
|
return [FormatterInterface::TYPE_TABULAR];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
@@ -104,18 +80,12 @@ class CountActivity implements ExportInterface
|
|||||||
throw new \LogicException("the key $key is not used by this export");
|
throw new \LogicException("the key $key is not used by this export");
|
||||||
}
|
}
|
||||||
|
|
||||||
return function($value) {
|
return static fn($value) => $value === '_header' ? 'Number of activities' : $value;
|
||||||
return $value === '_header' ?
|
|
||||||
'Number of activities'
|
|
||||||
:
|
|
||||||
$value
|
|
||||||
;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getQueryKeys($data)
|
public function getQueryKeys($data)
|
||||||
{
|
{
|
||||||
return array('export_count_activity');
|
return ['export_count_activity'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getResult($qb, $data)
|
public function getResult($qb, $data)
|
||||||
|
@@ -1,31 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Export\Export;
|
namespace Chill\ActivityBundle\Export\Export;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityRepository;
|
||||||
use Chill\MainBundle\Export\ListInterface;
|
use Chill\MainBundle\Export\ListInterface;
|
||||||
use Chill\ActivityBundle\Entity\ActivityReason;
|
use Chill\ActivityBundle\Entity\ActivityReason;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Chill\MainBundle\Entity\Scope;
|
use Doctrine\DBAL\Exception\InvalidArgumentException;
|
||||||
use Chill\ActivityBundle\Entity\ActivityType;
|
|
||||||
use Doctrine\ORM\Query\Expr;
|
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
|
||||||
use Symfony\Component\Security\Core\Role\Role;
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
@@ -37,33 +20,17 @@ use Chill\MainBundle\Export\FormatterInterface;
|
|||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a list for all activities
|
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
|
||||||
class ListActivity implements ListInterface
|
class ListActivity implements ListInterface
|
||||||
{
|
{
|
||||||
|
private ActivityRepository $activityRepository;
|
||||||
|
|
||||||
/**
|
protected EntityManagerInterface $entityManager;
|
||||||
*
|
|
||||||
* @var EntityManagerInterface
|
|
||||||
*/
|
|
||||||
protected $entityManager;
|
|
||||||
|
|
||||||
/**
|
protected TranslatorInterface $translator;
|
||||||
*
|
|
||||||
* @var TranslatorInterface
|
|
||||||
*/
|
|
||||||
protected $translator;
|
|
||||||
|
|
||||||
/**
|
protected TranslatableStringHelperInterface $translatableStringHelper;
|
||||||
*
|
|
||||||
* @var TranslatableStringHelper
|
|
||||||
*/
|
|
||||||
protected $translatableStringHelper;
|
|
||||||
|
|
||||||
protected $fields = array(
|
protected array $fields = [
|
||||||
'id',
|
'id',
|
||||||
'date',
|
'date',
|
||||||
'durationTime',
|
'durationTime',
|
||||||
@@ -75,32 +42,28 @@ class ListActivity implements ListInterface
|
|||||||
'person_firstname',
|
'person_firstname',
|
||||||
'person_lastname',
|
'person_lastname',
|
||||||
'person_id'
|
'person_id'
|
||||||
);
|
];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EntityManagerInterface $em,
|
EntityManagerInterface $em,
|
||||||
TranslatorInterface $translator,
|
TranslatorInterface $translator,
|
||||||
TranslatableStringHelper $translatableStringHelper
|
TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
)
|
ActivityRepository $activityRepository
|
||||||
{
|
) {
|
||||||
$this->entityManager = $em;
|
$this->entityManager = $em;
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
$this->translatableStringHelper = $translatableStringHelper;
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
|
$this->activityRepository = $activityRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* @param FormBuilderInterface $builder
|
|
||||||
*/
|
|
||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
$builder->add('fields', ChoiceType::class, array(
|
$builder->add('fields', ChoiceType::class, [
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
'choices' => array_combine($this->fields, $this->fields),
|
'choices' => array_combine($this->fields, $this->fields),
|
||||||
'label' => 'Fields to include in export',
|
'label' => 'Fields to include in export',
|
||||||
'constraints' => [new Callback(array(
|
'constraints' => [new Callback([
|
||||||
'callback' => function($selected, ExecutionContextInterface $context) {
|
'callback' => function($selected, ExecutionContextInterface $context) {
|
||||||
if (count($selected) === 0) {
|
if (count($selected) === 0) {
|
||||||
$context->buildViolation('You must select at least one element')
|
$context->buildViolation('You must select at least one element')
|
||||||
@@ -108,19 +71,14 @@ class ListActivity implements ListInterface
|
|||||||
->addViolation();
|
->addViolation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
))]
|
])]
|
||||||
));
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* @return type
|
|
||||||
*/
|
|
||||||
public function getAllowedFormattersTypes()
|
public function getAllowedFormattersTypes()
|
||||||
{
|
{
|
||||||
return array(FormatterInterface::TYPE_LIST);
|
return [FormatterInterface::TYPE_LIST];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDescription()
|
public function getDescription()
|
||||||
@@ -133,29 +91,32 @@ class ListActivity implements ListInterface
|
|||||||
switch ($key)
|
switch ($key)
|
||||||
{
|
{
|
||||||
case 'date' :
|
case 'date' :
|
||||||
return function($value) {
|
return static function($value) {
|
||||||
if ($value === '_header') return 'date';
|
if ($value === '_header') {
|
||||||
|
return 'date';
|
||||||
|
}
|
||||||
|
|
||||||
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
|
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
|
||||||
|
|
||||||
return $date->format('d-m-Y');
|
return $date->format('d-m-Y');
|
||||||
};
|
};
|
||||||
case 'attendee':
|
case 'attendee':
|
||||||
return function($value) {
|
return static function($value) {
|
||||||
if ($value === '_header') return 'attendee';
|
if ($value === '_header') {
|
||||||
|
return 'attendee';
|
||||||
|
}
|
||||||
|
|
||||||
return $value ? 1 : 0;
|
return $value ? 1 : 0;
|
||||||
};
|
};
|
||||||
case 'list_reasons' :
|
case 'list_reasons' :
|
||||||
/* @var $activityReasonsRepository EntityRepository */
|
$activityRepository = $this->activityRepository;
|
||||||
$activityRepository = $this->entityManager
|
|
||||||
->getRepository('ChillActivityBundle:Activity');
|
|
||||||
|
|
||||||
return function($value) use ($activityRepository) {
|
return function($value) use ($activityRepository): string {
|
||||||
if ($value === '_header') return 'activity reasons';
|
if ($value === '_header') {
|
||||||
|
return 'activity reasons';
|
||||||
|
}
|
||||||
|
|
||||||
$activity = $activityRepository
|
$activity = $activityRepository->find($value);
|
||||||
->find($value);
|
|
||||||
|
|
||||||
return implode(", ", array_map(function(ActivityReason $r) {
|
return implode(", ", array_map(function(ActivityReason $r) {
|
||||||
|
|
||||||
@@ -168,21 +129,25 @@ class ListActivity implements ListInterface
|
|||||||
};
|
};
|
||||||
case 'circle_name' :
|
case 'circle_name' :
|
||||||
return function($value) {
|
return function($value) {
|
||||||
if ($value === '_header') return 'circle';
|
if ($value === '_header') {
|
||||||
|
return 'circle';
|
||||||
|
}
|
||||||
|
|
||||||
return $this->translatableStringHelper
|
return $this->translatableStringHelper->localize(json_decode($value, true));
|
||||||
->localize(json_decode($value, true));
|
|
||||||
};
|
};
|
||||||
case 'type_name' :
|
case 'type_name' :
|
||||||
return function($value) {
|
return function($value) {
|
||||||
if ($value === '_header') return 'activity type';
|
if ($value === '_header') {
|
||||||
|
return 'activity type';
|
||||||
|
}
|
||||||
|
|
||||||
return $this->translatableStringHelper
|
return $this->translatableStringHelper->localize(json_decode($value, true));
|
||||||
->localize(json_decode($value, true));
|
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return function($value) use ($key) {
|
return static function($value) use ($key) {
|
||||||
if ($value === '_header') return $key;
|
if ($value === '_header') {
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
};
|
};
|
||||||
@@ -209,14 +174,13 @@ class ListActivity implements ListInterface
|
|||||||
return 'activity';
|
return 'activity';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = array())
|
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||||
{
|
{
|
||||||
$centers = array_map(function($el) { return $el['center']; }, $acl);
|
$centers = array_map(function($el) { return $el['center']; }, $acl);
|
||||||
|
|
||||||
// throw an error if any fields are present
|
// throw an error if any fields are present
|
||||||
if (!\array_key_exists('fields', $data)) {
|
if (!\array_key_exists('fields', $data)) {
|
||||||
throw new \Doctrine\DBAL\Exception\InvalidArgumentException("any fields "
|
throw new InvalidArgumentException('Any fields have been checked.');
|
||||||
. "have been checked");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$qb = $this->entityManager->createQueryBuilder();
|
$qb = $this->entityManager->createQueryBuilder();
|
||||||
@@ -227,7 +191,6 @@ class ListActivity implements ListInterface
|
|||||||
->join('person.center', 'center')
|
->join('person.center', 'center')
|
||||||
->andWhere('center IN (:authorized_centers)')
|
->andWhere('center IN (:authorized_centers)')
|
||||||
->setParameter('authorized_centers', $centers);
|
->setParameter('authorized_centers', $centers);
|
||||||
;
|
|
||||||
|
|
||||||
foreach ($this->fields as $f) {
|
foreach ($this->fields as $f) {
|
||||||
if (in_array($f, $data['fields'])) {
|
if (in_array($f, $data['fields'])) {
|
||||||
@@ -269,8 +232,6 @@ class ListActivity implements ListInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return $qb;
|
return $qb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +242,7 @@ class ListActivity implements ListInterface
|
|||||||
|
|
||||||
public function supportsModifiers()
|
public function supportsModifiers()
|
||||||
{
|
{
|
||||||
return array('activity', 'person');
|
return ['activity', 'person'];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -4,12 +4,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\ActivityBundle\Export\Export;
|
namespace Chill\ActivityBundle\Export\Export;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityRepository;
|
||||||
use Chill\MainBundle\Export\ExportInterface;
|
use Chill\MainBundle\Export\ExportInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Chill\MainBundle\Export\FormatterInterface;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Security\Core\Role\Role;
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
use Doctrine\ORM\Query;
|
use Doctrine\ORM\Query;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This export allow to compute stats on activity duration.
|
* This export allow to compute stats on activity duration.
|
||||||
@@ -18,7 +19,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
|||||||
*/
|
*/
|
||||||
class StatActivityDuration implements ExportInterface
|
class StatActivityDuration implements ExportInterface
|
||||||
{
|
{
|
||||||
protected EntityManagerInterface $entityManager;
|
private ActivityRepository $activityRepository;
|
||||||
|
|
||||||
public const SUM = 'sum';
|
public const SUM = 'sum';
|
||||||
|
|
||||||
@@ -30,13 +31,15 @@ class StatActivityDuration implements ExportInterface
|
|||||||
/**
|
/**
|
||||||
* @param string $action the stat to perform
|
* @param string $action the stat to perform
|
||||||
*/
|
*/
|
||||||
public function __construct(EntityManagerInterface $em, string $action = 'sum')
|
public function __construct(
|
||||||
{
|
ActivityRepository $activityRepository,
|
||||||
$this->entityManager = $em;
|
string $action = 'sum'
|
||||||
|
) {
|
||||||
$this->action = $action;
|
$this->action = $action;
|
||||||
|
$this->activityRepository = $activityRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -68,7 +71,7 @@ class StatActivityDuration implements ExportInterface
|
|||||||
$acl
|
$acl
|
||||||
);
|
);
|
||||||
|
|
||||||
$qb = $this->entityManager->createQueryBuilder();
|
$qb = $this->activityRepository->createQueryBuilder('activity');
|
||||||
|
|
||||||
$select = null;
|
$select = null;
|
||||||
|
|
||||||
@@ -77,7 +80,6 @@ class StatActivityDuration implements ExportInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $qb->select($select)
|
return $qb->select($select)
|
||||||
->from('ChillActivityBundle:Activity', 'activity')
|
|
||||||
->join('activity.person', 'person')
|
->join('activity.person', 'person')
|
||||||
->join('person.center', 'center')
|
->join('person.center', 'center')
|
||||||
->where($qb->expr()->in('center', ':centers'))
|
->where($qb->expr()->in('center', ':centers'))
|
||||||
@@ -96,7 +98,7 @@ class StatActivityDuration implements ExportInterface
|
|||||||
|
|
||||||
public function getAllowedFormattersTypes()
|
public function getAllowedFormattersTypes()
|
||||||
{
|
{
|
||||||
return array(\Chill\MainBundle\Export\FormatterInterface::TYPE_TABULAR);
|
return [FormatterInterface::TYPE_TABULAR];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
public function getLabels($key, array $values, $data)
|
||||||
|
@@ -1,25 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Export\Filter;
|
namespace Chill\ActivityBundle\Export\Filter;
|
||||||
|
|
||||||
use Chill\MainBundle\Export\FilterInterface;
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\FormEvent;
|
use Symfony\Component\Form\FormEvent;
|
||||||
use Symfony\Component\Form\FormEvents;
|
use Symfony\Component\Form\FormEvents;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||||
@@ -28,31 +15,21 @@ use Chill\MainBundle\Form\Type\Export\FilterType;
|
|||||||
use Doctrine\ORM\Query\Expr;
|
use Doctrine\ORM\Query\Expr;
|
||||||
use Symfony\Component\Translation\TranslatorInterface;
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
|
||||||
class ActivityDateFilter implements FilterInterface
|
class ActivityDateFilter implements FilterInterface
|
||||||
{
|
{
|
||||||
/**
|
protected TranslatorInterface $translator;
|
||||||
*
|
|
||||||
* @var TranslatorInterface
|
|
||||||
*/
|
|
||||||
protected $translator;
|
|
||||||
|
|
||||||
function __construct(TranslatorInterface $translator)
|
function __construct(TranslatorInterface $translator)
|
||||||
{
|
{
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function addRole()
|
public function addRole()
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
{
|
{
|
||||||
$where = $qb->getDQLPart('where');
|
$where = $qb->getDQLPart('where');
|
||||||
$clause = $qb->expr()->between('activity.date', ':date_from',
|
$clause = $qb->expr()->between('activity.date', ':date_from',
|
||||||
@@ -74,23 +51,31 @@ class ActivityDateFilter implements FilterInterface
|
|||||||
return 'activity';
|
return 'activity';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
$builder->add('date_from', DateType::class, array(
|
$builder->add(
|
||||||
'label' => "Activities after this date",
|
'date_from',
|
||||||
|
DateType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Activities after this date',
|
||||||
'data' => new \DateTime(),
|
'data' => new \DateTime(),
|
||||||
'attr' => array('class' => 'datepicker'),
|
'attr' => ['class' => 'datepicker'],
|
||||||
'widget'=> 'single_text',
|
'widget'=> 'single_text',
|
||||||
'format' => 'dd-MM-yyyy',
|
'format' => 'dd-MM-yyyy',
|
||||||
));
|
]
|
||||||
|
);
|
||||||
|
|
||||||
$builder->add('date_to', DateType::class, array(
|
$builder->add(
|
||||||
'label' => "Activities before this date",
|
'date_to',
|
||||||
|
DateType::class,
|
||||||
|
[
|
||||||
|
'label' => 'Activities before this date',
|
||||||
'data' => new \DateTime(),
|
'data' => new \DateTime(),
|
||||||
'attr' => array('class' => 'datepicker'),
|
'attr' => ['class' => 'datepicker'],
|
||||||
'widget'=> 'single_text',
|
'widget'=> 'single_text',
|
||||||
'format' => 'dd-MM-yyyy',
|
'format' => 'dd-MM-yyyy',
|
||||||
));
|
]
|
||||||
|
);
|
||||||
|
|
||||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
|
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
|
||||||
/* @var $filterForm \Symfony\Component\Form\FormInterface */
|
/* @var $filterForm \Symfony\Component\Form\FormInterface */
|
||||||
@@ -132,17 +117,18 @@ class ActivityDateFilter implements FilterInterface
|
|||||||
|
|
||||||
public function describeAction($data, $format = 'string')
|
public function describeAction($data, $format = 'string')
|
||||||
{
|
{
|
||||||
return array(
|
return [
|
||||||
"Filtered by date of activity: only between %date_from% and %date_to%",
|
'Filtered by date of activity: only between %date_from% and %date_to%',
|
||||||
array(
|
[
|
||||||
"%date_from%" => $data['date_from']->format('d-m-Y'),
|
'%date_from%' => $data['date_from']->format('d-m-Y'),
|
||||||
'%date_to%' => $data['date_to']->format('d-m-Y')
|
'%date_to%' => $data['date_to']->format('d-m-Y')
|
||||||
));
|
]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTitle()
|
public function getTitle()
|
||||||
{
|
{
|
||||||
return "Filtered by date activity";
|
return 'Filtered by date activity';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,25 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Copyright (C) 2016 Champs-Libres <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Export\Filter;
|
namespace Chill\ActivityBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
|
||||||
use Chill\MainBundle\Export\FilterInterface;
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
@@ -28,41 +15,24 @@ use Chill\MainBundle\Templating\TranslatableStringHelper;
|
|||||||
use Doctrine\ORM\Query\Expr;
|
use Doctrine\ORM\Query\Expr;
|
||||||
use Symfony\Component\Security\Core\Role\Role;
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
||||||
use Doctrine\ORM\EntityRepository;
|
|
||||||
use Doctrine\ORM\Query\Expr\Join;
|
use Doctrine\ORM\Query\Expr\Join;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||||
|
|
||||||
/**
|
class ActivityReasonFilter implements FilterInterface, ExportElementValidatedInterface
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
|
||||||
class ActivityReasonFilter implements FilterInterface,
|
|
||||||
ExportElementValidatedInterface
|
|
||||||
{
|
{
|
||||||
/**
|
protected TranslatableStringHelperInterface $translatableStringHelper;
|
||||||
*
|
|
||||||
* @var TranslatableStringHelper
|
|
||||||
*/
|
|
||||||
protected $translatableStringHelper;
|
|
||||||
|
|
||||||
/**
|
protected ActivityReasonRepository $activityReasonRepository;
|
||||||
* The repository for activity reasons
|
|
||||||
*
|
|
||||||
* @var EntityRepository
|
|
||||||
*/
|
|
||||||
protected $reasonRepository;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
TranslatableStringHelper $helper,
|
TranslatableStringHelper $helper,
|
||||||
EntityRepository $reasonRepository
|
ActivityReasonRepository $activityReasonRepository
|
||||||
) {
|
) {
|
||||||
$this->translatableStringHelper = $helper;
|
$this->translatableStringHelper = $helper;
|
||||||
$this->reasonRepository = $reasonRepository;
|
$this->activityReasonRepository = $activityReasonRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function alterQuery(QueryBuilder $qb, $data)
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
{
|
{
|
||||||
$where = $qb->getDQLPart('where');
|
$where = $qb->getDQLPart('where');
|
||||||
@@ -75,7 +45,7 @@ class ActivityReasonFilter implements FilterInterface,
|
|||||||
&&
|
&&
|
||||||
!$this->checkJoinAlreadyDefined($join['activity'], 'reasons')
|
!$this->checkJoinAlreadyDefined($join['activity'], 'reasons')
|
||||||
)
|
)
|
||||||
OR
|
||
|
||||||
(! array_key_exists('activity', $join))
|
(! array_key_exists('activity', $join))
|
||||||
) {
|
) {
|
||||||
$qb->add(
|
$qb->add(
|
||||||
@@ -101,7 +71,7 @@ class ActivityReasonFilter implements FilterInterface,
|
|||||||
* @param Join[] $joins
|
* @param Join[] $joins
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
private function checkJoinAlreadyDefined(array $joins, $alias)
|
private function checkJoinAlreadyDefined(array $joins, $alias): bool
|
||||||
{
|
{
|
||||||
foreach ($joins as $join) {
|
foreach ($joins as $join) {
|
||||||
if ($join->getAlias() === $alias) {
|
if ($join->getAlias() === $alias) {
|
||||||
@@ -119,26 +89,20 @@ class ActivityReasonFilter implements FilterInterface,
|
|||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
//create a local copy of translatableStringHelper
|
$builder->add('reasons', EntityType::class, [
|
||||||
$helper = $this->translatableStringHelper;
|
'class' => ActivityReason::class,
|
||||||
|
'choice_label' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getName()),
|
||||||
$builder->add('reasons', EntityType::class, array(
|
'group_by' => fn(ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
|
||||||
'class' => 'ChillActivityBundle:ActivityReason',
|
|
||||||
'choice_label' => function (ActivityReason $reason) use ($helper) {
|
|
||||||
return $helper->localize($reason->getName());
|
|
||||||
},
|
|
||||||
'group_by' => function(ActivityReason $reason) use ($helper) {
|
|
||||||
return $helper->localize($reason->getCategory()->getName());
|
|
||||||
},
|
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'expanded' => false
|
'expanded' => false
|
||||||
));
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validateForm($data, ExecutionContextInterface $context)
|
public function validateForm($data, ExecutionContextInterface $context)
|
||||||
{
|
{
|
||||||
if ($data['reasons'] === null || count($data['reasons']) === 0) {
|
if ($data['reasons'] === null || count($data['reasons']) === 0) {
|
||||||
$context->buildViolation("At least one reason must be choosen")
|
$context
|
||||||
|
->buildViolation('At least one reason must be chosen')
|
||||||
->addViolation();
|
->addViolation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,13 +121,15 @@ class ActivityReasonFilter implements FilterInterface,
|
|||||||
{
|
{
|
||||||
// collect all the reasons'name used in this filter in one array
|
// collect all the reasons'name used in this filter in one array
|
||||||
$reasonsNames = array_map(
|
$reasonsNames = array_map(
|
||||||
function(ActivityReason $r) {
|
fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
|
||||||
return "\"".$this->translatableStringHelper->localize($r->getName())."\"";
|
$this->activityReasonRepository->findBy(array('id' => $data['reasons']->toArray()))
|
||||||
},
|
|
||||||
$this->reasonRepository->findBy(array('id' => $data['reasons']->toArray()))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return array("Filtered by reasons: only %list%",
|
return [
|
||||||
["%list%" => implode(", ", $reasonsNames)]);
|
'Filtered by reasons: only %list%',
|
||||||
|
[
|
||||||
|
'%list%' => implode(", ", $reasonsNames),
|
||||||
|
]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,67 +1,37 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Copyright (C) 2018 Champs-Libres <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Export\Filter;
|
namespace Chill\ActivityBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
|
||||||
use Chill\MainBundle\Export\FilterInterface;
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
|
||||||
use Doctrine\ORM\Query\Expr;
|
use Doctrine\ORM\Query\Expr;
|
||||||
use Symfony\Component\Security\Core\Role\Role;
|
use Symfony\Component\Security\Core\Role\Role;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
|
||||||
use Doctrine\ORM\EntityRepository;
|
|
||||||
use Doctrine\ORM\Query\Expr\Join;
|
use Doctrine\ORM\Query\Expr\Join;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||||
use Chill\ActivityBundle\Entity\ActivityType;
|
use Chill\ActivityBundle\Entity\ActivityType;
|
||||||
|
|
||||||
/**
|
class ActivityTypeFilter implements FilterInterface, ExportElementValidatedInterface
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class ActivityTypeFilter implements FilterInterface,
|
|
||||||
ExportElementValidatedInterface
|
|
||||||
{
|
{
|
||||||
/**
|
protected TranslatableStringHelperInterface $translatableStringHelper;
|
||||||
*
|
|
||||||
* @var TranslatableStringHelper
|
|
||||||
*/
|
|
||||||
protected $translatableStringHelper;
|
|
||||||
|
|
||||||
/**
|
protected ActivityTypeRepository $activityTypeRepository;
|
||||||
* The repository for activity reasons
|
|
||||||
*
|
|
||||||
* @var EntityRepository
|
|
||||||
*/
|
|
||||||
protected $typeRepository;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
TranslatableStringHelper $helper,
|
TranslatableStringHelperInterface $translatableStringHelper,
|
||||||
EntityRepository $typeRepository
|
ActivityTypeRepository $activityTypeRepository
|
||||||
) {
|
) {
|
||||||
$this->translatableStringHelper = $helper;
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
$this->typeRepository = $typeRepository;
|
$this->activityTypeRepository = $activityTypeRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function alterQuery(QueryBuilder $qb, $data)
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
{
|
{
|
||||||
$where = $qb->getDQLPart('where');
|
$where = $qb->getDQLPart('where');
|
||||||
@@ -101,23 +71,23 @@ class ActivityTypeFilter implements FilterInterface,
|
|||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
//create a local copy of translatableStringHelper
|
$builder->add(
|
||||||
$helper = $this->translatableStringHelper;
|
'types',
|
||||||
|
EntityType::class,
|
||||||
$builder->add('types', EntityType::class, array(
|
[
|
||||||
'class' => ActivityType::class,
|
'class' => ActivityType::class,
|
||||||
'choice_label' => function (ActivityType $type) use ($helper) {
|
'choice_label' => fn(ActivityType $type) => $this->translatableStringHelper->localize($type->getName()),
|
||||||
return $helper->localize($type->getName());
|
|
||||||
},
|
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'expanded' => false
|
'expanded' => false
|
||||||
));
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validateForm($data, ExecutionContextInterface $context)
|
public function validateForm($data, ExecutionContextInterface $context)
|
||||||
{
|
{
|
||||||
if ($data['types'] === null || count($data['types']) === 0) {
|
if ($data['types'] === null || count($data['types']) === 0) {
|
||||||
$context->buildViolation("At least one type must be choosen")
|
$context
|
||||||
|
->buildViolation('At least one type must be chosen')
|
||||||
->addViolation();
|
->addViolation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,13 +106,15 @@ class ActivityTypeFilter implements FilterInterface,
|
|||||||
{
|
{
|
||||||
// collect all the reasons'name used in this filter in one array
|
// collect all the reasons'name used in this filter in one array
|
||||||
$reasonsNames = array_map(
|
$reasonsNames = array_map(
|
||||||
function(ActivityType $t) {
|
fn(ActivityType $t): string => '"' . $this->translatableStringHelper->localize($t->getName()) . '"',
|
||||||
return "\"".$this->translatableStringHelper->localize($t->getName())."\"";
|
$this->activityTypeRepository->findBy(['id' => $data['types']->toArray()])
|
||||||
},
|
|
||||||
$this->typeRepository->findBy(array('id' => $data['types']->toArray()))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return array("Filtered by activity type: only %list%",
|
return [
|
||||||
["%list%" => implode(", ", $reasonsNames)]);
|
'Filtered by activity type: only %list%',
|
||||||
|
[
|
||||||
|
'%list%' => implode(", ", $reasonsNames),
|
||||||
|
]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,25 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Copyright (C) 2017 Champs-Libres <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Export\Filter;
|
namespace Chill\ActivityBundle\Export\Filter;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
|
||||||
use Chill\MainBundle\Export\FilterInterface;
|
use Chill\MainBundle\Export\FilterInterface;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\FormEvent;
|
use Symfony\Component\Form\FormEvent;
|
||||||
use Symfony\Component\Form\FormEvents;
|
use Symfony\Component\Form\FormEvents;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||||
@@ -29,43 +18,23 @@ use Doctrine\ORM\Query\Expr;
|
|||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Chill\ActivityBundle\Entity\ActivityReason;
|
use Chill\ActivityBundle\Entity\ActivityReason;
|
||||||
use Doctrine\ORM\EntityRepository;
|
|
||||||
use Doctrine\ORM\EntityManager;
|
|
||||||
use Chill\PersonBundle\Export\Declarations;
|
use Chill\PersonBundle\Export\Declarations;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\Translation\TranslatorInterface;
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||||
|
|
||||||
/**
|
class PersonHavingActivityBetweenDateFilter implements FilterInterface, ExportElementValidatedInterface
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
|
||||||
class PersonHavingActivityBetweenDateFilter implements FilterInterface,
|
|
||||||
ExportElementValidatedInterface
|
|
||||||
{
|
{
|
||||||
|
protected TranslatableStringHelperInterface $translatableStringHelper;
|
||||||
|
|
||||||
/**
|
protected ActivityReasonRepository $activityReasonRepository;
|
||||||
*
|
|
||||||
* @var TranslatableStringHelper
|
|
||||||
*/
|
|
||||||
protected $translatableStringHelper;
|
|
||||||
|
|
||||||
/**
|
protected TranslatorInterface $translator;
|
||||||
*
|
|
||||||
* @var EntityRepository
|
|
||||||
*/
|
|
||||||
protected $activityReasonRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @var TranslatorInterface
|
|
||||||
*/
|
|
||||||
protected $translator;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
TranslatableStringHelper $translatableStringHelper,
|
TranslatableStringHelper $translatableStringHelper,
|
||||||
EntityRepository $activityReasonRepository,
|
ActivityReasonRepository $activityReasonRepository,
|
||||||
TranslatorInterface $translator
|
TranslatorInterface $translator
|
||||||
) {
|
) {
|
||||||
$this->translatableStringHelper = $translatableStringHelper;
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
@@ -73,32 +42,32 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
|
|||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function addRole()
|
public function addRole()
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data)
|
public function alterQuery(QueryBuilder $qb, $data)
|
||||||
{
|
{
|
||||||
// create a query for activity
|
// create a query for activity
|
||||||
$sqb = $qb->getEntityManager()->createQueryBuilder();
|
$sqb = $qb->getEntityManager()->createQueryBuilder();
|
||||||
$sqb->select("person_person_having_activity.id")
|
$sqb->select('person_person_having_activity.id')
|
||||||
->from("ChillActivityBundle:Activity", "activity_person_having_activity")
|
->from('ChillActivityBundle:Activity', 'activity_person_having_activity')
|
||||||
->join("activity_person_having_activity.person", "person_person_having_activity")
|
->join('activity_person_having_activity.person', 'person_person_having_activity');
|
||||||
;
|
|
||||||
// add clause between date
|
// add clause between date
|
||||||
$sqb->where("activity_person_having_activity.date BETWEEN "
|
$sqb->where('activity_person_having_activity.date BETWEEN '
|
||||||
. ":person_having_activity_between_date_from"
|
. ':person_having_activity_between_date_from'
|
||||||
. " AND "
|
. ' AND '
|
||||||
. ":person_having_activity_between_date_to");
|
. ':person_having_activity_between_date_to');
|
||||||
|
|
||||||
// add clause activity reason
|
// add clause activity reason
|
||||||
$sqb->join('activity_person_having_activity.reasons',
|
$sqb->join('activity_person_having_activity.reasons', 'reasons_person_having_activity');
|
||||||
'reasons_person_having_activity');
|
|
||||||
$sqb->andWhere(
|
$sqb->andWhere(
|
||||||
$sqb->expr()->in(
|
$sqb->expr()->in(
|
||||||
'reasons_person_having_activity',
|
'reasons_person_having_activity', ':person_having_activity_reasons'
|
||||||
":person_having_activity_reasons")
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$where = $qb->getDQLPart('where');
|
$where = $qb->getDQLPart('where');
|
||||||
@@ -123,42 +92,36 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
|
|||||||
return Declarations::PERSON_IMPLIED_IN;
|
return Declarations::PERSON_IMPLIED_IN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder)
|
public function buildForm(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
$builder->add('date_from', DateType::class, array(
|
$builder->add('date_from', DateType::class, [
|
||||||
'label' => "Implied in an activity after this date",
|
'label' => 'Implied in an activity after this date',
|
||||||
'data' => new \DateTime(),
|
'data' => new \DateTime(),
|
||||||
'attr' => array('class' => 'datepicker'),
|
'attr' => ['class' => 'datepicker'],
|
||||||
'widget'=> 'single_text',
|
'widget'=> 'single_text',
|
||||||
'format' => 'dd-MM-yyyy',
|
'format' => 'dd-MM-yyyy',
|
||||||
));
|
]);
|
||||||
|
|
||||||
$builder->add('date_to', DateType::class, array(
|
$builder->add('date_to', DateType::class, [
|
||||||
'label' => "Implied in an activity before this date",
|
'label' => 'Implied in an activity before this date',
|
||||||
'data' => new \DateTime(),
|
'data' => new \DateTime(),
|
||||||
'attr' => array('class' => 'datepicker'),
|
'attr' => ['class' => 'datepicker'],
|
||||||
'widget'=> 'single_text',
|
'widget'=> 'single_text',
|
||||||
'format' => 'dd-MM-yyyy',
|
'format' => 'dd-MM-yyyy',
|
||||||
));
|
]);
|
||||||
|
|
||||||
$builder->add('reasons', EntityType::class, array(
|
$builder->add('reasons', EntityType::class, [
|
||||||
'class' => 'ChillActivityBundle:ActivityReason',
|
'class' => ActivityReason::class,
|
||||||
'choice_label' => function (ActivityReason $reason) {
|
'choice_label' => fn (ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getName()),
|
||||||
return $this->translatableStringHelper
|
'group_by' => fn(ActivityReason $reason): ?string => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
|
||||||
->localize($reason->getName());
|
|
||||||
},
|
|
||||||
'group_by' => function(ActivityReason $reason) {
|
|
||||||
return $this->translatableStringHelper
|
|
||||||
->localize($reason->getCategory()->getName());
|
|
||||||
},
|
|
||||||
'data' => $this->activityReasonRepository->findAll(),
|
'data' => $this->activityReasonRepository->findAll(),
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'expanded' => false,
|
'expanded' => false,
|
||||||
'label' => "Activity reasons for those activities"
|
'label' => 'Activity reasons for those activities'
|
||||||
));
|
]);
|
||||||
|
|
||||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
|
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
|
||||||
/* @var $filterForm \Symfony\Component\Form\FormInterface */
|
/* @var FormInterface $filterForm */
|
||||||
$filterForm = $event->getForm()->getParent();
|
$filterForm = $event->getForm()->getParent();
|
||||||
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
|
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
|
||||||
|
|
||||||
@@ -198,31 +161,33 @@ class PersonHavingActivityBetweenDateFilter implements FilterInterface,
|
|||||||
public function validateForm($data, ExecutionContextInterface $context)
|
public function validateForm($data, ExecutionContextInterface $context)
|
||||||
{
|
{
|
||||||
if ($data['reasons'] === null || count($data['reasons']) === 0) {
|
if ($data['reasons'] === null || count($data['reasons']) === 0) {
|
||||||
$context->buildViolation("At least one reason must be choosen")
|
$context->buildViolation('At least one reason must be chosen')
|
||||||
->addViolation();
|
->addViolation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function describeAction($data, $format = 'string')
|
public function describeAction($data, $format = 'string')
|
||||||
{
|
{
|
||||||
return array(
|
return [
|
||||||
"Filtered by person having an activity between %date_from% and "
|
'Filtered by person having an activity between %date_from% and '
|
||||||
. "%date_to% with reasons %reasons_name%",
|
. '%date_to% with reasons %reasons_name%',
|
||||||
array(
|
[
|
||||||
"%date_from%" => $data['date_from']->format('d-m-Y'),
|
'%date_from%' => $data['date_from']->format('d-m-Y'),
|
||||||
'%date_to%' => $data['date_to']->format('d-m-Y'),
|
'%date_to%' => $data['date_to']->format('d-m-Y'),
|
||||||
"%reasons_name%" => implode(", ", array_map(
|
'%reasons_name%' => implode(
|
||||||
function (ActivityReason $r) {
|
", ",
|
||||||
return '"'.$this->translatableStringHelper->
|
array_map(
|
||||||
localize($r->getName()).'"';
|
fn(ActivityReason $r): string => '"' . $this->translatableStringHelper->localize($r->getName()) . '"',
|
||||||
},
|
$data['reasons']
|
||||||
$data['reasons']))
|
)
|
||||||
));
|
)
|
||||||
|
]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTitle()
|
public function getTitle()
|
||||||
{
|
{
|
||||||
return "Filtered by person having an activity in a period";
|
return 'Filtered by person having an activity in a period';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,56 +1,29 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Chill is a software for social workers
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
|
|
||||||
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Form\Type;
|
namespace Chill\ActivityBundle\Form\Type;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
|
||||||
use Doctrine\ORM\EntityRepository;
|
|
||||||
use Chill\ActivityBundle\Entity\ActivityType;
|
use Chill\ActivityBundle\Entity\ActivityType;
|
||||||
|
|
||||||
/**
|
|
||||||
* Description of TranslatableActivityType
|
|
||||||
*
|
|
||||||
* @author Champs-Libres Coop
|
|
||||||
*/
|
|
||||||
class TranslatableActivityType extends AbstractType
|
class TranslatableActivityType extends AbstractType
|
||||||
{
|
{
|
||||||
|
protected TranslatableStringHelperInterface $translatableStringHelper;
|
||||||
|
|
||||||
/**
|
protected ActivityTypeRepository $activityTypeRepository;
|
||||||
*
|
|
||||||
* @var TranslatableStringHelper
|
|
||||||
*/
|
|
||||||
protected $translatableStringHelper;
|
|
||||||
|
|
||||||
protected $activityTypeRepository;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
TranslatableStringHelper $helper,
|
TranslatableStringHelperInterface $helper,
|
||||||
EntityRepository $activityTypeRepository
|
ActivityTypeRepository $activityTypeRepository
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$this->translatableStringHelper = $helper;
|
$this->translatableStringHelper = $helper;
|
||||||
$this->activityTypeRepository = $activityTypeRepository;
|
$this->activityTypeRepository = $activityTypeRepository;
|
||||||
}
|
}
|
||||||
@@ -65,22 +38,21 @@ class TranslatableActivityType extends AbstractType
|
|||||||
return EntityType::class;
|
return EntityType::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, array $options) {
|
public function buildForm(FormBuilderInterface $builder, array $options) {
|
||||||
/* @var $qb \Doctrine\ORM\QueryBuilder */
|
/* @var QueryBuilder $qb */
|
||||||
$qb = $options['query_builder'];
|
$qb = $options['query_builder'];
|
||||||
|
|
||||||
if ($options['active_only'] === true) {
|
if ($options['active_only'] === true) {
|
||||||
$qb->where($qb->expr()->eq('at.active', ':active'));
|
$qb->where($qb->expr()->eq('at.active', ':active'));
|
||||||
$qb->setParameter('active', true, \Doctrine\DBAL\Types\Types::BOOLEAN);
|
$qb->setParameter('active', true, Types::BOOLEAN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
{
|
{
|
||||||
|
|
||||||
$resolver->setDefaults(
|
$resolver->setDefaults(
|
||||||
array(
|
array(
|
||||||
'class' => 'ChillActivityBundle:ActivityType',
|
'class' => ActivityType::class,
|
||||||
'active_only' => true,
|
'active_only' => true,
|
||||||
'query_builder' => $this->activityTypeRepository
|
'query_builder' => $this->activityTypeRepository
|
||||||
->createQueryBuilder('at'),
|
->createQueryBuilder('at'),
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\ActivityBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\ActivityReasonCategory;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method ActivityReasonCategory|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
|
* @method ActivityReasonCategory|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
|
* @method ActivityReasonCategory[] findAll()
|
||||||
|
* @method ActivityReasonCategory[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
|
*/
|
||||||
|
class ActivityReasonCategoryRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, ActivityReasonCategory::class);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\ActivityBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\ActivityReason;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method ActivityReason|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
|
* @method ActivityReason|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
|
* @method ActivityReason[] findAll()
|
||||||
|
* @method ActivityReason[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
|
*/
|
||||||
|
class ActivityReasonRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, ActivityReason::class);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,24 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Chill is a software for social workers
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
|
|
||||||
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\ActivityBundle\Repository;
|
namespace Chill\ActivityBundle\Repository;
|
||||||
|
|
||||||
@@ -29,10 +11,10 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
|||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method AccompanyingPeriodParticipation|null find($id, $lockMode = null, $lockVersion = null)
|
* @method Activity|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
* @method AccompanyingPeriodParticipation|null findOneBy(array $criteria, array $orderBy = null)
|
* @method Activity|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
* @method AccompanyingPeriodParticipation[] findAll()
|
* @method Activity[] findAll()
|
||||||
* @method AccompanyingPeriodParticipation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
* @method Activity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
*/
|
*/
|
||||||
class ActivityRepository extends ServiceEntityRepository
|
class ActivityRepository extends ServiceEntityRepository
|
||||||
{
|
{
|
||||||
@@ -42,12 +24,7 @@ class ActivityRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $person
|
* @return Activity[]
|
||||||
* @param array $scopes
|
|
||||||
* @param string[] $orderBy
|
|
||||||
* @param int $limit
|
|
||||||
* @param int $offset
|
|
||||||
* @return array|Activity[]
|
|
||||||
*/
|
*/
|
||||||
public function findByPersonImplied(Person $person, array $scopes, ?array $orderBy = [ 'date' => 'DESC'], ?int $limit = 100, ?int $offset = 0): array
|
public function findByPersonImplied(Person $person, array $scopes, ?array $orderBy = [ 'date' => 'DESC'], ?int $limit = 100, ?int $offset = 0): array
|
||||||
{
|
{
|
||||||
@@ -63,8 +40,7 @@ class ActivityRepository extends ServiceEntityRepository
|
|||||||
':person MEMBER OF a.persons'
|
':person MEMBER OF a.persons'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
->setParameter('person', $person)
|
->setParameter('person', $person);
|
||||||
;
|
|
||||||
|
|
||||||
foreach ($orderBy as $k => $dir) {
|
foreach ($orderBy as $k => $dir) {
|
||||||
$qb->addOrderBy('a.'.$k, $dir);
|
$qb->addOrderBy('a.'.$k, $dir);
|
||||||
@@ -72,17 +48,11 @@ class ActivityRepository extends ServiceEntityRepository
|
|||||||
|
|
||||||
$qb->setMaxResults($limit)->setFirstResult($offset);
|
$qb->setMaxResults($limit)->setFirstResult($offset);
|
||||||
|
|
||||||
return $qb->getQuery()
|
return $qb->getQuery()->getResult();
|
||||||
->getResult();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param AccompanyingPeriod $period
|
* @return Activity[]
|
||||||
* @param array $scopes
|
|
||||||
* @param int|null $limit
|
|
||||||
* @param int|null $offset
|
|
||||||
* @param array|string[] $orderBy
|
|
||||||
* @return array|Activity[]
|
|
||||||
*/
|
*/
|
||||||
public function findByAccompanyingPeriod(AccompanyingPeriod $period, array $scopes, ?bool $allowNullScope = false, ?int $limit = 100, ?int $offset = 0, array $orderBy = ['date' => 'desc']): array
|
public function findByAccompanyingPeriod(AccompanyingPeriod $period, array $scopes, ?bool $allowNullScope = false, ?int $limit = 100, ?int $offset = 0, array $orderBy = ['date' => 'desc']): array
|
||||||
{
|
{
|
||||||
@@ -92,8 +62,7 @@ class ActivityRepository extends ServiceEntityRepository
|
|||||||
if (!$allowNullScope) {
|
if (!$allowNullScope) {
|
||||||
$qb
|
$qb
|
||||||
->where($qb->expr()->in('a.scope', ':scopes'))
|
->where($qb->expr()->in('a.scope', ':scopes'))
|
||||||
->setParameter('scopes', $scopes)
|
->setParameter('scopes', $scopes);
|
||||||
;
|
|
||||||
} else {
|
} else {
|
||||||
$qb
|
$qb
|
||||||
->where(
|
->where(
|
||||||
@@ -102,16 +71,14 @@ class ActivityRepository extends ServiceEntityRepository
|
|||||||
$qb->expr()->isNull('a.scope')
|
$qb->expr()->isNull('a.scope')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
->setParameter('scopes', $scopes)
|
->setParameter('scopes', $scopes);
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$qb
|
$qb
|
||||||
->andWhere(
|
->andWhere(
|
||||||
$qb->expr()->eq('a.accompanyingPeriod', ':period')
|
$qb->expr()->eq('a.accompanyingPeriod', ':period')
|
||||||
)
|
)
|
||||||
->setParameter('period', $period)
|
->setParameter('period', $period);
|
||||||
;
|
|
||||||
|
|
||||||
foreach ($orderBy as $k => $dir) {
|
foreach ($orderBy as $k => $dir) {
|
||||||
$qb->addOrderBy('a.'.$k, $dir);
|
$qb->addOrderBy('a.'.$k, $dir);
|
||||||
@@ -119,7 +86,6 @@ class ActivityRepository extends ServiceEntityRepository
|
|||||||
|
|
||||||
$qb->setMaxResults($limit)->setFirstResult($offset);
|
$qb->setMaxResults($limit)->setFirstResult($offset);
|
||||||
|
|
||||||
return $qb->getQuery()
|
return $qb->getQuery()->getResult();
|
||||||
->getResult();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\ActivityBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\ActivityTypeCategory;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method ActivityTypeCategory|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
|
* @method ActivityTypeCategory|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
|
* @method ActivityTypeCategory[] findAll()
|
||||||
|
* @method ActivityTypeCategory[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
|
*/
|
||||||
|
class ActivityTypeCategoryRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, ActivityTypeCategory::class);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\ActivityBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\ActivityType;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method ActivityType|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
|
* @method ActivityType|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
|
* @method ActivityType[] findAll()
|
||||||
|
* @method ActivityType[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
|
*/
|
||||||
|
class ActivityTypeRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, ActivityType::class);
|
||||||
|
}
|
||||||
|
}
|
@@ -11,9 +11,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="getContext === 'accompanyingCourse' && filterSuggestedPersons.length > 0">
|
<div v-if="getContext === 'accompanyingCourse' && filterSuggestedPersons.length > 0">
|
||||||
<ul>
|
<ul class="list-unstyled">
|
||||||
<li v-for="p in filterSuggestedPersons" @click="addNewPerson(p)">
|
<li v-for="p in filterSuggestedPersons" @click="addNewPerson(p)">
|
||||||
|
<span class="badge bg-primary" style="cursor: pointer;">
|
||||||
|
<i class="fa fa-plus fa-fw text-success"></i>
|
||||||
{{ p.text }}
|
{{ p.text }}
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<span class="chill_denomination">
|
<span class="chill_denomination">
|
||||||
{{ textCutted }}
|
{{ textCutted }}
|
||||||
</span>
|
</span>
|
||||||
<a class="fa fa-fw fa-times"
|
<a class="fa fa-fw fa-times text-danger text-decoration-none"
|
||||||
@click.prevent="$emit('remove', person)">
|
@click.prevent="$emit('remove', person)">
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
@@ -27,14 +27,16 @@
|
|||||||
{{ activity.type.name | localize_translatable_string }}
|
{{ activity.type.name | localize_translatable_string }}
|
||||||
|
|
||||||
<ul class="small_in_title">
|
<ul class="small_in_title">
|
||||||
|
{% if activity.location and t.locationVisible %}
|
||||||
<li>
|
<li>
|
||||||
<abbr title="{{ 'location'|trans }}">{{ 'location'|trans ~ ': ' }}</abbr>
|
<span class="item-key">{{ 'location'|trans ~ ': ' }}</span>
|
||||||
{# TODO {% if activity.location %}{{ activity.location }}{% endif %} #}
|
<span>{{ activity.location.locationType.title|localize_translatable_string }}</span>
|
||||||
Domicile de l'usager
|
{{ activity.location.name }}
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
{% if activity.user and t.userVisible %}
|
{% if activity.user and t.userVisible %}
|
||||||
<li>
|
<li>
|
||||||
<abbr title="{{ 'Referrer'|trans }}">{{ 'Referrer'|trans ~ ': ' }}</abbr>
|
<span class="item-key">{{ 'Referrer'|trans ~ ': ' }}</span>
|
||||||
{{ activity.user.usernameCanonical }}
|
{{ activity.user.usernameCanonical }}
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h2 class="chill-red">{{ 'Concerned groups'|trans }}</h2>
|
<h2 class="chill-red">{{ 'Concerned groups'|trans }}</h2>
|
||||||
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': context, 'with_display': 'bloc' } %}
|
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with {'context': context, 'with_display': 'bloc', 'badge_person': 'true' } %}
|
||||||
|
|
||||||
<h2 class="chill-red">{{ 'Activity data'|trans }}</h2>
|
<h2 class="chill-red">{{ 'Activity data'|trans }}</h2>
|
||||||
|
|
||||||
|
@@ -1,4 +1,2 @@
|
|||||||
|
|
||||||
{{ dump(notification) }}
|
|
||||||
|
|
||||||
<a href="{{ path('chill_activity_activity_show', {'id': notification.relatedEntityId }) }}">Go to Activity</a>
|
<a href="{{ path('chill_activity_activity_show', {'id': notification.relatedEntityId }) }}">Go to Activity</a>
|
||||||
|
@@ -79,8 +79,7 @@ class TimelineActivityProvider implements TimelineProviderInterface
|
|||||||
$metadataActivity = $this->em->getClassMetadata(Activity::class);
|
$metadataActivity = $this->em->getClassMetadata(Activity::class);
|
||||||
$associationMapping = $metadataActivity->getAssociationMapping('person');
|
$associationMapping = $metadataActivity->getAssociationMapping('person');
|
||||||
$role = new Role('CHILL_ACTIVITY_SEE');
|
$role = new Role('CHILL_ACTIVITY_SEE');
|
||||||
$reachableScopes = $this->helper->getReachableScopes($this->user,
|
$reachableScopes = $this->helper->getReachableScopes($this->user, $role->getRole(), $person->getCenter());
|
||||||
$role, $person->getCenter());
|
|
||||||
$whereClause = sprintf(' {activity.person_id} = ? AND {activity.scope_id} IN ({scopes_ids}) ');
|
$whereClause = sprintf(' {activity.person_id} = ? AND {activity.scope_id} IN ({scopes_ids}) ');
|
||||||
$scopes_ids = [];
|
$scopes_ids = [];
|
||||||
|
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
chill.activity.timeline:
|
chill.activity.timeline:
|
||||||
class: Chill\ActivityBundle\Timeline\TimelineActivityProvider
|
class: Chill\ActivityBundle\Timeline\TimelineActivityProvider
|
||||||
@@ -13,17 +16,14 @@ services:
|
|||||||
- { name: chill.timeline, context: 'center' }
|
- { name: chill.timeline, context: 'center' }
|
||||||
|
|
||||||
Chill\ActivityBundle\Menu\:
|
Chill\ActivityBundle\Menu\:
|
||||||
autowire: true
|
|
||||||
autoconfigure: true
|
|
||||||
resource: '../Menu/'
|
resource: '../Menu/'
|
||||||
tags: ['chill.menu_builder']
|
tags: ['chill.menu_builder']
|
||||||
|
|
||||||
Chill\ActivityBundle\Notification\:
|
Chill\ActivityBundle\Notification\:
|
||||||
autowire: true
|
|
||||||
autoconfigure: true
|
|
||||||
resource: '../Notification'
|
resource: '../Notification'
|
||||||
|
|
||||||
Chill\ActivityBundle\Security\Authorization\:
|
Chill\ActivityBundle\Security\Authorization\:
|
||||||
resource: '../Security/Authorization/'
|
resource: '../Security/Authorization/'
|
||||||
autowire: true
|
|
||||||
autoconfigure: true
|
Chill\ActivityBundle\Repository\:
|
||||||
|
resource: '../Repository/'
|
||||||
|
@@ -1,57 +1,40 @@
|
|||||||
services:
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
chill.activity.export.count_activity:
|
chill.activity.export.count_activity:
|
||||||
class: Chill\ActivityBundle\Export\Export\CountActivity
|
class: Chill\ActivityBundle\Export\Export\CountActivity
|
||||||
arguments:
|
|
||||||
- "@doctrine.orm.entity_manager"
|
|
||||||
tags:
|
tags:
|
||||||
- { name: chill.export, alias: 'count_activity' }
|
- { name: chill.export, alias: 'count_activity' }
|
||||||
|
|
||||||
chill.activity.export.sum_activity_duration:
|
chill.activity.export.sum_activity_duration:
|
||||||
class: Chill\ActivityBundle\Export\Export\StatActivityDuration
|
class: Chill\ActivityBundle\Export\Export\StatActivityDuration
|
||||||
arguments:
|
|
||||||
- "@doctrine.orm.entity_manager"
|
|
||||||
- "sum"
|
|
||||||
tags:
|
tags:
|
||||||
- { name: chill.export, alias: 'sum_activity_duration' }
|
- { name: chill.export, alias: 'sum_activity_duration' }
|
||||||
|
|
||||||
chill.activity.export.list_activity:
|
chill.activity.export.list_activity:
|
||||||
class: Chill\ActivityBundle\Export\Export\ListActivity
|
class: Chill\ActivityBundle\Export\Export\ListActivity
|
||||||
arguments:
|
|
||||||
- "@doctrine.orm.entity_manager"
|
|
||||||
- "@translator"
|
|
||||||
- "@chill.main.helper.translatable_string"
|
|
||||||
tags:
|
tags:
|
||||||
- { name: chill.export, alias: 'list_activity' }
|
- { name: chill.export, alias: 'list_activity' }
|
||||||
|
|
||||||
chill.activity.export.reason_filter:
|
chill.activity.export.reason_filter:
|
||||||
class: Chill\ActivityBundle\Export\Filter\ActivityReasonFilter
|
class: Chill\ActivityBundle\Export\Filter\ActivityReasonFilter
|
||||||
arguments:
|
|
||||||
- "@chill.main.helper.translatable_string"
|
|
||||||
- "@chill_activity.repository.reason"
|
|
||||||
tags:
|
tags:
|
||||||
- { name: chill.export_filter, alias: 'activity_reason_filter' }
|
- { name: chill.export_filter, alias: 'activity_reason_filter' }
|
||||||
|
|
||||||
chill.activity.export.type_filter:
|
chill.activity.export.type_filter:
|
||||||
class: Chill\ActivityBundle\Export\Filter\ActivityTypeFilter
|
class: Chill\ActivityBundle\Export\Filter\ActivityTypeFilter
|
||||||
arguments:
|
|
||||||
- "@chill.main.helper.translatable_string"
|
|
||||||
- "@chill_activity.repository.activity_type"
|
|
||||||
tags:
|
tags:
|
||||||
- { name: chill.export_filter, alias: 'activity_type_filter' }
|
- { name: chill.export_filter, alias: 'activity_type_filter' }
|
||||||
|
|
||||||
chill.activity.export.date_filter:
|
chill.activity.export.date_filter:
|
||||||
class: Chill\ActivityBundle\Export\Filter\ActivityDateFilter
|
class: Chill\ActivityBundle\Export\Filter\ActivityDateFilter
|
||||||
arguments:
|
|
||||||
- "@translator"
|
|
||||||
tags:
|
tags:
|
||||||
- { name: chill.export_filter, alias: 'activity_date_filter' }
|
- { name: chill.export_filter, alias: 'activity_date_filter' }
|
||||||
|
|
||||||
chill.activity.export.person_having_an_activity_between_date_filter:
|
chill.activity.export.person_having_an_activity_between_date_filter:
|
||||||
class: Chill\ActivityBundle\Export\Filter\PersonHavingActivityBetweenDateFilter
|
class: Chill\ActivityBundle\Export\Filter\PersonHavingActivityBetweenDateFilter
|
||||||
arguments:
|
|
||||||
- "@chill.main.helper.translatable_string"
|
|
||||||
- "@chill_activity.repository.reason"
|
|
||||||
- "@translator"
|
|
||||||
tags:
|
tags:
|
||||||
- #0 register as a filter
|
- #0 register as a filter
|
||||||
name: chill.export_filter
|
name: chill.export_filter
|
||||||
@@ -59,24 +42,15 @@ services:
|
|||||||
|
|
||||||
chill.activity.export.reason_aggregator:
|
chill.activity.export.reason_aggregator:
|
||||||
class: Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator
|
class: Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator
|
||||||
arguments:
|
|
||||||
- "@chill_activity.repository.reason_category"
|
|
||||||
- "@chill_activity.repository.reason"
|
|
||||||
- "@chill.main.helper.translatable_string"
|
|
||||||
tags:
|
tags:
|
||||||
- { name: chill.export_aggregator, alias: activity_reason_aggregator }
|
- { name: chill.export_aggregator, alias: activity_reason_aggregator }
|
||||||
|
|
||||||
chill.activity.export.type_aggregator:
|
chill.activity.export.type_aggregator:
|
||||||
class: Chill\ActivityBundle\Export\Aggregator\ActivityTypeAggregator
|
class: Chill\ActivityBundle\Export\Aggregator\ActivityTypeAggregator
|
||||||
arguments:
|
|
||||||
- "@chill_activity.repository.activity_type"
|
|
||||||
- "@chill.main.helper.translatable_string"
|
|
||||||
tags:
|
tags:
|
||||||
- { name: chill.export_aggregator, alias: activity_type_aggregator }
|
- { name: chill.export_aggregator, alias: activity_type_aggregator }
|
||||||
|
|
||||||
chill.activity.export.user_aggregator:
|
chill.activity.export.user_aggregator:
|
||||||
class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator
|
class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator
|
||||||
arguments:
|
|
||||||
$em: "@doctrine.orm.entity_manager"
|
|
||||||
tags:
|
tags:
|
||||||
- { name: chill.export_aggregator, alias: activity_user_aggregator }
|
- { name: chill.export_aggregator, alias: activity_user_aggregator }
|
||||||
|
@@ -1,28 +1,12 @@
|
|||||||
---
|
---
|
||||||
services:
|
services:
|
||||||
chill_activity.repository.activity_type:
|
chill_activity.repository.activity_type: '@Chill\ActivityBundle\Repository\ActivityTypeRepository'
|
||||||
class: Doctrine\ORM\EntityRepository
|
chill_activity.repository.reason: '@Chill\ActivityBundle\Repository\ActivityReasonRepository'
|
||||||
factory: ['@doctrine.orm.entity_manager', getRepository]
|
chill_activity.repository.reason_category: '@Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository'
|
||||||
arguments:
|
|
||||||
- 'Chill\ActivityBundle\Entity\ActivityType'
|
|
||||||
|
|
||||||
chill_activity.repository.reason:
|
|
||||||
class: Doctrine\ORM\EntityRepository
|
|
||||||
factory: ['@doctrine.orm.entity_manager', getRepository]
|
|
||||||
arguments:
|
|
||||||
- 'Chill\ActivityBundle\Entity\ActivityReason'
|
|
||||||
|
|
||||||
chill_activity.repository.reason_category:
|
|
||||||
class: Doctrine\ORM\EntityRepository
|
|
||||||
factory: ['@doctrine.orm.entity_manager', getRepository]
|
|
||||||
arguments:
|
|
||||||
- 'Chill\ActivityBundle\Entity\ActivityReasonCategory'
|
|
||||||
|
|
||||||
Chill\ActivityBundle\Repository\ActivityRepository:
|
|
||||||
tags: [doctrine.repository_service]
|
|
||||||
arguments:
|
|
||||||
- '@Doctrine\Persistence\ManagerRegistry'
|
|
||||||
|
|
||||||
|
# This is not a repository but merely a service. It needs to be moved out for simplicity.
|
||||||
|
# The autowire and autoconfigure should be enabled globally and removed from the definition of the service.
|
||||||
|
# Once autoloaded, there is no need to alias it to the interface here, it will be done automatically by Symfony.
|
||||||
Chill\ActivityBundle\Repository\ActivityACLAwareRepository:
|
Chill\ActivityBundle\Repository\ActivityACLAwareRepository:
|
||||||
autowire: true
|
autowire: true
|
||||||
autoconfigure: true
|
autoconfigure: true
|
||||||
|
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\Migrations\Activity;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20211119173555 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'remove comment on deprecated json_array type';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$columns = [
|
||||||
|
'activitytype.name',
|
||||||
|
'activitytypecategory.name'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($columns as $col) {
|
||||||
|
$this->addSql("COMMENT ON COLUMN $col IS NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->throwIrreversibleMigrationException();
|
||||||
|
}
|
||||||
|
}
|
@@ -44,13 +44,15 @@ class AsideActivityCategory
|
|||||||
* @ORM\ManyToOne(targetEntity=AsideActivityCategory::class, inversedBy="children")
|
* @ORM\ManyToOne(targetEntity=AsideActivityCategory::class, inversedBy="children")
|
||||||
* @ORM\JoinColumn(nullable=true)
|
* @ORM\JoinColumn(nullable=true)
|
||||||
*/
|
*/
|
||||||
private $parent;
|
private ?AsideActivityCategory $parent = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\OneToMany(targetEntity=AsideActivityCategory::class, mappedBy="parent")
|
* @ORM\OneToMany(targetEntity=AsideActivityCategory::class, mappedBy="parent")
|
||||||
*/
|
*/
|
||||||
private $children;
|
private $children;
|
||||||
|
|
||||||
|
private AsideActivityCategory $oldParent;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->children = new ArrayCollection();
|
$this->children = new ArrayCollection();
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\AsideActivityBundle\Form;
|
namespace Chill\AsideActivityBundle\Form;
|
||||||
|
|
||||||
use Chill\AsideActivityBundle\Entity\AsideActivityCategory;
|
use Chill\AsideActivityBundle\Entity\AsideActivityCategory;
|
||||||
@@ -14,12 +16,11 @@ use Symfony\Component\Form\FormBuilderInterface;
|
|||||||
|
|
||||||
final class AsideActivityCategoryType extends AbstractType
|
final class AsideActivityCategoryType extends AbstractType
|
||||||
{
|
{
|
||||||
|
private CategoryRender $categoryRender;
|
||||||
|
|
||||||
protected $translatableStringHelper;
|
public function __construct(
|
||||||
|
CategoryRender $categoryRender
|
||||||
public function __construct(TranslatableStringHelper $translatableStringHelper, CategoryRender $categoryRender)
|
) {
|
||||||
{
|
|
||||||
$this->translatableStringHelper = $translatableStringHelper;
|
|
||||||
$this->categoryRender = $categoryRender;
|
$this->categoryRender = $categoryRender;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -25,18 +25,16 @@ use Symfony\Component\Templating\EngineInterface;
|
|||||||
|
|
||||||
final class AsideActivityFormType extends AbstractType
|
final class AsideActivityFormType extends AbstractType
|
||||||
{
|
{
|
||||||
protected array $timeChoices;
|
private array $timeChoices;
|
||||||
private TokenStorageInterface $storage;
|
private TokenStorageInterface $storage;
|
||||||
private CategoryRender $categoryRender;
|
private CategoryRender $categoryRender;
|
||||||
|
|
||||||
public function __construct (
|
public function __construct (
|
||||||
TranslatableStringHelper $translatableStringHelper,
|
|
||||||
ParameterBagInterface $parameterBag,
|
ParameterBagInterface $parameterBag,
|
||||||
TokenStorageInterface $storage,
|
TokenStorageInterface $storage,
|
||||||
CategoryRender $categoryRender
|
CategoryRender $categoryRender
|
||||||
){
|
){
|
||||||
$this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration');
|
$this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration');
|
||||||
$this->translatableStringHelper = $translatableStringHelper;
|
|
||||||
$this->storage = $storage;
|
$this->storage = $storage;
|
||||||
$this->categoryRender = $categoryRender;
|
$this->categoryRender = $categoryRender;
|
||||||
}
|
}
|
||||||
|
@@ -1,42 +1,34 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\AMLI\BudgetBundle\Form;
|
namespace Chill\AMLI\BudgetBundle\Form;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||||
use Chill\AMLI\BudgetBundle\Config\ConfigRepository;
|
use Chill\AMLI\BudgetBundle\Config\ConfigRepository;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
|
||||||
use Chill\AMLI\BudgetBundle\Entity\Charge;
|
use Chill\AMLI\BudgetBundle\Entity\Charge;
|
||||||
|
|
||||||
class ChargeType extends AbstractType
|
class ChargeType extends AbstractType
|
||||||
{
|
{
|
||||||
/**
|
protected ConfigRepository $configRepository;
|
||||||
*
|
|
||||||
* @var ConfigRepository
|
|
||||||
*/
|
|
||||||
protected $configRepository;
|
|
||||||
|
|
||||||
/**
|
protected TranslatableStringHelperInterface $translatableStringHelper;
|
||||||
*
|
|
||||||
* @var TranslatableStringHelper
|
|
||||||
*/
|
|
||||||
protected $translatableStringHelper;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ConfigRepository $configRepository,
|
ConfigRepository $configRepository,
|
||||||
TranslatableStringHelper $translatableStringHelper
|
TranslatableStringHelperInterface $translatableStringHelper
|
||||||
) {
|
) {
|
||||||
$this->configRepository = $configRepository;
|
$this->configRepository = $configRepository;
|
||||||
$this->translatableStringHelper = $translatableStringHelper;
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
$builder
|
$builder
|
||||||
@@ -45,10 +37,9 @@ class ChargeType extends AbstractType
|
|||||||
'placeholder' => 'Choose a charge type'
|
'placeholder' => 'Choose a charge type'
|
||||||
])
|
])
|
||||||
->add('amount', MoneyType::class)
|
->add('amount', MoneyType::class)
|
||||||
->add('comment', TextAreaType::class, [
|
->add('comment', TextareaType::class, [
|
||||||
'required' => false
|
'required' => false
|
||||||
])
|
]);
|
||||||
;
|
|
||||||
|
|
||||||
if ($options['show_start_date']) {
|
if ($options['show_start_date']) {
|
||||||
$builder->add('startDate', ChillDateType::class, [
|
$builder->add('startDate', ChillDateType::class, [
|
||||||
@@ -93,13 +84,10 @@ class ChargeType extends AbstractType
|
|||||||
return \array_flip($charges);
|
return \array_flip($charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
{
|
{
|
||||||
$resolver->setDefaults(array(
|
$resolver->setDefaults(array(
|
||||||
'data_class' => 'Chill\AMLI\BudgetBundle\Entity\Charge',
|
'data_class' => Charge::class,
|
||||||
'show_start_date' => true,
|
'show_start_date' => true,
|
||||||
'show_end_date' => true,
|
'show_end_date' => true,
|
||||||
'show_help' => true
|
'show_help' => true
|
||||||
@@ -108,17 +96,11 @@ class ChargeType extends AbstractType
|
|||||||
$resolver
|
$resolver
|
||||||
->setAllowedTypes('show_start_date', 'boolean')
|
->setAllowedTypes('show_start_date', 'boolean')
|
||||||
->setAllowedTypes('show_end_date', 'boolean')
|
->setAllowedTypes('show_end_date', 'boolean')
|
||||||
->setAllowedTypes('show_help', 'boolean')
|
->setAllowedTypes('show_help', 'boolean');
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getBlockPrefix()
|
public function getBlockPrefix()
|
||||||
{
|
{
|
||||||
return 'chill_amli_budgetbundle_charge';
|
return 'chill_amli_budgetbundle_charge';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\AMLI\BudgetBundle\Form;
|
namespace Chill\AMLI\BudgetBundle\Form;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
@@ -10,32 +13,22 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
|||||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||||
use Chill\AMLI\BudgetBundle\Config\ConfigRepository;
|
use Chill\AMLI\BudgetBundle\Config\ConfigRepository;
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
|
|
||||||
class ResourceType extends AbstractType
|
class ResourceType extends AbstractType
|
||||||
{
|
{
|
||||||
/**
|
protected ConfigRepository $configRepository;
|
||||||
*
|
|
||||||
* @var ConfigRepository
|
|
||||||
*/
|
|
||||||
protected $configRepository;
|
|
||||||
|
|
||||||
/**
|
protected TranslatableStringHelperInterface $translatableStringHelper;
|
||||||
*
|
|
||||||
* @var TranslatableStringHelper
|
|
||||||
*/
|
|
||||||
protected $translatableStringHelper;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ConfigRepository $configRepository,
|
ConfigRepository $configRepository,
|
||||||
TranslatableStringHelper $translatableStringHelper
|
TranslatableStringHelperInterface $translatableStringHelper
|
||||||
) {
|
) {
|
||||||
$this->configRepository = $configRepository;
|
$this->configRepository = $configRepository;
|
||||||
$this->translatableStringHelper = $translatableStringHelper;
|
$this->translatableStringHelper = $translatableStringHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
$builder
|
$builder
|
||||||
@@ -45,10 +38,9 @@ class ResourceType extends AbstractType
|
|||||||
'label' => 'Resource element type'
|
'label' => 'Resource element type'
|
||||||
])
|
])
|
||||||
->add('amount', MoneyType::class)
|
->add('amount', MoneyType::class)
|
||||||
->add('comment', TextAreaType::class, [
|
->add('comment', TextareaType::class, [
|
||||||
'required' => false
|
'required' => false
|
||||||
])
|
]);
|
||||||
;
|
|
||||||
|
|
||||||
if ($options['show_start_date']) {
|
if ($options['show_start_date']) {
|
||||||
$builder->add('startDate', ChillDateType::class, [
|
$builder->add('startDate', ChillDateType::class, [
|
||||||
@@ -64,6 +56,24 @@ class ResourceType extends AbstractType
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults(array(
|
||||||
|
'data_class' => Resource::class,
|
||||||
|
'show_start_date' => true,
|
||||||
|
'show_end_date' => true
|
||||||
|
));
|
||||||
|
|
||||||
|
$resolver
|
||||||
|
->setAllowedTypes('show_start_date', 'boolean')
|
||||||
|
->setAllowedTypes('show_end_date', 'boolean');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBlockPrefix()
|
||||||
|
{
|
||||||
|
return 'chill_amli_budgetbundle_resource';
|
||||||
|
}
|
||||||
|
|
||||||
private function getTypes()
|
private function getTypes()
|
||||||
{
|
{
|
||||||
$resources = $this->configRepository
|
$resources = $this->configRepository
|
||||||
@@ -78,31 +88,4 @@ class ResourceType extends AbstractType
|
|||||||
|
|
||||||
return \array_flip($resources);
|
return \array_flip($resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
|
||||||
{
|
|
||||||
$resolver->setDefaults(array(
|
|
||||||
'data_class' => Resource::class,
|
|
||||||
'show_start_date' => true,
|
|
||||||
'show_end_date' => true
|
|
||||||
));
|
|
||||||
|
|
||||||
$resolver
|
|
||||||
->setAllowedTypes('show_start_date', 'boolean')
|
|
||||||
->setAllowedTypes('show_end_date', 'boolean')
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getBlockPrefix()
|
|
||||||
{
|
|
||||||
return 'chill_amli_budgetbundle_resource';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\CalendarBundle\DataFixtures\ORM;
|
namespace Chill\CalendarBundle\DataFixtures\ORM;
|
||||||
|
|
||||||
use Chill\CalendarBundle\Entity\CalendarRange;
|
use Chill\CalendarBundle\Entity\CalendarRange;
|
||||||
@@ -15,6 +17,10 @@ use Doctrine\Persistence\ObjectManager;
|
|||||||
|
|
||||||
class LoadCalendarRange extends Fixture implements FixtureGroupInterface, OrderedFixtureInterface
|
class LoadCalendarRange extends Fixture implements FixtureGroupInterface, OrderedFixtureInterface
|
||||||
{
|
{
|
||||||
|
private UserRepository $userRepository;
|
||||||
|
|
||||||
|
public static array $references = [];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
UserRepository $userRepository
|
UserRepository $userRepository
|
||||||
) {
|
) {
|
||||||
@@ -31,8 +37,6 @@ class LoadCalendarRange extends Fixture implements FixtureGroupInterface, Ordere
|
|||||||
return ['calendar'];
|
return ['calendar'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static $references = [];
|
|
||||||
|
|
||||||
public function load(ObjectManager $manager): void
|
public function load(ObjectManager $manager): void
|
||||||
{
|
{
|
||||||
$arr = range(-50, 50);
|
$arr = range(-50, 50);
|
||||||
|
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\Migrations\Calendar;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20211119173557 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'remove comment on deprecated json_array type';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$columns = [
|
||||||
|
'chill_calendar.cancel_reason.name',
|
||||||
|
'chill_calendar.invite.status',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($columns as $col) {
|
||||||
|
$this->addSql("COMMENT ON COLUMN $col IS NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->throwIrreversibleMigrationException();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,28 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\CustomFieldsBundle\Form\DataTransformer;
|
namespace Chill\CustomFieldsBundle\Form\DataTransformer;
|
||||||
|
|
||||||
|
use Chill\CustomFieldsBundle\Entity\CustomField;
|
||||||
use Symfony\Component\Form\DataTransformerInterface;
|
use Symfony\Component\Form\DataTransformerInterface;
|
||||||
use Doctrine\Persistence\ObjectManager;
|
use Doctrine\Persistence\ObjectManager;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
|
||||||
|
|
||||||
class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
|
class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
|
||||||
/**
|
private ObjectManager $om;
|
||||||
* @var ObjectManager
|
|
||||||
*/
|
private array $customField;
|
||||||
private $om;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ObjectManager $om
|
|
||||||
*/
|
|
||||||
public function __construct(ObjectManager $om)
|
public function __construct(ObjectManager $om)
|
||||||
{
|
{
|
||||||
$this->om = $om;
|
$this->om = $om;
|
||||||
|
|
||||||
$customFields = $this->om
|
$customFields = $this->om
|
||||||
->getRepository('ChillCustomFieldsBundle:CustomField')
|
->getRepository(CustomField::class)
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
|
// @TODO: in the array_map callback, CustomField::getLabel() does not exist. What do we do here?
|
||||||
$customFieldsLablels = array_map(
|
$customFieldsLablels = array_map(
|
||||||
function($e) { return $e->getLabel(); },
|
function($e) { return $e->getLabel(); },
|
||||||
$customFields);
|
$customFields);
|
||||||
@@ -36,20 +35,12 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface {
|
|||||||
{
|
{
|
||||||
echo $customFieldsJSON;
|
echo $customFieldsJSON;
|
||||||
|
|
||||||
if($customFieldsJSON === null) { // lors de la creation
|
if($customFieldsJSON === null) {
|
||||||
$customFieldsArray = array();
|
$customFieldsArray = [];
|
||||||
} else {
|
} else {
|
||||||
$customFieldsArray = json_decode($customFieldsJSON,true);
|
$customFieldsArray = json_decode($customFieldsJSON, true, 512, JSON_THROW_ON_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
echo "<br> - 4 - <br>";
|
|
||||||
|
|
||||||
var_dump($customFieldsArray);
|
|
||||||
|
|
||||||
echo "<br> - 5 - <br>";
|
|
||||||
*/
|
|
||||||
|
|
||||||
$customFieldsArrayRet = array();
|
$customFieldsArrayRet = array();
|
||||||
|
|
||||||
foreach ($customFieldsArray as $key => $value) {
|
foreach ($customFieldsArray as $key => $value) {
|
||||||
|
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\DocGeneratorBundle\Serializer\Encoder;
|
||||||
|
|
||||||
|
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||||
|
|
||||||
|
class DocGenEncoder implements \Symfony\Component\Serializer\Encoder\EncoderInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function encode($data, string $format, array $context = [])
|
||||||
|
{
|
||||||
|
if (!$this->isAssociative($data)) {
|
||||||
|
throw new UnexpectedValueException("Only associative arrays are allowed; lists are not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
$this->recusiveEncoding($data, $result, '');
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function recusiveEncoding(array $data, array &$result, $path)
|
||||||
|
{
|
||||||
|
if ($this->isAssociative($data)) {
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
if (\is_array($value)) {
|
||||||
|
$this->recusiveEncoding($value, $result, $this->canonicalizeKey($path, $key));
|
||||||
|
} else {
|
||||||
|
$result[$this->canonicalizeKey($path, $key)] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach ($data as $elem) {
|
||||||
|
|
||||||
|
if (!$this->isAssociative($elem)) {
|
||||||
|
throw new UnexpectedValueException(sprintf("Embedded loops are not allowed. See data under %s path", $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
$sub = [];
|
||||||
|
$this->recusiveEncoding($elem, $sub, '');
|
||||||
|
$result[$path][] = $sub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function canonicalizeKey(string $path, string $key): string
|
||||||
|
{
|
||||||
|
return $path === '' ? $key : $path.'_'.$key;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isAssociative(array $data)
|
||||||
|
{
|
||||||
|
$keys = \array_keys($data);
|
||||||
|
|
||||||
|
return $keys !== \array_keys($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function supportsEncoding(string $format)
|
||||||
|
{
|
||||||
|
return $format === 'docgen';
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\DocGeneratorBundle\Serializer\Helper;
|
||||||
|
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
|
||||||
|
class NormalizeNullValueHelper
|
||||||
|
{
|
||||||
|
private NormalizerInterface $normalizer;
|
||||||
|
|
||||||
|
public function __construct(NormalizerInterface $normalizer)
|
||||||
|
{
|
||||||
|
$this->normalizer = $normalizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function normalize(array $attributes, string $format = 'docgen', ?array $context = [])
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($attributes as $key => $class) {
|
||||||
|
if (is_numeric($key)) {
|
||||||
|
$data[$class] = '';
|
||||||
|
} else {
|
||||||
|
switch ($class) {
|
||||||
|
case 'array':
|
||||||
|
case 'bool':
|
||||||
|
case 'double':
|
||||||
|
case 'float':
|
||||||
|
case 'int':
|
||||||
|
case 'resource':
|
||||||
|
case 'string':
|
||||||
|
case 'null':
|
||||||
|
$data[$key] = '';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$data[$key] = $this->normalizer->normalize(null, $format, \array_merge(
|
||||||
|
$context,
|
||||||
|
['docgen:expects' => $class]
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,177 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\DocGeneratorBundle\Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
|
||||||
|
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||||
|
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
||||||
|
use Symfony\Component\Serializer\Exception\ExceptionInterface;
|
||||||
|
use Symfony\Component\Serializer\Exception\LogicException;
|
||||||
|
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
|
||||||
|
use Symfony\Component\Serializer\Mapping\ClassMetadata;
|
||||||
|
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
|
||||||
|
class DocGenObjectNormalizer implements NormalizerInterface, NormalizerAwareInterface
|
||||||
|
{
|
||||||
|
use NormalizerAwareTrait;
|
||||||
|
|
||||||
|
private ClassMetadataFactoryInterface $classMetadataFactory;
|
||||||
|
private PropertyAccessor $propertyAccess;
|
||||||
|
|
||||||
|
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory)
|
||||||
|
{
|
||||||
|
$this->classMetadataFactory = $classMetadataFactory;
|
||||||
|
$this->propertyAccess = PropertyAccess::createPropertyAccessor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function normalize($object, string $format = null, array $context = [])
|
||||||
|
{
|
||||||
|
$classMetadataKey = $object ?? $context['docgen:expects'];
|
||||||
|
|
||||||
|
if (!$this->classMetadataFactory->hasMetadataFor($classMetadataKey)) {
|
||||||
|
throw new LogicException(sprintf("This object does not have metadata: %s. Add groups on this entity to allow to serialize with the format %s and groups %s", is_object($object) ? get_class($object) : $context['docgen:expects'], $format, \implode(', ', $context['groups'])));
|
||||||
|
}
|
||||||
|
|
||||||
|
$metadata = $this->classMetadataFactory->getMetadataFor($classMetadataKey);
|
||||||
|
$expectedGroups = \array_key_exists(AbstractNormalizer::GROUPS, $context) ?
|
||||||
|
\is_array($context[AbstractNormalizer::GROUPS]) ? $context[AbstractNormalizer::GROUPS] : [$context[AbstractNormalizer::GROUPS]]
|
||||||
|
: [];
|
||||||
|
$attributes = \array_filter(
|
||||||
|
$metadata->getAttributesMetadata(),
|
||||||
|
function (AttributeMetadata $a) use ($expectedGroups) {
|
||||||
|
foreach ($a->getGroups() as $g) {
|
||||||
|
if (\in_array($g, $expectedGroups, true)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (null === $object) {
|
||||||
|
return $this->normalizeNullData($format, $context, $metadata, $attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->normalizeObject($object, $format, $context, $expectedGroups, $metadata, $attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $format
|
||||||
|
* @param array $context
|
||||||
|
* @param array $expectedGroups
|
||||||
|
* @param ClassMetadata $metadata
|
||||||
|
* @param array|AttributeMetadata[] $attributes
|
||||||
|
*/
|
||||||
|
private function normalizeNullData(string $format, array $context, ClassMetadata $metadata, array $attributes): array
|
||||||
|
{
|
||||||
|
$keys = [];
|
||||||
|
|
||||||
|
foreach ($attributes as $attribute) {
|
||||||
|
$key = $attribute->getSerializedName() ?? $attribute->getName();
|
||||||
|
$keys[$key] = $this->getExpectedType($attribute, $metadata->getReflectionClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
$normalizer = new NormalizeNullValueHelper($this->normalizer);
|
||||||
|
|
||||||
|
return $normalizer->normalize($keys, $format, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $object
|
||||||
|
* @param $format
|
||||||
|
* @param array $context
|
||||||
|
* @param array $expectedGroups
|
||||||
|
* @param ClassMetadata $metadata
|
||||||
|
* @param array|AttributeMetadata[] $attributes
|
||||||
|
* @return array
|
||||||
|
* @throws ExceptionInterface
|
||||||
|
*/
|
||||||
|
private function normalizeObject($object, $format, array $context, array $expectedGroups, ClassMetadata $metadata, array $attributes)
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
$reflection = $metadata->getReflectionClass();
|
||||||
|
|
||||||
|
foreach ($attributes as $attribute) {
|
||||||
|
/** @var AttributeMetadata $attribute */
|
||||||
|
$value = $this->propertyAccess->getValue($object, $attribute->getName());
|
||||||
|
$key = $attribute->getSerializedName() ?? $attribute->getName();
|
||||||
|
|
||||||
|
if (is_object($value)) {
|
||||||
|
$data[$key] =
|
||||||
|
$this->normalizer->normalize($value, $format, \array_merge(
|
||||||
|
$context, $attribute->getNormalizationContextForGroups($expectedGroups)
|
||||||
|
));
|
||||||
|
} elseif (null === $value) {
|
||||||
|
$data[$key] = $this->normalizeNullOutputValue($format, $context, $attribute, $reflection);
|
||||||
|
} else {
|
||||||
|
$data[$key] = (string) $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getExpectedType(AttributeMetadata $attribute, \ReflectionClass $reflection): string
|
||||||
|
{
|
||||||
|
// we have to get the expected content
|
||||||
|
if ($reflection->hasProperty($attribute->getName())) {
|
||||||
|
$type = $reflection->getProperty($attribute->getName())->getType();
|
||||||
|
} elseif ($reflection->hasMethod($attribute->getName())) {
|
||||||
|
$type = $reflection->getMethod($attribute->getName())->getReturnType();
|
||||||
|
} else {
|
||||||
|
throw new \LogicException(sprintf(
|
||||||
|
"Could not determine how the content is determined for the attribute %s. Add attribute property only on property or method", $attribute->getName()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $type) {
|
||||||
|
throw new \LogicException(sprintf(
|
||||||
|
"Could not determine the type for this attribute: %s. Add a return type to the method or property declaration", $attribute->getName()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $type->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private function normalizeNullOutputValue($format, array $context, AttributeMetadata $attribute, \ReflectionClass $reflection)
|
||||||
|
{
|
||||||
|
$type = $this->getExpectedType($attribute, $reflection);
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'array':
|
||||||
|
case 'bool':
|
||||||
|
case 'double':
|
||||||
|
case 'float':
|
||||||
|
case 'int':
|
||||||
|
case 'resource':
|
||||||
|
case 'string':
|
||||||
|
return '';
|
||||||
|
default:
|
||||||
|
return $this->normalizer->normalize(
|
||||||
|
null,
|
||||||
|
$format,
|
||||||
|
\array_merge(
|
||||||
|
$context,
|
||||||
|
['docgen:expects' => $type]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function supportsNormalization($data, string $format = null): bool
|
||||||
|
{
|
||||||
|
return $format === 'docgen' && (is_object($data) || null === $data);
|
||||||
|
}
|
||||||
|
}
|
@@ -8,3 +8,10 @@ services:
|
|||||||
autowire: true
|
autowire: true
|
||||||
autoconfigure: true
|
autoconfigure: true
|
||||||
resource: '../Repository/'
|
resource: '../Repository/'
|
||||||
|
|
||||||
|
Chill\DocGeneratorBundle\Serializer\Normalizer\:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
resource: '../Serializer/Normalizer/'
|
||||||
|
tags:
|
||||||
|
- { name: 'serializer.normalizer', priority: -152 }
|
||||||
|
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\Migrations\DocGenerator;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20211119173556 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'remove comment on deprecated json_array type';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$columns = [
|
||||||
|
'chill_docgen_template.name'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($columns as $col) {
|
||||||
|
$this->addSql("COMMENT ON COLUMN $col IS NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->throwIrreversibleMigrationException();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\DocGeneratorBundle\Tests\Serializer\Encoder;
|
||||||
|
|
||||||
|
use Chill\DocGeneratorBundle\Serializer\Encoder\DocGenEncoder;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||||
|
|
||||||
|
class DocGenEncoderTest extends TestCase
|
||||||
|
{
|
||||||
|
private DocGenEncoder $encoder;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->encoder = new DocGenEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider generateEncodeData
|
||||||
|
*/
|
||||||
|
public function testEncode($expected, $data, string $msg)
|
||||||
|
{
|
||||||
|
$generated = $this->encoder->encode($data, 'docgen');
|
||||||
|
$this->assertEquals($expected, $generated, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEmbeddedLoopsThrowsException()
|
||||||
|
{
|
||||||
|
$this->expectException(UnexpectedValueException::class);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'data' => [
|
||||||
|
['item' => 'one'],
|
||||||
|
[
|
||||||
|
'embedded' => [
|
||||||
|
[
|
||||||
|
['subitem' => 'two'],
|
||||||
|
['subitem' => 'three']
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->encoder->encode($data, 'docgen');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateEncodeData()
|
||||||
|
{
|
||||||
|
yield [ ['tests' => 'ok'], ['tests' => 'ok'], "A simple test with a simple array"];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
// expected:
|
||||||
|
['item_subitem' => 'value'],
|
||||||
|
// data:
|
||||||
|
['item' => ['subitem' => 'value']],
|
||||||
|
"A test with multidimensional array"
|
||||||
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
// expected:
|
||||||
|
[ 'data' => [['item' => 'one'], ['item' => 'two']] ],
|
||||||
|
// data:
|
||||||
|
[ 'data' => [['item' => 'one'], ['item' => 'two']] ],
|
||||||
|
"a list of items"
|
||||||
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
// expected:
|
||||||
|
[ 'data' => [['item_subitem' => 'alpha'], ['item' => 'two']] ],
|
||||||
|
// data:
|
||||||
|
[ 'data' => [['item' => ['subitem' => 'alpha']], ['item' => 'two'] ] ],
|
||||||
|
"a list of items with multidimensional array inside item"
|
||||||
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
// expected:
|
||||||
|
[
|
||||||
|
'persons' => [
|
||||||
|
[
|
||||||
|
'firstname' => 'Jonathan',
|
||||||
|
'lastname' => 'Dupont',
|
||||||
|
'dateOfBirth_long' => '16 juin 1981',
|
||||||
|
'dateOfBirth_short' => '16/06/1981',
|
||||||
|
'father_firstname' => 'Marcel',
|
||||||
|
'father_lastname' => 'Dupont',
|
||||||
|
'father_dateOfBirth_long' => '10 novembre 1953',
|
||||||
|
'father_dateOfBirth_short' => '10/11/1953'
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
// data:
|
||||||
|
[
|
||||||
|
'persons' => [
|
||||||
|
[
|
||||||
|
'firstname' => 'Jonathan',
|
||||||
|
'lastname' => 'Dupont',
|
||||||
|
'dateOfBirth' => [ 'long' => '16 juin 1981', 'short' => '16/06/1981'],
|
||||||
|
'father' => [
|
||||||
|
'firstname' => 'Marcel',
|
||||||
|
'lastname' => 'Dupont',
|
||||||
|
'dateOfBirth' => ['long' => '10 novembre 1953', 'short' => '10/11/1953']
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"a longer list, with near real data inside and embedded associative arrays"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\DocGeneratorBundle\tests\Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
|
||||||
|
class DocGenObjectNormalizerTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
private NormalizerInterface $normalizer;
|
||||||
|
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
self::bootKernel();
|
||||||
|
|
||||||
|
$this->normalizer = self::$container->get(NormalizerInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNormalizationBasic()
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$user->setUsername('User Test');
|
||||||
|
$user->setMainCenter($center = new Center());
|
||||||
|
$center->setName('test');
|
||||||
|
|
||||||
|
$normalized = $this->normalizer->normalize($user, 'docgen', [ AbstractNormalizer::GROUPS => ['docgen:read']]);
|
||||||
|
$expected = [
|
||||||
|
'label' => 'User Test',
|
||||||
|
'email' => '',
|
||||||
|
'mainCenter' => [
|
||||||
|
'name' => 'test'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $normalized, "test normalization fo an user");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNormalizeWithNullValueEmbedded()
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$user->setUsername('User Test');
|
||||||
|
|
||||||
|
$normalized = $this->normalizer->normalize($user, 'docgen', [ AbstractNormalizer::GROUPS => ['docgen:read']]);
|
||||||
|
$expected = [
|
||||||
|
'label' => 'User Test',
|
||||||
|
'email' => '',
|
||||||
|
'mainCenter' => [
|
||||||
|
'name' => ''
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $normalized, "test normalization fo an user with null center");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNormalizeNullObjectWithObjectEmbedded()
|
||||||
|
{
|
||||||
|
$normalized = $this->normalizer->normalize(null, 'docgen', [
|
||||||
|
AbstractNormalizer::GROUPS => ['docgen:read'],
|
||||||
|
'docgen:expects' => User::class,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$expected = [
|
||||||
|
'label' => '',
|
||||||
|
'email' => '',
|
||||||
|
'mainCenter' => [
|
||||||
|
'name' => ''
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $normalized, "test normalization for a null user");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@@ -1,44 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2018 Champs-Libres SCRLFS
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\DocStoreBundle\Security\Authorization;
|
namespace Chill\DocStoreBundle\Security\Authorization;
|
||||||
|
|
||||||
use App\Security\Authorization\VoterHelperFactory;
|
|
||||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
|
||||||
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||||
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||||
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
|
||||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Role\Role;
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
||||||
{
|
{
|
||||||
const CREATE = 'CHILL_PERSON_DOCUMENT_CREATE';
|
const CREATE = 'CHILL_PERSON_DOCUMENT_CREATE';
|
||||||
|
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\Migrations\DocStore;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20211119173558 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'remove comment on deprecated json_array type';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$columns = [
|
||||||
|
'chill_doc.document_category.name',
|
||||||
|
'chill_doc.stored_object.key',
|
||||||
|
'chill_doc.stored_object.iv',
|
||||||
|
'chill_doc.stored_object.datas',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($columns as $col) {
|
||||||
|
$this->addSql("COMMENT ON COLUMN $col IS NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->throwIrreversibleMigrationException();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,50 +1,46 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\AMLI\FamilyMembersBundle\Controller;
|
namespace Chill\AMLI\FamilyMembersBundle\Controller;
|
||||||
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
use Chill\AMLI\FamilyMembersBundle\Repository\FamilyMemberRepository;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
|
||||||
use Chill\PersonBundle\Entity\Person;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember;
|
use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember;
|
||||||
use Chill\AMLI\FamilyMembersBundle\Security\Voter\FamilyMemberVoter;
|
use Chill\AMLI\FamilyMembersBundle\Security\Voter\FamilyMemberVoter;
|
||||||
use Chill\AMLI\FamilyMembersBundle\Form\FamilyMemberType;
|
use Chill\AMLI\FamilyMembersBundle\Form\FamilyMemberType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Translation\TranslatorInterface;
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
class FamilyMemberController extends Controller
|
class FamilyMemberController extends AbstractController
|
||||||
{
|
{
|
||||||
/**
|
private EntityManagerInterface $em;
|
||||||
*
|
|
||||||
* @var EntityManagerInterface
|
|
||||||
*/
|
|
||||||
protected $em;
|
|
||||||
|
|
||||||
/**
|
protected TranslatorInterface $translator;
|
||||||
*
|
|
||||||
* @var TranslatorInterface
|
|
||||||
*/
|
|
||||||
protected $translator;
|
|
||||||
|
|
||||||
/**
|
protected LoggerInterface $chillMainLogger;
|
||||||
*
|
|
||||||
* @var LoggerInterface
|
private FamilyMemberRepository $familyMemberRepository;
|
||||||
*/
|
|
||||||
protected $chillMainLogger;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EntityManagerInterface $em,
|
EntityManagerInterface $entityManager,
|
||||||
TranslatorInterface $translator,
|
TranslatorInterface $translator,
|
||||||
LoggerInterface $chillMainLogger
|
LoggerInterface $chillMainLogger,
|
||||||
|
FamilyMemberRepository $familyMemberRepository
|
||||||
) {
|
) {
|
||||||
$this->em = $em;
|
$this->em = $entityManager;
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
$this->chillMainLogger = $chillMainLogger;
|
$this->chillMainLogger = $chillMainLogger;
|
||||||
|
$this->familyMemberRepository = $familyMemberRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route(
|
* @Route(
|
||||||
* "{_locale}/family-members/family-members/by-person/{id}",
|
* "{_locale}/family-members/family-members/by-person/{id}",
|
||||||
@@ -55,14 +51,12 @@ class FamilyMemberController extends Controller
|
|||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $person);
|
$this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $person);
|
||||||
|
|
||||||
$familyMembers = $this->em
|
$familyMembers = $this->familyMemberRepository->findByPerson($person);
|
||||||
->getRepository(FamilyMember::class)
|
|
||||||
->findByPerson($person);
|
|
||||||
|
|
||||||
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:index.html.twig', array(
|
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:index.html.twig', [
|
||||||
'person' => $person,
|
'person' => $person,
|
||||||
'familyMembers' => $familyMembers
|
'familyMembers' => $familyMembers
|
||||||
));
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,9 +67,7 @@ class FamilyMemberController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function newAction(Person $person, Request $request)
|
public function newAction(Person $person, Request $request)
|
||||||
{
|
{
|
||||||
$familyMember = (new FamilyMember())
|
$familyMember = (new FamilyMember())->setPerson($person);
|
||||||
->setPerson($person)
|
|
||||||
;
|
|
||||||
|
|
||||||
$this->denyAccessUnlessGranted(FamilyMemberVoter::CREATE, $familyMember);
|
$this->denyAccessUnlessGranted(FamilyMemberVoter::CREATE, $familyMember);
|
||||||
|
|
||||||
@@ -84,10 +76,9 @@ class FamilyMemberController extends Controller
|
|||||||
|
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() and $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$em = $this->getDoctrine()->getManager();
|
$this->em->persist($familyMember);
|
||||||
$em->persist($familyMember);
|
$this->em->flush();
|
||||||
$em->flush();
|
|
||||||
|
|
||||||
$this->addFlash('success', $this->translator->trans('Family member created'));
|
$this->addFlash('success', $this->translator->trans('Family member created'));
|
||||||
|
|
||||||
@@ -96,10 +87,10 @@ class FamilyMemberController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:new.html.twig', array(
|
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:new.html.twig', [
|
||||||
'form' => $form->createView(),
|
'form' => $form->createView(),
|
||||||
'person' => $person
|
'person' => $person
|
||||||
));
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,9 +108,8 @@ class FamilyMemberController extends Controller
|
|||||||
|
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() and $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
$em = $this->getDoctrine()->getManager();
|
$this->em->flush();
|
||||||
$em->flush();
|
|
||||||
|
|
||||||
$this->addFlash('success', $this->translator->trans('Family member updated'));
|
$this->addFlash('success', $this->translator->trans('Family member updated'));
|
||||||
|
|
||||||
@@ -128,11 +118,11 @@ class FamilyMemberController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:edit.html.twig', array(
|
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:edit.html.twig', [
|
||||||
'familyMember' => $familyMember,
|
'familyMember' => $familyMember,
|
||||||
'form' => $form->createView(),
|
'form' => $form->createView(),
|
||||||
'person' => $familyMember->getPerson()
|
'person' => $familyMember->getPerson()
|
||||||
));
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,47 +131,42 @@ class FamilyMemberController extends Controller
|
|||||||
* "{_locale}/family-members/family-members/{id}/delete",
|
* "{_locale}/family-members/family-members/{id}/delete",
|
||||||
* name="chill_family_members_family_members_delete"
|
* name="chill_family_members_family_members_delete"
|
||||||
* )
|
* )
|
||||||
*
|
|
||||||
* @param FamilyMember $familyMember
|
|
||||||
* @param Request $request
|
|
||||||
* @return \Symfony\Component\BrowserKit\Response
|
|
||||||
*/
|
*/
|
||||||
public function deleteAction(FamilyMember $familyMember, Request $request)
|
public function deleteAction(FamilyMember $familyMember, Request $request): Response
|
||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted(FamilyMemberVoter::DELETE, $familyMember, 'You are not '
|
$this->denyAccessUnlessGranted(FamilyMemberVoter::DELETE, $familyMember, 'You are not '
|
||||||
. 'allowed to delete this family membership');
|
. 'allowed to delete this family membership');
|
||||||
|
|
||||||
$form = $this->createDeleteForm($id);
|
$form = $this->createDeleteForm();
|
||||||
|
|
||||||
if ($request->getMethod() === Request::METHOD_DELETE) {
|
if ($request->getMethod() === Request::METHOD_DELETE) {
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isValid()) {
|
if ($form->isValid()) {
|
||||||
$this->chillMainLogger->notice("A family member has been removed", array(
|
$this->chillMainLogger->notice("A family member has been removed", [
|
||||||
'by_user' => $this->getUser()->getUsername(),
|
'by_user' => $this->getUser()->getUsername(),
|
||||||
'family_member_id' => $familyMember->getId(),
|
'family_member_id' => $familyMember->getId(),
|
||||||
'name' => $familyMember->getFirstname()." ".$familyMember->getLastname(),
|
'name' => $familyMember->getFirstname()." ".$familyMember->getLastname(),
|
||||||
'link' => $familyMember->getLink()
|
'link' => $familyMember->getLink()
|
||||||
));
|
]);
|
||||||
|
|
||||||
$em = $this->getDoctrine()->getManager();
|
$this->em->remove($familyMember);
|
||||||
$em->remove($familyMember);
|
$this->em->flush();
|
||||||
$em->flush();
|
|
||||||
|
|
||||||
$this->addFlash('success', $this->translator
|
$this->addFlash('success', $this->translator
|
||||||
->trans("The family member has been successfully removed."));
|
->trans("The family member has been successfully removed."));
|
||||||
|
|
||||||
return $this->redirectToRoute('chill_family_members_family_members_index', array(
|
return $this->redirectToRoute('chill_family_members_family_members_index', [
|
||||||
'id' => $familyMember->getPerson()->getId()
|
'id' => $familyMember->getPerson()->getId()
|
||||||
));
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:confirm_delete.html.twig', array(
|
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:confirm_delete.html.twig', [
|
||||||
'familyMember' => $familyMember,
|
'familyMember' => $familyMember,
|
||||||
'delete_form' => $form->createView()
|
'delete_form' => $form->createView()
|
||||||
));
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -194,23 +179,20 @@ class FamilyMemberController extends Controller
|
|||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $familyMember);
|
$this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $familyMember);
|
||||||
|
|
||||||
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:view.html.twig', array(
|
return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:view.html.twig', [
|
||||||
'familyMember' => $familyMember
|
'familyMember' => $familyMember
|
||||||
));
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a form to delete a help request entity by id.
|
* Creates a form to delete a help request entity by id.
|
||||||
*
|
|
||||||
* @param mixed $id The entity id
|
|
||||||
*
|
|
||||||
* @return \Symfony\Component\Form\Form The form
|
|
||||||
*/
|
*/
|
||||||
private function createDeleteForm($id)
|
private function createDeleteForm(): FormInterface
|
||||||
{
|
{
|
||||||
return $this->createFormBuilder()
|
return $this
|
||||||
|
->createFormBuilder()
|
||||||
->setMethod(Request::METHOD_DELETE)
|
->setMethod(Request::METHOD_DELETE)
|
||||||
->add('submit', SubmitType::class, array('label' => 'Delete'))
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm()
|
->getForm()
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,31 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\AMLI\FamilyMembersBundle\Repository;
|
namespace Chill\AMLI\FamilyMembersBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\AMLI\FamilyMembersBundle\Entity\FamilyMember;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FamilyMemberRepository
|
* @method FamilyMember|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
*
|
* @method FamilyMember|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
|
* @method FamilyMember[] findAll()
|
||||||
|
* @method FamilyMember[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
*/
|
*/
|
||||||
class FamilyMemberRepository extends \Doctrine\ORM\EntityRepository
|
class FamilyMemberRepository extends ServiceEntityRepository
|
||||||
{
|
{
|
||||||
public function findActiveByPerson(Person $person)
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, FamilyMember::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FamilyMember[]
|
||||||
|
*/
|
||||||
|
public function findByPerson(Person $person): array
|
||||||
{
|
{
|
||||||
return $this->findBy(['person' => $person]);
|
return $this->findBy(['person' => $person]);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\MainBundle\CRUD\Controller;
|
namespace Chill\MainBundle\CRUD\Controller;
|
||||||
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
@@ -14,49 +16,39 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
|||||||
use Symfony\Component\Serializer\SerializerInterface;
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
use Symfony\Component\Translation\TranslatorInterface;
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class AbstractCRUDController extends AbstractController
|
abstract class AbstractCRUDController extends AbstractController
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The crud configuration
|
* The crud configuration
|
||||||
*
|
*
|
||||||
* This configuration si defined by `chill_main['crud']` or `chill_main['apis']`
|
* This configuration si defined by `chill_main['crud']` or `chill_main['apis']`
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
*/
|
||||||
protected array $crudConfig = [];
|
protected array $crudConfig = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the instance of the entity with the given id
|
* get the instance of the entity with the given id
|
||||||
*
|
*
|
||||||
* @param string $id
|
|
||||||
* @return object
|
|
||||||
* @throw Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the object is not found
|
* @throw Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the object is not found
|
||||||
*/
|
*/
|
||||||
protected function getEntity($action, $id, Request $request): object
|
protected function getEntity($action, string $id, Request $request): object
|
||||||
{
|
{
|
||||||
$e = $this->getDoctrine()
|
$e = $this
|
||||||
|
->getDoctrine()
|
||||||
->getRepository($this->getEntityClass())
|
->getRepository($this->getEntityClass())
|
||||||
->find($id);
|
->find($id);
|
||||||
|
|
||||||
if (NULL === $e) {
|
if (null === $e) {
|
||||||
throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id));
|
throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $e;
|
return $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an entity.
|
|
||||||
*
|
|
||||||
* @param string $action
|
|
||||||
* @param Request $request
|
|
||||||
* @return object
|
|
||||||
*/
|
|
||||||
protected function createEntity(string $action, Request $request): object
|
protected function createEntity(string $action, Request $request): object
|
||||||
{
|
{
|
||||||
$type = $this->getEntityClass();
|
$class = $this->getEntityClass();
|
||||||
|
|
||||||
return new $type;
|
return new $class;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,18 +56,13 @@ class AbstractCRUDController extends AbstractController
|
|||||||
*
|
*
|
||||||
* By default, count all entities. You can customize the query by
|
* By default, count all entities. You can customize the query by
|
||||||
* using the method `customizeQuery`.
|
* using the method `customizeQuery`.
|
||||||
*
|
|
||||||
* @param string $action
|
|
||||||
* @param Request $request
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
protected function countEntities(string $action, Request $request, $_format): int
|
protected function countEntities(string $action, Request $request, $_format): int
|
||||||
{
|
{
|
||||||
return $this->buildQueryEntities($action, $request)
|
return $this->buildQueryEntities($action, $request)
|
||||||
->select('COUNT(e)')
|
->select('COUNT(e)')
|
||||||
->getQuery()
|
->getQuery()
|
||||||
->getSingleScalarResult()
|
->getSingleScalarResult();
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,7 +74,6 @@ class AbstractCRUDController extends AbstractController
|
|||||||
* The method `orderEntity` is called internally to order entities.
|
* The method `orderEntity` is called internally to order entities.
|
||||||
*
|
*
|
||||||
* It returns, by default, a query builder.
|
* It returns, by default, a query builder.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator)
|
protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator)
|
||||||
{
|
{
|
||||||
@@ -101,7 +87,6 @@ class AbstractCRUDController extends AbstractController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add ordering fields in the query build by self::queryEntities
|
* Add ordering fields in the query build by self::queryEntities
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
|
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
|
||||||
{
|
{
|
||||||
@@ -118,8 +103,6 @@ class AbstractCRUDController extends AbstractController
|
|||||||
*
|
*
|
||||||
* The alias for the entity is "e".
|
* The alias for the entity is "e".
|
||||||
*
|
*
|
||||||
* @param string $action
|
|
||||||
* @param Request $request
|
|
||||||
* @return QueryBuilder
|
* @return QueryBuilder
|
||||||
*/
|
*/
|
||||||
protected function buildQueryEntities(string $action, Request $request)
|
protected function buildQueryEntities(string $action, Request $request)
|
||||||
@@ -127,8 +110,7 @@ class AbstractCRUDController extends AbstractController
|
|||||||
$qb = $this->getDoctrine()->getManager()
|
$qb = $this->getDoctrine()->getManager()
|
||||||
->createQueryBuilder()
|
->createQueryBuilder()
|
||||||
->select('e')
|
->select('e')
|
||||||
->from($this->getEntityClass(), 'e')
|
->from($this->getEntityClass(), 'e');
|
||||||
;
|
|
||||||
|
|
||||||
$this->customizeQuery($action, $request, $qb);
|
$this->customizeQuery($action, $request, $qb);
|
||||||
|
|
||||||
@@ -138,7 +120,7 @@ class AbstractCRUDController extends AbstractController
|
|||||||
protected function customizeQuery(string $action, Request $request, $query): void {}
|
protected function customizeQuery(string $action, Request $request, $query): void {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the result of the query
|
* Get the result of the query.
|
||||||
*/
|
*/
|
||||||
protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query)
|
protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query)
|
||||||
{
|
{
|
||||||
@@ -151,7 +133,7 @@ class AbstractCRUDController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method used by indexAction
|
* Method used by indexAction.
|
||||||
*/
|
*/
|
||||||
protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response
|
protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response
|
||||||
{
|
{
|
||||||
@@ -159,7 +141,7 @@ class AbstractCRUDController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method used by indexAction
|
* Method used by indexAction.
|
||||||
*/
|
*/
|
||||||
protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response
|
protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response
|
||||||
{
|
{
|
||||||
@@ -167,18 +149,17 @@ class AbstractCRUDController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method used by indexAction
|
* Method used by indexAction.
|
||||||
*/
|
*/
|
||||||
protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response
|
protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the complete FQDN of the class
|
* Get the FQDN of the class.
|
||||||
*
|
*
|
||||||
* @return string the complete fqdn of the class
|
* @return class-string The FQDN of the class
|
||||||
*/
|
*/
|
||||||
protected function getEntityClass(): string
|
protected function getEntityClass(): string
|
||||||
{
|
{
|
||||||
@@ -186,7 +167,7 @@ class AbstractCRUDController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* called on post fetch entity
|
* Called on post fetch entity.
|
||||||
*/
|
*/
|
||||||
protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response
|
protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response
|
||||||
{
|
{
|
||||||
@@ -194,7 +175,7 @@ class AbstractCRUDController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called on post check ACL
|
* Called on post check ACL.
|
||||||
*/
|
*/
|
||||||
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
|
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
|
||||||
{
|
{
|
||||||
@@ -214,12 +195,12 @@ class AbstractCRUDController extends AbstractController
|
|||||||
*/
|
*/
|
||||||
protected function checkACL(string $action, Request $request, string $_format, $entity = null)
|
protected function checkACL(string $action, Request $request, string $_format, $entity = null)
|
||||||
{
|
{
|
||||||
|
// @TODO: Implements abstract getRoleFor method or do it in the interface.
|
||||||
$this->denyAccessUnlessGranted($this->getRoleFor($action, $request, $entity, $_format), $entity);
|
$this->denyAccessUnlessGranted($this->getRoleFor($action, $request, $entity, $_format), $entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @return string The crud name.
|
||||||
* @return string the crud name
|
|
||||||
*/
|
*/
|
||||||
protected function getCrudName(): string
|
protected function getCrudName(): string
|
||||||
{
|
{
|
||||||
@@ -241,9 +222,6 @@ class AbstractCRUDController extends AbstractController
|
|||||||
$this->crudConfig = $config;
|
$this->crudConfig = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return PaginatorFactory
|
|
||||||
*/
|
|
||||||
protected function getPaginatorFactory(): PaginatorFactory
|
protected function getPaginatorFactory(): PaginatorFactory
|
||||||
{
|
{
|
||||||
return $this->container->get('chill_main.paginator_factory');
|
return $this->container->get('chill_main.paginator_factory');
|
||||||
@@ -254,9 +232,6 @@ class AbstractCRUDController extends AbstractController
|
|||||||
return $this->get('validator');
|
return $this->get('validator');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function getSubscribedServices(): array
|
public static function getSubscribedServices(): array
|
||||||
{
|
{
|
||||||
return \array_merge(
|
return \array_merge(
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace Chill\MainBundle;
|
namespace Chill\MainBundle;
|
||||||
|
|
||||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
|
use Chill\MainBundle\Search\SearchApiInterface;
|
||||||
use Chill\MainBundle\Search\SearchInterface;
|
use Chill\MainBundle\Search\SearchInterface;
|
||||||
use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
|
use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
|
||||||
use Chill\MainBundle\Security\ProvideRoleInterface;
|
use Chill\MainBundle\Security\ProvideRoleInterface;
|
||||||
@@ -41,6 +42,8 @@ class ChillMainBundle extends Bundle
|
|||||||
->addTag('chill_main.scope_resolver');
|
->addTag('chill_main.scope_resolver');
|
||||||
$container->registerForAutoconfiguration(ChillEntityRenderInterface::class)
|
$container->registerForAutoconfiguration(ChillEntityRenderInterface::class)
|
||||||
->addTag('chill.render_entity');
|
->addTag('chill.render_entity');
|
||||||
|
$container->registerForAutoconfiguration(SearchApiInterface::class)
|
||||||
|
->addTag('chill.search_api_provider');
|
||||||
|
|
||||||
$container->addCompilerPass(new SearchableServicesCompilerPass());
|
$container->addCompilerPass(new SearchableServicesCompilerPass());
|
||||||
$container->addCompilerPass(new ConfigConsistencyCompilerPass());
|
$container->addCompilerPass(new ConfigConsistencyCompilerPass());
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\MainBundle\Command;
|
namespace Chill\MainBundle\Command;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Repository\UserRepository;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
@@ -23,101 +26,51 @@ use League\Csv\Writer;
|
|||||||
|
|
||||||
class ChillImportUsersCommand extends Command
|
class ChillImportUsersCommand extends Command
|
||||||
{
|
{
|
||||||
|
protected EntityManagerInterface $em;
|
||||||
|
|
||||||
/**
|
protected ValidatorInterface $validator;
|
||||||
*
|
|
||||||
* @var EntityManagerInterface
|
|
||||||
*/
|
|
||||||
protected $em;
|
|
||||||
|
|
||||||
/**
|
protected LoggerInterface $logger;
|
||||||
*
|
|
||||||
* @var ValidatorInterface
|
|
||||||
*/
|
|
||||||
protected $validator;
|
|
||||||
|
|
||||||
/**
|
protected UserPasswordEncoderInterface $passwordEncoder;
|
||||||
*
|
|
||||||
* @var LoggerInterface
|
|
||||||
*/
|
|
||||||
protected $logger;
|
|
||||||
|
|
||||||
/**
|
protected UserRepository $userRepository;
|
||||||
*
|
|
||||||
* @var UserPasswordEncoderInterface
|
|
||||||
*/
|
|
||||||
protected $passwordEncoder;
|
|
||||||
|
|
||||||
/**
|
protected bool $doChanges = true;
|
||||||
*
|
|
||||||
* @var \Chill\MainBundle\Repository\UserRepository
|
|
||||||
*/
|
|
||||||
protected $userRepository;
|
|
||||||
|
|
||||||
/**
|
protected OutputInterface $tempOutput;
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $doChanges = true;
|
|
||||||
|
|
||||||
/**
|
protected InputInterface $tempInput;
|
||||||
*
|
|
||||||
* @var OutputInterface
|
|
||||||
*/
|
|
||||||
protected $tempOutput;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @var InputInterface
|
|
||||||
*/
|
|
||||||
protected $tempInput;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Centers and aliases.
|
* Centers and aliases.
|
||||||
*
|
*
|
||||||
* key are aliases, values are an array of centers
|
* key are aliases, values are an array of centers
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
*/
|
||||||
protected $centers = [];
|
protected array $centers;
|
||||||
|
|
||||||
/**
|
protected array $permissionGroups;
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $permissionGroups = [];
|
|
||||||
|
|
||||||
/**
|
protected array $groupCenters;
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $groupCenters = [];
|
|
||||||
|
|
||||||
/**
|
protected Writer $output;
|
||||||
*
|
|
||||||
* @var Writer
|
|
||||||
*/
|
|
||||||
protected $output = null;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EntityManagerInterface $em,
|
EntityManagerInterface $em,
|
||||||
LoggerInterface $logger,
|
LoggerInterface $logger,
|
||||||
UserPasswordEncoderInterface $passwordEncoder,
|
UserPasswordEncoderInterface $passwordEncoder,
|
||||||
ValidatorInterface $validator
|
ValidatorInterface $validator,
|
||||||
|
UserRepository $userRepository
|
||||||
) {
|
) {
|
||||||
$this->em = $em;
|
$this->em = $em;
|
||||||
$this->passwordEncoder = $passwordEncoder;
|
$this->passwordEncoder = $passwordEncoder;
|
||||||
$this->validator = $validator;
|
$this->validator = $validator;
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
|
$this->userRepository = $userRepository;
|
||||||
|
|
||||||
$this->userRepository = $em->getRepository(User::class);
|
|
||||||
|
|
||||||
parent::__construct('chill:main:import-users');
|
parent::__construct('chill:main:import-users');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected function configure()
|
protected function configure()
|
||||||
{
|
{
|
||||||
$this
|
$this
|
||||||
@@ -126,8 +79,7 @@ class ChillImportUsersCommand extends Command
|
|||||||
->addArgument('csvfile', InputArgument::REQUIRED, 'Path to the csv file. Columns are: `username`, `email`, `center` (can contain alias), `permission group`')
|
->addArgument('csvfile', InputArgument::REQUIRED, 'Path to the csv file. Columns are: `username`, `email`, `center` (can contain alias), `permission group`')
|
||||||
->addOption('grouping-centers', null, InputOption::VALUE_OPTIONAL, 'Path to a csv file to aggregate multiple centers into a single alias')
|
->addOption('grouping-centers', null, InputOption::VALUE_OPTIONAL, 'Path to a csv file to aggregate multiple centers into a single alias')
|
||||||
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not commit the changes')
|
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not commit the changes')
|
||||||
->addOption('csv-dump', null, InputOption::VALUE_REQUIRED, 'A path to dump a summary of the created file')
|
->addOption('csv-dump', null, InputOption::VALUE_REQUIRED, 'A path to dump a summary of the created file');
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
@@ -309,21 +261,14 @@ class ChillImportUsersCommand extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $this->permissionGroups[$alias];
|
return $this->permissionGroups[$alias];
|
||||||
} else {
|
|
||||||
$this->logger->error("Error while responding to a a question");
|
|
||||||
|
|
||||||
$this->tempOutput("Ok, I accept, but I do not know what to do. Please try again.");
|
|
||||||
|
|
||||||
throw new \RuntimeException("Error while responding to a question");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
$this->logger->error('Error while responding to a a question');
|
||||||
*
|
$this->tempOutput->writeln('Ok, I accept, but I do not know what to do. Please try again.');
|
||||||
* @param Center $center
|
|
||||||
* @param \Chill\MainBundle\Command\PermissionGroup $pg
|
throw new \RuntimeException('Error while responding to a question');
|
||||||
* @return GroupCenter
|
}
|
||||||
*/
|
|
||||||
protected function createOrGetGroupCenter(Center $center, PermissionsGroup $pg): GroupCenter
|
protected function createOrGetGroupCenter(Center $center, PermissionsGroup $pg): GroupCenter
|
||||||
{
|
{
|
||||||
if (\array_key_exists($center->getId(), $this->groupCenters)) {
|
if (\array_key_exists($center->getId(), $this->groupCenters)) {
|
||||||
|
@@ -1,20 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\MainBundle\Controller;
|
namespace Chill\MainBundle\Controller;
|
||||||
|
|
||||||
use Chill\MainBundle\CRUD\Controller\CRUDController;
|
use Chill\MainBundle\CRUD\Controller\CRUDController;
|
||||||
use Chill\MainBundle\Entity\Country;
|
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class AdminCountryCRUDController extends CRUDController
|
class AdminCountryCRUDController extends CRUDController
|
||||||
{
|
{
|
||||||
|
|
||||||
function __construct(PaginatorFactory $paginator)
|
|
||||||
{
|
|
||||||
$this->paginatorFactory = $paginator;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -137,17 +137,15 @@ class UserController extends CRUDController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function createEditPasswordForm(User $user): FormInterface
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param User $user
|
|
||||||
* @return \Symfony\Component\Form\Form
|
|
||||||
*/
|
|
||||||
private function createEditPasswordForm(User $user)
|
|
||||||
{
|
{
|
||||||
return $this->createForm(UserPasswordType::class, null, array(
|
return $this->createForm(
|
||||||
|
UserPasswordType::class,
|
||||||
|
null,
|
||||||
|
[
|
||||||
'user' => $user
|
'user' => $user
|
||||||
))
|
]
|
||||||
|
)
|
||||||
->add('submit', SubmitType::class, array('label' => 'Change password'))
|
->add('submit', SubmitType::class, array('label' => 'Change password'))
|
||||||
->remove('actual_password');
|
->remove('actual_password');
|
||||||
}
|
}
|
||||||
@@ -314,7 +312,7 @@ class UserController extends CRUDController
|
|||||||
{
|
{
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$form = $this->createForm(UserCurrentLocationType::class, $user)
|
$form = $this->createForm(UserCurrentLocationType::class, $user)
|
||||||
->add('submit', SubmitType::class, ['label' => 'Change current location'])
|
->add('submit', SubmitType::class, ['label' => 'Save'])
|
||||||
->handleRequest($request);
|
->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
@@ -23,12 +23,11 @@ namespace Chill\MainBundle\Entity;
|
|||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity
|
* @ORM\Entity
|
||||||
* @ORM\Table(name="centers")
|
* @ORM\Table(name="centers")
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
*/
|
||||||
class Center implements HasCenterInterface
|
class Center implements HasCenterInterface
|
||||||
{
|
{
|
||||||
@@ -46,8 +45,9 @@ class Center implements HasCenterInterface
|
|||||||
* @var string
|
* @var string
|
||||||
*
|
*
|
||||||
* @ORM\Column(type="string", length=255)
|
* @ORM\Column(type="string", length=255)
|
||||||
|
* @Serializer\Groups({"docgen:read"})
|
||||||
*/
|
*/
|
||||||
private $name;
|
private string $name = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection
|
* @var Collection
|
||||||
|
@@ -1,22 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Chill is a suite of a modules, Chill is a software for social workers
|
|
||||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\MainBundle\Entity;
|
namespace Chill\MainBundle\Entity;
|
||||||
|
|
||||||
@@ -28,37 +12,29 @@ use Doctrine\Common\Collections\ArrayCollection;
|
|||||||
* @ORM\Entity
|
* @ORM\Entity
|
||||||
* @ORM\Table(name="role_scopes")
|
* @ORM\Table(name="role_scopes")
|
||||||
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
|
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
*/
|
||||||
class RoleScope
|
class RoleScope
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var integer
|
|
||||||
*
|
|
||||||
* @ORM\Id
|
* @ORM\Id
|
||||||
* @ORM\Column(name="id", type="integer")
|
* @ORM\Column(name="id", type="integer")
|
||||||
* @ORM\GeneratedValue(strategy="AUTO")
|
* @ORM\GeneratedValue(strategy="AUTO")
|
||||||
*/
|
*/
|
||||||
private $id;
|
private ?int $id = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
|
||||||
*
|
|
||||||
* @ORM\Column(type="string", length=255)
|
* @ORM\Column(type="string", length=255)
|
||||||
*/
|
*/
|
||||||
private $role;
|
private ?string $role = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Scope
|
|
||||||
*
|
|
||||||
* @ORM\ManyToOne(
|
* @ORM\ManyToOne(
|
||||||
* targetEntity="Chill\MainBundle\Entity\Scope",
|
* targetEntity="Chill\MainBundle\Entity\Scope",
|
||||||
* inversedBy="roleScopes")
|
* inversedBy="roleScopes")
|
||||||
* @ORM\JoinColumn(nullable=true, name="scope_id")
|
* @ORM\JoinColumn(nullable=true, name="scope_id")
|
||||||
* @ORM\Cache(usage="NONSTRICT_READ_WRITE")
|
* @ORM\Cache(usage="NONSTRICT_READ_WRITE")
|
||||||
*/
|
*/
|
||||||
private $scope;
|
private ?Scope $scope = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection
|
* @var Collection
|
||||||
@@ -69,55 +45,33 @@ class RoleScope
|
|||||||
*/
|
*/
|
||||||
private $permissionsGroups;
|
private $permissionsGroups;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RoleScope constructor.
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->new = true;
|
|
||||||
$this->permissionsGroups = new ArrayCollection();
|
$this->permissionsGroups = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getId(): ?int
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getId()
|
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getRole(): ?string
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getRole()
|
|
||||||
{
|
{
|
||||||
return $this->role;
|
return $this->role;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getScope(): ?Scope
|
||||||
* @return Scope
|
|
||||||
*/
|
|
||||||
public function getScope()
|
|
||||||
{
|
{
|
||||||
return $this->scope;
|
return $this->scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setRole(?string $role = null): self
|
||||||
* @param type $role
|
|
||||||
* @return RoleScope
|
|
||||||
*/
|
|
||||||
public function setRole($role)
|
|
||||||
{
|
{
|
||||||
$this->role = $role;
|
$this->role = $role;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setScope(?Scope $scope = null): self
|
||||||
* @param Scope $scope
|
|
||||||
* @return RoleScope
|
|
||||||
*/
|
|
||||||
public function setScope(Scope $scope = null)
|
|
||||||
{
|
{
|
||||||
$this->scope = $scope;
|
$this->scope = $scope;
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@ use Chill\MainBundle\Entity\UserJob;
|
|||||||
use Chill\MainBundle\Entity\Location;
|
use Chill\MainBundle\Entity\Location;
|
||||||
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
|
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User
|
* User
|
||||||
@@ -17,7 +17,7 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
|||||||
* @ORM\Entity
|
* @ORM\Entity
|
||||||
* @ORM\Table(name="users")
|
* @ORM\Table(name="users")
|
||||||
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
|
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
|
||||||
* @DiscriminatorMap(typeProperty="type", mapping={
|
* @Serializer\DiscriminatorMap(typeProperty="type", mapping={
|
||||||
* "user"=User::class
|
* "user"=User::class
|
||||||
* })
|
* })
|
||||||
*/
|
*/
|
||||||
@@ -52,6 +52,7 @@ class User implements AdvancedUserInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="string", length=200)
|
* @ORM\Column(type="string", length=200)
|
||||||
|
* @Serializer\Groups({"docgen:read"})
|
||||||
*/
|
*/
|
||||||
private string $label = '';
|
private string $label = '';
|
||||||
|
|
||||||
@@ -59,8 +60,9 @@ class User implements AdvancedUserInterface {
|
|||||||
* @var string
|
* @var string
|
||||||
*
|
*
|
||||||
* @ORM\Column(type="string", length=150, nullable=true)
|
* @ORM\Column(type="string", length=150, nullable=true)
|
||||||
|
* @Serializer\Groups({"docgen:read"})
|
||||||
*/
|
*/
|
||||||
private $email;
|
private ?string $email = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
@@ -124,6 +126,7 @@ class User implements AdvancedUserInterface {
|
|||||||
/**
|
/**
|
||||||
* @var Center|null
|
* @var Center|null
|
||||||
* @ORM\ManyToOne(targetEntity=Center::class)
|
* @ORM\ManyToOne(targetEntity=Center::class)
|
||||||
|
* @Serializer\Groups({"docgen:read"})
|
||||||
*/
|
*/
|
||||||
private ?Center $mainCenter = null;
|
private ?Center $mainCenter = null;
|
||||||
|
|
||||||
|
@@ -1,44 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Chill is a software for social workers
|
|
||||||
* Copyright (C) 2016 Champs-Libres Coopérative <info@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\MainBundle\Pagination;
|
namespace Chill\MainBundle\Pagination;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PageGenerator associated with a Paginator
|
* PageGenerator associated with a Paginator.
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
* @author Champs Libres <info@champs-libres.coop>
|
|
||||||
*/
|
*/
|
||||||
class PageGenerator implements \Iterator
|
class PageGenerator implements \Iterator
|
||||||
{
|
{
|
||||||
/**
|
protected Paginator $paginator;
|
||||||
*
|
|
||||||
* @var Paginator
|
|
||||||
*/
|
|
||||||
protected $paginator;
|
|
||||||
|
|
||||||
/**
|
protected int $current = 1;
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $current = 1;
|
|
||||||
|
|
||||||
public function __construct(Paginator $paginator)
|
public function __construct(Paginator $paginator)
|
||||||
{
|
{
|
||||||
|
@@ -402,3 +402,11 @@ input.belgian_national_number {
|
|||||||
&.daily_counter {}
|
&.daily_counter {}
|
||||||
&.control_digit {}
|
&.control_digit {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// replace abbr
|
||||||
|
span.item-key {
|
||||||
|
font-variant: all-small-caps;
|
||||||
|
font-size: 90%;
|
||||||
|
background-color: #0000000a;
|
||||||
|
//text-decoration: dotted underline;
|
||||||
|
}
|
||||||
|
@@ -8,3 +8,43 @@ require('./bootstrap.scss');
|
|||||||
import Dropdown from 'bootstrap/js/src/dropdown';
|
import Dropdown from 'bootstrap/js/src/dropdown';
|
||||||
import Modal from 'bootstrap/js/dist/modal';
|
import Modal from 'bootstrap/js/dist/modal';
|
||||||
import Collapse from 'bootstrap/js/src/collapse';
|
import Collapse from 'bootstrap/js/src/collapse';
|
||||||
|
import Carousel from 'bootstrap/js/src/carousel';
|
||||||
|
|
||||||
|
//
|
||||||
|
// ACHeaderSlider is a small slider used in banner of AccompanyingCourse Section
|
||||||
|
// Initialize options, and show/hide controls in first/last slides
|
||||||
|
//
|
||||||
|
let ACHeaderSlider = document.querySelector('#ACHeaderSlider');
|
||||||
|
if (ACHeaderSlider) {
|
||||||
|
let controlPrev = ACHeaderSlider.querySelector('button[data-bs-slide="prev"]'),
|
||||||
|
controlNext = ACHeaderSlider.querySelector('button[data-bs-slide="next"]'),
|
||||||
|
length = ACHeaderSlider.querySelectorAll('.carousel-item').length,
|
||||||
|
last = length-1,
|
||||||
|
carousel = new Carousel(ACHeaderSlider, {
|
||||||
|
interval: false,
|
||||||
|
wrap: false,
|
||||||
|
ride: false,
|
||||||
|
keyboard: false,
|
||||||
|
touch: true
|
||||||
|
})
|
||||||
|
;
|
||||||
|
document.addEventListener('DOMContentLoaded', (e) => {
|
||||||
|
controlNext.classList.remove('visually-hidden');
|
||||||
|
});
|
||||||
|
ACHeaderSlider.addEventListener('slid.bs.carousel', (e) => {
|
||||||
|
//console.log('from slide', e.direction, e.relatedTarget, e.from, e.to );
|
||||||
|
switch (e.to) {
|
||||||
|
case 0:
|
||||||
|
controlPrev.classList.add('visually-hidden');
|
||||||
|
controlNext.classList.remove('visually-hidden');
|
||||||
|
break;
|
||||||
|
case last:
|
||||||
|
controlPrev.classList.remove('visually-hidden');
|
||||||
|
controlNext.classList.add('visually-hidden');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
controlPrev.classList.remove('visually-hidden');
|
||||||
|
controlNext.classList.remove('visually-hidden');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
<title>{{ installation.name }} - {% block title %}{% endblock %}</title>
|
<title>{{ installation.name }} - {% block title %}{{ 'Homepage'|trans }}{% endblock %}</title>
|
||||||
<link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon">
|
<link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon">
|
||||||
|
|
||||||
{{ encore_entry_link_tags('mod_bootstrap') }}
|
{{ encore_entry_link_tags('mod_bootstrap') }}
|
||||||
@@ -68,6 +68,9 @@
|
|||||||
<button type="submit" class="btn btn-lg btn-warning mt-3">
|
<button type="submit" class="btn btn-lg btn-warning mt-3">
|
||||||
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
|
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
|
||||||
</button>
|
</button>
|
||||||
|
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
|
||||||
|
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
|
||||||
|
</a>
|
||||||
</center>
|
</center>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\MainBundle\Routing;
|
namespace Chill\MainBundle\Routing;
|
||||||
|
|
||||||
use Symfony\Component\Routing\RouteCollection;
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
@@ -8,7 +10,6 @@ use Knp\Menu\FactoryInterface;
|
|||||||
use Knp\Menu\ItemInterface;
|
use Knp\Menu\ItemInterface;
|
||||||
use Symfony\Component\Translation\TranslatorInterface;
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class permit to build menu from the routing information
|
* This class permit to build menu from the routing information
|
||||||
* stored in each bundle.
|
* stored in each bundle.
|
||||||
@@ -20,29 +21,15 @@ use Symfony\Component\Translation\TranslatorInterface;
|
|||||||
class MenuComposer
|
class MenuComposer
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
private RouterInterface $router;
|
||||||
*
|
|
||||||
* @var RouterInterface
|
|
||||||
*/
|
|
||||||
private $router;
|
|
||||||
|
|
||||||
/**
|
private FactoryInterface $menuFactory;
|
||||||
*
|
|
||||||
* @var FactoryInterface
|
|
||||||
*/
|
|
||||||
private $menuFactory;
|
|
||||||
|
|
||||||
/**
|
private TranslatorInterface $translator;
|
||||||
*
|
|
||||||
* @var TranslatorInterface
|
|
||||||
*/
|
|
||||||
private $translator;
|
|
||||||
|
|
||||||
/**
|
private array $localMenuBuilders = [];
|
||||||
*
|
|
||||||
* @var
|
private RouteCollection $routeCollection;
|
||||||
*/
|
|
||||||
private $localMenuBuilders = [];
|
|
||||||
|
|
||||||
|
|
||||||
function __construct(
|
function __construct(
|
||||||
|
@@ -78,11 +78,11 @@ abstract class AbstractSearch implements SearchInterface
|
|||||||
$recomposed .= ' '.$term.':';
|
$recomposed .= ' '.$term.':';
|
||||||
$containsSpace = \strpos($terms[$term], " ") !== false;
|
$containsSpace = \strpos($terms[$term], " ") !== false;
|
||||||
if ($containsSpace) {
|
if ($containsSpace) {
|
||||||
$recomposed .= "(";
|
$recomposed .= '"';
|
||||||
}
|
}
|
||||||
$recomposed .= (mb_stristr(' ', $terms[$term]) === FALSE) ? $terms[$term] : '('.$terms[$term].')';
|
$recomposed .= (mb_stristr(' ', $terms[$term]) === FALSE) ? $terms[$term] : '('.$terms[$term].')';
|
||||||
if ($containsSpace) {
|
if ($containsSpace) {
|
||||||
$recomposed .= ")";
|
$recomposed .= '"';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,19 +20,15 @@ class SearchApi
|
|||||||
private EntityManagerInterface $em;
|
private EntityManagerInterface $em;
|
||||||
private PaginatorFactory $paginator;
|
private PaginatorFactory $paginator;
|
||||||
|
|
||||||
private array $providers = [];
|
private iterable $providers = [];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EntityManagerInterface $em,
|
EntityManagerInterface $em,
|
||||||
SearchPersonApiProvider $searchPerson,
|
iterable $providers,
|
||||||
ThirdPartyApiSearch $thirdPartyApiSearch,
|
|
||||||
SearchUserApiProvider $searchUser,
|
|
||||||
PaginatorFactory $paginator
|
PaginatorFactory $paginator
|
||||||
) {
|
) {
|
||||||
$this->em = $em;
|
$this->em = $em;
|
||||||
$this->providers[] = $searchPerson;
|
$this->providers = $providers;
|
||||||
$this->providers[] = $thirdPartyApiSearch;
|
|
||||||
$this->providers[] = $searchUser;
|
|
||||||
$this->paginator = $paginator;
|
$this->paginator = $paginator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,10 +64,15 @@ class SearchApi
|
|||||||
|
|
||||||
private function findProviders(string $pattern, array $types, array $parameters): array
|
private function findProviders(string $pattern, array $types, array $parameters): array
|
||||||
{
|
{
|
||||||
return \array_filter(
|
$providers = [];
|
||||||
$this->providers,
|
|
||||||
fn($p) => $p->supportsTypes($pattern, $types, $parameters)
|
foreach ($this->providers as $provider) {
|
||||||
);
|
if ($provider->supportsTypes($pattern, $types, $parameters)) {
|
||||||
|
$providers[] = $provider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function countItems($providers, $types, $parameters): int
|
private function countItems($providers, $types, $parameters): int
|
||||||
@@ -82,12 +83,12 @@ class SearchApi
|
|||||||
$countNq = $this->em->createNativeQuery($countQuery, $rsmCount);
|
$countNq = $this->em->createNativeQuery($countQuery, $rsmCount);
|
||||||
$countNq->setParameters($parameters);
|
$countNq->setParameters($parameters);
|
||||||
|
|
||||||
return $countNq->getSingleScalarResult();
|
return (int) $countNq->getSingleScalarResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildCountQuery(array $queries, $types, $parameters)
|
private function buildCountQuery(array $queries, $types, $parameters)
|
||||||
{
|
{
|
||||||
$query = "SELECT COUNT(*) AS count FROM ({union_unordered}) AS sq";
|
$query = "SELECT SUM(c) AS count FROM ({union_unordered}) AS sq";
|
||||||
$unions = [];
|
$unions = [];
|
||||||
$parameters = [];
|
$parameters = [];
|
||||||
|
|
||||||
@@ -141,17 +142,20 @@ class SearchApi
|
|||||||
private function prepareProviders(array $rawResults)
|
private function prepareProviders(array $rawResults)
|
||||||
{
|
{
|
||||||
$metadatas = [];
|
$metadatas = [];
|
||||||
|
$providers = [];
|
||||||
|
|
||||||
foreach ($rawResults as $r) {
|
foreach ($rawResults as $r) {
|
||||||
foreach ($this->providers as $k => $p) {
|
foreach ($this->providers as $k => $p) {
|
||||||
if ($p->supportsResult($r['key'], $r['metadata'])) {
|
if ($p->supportsResult($r['key'], $r['metadata'])) {
|
||||||
$metadatas[$k][] = $r['metadata'];
|
$metadatas[$k][] = $r['metadata'];
|
||||||
|
$providers[$k] = $p;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($metadatas as $k => $m) {
|
foreach ($metadatas as $k => $m) {
|
||||||
$this->providers[$k]->prepare($m);
|
$providers[$k]->prepare($m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,6 +4,8 @@ namespace Chill\MainBundle\Search;
|
|||||||
|
|
||||||
class SearchApiQuery
|
class SearchApiQuery
|
||||||
{
|
{
|
||||||
|
private array $select = [];
|
||||||
|
private array $selectParams = [];
|
||||||
private ?string $selectKey = null;
|
private ?string $selectKey = null;
|
||||||
private array $selectKeyParams = [];
|
private array $selectKeyParams = [];
|
||||||
private ?string $jsonbMetadata = null;
|
private ?string $jsonbMetadata = null;
|
||||||
@@ -15,6 +17,38 @@ class SearchApiQuery
|
|||||||
private array $whereClauses = [];
|
private array $whereClauses = [];
|
||||||
private array $whereClausesParams = [];
|
private array $whereClausesParams = [];
|
||||||
|
|
||||||
|
public function addSelectClause(string $select, array $params = []): self
|
||||||
|
{
|
||||||
|
$this->select[] = $select;
|
||||||
|
$this->selectParams = [...$this->selectParams, ...$params];
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resetSelectClause(): self
|
||||||
|
{
|
||||||
|
$this->select = [];
|
||||||
|
$this->selectParams = [];
|
||||||
|
$this->selectKey = null;
|
||||||
|
$this->selectKeyParams = [];
|
||||||
|
$this->jsonbMetadata = null;
|
||||||
|
$this->jsonbMetadataParams = [];
|
||||||
|
$this->pertinence = null;
|
||||||
|
$this->pertinenceParams = [];
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSelectClauses(): array
|
||||||
|
{
|
||||||
|
return $this->select;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSelectParams(): array
|
||||||
|
{
|
||||||
|
return $this->selectParams;
|
||||||
|
}
|
||||||
|
|
||||||
public function setSelectKey(string $selectKey, array $params = []): self
|
public function setSelectKey(string $selectKey, array $params = []): self
|
||||||
{
|
{
|
||||||
$this->selectKey = $selectKey;
|
$this->selectKey = $selectKey;
|
||||||
@@ -47,6 +81,16 @@ class SearchApiQuery
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFromClause(): string
|
||||||
|
{
|
||||||
|
return $this->fromClause;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFromParams(): array
|
||||||
|
{
|
||||||
|
return $this->fromClauseParams;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the where clause and replace all existing ones.
|
* Set the where clause and replace all existing ones.
|
||||||
*
|
*
|
||||||
@@ -54,7 +98,7 @@ class SearchApiQuery
|
|||||||
public function setWhereClauses(string $whereClause, array $params = []): self
|
public function setWhereClauses(string $whereClause, array $params = []): self
|
||||||
{
|
{
|
||||||
$this->whereClauses = [$whereClause];
|
$this->whereClauses = [$whereClause];
|
||||||
$this->whereClausesParams = [$params];
|
$this->whereClausesParams = $params;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -71,11 +115,53 @@ class SearchApiQuery
|
|||||||
public function andWhereClause(string $whereClause, array $params = []): self
|
public function andWhereClause(string $whereClause, array $params = []): self
|
||||||
{
|
{
|
||||||
$this->whereClauses[] = $whereClause;
|
$this->whereClauses[] = $whereClause;
|
||||||
$this->whereClausesParams[] = $params;
|
\array_push($this->whereClausesParams, ...$params);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildSelectParams(bool $count = false): array
|
||||||
|
{
|
||||||
|
if ($count) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$args = $this->getSelectParams();
|
||||||
|
|
||||||
|
if (null !== $this->selectKey) {
|
||||||
|
$args = [...$args, ...$this->selectKeyParams];
|
||||||
|
}
|
||||||
|
if (null !== $this->jsonbMetadata) {
|
||||||
|
$args = [...$args, ...$this->jsonbMetadataParams];
|
||||||
|
}
|
||||||
|
if (null !== $this->pertinence) {
|
||||||
|
$args = [...$args, ...$this->pertinenceParams];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildSelectClause(bool $countOnly = false): string
|
||||||
|
{
|
||||||
|
if ($countOnly) {
|
||||||
|
return 'count(*) AS c';
|
||||||
|
}
|
||||||
|
|
||||||
|
$selects = $this->getSelectClauses();
|
||||||
|
|
||||||
|
if (null !== $this->selectKey) {
|
||||||
|
$selects[] = \strtr("'{key}' AS key", [ '{key}' => $this->selectKey ]);
|
||||||
|
}
|
||||||
|
if (null !== $this->jsonbMetadata) {
|
||||||
|
$selects[] = \strtr('{metadata} AS metadata', [ '{metadata}' => $this->jsonbMetadata]);
|
||||||
|
}
|
||||||
|
if (null !== $this->pertinence) {
|
||||||
|
$selects[] = \strtr('{pertinence} AS pertinence', [ '{pertinence}' => $this->pertinence]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return \implode(', ', $selects);
|
||||||
|
}
|
||||||
|
|
||||||
public function buildQuery(bool $countOnly = false): string
|
public function buildQuery(bool $countOnly = false): string
|
||||||
{
|
{
|
||||||
$isMultiple = count($this->whereClauses);
|
$isMultiple = count($this->whereClauses);
|
||||||
@@ -87,19 +173,8 @@ class SearchApiQuery
|
|||||||
($isMultiple ? ')' : '')
|
($isMultiple ? ')' : '')
|
||||||
;
|
;
|
||||||
|
|
||||||
if (!$countOnly) {
|
$select = $this->buildSelectClause($countOnly);
|
||||||
$select = \strtr("
|
|
||||||
'{key}' AS key,
|
|
||||||
{metadata} AS metadata,
|
|
||||||
{pertinence} AS pertinence
|
|
||||||
", [
|
|
||||||
'{key}' => $this->selectKey,
|
|
||||||
'{metadata}' => $this->jsonbMetadata,
|
|
||||||
'{pertinence}' => $this->pertinence,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
$select = "1 AS c";
|
|
||||||
}
|
|
||||||
|
|
||||||
return \strtr("SELECT
|
return \strtr("SELECT
|
||||||
{select}
|
{select}
|
||||||
@@ -116,18 +191,16 @@ class SearchApiQuery
|
|||||||
public function buildParameters(bool $countOnly = false): array
|
public function buildParameters(bool $countOnly = false): array
|
||||||
{
|
{
|
||||||
if (!$countOnly) {
|
if (!$countOnly) {
|
||||||
return \array_merge(
|
return [
|
||||||
$this->selectKeyParams,
|
...$this->buildSelectParams($countOnly),
|
||||||
$this->jsonbMetadataParams,
|
...$this->fromClauseParams,
|
||||||
$this->pertinenceParams,
|
...$this->whereClausesParams,
|
||||||
$this->fromClauseParams,
|
];
|
||||||
\array_merge([], ...$this->whereClausesParams),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return \array_merge(
|
return [
|
||||||
$this->fromClauseParams,
|
...$this->fromClauseParams,
|
||||||
\array_merge([], ...$this->whereClausesParams),
|
...$this->whereClausesParams,
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\MainBundle\Search;
|
namespace Chill\MainBundle\Search;
|
||||||
|
|
||||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
@@ -10,6 +12,8 @@ class SearchApiResult
|
|||||||
|
|
||||||
private $result;
|
private $result;
|
||||||
|
|
||||||
|
private float $relevance;
|
||||||
|
|
||||||
public function __construct(float $relevance)
|
public function __construct(float $relevance)
|
||||||
{
|
{
|
||||||
$this->relevance = $relevance;
|
$this->relevance = $relevance;
|
||||||
|
@@ -121,14 +121,15 @@ class SearchProvider
|
|||||||
private function extractTerms(&$subject)
|
private function extractTerms(&$subject)
|
||||||
{
|
{
|
||||||
$terms = array();
|
$terms = array();
|
||||||
preg_match_all('/([a-z\-]+):([\w\-]+|\([^\(\r\n]+\))/', $subject, $matches);
|
$matches = [];
|
||||||
|
preg_match_all('/([a-z\-]+):(([^"][\S\-]+)|"[^"]*")/', $subject, $matches);
|
||||||
|
|
||||||
foreach ($matches[2] as $key => $match) {
|
foreach ($matches[2] as $key => $match) {
|
||||||
//remove from search pattern
|
//remove from search pattern
|
||||||
$this->mustBeExtracted[] = $matches[0][$key];
|
$this->mustBeExtracted[] = $matches[0][$key];
|
||||||
//strip parenthesis
|
//strip parenthesis
|
||||||
if (mb_substr($match, 0, 1) === '(' &&
|
if (mb_substr($match, 0, 1) === '"' &&
|
||||||
mb_substr($match, mb_strlen($match) - 1) === ')') {
|
mb_substr($match, mb_strlen($match) - 1) === '"') {
|
||||||
$match = trim(mb_substr($match, 1, mb_strlen($match) - 2));
|
$match = trim(mb_substr($match, 1, mb_strlen($match) - 2));
|
||||||
}
|
}
|
||||||
$terms[$matches[1][$key]] = $match;
|
$terms[$matches[1][$key]] = $match;
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Search\Utils;
|
||||||
|
|
||||||
|
use \DateTimeImmutable;
|
||||||
|
|
||||||
|
class ExtractDateFromPattern
|
||||||
|
{
|
||||||
|
private const DATE_PATTERN = [
|
||||||
|
["([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))", 'Y-m-d'], // 1981-05-12
|
||||||
|
["((0[1-9]|[12]\d|3[01])\/(0[1-9]|1[0-2])\/([12]\d{3}))", 'd/m/Y'], // 15/12/1980
|
||||||
|
["((0[1-9]|[12]\d|3[01])-(0[1-9]|1[0-2])-([12]\d{3}))", 'd-m-Y'], // 15/12/1980
|
||||||
|
];
|
||||||
|
|
||||||
|
public function extractDates(string $subject): SearchExtractionResult
|
||||||
|
{
|
||||||
|
$dates = [];
|
||||||
|
$filteredSubject = $subject;
|
||||||
|
|
||||||
|
foreach (self::DATE_PATTERN as [$pattern, $format]) {
|
||||||
|
$matches = [];
|
||||||
|
\preg_match_all($pattern, $filteredSubject, $matches);
|
||||||
|
|
||||||
|
foreach ($matches[0] as $match) {
|
||||||
|
$date = DateTimeImmutable::createFromFormat($format, $match);
|
||||||
|
if (false !== $date) {
|
||||||
|
$dates[] = $date;
|
||||||
|
// filter string: remove what is found
|
||||||
|
$filteredSubject = \trim(\strtr($filteredSubject, [$match => ""]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SearchExtractionResult($filteredSubject, $dates);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Search\Utils;
|
||||||
|
|
||||||
|
|
||||||
|
class ExtractPhonenumberFromPattern
|
||||||
|
{
|
||||||
|
private const PATTERN = "([\+]{0,1}[0-9\ ]{5,})";
|
||||||
|
|
||||||
|
public function extractPhonenumber(string $subject): SearchExtractionResult
|
||||||
|
{
|
||||||
|
$matches = [];
|
||||||
|
\preg_match(self::PATTERN, $subject,$matches);
|
||||||
|
|
||||||
|
if (0 < count($matches)) {
|
||||||
|
$phonenumber = [];
|
||||||
|
$length = 0;
|
||||||
|
|
||||||
|
foreach (\str_split(\trim($matches[0])) as $key => $char) {
|
||||||
|
switch ($char) {
|
||||||
|
case '0':
|
||||||
|
$length++;
|
||||||
|
if ($key === 0) { $phonenumber[] = '+32'; }
|
||||||
|
else { $phonenumber[] = $char; }
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
$length++;
|
||||||
|
$phonenumber[] = $char;
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \LogicException("should not match not alnum character");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($length > 5) {
|
||||||
|
$filtered = \trim(\strtr($subject, [$matches[0] => '']));
|
||||||
|
|
||||||
|
return new SearchExtractionResult($filtered, [\implode('', $phonenumber)] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SearchExtractionResult($subject, []);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Search\Utils;
|
||||||
|
|
||||||
|
class SearchExtractionResult
|
||||||
|
{
|
||||||
|
private string $filteredSubject;
|
||||||
|
private array $found;
|
||||||
|
|
||||||
|
public function __construct(string $filteredSubject, array $found)
|
||||||
|
{
|
||||||
|
$this->filteredSubject = $filteredSubject;
|
||||||
|
$this->found = $found;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFound(): array
|
||||||
|
{
|
||||||
|
return $this->found;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasResult(): bool
|
||||||
|
{
|
||||||
|
return [] !== $this->found;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilteredSubject(): string
|
||||||
|
{
|
||||||
|
return $this->filteredSubject;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,34 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
declare(strict_types=1);
|
||||||
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\MainBundle\Security\Authorization;
|
namespace Chill\MainBundle\Security\Authorization;
|
||||||
|
|
||||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Voter for Chill software.
|
* Voter for Chill software.
|
||||||
*
|
*
|
||||||
* This abstract Voter provide generic methods to handle object specific to Chill
|
* This abstract Voter provide generic methods to handle object specific to Chill
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
*/
|
||||||
abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
|
abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
|
||||||
{
|
{
|
||||||
@@ -39,6 +22,8 @@ abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
|
|||||||
. 'getSupportedAttributes and getSupportedClasses methods.',
|
. 'getSupportedAttributes and getSupportedClasses methods.',
|
||||||
E_USER_DEPRECATED);
|
E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
// @TODO: getSupportedAttributes() should be created in here and made abstract or in ChillVoterInterface.
|
||||||
|
// @TODO: getSupportedClasses() should be created in here and made abstract or in ChillVoterInterface.
|
||||||
return \in_array($attribute, $this->getSupportedAttributes($attribute))
|
return \in_array($attribute, $this->getSupportedAttributes($attribute))
|
||||||
&& \in_array(\get_class($subject), $this->getSupportedClasses());
|
&& \in_array(\get_class($subject), $this->getSupportedClasses());
|
||||||
}
|
}
|
||||||
@@ -49,7 +34,7 @@ abstract class AbstractChillVoter extends Voter implements ChillVoterInterface
|
|||||||
. 'methods introduced by Symfony 3.0, and do not rely on '
|
. 'methods introduced by Symfony 3.0, and do not rely on '
|
||||||
. 'isGranted method', E_USER_DEPRECATED);
|
. 'isGranted method', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
// @TODO: isGranted() should be created in here and made abstract or in ChillVoterInterface.
|
||||||
return $this->isGranted($attribute, $subject, $token->getUser());
|
return $this->isGranted($attribute, $subject, $token->getUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,44 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
/*
|
|
||||||
* Copyright (C) 2018 Julien Fastré <julien.fastre@champs-libres.coop>
|
declare(strict_types=1);
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
namespace Chill\MainBundle\Security\Authorization;
|
namespace Chill\MainBundle\Security\Authorization;
|
||||||
|
|
||||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Symfony\Component\Security\Core\Role\Role;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*/
|
|
||||||
class ChillExportVoter extends Voter
|
class ChillExportVoter extends Voter
|
||||||
{
|
{
|
||||||
const EXPORT = 'chill_export';
|
public const EXPORT = 'chill_export';
|
||||||
|
|
||||||
/**
|
protected AuthorizationHelperInterface $authorizationHelper;
|
||||||
*
|
|
||||||
* @var AuthorizationHelper
|
|
||||||
*/
|
|
||||||
protected $authorizationHelper;
|
|
||||||
|
|
||||||
public function __construct(AuthorizationHelper $authorizationHelper)
|
public function __construct(AuthorizationHelperInterface $authorizationHelper)
|
||||||
{
|
{
|
||||||
$this->authorizationHelper = $authorizationHelper;
|
$this->authorizationHelper = $authorizationHelper;
|
||||||
}
|
}
|
||||||
@@ -54,9 +30,6 @@ class ChillExportVoter extends Voter
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$centers = $this->authorizationHelper
|
return [] !== $this->authorizationHelper->getReachableCenters($token->getUser(), $attribute);
|
||||||
->getReachableCenters($token->getUser(), new Role($attribute));
|
|
||||||
|
|
||||||
return count($centers) > 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,27 +1,43 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Chill\MainBundle\Security\Resolver;
|
namespace Chill\MainBundle\Security\Resolver;
|
||||||
|
|
||||||
class CenterResolverDispatcher
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use CenterResolverManager and its interface CenterResolverManagerInterface
|
||||||
|
*/
|
||||||
|
final class CenterResolverDispatcher
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var iterabble|CenterResolverInterface[]
|
* @var CenterResolverInterface[]
|
||||||
*/
|
*/
|
||||||
private iterable $resolvers = [];
|
private iterable $resolvers;
|
||||||
|
|
||||||
public function __construct(iterable $resolvers)
|
public function __construct(iterable $resolvers = [])
|
||||||
{
|
{
|
||||||
$this->resolvers = $resolvers;
|
$this->resolvers = $resolvers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $entity
|
* @param object $entity
|
||||||
* @param array|null $options
|
|
||||||
* @return null|Center|Center[]
|
* @return null|Center|Center[]
|
||||||
*/
|
*/
|
||||||
public function resolveCenter($entity, ?array $options = [])
|
public function resolveCenter($entity, ?array $options = [])
|
||||||
{
|
{
|
||||||
foreach($this->resolvers as $priority => $resolver) {
|
trigger_deprecation(
|
||||||
|
'ChillMainBundle',
|
||||||
|
'dev-master',
|
||||||
|
'
|
||||||
|
Use the service CenterResolverManager through the interface CenterResolverManagerInterface.
|
||||||
|
The new method "CenterResolverManagerInterface::resolveCenters(): array" is available and the typing
|
||||||
|
has been improved in order to avoid mixing types.
|
||||||
|
'
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach($this->resolvers as $resolver) {
|
||||||
if ($resolver->supports($entity, $options)) {
|
if ($resolver->supports($entity, $options)) {
|
||||||
return $resolver->resolveCenter($entity, $options);
|
return $resolver->resolveCenter($entity, $options);
|
||||||
}
|
}
|
||||||
|
@@ -6,12 +6,14 @@ use Chill\MainBundle\Entity\Center;
|
|||||||
|
|
||||||
interface CenterResolverInterface
|
interface CenterResolverInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @param object $entity
|
||||||
|
*/
|
||||||
public function supports($entity, ?array $options = []): bool;
|
public function supports($entity, ?array $options = []): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $entity
|
* @param object $entity
|
||||||
* @param array|null $options
|
* @return Center|Center[]
|
||||||
* @return Center|array|Center[]
|
|
||||||
*/
|
*/
|
||||||
public function resolveCenter($entity, ?array $options = []);
|
public function resolveCenter($entity, ?array $options = []);
|
||||||
|
|
||||||
|
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Security\Resolver;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
|
||||||
|
final class CenterResolverManager implements CenterResolverManagerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CenterResolverInterface[]
|
||||||
|
*/
|
||||||
|
private iterable $resolvers;
|
||||||
|
|
||||||
|
public function __construct(iterable $resolvers = [])
|
||||||
|
{
|
||||||
|
$this->resolvers = $resolvers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resolveCenters($entity, ?array $options = []): array
|
||||||
|
{
|
||||||
|
foreach($this->resolvers as $resolver) {
|
||||||
|
if ($resolver->supports($entity, $options)) {
|
||||||
|
$resolved = $resolver->resolveCenter($entity, $options);
|
||||||
|
if (null === $resolved) {
|
||||||
|
return [];
|
||||||
|
} elseif ($resolved instanceof Center) {
|
||||||
|
return [$resolved];
|
||||||
|
} elseif (\is_array($resolved)) {
|
||||||
|
return $resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \UnexpectedValueException(sprintf("the return type of a %s should be an instance of %s, an array or null. Resolver is %s",
|
||||||
|
CenterResolverInterface::class, Center::class, get_class($resolver)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Security\Resolver;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Center;
|
||||||
|
|
||||||
|
interface CenterResolverManagerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param object $entity
|
||||||
|
* @return Center[]
|
||||||
|
*/
|
||||||
|
public function resolveCenters($entity, ?array $options = []): array;
|
||||||
|
}
|
@@ -6,20 +6,21 @@ use Twig\TwigFilter;
|
|||||||
|
|
||||||
final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension
|
final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension
|
||||||
{
|
{
|
||||||
private CenterResolverDispatcher $centerResolverDispatcher;
|
private CenterResolverManagerInterface $centerResolverDispatcher;
|
||||||
|
private ScopeResolverDispatcher $scopeResolverDispatcher;
|
||||||
|
|
||||||
/**
|
public function __construct(CenterResolverManagerInterface $centerResolverDispatcher, ScopeResolverDispatcher $scopeResolverDispatcher)
|
||||||
* @param CenterResolverDispatcher $centerResolverDispatcher
|
|
||||||
*/
|
|
||||||
public function __construct(CenterResolverDispatcher $centerResolverDispatcher)
|
|
||||||
{
|
{
|
||||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||||
|
$this->scopeResolverDispatcher = $scopeResolverDispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFilters()
|
public function getFilters()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
new TwigFilter('chill_resolve_center', [$this, 'resolveCenter'])
|
new TwigFilter('chill_resolve_center', [$this, 'resolveCenter']),
|
||||||
|
new TwigFilter('chill_resolve_scope', [$this, 'resolveScope']),
|
||||||
|
new TwigFilter('chill_is_scope_concerned', [$this, 'isScopeConcerned']),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +31,26 @@ final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension
|
|||||||
*/
|
*/
|
||||||
public function resolveCenter($entity, ?array $options = [])
|
public function resolveCenter($entity, ?array $options = [])
|
||||||
{
|
{
|
||||||
return $this->centerResolverDispatcher->resolveCenter($entity, $options);
|
return $this->centerResolverDispatcher->resolveCenters($entity, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $entity
|
||||||
|
* @param array|null $options
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isScopeConcerned($entity, ?array $options = [])
|
||||||
|
{
|
||||||
|
return $this->scopeResolverDispatcher->isConcerned($entity, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $entity
|
||||||
|
* @param array|null $options
|
||||||
|
* @return array|\Chill\MainBundle\Entity\Scope|\Chill\MainBundle\Entity\Scope[]
|
||||||
|
*/
|
||||||
|
public function resolveScope($entity, ?array $options = [])
|
||||||
|
{
|
||||||
|
return $this->scopeResolverDispatcher->resolveScope();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -52,7 +52,7 @@ class CenterNormalizer implements NormalizerInterface, DenormalizerInterface
|
|||||||
|
|
||||||
public function supportsNormalization($data, string $format = null): bool
|
public function supportsNormalization($data, string $format = null): bool
|
||||||
{
|
{
|
||||||
return $data instanceof Center;
|
return $data instanceof Center && $format === 'json';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function denormalize($data, string $type, string $format = null, array $context = [])
|
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||||
|
@@ -19,23 +19,78 @@
|
|||||||
|
|
||||||
namespace Chill\MainBundle\Serializer\Normalizer;
|
namespace Chill\MainBundle\Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
|
||||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||||
|
|
||||||
class DateNormalizer implements NormalizerInterface, DenormalizerInterface
|
class DateNormalizer implements ContextAwareNormalizerInterface, DenormalizerInterface
|
||||||
{
|
{
|
||||||
|
private RequestStack $requestStack;
|
||||||
|
private ParameterBagInterface $parameterBag;
|
||||||
|
|
||||||
|
public function __construct(RequestStack $requestStack, ParameterBagInterface $parameterBag)
|
||||||
|
{
|
||||||
|
$this->requestStack = $requestStack;
|
||||||
|
$this->parameterBag = $parameterBag;
|
||||||
|
}
|
||||||
|
|
||||||
public function normalize($date, string $format = null, array $context = array())
|
public function normalize($date, string $format = null, array $context = array())
|
||||||
{
|
{
|
||||||
/** @var \DateTimeInterface $date */
|
/** @var \DateTimeInterface $date */
|
||||||
|
switch($format) {
|
||||||
|
case 'json':
|
||||||
return [
|
return [
|
||||||
'datetime' => $date->format(\DateTimeInterface::ISO8601)
|
'datetime' => $date->format(\DateTimeInterface::ISO8601)
|
||||||
];
|
];
|
||||||
|
case 'docgen':
|
||||||
|
|
||||||
|
if (null === $date) {
|
||||||
|
return [
|
||||||
|
'long' => '', 'short' => ''
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supportsNormalization($data, string $format = null): bool
|
$hasTime = $date->format('His') !== "000000";
|
||||||
|
$request = $this->requestStack->getCurrentRequest();
|
||||||
|
$locale = null !== $request ? $request->getLocale() : $this->parameterBag->get('kernel.default_locale');
|
||||||
|
$formatterLong = \IntlDateFormatter::create(
|
||||||
|
$locale,
|
||||||
|
\IntlDateFormatter::LONG,
|
||||||
|
$hasTime ? \IntlDateFormatter::SHORT: \IntlDateFormatter::NONE
|
||||||
|
);
|
||||||
|
$formatterShort = \IntlDateFormatter::create(
|
||||||
|
$locale,
|
||||||
|
\IntlDateFormatter::SHORT,
|
||||||
|
$hasTime ? \IntlDateFormatter::SHORT: \IntlDateFormatter::NONE
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'short' => $formatterShort->format($date),
|
||||||
|
'long' => $formatterLong->format($date)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsNormalization($data, string $format = null, array $context = []): bool
|
||||||
{
|
{
|
||||||
|
if ($format === 'json') {
|
||||||
return $data instanceof \DateTimeInterface;
|
return $data instanceof \DateTimeInterface;
|
||||||
|
} elseif ($format === 'docgen') {
|
||||||
|
return $data instanceof \DateTimeInterface || (
|
||||||
|
$data === null
|
||||||
|
&& \array_key_exists('docgen:expects', $context)
|
||||||
|
&& (
|
||||||
|
$context['docgen:expects'] === \DateTimeInterface::class
|
||||||
|
|| $context['docgen:expects'] === \DateTime::class
|
||||||
|
|| $context['docgen:expects'] === \DateTimeImmutable::class
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function denormalize($data, string $type, string $format = null, array $context = [])
|
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||||
|
@@ -52,6 +52,6 @@ class UserNormalizer implements NormalizerInterface, NormalizerAwareInterface
|
|||||||
|
|
||||||
public function supportsNormalization($data, string $format = null): bool
|
public function supportsNormalization($data, string $format = null): bool
|
||||||
{
|
{
|
||||||
return $data instanceof User;
|
return $format === 'json' && $data instanceof User;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,92 +1,56 @@
|
|||||||
<?php
|
<?php
|
||||||
/*
|
|
||||||
* Chill is a suite of a modules, Chill is a software for social workers
|
declare(strict_types=1);
|
||||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Chill\MainBundle\Templating;
|
namespace Chill\MainBundle\Templating;
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
use Symfony\Component\Translation\Translator;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
/**
|
final class TranslatableStringHelper implements TranslatableStringHelperInterface
|
||||||
*
|
|
||||||
* This helper helps to find the string in current locale from translatable_strings
|
|
||||||
*
|
|
||||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class TranslatableStringHelper
|
|
||||||
{
|
{
|
||||||
/**
|
private RequestStack $requestStack;
|
||||||
*
|
|
||||||
* @var RequestStack
|
|
||||||
*/
|
|
||||||
private $requestStack;
|
|
||||||
|
|
||||||
private $fallbackLocales;
|
private TranslatorInterface $translator;
|
||||||
|
|
||||||
public function __construct(RequestStack $requestStack, Translator $translator)
|
public function __construct(RequestStack $requestStack, TranslatorInterface $translator)
|
||||||
{
|
{
|
||||||
$this->requestStack = $requestStack;
|
$this->requestStack = $requestStack;
|
||||||
$this->fallbackLocales = $translator->getFallbackLocales();
|
$this->translator = $translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function localize(array $translatableStrings): ?string
|
||||||
* return the string in current locale if it exists.
|
|
||||||
*
|
|
||||||
* If it does not exists; return the name in the first language available.
|
|
||||||
*
|
|
||||||
* Return a blank string if any strings are available.
|
|
||||||
* Return NULL if $translatableString is NULL
|
|
||||||
*
|
|
||||||
* @param array $translatableStrings
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function localize(array $translatableStrings)
|
|
||||||
{
|
{
|
||||||
if (NULL === $translatableStrings) {
|
if ([] === $translatableStrings) {
|
||||||
return NULL;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$language = $this->requestStack->getCurrentRequest()->getLocale();
|
$request = $this->requestStack->getCurrentRequest();
|
||||||
|
|
||||||
|
if (null === $request) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($translatableStrings[$language])) {
|
$language = $request->getLocale();
|
||||||
|
|
||||||
|
if (array_key_exists($language, $translatableStrings)) {
|
||||||
return $translatableStrings[$language];
|
return $translatableStrings[$language];
|
||||||
} else {
|
}
|
||||||
foreach ($this->fallbackLocales as $locale) {
|
|
||||||
if (array_key_exists($locale, $translatableStrings)) {
|
|
||||||
|
|
||||||
|
foreach ($this->translator->getFallbackLocales() as $locale) {
|
||||||
|
if (array_key_exists($locale, $translatableStrings)) {
|
||||||
return $translatableStrings[$locale];
|
return $translatableStrings[$locale];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// no fallback translation... trying the first available
|
// no fallback translation... trying the first available
|
||||||
$langs = array_keys($translatableStrings);
|
$langs = array_keys($translatableStrings);
|
||||||
|
|
||||||
if (count($langs) === 0) {
|
if ([] === $langs) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $translatableStrings[$langs[0]];
|
return $translatableStrings[$langs[0]];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Templating;
|
||||||
|
|
||||||
|
interface TranslatableStringHelperInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return the string in current locale if it exists.
|
||||||
|
*
|
||||||
|
* If it does not exists; return the name in the first language available.
|
||||||
|
*
|
||||||
|
* Return a blank string if any strings are available.
|
||||||
|
*/
|
||||||
|
public function localize(array $translatableStrings): ?string;
|
||||||
|
}
|
@@ -12,7 +12,7 @@ class SearchApiQueryTest extends TestCase
|
|||||||
$q = new SearchApiQuery();
|
$q = new SearchApiQuery();
|
||||||
$q->setSelectJsonbMetadata('boum')
|
$q->setSelectJsonbMetadata('boum')
|
||||||
->setSelectKey('bim')
|
->setSelectKey('bim')
|
||||||
->setSelectPertinence('1')
|
->setSelectPertinence('?', ['gamma'])
|
||||||
->setFromClause('badaboum')
|
->setFromClause('badaboum')
|
||||||
->andWhereClause('foo', [ 'alpha' ])
|
->andWhereClause('foo', [ 'alpha' ])
|
||||||
->andWhereClause('bar', [ 'beta' ])
|
->andWhereClause('bar', [ 'beta' ])
|
||||||
@@ -21,12 +21,12 @@ class SearchApiQueryTest extends TestCase
|
|||||||
$query = $q->buildQuery();
|
$query = $q->buildQuery();
|
||||||
|
|
||||||
$this->assertStringContainsString('(foo) AND (bar)', $query);
|
$this->assertStringContainsString('(foo) AND (bar)', $query);
|
||||||
$this->assertEquals(['alpha', 'beta'], $q->buildParameters());
|
$this->assertEquals(['gamma', 'alpha', 'beta'], $q->buildParameters());
|
||||||
|
|
||||||
$query = $q->buildQuery(true);
|
$query = $q->buildQuery(true);
|
||||||
|
|
||||||
$this->assertStringContainsString('(foo) AND (bar)', $query);
|
$this->assertStringContainsString('(foo) AND (bar)', $query);
|
||||||
$this->assertEquals(['alpha', 'beta'], $q->buildParameters());
|
$this->assertEquals(['gamma', 'alpha', 'beta'], $q->buildParameters());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWithoutWhereClause()
|
public function testWithoutWhereClause()
|
||||||
@@ -42,4 +42,20 @@ class SearchApiQueryTest extends TestCase
|
|||||||
$this->assertEquals([], $q->buildParameters());
|
$this->assertEquals([], $q->buildParameters());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testBuildParams()
|
||||||
|
{
|
||||||
|
$q = new SearchApiQuery();
|
||||||
|
|
||||||
|
$q
|
||||||
|
->addSelectClause('bada', [ 'one', 'two' ])
|
||||||
|
->addSelectClause('boum', ['three', 'four'])
|
||||||
|
->setWhereClauses('mywhere', [ 'six', 'seven'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$params = $q->buildParameters();
|
||||||
|
|
||||||
|
$this->assertEquals(['six', 'seven'], $q->buildParameters(true));
|
||||||
|
$this->assertEquals(['one', 'two', 'three', 'four', 'six', 'seven'], $q->buildParameters());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -57,7 +57,7 @@ class SearchProviderTest extends TestCase
|
|||||||
|
|
||||||
public function testSimplePattern()
|
public function testSimplePattern()
|
||||||
{
|
{
|
||||||
$terms = $this->p("@person birthdate:2014-01-02 name:(my name) is not my name");
|
$terms = $this->p("@person birthdate:2014-01-02 name:\"my name\" is not my name");
|
||||||
|
|
||||||
$this->assertEquals(array(
|
$this->assertEquals(array(
|
||||||
'_domain' => 'person',
|
'_domain' => 'person',
|
||||||
@@ -110,8 +110,7 @@ class SearchProviderTest extends TestCase
|
|||||||
|
|
||||||
public function testDoubleParenthesis()
|
public function testDoubleParenthesis()
|
||||||
{
|
{
|
||||||
$terms = $this->p("@papamobile name:(my beautiful name) residual "
|
$terms = $this->p('@papamobile name:"my beautiful name" residual surname:"i love techno"');
|
||||||
. "surname:(i love techno)");
|
|
||||||
|
|
||||||
$this->assertEquals(array(
|
$this->assertEquals(array(
|
||||||
'_domain' => 'papamobile',
|
'_domain' => 'papamobile',
|
||||||
@@ -147,7 +146,7 @@ class SearchProviderTest extends TestCase
|
|||||||
|
|
||||||
public function testTrimInParenthesis()
|
public function testTrimInParenthesis()
|
||||||
{
|
{
|
||||||
$terms = $this->p('foo:(bar )');
|
$terms = $this->p('foo:"bar "');
|
||||||
|
|
||||||
$this->assertEquals(array(
|
$this->assertEquals(array(
|
||||||
'_domain' => null,
|
'_domain' => null,
|
||||||
|
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Search\Utils;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Search\Utils\ExtractDateFromPattern;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ExtractDateFromPatternTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideSubjects
|
||||||
|
*/
|
||||||
|
public function testExtractDate(string $subject, string $filtered, int $count, ...$datesSearched)
|
||||||
|
{
|
||||||
|
$extractor = new ExtractDateFromPattern();
|
||||||
|
$result = $extractor->extractDates($subject);
|
||||||
|
|
||||||
|
$this->assertCount($count, $result->getFound());
|
||||||
|
$this->assertEquals($filtered, $result->getFilteredSubject());
|
||||||
|
$this->assertContainsOnlyInstancesOf(\DateTimeImmutable::class, $result->getFound());
|
||||||
|
|
||||||
|
$dates = \array_map(
|
||||||
|
function (\DateTimeImmutable $d) {
|
||||||
|
return $d->format('Y-m-d');
|
||||||
|
}, $result->getFound()
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($datesSearched as $date) {
|
||||||
|
$this->assertContains($date, $dates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideSubjects()
|
||||||
|
{
|
||||||
|
yield ["15/06/1981", "", 1, '1981-06-15'];
|
||||||
|
yield ["15/06/1981 30/12/1987", "", 2, '1981-06-15', '1987-12-30'];
|
||||||
|
yield ["diallo 15/06/1981", "diallo", 1, '1981-06-15'];
|
||||||
|
yield ["diallo 31/03/1981", "diallo", 1, '1981-03-31'];
|
||||||
|
yield ["diallo 15-06-1981", "diallo", 1, '1981-06-15'];
|
||||||
|
yield ["diallo 1981-12-08", "diallo", 1, '1981-12-08'];
|
||||||
|
yield ["diallo", "diallo", 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Search\Utils;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Search\Utils\ExtractPhonenumberFromPattern;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
class ExtractPhonenumberFromPatternTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider provideData
|
||||||
|
*/
|
||||||
|
public function testExtract($subject, $expectedCount, $expected, $filteredSubject, $msg)
|
||||||
|
{
|
||||||
|
$extractor = new ExtractPhonenumberFromPattern();
|
||||||
|
$result = $extractor->extractPhonenumber($subject);
|
||||||
|
|
||||||
|
$this->assertCount($expectedCount, $result->getFound());
|
||||||
|
$this->assertEquals($filteredSubject, $result->getFilteredSubject());
|
||||||
|
$this->assertEquals($expected, $result->getFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideData()
|
||||||
|
{
|
||||||
|
yield ['Diallo', 0, [], 'Diallo', "no phonenumber"];
|
||||||
|
yield ['Diallo 15/06/2021', 0, [], 'Diallo 15/06/2021', "no phonenumber and a date"];
|
||||||
|
yield ['Diallo 0486 123 456', 1, ['+32486123456'], 'Diallo', "a phonenumber and a name"];
|
||||||
|
yield ['Diallo 123 456', 1, ['123456'], 'Diallo', "a number and a name, without leadiing 0"];
|
||||||
|
yield ['123 456', 1, ['123456'], '', "only phonenumber"];
|
||||||
|
yield ['0123 456', 1, ['+32123456'], '', "only phonenumber with a leading 0"];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Serializer\Normalizer\DateNormalizer;
|
||||||
|
use Prophecy\Prophet;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
|
class DateNormalizerTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
private Prophet $prophet;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->prophet = new Prophet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSupports()
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTime(), 'json'));
|
||||||
|
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTimeImmutable(), 'json'));
|
||||||
|
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTime(), 'docgen'));
|
||||||
|
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(new \DateTimeImmutable(), 'docgen'));
|
||||||
|
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(null, 'docgen', ['docgen:expects' => \DateTimeImmutable::class]));
|
||||||
|
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(null, 'docgen', ['docgen:expects' => \DateTimeInterface::class]));
|
||||||
|
$this->assertTrue($this->buildDateNormalizer()->supportsNormalization(null, 'docgen', ['docgen:expects' => \DateTime::class]));
|
||||||
|
$this->assertFalse($this->buildDateNormalizer()->supportsNormalization(new \stdClass(), 'docgen'));
|
||||||
|
$this->assertFalse($this->buildDateNormalizer()->supportsNormalization(new \DateTime(), 'xml'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider generateDataNormalize
|
||||||
|
*/
|
||||||
|
public function testNormalize($expected, $date, $format, $locale, $msg)
|
||||||
|
{
|
||||||
|
$this->assertEquals($expected, $this->buildDateNormalizer($locale)->normalize($date, $format, []), $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildDateNormalizer(string $locale = null): DateNormalizer
|
||||||
|
{
|
||||||
|
$requestStack = $this->prophet->prophesize(RequestStack::class);
|
||||||
|
$parameterBag = new ParameterBag();
|
||||||
|
$parameterBag->set('kernel.default_locale', 'fr');
|
||||||
|
|
||||||
|
if ($locale === null) {
|
||||||
|
$requestStack->getCurrentRequest()->willReturn(null);
|
||||||
|
} else {
|
||||||
|
$request = $this->prophet->prophesize(Request::class);
|
||||||
|
$request->getLocale()->willReturn($locale);
|
||||||
|
$requestStack->getCurrentRequest()->willReturn($request->reveal());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DateNormalizer($requestStack->reveal(), $parameterBag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateDataNormalize()
|
||||||
|
{
|
||||||
|
$datetime = \DateTime::createFromFormat('Y-m-d H:i:sO', '2021-06-05 15:05:01+02:00');
|
||||||
|
$date = \DateTime::createFromFormat('Y-m-d H:i:sO', '2021-06-05 00:00:00+02:00');
|
||||||
|
yield [
|
||||||
|
['datetime' => '2021-06-05T15:05:01+0200'],
|
||||||
|
$datetime, 'json', null, 'simple normalization to json'
|
||||||
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
['long' => '5 juin 2021', 'short' => '05/06/2021'],
|
||||||
|
$date, 'docgen', 'fr', 'normalization to docgen for a date, with current request'
|
||||||
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
['long' => '5 juin 2021', 'short' => '05/06/2021'],
|
||||||
|
$date, 'docgen', null, 'normalization to docgen for a date, without current request'
|
||||||
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
['long' => '5 juin 2021 à 15:05', 'short' => '05/06/2021 15:05'],
|
||||||
|
$datetime, 'docgen', null, 'normalization to docgen for a datetime, without current request'
|
||||||
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
['long' => '', 'short' => ''],
|
||||||
|
null, 'docgen', null, 'normalization to docgen for a null datetime'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -8,38 +8,29 @@ services:
|
|||||||
|
|
||||||
Chill\MainBundle\Repository\:
|
Chill\MainBundle\Repository\:
|
||||||
resource: '../Repository/'
|
resource: '../Repository/'
|
||||||
autowire: true
|
|
||||||
autoconfigure: true
|
|
||||||
|
|
||||||
Chill\MainBundle\Repository\UserACLAwareRepositoryInterface: '@Chill\MainBundle\Repository\UserACLAwareRepository'
|
Chill\MainBundle\Repository\UserACLAwareRepositoryInterface: '@Chill\MainBundle\Repository\UserACLAwareRepository'
|
||||||
|
|
||||||
Chill\MainBundle\Serializer\Normalizer\:
|
Chill\MainBundle\Serializer\Normalizer\:
|
||||||
resource: '../Serializer/Normalizer'
|
resource: '../Serializer/Normalizer'
|
||||||
autoconfigure: true
|
|
||||||
autowire: true
|
|
||||||
tags:
|
tags:
|
||||||
- { name: 'serializer.normalizer', priority: 64 }
|
- { name: 'serializer.normalizer', priority: 64 }
|
||||||
|
|
||||||
Chill\MainBundle\Form\Type\:
|
Chill\MainBundle\Form\Type\:
|
||||||
resource: '../Form/Type'
|
resource: '../Form/Type'
|
||||||
autoconfigure: true
|
|
||||||
autowire: true
|
|
||||||
tags:
|
tags:
|
||||||
- { name: form.type }
|
- { name: form.type }
|
||||||
|
|
||||||
Chill\MainBundle\Doctrine\Event\:
|
Chill\MainBundle\Doctrine\Event\:
|
||||||
resource: '../Doctrine/Event/'
|
resource: '../Doctrine/Event/'
|
||||||
autowire: true
|
|
||||||
tags:
|
tags:
|
||||||
- { name: 'doctrine.event_subscriber' }
|
- { name: 'doctrine.event_subscriber' }
|
||||||
|
|
||||||
chill.main.helper.translatable_string:
|
chill.main.helper.translatable_string:
|
||||||
class: Chill\MainBundle\Templating\TranslatableStringHelper
|
class: Chill\MainBundle\Templating\TranslatableStringHelper
|
||||||
arguments:
|
|
||||||
- "@request_stack"
|
|
||||||
- "@translator.default"
|
|
||||||
|
|
||||||
Chill\MainBundle\Templating\TranslatableStringHelper: '@chill.main.helper.translatable_string'
|
Chill\MainBundle\Templating\TranslatableStringHelper: '@chill.main.helper.translatable_string'
|
||||||
|
Chill\MainBundle\Templating\TranslatableStringHelperInterface: '@Chill\MainBundle\Templating\TranslatableStringHelper'
|
||||||
|
|
||||||
chill.main.twig.translatable_string:
|
chill.main.twig.translatable_string:
|
||||||
class: Chill\MainBundle\Templating\TranslatableStringTwig
|
class: Chill\MainBundle\Templating\TranslatableStringTwig
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
services:
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
chill_main.tag_aware_cache:
|
chill_main.tag_aware_cache:
|
||||||
class: Symfony\Component\Cache\Adapter\TagAwareAdapter
|
class: Symfony\Component\Cache\Adapter\TagAwareAdapter
|
||||||
arguments:
|
arguments:
|
||||||
|
@@ -1,10 +1,9 @@
|
|||||||
services:
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
Chill\MainBundle\Command\ChillImportUsersCommand:
|
Chill\MainBundle\Command\ChillImportUsersCommand:
|
||||||
arguments:
|
|
||||||
$em: '@Doctrine\ORM\EntityManagerInterface'
|
|
||||||
$logger: '@Psr\Log\LoggerInterface'
|
|
||||||
$passwordEncoder: '@Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface'
|
|
||||||
$validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
|
|
||||||
tags:
|
tags:
|
||||||
- { name: console.command }
|
- { name: console.command }
|
||||||
|
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
services:
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
Chill\MainBundle\Controller\:
|
Chill\MainBundle\Controller\:
|
||||||
autowire: true
|
|
||||||
resource: '../../Controller'
|
resource: '../../Controller'
|
||||||
tags: ['controller.service_arguments']
|
tags: ['controller.service_arguments']
|
||||||
|
|
||||||
Chill\MainBundle\Controller\PasswordController:
|
Chill\MainBundle\Controller\PasswordController:
|
||||||
autowire: true
|
|
||||||
arguments:
|
arguments:
|
||||||
$chillLogger: '@monolog.logger.chill'
|
$chillLogger: '@monolog.logger.chill'
|
||||||
tags: ['controller.service_arguments']
|
tags: ['controller.service_arguments']
|
||||||
@@ -28,10 +29,6 @@ services:
|
|||||||
$validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
|
$validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
|
||||||
tags: ['controller.service_arguments']
|
tags: ['controller.service_arguments']
|
||||||
|
|
||||||
Chill\MainBundle\Controller\UserController:
|
|
||||||
autowire: true
|
|
||||||
autoconfigure: true
|
|
||||||
|
|
||||||
Chill\MainBundle\Controller\NotificationController:
|
Chill\MainBundle\Controller\NotificationController:
|
||||||
arguments:
|
arguments:
|
||||||
$security: '@Symfony\Component\Security\Core\Security'
|
$security: '@Symfony\Component\Security\Core\Security'
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
services:
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader:
|
Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader:
|
||||||
arguments:
|
arguments:
|
||||||
$crudConfig: '%chill_main_crud_route_loader_config%'
|
$crudConfig: '%chill_main_crud_route_loader_config%'
|
||||||
|
@@ -1,3 +1,7 @@
|
|||||||
---
|
---
|
||||||
services:
|
services:
|
||||||
'Chill\MainBundle\Doctrine\Migrations\VersionComparator': ~
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
|
Chill\MainBundle\Doctrine\Migrations\VersionComparator: ~
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user