From 5350a099514a9dc86c1cdd4f35b1ad08fbe21825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 25 May 2021 19:07:09 +0200 Subject: [PATCH] apply timeline for report --- .../config/services/timeline.yaml | 4 +- ...son_context.html.twig => report.html.twig} | 8 +- .../Timeline/TimelineReportProvider.php | 266 +++++++++++------- .../ChillReportBundle/config/services.yaml | 5 +- .../config/services/timeline.yaml | 8 +- 5 files changed, 173 insertions(+), 118 deletions(-) rename src/Bundle/ChillReportBundle/Resources/views/Timeline/{report_person_context.html.twig => report.html.twig} (71%) diff --git a/src/Bundle/ChillEventBundle/config/services/timeline.yaml b/src/Bundle/ChillEventBundle/config/services/timeline.yaml index 7a8eb366d..82f758d15 100644 --- a/src/Bundle/ChillEventBundle/config/services/timeline.yaml +++ b/src/Bundle/ChillEventBundle/config/services/timeline.yaml @@ -6,5 +6,5 @@ services: - '@chill.main.security.authorization.helper' - '@security.token_storage' public: true - tags: - - { name: chill.timeline, context: 'person' } + # tags: + # - { name: chill.timeline, context: 'person' } diff --git a/src/Bundle/ChillReportBundle/Resources/views/Timeline/report_person_context.html.twig b/src/Bundle/ChillReportBundle/Resources/views/Timeline/report.html.twig similarity index 71% rename from src/Bundle/ChillReportBundle/Resources/views/Timeline/report_person_context.html.twig rename to src/Bundle/ChillReportBundle/Resources/views/Timeline/report.html.twig index e9bcba85b..7929fed9e 100644 --- a/src/Bundle/ChillReportBundle/Resources/views/Timeline/report_person_context.html.twig +++ b/src/Bundle/ChillReportBundle/Resources/views/Timeline/report.html.twig @@ -1,9 +1,9 @@
-

{{ report.date|format_date('long') }} / {{ 'Report'|trans }}

+

{{ report.date|format_date('long') }} / {{ 'Report'|trans }}{% if 'person' != context %} / {{ report.person|chill_entity_render_box }}{% endif %}

{{ '%user% has filled a %report_label% report'|trans( { - '%user%' : user, + '%user%' : report.user, '%report_label%': report.CFGroup.name|localize_translatable_string, '%date%' : report.date|format_date('long') } ) }} @@ -25,13 +25,13 @@
  • - + {{ 'View the report'|trans }}
  • {% if is_granted('CHILL_REPORT_UPDATE', report) %}
  • - + {{ 'Update the report'|trans }}
  • diff --git a/src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php b/src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php index c6c13173a..49e237d87 100644 --- a/src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php +++ b/src/Bundle/ChillReportBundle/Timeline/TimelineReportProvider.php @@ -30,71 +30,34 @@ use Chill\PersonBundle\Entity\Person; use Chill\MainBundle\Entity\Scope; use Chill\CustomFieldsBundle\Service\CustomFieldsHelper; use Chill\ReportBundle\Entity\Report; +use Symfony\Component\Security\Core\Security; +use Chill\MainBundle\Timeline\TimelineSingleQuery; /** * Provide report for inclusion in timeline - * - * @author Julien Fastré - * @author Champs Libres */ class TimelineReportProvider implements TimelineProviderInterface { - /** - * - * @var EntityManager - */ - protected $em; + protected EntityManager $em; - /** - * - * @var AuthorizationHelper - */ - protected $helper; + protected AuthorizationHelper $helper; - /** - * - * @var \Chill\MainBundle\Entity\User - */ - protected $user; + protected CustomFieldsHelper $customFieldsHelper; - /** - * - * @var CustomFieldsHelper - */ - protected $customFieldsHelper; - - /** - * @var - */ protected $showEmptyValues; - /** - * TimelineReportProvider constructor. - * - * @param EntityManager $em - * @param AuthorizationHelper $helper - * @param TokenStorageInterface $storage - * @param CustomFieldsHelper $customFieldsHelper - * @param $showEmptyValues - */ public function __construct( EntityManager $em, AuthorizationHelper $helper, - TokenStorageInterface $storage, + Security $security, CustomFieldsHelper $customFieldsHelper, $showEmptyValues ) { $this->em = $em; $this->helper = $helper; - - if (!$storage->getToken()->getUser() instanceof \Chill\MainBundle\Entity\User) - { - throw new \RuntimeException('A user should be authenticated !'); - } - - $this->user = $storage->getToken()->getUser(); + $this->security = $security; $this->customFieldsHelper = $customFieldsHelper; $this->showEmptyValues = $showEmptyValues; } @@ -107,70 +70,162 @@ class TimelineReportProvider implements TimelineProviderInterface { $this->checkContext($context); - $metadataReport = $this->em->getClassMetadata('ChillReportBundle:Report'); - $metadataPerson = $this->em->getClassMetadata('ChillPersonBundle:Person'); - - return array( - 'id' => $metadataReport->getTableName() - .'.'.$metadataReport->getColumnName('id'), + $report = $this->em->getClassMetadata(Report::class); + [$where, $parameters] = $this->getWhereClause($context, $args); + + return TimelineSingleQuery::fromArray([ + 'id' => $report->getTableName() + .'.'.$report->getColumnName('id'), 'type' => 'report', - 'date' => $metadataReport->getTableName() - .'.'.$metadataReport->getColumnName('date'), - 'FROM' => $this->getFromClause($metadataReport, $metadataPerson), - 'WHERE' => $this->getWhereClause($metadataReport, $metadataPerson, - $args['person']) - ); + 'date' => $report->getTableName() + .'.'.$report->getColumnName('date'), + 'FROM' => $this->getFromClause($context), + 'WHERE' => $where, + 'parameters' => $parameters + ]); } - private function getWhereClause(ClassMetadata $metadataReport, - ClassMetadata $metadataPerson, Person $person) + private function getWhereClause(string $context, array $args): array { - $role = new Role('CHILL_REPORT_SEE'); - $reachableCenters = $this->helper->getReachableCenters($this->user, - $role); - $associationMapping = $metadataReport->getAssociationMapping('person'); - - // we start with reports having the person_id linked to person - // (currently only context "person" is supported) - $whereClause = sprintf('%s = %d', - $associationMapping['joinColumns'][0]['name'], - $person->getId()); - - // we add acl (reachable center and scopes) - $centerAndScopeLines = array(); - foreach ($reachableCenters as $center) { - $reachablesScopesId = array_map( - function(Scope $scope) { return $scope->getId(); }, - $this->helper->getReachableScopes($this->user, $role, - $person->getCenter()) - ); - - $centerAndScopeLines[] = sprintf('(%s = %d AND %s IN (%s))', - $metadataPerson->getTableName().'.'. - $metadataPerson->getAssociationMapping('center')['joinColumns'][0]['name'], - $center->getId(), - $metadataReport->getTableName().'.'. - $metadataReport->getAssociationMapping('scope')['joinColumns'][0]['name'], - implode(',', $reachablesScopesId)); - + switch ($context) { + case 'person': + return $this->getWhereClauseForPerson($context, $args); + case 'center': + return $this->getWhereClauseForCenter($context, $args); + default: + throw new \UnexpectedValueException("This context $context is not implemented"); } - $whereClause .= ' AND ('.implode(' OR ', $centerAndScopeLines).')'; - - return $whereClause; + } + + private function getWhereClauseForCenter(string $context, array $args): array + { + $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(), + $role); + $reportPersonId = $report->getAssociationMapping('person')['joinColumns'][0]['name']; + $reportScopeId = $report->getAssociationMapping('scope')['joinColumns'][0]['name']; + $personCenterId = $person->getAssociationMapping('center')['joinColumns'][0]['name']; + // parameters for the query, will be filled later + $parameters = []; + + // the clause, that will be joined with an "OR" + $centerScopesClause = "({person}.{center_id} = ? ". + "AND {report}.{scopes_id} IN ({scopes_ids}))"; + // container for formatted clauses + $formattedClauses = []; + + $askedCenters = $args['centers']; + foreach ($reachableCenters as $center) { + if (FALSE === \in_array($center, $askedCenters)) { + continue; + } + + // add the center id to the parameters + $parameters[] = $center->getId(); + // loop over scopes + $scopeIds = []; + foreach ($this->helper->getReachableScopes($this->security->getUser(), + $role, $center) as $scope) { + if (\in_array($scope->getId(), $scopeIds)) { + continue; + } + $scopeIds[] = $scope->getId(); + } + + $formattedClauses[] = \strtr($centerScopesClause, [ + '{scopes_ids}' => \implode(', ', \array_fill(0, \count($scopeIds), '?')) + ]); + // append $scopeIds to parameters + $parameters = \array_merge($parameters, $scopeIds); + } + + if (0 === count($formattedClauses)) { + return [ 'FALSE = TRUE', [] ]; + } + + return [ + \strtr( + \implode(' OR ', $formattedClauses), + [ + '{person}' => $person->getTableName(), + '{center_id}' => $personCenterId, + '{report}' => $report->getTableName(), + '{scopes_id}' => $reportScopeId, + ] + ), + $parameters + ]; + } + + private function getWhereClauseForPerson(string $context, array $args): array + { + $report = $this->em->getClassMetadata(Report::class); + $person = $this->em->getClassMetadata(Person::class); + $role = new Role('CHILL_REPORT_SEE'); + $reportPersonId = $report->getAssociationMapping('person')['joinColumns'][0]['name']; + $reportScopeId = $report->getAssociationMapping('scope')['joinColumns'][0]['name']; + $personCenterId = $person->getAssociationMapping('center')['joinColumns'][0]['name']; + // parameters for the query, will be filled later + $parameters = [ $args['person']->getId() ]; + + // 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, + $args['person']->getCenter()); + + foreach ($scopes as $scope) { + if (\in_array($scope->getId(), $parameters)) { + continue; + } + + $parameters[] = $scope->getId(); + } + + if (1 === count($parameters)) { + // nothing change, we simplify the clause + $clause = "{report}.{person_id} = ? AND FALSE = TRUE"; + } + + return [ + \strtr( + $clause, + [ + '{report}' => $report->getTableName(), + '{person_id}' => $reportPersonId, + '{scopes_id}' => $reportScopeId, + '{scopes_ids}' => \implode(', ', + \array_fill(0, \count($parameters)-1, '?')) + ] + ), + $parameters + ]; } - private function getFromClause(ClassMetadata $metadataReport, - ClassMetadata $metadataPerson) + private function getFromClause(string $context): string { - $associationMapping = $metadataReport->getAssociationMapping('person'); - - return $metadataReport->getTableName().' JOIN ' - .$metadataPerson->getTableName().' ON ' - .$metadataPerson->getTableName().'.'. - $associationMapping['joinColumns'][0]['referencedColumnName'] - .' = ' - .$associationMapping['joinColumns'][0]['name'] + $report = $this->em->getClassMetadata(Report::class); + $person = $this->em->getClassMetadata(Person::class); + $reportPersonId = $report + ->getAssociationMapping('person')['joinColumns'][0]['name'] ; + $personId = $report + ->getAssociationMapping('person')['joinColumns'][0]['referencedColumnName'] + ; + + $clause = "{report} ". + "JOIN {person} ON {report}.{person_id} = {person}.{id_person} "; + + return \strtr($clause, + [ + '{report}' => $report->getTableName(), + '{person}' => $person->getTableName(), + '{person_id}' => $reportPersonId, + '{id_person}' => $personId + ] + ); } /** @@ -199,12 +254,11 @@ class TimelineReportProvider implements TimelineProviderInterface $this->checkContext($context); return array( - 'template' => 'ChillReportBundle:Timeline:report_person_context.html.twig', + 'template' => 'ChillReportBundle:Timeline:report.html.twig', 'template_data' => array( - 'report' => $entity, - 'custom_fields_in_summary' => $this->getFieldsToRender($entity, $context), - 'person' => $args['person'], - 'user' => $entity->getUser() + 'report' => $entity, + 'context' => $context, + 'custom_fields_in_summary' => $this->getFieldsToRender($entity, $context), ) ); } @@ -272,9 +326,9 @@ class TimelineReportProvider implements TimelineProviderInterface */ private function checkContext($context) { - if ($context !== 'person') { + if ($context !== 'person' && $context !== 'center') { throw new \LogicException("The context '$context' is not " - . "supported. Currently only 'person' is supported"); + . "supported. Currently only 'person' and 'center' is supported"); } } diff --git a/src/Bundle/ChillReportBundle/config/services.yaml b/src/Bundle/ChillReportBundle/config/services.yaml index fec944ff5..477a94746 100644 --- a/src/Bundle/ChillReportBundle/config/services.yaml +++ b/src/Bundle/ChillReportBundle/config/services.yaml @@ -20,11 +20,12 @@ services: arguments: - '@doctrine.orm.entity_manager' - '@chill.main.security.authorization.helper' - - '@security.token_storage' + - '@Symfony\Component\Security\Core\Security' - '@chill.custom_field.helper' - '%chill_custom_fields.show_empty_values%' tags: - { name: chill.timeline, context: 'person' } + - { name: chill.timeline, context: 'center' } chill.report.security.authorization.report_voter: class: Chill\ReportBundle\Security\Authorization\ReportVoter @@ -43,4 +44,4 @@ services: - "@doctrine.orm.entity_manager" tags: - { name: form.type, alias: chill_reportbundle_report } - \ No newline at end of file + diff --git a/src/Bundle/ChillTaskBundle/config/services/timeline.yaml b/src/Bundle/ChillTaskBundle/config/services/timeline.yaml index d6ca33606..0962866be 100644 --- a/src/Bundle/ChillTaskBundle/config/services/timeline.yaml +++ b/src/Bundle/ChillTaskBundle/config/services/timeline.yaml @@ -6,12 +6,12 @@ services: $authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper' $tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface' public: true - tags: - - { name: 'chill.timeline', context: 'person' } + # tags: + # - { name: 'chill.timeline', context: 'person' } Chill\TaskBundle\Timeline\SingleTaskTaskLifeCycleEventTimelineProvider: arguments: $em: '@Doctrine\ORM\EntityManagerInterface' $registry: '@Symfony\Component\Workflow\Registry' - tags: - - { name: 'chill.timeline', context: 'task' } + # tags: + #- { name: 'chill.timeline', context: 'task' }