array( * 'block' => array( * 'route_parameters' => array('block' => $entity->id()), * ), * ), * @endcode * In this array, the outer key 'block' defines a "group" for contextual * links, and the inner array provides values for the route's placeholder * parameters (see @ref sec_placeholders above). * * To declare that a defined route should be a contextual link for a * contextual links group, put lines like the following in a * module_name.links.contextual.yml file (in the top-level directory for your * module): * @code * block_configure: * title: 'Configure block' * route_name: 'entity.block.edit_form' * group: 'block' * @endcode * Some notes: * - The first line is the machine name for your contextual link, which usually * matches the machine name of the route (given in the 'route_name' line). * - group: This needs to match the link group defined in the render array. * * Contextual links from other modules can be altered using * hook_contextual_links_alter(). * * @todo Derivatives are in flux for these; when they are more stable, add * documentation here. */ /** * @section Rendering menus * Once you have created menus (that contain menu links), you want to render * them. Drupal provides a block (Drupal\system\Plugin\Block\SystemMenuBlock) to * do so. * * However, perhaps you have more advanced needs and you're not satisfied with * what the menu blocks offer you. If that's the case, you'll want to: * - Instantiate \Drupal\Core\Menu\MenuTreeParameters, and set its values to * match your needs. Alternatively, you can use * MenuLinkTree::getCurrentRouteMenuTreeParameters() to get a typical * default set of parameters, and then customize them to suit your needs. * - Call \Drupal\Core\MenuLinkTree::load() with your menu link tree parameters, * this will return a menu link tree. * - Pass the menu tree to \Drupal\Core\Menu\MenuLinkTree::transform() to apply * menu link tree manipulators that transform the tree. You will almost always * want to apply access checking. The manipulators that you will typically * need can be found in \Drupal\Core\Menu\DefaultMenuTreeManipulators. * - Potentially write a custom menu tree manipulator, see * \Drupal\Core\Menu\DefaultMenuTreeManipulators for examples. This is only * necessary if you want to do things like adding extra metadata to rendered * links to display icons next to them. * - Pass the menu tree to \Drupal\Core\Menu\MenuLinkTree::build(), this will * build a renderable array. * * Combined, that would look like this: * @code * $menu_tree = \Drupal::menuTree(); * $menu_name = 'my_menu'; * * // Build the typical default set of menu tree parameters. * $parameters = $menu_tree->getCurrentRouteMenuTreeParameters($menu_name); * * // Load the tree based on this set of parameters. * $tree = $menu_tree->load($menu_name, $parameters); * * // Transform the tree using the manipulators you want. * $manipulators = array( * // Only show links that are accessible for the current user. * array('callable' => 'menu.default_tree_manipulators:checkAccess'), * // Use the default sorting of menu links. * array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'), * ); * $tree = $menu_tree->transform($tree, $manipulators); * * // Finally, build a renderable array from the transformed tree. * $menu = $menu_tree->build($tree); * * $menu_html = drupal_render($menu); * @endcode */ /** * Prepares variables for single local task link templates. * * Default template: menu-local-task.html.twig. * * @param array $variables * An associative array containing: * - element: A render element containing: * - #link: A menu link array with 'title', 'href', and 'localized_options' * keys. * - #active: A boolean indicating whether the local task is active. */ function template_preprocess_menu_local_task(&$variables) { $link = $variables['element']['#link']; $link += array( 'localized_options' => array(), ); $link_text = $link['title']; if (!empty($variables['element']['#active'])) { // Add text to indicate active tab for non-visual users. $active = '' . t('(active tab)') . ''; $variables['attributes']['class'] = array('active'); // If the link does not contain HTML already, String::checkPlain() it now. // After we set 'html'=TRUE the link will not be sanitized by l(). if (empty($link['localized_options']['html'])) { $link['title'] = String::checkPlain($link['title']); } $link['localized_options']['html'] = TRUE; $link_text = t('!local-task-title!active', array('!local-task-title' => $link['title'], '!active' => $active)); } $link['localized_options']['set_active_class'] = TRUE; $variables['link'] = array( '#type' => 'link', '#title' => $link_text, '#url' => $link['url'], '#options' => $link['localized_options'], ); } /** * Prepares variables for single local action link templates. * * Default template: menu-local-action.html.twig. * * @param array $variables * An associative array containing: * - element: A render element containing: * - #link: A menu link array with 'title', 'href', and 'localized_options' * keys. */ function template_preprocess_menu_local_action(&$variables) { $link = $variables['element']['#link']; $link += array( 'localized_options' => array(), ); $link['localized_options']['attributes']['class'][] = 'button'; $link['localized_options']['attributes']['class'][] = 'button-action'; $link['localized_options']['set_active_class'] = TRUE; $variables['link'] = array( '#type' => 'link', '#title' => $link['title'], '#options' => $link['localized_options'], '#url' => $link['url'], ); } /** * Returns an array containing the names of system-defined (default) menus. */ function menu_list_system_menus() { return array( 'tools' => 'Tools', 'admin' => 'Administration', 'account' => 'User account menu', 'main' => 'Main navigation', 'footer' => 'Footer menu', ); } /** * Collects the local tasks (tabs), action links, and the root path. * * @param int $level * The level of tasks you ask for. Primary tasks are 0, secondary are 1. * * @return array * An array containing * - tabs: Local tasks for the requested level. * - actions: Action links for the requested level. * - root_path: The router path for the current page. If the current page is * a default local task, then this corresponds to the parent tab. * * @see hook_menu_local_tasks() * @see hook_menu_local_tasks_alter() */ function menu_local_tasks($level = 0) { $data = &drupal_static(__FUNCTION__); $root_path = &drupal_static(__FUNCTION__ . ':root_path', ''); $empty = array( 'tabs' => array(), 'actions' => array(), 'root_path' => &$root_path, ); if (!isset($data)) { // Look for route-based tabs. $data['tabs'] = array(); $data['actions'] = array(); $route_name = \Drupal::routeMatch()->getRouteName(); if (!\Drupal::request()->attributes->has('exception') && !empty($route_name)) { $manager = \Drupal::service('plugin.manager.menu.local_task'); $local_tasks = $manager->getTasksBuild($route_name); foreach ($local_tasks as $level => $items) { $data['tabs'][$level] = empty($data['tabs'][$level]) ? $items : array_merge($data['tabs'][$level], $items); } } // Allow modules to dynamically add further tasks. $module_handler = \Drupal::moduleHandler(); foreach ($module_handler->getImplementations('menu_local_tasks') as $module) { $function = $module . '_menu_local_tasks'; $function($data, $route_name); } // Allow modules to alter local tasks. $module_handler->alter('menu_local_tasks', $data, $route_name); } if (isset($data['tabs'][$level])) { return array( 'tabs' => $data['tabs'][$level], 'actions' => $data['actions'], 'root_path' => $root_path, ); } elseif (!empty($data['actions'])) { return array('actions' => $data['actions']) + $empty; } return $empty; } /** * Returns the rendered local tasks at the top level. */ function menu_primary_local_tasks() { $links = menu_local_tasks(0); // Do not display single tabs. return count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : ''; } /** * Returns the rendered local tasks at the second level. */ function menu_secondary_local_tasks() { $links = menu_local_tasks(1); // Do not display single tabs. return count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : ''; } /** * Returns the rendered local actions at the current level. */ function menu_get_local_actions() { $links = menu_local_tasks(); $route_name = Drupal::routeMatch()->getRouteName(); $manager = \Drupal::service('plugin.manager.menu.local_action'); return $manager->getActionsForRoute($route_name) + $links['actions']; } /** * Returns the router path, or the path for a default local task's parent. */ function menu_tab_root_path() { $links = menu_local_tasks(); return $links['root_path']; } /** * Returns a renderable element for the primary and secondary tabs. */ function menu_local_tabs() { $build = array( '#theme' => 'menu_local_tasks', '#primary' => menu_primary_local_tasks(), '#secondary' => menu_secondary_local_tasks(), ); return !empty($build['#primary']) || !empty($build['#secondary']) ? $build : array(); } /** * Clears all cached menu data. * * This should be called any time broad changes * might have been made to the router items or menu links. */ function menu_cache_clear_all() { \Drupal::cache('menu')->invalidateAll(); } /** * @} End of "defgroup menu". */