diff --git a/core/lib/Drupal/Component/Utility/Url.php b/core/lib/Drupal/Component/Utility/Url.php index c7eff3e..9c13516 100644 --- a/core/lib/Drupal/Component/Utility/Url.php +++ b/core/lib/Drupal/Component/Utility/Url.php @@ -9,6 +9,8 @@ /** * Helper class URL based methods. + * + * @todo Rename to UrlHelper in https://drupal.org/node/2184653. */ class Url { diff --git a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php index f2b4f25..f0e0e46 100644 --- a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php +++ b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php @@ -8,7 +8,8 @@ namespace Drupal\Core\Form; use Drupal\Component\Utility\String; -use Drupal\Component\Utility\Url; +use Drupal\Component\Utility\Url as UrlHelper; +use Drupal\Core\Url; use Symfony\Component\HttpFoundation\Request; /** @@ -36,7 +37,7 @@ public static function buildCancelLink(ConfirmFormInterface $form, Request $requ $query = $request->query; // If a destination is specified, that serves as the cancel link. if ($query->has('destination')) { - $options = Url::parse($query->get('destination')); + $options = UrlHelper::parse($query->get('destination')); $link = array( '#href' => $options['path'], '#options' => $options, @@ -44,19 +45,19 @@ public static function buildCancelLink(ConfirmFormInterface $form, Request $requ } // Check for a route-based cancel link. elseif ($route = $form->getCancelRoute()) { - if (empty($route['route_name'])) { - throw new \UnexpectedValueException(String::format('Missing route name in !class::getCancelRoute().', array('!class' => get_class($form)))); + if (!($route instanceof Url)) { + if (empty($route['route_name'])) { + throw new \UnexpectedValueException(String::format('Missing route name in !class::getCancelRoute().', array('!class' => get_class($form)))); + } + // Ensure there is something to pass as the params and options. + $route += array( + 'route_parameters' => array(), + 'options' => array(), + ); + $route = new Url($route['route_name'], $route['route_parameters'], $route['options']); } - // Ensure there is something to pass as the params and options. - $route += array( - 'route_parameters' => array(), - 'options' => array(), - ); - $link = array( - '#route_name' => $route['route_name'], - '#route_parameters' => $route['route_parameters'], - '#options' => $route['options'], - ); + + $link = $route->toRenderArray(); } $link['#type'] = 'link'; diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index cd82931..d376067 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -10,7 +10,7 @@ use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\Unicode; -use Drupal\Component\Utility\Url; +use Drupal\Component\Utility\Url as UrlHelper; use Drupal\Core\Access\CsrfTokenGenerator; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\HttpKernel; @@ -18,6 +18,7 @@ use Drupal\Core\Render\Element; use Drupal\Core\Routing\UrlGeneratorInterface; use Drupal\Core\StringTranslation\TranslationInterface; +use Drupal\Core\Url; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -847,7 +848,7 @@ public function validateForm($form_id, &$form, &$form_state) { if (isset($form['#token'])) { if (!$this->csrfToken->validate($form_state['values']['form_token'], $form['#token'])) { $path = $this->request->attributes->get('_system_path'); - $query = Url::filterQueryParameters($this->request->query->all()); + $query = UrlHelper::filterQueryParameters($this->request->query->all()); $url = $this->urlGenerator->generateFromPath($path, array('query' => $query)); // Setting this error will cause the form to fail validation. @@ -933,13 +934,17 @@ public function redirectForm($form_state) { // Check for a route-based redirection. if (isset($form_state['redirect_route'])) { - $form_state['redirect_route'] += array( - 'route_parameters' => array(), - 'options' => array(), - ); - $form_state['redirect_route']['options']['absolute'] = TRUE; - $url = $this->urlGenerator->generateFromRoute($form_state['redirect_route']['route_name'], $form_state['redirect_route']['route_parameters'], $form_state['redirect_route']['options']); - return new RedirectResponse($url); + // @todo Remove once all redirects are converted to Url. + if (!($form_state['redirect_route'] instanceof Url)) { + $form_state['redirect_route'] += array( + 'route_parameters' => array(), + 'options' => array(), + ); + $form_state['redirect_route'] = new Url($form_state['redirect_route']['route_name'], $form_state['redirect_route']['route_parameters'], $form_state['redirect_route']['options']); + } + + $form_state['redirect_route']->setAbsolute(); + return new RedirectResponse($form_state['redirect_route']->toString()); } // Only invoke a redirection if redirect value was not set to FALSE. @@ -1318,7 +1323,7 @@ public function doBuildForm($form_id, &$element, &$form_state) { // Special handling if we're on the top level form element. if (isset($element['#type']) && $element['#type'] == 'form') { if (!empty($element['#https']) && settings()->get('mixed_mode_sessions', FALSE) && - !Url::isExternal($element['#action'])) { + !UrlHelper::isExternal($element['#action'])) { global $base_root; // Not an external URL so ensure that it is secure. diff --git a/core/lib/Drupal/Core/Routing/MatchingRouteNotFoundException.php b/core/lib/Drupal/Core/Routing/MatchingRouteNotFoundException.php new file mode 100644 index 0000000..6856580 --- /dev/null +++ b/core/lib/Drupal/Core/Routing/MatchingRouteNotFoundException.php @@ -0,0 +1,17 @@ +match($path); } - /** - * Returns the route_name and route parameters matching a system path. - * - * @todo Find a better place for this method in - * https://drupal.org/node/2153891. - * - * @param string $link_path - * The link path to find a route name for. - * - * @return array - * Returns an array with both the route name and parameters, or an empty - * array if no route was matched. - */ - public function findRouteNameParameters($link_path) { - // Look up the route_name used for the given path. - $request = Request::create('/' . $link_path); - $request->attributes->set('_system_path', $link_path); - try { - $result = \Drupal::service('router')->matchRequest($request); - $return = array(); - $return[] = isset($result['_route']) ? $result['_route'] : ''; - $return[] = $result['_raw_variables']->all(); - return $return; - } - catch (\Exception $e) { - return array(); - } - } - } diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php new file mode 100644 index 0000000..23dbeaf --- /dev/null +++ b/core/lib/Drupal/Core/Url.php @@ -0,0 +1,386 @@ +routeName = $route_name; + $this->routeParameters = $route_parameters; + $this->options = $options; + } + + /** + * Returns the Url object matching a path. + * + * @param string $path + * A path (e.g. 'node/1', 'http://drupal.org'). + * + * @return static + * An Url object. + * + * @throws \Drupal\Core\Routing\MatchingRouteNotFoundException + * Thrown when the path cannot be matched. + */ + public static function createFromPath($path) { + if (UrlHelper::isExternal($path)) { + $url = new static($path); + $url->setExternal(); + return $url; + } + + // Special case the front page route. + if ($path == '') { + $route_name = $path; + $route_parameters = array(); + } + else { + // Look up the route name and parameters used for the given path. + try { + $result = \Drupal::service('router')->match('/' . $path); + } + catch (ResourceNotFoundException $e) { + throw new MatchingRouteNotFoundException(sprintf('No matching route could be found for the path "%s"', $path), 0, $e); + } + $route_name = $result[RouteObjectInterface::ROUTE_NAME]; + $route_parameters = $result['_raw_variables']->all(); + } + return new static($route_name, $route_parameters); + } + + /** + * Returns the Url object matching a request. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * A request object. + * + * @return static + * A Url object. + * + * @throws \Drupal\Core\Routing\MatchingRouteNotFoundException + * Thrown when the request cannot be matched. + */ + public static function createFromRequest(Request $request) { + try { + $result = \Drupal::service('router')->matchRequest($request); + } + catch (ResourceNotFoundException $e) { + throw new MatchingRouteNotFoundException(sprintf('No matching route could be found for the request: %s', $request), 0, $e); + } + $route_name = $result[RouteObjectInterface::ROUTE_NAME]; + $route_parameters = $result['_raw_variables']->all(); + return new static($route_name, $route_parameters); + } + + /** + * Sets this Url to be external. + * + * @return $this + */ + protected function setExternal() { + $this->external = TRUE; + + // What was passed in as the route name is actually the path. + $this->path = $this->routeName; + + // Set empty route name and parameters. + $this->routeName = ''; + $this->routeParameters = array(); + + return $this; + } + + /** + * Indicates if this Url is external. + * + * @return bool + */ + public function isExternal() { + return $this->external; + } + + /** + * Returns the route name. + * + * @return string + */ + public function getRouteName() { + return $this->routeName; + } + + /** + * Returns the route parameters. + * + * @return array + */ + public function getRouteParameters() { + return $this->routeParameters; + } + + /** + * Sets the route parameters. + * + * @param array $parameters + * The array of parameters. + * + * @return $this + */ + public function setRouteParameters($parameters) { + if ($this->isExternal()) { + throw new \Exception('External URLs do not have route parameters.'); + } + $this->routeParameters = $parameters; + return $this; + } + + /** + * Sets a specific route parameter. + * + * @param string $key + * The key of the route parameter. + * @param mixed $value + * The route parameter. + * + * @return $this + */ + public function setRouteParameter($key, $value) { + if ($this->isExternal()) { + throw new \Exception('External URLs do not have route parameters.'); + } + $this->routeParameters[$key] = $value; + return $this; + } + + /** + * Returns the URL options. + * + * @return array + */ + public function getOptions() { + return $this->options; + } + + /** + * Gets a specific option. + * + * @param string $name + * The name of the option. + * + * @return mixed + * The value for a specific option, or NULL if it does not exist. + */ + public function getOption($name) { + if (!isset($this->options[$name])) { + return NULL; + } + + return $this->options[$name]; + } + + /** + * Sets the URL options. + * + * @param array $options + * The array of options. + * + * @return $this + */ + public function setOptions($options) { + $this->options = $options; + return $this; + } + + /** + * Sets a specific option. + * + * @param string $name + * The name of the option. + * @param mixed $value + * The option value. + * + * @return $this + */ + public function setOption($name, $value) { + $this->options[$name] = $value; + return $this; + } + + /** + * Sets the absolute value for this Url. + * + * @param bool $absolute + * (optional) Whether to make this Url absolute or not. Defaults to TRUE. + * + * @return $this + */ + public function setAbsolute($absolute = TRUE) { + $this->options['absolute'] = $absolute; + return $this; + } + + /** + * Generates the path for this Url object. + */ + public function toString() { + if ($this->isExternal()) { + return $this->urlGenerator()->generateFromPath($this->path, $this->getOptions()); + } + + return $this->urlGenerator()->generateFromRoute($this->getRouteName(), $this->getRouteParameters(), $this->getOptions()); + } + + /** + * Returns all the information about the route. + * + * @return array + * An associative array containing all the properties of the route. + */ + public function toArray() { + return array( + 'route_name' => $this->getRouteName(), + 'route_parameters' => $this->getRouteParameters(), + 'options' => $this->getOptions(), + ); + } + + /** + * Returns the route information for a render array. + * + * @return array + * An associative array suitable for a render array. + */ + public function toRenderArray() { + return array( + '#route_name' => $this->getRouteName(), + '#route_parameters' => $this->getRouteParameters(), + '#options' => $this->getOptions(), + ); + } + + /** + * Returns the internal path for this route. + * + * This path will not include any prefixes, fragments, or query strings. + * + * @return string + * The internal path for this route. + */ + public function getInternalPath() { + if ($this->isExternal()) { + throw new \Exception('External URLs do not have internal representations.'); + } + return $this->urlGenerator()->getPathFromRoute($this->getRouteName(), $this->getRouteParameters()); + } + + /** + * Gets the URL generator. + * + * @return \Drupal\Core\Routing\UrlGeneratorInterface + * The URL generator. + */ + protected function urlGenerator() { + if (!$this->urlGenerator) { + $this->urlGenerator = \Drupal::urlGenerator(); + } + return $this->urlGenerator; + } + + /** + * Sets the URL generator. + * + * @param \Drupal\Core\Routing\UrlGeneratorInterface + * The URL generator. + * + * @return $this + */ + public function setUrlGenerator(UrlGeneratorInterface $url_generator) { + $this->urlGenerator = $url_generator; + return $this; + } + +} diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php index 3e3033c..1ebc5bf 100644 --- a/core/lib/Drupal/Core/Utility/LinkGenerator.php +++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php @@ -13,6 +13,7 @@ use Drupal\Core\Path\AliasManagerInterface; use Drupal\Core\Template\Attribute; use Drupal\Core\Routing\UrlGeneratorInterface; +use Drupal\Core\Url; /** * Provides a class which generates a link with route names and parameters. @@ -68,14 +69,13 @@ public function __construct(UrlGeneratorInterface $url_generator, ModuleHandlerI * * @see system_page_build() */ - public function generate($text, $route_name, array $parameters = array(), array $options = array()) { + public function generateFromUrl($text, Url $url) { // Start building a structured representation of our link to be altered later. $variables = array( // @todo Inject the service when drupal_render() is converted to one. 'text' => is_array($text) ? drupal_render($text) : $text, - 'route_name' => $route_name, - 'parameters' => $parameters, - 'options' => $options, + 'url' => $url, + 'options' => $url->getOptions(), ); // Merge in default options. @@ -85,6 +85,7 @@ public function generate($text, $route_name, array $parameters = array(), array 'html' => FALSE, 'language' => NULL, 'set_active_class' => FALSE, + 'absolute' => FALSE, ); // Add a hreflang attribute if we know the language of this link's url and @@ -106,7 +107,7 @@ public function generate($text, $route_name, array $parameters = array(), array // Add a "data-drupal-link-system-path" attribute to let the // drupal.active-link library know the path in a standardized manner. if (!isset($variables['options']['attributes']['data-drupal-link-system-path'])) { - $path = $this->urlGenerator->getPathFromRoute($route_name, $parameters); + $path = $url->getInternalPath(); $variables['options']['attributes']['data-drupal-link-system-path'] = $this->aliasManager->getSystemPath($path); } } @@ -123,10 +124,11 @@ public function generate($text, $route_name, array $parameters = array(), array // Move attributes out of options. generateFromRoute(() doesn't need them. $attributes = new Attribute($variables['options']['attributes']); unset($variables['options']['attributes']); + $url->setOptions($variables['options']); // The result of the url generator is a plain-text URL. Because we are using // it here in an HTML argument context, we need to encode it properly. - $url = String::checkPlain($this->urlGenerator->generateFromRoute($variables['route_name'], $variables['parameters'], $variables['options'])); + $url = String::checkPlain($url->toString()); // Sanitize the link text if necessary. $text = $variables['options']['html'] ? $variables['text'] : String::checkPlain($variables['text']); @@ -134,4 +136,13 @@ public function generate($text, $route_name, array $parameters = array(), array return '' . $text . ''; } + /** + * {@inheritdoc} + */ + public function generate($text, $route_name, array $parameters = array(), array $options = array()) { + $url = new Url($route_name, $parameters, $options); + $url->setUrlGenerator($this->urlGenerator); + return $this->generateFromUrl($text, $url); + } + } diff --git a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php index 888ab85..e6e74a7 100644 --- a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php +++ b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php @@ -7,6 +7,8 @@ namespace Drupal\Core\Utility; +use Drupal\Core\Url; + /** * Defines an interface for generating links from route names and parameters. */ @@ -77,4 +79,17 @@ */ public function generate($text, $route_name, array $parameters = array(), array $options = array()); + /** + * Renders a link to a URL. + * + * @param string $text + * The link text for the anchor tag as a translated string. + * @param \Drupal\Core\Url $url + * The URL object used for the link. + * + * @return string + * An HTML string containing a link to the given route and parameters. + */ + public function generateFromUrl($text, Url $url); + } diff --git a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php index 0eeb197..fde077e 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php @@ -9,6 +9,7 @@ use Drupal\Core\Entity\Entity; use Drupal\Core\Entity\EntityStorageControllerInterface; +use Drupal\Core\Url; use Drupal\menu_link\MenuLinkInterface; use Symfony\Component\Routing\Route; @@ -505,13 +506,9 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { // Find the route_name. if (!isset($this->route_name)) { - if (!$this->external && $result = \Drupal::service('router.matcher.final_matcher')->findRouteNameParameters($this->link_path)) { - list($this->route_name, $this->route_parameters) = $result; - } - else { - $this->route_name = ''; - $this->route_parameters = array(); - } + $url = Url::createFromPath($this->link_path); + $this->route_name = $url->getRouteName(); + $this->route_parameters = $url->getRouteParameters(); } elseif (empty($this->link_path)) { $this->link_path = \Drupal::urlGenerator()->getPathFromRoute($this->route_name, $this->route_parameters); diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php index 281f538..e73c7ab 100644 --- a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php +++ b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php @@ -11,6 +11,7 @@ use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\Core\Field\FieldDefinition; use Drupal\Core\Routing\UrlMatcher; +use Drupal\Core\Url; use Drupal\shortcut\ShortcutInterface; /** @@ -125,10 +126,9 @@ public static function preCreate(EntityStorageControllerInterface $storage_contr public function preSave(EntityStorageControllerInterface $storage_controller) { parent::preSave($storage_controller); - if ($route_info = \Drupal::service('router.matcher.final_matcher')->findRouteNameParameters($this->path->value)) { - $this->setRouteName($route_info[0]); - $this->setRouteParams($route_info[1]); - } + $url = Url::createFromPath($this->path->value); + $this->setRouteName($url->getRouteName()); + $this->setRouteParams($url->getRouteParameters()); } /** diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module index a988cf4..8955301 100644 --- a/core/modules/shortcut/shortcut.module +++ b/core/modules/shortcut/shortcut.module @@ -6,6 +6,7 @@ */ use Drupal\Core\Routing\UrlMatcher; +use Drupal\Core\Url; use Drupal\shortcut\ShortcutSetInterface; use Symfony\Component\HttpFoundation\Request; @@ -389,7 +390,7 @@ function shortcut_preprocess_page(&$variables) { // pages). if (shortcut_set_edit_access() && ($item = menu_get_item()) && $item['access']) { $link = current_path(); - if (!($route_info = \Drupal::service('router.matcher.final_matcher')->findRouteNameParameters($link))) { + if (!($url = Url::createFromPath($link))) { // Bail out early if we couldn't find a matching route. return; } @@ -405,7 +406,7 @@ function shortcut_preprocess_page(&$variables) { // Check if $link is already a shortcut and set $link_mode accordingly. $shortcuts = \Drupal::entityManager()->getStorageController('shortcut')->loadByProperties(array('shortcut_set' => $shortcut_set->id())); foreach ($shortcuts as $shortcut) { - if ($shortcut->getRouteName() == $route_info[0] && $shortcut->getRouteParams() == $route_info[1]) { + if ($shortcut->getRouteName() == $url->getRouteName() && $shortcut->getRouteParams() == $url->getRouteParameters()) { $shortcut_id = $shortcut->id(); break; } diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php index c0fa026..2ad1ab4 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php @@ -75,7 +75,6 @@ function testBreadCrumbs() { 'admin/structure/taxonomy/manage/tags' => t('Tags'), ); $this->assertBreadcrumb('admin/structure/taxonomy/manage/tags/overview', $trail); - $this->assertBreadcrumb('admin/structure/taxonomy/manage/tags/fields', $trail); $this->assertBreadcrumb('admin/structure/taxonomy/manage/tags/add', $trail); // Verify Menu administration breadcrumbs. diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index f18174d..14c5156 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -3171,13 +3171,7 @@ function hook_filetransfer_info_alter(&$filetransfer_info) { * - text: The link text for the anchor tag as a translated string. * - url_is_active: Whether or not the link points to the currently active * URL. - * - path: If this link is being generated by l(), this system path, relative - * path, or external URL will be passed to url() to generate the href - * attribute for this link. - * - route_name: The name of the route to use to generate the link, if - * this is a "route link". - * - parameters: Any parameters needed to render the route path pattern, if - * this is a "route link". + * - url: The \Drupal\Core\Url object. * - options: An associative array of additional options that will be passed * to either \Drupal\Core\Routing\UrlGenerator::generateFromPath() or * \Drupal\Core\Routing\UrlGenerator::generateFromRoute() to generate the diff --git a/core/modules/system/tests/modules/router_test_directory/router_test.routing.yml b/core/modules/system/tests/modules/router_test_directory/router_test.routing.yml index d0460a1..7405d88 100644 --- a/core/modules/system/tests/modules/router_test_directory/router_test.routing.yml +++ b/core/modules/system/tests/modules/router_test_directory/router_test.routing.yml @@ -93,3 +93,37 @@ router_test.14: defaults: _controller: '\Drupal\router_test\TestControllers::test9' +router_test.hierarchy_parent: + path: '/menu-test/parent' + defaults: + _controller: '\Drupal\router_test\TestControllers::test' + requirements: + _access: 'TRUE' + +router_test.hierarchy_parent_child1: + path: '/menu-test/parent/child-1' + defaults: + _controller: '\Drupal\router_test\TestControllers::test' + requirements: + _access: 'TRUE' + +router_test.hierarchy_parent_child1_1: + path: '/menu-test/parent/child-1/child-1-1' + defaults: + _controller: '\Drupal\router_test\TestControllers::test' + requirements: + _access: 'TRUE' + +router_test.hierarchy_parent_child1_2: + path: '/menu-test/parent/child-1/child-1-2' + defaults: + _controller: '\Drupal\router_test\TestControllers::test' + requirements: + _access: 'TRUE' + +router_test.hierarchy_parent_child2: + path: '/menu-test/parent/child-2' + defaults: + _controller: '\Drupal\router_test\TestControllers::test' + requirements: + _access: 'TRUE' diff --git a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php new file mode 100644 index 0000000..ac867c3 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php @@ -0,0 +1,198 @@ + 'Url object (external)', + 'description' => 'Tests the \Drupal\Core\Url class with external paths.', + 'group' => 'Routing', + ); + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface'); + $this->urlGenerator->expects($this->any()) + ->method('generateFromPath') + ->will($this->returnCallback(function ($path) { + return $path; + })); + + $this->router = $this->getMock('Drupal\Tests\Core\Routing\TestRouterInterface'); + $container = new ContainerBuilder(); + $container->set('router', $this->router); + $container->set('url_generator', $this->urlGenerator); + \Drupal::setContainer($container); + } + + /** + * Tests the createFromPath method. + * + * @covers ::createFromPath() + * @covers ::setExternal() + */ + public function testCreateFromPath() { + $url = Url::createFromPath($this->path); + $this->assertInstanceOf('Drupal\Core\Url', $url); + $this->assertTrue($url->isExternal()); + return $url; + } + + /** + * Tests the createFromRequest method. + * + * @covers ::createFromRequest() + * + * @expectedException \Drupal\Core\Routing\MatchingRouteNotFoundException + * @expectedExceptionMessage No matching route could be found for the request: request_as_a_string + */ + public function testCreateFromRequest() { + // Mock the request in order to override the __toString() method. + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + $request->expects($this->once()) + ->method('__toString') + ->will($this->returnValue('request_as_a_string')); + + $this->router->expects($this->once()) + ->method('matchRequest') + ->with($request) + ->will($this->throwException(new ResourceNotFoundException())); + + $this->assertNull(Url::createFromRequest($request)); + } + + /** + * Tests the isExternal() method. + * + * @depends testCreateFromPath + * + * @covers ::isExternal() + */ + public function testIsExternal(Url $url) { + $this->assertTrue($url->isExternal()); + } + + /** + * Tests the toString() method. + * + * @depends testCreateFromPath + * + * @covers ::toString() + */ + public function testToString(Url $url) { + $this->assertSame($this->path, $url->toString()); + } + + /** + * Tests the toArray() method. + * + * @depends testCreateFromPath + * + * @covers ::toArray() + */ + public function testToArray(Url $url) { + $expected = array( + 'route_name' => '', + 'route_parameters' => array(), + 'options' => array(), + ); + $this->assertSame($expected, $url->toArray()); + } + + /** + * Tests the getRouteName() method. + * + * @depends testCreateFromPath + * + * @covers ::getRouteName() + */ + public function testGetRouteName(Url $url) { + $this->assertSame('', $url->getRouteName()); + } + + /** + * Tests the getRouteParameters() method. + * + * @depends testCreateFromPath + * + * @covers ::getRouteParameters() + */ + public function testGetRouteParameters(Url $url) { + $this->assertSame(array(), $url->getRouteParameters()); + } + + /** + * Tests the getInternalPath() method. + * + * @depends testCreateFromPath + * + * @covers ::getInternalPath() + * + * @expectedException \Exception + */ + public function testGetInternalPath(Url $url) { + $this->assertNull($url->getInternalPath()); + } + + /** + * Tests the getOptions() method. + * + * @depends testCreateFromPath + * + * @covers ::getOptions() + */ + public function testGetOptions(Url $url) { + $this->assertInternalType('array', $url->getOptions()); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php index 0b52a75..4537b6e 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php @@ -7,8 +7,10 @@ namespace Drupal\Tests\Core\Form { +use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Form\FormInterface; +use Drupal\Core\Url; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -32,6 +34,17 @@ public static function getInfo() { } /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + + $container = new ContainerBuilder(); + $container->set('url_generator', $this->urlGenerator); + \Drupal::setContainer($container); + } + + /** * Tests the getFormId() method with a string based form ID. */ public function testGetFormIdWithString() { @@ -225,6 +238,7 @@ public function providerTestRedirectWithRouteWithResult() { return array( array(array('redirect_route' => array('route_name' => 'test_route_a')), 'test-route'), array(array('redirect_route' => array('route_name' => 'test_route_b', 'route_parameters' => array('key' => 'value'))), 'test-route/value'), + array(array('redirect_route' => new Url('test_route_b', array('key' => 'value'))), 'test-route/value'), ); } diff --git a/core/tests/Drupal/Tests/Core/Routing/TestRouterInterface.php b/core/tests/Drupal/Tests/Core/Routing/TestRouterInterface.php new file mode 100644 index 0000000..6c2c21a --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Routing/TestRouterInterface.php @@ -0,0 +1,17 @@ + 'UrlMatcher', - 'description' => 'Confirm that the UrlMatcher is functioning properly.', - 'group' => 'Routing', - ); - } - - /** - * {@inheritdoc} - */ - protected function setUp() { - $this->matcher = new UrlMatcher(); - } - - /** - * Tests the findRouteNameParameters method. - * - * @see \Drupal\Core\Routing\UrlMatcher::findRouteNameParameters() - */ - public function testFindRouteNameParameters() { - $router = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface'); - $container = new ContainerBuilder(); - $container->set('router', $router); - \Drupal::setContainer($container); - - $router->expects($this->at(0)) - ->method('matchRequest') - ->will($this->returnValue(array( - RouteObjectInterface::ROUTE_NAME => 'view.frontpage.page_1', - '_raw_variables' => new ParameterBag(), - ))); - $router->expects($this->at(1)) - ->method('matchRequest') - ->will($this->returnValue(array( - RouteObjectInterface::ROUTE_NAME => 'node_view', - '_raw_variables' => new ParameterBag(array('node' => '1')), - ))); - $router->expects($this->at(2)) - ->method('matchRequest') - ->will($this->returnValue(array( - RouteObjectInterface::ROUTE_NAME => 'node_edit', - '_raw_variables' => new ParameterBag(array('node' => '2')), - ))); - $router->expects($this->at(3)) - ->method('matchRequest') - ->will($this->throwException(new ResourceNotFoundException())); - - $this->assertEquals(array('view.frontpage.page_1', array()), $this->matcher->findRouteNameParameters('node')); - $this->assertEquals(array('node_view', array('node' => '1')), $this->matcher->findRouteNameParameters('node/1')); - $this->assertEquals(array('node_edit', array('node' => '2')), $this->matcher->findRouteNameParameters('node/2/edit')); - - $this->assertEquals(array(), $this->matcher->findRouteNameParameters('non-existing')); - } - -} diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php new file mode 100644 index 0000000..dd786dc --- /dev/null +++ b/core/tests/Drupal/Tests/Core/UrlTest.php @@ -0,0 +1,290 @@ + 'Url object (internal)', + 'description' => 'Tests the \Drupal\Core\Url class.', + 'group' => 'Routing', + ); + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $map = array(); + $map[] = array('view.frontpage.page_1', array(), array(), '/node'); + $map[] = array('node_view', array('node' => '1'), array(), '/node/1'); + $map[] = array('node_edit', array('node' => '2'), array(), '/node/2/edit'); + $this->map = $map; + + $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface'); + $this->urlGenerator->expects($this->any()) + ->method('generateFromRoute') + ->will($this->returnValueMap($this->map)); + + $this->router = $this->getMock('Drupal\Tests\Core\Routing\TestRouterInterface'); + $container = new ContainerBuilder(); + $container->set('router', $this->router); + $container->set('url_generator', $this->urlGenerator); + \Drupal::setContainer($container); + } + + /** + * Tests the createFromPath method. + * + * @covers ::createFromPath() + */ + public function testCreateFromPath() { + $this->router->expects($this->any()) + ->method('match') + ->will($this->returnValueMap(array( + array('/node', array( + RouteObjectInterface::ROUTE_NAME => 'view.frontpage.page_1', + '_raw_variables' => new ParameterBag(), + )), + array('/node/1', array( + RouteObjectInterface::ROUTE_NAME => 'node_view', + '_raw_variables' => new ParameterBag(array('node' => '1')), + )), + array('/node/2/edit', array( + RouteObjectInterface::ROUTE_NAME => 'node_edit', + '_raw_variables' => new ParameterBag(array('node' => '2')), + )), + ))); + + $urls = array(); + foreach ($this->map as $index => $values) { + $path = trim(array_pop($values), '/'); + $url = Url::createFromPath($path); + $this->assertSame($values, array_values($url->toArray())); + $urls[$index] = $url; + } + return $urls; + } + + /** + * Tests the createFromPath method with the special path. + * + * @covers ::createFromPath() + */ + public function testCreateFromPathFront() { + $url = Url::createFromPath(''); + $this->assertSame('', $url->getRouteName()); + } + + /** + * Tests that an invalid path will thrown an exception. + * + * @covers ::createFromPath() + * + * @expectedException \Drupal\Core\Routing\MatchingRouteNotFoundException + * @expectedExceptionMessage No matching route could be found for the path "non-existent" + */ + public function testCreateFromPathInvalid() { + $this->router->expects($this->once()) + ->method('match') + ->with('/non-existent') + ->will($this->throwException(new ResourceNotFoundException())); + + $this->assertNull(Url::createFromPath('non-existent')); + } + + /** + * Tests the createFromRequest method. + * + * @covers ::createFromRequest() + */ + public function testCreateFromRequest() { + $attributes = array( + '_raw_variables' => new ParameterBag(array( + 'color' => 'chartreuse', + )), + RouteObjectInterface::ROUTE_NAME => 'the_route_name', + ); + $request = new Request(array(), array(), $attributes); + + $this->router->expects($this->once()) + ->method('matchRequest') + ->with($request) + ->will($this->returnValue($attributes)); + + $url = Url::createFromRequest($request); + $expected = new Url('the_route_name', array('color' => 'chartreuse')); + $this->assertEquals($expected, $url); + } + + /** + * Tests that an invalid request will thrown an exception. + * + * @covers ::createFromRequest() + * + * @expectedException \Drupal\Core\Routing\MatchingRouteNotFoundException + * @expectedExceptionMessage No matching route could be found for the request: request_as_a_string + */ + public function testCreateFromRequestInvalid() { + // Mock the request in order to override the __toString() method. + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + $request->expects($this->once()) + ->method('__toString') + ->will($this->returnValue('request_as_a_string')); + + $this->router->expects($this->once()) + ->method('matchRequest') + ->with($request) + ->will($this->throwException(new ResourceNotFoundException())); + + $this->assertNull(Url::createFromRequest($request)); + } + + /** + * Tests the isExternal() method. + * + * @depends testCreateFromPath + * + * @covers ::isExternal() + */ + public function testIsExternal($urls) { + foreach ($urls as $url) { + $this->assertFalse($url->isExternal()); + } + } + + /** + * Tests the toString() method. + * + * @param \Drupal\Core\Url[] $urls + * An array of Url objects. + * + * @depends testCreateFromPath + * + * @covers ::toString() + */ + public function testToString($urls) { + foreach ($urls as $index => $url) { + $path = array_pop($this->map[$index]); + $this->assertSame($path, $url->toString()); + } + } + + /** + * Tests the toArray() method. + * + * @param \Drupal\Core\Url[] $urls + * An array of Url objects. + * + * @depends testCreateFromPath + * + * @covers ::toArray() + */ + public function testToArray($urls) { + foreach ($urls as $index => $url) { + $expected = array( + 'route_name' => $this->map[$index][0], + 'route_parameters' => $this->map[$index][1], + 'options' => $this->map[$index][2], + ); + $this->assertSame($expected, $url->toArray()); + } + } + + /** + * Tests the getRouteName() method. + * + * @param \Drupal\Core\Url[] $urls + * An array of Url objects. + * + * @depends testCreateFromPath + * + * @covers ::getRouteName() + */ + public function testGetRouteName($urls) { + foreach ($urls as $index => $url) { + $this->assertSame($this->map[$index][0], $url->getRouteName()); + } + } + + /** + * Tests the getRouteParameters() method. + * + * @param \Drupal\Core\Url[] $urls + * An array of Url objects. + * + * @depends testCreateFromPath + * + * @covers ::getRouteParameters() + */ + public function testGetRouteParameters($urls) { + foreach ($urls as $index => $url) { + $this->assertSame($this->map[$index][1], $url->getRouteParameters()); + } + } + + /** + * Tests the getOptions() method. + * + * @param \Drupal\Core\Url[] $urls + * An array of Url objects. + * + * @depends testCreateFromPath + * + * @covers ::getOptions() + */ + public function testGetOptions($urls) { + foreach ($urls as $index => $url) { + $this->assertSame($this->map[$index][2], $url->getOptions()); + } + } + +} diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php index 9b80f30..07edbc7 100644 --- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php @@ -8,6 +8,7 @@ namespace Drupal\Tests\Core\Utility { use Drupal\Core\Language\Language; +use Drupal\Core\Url; use Drupal\Core\Utility\LinkGenerator; use Drupal\Tests\UnitTestCase; @@ -15,6 +16,8 @@ * Tests the link generator. * * @see \Drupal\Core\Utility\LinkGenerator + * + * @coversDefaultClass \Drupal\Core\Utility\LinkGenerator */ class LinkGeneratorTest extends UnitTestCase { @@ -54,6 +57,7 @@ class LinkGeneratorTest extends UnitTestCase { 'html' => FALSE, 'language' => NULL, 'set_active_class' => FALSE, + 'absolute' => FALSE, ); /** @@ -125,6 +129,34 @@ public function testGenerateHrefs($route_name, array $parameters, $absolute, $ur } /** + * Tests the generateFromUrl() method. + * + * @covers ::generateFromUrl() + */ + public function testGenerateFromUrl() { + $this->urlGenerator->expects($this->once()) + ->method('generateFromRoute') + ->with('test_route_1', array(), array('fragment' => 'the-fragment') + $this->defaultOptions) + ->will($this->returnValue('/test-route-1#the-fragment')); + + $this->moduleHandler->expects($this->once()) + ->method('alter') + ->with('link', $this->isType('array')); + + $url = new Url('test_route_1', array(), array('fragment' => 'the-fragment')); + $url->setUrlGenerator($this->urlGenerator); + + $result = $this->linkGenerator->generateFromUrl('Test', $url); + $this->assertTag(array( + 'tag' => 'a', + 'attributes' => array( + 'href' => '/test-route-1#the-fragment', + ), + 'content' => 'Test', + ), $result); + } + + /** * Tests the link method with additional attributes. * * @see \Drupal\Core\Utility\LinkGenerator::generate()