From 7574b5bfac3c637ce1912ca1dde5a2db99b36220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 9 Nov 2021 17:04:32 +0100 Subject: [PATCH 01/65] doc for authorization --- .../development/access_control_model.rst | 788 ++++++++++++++++-- 1 file changed, 704 insertions(+), 84 deletions(-) diff --git a/docs/source/development/access_control_model.rst b/docs/source/development/access_control_model.rst index 58fe44319..e159f7fb6 100644 --- a/docs/source/development/access_control_model.rst +++ b/docs/source/development/access_control_model.rst @@ -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. +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 %} + + + {{ 'Center'|trans|upper}} : + + {% 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 %} + + Scopes : + {% for s in scopes %} + {{ c.name|localize_translatable_string }} + {% if not loop.last %}, {% endif %} + {% endfor %} + {%- endif -%} + +Build a ``Voter`` +----------------- + +.. code-block:: php + + 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 --------------------------- +========================== 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. 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. @@ -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. 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, ...). @@ -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". + +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 ------------------------------------ +=================================== .. 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. 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 : @@ -100,34 +311,23 @@ Just use the symfony way-of-doing, but do not forget to associate the entity you And in template : -.. code-block:: html+jinja +.. code-block:: twig {{ 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: - getReachableCenters: to get reachable centers 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 `_ 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. @@ -152,7 +352,7 @@ To create your own roles, you should: Declare your role ------------------- +^^^^^^^^^^^^^^^^^^ 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 ---------------------- +^^^^^^^^^^^^^^^^^^^^^ 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 + + 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 %} + + + {{ 'Center'|trans|upper}} : + + {% 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 %} + + Scopes : + {% 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 + + 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: + + + + From e7fcebc99ebd68c0a88f346bf27fff5f1ec798e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 9 Nov 2021 17:04:49 +0100 Subject: [PATCH 02/65] add some method for resolving scope in twig --- .../DataFixtures/ORM/LoadActivity.php | 2 ++ .../Authorization/PersonDocumentVoter.php | 25 ------------- .../Resolver/ResolverTwigExtension.php | 30 +++++++++++++--- .../Helper/RandomPersonHelperTrait.php | 35 +++++++++++++++++++ .../Resources/views/Entity/person.html.twig | 10 +++++- .../Controller/SingleTaskController.php | 12 ------- 6 files changed, 71 insertions(+), 43 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/DataFixtures/Helper/RandomPersonHelperTrait.php diff --git a/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivity.php b/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivity.php index a23b2d73f..15db14b75 100644 --- a/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivity.php +++ b/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivity.php @@ -22,6 +22,7 @@ namespace Chill\ActivityBundle\DataFixtures\ORM; +use Chill\PersonBundle\DataFixtures\Helper\RandomPersonHelperTrait; use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Persistence\ObjectManager; @@ -41,6 +42,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; class LoadActivity extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface { use \Symfony\Component\DependencyInjection\ContainerAwareTrait; + use RandomPersonHelperTrait; /** * @var \Faker\Generator diff --git a/src/Bundle/ChillDocStoreBundle/Security/Authorization/PersonDocumentVoter.php b/src/Bundle/ChillDocStoreBundle/Security/Authorization/PersonDocumentVoter.php index 3eb21ed19..73254278b 100644 --- a/src/Bundle/ChillDocStoreBundle/Security/Authorization/PersonDocumentVoter.php +++ b/src/Bundle/ChillDocStoreBundle/Security/Authorization/PersonDocumentVoter.php @@ -1,44 +1,19 @@ . - */ - namespace Chill\DocStoreBundle\Security\Authorization; -use App\Security\Authorization\VoterHelperFactory; use Chill\MainBundle\Security\Authorization\AbstractChillVoter; -use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface; use Chill\MainBundle\Security\Authorization\VoterHelperInterface; use Chill\MainBundle\Security\ProvideRoleHierarchyInterface; use Chill\DocStoreBundle\Entity\PersonDocument; -use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; use Chill\PersonBundle\Entity\Person; use Chill\MainBundle\Entity\User; 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\Role\Role; use Psr\Log\LoggerInterface; use Symfony\Component\Security\Core\Security; -/** - * - */ class PersonDocumentVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface { const CREATE = 'CHILL_PERSON_DOCUMENT_CREATE'; diff --git a/src/Bundle/ChillMainBundle/Security/Resolver/ResolverTwigExtension.php b/src/Bundle/ChillMainBundle/Security/Resolver/ResolverTwigExtension.php index 21ece21a3..c8fdea5ff 100644 --- a/src/Bundle/ChillMainBundle/Security/Resolver/ResolverTwigExtension.php +++ b/src/Bundle/ChillMainBundle/Security/Resolver/ResolverTwigExtension.php @@ -7,19 +7,20 @@ use Twig\TwigFilter; final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension { private CenterResolverDispatcher $centerResolverDispatcher; + private ScopeResolverInterface $scopeResolverDispatcher; - /** - * @param CenterResolverDispatcher $centerResolverDispatcher - */ - public function __construct(CenterResolverDispatcher $centerResolverDispatcher) + public function __construct(CenterResolverDispatcher $centerResolverDispatcher, ScopeResolverInterface $scopeResolverDispatcher) { $this->centerResolverDispatcher = $centerResolverDispatcher; + $this->scopeResolverDispatcher = $scopeResolverDispatcher; } public function getFilters() { 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']), ]; } @@ -33,4 +34,23 @@ final class ResolverTwigExtension extends \Twig\Extension\AbstractExtension return $this->centerResolverDispatcher->resolveCenter($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(); + } } diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/Helper/RandomPersonHelperTrait.php b/src/Bundle/ChillPersonBundle/DataFixtures/Helper/RandomPersonHelperTrait.php new file mode 100644 index 000000000..c6cef7aa0 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/DataFixtures/Helper/RandomPersonHelperTrait.php @@ -0,0 +1,35 @@ +createQueryBuilder(); + $qb + ->from(Person::class, 'p') + ; + + if (null === $this->nbOfPersons) { + $this->nbOfPersons = $qb + ->select('COUNT(p)') + ->getQuery() + ->getSingleScalarResult() + ; + } + + return $qb + ->select('p') + ->setMaxResults(1) + ->setFirstResult(\random_int(0, $this->nbOfPersons)) + ->getQuery() + ->getSingleResult() + ; + } +} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig index d2ed09135..1bf08602a 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Entity/person.html.twig @@ -149,9 +149,17 @@ {% endif %} {% if options['addCenter'] and 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 %}
  • - {{ person|chill_resolve_center.name }} + {% for c in centers %} + {{ c.name|upper }} + {% if not loop.last %}, {% endif %} + {% endfor %}
  • {% endif %} diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index d8944ae93..d10f18c4c 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -2,9 +2,7 @@ namespace Chill\TaskBundle\Controller; -use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; -use Chill\MainBundle\Security\Resolver\CenterResolverInterface; use Chill\MainBundle\Templating\Listing\FilterOrderHelper; use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface; use Chill\PersonBundle\Privacy\PrivacyEvent; @@ -18,26 +16,16 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Chill\TaskBundle\Entity\SingleTask; use Chill\TaskBundle\Form\SingleTaskType; -use Chill\TaskBundle\Form\SingleTaskListType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormFactoryInterface; use Chill\TaskBundle\Security\Authorization\TaskVoter; use Symfony\Component\Security\Core\Role\Role; use Chill\MainBundle\Pagination\PaginatorFactory; -use Chill\TaskBundle\Repository\SingleTaskRepository; -use Chill\MainBundle\Entity\User; -use Chill\PersonBundle\Security\Authorization\PersonVoter; -use Chill\PersonBundle\Repository\PersonRepository; use Chill\TaskBundle\Event\TaskEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Chill\TaskBundle\Event\UI\UIEvent; -use Chill\MainBundle\Repository\CenterRepository; use Chill\MainBundle\Timeline\TimelineBuilder; use Chill\PersonBundle\Entity\AccompanyingPeriod; -use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; -use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; -use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface as TranslationTranslatorInterface; From 6cfcf91757f4f5fcc4c6725ff300c51cb058238d Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 16 Nov 2021 16:28:20 +0100 Subject: [PATCH 03/65] fix: Follow up f8aeb085946f7992afc551bbdf22d04b944cd08f and fix parameters type. --- .../Controller/ActivityController.php | 96 +++++++++---------- .../ChillActivityBundle/Entity/Activity.php | 23 +---- 2 files changed, 50 insertions(+), 69 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 9b1f3f48a..543aa1f6b 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -4,15 +4,20 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Controller; +use Chill\ActivityBundle\Entity\ActivityReason; +use Chill\ActivityBundle\Entity\ActivityTypeCategory; use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface; use Chill\ActivityBundle\Security\Authorization\ActivityVoter; +use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Privacy\PrivacyEvent; +use Chill\ThirdPartyBundle\Entity\ThirdParty; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\SubmitType; @@ -65,10 +70,10 @@ final class ActivityController extends AbstractController $activities = $this->activityACLAwareRepository ->findByPerson($person, ActivityVoter::SEE, 0, null); - $event = new PrivacyEvent($person, array( + $event = new PrivacyEvent($person, [ 'element_class' => Activity::class, 'action' => 'list' - )); + ]); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); $view = 'ChillActivityBundle:Activity:listPerson.html.twig'; @@ -106,11 +111,11 @@ final class ActivityController extends AbstractController $data = []; - $activityTypeCategories = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityTypeCategory::class) + $activityTypeCategories = $em->getRepository(ActivityTypeCategory::class) ->findBy(['active' => true], ['ordering' => 'ASC']); foreach ($activityTypeCategories as $activityTypeCategory) { - $activityTypes = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class) + $activityTypes = $em->getRepository(ActivityType::class) ->findBy(['active' => true, 'category' => $activityTypeCategory], ['ordering' => 'ASC']); $data[] = [ @@ -119,12 +124,6 @@ final class ActivityController extends AbstractController ]; } - if ($request->query->has('activityData')) { - $activityData = $request->query->get('activityData'); - } else { - $activityData = []; - } - if ($view === null) { throw $this->createNotFoundException('Template not found'); } @@ -133,7 +132,7 @@ final class ActivityController extends AbstractController 'person' => $person, 'accompanyingCourse' => $accompanyingPeriod, 'data' => $data, - 'activityData' => $activityData + 'activityData' => $request->query->get('activityData', []), ]); } @@ -151,7 +150,7 @@ final class ActivityController extends AbstractController } $activityType_id = $request->get('activityType_id', 0); - $activityType = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityType::class) + $activityType = $em->getRepository(ActivityType::class) ->find($activityType_id); if (isset($activityType) && !$activityType->isActive()) { @@ -210,20 +209,20 @@ final class ActivityController extends AbstractController if (array_key_exists('personsId', $activityData)) { foreach($activityData['personsId'] as $personId){ - $concernedPerson = $em->getRepository(\Chill\PersonBundle\Entity\Person::class)->find($personId); + $concernedPerson = $em->getRepository(Person::class)->find($personId); $entity->addPerson($concernedPerson); } } if (array_key_exists('professionalsId', $activityData)) { foreach($activityData['professionalsId'] as $professionalsId){ - $professional = $em->getRepository(\Chill\ThirdPartyBundle\Entity\ThirdParty::class)->find($professionalsId); + $professional = $em->getRepository(ThirdParty::class)->find($professionalsId); $entity->addThirdParty($professional); } } if (array_key_exists('location', $activityData)) { - $location = $em->getRepository(\Chill\MainBundle\Entity\Location::class)->find($activityData['location']); + $location = $em->getRepository(Location::class)->find($activityData['location']); $entity->setLocation($location); } @@ -288,13 +287,15 @@ final class ActivityController extends AbstractController $view = 'ChillActivityBundle:Activity:showPerson.html.twig'; } - $entity = $em->getRepository('ChillActivityBundle:Activity')->find($id); + /** @var Activity $entity */ + $entity = $em->getRepository(Activity::class)->find($id); - if (!$entity) { + if (null === $entity) { throw $this->createNotFoundException('Unable to find Activity entity.'); } if (null !== $accompanyingPeriod) { + // @TODO: Properties created dynamically. $entity->personsAssociated = $entity->getPersonsAssociated(); $entity->personsNotAssociated = $entity->getPersonsNotAssociated(); } @@ -302,7 +303,7 @@ final class ActivityController extends AbstractController // TODO revoir le Voter de Activity pour tenir compte qu'une activité peut appartenir a une période // $this->denyAccessUnlessGranted('CHILL_ACTIVITY_SEE', $entity); - $deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod); + $deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod); // TODO /* @@ -318,17 +319,16 @@ final class ActivityController extends AbstractController throw $this->createNotFoundException('Template not found'); } - return $this->render($view, array( + return $this->render($view, [ 'person' => $person, 'accompanyingCourse' => $accompanyingPeriod, 'entity' => $entity, 'delete_form' => $deleteForm->createView(), - )); + ]); } /** * Displays a form to edit an existing Activity entity. - * */ public function editAction($id, Request $request): Response { @@ -343,9 +343,10 @@ final class ActivityController extends AbstractController $view = 'ChillActivityBundle:Activity:editPerson.html.twig'; } - $entity = $em->getRepository('ChillActivityBundle:Activity')->find($id); + /** @var Activity $entity */ + $entity = $em->getRepository(Activity::class)->find($id); - if (!$entity) { + if (null === $entity) { throw $this->createNotFoundException('Unable to find Activity entity.'); } @@ -366,11 +367,12 @@ final class ActivityController extends AbstractController $this->addFlash('success', $this->get('translator')->trans('Success : activity updated!')); $params = $this->buildParamsToUrl($person, $accompanyingPeriod); - $params['id'] = $id; + $params['id'] = $entity->getId(); + return $this->redirectToRoute('chill_activity_activity_show', $params); } - $deleteForm = $this->createDeleteForm($id, $person, $accompanyingPeriod); + $deleteForm = $this->createDeleteForm($entity->getId(), $person, $accompanyingPeriod); /* * TODO @@ -388,19 +390,18 @@ final class ActivityController extends AbstractController $activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']); - return $this->render($view, array( + return $this->render($view, [ 'entity' => $entity, 'edit_form' => $form->createView(), 'delete_form' => $deleteForm->createView(), 'person' => $person, 'accompanyingCourse' => $accompanyingPeriod, 'activity_json' => $activity_array - )); + ]); } /** * Deletes a Activity entity. - * */ public function deleteAction(Request $request, $id) { @@ -415,8 +416,8 @@ final class ActivityController extends AbstractController $view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig'; } - /* @var $activity Activity */ - $activity = $em->getRepository('ChillActivityBundle:Activity')->find($id); + /* @var Activity $activity */ + $activity = $em->getRepository(Activity::class)->find($id); if (!$activity) { throw $this->createNotFoundException('Unable to find Activity entity.'); @@ -425,27 +426,28 @@ final class ActivityController extends AbstractController // TODO // $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) { $form->handleRequest($request); if ($form->isValid()) { - - $this->logger->notice("An activity has been removed", array( + $this->logger->notice("An activity has been removed", [ 'by_user' => $this->getUser()->getUsername(), 'activity_id' => $activity->getId(), 'person_id' => $activity->getPerson() ? $activity->getPerson()->getId() : null, 'comment' => $activity->getComment()->getComment(), 'scope_id' => $activity->getScope() ? $activity->getScope()->getId() : null, 'reasons_ids' => $activity->getReasons() - ->map(function ($ar) { return $ar->getId(); }) + ->map( + static fn (ActivityReason $ar): int => $ar->getId() + ) ->toArray(), 'type_id' => $activity->getType()->getId(), 'duration' => $activity->getDurationTime() ? $activity->getDurationTime()->format('U') : null, 'date' => $activity->getDate()->format('Y-m-d'), 'attendee' => $activity->getAttendee() - )); + ]); $em->remove($activity); $em->flush(); @@ -454,6 +456,7 @@ final class ActivityController extends AbstractController ->trans("The activity has been successfully removed.")); $params = $this->buildParamsToUrl($person, $accompanyingPeriod); + return $this->redirectToRoute('chill_activity_activity_list', $params); } } @@ -462,18 +465,18 @@ final class ActivityController extends AbstractController throw $this->createNotFoundException('Template not found'); } - return $this->render($view, array( + return $this->render($view, [ 'activity' => $activity, 'delete_form' => $form->createView(), 'person' => $person, 'accompanyingCourse' => $accompanyingPeriod, - )); + ]); } /** * 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['id'] = $id; @@ -481,9 +484,8 @@ final class ActivityController extends AbstractController return $this->createFormBuilder() ->setAction($this->generateUrl('chill_activity_activity_delete', $params)) ->setMethod('DELETE') - ->add('submit', SubmitType::class, array('label' => 'Delete')) - ->getForm() - ; + ->add('submit', SubmitType::class, ['label' => 'Delete']) + ->getForm(); } private function getEntity(Request $request): array @@ -515,21 +517,19 @@ final class ActivityController extends AbstractController } return [ - $person, $accompanyingPeriod + $person, + $accompanyingPeriod ]; } - private function buildParamsToUrl( - ?Person $person, - ?AccompanyingPeriod $accompanyingPeriod - ): array { + private function buildParamsToUrl(?Person $person, ?AccompanyingPeriod $accompanyingPeriod): array { $params = []; - if ($person) { + if (null !== $person) { $params['person_id'] = $person->getId(); } - if ($accompanyingPeriod) { + if (null !== $accompanyingPeriod) { $params['accompanying_period_id'] = $accompanyingPeriod->getId(); } diff --git a/src/Bundle/ChillActivityBundle/Entity/Activity.php b/src/Bundle/ChillActivityBundle/Entity/Activity.php index e25448f10..4ed654c85 100644 --- a/src/Bundle/ChillActivityBundle/Entity/Activity.php +++ b/src/Bundle/ChillActivityBundle/Entity/Activity.php @@ -1,27 +1,8 @@ - * - * 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 . - */ - namespace Chill\ActivityBundle\Entity; use Chill\DocStoreBundle\Entity\Document; -use Chill\DocStoreBundle\Entity\StoredObject; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Entity\Location; use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface; @@ -38,7 +19,7 @@ use Chill\MainBundle\Entity\HasCenterInterface; use Chill\MainBundle\Entity\HasScopeInterface; use Doctrine\Common\Collections\Collection; 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\DiscriminatorMap; @@ -202,7 +183,7 @@ class Activity implements HasCenterInterface, HasScopeInterface, AccompanyingPer return $this->id; } - public function setUser(User $user): self + public function setUser(UserInterface $user): self { $this->user = $user; From db2010082a7bddba324d9660ba9002bae3e6deeb Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 16 Nov 2021 16:37:45 +0100 Subject: [PATCH 04/65] fix: SA: Fix "...Access to an undefined property..." rule. SA stands for Static Analysis. --- phpstan-critical.neon | 5 --- .../JsonCustomFieldToArrayTransformer.php | 45 ++++++++----------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/phpstan-critical.neon b/phpstan-critical.neon index cb0ac38ce..31bd2f99f 100644 --- a/phpstan-critical.neon +++ b/phpstan-critical.neon @@ -195,11 +195,6 @@ parameters: 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\\(\\)\\.$#" count: 1 diff --git a/src/Bundle/ChillCustomFieldsBundle/Form/DataTransformer/JsonCustomFieldToArrayTransformer.php b/src/Bundle/ChillCustomFieldsBundle/Form/DataTransformer/JsonCustomFieldToArrayTransformer.php index 6b54df801..2d04c036d 100644 --- a/src/Bundle/ChillCustomFieldsBundle/Form/DataTransformer/JsonCustomFieldToArrayTransformer.php +++ b/src/Bundle/ChillCustomFieldsBundle/Form/DataTransformer/JsonCustomFieldToArrayTransformer.php @@ -1,28 +1,27 @@ om = $om; $customFields = $this->om - ->getRepository('ChillCustomFieldsBundle:CustomField') + ->getRepository(CustomField::class) ->findAll(); - + + // @TODO: in the array_map callback, CustomField::getLabel() does not exist. What do we do here? $customFieldsLablels = array_map( function($e) { return $e->getLabel(); }, $customFields); @@ -36,20 +35,12 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface { { echo $customFieldsJSON; - if($customFieldsJSON === null) { // lors de la creation - $customFieldsArray = array(); + if($customFieldsJSON === null) { + $customFieldsArray = []; } else { - $customFieldsArray = json_decode($customFieldsJSON,true); + $customFieldsArray = json_decode($customFieldsJSON, true, 512, JSON_THROW_ON_ERROR); } - /* - echo "
    - 4 -
    "; - - var_dump($customFieldsArray); - - echo "
    - 5 -
    "; - */ - $customFieldsArrayRet = array(); foreach ($customFieldsArray as $key => $value) { @@ -62,7 +53,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface { } else { $entityClass = substr($type, 10, -1); } - + $customFieldsArrayRet[$key] = $this->om ->getRepository('ChillCustomFieldsBundle:' . $entityClass) ->findOneById($value); @@ -86,10 +77,10 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface { { /* echo "
    - - 7 -
    "; - + var_dump(array_keys($customFieldsArray)); - + echo "
    - - 8 -
    "; var_dump(array_keys($this->customField)); @@ -112,7 +103,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface { //$entityClass = substr($type, 10, -1); //echo $entityClasss; if(strpos($type, 'ManyToOnePersist') === 0) { - // PEUT ETRE A FAIRE SI SEULEMENT $value->getId() ne renvoie rien... + // PEUT ETRE A FAIRE SI SEULEMENT $value->getId() ne renvoie rien... // // $this->om->persist($value); // pas bon ici @@ -121,7 +112,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface { // et faire le persist qd fait sur l'obj parent // regarder : http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html // ou : http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html - // dans yml : + // dans yml : // lifecycleCallbacks: // prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ] $this->om->flush(); // sinon l'id pose pbm @@ -142,4 +133,4 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface { return json_encode($customFieldsArrayRet); } -} \ No newline at end of file +} From 5432242376f17b894247b97d3ec39cf5ec1e8bf4 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 16 Nov 2021 17:13:39 +0100 Subject: [PATCH 05/65] fix: SA: Fix many critical rules. SA stands for Static Analysis. --- phpstan-critical.neon | 70 ----- .../src/Entity/AsideActivityCategory.php | 4 +- .../src/Form/AsideActivityCategoryType.php | 11 +- .../src/Form/AsideActivityFormType.php | 4 +- .../DataFixtures/ORM/LoadCalendarRange.php | 8 +- .../Controller/FamilyMemberController.php | 70 ++--- .../Controller/AbstractCRUDController.php | 117 ++++---- .../Command/ChillImportUsersCommand.php | 271 +++++++----------- .../Controller/AdminCountryCRUDController.php | 14 +- .../Controller/UserController.php | 21 +- .../ChillMainBundle/Entity/RoleScope.php | 52 +--- .../Pagination/PageGenerator.php | 45 +-- .../ChillMainBundle/Routing/MenuComposer.php | 91 +++--- .../Search/SearchApiResult.php | 6 +- .../Authorization/AbstractChillVoter.php | 25 +- .../Controller/TimelinePersonController.php | 57 ++-- .../DataFixtures/ORM/LoadHousehold.php | 12 +- .../Form/CreationPersonType.php | 35 +-- .../Timeline/TimelineReportProvider.php | 87 +++--- 19 files changed, 345 insertions(+), 655 deletions(-) diff --git a/phpstan-critical.neon b/phpstan-critical.neon index 31bd2f99f..2e2f778d8 100644 --- a/phpstan-critical.neon +++ b/phpstan-critical.neon @@ -90,51 +90,21 @@ parameters: count: 1 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\\(\\)\\.$#" count: 1 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\\(\\)\\.$#" count: 1 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$#" count: 1 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\\(\\)\\.$#" count: 1 @@ -155,46 +125,6 @@ parameters: count: 3 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: "#^Call to an undefined method Chill\\\\ThirdPartyBundle\\\\Form\\\\Type\\\\PickThirdPartyTypeCategoryType\\:\\:transform\\(\\)\\.$#" count: 1 diff --git a/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivityCategory.php b/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivityCategory.php index 09087226c..143e430f7 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivityCategory.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivityCategory.php @@ -44,13 +44,15 @@ class AsideActivityCategory * @ORM\ManyToOne(targetEntity=AsideActivityCategory::class, inversedBy="children") * @ORM\JoinColumn(nullable=true) */ - private $parent; + private AsideActivityCategory $parent; /** * @ORM\OneToMany(targetEntity=AsideActivityCategory::class, mappedBy="parent") */ private $children; + private AsideActivityCategory $oldParent; + public function __construct() { $this->children = new ArrayCollection(); diff --git a/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityCategoryType.php b/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityCategoryType.php index 0060ab1b9..e438998b0 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityCategoryType.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityCategoryType.php @@ -1,5 +1,7 @@ translatableStringHelper = $translatableStringHelper; + public function __construct( + CategoryRender $categoryRender + ) { $this->categoryRender = $categoryRender; } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php b/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php index 53ce5bd40..f517279bb 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php @@ -25,18 +25,16 @@ use Symfony\Component\Templating\EngineInterface; final class AsideActivityFormType extends AbstractType { - protected array $timeChoices; + private array $timeChoices; private TokenStorageInterface $storage; private CategoryRender $categoryRender; public function __construct ( - TranslatableStringHelper $translatableStringHelper, ParameterBagInterface $parameterBag, TokenStorageInterface $storage, CategoryRender $categoryRender ){ $this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration'); - $this->translatableStringHelper = $translatableStringHelper; $this->storage = $storage; $this->categoryRender = $categoryRender; } diff --git a/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadCalendarRange.php b/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadCalendarRange.php index c23871c26..42218da03 100644 --- a/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadCalendarRange.php +++ b/src/Bundle/ChillCalendarBundle/DataFixtures/ORM/LoadCalendarRange.php @@ -1,5 +1,7 @@ chillMainLogger = $chillMainLogger; } - + /** * @Route( * "{_locale}/family-members/family-members/by-person/{id}", @@ -54,11 +42,11 @@ class FamilyMemberController extends Controller public function indexAction(Person $person) { $this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $person); - + $familyMembers = $this->em ->getRepository(FamilyMember::class) ->findByPerson($person); - + return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:index.html.twig', array( 'person' => $person, 'familyMembers' => $familyMembers @@ -76,26 +64,26 @@ class FamilyMemberController extends Controller $familyMember = (new FamilyMember()) ->setPerson($person) ; - + $this->denyAccessUnlessGranted(FamilyMemberVoter::CREATE, $familyMember); - + $form = $this->createForm(FamilyMemberType::class, $familyMember); $form->add('submit', SubmitType::class); - + $form->handleRequest($request); - + if ($form->isSubmitted() and $form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($familyMember); $em->flush(); - + $this->addFlash('success', $this->translator->trans('Family member created')); - + return $this->redirectToRoute('chill_family_members_family_members_index', [ 'id' => $person->getId() ]); } - + return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:new.html.twig', array( 'form' => $form->createView(), 'person' => $person @@ -111,37 +99,37 @@ class FamilyMemberController extends Controller public function editAction(FamilyMember $familyMember, Request $request) { $this->denyAccessUnlessGranted(FamilyMemberVoter::UPDATE, $familyMember); - + $form = $this->createForm(FamilyMemberType::class, $familyMember); $form->add('submit', SubmitType::class); - + $form->handleRequest($request); - + if ($form->isSubmitted() and $form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->flush(); - + $this->addFlash('success', $this->translator->trans('Family member updated')); - + return $this->redirectToRoute('chill_family_members_family_members_index', [ 'id' => $familyMember->getPerson()->getId() ]); } - + return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:edit.html.twig', array( 'familyMember' => $familyMember, 'form' => $form->createView(), 'person' => $familyMember->getPerson() )); } - + /** - * + * * @Route( * "{_locale}/family-members/family-members/{id}/delete", * name="chill_family_members_family_members_delete" * ) - * + * * @param FamilyMember $familyMember * @param Request $request * @return \Symfony\Component\BrowserKit\Response @@ -183,7 +171,7 @@ class FamilyMemberController extends Controller 'delete_form' => $form->createView() )); } - + /** * @Route( * "{_locale}/family-members/family-members/{id}/view", @@ -193,12 +181,12 @@ class FamilyMemberController extends Controller public function viewAction(FamilyMember $familyMember) { $this->denyAccessUnlessGranted(FamilyMemberVoter::SHOW, $familyMember); - + return $this->render('ChillAMLIFamilyMembersBundle:FamilyMember:view.html.twig', array( 'familyMember' => $familyMember )); } - + /** * Creates a form to delete a help request entity by id. * diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php index a1d28483d..451ce8aab 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php @@ -1,5 +1,7 @@ getDoctrine() + $e = $this + ->getDoctrine() ->getRepository($this->getEntityClass()) ->find($id); - if (NULL === $e) { + if (null === $e) { throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id)); } @@ -47,61 +46,50 @@ class AbstractCRUDController extends AbstractController /** * Create an entity. - * - * @param string $action - * @param Request $request + * * @return object */ protected function createEntity(string $action, Request $request): object { - $type = $this->getEntityClass(); - - return new $type; + return $this->getEntityClass(); } /** * Count the number of entities * - * 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`. - * - * @param string $action - * @param Request $request - * @return int */ protected function countEntities(string $action, Request $request, $_format): int { return $this->buildQueryEntities($action, $request) ->select('COUNT(e)') ->getQuery() - ->getSingleScalarResult() - ; + ->getSingleScalarResult(); } /** * Query the entity. - * + * * By default, get all entities. You can customize the query by using the * method `customizeQuery`. - * + * * The method `orderEntity` is called internally to order entities. - * + * * It returns, by default, a query builder. - * */ protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator) { $query = $this->buildQueryEntities($action, $request) ->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber()) ->setMaxResults($paginator->getItemsPerPage()); - + // allow to order queries and return the new query return $this->orderQuery($action, $query, $request, $paginator, $_format); } /** * Add ordering fields in the query build by self::queryEntities - * */ protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format) { @@ -112,14 +100,12 @@ class AbstractCRUDController extends AbstractController * Build the base query for listing all entities. * * This method is used internally by `countEntities` `queryEntities` - * + * * This base query does not contains any `WHERE` or `SELECT` clauses. You * can add some by using the method `customizeQuery`. * * The alias for the entity is "e". - * - * @param string $action - * @param Request $request + * * @return QueryBuilder */ protected function buildQueryEntities(string $action, Request $request) @@ -127,8 +113,7 @@ class AbstractCRUDController extends AbstractController $qb = $this->getDoctrine()->getManager() ->createQueryBuilder() ->select('e') - ->from($this->getEntityClass(), 'e') - ; + ->from($this->getEntityClass(), 'e'); $this->customizeQuery($action, $request, $qb); @@ -138,55 +123,55 @@ class AbstractCRUDController extends AbstractController 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) { return $query->getQuery()->getResult(); } protected function onPreIndex(string $action, Request $request, string $_format): ?Response - { - return null; - } - - /** - * method used by indexAction - */ - protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response - { + { return null; } /** - * method used by indexAction + * Method used by indexAction. + */ + protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response + { + return null; + } + + /** + * Method used by indexAction. */ protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response { return null; } - + /** - * method used by indexAction + * Method used by indexAction. */ protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response { return null; } - + /** - * Get the complete FQDN of the class - * - * @return string the complete fqdn of the class + * Get the FQDN of the class. + * + * @return string The FQDN of the class */ protected function getEntityClass(): string { return $this->crudConfig['class']; } - + /** - * called on post fetch entity + * Called on post fetch entity. */ protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response { @@ -194,7 +179,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 { @@ -203,23 +188,23 @@ class AbstractCRUDController extends AbstractController /** * check the acl. Called by every action. - * - * By default, check the role given by `getRoleFor` for the value given in + * + * By default, check the role given by `getRoleFor` for the value given in * $entity. - * + * * Throw an \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException * if not accessible. - * + * * @throws \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException */ 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); } /** - * - * @return string the crud name + * @return string The crud name. */ protected function getCrudName(): string { @@ -230,7 +215,7 @@ class AbstractCRUDController extends AbstractController { return $this->crudConfig['actions'][$action]; } - + /** * Set the crud configuration * @@ -241,9 +226,6 @@ class AbstractCRUDController extends AbstractController $this->crudConfig = $config; } - /** - * @return PaginatorFactory - */ protected function getPaginatorFactory(): PaginatorFactory { return $this->container->get('chill_main.paginator_factory'); @@ -254,9 +236,6 @@ class AbstractCRUDController extends AbstractController return $this->get('validator'); } - /** - * @return array - */ public static function getSubscribedServices(): array { return \array_merge( diff --git a/src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php b/src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php index 99bd17d8f..d3fb6980f 100644 --- a/src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php +++ b/src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php @@ -1,7 +1,10 @@ passwordEncoder = $passwordEncoder; $this->validator = $validator; $this->logger = $logger; - - + $this->userRepository = $em->getRepository(User::class); - + parent::__construct('chill:main:import-users'); } - - protected function configure() { $this @@ -126,25 +79,24 @@ class ChillImportUsersCommand extends Command ->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('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) { $this->tempOutput = $output; $this->tempInput = $input; - + if ($input->getOption('dry-run')) { $this->doChanges = false; } - + $this->prepareWriter(); - + if ($input->hasOption('grouping-centers')) { $this->prepareGroupingCenters(); } - + try { $this->loadUsers(); } @@ -152,19 +104,19 @@ class ChillImportUsersCommand extends Command throw $e; } } - + protected function prepareWriter() { $this->output = $output = Writer::createFromPath($this->tempInput ->getOption('csv-dump'), 'a+'); - + $output->insertOne([ 'email', 'username', 'id' ]); } - + protected function appendUserToFile(User $user) { $this->output->insertOne( [ @@ -173,35 +125,35 @@ class ChillImportUsersCommand extends Command $user->getId() ]); } - + protected function loadUsers() { $reader = Reader::createFromPath($this->tempInput->getArgument('csvfile')); $reader->setHeaderOffset(0); - + foreach ($reader->getRecords() as $line => $r) { $this->logger->debug("starting handling new line", [ 'line' => $line ]); - + if ($this->doesUserExists($r)) { $this->tempOutput->writeln(sprintf("User with username '%s' already " . "exists, skipping", $r["username"])); - + $this->logger->info("One user already exists, skipping creation", [ 'username_in_file' => $r['username'], 'email_in_file' => $r['email'], 'line' => $line ]); - + continue; } - + $user = $this->createUser($line, $r); $this->appendUserToFile($user); } } - + protected function doesUserExists($data) { if ($this->userRepository->countByUsernameOrEmail($data['username']) > 0) { @@ -211,10 +163,10 @@ class ChillImportUsersCommand extends Command if ($this->userRepository->countByUsernameOrEmail($data['email']) > 0) { return true; } - + return false; } - + protected function createUser($offset, $data) { $user = new User(); @@ -222,41 +174,41 @@ class ChillImportUsersCommand extends Command ->setEmail(\trim($data['email'])) ->setUsername(\trim($data['username'])) ->setEnabled(true) - ->setPassword($this->passwordEncoder->encodePassword($user, + ->setPassword($this->passwordEncoder->encodePassword($user, \bin2hex(\random_bytes(32)))) ; - + $errors = $this->validator->validate($user); - + if ($errors->count() > 0) { $errorMessages = $this->concatenateViolations($errors); - + $this->tempOutput->writeln(sprintf("%d errors found with user with username \"%s\" at line %d", $errors->count(), $data['username'], $offset)); $this->tempOutput->writeln($errorMessages); throw new \RuntimeException("Found errors while creating an user. " . "Watch messages in command output"); } - + $pgs = $this->getPermissionGroup($data['permission group']); $centers = $this->getCenters($data['center']); - + foreach($pgs as $pg) { foreach ($centers as $center) { $groupcenter = $this->createOrGetGroupCenter($center, $pg); - + if (FALSE === $user->getGroupCenters()->contains($groupcenter)) { $user->addGroupCenter($groupcenter); } } } - + if ($this->doChanges) { $this->em->persist($user); $this->em->flush(); } - + $this->logger->notice("Create user", [ 'username' => $user->getUsername(), 'id' => $user->getId(), @@ -265,65 +217,58 @@ class ChillImportUsersCommand extends Command return $user; } - + protected function getPermissionGroup($alias) { if (\array_key_exists($alias, $this->permissionGroups)) { return $this->permissionGroups[$alias]; } - + $permissionGroupsByName = []; - + foreach($this->em->getRepository(PermissionsGroup::class) ->findAll() as $permissionGroup) { $permissionGroupsByName[$permissionGroup->getName()] = $permissionGroup; } - + if (count($permissionGroupsByName) === 0) { throw new \RuntimeException("no permission groups found. Create them " . "before importing users"); } - - $question = new ChoiceQuestion("To which permission groups associate with \"$alias\" ?", + + $question = new ChoiceQuestion("To which permission groups associate with \"$alias\" ?", \array_keys($permissionGroupsByName)); $question ->setMultiselect(true) ->setAutocompleterValues(\array_keys($permissionGroupsByName)) ->setNormalizer(function($value) { if (NULL === $value) { return ''; } - + return \trim($value); }) ; $helper = $this->getHelper('question'); - + $keys = $helper->ask($this->tempInput, $this->tempOutput, $question); - + $this->tempOutput->writeln("You have chosen ".\implode(", ", $keys)); - - if ($helper->ask($this->tempInput, $this->tempOutput, + + if ($helper->ask($this->tempInput, $this->tempOutput, new ConfirmationQuestion("Are you sure ?", true))) { - + foreach ($keys as $key) { $this->permissionGroups[$alias][] = $permissionGroupsByName[$key]; } - + 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.'); + + throw new \RuntimeException('Error while responding to a question'); } - - /** - * - * @param Center $center - * @param \Chill\MainBundle\Command\PermissionGroup $pg - * @return GroupCenter - */ + protected function createOrGetGroupCenter(Center $center, PermissionsGroup $pg): GroupCenter { if (\array_key_exists($center->getId(), $this->groupCenters)) { @@ -331,36 +276,36 @@ class ChillImportUsersCommand extends Command return $this->groupCenters[$center->getId()][$pg->getId()]; } } - + $repository = $this->em->getRepository(GroupCenter::class); - + $groupCenter = $repository->findOneBy(array( 'center' => $center, 'permissionsGroup' => $pg )); - + if ($groupCenter === NULL) { $groupCenter = new GroupCenter(); $groupCenter ->setCenter($center) ->setPermissionsGroup($pg) ; - + $this->em->persist($groupCenter); } - + $this->groupCenters[$center->getId()][$pg->getId()] = $groupCenter; - + return $groupCenter; } - + protected function prepareGroupingCenters() { $reader = Reader::createFromPath($this->tempInput->getOption('grouping-centers')); $reader->setHeaderOffset(0); - + foreach ($reader->getRecords() as $r) { - $this->centers[$r['alias']] = + $this->centers[$r['alias']] = \array_merge( $this->centers[$r['alias']] ?? [], $this->getCenters($r['center'] @@ -368,18 +313,18 @@ class ChillImportUsersCommand extends Command ); } } - + /** * return a list of centers matching the name of alias. - * + * * If the name match one center, this center is returned in an array. - * - * If the name match an alias, the centers corresponding to the alias are + * + * If the name match an alias, the centers corresponding to the alias are * returned in an array. - * + * * If the center is not found or alias is not created, a new center is created * and suggested to user - * + * * @param string $name the name of the center or the alias regrouping center * @return Center[] */ @@ -387,62 +332,62 @@ class ChillImportUsersCommand extends Command { // sanitize $name = \trim($name); - + if (\array_key_exists($name, $this->centers)) { return $this->centers[$name]; } - + // search for a center with given name $center = $this->em->getRepository(Center::class) ->findOneByName($name); - + if ($center instanceof Center) { $this->centers[$name] = [$center]; - + return $this->centers[$name]; } - + // suggest and create $center = (new Center()) ->setName($name); - + $this->tempOutput->writeln("Center with name \"$name\" not found."); $qFormatter = $this->getHelper('question'); $question = new ConfirmationQuestion("Create a center with name \"$name\" ?", true); - + if ($qFormatter->ask($this->tempInput, $this->tempOutput, $question)) { $this->centers[$name] = [ $center ]; - + $errors = $this->validator->validate($center); - + if ($errors->count() > 0) { $errorMessages = $this->concatenateViolations($errors); - + $this->tempOutput->writeln(sprintf("%d errors found with center with name \"%s\"", $errors->count(), $name)); $this->tempOutput->writeln($errorMessages); - + throw new \RuntimeException("Found errors while creating one center. " . "Watch messages in command output"); } - + $this->em->persist($center); - + return $this->centers[$name]; } - + return null; } - + protected function concatenateViolations(ConstraintViolationListInterface $list) { $str = []; - + foreach ($list as $e) { /* @var $e \Symfony\Component\Validator\ConstraintViolationInterface */ $str[] = $e->getMessage(); } - + return \implode(";", $str); } - + } diff --git a/src/Bundle/ChillMainBundle/Controller/AdminCountryCRUDController.php b/src/Bundle/ChillMainBundle/Controller/AdminCountryCRUDController.php index 310a36c60..3be418698 100644 --- a/src/Bundle/ChillMainBundle/Controller/AdminCountryCRUDController.php +++ b/src/Bundle/ChillMainBundle/Controller/AdminCountryCRUDController.php @@ -1,20 +1,12 @@ paginatorFactory = $paginator; - } + } diff --git a/src/Bundle/ChillMainBundle/Controller/UserController.php b/src/Bundle/ChillMainBundle/Controller/UserController.php index bb8203011..bd2ad89d7 100644 --- a/src/Bundle/ChillMainBundle/Controller/UserController.php +++ b/src/Bundle/ChillMainBundle/Controller/UserController.php @@ -136,20 +136,17 @@ class UserController extends CRUDController ]); } - /** - * - * - * @param User $user - * @return \Symfony\Component\Form\Form - */ - private function createEditPasswordForm(User $user) + private function createEditPasswordForm(User $user): FormInterface { - return $this->createForm(UserPasswordType::class, null, array( - 'user' => $user - )) + return $this->createForm( + UserPasswordType::class, + null, + [ + 'user' => $user + ] + ) ->add('submit', SubmitType::class, array('label' => 'Change password')) - ->remove('actual_password') - ; + ->remove('actual_password'); } /** diff --git a/src/Bundle/ChillMainBundle/Entity/RoleScope.php b/src/Bundle/ChillMainBundle/Entity/RoleScope.php index 7842267a9..315e8f594 100644 --- a/src/Bundle/ChillMainBundle/Entity/RoleScope.php +++ b/src/Bundle/ChillMainBundle/Entity/RoleScope.php @@ -1,23 +1,5 @@ - * - * 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 . - */ - namespace Chill\MainBundle\Entity; use Doctrine\ORM\Mapping as ORM; @@ -28,38 +10,30 @@ use Doctrine\Common\Collections\ArrayCollection; * @ORM\Entity * @ORM\Table(name="role_scopes") * @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region") - * - * @author Julien Fastré */ class RoleScope { /** - * @var integer - * * @ORM\Id * @ORM\Column(name="id", type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ - private $id; - + private int $id; + /** - * @var string - * * @ORM\Column(type="string", length=255) */ - private $role; - + private string $role; + /** - * @var Scope - * * @ORM\ManyToOne( * targetEntity="Chill\MainBundle\Entity\Scope", * inversedBy="roleScopes") * @ORM\JoinColumn(nullable=true, name="scope_id") * @ORM\Cache(usage="NONSTRICT_READ_WRITE") */ - private $scope; - + private Scope $scope; + /** * @var Collection * @@ -68,16 +42,14 @@ class RoleScope * mappedBy="roleScopes") */ private $permissionsGroups; - - - /** - * RoleScope constructor. - */ + + private bool $new; + public function __construct() { $this->new = true; $this->permissionsGroups = new ArrayCollection(); } - + /** * @return int */ @@ -101,7 +73,7 @@ class RoleScope { return $this->scope; } - + /** * @param type $role * @return RoleScope @@ -120,7 +92,7 @@ class RoleScope public function setScope(Scope $scope = null) { $this->scope = $scope; - + return $this; } } diff --git a/src/Bundle/ChillMainBundle/Pagination/PageGenerator.php b/src/Bundle/ChillMainBundle/Pagination/PageGenerator.php index 26add87fa..93c481a2e 100644 --- a/src/Bundle/ChillMainBundle/Pagination/PageGenerator.php +++ b/src/Bundle/ChillMainBundle/Pagination/PageGenerator.php @@ -1,50 +1,23 @@ - * - * 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 . - */ +declare(strict_types=1); namespace Chill\MainBundle\Pagination; /** - * PageGenerator associated with a Paginator - * - * @author Julien Fastré - * @author Champs Libres + * PageGenerator associated with a Paginator. */ class PageGenerator implements \Iterator { - /** - * - * @var Paginator - */ - protected $paginator; - - /** - * - * @var int - */ - protected $current = 1; - - public function __construct(Paginator $paginator) + protected Paginator $paginator; + + protected int $current = 1; + + public function __construct(Paginator $paginator) { $this->paginator = $paginator;; } - + public function current() { return $this->paginator->getPage($current); @@ -67,7 +40,7 @@ class PageGenerator implements \Iterator public function valid() { - return $this->current > 0 + return $this->current > 0 && $this->current <= $this->paginator->countPages(); } } diff --git a/src/Bundle/ChillMainBundle/Routing/MenuComposer.php b/src/Bundle/ChillMainBundle/Routing/MenuComposer.php index 0bcee4c81..b1883c61d 100644 --- a/src/Bundle/ChillMainBundle/Routing/MenuComposer.php +++ b/src/Bundle/ChillMainBundle/Routing/MenuComposer.php @@ -1,5 +1,7 @@ all() as $routeKey => $route) { if ($route->hasOption('menus')) { - + if (array_key_exists($menuId, $route->getOption('menus'))) { $route = $route->getOption('menus')[$menuId]; @@ -101,12 +88,12 @@ class MenuComposer return $routes; } - + public function getMenuFor($menuId, array $parameters = array()) { $routes = $this->getRoutesFor($menuId, $parameters); $menu = $this->menuFactory->createItem($menuId); - + // build menu from routes foreach ($routes as $order => $route) { $menu->addChild($this->translator->trans($route['label']), [ @@ -121,24 +108,24 @@ class MenuComposer ]) ; } - + if ($this->hasLocalMenuBuilder($menuId)) { foreach ($this->localMenuBuilders[$menuId] as $builder) { /* @var $builder LocalMenuBuilderInterface */ $builder->buildMenu($menuId, $menu, $parameters['args']); } } - + $this->reorderMenu($menu); - + return $menu; } - + /** * recursive function to resolve the order of a array of routes. - * If the order chosen in routing.yml is already in used, find the + * If the order chosen in routing.yml is already in used, find the * first next order available. - * + * * @param array $routes the routes previously added * @param int $order * @return int @@ -151,41 +138,41 @@ class MenuComposer return $order; } } - - private function reorderMenu(ItemInterface $menu) + + private function reorderMenu(ItemInterface $menu) { $ordered = []; $unordered = []; - + foreach ($menu->getChildren() as $name => $item) { $order = $item->getExtra('order'); - + if ($order !== null) { $ordered[$this->resolveOrder($ordered, $order)] = $name; } else { $unordered = $name; } } - + ksort($ordered); - + $menus = \array_merge(\array_values($ordered), $unordered); $menu->reorderChildren($menus); } - - - public function addLocalMenuBuilder(LocalMenuBuilderInterface $menuBuilder, $menuId) + + + public function addLocalMenuBuilder(LocalMenuBuilderInterface $menuBuilder, $menuId) { $this->localMenuBuilders[$menuId][] = $menuBuilder; } - + /** * Return true if the menu has at least one builder. - * + * * This function is a helper to determine if the method `getMenuFor` * should be used, or `getRouteFor`. The method `getMenuFor` should be used * if the result is true (it **does** exists at least one menu builder. - * + * * @param string $menuId * @return bool */ diff --git a/src/Bundle/ChillMainBundle/Search/SearchApiResult.php b/src/Bundle/ChillMainBundle/Search/SearchApiResult.php index bbc66877f..c44db3339 100644 --- a/src/Bundle/ChillMainBundle/Search/SearchApiResult.php +++ b/src/Bundle/ChillMainBundle/Search/SearchApiResult.php @@ -1,5 +1,7 @@ relevance = $relevance; @@ -20,7 +24,7 @@ class SearchApiResult $this->result = $result; return $this; - } + } /** * @Serializer\Groups({"read"}) diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php b/src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php index 9131a6501..fec4da627 100644 --- a/src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php +++ b/src/Bundle/ChillMainBundle/Security/Authorization/AbstractChillVoter.php @@ -1,34 +1,17 @@ - * - * 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 . - */ +declare(strict_types=1); namespace Chill\MainBundle\Security\Authorization; use Symfony\Component\Security\Core\Authorization\Voter\Voter; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\User\UserInterface; /** * Voter for Chill software. * * This abstract Voter provide generic methods to handle object specific to Chill - * - * - * @author Julien Fastré */ abstract class AbstractChillVoter extends Voter implements ChillVoterInterface { @@ -39,6 +22,8 @@ abstract class AbstractChillVoter extends Voter implements ChillVoterInterface . 'getSupportedAttributes and getSupportedClasses methods.', 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)) && \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 ' . '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()); } - } diff --git a/src/Bundle/ChillPersonBundle/Controller/TimelinePersonController.php b/src/Bundle/ChillPersonBundle/Controller/TimelinePersonController.php index 4e3d67755..389c723ce 100644 --- a/src/Bundle/ChillPersonBundle/Controller/TimelinePersonController.php +++ b/src/Bundle/ChillPersonBundle/Controller/TimelinePersonController.php @@ -1,87 +1,62 @@ - * - * 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 . - */ +declare(strict_types=1); namespace Chill\PersonBundle\Controller; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; +use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Privacy\PrivacyEvent; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; use Chill\MainBundle\Timeline\TimelineBuilder; use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\PersonBundle\Security\Authorization\PersonVoter; -use Chill\MainBundle\Security\Authorization\AuthorizationHelper; -use Symfony\Component\Security\Core\Role\Role; class TimelinePersonController extends AbstractController { - protected EventDispatcherInterface $eventDispatcher; - + protected TimelineBuilder $timelineBuilder; - + protected PaginatorFactory $paginatorFactory; - - /** - * TimelinePersonController constructor. - * - * @param EventDispatcherInterface $eventDispatcher - */ + public function __construct( EventDispatcherInterface $eventDispatcher, TimelineBuilder $timelineBuilder, - PaginatorFactory $paginatorFactory, - AuthorizationHelper $authorizationHelper + PaginatorFactory $paginatorFactory ) { $this->eventDispatcher = $eventDispatcher; $this->timelineBuilder = $timelineBuilder; $this->paginatorFactory = $paginatorFactory; - $this->authorizationHelper = $authorizationHelper; } - - + public function personAction(Request $request, $person_id) { $person = $this->getDoctrine() - ->getRepository('ChillPersonBundle:Person') + ->getRepository(Person::class) ->find($person_id); if ($person === NULL) { throw $this->createNotFoundException(); } - + $this->denyAccessUnlessGranted(PersonVoter::SEE, $person); - - $nbItems = $this->timelineBuilder->countItems('person', + + $nbItems = $this->timelineBuilder->countItems('person', [ 'person' => $person ] ); - + $paginator = $this->paginatorFactory->create($nbItems); - + $event = new PrivacyEvent($person, array('action' => 'timeline')); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); - + return $this->render('ChillPersonBundle:Timeline:index.html.twig', array ( 'timeline' => $this->timelineBuilder->getTimelineHTML( - 'person', + 'person', array('person' => $person), $paginator->getCurrentPage()->getFirstItemNumber(), $paginator->getItemsPerPage() diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php index 14420ed88..50bf94e58 100644 --- a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php +++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadHousehold.php @@ -1,5 +1,7 @@ editorFactory = $editorFactory; @@ -149,14 +153,14 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface private function preparePersonIds() { + // @TODO: Remove this and make this service stateless $this->personIds = $this->em ->createQuery('SELECT p.id FROM '.Person::class.' p '. 'JOIN p.center c '. 'WHERE c.name = :center ' ) ->setParameter('center', 'Center A') - ->getScalarResult() - ; + ->getScalarResult(); \shuffle($this->personIds); } @@ -169,9 +173,7 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface for ($i=0; $i < $nb; $i++) { $personId = \array_pop($this->personIds)['id']; - $persons[] = $this->em->getRepository(Person::class) - ->find($personId) - ; + $persons[] = $this->em->getRepository(Person::class)->find($personId); } return $persons; diff --git a/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php b/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php index 3fa8f5cda..704a6e47b 100644 --- a/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php +++ b/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php @@ -1,28 +1,10 @@ - * - * 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 . - */ +declare(strict_types=1); namespace Chill\PersonBundle\Form; use Chill\MainBundle\Form\Event\CustomizeFormEvent; -use Chill\MainBundle\Repository\CenterRepository; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Security\Authorization\PersonVoter; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; @@ -30,12 +12,9 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; -use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\PickCenterType; use Chill\PersonBundle\Form\Type\GenderType; -use Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer; use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper; use Chill\PersonBundle\Form\Type\PersonAltNameType; @@ -43,27 +22,19 @@ final class CreationPersonType extends AbstractType { // TODO: This is only used in test. // TODO: See if this is still valid and update accordingly. - const NAME = 'chill_personbundle_person_creation'; + public const NAME = 'chill_personbundle_person_creation'; - private CenterRepository $centerRepository; - - /** - * - * @var ConfigPersonAltNamesHelper - */ - protected $configPersonAltNamesHelper; + private ConfigPersonAltNamesHelper $configPersonAltNamesHelper; private EventDispatcherInterface $dispatcher; private bool $askCenters; public function __construct( - CenterRepository $centerRepository, ConfigPersonAltNamesHelper $configPersonAltNamesHelper, EventDispatcherInterface $dispatcher, ParameterBagInterface $parameterBag ) { - $this->centerTransformer = $centerRepository; $this->configPersonAltNamesHelper = $configPersonAltNamesHelper; $this->dispatcher = $dispatcher; $this->askCenters = $parameterBag->get('chill_main')['acl']['form_show_centers']; diff --git a/src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php b/src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php index 49e237d87..a0e77bbaa 100644 --- a/src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php +++ b/src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php @@ -1,22 +1,6 @@ - * - * 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 . - */ +declare(strict_types=1); namespace Chill\ReportBundle\Timeline; @@ -38,38 +22,39 @@ use Chill\MainBundle\Timeline\TimelineSingleQuery; */ class TimelineReportProvider implements TimelineProviderInterface { - + protected EntityManager $em; - + protected AuthorizationHelper $helper; - + protected CustomFieldsHelper $customFieldsHelper; - - protected $showEmptyValues; - + + protected bool $showEmptyValues; + + private Security $security; + public function __construct( EntityManager $em, AuthorizationHelper $helper, Security $security, CustomFieldsHelper $customFieldsHelper, $showEmptyValues - ) - { + ) { $this->em = $em; $this->helper = $helper; $this->security = $security; $this->customFieldsHelper = $customFieldsHelper; $this->showEmptyValues = $showEmptyValues; } - + /** - * + * * {@inheritDoc} */ public function fetchQuery($context, array $args) { $this->checkContext($context); - + $report = $this->em->getClassMetadata(Report::class); [$where, $parameters] = $this->getWhereClause($context, $args); @@ -84,7 +69,7 @@ class TimelineReportProvider implements TimelineProviderInterface 'parameters' => $parameters ]); } - + private function getWhereClause(string $context, array $args): array { switch ($context) { @@ -102,7 +87,7 @@ class TimelineReportProvider implements TimelineProviderInterface $report = $this->em->getClassMetadata(Report::class); $person = $this->em->getClassMetadata(Person::class); $role = new Role('CHILL_REPORT_SEE'); - $reachableCenters = $this->helper->getReachableCenters($this->security->getUser(), + $reachableCenters = $this->helper->getReachableCenters($this->security->getUser(), $role); $reportPersonId = $report->getAssociationMapping('person')['joinColumns'][0]['name']; $reportScopeId = $report->getAssociationMapping('scope')['joinColumns'][0]['name']; @@ -123,13 +108,13 @@ class TimelineReportProvider implements TimelineProviderInterface } // add the center id to the parameters - $parameters[] = $center->getId(); + $parameters[] = $center->getId(); // loop over scopes $scopeIds = []; - foreach ($this->helper->getReachableScopes($this->security->getUser(), + foreach ($this->helper->getReachableScopes($this->security->getUser(), $role, $center) as $scope) { if (\in_array($scope->getId(), $scopeIds)) { - continue; + continue; } $scopeIds[] = $scope->getId(); } @@ -173,7 +158,7 @@ class TimelineReportProvider implements TimelineProviderInterface // this is the final clause that we are going to fill $clause = "{report}.{person_id} = ? AND {report}.{scopes_id} IN ({scopes_ids})"; // iterate over reachable scopes - $scopes = $this->helper->getReachableScopes($this->security->getUser(), $role, + $scopes = $this->helper->getReachableScopes($this->security->getUser(), $role, $args['person']->getCenter()); foreach ($scopes as $scope) { @@ -194,16 +179,16 @@ class TimelineReportProvider implements TimelineProviderInterface $clause, [ '{report}' => $report->getTableName(), - '{person_id}' => $reportPersonId, + '{person_id}' => $reportPersonId, '{scopes_id}' => $reportScopeId, - '{scopes_ids}' => \implode(', ', + '{scopes_ids}' => \implode(', ', \array_fill(0, \count($parameters)-1, '?')) ] ), $parameters ]; } - + private function getFromClause(string $context): string { $report = $this->em->getClassMetadata(Report::class); @@ -229,30 +214,30 @@ class TimelineReportProvider implements TimelineProviderInterface } /** - * + * * {@inheritDoc} */ public function getEntities(array $ids) { $reports = $this->em->getRepository('ChillReportBundle:Report') ->findBy(array('id' => $ids)); - + $result = array(); foreach($reports as $report) { $result[$report->getId()] = $report; } - + return $result; } /** - * + * * {@inheritDoc} */ public function getEntityTemplate($entity, $context, array $args) { $this->checkContext($context); - + return array( 'template' => 'ChillReportBundle:Timeline:report.html.twig', 'template_data' => array( @@ -262,19 +247,19 @@ class TimelineReportProvider implements TimelineProviderInterface ) ); } - + protected function getFieldsToRender(Report $entity, $context, array $args = array()) { //gather all custom fields which should appears in summary $gatheredFields = array(); - + if (array_key_exists('summary_fields', $entity->getCFGroup()->getOptions())) { // keep in memory title $title = null; $subtitle = null; - + foreach ($entity->getCFGroup()->getCustomFields() as $customField) { - if (in_array($customField->getSlug(), + if (in_array($customField->getSlug(), $entity->getCFGroup()->getOptions()['summary_fields'])) { // if we do not want to show empty values if ($this->showEmptyValues === false) { @@ -304,23 +289,23 @@ class TimelineReportProvider implements TimelineProviderInterface } } } - + return $gatheredFields; - + } /** - * + * * {@inheritDoc} */ public function supportsType($type) { return $type === 'report'; } - + /** * check if the context is supported - * + * * @param string $context * @throws \LogicException if the context is not supported */ From 7f02130ff24f563c3eef05bd4de1746209ed0208 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 16 Nov 2021 20:58:28 +0100 Subject: [PATCH 06/65] fix: Return type of getAge(). Issue highlighted by c68bda5c9b894236860fe1bacca8c9467052eb07. --- src/Bundle/ChillPersonBundle/Entity/Person.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index e17bd5ceb..fc6a7058f 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -839,16 +839,16 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * * If the person has a deathdate, calculate the age at the deathdate. * - * @param string $at a valid string to create a DateTime - * @return int|null + * @param string $at A valid string to create a DateTime. */ - public function getAge($at = 'now'): ?int + public function getAge(string $at = 'now'): ?int { if ($this->birthdate instanceof \DateTimeInterface) { if ($this->deathdate instanceof \DateTimeInterface) { - return date_diff($this->birthdate, $this->deathdate)->format("%y"); + return (int) date_diff($this->birthdate, $this->deathdate)->format('%y'); } - return date_diff($this->birthdate, date_create($at))->format("%y"); + + return (int) date_diff($this->birthdate, date_create($at))->format('%y'); } return null; From 5d74b3ab0ac37ac6f38ebf853248e86bdb38a608 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 17 Nov 2021 10:58:52 +0100 Subject: [PATCH 07/65] fix errors when clearing cache --- .../ChillMainBundle/Command/ChillImportUsersCommand.php | 6 +++--- src/Bundle/ChillMainBundle/config/services/command.yaml | 1 + .../ChillPersonBundle/config/services/controller.yaml | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php b/src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php index d3fb6980f..b9c0116d3 100644 --- a/src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php +++ b/src/Bundle/ChillMainBundle/Command/ChillImportUsersCommand.php @@ -59,14 +59,14 @@ class ChillImportUsersCommand extends Command EntityManagerInterface $em, LoggerInterface $logger, UserPasswordEncoderInterface $passwordEncoder, - ValidatorInterface $validator + ValidatorInterface $validator, + UserRepository $userRepository ) { $this->em = $em; $this->passwordEncoder = $passwordEncoder; $this->validator = $validator; $this->logger = $logger; - - $this->userRepository = $em->getRepository(User::class); + $this->userRepository = $userRepository; parent::__construct('chill:main:import-users'); } diff --git a/src/Bundle/ChillMainBundle/config/services/command.yaml b/src/Bundle/ChillMainBundle/config/services/command.yaml index 5df80c2ad..a005f9944 100644 --- a/src/Bundle/ChillMainBundle/config/services/command.yaml +++ b/src/Bundle/ChillMainBundle/config/services/command.yaml @@ -5,6 +5,7 @@ services: $logger: '@Psr\Log\LoggerInterface' $passwordEncoder: '@Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface' $validator: '@Symfony\Component\Validator\Validator\ValidatorInterface' + $userRepository: '@Chill\MainBundle\Repository\UserRepository' tags: - { name: console.command } diff --git a/src/Bundle/ChillPersonBundle/config/services/controller.yaml b/src/Bundle/ChillPersonBundle/config/services/controller.yaml index b03ccf966..369fe63bb 100644 --- a/src/Bundle/ChillPersonBundle/config/services/controller.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/controller.yaml @@ -8,7 +8,6 @@ services: $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface' $timelineBuilder: '@chill_main.timeline_builder' $paginatorFactory: '@chill_main.paginator_factory' - $authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper' tags: ['controller.service_arguments'] Chill\PersonBundle\Controller\AccompanyingPeriodController: From afa8d7cb728fac408397895d96c050be07995e38 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 17 Nov 2021 11:46:15 +0100 Subject: [PATCH 08/65] fix: Creation of entity in createEntity(). Issue introduced in 5432242376f17b894247b97d3ec39cf5ec1e8bf4. --- .../CRUD/Controller/AbstractCRUDController.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php index 451ce8aab..ae19fb433 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php @@ -44,14 +44,9 @@ abstract class AbstractCRUDController extends AbstractController return $e; } - /** - * Create an entity. - * - * @return object - */ protected function createEntity(string $action, Request $request): object { - return $this->getEntityClass(); + return new ($this->getEntityClass()); } /** @@ -159,11 +154,10 @@ abstract class AbstractCRUDController extends AbstractController return null; } - /** * Get the FQDN of the class. * - * @return string The FQDN of the class + * @return class-string The FQDN of the class */ protected function getEntityClass(): string { From 4f56bb24643345c0a2d7295ff9233bcae3493590 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 17 Nov 2021 11:58:15 +0100 Subject: [PATCH 09/65] fix: Creation of entity in createEntity(). Issue introduced in 5432242376f17b894247b97d3ec39cf5ec1e8bf4. --- .../CRUD/Controller/AbstractCRUDController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php index ae19fb433..f88982070 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php @@ -46,7 +46,9 @@ abstract class AbstractCRUDController extends AbstractController protected function createEntity(string $action, Request $request): object { - return new ($this->getEntityClass()); + $class = $this->getEntityClass(); + + return new $class; } /** From 1a049730728f2b7b03099ab03d039501e591bd9c Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 17 Nov 2021 12:18:34 +0100 Subject: [PATCH 10/65] visgraph: refresh after post/patch/delete request, fix missing key in POST body parameter --- .../Resources/public/vuejs/VisGraph/App.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue index 60db92628..8696041f9 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/App.vue @@ -356,7 +356,7 @@ export default { addRelationshipModal(edgeData) { //console.log('==- addRelationshipModal', edgeData) this.modal = { - data: { from: edgeData.from, to: edgeData.to }, + data: { from: edgeData.from, to: edgeData.to, reverse: false }, action: 'create', showModal: true, title: 'visgraph.add_relationship_link', @@ -414,6 +414,7 @@ export default { this.$store.commit('removeLink', this.modal.data.id) this.modal.showModal = false this.resetForm() + this.forceUpdateComponent() }, submitRelationship() { console.log('submitRelationship', this.modal.action) @@ -426,6 +427,7 @@ export default { this.$store.dispatch('addLinkFromRelationship', relationship) this.modal.showModal = false this.resetForm() + this.forceUpdateComponent() resolve() })) .catch() @@ -437,6 +439,7 @@ export default { this.$store.commit('updateLink', relationship) this.modal.showModal = false this.resetForm() + this.forceUpdateComponent() resolve() })) .catch() From 0e98010d0ebcaa55067f0a21b99951c6326917c3 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 17 Nov 2021 16:05:30 +0100 Subject: [PATCH 11/65] #101 improve translations + hide title --- .../Resources/public/vuejs/AccompanyingCourse/js/i18n.js | 2 +- .../views/AccompanyingCourse/_join_household.html.twig | 2 +- .../views/AccompanyingCourse/_warning_address.html.twig | 2 +- .../Resources/views/AccompanyingCourse/index.html.twig | 5 ++--- .../list_recent_by_accompanying_period.html.twig | 7 ------- .../translations/messages+intl-icu.fr.yaml | 6 +++--- src/Bundle/ChillPersonBundle/translations/messages.fr.yml | 1 + 7 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/js/i18n.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/js/i18n.js index 10b656e4a..f5e38fefc 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/js/i18n.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/js/i18n.js @@ -52,7 +52,7 @@ const appMessages = { show_household_number: "Voir le ménage (n° {id})", show_household: "Voir le ménage", person_without_household_warning: "Certaines usagers n'appartiennent actuellement à aucun ménage. Renseignez leur appartenance dès que possible.", - update_household: "Modifier l'appartenance", + update_household: "Renseigner l'appartenance", participation_not_valid: "Sélectionnez ou créez au minimum 1 usager", }, requestor: { diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_join_household.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_join_household.html.twig index 7ef2de466..fda36da85 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_join_household.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_join_household.html.twig @@ -6,7 +6,7 @@ - Corriger + {{ 'fix it'|trans }} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_warning_address.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_warning_address.html.twig index f04a8a376..2febc7967 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_warning_address.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/_warning_address.html.twig @@ -8,7 +8,7 @@ - Corriger + {{ 'fix it'|trans }} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig index 612c3a46b..1c9814a10 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig @@ -83,7 +83,7 @@ @@ -101,8 +101,7 @@ {% set accompanying_course_id = accompanyingCourse.id %} {% endif %} -

    {{ 'Last activities' |trans }}

    - +

    {{ 'Last activities' |trans }}

    {% include 'ChillActivityBundle:Activity:list_recent.html.twig' with { 'context': 'accompanyingCourse', 'no_action': true } %} {% endblock %} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_recent_by_accompanying_period.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_recent_by_accompanying_period.html.twig index 61f48aa76..979488ff3 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_recent_by_accompanying_period.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_recent_by_accompanying_period.html.twig @@ -1,10 +1,3 @@ - {% if works|length == 0 %} -

    {{ 'accompanying_course_work.Any work'|trans }} - {# TODO link #} -

    - {% endif %} -
    {% for w in works | slice(0,5) %} diff --git a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml index e815153c8..43f83059b 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml +++ b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml @@ -16,7 +16,7 @@ household: Household: Ménage Household number: Ménage {household_num} Household members: Membres du ménage - Household editor: Modifier l'appartenance + Household editor: Renseigner l'appartenance Members at same time: Membres simultanés Any simultaneous members: Aucun membre simultanément Select people to move: Choisir les usagers @@ -51,7 +51,7 @@ household: is holder: Est titulaire is not holder: N'est pas titulaire holder: Titulaire - Edit member household: Modifier l'appartenance au ménage + Edit member household: Renseigner l'appartenance au ménage Edit his household: Modifier son appartenance au ménage Current household members: Membres actuels Household summary: Résumé du ménage @@ -61,7 +61,7 @@ household: Household relationships: Filiations dans le ménage Current address: Adresse actuelle Household does not have any address currently: Le ménage n'a pas d'adresse renseignée actuellement - Edit household members: Modifier l'appartenance au ménage + Edit household members: Renseigner l'appartenance au ménage and x other persons: >- {x, plural, one {et une autre personne} diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 7f9527b91..09ca5bb81 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -390,6 +390,7 @@ This course has a temporarily location: Localisation temporaire Choose a person to locate by: Localiser auprès d'un usager concerné Associate at least one member with an household, and set an address to this household: Associez au moins un membre du parcours à un ménage, et indiquez une adresse à ce ménage. Locate by: Localiser auprès de +fix it: Compléter # Household Household: Ménage From 2492a9281f0943b7ea14c42fa5513b251a49abb0 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 17 Nov 2021 16:06:55 +0100 Subject: [PATCH 12/65] improve household button in course participation --- .../PersonsAssociated/ParticipationItem.vue | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated/ParticipationItem.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated/ParticipationItem.vue index fd3f59cef..a9138e0b3 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated/ParticipationItem.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated/ParticipationItem.vue @@ -5,7 +5,7 @@ addId : false, addEntity: false, addLink: false, - addHouseholdLink: true, + addHouseholdLink: false, addAltNames: true, addAge : true, hLevel : 3, @@ -20,14 +20,15 @@ v-if="hasCurrentHouseholdAddress" v-bind:person="participation.person"> +
  • + + + +
  • -
  • -
      +
      • - {{ p.text }} + + + {{ p.text }} +
    diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/ConcernedGroups/PersonBadge.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/ConcernedGroups/PersonBadge.vue index bef11159c..0b2dd6613 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/ConcernedGroups/PersonBadge.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/ConcernedGroups/PersonBadge.vue @@ -4,7 +4,7 @@ {{ textCutted }} - diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig index e11ab863b..a30034257 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/show.html.twig @@ -55,7 +55,7 @@ {% endif %}

    {{ 'Concerned groups'|trans }}

    -{% 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' } %}

    {{ 'Activity data'|trans }}

    From a6001961519d2b3660969f79836eeef82a2842fc Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 18 Nov 2021 16:25:36 +0100 Subject: [PATCH 43/65] vue_accourse referrer: style for suggestions, add and remove items --- .../AccompanyingCourse/components/Referrer.vue | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Referrer.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Referrer.vue index c39e043c6..b67bbc038 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Referrer.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Referrer.vue @@ -19,16 +19,16 @@ @select="updateReferrer"> - - -
    From aba47600ff2501b54aa8a0bbf30784a334b5db27 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 18 Nov 2021 17:56:46 +0100 Subject: [PATCH 44/65] hop --- .../Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue index 5bb52358b..b3ddffbd7 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue @@ -119,9 +119,9 @@

    {{ $t('persons_involved') }}

    -
      +
      • - +
      From 5a6a15a35126e7f7008bd61cd5e0bab3ba063606 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Fri, 19 Nov 2021 12:31:48 +0100 Subject: [PATCH 45/65] fix: Add missing repository. --- .../ActivityTypeCategoryRepository.php | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/Bundle/ChillActivityBundle/Repository/ActivityTypeCategoryRepository.php diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityTypeCategoryRepository.php b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeCategoryRepository.php new file mode 100644 index 000000000..62a6a9a0d --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeCategoryRepository.php @@ -0,0 +1,23 @@ + Date: Fri, 19 Nov 2021 12:32:05 +0100 Subject: [PATCH 46/65] fix: Fix wrong repository. --- .../Controller/ActivityController.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 47ebebd09..5e7e0de21 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -7,6 +7,7 @@ namespace Chill\ActivityBundle\Controller; use Chill\ActivityBundle\Entity\ActivityReason; 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\MainBundle\Repository\LocationRepository; @@ -54,9 +55,12 @@ final class ActivityController extends AbstractController private AccompanyingPeriodRepository $accompanyingPeriodRepository; + private ActivityTypeCategoryRepository $activityTypeCategoryRepository; + public function __construct( ActivityACLAwareRepositoryInterface $activityACLAwareRepository, ActivityTypeRepository $activityTypeRepository, + ActivityTypeCategoryRepository $activityTypeCategoryRepository, PersonRepository $personRepository, ThirdPartyRepository $thirdPartyRepository, LocationRepository $locationRepository, @@ -69,6 +73,7 @@ final class ActivityController extends AbstractController ) { $this->activityACLAwareRepository = $activityACLAwareRepository; $this->activityTypeRepository = $activityTypeRepository; + $this->activityTypeCategoryRepository = $activityTypeCategoryRepository; $this->personRepository = $personRepository; $this->thirdPartyRepository = $thirdPartyRepository; $this->locationRepository = $locationRepository; @@ -136,10 +141,17 @@ final class ActivityController extends AbstractController $data = []; - $activityTypeCategories = $this->activityTypeRepository->findBy(['active' => true], ['ordering' => 'ASC']); + $activityTypeCategories = $this + ->activityTypeCategoryRepository + ->findBy(['active' => true], ['ordering' => 'ASC']); foreach ($activityTypeCategories as $activityTypeCategory) { - $activityTypes = $this->activityTypeRepository->findBy(['active' => true, 'category' => $activityTypeCategory], ['ordering' => 'ASC']); + $activityTypes = $this + ->activityTypeRepository + ->findBy( + ['active' => true, 'category' => $activityTypeCategory], + ['ordering' => 'ASC'] + ); $data[] = [ 'activityTypeCategory' => $activityTypeCategory, From 0a522b465fbb28eec9c3fa9c162a24930c945b9e Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Fri, 19 Nov 2021 12:32:21 +0100 Subject: [PATCH 47/65] fix: Update typing information. --- .../Entity/ActivityTypeCategory.php | 28 ++----------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Entity/ActivityTypeCategory.php b/src/Bundle/ChillActivityBundle/Entity/ActivityTypeCategory.php index a95424312..4b06ca9b5 100644 --- a/src/Bundle/ChillActivityBundle/Entity/ActivityTypeCategory.php +++ b/src/Bundle/ChillActivityBundle/Entity/ActivityTypeCategory.php @@ -1,31 +1,12 @@ - * - * 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 . - */ +declare(strict_types=1); namespace Chill\ActivityBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** - * Class ActivityTypeCateogry - * - * @package Chill\ActivityBundle\Entity * @ORM\Entity() * @ORM\Table(name="activitytypecategory") * @ORM\HasLifecycleCallbacks() @@ -37,7 +18,7 @@ class ActivityTypeCategory * @ORM\Column(name="id", type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ - private ?int $id; + private ?int $id = null; /** * @ORM\Column(type="json") @@ -54,10 +35,7 @@ class ActivityTypeCategory */ private float $ordering = 0.0; - /** - * Get id - */ - public function getId(): int + public function getId(): ?int { return $this->id; } From f1113ee44841949ff4ce55fb431aad19b7e222e2 Mon Sep 17 00:00:00 2001 From: nobohan Date: Fri, 19 Nov 2021 14:48:31 +0100 Subject: [PATCH 48/65] accompanying course: add client-side validation if no origin --- .../vuejs/AccompanyingCourse/components/Confirm.vue | 9 +++++++-- .../AccompanyingCourse/components/OriginDemand.vue | 10 ++++++++-- .../public/vuejs/AccompanyingCourse/js/i18n.js | 2 ++ .../public/vuejs/AccompanyingCourse/store/index.js | 4 ++++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue index f23a7c9e5..4142b4f50 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue @@ -10,7 +10,7 @@ + +
      + {{ $t('origin.not_valid') }} +
    + + diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig index e55b39f64..2f1e75a7f 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig @@ -23,11 +23,28 @@
    -
    +
    - {# vue teleport fragment here #} - +
    + +
    diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig index 1c9814a10..434a87759 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig @@ -23,32 +23,6 @@ {% block content %}
    -
    - {% for h in participationsByHousehold %} - {% set householdClass = (h.household is not null) ? 'household-' ~ h.household.id : 'no-household alert alert-warning' %} - {% set householdTitle = (h.household is not null) ? - 'household.Household number'|trans({'household_num': h.household.id }) : 'household.Never in any household'|trans %} - - {% if h.household is not null %} - - {% endif %} - {% for p in h.members %} - - {# include vue_onthefly component #} - {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { - targetEntity: { name: 'person', id: p.person.id }, - action: 'show', - displayBadge: true, - buttonText: p.person|chill_entity_render_string - } %} - - {% endfor %} - - {% endfor %} -
    - {% if 'DRAFT' == accompanyingCourse.step %}
    {% include '@ChillPerson/AccompanyingCourse/_still_draft.html.twig' %} diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 09ca5bb81..5054a3922 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -430,3 +430,5 @@ accompanying_course_work: Person addresses: Adresses de résidence Household addresses: Adresses de domicile Insert an address: Insérer une adresse +see social issues: Voir les problématiques sociales +see persons associated: Voir les usagers concernés From 31ec15507025825c29aba4f3fb8c3720f2bffd83 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Fri, 19 Nov 2021 17:27:53 +0100 Subject: [PATCH 51/65] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31c3e1b5d..48ac6aa2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to * [person] do not ask for center any more on person creation * [3party] do not ask for center any more on 3party creation * [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) +* [accompanying course] Add associated persons in banner details. social-issues and associated-persons are slides in same space. ## Test releases From fec852b0447aefebb55b3f07cd808d6b8d23ad0b Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Fri, 19 Nov 2021 18:25:07 +0100 Subject: [PATCH 52/65] carousel: fix span household position --- .../AccompanyingCourse/components/Banner/PersonsAssociated.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/PersonsAssociated.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/PersonsAssociated.vue index 1e6eeb9fa..005350b96 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/PersonsAssociated.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/PersonsAssociated.vue @@ -65,6 +65,7 @@ export default {