From 2ff22a73fa93440d1266ae8db41c2437c8c3ec46 Mon Sep 17 00:00:00 2001 From: Tchama Date: Fri, 8 Feb 2019 16:39:12 +0100 Subject: [PATCH] add and display events in history timeline --- DependencyInjection/ChillEventExtension.php | 1 + Resources/config/services/timeline.yml | 9 + Resources/views/EventReason/macro.html.twig | 3 + .../Timeline/event_person_context.html.twig | 61 +++++ Timeline/TimelineEventProvider.php | 242 ++++++++++++++++++ 5 files changed, 316 insertions(+) create mode 100644 Resources/config/services/timeline.yml create mode 100644 Resources/views/EventReason/macro.html.twig create mode 100644 Resources/views/Timeline/event_person_context.html.twig create mode 100644 Timeline/TimelineEventProvider.php diff --git a/DependencyInjection/ChillEventExtension.php b/DependencyInjection/ChillEventExtension.php index 8fe36b2e4..462b17dd6 100644 --- a/DependencyInjection/ChillEventExtension.php +++ b/DependencyInjection/ChillEventExtension.php @@ -33,6 +33,7 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface $loader->load('fixtures.yml'); $loader->load('menu.yml'); $loader->load('controller.yml'); + $loader->load('timeline.yml'); } /* (non-PHPdoc) diff --git a/Resources/config/services/timeline.yml b/Resources/config/services/timeline.yml new file mode 100644 index 000000000..44efe2205 --- /dev/null +++ b/Resources/config/services/timeline.yml @@ -0,0 +1,9 @@ +services: + chill.event.timeline: + class: Chill\EventBundle\Timeline\TimelineEventProvider + arguments: + - '@doctrine.orm.entity_manager' + - '@chill.main.security.authorization.helper' + - '@security.token_storage' + tags: + - { name: chill.timeline, context: 'person' } diff --git a/Resources/views/EventReason/macro.html.twig b/Resources/views/EventReason/macro.html.twig new file mode 100644 index 000000000..c9e3e3729 --- /dev/null +++ b/Resources/views/EventReason/macro.html.twig @@ -0,0 +1,3 @@ +{%- macro reason(r) -%} +  {{ r.name | localize_translatable_string }} +{%- endmacro -%} \ No newline at end of file diff --git a/Resources/views/Timeline/event_person_context.html.twig b/Resources/views/Timeline/event_person_context.html.twig new file mode 100644 index 000000000..ff4e7723a --- /dev/null +++ b/Resources/views/Timeline/event_person_context.html.twig @@ -0,0 +1,61 @@ +{% import 'ChillEventBundle:EventReason:macro.html.twig' as m %} + +{{ dump() }} +{% for participation in event.participations %} + {{ participation.id }} ---> + {{ participation.lastupdate|localizeddate('long', 'none') }} +
+{% endfor %} + +{# +TODO +- user a inscrit la personne à l'événement machin +- user a modifié le statut de la personne inscrite à l'événement machin. nouveau statut : +- user a modifié le rôle de la personne inscrite à l'événement machin. nouveau rôle : +- participation de la personne à l'événement machin +#} + +
+

{{ event.date|localizeddate('long', 'none') }} / {{ 'Event'|trans }}

+
+ {{ '%user% has done an %event_type%'|trans( + { + '%user%' : user, + '%event_type%': event.type.name|localize_translatable_string, + '%date%' : event.date|localizeddate('long', 'none') } + ) }} + + + {% if is_granted(constant('Chill\\ActivityBundle\\Security\\Authorization\\ActivityVoter::SEE_DETAILS'), event) %} +
+
{{ 'Remark'|trans }}
+
{% if event.remark is empty %}{{ 'No remarks'|trans }}{% else %}
{{ event.remark|nl2br }}
{% endif %}
+ +
{{ 'Reasons'|trans }}
+ {%- if event.reasons is empty -%} +
{{ 'No reason associated'|trans }}
+ {%- else -%} +
{% for r in event.reasons %}{{ m.reason(r) }} {% endfor %}
+ {%- endif -%} + +
+ {% endif %} + +
+ + +
diff --git a/Timeline/TimelineEventProvider.php b/Timeline/TimelineEventProvider.php new file mode 100644 index 000000000..a718882d4 --- /dev/null +++ b/Timeline/TimelineEventProvider.php @@ -0,0 +1,242 @@ + + * + * 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\EventBundle\Timeline; + +use Chill\EventBundle\Entity\Event; +use Chill\MainBundle\Entity\Scope; +use Chill\MainBundle\Timeline\TimelineProviderInterface; +use Doctrine\ORM\EntityManager; +use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Role\Role; +use Doctrine\ORM\Mapping\ClassMetadata; +use Chill\PersonBundle\Entity\Person; +use Chill\MainBundle\Entity\User; + +/** + * Class TimelineEventProvider + * + * @package Chill\EventBundle\Timeline + * @author Mathieu Jaumotte jaum_mathieu@collectifs.net + */ +class TimelineEventProvider implements TimelineProviderInterface +{ + /** + * @var EntityManager + */ + protected $em; + + /** + * @var AuthorizationHelper + */ + protected $helper; + + /** + * @var User + */ + protected $user; + + /** + * TimelineEventProvider constructor. + * + * @param EntityManager $em + * @param AuthorizationHelper $helper + * @param TokenStorage $storage + */ + public function __construct( + EntityManager $em, + AuthorizationHelper $helper, + TokenStorage $storage + ) { + $this->em = $em; + $this->helper = $helper; + if (!$storage->getToken()->getUser() instanceof User) { + throw new \RuntimeException('A user should be authenticated !'); + } + $this->user = $storage->getToken()->getUser(); + } + + /** + * @param string $context + * @param array $args + * @return array|string[] + * @throws \Doctrine\ORM\Mapping\MappingException + */ + public function fetchQuery($context, array $args) + { + $this->checkContext($context); + + $metadataEvent = $this->em->getClassMetadata('ChillEventBundle:Event'); + $metadataParticipation = $this->em->getClassMetadata('ChillEventBundle:Participation'); + $metadataPerson = $this->em->getClassMetadata('ChillPersonBundle:Person'); + + $query = array( + 'id' => $metadataEvent->getTableName().'.'.$metadataEvent->getColumnName('id'), + 'type' => 'event', + 'date' => $metadataEvent->getTableName().'.'.$metadataEvent->getColumnName('date'), + 'FROM' => $this->getFromClause($metadataEvent, $metadataParticipation, $metadataPerson), + 'WHERE' => $this->getWhereClause($metadataEvent, $metadataParticipation, $metadataPerson, $args['person']) + ); + + dump($query); + return $query; + } + + /** + * @param ClassMetadata $metadataEvent + * @param ClassMetadata $metadataParticipation + * @param ClassMetadata $metadataPerson + * @return string + * @throws \Doctrine\ORM\Mapping\MappingException + */ + private function getFromClause( + ClassMetadata $metadataEvent, + ClassMetadata $metadataParticipation, + ClassMetadata $metadataPerson + ) { + $eventParticipationMapping = $metadataParticipation->getAssociationMapping('event'); + $participationPersonMapping = $metadataParticipation->getAssociationMapping('person'); + + return $metadataEvent->getTableName() + + .' JOIN '.$metadataParticipation->getTableName() + .' ON ' .$metadataParticipation->getTableName() + .'.' .$eventParticipationMapping['joinColumns'][0]['name'] + .' = ' .$metadataEvent->getTableName() + .'.' .$eventParticipationMapping['joinColumns'][0]['referencedColumnName'] + + .' JOIN '.$metadataPerson->getTableName() + .' ON ' .$metadataPerson->getTableName() + .'.' .$participationPersonMapping['joinColumns'][0]['referencedColumnName'] + .' = ' .$metadataParticipation->getTableName() + .'.' .$participationPersonMapping['joinColumns'][0]['name'] + ; + } + + /** + * @param ClassMetadata $metadataEvent + * @param ClassMetadata $metadataParticipation + * @param ClassMetadata $metadataPerson + * @param Person $person + * @return string + * @throws \Doctrine\ORM\Mapping\MappingException + */ + private function getWhereClause( + ClassMetadata $metadataEvent, + ClassMetadata $metadataParticipation, + ClassMetadata $metadataPerson, + Person $person + ) { + $role = new Role('CHILL_EVENT_SEE'); + + $reachableCenters = $this->helper->getReachableCenters($this->user, $role); + $associationMapping = $metadataParticipation->getAssociationMapping('person'); + + if (count($reachableCenters) === 0) { + return 'FALSE = TRUE'; + } + + $whereClause = sprintf( '%s = %d', + $associationMapping['joinColumns'][0]['name'], + $person->getId()); + + // and + $centerAndScopeLines = array(); + foreach ($reachableCenters as $center) { + $reachableCircleId = array_map( + function (Scope $scope) { return $scope->getId(); }, + $this->helper->getReachableCircles($this->user, $role, $person->getCenter()) + ); + $centerAndScopeLines[] = sprintf( + '(%s = %d AND %s IN (%s))', + $metadataPerson->getTableName() . '.' . + $metadataPerson->getAssociationMapping('center')['joinColumns'][0]['name'], + $center->getId(), + $metadataEvent->getTableName() . '.' . + $metadataEvent->getAssociationMapping('circle')['joinColumns'][0]['name'], + implode(',', $reachableCircleId) + ); + } + $whereClause .= ' AND ('. implode(' OR ', $centerAndScopeLines) .')'; + + return $whereClause; + } + + /** + * check if the context is supported + * + * @param string $context + * @throws \LogicException if the context is not supported + */ + private function checkContext($context) + { + if ($context !== 'person') { + throw new \LogicException("The context '$context' is not " + . "supported. Currently only 'person' is supported"); + } + } + + /** + * @param string $type + * @return bool + */ + public function supportsType($type) + { + return $type === 'event'; + } + + /** + * @param array $ids + * @return array|mixed[] + */ + public function getEntities(array $ids) { + + $events = $this->em->getRepository(Event::class) + ->findBy(array('id' => $ids)); + + $result = array(); + foreach ($events as $event) { + $result[$event->getId()] = $event; + } + return $result; + } + + /** + * @param Event $entity + * @param string $context + * @param array $args + * @return array|mixed[] + */ + public function getEntityTemplate($entity, $context, array $args) { + + $this->checkContext($context); + + return array( + 'template' => 'ChillEventBundle:Timeline:event_person_context.html.twig', + 'template_data' => array( + 'event' => $entity, + 'person' => $args['person'], + 'user' => $this->user + ) + ); + } + +} \ No newline at end of file