diff --git a/Resources/config/services.yml b/Resources/config/services.yml
index b568ab657..6a3f521c4 100644
--- a/Resources/config/services.yml
+++ b/Resources/config/services.yml
@@ -4,8 +4,15 @@ parameters:
services:
chill.main.menu_composer:
class: CL\Chill\MainBundle\Routing\MenuComposer
+ #must be set in function to avoid circular reference with chill.main.twig.chill_menu
+ calls:
+ - [setRoute, ["@router"]]
+
+ chill.main.twig.chill_menu:
+ class: CL\Chill\MainBundle\Routing\MenuTwig
arguments:
- - "@router"
-# cl_chill_main.example:
-# class: %cl_chill_main.example.class%
-# arguments: [@service_id, "plain_value", %parameter%]
+ - "@chill.main.menu_composer"
+ calls:
+ - [setContainer, ["@service_container"]]
+ tags:
+ - { name: twig.extension }
diff --git a/Resources/views/Menu/defaultMenu.html.twig b/Resources/views/Menu/defaultMenu.html.twig
new file mode 100644
index 000000000..39e20cd00
--- /dev/null
+++ b/Resources/views/Menu/defaultMenu.html.twig
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/Routing/MenuComposer.php b/Routing/MenuComposer.php
index 29e998f0e..3d1b51c73 100644
--- a/Routing/MenuComposer.php
+++ b/Routing/MenuComposer.php
@@ -22,10 +22,16 @@ class MenuComposer
*/
private $routeCollection;
- public function __construct(RouterInterface $router)
+ /**
+ *
+ * @internal must be set in function instead of controller to avoid circular reference
+ * with MenuTwig
+ * @param RouterInterface $router
+ */
+ public function setRoute(RouterInterface $router)
{
//see remark in MenuComposer::setRouteCollection
- $this->setRouteCollection($router->getRouteCollection());
+ $this->routeCollection = $router->getRouteCollection();
}
/**
@@ -41,6 +47,14 @@ class MenuComposer
$this->routeCollection = $routeCollection;
}
+ /**
+ * Return an array of routes added to $menuId,
+ * The array is aimed to build route with MenuTwig
+ *
+ * @param string $menuId
+ * @param array $parameters see https://redmine.champs-libres.coop/issues/179
+ * @return array
+ */
public function getRoutesFor($menuId, array $parameters = array())
{
$routes = array();
diff --git a/Routing/MenuTwig.php b/Routing/MenuTwig.php
new file mode 100644
index 000000000..1ed8db231
--- /dev/null
+++ b/Routing/MenuTwig.php
@@ -0,0 +1,107 @@
+
+ *
+ * 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 CL\Chill\MainBundle\Routing;
+
+use CL\Chill\MainBundle\Routing\MenuComposer;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Add the filter 'chill_menu'
+ *
+ * @author Julien Fastré
+ */
+class MenuTwig extends \Twig_Extension implements ContainerAwareInterface
+{
+
+ /**
+ *
+ * @var MenuComposer
+ */
+ private $menuComposer;
+
+ /**
+ *
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ private $container;
+
+ /**
+ * the default parameters for chillMenu
+ *
+ * @var mixed[]
+ */
+ private $defaultParams = array(
+ 'layout' => 'CLChillMainBundle:Menu:defaultMenu.html.twig',
+ 'args' => array(),
+ 'activeRouteKey' => null
+ );
+
+ public function __construct(MenuComposer $menuComposer)
+ {
+ $this->menuComposer = $menuComposer;
+ }
+
+ public function getFunctions()
+ {
+ return [new \Twig_SimpleFunction('chill_menu',
+ array($this, 'chillMenu'), array('is_safe' => array('html')))
+ ];
+ }
+
+ /**
+ * Render a Menu corresponding to $menuId
+ *
+ * Expected params :
+ * - args: the arguments to build the path (i.e: if pattern is /something/{bar}, args must contain {'bar': 'foo'}
+ * - layout: the layout. Absolute path needed (i.e.: CLChillXyzBundle:section:foo.html.twig)
+ * - activeRouteKey : the key active, will render the menu differently.
+ *
+ * see https://redmine.champs-libres.coop/issues/179 for more informations
+ *
+ * @param string $menuId
+ * @param mixed[] $params
+ */
+ public function chillMenu($menuId, array $params = array())
+ {
+ $resolvedParams = array_merge($this->defaultParams, $params);
+
+ $layout = $resolvedParams['layout'];
+ unset($resolvedParams['layout']);
+
+ $resolvedParams['routes'] = $this->menuComposer->getRoutesFor($menuId);
+
+ return $this->container->get('templating')
+ ->render($layout, $resolvedParams);
+ }
+
+ public function getName()
+ {
+ return 'chill_menu';
+ }
+
+ public function setContainer(ContainerInterface $container = null)
+ {
+ $this->container = $container;
+ }
+
+}
diff --git a/Tests/Fixtures/App/config/config.yml b/Tests/Fixtures/App/config/config.yml
index 9522a26e4..0046eed72 100644
--- a/Tests/Fixtures/App/config/config.yml
+++ b/Tests/Fixtures/App/config/config.yml
@@ -8,4 +8,4 @@ framework:
translator: { fallback: fr }
profiler: { only_exceptions: false }
templating:
- engines: ['twig']
\ No newline at end of file
+ engines: ['twig']
diff --git a/Tests/Fixtures/App/config/routing.yml b/Tests/Fixtures/App/config/routing.yml
index 9e42a3faf..62747f142 100644
--- a/Tests/Fixtures/App/config/routing.yml
+++ b/Tests/Fixtures/App/config/routing.yml
@@ -22,4 +22,13 @@ chill_main_dummy_1:
dummy0:
order: 50
label: 'test1'
- helper: 'great helper'
\ No newline at end of file
+ helper: 'great helper'
+
+chill_main_dummy_2:
+ pattern: /dummy2/{param}
+ defaults: {_controller: CLChillMainBundle:Default:index }
+ options:
+ menus:
+ dummy0:
+ order: 50
+ label: test2
\ No newline at end of file
diff --git a/Tests/Fixtures/Resources/views/menus/fakeTemplate.html.twig b/Tests/Fixtures/Resources/views/menus/fakeTemplate.html.twig
new file mode 100644
index 000000000..3b1236389
--- /dev/null
+++ b/Tests/Fixtures/Resources/views/menus/fakeTemplate.html.twig
@@ -0,0 +1 @@
+fake template
\ No newline at end of file
diff --git a/Tests/Fixtures/Resources/views/menus/normalMenu.html.twig b/Tests/Fixtures/Resources/views/menus/normalMenu.html.twig
new file mode 100644
index 000000000..af558b6f2
--- /dev/null
+++ b/Tests/Fixtures/Resources/views/menus/normalMenu.html.twig
@@ -0,0 +1 @@
+{{ chill_menu('dummy0', {'args' : { 'param' :'fake' }, 'activeRouteKey': 'chill_main_dummy_0' }) }}
\ No newline at end of file
diff --git a/Tests/Fixtures/Resources/views/menus/overrideTemplate.html.twig b/Tests/Fixtures/Resources/views/menus/overrideTemplate.html.twig
new file mode 100644
index 000000000..4cf71b30f
--- /dev/null
+++ b/Tests/Fixtures/Resources/views/menus/overrideTemplate.html.twig
@@ -0,0 +1 @@
+{{ chill_menu('dummy1', {'layout' : '@tests/menus/fakeTemplate.html.twig' }) }}
\ No newline at end of file
diff --git a/Tests/Services/ChillMenuTwigFunctionTest.php b/Tests/Services/ChillMenuTwigFunctionTest.php
new file mode 100644
index 000000000..b2afd6d82
--- /dev/null
+++ b/Tests/Services/ChillMenuTwigFunctionTest.php
@@ -0,0 +1,86 @@
+
+ * Copyright (C) 2014, Champs Libres Cooperative SCRLFS,
+ *
+ * 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 CL\Chill\MainBundle\Tests\Services;
+
+use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
+use Symfony\Component\DomCrawler\Crawler;
+
+/**
+ * Test the Twig function 'chill_menu'
+ *
+ * @author Julien Fastré
+ */
+class ChillMenuTwigFunctionTest extends KernelTestCase
+{
+
+ private static $templating;
+
+ public static function setUpBeforeClass()
+ {
+ self::bootKernel(array('environment' => 'test'));
+ static::$templating = static::$kernel
+ ->getContainer()->get('templating');
+ //load templates in Tests/Resources/views
+ static::$kernel->getContainer()->get('twig.loader')
+ ->addPath(__DIR__.'/../Fixtures/Resources/views/', $namespace = 'tests');
+ }
+
+ public function testNormalMenu()
+ {
+ $content = static::$templating->render('@tests/menus/normalMenu.html.twig');
+ $crawler = new Crawler($content);
+
+ $ul = $crawler->filter('ul')->getNode(0);
+ $this->assertEquals( 'ul', $ul->tagName);
+
+ $lis = $crawler->filter('ul')->children();
+ $this->assertEquals(3, count($lis));
+
+ $lis->each(function(Crawler $node, $i) {
+ $this->assertEquals('li', $node->getNode(0)->tagName);
+
+ $a = $node->children()->getNode(0);
+ $this->assertEquals('a', $a->tagName);
+ switch($i) {
+ case 0:
+ $this->assertEquals('/dummy?param=fake', $a->getAttribute('href'));
+ $this->assertEquals('active', $a->getAttribute('class'));
+ $this->assertEquals('test0', $a->nodeValue);
+ break;
+ case 1:
+ $this->assertEquals('/dummy1?param=fake', $a->getAttribute('href'));
+ $this->assertEmpty($a->getAttribute('class'));
+ $this->assertEquals('test1', $a->nodeValue);
+ break;
+ case 3:
+ $this->assertEquals('/dummy2/fake', $a->getAttribute('href'));
+ $this->assertEmpty($a->getAttribute('class'));
+ $this->assertEquals('test2', $a->nodeValue);
+ }
+ });
+ }
+
+ public function testMenuOverrideTemplate()
+ {
+ $content = static::$templating->render('@tests/menus/overrideTemplate.html.twig');
+ $this->assertEquals('fake template', $content);
+ }
+}
diff --git a/Tests/Services/MenuComposerTest.php b/Tests/Services/MenuComposerTest.php
index 8f6c28755..671f6b146 100644
--- a/Tests/Services/MenuComposerTest.php
+++ b/Tests/Services/MenuComposerTest.php
@@ -3,7 +3,6 @@
namespace CL\Chill\MainBundle\Tests\Services;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
-use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\RouteCollection;
/**
@@ -43,7 +42,7 @@ class MenuComposerTest extends KernelTestCase
$routes = $this->menuComposer->getRoutesFor('dummy0');
$this->assertInternalType('array', $routes);
- $this->assertCount(2, $routes);
+ $this->assertCount(3, $routes);
//check that the keys are sorted
$orders = array_keys($routes);
foreach ($orders as $key => $order){
@@ -65,6 +64,10 @@ class MenuComposerTest extends KernelTestCase
'key' => 'chill_main_dummy_1',
'label' => 'test1',
'helper'=> 'great helper'
+ ),
+ 52 => array(
+ 'key' => 'chill_main_dummy_2',
+ 'label' => 'test2'
));
diff --git a/composer.json b/composer.json
index a5cf513c1..cb68260ee 100644
--- a/composer.json
+++ b/composer.json
@@ -22,5 +22,8 @@
"symfony/framework-bundle": "2.5.*",
"symfony/yaml": "2.5.*",
"symfony/symfony": "2.5.*"
+ },
+ "require-dev": {
+ "symfony/dom-crawler": "2.5"
}
}