diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml index a6f61b68ed..e68bbae5a8 100644 --- a/core/modules/system/config/schema/system.schema.yml +++ b/core/modules/system/config/schema/system.schema.yml @@ -337,6 +337,9 @@ block.settings.system_menu_block:*: depth: type: integer label: 'Maximum number of levels' + expand_all_items: + type: boolean + label: 'Expand all items' block.settings.local_tasks_block: type: block_settings diff --git a/core/modules/system/src/Plugin/Block/SystemMenuBlock.php b/core/modules/system/src/Plugin/Block/SystemMenuBlock.php index a551ca64d3..72e4963923 100644 --- a/core/modules/system/src/Plugin/Block/SystemMenuBlock.php +++ b/core/modules/system/src/Plugin/Block/SystemMenuBlock.php @@ -5,7 +5,9 @@ use Drupal\Core\Block\BlockBase; use Drupal\Core\Cache\Cache; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Menu\MenuActiveTrailInterface; use Drupal\Core\Menu\MenuLinkTreeInterface; +use Drupal\Core\Menu\MenuTreeParameters; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -31,6 +33,13 @@ class SystemMenuBlock extends BlockBase implements ContainerFactoryPluginInterfa */ protected $menuTree; + /** + * The active menu trail service. + * + * @var \Drupal\Core\Menu\MenuActiveTrailInterface + */ + protected $menuActiveTrail; + /** * Constructs a new SystemMenuBlock. * @@ -42,10 +51,13 @@ class SystemMenuBlock extends BlockBase implements ContainerFactoryPluginInterfa * The plugin implementation definition. * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree * The menu tree service. + * @param \Drupal\Core\Menu\MenuActiveTrailInterface $menu_active_trail + * The active menu trail service. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, MenuLinkTreeInterface $menu_tree) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, MenuLinkTreeInterface $menu_tree, MenuActiveTrailInterface $menu_active_trail) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->menuTree = $menu_tree; + $this->menuActiveTrail = $menu_active_trail; } /** @@ -56,7 +68,8 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('menu.link_tree') + $container->get('menu.link_tree'), + $container->get('menu.active_trail') ); } @@ -98,6 +111,12 @@ public function blockForm($form, FormStateInterface $form_state) { '#required' => TRUE, ]; + $form['menu_levels']['expand_all_items'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Expand all menu items'), + '#default_value' => ![$config['expand_all_items']], + ]; + return $form; } @@ -117,6 +136,7 @@ public static function processMenuLevelParents(&$element, FormStateInterface $fo public function blockSubmit($form, FormStateInterface $form_state) { $this->configuration['level'] = $form_state->getValue('level'); $this->configuration['depth'] = $form_state->getValue('depth'); + $this->configuration['expand_all_items'] = $form_state->getValue('expand_all_items'); } /** @@ -124,7 +144,14 @@ public function blockSubmit($form, FormStateInterface $form_state) { */ public function build() { $menu_name = $this->getDerivativeId(); - $parameters = $this->menuTree->getCurrentRouteMenuTreeParameters($menu_name); + if ($this->configuration['expand_all_items']) { + $parameters = new MenuTreeParameters(); + $active_trail = $this->menuActiveTrail->getActiveTrailIds($menu_name); + $parameters->setActiveTrail($active_trail); + } + else { + $parameters = $this->menuTree->getCurrentRouteMenuTreeParameters($menu_name); + } // Adjust the menu tree parameters based on the block's configuration. $level = $this->configuration['level']; @@ -173,6 +200,7 @@ public function defaultConfiguration() { return [ 'level' => 1, 'depth' => 0, + 'expand_all_items' => FALSE, ]; } diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php index ba8e798ca1..ee9fc3c887 100644 --- a/core/modules/system/system.post_update.php +++ b/core/modules/system/system.post_update.php @@ -178,3 +178,18 @@ function system_post_update_extra_fields(&$sandbox = NULL) { function system_post_update_states_clear_cache() { // Empty post-update hook. } + +/** + * Initialize 'expand_all_items' values to system_menu_block. + */ +function system_post_update_add_expand_all_items_key_in_system_menu_block() { + $block_storage = \Drupal::entityTypeManager()->getStorage('block'); + $blocks = $block_storage->loadMultiple(); + /** @var \Drupal\block\BlockInterface[] $blocks */ + foreach ($blocks as $block) { + if (strpos($block->getPluginId(), 'system_menu_block:') !== FALSE) { + $block->set('settings.expand_all_items', FALSE); + $block->save(); + } + } +} diff --git a/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php b/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php index 7a6d6685e9..55709aaba1 100644 --- a/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php +++ b/core/modules/system/tests/src/Kernel/Block/SystemMenuBlockTest.php @@ -282,6 +282,82 @@ public function testConfigLevelDepth() { } } + /** + * Tests the config expanded option. + */ + public function testConfigExpanded() { + + // Helper function to generate a configured block instance. + $place_block = function ($level, $depth) { + return $this->blockManager->createInstance('system_menu_block:' . $this->menu->id(), array( + 'region' => 'footer', + 'id' => 'machinename', + 'theme' => 'stark', + 'level' => $level, + 'depth' => $depth, + 'expand_all_items' => TRUE, + )); + }; + + // All the different block instances we're going to test. + $blocks = [ + 'all' => $place_block(1, 0), + 'level_1_and_beyond' => $place_block(1, 0), + 'level_2_and_beyond' => $place_block(2, 0), + 'level_3_and_beyond' => $place_block(3, 0), + ]; + + $expectations = []; + $expectations['all'] = [ + 'test.example1' => [], + 'test.example2' => [ + 'test.example3' => [ + 'test.example4' => [], + ], + ], + 'test.example5' => [ + 'test.example7' => [], + ], + 'test.example6' => [], + 'test.example8' => [], + ]; + $expectations['level_1_and_beyond'] = $expectations['all']; + $expectations['level_2_and_beyond'] = [ + 'test.example3' => [ + 'test.example4' => [], + ], + 'test.example7' => [], + ]; + $expectations['level_3_and_beyond'] = [ + 'test.example4' => [], + ]; + + // Scenario 1: test all block instances when there's no active trail. + foreach ($blocks as $id => $block) { + $block_build = $block->build(); + $items = isset($block_build['#items']) ? $block_build['#items'] : []; + $this->assertSame($expectations[$id], $this->convertBuiltMenuToIdTree($items), 'Menu block "' . $id . '" with no active trail renders the expected tree.'); + } + + // Scenario 2: test all block instances when there's an active trail. + $route = $this->container->get('router.route_provider')->getRouteByName('example3'); + $request = new Request(); + $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'example3'); + $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $route); + $this->container->get('request_stack')->push($request); + // \Drupal\Core\Menu\MenuActiveTrail uses the cache collector pattern, which + // includes static caching. Since this second scenario simulates a second + // request, we must also simulate it for the MenuActiveTrail service, by + // clearing the cache collector's static cache. + \Drupal::service('menu.active_trail')->clear(); + + foreach ($blocks as $id => $block) { + $block_build = $block->build(); + $items = isset($block_build['#items']) ? $block_build['#items'] : []; + $this->assertSame($expectations[$id], $this->convertBuiltMenuToIdTree($items), 'Menu block "' . $id . '" with an active trail renders the expected tree.'); + } + } + /** * Helper method to allow for easy menu link tree structure assertions. * diff --git a/core/profiles/minimal/config/install/block.block.stark_admin.yml b/core/profiles/minimal/config/install/block.block.stark_admin.yml index d4deb186da..4a30bafa0d 100644 --- a/core/profiles/minimal/config/install/block.block.stark_admin.yml +++ b/core/profiles/minimal/config/install/block.block.stark_admin.yml @@ -20,4 +20,5 @@ settings: label_display: visible level: 1 depth: 0 + expand_all_items: false visibility: { } diff --git a/core/profiles/minimal/config/install/block.block.stark_tools.yml b/core/profiles/minimal/config/install/block.block.stark_tools.yml index 2b6d1d5e44..fa8e72462b 100644 --- a/core/profiles/minimal/config/install/block.block.stark_tools.yml +++ b/core/profiles/minimal/config/install/block.block.stark_tools.yml @@ -20,4 +20,5 @@ settings: label_display: visible level: 1 depth: 0 + expand_all_items: false visibility: { } diff --git a/core/profiles/standard/config/install/block.block.bartik_account_menu.yml b/core/profiles/standard/config/install/block.block.bartik_account_menu.yml index 93cadc8bac..7999f9a5ed 100644 --- a/core/profiles/standard/config/install/block.block.bartik_account_menu.yml +++ b/core/profiles/standard/config/install/block.block.bartik_account_menu.yml @@ -20,4 +20,5 @@ settings: label_display: '0' level: 1 depth: 1 + expand_all_items: false visibility: { } diff --git a/core/profiles/standard/config/install/block.block.bartik_footer.yml b/core/profiles/standard/config/install/block.block.bartik_footer.yml index a8623d11ea..3e689c96b7 100644 --- a/core/profiles/standard/config/install/block.block.bartik_footer.yml +++ b/core/profiles/standard/config/install/block.block.bartik_footer.yml @@ -20,4 +20,5 @@ settings: label_display: '0' level: 1 depth: 0 + expand_all_items: false visibility: { } diff --git a/core/profiles/standard/config/install/block.block.bartik_main_menu.yml b/core/profiles/standard/config/install/block.block.bartik_main_menu.yml index dc7ebecb02..e2b601baf8 100644 --- a/core/profiles/standard/config/install/block.block.bartik_main_menu.yml +++ b/core/profiles/standard/config/install/block.block.bartik_main_menu.yml @@ -20,4 +20,5 @@ settings: label_display: '0' level: 1 depth: 1 + expand_all_items: false visibility: { } diff --git a/core/profiles/standard/config/install/block.block.bartik_tools.yml b/core/profiles/standard/config/install/block.block.bartik_tools.yml index 5a2aae5cb3..8734d2e0c4 100644 --- a/core/profiles/standard/config/install/block.block.bartik_tools.yml +++ b/core/profiles/standard/config/install/block.block.bartik_tools.yml @@ -20,4 +20,5 @@ settings: label_display: visible level: 1 depth: 0 + expand_all_items: false visibility: { }