diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 987d9d0..f65e84c 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -6,11 +6,13 @@ */ use Drupal\Component\Utility\String; +use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Cache\Cache; use Drupal\Core\Language\Language; use Drupal\Core\Template\Attribute; use Drupal\menu_link\MenuLinkInterface; use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\Routing\Exception\RouteNotFoundException; /** * @defgroup menu Menu and routing system @@ -1006,11 +1008,49 @@ function _menu_link_save_recursive($controller, $machine_name, &$children, &$lin function menu_link_get_defaults() { $module_handler = \Drupal::moduleHandler(); $all_links = $module_handler->invokeAll('menu_link_defaults'); + /** @var \Drupal\Core\Routing\RouteProviderInterface $route_provider */ + $route_provider = \Drupal::service('router.route_provider'); + // Fill in the machine name from the array key. - foreach ($all_links as $machine_name => &$link) { - $link['machine_name'] = $machine_name; + foreach ($all_links as $machine_name => $link) { + $all_links[$machine_name]['machine_name'] = $machine_name; } $module_handler->alter('menu_link_defaults', $all_links); + + // Validate the menu link definitions. + foreach ($all_links as $machine_name => $link) { + + // Check for required array keys. + if (!isset($link['link_title'])) { + throw new \Exception(sprintf('Menu link %s does not have a "link_title" key in its definition.', $machine_name)); + } + + // Check that the link path is external. + if (isset($link['link_path']) && UrlHelper::isValid($link['link_path'], TRUE)) { + throw new \Exception(sprintf('link_path %s for menu link %s must be absolute.', $link['link_path'], $machine_name)); + } + + // Check if the specified parent menu link exists. + if (isset($link['parent']) && !isset($all_links[$link['parent']])) { + throw new \Exception(sprintf('Menu link %s specifies it has parent %s, but no menu link with that machine name exists.', $machine_name, $link['parent'])); + } + + // Check for either a route name or a link path. + if (!isset($link['route_name']) && !isset($link['link_path'])) { + throw new \Exception(sprintf('Menu link %s must provide either a "route_name" or a "link_path" key in its definition.', $machine_name)); + } + + // Check if the specified route exists. + if (isset($link['route_name'])) { + try { + $route_provider->getRouteByName($link['route_name']); + } + catch (RouteNotFoundException $e) { + throw new \Exception(sprintf('Menu link %s specifies route name %s, but no route with that machine name exists.', $machine_name, $link['route_name']), 0, $e); + } + } + } + return $all_links; } diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 8083f89..c136826 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -429,7 +429,7 @@ function hook_page_build(&$page) { * The value corresponding to each machine name key is an associative array * that may contain the following key-value pairs: * - link_title: (required) The untranslated title of the menu item. - * - description: The untranslated description of the link. + * - description: (optional) The untranslated description of the link. * - route_name: (optional) The route name to be used to build the path. * Either a route_name or a link_path must be provided. * - route_parameters: (optional) The route parameters to build the path.