diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 9a0d713..4d8e238 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -19,6 +19,7 @@ use Drupal\Core\Utility\ThemeRegistry; use Drupal\Core\Theme\ThemeSettings; use Drupal\Component\Utility\NestedArray; +use Drupal\Component\Utility\MapArray; /** * @defgroup content_flags Content markers @@ -1645,8 +1646,6 @@ function template_preprocess_status_messages(&$variables) { * http://www.w3.org/TR/WCAG-TECHS/H42.html for more information. */ function theme_links($variables) { - $language_url = language(Language::TYPE_URL); - $links = $variables['links']; $attributes = $variables['attributes']; $heading = $variables['heading']; @@ -1678,9 +1677,17 @@ function theme_links($variables) { $num_links = count($links); $i = 0; + $active = \Drupal::linkGenerator()->getActive(); + foreach ($links as $key => $link) { $i++; + $link += array( + 'href' => NULL, + 'route_name' => NULL, + 'route_parameters' => NULL, + ); + $class = array(); // Use the array key as class name. $class[] = drupal_html_class($key); @@ -1693,31 +1700,47 @@ function theme_links($variables) { $class[] = 'last'; } - // Handle links. - if (isset($link['href'])) { + $link_element = array( + '#type' => 'link', + '#title' => $link['title'], + '#options' => array_diff_key($link, MapArray::copyValuesToKeys(array('title', 'href', 'route_name', 'route_parameters'))), + '#href' => $link['href'], + '#route_name' => $link['route_name'], + '#route_parameters' => $link['route_parameters'], + ); + + // @todo Reconcile Views usage of 'ajax' as a boolean with the rest of + // core's usage of it as an array. + if (isset($link['ajax']) && is_array($link['ajax'])) { + $link_element['#ajax'] = $link['ajax']; + } + + // Handle links and ensure that the active class is added on the LIs. + if (isset($link['route_name'])) { + $variables = array( + 'options' => array(), + ); + if (!empty($link['language'])) { + $variables['options']['language'] = $link['language']; + } + + if (($link['route_name'] == $active['route_name']) + // The language of an active link is equal to the current language. + && (empty($variables['options']['language']) || ($variables['options']['language']->id == $active['language'])) + && ($link['route_parameters'] == $active['parameters'])) { + $class[] = 'active'; + } + + $item = drupal_render($link_element); + } + elseif (isset($link['href'])) { + $language_url = language(Language::TYPE_URL); $is_current_path = ($link['href'] == current_path() || ($link['href'] == '' && drupal_is_front_page())); $is_current_language = (empty($link['language']) || $link['language']->id == $language_url->id); if ($is_current_path && $is_current_language) { $class[] = 'active'; } - // @todo Reconcile Views usage of 'ajax' as a boolean with the rest of - // core's usage of it as an array. - if (isset($link['ajax']) && is_array($link['ajax'])) { - // To attach Ajax behavior, render a link element, rather than just - // call l(). - $link_element = array( - '#type' => 'link', - '#title' => $link['title'], - '#href' => $link['href'], - '#ajax' => $link['ajax'], - '#options' => array_diff_key($link, drupal_map_assoc(array('title', 'href', 'ajax'))), - ); - $item = drupal_render($link_element); - } - else { - // Pass in $link as $options, they share the same keys. - $item = l($link['title'], $link['href'], $link); - } + $item = drupal_render($link_element); } // Handle title-only text items. else { diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php index 12dc3d6..318297a 100644 --- a/core/lib/Drupal/Core/Utility/LinkGenerator.php +++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php @@ -85,6 +85,20 @@ public function setRequest(Request $request) { } /** + * Returns information for the currently active route. + * + * @return array + * An array of active route information, containing the following keys: + * - route_name: The currently active route_name + * - language: The current language + * - parameters: An array of request parameters and any query string + * parameters. + */ + public function getActive() { + return $this->active; + } + + /** * {@inheritdoc} */ public function generate($text, $route_name, array $parameters = array(), array $options = array()) { @@ -152,4 +166,14 @@ public function generate($text, $route_name, array $parameters = array(), array return '' . $text . ''; } + /** + * {@inheritdoc} + */ + public function isRouteActive($route_name, array $route_parameters, array $variables) { + return ($route_name == $this->active['route_name']) + // The language of an active link is equal to the current language. + && (empty($variables['options']['language']) || ($variables['options']['language']->id == $this->active['language'])) + && ($route_parameters == $this->active['parameters']); + } + } diff --git a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php index cf1b0d8..313c4ab 100644 --- a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php +++ b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php @@ -71,4 +71,19 @@ */ public function generate($text, $route_name, array $parameters = array(), array $options = array()); + /** + * Determines whether a given route is the current active route. + * + * @param string $route_name + * The route name to check whether it is the current active one. + * @param array $route_parameters + * The route parameters belonging to $route_name to check. + * @param array $variables + * A link variables array containing the link options. + * + * @return bool + * Returns TRUE if the route matches the current route, otherwise FALSE. + */ + public function isRouteActive($route_name, array $route_parameters, array $variables); + } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Controller/AggregatorController.php b/core/modules/aggregator/lib/Drupal/aggregator/Controller/AggregatorController.php index 4d5dd9a..932aba1 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Controller/AggregatorController.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Controller/AggregatorController.php @@ -186,19 +186,23 @@ public function adminOverview() { $links = array(); $links['edit'] = array( 'title' => $this->t('Edit'), - 'href' => "admin/config/services/aggregator/edit/feed/$feed->fid", + 'route_name' => 'aggregator.feed_edit', + 'route_parameters' => array('aggregator_feed' => $feed->fid), ); $links['delete'] = array( 'title' => $this->t('Delete'), - 'href' => "admin/config/services/aggregator/delete/feed/$feed->fid", + 'route_name' => 'aggregator.feed_delete', + 'route_parameters' => array('aggregator_feed' => $feed->fid), ); $links['remove'] = array( 'title' => $this->t('Remove items'), - 'href' => "admin/config/services/aggregator/remove/$feed->fid", + 'route_name' => 'aggregator.feed_items_delete', + 'route_parameters' => array('aggregator_feed' => $feed->fid), ); $links['update'] = array( 'title' => $this->t('Update items'), - 'href' => "admin/config/services/aggregator/update/$feed->fid", + 'route_name' => 'aggregator.feed_refresh', + 'route_parameters' => array('aggregator_feed' => $feed->fid), 'query' => array('token' => drupal_get_token("aggregator/update/$feed->fid")), ); $row[] = array( @@ -228,11 +232,13 @@ public function adminOverview() { $links = array(); $links['edit'] = array( 'title' => $this->t('Edit'), - 'href' => "admin/config/services/aggregator/edit/category/$category->cid", + 'route_name' => 'aggregator.category_admin_edit', + 'route_parameters' => array('cid' => $category->cid), ); $links['delete'] = array( 'title' => $this->t('Delete'), - 'href' => "admin/config/services/aggregator/delete/category/$category->cid", + 'route_name' => 'aggregator.category_delete', + 'route_parameters' => array('cid' => $category->cid), ); $row[] = array( 'data' => array( diff --git a/core/modules/ban/lib/Drupal/ban/Form/BanAdmin.php b/core/modules/ban/lib/Drupal/ban/Form/BanAdmin.php index f15f734..edfe0c2 100644 --- a/core/modules/ban/lib/Drupal/ban/Form/BanAdmin.php +++ b/core/modules/ban/lib/Drupal/ban/Form/BanAdmin.php @@ -63,7 +63,8 @@ public function buildForm(array $form, array &$form_state, $default_ip = '') { $links = array(); $links['delete'] = array( 'title' => $this->t('delete'), - 'href' => "admin/config/people/ban/delete/$ip->iid", + 'route_name' => 'ban.delete', + 'route_parameters' => array('ban_id' => $ip->iid), ); $row[] = array( 'data' => array( diff --git a/core/modules/book/book.admin.inc b/core/modules/book/book.admin.inc index 1d7061b..0ffefc5 100644 --- a/core/modules/book/book.admin.inc +++ b/core/modules/book/book.admin.inc @@ -53,12 +53,14 @@ function theme_book_admin_table($variables) { if ($access) { $links['edit'] = array( 'title' => t('Edit'), - 'href' => "node/$nid/edit", + 'route_name' => 'node.page_edit', + 'route_parameters' => array('node' => $nid), 'query' => $destination, ); $links['delete'] = array( 'title' => t('Delete'), - 'href' => "node/$nid/delete", + 'route_name' => 'node.delete_confirm', + 'route_parameters' => array('node' => $nid), 'query' => $destination, ); } diff --git a/core/modules/book/lib/Drupal/book/Controller/BookController.php b/core/modules/book/lib/Drupal/book/Controller/BookController.php index 214a801..b9920d2 100644 --- a/core/modules/book/lib/Drupal/book/Controller/BookController.php +++ b/core/modules/book/lib/Drupal/book/Controller/BookController.php @@ -77,7 +77,8 @@ public function adminOverview() { $links = array(); $links['edit'] = array( 'title' => t('Edit order and titles'), - 'href' => 'admin/structure/book/' . $book['nid'], + 'route_name' => 'book.admin_edit', + 'route_parameters' => array('node' => $book['nid']), ); $row[] = array( 'data' => array( diff --git a/core/modules/comment/comment.admin.inc b/core/modules/comment/comment.admin.inc index f17fa59..6893638 100644 --- a/core/modules/comment/comment.admin.inc +++ b/core/modules/comment/comment.admin.inc @@ -155,13 +155,16 @@ function comment_admin_overview($form, &$form_state, $arg) { $links = array(); $links['edit'] = array( 'title' => t('edit'), - 'href' => 'comment/' . $comment->id() . '/edit', + 'route_name' => 'comment.edit_page', + 'route_parameters' => array('comment' => $comment->id()), 'query' => $destination, ); if (module_invoke('content_translation', 'translate_access', $comment)) { $links['translate'] = array( 'title' => t('translate'), 'href' => 'comment/' . $comment->id() . '/translations', + 'route_name' => 'content_translation.translation_overview_comment', + 'route_parameters' => array('comment' => $comment->id()), 'query' => $destination, ); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/FunctionsTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/FunctionsTest.php index 69d5ac6..d1930c9 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/FunctionsTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/FunctionsTest.php @@ -13,6 +13,14 @@ * Tests for common theme functions. */ class FunctionsTest extends WebTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('router_test'); + public static function getInfo() { return array( 'name' => 'Theme functions', @@ -172,13 +180,19 @@ function testLinks() { 'title' => 'Front page', 'href' => '', ), + 'router-test' => array( + 'title' => 'Test route', + 'route_name' => 'router_test.1', + 'route_parameters' => array(), + ), ); $expected_links = ''; $expected_links .= ''; // Verify that passing a string as heading works. @@ -210,7 +224,8 @@ function testLinks() { $expected_links .= ''; $expected = $expected_heading . $expected_links; $this->assertThemeOutput('links', $variables, $expected); diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php index 6b7334f..a6cdac3 100644 --- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php @@ -333,6 +333,7 @@ public function testGenerateActive() { $request = new Request(array(), array(), array('system_path' => 'test-route-2')); $this->linkGenerator->setRequest($request); $result = $this->linkGenerator->generate('Test', 'test_route_1'); + $this->assertFalse($this->linkGenerator->isRouteActive('test_route_1', array(), array())); $this->assertNotTag(array( 'tag' => 'a', 'attributes' => array('class' => 'active'), @@ -346,6 +347,7 @@ public function testGenerateActive() { $request->attributes->set('_raw_variables', $raw_variables); $this->linkGenerator->setRequest($request); $result = $this->linkGenerator->generate('Test', 'test_route_1'); + $this->assertTrue($this->linkGenerator->isRouteActive('test_route_1', array(), array())); $this->assertTag(array( 'tag' => 'a', 'attributes' => array('class' => 'active'), @@ -353,6 +355,7 @@ public function testGenerateActive() { // Render a link with the same path and language as the current path. $result = $this->linkGenerator->generate('Test', 'test_route_1'); + $this->assertTrue($this->linkGenerator->isRouteActive('test_route_1', array(), array())); $this->assertTag(array( 'tag' => 'a', 'attributes' => array('class' => 'active'), @@ -366,6 +369,7 @@ public function testGenerateActive() { array(), array('language' => new Language(array('id' => 'de'))) ); + $this->assertFalse($this->linkGenerator->isRouteActive('test_route_1', array(), array('options' => array('language' => new Language(array('id' => 'de')))))); $this->assertNotTag(array( 'tag' => 'a', 'attributes' => array('class' => 'active'), @@ -382,6 +386,7 @@ public function testGenerateActive() { array(), array('query' => array('value' => 'example_1') )); + $this->assertTrue($this->linkGenerator->isRouteActive('test_route_3', array('value' => 'example_1'), array())); $this->assertTag(array( 'tag' => 'a', 'attributes' => array('class' => 'active'), @@ -395,6 +400,7 @@ public function testGenerateActive() { array(), array('query' => array('value' => 'example_2')) ); + $this->assertFalse($this->linkGenerator->isRouteActive('test_route_3', array('value' => 'example_2'), array())); $this->assertNotTag(array( 'tag' => 'a', 'attributes' => array('class' => 'active'), @@ -410,6 +416,7 @@ public function testGenerateActive() { array('object' => '1'), array('query' => array('value' => 'example_1')) ); + $this->assertTrue($this->linkGenerator->isRouteActive('test_route_4', array('object' => '1', 'value' => 'example_1'), array())); $this->assertTag(array( 'tag' => 'a', 'attributes' => array('class' => 'active'),