Menu system
This chapter introduces the Drupal 7 menu system and describes how to to make forms and pages with content created using code accessible through menu routes.
Table of contents
Introduction
In this chapter will introduce the Drupal 7 menu system. The menu system facilitates site navigation by means of paths.
The Drupal 7 menu system drives both site navigation from a user perspective and the callback system that Drupal uses to respond to URLs passed from the browser.
The example in the previous chapter
demonstrated very simple use of Drupal's menu system by means
of hook_menu()
. In this chapter, we shall dig deeper
into the menu system.
The
convention is to place hook_menu()
and any access
functions in the project's .module
file, while all the
callbacks and their handlers go into a file with a name eding
with .admin.inc
. To get this file loaded when it is
needed, use the file
attribute in the array contructed
by hook_menu()
(see examples below). There is no need to
list it in the project's .info
-file.
You may put items into the existing menus on the website by means of the administrative GUI. To do so, navigate to
and click “add link” for the menu you want to modify.The menu system in Drupal 7 follows a simple hierarchy defined by
paths. Implementations of hook_menu()
define menu items
and assign them to paths (which should be unique). The menu system
aggregates these items and determines the menu hierarchy from the
paths.
For example, if the paths defined were:
a
,
a/b
,
e
,
a/b/c/d
,
f/g
,
a/b/h
, the menu system would form the structure:
- a
- a/b
- a/b/c/d
- a/b/h
- a/b
- e
- f/g
Modules that wants to display page content that can be addressed by
a URL in Drupal 7 must register a path for it. Registering a path
means defining a menu router entry for the page by implementing
hook_menu()
.
To define your menu router entry, first you will need to choose a path. All paths originate at your site's base URL.
Drupal 7 stores menu router items in a database table:
{menu_router}
. It does this because looking this up in
the database query is faster than searching through all
implementations of
hook_menu()
to find a match for the path.
To create a menu router item for a custom page,
hook_menu()
should return an associative array
defining the item.
Menu types
Your menu array may specify a menu type. The following types exist:
MENU_CALLBACK
: A hidden, internal callback, typically used for API calls.MENU_LOCAL_TASK
: A task specific to the parent item (e.g.node/52/edit
), usually rendered as a tab above the parent item.MENU_DEFAULT_LOCAL_TASK
: The “default” task. I.e. the task which is initially active. Every set of local tasks should provide one “default” task.MENU_LOCAL_ACTIONS
: An action specific to the parent, usually rendered as a link, rather than a tab.MENU_NORMAL_ITEM
: A “normal” menu item that appears in a menu and in breadcrumbs.MENU_SUGGESTED_ITEMS
: A normal menu item, hidden until enabled by an administrator. They act just asMENU_CALLBACK
until enabled, at which time they act likeMENU_NORMAL_ITEM
.
Source: Menu item types.
The MENU_NORMAL_ITEM
will by default show up in the
navigation menu, to add it to another manu, set it explictly:
function mymodule_menu() { $items['helloworld'] = array( 'title' => 'Hello world', 'page callback' => 'mymodule_custom', 'menu_name' => 'main-menu', 'access callback' => TRUE, 'type' => MENU_NORMAL_ITEM, ); return $items; }
Menu for a project
In addition to the menus that can be accessed through
or programatically added to a named menu, Drupal 7 projects can set up menus for the administrative pages for a project.To get a configuration menu linked to from the site's modules overview page,
you have to have its path assigned to the attribute configure
in the
project's .info
-file. Example:
configure = admin/config/people/example-menu
A project may create several menus, but there should only be one configuation menu.
The most common type of menu gives the function
drupal_get_form()
as the callback, and gives the name of the function to implement the
business logic for the form in the first page argument.
Below is an example of a menu to access an administrative menu with
two tabs. To add more tabs, just reuse the boilerplate for the last
item. If no tabs are required, only the first item
(MENU_NORMAL_ITEM
) is required.
/** * Implements hook_menu(). */ function mymodule_menu() { $items = array(); $items['admin/config/people/example-menu'] = array( 'title' => 'Example', 'description' => 'This description shows up on the configuration page.', 'page callback' => 'drupal_get_form', 'page arguments' => array('mymodule_tab1'), 'access callback' => TRUE, 'file' => 'mymodule.admin.inc', 'type' => MENU_NORMAL_ITEM, ); $items['admin/config/people/example-menu/not-shown] = array( 'title' => 'First tab', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 0, ); $items['admin/config/people/example-menu/second'] = array( 'title' => 'Second tab', // No description required. 'page callback' => 'drupal_get_form', 'page arguments' => array('mymodule_tab2'), 'access callback' => TRUE, 'file' => 'mymodule.admin.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 1, ); return $items; }
The MENU_DEFAULT_LOCAL_TASK
is the default tab shown
at the path pointed to by MENU_NORMAL_ITEM
. The path for
the MENU_DEFAULT_LOCAL_TASK
must be different
from the path for MENU_NORMAL_ITEM
, but it is not
displayed to the user.
Below are the boilerplate callback functions for the above menu.
/* * Menu callback: Provide first tab page for MyModule. * * @return array * Form. */ function mymodule_tab1() { $form['mymodule_msg'] = array( '#markup' => t('This is the first tab'), ); return $form; } /* * Menu callback: Provide second tab page for MyModule. * * @return array * Form. */ function mymodule_tab2() { $form['mymodule_msg'] = array( '#markup' => t('This is the second tab'), ); return $form; }
Custom callback
Instead of using the predefined callback function
drupal_get_form()
,
you may write a custom callback function. You will typically do this
if the menu does not link to a form, but to content you want to create
programmatically, rather than by using the GUI to create a node.
To pass arguments to the callback function, use
page arguments
.
The page arguments may be strings:
function mymodule_menu() { $items['helloworld'] = array( 'title' => 'Hello world', 'page callback' => 'mymodule_custom', 'page arguments' => array('wonderful', 'crazy'), 'access callback' => TRUE, 'file' => 'mymodule.admin.inc', 'type' => MENU_NORMAL_ITEM, ); return $items; }
A callback function that outputs the aguments:
function mymodule_custom($arg1, $arg2) { $content['raw_markup'] = array( '#type' => 'markup', '#markup' => '<p>Hello, ' . $arg1 . '! It is a ' . $arg2 . ' world.</p>', ); return $content; }
To pass the arguments as part of the path, use numeric
page arguments
:
function mymodule_menu() { $items['helloworld/%/%'] = array( 'title' => 'Hello world', 'page callback' => 'mymodule_custom', 'page arguments' => array(1,2), 'access callback' => TRUE, 'file' => 'mymodule.admin.inc', 'type' => MENU_NORMAL_ITEM, ); return $items; }
Given the same callback function, the following path will produce an identical output as the previous example.
/helloworld/wonderful/crazy
Access control
In the examples above, access callback is set to the
constant TRUE
, giving everyone access.
This is not recommended, as it would provide access to users that don't even have the access content
permission granted.
Instead, make sure access callback is a function (by default, it is:
user_access()
).
When it is a function you should also provide access arguments. This is an
array containing the arguments passed to the access callback function. Example:
function mymodule_menu() { $items['helloworld'] = array( 'title' => 'Hello world', 'page callback' => 'mymodule_custom', //'access callback' => 'user_access', 'access arguments' => array('access content'), 'type' => MENU_NORMAL_ITEM, ); return $items; }
As with other menu callbacks, the arguments must be a string or a
number. If it is a number, the value will be replaced with a value
taken from the path. If it is a string, the value will be the
name of the permission to check for (here: access content
).
The access callback function is a the name of a any function that is called to verify if the user has access to the menu callback. It should return either TRUE (the user is granted access) or FALSE (the user is denied access).
If
you don't want to specify permissions that are specific to your module, you may want
to consider reusing the following core permissions (hopefully the intended use is obvious):
“access content
” (can be given to all roles),
“administer site configuration
” (give to trusted roles only),
“administer users
” (give to trusted roles only).
Final word
[TBA]
Last update: 2019-04-08 [gh].