Menu Local Tasks - Tutorial?

mcunliffe - February 24, 2007 - 15:58

I have read all the documentation I can find on hook_menu(). I understand the normal menu system fine, but somehow I just can't get it through my thick skull how to implement the local tasks.

Is there a good online tutorial that someone could point me to? Or could someone please explain this in simple terms from beginning to end. The regular menu system is very logical, I'm just not sure what I'm missing here. Every built-in module seems a little to complicated for me to understand.

- Martin

Brief explanation

mdixoncm - February 24, 2007 - 17:12

2 simple steps to understanding local tasks.

1. DONT PANIC.
2. Steal the code you need from somewhere else ... like the user module for example.

Local tasks are displayed in the tabs across the top of the page. A good example of an implementation of local tasks is the login screen - by default you see 3 tabs login, register and forgot password.

The code for this is below

    $items[] = array('path' => 'user/login', 'title' => t('log in'),
      'callback' => 'user_login', 'type' => MENU_DEFAULT_LOCAL_TASK);
    $items[] = array('path' => 'user/register', 'title' => t('register'),
      'callback' => 'user_register', 'access' => $user->uid == 0 && variable_get('user_register', 1), 'type' => MENU_LOCAL_TASK);
    $items[] = array('path' => 'user/password', 'title' => t('request new password'),
      'callback' => 'user_pass', 'access' => $user->uid == 0, 'type' => MENU_LOCAL_TASK);

So you can see that everything is exactly like defining a normal menu item, but just by setting the type to be MENU_LOCAL_TASK drupal will display the menu items as tabs.

Not sure if thats such a good explanation - but hopefully the code will paint a thousand words :)

cool cool,

Mike

Like books? Check out booktribes the new (Drupal based) community for book lovers
from Computerminds

Follow Up

mcunliffe - March 1, 2007 - 01:12

Thanks for the input, it allowed me to make some great strides. A couple of follow up questions if I may...

(BTW, I have read and read and read the documentation, but I'm getting lost in it.)

1. When using the standard view/edit node screen [xxx_form()], is it possible to add another local task? It doesn't really appear to be working for me if it does.

2. If the above is not possible, I figure I can make up my own custom forms, etc. In this case then, how do I determine what action is being taken on the form. How do I know if it's a new record, an edit, or a deletion? This may sound a little naive but I'm also not sure where to put the code to execute the proper action.

Any help would be greatly appreciated.

- Martin

...

mooffie - March 1, 2007 - 04:55

When using the standard view/edit node screen [xxx_form()], is it possible to add another local task? It doesn't really appear to be working for me if it does.

Yes, it's possible. You use the same code mdixoncm provided, but the trick is to find out the correct path. Let's say we want to add a 'bozo' tab besides the 'view' and 'edit' ones. Drupal knows tabs are siblings by examining their paths. So the 'bozo' tab must have a 'node/1234/bozo' path for Drupal to know it's a sibling to 'view' (node/1234) and 'edit' (node/1234/edit).

Here's how to do it:

function mymodule_menu($may_cache) {

  $items = array();

  if ($may_cache) {
    // ...
  } else {
    $items[] = array(
       'path'     => 'node/'. arg(1). '/bozo',
       'title'    => t('Bozo this node'),
       'callback' => 'mymodule_bozo',
       'type'     => MENU_LOCAL_TASK,
    );
  }

  return $items;
}

function mymodule_bozo() {
  return "Your node has been bozoed successfully!";
}

Note the arg(1) thing. It extracts the node ID from the URL. If we're browsing node #234, our bozo 'path' will be 'node/234/bozo'.

I'm also not sure where to put the code to execute the proper action

Web programming is event-based. It's not the old-style programming we learned at high-school, where we were the ones to hand control to each procedure (aka 'function') ourselves. Event-based programming is somewhat different.

And we don't program the application from scratch. We use some framework. Drupal is such a framework. There are others as well.

Don't fret when you don't understand code you see. You're not supposed to understand it as long as you haven't learned the basics of the Drupal framework.

I'm also not sure where to put the code to execute the proper action

let's return to my code above. When I handed the $items array to Drupal, I described to him what piece of code it should hand control to when the 'node/123/bozo' path is accessed. That's event-based programming. It's different from traditional programming.

Start at the beginning. the handbooks contain some tutorials.

Thank you.

mcunliffe - March 2, 2007 - 03:43

Thank you moofie.

This has provided that last little bit of understanding that I needed. After your additional notes and some more reading I've got exactly what I needed now. Thank you for all your time.

- Martin

I am not able to add local tasks

cpp - April 26, 2007 - 08:06

Hi,
I read your reply I follow the same instructions. Still local tasks are not displayed.
code in mymodule_menu

$items[] = array(
'path' => 'node/add/edit',
'title' => t('Bozo this node'),
'callback' => 'mymodule_bozo',
'type' => MENU_IS_LOCAL_TASK,
);

I check in menu.inc following condition is not returning true.

menu.inc
if (($_menu['items'][$mid]['type'] & MENU_IS_LOCAL_TASK) && _menu_item_is_accessible($mid)) {

Can u please help me

...

mooffie - April 26, 2007 - 12:59

'path' => 'node/add/edit',

What are you trying to achieve here?
'node/add/edit' seems weird to me.

It is eg

cpp - April 26, 2007 - 13:19

I want to give path for different modules like amazon search

Yeah but why would you want

Chill35 - April 30, 2007 - 02:22

Yeah but why would you want to ADD and EDIT content at the same time ? It doesn't make sense.

Caroline
A coder's guide to file download in Drupal
Who am I | Where are we
11 heavens

Adding tabs

justineggert - September 22, 2007 - 21:29

I'm not sure if this applies or will help anyone... but I was looking for a way to just add items to the local tasks tabs and only for specific nodes. I added this to my page.tpl.php file before the $tabs variable is called.

if ( !empty($tabs) && $_GET['q'] == 'node/' ) : $tabs = ereg_replace("</ul>", '<li><a href="?q=node/" target="_blank">Tab Title</a></li></ul>', $tabs); endif;

You can use this for as many items as you want and also have it apply to all pages by removing

&& $_GET['q'] == 'node/'

in the if statement

Note entirely sure that this is the best way

nicholasThompson - November 12, 2007 - 15:22

Adding functional stuff like this to the template it generally considered bad practice... A better method would be to create a module and in hook_menu do...

<?php
function example_perm() {
  return array(
'example callback permission');
}

function
example_menu($may_cache) {
 
$items = array();

  if (
$may_cache) {
   
//Nothing goes in here
 
}
  else {
   
//Non-cachable menu items
   
if (arg(0) == 'node' && is_numeric(arg(1)) {
     
$node = node_load(arg(1));
      if (
$node->type == 'blog') {
       
$items[] = array(
         
'path' => 'node/' . arg(1) . '/example',
         
'title' => t('Example Tab'),
         
'callback' => 'example_node_callback',
         
'access' => user_access('example callback permission'),
        );
      }
    }
  }

  return
$items;
}

function
example_node_callback() {
  return
t('THIS IS AN EXAMPLE!');
}
?>

That example will check the path and, if there is a node on this path (eg node/5) then it will load the node (eg, 5) and then check its type (eg, blog). If it IS a 'blog' then it will add a new tab to the list and it will also handle the callback AND the access permissions (so if your user does not have a role with that permission set then you will NOT see the tab).

Just noticed (a few weeks

nicholasThompson - November 30, 2007 - 15:05

Just noticed (a few weeks later) that there needs to be a type => MENU_LOCAL_TASK on the example menu item.

VERY helpful...

tROCK - April 17, 2008 - 19:34

VERY helpful... thanks.

There is one other minor typo... You need one more ")" at the end of your first conditional.

Here is the re-written code with the extra ) and the MENU_LOCAL_TASK edit implemented.

<?php
function example_perm() {
  return array(
'example callback permission');
}

function
example_menu($may_cache) {
 
$items = array();

  if (
$may_cache) {
   
//Nothing goes in here
 
}
  else {
   
//Non-cachable menu items
   
if (arg(0) == 'node' && is_numeric(arg(1))) {
     
$node = node_load(arg(1));
      if (
$node->type == 'blog') {
       
$items[] = array(
         
'path' => 'node/' . arg(1) . '/example',
         
'title' => t('Example Tab'),
         
'type' => MENU_LOCAL_TASK,
         
'callback' => 'example_node_callback',
         
'access' => user_access('example callback permission'),
        );
      }
    }
  }

  return
$items;
}

function
example_node_callback() {
  return
t('THIS IS AN EXAMPLE!');
}
?>

 
 

Drupal is a registered trademark of Dries Buytaert.