diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 39f7d43..17bde56 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -2757,7 +2757,7 @@ function menu_router_build($save = FALSE) { // a given item came from. $callbacks = array(); // Fetch module details. - $modules = system_rebuild_module_data(); + $modules = Drupal::moduleHandler()->getModuleList(); foreach (Drupal::moduleHandler()->getImplementations('menu') as $module) { $router_items = call_user_func($module . '_menu'); if (isset($router_items) && is_array($router_items)) { @@ -2777,15 +2777,22 @@ function menu_router_build($save = FALSE) { if (isset($router_item['type']) && $router_item['type'] == MENU_DEFAULT_LOCAL_TASK) { unset($callbacks[$path]['route_name']); } - $module_details = $modules[$router_item['module']]; - if (isset($router_item['page callback']) && strpos($module_details->uri, 'core/modules') !== 0) { + // Get the path to the module. + $module_path = isset($modules[$router_item['module']]) ? $modules[$router_item['module']] : FALSE; + if (isset($router_item['page callback']) && (!$module_path || strpos($module_path, 'core/modules') !== 0)) { // This is a non-core module, legacy hook_menu() page callbacks are not // supported. - watchdog('system', 'Callback for %path from module %module uses a legacy page callback and has been ignored', array( + watchdog('system', 'Callback for %path from module %module uses a legacy page callback and will be ignored.', array( '%path' => $path, '%module' => $router_item['module'], ), WATCHDOG_WARNING); - unset($callbacks[$path]); + drupal_set_message(t('Callback for %path from module %module uses a legacy page callback and will be ignored.', array( + '%path' => $path, + '%module' => $router_item['module'], + )), 'warning'); + if (!settings()->get('legacy_route_debug', FALSE)) { + unset($callbacks[$path]); + } } // If the menu item references a route, normalize the route information // into the old structure. Note that routes are keyed by name, not path, 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 53a7cc1..79f58d5 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php @@ -640,6 +640,14 @@ public function testLegacySupport() { $this->drupalGet('router_test/test5'); $this->assertResponse(200); $this->assertTitle(t('Supported menu item') . ' | Drupal'); + + // Now set the 'legacy_route_debug' setting and rebuild the menu. + $this->settingsSet('legacy_route_debug', TRUE); + menu_router_rebuild(); + + // Now try to access the legacy item. + $this->drupalGet('legacy-unsupported'); + $this->assertResponse(200); } } diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index ca80944..007aa69 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -490,157 +490,43 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) { } /** - * Define menu items and page callbacks. + * Define menu links and local tasks. * - * This hook enables modules to register paths in order to define how URL - * requests are handled. Paths may be registered for URL handling only, or they - * can register a link to be placed in a menu (usually the Tools menu). A - * path and its associated information is commonly called a "menu router item". - * This hook is rarely called (for example, when modules are enabled), and - * its results are cached in the database. + * This hook enables modules to register links to be displayed in menus, + * as breadcrumbs and as local tasks. * * hook_menu() implementations return an associative array whose keys define * paths and whose values are an associative array of properties for each * path. (The complete list of properties is in the return value section below.) * * @section sec_callback_funcs Callback Functions - * The definition for each path may include a page callback function, which is - * invoked when the registered path is requested. If there is no other - * registered path that fits the requested path better, any further path - * components are passed to the callback function. For example, your module - * could register path 'abc/def': + * The definition for each path may no longer include a page callback function. + * Registering page callbacks with hook_menu is not supported in Drupal 8. + * Instead you must create a module.routing.yml file which registers controllers + * for your module paths. See @link https://drupal.org/node/1800686 @endlink for + * further details. Note that you can continue to register page callbacks using + * hook_menu by adding the following to settings.php. * @code - * function mymodule_menu() { - * $items['abc/def'] = array( - * 'page callback' => 'mymodule_abc_view', - * ); - * return $items; - * } - * - * function mymodule_abc_view($ghi = 0, $jkl = '') { - * // ... - * } + * $settings['legacy_route_debug'] = TRUE * @endcode - * When path 'abc/def' is requested, no further path components are in the - * request, and no additional arguments are passed to the callback function (so - * $ghi and $jkl would take the default values as defined in the function - * signature). When 'abc/def/123/foo' is requested, $ghi will be '123' and - * $jkl will be 'foo'. Note that this automatic passing of optional path - * arguments applies only to page and theme callback functions. - * - * @subsection sub_callback_arguments Callback Arguments - * In addition to optional path arguments, the page callback and other callback - * functions may specify argument lists as arrays. These argument lists may - * contain both fixed/hard-coded argument values and integers that correspond - * to path components. When integers are used and the callback function is - * called, the corresponding path components will be substituted for the - * integers. That is, the integer 0 in an argument list will be replaced with - * the first path component, integer 1 with the second, and so on (path - * components are numbered starting from zero). To pass an integer without it - * being replaced with its respective path component, use the string value of - * the integer (e.g., '1') as the argument value. This substitution feature - * allows you to re-use a callback function for several different paths. For - * example: + * Note however that this a convenience for debugging/porting module purposes + * only and will be removed before a Drupal 8.0 release. Modules that register + * page callbacks using hook_menu() will cause a warning to be logged and + * displayed to the screen. + * + * @section registering_menu_links Registering menu links + * For example your module.routing.yml could register path 'abc/def' as abc_def, + * to create a menu link and provide a page or breadcrumb title for this link + * you would add the following to your hook_menu() definition. * @code * function mymodule_menu() { * $items['abc/def'] = array( - * 'page callback' => 'mymodule_abc_view', - * 'page arguments' => array(1, 'foo'), + * 'title' => 'View abc for def', + * 'route_name' => 'abc_def', * ); * return $items; * } * @endcode - * When path 'abc/def' is requested, the page callback function will get 'def' - * as the first argument and (always) 'foo' as the second argument. - * - * If a page callback function uses an argument list array, and its path is - * requested with optional path arguments, then the list array's arguments are - * passed to the callback function first, followed by the optional path - * arguments. Using the above example, when path 'abc/def/bar/baz' is requested, - * mymodule_abc_view() will be called with 'def', 'foo', 'bar' and 'baz' as - * arguments, in that order. - * - * Special care should be taken for the page callback drupal_get_form(), because - * your specific form callback function will always receive $form and - * &$form_state as the first function arguments: - * @code - * function mymodule_abc_form($form, &$form_state) { - * // ... - * return $form; - * } - * @endcode - * See @link form_api Form API documentation @endlink for details. - * - * @section sec_path_wildcards Wildcards in Paths - * @subsection sub_simple_wildcards Simple Wildcards - * Wildcards within paths also work with integer substitution. For example, - * your module could register path 'my-module/%/edit': - * @code - * $items['my-module/%/edit'] = array( - * 'page callback' => 'mymodule_abc_edit', - * 'page arguments' => array(1), - * ); - * @endcode - * When path 'my-module/foo/edit' is requested, integer 1 will be replaced - * with 'foo' and passed to the callback function. Note that wildcards may not - * be used as the first component. - * - * @subsection sub_autoload_wildcards Auto-Loader Wildcards - * Registered paths may also contain special "auto-loader" wildcard components - * in the form of '%mymodule_abc', where the '%' part means that this path - * component is a wildcard, and the 'mymodule_abc' part defines the prefix for a - * load function, which here would be named mymodule_abc_load(). When a matching - * path is requested, your load function will receive as its first argument the - * path component in the position of the wildcard; load functions may also be - * passed additional arguments (see "load arguments" in the return value - * section below). For example, your module could register path - * 'my-module/%mymodule_abc/edit': - * @code - * $items['my-module/%mymodule_abc/edit'] = array( - * 'page callback' => 'mymodule_abc_edit', - * 'page arguments' => array(1), - * ); - * @endcode - * When path 'my-module/123/edit' is requested, your load function - * mymodule_abc_load() will be invoked with the argument '123', and should - * load and return an "abc" object with internal id 123: - * @code - * function mymodule_abc_load($abc_id) { - * return db_query("SELECT * FROM {mymodule_abc} WHERE abc_id = :abc_id", array(':abc_id' => $abc_id))->fetchObject(); - * } - * @endcode - * This 'abc' object will then be passed into the callback functions defined - * for the menu item, such as the page callback function mymodule_abc_edit() - * to replace the integer 1 in the argument array. Note that a load function - * should return NULL when it is unable to provide a loadable object. For - * example, the node_load() function for the 'node/%node/edit' menu item will - * return NULL for the path 'node/999/edit' if a node with a node ID of 999 - * does not exist. The menu routing system will return a 404 error in this case. - * - * @subsection sub_argument_wildcards Argument Wildcards - * You can also define a %wildcard_to_arg() function (for the example menu - * entry above this would be 'mymodule_abc_to_arg()'). The _to_arg() function - * is invoked to retrieve a value that is used in the path in place of the - * wildcard. A good example is user.module, which defines - * user_uid_optional_to_arg() (corresponding to the menu entry - * 'tracker/%user_uid_optional'). This function returns the user ID of the - * current user. - * - * The _to_arg() function will get called with three arguments: - * - $arg: A string representing whatever argument may have been supplied by - * the caller (this is particularly useful if you want the _to_arg() - * function only supply a (default) value if no other value is specified, - * as in the case of user_uid_optional_to_arg(). - * - $map: An array of all path fragments (e.g. array('node','123','edit') for - * 'node/123/edit'). - * - $index: An integer indicating which element of $map corresponds to $arg. - * - * _load() and _to_arg() functions may seem similar at first glance, but they - * have different purposes and are called at different times. _load() - * functions are called when the menu system is collecting arguments to pass - * to the callback functions defined for the menu item. _to_arg() functions - * are called when the menu system is generating links to related paths, such - * as the tabs for a set of MENU_LOCAL_TASK items. * * @section sec_render_tabs Rendering Menu Items As Tabs * You can also make groups of menu items to be rendered (by default) as tabs @@ -655,22 +541,20 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) { * $items['admin/config/system/foo'] = array( * 'title' => 'Foo settings', * 'type' => MENU_NORMAL_ITEM, - * // Page callback, etc. need to be added here. + * 'route_name' => 'foosettings' * ); * // Make "Tab 1" the main tab on the "Foo settings" page * $items['admin/config/system/foo/tab1'] = array( * 'title' => 'Tab 1', * 'type' => MENU_DEFAULT_LOCAL_TASK, - * // Access callback, page callback, and theme callback will be inherited - * // from 'admin/config/system/foo', if not specified here to override. + * // Route name would be inherited from 'admin/config/system/foo', if not + * // specified here to override. * ); * // Make an additional tab called "Tab 2" on "Foo settings" * $items['admin/config/system/foo/tab2'] = array( * 'title' => 'Tab 2', * 'type' => MENU_LOCAL_TASK, - * // Page callback and theme callback will be inherited from - * // 'admin/config/system/foo', if not specified here to override. - * // Need to add access callback or access arguments. + * 'route_name' => 'foo2', * ); * @endcode * @@ -688,67 +572,6 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) { * t(). If you require only the raw string to be output, set this to FALSE. * - description arguments: Arguments to send to t() or your custom callback, * with path component substitution as described above. - * - "page callback": The function to call to display a web page when the user - * visits the path. If omitted, the parent menu item's callback will be used - * instead. - * - "page arguments": An array of arguments to pass to the page callback - * function, with path component substitution as described above. - * - "access callback": A function returning TRUE if the user has access - * rights to this menu item, and FALSE if not. It can also be a boolean - * constant instead of a function, and you can also use numeric values - * (will be cast to boolean). Defaults to user_access() unless a value is - * inherited from the parent menu item; only MENU_DEFAULT_LOCAL_TASK items - * can inherit access callbacks. To use the user_access() default callback, - * you must specify the permission to check as 'access arguments' (see - * below). - * - "access arguments": An array of arguments to pass to the access callback - * function, with path component substitution as described above. If the - * access callback is inherited (see above), the access arguments will be - * inherited with it, unless overridden in the child menu item. - * - "theme callback": (optional) A function returning the machine-readable - * name of the theme that will be used to render the page. If not provided, - * the value will be inherited from a parent menu item. If there is no - * theme callback, or if the function does not return the name of a current - * active theme on the site, the theme for this page will be determined by - * either hook_custom_theme() or the default theme instead. As a general - * rule, the use of theme callback functions should be limited to pages - * whose functionality is very closely tied to a particular theme, since - * they can only be overridden by modules which specifically target those - * pages in hook_menu_alter(). Modules implementing more generic theme - * switching functionality (for example, a module which allows the theme to - * be set dynamically based on the current user's role) should use - * hook_custom_theme() instead. - * - "theme arguments": An array of arguments to pass to the theme callback - * function, with path component substitution as described above. - * - "file": A file that will be included before the page callback is called; - * this allows page callback functions to be in separate files. The file - * should be relative to the implementing module's directory unless - * otherwise specified by the "file path" option. Does not apply to other - * callbacks (only page callback). - * - "file path": The path to the directory containing the file specified in - * "file". This defaults to the path to the module implementing the hook. - * - "load arguments": An array of arguments to be passed to each of the - * wildcard object loaders in the path, after the path argument itself. - * For example, if a module registers path node/%node/revisions/%/view - * with load arguments set to array(3), the '%node' in the path indicates - * that the loader function node_load() will be called with the second - * path component as the first argument. The 3 in the load arguments - * indicates that the fourth path component will also be passed to - * node_load() (numbering of path components starts at zero). So, if path - * node/12/revisions/29/view is requested, node_load(12, 29) will be called. - * There are also two "magic" values that can be used in load arguments. - * "%index" indicates the index of the wildcard path component. "%map" - * indicates the path components as an array. For example, if a module - * registers for several paths of the form 'user/%user_category/edit/*', all - * of them can use the same load function user_category_load(), by setting - * the load arguments to array('%map', '%index'). For instance, if the user - * is editing category 'foo' by requesting path 'user/32/edit/foo', the load - * function user_category_load() will be called with 32 as its first - * argument, the array ('user', 32, 'edit', 'foo') as the map argument, - * and 1 as the index argument (because %user_category is the second path - * component and numbering starts at zero). user_category_load() can then - * use these values to extract the information that 'foo' is the category - * being requested. * - "weight": An integer that determines the relative position of items in * the menu; higher-weighted items sink. Defaults to 0. Menu items with the * same weight are ordered alphabetically. @@ -783,8 +606,6 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) { * Many shortcut bitmasks are provided as constants in menu.inc: * - MENU_NORMAL_ITEM: Normal menu items show up in the menu tree and can be * moved/hidden by the administrator. - * - MENU_CALLBACK: Callbacks simply register a path so that the correct - * information is generated when the path is accessed. * - MENU_SUGGESTED_ITEM: Modules may "suggest" menu items that the * administrator may enable. * - MENU_LOCAL_ACTION: Local actions are menu items that describe actions