You are here

menu_manipulator.module in Menu Manipulator 8

File

menu_manipulator.module
View source
<?php

/**
 * @file
 * Contains menu_manipulator.module.
 */
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Routing\CurrentRouteMatch;

/**
 * Implements hook_help().
 *
 * @inheritdoc
 */
function menu_manipulator_help($route_name, CurrentRouteMatch $route_match) {
  if ($route_name == 'help.page.menu_manipulator') {
    $text = file_get_contents(dirname(__FILE__) . '/README.md');
    if (!\Drupal::moduleHandler()
      ->moduleExists('markdown')) {
      return '<pre>' . $text . '</pre>';
    }
    else {

      // Use the Markdown filter to render the README.
      $filter_manager = \Drupal::service('plugin.manager.filter');
      $settings = \Drupal::configFactory()
        ->get('markdown.settings')
        ->getRawData();
      $config = [
        'settings' => $settings,
      ];
      $filter = $filter_manager
        ->createInstance('markdown', $config);
      return $filter
        ->process($text, 'en');
    }
  }
  return NULL;
}

/**
 * Get a menu tree filtered by the current language.
 *
 * Based on our custom MenuTreeManipulators services.
 * This is pretty useful as of now (8.4.x) as Drupal doesn't
 * provide a way to filer MenuLinkContent entity by their language.
 *
 * @param string $menu_name
 *   The menu machine name.
 *
 * @return array
 *   The filtered MenuTree renderable array.
 *
 * @code
 * // Implements theme_preprocess_menu().
 * function yourtheme_preprocess_menu(&$variables, $hook) {
 *   if (isset($variables['menu_name']) && $menu_name = $variables['menu_name']) {
 *     $moduleHandler = \Drupal::service('module_handler');
 *     if ($moduleHandler->moduleExists('menu_manipulator')) {
 *       $menu_tree_translated = menu_manipulator_get_multilingual_menu($menu_name);
 *       $variables['items'] = isset($menu_tree_translated['#items']) ? $menu_tree_translated['#items'] : [];
 *     }
 *   }
 * }
 * @endcode
 */
function menu_manipulator_get_multilingual_menu(string $menu_name) {
  $menu_tree = \Drupal::menuTree();
  $manipulators = [
    [
      'callable' => 'menu.default_tree_manipulators:checkAccess',
    ],
    [
      'callable' => 'menu.default_tree_manipulators:generateIndexAndSort',
    ],
    [
      'callable' => 'menu_manipulator.menu_tree_manipulators:filterByCurrentLanguage',
    ],
  ];
  if ($menu_name == 'admin') {

    // Specific manipulation for the "Admin Toolbar" menu.
    // See admin_toolbar_prerender_toolbar_administration_tray()
    // in admin_toolbar.module.
    $parameters = new MenuTreeParameters();
    $parameters
      ->setRoot('system.admin')
      ->excludeRoot()
      ->setMaxDepth(4)
      ->onlyEnabledLinks();
  }
  else {

    // Default method to retrieve menu items.
    // See MenuLinkTreeInteface::getCurrentRouteMenuTreeParameters().
    $parameters = $menu_tree
      ->getCurrentRouteMenuTreeParameters($menu_name);
  }

  // Manipulate the menu tree to filter by current language.
  $menu = $menu_tree
    ->load($menu_name, $parameters);
  $menu = $menu_tree
    ->transform($menu, $manipulators);
  return $menu_tree
    ->build($menu);
}

/**
 * Implements theme_preprocess_menu().
 */
function menu_manipulator_preprocess_menu(&$variables, $hook) {
  $config = \Drupal::config('menu_manipulator.settings');
  if ($config
    ->get('preprocess_menus_title')) {

    // Populate menu title variable for Twig.
    if (isset($variables['menu_name'])) {
      $menu = \Drupal::service('entity_type.manager')
        ->getStorage('menu')
        ->load($variables['menu_name']);
      if (NULL != $menu) {
        $variables['menu_title'] = [
          '#markup' => \Drupal::translation()
            ->translate('@label', [
            '@label' => $menu
              ->label(),
          ]),
        ];
      }
    }
  }
  if ($config
    ->get('preprocess_menus_language')) {
    if (isset($variables['menu_name'])) {

      // Automatically filter menu by language.
      $do_filter = TRUE;

      // Check if this menu has to be filtered.
      if (is_array($config
        ->get('preprocess_menus_language_list'))) {
        $do_filter = array_intersect([
          $variables['menu_name'],
        ], $config
          ->get('preprocess_menus_language_list'));
      }

      // Actually filter menu by language.
      if ($do_filter) {
        $menu_tree_translated = menu_manipulator_get_multilingual_menu($variables['menu_name']);
        $variables['items'] = isset($menu_tree_translated['#items']) ? $menu_tree_translated['#items'] : [];
      }
    }
  }
  if ($config
    ->get('preprocess_menus_icon')) {
    if (isset($variables['menu_name'])) {

      // Do not process icons variable by default.
      $do_filter = FALSE;

      // Check if this menu is selected has been selected by user.
      if (is_array($config
        ->get('preprocess_menus_icon_list'))) {
        $do_filter = array_intersect([
          $variables['menu_name'],
        ], $config
          ->get('preprocess_menus_icon_list'));
      }

      // Filter menu to display icons.
      if ($do_filter) {
        foreach ($variables['items'] as $key => $item) {
          $item_options = $item['original_link']
            ->getOptions();
          if (isset($item_options['icon']) && ($icon = $item_options['icon'])) {
            $variables['items'][$key]['icon'] = $icon;
          }
        }
      }
    }
  }
}

/**
 * Implements hook_form_BASE_FORM_ID_alter() for the Menu Link Content form.
 *
 * @todo make $icon_list administrable.
 */
function menu_manipulator_form_menu_link_content_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
  $menu_link = $form_state
    ->getFormObject()
    ->getEntity();
  $menu_link_options = $menu_link->link ? $menu_link->link
    ->first()->options : [];
  $config = \Drupal::config('menu_manipulator.settings');

  // Hide icon options by default.
  $do_filter = FALSE;

  // Check if this menu is selected has been selected by user.
  if (is_array($config
    ->get('preprocess_menus_icon_list'))) {
    $do_filter = array_intersect([
      $menu_link
        ->getMenuName(),
    ], $config
      ->get('preprocess_menus_icon_list'));
  }

  // Stop now if not allowing Icons processing for this menu.
  if (!$do_filter) {
    return;
  }
  $icon_list_value = $config
    ->get('menu_link_icon_list') ?: '';
  $icon_list = menu_manipulator_prepare_associative_list($icon_list_value);
  $form['menu_link_icon'] = [
    '#type' => 'select',
    '#title' => t('Icon'),
    '#description' => t('Select icon for this menu link.'),
    '#weight' => -2,
    '#tree' => TRUE,
    '#access' => \Drupal::currentUser()
      ->hasPermission('use menu link icon'),
    '#empty_option' => t('- Select -'),
    '#options' => $icon_list,
    '#default_value' => isset($menu_link_options['icon']) ? $menu_link_options['icon'] : NULL,
  ];
  $form['actions']['submit']['#submit'][] = 'menu_manipulator_menu_link_content_form_submit';
}

/**
 * Submit function for menu add / edit form.
 */
function menu_manipulator_menu_link_content_form_submit($form, FormStateInterface $form_state) {
  $menu_link = $form_state
    ->getFormObject()
    ->getEntity();
  if (!$menu_link->link) {
    return;
  }
  $menu_link_options = $menu_link->link
    ->first()->options ?: [];

  // Attach extra options to Menu Link Content entity.
  $menu_link_options = $menu_link->link
    ->first()->options ?: [];
  $menu_link_options['icon'] = $form_state
    ->getValue('menu_link_icon');
  $menu_link->link
    ->first()->options = $menu_link_options;
  $menu_link
    ->save();
}

/**
 * Helper function to filter the associative key|label configuration.
 *
 * @param string $string
 *   The original value.
 *
 * @return array
 *   An associative array of values.
 *
 * @see Drupal\options\Plugin\Field\FieldType\ListItemBase::extractedAllowedValues();
 */
function menu_manipulator_prepare_associative_list($string) {
  $values = [];
  $list = explode("\n", $string);
  $list = array_map('trim', $list);
  $list = array_filter($list, 'strlen');
  $generated_keys = $explicit_keys = FALSE;
  foreach ($list as $text) {

    // Check for an explicit key.
    $matches = [];
    if (preg_match('/(.*)\\|(.*)/', $text, $matches)) {

      // Trim key and value to avoid unwanted spaces issues.
      $key = trim($matches[1]);
      $value = trim($matches[2]);
      $explicit_keys = TRUE;
    }
    $values[$key] = $value;
  }

  // We generate keys only if the list contains no explicit key at all.
  if ($explicit_keys && $generated_keys) {
    return [];
  }
  return $values;
}

Functions

Namesort descending Description
menu_manipulator_form_menu_link_content_form_alter Implements hook_form_BASE_FORM_ID_alter() for the Menu Link Content form.
menu_manipulator_get_multilingual_menu Get a menu tree filtered by the current language.
menu_manipulator_help Implements hook_help().
menu_manipulator_menu_link_content_form_submit Submit function for menu add / edit form.
menu_manipulator_prepare_associative_list Helper function to filter the associative key|label configuration.
menu_manipulator_preprocess_menu Implements theme_preprocess_menu().