You are here

menu_link_attributes.module in Menu Link Attributes 8

Core file of the Menu Link Attributes module.

File

menu_link_attributes.module
View source
<?php

/**
 * @file
 * Core file of the Menu Link Attributes module.
 */
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Url;

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function menu_link_attributes_form_menu_link_content_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
  $account = \Drupal::currentUser();
  $attributes = \Drupal::config('menu_link_attributes.config')
    ->get('attributes') ?: [];
  $menu_link = $form_state
    ->getFormObject()
    ->getEntity();
  $menu_link_options = $menu_link->link ? $menu_link->link
    ->first()->options : [];
  if (!$account
    ->hasPermission('use menu link attributes')) {
    $form_state
      ->set('menu_link_options', $menu_link_options);
    $form['actions']['submit']['#submit'][] = 'menu_link_attributes_menu_link_content_form_preserve';
    return;
  }
  $form['options']['attributes'] = [
    '#type' => 'details',
    '#title' => t('Attributes'),
    '#weight' => -3,
    '#tree' => TRUE,
  ];
  $form['options']['container_attributes'] = [
    '#type' => 'details',
    '#title' => t('Container attributes'),
    '#description' => t('These attributes are applied to the container element (<code>&lt;li&gt;</code>) of the menu link.'),
    '#weight' => -2,
    '#tree' => TRUE,
    '#access' => FALSE,
  ];
  $config_path = Url::fromRoute('menu_link_attributes.config')
    ->toString();
  $referrer_path = parse_url(\Drupal::request()->headers
    ->get('referer'))['path'];
  $coming_from_config = $config_path == $referrer_path;

  // Open <details> element if coming from config page.
  if ($coming_from_config) {
    $form['options']['attributes']['#open'] = TRUE;
  }
  $destination = \Drupal::destination()
    ->getAsArray();
  $config_path = Url::fromRoute('menu_link_attributes.config', [], [
    'query' => $destination,
  ])
    ->toString();
  if ($account
    ->hasPermission('administer menu link attributes')) {
    if (count($attributes)) {
      $form['options']['attributes']['#description'] = '<small>' . t('Manage available attributes <a href="@config">here</a>.', [
        '@config' => $config_path,
      ]) . '</small>';
    }
    else {
      $form['options']['attributes']['help'] = [
        '#markup' => t('Manage available attributes <a href="@config">here</a>.', [
          '@config' => $config_path,
        ]),
      ];
    }
  }
  $autofocus = FALSE;

  // Iterate all defined attributes and create text field for them.
  foreach ($attributes as $attribute => $info) {
    $is_container_attribute = menu_link_attributes_is_container_attribute($attribute);
    $attribute_formatted = $attribute;
    $attributes_key = 'attributes';
    if ($is_container_attribute) {
      $attributes_key = 'container_attributes';
      $attribute_formatted = preg_replace('/^container_/', '', $attribute);
    }

    // Provide default label / description for attributes.
    if (empty($info['label'])) {
      $info['label'] = str_replace('-', ' ', Unicode::ucfirst($attribute_formatted));
    }
    if (empty($info['description'])) {
      $info['description'] = t('Enter value for <code>@attribute</code> attribute.', [
        '@attribute' => $attribute_formatted,
      ]);
    }

    // Determine type based on options field.
    if (empty($info['type'])) {
      $type = !empty($info['options']) ? 'select' : 'textfield';
    }
    else {
      $type = $info['type'];
    }
    $form['options']['attributes'][$attribute] = [
      '#type' => $type,
      '#title' => $info['label'],
      '#description' => $info['description'],
      '#default_value' => isset($menu_link_options[$attributes_key][$attribute_formatted]) ? $menu_link_options[$attributes_key][$attribute_formatted] : (isset($info['default_value']) ? $info['default_value'] : ''),
    ];

    // Fill options if select list.
    if ($type == 'select') {
      $form['options']['attributes'][$attribute]['#empty_option'] = t('- Select -');
      $form['options']['attributes'][$attribute]['#options'] = $info['options'];
    }

    // Fill options if type is "managed_file".
    if ($type === 'managed_file' && !empty($info['upload_location'])) {
      $form['options']['attributes'][$attribute]['#upload_location'] = $info['upload_location'];
    }

    // Add "autofocus" attribute for first attribute input field
    // if coming from config page.
    if ($coming_from_config && !$autofocus) {
      $form['options']['attributes'][$attribute]['#attributes'] = [
        'autofocus' => 'autofocus',
      ];
      $autofocus = TRUE;
    }
  }
  $form['actions']['submit']['#submit'][] = 'menu_link_attributes_menu_link_content_form_submit';
}

/**
 * Submit function for menu add / edit form.
 */
function menu_link_attributes_menu_link_content_form_submit($form, FormStateInterface $form_state) {

  /** @var \Drupal\menu_link_content\Form\MenuLinkContentForm $form_object */
  $form_object = $form_state
    ->getFormObject();
  $menu_link = $form_object
    ->getEntity();
  if (!$menu_link->link || $menu_link->link
    ->isEmpty()) {
    return;
  }
  $menu_link_options = $menu_link->link
    ->first()->options ?: [];
  $menu_link_attributes = [
    'attributes' => $form_state
      ->getValue('attributes'),
  ];
  foreach ($menu_link_attributes['attributes'] as $attribute_name => &$attribute_value) {

    // Make "class" attributes an array.
    if (in_array($attribute_name, [
      'class',
      'container_class',
    ]) && !is_array($attribute_value)) {
      $attribute_value = [
        (string) $attribute_value,
      ];
    }
    $is_container_attribute = menu_link_attributes_is_container_attribute($attribute_name);
    if ($is_container_attribute) {
      unset($menu_link_attributes['attributes'][$attribute_name]);
      $attribute_name = preg_replace('/^container_/', '', $attribute_name);
      $menu_link_attributes['container_attributes'][$attribute_name] = $attribute_value;
    }
  }

  // Remove empty attribute values.
  foreach ($menu_link_attributes as $attribute_group => $grouped_attributes) {
    foreach ($grouped_attributes as $group_attribute_name => $group_attribute_value) {
      if (is_array($group_attribute_value)) {
        $menu_link_attributes[$attribute_group][$group_attribute_name] = array_filter($group_attribute_value);
      }
      elseif (mb_strlen($group_attribute_value) === 0) {
        unset($menu_link_attributes[$attribute_group][$group_attribute_name]);
      }
    }
  }
  $menu_link_attributes = array_filter($menu_link_attributes);
  $menu_link_options = array_merge_recursive($menu_link_options, $menu_link_attributes);
  if (count($menu_link_attributes)) {
    $menu_link->link
      ->first()->options = $menu_link_options;
    $menu_link
      ->save();
  }
}

/**
 * Preserve menu link attributes on form submit.
 *
 * @param array $form
 *   An associative array containing the structure of the form.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The current state of the form.
 *
 * @throws \Drupal\Core\Entity\EntityStorageException
 */
function menu_link_attributes_menu_link_content_form_preserve(array $form, FormStateInterface $form_state) {

  /** @var \Drupal\menu_link_content\Form\MenuLinkContentForm $form_object */
  $form_object = $form_state
    ->getFormObject();
  $menu_link = $form_object
    ->getEntity();
  if (!$menu_link->link || $menu_link->link
    ->isEmpty()) {
    return;
  }
  $menu_link_options = $form_state
    ->get('menu_link_options');
  if (!empty($menu_link_options)) {
    $menu_link->link->options = $menu_link_options;
    $menu_link
      ->save();
  }
}

/**
 * Helper function to determine if attribute is a container attribute.
 *
 * @param string $attribute_name
 *   The name of the attribute.
 *
 * @return bool
 *   TRUE if the attribute is a container attribute.
 */
function menu_link_attributes_is_container_attribute($attribute_name) {
  $attributes = \Drupal::config('menu_link_attributes.config')
    ->get('attributes') ?: [];
  $info = $attributes[$attribute_name];
  $possible_container_attribute = strpos($attribute_name, 'container_') === 0;
  $disabled_container = isset($info['container']) && !$info['container'];
  $possible_container_attribute = $possible_container_attribute && !$disabled_container;
  $container_attribute_enabled = isset($info['container']) && $info['container'];
  $is_container_attribute = $possible_container_attribute || $container_attribute_enabled;
  return $is_container_attribute;
}

/**
 * Implements template_preprocess_menu().
 */
function menu_link_attributes_preprocess_menu(&$variables) {
  _menu_link_attributes_preprocess_menu_items($variables['items']);
}

/**
 * Helper function to recursively set list item attributes.
 */
function _menu_link_attributes_preprocess_menu_items(&$items) {
  foreach ($items as $item) {

    /** @var \Drupal\Core\Menu\MenuLinkDefault $menu_link */
    $menu_link = isset($item['original_link']) ? $item['original_link'] : NULL;
    $options = !empty($menu_link) ? $menu_link
      ->getOptions() : NULL;

    // Apply container attributes on <li> element.
    if ($options && isset($options['container_attributes'])) {
      foreach ($options['container_attributes'] as $attribute => $value) {
        $item['attributes']
          ->setAttribute($attribute, $value);
      }
    }
    if (!empty($item['below'])) {
      _menu_link_attributes_preprocess_menu_items($item['below']);
    }
  }
}

Functions

Namesort descending Description
menu_link_attributes_form_menu_link_content_form_alter Implements hook_form_BASE_FORM_ID_alter().
menu_link_attributes_is_container_attribute Helper function to determine if attribute is a container attribute.
menu_link_attributes_menu_link_content_form_preserve Preserve menu link attributes on form submit.
menu_link_attributes_menu_link_content_form_submit Submit function for menu add / edit form.
menu_link_attributes_preprocess_menu Implements template_preprocess_menu().
_menu_link_attributes_preprocess_menu_items Helper function to recursively set list item attributes.