Go forth and tabbify with Quicktabs 3.0 for Drupal 7

The Quicktabs module now has a stable release on two Drupal 6 branches (that's right, it never had a stable D6 release until a few weeks ago :-/) and on two D7 branches. The 7.x-3.x branch represents a complete rewrite of the module, providing much greater flexibility in what can be done with it.

Quicktabs 3.0 for Drupal 7 uses ctools for plugin management and provides two types of plugins: renderer and content plugins. The renderer plugins provided with the module are jQuery UI Tabs, jQuery UI Accordion, and classic Quicktabs, which is essentially the original "homegrown" tabs mechanism, including ajax support*. Content plugins are for providing different "things" that can be output as tab content, e.g. blocks, views, nodes, other Quicktabs instances. One new content plugin that's provided is the menu callback plugin, which allows you to specify any Drupal path and have that displayed as the content of a tab.

So an obvious way that developers can extend Quicktabs is to write plugins for it, and I've been pleased to hear of some interesting plugins already in the works. However, there is another important way in which Quicktabs is now far more flexible than it ever was before and that's in the way that you can programmatically build a Quicktabs instance. You could do this to a certain extent in previous versions, but the 7.x-3.x branch allows you to shove basically whatever you want into a Quicktabs instance.

A neat example of this is if your page design calls for having the current node's comments as one of a set of tabs (the other tabs might be views of content related to the node or whatever). So you can implement hook_page_alter(), where you'll get the entire page array, grab the comments out of it, and smoosh them into a Quicktabs instance. Like this:

<?php
/**
* Implements hook_page_alter().
*/
function my_module_page_alter(&$data) {
  if (isset(
$data['content']['system_main']['nodes'])) {
   
$nids = element_children($data['content']['system_main']['nodes']);
   
$nid = $nids[0];
   
// We'll place the node comments inside a quicktabs block.
   
$comments = $data['content']['system_main']['nodes'][$nid]['comments'];
    unset(
$data['content']['system_main']['nodes'][$nid]['comments']);
   
$overrides = array('ajax' => 0, 'style' => 'nostyle'); // Override some of the settings for the QT instance.
   
$custom_tabs = array(
      array(
       
'title' => 'Comments',
       
'contents' => $comments, // Custom tabs must have a contents property.
       
'weight' => 0
     
)
    );
   
// Our 'node_activities' Quicktabs instance has just one tab, we're going to
    // add a second tab to it, containing the node comments.
   
$data['content']['qt_activities'] = array(
     
'content' => quicktabs_build_quicktabs('node_activities', $overrides, $custom_tabs),
     
'#weight' => 99,
    );
  }
}
?>

And the result:

The documentation for the quicktabs_build_quicktabs() function explains the parameters it takes:
* @param name. The machine name of the Quicktabs instance to build - if a name
* is passed that does not correspond to an existing instance, then it is taken
* to be a completely custom instance and is built from only the custom tabs
* that are passed in.
*
* @param overrides. Options that will override the options of the Quicktabs
* instance from the database, or if no existing instance is being used, these
* will override the default options. Possible keys are 'style', 'hide_empty_tabs',
* ajax', 'default_tab', 'renderer', 'title'.
*
* @param custom_tabs. An array representing custom tab contents, which will be
* appended to the Quicktabs instance from the database, or if no existing instance
* is being used, the custom tabs will be the entire contents. An example custom_tabs
* array would be array(array('title' => 'custom', 'contents' => array('#markup' =>
* t('Some markup'), 'weight' => 5));

Essentially, you can take advantage of this function to rearrange parts of a render array into tabbed content or create tabbed content from scratch.

As with the 6.x-3.x version, Quicktabs instances are fully exportable as features components. Go forth and tabbify!

*jQuery UI Tabs provides an ajax option but it's actually quite inflexible and I felt it wasn't what we needed so I spent some time trying to make Tabs work with the new Ajax framework in core, i.e. suppressing its ajax behaviour in favour of the more flexible functionality in core's ajax framework. I came up against a bit of a brick wall in that endeavour - see http://drupal.org/node/1108978.

Problem: comments 'Preview'

A problem with the comments section is that when you click 'Preview', you lose the tabs. Evidently, 'Preview' is presenting the same data in that node, but not using the page.tpl.php template that creates the tabs. Any idea how to get 'Preview' to use the page.tpl.php template we want?

Glad to see this code,

Glad to see this code, thanks!
However, I am having a problem getting it to work (in D7.8).
I can see that the $contents variable has been set correctly, and that quicktabs_build_quicktabs() has added a big data structure to $data['content']['qt_activities']; however no tab structure appears.
I am unsure where to go from here for troubleshooting this or giving you more usable info.
One thing that has me a little confused is the comment:

// Our 'node_activities' Quicktabs instance has just one tab, we're going
// to add a second tab to it, containing the node comments.

In the code, I don't see where the first tab was created, if this comments section is the 2nd tab.
Could you advise on what steps to take from here? Thanks!

OK, I see what was happening.

OK, I see what was happening. Given only your code above, a Quicktabs instance of just 1 tab is created, and because of the code 'style' => 'nostyle' that one tab doesn't look like a tab. By changing the style to 'Basic' and adding another tab, I can see that this is working. Here is the portion of code I modified:

    $overrides = array('ajax' => 0, 'style' => 'Basic'); // Override some of the settings for the QT instance.
    $custom_tabs = array(
      array(
        'title' => 'Comments',
        'contents' => $comments, // Custom tabs must have a contents property.
        'weight' => 0
      ),
      array(
        'title' => 'Stuff',
        'contents' => 'stuff',
        'weight' => 0
      )

The 'node_activities'

The 'node_activities' Quicktabs instance was created via the UI, or defined in an implementation of hook_quicktabs_default_quicktabs. It already has one tab.

Thank You - quick question

This is a great article. I'm using Quicktabs for the first time. I have a question in regard to the first image in the article (my first tab, etc..). What setting did you use there? I've been experimenting and can't get the content (or teaser is what I'm looking to do it on) to collapse.

Thanks

This is using the "accordion"

This is using the "accordion" renderer plugin. On the Quicktabs admin form, when you're creating or editing a Quicktabs instance, one of the options is "renderer" and you can select "accordion", "quicktabs" or "ui_tabs".

I'm wondering whether

I'm wondering whether quicktabs could utilize the cotent_type plugin from ctools. There are things for everything.
Perhaps it would be enough if quicktabs could have a generic menu-callback for it.

There is an issue for this in

There is an issue for this in the queue: http://drupal.org/node/1065684 I just haven't had a chance to look into it properly yet.