The place to hook into the process is through the use of the menu hook in your module. This allows you to define
menu items that will be included in the router table. Let’s build an example module called menufun.mdule to experiment
with the menusystem. We’ll map the Drupal path menufun to the PHP function that we’ll write named menufun_hello().
First, we need a menufun.info file at sites/all/modules/custom/menufun/menufun.info:
name = Menu Fun
description = Learning about the menu system.
package = Pro Drupal Development core = 7.x
files[] = menufun.module
Then we need to create the sites/all/modules/custom/menufun/menufun.module file, which contains our hook_menu() implementation and the function we want to run.
<?php
/**
* @file
* Use this module to learn about Drupal's menu system.
*/
/**
* Implementation of hook_menu().
*/
function menufun_menu() {
$items['menufun'] = array( ‘title’ => ‘Greeting’,'page callback' => 'menufun_hello', 'access callback' => TRUE,
'type' => MENU_CALLBACK,);
return $items;}
In the foregoing code, you’ll see that we’ve created our menu ($items[‘menufun’]) by creating an array with three key/value pairs:
“title”: A required value that defines the untranslated title of the menu item
“page callback”: The function that will be called when the user visits the menu path “access callback”: Typically this
would contain a function that returns a Boolean value.
/**
* Page callback.
*/
function menufun_hello()
{
return t('Hello!');
}
Enabling the module at Modules causes the menu item to be inserted into the router table, so Drupal will now find and run our function when we go to http://example.com/?q=menufun, as shown in Figure 4-3.
The important thing to notice is that we are defining a path and mapping it to a function. The path is a Drupal path. We defined the path as the key of our $items array. We are using a path that is the same as the name of our module. This practice assures apristine URL namespace. However, you can define any path.
Figure 4-3. The menu item has enabled Drupal to find and run the menufun_hello() function.
Page Callback Arguments
Sometimes, you may wish to provide more information to the page callback function that is mapped to the path. First of all, any additional parts of the path are automatically passed along. Let’s change our
function as follows:
function menufun_hello($first_name = '', $last_name = ''
{
return t('Hello @first_name @last_name',array('@first_name' =>
$first_name, '@last_name' => $last_name));
}
Now if we go to http://example.com/?q=menufun/John/Doe, we get the output shown in Figure 4-4.
Figure 4-4. Parts of the path are passed along to the callback function.
Notice how each of the extra components of the URL was passed as a parameter to our callback
function. You can also define page callback arguments inside the menu hook by adding an optional
page arguments key to the $items array. Defining a page argument is useful because it allows you to gain more control over the parameters that are being passed to the callback function.
As an example, let’s update our menufun module by adding page arguments for our menu item:
function menufun_menu()
{
$items['menufun'] = array(
'title' => 'Greeting', 'page
callback' => 'menufun_hello
',
'page arguments' => array
('Jane', 'Doe'), 'access
callback' => TRUE, 'type' =>
MENU_CALLBACK,);
return $items;}
After Drupal has followed all the instructions that are explicitly given for page arguments, any remaining path arguments that are unaccounted for also get sent into the page callback function as extra parameters, using PHP’s parameter
over loading feature for functions. The arguments from the URL are still available; to access them, you would change the
function signature of your callback to add parameters from the URL. So with our revised menu item, the following
function signature would result in $first_namebeing Jane (from the first item in the page arguments array), and
$last_name being Doe (from the second item in the page arguments array).
function menufun_hello($first_name = '', $last_name = '') {...}
Let’s test this by putting Jane Doe in the page arguments and John Doe in the URL and seeing which appears. Going to http://example.com/?q=menufun/John/Doe will now yield the results shown in Figure 4-5 (if you’re not getting
those results, you forgot to rebuild your menus).
Figure 4-5. Passing and displaying arguments to the callback function
If you wanted to use the values passed in
the URL, you could up date the page callback function, use the values as
follows
function menufun_hello($first_name = '', $last_name = ‘’)
{
return t('Hello @first_name @last_name from @from_first_name @from_last_name',
array('@first_name' => $first_name, '@last_name' => $last_name));
}
Update your version, clear cache, and give it a try to see the results when you use http://example.com/?q=menufun.
Page Callbacks in Other Files
If you don’t specify otherwise, Drupal assumes that your page callback can be found inside your .module file.
In Drupal 7, many modules are split up into multiple files that get conditionally loaded so that a minimum amount of code is loaded on each pagerequest. The file key (e.g., ‘file’ => ‘menufun_greetings.inc’) of a menu item is used to specify the name of the file that contains the callback function.
As an example, I’ll update the menufun.module hook_menu() function to include the name of the file where the
new callback function resides. The following code adds ‘file’ => ‘menufun_greeting’ to the item array. I also
changed the page callback tomenufun_greeting just to demonstrate that the callback isn’t using the function
that already exists in the menufun.module file.
/**
* Implementation of hook_menu().
*/
function menufun_menu() {
$items['menufun'] = array( 'title' => 'Menu Fun','page callback' => 'menufun_greeting', 'file' => 'menufun_
greeting.inc','page arguments' => array('Jane', 'Doe'), 'access callback' => TRUE,'type' => MENU_CALLBACK,);return $items;
}
Next I’ll create a new file named menu
fun_greeting.inc in the menu
fun directory with the following code.
<?php
function menufun_greeting($first_name = '', $last_name = '', $from_first_name='',
$from_last_name='')
{
return t('Hello @first_name @last_name from @from_first_name @from_last_name',
array('@first_name' => $first_name, '@last_name' => $last_name, '@from_first_name' =>
$from_first_name, '@from_last_name' => $from_last_name));
}
Save both files, clear your cache, and test the revised approach. You should get exactly the same results, only this time the callback function resides externally from the .module file.
Adding a Link to the Navigation Block
In the menufun example, we declared that our menu item was of type MENU_CALLBACK. By changing the type to
MENU_NORMAL_ITEM, we indicate that we don’t simply want to map the path to a callback function; we also want
Drupal to include it in amenu.
function menufun_menu()
{
$items['menufun'] = array( 'title' => 'Menu Fun','page callback' => 'menufun_greeting', 'file' => 'menufun_
greeting.inc','page arguments' => array('Jane', 'Doe'), 'access callback' => TRUE,'type' => MENU_NORMAL_
ITEM,);
return $items;
}
The menu item would now show up in the navigation block, as shown in Figure 4-6.
Figure 4-6. The menu item appears in the navigation block.
If we don’t like where it is placed, we can move it up or down by decreasing or increasing its weight.
Weight is another key in the menu item definition:
function menufun_menu()
{
$items['menufun'] = array( 'title' => 'Greeting','page callback' => 'menufun_hello', 'page arguments' =>
array('Jane', 'Doe'), 'access callback' => TRUE,'weight' => -1,);
return $items;
}
The effect of our weight decrease is shown in Figure 4-7. Menu items can also be relocated without changing
code by using the menu administration tools, located at Structure > Menus (the menu module must be enabled
for these tools to appear).
Figure 4-7. Heavier menu
items sink down in the navigation block.
Menu Nesting
So far, we’ve defined only a single static menu item. Let’s add a second and another callback to go with it:
function menufun_menu() {
$items['menufun'] = array( 'title' => 'Menu Fun','page callback' => 'menufun_greeting', 'file' => 'menufun_
greeting.inc','page arguments' => array('Jane', 'Doe'), 'access callback' => TRUE,'type' => MENU_NORMAL_
ITEM, 'weight' => '-1',);
$items['menufun/farewell'] = array( 'title' => 'Farewell','page callback' => 'menufun_farewell', 'file' => 'menufun_greeting.inc','access callback' => TRUE, 'type' => MENU_NORMAL_ITEM,);
return $items;}
Next in the menufun_greeting.inc file, add the page callback function menufun_farewell, as shown here:
function menufun_farewell() { return t('Goodbye');}
After updating the module, remember to clear cache. Drupal will notice that the path of the second menu item (menufun/
farewell) is a child of the first menu item’s path (menufun). Thus, when rendering (transforming to HTML) the menu,
Drupal will indent the second menu, as shown in Figure 4-8. It hasalso correctly set the breadcrumb trail at the top
of the page to indicate the nesting. Of course, a theme may render menus or breadcrumb trails however the designer
wishes.
Figure 4-8. Nested menu
Access Control
In our examples so far, we’ve simply set the access callback key of the menu item to TRUE, meaning that any
one can access our menu. Usually, menu access is controlled by defining permissions inside the module using
hook_permission() and testing thosepermissions using a function. The name of the function to use is defined in theaccess callback key of the menu item and is typically user_access. Let’s define a permission called receive
greeting; if a user does not have a role that has been granted this permission, the user will receive an “
/**
* Implementation of hook_permission()
*/
function menufun_permission() { return array('receive greeting' => array(‘title' => t('Receive a greeting'),'description' => t('Allow users receive a greeting message'),),);}
/**
* Implementation of hook_menu().
*/
function menufun_menu() {
$items['menufun'] = array( 'title' => 'Menu Fun','page callback' => 'menufun_greeting', 'file' => 'menufun_greeting.inc','page arguments' => array('Jane', 'Doe'), 'access callback' => 'user_access','access
arguments' => array('receive greeting'), 'type' => MENU_NORMAL_ITEM,'weight' => '-1',);
$items['menufun/farewell'] = array( 'title' => 'Farewell','page callback' => 'menufun_farewell', 'file' =>
'menufun_greeting.inc', 'access callback' => 'user_access','access arguments' => array('receive greeting'), 'type' => MENU_NORMAL_ITEM,);
return $items;}
In the preceding code, access will be determined by the result of a call to user_access ('receive greeting'). In
this way, the menu system serves as a gatekeeper, determining which paths may be accessed and which will be
denied based on the user’s role.
Child menu items do not inherit access callbacks and access arguments from their parents. The access arguments key must be defined for every menu item. The access callback key must be defined only if it differs from user_
access. The exception to thisis any menu item of type MENU_DEFAULT_LOCAL_TASK, which will inherit the
parent access callback and access arguments, though for clarity it is best to explicitly define these keys even
for default local tasks.
Title Localization
and Customization
There are two types of titles, static and dynamic. Static titles are created by assigning a value to the “title” key.Dynamic titles are created through a title callback function. Drupal automatically translates static title values for
you, so there’s no need to wrap the title with t(). If you use dynamic titles, through a title call back function, youare responsible for doing the translation within your callback.
'title' => t('Greeting') // No! don't use t() in menu item titles or descriptions.title callback key:
Defining a Title Callback
Titles may be created dynamically at runtime through the use of a title callback. The following example demonstrates the use of a title callback function that sets the value of the title to the current date and time. Since
I’m using a title callback, the function isresponsible for performing the translation before the value is returned. To perform the translation, I’ll wrap the value returned with t().
function menufun_menu() {
$items['menufun'] = array( 'title' => 'Greeting','title callback' => 'menufun_title', 'description' => 'A salutation.', 'page callback' => 'menufun_hello', 'access callback' => TRUE,);
return $items;
}
/**
* Page callback.
*/
function menufun_hello() { return t('Hello!');
}
/**
* Title callback.
*/
function menufun_title() {
$now = format_date(time());
return t('It is now @time', array('@time' => $now));}
As shown in Figure 4-9, setting of the menu item title at run time can be achieved through the use of a
custom title callback.
Figure 4-9. Title callback setting the title of a menu item
But what if we want to decouple the menu item title from the title of the page? Easy we set the page title using
drupal_set_title():
function menufun_title() {
drupal_set_title(t('The page title'));
$now = format_date(time());
return t('It is now @time', array('@time' => $now));
}
This results in one title for the page and another for the menu item, as shown in Figure 4-10.
Figure 4-10. Separate titles for the menu item and the page
Wildcards in Menu Items
So far, we have been using regular Drupal path names in our menu items, names like menufun and menufun/farewell. But Drupal often uses paths like user/4/track or node/15/edit, where part of the path is dynamic.
Let’s look at how that works.
Basic Wildcards
The % character is a wildcard character in Drupal menu items, meaning the value is determined at runtime by
the value found in the position of the URL that contains the wildcard. Here’s a menu item that uses a
wildcard:
function menufun_menu()
{
$items['menufun/%'] = array( 'title' => 'Hi','page callback' => 'menufun_hello', 'page arguments' =>
array(1), 'access callback' => TRUE,);
return $items;}
This menu item will work for the Drupal paths menufun/hi, menufun/foo/bar, menufun/123, and menufun/
file.html. It will not work for the path menufun; a separate menu item would have to be written for that path because it consists of only onepart, and the wildcard menufun/% will match only a string with two parts. Note that although % is often used to designate a number (as in user/%/edit for user/2375/edit), it will match
any text in that position.
Wildcards and Page Callback Parameters
A wildcard at the end of the menu path does not interfere with the passing of additional parts of the URL to the page callback, because the wildcard matches only up to the next slash. Continuing with our example of the menufun/%
path, the URLhttp://example.com/?q=menufun/foo/Fred would have the string foo matched by the wildcard,
and the last portion of the path (Fred) would be passed as a parameter to the page callback.
Using the Value of a Wildcard
To use the part of the path that matched, specify the number of the path’s part in the page arguments key:
function menufun_menu()
{$items['menufun/%/bar/baz'] = array( 'title' => 'Hi','page callback' => 'menufun_hello', 'page
arguments' => array(1), // The matched wildcard.'access callback' => TRUE,);
return $items;}
/**
* Page callback.
*/
function menufun_hello($name = NULL) {
return t('Hello. $name is @name', array('@name' => $name));
}
The parameters received by our page callback function menufun_hello() will be as shown in Figure 4-11.
Figure 4-11. The first parameter is from the matched wildcard.
The first parameter, $name, is being passed via the page callback. The entry array(1) for the page callback
means, “please pass part 1 of the path, whatever that is.” We start counting at 0, so part 0 is menufun, part 1 is whatever the wildcard matched,part 2 would be bar, and so on. The second parameter, $b, is being passed
because of Drupal’s behavior of passing the portion of the path beyond the Drupal path as a parameter (see“PageCallback Arguments” earlier in this chapter).
Wildcards and Parameter Replacement
In practice, parts of a Drupal path are generally used to view or change an object, such as a node or a user. For example, the path node/%/edit is used to edit a node, and the path user/% is used to view information about a user by user ID. Let’s take a look atthe menu item for the latter, which can be found in the hook_menu() implementation in modules/user/user.module. The corresponding URL that this path matches would be something like
http://example.com/?q=user/2375. That’s the URL you would click to see the “My account” page on a
Drupal site.
$items['user/%user_uid_only_optional'] = array ( 'title' => 'My account','title callback' =>'user_page_title','title arguments' => array(1), 'page callback' => 'user_view_page', 'page arguments' => array(1),
'access callback' => 'user_view_access', 'access arguments' => array(1),'weight' => -10, 'menu_name' => 'user-menu',);
When Drupal creates the menu using user/%user_uid_only_optional, it replaces the %user_uid_only_optional using the process as described below:
1. In the second segment, match the string after the % and before the next possible slash. In this case,
the string would be user_uid_optional.
2. Append _load to the string to generate the name of a function. In this case, the name of the function is
user_uid_optional_load.
3. Call the function and pass it, as a parameter, the value of the wildcard in the Drupal path. So if the URL ishttp://example.com/?q=user/2375, the Drupal path is user/2375, and the wildcard matches the second
segment, which is2375. So a call is made to user_uid_optional_load('2375').
4. The result of this call is then used in place of the wildcard. So when the title callback is called with the
title arguments of array(1), instead of passing part 1 of the Drupal path (2375), we pass the result of the call touser_uid_optional_load('2375'), which is a user object. Think of it as a portion of the Drupal path being replaced by the object it represents.
5. Note that the page and access callbacks will also use the replacement object. So in the previous menu
item, user_view_access() will be called for access and user_view() will be called to generate the page content,
and both will be passed the user object for user 2375.
Passing Additional Arguments to the Load Function
If additional arguments need to be passed to the load function, they can be defined in the load arguments key. Here’s an example from the node module: the menu item for viewing a node revision. Both the node ID and theID of the revision need to bepassed to the load function, which is node_load().
$items['node/%node/revisions/%/view'] = array( 'title' => 'Revisions','load arguments' => array(3),
'page callback' => 'node_show', 'page arguments' => array(1, TRUE),'access callback' => '_node_revision_access', 'access arguments' => array(1),);
The menu item specifies array(3) for the load arguments key. This means that in addition to the wildcard value for the node ID, which is passed automatically to the load function as outlined previously, a single additional parameter will be passed tothe load function, since array(3) has one member—that is, the integer 3. As you saw
in the “Using the Value of a Wildcard” section, this means that the part of the path in position 3 will be used. The position and path arguments for the example URLhttp://example.com/?q=node/56/revisions/4/view
are shown in Table 4-2.
Table 4-2. Position and Arguments for Drupal Path node/%node/revisions/%/view
When Viewing the Page http://example.com/?q=node/56/revisions/4/view
|
||
Position
|
Argument
|
Value from URL
|
0
|
node
|
node
|
1
|
%node
|
56
|
2
|
revisions
|
revisions
|
3
|
%
|
4
|
4
|
view
|
view
|
Thus, defining the load arguments key means that the call node_load('56', '4') will be made instead of node_load('56').
When the page callback runs, the load function will have replaced the value '56' with the loaded node object, so the
page callback call will be node_show($node, NULL, TRUE).
Special, Predefined Load Arguments: %map and %index
There are two special load arguments. The %map token passes the current Drupal path as an array. In the preceding
example, if %map were passed as a load argument, its value would be array('node', '56', 'revisions', '4', 'view'). The
values of the map can bemanipulated by the load function if it declares the parameter as a reference. So for the preceding
example, the token’s value would be 1 because the wildcard is at position 1, as shown in Table 4-2.
Building Paths from Wildcards Using to_arg() Functions
Recall that I said that Drupal cannot produce a valid link from a Drupal path that contains a wildcard, like user/%(after all, how would Drupal know what to replace the % with)? That’s not strictly true. We can define a helper
function that produces areplacement for the wildcard that Drupal can then use when building the link. In the “My
account” menu item, the path for the “My account” link is produced with the following steps:
1. The Drupal path is originally user/%user_uid_optional.
2. When building the link, Drupal looks for a function with the name user_uid_optional_to_arg(). If this function is not defined, Drupal cannot figure out how to build the path and does not display the link.
3. If the function is found, Drupal uses the result of the function as a replacement for the wildcard in the link. The
user_uid_optional_to_arg() function returns the user ID of the current user, so if you are user 4, Drupal connects the
“My account” link to http://example.com/?q=user/4.
The use of a to_arg() function is not specific to the execution of a given path. In other words, the to_arg() function is run
during link building on any page, not the specific page that matches the Drupal path of a menu item. The “My account”
link is shown on all pages, not just when the page http://example.com/?q=user/3 is being viewed.
Special Cases for Wildcards and to_arg() Functions
The to_arg() function that Drupal will look for when building a link for a menu item is based on the string following the
wildcard in the Drupal path. This can be any string, as in this example:
/**
* Implementation of hook_menu().
*/ function_menufun_menu() {
$items['menufun/%a_zoo_animal'] = array( 'title' => 'Hi','page callback' => 'menufun_hello', 'page arguments' => array(1),'access callback' => TRUE, 'type' => MENU_NORMAL_ITEM, 'weight' => -10);
return $items;
}
function menufun_hello($animal) { return t(“Hello $animal”);}
function a_zoo_animal_to_arg($arg) {
// $arg is '%' since it is a wildcard
// Let's replace it with a zoo animal.
return 'tiger';}
This causes the link “Hi” to appear in the navigation block. The URL for the link is http://example.com/?q=menufun/tiger. Normally, you would not replace the wildcard with a static string as in this simple example. Rather, the
to_arg() function would produce something
dynamic, like the uid of the current user or the nid of the current node.
Altering Menu Items from Other Modules
When Drupal rebuilds the menu_router table and updates the menu_link tables (for example, when a new module is enabled),
modules are given a chance to change any menu item by implementing hook_menu_alter(). For example, the “Log off” menu item
logs out the current user by calling user_logout(), which destroys the user’s session and then redirects the user to the site’s home
page. The user_logout() function lives in modules/user/user.pages.inc, so the menu item for theDrupal path has a file key defined.
So normally Drupal loads the file modules/user/user.pages.inc and runs the user_logout() page callback when a user clicks the
“Log out” link from the navigation block. Let’s change that to redirect users who are logging out to drupal.org.
/**
* Implementation of hook_menu_alter().
*
* @param array $items
* Menu items keyed by path.
*/
function menufun_menu_alter(&$items) {
// Replace the page callback to 'user_logout' with a call to
// our own page callback.
$items['logout']['page callback'] = 'menufun_user_logout';
$items[‘logout’][‘access callback’] = ‘user_is_logged_in’;
// Drupal no longer has to load the user.pages.inc file
// since it will be calling our menufun_user_logout(), which
// is in our module -- and that's already in scope.unset($items['logout']['file']);}
/**
* Menu callback; logs the current user out, and redirects to drupal.org.
* This is a modified version of user_logout().
*/
function menufun_user_logout() { global $user;
watchdog('menufun', 'Session closed for %name.', array('%name' => $user->name));
// Destroy the current session: session_destroy();
// Run the 'logout' operation of the user hook so modules can respond
// to the logout if they want to.
module_invoke_all('user', 'logout', NULL, $user);
// Load the anonymous user so the global $user object will be correct
// on any hook_exit() implementations.
$user = drupal_anonymous_user();
}
Before our hook_menu_alter() implementation ran, the menu item for the logout path looked like this:
array('access callback' => 'user_is_logged_in', 'file'
=> 'user.pages.inc', 'module'
=> 'user', 'page callback' => 'user_logout', 'title'
=> 'Log out', 'weight' => 10,)
And after we have altered it, the page callback is now set to menufun_user_logout:
array('access callback' => 'user_is_logged_in', 'module' => 'user','page callback' => 'menufun_user_logout',
'title' => 'Log out','weight'
=> 10,)
Altering Menu Links from Other Modules
When Drupal saves a menu item to the menu_link table, modules are given a chance to change the link by
implementing hook_menu_link_alter(). Here is how the “Log out” menu item could be changed to be titled “Sign off.”
/**
* Implements hook_menu_link_alter().
*
* @param $item
* Associative array defining a menu link as passed into menu_link_save()
*/
function menufun_menu_link_alter(&$item) {
if ($item['link_path'] == 'user/logout') {
$item['link_title'] = 'Sign off';}}
This hook should be used to modify the title or weight of a link. If you need to modify other properties of a menu item,
such as the access callback, use hook_menu_alter() instead.
Kinds of Menu Items
When you are adding a menu item in the menu hook, one of the possible keys you can use is the type. If you do notdefine a type, the default type MENU_NORMAL_ITEM will be used. Drupal will treat your menu item differently
according to the typeyou assign. Each menu item type is composed of a series of flags, or attributes (see includes/
menu.inc). Table 4-3 lists the menu item type flags.
Table 4-3. Menu Item Type Flags
|
||||||
Binary
|
Hexadecimal
|
Decimal
|
Constant
|
Description
|
||
000000000001 0x00
|
01
|
1
|
MENU_IS_ROOT
|
Menu item is the root of
the menu tree
|
||
000000000010 0x00
|
02
|
2
|
MENU_VISIBLE_IN_TREE
|
Menu item is visible in the
menu tree
|
||
000000000100 0x00
|
04
|
4
|
|
Menu item is visible in the
breadcrumb
|
||
000000001000 0x00
|
08
|
8
|
MENU_LINKS_TO_PARENT
|
Menu item links back to
its parent
|
||
000000100000 0x00
|
20
|
32
|
MENU_MODIFIED_BY_ADMIN
|
Menu item can be
modified by administrator
|
||
000001000000 0x00
|
40
|
64
|
MENU_CREATED_BY_ADMIN
|
Menu item
was created by administrarator
|
||
000010000000 0x00
|
80
|
128
|
MENU_IS_LOCAL_TASK
|
Menu item is a local task
|
||
000100000000 0x01
|
00
|
256
|
MENU_IS_LOCAL_ACTION
|
Menu item is a local action
|
For example, the constant MENU_NORMAL_ITEM (define('MENU_NORMAL_ITEM', MENU_VISIBLE_IN_TREE |
MENU_VISIBLE_IN_BREADCRUMB) has the flags MENU_VISIBLE_IN_TREE and MENU_VISIBLE_IN_
BREADCRUMB, as shown in Table 4-4.
Table 4-4. Flags of the Menu Item Type MENU_NORMAL_ITEM
|
|
Binary
|
Constant
|
0000010
|
MENU_VISIBLE_IN_TREE
|
000000000100
|
MENU_VISIBLE_IN_BREADCRUMB
|
000000000110
|
MENU_NORMAL_ITEM
|
Therefore, MENU_NORMAL_ITEM has the following flags: 000000000110.
Table 4-5 shows the available menu item typesand the flags they express.
*This constant is created with an additional bitwise or with 0x0010.
So which constant should you use when defining the type of your menu item? Look at Table 4-5 and see
which flags you want enabled, and use the constant that contains those flags. For a detailed description of each constant, see the comments inincludes/menu.inc. The most commonly used are MENU_CALLBACK, MENU_LOCAL_TASK, and MENU_DEFAULT_LOCAL_TASK. Read on for details.
Common Tasks
This section lays out some typical approaches to common problems confronting developers when working with menus.
Assigning Callbacks Without Adding a Link to the Menu
Often, you may want to map a URL to a function without creating a visible menu item. For example, may
be you have a JavaScript function in a web form that needs to get a list of states from Drupal, so you
need to wire up a URL to a PHP function but haveno need of including this in any navigation menu. You can do this by assigning the MENU_CALLBACK type to your menu item, as in the first example in this
chapter.
Displaying Menu Items As Tabs
A callback that is displayed as a tab is known as a local task and has the type MENU_LOCAL_TASK or MENU_DEFAULT_LOCAL_TASK. The title of a local task should be a short verb, such as “add” or
“list.” Local tasks usually act on some kind ofobject, such as a node, or user. You can think of a local
task as being a semantic declaration about a menu item, which is normally rendered as a tab—similar to
the way that the <strong> tag is a semantic declaration and is usually rendered as boldfaced text.
Local tasks must have a parent item in order for the tabs to be rendered. A common practice is to assign a callback to a root path like milkshake, and then assign local tasks to paths that extend that path, like milkshake/prepare, milkshake/drink, and soforth. Drupal has built-in theming support for two levels of tabbed local tasks. (Additional levels are supported by the underlying system, but your theme would have to provide support for displaying these additional levels.)
The order in which tabs are rendered is determined by alphabetically sorting on the value of title for each menu item. If this order is not to your liking, you can add a weight key to your menu items, and they will be sorted by weight instead. The following example shows code that results in two main tabs and two subtabs under the default local task. Create sites/all/modules/custom/milkshake/milkshake.info as follows
name = Milkshake
description = Demonstrates menu local tasks.
package = Pro Drupal Development core = 7.x
files[] = milkshake.module
Then enter the following for sites/all/modules/custom/milkshake/milkshake.module:
<?php
/**
* @file
* Use this module to learn about Drupal's menu system,
* specifically how local tasks work.
*/
/**
* Implements hook_menu().
*/
function milkshake_menu() {$items['milkshake'] = array( 'title' => 'Milkshake flavors', 'access
arguments' => TRUE,'page callback' => 'milkshake_overview', 'type' => MENU_NORMAL_ITEM,);
$items['milkshake/list'] = array( 'title' => 'List flavors', 'access arguments' => TRUE, 'type' =>
MENU_DEFAULT_LOCAL_TASK, 'weight' => 0,);
$items['milkshake/add'] = array( 'title' => 'Add flavor', 'access arguments' => TRUE,'page callback' => 'milkshake_add', 'type' => MENU_LOCAL_TASK,'weight' => 1,);
$items['milkshake/list/fruity'] = array( 'title' => 'Fruity flavors','access arguments' => TRUE, 'page callback' => 'milkshake_list','page arguments' => array(2), // Pass 'fruity'.'type' => MENU_LOCAL_TASK,);
$items['milkshake/list/candy'] = array( 'title' => 'Candy flavors','access arguments' => TRUE,
'page callback' => 'milkshake_list','page arguments' => array(2), // Pass 'candy'.
'type' => MENU_LOCAL_TASK,);
return $items;}
function milkshake_overview() {$output = t('The following flavors are available...');
// ... more code here return $output;}
function milkshake_add()
{
return t('A handy form to add flavors might go here...');
}
function milkshake_list($type)
{return t('List @type flavors', array('@type' => $type));}
Figure 4-12 shows the tabbed interface.
Figure 4-12. Local tasks and tabbed menus
Hiding Existing Menu Items
Existing menu items can be hidden by changing the hidden attribute of their link item. Suppose you want to remove the “Create content” menu item for some reason. Use our old friend hook_menu_link_alter():
/**
* Implements hook_menu_link_alter().
*/
function menufun_menu_link_alter(&$item) {
// Hide the Create content link.
if ($item['link_path'] == 'node/add') {
$item['hidden'] = 1;
}
}
Using menu.module
Enabling Drupal’s menu module provides a handy user interface for the site administrator to customize existing
menus such as the navigation or main menus, and to add new menus. When the menu_rebuild() function in
includes/menu.inc is run, the datastructure that represents the menu tree is stored in the database. This happens when you enable or disable modules or otherwise mess with things that affect the composition of the menu tree. The data is saved into the menu_router table of the database, and theinformation about links is stored in the menu_links table.
During the process of building the links for a page, Drupal first builds the tree based on path information
received from modules’ menu hook implementations and stored in the menu_router table, and then it overlays
that information with the menu information from the database. This behavior is what allows you to use menu.module to change the parent, path, title, and description of the menu tree— you are not really changing the
underlying tree; rather, you are creating data that is then over
laid on top of it.
Common Mistakes
You’ve just implemented the menu hook in your module, but your callbacks aren’t firing, your menus aren’t showing up, or things just plain aren’t working. Here are a few common things to check:
• Have you set an access callback key to a function that is returning FALSE?
• Did you forget to add the line return $items; at the end of your menu hook?
• Did you accidentally make the value of access arguments or page arguments a string instead of an array?
• Have you cleared your menu cache and rebuilt the menu?
• If you’re trying to get menu items to show up as tabs by assigning the type as
MENU_LOCAL_TASK, have you assigned a parent item that has a page callback?
• If you’re working with local tasks, do you have at least two tabs on a page (this is required for them to appear)?
Summary
After reading this chapter, you should be able to
• Map URLs to functions in your module or other modules or .inc files.
• Understand how access control works.
• Understand how wildcards work in paths.
• Create pages with tabs (local tasks) that map to functions.
• Modify existing menu items and links programmatically.
For further reading, the comments in menu.inc are worth checking out.
Also, see http://api.drupal.org/?q=api/group/menu/7.
Không có nhận xét nào:
Đăng nhận xét