Drupal - Create a Menu Tab with Views for a Node Content Type

Category:

This article describes how to place a custom local task menu tab alongside the 'View' and 'Edit' tabs on a content type.

In the example screen shot above, we have various local task menu tabs when viewing a node as an admin user. The 'View', 'Edit' and 'Revisions' tabs are provided by Drupal Core, the 'Group' and 'Menus' tabs are provided by the Organic Groups module and the 'Devel' tab is provided by, you guessed it, the Devel module. The 'Content' tab is our custom tab. When clicking on it, we want the page to display all the nodes related to the current node through a node reference field.

Creating the local task menu tab...

 

1. Create a View with a Page Display and Menu Tab

2. Set the Page Settings for the View Display

In this particular example, we want the custom tab to be called 'Content' and to have its path set to:

node/%/content

A path like this is ready to take in a Node ID Views Contextual Filter, so the results are filtered on the node we are looking at. It is important your path follow this pattern so it will fall into the correct local task menu.

Next we'll  setup the Menu Tab in the Page Settings for the View Display.

We also can limit access to certain user roles, if necessary.

3. Setup the View Fields, Sort Criteria and Filter Criteria

Feel free to add any fields, sorting and filters you'd like to get the data you'd like. In my case, I was only interested in sorting by the nodes last updated date and seeing these fields:

  • Node ID
  • Node Title
  • Node Type
  • Author
  • Created
  • Last Updated
  • Published

4. Setup the View Contextual Filter

In my particular example, I did not need any filters because I wanted results across all content types and didn't care if they were published or not. What I did want however, was a contextual filter so the results could be limited to the current node.

Under the 'Contextual Filters' settings on your View Display, click the 'Add' button to create a contextual filter for your Node ID. Typically you could use one of your node reference fields here as your contextual filter. You may have to tinker with your Contextual Filter settings for a while to get the results you want, be sure to use the 'Preview' mode in views and send along a Node ID to the preview.

5. Save the View

Once you have the results you want filtering on your node id, just save your view and you will now have a custom local task menu tab show up on all of your nodes!

Pretty cool huh? Yes indeed. But what if you want this tab to only show up on a particular content type? Because right now, it will show up on all content types.

How to get the tab to show up on a specific content type...

 

1. Implement hook_menu_alter() to set a custom 'access callback' function on our views' page path

/**
 * Implements hook_menu_alter().
 */
function my_module_menu_alter(&$items) {
  // Set a custom access callback function for our view page display path.
  $items['node/%/content']['access callback'] = 'my_module_node_content_custom_access_callback';
}

2. Implement hook_module_implements_alter() so our module's hook_menu_alter() is called after views

/**
 * Implements hook_module_implements_alter().
 */
function my_module_module_implements_alter(&$implementations, $hook) {
  // When the implementations of hook_menu_alter are called, we need our module
  // to be called after views, so let's remove it from the implementations then
  // add it to the end.
  if ($hook == 'menu_alter') {
    if (isset($implementations['my_module'])) {
      unset($implementations['my_module']);
      $implementations['my_module'] = FALSE;
    }
  }
}

3. Implement our custom 'access callback' function which will determine wether or not our custom menu tab will show up

/**
 * A custom 'access callback' function used by our view page display
 * to determine if its local task menu tab should show up or not.
 */
function my_module_node_content_custom_access_callback($options = array()) {

  // Grab the default access callback function name, prepare the access
  // arguments, then see what the default access call back result is
  // according to views.
  $access_callback = $options[0];
  $access_arguments = $options[1];
  $access = call_user_func_array($access_callback, $access_arguments);

  // If the default access call back was false, then the user is not allowed
  // access.
  if (!$access) {
    return FALSE;
  }

  // So far the user is allowed access from the views' settings, let's now
  // determine if we want to customize the access to the tab.

  // If the node type is not an article, then we'll deny access, otherwise grant
  // access. 
  $node = node_load(arg(1));
  if ($node && $node->type != 'article') {
    return FALSE;
  }
  else {
    return TRUE; 
  }
}

4. Flush all of Drupal's Caches

After implementing the two hooks and custom access callback function in your custom module, flush all of Drupal's caches then check out your new menu tab!

Conclusion

Thanks for stopping by and reading this article, I hope it helped bring a custom local task menu tab to your content type.

Please let me know of any alternative methods and/or contrib modules that would make this task easier!

Comments

Thanks for it nice article.

No prob alinouman, thank you!

Hi Tyler,

Some time ago I did EXACTLY the same stuff as yours in my site, but in addition to filtering the "node type" where to show the Views tab, I had the need to hide the Views tab if the View returns no results.

To do so the only solution I found was to programmatically execute the Views inside my 'access callback' and then return FALSE if the views returns  no results. Obviously this is not a good workaround because every time a node is viewed I have to execute a view... but it is the only I found. (ref to: http://drupal.org/node/1485188 )

Do you know some alternatives?

Thank you

Hi MXT,

The alternative that comes to mind, would be to use hook_node_load(), then when your node is loaded, set up the hook to check the view result count, then attach something like $node->show_my_view_tab = true;

Then in your access callback function (the node should be passed to it), check for that boolean value, then return your true/false value. I hope this helps!

Sir,
I am new to drupal. I want to, insert new tab into main menu. I have added. But I dont know where that page contents will added.Can you help me ?

Sir,
Already I have added blocks. I dont know where we will add New menu tab' s 'Body content'. Whenever I add new block, that time body content option is there.

Hi!

Thank you for this post! I tried it out, and it owrks fine. 

I have not used it, howeer, as I could not figure out how to make this work for more tabs and different content types..

 

Then I found out, that views already does all this out of the box in advanced->contextual filters->specify validation criteria

Thanks Joachim! Next time I try placing a tab on a content type, I'll be sure to check out the 'specify validation criteria' on the view's contextual filter settings to see what options are available.

I've found out that if a view uses relations then the access callback url should be node/%views_arg/content instead of node/%/content. Otherwise EntityMalformedException may arise.

Thanks Art!