diff --git a/core/modules/system/tests/src/Functional/Cache/AssertPageCacheContextsAndTagsTrait.php b/core/modules/system/tests/src/Functional/Cache/AssertPageCacheContextsAndTagsTrait.php index 1cc2cad..969e794 100644 --- a/core/modules/system/tests/src/Functional/Cache/AssertPageCacheContextsAndTagsTrait.php +++ b/core/modules/system/tests/src/Functional/Cache/AssertPageCacheContextsAndTagsTrait.php @@ -134,7 +134,7 @@ protected function assertCacheTags(array $expected_tags, $include_default_tags = */ protected function assertCacheContexts(array $expected_contexts, $message = NULL, $include_default_contexts = TRUE) { if ($include_default_contexts) { - $default_contexts = ['languages:language_interface', 'theme']; + $default_contexts = ['languages:language_interface', 'route', 'theme']; // Add the user.permission context to the list of default contexts except // when user is already there. if (!in_array('user', $expected_contexts)) { diff --git a/core/modules/toolbar/src/Controller/ToolbarController.php b/core/modules/toolbar/src/Controller/ToolbarController.php index c94e35b..6405955 100644 --- a/core/modules/toolbar/src/Controller/ToolbarController.php +++ b/core/modules/toolbar/src/Controller/ToolbarController.php @@ -90,7 +90,11 @@ public static function preRenderAdministrationTray(array $element) { * @internal */ public static function preRenderGetRenderedSubtrees(array $data) { + $output = ''; + /** @var \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree */ $menu_tree = \Drupal::service('toolbar.menu_tree'); + /** @var \Drupal\Core\Menu\LocalTaskManagerInterface $local_task_manager */ + $local_task_manager = \Drupal::service('plugin.manager.menu.local_task'); // Load the administration menu. The first level is the "Administration" // link. In order to load the children of that link and the subsequent two // levels, start at the second level and end at the fourth. @@ -108,13 +112,97 @@ public static function preRenderGetRenderedSubtrees(array $data) { // Calculated the combined cacheability of all subtrees. $cacheability = new CacheableMetadata(); foreach ($tree as $element) { + // Get local tasks for this menu item and build render array. + $local_tasks = $local_task_manager->getTasksBuild($element->link->getRouteName(), $cacheability); /** @var \Drupal\Core\Menu\MenuLinkInterface $link */ $link = $element->link; if ($element->subtree) { $subtree = $menu_tree->build($element->subtree); + if (count($local_tasks)) { + // Reduce the local tasks tree by elements already in the admin menu + // tree. + $local_tasks_subtree_reduced = []; + foreach ($local_tasks as $key => $tasks) { + foreach ($local_tasks[$key] as $task_key => $task) { + if (!isset($subtree['#items'][$task_key]) && $task_key != $element->link->getRouteName()) { + $local_tasks_subtree_reduced[$key][$task_key] = $task; + } + } + } + // Merge the local tasks into the existing 2nd level subtree. + if (count($local_tasks_subtree_reduced)) { + $local_tasks_subtree_build = _build_tasks_as_menu_tree($local_tasks_subtree_reduced); + $subtree['#items'] += $local_tasks_subtree_build['#items']; + } + } + foreach ($element->subtree as $sub_element) { + // Get local tasks for this sub menu item and build render array. + $sub_local_tasks = $local_task_manager->getTasksBuild($sub_element->link->getRouteName(), $cacheability); + $route_name = $sub_element->link->getRouteName(); + $route_name_parent = $element->link->getRouteName(); + if (count($sub_local_tasks)) { + $sub_local_tasks_subtree_reduced = []; + if (count($sub_element->subtree)) { + // Reduce the local tasks tree by elements already in the admin + // menu tree. + foreach ($sub_local_tasks as $key => $tasks) { + foreach ($sub_local_tasks[$key] as $task_key => $task) { + if (!isset($subtree['#items'][$task_key]) + && !isset($subtree['#items'][$route_name]['below'][$task_key]) + && $task_key != $route_name + && $task_key != $route_name_parent) { + $sub_local_tasks_subtree_reduced[$key][$task_key] = $task; + } + } + } + // Merge the local tasks into the existing 3rd level subtree. + if (count(current($sub_local_tasks_subtree_reduced))) { + $sub_local_tasks_subtree_build = _build_tasks_as_menu_tree($sub_local_tasks_subtree_reduced); + $subtree['#items'][$route_name]['below'] += $sub_local_tasks_subtree_build['#items']; + } + } + else { + // Reduce the local tasks tree by elements already in the admin + // menu tree. + foreach ($sub_local_tasks as $key => $tasks) { + foreach ($sub_local_tasks[$key] as $task_key => $task) { + if (!isset($subtree['#items'][$task_key]) && $task_key != $route_name && $task_key != $route_name_parent) { + $sub_local_tasks_subtree_reduced[$key][$task_key] = $task; + } + } + } + // Add the local tasks as new 3rd level subtree. + if (!empty(current($sub_local_tasks_subtree_reduced))) { + $sub_local_tasks_subtree_build = _build_tasks_as_menu_tree($sub_local_tasks_subtree_reduced); + $subtree['#items'][$route_name]['below'] = $sub_local_tasks_subtree_build['#items']; + } + } + } + } $output = \Drupal::service('renderer')->renderPlain($subtree); $cacheability = $cacheability->merge(CacheableMetadata::createFromRenderArray($subtree)); } + elseif (count($local_tasks)) { + // Reduce the local tasks tree by elements already in the admin menu + // tree. + $local_tasks_subtree_reduced = []; + foreach ($local_tasks as $key => $tasks) { + foreach ($local_tasks[$key] as $task_key => $task) { + if ($task_key != $element->link->getRouteName()) { + $local_tasks_subtree_reduced[$key][$task_key] = $task; + } + } + } + // Add the local tasks as new 2nd level subtree. + if (count($local_tasks_subtree_reduced)) { + $local_tasks_subtree_build = _build_tasks_as_menu_tree($local_tasks_subtree_reduced); + $output = \Drupal::service('renderer')->renderPlain($local_tasks_subtree_build); + $cacheability = $cacheability->merge(CacheableMetadata::createFromRenderArray($local_tasks_subtree_build)); + } + else { + $output = ''; + } + } else { $output = ''; } diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module index 483a30b..c95bba2 100644 --- a/core/modules/toolbar/toolbar.module +++ b/core/modules/toolbar/toolbar.module @@ -290,3 +290,30 @@ function _toolbar_get_subtrees_hash() { $hash = Crypt::hashBase64(serialize($subtrees)); return [$hash, $cacheability]; } + +/** + * Generates a toolbar menu tree from a given tree of local tasks. + * + * @param \Drupal\Core\Menu\LocalTaskInterface[] $tree + * An array of task levels. + * + * @return array + * A render array representing the menu tree. + */ +function _build_tasks_as_menu_tree($tree) { + $menu_tree = [ + '#sorted' => TRUE, + '#theme' => 'menu__toolbar__admin', + '#items' => [] + ]; + foreach ($tree as $level => $instances) { + foreach ($instances as $plugin_id => $child) { + // Only 1st level of local tasks will be rendered. + if ($level == 0) { + $menu_tree['#items'][$plugin_id] = $child['#link']; + } + } + } + + return $menu_tree; +}