Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 additions and 0 deletions
|
@ -0,0 +1 @@
|
|||
override_parent_selector: false
|
23
web/core/modules/menu_ui/config/schema/menu_ui.schema.yml
Normal file
23
web/core/modules/menu_ui/config/schema/menu_ui.schema.yml
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Schema for configuration files of the Menu UI module.
|
||||
|
||||
menu_ui.settings:
|
||||
type: config_object
|
||||
label: 'Menu settings'
|
||||
mapping:
|
||||
override_parent_selector:
|
||||
type: boolean
|
||||
label: 'Override parent selector'
|
||||
|
||||
node.type.*.third_party.menu_ui:
|
||||
type: mapping
|
||||
label: 'Per-content type menu settings'
|
||||
mapping:
|
||||
available_menus:
|
||||
type: sequence
|
||||
label: 'Available menus'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Menu machine name'
|
||||
parent:
|
||||
type: string
|
||||
label: 'Parent'
|
6
web/core/modules/menu_ui/css/menu_ui.admin.css
Normal file
6
web/core/modules/menu_ui/css/menu_ui.admin.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
.menu-enabled {
|
||||
width: 70px;
|
||||
}
|
||||
.menu-label {
|
||||
font-weight: bold;
|
||||
}
|
68
web/core/modules/menu_ui/menu_ui.admin.js
Normal file
68
web/core/modules/menu_ui/menu_ui.admin.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* @file
|
||||
* Menu UI admin behaviors.
|
||||
*/
|
||||
|
||||
(function ($, Drupal) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.menuUiChangeParentItems = {
|
||||
attach: function (context, settings) {
|
||||
var $menu = $('#edit-menu').once('menu-parent');
|
||||
if ($menu.length) {
|
||||
// Update the list of available parent menu items to match the initial
|
||||
// available menus.
|
||||
Drupal.menuUiUpdateParentList();
|
||||
|
||||
// Update list of available parent menu items.
|
||||
$menu.on('change', 'input', Drupal.menuUiUpdateParentList);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to set the options of the menu parent item dropdown.
|
||||
*/
|
||||
Drupal.menuUiUpdateParentList = function () {
|
||||
var $menu = $('#edit-menu');
|
||||
var values = [];
|
||||
|
||||
$menu.find('input:checked').each(function () {
|
||||
// Get the names of all checked menus.
|
||||
values.push(Drupal.checkPlain($.trim($(this).val())));
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: location.protocol + '//' + location.host + Drupal.url('admin/structure/menu/parents'),
|
||||
type: 'POST',
|
||||
data: {'menus[]': values},
|
||||
dataType: 'json',
|
||||
success: function (options) {
|
||||
var $select = $('#edit-menu-parent');
|
||||
// Save key of last selected element.
|
||||
var selected = $select.val();
|
||||
// Remove all existing options from dropdown.
|
||||
$select.children().remove();
|
||||
// Add new options to dropdown. Keep a count of options for testing later.
|
||||
var totalOptions = 0;
|
||||
for (var machineName in options) {
|
||||
if (options.hasOwnProperty(machineName)) {
|
||||
$select.append(
|
||||
$('<option ' + (machineName === selected ? ' selected="selected"' : '') + '></option>').val(machineName).text(options[machineName])
|
||||
);
|
||||
totalOptions++;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide the parent options if there are no options for it.
|
||||
$select.closest('div').toggle(totalOptions > 0).attr('hidden', totalOptions === 0);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
})(jQuery, Drupal);
|
9
web/core/modules/menu_ui/menu_ui.info.yml
Normal file
9
web/core/modules/menu_ui/menu_ui.info.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
name: Menu UI
|
||||
type: module
|
||||
description: 'Allows administrators to customize the site navigation menu.'
|
||||
package: Core
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
configure: entity.menu.collection
|
||||
dependencies:
|
||||
- menu_link_content
|
91
web/core/modules/menu_ui/menu_ui.js
Normal file
91
web/core/modules/menu_ui/menu_ui.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* @file
|
||||
* Menu UI behaviors.
|
||||
*/
|
||||
|
||||
(function ($, Drupal) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Set a summary on the menu link form.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Find the form and call `drupalSetSummary` on it.
|
||||
*/
|
||||
Drupal.behaviors.menuUiDetailsSummaries = {
|
||||
attach: function (context) {
|
||||
$(context).find('.menu-link-form').drupalSetSummary(function (context) {
|
||||
var $context = $(context);
|
||||
if ($context.find('.js-form-item-menu-enabled input').is(':checked')) {
|
||||
return Drupal.checkPlain($context.find('.js-form-item-menu-title input').val());
|
||||
}
|
||||
else {
|
||||
return Drupal.t('Not in menu');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Automatically fill in a menu link title, if possible.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches change and keyup behavior for automatically filling out menu
|
||||
* link titles.
|
||||
*/
|
||||
Drupal.behaviors.menuUiLinkAutomaticTitle = {
|
||||
attach: function (context) {
|
||||
var $context = $(context);
|
||||
$context.find('.menu-link-form').each(function () {
|
||||
var $this = $(this);
|
||||
// Try to find menu settings widget elements as well as a 'title' field
|
||||
// in the form, but play nicely with user permissions and form
|
||||
// alterations.
|
||||
var $checkbox = $this.find('.js-form-item-menu-enabled input');
|
||||
var $link_title = $context.find('.js-form-item-menu-title input');
|
||||
var $title = $this.closest('form').find('.js-form-item-title-0-value input');
|
||||
// Bail out if we do not have all required fields.
|
||||
if (!($checkbox.length && $link_title.length && $title.length)) {
|
||||
return;
|
||||
}
|
||||
// If there is a link title already, mark it as overridden. The user
|
||||
// expects that toggling the checkbox twice will take over the node's
|
||||
// title.
|
||||
if ($checkbox.is(':checked') && $link_title.val().length) {
|
||||
$link_title.data('menuLinkAutomaticTitleOverridden', true);
|
||||
}
|
||||
// Whenever the value is changed manually, disable this behavior.
|
||||
$link_title.on('keyup', function () {
|
||||
$link_title.data('menuLinkAutomaticTitleOverridden', true);
|
||||
});
|
||||
// Global trigger on checkbox (do not fill-in a value when disabled).
|
||||
$checkbox.on('change', function () {
|
||||
if ($checkbox.is(':checked')) {
|
||||
if (!$link_title.data('menuLinkAutomaticTitleOverridden')) {
|
||||
$link_title.val($title.val());
|
||||
}
|
||||
}
|
||||
else {
|
||||
$link_title.val('');
|
||||
$link_title.removeData('menuLinkAutomaticTitleOverridden');
|
||||
}
|
||||
$checkbox.closest('.vertical-tabs-pane').trigger('summaryUpdated');
|
||||
$checkbox.trigger('formUpdated');
|
||||
});
|
||||
// Take over any title change.
|
||||
$title.on('keyup', function () {
|
||||
if (!$link_title.data('menuLinkAutomaticTitleOverridden') && $checkbox.is(':checked')) {
|
||||
$link_title.val($title.val());
|
||||
$link_title.val($title.val()).trigger('formUpdated');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal);
|
22
web/core/modules/menu_ui/menu_ui.libraries.yml
Normal file
22
web/core/modules/menu_ui/menu_ui.libraries.yml
Normal file
|
@ -0,0 +1,22 @@
|
|||
drupal.menu_ui:
|
||||
version: VERSION
|
||||
js:
|
||||
menu_ui.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/drupal.form
|
||||
|
||||
drupal.menu_ui.admin:
|
||||
version: VERSION
|
||||
js:
|
||||
menu_ui.admin.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
|
||||
drupal.menu_ui.adminforms:
|
||||
version: VERSION
|
||||
css:
|
||||
theme:
|
||||
css/menu_ui.admin.css: {}
|
12
web/core/modules/menu_ui/menu_ui.links.action.yml
Normal file
12
web/core/modules/menu_ui/menu_ui.links.action.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
entity.menu.add_link_form:
|
||||
route_name: entity.menu.add_link_form
|
||||
title: 'Add link'
|
||||
class: \Drupal\menu_ui\Plugin\Menu\LocalAction\MenuLinkAdd
|
||||
appears_on:
|
||||
- entity.menu.edit_form
|
||||
|
||||
entity.menu.add_form:
|
||||
route_name: entity.menu.add_form
|
||||
title: 'Add menu'
|
||||
appears_on:
|
||||
- entity.menu.collection
|
4
web/core/modules/menu_ui/menu_ui.links.contextual.yml
Normal file
4
web/core/modules/menu_ui/menu_ui.links.contextual.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
entity.menu.edit_form:
|
||||
title: 'Edit menu'
|
||||
route_name: 'entity.menu.edit_form'
|
||||
group: menu
|
5
web/core/modules/menu_ui/menu_ui.links.menu.yml
Normal file
5
web/core/modules/menu_ui/menu_ui.links.menu.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
entity.menu.collection:
|
||||
title: Menus
|
||||
description: 'Manage menus and menu links.'
|
||||
route_name: entity.menu.collection
|
||||
parent: system.admin_structure
|
9
web/core/modules/menu_ui/menu_ui.links.task.yml
Normal file
9
web/core/modules/menu_ui/menu_ui.links.task.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
entity.menu.edit_form:
|
||||
title: 'Edit menu'
|
||||
route_name: entity.menu.edit_form
|
||||
base_route: entity.menu.edit_form
|
||||
|
||||
entity.menu.collection:
|
||||
title: 'List'
|
||||
route_name: entity.menu.collection
|
||||
base_route: entity.menu.collection
|
514
web/core/modules/menu_ui/menu_ui.module
Normal file
514
web/core/modules/menu_ui/menu_ui.module
Normal file
|
@ -0,0 +1,514 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Allows administrators to customize the site's navigation menus.
|
||||
*
|
||||
* A menu (in this context) is a hierarchical collection of links, generally
|
||||
* used for navigation.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Breadcrumb\Breadcrumb;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Link;
|
||||
use Drupal\Core\Menu\MenuLinkInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\node\NodeTypeInterface;
|
||||
use Drupal\system\Entity\Menu;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Maximum length of menu name as entered by the user. Database length is 32
|
||||
* and we add a menu- prefix.
|
||||
*/
|
||||
const MENU_MAX_MENU_NAME_LENGTH_UI = 27;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function menu_ui_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.menu_ui':
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Menu UI 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 UI module</a>.', array(':menu' => 'https://www.drupal.org/documentation/modules/menu/')) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Managing menus') . '</dt>';
|
||||
$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::moduleHandler()->moduleExists('block')) ? \Drupal::url('help.page', array('name' => 'block')) : '#', ':menu' => \Drupal::url('entity.menu.collection'))) . '</dd>';
|
||||
$output .= '<dt>' . t('Displaying menus') . '</dt>';
|
||||
$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>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
}
|
||||
if ($route_name == 'entity.menu.add_form' && \Drupal::moduleHandler()->moduleExists('block') && \Drupal::currentUser()->hasPermission('administer blocks')) {
|
||||
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 ($route_name == 'entity.menu.collection' && \Drupal::moduleHandler()->moduleExists('block') && \Drupal::currentUser()->hasPermission('administer blocks')) {
|
||||
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>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_type_build().
|
||||
*/
|
||||
function menu_ui_entity_type_build(array &$entity_types) {
|
||||
/** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
|
||||
$entity_types['menu']
|
||||
->setFormClass('add', 'Drupal\menu_ui\MenuForm')
|
||||
->setFormClass('edit', 'Drupal\menu_ui\MenuForm')
|
||||
->setFormClass('delete', 'Drupal\menu_ui\Form\MenuDeleteForm')
|
||||
->setListBuilderClass('Drupal\menu_ui\MenuListBuilder')
|
||||
->setLinkTemplate('add-form', '/admin/structure/menu/add')
|
||||
->setLinkTemplate('delete-form', '/admin/structure/menu/manage/{menu}/delete')
|
||||
->setLinkTemplate('edit-form', '/admin/structure/menu/manage/{menu}')
|
||||
->setLinkTemplate('add-link-form', '/admin/structure/menu/manage/{menu}/add')
|
||||
->setLinkTemplate('collection', '/admin/structure/menu');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_insert( for menu entities.
|
||||
*/
|
||||
function menu_ui_menu_insert(Menu $menu) {
|
||||
menu_cache_clear_all();
|
||||
// Invalidate the block cache to update menu-based derivatives.
|
||||
if (\Drupal::moduleHandler()->moduleExists('block')) {
|
||||
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_update() for menu entities.
|
||||
*/
|
||||
function menu_ui_menu_update(Menu $menu) {
|
||||
menu_cache_clear_all();
|
||||
// Invalidate the block cache to update menu-based derivatives.
|
||||
if (\Drupal::moduleHandler()->moduleExists('block')) {
|
||||
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_predelete() for menu entities.
|
||||
*/
|
||||
function menu_ui_menu_predelete(Menu $menu) {
|
||||
// Delete all links from the menu.
|
||||
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
$menu_link_manager->deleteLinksInMenu($menu->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_delete() for menu entities.
|
||||
*/
|
||||
function menu_ui_menu_delete(Menu $menu) {
|
||||
menu_cache_clear_all();
|
||||
|
||||
// Invalidate the block cache to update menu-based derivatives.
|
||||
if (\Drupal::moduleHandler()->moduleExists('block')) {
|
||||
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_view_BASE_BLOCK_ID_alter() for 'system_menu_block'.
|
||||
*/
|
||||
function menu_ui_block_view_system_menu_block_alter(array &$build, BlockPluginInterface $block) {
|
||||
if ($block->getBaseId() == 'system_menu_block') {
|
||||
$menu_name = $block->getDerivativeId();
|
||||
$build['#contextual_links']['menu'] = array(
|
||||
'route_parameters' => array('menu' => $menu_name),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create or update a menu link for a node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* Node entity.
|
||||
* @param array $values
|
||||
* Values for the menu link.
|
||||
*/
|
||||
function _menu_ui_node_save(NodeInterface $node, array $values) {
|
||||
/** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */
|
||||
if (!empty($values['entity_id'])) {
|
||||
$entity = MenuLinkContent::load($values['entity_id']);
|
||||
if ($entity->isTranslatable()) {
|
||||
if (!$entity->hasTranslation($node->language()->getId())) {
|
||||
$entity = $entity->addTranslation($node->language()->getId(), $entity->toArray());
|
||||
}
|
||||
else {
|
||||
$entity = $entity->getTranslation($node->language()->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Create a new menu_link_content entity.
|
||||
$entity = MenuLinkContent::create(array(
|
||||
'link' => ['uri' => 'entity:node/' . $node->id()],
|
||||
'langcode' => $node->language()->getId(),
|
||||
));
|
||||
$entity->enabled->value = 1;
|
||||
}
|
||||
$entity->title->value = trim($values['title']);
|
||||
$entity->description->value = trim($values['description']);
|
||||
$entity->menu_name->value = $values['menu_name'];
|
||||
$entity->parent->value = $values['parent'];
|
||||
$entity->weight->value = isset($values['weight']) ? $values['weight'] : 0;
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_predelete() for node entities.
|
||||
*/
|
||||
function menu_ui_node_predelete(EntityInterface $node) {
|
||||
// Delete all MenuLinkContent links that point to this node.
|
||||
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
$result = $menu_link_manager->loadLinksByRoute('entity.node.canonical', array('node' => $node->id()));
|
||||
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $id => $instance) {
|
||||
if ($instance->isDeletable() && strpos($id, 'menu_link_content:') === 0) {
|
||||
$instance->deleteLink();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the definition for a menu link for the given node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node entity.
|
||||
*
|
||||
* @return array
|
||||
* An array that contains default values for the menu link form.
|
||||
*/
|
||||
function menu_ui_get_menu_link_defaults(NodeInterface $node) {
|
||||
// Prepare the definition for the edit form.
|
||||
/** @var \Drupal\node\NodeTypeInterface $node_type */
|
||||
$node_type = $node->type->entity;
|
||||
$menu_name = strtok($node_type->getThirdPartySetting('menu_ui', 'parent', 'main:'), ':');
|
||||
$defaults = FALSE;
|
||||
if ($node->id()) {
|
||||
$id = FALSE;
|
||||
// Give priority to the default menu
|
||||
$type_menus = $node_type->getThirdPartySetting('menu_ui', 'available_menus', array('main'));
|
||||
if (in_array($menu_name, $type_menus)) {
|
||||
$query = \Drupal::entityQuery('menu_link_content')
|
||||
->condition('link.uri', 'node/' . $node->id())
|
||||
->condition('menu_name', $menu_name)
|
||||
->sort('id', 'ASC')
|
||||
->range(0, 1);
|
||||
$result = $query->execute();
|
||||
|
||||
$id = (!empty($result)) ? reset($result) : FALSE;
|
||||
}
|
||||
// Check all allowed menus if a link does not exist in the default menu.
|
||||
if (!$id && !empty($type_menus)) {
|
||||
$query = \Drupal::entityQuery('menu_link_content')
|
||||
->condition('link.uri', 'entity:node/' . $node->id())
|
||||
->condition('menu_name', array_values($type_menus), 'IN')
|
||||
->sort('id', 'ASC')
|
||||
->range(0, 1);
|
||||
$result = $query->execute();
|
||||
|
||||
$id = (!empty($result)) ? reset($result) : FALSE;
|
||||
}
|
||||
if ($id) {
|
||||
$menu_link = MenuLinkContent::load($id);
|
||||
$menu_link = \Drupal::service('entity.repository')->getTranslationFromContext($menu_link);
|
||||
$defaults = array(
|
||||
'entity_id' => $menu_link->id(),
|
||||
'id' => $menu_link->getPluginId(),
|
||||
'title' => $menu_link->getTitle(),
|
||||
'title_max_length' => $menu_link->getFieldDefinitions()['title']->getSetting('max_length'),
|
||||
'description' => $menu_link->getDescription(),
|
||||
'menu_name' => $menu_link->getMenuName(),
|
||||
'parent' => $menu_link->getParentId(),
|
||||
'weight' => $menu_link->getWeight(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$defaults) {
|
||||
// Get the default max_length of a menu link title from the base field
|
||||
// definition.
|
||||
$field_definitions = \Drupal::entityManager()->getBaseFieldDefinitions('menu_link_content');
|
||||
$max_length = $field_definitions['title']->getSetting('max_length');
|
||||
$defaults = array(
|
||||
'entity_id' => 0,
|
||||
'id' => '',
|
||||
'title' => '',
|
||||
'title_max_length' => $max_length,
|
||||
'description' => '',
|
||||
'menu_name' => $menu_name,
|
||||
'parent' => '',
|
||||
'weight' => 0,
|
||||
);
|
||||
}
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_BASE_FORM_ID_alter() for \Drupal\node\NodeForm.
|
||||
*
|
||||
* Adds menu item fields to the node form.
|
||||
*
|
||||
* @see menu_ui_form_node_form_submit()
|
||||
*/
|
||||
function menu_ui_form_node_form_alter(&$form, FormStateInterface $form_state) {
|
||||
// Generate a list of possible parents (not including this link or descendants).
|
||||
// @todo This must be handled in a #process handler.
|
||||
$node = $form_state->getFormObject()->getEntity();
|
||||
$defaults = menu_ui_get_menu_link_defaults($node);
|
||||
/** @var \Drupal\node\NodeTypeInterface $node_type */
|
||||
$node_type = $node->type->entity;
|
||||
/** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
|
||||
$menu_parent_selector = \Drupal::service('menu.parent_form_selector');
|
||||
$menu_names = menu_ui_get_menus();
|
||||
$type_menus = $node_type->getThirdPartySetting('menu_ui', 'available_menus', array('main'));
|
||||
$available_menus = array();
|
||||
foreach ($type_menus as $menu) {
|
||||
$available_menus[$menu] = $menu_names[$menu];
|
||||
}
|
||||
if ($defaults['id']) {
|
||||
$default = $defaults['menu_name'] . ':' . $defaults['parent'];
|
||||
}
|
||||
else {
|
||||
$default = $node_type->getThirdPartySetting('menu_ui', 'parent', 'main:');
|
||||
}
|
||||
$parent_element = $menu_parent_selector->parentSelectElement($default, $defaults['id'], $available_menus);
|
||||
// If no possible parent menu items were found, there is nothing to display.
|
||||
if (empty($parent_element)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$form['menu'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Menu settings'),
|
||||
'#access' => \Drupal::currentUser()->hasPermission('administer menu'),
|
||||
'#open' => (bool) $defaults['id'],
|
||||
'#group' => 'advanced',
|
||||
'#attached' => array(
|
||||
'library' => array('menu_ui/drupal.menu_ui'),
|
||||
),
|
||||
'#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) $defaults['id'],
|
||||
);
|
||||
$form['menu']['link'] = array(
|
||||
'#type' => 'container',
|
||||
'#parents' => array('menu'),
|
||||
'#states' => array(
|
||||
'invisible' => array(
|
||||
'input[name="menu[enabled]"]' => array('checked' => FALSE),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Populate the element with the link data.
|
||||
foreach (array('id', 'entity_id') as $key) {
|
||||
$form['menu']['link'][$key] = array('#type' => 'value', '#value' => $defaults[$key]);
|
||||
}
|
||||
|
||||
$form['menu']['link']['title'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Menu link title'),
|
||||
'#default_value' => $defaults['title'],
|
||||
'#maxlength' => $defaults['title_max_length'],
|
||||
);
|
||||
|
||||
$form['menu']['link']['description'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Description'),
|
||||
'#default_value' => $defaults['description'],
|
||||
'#rows' => 1,
|
||||
'#description' => t('Shown when hovering over the menu link.'),
|
||||
);
|
||||
|
||||
$form['menu']['link']['menu_parent'] = $parent_element;
|
||||
$form['menu']['link']['menu_parent']['#title'] = t('Parent item');
|
||||
$form['menu']['link']['menu_parent']['#attributes']['class'][] = 'menu-parent-select';
|
||||
|
||||
$form['menu']['link']['weight'] = array(
|
||||
'#type' => 'number',
|
||||
'#title' => t('Weight'),
|
||||
'#default_value' => $defaults['weight'],
|
||||
'#description' => t('Menu links with lower weights are displayed before links with higher weights.'),
|
||||
);
|
||||
|
||||
foreach (array_keys($form['actions']) as $action) {
|
||||
if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
|
||||
$form['actions'][$action]['#submit'][] = 'menu_ui_form_node_form_submit';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for menu item field on the node form.
|
||||
*
|
||||
* @see menu_ui_form_node_form_alter()
|
||||
*/
|
||||
function menu_ui_form_node_form_submit($form, FormStateInterface $form_state) {
|
||||
$node = $form_state->getFormObject()->getEntity();
|
||||
if (!$form_state->isValueEmpty('menu')) {
|
||||
$values = $form_state->getValue('menu');
|
||||
if (empty($values['enabled'])) {
|
||||
if ($values['entity_id']) {
|
||||
$entity = MenuLinkContent::load($values['entity_id']);
|
||||
$entity->delete();
|
||||
}
|
||||
}
|
||||
elseif (trim($values['title'])) {
|
||||
// Decompose the selected menu parent option into 'menu_name' and 'parent',
|
||||
// if the form used the default parent selection widget.
|
||||
if (!empty($values['menu_parent'])) {
|
||||
list($menu_name, $parent) = explode(':', $values['menu_parent'], 2);
|
||||
$values['menu_name'] = $menu_name;
|
||||
$values['parent'] = $parent;
|
||||
}
|
||||
_menu_ui_node_save($node, $values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for \Drupal\node\NodeTypeForm.
|
||||
*
|
||||
* Adds menu options to the node type form.
|
||||
*
|
||||
* @see NodeTypeForm::form()
|
||||
* @see menu_ui_form_node_type_form_submit()
|
||||
*/
|
||||
function menu_ui_form_node_type_form_alter(&$form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
|
||||
$menu_parent_selector = \Drupal::service('menu.parent_form_selector');
|
||||
$menu_options = menu_ui_get_menus();
|
||||
/** @var \Drupal\node\NodeTypeInterface $type */
|
||||
$type = $form_state->getFormObject()->getEntity();
|
||||
$form['menu'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Menu settings'),
|
||||
'#attached' => array(
|
||||
'library' => array('menu_ui/drupal.menu_ui.admin'),
|
||||
),
|
||||
'#group' => 'additional_settings',
|
||||
);
|
||||
$form['menu']['menu_options'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Available menus'),
|
||||
'#default_value' => $type->getThirdPartySetting('menu_ui', 'available_menus', array('main')),
|
||||
'#options' => $menu_options,
|
||||
'#description' => t('The menus available to place links in for this content type.'),
|
||||
);
|
||||
// @todo See if we can avoid pre-loading all options by changing the form or
|
||||
// using a #process callback. https://www.drupal.org/node/2310319
|
||||
// To avoid an 'illegal option' error after saving the form we have to load
|
||||
// all available menu parents. Otherwise, it is not possible to dynamically
|
||||
// add options to the list using ajax.
|
||||
$options_cacheability = new CacheableMetadata();
|
||||
$options = $menu_parent_selector->getParentSelectOptions('', NULL, $options_cacheability);
|
||||
$form['menu']['menu_parent'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Default parent item'),
|
||||
'#default_value' => $type->getThirdPartySetting('menu_ui', 'parent', 'main:'),
|
||||
'#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')),
|
||||
);
|
||||
$options_cacheability->applyTo($form['menu']['menu_parent']);
|
||||
|
||||
$form['#validate'][] = 'menu_ui_form_node_type_form_validate';
|
||||
$form['#entity_builders'][] = 'menu_ui_form_node_type_form_builder';
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate handler for forms with menu options.
|
||||
*
|
||||
* @see menu_ui_form_node_type_form_alter()
|
||||
*/
|
||||
function menu_ui_form_node_type_form_validate(&$form, FormStateInterface $form_state) {
|
||||
$available_menus = array_filter($form_state->getValue('menu_options'));
|
||||
// If there is at least one menu allowed, the selected item should be in
|
||||
// one of them.
|
||||
if (count($available_menus)) {
|
||||
$menu_item_id_parts = explode(':', $form_state->getValue('menu_parent'));
|
||||
if (!in_array($menu_item_id_parts[0], $available_menus)) {
|
||||
$form_state->setErrorByName('menu_parent', t('The selected menu item is not under one of the selected menus.'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$form_state->setValue('menu_parent', '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity builder for the node type form with menu options.
|
||||
*
|
||||
* @see menu_ui_form_node_type_form_alter()
|
||||
*/
|
||||
function menu_ui_form_node_type_form_builder($entity_type, NodeTypeInterface $type, &$form, FormStateInterface $form_state) {
|
||||
$type->setThirdPartySetting('menu_ui', 'available_menus', array_values(array_filter($form_state->getValue('menu_options'))));
|
||||
$type->setThirdPartySetting('menu_ui', 'parent', $form_state->getValue('menu_parent'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array of the custom menus names.
|
||||
*
|
||||
* @param bool $all
|
||||
* (optional) If FALSE return only user-added menus, or if TRUE also include
|
||||
* the menus defined by the system. Defaults to TRUE.
|
||||
*
|
||||
* @return array
|
||||
* An array with the machine-readable names as the keys, and human-readable
|
||||
* titles as the values.
|
||||
*/
|
||||
function menu_ui_get_menus($all = TRUE) {
|
||||
if ($custom_menus = Menu::loadMultiple()) {
|
||||
if (!$all) {
|
||||
$custom_menus = array_diff_key($custom_menus, menu_list_system_menus());
|
||||
}
|
||||
foreach ($custom_menus as $menu_name => $menu) {
|
||||
$custom_menus[$menu_name] = $menu->label();
|
||||
}
|
||||
asort($custom_menus);
|
||||
}
|
||||
return $custom_menus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_HOOK() for block templates.
|
||||
*/
|
||||
function menu_ui_preprocess_block(&$variables) {
|
||||
if ($variables['configuration']['provider'] == 'menu_ui') {
|
||||
$variables['attributes']['role'] = 'navigation';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_system_breadcrumb_alter().
|
||||
*/
|
||||
function menu_ui_system_breadcrumb_alter(Breadcrumb &$breadcrumb, RouteMatchInterface $route_match, array $context) {
|
||||
// Custom breadcrumb behavior for editing menu links, we append a link to
|
||||
// the menu in which the link is found.
|
||||
if (($route_match->getRouteName() == 'menu_ui.link_edit') && $menu_link = $route_match->getParameter('menu_link_plugin')) {
|
||||
if (($menu_link instanceof MenuLinkInterface)) {
|
||||
// Add a link to the menu admin screen.
|
||||
$menu = Menu::load($menu_link->getMenuName());
|
||||
$breadcrumb->addLink(Link::createFromRoute($menu->label(), 'entity.menu.edit_form', ['menu' => $menu->id()]));
|
||||
}
|
||||
}
|
||||
}
|
63
web/core/modules/menu_ui/menu_ui.routing.yml
Normal file
63
web/core/modules/menu_ui/menu_ui.routing.yml
Normal file
|
@ -0,0 +1,63 @@
|
|||
entity.menu.collection:
|
||||
path: '/admin/structure/menu'
|
||||
defaults:
|
||||
_entity_list: 'menu'
|
||||
_title: 'Menus'
|
||||
requirements:
|
||||
_permission: 'administer menu'
|
||||
|
||||
menu_ui.parent_options_js:
|
||||
path: '/admin/structure/menu/parents'
|
||||
defaults:
|
||||
_controller: '\Drupal\menu_ui\Controller\MenuController::getParentOptions'
|
||||
requirements:
|
||||
_permission: 'administer menu'
|
||||
|
||||
menu_ui.link_edit:
|
||||
path: '/admin/structure/menu/link/{menu_link_plugin}/edit'
|
||||
defaults:
|
||||
_form: 'Drupal\menu_ui\Form\MenuLinkEditForm'
|
||||
_title: 'Edit menu link'
|
||||
options:
|
||||
parameters:
|
||||
menu_link_plugin:
|
||||
type: menu_link_plugin
|
||||
requirements:
|
||||
_permission: 'administer menu'
|
||||
|
||||
menu_ui.link_reset:
|
||||
path: '/admin/structure/menu/link/{menu_link_plugin}/reset'
|
||||
defaults:
|
||||
_form: 'Drupal\menu_ui\Form\MenuLinkResetForm'
|
||||
_title: 'Reset menu link'
|
||||
options:
|
||||
parameters:
|
||||
menu_link_plugin:
|
||||
type: menu_link_plugin
|
||||
requirements:
|
||||
_permission: 'administer menu'
|
||||
_custom_access: '\Drupal\menu_ui\Form\MenuLinkResetForm::linkIsResettable'
|
||||
|
||||
entity.menu.add_form:
|
||||
path: '/admin/structure/menu/add'
|
||||
defaults:
|
||||
_entity_form: 'menu.add'
|
||||
_title: 'Add menu'
|
||||
requirements:
|
||||
_entity_create_access: 'menu'
|
||||
|
||||
entity.menu.edit_form:
|
||||
path: '/admin/structure/menu/manage/{menu}'
|
||||
defaults:
|
||||
_entity_form: 'menu.edit'
|
||||
_title_callback: '\Drupal\menu_ui\Controller\MenuController::menuTitle'
|
||||
requirements:
|
||||
_entity_access: 'menu.update'
|
||||
|
||||
entity.menu.delete_form:
|
||||
path: '/admin/structure/menu/manage/{menu}/delete'
|
||||
defaults:
|
||||
_entity_form: 'menu.delete'
|
||||
_title: 'Delete menu'
|
||||
requirements:
|
||||
_entity_access: 'menu.delete'
|
|
@ -0,0 +1,15 @@
|
|||
# The menu migration is in the system module and the menu_links migration is in the menu_link_content module.
|
||||
id: menu_settings
|
||||
label: Menu UI configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
- Drupal 7
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- menu_override_parent_selector
|
||||
process:
|
||||
override_parent_selector: menu_override_parent_selector
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: menu_ui.settings
|
78
web/core/modules/menu_ui/src/Controller/MenuController.php
Normal file
78
web/core/modules/menu_ui/src/Controller/MenuController.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Menu\MenuParentFormSelectorInterface;
|
||||
use Drupal\system\MenuInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Returns responses for Menu routes.
|
||||
*/
|
||||
class MenuController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The menu parent form service.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuParentFormSelectorInterface
|
||||
*/
|
||||
protected $menuParentSelector;
|
||||
|
||||
/**
|
||||
* Creates a new MenuController object.
|
||||
*
|
||||
* @param \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_form
|
||||
* The menu parent form service.
|
||||
*/
|
||||
public function __construct(MenuParentFormSelectorInterface $menu_parent_form) {
|
||||
$this->menuParentSelector = $menu_parent_form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('menu.parent_form_selector'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the available menus and menu items as a JavaScript array.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request of the page.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* The available menu and menu items.
|
||||
*/
|
||||
public function getParentOptions(Request $request) {
|
||||
$available_menus = array();
|
||||
if ($menus = $request->request->get('menus')) {
|
||||
foreach ($menus as $menu) {
|
||||
$available_menus[$menu] = $menu;
|
||||
}
|
||||
}
|
||||
// @todo Update this to use the optional $cacheability parameter, so that
|
||||
// a cacheable JSON response can be sent.
|
||||
$options = $this->menuParentSelector->getParentSelectOptions('', $available_menus);
|
||||
|
||||
return new JsonResponse($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Route title callback.
|
||||
*
|
||||
* @param \Drupal\system\MenuInterface $menu
|
||||
* The menu entity.
|
||||
*
|
||||
* @return array
|
||||
* The menu label as a render array.
|
||||
*/
|
||||
public function menuTitle(MenuInterface $menu) {
|
||||
return ['#markup' => $menu->label(), '#allowed_tags' => Xss::getHtmlTagList()];
|
||||
}
|
||||
|
||||
}
|
96
web/core/modules/menu_ui/src/Form/MenuDeleteForm.php
Normal file
96
web/core/modules/menu_ui/src/Form/MenuDeleteForm.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Form;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Entity\EntityDeleteForm;
|
||||
use Drupal\Core\Menu\MenuLinkManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a confirmation form for deletion of a custom menu.
|
||||
*/
|
||||
class MenuDeleteForm extends EntityDeleteForm {
|
||||
|
||||
/**
|
||||
* The menu link manager.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
|
||||
*/
|
||||
protected $menuLinkManager;
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Constructs a new MenuDeleteForm.
|
||||
*
|
||||
* @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
|
||||
* The menu link manager.
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
*/
|
||||
public function __construct(MenuLinkManagerInterface $menu_link_manager, Connection $connection) {
|
||||
$this->menuLinkManager = $menu_link_manager;
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.menu.link'),
|
||||
$container->get('database')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
$caption = '';
|
||||
$num_links = $this->menuLinkManager->countMenuLinks($this->entity->id());
|
||||
if ($num_links) {
|
||||
$caption .= '<p>' . $this->formatPlural($num_links, '<strong>Warning:</strong> There is currently 1 menu link in %title. It will be deleted (system-defined items will be reset).', '<strong>Warning:</strong> There are currently @count menu links in %title. They will be deleted (system-defined links will be reset).', array('%title' => $this->entity->label())) . '</p>';
|
||||
}
|
||||
$caption .= '<p>' . t('This action cannot be undone.') . '</p>';
|
||||
return $caption;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function logDeletionMessage() {
|
||||
$this->logger('menu')->notice('Deleted custom menu %title and all its menu links.', array('%title' => $this->entity->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// Locked menus may not be deleted.
|
||||
if ($this->entity->isLocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete all links to the overview page for this menu.
|
||||
// @todo Add a more generic helper function to the menu link plugin
|
||||
// manager to remove links to a entity or other ID used as a route
|
||||
// parameter that is being removed. Also, consider moving this to
|
||||
// menu_ui.module as part of a generic response to entity deletion.
|
||||
// https://www.drupal.org/node/2310329
|
||||
$menu_links = $this->menuLinkManager->loadLinksByRoute('entity.menu.edit_form', array('menu' => $this->entity->id()), TRUE);
|
||||
foreach ($menu_links as $id => $link) {
|
||||
$this->menuLinkManager->removeDefinition($id);
|
||||
}
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
99
web/core/modules/menu_ui/src/Form/MenuLinkEditForm.php
Normal file
99
web/core/modules/menu_ui/src/Form/MenuLinkEditForm.php
Normal file
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Form;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ClassResolverInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Menu\MenuLinkInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a generic edit form for all menu link plugin types.
|
||||
*
|
||||
* The menu link plugin defines which class defines the corresponding form.
|
||||
*
|
||||
* @see \Drupal\Core\Menu\MenuLinkInterface::getFormClass()
|
||||
*/
|
||||
class MenuLinkEditForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The class resolver.
|
||||
*
|
||||
* @var \Drupal\Core\DependencyInjection\ClassResolverInterface
|
||||
*/
|
||||
protected $classResolver;
|
||||
|
||||
/**
|
||||
* Constructs a MenuLinkEditForm object.
|
||||
*
|
||||
* @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
|
||||
* The class resolver.
|
||||
*/
|
||||
public function __construct(ClassResolverInterface $class_resolver) {
|
||||
$this->classResolver = $class_resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('class_resolver')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'menu_link_edit';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param \Drupal\Core\Menu\MenuLinkInterface $menu_link_plugin
|
||||
* The plugin instance to use for this form.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, MenuLinkInterface $menu_link_plugin = NULL) {
|
||||
$form['menu_link_id'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $menu_link_plugin->getPluginId(),
|
||||
);
|
||||
$class_name = $menu_link_plugin->getFormClass();
|
||||
$form['#plugin_form'] = $this->classResolver->getInstanceFromDefinition($class_name);
|
||||
$form['#plugin_form']->setMenuLinkInstance($menu_link_plugin);
|
||||
|
||||
$form += $form['#plugin_form']->buildConfigurationForm($form, $form_state);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#button_type' => 'primary',
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$form['#plugin_form']->validateConfigurationForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$link = $form['#plugin_form']->submitConfigurationForm($form, $form_state);
|
||||
|
||||
drupal_set_message($this->t('The menu link has been saved.'));
|
||||
$form_state->setRedirect(
|
||||
'entity.menu.edit_form',
|
||||
array('menu' => $link->getMenuName())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
120
web/core/modules/menu_ui/src/Form/MenuLinkResetForm.php
Normal file
120
web/core/modules/menu_ui/src/Form/MenuLinkResetForm.php
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Form;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Menu\MenuLinkManagerInterface;
|
||||
use Drupal\Core\Menu\MenuLinkInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a confirmation form for resetting a single modified menu link.
|
||||
*/
|
||||
class MenuLinkResetForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The menu link manager.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
|
||||
*/
|
||||
protected $menuLinkManager;
|
||||
|
||||
/**
|
||||
* The menu link.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkInterface
|
||||
*/
|
||||
protected $link;
|
||||
|
||||
/**
|
||||
* Constructs a MenuLinkResetForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
|
||||
* The menu link manager.
|
||||
*/
|
||||
public function __construct(MenuLinkManagerInterface $menu_link_manager) {
|
||||
$this->menuLinkManager = $menu_link_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.menu.link')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'menu_link_reset_confirm';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to reset the link %item to its default values?', array('%item' => $this->link->getTitle()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.menu.edit_form', array(
|
||||
'menu' => $this->link->getMenuName(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->t('Any customizations will be lost. This action cannot be undone.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Reset');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, MenuLinkInterface $menu_link_plugin = NULL) {
|
||||
$this->link = $menu_link_plugin;
|
||||
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->link = $this->menuLinkManager->resetLink($this->link->getPluginId());
|
||||
drupal_set_message($this->t('The menu link was reset to its default settings.'));
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access based on whether the link can be reset.
|
||||
*
|
||||
* @param \Drupal\Core\Menu\MenuLinkInterface $menu_link_plugin
|
||||
* The menu link plugin being checked.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function linkIsResettable(MenuLinkInterface $menu_link_plugin) {
|
||||
return AccessResult::allowedIf($menu_link_plugin->isResettable())->setCacheMaxAge(0);
|
||||
}
|
||||
|
||||
}
|
489
web/core/modules/menu_ui/src/MenuForm.php
Normal file
489
web/core/modules/menu_ui/src/MenuForm.php
Normal file
|
@ -0,0 +1,489 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Link;
|
||||
use Drupal\Core\Menu\MenuLinkManagerInterface;
|
||||
use Drupal\Core\Menu\MenuLinkTreeElement;
|
||||
use Drupal\Core\Menu\MenuLinkTreeInterface;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Utility\LinkGeneratorInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Base form for menu edit forms.
|
||||
*/
|
||||
class MenuForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* The factory for entity queries.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $entityQueryFactory;
|
||||
|
||||
/**
|
||||
* The menu link manager.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
|
||||
*/
|
||||
protected $menuLinkManager;
|
||||
|
||||
/**
|
||||
* The menu tree service.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkTreeInterface
|
||||
*/
|
||||
protected $menuTree;
|
||||
|
||||
/**
|
||||
* The link generator.
|
||||
*
|
||||
* @var \Drupal\Core\Utility\LinkGeneratorInterface
|
||||
*/
|
||||
protected $linkGenerator;
|
||||
|
||||
/**
|
||||
* The overview tree form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $overviewTreeForm = array('#tree' => TRUE);
|
||||
|
||||
/**
|
||||
* Constructs a MenuForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\Query\QueryFactory $entity_query_factory
|
||||
* The factory for entity queries.
|
||||
* @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
|
||||
* The menu link manager.
|
||||
* @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree
|
||||
* The menu tree service.
|
||||
* @param \Drupal\Core\Utility\LinkGeneratorInterface $link_generator
|
||||
* The link generator.
|
||||
*/
|
||||
public function __construct(QueryFactory $entity_query_factory, MenuLinkManagerInterface $menu_link_manager, MenuLinkTreeInterface $menu_tree, LinkGeneratorInterface $link_generator) {
|
||||
$this->entityQueryFactory = $entity_query_factory;
|
||||
$this->menuLinkManager = $menu_link_manager;
|
||||
$this->menuTree = $menu_tree;
|
||||
$this->linkGenerator = $link_generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.query'),
|
||||
$container->get('plugin.manager.menu.link'),
|
||||
$container->get('menu.link_tree'),
|
||||
$container->get('link_generator')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$menu = $this->entity;
|
||||
|
||||
if ($this->operation == 'edit') {
|
||||
$form['#title'] = $this->t('Edit menu %label', array('%label' => $menu->label()));
|
||||
}
|
||||
|
||||
$form['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Title'),
|
||||
'#default_value' => $menu->label(),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['id'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#title' => $this->t('Menu name'),
|
||||
'#default_value' => $menu->id(),
|
||||
'#maxlength' => MENU_MAX_MENU_NAME_LENGTH_UI,
|
||||
'#description' => $this->t('A unique name to construct the URL for the menu. It must only contain lowercase letters, numbers and hyphens.'),
|
||||
'#machine_name' => array(
|
||||
'exists' => array($this, 'menuNameExists'),
|
||||
'source' => array('label'),
|
||||
'replace_pattern' => '[^a-z0-9-]+',
|
||||
'replace' => '-',
|
||||
),
|
||||
// A menu's machine name cannot be changed.
|
||||
'#disabled' => !$menu->isNew() || $menu->isLocked(),
|
||||
);
|
||||
$form['description'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Administrative summary'),
|
||||
'#maxlength' => 512,
|
||||
'#default_value' => $menu->getDescription(),
|
||||
);
|
||||
|
||||
$form['langcode'] = array(
|
||||
'#type' => 'language_select',
|
||||
'#title' => t('Menu language'),
|
||||
'#languages' => LanguageInterface::STATE_ALL,
|
||||
'#default_value' => $menu->language()->getId(),
|
||||
);
|
||||
|
||||
// Add menu links administration form for existing menus.
|
||||
if (!$menu->isNew() || $menu->isLocked()) {
|
||||
// Form API supports constructing and validating self-contained sections
|
||||
// within forms, but does not allow handling the form section's submission
|
||||
// equally separated yet. Therefore, we use a $form_state key to point to
|
||||
// the parents of the form section.
|
||||
// @see self::submitOverviewForm()
|
||||
$form_state->set('menu_overview_form_parents', ['links']);
|
||||
$form['links'] = array();
|
||||
$form['links'] = $this->buildOverviewForm($form['links'], $form_state);
|
||||
}
|
||||
|
||||
return parent::form($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a menu name already exists.
|
||||
*
|
||||
* @param string $value
|
||||
* The name of the menu.
|
||||
*
|
||||
* @return bool
|
||||
* Returns TRUE if the menu already exists, FALSE otherwise.
|
||||
*/
|
||||
public function menuNameExists($value) {
|
||||
// Check first to see if a menu with this ID exists.
|
||||
if ($this->entityQueryFactory->get('menu')->condition('id', $value)->range(0, 1)->count()->execute()) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Check for a link assigned to this menu.
|
||||
return $this->menuLinkManager->menuNameInUse($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$menu = $this->entity;
|
||||
$status = $menu->save();
|
||||
$edit_link = $this->entity->link($this->t('Edit'));
|
||||
if ($status == SAVED_UPDATED) {
|
||||
drupal_set_message($this->t('Menu %label has been updated.', array('%label' => $menu->label())));
|
||||
$this->logger('menu')->notice('Menu %label has been updated.', array('%label' => $menu->label(), 'link' => $edit_link));
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('Menu %label has been added.', array('%label' => $menu->label())));
|
||||
$this->logger('menu')->notice('Menu %label has been added.', array('%label' => $menu->label(), 'link' => $edit_link));
|
||||
}
|
||||
|
||||
$form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
|
||||
if (!$this->entity->isNew() || $this->entity->isLocked()) {
|
||||
$this->submitOverviewForm($form, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form constructor to edit an entire menu tree at once.
|
||||
*
|
||||
* Shows for one menu the menu links accessible to the current user and
|
||||
* relevant operations.
|
||||
*
|
||||
* This form constructor can be integrated as a section into another form. It
|
||||
* relies on the following keys in $form_state:
|
||||
* - menu: A menu entity.
|
||||
* - menu_overview_form_parents: An array containing the parent keys to this
|
||||
* form.
|
||||
* Forms integrating this section should call menu_overview_form_submit() from
|
||||
* their form submit handler.
|
||||
*/
|
||||
protected function buildOverviewForm(array &$form, FormStateInterface $form_state) {
|
||||
// Ensure that menu_overview_form_submit() knows the parents of this form
|
||||
// section.
|
||||
if (!$form_state->has('menu_overview_form_parents')) {
|
||||
$form_state->set('menu_overview_form_parents', []);
|
||||
}
|
||||
|
||||
$form['#attached']['library'][] = 'menu_ui/drupal.menu_ui.adminforms';
|
||||
|
||||
$tree = $this->menuTree->load($this->entity->id(), new MenuTreeParameters());
|
||||
|
||||
// We indicate that a menu administrator is running the menu access check.
|
||||
$this->getRequest()->attributes->set('_menu_admin', TRUE);
|
||||
$manipulators = array(
|
||||
array('callable' => 'menu.default_tree_manipulators:checkAccess'),
|
||||
array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
|
||||
);
|
||||
$tree = $this->menuTree->transform($tree, $manipulators);
|
||||
$this->getRequest()->attributes->set('_menu_admin', FALSE);
|
||||
|
||||
// Determine the delta; the number of weights to be made available.
|
||||
$count = function(array $tree) {
|
||||
$sum = function ($carry, MenuLinkTreeElement $item) {
|
||||
return $carry + $item->count();
|
||||
};
|
||||
return array_reduce($tree, $sum);
|
||||
};
|
||||
$delta = max($count($tree), 50);
|
||||
|
||||
$form['links'] = array(
|
||||
'#type' => 'table',
|
||||
'#theme' => 'table__menu_overview',
|
||||
'#header' => array(
|
||||
$this->t('Menu link'),
|
||||
array(
|
||||
'data' => $this->t('Enabled'),
|
||||
'class' => array('checkbox'),
|
||||
),
|
||||
$this->t('Weight'),
|
||||
array(
|
||||
'data' => $this->t('Operations'),
|
||||
'colspan' => 3,
|
||||
),
|
||||
),
|
||||
'#attributes' => array(
|
||||
'id' => 'menu-overview',
|
||||
),
|
||||
'#tabledrag' => array(
|
||||
array(
|
||||
'action' => 'match',
|
||||
'relationship' => 'parent',
|
||||
'group' => 'menu-parent',
|
||||
'subgroup' => 'menu-parent',
|
||||
'source' => 'menu-id',
|
||||
'hidden' => TRUE,
|
||||
'limit' => \Drupal::menuTree()->maxDepth() - 1,
|
||||
),
|
||||
array(
|
||||
'action' => 'order',
|
||||
'relationship' => 'sibling',
|
||||
'group' => 'menu-weight',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$form['links']['#empty'] = $this->t('There are no menu links yet. <a href=":url">Add link</a>.', [
|
||||
':url' => $this->url('entity.menu.add_link_form', ['menu' => $this->entity->id()], [
|
||||
'query' => ['destination' => $this->entity->url('edit-form')],
|
||||
]),
|
||||
]);
|
||||
$links = $this->buildOverviewTreeForm($tree, $delta);
|
||||
foreach (Element::children($links) as $id) {
|
||||
if (isset($links[$id]['#item'])) {
|
||||
$element = $links[$id];
|
||||
|
||||
$form['links'][$id]['#item'] = $element['#item'];
|
||||
|
||||
// TableDrag: Mark the table row as draggable.
|
||||
$form['links'][$id]['#attributes'] = $element['#attributes'];
|
||||
$form['links'][$id]['#attributes']['class'][] = 'draggable';
|
||||
|
||||
// TableDrag: Sort the table row according to its existing/configured weight.
|
||||
$form['links'][$id]['#weight'] = $element['#item']->link->getWeight();
|
||||
|
||||
// Add special classes to be used for tabledrag.js.
|
||||
$element['parent']['#attributes']['class'] = array('menu-parent');
|
||||
$element['weight']['#attributes']['class'] = array('menu-weight');
|
||||
$element['id']['#attributes']['class'] = array('menu-id');
|
||||
|
||||
$form['links'][$id]['title'] = array(
|
||||
array(
|
||||
'#theme' => 'indentation',
|
||||
'#size' => $element['#item']->depth - 1,
|
||||
),
|
||||
$element['title'],
|
||||
);
|
||||
$form['links'][$id]['enabled'] = $element['enabled'];
|
||||
$form['links'][$id]['enabled']['#wrapper_attributes']['class'] = array('checkbox', 'menu-enabled');
|
||||
|
||||
$form['links'][$id]['weight'] = $element['weight'];
|
||||
|
||||
// Operations (dropbutton) column.
|
||||
$form['links'][$id]['operations'] = $element['operations'];
|
||||
|
||||
$form['links'][$id]['id'] = $element['id'];
|
||||
$form['links'][$id]['parent'] = $element['parent'];
|
||||
}
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive helper function for buildOverviewForm().
|
||||
*
|
||||
* @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
|
||||
* The tree retrieved by \Drupal\Core\Menu\MenuLinkTreeInterface::load().
|
||||
* @param int $delta
|
||||
* The default number of menu items used in the menu weight selector is 50.
|
||||
*
|
||||
* @return array
|
||||
* The overview tree form.
|
||||
*/
|
||||
protected function buildOverviewTreeForm($tree, $delta) {
|
||||
$form = &$this->overviewTreeForm;
|
||||
$tree_access_cacheability = new CacheableMetadata();
|
||||
foreach ($tree as $element) {
|
||||
$tree_access_cacheability = $tree_access_cacheability->merge(CacheableMetadata::createFromObject($element->access));
|
||||
|
||||
// Only render accessible links.
|
||||
if (!$element->access->isAllowed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var \Drupal\Core\Menu\MenuLinkInterface $link */
|
||||
$link = $element->link;
|
||||
if ($link) {
|
||||
$id = 'menu_plugin_id:' . $link->getPluginId();
|
||||
$form[$id]['#item'] = $element;
|
||||
$form[$id]['#attributes'] = $link->isEnabled() ? array('class' => array('menu-enabled')) : array('class' => array('menu-disabled'));
|
||||
$form[$id]['title'] = Link::fromTextAndUrl($link->getTitle(), $link->getUrlObject())->toRenderable();
|
||||
if (!$link->isEnabled()) {
|
||||
$form[$id]['title']['#suffix'] = ' (' . $this->t('disabled') . ')';
|
||||
}
|
||||
// @todo Remove this in https://www.drupal.org/node/2568785.
|
||||
elseif ($id === 'menu_plugin_id:user.logout') {
|
||||
$form[$id]['title']['#suffix'] = ' (' . $this->t('<q>Log in</q> for anonymous users') . ')';
|
||||
}
|
||||
// @todo Remove this in https://www.drupal.org/node/2568785.
|
||||
elseif (($url = $link->getUrlObject()) && $url->isRouted() && $url->getRouteName() == 'user.page') {
|
||||
$form[$id]['title']['#suffix'] = ' (' . $this->t('logged in users only') . ')';
|
||||
}
|
||||
|
||||
$form[$id]['enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Enable @title menu link', array('@title' => $link->getTitle())),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => $link->isEnabled(),
|
||||
);
|
||||
$form[$id]['weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#delta' => $delta,
|
||||
'#default_value' => $link->getWeight(),
|
||||
'#title' => $this->t('Weight for @title', array('@title' => $link->getTitle())),
|
||||
'#title_display' => 'invisible',
|
||||
);
|
||||
$form[$id]['id'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => $link->getPluginId(),
|
||||
);
|
||||
$form[$id]['parent'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#default_value' => $link->getParent(),
|
||||
);
|
||||
// Build a list of operations.
|
||||
$operations = array();
|
||||
$operations['edit'] = array(
|
||||
'title' => $this->t('Edit'),
|
||||
);
|
||||
// Allow for a custom edit link per plugin.
|
||||
$edit_route = $link->getEditRoute();
|
||||
if ($edit_route) {
|
||||
$operations['edit']['url'] = $edit_route;
|
||||
// Bring the user back to the menu overview.
|
||||
$operations['edit']['query'] = $this->getDestinationArray();
|
||||
}
|
||||
else {
|
||||
// Fall back to the standard edit link.
|
||||
$operations['edit'] += array(
|
||||
'url' => Url::fromRoute('menu_ui.link_edit', ['menu_link_plugin' => $link->getPluginId()]),
|
||||
);
|
||||
}
|
||||
// Links can either be reset or deleted, not both.
|
||||
if ($link->isResettable()) {
|
||||
$operations['reset'] = array(
|
||||
'title' => $this->t('Reset'),
|
||||
'url' => Url::fromRoute('menu_ui.link_reset', ['menu_link_plugin' => $link->getPluginId()]),
|
||||
);
|
||||
}
|
||||
elseif ($delete_link = $link->getDeleteRoute()) {
|
||||
$operations['delete']['url'] = $delete_link;
|
||||
$operations['delete']['query'] = $this->getDestinationArray();
|
||||
$operations['delete']['title'] = $this->t('Delete');
|
||||
}
|
||||
if ($link->isTranslatable()) {
|
||||
$operations['translate'] = array(
|
||||
'title' => $this->t('Translate'),
|
||||
'url' => $link->getTranslateRoute(),
|
||||
);
|
||||
}
|
||||
$form[$id]['operations'] = array(
|
||||
'#type' => 'operations',
|
||||
'#links' => $operations,
|
||||
);
|
||||
}
|
||||
|
||||
if ($element->subtree) {
|
||||
$this->buildOverviewTreeForm($element->subtree, $delta);
|
||||
}
|
||||
}
|
||||
|
||||
$tree_access_cacheability
|
||||
->merge(CacheableMetadata::createFromRenderArray($form))
|
||||
->applyTo($form);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for the menu overview form.
|
||||
*
|
||||
* This function takes great care in saving parent items first, then items
|
||||
* underneath them. Saving items in the incorrect order can break the tree.
|
||||
*/
|
||||
protected function submitOverviewForm(array $complete_form, FormStateInterface $form_state) {
|
||||
// Form API supports constructing and validating self-contained sections
|
||||
// within forms, but does not allow to handle the form section's submission
|
||||
// equally separated yet. Therefore, we use a $form_state key to point to
|
||||
// the parents of the form section.
|
||||
$parents = $form_state->get('menu_overview_form_parents');
|
||||
$input = NestedArray::getValue($form_state->getUserInput(), $parents);
|
||||
$form = &NestedArray::getValue($complete_form, $parents);
|
||||
|
||||
// When dealing with saving menu items, the order in which these items are
|
||||
// saved is critical. If a changed child item is saved before its parent,
|
||||
// the child item could be saved with an invalid path past its immediate
|
||||
// parent. To prevent this, save items in the form in the same order they
|
||||
// are sent, ensuring parents are saved first, then their children.
|
||||
// See https://www.drupal.org/node/181126#comment-632270.
|
||||
$order = is_array($input) ? array_flip(array_keys($input)) : array();
|
||||
// Update our original form with the new order.
|
||||
$form = array_intersect_key(array_merge($order, $form), $form);
|
||||
|
||||
$fields = array('weight', 'parent', 'enabled');
|
||||
$form_links = $form['links'];
|
||||
foreach (Element::children($form_links) as $id) {
|
||||
if (isset($form_links[$id]['#item'])) {
|
||||
$element = $form_links[$id];
|
||||
$updated_values = array();
|
||||
// Update any fields that have changed in this menu item.
|
||||
foreach ($fields as $field) {
|
||||
if ($element[$field]['#value'] != $element[$field]['#default_value']) {
|
||||
$updated_values[$field] = $element[$field]['#value'];
|
||||
}
|
||||
}
|
||||
if ($updated_values) {
|
||||
// Use the ID from the actual plugin instance since the hidden value
|
||||
// in the form could be tampered with.
|
||||
$this->menuLinkManager->updateDefinition($element['#item']->link->getPLuginId(), $updated_values);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
69
web/core/modules/menu_ui/src/MenuListBuilder.php
Normal file
69
web/core/modules/menu_ui/src/MenuListBuilder.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of menu entities.
|
||||
*
|
||||
* @see \Drupal\system\Entity\Menu
|
||||
* @see menu_entity_info()
|
||||
*/
|
||||
class MenuListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['title'] = t('Title');
|
||||
$header['description'] = array(
|
||||
'data' => t('Description'),
|
||||
'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
|
||||
);
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$row['title'] = array(
|
||||
'data' => $entity->label(),
|
||||
'class' => array('menu-label'),
|
||||
);
|
||||
$row['description']['data'] = ['#markup' => $entity->getDescription()];
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOperations(EntityInterface $entity) {
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
|
||||
if (isset($operations['edit'])) {
|
||||
$operations['edit']['title'] = t('Edit menu');
|
||||
$operations['add'] = array(
|
||||
'title' => t('Add link'),
|
||||
'weight' => 20,
|
||||
'url' => $entity->urlInfo('add-link-form'),
|
||||
);
|
||||
}
|
||||
if (isset($operations['delete'])) {
|
||||
$operations['delete']['title'] = t('Delete menu');
|
||||
}
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
$build = parent::render();
|
||||
$build['#attached']['library'][] = "menu_ui/drupal.menu_ui.adminforms";
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Plugin\Menu\LocalAction;
|
||||
|
||||
use Drupal\Core\Menu\LocalActionDefault;
|
||||
use Drupal\Core\Routing\RedirectDestinationInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Modifies the 'Add link' local action to add a destination.
|
||||
*/
|
||||
class MenuLinkAdd extends LocalActionDefault {
|
||||
|
||||
/**
|
||||
* The redirect destination.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RedirectDestinationInterface
|
||||
*/
|
||||
private $redirectDestination;
|
||||
|
||||
/**
|
||||
* Constructs a MenuLinkAdd object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
|
||||
* The route provider to load routes by name.
|
||||
* @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
|
||||
* The redirect destination.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, RedirectDestinationInterface $redirect_destination) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider);
|
||||
|
||||
$this->redirectDestination = $redirect_destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('router.route_provider'),
|
||||
$container->get('redirect.destination')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOptions(RouteMatchInterface $route_match) {
|
||||
$options = parent::getOptions($route_match);
|
||||
// Append the current path as destination to the query string.
|
||||
$options['query']['destination'] = $this->redirectDestination->get();
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
113
web/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
Normal file
113
web/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
Normal file
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Tests;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\system\Tests\Cache\PageCacheTagsTestBase;
|
||||
use Drupal\system\Entity\Menu;
|
||||
|
||||
/**
|
||||
* Tests the Menu and Menu Link entities' cache tags.
|
||||
*
|
||||
* @group menu_ui
|
||||
*/
|
||||
class MenuCacheTagsTest extends PageCacheTagsTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('menu_ui', 'block', 'test_page_test');
|
||||
|
||||
/**
|
||||
* Tests cache tags presence and invalidation of the Menu entity.
|
||||
*
|
||||
* Tests the following cache tags:
|
||||
* - "menu:<menu ID>"
|
||||
*/
|
||||
public function testMenuBlock() {
|
||||
$url = Url::fromRoute('test_page_test.test_page');
|
||||
|
||||
// Create a Llama menu, add a link to it and place the corresponding block.
|
||||
$menu = Menu::create(array(
|
||||
'id' => 'llama',
|
||||
'label' => 'Llama',
|
||||
'description' => 'Description text',
|
||||
));
|
||||
$menu->save();
|
||||
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
// Move a link into the new menu.
|
||||
$menu_link = $menu_link_manager->updateDefinition('test_page_test.test_page', array('menu_name' => 'llama', 'parent' => ''));
|
||||
$block = $this->drupalPlaceBlock('system_menu_block:llama', array('label' => 'Llama', 'provider' => 'system', 'region' => 'footer'));
|
||||
|
||||
// Prime the page cache.
|
||||
$this->verifyPageCache($url, 'MISS');
|
||||
|
||||
// Verify a cache hit, but also the presence of the correct cache tags.
|
||||
$expected_tags = array(
|
||||
'rendered',
|
||||
'block_view',
|
||||
'config:block_list',
|
||||
'config:block.block.' . $block->id(),
|
||||
'config:system.menu.llama',
|
||||
// The cache contexts associated with the (in)accessible menu links are
|
||||
// bubbled.
|
||||
'config:user.role.anonymous',
|
||||
);
|
||||
$this->verifyPageCache($url, 'HIT', $expected_tags);
|
||||
|
||||
// Verify that after modifying the menu, there is a cache miss.
|
||||
$this->pass('Test modification of menu.', 'Debug');
|
||||
$menu->set('label', 'Awesome llama');
|
||||
$menu->save();
|
||||
$this->verifyPageCache($url, 'MISS');
|
||||
|
||||
// Verify a cache hit.
|
||||
$this->verifyPageCache($url, 'HIT');
|
||||
|
||||
// Verify that after modifying the menu link weight, there is a cache miss.
|
||||
$menu_link_manager->updateDefinition('test_page_test.test_page', array('weight' => -10));
|
||||
$this->pass('Test modification of menu link.', 'Debug');
|
||||
$this->verifyPageCache($url, 'MISS');
|
||||
|
||||
// Verify a cache hit.
|
||||
$this->verifyPageCache($url, 'HIT');
|
||||
|
||||
// Verify that after adding a menu link, there is a cache miss.
|
||||
$this->pass('Test addition of menu link.', 'Debug');
|
||||
$menu_link_2 = MenuLinkContent::create(array(
|
||||
'id' => '',
|
||||
'parent' => '',
|
||||
'title' => 'Alpaca',
|
||||
'menu_name' => 'llama',
|
||||
'link' => [[
|
||||
'uri' => 'internal:/',
|
||||
]],
|
||||
'bundle' => 'menu_name',
|
||||
));
|
||||
$menu_link_2->save();
|
||||
$this->verifyPageCache($url, 'MISS');
|
||||
|
||||
// Verify a cache hit.
|
||||
$this->verifyPageCache($url, 'HIT');
|
||||
|
||||
// Verify that after resetting the first menu link, there is a cache miss.
|
||||
$this->pass('Test reset of menu link.', 'Debug');
|
||||
$this->assertTrue($menu_link->isResettable(), 'First link can be reset');
|
||||
$menu_link = $menu_link_manager->resetLink($menu_link->getPluginId());
|
||||
$this->verifyPageCache($url, 'MISS');
|
||||
|
||||
// Verify a cache hit.
|
||||
$this->verifyPageCache($url, 'HIT', $expected_tags);
|
||||
|
||||
// Verify that after deleting the menu, there is a cache miss.
|
||||
$this->pass('Test deletion of menu.', 'Debug');
|
||||
$menu->delete();
|
||||
$this->verifyPageCache($url, 'MISS');
|
||||
|
||||
// Verify a cache hit.
|
||||
$this->verifyPageCache($url, 'HIT', ['config:block_list', 'config:user.role.anonymous', 'rendered']);
|
||||
}
|
||||
|
||||
}
|
133
web/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
Normal file
133
web/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
Normal file
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\language\Entity\ContentLanguageSettings;
|
||||
|
||||
/**
|
||||
* Tests for menu_ui language settings.
|
||||
*
|
||||
* Create menu and menu links in non-English language, and edit language
|
||||
* settings.
|
||||
*
|
||||
* @group menu_ui
|
||||
*/
|
||||
class MenuLanguageTest extends MenuWebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('language');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(array('access administration pages', 'administer menu')));
|
||||
|
||||
// Add some custom languages.
|
||||
foreach (array('aa', 'bb', 'cc', 'cs') as $language_code) {
|
||||
ConfigurableLanguage::create(array(
|
||||
'id' => $language_code,
|
||||
'label' => $this->randomMachineName(),
|
||||
))->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests menu language settings and the defaults for menu link items.
|
||||
*/
|
||||
function testMenuLanguage() {
|
||||
// Create a test menu to test the various language-related settings.
|
||||
// Machine name has to be lowercase.
|
||||
$menu_name = Unicode::strtolower($this->randomMachineName(16));
|
||||
$label = $this->randomString();
|
||||
$edit = array(
|
||||
'id' => $menu_name,
|
||||
'description' => '',
|
||||
'label' => $label,
|
||||
'langcode' => 'aa',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/menu/add', $edit, t('Save'));
|
||||
ContentLanguageSettings::loadByEntityTypeBundle('menu_link_content', 'menu_link_content')
|
||||
->setDefaultLangcode('bb')
|
||||
->setLanguageAlterable(TRUE)
|
||||
->save();
|
||||
|
||||
// Check menu language.
|
||||
$this->assertOptionSelected('edit-langcode', $edit['langcode'], 'The menu language was correctly selected.');
|
||||
|
||||
// Test menu link language.
|
||||
$link_path = '/';
|
||||
|
||||
// Add a menu link.
|
||||
$link_title = $this->randomString();
|
||||
$edit = array(
|
||||
'title[0][value]' => $link_title,
|
||||
'link[0][uri]' => $link_path,
|
||||
);
|
||||
$this->drupalPostForm("admin/structure/menu/manage/$menu_name/add", $edit, t('Save'));
|
||||
// Check the link was added with the correct menu link default language.
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link_content', array('title' => $link_title));
|
||||
$menu_link = reset($menu_links);
|
||||
$this->assertMenuLink($menu_link->getPluginId(), array(
|
||||
'menu_name' => $menu_name,
|
||||
'route_name' => '<front>',
|
||||
'langcode' => 'bb',
|
||||
));
|
||||
|
||||
// Edit menu link default, changing it to cc.
|
||||
ContentLanguageSettings::loadByEntityTypeBundle('menu_link_content', 'menu_link_content')
|
||||
->setDefaultLangcode('cc')
|
||||
->setLanguageAlterable(TRUE)
|
||||
->save();
|
||||
|
||||
// Add a menu link.
|
||||
$link_title = $this->randomString();
|
||||
$edit = array(
|
||||
'title[0][value]' => $link_title,
|
||||
'link[0][uri]' => $link_path,
|
||||
);
|
||||
$this->drupalPostForm("admin/structure/menu/manage/$menu_name/add", $edit, t('Save'));
|
||||
// Check the link was added with the correct new menu link default language.
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link_content', array('title' => $link_title));
|
||||
$menu_link = reset($menu_links);
|
||||
$this->assertMenuLink($menu_link->getPluginId(), array(
|
||||
'menu_name' => $menu_name,
|
||||
'route_name' => '<front>',
|
||||
'langcode' => 'cc',
|
||||
));
|
||||
|
||||
// Now change the language of the new link to 'bb'.
|
||||
$edit = array(
|
||||
'langcode[0][value]' => 'bb',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/menu/item/' . $menu_link->id() . '/edit', $edit, t('Save'));
|
||||
$this->assertMenuLink($menu_link->getPluginId(), array(
|
||||
'menu_name' => $menu_name,
|
||||
'route_name' => '<front>',
|
||||
'langcode' => 'bb',
|
||||
));
|
||||
|
||||
// Saving menu link items ends up on the edit menu page. To check the menu
|
||||
// link has the correct language default on edit, go to the menu link edit
|
||||
// page first.
|
||||
$this->drupalGet('admin/structure/menu/item/' . $menu_link->id() . '/edit');
|
||||
// Check that the language selector has the correct default value.
|
||||
$this->assertOptionSelected('edit-langcode-0-value', 'bb', 'The menu link language was correctly selected.');
|
||||
|
||||
// Edit menu to hide the language select on menu link item add.
|
||||
ContentLanguageSettings::loadByEntityTypeBundle('menu_link_content', 'menu_link_content')
|
||||
->setDefaultLangcode('cc')
|
||||
->setLanguageAlterable(FALSE)
|
||||
->save();
|
||||
|
||||
// Check that the language selector is not available on menu link add page.
|
||||
$this->drupalGet("admin/structure/menu/manage/$menu_name/add");
|
||||
$this->assertNoField('edit-langcode-0-value', 'The language selector field was hidden the page');
|
||||
}
|
||||
|
||||
}
|
67
web/core/modules/menu_ui/src/Tests/MenuLinkReorderTest.php
Normal file
67
web/core/modules/menu_ui/src/Tests/MenuLinkReorderTest.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Reorder menu items.
|
||||
*
|
||||
* @group menu_ui
|
||||
*/
|
||||
class MenuLinkReorderTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* An administrator user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $administrator;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('menu_ui', 'test_page_test', 'node', 'block');
|
||||
|
||||
/**
|
||||
* Test creating, editing, deleting menu links via node form widget.
|
||||
*/
|
||||
function testDefaultMenuLinkReorder() {
|
||||
|
||||
// Add the main menu block.
|
||||
$this->drupalPlaceBlock('system_menu_block:main');
|
||||
|
||||
// Assert that the Home link is available.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertLink('Home');
|
||||
|
||||
// The administrator user that can re-order menu links.
|
||||
$this->administrator = $this->drupalCreateUser(array(
|
||||
'administer site configuration',
|
||||
'access administration pages',
|
||||
'administer menu',
|
||||
));
|
||||
$this->drupalLogin($this->administrator);
|
||||
|
||||
// Change the weight of the link to a non default value.
|
||||
$edit = array(
|
||||
'links[menu_plugin_id:test_page_test.front_page][weight]' => -10,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/menu/manage/main', $edit, t('Save'));
|
||||
|
||||
// The link is still there.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertLink('Home');
|
||||
|
||||
// Clear all caches.
|
||||
$this->drupalPostForm('admin/config/development/performance', [], t('Clear all caches'));
|
||||
|
||||
// Clearing all caches should not affect the state of the menu link.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertLink('Home');
|
||||
|
||||
}
|
||||
|
||||
}
|
341
web/core/modules/menu_ui/src/Tests/MenuNodeTest.php
Normal file
341
web/core/modules/menu_ui/src/Tests/MenuNodeTest.php
Normal file
|
@ -0,0 +1,341 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Add, edit, and delete a node with menu link.
|
||||
*
|
||||
* @group menu_ui
|
||||
*/
|
||||
class MenuNodeTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* An editor user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $editor;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('menu_ui', 'test_page_test', 'node', 'block', 'locale', 'language', 'content_translation');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('system_menu_block:main');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
|
||||
|
||||
$this->editor = $this->drupalCreateUser(array(
|
||||
'access administration pages',
|
||||
'administer content types',
|
||||
'administer menu',
|
||||
'create page content',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
'create content translations',
|
||||
'update content translations',
|
||||
'delete content translations',
|
||||
'translate any entity',
|
||||
));
|
||||
$this->drupalLogin($this->editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating, editing, deleting menu links via node form widget.
|
||||
*/
|
||||
function testMenuNodeFormWidget() {
|
||||
// Verify that cacheability metadata is bubbled from the menu link tree
|
||||
// access checking that is performed when determining the "default parent
|
||||
// item" options in menu_ui_form_node_type_form_alter(). The "log out" link
|
||||
// adds the "user.roles:authenticated" cache context.
|
||||
$this->drupalGet('admin/structure/types/manage/page');
|
||||
$this->assertCacheContext('user.roles:authenticated');
|
||||
|
||||
// Verify that the menu link title has the correct maxlength.
|
||||
$max_length = \Drupal::entityManager()->getBaseFieldDefinitions('menu_link_content')['title']->getSetting('max_length');
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertPattern('/<input .* id="edit-menu-title" .* maxlength="' . $max_length . '" .* \/>/', 'Menu link title field has correct maxlength in node add form.');
|
||||
|
||||
// Disable the default main menu, so that no menus are enabled.
|
||||
$edit = array(
|
||||
'menu_options[main]' => FALSE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
|
||||
// Verify that no menu settings are displayed and nodes can be created.
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertText(t('Create Basic page'));
|
||||
$this->assertNoText(t('Menu settings'));
|
||||
$node_title = $this->randomMachineName();
|
||||
$edit = array(
|
||||
'title[0][value]' => $node_title,
|
||||
'body[0][value]' => $this->randomString(),
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($node_title);
|
||||
$this->assertEqual($node->getTitle(), $edit['title[0][value]']);
|
||||
|
||||
// Test that we cannot set a menu item from a menu that is not set as
|
||||
// available.
|
||||
$edit = array(
|
||||
'menu_options[tools]' => 1,
|
||||
'menu_parent' => 'main:',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
$this->assertText(t('The selected menu item is not under one of the selected menus.'));
|
||||
$this->assertNoRaw(t('The content type %name has been updated.', array('%name' => 'Basic page')));
|
||||
|
||||
// Enable Tools menu as available menu.
|
||||
$edit = array(
|
||||
'menu_options[main]' => 1,
|
||||
'menu_options[tools]' => 1,
|
||||
'menu_parent' => 'main:',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
$this->assertRaw(t('The content type %name has been updated.', array('%name' => 'Basic page')));
|
||||
|
||||
// Test that we can preview a node that will create a menu item.
|
||||
$edit = array(
|
||||
'title[0][value]' => $node_title,
|
||||
'menu[enabled]' => 1,
|
||||
'menu[title]' => 'Test preview',
|
||||
);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Preview'));
|
||||
|
||||
// Create a node.
|
||||
$node_title = $this->randomMachineName();
|
||||
$edit = array(
|
||||
'title[0][value]' => $node_title,
|
||||
'body[0][value]' => $this->randomString(),
|
||||
);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($node_title);
|
||||
// Assert that there is no link for the node.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertNoLink($node_title);
|
||||
|
||||
// Edit the node, enable the menu link setting, but skip the link title.
|
||||
$edit = array(
|
||||
'menu[enabled]' => 1,
|
||||
);
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
// Assert that there is no link for the node.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertNoLink($node_title);
|
||||
|
||||
// Use not only the save button, but also the two special buttons:
|
||||
// 'Save and publish' as well as 'Save and keep published'.
|
||||
// These buttons just appear for 'administer nodes' users.
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'access administration pages',
|
||||
'administer content types',
|
||||
'administer nodes',
|
||||
'administer menu',
|
||||
'create page content',
|
||||
'edit any page content',
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
foreach (['Save and unpublish' => FALSE, 'Save and keep unpublished' => FALSE, 'Save and publish' => TRUE, 'Save and keep published' => TRUE] as $submit => $visible) {
|
||||
$edit = [
|
||||
'menu[enabled]' => 1,
|
||||
'menu[title]' => $node_title,
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, $submit);
|
||||
// Assert that the link exists.
|
||||
$this->drupalGet('test-page');
|
||||
if ($visible) {
|
||||
$this->assertLink($node_title, 0, 'Found a menu link after submitted with ' . $submit);
|
||||
}
|
||||
else {
|
||||
$this->assertNoLink($node_title, 'Found no menu link after submitted with ' . $submit);
|
||||
}
|
||||
}
|
||||
|
||||
// Log back in as normal user.
|
||||
$this->drupalLogin($this->editor);
|
||||
// Edit the node and create a menu link.
|
||||
$edit = array(
|
||||
'menu[enabled]' => 1,
|
||||
'menu[title]' => $node_title,
|
||||
'menu[weight]' => 17,
|
||||
);
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
// Assert that the link exists.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertLink($node_title);
|
||||
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldById('edit-menu-weight', 17, 'Menu weight correct in edit form');
|
||||
$this->assertPattern('/<input .* id="edit-menu-title" .* maxlength="' . $max_length . '" .* \/>/', 'Menu link title field has correct maxlength in node edit form.');
|
||||
|
||||
// Disable the menu link, then edit the node--the link should stay disabled.
|
||||
$link_id = menu_ui_get_menu_link_defaults($node)['entity_id'];
|
||||
/** @var \Drupal\menu_link_content\Entity\MenuLinkContent $link */
|
||||
$link = MenuLinkContent::load($link_id);
|
||||
$link->set('enabled', FALSE);
|
||||
$link->save();
|
||||
$this->drupalPostForm($node->urlInfo('edit-form'), $edit, t('Save'));
|
||||
$link = MenuLinkContent::load($link_id);
|
||||
$this->assertFalse($link->isEnabled(), 'Saving a node with a disabled menu link keeps the menu link disabled.');
|
||||
|
||||
// Edit the node and remove the menu link.
|
||||
$edit = array(
|
||||
'menu[enabled]' => FALSE,
|
||||
);
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
// Assert that there is no link for the node.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertNoLink($node_title);
|
||||
|
||||
// Add a menu link to the Administration menu.
|
||||
$item = MenuLinkContent::create(array(
|
||||
'link' => [['uri' => 'entity:node/' . $node->id()]],
|
||||
'title' => $this->randomMachineName(16),
|
||||
'menu_name' => 'admin',
|
||||
));
|
||||
$item->save();
|
||||
|
||||
// Assert that disabled Administration menu is not shown on the
|
||||
// node/$nid/edit page.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertText('Provide a menu link', 'Link in not allowed menu not shown in node edit form');
|
||||
// Assert that the link is still in the Administration menu after save.
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
$link = MenuLinkContent::load($item->id());
|
||||
$this->assertTrue($link, 'Link in not allowed menu still exists after saving node');
|
||||
|
||||
// Move the menu link back to the Tools menu.
|
||||
$item->menu_name->value = 'tools';
|
||||
$item->save();
|
||||
// Create a second node.
|
||||
$child_node = $this->drupalCreateNode(array('type' => 'article'));
|
||||
// Assign a menu link to the second node, being a child of the first one.
|
||||
$child_item = MenuLinkContent::create(array(
|
||||
'link' => [['uri' => 'entity:node/' . $child_node->id()]],
|
||||
'title' => $this->randomMachineName(16),
|
||||
'parent' => $item->getPluginId(),
|
||||
'menu_name' => $item->getMenuName(),
|
||||
));
|
||||
$child_item->save();
|
||||
// Edit the first node.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
// Assert that it is not possible to set the parent of the first node to itself or the second node.
|
||||
$this->assertNoOption('edit-menu-menu-parent', 'tools:' . $item->getPluginId());
|
||||
$this->assertNoOption('edit-menu-menu-parent', 'tools:' . $child_item->getPluginId());
|
||||
// Assert that unallowed Administration menu is not available in options.
|
||||
$this->assertNoOption('edit-menu-menu-parent', 'admin:');
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing correct loading and saving of menu links via node form widget in a multilingual environment.
|
||||
*/
|
||||
function testMultilingualMenuNodeFormWidget() {
|
||||
// Setup languages.
|
||||
$langcodes = array('de');
|
||||
foreach ($langcodes as $langcode) {
|
||||
ConfigurableLanguage::createFromLangcode($langcode)->save();
|
||||
}
|
||||
array_unshift($langcodes, \Drupal::languageManager()->getDefaultLanguage()->getId());
|
||||
|
||||
$config = \Drupal::service('config.factory')->getEditable('language.negotiation');
|
||||
// Ensure path prefix is used to determine the language.
|
||||
$config->set('url.source', 'path_prefix');
|
||||
// Ensure that there's a path prefix set for english as well.
|
||||
$config->set('url.prefixes.' . $langcodes[0], $langcodes[0]);
|
||||
$config->save();
|
||||
|
||||
$this->rebuildContainer();
|
||||
|
||||
$languages = array();
|
||||
foreach ($langcodes as $langcode) {
|
||||
$languages[$langcode] = ConfigurableLanguage::load($langcode);
|
||||
}
|
||||
|
||||
// Use a UI form submission to make the node type and menu link content entity translatable.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$edit = array(
|
||||
'entity_types[node]' => TRUE,
|
||||
'entity_types[menu_link_content]' => TRUE,
|
||||
'settings[node][page][settings][language][language_alterable]' => TRUE,
|
||||
'settings[node][page][translatable]' => TRUE,
|
||||
'settings[node][page][fields][title]' => TRUE,
|
||||
'settings[menu_link_content][menu_link_content][translatable]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
|
||||
|
||||
// Log out and back in as normal user.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->editor);
|
||||
|
||||
// Create a node.
|
||||
$node_title = $this->randomMachineName(8);
|
||||
$node = Node::create([
|
||||
'type' => 'page',
|
||||
'title' => $node_title,
|
||||
'body' => $this->randomMachineName(16),
|
||||
'uid' => $this->editor->id(),
|
||||
'status' => 1,
|
||||
'langcode' => $langcodes[0],
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
// Create translation.
|
||||
$translated_node_title = $this->randomMachineName(8);
|
||||
$node->addTranslation($langcodes[1], ['title' => $translated_node_title, 'body' => $this->randomMachineName(16), 'status' => 1]);
|
||||
$node->save();
|
||||
|
||||
// Edit the node and create a menu link.
|
||||
$edit = array(
|
||||
'menu[enabled]' => 1,
|
||||
'menu[title]' => $node_title,
|
||||
'menu[weight]' => 17,
|
||||
);
|
||||
$options = array('language' => $languages[$langcodes[0]]);
|
||||
$url = $node->toUrl('edit-form', $options);
|
||||
$this->drupalPostForm($url, $edit, t('Save') . ' ' . t('(this translation)'));
|
||||
|
||||
// Edit the node in a different language and translate the menu link.
|
||||
$edit = array(
|
||||
'menu[enabled]' => 1,
|
||||
'menu[title]' => $translated_node_title,
|
||||
'menu[weight]' => 17,
|
||||
);
|
||||
$options = array('language' => $languages[$langcodes[1]]);
|
||||
$url = $node->toUrl('edit-form', $options);
|
||||
$this->drupalPostForm($url, $edit, t('Save') . ' ' . t('(this translation)'));
|
||||
|
||||
// Assert that the original link exists in the frontend.
|
||||
$this->drupalGet('node/' . $node->id(), array('language' => $languages[$langcodes[0]]));
|
||||
$this->assertLink($node_title);
|
||||
|
||||
// Assert that the translated link exists in the frontend.
|
||||
$this->drupalGet('node/' . $node->id(), array('language' => $languages[$langcodes[1]]));
|
||||
$this->assertLink($translated_node_title);
|
||||
|
||||
// Revisit the edit page in original language, check the loaded menu item title and save.
|
||||
$options = array('language' => $languages[$langcodes[0]]);
|
||||
$url = $node->toUrl('edit-form', $options);
|
||||
$this->drupalGet($url);
|
||||
$this->assertFieldById('edit-menu-title', $node_title);
|
||||
$this->drupalPostForm(NULL, [], t('Save') . ' ' . t('(this translation)'));
|
||||
|
||||
// Revisit the edit page of the translation and check the loaded menu item title.
|
||||
$options = array('language' => $languages[$langcodes[1]]);
|
||||
$url = $node->toUrl('edit-form', $options);
|
||||
$this->drupalGet($url);
|
||||
$this->assertFieldById('edit-menu-title', $translated_node_title);
|
||||
}
|
||||
|
||||
}
|
961
web/core/modules/menu_ui/src/Tests/MenuTest.php
Normal file
961
web/core/modules/menu_ui/src/Tests/MenuTest.php
Normal file
|
@ -0,0 +1,961 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Tests;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Menu\MenuLinkInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\system\Entity\Menu;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Add a custom menu, add menu links to the custom menu and Tools menu, check
|
||||
* their data, and delete them using the UI.
|
||||
*
|
||||
* @group menu_ui
|
||||
*/
|
||||
class MenuTest extends MenuWebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'block', 'contextual', 'help', 'path', 'test_page_test');
|
||||
|
||||
/**
|
||||
* A user with administration rights.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* An authenticated user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $authenticatedUser;
|
||||
|
||||
/**
|
||||
* Array of placed menu blocks keyed by block ID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $blockPlacements;
|
||||
|
||||
/**
|
||||
* A test menu.
|
||||
*
|
||||
* @var \Drupal\system\Entity\Menu
|
||||
*/
|
||||
protected $menu;
|
||||
|
||||
/**
|
||||
* An array of test menu links.
|
||||
*
|
||||
* @var \Drupal\menu_link_content\MenuLinkContentInterface[]
|
||||
*/
|
||||
protected $items;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
|
||||
|
||||
// Create users.
|
||||
$this->adminUser = $this->drupalCreateUser(array('access administration pages', 'administer blocks', 'administer menu', 'create article content'));
|
||||
$this->authenticatedUser = $this->drupalCreateUser(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests menu functionality using the admin and user interfaces.
|
||||
*/
|
||||
function testMenu() {
|
||||
// Log in the user.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->items = array();
|
||||
|
||||
$this->menu = $this->addCustomMenu();
|
||||
$this->doMenuTests();
|
||||
$this->doTestMenuBlock();
|
||||
$this->addInvalidMenuLink();
|
||||
$this->addCustomMenuCRUD();
|
||||
|
||||
// Verify that the menu links rebuild is idempotent and leaves the same
|
||||
// number of links in the table.
|
||||
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
$before_count = $menu_link_manager->countMenuLinks(NULL);
|
||||
$menu_link_manager->rebuild();
|
||||
$after_count = $menu_link_manager->countMenuLinks(NULL);
|
||||
$this->assertIdentical($before_count, $after_count, 'MenuLinkManager::rebuild() does not add more links');
|
||||
// Do standard user tests.
|
||||
// Log in the user.
|
||||
$this->drupalLogin($this->authenticatedUser);
|
||||
$this->verifyAccess(403);
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
// Menu link URIs are stored as 'internal:/node/$nid'.
|
||||
$node = Node::load(str_replace('internal:/node/', '', $item->link->uri));
|
||||
$this->verifyMenuLink($item, $node);
|
||||
}
|
||||
|
||||
// Log in the administrator.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Verify delete link exists and reset link does not exist.
|
||||
$this->drupalGet('admin/structure/menu/manage/' . $this->menu->id());
|
||||
$this->assertLinkByHref(Url::fromRoute('entity.menu_link_content.delete_form', ['menu_link_content' => $this->items[0]->id()])->toString());
|
||||
$this->assertNoLinkByHref(Url::fromRoute('menu_ui.link_reset', ['menu_link_plugin' => $this->items[0]->getPluginId()])->toString());
|
||||
// Check delete and reset access.
|
||||
$this->drupalGet('admin/structure/menu/item/' . $this->items[0]->id() . '/delete');
|
||||
$this->assertResponse(200);
|
||||
$this->drupalGet('admin/structure/menu/link/' . $this->items[0]->getPluginId() . '/reset');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Delete menu links.
|
||||
foreach ($this->items as $item) {
|
||||
$this->deleteMenuLink($item);
|
||||
}
|
||||
|
||||
// Delete custom menu.
|
||||
$this->deleteCustomMenu();
|
||||
|
||||
// Modify and reset a standard menu link.
|
||||
$instance = $this->getStandardMenuLink();
|
||||
$old_weight = $instance->getWeight();
|
||||
// Edit the static menu link.
|
||||
$edit = array();
|
||||
$edit['weight'] = 10;
|
||||
$id = $instance->getPluginId();
|
||||
$this->drupalPostForm("admin/structure/menu/link/$id/edit", $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertText('The menu link has been saved.');
|
||||
$menu_link_manager->resetDefinitions();
|
||||
|
||||
$instance = $menu_link_manager->createInstance($instance->getPluginId());
|
||||
$this->assertEqual($edit['weight'], $instance->getWeight(), 'Saving an existing link updates the weight.');
|
||||
$this->resetMenuLink($instance, $old_weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom menu using CRUD functions.
|
||||
*/
|
||||
function addCustomMenuCRUD() {
|
||||
// Add a new custom menu.
|
||||
$menu_name = substr(hash('sha256', $this->randomMachineName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI);
|
||||
$label = $this->randomMachineName(16);
|
||||
|
||||
$menu = Menu::create(array(
|
||||
'id' => $menu_name,
|
||||
'label' => $label,
|
||||
'description' => 'Description text',
|
||||
));
|
||||
$menu->save();
|
||||
|
||||
// Assert the new menu.
|
||||
$this->drupalGet('admin/structure/menu/manage/' . $menu_name);
|
||||
$this->assertRaw($label, 'Custom menu was added.');
|
||||
|
||||
// Edit the menu.
|
||||
$new_label = $this->randomMachineName(16);
|
||||
$menu->set('label', $new_label);
|
||||
$menu->save();
|
||||
$this->drupalGet('admin/structure/menu/manage/' . $menu_name);
|
||||
$this->assertRaw($new_label, 'Custom menu was edited.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom menu.
|
||||
*
|
||||
* @return \Drupal\system\Entity\Menu
|
||||
* The custom menu that has been created.
|
||||
*/
|
||||
function addCustomMenu() {
|
||||
// Try adding a menu using a menu_name that is too long.
|
||||
$this->drupalGet('admin/structure/menu/add');
|
||||
$menu_name = substr(hash('sha256', $this->randomMachineName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI + 1);
|
||||
$label = $this->randomMachineName(16);
|
||||
$edit = array(
|
||||
'id' => $menu_name,
|
||||
'description' => '',
|
||||
'label' => $label,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/menu/add', $edit, t('Save'));
|
||||
|
||||
// Verify that using a menu_name that is too long results in a validation
|
||||
// message.
|
||||
$this->assertRaw(t('@name cannot be longer than %max characters but is currently %length characters long.', array(
|
||||
'@name' => t('Menu name'),
|
||||
'%max' => MENU_MAX_MENU_NAME_LENGTH_UI,
|
||||
'%length' => Unicode::strlen($menu_name),
|
||||
)));
|
||||
|
||||
// Change the menu_name so it no longer exceeds the maximum length.
|
||||
$menu_name = substr(hash('sha256', $this->randomMachineName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI);
|
||||
$edit['id'] = $menu_name;
|
||||
$this->drupalPostForm('admin/structure/menu/add', $edit, t('Save'));
|
||||
|
||||
// Verify that no validation error is given for menu_name length.
|
||||
$this->assertNoRaw(t('@name cannot be longer than %max characters but is currently %length characters long.', array(
|
||||
'@name' => t('Menu name'),
|
||||
'%max' => MENU_MAX_MENU_NAME_LENGTH_UI,
|
||||
'%length' => Unicode::strlen($menu_name),
|
||||
)));
|
||||
// Verify that the confirmation message is displayed.
|
||||
$this->assertRaw(t('Menu %label has been added.', array('%label' => $label)));
|
||||
$this->drupalGet('admin/structure/menu');
|
||||
$this->assertText($label, 'Menu created');
|
||||
|
||||
// Confirm that the custom menu block is available.
|
||||
$this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default'));
|
||||
$this->clickLinkPartialName('Place block');
|
||||
$this->assertText($label);
|
||||
|
||||
// Enable the block.
|
||||
$block = $this->drupalPlaceBlock('system_menu_block:' . $menu_name);
|
||||
$this->blockPlacements[$menu_name] = $block->id();
|
||||
return Menu::load($menu_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the locally stored custom menu.
|
||||
*
|
||||
* This deletes the custom menu that is stored in $this->menu and performs
|
||||
* tests on the menu delete user interface.
|
||||
*/
|
||||
function deleteCustomMenu() {
|
||||
$menu_name = $this->menu->id();
|
||||
$label = $this->menu->label();
|
||||
|
||||
// Delete custom menu.
|
||||
$this->drupalPostForm("admin/structure/menu/manage/$menu_name/delete", array(), t('Delete'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw(t('The menu %title has been deleted.', array('%title' => $label)), 'Custom menu was deleted');
|
||||
$this->assertNull(Menu::load($menu_name), 'Custom menu was deleted');
|
||||
// Test if all menu links associated with the menu were removed from
|
||||
// database.
|
||||
$result = entity_load_multiple_by_properties('menu_link_content', array('menu_name' => $menu_name));
|
||||
$this->assertFalse($result, 'All menu links associated with the custom menu were deleted.');
|
||||
|
||||
// Make sure there's no delete button on system menus.
|
||||
$this->drupalGet('admin/structure/menu/manage/main');
|
||||
$this->assertNoRaw('edit-delete', 'The delete button was not found');
|
||||
|
||||
// Try to delete the main menu.
|
||||
$this->drupalGet('admin/structure/menu/manage/main/delete');
|
||||
$this->assertText(t('You are not authorized to access this page.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests menu functionality.
|
||||
*/
|
||||
function doMenuTests() {
|
||||
$menu_name = $this->menu->id();
|
||||
|
||||
// Test the 'Add link' local action.
|
||||
$this->drupalGet(Url::fromRoute('entity.menu.edit_form', ['menu' => $menu_name]));
|
||||
|
||||
$this->clickLink(t('Add link'));
|
||||
$link_title = $this->randomString();
|
||||
$this->drupalPostForm(NULL, array('link[0][uri]' => '/', 'title[0][value]' => $link_title), t('Save'));
|
||||
$this->assertUrl(Url::fromRoute('entity.menu.edit_form', ['menu' => $menu_name]));
|
||||
// Test the 'Edit' operation.
|
||||
$this->clickLink(t('Edit'));
|
||||
$this->assertFieldByName('title[0][value]', $link_title);
|
||||
$link_title = $this->randomString();
|
||||
$this->drupalPostForm(NULL, array('title[0][value]' => $link_title), t('Save'));
|
||||
$this->assertUrl(Url::fromRoute('entity.menu.edit_form', ['menu' => $menu_name]));
|
||||
// Test the 'Delete' operation.
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertRaw(t('Are you sure you want to delete the custom menu link %item?', array('%item' => $link_title)));
|
||||
$this->drupalPostForm(NULL, array(), t('Delete'));
|
||||
$this->assertUrl(Url::fromRoute('entity.menu.edit_form', ['menu' => $menu_name]));
|
||||
|
||||
// Add nodes to use as links for menu links.
|
||||
$node1 = $this->drupalCreateNode(array('type' => 'article'));
|
||||
$node2 = $this->drupalCreateNode(array('type' => 'article'));
|
||||
$node3 = $this->drupalCreateNode(array('type' => 'article'));
|
||||
$node4 = $this->drupalCreateNode(array('type' => 'article'));
|
||||
// Create a node with an alias.
|
||||
$node5 = $this->drupalCreateNode(array(
|
||||
'type' => 'article',
|
||||
'path' => array(
|
||||
'alias' => '/node5',
|
||||
),
|
||||
));
|
||||
|
||||
// Verify add link button.
|
||||
$this->drupalGet('admin/structure/menu');
|
||||
$this->assertLinkByHref('admin/structure/menu/manage/' . $menu_name . '/add', 0, "The add menu link button URL is correct");
|
||||
|
||||
// Verify form defaults.
|
||||
$this->doMenuLinkFormDefaultsTest();
|
||||
|
||||
// Add menu links.
|
||||
$item1 = $this->addMenuLink('', '/node/' . $node1->id(), $menu_name, TRUE);
|
||||
$item2 = $this->addMenuLink($item1->getPluginId(), '/node/' . $node2->id(), $menu_name, FALSE);
|
||||
$item3 = $this->addMenuLink($item2->getPluginId(), '/node/' . $node3->id(), $menu_name);
|
||||
|
||||
// Hierarchy
|
||||
// <$menu_name>
|
||||
// - item1
|
||||
// -- item2
|
||||
// --- item3
|
||||
|
||||
$this->assertMenuLink($item1->getPluginId(), array(
|
||||
'children' => array($item2->getPluginId(), $item3->getPluginId()),
|
||||
'parents' => array($item1->getPluginId()),
|
||||
// We assert the language code here to make sure that the language
|
||||
// selection element degrades gracefully without the Language module.
|
||||
'langcode' => 'en',
|
||||
));
|
||||
$this->assertMenuLink($item2->getPluginId(), array(
|
||||
'children' => array($item3->getPluginId()),
|
||||
'parents' => array($item2->getPluginId(), $item1->getPluginId()),
|
||||
// See above.
|
||||
'langcode' => 'en',
|
||||
));
|
||||
$this->assertMenuLink($item3->getPluginId(), array(
|
||||
'children' => array(),
|
||||
'parents' => array($item3->getPluginId(), $item2->getPluginId(), $item1->getPluginId()),
|
||||
// See above.
|
||||
'langcode' => 'en',
|
||||
));
|
||||
|
||||
// Verify menu links.
|
||||
$this->verifyMenuLink($item1, $node1);
|
||||
$this->verifyMenuLink($item2, $node2, $item1, $node1);
|
||||
$this->verifyMenuLink($item3, $node3, $item2, $node2);
|
||||
|
||||
// Add more menu links.
|
||||
$item4 = $this->addMenuLink('', '/node/' . $node4->id(), $menu_name);
|
||||
$item5 = $this->addMenuLink($item4->getPluginId(), '/node/' . $node5->id(), $menu_name);
|
||||
// Create a menu link pointing to an alias.
|
||||
$item6 = $this->addMenuLink($item4->getPluginId(), '/node5', $menu_name, TRUE, '0');
|
||||
|
||||
// Hierarchy
|
||||
// <$menu_name>
|
||||
// - item1
|
||||
// -- item2
|
||||
// --- item3
|
||||
// - item4
|
||||
// -- item5
|
||||
// -- item6
|
||||
|
||||
$this->assertMenuLink($item4->getPluginId(), array(
|
||||
'children' => array($item5->getPluginId(), $item6->getPluginId()),
|
||||
'parents' => array($item4->getPluginId()),
|
||||
// See above.
|
||||
'langcode' => 'en',
|
||||
));
|
||||
$this->assertMenuLink($item5->getPluginId(), array(
|
||||
'children' => array(),
|
||||
'parents' => array($item5->getPluginId(), $item4->getPluginId()),
|
||||
'langcode' => 'en',
|
||||
));
|
||||
$this->assertMenuLink($item6->getPluginId(), array(
|
||||
'children' => array(),
|
||||
'parents' => array($item6->getPluginId(), $item4->getPluginId()),
|
||||
'route_name' => 'entity.node.canonical',
|
||||
'route_parameters' => array('node' => $node5->id()),
|
||||
'url' => '',
|
||||
// See above.
|
||||
'langcode' => 'en',
|
||||
));
|
||||
|
||||
// Modify menu links.
|
||||
$this->modifyMenuLink($item1);
|
||||
$this->modifyMenuLink($item2);
|
||||
|
||||
// Toggle menu links.
|
||||
$this->toggleMenuLink($item1);
|
||||
$this->toggleMenuLink($item2);
|
||||
|
||||
// Move link and verify that descendants are updated.
|
||||
$this->moveMenuLink($item2, $item5->getPluginId(), $menu_name);
|
||||
// Hierarchy
|
||||
// <$menu_name>
|
||||
// - item1
|
||||
// - item4
|
||||
// -- item5
|
||||
// --- item2
|
||||
// ---- item3
|
||||
// -- item6
|
||||
|
||||
$this->assertMenuLink($item1->getPluginId(), array(
|
||||
'children' => array(),
|
||||
'parents' => array($item1->getPluginId()),
|
||||
// See above.
|
||||
'langcode' => 'en',
|
||||
));
|
||||
$this->assertMenuLink($item4->getPluginId(), array(
|
||||
'children' => array($item5->getPluginId(), $item6->getPluginId(), $item2->getPluginId(), $item3->getPluginId()),
|
||||
'parents' => array($item4->getPluginId()),
|
||||
// See above.
|
||||
'langcode' => 'en',
|
||||
));
|
||||
|
||||
$this->assertMenuLink($item5->getPluginId(), array(
|
||||
'children' => array($item2->getPluginId(), $item3->getPluginId()),
|
||||
'parents' => array($item5->getPluginId(), $item4->getPluginId()),
|
||||
// See above.
|
||||
'langcode' => 'en',
|
||||
));
|
||||
$this->assertMenuLink($item2->getPluginId(), array(
|
||||
'children' => array($item3->getPluginId()),
|
||||
'parents' => array($item2->getPluginId(), $item5->getPluginId(), $item4->getPluginId()),
|
||||
// See above.
|
||||
'langcode' => 'en',
|
||||
));
|
||||
$this->assertMenuLink($item3->getPluginId(), array(
|
||||
'children' => array(),
|
||||
'parents' => array($item3->getPluginId(), $item2->getPluginId(), $item5->getPluginId(), $item4->getPluginId()),
|
||||
// See above.
|
||||
'langcode' => 'en',
|
||||
));
|
||||
|
||||
// Add 102 menu links with increasing weights, then make sure the last-added
|
||||
// item's weight doesn't get changed because of the old hardcoded delta=50.
|
||||
$items = array();
|
||||
for ($i = -50; $i <= 51; $i++) {
|
||||
$items[$i] = $this->addMenuLink('', '/node/' . $node1->id(), $menu_name, TRUE, strval($i));
|
||||
}
|
||||
$this->assertMenuLink($items[51]->getPluginId(), array('weight' => '51'));
|
||||
|
||||
// Disable a link and then re-enable the link via the overview form.
|
||||
$this->disableMenuLink($item1);
|
||||
$edit = array();
|
||||
$edit['links[menu_plugin_id:' . $item1->getPluginId() . '][enabled]'] = TRUE;
|
||||
$this->drupalPostForm('admin/structure/menu/manage/' . $item1->getMenuName(), $edit, t('Save'));
|
||||
|
||||
// Mark item2, item4 and item5 as expanded.
|
||||
// This is done in order to show them on the frontpage.
|
||||
$item2->expanded->value = 1;
|
||||
$item2->save();
|
||||
$item4->expanded->value = 1;
|
||||
$item4->save();
|
||||
$item5->expanded->value = 1;
|
||||
$item5->save();
|
||||
|
||||
// Verify in the database.
|
||||
$this->assertMenuLink($item1->getPluginId(), array('enabled' => 1));
|
||||
|
||||
// Add an external link.
|
||||
$item7 = $this->addMenuLink('', 'https://www.drupal.org', $menu_name);
|
||||
$this->assertMenuLink($item7->getPluginId(), array('url' => 'https://www.drupal.org'));
|
||||
|
||||
// Add <front> menu item.
|
||||
$item8 = $this->addMenuLink('', '/', $menu_name);
|
||||
$this->assertMenuLink($item8->getPluginId(), array('route_name' => '<front>'));
|
||||
$this->drupalGet('');
|
||||
$this->assertResponse(200);
|
||||
// Make sure we get routed correctly.
|
||||
$this->clickLink($item8->getTitle());
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Check invalid menu link parents.
|
||||
$this->checkInvalidParentMenuLinks();
|
||||
|
||||
// Save menu links for later tests.
|
||||
$this->items[] = $item1;
|
||||
$this->items[] = $item2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the proper default values are set when adding a menu link
|
||||
*/
|
||||
protected function doMenuLinkFormDefaultsTest() {
|
||||
$this->drupalGet("admin/structure/menu/manage/tools/add");
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->assertFieldByName('title[0][value]', '');
|
||||
$this->assertFieldByName('link[0][uri]', '');
|
||||
|
||||
$this->assertNoFieldChecked('edit-expanded-value');
|
||||
$this->assertFieldChecked('edit-enabled-value');
|
||||
|
||||
$this->assertFieldByName('description[0][value]', '');
|
||||
$this->assertFieldByName('weight[0][value]', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds and removes a menu link with a query string and fragment.
|
||||
*/
|
||||
function testMenuQueryAndFragment() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Make a path with query and fragment on.
|
||||
$path = '/test-page?arg1=value1&arg2=value2';
|
||||
$item = $this->addMenuLink('', $path);
|
||||
|
||||
$this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
|
||||
$this->assertFieldByName('link[0][uri]', $path, 'Path is found with both query and fragment.');
|
||||
|
||||
// Now change the path to something without query and fragment.
|
||||
$path = '/test-page';
|
||||
$this->drupalPostForm('admin/structure/menu/item/' . $item->id() . '/edit', array('link[0][uri]' => $path), t('Save'));
|
||||
$this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
|
||||
$this->assertFieldByName('link[0][uri]', $path, 'Path no longer has query or fragment.');
|
||||
|
||||
// Use <front>#fragment and ensure that saving it does not lose its content.
|
||||
$path = '<front>?arg1=value#fragment';
|
||||
$item = $this->addMenuLink('', $path);
|
||||
|
||||
$this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
|
||||
$this->assertFieldByName('link[0][uri]', $path, 'Path is found with both query and fragment.');
|
||||
|
||||
$this->drupalPostForm('admin/structure/menu/item/' . $item->id() . '/edit', array(), t('Save'));
|
||||
|
||||
$this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
|
||||
$this->assertFieldByName('link[0][uri]', $path, 'Path is found with both query and fragment.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests renaming the built-in menu.
|
||||
*/
|
||||
function testSystemMenuRename() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$edit = array(
|
||||
'label' => $this->randomMachineName(16),
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/menu/manage/main', $edit, t('Save'));
|
||||
|
||||
// Make sure menu shows up with new name in block addition.
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
$this->drupalget('admin/structure/block/list/' . $default_theme);
|
||||
$this->clickLinkPartialName('Place block');
|
||||
$this->assertText($edit['label']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that menu items pointing to unpublished nodes are editable.
|
||||
*/
|
||||
function testUnpublishedNodeMenuItem() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array('access administration pages', 'administer blocks', 'administer menu', 'create article content', 'bypass node access')));
|
||||
// Create an unpublished node.
|
||||
$node = $this->drupalCreateNode(array(
|
||||
'type' => 'article',
|
||||
'status' => NODE_NOT_PUBLISHED,
|
||||
));
|
||||
|
||||
$item = $this->addMenuLink('', '/node/' . $node->id());
|
||||
$this->modifyMenuLink($item);
|
||||
|
||||
// Test that a user with 'administer menu' but without 'bypass node access'
|
||||
// cannot see the menu item.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('admin/structure/menu/manage/' . $item->getMenuName());
|
||||
$this->assertNoText($item->getTitle(), "Menu link pointing to unpublished node is only visible to users with 'bypass node access' permission");
|
||||
// The cache contexts associated with the (in)accessible menu links are
|
||||
// bubbled. See DefaultMenuLinkTreeManipulators::menuLinkCheckAccess().
|
||||
$this->assertCacheContext('user.permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the contextual links on a menu block.
|
||||
*/
|
||||
public function testBlockContextualLinks() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array('administer menu', 'access contextual links', 'administer blocks')));
|
||||
$custom_menu = $this->addCustomMenu();
|
||||
$this->addMenuLink('', '/', $custom_menu->id());
|
||||
$block = $this->drupalPlaceBlock('system_menu_block:' . $custom_menu->id(), array('label' => 'Custom menu', 'provider' => 'system'));
|
||||
$this->drupalGet('test-page');
|
||||
|
||||
$id = 'block:block=' . $block->id() . ':langcode=en|menu:menu=' . $custom_menu->id() . ':langcode=en';
|
||||
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:assertContextualLinkPlaceHolder()
|
||||
$this->assertRaw('<div data-contextual-id="' . $id . '"></div>', format_string('Contextual link placeholder with id @id exists.', array('@id' => $id)));
|
||||
|
||||
// Get server-rendered contextual links.
|
||||
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:renderContextualLinks()
|
||||
$post = array('ids[0]' => $id);
|
||||
$response = $this->drupalPost('contextual/render', 'application/json', $post, array('query' => array('destination' => 'test-page')));
|
||||
$this->assertResponse(200);
|
||||
$json = Json::decode($response);
|
||||
$this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="block-configure"><a href="' . base_path() . 'admin/structure/block/manage/' . $block->id() . '">Configure block</a></li><li class="entitymenuedit-form"><a href="' . base_path() . 'admin/structure/menu/manage/' . $custom_menu->id() . '">Edit menu</a></li></ul>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a menu link using the UI.
|
||||
*
|
||||
* @param string $parent
|
||||
* Optional parent menu link id.
|
||||
* @param string $path
|
||||
* The path to enter on the form. Defaults to the front page.
|
||||
* @param string $menu_name
|
||||
* Menu name. Defaults to 'tools'.
|
||||
* @param bool $expanded
|
||||
* Whether or not this menu link is expanded. Setting this to TRUE should
|
||||
* test whether it works when we do the authenticatedUser tests. Defaults
|
||||
* to FALSE.
|
||||
* @param string $weight
|
||||
* Menu weight. Defaults to 0.
|
||||
*
|
||||
* @return \Drupal\menu_link_content\Entity\MenuLinkContent
|
||||
* A menu link entity.
|
||||
*/
|
||||
function addMenuLink($parent = '', $path = '/', $menu_name = 'tools', $expanded = FALSE, $weight = '0') {
|
||||
// View add menu link page.
|
||||
$this->drupalGet("admin/structure/menu/manage/$menu_name/add");
|
||||
$this->assertResponse(200);
|
||||
|
||||
$title = '!link_' . $this->randomMachineName(16);
|
||||
$edit = array(
|
||||
'link[0][uri]' => $path,
|
||||
'title[0][value]' => $title,
|
||||
'description[0][value]' => '',
|
||||
'enabled[value]' => 1,
|
||||
'expanded[value]' => $expanded,
|
||||
'menu_parent' => $menu_name . ':' . $parent,
|
||||
'weight[0][value]' => $weight,
|
||||
);
|
||||
|
||||
// Add menu link.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertText('The menu link has been saved.');
|
||||
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link_content', array('title' => $title));
|
||||
|
||||
$menu_link = reset($menu_links);
|
||||
$this->assertTrue($menu_link, 'Menu link was found in database.');
|
||||
$this->assertMenuLink($menu_link->getPluginId(), array('menu_name' => $menu_name, 'children' => array(), 'parent' => $parent));
|
||||
|
||||
return $menu_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to add menu link with invalid path or no access permission.
|
||||
*/
|
||||
function addInvalidMenuLink() {
|
||||
foreach (array('access' => '/admin/people/permissions') as $type => $link_path) {
|
||||
$edit = array(
|
||||
'link[0][uri]' => $link_path,
|
||||
'title[0][value]' => 'title',
|
||||
);
|
||||
$this->drupalPostForm("admin/structure/menu/manage/{$this->menu->id()}/add", $edit, t('Save'));
|
||||
$this->assertRaw(t("The path '@link_path' is inaccessible.", array('@link_path' => $link_path)), 'Menu link was not created');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that parent options are limited by depth when adding menu links.
|
||||
*/
|
||||
function checkInvalidParentMenuLinks() {
|
||||
$last_link = NULL;
|
||||
$created_links = array();
|
||||
|
||||
// Get the max depth of the tree.
|
||||
$menu_link_tree = \Drupal::service('menu.link_tree');
|
||||
$max_depth = $menu_link_tree->maxDepth();
|
||||
|
||||
// Create a maximum number of menu links, each a child of the previous.
|
||||
for ($i = 0; $i <= $max_depth - 1; $i++) {
|
||||
$parent = $last_link ? 'tools:' . $last_link->getPluginId() : 'tools:';
|
||||
$title = 'title' . $i;
|
||||
$edit = array(
|
||||
'link[0][uri]' => '/',
|
||||
'title[0][value]' => $title,
|
||||
'menu_parent' => $parent,
|
||||
'description[0][value]' => '',
|
||||
'enabled[value]' => 1,
|
||||
'expanded[value]' => FALSE,
|
||||
'weight[0][value]' => '0',
|
||||
);
|
||||
$this->drupalPostForm("admin/structure/menu/manage/{$this->menu->id()}/add", $edit, t('Save'));
|
||||
$menu_links = entity_load_multiple_by_properties('menu_link_content', array('title' => $title));
|
||||
$last_link = reset($menu_links);
|
||||
$created_links[] = 'tools:' . $last_link->getPluginId();
|
||||
}
|
||||
|
||||
// The last link cannot be a parent in the new menu link form.
|
||||
$this->drupalGet('admin/structure/menu/manage/admin/add');
|
||||
$value = 'tools:' . $last_link->getPluginId();
|
||||
$this->assertNoOption('edit-menu-parent', $value, 'The invalid option is not there.');
|
||||
|
||||
// All but the last link can be parents in the new menu link form.
|
||||
array_pop($created_links);
|
||||
foreach ($created_links as $key => $link) {
|
||||
$this->assertOption('edit-menu-parent', $link, 'The valid option number ' . ($key + 1) . ' is there.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a menu link using the UI.
|
||||
*
|
||||
* @param \Drupal\menu_link_content\Entity\MenuLinkContent $item
|
||||
* Menu link.
|
||||
* @param object $item_node
|
||||
* Menu link content node.
|
||||
* @param \Drupal\menu_link_content\Entity\MenuLinkContent $parent
|
||||
* Parent menu link.
|
||||
* @param object $parent_node
|
||||
* Parent menu link content node.
|
||||
*/
|
||||
function verifyMenuLink(MenuLinkContent $item, $item_node, MenuLinkContent $parent = NULL, $parent_node = NULL) {
|
||||
// View home page.
|
||||
$this->drupalGet('');
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Verify parent menu link.
|
||||
if (isset($parent)) {
|
||||
// Verify menu link.
|
||||
$title = $parent->getTitle();
|
||||
$this->assertLink($title, 0, 'Parent menu link was displayed');
|
||||
|
||||
// Verify menu link link.
|
||||
$this->clickLink($title);
|
||||
$title = $parent_node->label();
|
||||
$this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Parent menu link link target was correct');
|
||||
}
|
||||
|
||||
// Verify menu link.
|
||||
$title = $item->getTitle();
|
||||
$this->assertLink($title, 0, 'Menu link was displayed');
|
||||
|
||||
// Verify menu link link.
|
||||
$this->clickLink($title);
|
||||
$title = $item_node->label();
|
||||
$this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Menu link link target was correct');
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the parent of a menu link using the UI.
|
||||
*
|
||||
* @param \Drupal\menu_link_content\MenuLinkContentInterface $item
|
||||
* The menu link item to move.
|
||||
* @param int $parent
|
||||
* The id of the new parent.
|
||||
* @param string $menu_name
|
||||
* The menu the menu link will be moved to.
|
||||
*/
|
||||
function moveMenuLink(MenuLinkContent $item, $parent, $menu_name) {
|
||||
$mlid = $item->id();
|
||||
|
||||
$edit = array(
|
||||
'menu_parent' => $menu_name . ':' . $parent,
|
||||
);
|
||||
$this->drupalPostForm("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies a menu link using the UI.
|
||||
*
|
||||
* @param \Drupal\menu_link_content\Entity\MenuLinkContent $item
|
||||
* Menu link entity.
|
||||
*/
|
||||
function modifyMenuLink(MenuLinkContent $item) {
|
||||
$item->title->value = $this->randomMachineName(16);
|
||||
|
||||
$mlid = $item->id();
|
||||
$title = $item->getTitle();
|
||||
|
||||
// Edit menu link.
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = $title;
|
||||
$this->drupalPostForm("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertText('The menu link has been saved.');
|
||||
// Verify menu link.
|
||||
$this->drupalGet('admin/structure/menu/manage/' . $item->getMenuName());
|
||||
$this->assertText($title, 'Menu link was edited');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets a standard menu link using the UI.
|
||||
*
|
||||
* @param \Drupal\Core\Menu\MenuLinkInterface $menu_link
|
||||
* The Menu link.
|
||||
* @param int $old_weight
|
||||
* Original title for menu link.
|
||||
*/
|
||||
function resetMenuLink(MenuLinkInterface $menu_link, $old_weight) {
|
||||
// Reset menu link.
|
||||
$this->drupalPostForm("admin/structure/menu/link/{$menu_link->getPluginId()}/reset", array(), t('Reset'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw(t('The menu link was reset to its default settings.'), 'Menu link was reset');
|
||||
|
||||
// Verify menu link.
|
||||
$instance = \Drupal::service('plugin.manager.menu.link')->createInstance($menu_link->getPluginId());
|
||||
$this->assertEqual($old_weight, $instance->getWeight(), 'Resets to the old weight.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a menu link using the UI.
|
||||
*
|
||||
* @param \Drupal\menu_link_content\Entity\MenuLinkContent $item
|
||||
* Menu link.
|
||||
*/
|
||||
function deleteMenuLink(MenuLinkContent $item) {
|
||||
$mlid = $item->id();
|
||||
$title = $item->getTitle();
|
||||
|
||||
// Delete menu link.
|
||||
$this->drupalPostForm("admin/structure/menu/item/$mlid/delete", array(), t('Delete'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw(t('The menu link %title has been deleted.', array('%title' => $title)), 'Menu link was deleted');
|
||||
|
||||
// Verify deletion.
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText($title, 'Menu link was deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternately disables and enables a menu link.
|
||||
*
|
||||
* @param \Drupal\menu_link_content\Entity\MenuLinkContent $item
|
||||
* Menu link.
|
||||
*/
|
||||
function toggleMenuLink(MenuLinkContent $item) {
|
||||
$this->disableMenuLink($item);
|
||||
|
||||
// Verify menu link is absent.
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText($item->getTitle(), 'Menu link was not displayed');
|
||||
$this->enableMenuLink($item);
|
||||
|
||||
// Verify menu link is displayed.
|
||||
$this->drupalGet('');
|
||||
$this->assertText($item->getTitle(), 'Menu link was displayed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables a menu link.
|
||||
*
|
||||
* @param \Drupal\menu_link_content\Entity\MenuLinkContent $item
|
||||
* Menu link.
|
||||
*/
|
||||
function disableMenuLink(MenuLinkContent $item) {
|
||||
$mlid = $item->id();
|
||||
$edit['enabled[value]'] = FALSE;
|
||||
$this->drupalPostForm("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
|
||||
|
||||
// Unlike most other modules, there is no confirmation message displayed.
|
||||
// Verify in the database.
|
||||
$this->assertMenuLink($item->getPluginId(), array('enabled' => 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables a menu link.
|
||||
*
|
||||
* @param \Drupal\menu_link_content\Entity\MenuLinkContent $item
|
||||
* Menu link.
|
||||
*/
|
||||
function enableMenuLink(MenuLinkContent $item) {
|
||||
$mlid = $item->id();
|
||||
$edit['enabled[value]'] = TRUE;
|
||||
$this->drupalPostForm("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
|
||||
|
||||
// Verify in the database.
|
||||
$this->assertMenuLink($item->getPluginId(), array('enabled' => 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if administrative users other than user 1 can access the menu parents
|
||||
* AJAX callback.
|
||||
*/
|
||||
public function testMenuParentsJsAccess() {
|
||||
$admin = $this->drupalCreateUser(array('administer menu'));
|
||||
$this->drupalLogin($admin);
|
||||
// Just check access to the callback overall, the POST data is irrelevant.
|
||||
$this->drupalGetAjax('admin/structure/menu/parents');
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Do standard user tests.
|
||||
// Log in the user.
|
||||
$this->drupalLogin($this->authenticatedUser);
|
||||
$this->drupalGetAjax('admin/structure/menu/parents');
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns standard menu link.
|
||||
*
|
||||
* @return \Drupal\Core\Menu\MenuLinkInterface
|
||||
* A menu link plugin.
|
||||
*/
|
||||
private function getStandardMenuLink() {
|
||||
// Retrieve menu link id of the Log out menu link, which will always be on
|
||||
// the front page.
|
||||
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
$instance = $menu_link_manager->getInstance(['id' => 'user.logout']);
|
||||
|
||||
$this->assertTrue((bool) $instance, 'Standard menu link was loaded');
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the logged in user has the desired access to various menu pages.
|
||||
*
|
||||
* @param int $response
|
||||
* (optional) The expected HTTP response code. Defaults to 200.
|
||||
*/
|
||||
private function verifyAccess($response = 200) {
|
||||
// View menu help page.
|
||||
$this->drupalGet('admin/help/menu');
|
||||
$this->assertResponse($response);
|
||||
if ($response == 200) {
|
||||
$this->assertText(t('Menu'), 'Menu help was displayed');
|
||||
}
|
||||
|
||||
// View menu build overview page.
|
||||
$this->drupalGet('admin/structure/menu');
|
||||
$this->assertResponse($response);
|
||||
if ($response == 200) {
|
||||
$this->assertText(t('Menus'), 'Menu build overview page was displayed');
|
||||
}
|
||||
|
||||
// View tools menu customization page.
|
||||
$this->drupalGet('admin/structure/menu/manage/' . $this->menu->id());
|
||||
$this->assertResponse($response);
|
||||
if ($response == 200) {
|
||||
$this->assertText(t('Tools'), 'Tools menu page was displayed');
|
||||
}
|
||||
|
||||
// View menu edit page for a static link.
|
||||
$item = $this->getStandardMenuLink();
|
||||
$this->drupalGet('admin/structure/menu/link/' . $item->getPluginId() . '/edit');
|
||||
$this->assertResponse($response);
|
||||
if ($response == 200) {
|
||||
$this->assertText(t('Edit menu item'), 'Menu edit page was displayed');
|
||||
}
|
||||
|
||||
// View add menu page.
|
||||
$this->drupalGet('admin/structure/menu/add');
|
||||
$this->assertResponse($response);
|
||||
if ($response == 200) {
|
||||
$this->assertText(t('Menus'), 'Add menu page was displayed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests menu block settings.
|
||||
*/
|
||||
protected function doTestMenuBlock() {
|
||||
$menu_id = $this->menu->id();
|
||||
$block_id = $this->blockPlacements[$menu_id];
|
||||
$this->drupalGet('admin/structure/block/manage/' . $block_id);
|
||||
$this->drupalPostForm(NULL, [
|
||||
'settings[depth]' => 3,
|
||||
'settings[level]' => 2,
|
||||
], t('Save block'));
|
||||
$block = Block::load($block_id);
|
||||
$settings = $block->getPlugin()->getConfiguration();
|
||||
$this->assertEqual($settings['depth'], 3);
|
||||
$this->assertEqual($settings['level'], 2);
|
||||
// Reset settings.
|
||||
$block->getPlugin()->setConfigurationValue('depth', 0);
|
||||
$block->getPlugin()->setConfigurationValue('level', 1);
|
||||
$block->save();
|
||||
}
|
||||
|
||||
}
|
33
web/core/modules/menu_ui/src/Tests/MenuUninstallTest.php
Normal file
33
web/core/modules/menu_ui/src/Tests/MenuUninstallTest.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\system\Entity\Menu;
|
||||
|
||||
/**
|
||||
* Tests that uninstalling menu does not remove custom menus.
|
||||
*
|
||||
* @group menu_ui
|
||||
*/
|
||||
class MenuUninstallTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('menu_ui');
|
||||
|
||||
/**
|
||||
* Tests Menu uninstall.
|
||||
*/
|
||||
public function testMenuUninstall() {
|
||||
\Drupal::service('module_installer')->uninstall(array('menu_ui'));
|
||||
|
||||
\Drupal::entityManager()->getStorage('menu')->resetCache(array('admin'));
|
||||
|
||||
$this->assertTrue(Menu::load('admin'), 'The \'admin\' menu still exists after uninstalling Menu UI module.');
|
||||
}
|
||||
|
||||
}
|
77
web/core/modules/menu_ui/src/Tests/MenuWebTestBase.php
Normal file
77
web/core/modules/menu_ui/src/Tests/MenuWebTestBase.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\menu_ui\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Base class for menu web tests.
|
||||
*/
|
||||
abstract class MenuWebTestBase extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('menu_ui', 'menu_link_content');
|
||||
|
||||
/**
|
||||
* Fetches the menu item from the database and compares it to expected item.
|
||||
*
|
||||
* @param int $menu_plugin_id
|
||||
* Menu item id.
|
||||
* @param array $expected_item
|
||||
* Array containing properties to verify.
|
||||
*/
|
||||
function assertMenuLink($menu_plugin_id, array $expected_item) {
|
||||
// Retrieve menu link.
|
||||
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
$menu_link_manager->resetDefinitions();
|
||||
// Reset the static load cache.
|
||||
\Drupal::entityManager()->getStorage('menu_link_content')->resetCache();
|
||||
$definition = $menu_link_manager->getDefinition($menu_plugin_id);
|
||||
|
||||
$entity = NULL;
|
||||
|
||||
// Pull the path from the menu link content.
|
||||
if (strpos($menu_plugin_id, 'menu_link_content') === 0) {
|
||||
list(, $uuid) = explode(':', $menu_plugin_id, 2);
|
||||
/** @var \Drupal\menu_link_content\Entity\MenuLinkContent $entity */
|
||||
$entity = \Drupal::entityManager()->loadEntityByUuid('menu_link_content', $uuid);
|
||||
}
|
||||
|
||||
if (isset($expected_item['children'])) {
|
||||
$child_ids = array_values($menu_link_manager->getChildIds($menu_plugin_id));
|
||||
sort($expected_item['children']);
|
||||
if ($child_ids) {
|
||||
sort($child_ids);
|
||||
}
|
||||
$this->assertEqual($expected_item['children'], $child_ids);
|
||||
unset($expected_item['children']);
|
||||
}
|
||||
|
||||
if (isset($expected_item['parents'])) {
|
||||
$parent_ids = array_values($menu_link_manager->getParentIds($menu_plugin_id));
|
||||
$this->assertEqual($expected_item['parents'], $parent_ids);
|
||||
unset($expected_item['parents']);
|
||||
}
|
||||
|
||||
if (isset($expected_item['langcode']) && $entity) {
|
||||
$this->assertEqual($entity->langcode->value, $expected_item['langcode']);
|
||||
unset($expected_item['langcode']);
|
||||
}
|
||||
|
||||
if (isset($expected_item['enabled']) && $entity) {
|
||||
$this->assertEqual($entity->enabled->value, $expected_item['enabled']);
|
||||
unset($expected_item['enabled']);
|
||||
}
|
||||
|
||||
foreach ($expected_item as $key => $value) {
|
||||
$this->assertTrue(isset($definition[$key]));
|
||||
$this->assertEqual($definition[$key], $value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\menu_ui\Kernel\Migrate;
|
||||
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
|
||||
/**
|
||||
* Tests migration of menu_ui settings.
|
||||
*
|
||||
* @group menu_ui
|
||||
*/
|
||||
class MigrateMenuSettingsTest extends MigrateDrupal7TestBase {
|
||||
|
||||
public static $modules = ['menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(['menu_ui']);
|
||||
$this->executeMigration('menu_settings');
|
||||
}
|
||||
|
||||
public function testMigration() {
|
||||
$this->assertTrue(\Drupal::config('menu_ui.settings')->get('override_parent_selector'));
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue