Commit daaccc3b authored by webchick's avatar webchick

Issue #1869476 by rteijeiro, LewisNyman, lauriii, Wim Leers, mdrummond,...

Issue #1869476 by rteijeiro, LewisNyman, lauriii, Wim Leers, mdrummond, swentel, hosef, cbiggins, larowlan, sun, EclipseGc, Gábor Hojtsy: Convert global menus (primary links, secondary links) into blocks.
parent f140282e
......@@ -161,12 +161,6 @@ theme_settings:
node_user_picture:
type: boolean
label: 'User pictures in posts'
main_menu:
type: boolean
label: 'Main menu'
secondary_menu:
type: boolean
label: 'Secondary menu'
slogan:
type: boolean
label: 'Site slogan'
......
......@@ -2451,20 +2451,6 @@ function drupal_prepare_page($page) {
\Drupal::moduleHandler()->alter('page', $page);
\Drupal::theme()->alter('page', $page);
// The "main" and "secondary" menus are never part of the page-level render
// array and therefore their cache tags will never bubble up into the page
// cache, even though they should be. This happens because they're rendered
// directly by the theme system.
// @todo Remove this once https://drupal.org/node/1869476 lands.
if (theme_get_setting('features.main_menu') && count(menu_main_menu())) {
$main_links_source = _menu_get_links_source('main_links', 'main');
$page['page_top']['#cache']['tags'][] = 'menu:' . $main_links_source;
}
if (theme_get_setting('features.secondary_menu') && count(menu_secondary_menu())) {
$secondary_links_source = _menu_get_links_source('secondary_links', 'account');
$page['page_top']['#cache']['tags'][] = 'menu:' . $secondary_links_source;
}
// If no module has taken care of the main content, add it to the page now.
// This allows the site to still be usable even if no modules that
// control page regions (for example, the Block module) are enabled.
......
......@@ -413,72 +413,6 @@ function menu_list_system_menus() {
);
}
/**
* Returns an array of links to be rendered as the Main menu.
*/
function menu_main_menu() {
$main_links_source = _menu_get_links_source('main_links', 'main');
return menu_navigation_links($main_links_source);
}
/**
* Returns an array of links to be rendered as the Secondary links.
*/
function menu_secondary_menu() {
$main_links_source = _menu_get_links_source('main_links', 'main');
$secondary_links_source = _menu_get_links_source('secondary_links', 'account');
// If the secondary menu source is set as the primary menu, we display the
// second level of the primary menu.
if ($secondary_links_source == $main_links_source) {
return menu_navigation_links($main_links_source, 1);
}
else {
return menu_navigation_links($secondary_links_source, 0);
}
}
/**
* Returns the source of links of a menu.
*
* @param string $name
* A string configuration key of menu link source.
* @param string $default
* Default menu name.
*
* @return string
* Returns menu name, if exist
*/
function _menu_get_links_source($name, $default) {
$config = \Drupal::config('menu_ui.settings');
return \Drupal::moduleHandler()->moduleExists('menu_ui') ? $config->get($name) : $default;
}
/**
* Builds a renderable array for a navigation menu.
*
* @param string $menu_name
* The name of the menu.
* @param int $level
* Optional, the depth of the menu to be returned.
*
* @return array
* A renderable array.
*/
function menu_navigation_links($menu_name, $level = 0) {
$menu_tree = \Drupal::menuTree();
$parameters = $menu_tree->getCurrentRouteMenuTreeParameters($menu_name);
$parameters->setMaxDepth($level + 1);
$tree = $menu_tree->load($menu_name, $parameters);
$manipulators = array(
array('callable' => 'menu.default_tree_manipulators:checkAccess'),
array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
array('callable' => 'menu.default_tree_manipulators:extractSubtreeOfActiveTrail', 'args' => array($level)),
);
$tree = $menu_tree->transform($tree, $manipulators);
return $menu_tree->build($tree);
}
/**
* Collects the local tasks (tabs), action links, and the root path.
*
......
......@@ -1805,8 +1805,6 @@ function template_preprocess_page(&$variables) {
$variables['site_slogan'] = (theme_get_setting('features.slogan') ? Xss::filterAdmin($site_config->get('slogan')) : '');
if (!defined('MAINTENANCE_MODE')) {
$variables['main_menu'] = theme_get_setting('features.main_menu') ? menu_main_menu() : array();
$variables['secondary_menu'] = theme_get_setting('features.secondary_menu') ? menu_secondary_menu() : array();
$variables['action_links'] = menu_get_local_actions();
$variables['tabs'] = menu_local_tabs();
......@@ -1824,21 +1822,11 @@ function template_preprocess_page(&$variables) {
}
}
else {
$variables['main_menu'] = array();
$variables['secondary_menu'] = array();
$variables['action_links'] = array();
$variables['tabs'] = array();
$variables['feed_icons'] = '';
}
// Pass the main menu and secondary menu to the template as render arrays.
if (!empty($variables['main_menu'])) {
$variables['main_menu']['#prefix'] = '<h2 id="links__system_main_menu" class="visually-hidden">' . t('Main menu') . '</h2>';
}
if (!empty($variables['secondary_menu'])) {
$variables['secondary_menu']['#prefix'] = '<h2 id="links__system_secondary_menu" class="visually-hidden">' . t('Secondary menu') . '</h2>';
}
if ($node = \Drupal::routeMatch()->getParameter('node')) {
$variables['node'] = $node;
}
......
......@@ -35,8 +35,6 @@ class ThemeHandler implements ThemeHandlerInterface {
'node_user_picture',
'comment_user_picture',
'comment_user_verification',
'main_menu',
'secondary_menu',
);
/**
......@@ -451,6 +449,8 @@ public function rebuildThemeData() {
'sidebar_second' => 'Right sidebar',
'content' => 'Content',
'header' => 'Header',
'primary_menu' => 'Primary menu',
'secondary_menu' => 'Secondary menu',
'footer' => 'Footer',
'highlighted' => 'Highlighted',
'help' => 'Help',
......
......@@ -234,31 +234,4 @@ public function flatten(array $tree) {
return $tree;
}
/**
* Extracts a subtree of the active trail.
*
* @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
* The menu link tree to manipulate.
* @param int $level
* The level in the active trail to extract.
*
* @return \Drupal\Core\Menu\MenuLinkTreeElement[]
* The manipulated menu link tree.
*/
public function extractSubtreeOfActiveTrail(array $tree, $level) {
// Go down the active trail until the right level is reached.
while ($level-- > 0 && $tree) {
// Loop through the current level's elements until we find one that is in
// the active trail.
while ($element = array_shift($tree)) {
if ($element->inActiveTrail) {
// If the element is in the active trail, we continue in the subtree.
$tree = $element->subtree;
break;
}
}
}
return $tree;
}
}
......@@ -48,7 +48,7 @@ function testHtml() {
$this->assertFieldByXPath('//div[@id="block-test-html-block" and @data-custom-attribute="foo"]', NULL, 'HTML ID and attributes for test block are valid and on the same DOM element.');
// Ensure expected markup for a menu block.
$elements = $this->xpath('//div[contains(@class, :div-class)]/ul[contains(@class, :ul-class)]/li', array(':div-class' => 'block-system', ':ul-class' => 'menu'));
$elements = $this->xpath('//nav[contains(@class, :nav-class)]/ul[contains(@class, :ul-class)]/li', array(':nav-class' => 'block-menu', ':ul-class' => 'menu'));
$this->assertTrue(!empty($elements), 'The proper block markup was found.');
}
......
......@@ -64,7 +64,7 @@ protected function setUp() {
),
array(
'label' => 'Powered by Drupal',
'tr' => '12',
'tr' => '16',
'plugin_id' => 'system_powered_by_block',
'settings' => array('region' => 'footer', 'id' => 'powered'),
'test_weight' => '0',
......
main_links: main
secondary_links: account
override_parent_selector: false
......@@ -4,12 +4,6 @@ menu_ui.settings:
type: mapping
label: 'Menu settings'
mapping:
main_links:
type: string
label: 'Main links'
secondary_links:
type: string
label: 'Source for secondary links'
override_parent_selector:
type: boolean
label: 'Override parent selector'
......
......@@ -7,9 +7,3 @@ menu_ui.overview_page:
title: 'List'
route_name: menu_ui.overview_page
base_route: menu_ui.overview_page
menu_ui.settings:
title: 'Settings'
route_name: menu_ui.settings
base_route: menu_ui.overview_page
weight: 100
menu_ui.settings:
path: '/admin/structure/menu/settings'
defaults:
_form: 'Drupal\menu_ui\MenuSettingsForm'
_title: 'Menus'
requirements:
_permission: 'administer menu'
menu_ui.overview_page:
path: '/admin/structure/menu'
defaults:
......
<?php
/**
* @file
* Contains \Drupal\menu_ui\MenuSettingsForm.
*/
namespace Drupal\menu_ui;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Select the menus to be used for the main and secondary links for this site.
*/
class MenuSettingsForm extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'menu_configure';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('menu_ui.settings');
$form['intro'] = array(
'#type' => 'item',
'#markup' => t('The Menu UI module allows on-the-fly creation of menu links in the content authoring forms. To configure these settings for a particular content type, visit the <a href="@content-types">Content types</a> page, click the <em>edit</em> link for the content type, and go to the <em>Menu settings</em> section.', array('@content-types' => $this->url('node.overview_types'))),
);
$menu_options = menu_ui_get_menus();
$main = $config->get('main_links');
$form['menu_main_links_source'] = array(
'#type' => 'select',
'#title' => t('Source for the Main links'),
'#default_value' => $main,
'#empty_option' => t('No Main links'),
'#options' => $menu_options,
'#tree' => FALSE,
'#description' => t('Select what should be displayed as the Main links (typically at the top of the page).'),
);
$form['menu_secondary_links_source'] = array(
'#type' => 'select',
'#title' => t('Source for the Secondary links'),
'#default_value' => $config->get('secondary_links'),
'#empty_option' => t('No Secondary links'),
'#options' => $menu_options,
'#tree' => FALSE,
'#description' => t('Select the source for the Secondary links. An advanced option allows you to use the same source for both Main links (currently %main) and Secondary links: if your source menu has two levels of hierarchy, the top level menu links will appear in the Main links, and the children of the active link will appear in the Secondary links.', array('%main' => $main ? $menu_options[$main] : t('none'))),
);
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->config('menu_ui.settings')
->set('main_links', $form_state->getValue('menu_main_links_source'))
->set('secondary_links', $form_state->getValue('menu_secondary_links_source'))
->save();
parent::submitForm($form, $form_state);
}
}
......@@ -22,10 +22,13 @@ class MenuNodeTest extends WebTestBase {
*
* @var array
*/
public static $modules = array('menu_ui', 'test_page_test', 'node');
public static $modules = array('menu_ui', 'test_page_test', 'node', 'block');
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('system_menu_block:main');
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
$this->admin_user = $this->drupalCreateUser(array(
......
......@@ -839,13 +839,6 @@ private function verifyAccess($response = 200) {
$this->assertText(t('Edit menu item'), 'Menu edit page was displayed');
}
// View menu settings page.
$this->drupalGet('admin/structure/menu/settings');
$this->assertResponse($response);
if ($response == 200) {
$this->assertText(t('Menus'), 'Menu settings page was displayed');
}
// View add menu page.
$this->drupalGet('admin/structure/menu/add');
$this->assertResponse($response);
......
......@@ -22,14 +22,6 @@ public function load() {
'name',
'value',
))
->values(array(
'name' => 'menu_primary_links_source',
'value' => 's:13:"primary-links";',
))
->values(array(
'name' => 'menu_secondary_links_source',
'value' => 's:15:"secondary-links";',
))
->values(array(
'name' => 'menu_override_parent_selector',
'value' => 'b:0;',
......
......@@ -46,8 +46,6 @@ protected function setUp() {
*/
public function testMenuSettings() {
$config = \Drupal::config('menu_ui.settings');
$this->assertIdentical($config->get('main_links'), 'primary-links');
$this->assertIdentical($config->get('secondary_links'), 'secondary-links');
$this->assertIdentical($config->get('override_parent_selector'), FALSE);
$this->assertConfigSchema(\Drupal::service('config.typed'), 'menu_ui.settings', $config->get());
}
......
......@@ -10,8 +10,6 @@ features:
logo: true
name: true
node_user_picture: true
main_menu: true
secondary_menu: true
slogan: true
logo:
path: ''
......
......@@ -106,8 +106,6 @@ public function buildForm(array $form, FormStateInterface $form_state, $theme =
'comment_user_picture' => t('User pictures in comments'),
'comment_user_verification' => t('User verification status in comments'),
'favicon' => t('Shortcut icon'),
'main_menu' => t('Main menu'),
'secondary_menu' => t('Secondary menu'),
);
// Some features are not always available
......
......@@ -77,7 +77,11 @@ function testPageCacheTags() {
'block:bartik_login',
'block:bartik_footer',
'block:bartik_powered',
'block:bartik_main_menu',
'block:bartik_account_menu',
'block_plugin:system_main_block',
'block_plugin:system_menu_block__account',
'block_plugin:system_menu_block__main',
'block_plugin:system_menu_block__tools',
'block_plugin:user_login_block',
'block_plugin:system_menu_block__footer',
......@@ -86,6 +90,7 @@ function testPageCacheTags() {
'node:' . $node_1->id(),
'user:' . $author_1->id(),
'filter_format:basic_html',
'menu:account',
'menu:tools',
'menu:footer',
'menu:main',
......@@ -103,7 +108,11 @@ function testPageCacheTags() {
'block:' . $block->id(),
'block:bartik_footer',
'block:bartik_powered',
'block:bartik_main_menu',
'block:bartik_account_menu',
'block_plugin:system_main_block',
'block_plugin:system_menu_block__account',
'block_plugin:system_menu_block__main',
'block_plugin:system_menu_block__tools',
'block_plugin:user_login_block',
'block_plugin:views_block__comments_recent-block_1',
......@@ -113,6 +122,7 @@ function testPageCacheTags() {
'node:' . $node_2->id(),
'user:' . $author_2->id(),
'filter_format:full_html',
'menu:account',
'menu:tools',
'menu:footer',
'menu:main',
......
......@@ -278,7 +278,7 @@ function testBreadCrumbs() {
// untranslated menu links automatically generated from menu router items
// ('taxonomy/term/%') should never be translated and appear in any menu
// other than the breadcrumb trail.
$elements = $this->xpath('//div[@id=:menu]/descendant::a[@href=:href]', array(
$elements = $this->xpath('//nav[@id=:menu]/descendant::a[@href=:href]', array(
':menu' => 'block-bartik-tools',
':href' => url($link_path),
));
......
......@@ -75,8 +75,6 @@ function testTitleXSS() {
$edit = array(
'toggle_name' => TRUE,
'toggle_slogan' => TRUE,
'toggle_main_menu' => TRUE,
'toggle_secondary_menu' => TRUE,
);
$this->drupalPostForm('admin/appearance/settings', $edit, t('Save configuration'));
......
......@@ -156,13 +156,18 @@ function system_help($route_name, RouteMatchInterface $route_match) {
function system_theme() {
return array_merge(drupal_common_theme(), array(
// Normally theme suggestion templates are only picked up when they are in
// themes. We explicitly define the block__system_branding_block theme
// suggestion here so that the template in core/modules/system/templates
// is picked up.
// themes. We explicitly define theme suggestions here so that the block
// templates in core/modules/system/templates are picked up.
'block__system_branding_block' => array(
'render element' => 'elements',
'base hook' => 'block',
'template' => 'block--system-branding-block',
),
'block__system_menu_block' => array(
'render element' => 'elements',
'base hook' => 'block',
'template' => 'block--system-menu-block',
),
'system_themes_page' => array(
'variables' => array(
'theme_groups' => array(),
......@@ -673,14 +678,6 @@ function system_preprocess_block(&$variables) {
case 'system_help_block':
$variables['attributes']['role'] = 'complementary';
break;
case 'system_menu_block':
$menus = menu_list_system_menus();
if (isset($menus[$variables['derivative_plugin_id']])) {
$variables['attributes']['role'] = 'navigation';
$variables['attributes']['class'][] = 'block-menu';
}
break;
}
}
......@@ -928,8 +925,6 @@ function _system_default_theme_features() {
'node_user_picture',
'comment_user_picture',
'comment_user_verification',
'main_menu',
'secondary_menu',
);
}
......
{#
/**
* @file
* Default theme implementation for a menu block.
*
* Available variables:
* - plugin_id: The ID of the block implementation.
* - label: The configured label of the block if visible.
* - configuration: A list of the block's configuration values.
* - label: The configured label for the block.
* - label_display: The display settings for the label.
* - module: The module that provided this block plugin.
* - cache: The cache settings.
* - Block plugin specific settings will also be stored here.
* - block - The full block entity.
* - label_hidden: The hidden block title value if the block was
* configured to hide the title ('label' is empty in this case).
* - module: The module that generated the block.
* - delta: An ID for the block, unique within each module.
* - region: The block region embedding the current block.
* - content: The content of this block.
* - attributes: HTML attributes for the containing element.
* - id: A valid HTML ID and guaranteed unique.
* - title_attributes: HTML attributes for the title element.
* - content_attributes: HTML attributes for the content element.
* - title_prefix: Additional output populated by modules, intended to be
* displayed in front of the main title tag that appears in the template.
* - title_suffix: Additional output populated by modules, intended to be
* displayed after the main title tag that appears in the template.
*
* Headings should be used on navigation menus that consistently appear on
* multiple pages. When this menu block's label is configured to not be
* displayed, it is automatically made invisible using the 'visually-hidden' CSS
* class, which still keeps it visible for screen-readers and assistive
* technology. Headings allow screen-reader and keyboard only users to navigate
* to or skip the links.
* See http://juicystudio.com/article/screen-readers-display-none.php and
* http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.
*
* @ingroup themeable
*/
#}
{%
set classes = [
'block',
'block-menu',
'navigation',
'menu--' ~ derivative_plugin_id|clean_class,
]
%}
{% set heading_id = attributes.id ~ '-menu'|clean_id %}
<nav{{ attributes.addClass(classes) }} role="navigation" aria-labelledby="{{ heading_id }}">
{# Label. If not displayed, we still provide it for screen readers. #}
{% if not configuration.label_display %}
{% set title_attributes = title_attributes.addClass('visually-hidden') %}
{% endif %}
{{ title_prefix }}
<h2 id="{{ heading_id }}"{{ title_attributes }}>{{ configuration.label }}</h2>
{{ title_suffix }}
{# Menu. #}
{% block content %}
{{ content }}
{% endblock %}
</nav>
......@@ -26,9 +26,6 @@
* slogan has been disabled in theme settings.
*
* Navigation:
* - main_menu: The Main menu links for the site, if they have been configured.
* - secondary_menu: The Secondary menu links for the site, if they have been
* configured.
* - breadcrumb: The breadcrumb trail for the current page.
*
* Page content (in order of occurrence in the default page.html.twig):
......@@ -50,6 +47,8 @@
*
* Regions:
* - page.header: Items for the header region.
* - page.primary_menu: Items for the primary menu region.
* - page.secondary_menu: Items for the secondary menu region.
* - page.highlighted: Items for the highlighted content region.
* - page.help: Dynamic help text, mostly for admin pages.
* - page.content: The main content of the current page.
......@@ -90,17 +89,13 @@
<div class="site-slogan">{{ site_slogan }}</div>
{% endif %}
</div>{# ./name-and-slogan #}
{% endif %}
{% endif %}
{{ page.header }}
</header>
{% if main_menu or secondary_menu %}
<nav role="navigation" aria-labelledby="links__system_main_menu">
{{ main_menu }}
{{ secondary_menu }}
</nav>
{% endif %}
{{ page.primary_menu }}
{{ page.secondary_menu }}
{{ breadcrumb }}
......
......@@ -24,8 +24,12 @@ class UserAccountLinksTests extends WebTestBase {
*/
public static $modules = array('menu_ui', 'block', 'test_page_test');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('system_menu_block:account');
// Make test-page default.
\Drupal::config('system.site')->set('page.front', 'test-page')->save();
}
......
......@@ -22,9 +22,21 @@ class UserPasswordResetTest extends WebTestBase {
*/
protected $account;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('system_menu_block:account');
// Create a user.
$account = $this->drupalCreateUser();
......
......@@ -108,7 +108,7 @@ public function testPageDisplayMenu() {
$this->drupalPlaceBlock('system_menu_block:tools');
$this->drupalGet('<front>');
$menu_link = $this->cssSelect('div.block-menu ul.menu a');
$menu_link = $this->cssSelect('nav.block-menu ul.menu a');
$this->assertEqual((string) $menu_link[0], 'Test menu link');
// Update the menu link.
......@@ -117,7 +117,7 @@ public function testPageDisplayMenu() {
], t('Save'));
$this->drupalGet('<front>');
$menu_link = $this->cssSelect('div.block-menu ul.menu a');