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() ? '