menu.module 22.7 KB
Newer Older
1 2
<?php

Dries's avatar
 
Dries committed
3 4
/**
 * @file
5 6 7 8 9 10 11
 * Allows administrators to customize the site's navigation menus.
 *
 * A menu (in this context) is a hierarchical collection of links, generally
 * used for navigation. This is not to be confused with the
 * @link menu Menu system @endlink of menu.inc and hook_menu(), which defines
 * page routing requests for Drupal, and also allows the defined page routing
 * URLs to be added to the main site navigation menu.
Dries's avatar
 
Dries committed
12 13
 */

14
use Drupal\Core\Entity\EntityInterface;
15
use Drupal\block\BlockPluginInterface;
16
use Drupal\Core\Render\Element;
17
use Drupal\node\NodeTypeInterface;
18
use Drupal\system\Entity\Menu;
19
use Symfony\Component\HttpFoundation\JsonResponse;
20
use Drupal\menu_link\Entity\MenuLink;
21
use Drupal\menu_link\MenuLinkStorage;
22
use Drupal\node\NodeInterface;
23

24 25 26 27
/**
 * Maximum length of menu name as entered by the user. Database length is 32
 * and we add a menu- prefix.
 */
28
const MENU_MAX_MENU_NAME_LENGTH_UI = 27;
29

30
/**
31
 * Implements hook_help().
32
 */
33 34
function menu_help($path, $arg) {
  switch ($path) {
35
    case 'admin/help#menu':
36 37
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
38
      $output .= '<p>' . t('The Menu module provides an interface for managing menus. A menu is a hierarchical collection of links, which can be within or external to the site, generally used for navigation. For more information, see the <a href="!menu">online documentation for the Menu module</a>.', array('!menu' => 'https://drupal.org/documentation/modules/menu/')) . '</p>';
39 40 41
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
      $output .= '<dt>' . t('Managing menus') . '</dt>';
42
      $output .= '<dd>' . t('Users with the <em>Administer menus and menu items</em> permission can add, edit, and delete custom menus on the <a href="!menu">Menus page</a>. Custom menus can be special site menus, menus of external links, or any combination of internal and external links. You may create an unlimited number of additional menus, each of which will automatically have an associated block (if you have the <a href="!block_help">Block module</a> installed). By selecting <em>Edit menu</em>, you can add, edit, or delete links for a given menu. The links listing page provides a drag-and-drop interface for controlling the order of links, and creating a hierarchy within the menu.', array('!block_help' => \Drupal::url('help.page', array('name' => 'block')), '!menu' => \Drupal::url('menu.overview_page'))) . '</dd>';
43
      $output .= '<dt>' . t('Displaying menus') . '</dt>';
44
      $output .= '<dd>' . t('If you have the Block module enabled, then each menu that you create is rendered in a block that you enable and position on the <a href="!blocks">Block layout page</a>. In some <a href="!themes">themes</a>, the main menu and possibly the secondary menu will be output automatically; you may be able to disable this behavior on the <a href="!themes">theme\'s settings page</a>.', array('!blocks' => (\Drupal::moduleHandler()->moduleExists('block')) ? \Drupal::url('block.admin_display') : '#', '!themes' => \Drupal::url('system.themes_page'), '!theme_settings' => \Drupal::url('system.theme_settings'))) . '</dd>';
45
      $output .= '</dl>';
46
      return $output;
47
  }
48 49 50 51 52
  if ($path == 'admin/structure/menu/add' && \Drupal::moduleHandler()->moduleExists('block')) {
      return '<p>' . t('You can enable the newly-created block for this menu on the <a href="!blocks">Block layout page</a>.', array('!blocks' => \Drupal::url('block.admin_display'))) . '</p>';
  }
  elseif ($path == 'admin/structure/menu' && \Drupal::moduleHandler()->moduleExists('block')) {
    return '<p>' . t('Each menu has a corresponding block that is managed on the <a href="!blocks">Block layout page</a>.', array('!blocks' => \Drupal::url('block.admin_display'))) . '</p>';
53
  }
54 55
}

56
/**
57
 * Implements hook_permission().
58
 */
59
function menu_permission() {
60
  return array(
61
    'administer menu' => array(
62
      'title' => t('Administer menus and menu items'),
63
    ),
64
  );
65 66
}

67
/**
68
 * Implements hook_entity_type_build().
69
 */
70 71 72
function menu_entity_type_build(array &$entity_types) {
  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
  $entity_types['menu']
73 74 75
    ->setFormClass('add', 'Drupal\menu\MenuFormController')
    ->setFormClass('edit', 'Drupal\menu\MenuFormController')
    ->setFormClass('delete', 'Drupal\menu\Form\MenuDeleteForm')
76
    ->setListBuilderClass('Drupal\menu\MenuListBuilder')
77 78
    ->setLinkTemplate('add-form', 'menu.link_add')
    ->setLinkTemplate('delete-form', 'menu.delete_menu')
79
    ->setLinkTemplate('edit-form', 'menu.menu_edit');
80

81
  $entity_types['menu_link']
82
    ->setFormClass('delete', 'Drupal\menu\Form\MenuLinkDeleteForm')
83 84
    ->setFormClass('reset', 'Drupal\menu\Form\MenuLinkResetForm')
    ->setLinkTemplate('delete-form', 'menu.link_delete');
85 86
}

87 88 89 90 91
/**
 * Implements hook_entity_bundle_info().
 */
function menu_entity_bundle_info() {
  $bundles = array();
92
  $config_names = \Drupal::configFactory()->listAll('system.menu.');
93
  foreach ($config_names as $config_name) {
94
    $config = \Drupal::config($config_name);
95 96 97 98 99 100 101 102
    $bundles['menu_link'][$config->get('id')] = array(
      'label' => $config->get('label'),
    );
  }

  return $bundles;
}

103
/**
104
 * Implements hook_theme().
105 106 107 108
 */
function menu_theme() {
  return array(
    'menu_overview_form' => array(
109
      'file' => 'menu.admin.inc',
110
      'render element' => 'form',
111 112 113 114
    ),
  );
}

115 116
/**
 * Load the data for a single custom menu.
117 118 119
 *
 * @param $menu_name
 *   The unique name of a custom menu to load.
120
 * @return
121
 *   Array defining the custom menu, or NULL if the menu doesn't exist.
122 123
 */
function menu_load($menu_name) {
124
  return entity_load('menu', $menu_name);
125 126 127
}

/**
128
 * Implements hook_menu_insert()
129
 */
130 131 132
function menu_menu_insert(Menu $menu) {
  menu_cache_clear_all();
  // Invalidate the block cache to update menu-based derivatives.
133
  if (\Drupal::moduleHandler()->moduleExists('block')) {
134
    \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
135 136 137 138
  }
  // Make sure the menu is present in the active menus variable so that its
  // items may appear in the menu active trail.
  // See menu_set_active_menu_names().
139
  $config = \Drupal::config('system.menu');
140 141 142 143 144 145 146

  $active_menus = $config->get('active_menus_default') ?: array_keys(menu_get_menus());
  if (!in_array($menu->id(), $active_menus)) {
    $active_menus[] = $menu->id();
    $config
      ->set('active_menus_default', $active_menus)
      ->save();
147
  }
148 149
}

150
/**
151 152 153
 * Implements hook_menu_update().
 */
function menu_menu_update(Menu $menu) {
154
  menu_cache_clear_all();
155
  // Invalidate the block cache to update menu-based derivatives.
156
  if (\Drupal::moduleHandler()->moduleExists('block')) {
157
    \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
158
  }
159 160 161
}

/**
162
 * Implements hook_menu_predelete().
163
 */
164
function menu_menu_predelete(Menu $menu) {
165
  // Delete all links from the menu.
166
  menu_delete_links($menu->id());
167

168
  // Remove menu from active menus variable.
169 170 171 172 173 174 175 176 177 178
  $config = \Drupal::config('system.menu');
  $active_menus = $config->get('active_menus_default') ?: array_keys(menu_get_menus());
  if (in_array($menu->id(), $active_menus)) {
    $active_menus = array_diff($active_menus, array($menu->id()));
    // Prevent the gap left by the removed menu from causing array indices to
    // be saved.
    $active_menus = array_values($active_menus);
    $config
      ->set('active_menus_default', $active_menus)
      ->save();
179
  }
180
}
181

182 183 184 185
/**
 * Implements hook_menu_delete().
 */
function menu_menu_delete(Menu $menu) {
186
  menu_cache_clear_all();
187

188
  // Invalidate the block cache to update menu-based derivatives.
189
  if (\Drupal::moduleHandler()->moduleExists('block')) {
190
    \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
191
  }
192 193
}

194
/**
195 196
 * Returns a list of menu links that are valid possible parents for the given
 * menu link.
197
 *
198
 * @param array $menus
199
 *   An array of menu names and titles, such as from menu_get_menus().
200
 * @param \Drupal\menu_link\Entity\MenuLink $menu_link
201 202 203
 *   The menu link for which to generate a list of parents.
 *   If $menu_link->id() == 0 then the complete tree is returned.
 * @param string $type
204 205
 *   The node type for which to generate a list of parents.
 *   If $item itself is a node type then $type is ignored.
206 207 208 209
 *
 * @return array
 *   An array of menu link titles keyed by a string containing the menu name and
 *   mlid. The list excludes the given item and its children.
210 211
 *
 * @todo This has to be turned into a #process form element callback. The
212
 *   'override_parent_selector' variable is entirely superfluous.
213
 */
214
function menu_parent_options(array $menus, MenuLink $menu_link = NULL, $type = NULL) {
215 216 217
  // The menu_links table can be practically any size and we need a way to
  // allow contrib modules to provide more scalable pattern choosers.
  // hook_form_alter is too late in itself because all the possible parents are
218
  // retrieved here, unless override_parent_selector is set to TRUE.
219
  if (\Drupal::config('menu.settings')->get('override_parent_selector')) {
220 221
    return array();
  }
222

223 224
  if (!$menu_link) {
    $menu_link = entity_create('menu_link', array('mlid' => 0));
225
  }
226 227 228

  $available_menus = array();
  if (!$type) {
229
    // If no node type is set, use all menus given to this function.
230 231 232
    $available_menus = $menus;
  }
  else {
233
    // If a node type is set, use all available menus for this type.
234
    $type_menus = \Drupal::config("menu.entity.node.$type")->get('available_menus');
235 236 237 238 239
    foreach ($type_menus as $menu) {
      $available_menus[$menu] = $menu;
    }
  }

240
  return _menu_get_options($menus, $available_menus, $menu_link);
241 242 243 244 245 246
}

/**
 * Helper function to get the items of the given menu.
 */
function _menu_get_options($menus, $available_menus, $item) {
247
  // If the item has children, there is an added limit to the depth of valid parents.
248 249 250 251 252 253
  if (isset($item['parent_depth_limit'])) {
    $limit = $item['parent_depth_limit'];
  }
  else {
    $limit = _menu_parent_depth_limit($item);
  }
254

255 256 257
  /** @var \Drupal\menu_link\MenuTreeInterface $menu_tree */
  $menu_tree = \Drupal::service('menu_link.tree');

258
  $options = array();
259
  foreach ($menus as $menu_name => $title) {
260
    if (isset($available_menus[$menu_name])) {
261
      $tree = $menu_tree->buildAllData($menu_name, NULL);
262 263 264
      $options[$menu_name . ':0'] = '<' . $title . '>';
      _menu_parents_recurse($tree, $menu_name, '--', $options, $item['mlid'], $limit);
    }
265
  }
266
  return $options;
267 268 269
}

/**
270
 * Recursive helper function for menu_parent_options().
271
 */
272
function _menu_parents_recurse($tree, $menu_name, $indent, &$options, $exclude, $depth_limit) {
273
  foreach ($tree as $data) {
274 275 276 277
    if ($data['link']['depth'] > $depth_limit) {
      // Don't iterate through any links on this level.
      break;
    }
278
    if ($data['link']['mlid'] != $exclude && $data['link']['hidden'] >= 0) {
279
      $title = $indent . ' ' . truncate_utf8($data['link']['title'], 30, TRUE, FALSE);
280
      if ($data['link']['hidden']) {
281
        $title .= ' (' . t('disabled') . ')';
282
      }
283
      $options[$menu_name . ':' . $data['link']['mlid']] = $title;
284
      if ($data['below']) {
285
        _menu_parents_recurse($data['below'], $menu_name, $indent . '--', $options, $exclude, $depth_limit);
286 287 288
      }
    }
  }
289 290
}

291
/**
292
 * Implements hook_block_view_BASE_BLOCK_ID_alter() for 'system_menu_block'.
293
 */
294
function menu_block_view_system_menu_block_alter(array &$build, BlockPluginInterface $block) {
295
  // Add contextual links for system menu blocks.
296
  $menus = menu_list_system_menus();
297
  $menu_name = $block->getDerivativeId();
298
  if (isset($menus[$menu_name]) && isset($build['content'])) {
299
    foreach (Element::children($build['content']) as $key) {
300
      $build['#contextual_links']['menu'] = array(
301 302
        'route_parameters' => array('menu' => $build['content'][$key]['#original_link']['menu_name']),
      );
303 304 305 306
    }
  }
}

307
/**
308
 * Implements hook_node_insert().
309
 */
310
function menu_node_insert(EntityInterface $node) {
311
  menu_node_save($node);
312 313 314
}

/**
315
 * Implements hook_node_update().
316
 */
317
function menu_node_update(EntityInterface $node) {
318 319 320
  menu_node_save($node);
}

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
/**
 * Implements hook_node_type_insert().
 */
function menu_node_type_insert(NodeTypeInterface $type) {
  \Drupal::config('menu.entity.node.' . $type->id())
    ->set('available_menus', array('main'))
    ->set('parent', 'main:0')
    ->save();
}

/**
 * Implements hook_node_type_delete().
 */
function menu_node_type_delete(NodeTypeInterface $type) {
  \Drupal::config('menu.entity.node.' . $type->id())->delete();
}

338 339 340
/**
 * Helper for hook_node_insert() and hook_node_update().
 */
341
function menu_node_save(EntityInterface $node) {
342
  if (isset($node->menu)) {
343 344
    $link = &$node->menu;
    if (empty($link['enabled'])) {
345
      if (!$link->isNew()) {
346 347
        menu_link_delete($link['mlid']);
      }
348
    }
349 350
    elseif (trim($link['link_title'])) {
      $link['link_title'] = trim($link['link_title']);
351
      $link['link_path'] = 'node/' . $node->id();
352 353 354 355 356 357 358
      if (trim($link['description'])) {
        $link['options']['attributes']['title'] = trim($link['description']);
      }
      else {
        // If the description field was left empty, remove the title attribute
        // from the menu link.
        unset($link['options']['attributes']['title']);
359
      }
360
      if (!menu_link_save($link)) {
361
        drupal_set_message(t('There was an error saving the menu link.'), 'error');
362
      }
363 364 365 366 367
    }
  }
}

/**
368
 * Implements hook_node_predelete().
369
 */
370
function menu_node_predelete(EntityInterface $node) {
371
  // Delete all menu module links that point to this node.
372
  $query = \Drupal::entityQuery('menu_link')
373
    ->condition('link_path', 'node/' . $node->id())
374 375 376 377 378
    ->condition('module', 'menu');
  $result = $query->execute();

  if (!empty($result)) {
    menu_link_delete_multiple($result);
379 380 381 382
  }
}

/**
383
 * Implements hook_node_prepare_form().
384
 */
385
function menu_node_prepare_form(NodeInterface $node, $operation, array &$form_state) {
386 387
  if (empty($node->menu)) {
    // Prepare the node for the edit form so that $node->menu always exists.
388 389
    $node_type_config = \Drupal::config('menu.entity.node.' . $node->getType());
    $menu_name = strtok($node_type_config->get('parent'), ':');
390
    $menu_link = FALSE;
391
    if ($node->id()) {
392
      $mlid = FALSE;
393
      // Give priority to the default menu
394
      $type_menus = $node_type_config->get('available_menus');
395
      if (in_array($menu_name, $type_menus)) {
396
        $query = \Drupal::entityQuery('menu_link')
397
          ->condition('link_path', 'node/' . $node->id())
398 399 400 401 402 403 404
          ->condition('menu_name', $menu_name)
          ->condition('module', 'menu')
          ->sort('mlid', 'ASC')
          ->range(0, 1);
        $result = $query->execute();

        $mlid = (!empty($result)) ? reset($result) : FALSE;
405 406 407
      }
      // Check all allowed menus if a link does not exist in the default menu.
      if (!$mlid && !empty($type_menus)) {
408
        $query = \Drupal::entityQuery('menu_link')
409
          ->condition('link_path', 'node/' . $node->id())
410 411 412 413 414 415 416
          ->condition('menu_name', array_values($type_menus), 'IN')
          ->condition('module', 'menu')
          ->sort('mlid', 'ASC')
          ->range(0, 1);
        $result = $query->execute();

        $mlid = (!empty($result)) ? reset($result) : FALSE;
417 418
      }
      if ($mlid) {
419
        $menu_link = menu_link_load($mlid);
420 421
      }
    }
422 423 424 425 426 427 428 429

    if (!$menu_link) {
      $menu_link = entity_create('menu_link', array(
        'mlid' => 0,
        'plid' => 0,
        'menu_name' => $menu_name,
      ));
    }
430
    // Set default values.
431
    $node->menu = $menu_link;
432 433 434 435
  }
  // Find the depth limit for the parent select.
  if (!isset($node->menu['parent_depth_limit'])) {
    $node->menu['parent_depth_limit'] = _menu_parent_depth_limit($node->menu);
436 437 438
  }
}

439 440 441 442
/**
 * Find the depth limit for items in the parent select.
 */
function _menu_parent_depth_limit($item) {
443
  return MENU_MAX_DEPTH - 1 - (($item['mlid'] && $item['has_children']) ? entity_get_controller('menu_link')->findChildrenRelativeDepth($item) : 0);
444 445
}

446
/**
447 448 449 450 451
 * Implements hook_form_BASE_FORM_ID_alter().
 *
 * Adds menu item fields to the node form.
 *
 * @see menu_node_submit()
452
 */
453
function menu_form_node_form_alter(&$form, $form_state) {
454
  // Generate a list of possible parents (not including this link or descendants).
455
  // @todo This must be handled in a #process handler.
456
  $node = $form_state['controller']->getEntity();
457
  $link = $node->menu;
458
  $type = $node->getType();
459
  $options = menu_parent_options(menu_get_menus(), $link, $type);
460 461 462 463 464 465
  // If no possible parent menu items were found, there is nothing to display.
  if (empty($options)) {
    return;
  }

  $form['menu'] = array(
466
    '#type' => 'details',
467
    '#title' => t('Menu settings'),
468
    '#access' => \Drupal::currentUser()->hasPermission('administer menu'),
469
    '#open' => !empty($link['link_title']),
470
    '#group' => 'advanced',
471
    '#attached' => array(
472
      'library' => array('menu/drupal.menu'),
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
    ),
    '#tree' => TRUE,
    '#weight' => -2,
    '#attributes' => array('class' => array('menu-link-form')),
  );
  $form['menu']['enabled'] = array(
    '#type' => 'checkbox',
    '#title' => t('Provide a menu link'),
    '#default_value' => (int) (bool) $link['mlid'],
  );
  $form['menu']['link'] = array(
    '#type' => 'container',
    '#parents' => array('menu'),
    '#states' => array(
      'invisible' => array(
        'input[name="menu[enabled]"]' => array('checked' => FALSE),
489
      ),
490 491
    ),
  );
492

493 494 495 496
  // Populate the element with the link data.
  foreach (array('mlid', 'module', 'hidden', 'has_children', 'customized', 'options', 'expanded', 'hidden', 'parent_depth_limit') as $key) {
    $form['menu']['link'][$key] = array('#type' => 'value', '#value' => $link[$key]);
  }
497

498 499 500 501 502
  $form['menu']['link']['link_title'] = array(
    '#type' => 'textfield',
    '#title' => t('Menu link title'),
    '#default_value' => $link['link_title'],
  );
503

504 505 506 507 508 509 510
  $form['menu']['link']['description'] = array(
    '#type' => 'textarea',
    '#title' => t('Description'),
    '#default_value' => isset($link['options']['attributes']['title']) ? $link['options']['attributes']['title'] : '',
    '#rows' => 1,
    '#description' => t('Shown when hovering over the menu link.'),
  );
511

512 513 514 515 516 517
  if ($link['mlid']) {
    $default = $link['menu_name'] . ':' . $link['plid'];
  }
  else {
    $default = \Drupal::config('menu.entity.node.'.$type)->get('parent');
  }
518 519 520 521
  // If the current parent menu item is not present in options, use the first
  // available option as default value.
  // @todo User should not be allowed to access menu link settings in such a
  // case.
522
  if (!isset($options[$default])) {
523 524
    $array = array_keys($options);
    $default = reset($array);
525
  }
526 527 528 529 530 531 532
  $form['menu']['link']['parent'] = array(
    '#type' => 'select',
    '#title' => t('Parent item'),
    '#default_value' => $default,
    '#options' => $options,
    '#attributes' => array('class' => array('menu-parent-select')),
  );
533 534

  // Get number of items in menu so the weight selector is sized appropriately.
535
  $delta = entity_get_controller('menu_link')->countMenuLinks($link->menu_name);
536 537 538 539
  if ($delta < 50) {
    // Old hardcoded value
    $delta = 50;
  }
540 541 542
  $form['menu']['link']['weight'] = array(
    '#type' => 'weight',
    '#title' => t('Weight'),
543
    '#delta' => $delta,
544
    '#default_value' => $link['weight'],
545
    '#description' => t('Menu links with lower weights are displayed before links with higher weights.'),
546
  );
547 548
}

549
/**
550
 * Implements hook_node_submit().
551 552
 *
 * @see menu_form_node_form_alter()
553
 */
554
function menu_node_submit(EntityInterface $node, $form, $form_state) {
555 556 557 558 559 560 561
  if (!empty($form_state['values']['menu'])) {
    $node->menu = entity_create('menu_link', $form_state['values']['menu']);
    // Decompose the selected menu parent option into 'menu_name' and 'plid', if
    // the form used the default parent selection widget.
    if (!empty($form_state['values']['menu']['parent'])) {
      list($node->menu['menu_name'], $node->menu['plid']) = explode(':', $form_state['values']['menu']['parent']);
    }
562 563 564
  }
}

565
/**
566 567
 * Implements hook_form_FORM_ID_alter().
 *
568
 * Adds menu options to the node type form.
569 570 571
 *
 * @see NodeTypeFormController::form().
 * @see menu_form_node_type_form_submit().
572 573 574
 */
function menu_form_node_type_form_alter(&$form, $form_state) {
  $menu_options = menu_get_menus();
575
  $type = $form_state['controller']->getEntity();
576 577 578 579 580 581 582 583 584
  if ($type->id()) {
    $config_values = \Drupal::config('menu.entity.node.' . $type->id())->get();
  }
  else {
    $config_values = array(
      'available_menus' => array('main'),
      'parent' => 'main:0',
    );
  }
585
  $form['menu'] = array(
586
    '#type' => 'details',
587 588
    '#title' => t('Menu settings'),
    '#attached' => array(
589
      'library' => array('menu/drupal.menu.admin'),
590 591 592 593 594 595
    ),
    '#group' => 'additional_settings',
  );
  $form['menu']['menu_options'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Available menus'),
596
    '#default_value' => $config_values['available_menus'],
597 598 599 600 601 602
    '#options' => $menu_options,
    '#description' => t('The menus available to place links in for this content type.'),
  );
  // To avoid an 'illegal option' error after saving the form we have to load
  // all available menu items.
  // Otherwise it is not possible to dynamically add options to the list.
603
  // @todo Convert menu_parent_options() into a #process callback.
604 605
  $menu_link = entity_create('menu_link', array('mlid' => 0));
  $options = menu_parent_options(menu_get_menus(), $menu_link);
606 607 608
  $form['menu']['menu_parent'] = array(
    '#type' => 'select',
    '#title' => t('Default parent item'),
609
    '#default_value' => $config_values['parent'],
610 611 612 613 614
    '#options' => $options,
    '#description' => t('Choose the menu item to be the default parent for a new link in the content authoring form.'),
    '#attributes' => array('class' => array('menu-title-select')),
  );

615 616 617 618 619 620 621 622 623 624 625 626 627 628
  $form['actions']['submit']['#submit'][] = 'menu_form_node_type_form_submit';
}

/**
 * Submit handler for forms with menu options.
 *
 * @see menu_form_node_type_form_alter().
 */
function menu_form_node_type_form_submit(&$form, $form_state) {
  $type = $form_state['controller']->getEntity();
  \Drupal::config('menu.entity.node.' . $type->id())
    ->set('available_menus', array_values(array_filter($form_state['values']['menu_options'])))
    ->set('parent', $form_state['values']['menu_parent'])
    ->save();
629 630
}

631 632 633 634 635 636 637 638 639 640
/**
 * Return an associative array of the custom menus names.
 *
 * @param $all
 *   If FALSE return only user-added menus, or if TRUE also include
 *   the menus defined by the system.
 * @return
 *   An array with the machine-readable names as the keys, and human-readable
 *   titles as the values.
 */
641
function menu_get_menus($all = TRUE) {
642
  if ($custom_menus = entity_load_multiple('menu')) {
643 644 645 646
    if (!$all) {
      $custom_menus = array_diff_key($custom_menus, menu_list_system_menus());
    }
    foreach ($custom_menus as $menu_name => $menu) {
647
      $custom_menus[$menu_name] = $menu->label();
648 649
    }
    asort($custom_menus);
650
  }
651
  return $custom_menus;
652
}
653 654

/**
655
 * Implements hook_preprocess_HOOK() for block templates.
656 657
 */
function menu_preprocess_block(&$variables) {
658
  if ($variables['configuration']['provider'] == 'menu') {
659
    $variables['attributes']['role'] = 'navigation';
660 661
  }
}