Same name and namespace in other branches
  1. 8.9.x core/includes/theme.inc \template_preprocess_links()
  2. 9 core/includes/theme.inc \template_preprocess_links()

Prepares variables for links templates.

Default template: links.html.twig.

Unfortunately links templates duplicate the "active" class handling of l() and LinkGenerator::generate() because it needs to be able to set the "active" class not on the links themselves (<a> tags), but on the list items (<li> tags) that contain the links. This is necessary for CSS to be able to style list items differently when the link is active, since CSS does not yet allow one to style list items only if it contains a certain element with a certain class. I.e. we cannot yet convert this jQuery selector to a CSS selector: jQuery('li:has("a.is-active")')

Parameters

array $variables: An associative array containing:

  • links: An array of links to be themed. Each link itself is an array, with the following elements:

    • title: The link text.
    • url: (optional) The \Drupal\Core\Url object to link to. If the 'url' element is supplied, the 'title' and 'url' are used to generate a link through \Drupal::linkGenerator()->generate(). All data from the link array other than 'title' and 'url' are added as #options on the URL object. See \Drupal\Core\Url::fromUri() for details on the options. If no 'url' is supplied, the 'title' is printed as plain text.
    • attributes: (optional) Attributes for the anchor, or for the <span> tag used in its place if no 'url' is supplied. If element 'class' is included, it must be an array of one or more class names.
  • attributes: A keyed array of attributes for the <ul> containing the list of links.
  • set_active_class: (optional) Whether each link should compare the route_name + route_parameters or URL (path), language, and query options to the current URL, to determine whether the link is "active". If so, attributes will be added to the HTML elements for both the link and the list item that contains it, which will result in an "is-active" class being added to both. The class is added via JavaScript for authenticated users (in the active-link library), and via PHP for anonymous users (in the \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter class).
  • heading: (optional) A heading to precede the links. May be an associative array or a string. If it's an array, it can have the following elements:

    • text: The heading text.
    • level: The heading level (e.g. 'h2', 'h3').
    • attributes: (optional) An array of the CSS attributes for the heading.

    When using a string it will be used as the text of the heading and the level will default to 'h2'. Headings should be used on navigation menus and any list of links that consistently appears on multiple pages. To make the heading invisible use the 'visually-hidden' CSS class. Do not use 'display:none', which removes it from screen readers and assistive technology. Headings allow screen reader and keyboard only users to navigate to or skip the links. See http://juicystudio.com/article/screen-readers-display-none.php and http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.

See also

\Drupal\Core\Utility\LinkGenerator

\Drupal\Core\Utility\LinkGenerator::generate()

system_page_attachments()

File

core/includes/theme.inc, line 649
The theme system, which controls the output of Drupal.

Code

function template_preprocess_links(&$variables) {
  $links = $variables['links'];
  $heading =& $variables['heading'];
  if (!empty($links)) {

    // Prepend the heading to the list, if any.
    if (!empty($heading)) {

      // Convert a string heading into an array, using a <h2> tag by default.
      if (is_string($heading)) {
        $heading = [
          'text' => $heading,
        ];
      }

      // Merge in default array properties into $heading.
      $heading += [
        'level' => 'h2',
        'attributes' => [],
      ];

      // Convert the attributes array into an Attribute object.
      $heading['attributes'] = new Attribute($heading['attributes']);
    }
    $variables['links'] = [];
    foreach ($links as $key => $link) {
      $item = [];
      $link += [
        'ajax' => NULL,
        'url' => NULL,
      ];
      $li_attributes = [];
      $keys = [
        'title',
        'url',
      ];
      $link_element = [
        '#type' => 'link',
        '#title' => $link['title'],
        '#options' => array_diff_key($link, array_combine($keys, $keys)),
        '#url' => $link['url'],
        '#ajax' => $link['ajax'],
      ];

      // Handle links and ensure that the active class is added on the LIs, but
      // only if the 'set_active_class' option is not empty. Links templates
      // duplicate the "is-active" class handling of l() and
      // LinkGenerator::generate() because they need to be able to set the
      // "is-active" class not on the links themselves (<a> tags), but on the
      // list items (<li> tags) that contain the links. This is necessary for
      // CSS to be able to style list items differently when the link is active,
      // since CSS does not yet allow one to style list items only if they
      // contain a certain element with a certain class. That is, we cannot yet
      // convert this jQuery selector to a CSS selector:
      // jQuery('li:has("a.is-active")')
      if (isset($link['url'])) {
        if (!empty($variables['set_active_class'])) {

          // Also enable set_active_class for the contained link.
          $link_element['#options']['set_active_class'] = TRUE;
          if (!empty($link['language'])) {
            $li_attributes['hreflang'] = $link['language']
              ->getId();
          }

          // Add a "data-drupal-link-query" attribute to let the
          // drupal.active-link library know the query in a standardized manner.
          // Only add the data- attribute. The "is-active" class will be
          // calculated using JavaScript, to prevent breaking the render cache.
          if (!empty($link['query'])) {
            $query = $link['query'];
            ksort($query);
            $li_attributes['data-drupal-link-query'] = Json::encode($query);
          }

          /** @var \Drupal\Core\Url $url */
          $url = $link['url'];
          if ($url
            ->isRouted()) {

            // Add a "data-drupal-link-system-path" attribute to let the
            // drupal.active-link library know the path in a standardized
            // manner. Only add the data- attribute. The "is-active" class will
            // be calculated using JavaScript, to prevent breaking the render
            // cache.
            $system_path = $url
              ->getInternalPath();

            // @todo System path is deprecated - use the route name and parameters.
            // Special case for the front page.
            $li_attributes['data-drupal-link-system-path'] = $system_path == '' ? '<front>' : $system_path;
          }
        }
        $item['link'] = $link_element;
      }

      // Handle title-only text items.
      $item['text'] = $link['title'];
      if (isset($link['attributes'])) {
        $item['text_attributes'] = new Attribute($link['attributes']);
      }

      // Handle list item attributes.
      $item['attributes'] = new Attribute($li_attributes);

      // Add the item to the list of links.
      $variables['links'][$key] = $item;
    }
  }
}