mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Squashed commit of the following: commit e1236655e1514fd207818aeb57789eca0d949453 Merge: c0b349b fb15bd3 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Tue Jun 30 09:51:35 2015 +0200 Merge remote-tracking branch 'origin/master' into add_acl In order to prepare merging of add_acl to master Conflicts: composer.json commit c0b349bb5f31fe79c84f82d4dd6658c9e90ef728 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Tue Jun 30 00:40:13 2015 +0200 fix infos in composer.json [ci skip] commit 106bbf56a5060efd2a89232f278692eeb57e3092 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Mon Jun 29 22:58:48 2015 +0200 add username and password to client auth options [ci skip] commit c4990972711850616aa1426394884223d63b504f Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Mon Jun 29 22:22:20 2015 +0200 fix quoting in timelinebuilder commit 1db7cbea5a0fb0e8d396f8c9d8dc01240b47e96f Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Thu Jun 25 22:43:46 2015 +0200 remove data_class to allow edit form commit 7c999279310b5e2b9ecef8a9b5001c71910a822d Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Wed Jun 24 22:33:42 2015 +0200 fix doc for AppendScopeChoiceTypeTrait commit 839d4c43bf6f463e705b47d5dbc0cdf7853db0b6 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Wed Jun 24 22:30:13 2015 +0200 refactor: move scope field to a trait Example usage : ``` class AbcType extends Symfony\Component\Form\AbstractType { use AppendScopeChoiceTypeTrait; protected $authorizationHelper; protected $translatableStringHelper; protected $user; public function __construct(AuthorizationHelper $helper, TokenStorageInterface $tokenStorage, TranslatableStringHelper $translatableStringHelper) { $this->authorizationHelper = $helper; $this->user = $tokenStorage->getToken()->getUser(); $this->translatableStringHelper = $translatableStringHelper; } public function buildForm(FormBuilder $builder, array $options) { // ... add your form there // append the scope using FormEvents: PRE_SET_DATA $this->appendScopeChoices($builder, $options['role'], $options['center'], $this->user, $this->authorizationHelper, $this->translatableStringHelper); } public function configureOptions(OptionsResolver $resolver) { // ... add your options // add an option 'role' and 'center' to your form (optional) $this->appendScopeChoicesOptions($resolver); } } ``` [ci skip] commit a1ac530f343146eee12b5982e4e6fceb6dc1da66 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Wed Jun 24 00:24:30 2015 +0200 remove unused statements commit 74f0a4ce5dfdfa4f8fc39ce1cfe726d945e9bdec Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Wed Jun 24 00:09:47 2015 +0200 add missing unused statement commit b3a49f2de8758b51c57af6ff0437f30b2fdef72e Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Tue Jun 23 23:58:57 2015 +0200 remove ScopeType strategy and fix autorization helper commit aaa70b5eeae76b0950110f51b8274e08bef10576 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Tue Jun 23 21:35:06 2015 +0200 create scope type commit 8f5b2b23c9448b8c8e752e46bdd7f9f721054a98 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Fri Jun 19 18:13:54 2015 +0200 add getREachableCenter method + test on Autho.Helper commit ab2ccb8c287f9aef12912ea9b7f5dc4998209d77 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Fri Jun 19 17:27:22 2015 +0200 remove debug information [ci skip] commit 9d481c07966a5d769d1418bb366bd6b1ea9d4f76 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Fri Jun 19 17:24:57 2015 +0200 fix role hierarchy Now we test effectively that a user has access, not that a role may grant access commit f4b17d0ae398fd6c26d4907377761e9f15d36a90 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Thu Jun 18 02:08:55 2015 +0200 fix trait conflicts traits does not share the same instance of prophet any more commit baac8ce97ba0acd4c4fe6f03278f7a0d30da28ad Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Thu Jun 18 01:56:39 2015 +0200 try to fix trait error in zend strict mode (used by travis) commit 7b9fa4b14bde72c7036a29c03a0c6cca3c7e6c74 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Thu Jun 18 01:24:09 2015 +0200 fix error on trait hierarchy (should be) commit f8b3451089f7017653bf8d280b640356df6a1841 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Thu Jun 18 01:03:04 2015 +0200 add userHasAccess method to AuthorizationHelper This method may be used in voter to check access. It supports both hasCenterInterface and HasScopeInterface and check all required permission. commit 9ad9f624a0aa73ca2e639fd68fe4d5559da2bbd7 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Thu Jun 18 01:01:16 2015 +0200 add utilities to generate prophesized entities entities - User (with permissions) - Center - Scope may be generated by trait/methods This ease test writing about acl commit 16008b9e64bb7f551319abea494db1c1c5a12b82 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Fri Jun 12 22:42:46 2015 +0200 add test to CenterType commit 55e2c64aba9714caf09df8dbc9595980826b296a Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Fri Jun 12 20:50:05 2015 +0200 first implementation of test on CenterType [TRANSFER][ci skip] commit 548fb24927cc470794a51d81f1ba6af52237363f Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Wed Jun 10 21:15:52 2015 +0200 add center type the center type is hidden if the current user can reach only one center, and is `entity` type if the user can reach multiple centers commit 024e3ef8d969d25560406864d86768a260ef4402 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Mon Jun 8 00:28:51 2015 +0200 add first impl of Access Model - first classes and interfaces - authorizationHelper + test - rewrite loadUser to have multi-center commit bc5ae70c83c39a0a738e78313e33020fc284f456 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Sun Jun 7 22:11:13 2015 +0200 make deprecations message not fail tests commit ab9308ed62e45171e9d355ec033b0862b9274e07 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Sat Jun 6 10:19:19 2015 +0200 introducting phpunit-bridge to handle deprecation warnings commit 5b7a43c4d058af58578c30120670c5be5b11cfd8 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Fri Jun 5 12:49:23 2015 +0200 fix options resolver deprecation commit a5b4e5743f790c16dfd04e5068c5ecca9c2b1583 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Fri Jun 5 12:36:17 2015 +0200 remove warning about deprecation in phpunit commit 56621767936df1ea1293c49ae06380d7735c8d6c Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Fri Jun 5 11:59:50 2015 +0200 fix pattern deprecation in routing/test pattern=> path commit 17d40fc5294b245d7377572a8c918abab2484985 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Fri Jun 5 11:40:59 2015 +0200 fix deprecation of pattern in routing pattern => path in routing commit 6a33752c6439bd9dbc707df010ce1d8765eeb5fe Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Fri Jun 5 11:21:18 2015 +0200 fix twig.form.resources deprecation the new key is twig.form_themes commit adf03eb819f2d443f87d7b744f59a5adf51d2b31 Author: Julien Fastré <julien.fastre@champs-libres.coop> Date: Fri Jun 5 10:56:51 2015 +0200 switch to symfony 2.7 [ci-skip]
263 lines
8.4 KiB
PHP
263 lines
8.4 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Copyright (C) 2015 Champs-Libres Coopérative <info@champs-libres.coop>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
namespace Chill\MainBundle\Timeline;
|
|
|
|
use Doctrine\ORM\Query\ResultSetMapping;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
|
|
|
/**
|
|
* Build timeline
|
|
*
|
|
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
|
*/
|
|
class TimelineBuilder implements ContainerAwareInterface
|
|
{
|
|
|
|
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
|
|
|
/**
|
|
*
|
|
* @var \Doctrine\ORM\EntityManagerInterface
|
|
*/
|
|
private $em;
|
|
|
|
public function __construct(EntityManagerInterface $em)
|
|
{
|
|
$this->em = $em;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @var string references to providers services
|
|
*/
|
|
private $providers = array();
|
|
|
|
/**
|
|
* return an HTML string with timeline
|
|
*
|
|
* This function must be called from controller
|
|
*
|
|
* @example https://redmine.champs-libres.coop/projects/chillperson/repository/revisions/bd2e1b1808f73e39532e9538413025df5487cad0/entry/Controller/TimelinePersonController.php#L47 the implementation in person bundle
|
|
*
|
|
* @param string $context
|
|
* @param array $args arguments defined by the bundle which create the context
|
|
* @param int $page first page = 0
|
|
* @param int $number number of items by page
|
|
* @return string an HTML representation, must be included using `|raw` filter
|
|
*/
|
|
public function getTimelineHTML($context, array $args, $page = 0, $number = 20)
|
|
{
|
|
$query = $this->buildUnionQuery($context, $args, $page, $number);
|
|
$fetched = $this->runQuery($query);
|
|
$entitiesByKey = $this->getEntities($fetched, $context);
|
|
|
|
return $this->render($fetched, $entitiesByKey, $context, $args);
|
|
}
|
|
|
|
/**
|
|
* add a provider id
|
|
*
|
|
* @internal This function is called by the TimelineCompilerClass
|
|
*
|
|
* @param string $context the context of the service
|
|
* @param string $id the
|
|
*/
|
|
public function addProvider($context, $id)
|
|
{
|
|
$this->providers[$context][] = $id;
|
|
}
|
|
|
|
/**
|
|
* Get providers by context
|
|
*
|
|
* @param string $context
|
|
* @return TimelineProviderInterface[]
|
|
*/
|
|
public function getProvidersByContext($context)
|
|
{
|
|
$providers = array();
|
|
|
|
foreach($this->providers[$context] as $providerId) {
|
|
$providers[] = $this->container->get($providerId);
|
|
}
|
|
|
|
return $providers;
|
|
}
|
|
|
|
/**
|
|
* build the UNION query with all providers
|
|
*
|
|
* @uses self::buildSelectQuery to build individual SELECT queries
|
|
*
|
|
* @param string $context
|
|
* @param mixed $args
|
|
* @param int $page
|
|
* @param int $number
|
|
* @return string
|
|
* @throws \LogicException if no builder have been defined for this context
|
|
*/
|
|
private function buildUnionQuery($context, array $args, $page, $number)
|
|
{
|
|
//throw an exception if no provider have been defined for this context
|
|
if (!array_key_exists($context, $this->providers)) {
|
|
throw new \LogicException(sprintf('No builders have been defined for "%s"'
|
|
. ' context', $context));
|
|
}
|
|
|
|
//append SELECT queries with UNION keyword between them
|
|
$union = '';
|
|
foreach($this->getProvidersByContext($context) as $provider) {
|
|
$select = $this->buildSelectQuery($provider, $context, $args);
|
|
$append = ($union === '') ? $select : ' UNION '.$select;
|
|
$union .= $append;
|
|
}
|
|
//add ORDER BY clause and LIMIT
|
|
$union .= sprintf(' ORDER BY date LIMIT %d OFFSET %d',
|
|
$number, $page * $number);
|
|
|
|
return $union;
|
|
}
|
|
|
|
/**
|
|
* return the SQL SELECT query as a string,
|
|
*
|
|
* @uses TimelineProfiderInterface::fetchQuery use the fetchQuery function
|
|
* @param \Chill\MainBundle\Timeline\TimelineProviderInterface $provider
|
|
* @param string $context
|
|
* @param mixed[] $args
|
|
* @return string
|
|
*/
|
|
private function buildSelectQuery(TimelineProviderInterface $provider, $context, array $args)
|
|
{
|
|
$data = $provider->fetchQuery($context, $args);
|
|
|
|
return sprintf(
|
|
'SELECT %s AS id, '
|
|
. '%s AS "date", '
|
|
. "'%s' AS type "
|
|
. 'FROM %s '
|
|
. 'WHERE %s',
|
|
$data['id'],
|
|
$data['date'],
|
|
$data['type'],
|
|
$data['FROM'],
|
|
$data['WHERE']);
|
|
}
|
|
|
|
/**
|
|
* run the UNION query and return result as an array
|
|
*
|
|
* @param string $query
|
|
* @return array
|
|
*/
|
|
private function runQuery($query)
|
|
{
|
|
$resultSetMapping = (new ResultSetMapping())
|
|
->addScalarResult('id', 'id')
|
|
->addScalarResult('type', 'type')
|
|
->addScalarResult('date', 'date');
|
|
|
|
return $this->em->createNativeQuery($query, $resultSetMapping)
|
|
->getArrayResult();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param array $queriedIds
|
|
* @param string $context
|
|
* @return array with the form array($type => [$entity, $entity, $entity])
|
|
*/
|
|
private function getEntities(array $queriedIds, $context)
|
|
{
|
|
//gather entities by type to pass all id with same type to the TimelineProvider.
|
|
$idsByType = array();
|
|
|
|
foreach($queriedIds as $result) {
|
|
$idsByType[$result['type']][] = $result['id'];
|
|
}
|
|
|
|
//fetch entities from providers
|
|
$entitiesByType = array();
|
|
foreach ($idsByType as $type => $ids) {
|
|
//iterate providers for current context
|
|
foreach($this->getProvidersByContext($context) as $provider) {
|
|
if ($provider->supportsType($type)) {
|
|
$entitiesByType[$type] = $provider->getEntities($ids);
|
|
break; //we assume that providers have unique keys => we break the loop
|
|
}
|
|
}
|
|
}
|
|
|
|
return $entitiesByType;
|
|
}
|
|
|
|
/**
|
|
* render the timeline as HTML
|
|
*
|
|
* @param array $fetched
|
|
* @param array $entitiesByType
|
|
* @param string $context
|
|
* @param mixed[] $args
|
|
* @return string the HTML representation of the timeline
|
|
*/
|
|
private function render(array $fetched, array $entitiesByType, $context, array $args)
|
|
{
|
|
//add results to a pretty array
|
|
$timelineEntries = array();
|
|
foreach ($fetched as $result) {
|
|
$data = $this->getTemplateData(
|
|
$result['type'],
|
|
$entitiesByType[$result['type']][$result['id']], //the entity
|
|
$context,
|
|
$args);
|
|
$timelineEntry['date'] = new \DateTime($result['date']);
|
|
$timelineEntry['template'] = $data['template'];
|
|
$timelineEntry['template_data'] = $data['template_data'];
|
|
|
|
$timelineEntries[] = $timelineEntry;
|
|
}
|
|
|
|
return $this->container->get('templating')
|
|
->render('ChillMainBundle:Timeline:index.html.twig', array(
|
|
'results' => $timelineEntries
|
|
));
|
|
|
|
}
|
|
|
|
/**
|
|
* get the template data from the provider for the given entity, by type.
|
|
*
|
|
* @param string $type
|
|
* @param mixed $entity
|
|
* @param string $context
|
|
* @param mixed[] $args
|
|
* @return array the template data fetched from the provider
|
|
*/
|
|
private function getTemplateData($type, $entity, $context, array $args)
|
|
{
|
|
foreach($this->getProvidersByContext($context) as $provider) {
|
|
if ($provider->supportsType($type)) {
|
|
return $provider->getEntityTemplate($entity, $context, $args);
|
|
}
|
|
}
|
|
}
|
|
}
|