diff --git a/Resources/config/services.yml b/Resources/config/services.yml index 2a24edf02..c5532dc9b 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -35,4 +35,14 @@ services: arguments: - "@chill.main.security.authorization.helper" tags: - - { name: security.voter } \ No newline at end of file + - { name: security.voter } + + + chill.activity.timeline: + class: Chill\ActivityBundle\Timeline\TimelineActivityProvider + arguments: + - '@doctrine.orm.entity_manager' + - '@chill.main.security.authorization.helper' + - '@security.token_storage' + tags: + - { name: chill.timeline, context: 'person' } \ No newline at end of file diff --git a/Resources/translations/messages.fr.yml b/Resources/translations/messages.fr.yml new file mode 100644 index 000000000..cd3e69010 --- /dev/null +++ b/Resources/translations/messages.fr.yml @@ -0,0 +1,5 @@ +#timeline +'%user% has done an %activity_type% on %date%': %user% a effectué une activité de type "%activity_type%" le %date% +Activity: Activité +View the activity: Voir l'activité + diff --git a/Resources/views/Timeline/activity_person_context.html.twig b/Resources/views/Timeline/activity_person_context.html.twig new file mode 100644 index 000000000..eea0dbd1d --- /dev/null +++ b/Resources/views/Timeline/activity_person_context.html.twig @@ -0,0 +1,13 @@ +
+

{{ 'Activity'|trans }}

+
+ {{ '%user% has done an %activity_type% on %date%'|trans( + { + '%user%' : user, + '%activity_type%': activity.type.name|localize_translatable_string, + '%date%' : activity.date|localizeddate('long', 'none') } + ) }} {{ 'View the activity'|trans }} + +
+
diff --git a/Tests/Timeline/TimelineProviderTest.php b/Tests/Timeline/TimelineProviderTest.php new file mode 100644 index 000000000..7c25cc0d9 --- /dev/null +++ b/Tests/Timeline/TimelineProviderTest.php @@ -0,0 +1,36 @@ + + * + * 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\Tests\Timeline; + +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +/** + * + * + * @author Julien Fastré + */ +class TimelineProviderTest extends WebTestCase +{ + public function testAnActivityIsShownOnTimeline() + { + $this->markTestSkipped("we have to write fixtures before writing this tests"); + } + +} diff --git a/Timeline/TimelineActivityProvider.php b/Timeline/TimelineActivityProvider.php new file mode 100644 index 000000000..b54fe34b0 --- /dev/null +++ b/Timeline/TimelineActivityProvider.php @@ -0,0 +1,205 @@ + + * + * 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\Timeline; + +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\Scope; + +/** + * Provide activity for inclusion in timeline + * + * @author Julien Fastré + * @author Champs Libres + */ +class TimelineActivityProvider implements TimelineProviderInterface +{ + + /** + * + * @var EntityManager + */ + protected $em; + + /** + * + * @var AuthorizationHelper + */ + protected $helper; + + /** + * + * @var \Chill\MainBundle\Entity\User + */ + protected $user; + + public function __construct(EntityManager $em, AuthorizationHelper $helper, + TokenStorage $storage) + { + $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(); + } + + /** + * + * {@inheritDoc} + */ + public function fetchQuery($context, array $args) + { + $this->checkContext($context); + + $metadataActivity = $this->em->getClassMetadata('ChillActivityBundle:Activity'); + $metadataPerson = $this->em->getClassMetadata('ChillPersonBundle:Person'); + + return array( + 'id' => $metadataActivity->getTableName() + .'.'.$metadataActivity->getColumnName('id'), + 'type' => 'activity', + 'date' => $metadataActivity->getTableName() + .'.'.$metadataActivity->getColumnName('date'), + 'FROM' => $this->getFromClause($metadataActivity, $metadataPerson), + 'WHERE' => $this->getWhereClause($metadataActivity, $metadataPerson, + $args['person']) + ); + } + + private function getWhereClause(ClassMetadata $metadataActivity, + ClassMetadata $metadataPerson, Person $person) + { + $role = new Role('CHILL_ACTIVITY_SEE'); + $reachableCenters = $this->helper->getReachableCenters($this->user, + $role); + $associationMapping = $metadataActivity->getAssociationMapping('person'); + + // we start with activities 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(), + $metadataActivity->getTableName().'.'. + $metadataActivity->getAssociationMapping('scope')['joinColumns'][0]['name'], + implode(',', $reachablesScopesId)); + + } + $whereClause .= ' AND ('.implode(' OR ', $centerAndScopeLines).')'; + + return $whereClause; + } + + private function getFromClause(ClassMetadata $metadataActivity, + ClassMetadata $metadataPerson) + { + $associationMapping = $metadataActivity->getAssociationMapping('person'); + + return $metadataActivity->getTableName().' JOIN ' + .$metadataPerson->getTableName().' ON ' + .$metadataPerson->getTableName().'.'. + $associationMapping['joinColumns'][0]['referencedColumnName'] + .' = ' + .$associationMapping['joinColumns'][0]['name'] + ; + } + + /** + * + * {@inheritDoc} + */ + public function getEntities(array $ids) + { + $activities = $this->em->getRepository('ChillActivityBundle:Activity') + ->findBy(array('id' => $ids)); + + $result = array(); + foreach($activities as $activity) { + $result[$activity->getId()] = $activity; + } + + return $result; + } + + /** + * + * {@inheritDoc} + */ + public function getEntityTemplate($entity, $context, array $args) + { + $this->checkContext($context); + + return array( + 'template' => 'ChillActivityBundle:Timeline:activity_person_context.html.twig', + 'template_data' => array( + 'activity' => $entity, + 'person' => $args['person'], + 'user' => $entity->getUser() + ) + ); + } + + /** + * + * {@inheritDoc} + */ + public function supportsType($type) + { + return $type === 'activity'; + } + + /** + * 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"); + } + } + +}