Thứ Bảy, 24 tháng 5, 2014

Creating a Menu Item [The Menu System]

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 wan 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 morcontrol 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 featurfor 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 specify otherwise, Drupal assumes that your page callback can be found inside your .modulfile.
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 dont 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 
Access denied” message if he or she tries to go thttp://example.com/?q=menufun.
/**
*   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, Ill 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 usin
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 paths 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  of the path, whatever that is.” We start counting at 0, so part  imenufun, 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 an %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 title “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_VISIBLE_IN_BREADCRUMB
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
000001
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 yo
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 hoo 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 commothings 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