diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index f414d53..2a8eb4c 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1673,14 +1673,7 @@ function drupal_get_messages($type = NULL, $clear_queue = TRUE) { * The current page's title. */ function drupal_get_title() { - $title = drupal_set_title(); - - // During a bootstrap, menu.inc is not included and thus we cannot provide a title. - if (!isset($title) && function_exists('menu_get_active_title')) { - $title = String::checkPlain(menu_get_active_title()); - } - - return $title; + return drupal_set_title(); } /** diff --git a/core/includes/menu.inc b/core/includes/menu.inc index a8a30b6..fe4635a 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -655,7 +655,7 @@ function _menu_item_localize(&$item, $map, $link_translate = FALSE) { // itself; can't localize. // If we are translating a router item (tabs, page, breadcrumb), then we // can always use the information from the router item. - if (!$link_translate || ($item['title'] == $item['link_title'])) { + if (!$link_translate || !isset($item['link_title']) || ($item['title'] == $item['link_title'])) { // t() is a special case. Since it is used very close to all the time, // we handle it directly instead of using indirect, slower methods. if ($title_callback == 't') { @@ -854,95 +854,41 @@ function menu_tail_load($arg, &$map, $index) { } /** - * Provides menu link access control, translation, and argument handling. + * Provides menu link unserializing, access control, and argument handling. * * This function is similar to _menu_translate(), but it also does * link-specific preparation (such as always calling to_arg() functions). * * @param $item * A menu link. - * @param $translate - * (optional) Whether to try to translate a link containing dynamic path - * argument placeholders (%) based on the menu router item of the current - * path. Defaults to FALSE. Internally used for breadcrumbs. * - * @return - * Returns the map of path arguments with objects loaded as defined in the - * $item['load_functions']. * $item['access'] becomes TRUE if the item is accessible, FALSE otherwise. - * $item['href'] is generated from link_path, possibly by to_arg functions. - * $item['title'] is generated from link_title, and may be localized. - * $item['options'] is unserialized; it is also changed within the call here - * to $item['localized_options'] by _menu_item_localize(). + * $item['href'] is generated from link_path, or set to NULL for routes. + * $item['title'] is generated from link_title. + * $item['route_parameters'] is unserialized if needed. + * $item['options'] is unserialized and copied to $item['localized_options']. */ -function _menu_link_translate(&$item, $translate = FALSE) { +function _menu_link_prepare(&$item) { if (!is_array($item['options'])) { - $item['options'] = unserialize($item['options']); + $item['options'] = (array) unserialize($item['options']); } - if ($item['external']) { + $item['localized_options'] = $item['options']; + $item['title'] = $item['link_title']; + if ($item['external'] || empty($item['route_name'])) { $item['access'] = 1; - $map = array(); $item['href'] = $item['link_path']; - $item['title'] = $item['link_title']; - $item['localized_options'] = $item['options']; + $item['route_parameters'] = array(); + // Set to NULL so that drupal_pre_render_link() is certain to skip it. + $item['route_name'] = NULL; } else { - // Complete the path of the menu link with elements from the current path, - // if it contains dynamic placeholders (%). - $map = explode('/', $item['link_path']); - if (strpos($item['link_path'], '%') !== FALSE) { - // Invoke registered to_arg callbacks. - if (!empty($item['to_arg_functions'])) { - _menu_link_map_translate($map, $item['to_arg_functions']); - } - // Or try to derive the path argument map from the current router item, - // if this $item's path is within the router item's path. This means - // that if we are on the current path 'foo/%/bar/%/baz', then - // menu_get_item() will have translated the menu router item for the - // current path, and we can take over the argument map for a link like - // 'foo/%/bar'. This inheritance is only valid for breadcrumb links. - // @see _menu_tree_check_access() - elseif ($translate && ($current_router_item = menu_get_item())) { - // If $translate is TRUE, then this link is in the active trail. - // Only translate paths within the current path. - if (strpos($current_router_item['path'], $item['link_path']) === 0) { - $count = count($map); - $map = array_slice($current_router_item['original_map'], 0, $count); - $item['original_map'] = $map; - if (isset($current_router_item['map'])) { - $item['map'] = array_slice($current_router_item['map'], 0, $count); - } - // Reset access to check it (for the first time). - unset($item['access']); - } - } - } - $item['href'] = implode('/', $map); - - // Skip links containing untranslated arguments. - if (strpos($item['href'], '%') !== FALSE) { - $item['access'] = FALSE; - return FALSE; + $item['href'] = NULL; + if (!is_array($item['route_parameters'])) { + $item['route_parameters'] = (array) unserialize($item['route_parameters']); } // menu_tree_check_access() may set this ahead of time for links to nodes. if (!isset($item['access'])) { - if ($route = $item->getRoute()) { - $item['access'] = menu_item_route_access($route, $item['href'], $map); - } - elseif (!empty($item['load_functions']) && !_menu_load_objects($item, $map)) { - // An error occurred loading an object. - $item['access'] = FALSE; - return FALSE; - } - // Apply the access check defined in hook_menu() if there is not route - // defined. - else { - _menu_check_access($item, $map); - } - } - // For performance, don't localize a link the user can't access. - if ($item['access']) { - _menu_item_localize($item, $map, TRUE); + $item['access'] = Drupal::getContainer()->get('access_manager')->checkNamedRoute($item['route_name'], $item['route_parameters'], \Drupal::currentUser()); } } @@ -950,10 +896,8 @@ function _menu_link_translate(&$item, $translate = FALSE) { // options array. For performance reasons we only invoke this hook if the link // has the 'alter' flag set in the options array. if (!empty($item['options']['alter'])) { - drupal_alter('translated_menu_link', $item, $map); + drupal_alter('prepared_menu_link', $item); } - - return $map; } /** @@ -973,9 +917,11 @@ function _menu_link_translate(&$item, $translate = FALSE) { * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException * If the system path in $href does not match the $route. */ -function menu_item_route_access(Route $route, $href, &$map) { - $request = RequestHelper::duplicate(\Drupal::request(), '/' . $href); - $request->attributes->set('_system_path', $href); +function menu_item_route_access(Route $route, $href, &$map, Request $request = NULL) { + if (!isset($request)) { + $request = RequestHelper::duplicate(\Drupal::request(), '/' . $href); + $request->attributes->set('_system_path', $href); + } // Attempt to match this path to provide a fully built request to the // access checker. try { @@ -1106,7 +1052,6 @@ function menu_tree_output($tree) { } } - $router_item = menu_get_item(); $num_items = count($items); foreach ($items as $i => $data) { $class = array(); @@ -1133,21 +1078,12 @@ function menu_tree_output($tree) { $class[] = 'active-trail'; $data['link']['localized_options']['attributes']['class'][] = 'active-trail'; } - // Normally, l() compares the href of every link with the current path and - // sets the active class accordingly. But local tasks do not appear in menu - // trees, so if the current path is a local task, and this link is its - // tab root, then we have to set the class manually. - if ($data['link']['href'] == $router_item['tab_root_href'] && $data['link']['href'] != current_path()) { - $data['link']['localized_options']['attributes']['class'][] = 'active'; - } // Allow menu-specific theme overrides. $element['#theme'] = 'menu_link__' . strtr($data['link']['menu_name'], '-', '_'); $element['#attributes']['class'] = $class; - $element['#title'] = $data['link']['title']; - $element['#href'] = $data['link']['href']; - $element['#localized_options'] = !empty($data['link']['localized_options']) ? $data['link']['localized_options'] : array(); - $element['#below'] = $data['below'] ? menu_tree_output($data['below']) : $data['below']; + $element['link'] = $data['link']->build(); + $element['below'] = $data['below'] ? menu_tree_output($data['below']) : $data['below']; $element['#original_link'] = $data['link']; // Index using the link's unique mlid. $build[$data['link']['mlid']] = $element; @@ -1585,7 +1521,7 @@ function _menu_tree_check_access(&$tree) { $new_tree = array(); foreach ($tree as $key => $v) { $item = &$tree[$key]['link']; - _menu_link_translate($item); + _menu_link_prepare($item); if ($item['access'] || ($item['in_active_trail'] && strpos($item['href'], '%') !== FALSE)) { if ($tree[$key]['below']) { _menu_tree_check_access($tree[$key]['below']); @@ -1702,10 +1638,10 @@ function theme_menu_link(array $variables) { $element = $variables['element']; $sub_menu = ''; - if ($element['#below']) { - $sub_menu = drupal_render($element['#below']); + if ($element['below']) { + $sub_menu = drupal_render($element['below']); } - $output = l($element['#title'], $element['#href'], $element['#localized_options']); + $output = drupal_render($element['link']); return '' . $output . $sub_menu . "\n"; } @@ -2350,10 +2286,10 @@ function menu_set_active_trail($new_trail = NULL) { // Try to retrieve a menu link corresponding to the current path. If more // than one exists, the link from the most preferred menu is returned. $preferred_link = menu_link_get_preferred(); - $current_item = menu_get_item(); // There is a link for the current path. if ($preferred_link) { + _menu_link_translate($preferred_link); // Pass TRUE for $only_active_trail to make menu_tree_page_data() build // a stripped down menu tree containing the active trail only, in case // the given menu has not been built in this request yet. @@ -2362,7 +2298,6 @@ function menu_set_active_trail($new_trail = NULL) { } // There is no link for the current path. else { - $preferred_link = $current_item; $curr = FALSE; } @@ -2380,7 +2315,7 @@ function menu_set_active_trail($new_trail = NULL) { // @see _menu_tree_check_access() // @see _menu_link_translate() if (strpos($link['href'], '%') !== FALSE) { - _menu_link_translate($link, TRUE); + _menu_link_translate($link); } if ($link['access']) { $trail[] = $link; @@ -2435,20 +2370,10 @@ function menu_link_get_preferred($path = NULL, $selected_menu = NULL) { // which are ordered by priority (translated hrefs are preferred over // untranslated paths). Afterwards, the most relevant path is picked from // the menus, ordered by menu preference. - $item = menu_get_item($path); $path_candidates = array(); // 1. The current item href. - $path_candidates[$item['href']] = $item['href']; + $path_candidates[$path] = $path; // 2. The tab root href of the current item (if any). - if ($item['tab_parent'] && ($tab_root = menu_get_item($item['tab_root_href']))) { - $path_candidates[$tab_root['href']] = $tab_root['href']; - } - // 3. The current item path (with wildcards). - $path_candidates[$item['path']] = $item['path']; - // 4. The tab root path of the current item (if any). - if (!empty($tab_root)) { - $path_candidates[$tab_root['path']] = $tab_root['path']; - } // Retrieve a list of menu names, ordered by preference. $menu_names = menu_get_active_menu_names(); @@ -2474,9 +2399,9 @@ function menu_link_get_preferred($path = NULL, $selected_menu = NULL) { foreach ($menu_names as $menu_name) { if (empty($preferred_links[$path][$menu_name]) && isset($candidates[$link_path][$menu_name])) { $candidate_item = $candidates[$link_path][$menu_name]; - $map = explode('/', $path); - _menu_translate($candidate_item, $map); - if ($candidate_item['access']) { + //$map = explode('/', $path); + //_menu_translate($candidate_item, $map); + if (TRUE || $candidate_item['access']) { $preferred_links[$path][$menu_name] = $candidate_item; if (empty($preferred_links[$path][MENU_PREFERRED_LINK])) { // Store the most specific link. @@ -2487,6 +2412,8 @@ function menu_link_get_preferred($path = NULL, $selected_menu = NULL) { } } } + + // @todo How do we fetch the title of tabs and other non menu links. } return isset($preferred_links[$path][$selected_menu]) ? $preferred_links[$path][$selected_menu] : FALSE; @@ -2515,19 +2442,6 @@ function menu_get_active_trail() { } /** - * Gets the title of the current page, as determined by the active trail. - */ -function menu_get_active_title() { - $active_trail = menu_get_active_trail(); - - foreach (array_reverse($active_trail) as $item) { - if (!(bool) ($item['type'] & MENU_IS_LOCAL_TASK)) { - return $item['title']; - } - } -} - -/** * Clears the cached cached data for a single named menu. */ function menu_cache_clear($menu_name = 'tools') { @@ -2584,7 +2498,7 @@ function menu_router_rebuild() { try { list($menu, $masks) = menu_router_build(TRUE); - _menu_navigation_links_rebuild($menu); + menu_default_links_rebuild(); // Clear the menu, page and block caches. menu_cache_clear_all(); _menu_clear_page_cache(); @@ -2689,116 +2603,125 @@ function menu_get_router() { } /** - * Builds menu links for the items in the menu router. - * - * @todo This function should be removed/refactored. + * Recursive helper to save menu links. */ -function _menu_navigation_links_rebuild($menu) { - if (!\Drupal::moduleHandler()->moduleExists('menu_link')) { +function _menu_link_save_recursive($controller, $machine_name, &$children, &$links) { + $menu_link = $links[$machine_name]; + if ($menu_link->isNew() || !$menu_link->customized) { + if (!isset($menu_link->plid) && !empty($menu_link->parent) && !empty($links[$menu_link->parent])) { + $parent = $links[$menu_link->parent]; + + if (empty($menu_link->menu_name) || $parent->menu_name == $menu_link->menu_name) { + $menu_link->plid = $parent->id(); + $menu_link->menu_name = $parent->menu_name; + } + } + $controller->save($menu_link); + } + if (!empty($children[$machine_name])) { + foreach ($children[$machine_name] as $next_name) { + _menu_link_save_recursive($controller, $next_name, $children, $links); + } + } + // Remove process links names so we can find stragglers. + unset($children[$machine_name]); +} + +/** + * Builds menu links for the items returned from hook_default_meanu_links(). + */ +function menu_default_links_rebuild() { + $module_handler = \Drupal::moduleHandler(); + if (!$module_handler->moduleExists('menu_link')) { // The Menu link module may not be available during install, so rebuild // when possible. return; } $menu_link_controller = \Drupal::entityManager() ->getStorageController('menu_link'); - - // Add normal and suggested items as links. - $router_items = array(); - foreach ($menu as $path => $router_item) { - if ($router_item['_visible']) { - $router_items[$path] = $router_item; - $sort[$path] = $router_item['_number_parts']; - } - } - if ($router_items) { - // Keep an array of processed menu links, to allow - // Drupal\menu_link\MenuLinkStorageController::save() to check this for - // parents instead of querying the database. - $parent_candidates = array(); - // Make sure no child comes before its parent. - array_multisort($sort, SORT_NUMERIC, $router_items); - - foreach ($router_items as $key => $router_item) { + $links = array(); + $children = array(); + $top_links = array(); + $all_links = $module_handler->invokeAll('default_menu_links'); + $module_handler->alter('default_menu_links', $all_links); + if ($all_links) { + foreach ($all_links as $machine_name => $link) { + // Note, we set this as 'system', so that we can be sure to distinguish all + // the menu links generated automatically from hook_default_menu_links(). + $link['machine_name'] = $machine_name; + $link['module'] = 'system'; + $link += array( + 'type' => MENU_NORMAL_ITEM, + 'hidden' => 0, + 'options' => empty($link['description']) ? array() : array('attributes' => array('title' => $link['description'])), + ); + // Suggested items are disabled by default. + if ($link['type'] == MENU_SUGGESTED_ITEM) { + $link['hidden'] = 1; + } // For performance reasons, do a straight query now and convert to a menu // link entity later. // @todo revisit before release. $existing_item = db_select('menu_links') ->fields('menu_links') - ->condition('link_path', $router_item['path']) + ->condition('machine_name', $machine_name) ->condition('module', 'system') - ->execute()->fetchAll(); + ->execute()->fetchObject(); if ($existing_item) { - $existing_item = reset($existing_item); $existing_item->options = unserialize($existing_item->options); $existing_item->route_parameters = unserialize($existing_item->route_parameters); - - $router_item['mlid'] = $existing_item->mlid; - $router_item['uuid'] = $existing_item->uuid; - // A change in hook_menu may move the link to a different menu - if (empty($router_item['menu_name']) || ($router_item['menu_name'] == $existing_item->menu_name)) { - $router_item['menu_name'] = $existing_item->menu_name; - $router_item['plid'] = $existing_item->plid; - } - else { - // It moved to a new menu. - // Let Drupal\menu_link\MenuLinkStorageController::save() try to find - // a new parent based on the path. - unset($router_item['plid']); + $link['mlid'] = $existing_item->mlid; + $link['plid'] = $existing_item->plid; + $link['uuid'] = $existing_item->uuid; + $link['customized'] = $existing_item->customized; + $link['updated'] = $existing_item->updated; + $menu_link = $menu_link_controller->create($link); + if (!$existing_item->customized) { + // A change in hook_default_menu_links may move the link to a + // different menu. + if (!empty($link['menu_name']) && ($link['menu_name'] != $existing_item->menu_name)) { + $menu_link->plid = NULL; + $menu_link->menu_name = $link['menu_name']; + } + $menu_link->original = $existing_item; } - $router_item['has_children'] = $existing_item->has_children; - $router_item['updated'] = $existing_item->updated; - - // Convert the existing item to a typed object. - $existing_item = $menu_link_controller->create(get_object_vars($existing_item)); } else { - $existing_item = NULL; + if (empty($link['route_name']) && empty($link['link_path'])) { + $link['route_name'] = ''; + } + $menu_link = $menu_link_controller->create($link); } - - if ($existing_item && $existing_item->customized) { - $parent_candidates[$existing_item->mlid] = $existing_item; + if (!empty($link['parent'])) { + $children[$link['parent']][$machine_name] = $machine_name; + $menu_link->parent = $link['parent']; + if (empty($link['menu_name'])) { + // Unset the default menu name so it's populated from the parent. + unset($menu_link->menu_name); + } } else { - $menu_link = MenuLink::buildFromRouterItem($router_item); - $menu_link->original = $existing_item; - $menu_link->parentCandidates = $parent_candidates; - $menu_link_controller->save($menu_link); - $parent_candidates[$menu_link->id()] = $menu_link; - unset($router_items[$key]); + // A top level link - we need them to root our tree. + $top_links[$machine_name] = $machine_name; + $menu_link->plid = 0; } + $links[$machine_name] = $menu_link; } } - - $paths = array_keys($menu); - // Updated and customized items whose router paths are gone need new ones. - $menu_links = $menu_link_controller->loadUpdatedCustomized($paths); - foreach ($menu_links as $menu_link) { - $router_path = _menu_find_router_path($menu_link->link_path); - if (!empty($router_path) && ($router_path != $menu_link->router_path || $menu_link->updated)) { - // If the router path and the link path matches, it's surely a working - // item, so we clear the updated flag. - $updated = $menu_link->updated && $router_path != $menu_link->link_path; - - $menu_link->router_path = $router_path; - $menu_link->updated = (int) $updated; - $menu_link_controller->save($menu_link); + foreach ($top_links as $machine_name) { + _menu_link_save_recursive($menu_link_controller, $machine_name, $children, $links); + } + // Handle any children we didn't find starting from top-level links. + foreach ($children as $missing_parent => $orphan_links) { + foreach ($orphan_links as $machine_name) { + // Force it to the top level. + $links[$machine_name]->plid = 0; + _menu_link_save_recursive($menu_link_controller, $machine_name, $children, $links); } } - // Find any item whose router path does not exist any more. - $query = \Drupal::entityQuery('menu_link') - ->condition('router_path', $paths, 'NOT IN') - ->condition('external', 0) - ->condition('updated', 0) - ->condition('customized', 0) - ->sort('depth', 'DESC'); - $result = $query->execute(); - - // Remove all such items. Starting from those with the greatest depth will - // minimize the amount of re-parenting done by the menu link controller. - if (!empty($result)) { - menu_link_delete_multiple($result, TRUE); - } + // @todo - remove links based on their machine names when module are + // uninstalled, or mark them disabled if the route names are missing? } /** @@ -3024,7 +2947,7 @@ function _menu_router_build($callbacks, $save = FALSE) { // previous iteration assigned one already), try to find the menu name // of the parent item in the currently stored menu links. if (!isset($parent['menu_name'])) { - $menu_name = db_query("SELECT menu_name FROM {menu_links} WHERE router_path = :router_path AND module = 'system'", array(':router_path' => $parent_path))->fetchField(); + $menu_name = db_query("SELECT menu_name FROM {menu_links} WHERE link_path = :link_path AND module = 'system'", array(':link_path' => $parent_path))->fetchField(); if ($menu_name) { $parent['menu_name'] = $menu_name; } diff --git a/core/includes/path.inc b/core/includes/path.inc index 9c41abd..58bd0a1 100644 --- a/core/includes/path.inc +++ b/core/includes/path.inc @@ -5,6 +5,7 @@ * Functions to handle paths in Drupal. */ +use Drupal\Core\Routing\RequestHelper; use Symfony\Component\HttpFoundation\Request; /** @@ -195,9 +196,24 @@ function drupal_valid_path($path, $dynamic_allowed = FALSE) { global $menu_admin; // We indicate that a menu administrator is running the menu access check. $menu_admin = TRUE; + /** @var $route_provider \Drupal\Core\Routing\RouteProviderInterface */ + $route_provider = \Drupal::service('router.route_provider'); + + if ($dynamic_allowed && preg_match('/\/\%/', $path)) { + $router_path = '/' . str_replace('%', '{}', $path); + } + else { + $router_path = $path; + } + if ($path == '' || url_is_external($path)) { $item = array('access' => TRUE); } + elseif (($collection = $route_provider->getRoutesByPattern('/' . $router_path)) && $collection->count() > 0) { + $routes = $collection->all(); + $route = reset($routes); + $route_name = key($routes); + } elseif ($dynamic_allowed && preg_match('/\/\%/', $path)) { // Path is dynamic (ie 'user/%'), so check directly against menu_router table. if ($item = db_query("SELECT * FROM {menu_router} where path = :path", array(':path' => $path))->fetchAssoc()) { @@ -206,16 +222,22 @@ function drupal_valid_path($path, $dynamic_allowed = FALSE) { $item['external'] = FALSE; $item['options'] = ''; _menu_link_translate($item); + $route_name = $item['route_name']; } } else { $item = menu_get_item($path); + $route_name = $item['route_name']; } // Check the new routing system. - if (!empty($item['route_name'])) { + if (!empty($route_name)) { $map = array(); - $route = \Drupal::service('router.route_provider')->getRouteByName($item['route_name']); - $item['access'] = menu_item_route_access($route, $path, $map); + $route = \Drupal::service('router.route_provider')->getRouteByName($route_name); + $request = RequestHelper::duplicate(\Drupal::request(), '/' . $path); + $request->attributes->set('_system_path', $path); + $request->attributes->set('_menu_admin', TRUE); + + $item['access'] = menu_item_route_access($route, $path, $map, $request); } $menu_admin = FALSE; return $item && $item['access']; diff --git a/core/modules/action/action.module b/core/modules/action/action.module index 5ecfb63..f7310d1 100644 --- a/core/modules/action/action.module +++ b/core/modules/action/action.module @@ -57,6 +57,20 @@ function action_menu() { } /** + * Implements hook_default_menu_links(). + */ +function action_default_menu_links() { + $links['admin.config.system.actions'] = array( + 'link_title' => 'Actions', + 'description' => 'Manage the actions defined for your site.', + 'route_name' => 'action.admin', + 'parent' => 'admin.config.system', + ); + + return $links; +} + +/** * Implements hook_entity_info(). */ function action_entity_info(&$entity_info) { diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module index 0f9f718..2957295 100644 --- a/core/modules/aggregator/aggregator.module +++ b/core/modules/aggregator/aggregator.module @@ -145,6 +145,34 @@ function aggregator_menu() { } /** + * Implements hook_default_menu_links(). + */ +function aggregator_default_menu_links() { + $links = array(); + $links['aggregator.admin_overview'] = array( + 'link_title' => 'Feed aggregator', + 'description' => "Configure which content your site aggregates from other sites, how often it polls them, and how they're categorized.", + 'route_name' => 'aggregator.admin_overview', + 'weight' => 10, + ); + $links['aggregator'] = array( + 'link_title' => 'Feed aggregator', + 'weight' => 5, + 'route_name' => 'aggregator.page_last', + ); + $links['aggregator.sources'] = array( + 'link_title' => 'Sources', + 'route_name' => 'aggregator.sources', + ); + $links['aggregator.categories'] = array( + 'link_title' => 'Categories', + 'route_name' => 'aggregator.categories', + ); + + return $links; +} + +/** * Implements hook_permission(). */ function aggregator_permission() { diff --git a/core/modules/ban/ban.module b/core/modules/ban/ban.module index 0dce1d9..5e0e30b 100644 --- a/core/modules/ban/ban.module +++ b/core/modules/ban/ban.module @@ -53,3 +53,18 @@ function ban_menu() { ); return $items; } + +/** + * Implements hook_default_menu_links(). + */ +function ban_default_menu_links() { + $links['admin.config.people.ban'] = array( + 'link_title' => 'IP address bans', + 'description' => 'Manage banned IP addresses.', + 'route_name' => 'ban.admin_page', + 'weight' => 10, + 'parent' => 'admin.config.people', + ); + + return $links; +} diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 331e985..750a717 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -145,6 +145,20 @@ function _block_custom_theme($theme = NULL) { } /** + * Implements hook_default_menu_links(). + */ +function block_default_menu_links() { + $links['admin.structure.block'] = array( + 'link_title' => 'Blocks', + 'parent' => 'admin.structure', + 'description' => 'Configure what block content appears in your site\'s sidebars and other regions.', + 'route_name' => 'block.admin_display', + ); + + return $links; +} + +/** * Implements hook_page_build(). * * Renders blocks into their regions. diff --git a/core/modules/book/book.module b/core/modules/book/book.module index d075b21..2bf4bc4 100644 --- a/core/modules/book/book.module +++ b/core/modules/book/book.module @@ -186,6 +186,26 @@ function book_menu() { } /** + * Implements hook_default_menu_links(). + */ +function book_default_menu_links() { + $links['admin.structure.book'] = array( + 'link_title' => 'Books', + 'description' => "Manage your site's book outlines.", + 'parent' => 'admin.structure', + 'route_name' => 'book.admin', + ); + $links['book'] = array( + 'link_title' => 'Books', + 'route_name' => 'book.render', + // @TODO what to do about MENU_SUGGESTED_ITEM, maybe specify no menu_name? + 'type' => MENU_SUGGESTED_ITEM, + ); + + return $links; +} + +/** * Access callback: Determines if the outline tab is accessible. * * Path: @@ -889,7 +909,7 @@ function book_menu_subtree_data($link) { // If the subtree data was not in the cache, $data will be NULL. if (!isset($data)) { $query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC)); - $query->join('menu_router', 'm', 'm.path = ml.router_path'); + $query->join('menu_router', 'm', 'm.path = ml.link_path'); $query->join('book', 'b', 'ml.mlid = b.mlid'); $query->fields('b'); $query->fields('m', array('load_functions', 'to_arg_functions', 'access_callback', 'access_arguments', 'page_callback', 'page_arguments', 'title', 'title_callback', 'title_arguments', 'type')); diff --git a/core/modules/book/lib/Drupal/book/BookManager.php b/core/modules/book/lib/Drupal/book/BookManager.php index d246632..789743c 100644 --- a/core/modules/book/lib/Drupal/book/BookManager.php +++ b/core/modules/book/lib/Drupal/book/BookManager.php @@ -129,7 +129,7 @@ public function getLinkDefaults($nid) { 'menu_name' => '', 'nid' => $nid, 'bid' => 0, - 'router_path' => 'node/%', + 'link_path' => 'node/%', 'plid' => 0, 'mlid' => 0, 'has_children' => 0, @@ -187,7 +187,7 @@ public function addFormElements(array $form, array &$form_state, NodeInterface $ ), '#tree' => TRUE, ); - foreach (array('menu_name', 'mlid', 'nid', 'router_path', 'has_children', 'options', 'module', 'original_bid', 'parent_depth_limit') as $key) { + foreach (array('menu_name', 'mlid', 'nid', 'link_path', 'has_children', 'options', 'module', 'original_bid', 'parent_depth_limit') as $key) { $form['book'][$key] = array( '#type' => 'value', '#value' => $node->book[$key], diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index 6fcbc2b..bab585f 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -228,6 +228,20 @@ function comment_menu() { } /** + * Implements hook_default_menu_links(). + */ +function comment_default_menu_links() { + $links['admin.content.comment'] = array( + 'link_title' => 'Comments', + 'route_name' => 'comment.admin', + 'parent' => 'admin.content', + 'description' => 'List and edit site comments and the comment approval queue.', + ); + + return $links; +} + +/** * Implements hook_menu_alter(). */ function comment_menu_alter(&$items) { diff --git a/core/modules/config/config.module b/core/modules/config/config.module index 76deec4..aee4c8d 100644 --- a/core/modules/config/config.module +++ b/core/modules/config/config.module @@ -69,3 +69,17 @@ function config_menu() { return $items; } + +/** + * Implements hook_default_menu_links(). + */ +function config_default_menu_links() { + $links['admin.config.development.configuration'] = array( + 'link_title' => 'Configuration management', + 'description' => 'Import, export, or synchronize your site configuration.', + 'route_name' => 'config.sync', + 'parent' => 'admin.config.development', + ); + + return $links; +} diff --git a/core/modules/config/tests/config_test/config_test.routing.yml b/core/modules/config/tests/config_test/config_test.routing.yml index 7b973ab..cc81bc3 100644 --- a/core/modules/config/tests/config_test/config_test.routing.yml +++ b/core/modules/config/tests/config_test/config_test.routing.yml @@ -2,6 +2,7 @@ config_test.list_page: path: '/admin/structure/config_test' defaults: _entity_list: 'config_test' + _title: 'Test configuration' requirements: _access: 'TRUE' diff --git a/core/modules/config_translation/config_translation.module b/core/modules/config_translation/config_translation.module index 0fc1170..efb4110 100644 --- a/core/modules/config_translation/config_translation.module +++ b/core/modules/config_translation/config_translation.module @@ -46,6 +46,21 @@ function config_translation_menu() { } /** + * Implements hook_default_menu_links(). + */ +function config_translation_default_menu_links() { + $links['admin.config.regional.config-translation'] = array( + 'link_title' => 'Configuration translation', + 'parent' => 'admin.config.regional', + 'description' => 'Translate the configuration.', + 'route_name' => 'config_translation.mapper_list', + 'weight' => 30, + ); + + return $links; +} + +/** * Implements hook_permission(). */ function config_translation_permission() { diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module index 58e343e..265f255 100644 --- a/core/modules/contact/contact.module +++ b/core/modules/contact/contact.module @@ -91,6 +91,26 @@ function contact_menu() { } /** + * Implements hook_default_menu_links(). + */ +function contact_default_menu_links() { + $links['admin.structure.contact'] = array( + 'link_title' => 'Contact form categories', + 'parent' => 'admin.structure', + 'description' => 'Create a system contact form and set up categories for the form to use.', + 'route_name' => 'contact.category_list', + ); + + $links['contact'] = array( + 'link_title' => 'Contact', + 'route_name' => 'contact.site_page', + 'menu_name' => 'footer', + 'type' => MENU_SUGGESTED_ITEM, + ); + return $links; +} + +/** * Access callback: Checks access for a user's personal contact form. * * @param $account diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index 1454bd8..e63ce3d 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -277,6 +277,15 @@ function content_translation_menu_alter(array &$items) { } /** + * Implements hook_default_menu_links_alter(). + */ +function content_translation_default_menu_links_alter(array &$links) { + // Clarify where translation settings are located. + $items['admin.config.regional.language.content_settings_page']['link_title'] = 'Content language and translation'; + $items['admin.config.regional.language.content_settings_page']['description'] = 'Configure language and translation support for content.'; +} + +/** * Implements hook_local_tasks_alter(). */ function content_translation_local_tasks_alter(&$local_tasks) { diff --git a/core/modules/dblog/dblog.module b/core/modules/dblog/dblog.module index 0f269b0..3abe865 100644 --- a/core/modules/dblog/dblog.module +++ b/core/modules/dblog/dblog.module @@ -73,6 +73,42 @@ function dblog_menu() { } /** + * Implements hook_default_menu_links(). + */ +function dblog_default_menu_links() { + $links['admin.reports.dblog'] = array( + 'link_title' => 'Recent log messages', + 'parent' => 'admin.reports', + 'description' => 'View events that have recently been logged.', + 'route_name' => 'dblog.overview', + 'weight' => -1, + ); + $links['admin.reports.page-not-found'] = array( + 'link_title' => "Top 'page not found' errors", + 'route_name' => 'dblog.page_not_found', + 'parent' => 'admin.reports', + 'description' => "View 'page not found' errors (404s).", + ); + $links['admin.reports.access-denied'] = array( + 'link_title' => "Top 'access denied' errors", + 'route_name' => 'dblog.access_denied', + 'description' => "View 'access denied' errors (403s).", + 'parent' => 'admin.reports', + ); + + if (\Drupal::moduleHandler()->moduleExists('search')) { + $links['admin.reports.search'] = array( + 'link_title' => 'Top search phrases', + 'route_name' => 'dblog.search', + 'description' => 'View most popular search phrases.', + 'parent' => 'admin.reports', + ); + } + + return $links; +} + +/** * Implements hook_page_build(). */ function dblog_page_build(&$page) { diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module index 5684ad0..0dfab01 100644 --- a/core/modules/editor/editor.module +++ b/core/modules/editor/editor.module @@ -48,6 +48,14 @@ function editor_menu_alter(&$items) { } /** + * Implements hook_default_menu_links_alter(). + */ +function editor_default_menu_links_alter(array &$links) { + $links['admin.config.content.formats']['link_title'] = 'Text formats and editors'; + $links['admin.config.content.formats']['description'] = 'Configure how user-contributed content is filtered and formatted, as well as the text editor user interface (WYSIWYGs or toolbars).'; +} + +/** * Implements hook_element_info(). * * Extends the functionality of text_format elements (provided by Filter diff --git a/core/modules/entity/entity.module b/core/modules/entity/entity.module index ef82f18..880b7e9 100644 --- a/core/modules/entity/entity.module +++ b/core/modules/entity/entity.module @@ -81,6 +81,36 @@ function entity_menu() { } /** + * Implements hook_default_menu_links(). + */ +function entity_default_menu_links() { + $links['admin.structure.display-modes'] = array( + 'link_title' => 'Display modes', + 'description' => 'Configure what displays are available for your content and forms.', + 'route_name' => 'entity.display_mode', + 'parent' => 'admin.structure', + ); + + // View modes. + $links['admin.structure.display-modes.view'] = array( + 'link_title' => 'View modes', + 'description' => 'Manage custom view modes.', + 'route_name' => 'entity.view_mode_list', + 'parent' => 'admin.structure.display-modes', + ); + + // Form modes. + $links['admin.structure.display-modes.form'] = array( + 'link_title' => 'Form modes', + 'description' => 'Manage custom form modes.', + 'route_name' => 'entity.form_mode_list', + 'parent' => 'admin.structure.display-modes', + ); + + return $links; +} + +/** * Implements hook_local_actions(). */ function entity_local_actions() { diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module index 2033cf6..a6085b4 100644 --- a/core/modules/field_ui/field_ui.module +++ b/core/modules/field_ui/field_ui.module @@ -66,6 +66,20 @@ function field_ui_menu() { } /** + * Implements hook_default_menu_links(). + */ +function field_ui_default_menu_links() { + $links['admin.reports.fields'] = array( + 'link_title' => 'Field list', + 'description' => 'Overview of fields on all entity types.', + 'route_name' => 'field_ui.list', + 'parent' => 'admin.reports', + ); + + return $links; +} + +/** * Implements hook_permission(). */ function field_ui_permission() { diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index 922688d..b8621fe 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -153,6 +153,26 @@ function filter_menu() { } /** + * Implements hook_default_menu_links(). + */ +function filter_default_menu_links() { + $links['filter.tips'] = array( + 'link_title' => 'Compose tips', + 'type' => MENU_SUGGESTED_ITEM, + 'route_name' => 'filter.tips_all', + ); + + $links['admin.config.content.formats'] = array( + 'link_title' => 'Text formats', + 'parent' => 'admin.config.content', + 'description' => 'Configure how content input by users is filtered, including allowed HTML tags. Also allows enabling of module-provided filters.', + 'route_name' => 'filter.admin_overview', + ); + + return $links; +} + +/** * Implements hook_permission(). */ function filter_permission() { diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 95ebe76..19f437e 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -135,6 +135,24 @@ function forum_menu() { } /** + * Implements hook_default_menu_links(). + */ +function forum_default_menu_links() { + $items['forum'] = array( + 'link_title' => 'Forums', + 'route_name' => 'forum.index', + 'menu_name' => 'tools', + ); + $items['admin.structure.forum'] = array( + 'link_title' => 'Forums', + 'parent' => 'admin.structure', + 'description' => 'Control forum hierarchy settings.', + 'route_name' => 'forum.overview', + ); + return $items; +} + +/** * Implements hook_menu_local_tasks(). */ function forum_menu_local_tasks(&$data, $route_name) { diff --git a/core/modules/forum/forum.routing.yml b/core/modules/forum/forum.routing.yml index 3a0b6b9..aa21336 100644 --- a/core/modules/forum/forum.routing.yml +++ b/core/modules/forum/forum.routing.yml @@ -26,6 +26,7 @@ forum.page: path: '/forum/{taxonomy_term}' defaults: _content: '\Drupal\forum\Controller\ForumController::forumPage' + _title_callback: '\Drupal\taxonomy\Controller\TaxonomyController::termTitle' requirements: _permission: 'access content' diff --git a/core/modules/help/help.module b/core/modules/help/help.module index 9ccef10..4388d94 100644 --- a/core/modules/help/help.module +++ b/core/modules/help/help.module @@ -25,6 +25,21 @@ function help_menu() { } /** + * Implements hook_default_menu_links(). + */ +function help_default_menu_links() { + $links['admin.help'] = array( + 'link_title' => 'Help', + 'description' => 'Reference for usage, configuration, and modules.', + 'route_name' => 'help.main', + 'weight' => 9, + 'parent' => 'admin', + ); + + return $links; +} + +/** * Implements hook_help(). */ function help_help($path, $arg) { diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 23443ff..8881e48 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -125,6 +125,20 @@ function image_menu() { } /** + * Implements hook_default_menu_links(). + */ +function image_default_menu_links() { + $links['admin.config.media.image-styles'] = array( + 'link_title' => 'Image styles', + 'description' => 'Configure styles that can be used for resizing or adjusting images on display.', + 'parent' => 'admin.config.media', + 'route_name' => 'image.style_list', + ); + + return $links; +} + +/** * Implements hook_theme(). */ function image_theme() { diff --git a/core/modules/language/language.module b/core/modules/language/language.module index b0dd216..bd673a8 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -86,6 +86,30 @@ function language_menu() { } /** + * Implements hook_default_menu_links(). + */ +function language_default_menu_links() { + // Base language management and configuration. + $links['admin.config.regional.language.admin_overview'] = array( + 'link_title' => 'Languages', + 'description' => 'Configure languages for content and the user interface.', + 'route_name' => 'language.admin_overview', + 'parent' => 'admin.config.regional', + 'weight' => 0, + ); + // Content language settings. + $links['admin.config.regional.language.content_settings_page'] = array( + 'link_title' => 'Content language', + 'description' => 'Configure language support for content.', + 'route_name' => 'language.content_settings_page', + 'parent' => 'admin.config.regional', + 'weight' => 0, + ); + + return $links; +} + +/** * Editing or deleting locked languages should not be possible. * * @deprecated Use \Drupal\language\LanguageAccessController instead. diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index e9b539c..d3950ad 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -187,6 +187,29 @@ function locale_menu() { } /** + * Implements hook_default_menu_links(). + */ +function locale_default_menu_links() { + // Translation functionality. + $links['admin.config.regional.translate'] = array( + 'link_title' => 'User interface translation', + 'description' => 'Translate the built-in user interface.', + 'route_name' => 'locale.translate_page', + 'parent' => 'admin.config.regional', + 'weight' => 15, + ); + + $links['admin.reports.translations'] = array( + 'link_title' => 'Available translation updates', + 'route_name' => 'locale.translate_status', + 'description' => 'Get a status report about available interface translations for your installed modules and themes.', + 'parent' => 'admin.reports', + ); + + return $links; +} + +/** * Implements hook_permission(). */ function locale_permission() { diff --git a/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php b/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php index 35d074d..36fc607 100644 --- a/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php +++ b/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php @@ -106,7 +106,7 @@ public function submit(array $form, array &$form_state) { // Reset all the menu links defined by the system via hook_menu(). // @todo Convert this to an EFQ once we figure out 'ORDER BY m.number_parts'. - $result = $this->connection->query("SELECT mlid FROM {menu_links} ml INNER JOIN {menu_router} m ON ml.router_path = m.path WHERE ml.menu_name = :menu AND ml.module = 'system' ORDER BY m.number_parts ASC", array(':menu' => $this->entity->id()), array('fetch' => \PDO::FETCH_ASSOC))->fetchCol(); + $result = $this->connection->query("SELECT mlid FROM {menu_links} ml INNER JOIN {menu_router} m ON ml.link_path = m.path WHERE ml.menu_name = :menu AND ml.module = 'system' ORDER BY m.number_parts ASC", array(':menu' => $this->entity->id()), array('fetch' => \PDO::FETCH_ASSOC))->fetchCol(); $menu_links = $this->storageController->loadMultiple($result); foreach ($menu_links as $link) { $link->reset(); diff --git a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php index f4f9e53..aad0c4b 100644 --- a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php +++ b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php @@ -700,7 +700,7 @@ private function getStandardMenuLink() { // the front page. $query = \Drupal::entityQuery('menu_link') ->condition('module', 'system') - ->condition('router_path', 'user/logout'); + ->condition('link_path', 'user/logout'); $result = $query->execute(); if (!empty($result)) { $mlid = reset($result); diff --git a/core/modules/menu/menu.install b/core/modules/menu/menu.install index b7dc67d..2fee65e 100644 --- a/core/modules/menu/menu.install +++ b/core/modules/menu/menu.install @@ -19,7 +19,7 @@ function menu_install() { $base_link = entity_create('menu_link', array( 'menu_name' => $system_link->menu_name, - 'router_path' => 'admin/structure/menu/manage/%', + 'link_path' => 'admin/structure/menu/manage/%', 'module' => 'menu', )); diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module index fc149ee..b3d9048 100644 --- a/core/modules/menu/menu.module +++ b/core/modules/menu/menu.module @@ -103,6 +103,19 @@ function menu_menu() { } /** + * Implements hook_default_menu_links(). + */ +function menu_default_menu_links() { + $links['admin.structure.menu'] = array( + 'link_title' => 'Menus', + 'description' => 'Add new menus to your site, edit existing menus, and rename and reorganize menu links.', + 'route_name' => 'menu.overview_page', + 'parent' => 'admin.structure', + ); + return $links; +} + +/** * Implements hook_entity_info(). */ function menu_entity_info(&$entity_info) { diff --git a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php index eb34661..527ba57 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php @@ -72,6 +72,13 @@ class MenuLink extends Entity implements \ArrayAccess, MenuLinkInterface { public $mlid; /** + * An optional machine name if defined via hook_default_menu_links(). + * + * @var string + */ + public $machine_name; + + /** * The menu link UUID. * * @var string @@ -260,7 +267,7 @@ class MenuLink extends Entity implements \ArrayAccess, MenuLinkInterface { * * @var array */ - public $route_parameters; + public $route_parameters = array(); /** * The route object associated with this menu link, if any. @@ -372,8 +379,9 @@ public function reset() { // would not be reset, because properties like the original 'menu_name' are // not stored anywhere else. Since resetting a link happens rarely and this // is a one-time operation, retrieving the full menu router does no harm. + // @FIXME Decide whether we want to keep the reset functionality. $menu = menu_get_router(); - $router_item = $menu[$this->router_path]; + $router_item = $menu[$this->link_path]; $new_link = self::buildFromRouterItem($router_item); // Merge existing menu link's ID and 'has_children' property. foreach (array('mlid', 'has_children') as $key) { @@ -384,30 +392,6 @@ public function reset() { } /** - * {@inheritdoc} - */ - public static function buildFromRouterItem(array $item) { - // Suggested items are disabled by default. - if ($item['type'] == MENU_SUGGESTED_ITEM) { - $item['hidden'] = 1; - } - // Hide all items that are not visible in the tree. - elseif (!($item['type'] & MENU_VISIBLE_IN_TREE)) { - $item['hidden'] = -1; - } - // Note, we set this as 'system', so that we can be sure to distinguish all - // the menu links generated automatically from entries in {menu_router}. - $item['module'] = 'system'; - $item += array( - 'link_title' => $item['title'], - 'link_path' => $item['path'], - 'options' => empty($item['description']) ? array() : array('attributes' => array('title' => $item['description'])), - ); - return \Drupal::entityManager() - ->getStorageController('menu_link')->create($item); - } - - /** * Implements ArrayAccess::offsetExists(). */ public function offsetExists($offset) { @@ -494,8 +478,7 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { $this->external = (url_is_external($this->link_path) || $this->link_path == '') ? 1 : 0; // Try to find a parent link. If found, assign it and derive its menu. - $parent_candidates = !empty($this->parentCandidates) ? $this->parentCandidates : array(); - $parent = $this->findParent($storage_controller, $parent_candidates); + $parent = $this->findParent($storage_controller); if ($parent) { $this->plid = $parent->id(); $this->menu_name = $parent->menu_name; @@ -534,17 +517,7 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { if (isset($this->original) && ($this->plid != $this->original->plid || $this->menu_name != $this->original->menu_name)) { $storage_controller->moveChildren($this); } - // Find the router_path. - if (empty($this->router_path) || empty($this->original) || (isset($this->original) && $this->original->link_path != $this->link_path)) { - if ($this->external) { - $this->router_path = ''; - } - else { - // Find the router path which will serve this path. - $this->parts = explode('/', $this->link_path, MENU_MAX_PARTS); - $this->router_path = _menu_find_router_path($this->link_path); - } - } + // Find the route_name. if (!isset($this->route_name)) { if ($result = static::findRouteNameParameters($this->link_path)) { @@ -555,6 +528,9 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { $this->route_parameters = array(); } } + elseif (empty($this->link_path)) { + $this->link_path = \Drupal::urlGenerator()->getPathFromRoute($this->route_name, $this->route_parameters); + } } /** @@ -597,7 +573,7 @@ public static function findRouteNameParameters($link_path) { /** * {@inheritdoc} */ - public function setParents(EntityInterface $parent) { + protected function setParents(MenuLink $parent) { $i = 1; while ($i < $this->depth) { $p = 'p' . $i++; @@ -615,7 +591,7 @@ public function setParents(EntityInterface $parent) { /** * {@inheritdoc} */ - public function findParent(EntityStorageControllerInterface $storage_controller, array $parent_candidates = array()) { + protected function findParent(EntityStorageControllerInterface $storage_controller) { $parent = FALSE; // This item is explicitely top-level, skip the rest of the parenting. @@ -638,26 +614,29 @@ public function findParent(EntityStorageControllerInterface $storage_controller, } foreach ($candidates as $mlid) { - if (isset($parent_candidates[$mlid])) { - $parent = $parent_candidates[$mlid]; - } - else { - $parent = $storage_controller->load($mlid); - } + $parent = $storage_controller->load($mlid); if ($parent) { - return $parent; + break; } } - - // If everything else failed, try to derive the parent from the path - // hierarchy. This only makes sense for links derived from menu router - // items (ie. from hook_menu()). - if ($this->module == 'system') { - $parent = $storage_controller->getParentFromHierarchy($this); - } - return $parent; } - + /** + * Builds and returns the renderable array for this menu link. + * + * @return array + * A renderable array representing the content of the link. + */ + public function build() { + $build = array( + '#type' => 'link', + '#title' => $this->title, + '#href' => $this->href, + '#route_name' => $this->route_name ? $this->route_name : NULL, + '#route_parameters' => $this->route_parameters, + '#options' => !empty($this->localized_options) ? $this->localized_options : array(), + ); + return $build; + } } diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php index fcf5415..743dd46 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php @@ -7,10 +7,8 @@ namespace Drupal\menu_link; -use Symfony\Component\Routing\Route; -use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityStorageControllerInterface; +use Symfony\Component\Routing\Route; /** * Provides an interface defining a menu link entity. @@ -39,23 +37,12 @@ public function setRouteObject(Route $route); /** * Resets a system-defined menu link. * - * @return \Drupal\Core\Entity\EntityInterface + * @return \Drupal\menu_link\Entity\MenuLinkInterface * A menu link entity. */ public function reset(); /** - * Builds a menu link entity from a router item. - * - * @param array $item - * A menu router item. - * - * @return \Drupal\menu_link\MenuLinkInterface - * A menu link entity. - */ - public static function buildFromRouterItem(array $item); - - /** * Returns the route_name and route parameters matching a system path. * * @param string $link_path @@ -67,33 +54,4 @@ public static function buildFromRouterItem(array $item); */ public static function findRouteNameParameters($link_path); - /** - * Sets the p1 through p9 properties for a menu link entity being saved. - * - * @param \Drupal\Core\Entity\EntityInterface $parent - * A menu link entity. - */ - public function setParents(EntityInterface $parent); - - /** - * Finds a possible parent for a given menu link entity. - * - * Because the parent of a given link might not exist anymore in the database, - * we apply a set of heuristics to determine a proper parent: - * - * - use the passed parent link if specified and existing. - * - else, use the first existing link down the previous link hierarchy - * - else, for system menu links (derived from hook_menu()), reparent - * based on the path hierarchy. - * - * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller - * Storage controller object. - * @param array $parent_candidates - * An array of menu link entities keyed by mlid. - * - * @return \Drupal\Core\Entity\EntityInterface|false - * A menu link entity structure of the possible parent or FALSE if no valid - * parent has been found. - */ - public function findParent(EntityStorageControllerInterface $storage_controller, array $parent_candidates = array()); } diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php index c7ef831..d5106da 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php @@ -100,7 +100,7 @@ public static function createInstance(ContainerInterface $container, $entity_typ protected function buildQuery($ids, $revision_id = FALSE) { $query = parent::buildQuery($ids, $revision_id); // Specify additional fields from the {menu_router} table. - $query->leftJoin('menu_router', 'm', 'base.router_path = m.path'); + $query->leftJoin('menu_router', 'm', 'base.link_path = m.path'); $query->fields('m', static::$routerItemFields); return $query; } @@ -118,9 +118,6 @@ protected function attachLoad(&$menu_links, $load_revision = FALSE) { $menu_link->options = unserialize($menu_link->options); $menu_link->route_parameters = unserialize($menu_link->route_parameters); - // Use the weight property from the menu link. - $menu_link->router_item['weight'] = $menu_link->weight; - // By default use the menu_name as type. $menu_link->bundle = $menu_link->menu_name; @@ -235,7 +232,7 @@ public function loadUpdatedCustomized(array $router_paths) { ->condition(db_or() ->condition('updated', 1) ->condition(db_and() - ->condition('router_path', $router_paths, 'NOT IN') + ->condition('link_path', $router_paths, 'NOT IN') ->condition('external', 0) ->condition('customized', 1) ) diff --git a/core/modules/menu_link/menu_link.api.php b/core/modules/menu_link/menu_link.api.php index 197aee8..e7b3dd7 100644 --- a/core/modules/menu_link/menu_link.api.php +++ b/core/modules/menu_link/menu_link.api.php @@ -11,14 +11,13 @@ */ /** - * Alter a menu link after it has been translated and before it is rendered. + * Alter a menu link after it has been prepared and before it is rendered. * - * This hook is invoked from _menu_link_translate() after a menu link has been - * translated; i.e., after dynamic path argument placeholders (%) have been - * replaced with actual values, the user access to the link's target page has - * been checked, and the link has been localized. It is only invoked if - * $menu_link['options']['alter'] has been set to a non-empty value (e.g. TRUE). - * This flag should be set using hook_menu_link_presave(). + * This hook is invoked from _menu_link_prepare() after a menu link has been + * translated; i.e., after the user access to the link's target page has + * been checked. It is only invoked if $menu_link['options']['alter'] has been + * set to a non-empty value (e.g. TRUE). This flag should be set using + * hook_menu_link_presave(). * * Implementations of this hook are able to alter any property of the menu link. * For example, this hook may be used to add a page-specific query string to all @@ -29,12 +28,10 @@ * * @param \Drupal\menu_link\Entity\MenuLink $menu_link * A menu link entity. - * @param array $map - * Associative array containing the menu $map (path parts and/or objects). * * @see hook_menu_link_alter() */ -function hook_translated_menu_link_alter(\Drupal\menu_link\Entity\MenuLink &$menu_link, $map) { +function hook_prepared_menu_link_alter(\Drupal\menu_link\Entity\MenuLink &$menu_link, $map) { if ($menu_link->href == 'devel/cache/clear') { $menu_link->localized_options['query'] = drupal_get_destination(); } diff --git a/core/modules/menu_link/menu_link.install b/core/modules/menu_link/menu_link.install index 976653f..c789d34 100644 --- a/core/modules/menu_link/menu_link.install +++ b/core/modules/menu_link/menu_link.install @@ -31,6 +31,12 @@ function menu_link_schema() { 'length' => 128, 'not null' => FALSE, ), + 'machine_name' => array( + 'description' => 'Unique machine name: Optional human-readable ID for this link.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), 'plid' => array( 'description' => 'The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.', 'type' => 'int', @@ -45,13 +51,6 @@ function menu_link_schema() { 'not null' => TRUE, 'default' => '', ), - 'router_path' => array( - 'description' => 'For links corresponding to a Drupal path (external = 0), this connects the link to a {menu_router}.path for joins.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), 'langcode' => array( 'description' => 'The {language}.langcode of this link.', 'type' => 'varchar', @@ -60,7 +59,7 @@ function menu_link_schema() { 'default' => '', ), 'link_title' => array( - 'description' => 'The text displayed for the link, which may be modified by a title callback stored in {menu_router}.', + 'description' => 'The text displayed for the link.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, @@ -214,7 +213,6 @@ function menu_link_schema() { 'path_menu' => array(array('link_path', 128), 'menu_name'), 'menu_plid_expand_child' => array('menu_name', 'plid', 'expanded', 'has_children'), 'menu_parents' => array('menu_name', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9'), - 'router_path' => array(array('router_path', 128)), ), 'primary key' => array('mlid'), ); diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 8da4271..a80d1a6 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -981,6 +981,30 @@ function node_menu() { } /** + * Implements hook_default_menu_links(). + */ +function node_default_menu_links() { + $links['admin.content'] = array( + 'link_title' => 'Content', + 'route_name' => 'node.content_overview', + 'parent' => 'admin', + 'description' => 'Find and manage content.', + ); + + $links['admin.structure.types'] = array( + 'link_title' => 'Content types', + 'parent' => 'admin.structure', + 'description' => 'Manage content types, including default status, front page promotion, comment settings, etc.', + 'route_name' => 'node.overview_types', + ); + $links['node.add'] = array( + 'link_title' => 'Add content', + 'route_name' => 'node.add_page', + ); + return $links; +} + +/** * Implements hook_menu_local_tasks(). */ function node_menu_local_tasks(&$data, $route_name) { diff --git a/core/modules/path/path.module b/core/modules/path/path.module index d33f881..51646ab 100644 --- a/core/modules/path/path.module +++ b/core/modules/path/path.module @@ -79,6 +79,21 @@ function path_menu() { } /** + * Implements hook_default_menu_links(). + */ +function path_default_menu_links() { + $links['admin.config.search.path'] = array( + 'link_title' => 'URL aliases', + 'description' => "Change your site's URL paths by aliasing them.", + 'route_name' => 'path.admin_overview', + 'parent' => 'admin.config.search', + 'weight' => -5, + ); + + return $links; +} + +/** * Implements hook_form_BASE_FORM_ID_alter() for node_form(). * * @see path_form_element_validate() diff --git a/core/modules/picture/picture.module b/core/modules/picture/picture.module index b9b3b01..5d5d10c 100644 --- a/core/modules/picture/picture.module +++ b/core/modules/picture/picture.module @@ -77,6 +77,21 @@ function picture_menu() { } /** + * Implements hook_default_menu_links(). + */ +function picture_default_menu_links() { + $links['admin.config.media.picturemapping'] = array( + 'link_title' => 'Picture Mappings', + 'description' => 'Manage picture mappings', + 'weight' => 10, + 'route_name' => 'picture.mapping_page', + 'parent' => 'admin.config.media', + ); + + return $links; +} + +/** * Implements hook_library_info(). */ function picture_library_info() { diff --git a/core/modules/search/search.module b/core/modules/search/search.module index 56e6859..4ff219c 100644 --- a/core/modules/search/search.module +++ b/core/modules/search/search.module @@ -190,6 +190,26 @@ function search_menu() { } /** + * Implements hook_default_menu_links(). + */ +function search_default_menu_links() { + $links['search'] = array( + 'link_title' => 'Search', + 'route_name' => 'search.view', + 'type' => MENU_SUGGESTED_ITEM, + ); + $links['admin.config.search.settings'] = array( + 'link_title' => 'Search settings', + 'parent' => 'admin.config.search', + 'description' => 'Configure relevance settings for search and other indexing options.', + 'route_name' => 'search.settings', + 'weight' => -10, + ); + + return $links; +} + +/** * Returns information about the default search plugin. * * @return array diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module index d0594b0..29f21e2 100644 --- a/core/modules/shortcut/shortcut.module +++ b/core/modules/shortcut/shortcut.module @@ -109,6 +109,20 @@ function shortcut_menu() { } /** + * Implements hook_default_menu_links(). + */ +function shortcut_default_menu_links() { + $links['admin.config.user-interface.shortcut'] = array( + 'link_title' => 'Shortcuts', + 'description' => 'Add and modify shortcut sets.', + 'route_name' => 'shortcut.set_admin', + 'parent' => 'admin.config.user-interface', + ); + + return $links; +} + +/** * Implements hook_admin_paths(). */ function shortcut_admin_paths() { diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index 6275ec3..ef22cdf 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -58,6 +58,21 @@ function simpletest_menu() { } /** + * Implements hook_default_menu_links(). + */ +function simpletest_default_menu_links() { + $links['admin.config.development.testing'] = array( + 'link_title' => 'Testing', + 'description' => 'Run tests against Drupal core and your modules. These tests help assure that your site code is working as designed.', + 'route_name' => 'simpletest.test_form', + 'parent' => 'admin.config.development', + 'weight' => -5, + ); + + return $links; +} + +/** * Implements hook_permission(). */ function simpletest_permission() { diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module index d2dfb4c..3f7ccde 100644 --- a/core/modules/statistics/statistics.module +++ b/core/modules/statistics/statistics.module @@ -91,13 +91,26 @@ function statistics_menu() { 'title' => 'Statistics', 'description' => 'Control details about what and how your site logs content statistics.', 'route_name' => 'statistics.settings', - 'access arguments' => array('administer statistics'), 'weight' => -15, ); return $items; } /** + * Implements hook_default_menu_links(). + */ +function statistics_default_menu_links() { + $links['admin.config.system.statistics'] = array( + 'link_title' => 'Statistics', + 'description' => 'Control details about what and how your site logs content statistics.', + 'route_name' => 'statistics.settings', + 'parent' => 'admin.config.system', + 'weight' => -15, + ); + return $links; +} + +/** * Implements hook_cron(). */ function statistics_cron() { diff --git a/core/modules/system/lib/Drupal/system/Controller/SystemController.php b/core/modules/system/lib/Drupal/system/Controller/SystemController.php index 6145a11..7b560dc 100644 --- a/core/modules/system/lib/Drupal/system/Controller/SystemController.php +++ b/core/modules/system/lib/Drupal/system/Controller/SystemController.php @@ -85,7 +85,7 @@ public function overview() { if (!empty($result)) { $menu_links = $menu_link_storage->loadMultiple($result); foreach ($menu_links as $item) { - _menu_link_translate($item); + _menu_link_prepare($item); if (!$item['access']) { continue; } diff --git a/core/modules/system/lib/Drupal/system/SystemManager.php b/core/modules/system/lib/Drupal/system/SystemManager.php index 07e8c0a..5637ff6 100644 --- a/core/modules/system/lib/Drupal/system/SystemManager.php +++ b/core/modules/system/lib/Drupal/system/SystemManager.php @@ -194,7 +194,7 @@ public function getAdminBlock($item) { } if (!isset($item['mlid'])) { - $menu_links = $this->menuLinkStorage->loadByProperties(array('router_path' => $item['path'], 'module' => 'system')); + $menu_links = $this->menuLinkStorage->loadByProperties(array('link_path' => $item['path'], 'module' => 'system')); $menu_link = reset($menu_links); $item['mlid'] = $menu_link->id(); $item['menu_name'] = $menu_link->menu_name; @@ -207,18 +207,19 @@ public function getAdminBlock($item) { $content = array(); $menu_links = $this->menuLinkStorage->loadByProperties(array('plid' => $item['mlid'], 'menu_name' => $item['menu_name'], 'hidden' => 0)); foreach ($menu_links as $link) { - _menu_link_translate($link); + _menu_link_prepare($link); if ($link['access']) { - // The link description, either derived from 'description' in - // hook_menu() or customized via menu module is used as title attribute. - if (!empty($link['localized_options']['attributes']['title'])) { - $link['description'] = $link['localized_options']['attributes']['title']; - unset($link['localized_options']['attributes']['title']); - } // Prepare for sorting as in function _menu_tree_check_access(). // The weight is offset so it is always positive, with a uniform 5-digits. $key = (50000 + $link['weight']) . ' ' . Unicode::strtolower($link['title']) . ' ' . $link['mlid']; - $content[$key] = $link; + $content[$key] = $link->build(); + // The link description, either derived from 'description' in + // hook_menu() or customized via menu module is used as title attribute. + // @todo - make description a separate entity property. + if (!empty($content[$key]['#options']['attributes']['title'])) { + $content[$key]['#description'] = $content[$key]['#options']['attributes']['title']; + unset($content[$key]['#options']['attributes']['title']); + } } } ksort($content); diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php index 9ab9103..6d7699a 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php @@ -338,10 +338,7 @@ function testBreadCrumbs() { $trail += array( 'user/' . $this->web_user->id() => $this->web_user->getUsername(), ); - $tree = array( - 'user' => t('My account'), - ); - $this->assertBreadcrumb('user/' . $this->web_user->id() . '/edit', $trail, $this->web_user->getUsername(), $tree); + $this->assertBreadcrumb('user/' . $this->web_user->id() . '/edit', $trail, $this->web_user->getUsername()); // Create an only slightly privileged user being able to access site reports // but not administration pages. diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php index 71cf0c4..ab55143 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php @@ -178,7 +178,7 @@ protected function doTestMenuName() { $admin_user = $this->drupalCreateUser(array('administer site configuration')); $this->drupalLogin($admin_user); - $menu_links = entity_load_multiple_by_properties('menu_link', array('router_path' => 'menu_name_test')); + $menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => 'menu_name_test')); $menu_link = reset($menu_links); $this->assertEqual($menu_link->menu_name, 'original', 'Menu name is "original".'); @@ -188,7 +188,7 @@ protected function doTestMenuName() { \Drupal::service('router.builder')->rebuild(); menu_router_rebuild(); - $menu_links = entity_load_multiple_by_properties('menu_link', array('router_path' => 'menu_name_test')); + $menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => 'menu_name_test')); $menu_link = reset($menu_links); $this->assertEqual($menu_link->menu_name, 'changed', 'Menu name was successfully changed after rebuild.'); } @@ -214,14 +214,14 @@ protected function doTestMenuHierarchy() { protected function doTestMenuHidden() { // Verify links for one dynamic argument. $query = \Drupal::entityQuery('menu_link') - ->condition('router_path', 'menu-test/hidden/menu', 'STARTS_WITH') - ->sort('router_path'); + ->condition('link_path', 'menu-test/hidden/menu', 'STARTS_WITH') + ->sort('link_path'); $result = $query->execute(); $menu_links = menu_link_load_multiple($result); $links = array(); foreach ($menu_links as $menu_link) { - $links[$menu_link->router_path] = $menu_link; + $links[$menu_link->link_path] = $menu_link; } $parent = $links['menu-test/hidden/menu']; @@ -229,43 +229,43 @@ protected function doTestMenuHidden() { $plid = $parent['mlid']; $link = $links['menu-test/hidden/menu/list']; - $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); + $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['link_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); + $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['link_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); $link = $links['menu-test/hidden/menu/settings']; - $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); + $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['link_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); + $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['link_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); $link = $links['menu-test/hidden/menu/manage/%']; - $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); + $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['link_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); + $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['link_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); $parent = $links['menu-test/hidden/menu/manage/%']; $depth = $parent['depth'] + 1; $plid = $parent['mlid']; $link = $links['menu-test/hidden/menu/manage/%/list']; - $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); + $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['link_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); + $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['link_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); $link = $links['menu-test/hidden/menu/manage/%/edit']; - $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); + $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['link_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); + $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['link_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); $link = $links['menu-test/hidden/menu/manage/%/delete']; - $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); + $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['link_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); + $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['link_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); // Verify links for two dynamic arguments. $query = \Drupal::entityQuery('menu_link') - ->condition('router_path', 'menu-test/hidden/block', 'STARTS_WITH') - ->sort('router_path'); + ->condition('link_path', 'menu-test/hidden/block', 'STARTS_WITH') + ->sort('link_path'); $result = $query->execute(); $menu_links = menu_link_load_multiple($result); $links = array(); foreach ($menu_links as $menu_link) { - $links[$menu_link->router_path] = $menu_link; + $links[$menu_link->link_path] = $menu_link; } $parent = $links['menu-test/hidden/block']; @@ -273,20 +273,20 @@ protected function doTestMenuHidden() { $plid = $parent['mlid']; $link = $links['menu-test/hidden/block/list']; - $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); + $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['link_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); + $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['link_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); $link = $links['menu-test/hidden/block/manage/%/%']; - $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); + $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['link_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); + $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['link_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); $parent = $links['menu-test/hidden/block/manage/%/%']; $depth = $parent['depth'] + 1; $plid = $parent['mlid']; $link = $links['menu-test/hidden/block/manage/%/%/delete']; - $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); + $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['link_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); + $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['link_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); } /** diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index 29237fd..771a3ab 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -9,6 +9,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; /** * Menu callback; displays a listing of all themes. @@ -346,9 +347,9 @@ function theme_admin_block_content($variables) { } $output .= '
'; foreach ($content as $item) { - $output .= '
' . l($item['title'], $item['href'], $item['localized_options']) . '
'; - if (!$compact && isset($item['description'])) { - $output .= '
' . filter_xss_admin($item['description']) . '
'; + $output .= '
' . drupal_render($item) . '
'; + if (!$compact && isset($item['#description'])) { + $output .= '
' . filter_xss_admin($item['#description']) . '
'; } } $output .= '
'; @@ -779,3 +780,4 @@ function theme_system_themes_page($variables) { return $output; } + diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 697cbba..67f0121 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -2295,6 +2295,23 @@ function system_update_8060() { } /** + * Add machine_name column to the menu_links table. + */ +function system_update_8061() { + $spec = array( + 'description' => 'Machine name: Optional human-readable ID for this link.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ); + + db_add_field('menu_links', 'machine_name', $spec); + // Do away with the field that joins to the old {menu_router}. + db_drop_index('menu_links', 'router_path'); + db_drop_field('menu_links', 'router_path'); +} + +/** * @} End of "defgroup updates-7.x-to-8.x". * The next series of updates should start at 9000. */ diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 883faae..1fd5370 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -9,10 +9,12 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Language\Language; use Drupal\Core\Utility\ModuleInfo; +use Drupal\menu_link\MenuLinkInterface; use Drupal\user\UserInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; use Guzzle\Http\Exception\BadResponseException; use Guzzle\Http\Exception\RequestException; @@ -823,6 +825,208 @@ function system_menu() { } /** + * Implements hook_default_menu_links(). + */ +function system_default_menu_links() { + + $items['admin'] = array( + 'link_title' => 'Administration', + 'route_name' => 'system.admin', + 'weight' => 9, + 'menu_name' => 'admin', + ); + + // Menu items that are basically just menu blocks. + $items['admin.structure'] = array( + 'route_name' => 'system.admin_structure', + 'parent' => 'admin', + 'description' => 'Administer blocks, content types, menus, etc.', + 'link_title' => 'Structure', + 'weight' => -8, + ); + // Appearance. + $items['admin.appearance'] = array( + 'route_name' => 'system.themes_page', + 'link_title' => 'Appearance', + 'parent' => 'admin', + 'weight' => -6, + ); + // Modules. + $items['admin.modules'] = array( + 'link_title' => 'Extend', + 'parent' => 'admin', + 'route_name' => 'system.modules_list', + 'weight' => -2, + ); + // Configuration. + $items['admin.config'] = array( + 'link_title' => 'Configuration', + 'parent' => 'admin', + 'description' => 'Administer settings.', + 'route_name' => 'system.admin_config', + ); + + // Media settings. + $items['admin.config.media'] = array( + 'route_name' => 'system.admin_config_media', + 'parent' => 'admin.config', + 'link_title' => 'Media', + 'weight' => -10, + ); + $items['admin.config.media.file-system'] = array( + 'link_title' => 'File system', + 'description' => 'Tell Drupal where to store uploaded files and how they are accessed.', + 'parent' => 'admin.config.media', + 'route_name' => 'system.file_system_settings', + ); + $items['admin.config.media.image-toolkit'] = array( + 'link_title' => 'Image toolkit', + 'parent' => 'admin.config.media', + 'route_name' => 'system.image_toolkit_settings', + 'description' => 'Choose which image toolkit to use if you have installed optional toolkits.', + 'weight' => 20, + ); + + // Service settings. + $items['admin.config.services'] = array( + 'link_title' => 'Web services', + 'parent' => 'admin.config', + 'route_name' => 'system.admin_config_services', + 'weight' => 0, + ); + $items['admin.config.services.rss-publishing'] = array( + 'link_title' => 'RSS publishing', + 'parent' => 'admin.config.services', + 'description' => 'Configure the site description, the number of items per feed and whether feeds should be titles/teasers/full-text.', + 'route_name' => 'system.rss_feeds_settings', + ); + + // Development settings. + $items['admin.config.development'] = array( + 'route_name' => 'system.admin_config_development', + 'parent' => 'admin.config', + 'link_title' => 'Development', + 'description' => 'Development tools.', + 'weight' => -10, + ); + $items['admin.config.development.maintenance'] = array( + 'link_title' => 'Maintenance mode', + 'parent' => 'admin.config.development', + 'description' => 'Take the site offline for maintenance or bring it back online.', + 'route_name' => 'system.site_maintenance_mode', + 'weight' => -10, + ); + $items['admin.config.development.performance'] = array( + 'link_title' => 'Performance', + 'parent' => 'admin.config.development', + 'description' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.', + 'route_name' => 'system.performance_settings', + 'weight' => -20, + ); + $items['admin.config.development.logging'] = array( + 'link_title' => 'Logging and errors', + 'parent' => 'admin.config.development', + 'description' => "Settings for logging and alerts modules. Various modules can route Drupal's system events to different destinations, such as syslog, database, email, etc.", + 'route_name' => 'system.logging_settings', + 'weight' => -15, + ); + + // Regional and date settings. + $items['admin.config.regional'] = array( + 'route_name' => 'system.admin_config_regional', + 'link_title' => 'Regional and language', + 'parent' => 'admin.config', + 'description' => 'Regional settings, localization and translation.', + 'weight' => -5, + ); + $items['admin.config.regional.settings'] = array( + 'link_title' => 'Regional settings', + 'parent' => 'admin.config.regional', + 'description' => "Settings for the site's default time zone and country.", + 'route_name' => 'system.regional_settings', + 'weight' => -20, + ); + $items['admin.config.regional.date-time'] = array( + 'link_title' => 'Date and time formats', + 'parent' => 'admin.config.regional', + 'description' => 'Configure display format strings for date and time.', + 'route_name' => 'system.date_format_list', + 'weight' => -9, + ); + + // Search settings. + $items['admin.config.search'] = array( + 'link_title' => 'Search and metadata', + 'route_name' => 'system.admin_config_search', + 'parent' => 'admin.config', + 'description' => 'Local site search, metadata and SEO.', + 'weight' => -10, + ); + + // System settings. + $items['admin.config.system'] = array( + 'link_title' => 'System', + 'link_path' => 'admin/config/system', + 'parent' => 'admin.config', + 'description' => 'General system related configuration.', + 'weight' => -20, + ); + $items['admin.config.system.site-information'] = array( + 'link_title' => 'Site information', + 'parent' => 'admin.config.system', + 'description' => 'Change site name, e-mail address, slogan, default front page, and number of posts per page, error pages.', + 'route_name' => 'system.site_information_settings', + 'weight' => -20, + ); + $items['admin.config.system.cron'] = array( + 'link_title' => 'Cron', + 'parent' => 'admin.config.system', + 'description' => 'Manage automatic site maintenance tasks.', + 'route_name' => 'system.cron_settings', + 'weight' => 20, + ); + // Additional categories + $items['admin.config.user-interface'] = array( + 'link_title' => 'User interface', + 'route_name' => 'system.admin_config_ui', + 'parent' => 'admin.config', + 'description' => 'Tools that enhance the user interface.', + 'weight' => -15, + ); + $items['admin.config.workflow'] = array( + 'link_title' => 'Workflow', + 'route_name' => 'system.admin_config_workflow', + 'parent' => 'admin.config', + 'description' => 'Content workflow, editorial workflow tools.', + 'weight' => 5, + ); + $items['admin.config.content'] = array( + 'link_title' => 'Content authoring', + 'route_name' => 'system.admin_config_content', + 'parent' => 'admin.config', + 'description' => 'Settings related to formatting and authoring content.', + 'weight' => -15, + ); + + // Reports. + $items['admin.reports'] = array( + 'link_title' => 'Reports', + 'route_name' => 'system.admin_reports', + 'parent' => 'admin', + 'description' => 'View reports, updates, and errors.', + 'weight' => 5, + ); + $items['admin.reports.status'] = array( + 'link_title' => 'Status report', + 'parent' => 'admin.reports', + 'description' => "Get a status report about your site's operation and any detected problems.", + 'route_name' => 'system.status', + ); + + return $items; +} + +/** * Implements hook_theme_suggestions_HOOK(). */ function system_theme_suggestions_html(array $variables) { diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module index ab2ef30..d5840ba 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.module +++ b/core/modules/system/tests/modules/menu_test/menu_test.module @@ -297,6 +297,125 @@ function menu_test_menu() { } /** + * Implements hook_default_menu_links(). + */ +function menu_test_default_menu_links() { + // The name of the menu changes during the course of the test. Using a $_GET. + $items['menu_name_test'] = array( + 'link_title' => 'Test menu_name router item', + 'route_name' => 'menu_test.menu_name_test', + 'menu_name' => menu_test_menu_name(), + ); + // This item uses SystemController::systemAdminMenuBlockPage() to list child + // items. + $items['menu_callback_description'] = array( + 'link_title' => 'Menu item title', + 'description' => 'Menu item description parent', + 'route_name' => 'menu_test.callback_description', + ); + // This item tests the description key. + $items['menu_callback_description.description-plain'] = array( + 'link_title' => 'Menu item with a regular description', + 'description' => 'Menu item description text', + 'route_name' => 'menu_test.callback_description_plain', + 'parent' => 'menu_callback_description', + ); + // This item tests using a description callback. + $items['menu_callback_description.description-callback'] = array( + 'link_title' => 'Menu item with a description set with a callback', + 'description callback' => 'check_plain', + 'description arguments' => array('Menu item description arguments'), + 'route_name' => 'menu_test.callback_description_callback', + 'parent' => 'menu_callback_description', + ); + // Use FALSE as 'title callback' to bypass t(). + $items['menu_no_title_callback'] = array( + 'link_title' => 'A title with @placeholder', + 'title' => 'A title with @placeholder', + 'title callback' => FALSE, + 'title arguments' => array('@placeholder' => 'some other text'), + 'route_name' => 'menu_test.menu_no_title_callback', + ); + + // Hierarchical tests. + $items['menu-test.hierarchy.parent'] = array( + 'link_title' => 'Parent menu router', + 'route_name' => 'menu_test.hierarchy_parent', + ); + $items['menu-test.hierarchy.parent.child'] = array( + 'link_title' => 'Child menu router', + 'route_name' => 'menu_test.hierarchy_parent_child', + 'parent' => 'menu_test.hierarchy_parent', + ); + $items['menu-test.hierarchy.parent.child2.child'] = array( + 'link_title' => 'Unattached subchild router', + 'route_name' => 'menu_test.hierarchy_parent_child2', + 'parent' => 'menu_test.hierarchy_parent.child', + ); + // Path containing "exotic" characters. + $path = "menu-test/ -._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters. + "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string. + "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets. + $items[$path] = array( + 'link_title' => '"Exotic" path', + 'route_name' => 'menu_test.exotic_path', + ); + + // Hidden tests; base parents. + // Same structure as in Menu and Block modules. Since those structures can + // change, we need to simulate our own in here. + $items['menu-test'] = array( + 'link_title' => 'Menu test root', + 'route_name' => 'menu_test.menu_test', + ); + $items['menu-test.hidden'] = array( + 'link_title' => 'Hidden test root', + 'route_name' => 'menu_test.hidden', + 'parent' => 'menu-test', + ); + + // Hidden tests; one dynamic argument. + $items['menu-test.hidden.menu'] = array( + 'link_title' => 'Menus', + 'route_name' => 'menu_test.hidden_menu', + 'parent' => 'menu-test.hidden', + ); + + // Hidden tests; two dynamic arguments. + $items['menu-test.hidden.block'] = array( + 'link_title' => 'Blocks', + 'route_name' => 'menu_test.hidden_block', + 'parent' => 'menu-test.hidden', + ); + + // Menu trail tests. + // @see MenuTrailTestCase + $items['menu-test/menu-trail'] = array( + 'link_title' => 'Menu trail - Case 1', + 'route_name' => 'menu_test.menu_trail', + 'parent' => 'menu-test', + ); + $items['admin/config/development/menu-trail'] = array( + 'link_title' => 'Menu trail - Case 2', + 'description' => 'Tests menu_tree_set_path()', + 'route_name' => 'menu_test.menu_trail_admin', + 'parent' => 'admin.config.development', + ); + $items['menu-test/custom-403-page'] = array( + 'link_title' => 'Custom 403 page', + 'route_name' => 'menu_test.custom_403', + 'parent' => 'menu-test', + ); + $items['menu-test/custom-404-page'] = array( + 'link_title' => 'Custom 404 page', + 'route_name' => 'menu_test.custom_404', + 'parent' => 'menu-test', + ); + + return $items; +} + +/** * Implements hook_menu_local_tasks(). * * If the menu_test.settings configuration 'tasks.add' has been set, adds diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php index e79c37c..7685cd4 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php @@ -57,6 +57,19 @@ public function termTitle(TermInterface $taxonomy_term) { } /** + * Route title callback. + * + * @param \Drupal\taxonomy\VocabularyInterface $taxonomy_vocabulary + * The taxonomy term. + * + * @return string + * The term label. + */ + public function vocabularyTitle(VocabularyInterface $taxonomy_vocabulary) { + return Xss::filter($taxonomy_vocabulary->label()); + } + + /** * @todo Remove taxonomy_term_feed(). */ public function termFeed(TermInterface $taxonomy_term) { diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index c5f6153..2230d58 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -274,6 +274,20 @@ function taxonomy_menu() { } /** + * Implements hook_default_menu_links(). + */ +function taxonomy_default_menu_links() { + $links['admin.structure.taxonomy'] = array( + 'link_title' => 'Taxonomy', + 'parent' => 'admin.structure', + 'description' => 'Manage tagging, categorization, and classification of your content.', + 'route_name' => 'taxonomy.vocabulary_list', + ); + + return $links; +} + +/** * Implements hook_admin_paths(). */ function taxonomy_admin_paths() { diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml index 8040860..b5f3c2e 100644 --- a/core/modules/taxonomy/taxonomy.routing.yml +++ b/core/modules/taxonomy/taxonomy.routing.yml @@ -79,6 +79,7 @@ taxonomy.overview_terms: path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}' defaults: _form: 'Drupal\taxonomy\Form\OverviewTerms' + _title_callback: 'Drupal\taxonomy\Controller\TaxonomyController::vocabularyTitle' requirements: _entity_access: 'taxonomy_vocabulary.view' diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module index 878a755..e4a8d13 100644 --- a/core/modules/tracker/tracker.module +++ b/core/modules/tracker/tracker.module @@ -31,6 +31,18 @@ function tracker_help($path, $arg) { } /** + * Implements hook_default_menu_links(). + */ +function tracker_default_menu_links() { + $links['tracker'] = array( + 'link_title' => 'Recent content', + 'route_name' => 'tracker.page', + ); + + return $links; +} + +/** * Implements hook_cron(). * * Updates tracking information for any items still to be tracked. The state diff --git a/core/modules/update/update.module b/core/modules/update/update.module index 4cf9c0d..ea42b6c 100644 --- a/core/modules/update/update.module +++ b/core/modules/update/update.module @@ -161,6 +161,21 @@ function update_menu() { } /** + * Implements hook_default_menu_links(). + */ +function update_default_menu_links() { + $links['admin.reports.updates'] = array( + 'link_title' => 'Available updates', + 'description' => 'Get a status report about available updates for your installed modules and themes.', + 'route_name' => 'update.status', + 'parent' => 'admin.reports', + 'weight' => -50, + ); + + return $links; +} + +/** * Access callback: Resolves if the current user can access updater menu items. * * It both enforces the 'administer software updates' permission and the global diff --git a/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php b/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php index 547057b..ae11687 100644 --- a/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php +++ b/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php @@ -28,7 +28,7 @@ public function appliesTo() { * {@inheritdoc} */ public function access(Route $route, Request $request, AccountInterface $account) { - return $account->isAuthenticated() ? static::ALLOW : static::DENY; + return ($request->attributes->get('_menu_admin') || $account->isAuthenticated()) ? static::ALLOW : static::DENY; } } diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 5751f02..1eb0e36 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -755,13 +755,6 @@ function user_menu() { 'type' => MENU_CALLBACK, ); - $items['user/logout'] = array( - 'title' => 'Log out', - 'route_name' => 'user.logout', - 'weight' => 10, - 'menu_name' => 'account', - ); - // User listing pages. $items['admin/people'] = array( 'title' => 'People', @@ -820,6 +813,62 @@ function user_menu() { } /** + * Implements hook_default_menu_links(). + */ +function user_default_menu_links() { + // Registration and login pages. + $links['user'] = array( + 'link_title' => 'My account', + 'weight' => -10, + 'route_name' => 'user.page', + 'menu_name' => 'account', + ); + + $links['user.logout'] = array( + 'link_title' => 'Log out', + 'route_name' => 'user.logout', + 'weight' => 10, + 'menu_name' => 'account', + ); + + // User listing pages. + $links['admin.people'] = array( + 'link_title' => 'People', + 'route_name' => 'user.admin_account', + 'description' => 'Manage user accounts, roles, and permissions.', + 'parent' => 'admin', + 'weight' => -4, + ); + // Permissions and role forms. + $links['admin.people.permissions'] = array( + 'link_title' => 'Permissions', + 'parent' => 'admin.people', + 'description' => 'Determine access to features by selecting permissions for roles.', + 'route_name' => 'user.admin_permissions', + ); + + // Administration pages. + $links['admin.config.people'] = array( + 'link_title' => 'People', + 'route_name' => 'user.admin_index', + 'parent' => 'admin.config', + 'description' => 'Configure user accounts.', + 'position' => 'left', + 'weight' => -20, + ); + + $links['admin.config.people.accounts'] = array( + 'link_title' => 'Account settings', + 'parent' => 'admin.config.people', + 'description' => 'Configure default behavior of users, including registration requirements, e-mails, and fields.', + 'weight' => -10, + 'route_name' => 'user.account_settings', + ); + + return $links; +} + +/** * Implements hook_menu_link_presave(). */ function user_menu_link_presave(MenuLink $menu_link) { @@ -827,15 +876,9 @@ function user_menu_link_presave(MenuLink $menu_link) { // for authenticated users. Authenticated users should see "My account", but // anonymous users should not see it at all. Therefore, invoke // user_menu_link_load() to conditionally hide the link. - if ($menu_link->link_path == 'user' && $menu_link->module == 'system') { + if ($menu_link->machine_name == 'user' && $menu_link->module == 'system') { $menu_link->options['alter'] = TRUE; } - - // Force the Logout link to appear on the top-level of 'account' menu by - // default (i.e., unless it has been customized). - if ($menu_link->link_path == 'user/logout' && $menu_link->module == 'system' && empty($menu_link->customized)) { - $menu_link->plid = 0; - } } /** diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php index d8870a5..8acb9df 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php @@ -2462,6 +2462,21 @@ public function renderMoreLink() { } /** + * Creates menu links, if this display provides some. + * + * @param array $existing_links + * An array of already existing menu items provided by drupal. + * + * @return array + * The menu links registers for this display. + * + * @see hook_default_menu_links() + */ + public function executeHookDefaultMenuLinks(array &$existing_links) { + return array(); + } + + /** * If this display creates a page with a menu item, implement it here. * * @param array $callbacks diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php index 19893c7..5e9f306 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php @@ -252,6 +252,70 @@ public function alterRoutes(RouteCollection $collection) { } /** + * {@inheritdoc} + */ + public function executeHookDefaultMenuLinks(array &$existing_links) { + $links = array(); + + // Replace % with the link to our standard views argument loader + // views_arg_load -- which lives in views.module. + + $bits = explode('/', $this->getOption('path')); + $page_arguments = array($this->view->storage->id(), $this->display['id']); + $this->view->initHandlers(); + $view_arguments = $this->view->argument; + + // Replace % with %views_arg for menu autoloading and add to the + // page arguments so the argument actually comes through. + foreach ($bits as $pos => $bit) { + if ($bit == '%') { + // If a view requires any arguments we cannot create a static menu link. + return array(); + } + } + + $view_route_names = $this->state->get('views.view_route_names') ?: array(); + + $path = implode('/', $bits); + $menu_link_id = str_replace('/', '.', $path); + $links[$menu_link_id] = array(); + + if ($path) { + // Some views might override existing paths, so we have to set the route + // name based upon the altering. + $view_id_display = "{$this->view->storage->id()}.{$this->display['id']}"; + $links[$menu_link_id] = array( + 'route_name' => isset($view_route_names[$view_id_display]) ? $view_route_names[$view_id_display] : "view.$view_id_display", + // Identify URL embedded arguments and correlate them to a handler. + 'load arguments' => array($this->view->storage->id(), $this->display['id'], '%index'), + ); + + $menu = $this->getOption('menu'); + if (empty($menu)) { + $menu = array('type' => 'none'); + } + // Set the title and description if we have one. + if ($menu['type'] != 'none') { + $links[$menu_link_id]['link_title'] = $menu['title']; + $links[$menu_link_id]['description'] = $menu['description']; + } + + if (isset($menu['weight'])) { + $links[$menu_link_id]['weight'] = intval($menu['weight']); + } + + switch ($menu['type']) { + case 'normal': + // Insert item into the proper menu. + $links[$menu_link_id]['menu_name'] = $menu['name']; + break; + } + } + + return $links; + } + + /** * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::executeHookMenu(). */ public function executeHookMenu($callbacks) { diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php index 0203ce8..1137803 100644 --- a/core/modules/views/lib/Drupal/views/ViewExecutable.php +++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php @@ -1512,6 +1512,30 @@ public function executeHookMenu($display_id = NULL, &$callbacks = array()) { } /** + * Called to get hook_default_menu_links() information from the view and the named display handler. + * + * @param string $display_id + * A display id. + * @param array $links + * A menu callback array passed from views_default_menu_links_alter(). + * + * @return array|bool + */ + public function executeHookDefaultMenuLinks($display_id = NULL, &$links = array()) { + // Prepare the view with the information we have. + + // This was probably already called, but it's good to be safe. + if (!$this->setDisplay($display_id)) { + return FALSE; + } + + // Execute the hook. + if (isset($this->display_handler)) { + return $this->display_handler->executeHookDefaultMenuLinks($links); + } + } + + /** * Determine if the given user has access to the view. Note that * this sets the display handler if it hasn't been. */ diff --git a/core/modules/views/views.module b/core/modules/views/views.module index 8445804..39caf41 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -306,6 +306,22 @@ function views_menu_alter(&$callbacks) { } /** + * Implements hook_default_menu_links_alter(). + */ +function views_default_menu_links_alter(array &$links) { + // @todo What to do about all that crazy logic in views_menu_alter(). + $views = Views::getApplicableViews('uses_hook_menu'); + foreach ($views as $data) { + /** @var \Drupal\views\ViewExecutable $view */ + list($view, $display_id) = $data; + $result = $view->executeHookDefaultMenuLinks($display_id, $links); + foreach ($result as $link_id => $link) { + $links[$link_id] = $link; + } + } +} + +/** * Helper function for menu loading. This will automatically be * called in order to 'load' a views argument; primarily it * will be used to perform validation. diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module index c6a6954..d5d347a 100644 --- a/core/modules/views_ui/views_ui.module +++ b/core/modules/views_ui/views_ui.module @@ -36,6 +36,31 @@ function views_ui_menu() { } /** + * Implements hook_default_menu_links(). + */ +function views_ui_default_menu_links() { + $links = array(); + + // Top-level Views module pages (not tied to a particular View). + $links['admin.structure.views'] = array( + 'link_title' => 'Views', + 'parent' => 'admin.structure', + 'description' => 'Manage customized lists of content.', + 'route_name' => 'views_ui.list', + ); + + // A page in the Reports section to show usage of plugins in all views. + $links['admin.reports.views-plugins'] = array( + 'link_title' => 'Views plugins', + 'parent' => 'admin.reports', + 'description' => 'Overview of plugins used in all views.', + 'route_name' => 'views_ui.reports_plugins', + ); + + return $links; +} + +/** * Implements hook_entity_info(). */ function views_ui_entity_info(&$entity_info) { diff --git a/core/themes/seven/seven.theme b/core/themes/seven/seven.theme index 7defd49..26527ff 100644 --- a/core/themes/seven/seven.theme +++ b/core/themes/seven/seven.theme @@ -114,13 +114,12 @@ function seven_admin_block_content($variables) { $output = system_admin_compact_mode() ? '
    ' : '
      '; foreach ($content as $item) { $output .= '
    • '; - $content = '' . filter_xss_admin($item['title']) . ''; - $options = $item['localized_options']; - $options['html'] = TRUE; - if (isset($item['description']) && !system_admin_compact_mode()) { - $content .= '
      ' . filter_xss_admin($item['description']) . '
      '; + $item['#title'] = '' . filter_xss_admin($item['#title']) . ''; + $item['#options']['html'] = TRUE; + if (isset($item['#description']) && !system_admin_compact_mode()) { + $item['#title'] .= '
      ' . filter_xss_admin($item['#description']) . '
      '; } - $output .= l($content, $item['href'], $options); + $output .= drupal_render($item); $output .= '
    • '; } $output .= '
    ';