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

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...

Remember, user #1 always has access, and will always see the 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...

UPDATE: See this comment for a much easier approach to controlling access to this tab. Otherwise you may try the alternative code-based approach listed below. However, I don't think this approach works anymore with later versions of Views because I can't seem to get my Views path to show up in the $items array. So your best bet would be to use Joachim's comment linked above.

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!

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 )

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!

Thanks again for this Joachim, just today I needed this feature again, and the approach you mentioned works much better. In fact, the hook_menu_alter() approach doesn't seem to work anymore, because the path I created in my view doesn't even show up in $items.

"Sub tabs" are typically powered by local tasks. So I think you'd have to create a custom module with local tasks via hook_menu() to accomplish this. Otherwise, perhaps one of the many contrib modules that build menus can be of use here.