render($element[$key]); } // Add the tray. if (isset($element[$key]['tray'])) { $attributes = []; if (!empty($element[$key]['tray']['#wrapper_attributes'])) { $attributes = $element[$key]['tray']['#wrapper_attributes']; } $variables['trays'][$key] = [ 'links' => $element[$key]['tray'], 'attributes' => new Attribute($attributes), ]; if (array_key_exists('#heading', $element[$key]['tray'])) { $variables['trays'][$key]['label'] = $element[$key]['tray']['#heading']; } } // Add the tab. if (isset($element[$key]['tab'])) { $attributes = []; // Pass the wrapper attributes along. if (!empty($element[$key]['#wrapper_attributes'])) { $attributes = $element[$key]['#wrapper_attributes']; } $variables['tabs'][$key] = [ 'link' => $element[$key]['tab'], 'attributes' => new Attribute($attributes), ]; } // Add other non-tray, non-tab child elements to the remainder variable for // later rendering. foreach (Element::children($element[$key]) as $child_key) { if (!in_array($child_key, ['tray', 'tab'])) { $variables['remainder'][$key][$child_key] = $element[$key][$child_key]; } } } } /** * Adds toolbar-specific attributes to the menu link tree. * * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree * The menu link tree to manipulate. * * @return \Drupal\Core\Menu\MenuLinkTreeElement[] * The manipulated menu link tree. */ function toolbar_menu_navigation_links(array $tree) { foreach ($tree as $element) { if ($element->subtree) { toolbar_menu_navigation_links($element->subtree); } // Make sure we have a path specific ID in place, so we can attach icons // and behaviors to the menu links. $link = $element->link; $url = $link->getUrlObject(); if (!$url->isRouted()) { // This is an unusual case, so just get a distinct, safe string. $id = substr(Crypt::hashBase64($url->getUri()), 0, 16); } else { $id = str_replace(['.', '<', '>'], ['-', '', ''], $url->getRouteName()); } // Get the non-localized title to make the icon class. $definition = $link->getPluginDefinition(); $element->options['attributes']['id'] = 'toolbar-link-' . $id; $element->options['attributes']['class'][] = 'toolbar-icon'; $element->options['attributes']['class'][] = 'toolbar-icon-' . strtolower(str_replace(['.', ' ', '_'], ['-', '-', '-'], $definition['id'])); $element->options['attributes']['title'] = $link->getDescription(); } return $tree; } /** * Implements hook_preprocess_HOOK() for HTML document templates. */ function toolbar_preprocess_html(&$variables): void { if (!\Drupal::currentUser()->hasPermission('access toolbar')) { return; } $variables['attributes']['class'][] = 'toolbar-loading'; } /** * Returns the rendered subtree of each top-level toolbar link. * * @return array * An array with the following key-value pairs: * - 'subtrees': the rendered subtrees * - 'cacheability: the associated cacheability. */ function toolbar_get_rendered_subtrees() { $data = [ '#pre_render' => [[ToolbarController::class, 'preRenderGetRenderedSubtrees']], '#cache' => [ 'keys' => [ 'toolbar_rendered_subtrees', ], ], '#cache_properties' => ['#subtrees'], ]; /** @var \Drupal\Core\Render\Renderer $renderer */ $renderer = \Drupal::service('renderer'); // The pre_render process populates $data during the render pipeline. // We need to pass by reference so that populated data can be returned and // used to resolve cacheability. $renderer->executeInRenderContext(new RenderContext(), function () use ($renderer, &$data) { $renderer->render($data); }); return [$data['#subtrees'], CacheableMetadata::createFromRenderArray($data)]; } /** * Returns the hash of the user-rendered toolbar subtrees and cacheability. * * @return array * An array with the hash of the toolbar subtrees and cacheability. */ function _toolbar_get_subtrees_hash() { [$subtrees, $cacheability] = toolbar_get_rendered_subtrees(); $hash = Crypt::hashBase64(serialize($subtrees)); return [$hash, $cacheability]; }