From 7c6b5424b8ca9a6232e78125bd6211b5266adfe8 Mon Sep 17 00:00:00 2001
From: Mark Carver <mark.carver@me.com>
Date: Sat, 5 Oct 2013 15:26:46 -0500
Subject: Issue #2073811 by Mark Carver, dawehner, pwolanin: Add a url
 generator twig extension

---
 core/lib/Drupal/Core/CoreServiceProvider.php       |  7 +++-
 core/lib/Drupal/Core/Template/TwigExtension.php    | 42 ++++++++++++++++++++--
 .../Drupal/system/Tests/Theme/ThemeTestTwig.php    | 23 ++++++++++++
 .../twig_theme_test/TwigThemeTestController.php    |  9 +++++
 .../twig_theme_test.url_generator.html.twig        |  8 +++++
 .../modules/twig_theme_test/twig_theme_test.module |  4 +++
 .../twig_theme_test/twig_theme_test.routing.yml    |  6 ++++
 core/scripts/run-tests.sh                          |  1 +
 8 files changed, 97 insertions(+), 3 deletions(-)
 create mode 100644 core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.url_generator.html.twig

diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index 9cd8f5a..7abefdc 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -109,6 +109,11 @@ public static function registerTwig(ContainerBuilder $container) {
       ->addArgument(DRUPAL_ROOT);
     $container->setAlias('twig.loader', 'twig.loader.filesystem');

+    $twig_extension = new Definition('Drupal\Core\Template\TwigExtension');
+    // When in the installer these services are not yet available.
+    if (!drupal_installation_attempted()) {
+      $twig_extension->addMethodCall('setGenerators', array(new Reference('url_generator')));
+    }
     $container->register('twig', 'Drupal\Core\Template\TwigEnvironment')
       ->addArgument(new Reference('twig.loader'))
       ->addArgument(array(
@@ -128,7 +133,7 @@ public static function registerTwig(ContainerBuilder $container) {
         'debug' => settings()->get('twig_debug', FALSE),
         'auto_reload' => settings()->get('twig_auto_reload', NULL),
       ))
-      ->addMethodCall('addExtension', array(new Definition('Drupal\Core\Template\TwigExtension')))
+      ->addMethodCall('addExtension', array($twig_extension))
       // @todo Figure out what to do about debugging functions.
       // @see http://drupal.org/node/1804998
       ->addMethodCall('addExtension', array(new Definition('Twig_Extension_Debug')));
diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php
index 9cd17b0..dd92545 100644
--- a/core/lib/Drupal/Core/Template/TwigExtension.php
+++ b/core/lib/Drupal/Core/Template/TwigExtension.php
@@ -10,6 +10,8 @@
  */

 namespace Drupal\Core\Template;
+use Drupal\Core\Routing\UrlGeneratorInterface;
+use Guzzle\Common\Exception\ExceptionCollection;

 /**
  * A class for providing Twig extensions (specific Twig_NodeVisitors, filters and functions).
@@ -17,11 +19,28 @@
  * @see \Drupal\Core\CoreServiceProvider
  */
 class TwigExtension extends \Twig_Extension {
+
+  /**
+   * The URL generator.
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface
+   */
+  protected $urlGenerator;
+
+  /**
+   * Constructs \Drupal\Core\Template\TwigExtension.
+   *
+   * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
+   *   The URL generator.
+   */
+  public function setGenerators(UrlGeneratorInterface $url_generator) {
+    $this->urlGenerator = $url_generator;
+  }
+
   public function getFunctions() {
     // @todo re-add unset => twig_unset if this is really needed
     return array(
-      // @todo Remove URL function once http://drupal.org/node/1778610 is resolved.
-      'url' => new \Twig_Function_Function('url'),
+      'url' => new \Twig_SimpleFunction('url', array($this, 'generateUrl')),
       // These functions will receive a TwigReference object, if a render array is detected
       'hide' => new TwigReferenceFunction('twig_hide'),
       'render_var' => new TwigReferenceFunction('twig_render_var'),
@@ -63,5 +82,24 @@ public function getName()
   {
     return 'drupal_core';
   }
+
+  /**
+   * Generates a URL using the route generator or path generator.
+   *
+   * @see \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute()
+   * @see \Drupal\Core\Routing\UrlGeneratorInterface::generateFromPath()
+   */
+  public function generateUrl() {
+    $args = func_get_args();
+    // Try generating a route first.
+    try {
+      return call_user_func_array(array($this->urlGenerator, 'generateFromRoute'), $args);
+    }
+    // If provided string isn't a route, try generating from a path instead.
+    // This method is deprecated and is only used as a last resort.
+    catch (\Exception $e) {
+      return call_user_func_array(array($this->urlGenerator, 'generateFromPath'), $args);
+    }
+  }
 }

diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTestTwig.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTestTwig.php
index 2dce51f..0afec1e 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTestTwig.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTestTwig.php
@@ -47,4 +47,27 @@ function testTwigVariableDataTypes() {
     }
   }

+  /**
+   * Tests the url and url_generate Twig function.
+   */
+  public function testTwigUrlGenerator() {
+    $this->drupalGet('twig-theme-test/url-generator');
+    // Find the absolute URL of the current site.
+    $url_generator = $this->container->get('url_generator');
+    $link_generator = $this->container->get('link_generator');
+    $expected = array(
+      'url (as path) not absolute: ' . $url_generator->generateFromPath('user/register'),
+      'url (as path) absolute: ' . $url_generator->generateFromPath('user/register', array('absolute' => TRUE)),
+      'url (as route) not absolute: ' . $url_generator->generateFromRoute('user.register'),
+      'url (as route) absolute: ' . $url_generator->generateFromRoute('user.register', array(), array('absolute' => TRUE)),
+      'url (as route) absolute with fragment: ' . $url_generator->generateFromRoute('user.register', array(), array('absolute' => TRUE, 'fragment' => 'bottom')),
+    );
+    // Make sure we got something.
+    $content = $this->drupalGetContent();
+    $this->assertFalse(empty($content), 'Page content is not empty');
+    foreach ($expected as $string) {
+      $this->assertRaw('<div>' . $string . '</div>');
+    }
+  }
+
 }
diff --git a/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php b/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php
index 9179de9..ced7226 100644
--- a/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php
+++ b/core/modules/system/tests/modules/twig_theme_test/lib/Drupal/twig_theme_test/TwigThemeTestController.php
@@ -38,4 +38,13 @@ public function transBlockRender() {
     );
   }

+  /**
+   * Renders for testing url_generator functions in a Twig template.
+   */
+  public function urlGeneratorRender() {
+    return array(
+      '#theme' => 'twig_theme_test_url_generator',
+    );
+  }
+
 }
diff --git a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.url_generator.html.twig b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.url_generator.html.twig
new file mode 100644
index 0000000..507721a
--- /dev/null
+++ b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.url_generator.html.twig
@@ -0,0 +1,8 @@
+{# Test the url twig function with paths #}
+<div>url (as path) not absolute: {{ url('user/register') }}</div>
+<div>url (as path) absolute: {{ url('user/register', {'absolute': 1 }) }}</div>
+
+{# Test the url twig function with routes #}
+<div>url (as route) not absolute: {{ url('user.register') }}</div>
+<div>url (as route) absolute: {{ url('user.register', {}, {'absolute': '1' }) }}</div>
+<div>url (as route) absolute with fragment: {{ url('user.register', {}, {'absolute': '1', 'fragment': 'bottom' }) }}</div>
diff --git a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.module b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.module
index 934d1b0..fc51628 100644
--- a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.module
+++ b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.module
@@ -11,6 +11,10 @@ function twig_theme_test_theme($existing, $type, $theme, $path) {
     'variables' => array(),
     'template' => 'twig_theme_test.trans',
   );
+  $items['twig_theme_test_url_generator'] = array(
+    'variables' => array(),
+    'template' => 'twig_theme_test.url_generator',
+  );
   return $items;
 }

diff --git a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.routing.yml b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.routing.yml
index 07880fb..904e0ea 100644
--- a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.routing.yml
+++ b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.routing.yml
@@ -11,3 +11,9 @@ twig_theme_test.trans:
     _content: '\Drupal\twig_theme_test\TwigThemeTestController::transBlockRender'
   requirements:
     _permission: 'access content'
+twig_theme_test_url_generator:
+  path: '/twig-theme-test/url-generator'
+  defaults:
+    _content: '\Drupal\twig_theme_test\TwigThemeTestController::urlGeneratorRender'
+  requirements:
+    _permission: 'access content'
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 31ea371..e728abe 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -301,6 +301,7 @@ function simpletest_script_init($server_software) {
   }

   $_SERVER['HTTP_HOST'] = $host;
+  $_SERVER['SERVER_PORT'] =  isset($parsed_url['port']) ? $parsed_url['port'] : 80;
   $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
   $_SERVER['SERVER_ADDR'] = '127.0.0.1';
   $_SERVER['SERVER_SOFTWARE'] = $server_software;
--
1.8.3.4

