Inheritance rules
Before diving into the various callbacks, we shall learn the simple but very powerful inheritance rules. We are using the parents of a given path for this, for example the parents of node/%/view are:
node/%
node
If a page callback is not defined for a path, then we look at the closest parent which has one and use it. If the page callback is inherited from a parent, then the page arguments, the file and the file path are inherited from the parent as a whole, but can be overwritten. Most of the time, is only required to overwrite the page arguments and nothing else.
If the page callback is defined for a given path, then there is not inheritance at all and the page arguments, file, and file path must be defined if they are required.
For example:
<?php
$items['admin/user/roles'] = array(
'title' => 'Roles',
'page callback' => 'drupal_get_form',
'page arguments' => array('user_admin_new_role'),
'file' => 'user.admin.inc'
);
$items['admin/user/roles/edit'] = array(
'title' => 'Edit role',
'page arguments' => array('user_admin_role'),
);
?>The above is a shorthand for:
<?php
$items['admin/user/roles'] = array(
'title' => 'Roles',
'page callback' => 'drupal_get_form',
'page arguments' => array('user_admin_new_role'),
'file' => 'user.admin.inc'
);
$items['admin/user/roles/edit'] = array(
'title' => 'Edit role',
'page callback' => 'drupal_get_form',
'page arguments' => array('user_admin_role'),
'file' => 'user.admin.inc',
);
?>Note that the 'page callback' and the 'file' are inherited from the parent whilst the 'page arguments' is defined (overwritten) for this path.
If you would have:
<?php
$items['admin/user/roles/edit'] = array(
'title' => 'Edit role',
);
?>then it would be a shorthand for:
<?php
$items['admin/user/roles/edit'] = array(
'title' => 'Edit role',
'page callback' => 'drupal_get_form',
'page arguments' => array('user_admin_new_role'),
'file' => 'user.admin.inc',
);
?>In this case the callback, the arguments and the file are inherited.
If you would have:
<?php
$items['admin/user/rules'] = array(
'title' => 'Access rules',
'page callback' => 'user_admin_access',
'file' => 'user.admin.inc',
);
$items['admin/user/rules/edit'] = array(
'title' => 'Edit rule',
'page callback' => 'user_admin_access_edit',
'file' => 'user.admin.inc',
);
?>In this case there is not inheritance because a 'page callback' is defined for the 'admin/user/rules/edit' path, so if the 'page arguments', 'file', and 'file path' are required, must defined in the menu item definition.
Inheritance for access callbacks was supported the same way as with page callbacks before Drupal 6.2. Since Drupal 6.2 however, access callbacks are only inherited for default local tasks, and not inherited otherwise (neither the callback nor the arguments).
In the general case (i.e. all cases except default local tasks), if neither 'access callback' nor 'access arguments' is set then access will be denied (the 'access callback' will be set to 0). If 'access callback' is not set but 'access arguments' is then http://api.drupal.org/api/function/user_access/6 will be used as the 'access callback', i.e. you don't have to provide the access callback on all menu items - only on those that need to use something other than the default user_access(). If 'access callback' is set but 'access arguments' is not then 'access arguments' defaults to the empty array.
To see how inheritance is built check the implementation of _menu_router_build
It's worth noting that when menu items define page callbacks, this disables inheritance, and it also disables any nesting that you might expect based on paths. In the last example above, the two menu items 'Access rules' (admin/user/rules) and 'Edit rule' (admin/user/rules/edit) will be peers in the Navigation menu, rather than one being nested under the other (as might be expected).
