Upgrading your menu system from 5.x to 6.x
Cached part
This is very simple, a search-and-replace operation. Here is a list of changes:
- hook_menu() no longer takes any parameters (remove
$may_cache). - The value of
pathis the new index for$items. callbackbecomespage callbackcallback argumentsbecomespage argumentsaccessbecomesaccess callbackandaccess arguments.For example,
access => user_access('administer nodes')becomes'access callback' => 'user_access', 'access arguments' => array('administer nodes').However, the default for
'access callback'is'user_access'so you can leave that out.Complex access things must be moved to a function which can be called on runtime,
user_is_anonymousanduser_is_logged_inare useful helpers. See Access control for more.- The title and description arguments should not have strings wrapped in t(), because translation of these happen in a later stage in the menu system. This allows translation of menu items to any language required on a site, adapting to the language used on the page.
Non-cached part
Let's suppose you had
<?php
/**
* Implementation of hook_menu().
*/
function aggregator_menu($may_cache) {
if (!$may_cache) {
if (arg(0) == 'aggregator' && is_numeric(arg(2))) {
if (arg(1) == 'sources') {
$feed = aggregator_get_feed(arg(2));
if ($feed) {
$items[] = array('path' => 'aggregator/sources/'. $feed['fid'] .'/configure',
'title' => t('Configure'),
'callback' => 'drupal_get_form',
'callback arguments' => array('aggregator_form_feed', $feed),
'access' => user_access('administer news feeds'),
'type' => MENU_LOCAL_TASK,
'weight' => 1);
}
}
}
}
return $items;
}
?>this becomes
<?php
/**
* Implementation of hook_menu().
*/
function aggregator_menu() {
$items['aggregator/sources/%aggregator_feed/configure'] = array(
'title' => 'Configure',
'page callback' => 'drupal_get_form',
'page arguments' => array('aggregator_form_feed', 2),
// NOTE: as of Drupal 6.2, all menu items are *required* to have
// access control.
'access arguments' => array('administer news feeds'),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
return $items;
}
?>If we would use just a percent sign aggregator/sources/%/configure it'd match everything, including non-numeric values as well, like aggregator/sources/foo/configure. However, with %aggregator_feed we ask for a feed to be loaded based on the third argument and anything that's not a valid feed will lead to a 404.
About the use of wildcards, foreach() loops and MENU_ITEMs
In Drupal 5, we used to use foreach() loops to recursively declare several MENU_ITEMs or MENU_SUGGESTED_ITEMs.
In Drupal 6, we only need one entry in hook_menu() with the use of a proper wildcard. Additionally, you can create as many menu items (enabled or not) as you see fit with menu_link_save().
For a detailed discussion on this topic, see this issue.
Additional Run Once Code
Non menu code that was placed in hook_menu !$may_cache so that it could be run during initialisation, should now be moved to hook_init. Previously we called hook_init twice, once early in the bootstrap process, second just after the bootstrap has finished. The first instance is now called boot instead of init.
If you were using a loop in your menu code then you likely need menu_link_save.
A horribly complex example can be found in the menu of http://drupal.org/files/issues/akismet.d6-port.patch. It involves just about every possible path manipulation trick one could ever need for the path akismet/%akismet/%/'. $op, and then some. It passes the real page callback in $map[0] which is a bit of a hack, but is also quite useful.

Upgrading to version 6 menu system
Thanks,
To make the code comparable shouldn't the version 6 be:
$items['aggregator/sources/%aggregator_get_feed/configure'] = array(It blows my mind a bit that the 2 in:
'page arguments' => array('aggregator_form_feed', 2),
is the result of calling a function on the second argument, and so could be a different type (a whole feed) than was in the original URL.
Good on ya!!
PS you might want to put Drupal 6 somewhere in the title
Actually, they are assuming
Actually, they are assuming you would rename the function to 'aggregator_feed_load', as the '_load' part is automatically added to the loader function's name before calling. That should probably be noted as part of the process as it's a little bit misleading as to how the wildcarding works.
Adding MENU_LOCAL_TASK items for your node-type
If you need to add a MENU_LOCAL_TASK for a specific node-type, use a loader:
<?php
$items['node/%my_node_type/new_tab'] = array(
'title' => 'New Tab',
'page callback' => 'mycallback',
'page arguments' => array(1),
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK
)
...
function my_node_type_load($arg) {
$node = node_load($arg);
if($node->type == 'my_type')
return $node;
return FALSE;
}
?>
how to deal with if...else statements in menu_hook
IN one of the module i am trying to port from 5x to 6x. the menu_hook has the following colde
if ($may_cache)
{
code goes here
}
else
{
different code goes here
}
My question is since Drupal6 menu_hook doesnot take $may_cache parameters how can i write a if..else statement if i remove $may_cache from above? any suggestions would be greatly appreciated.
Regards,
judef