addCommand(new SetSubtreesCommand($subtrees)); // The Expires HTTP header is the heart of the client-side HTTP caching. The // additional server-side page cache only takes effect when the client // accesses the callback URL again (e.g., after clearing the browser cache // or when force-reloading a Drupal page). $max_age = 365 * 24 * 60 * 60; $response->setPrivate(); $response->setMaxAge($max_age); $expires = new \DateTime(); $expires->setTimestamp($this->time->getRequestTime() + $max_age); $response->setExpires($expires); return $response; } /** * Checks access for the subtree controller. * * @param string $hash * The hash of the toolbar subtrees. * * @return \Drupal\Core\Access\AccessResultInterface * The access result. */ public function checkSubTreeAccess($hash) { $expected_hash = _toolbar_get_subtrees_hash()[0]; return AccessResult::allowedIf($this->currentUser()->hasPermission('access toolbar') && hash_equals($expected_hash, $hash))->cachePerPermissions(); } /** * Renders the toolbar's administration tray. * * @param array $element * A renderable array. * * @return array * The updated renderable array. * * @see \Drupal\Core\Render\RendererInterface::render() */ public static function preRenderAdministrationTray(array $element) { $menu_tree = \Drupal::service('toolbar.menu_tree'); // Load the administrative menu. The first level is the "Administration" // link. In order to load the children of that link, start and end on the // second level. $parameters = new MenuTreeParameters(); $parameters->setMinDepth(2)->setMaxDepth(2)->onlyEnabledLinks(); // @todo Make the menu configurable in https://www.drupal.org/node/1869638. $tree = $menu_tree->load('admin', $parameters); $manipulators = [ ['callable' => 'menu.default_tree_manipulators:checkAccess'], ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'], ['callable' => 'toolbar_menu_navigation_links'], ]; $tree = $menu_tree->transform($tree, $manipulators); $element['administration_menu'] = $menu_tree->build($tree); return $element; } /** * Render API callback: Prepares the subtrees. * * This function is assigned as a #pre_render callback. * * @internal */ public static function preRenderGetRenderedSubtrees(array $data) { $menu_tree = \Drupal::service('toolbar.menu_tree'); $renderer = \Drupal::service('renderer'); // 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. $parameters = new MenuTreeParameters(); $parameters->setMinDepth(2)->setMaxDepth(4)->onlyEnabledLinks(); // @todo Make the menu configurable in https://www.drupal.org/node/1869638. $tree = $menu_tree->load('admin', $parameters); $manipulators = [ ['callable' => 'menu.default_tree_manipulators:checkAccess'], ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'], ['callable' => 'toolbar_menu_navigation_links'], ]; $tree = $menu_tree->transform($tree, $manipulators); $subtrees = []; // Calculated the combined cacheability of all subtrees. $cacheability = CacheableMetadata::createFromRenderArray($data); foreach ($tree as $element) { /** @var \Drupal\Core\Menu\MenuLinkInterface $link */ $link = $element->link; if ($element->subtree) { $subtree = $menu_tree->build($element->subtree); $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($renderer, $subtree) { return $renderer->render($subtree); }); $cacheability = $cacheability->merge(CacheableMetadata::createFromRenderArray($subtree)); } else { $output = ''; } // Many routes have dots as route name, while some special ones like // have <> characters in them. $url = $link->getUrlObject(); $id = str_replace(['.', '<', '>'], ['-', '', ''], $url->isRouted() ? $url->getRouteName() : $url->getUri()); $subtrees[$id] = $output; } // Store the subtrees, along with the cacheability metadata. $cacheability->applyTo($data); $data['#subtrees'] = $subtrees; return $data; } /** * {@inheritdoc} */ public static function trustedCallbacks() { return ['preRenderAdministrationTray', 'preRenderGetRenderedSubtrees']; } }