Mar
3
2011

Drupal 7.0 Preprocess Page Templates – theme_hook_suggestions

I had to do some research and testing to understand the changes from Drupal 6 to Drupal 7 template suggestions. At the time of writing this a lot of documentation appears to be non-existent or somewhat inaccurate. Documenting my own testing will hopefully save you a day of time on this.

Share
Email

Zen

Another note to prevent frustration. I’m using the D7 Zen theme. If you’re not I suggest you do (or any theme framework) for a multitude of other reasons. Anyways, it’s possible that something I mention here only works because my theme is a sub-theme of zen which is actually enhancing functionality in places I’ve otherwise not realized.

Page – Default and Custom Template Suggestions

This is an outline of built in and custom template suggestions. The custom ones are noted in the description. The chunk of preprocess_page I’ll be referencing is immediately below the applicable template suggestion. You’ll want to add this to your template.php to have the full range of page–*.tpl.php files I mention here.

function mytheme_preprocess_page(&$vars) {
  if (isset($vars['node']->type)) { // We don't want to apply this on taxonomy or view pages
    // Splice (2) is based on existing default suggestions. Change it if you need to.
    array_splice($vars['theme_hook_suggestions'], -1, 0, 'page__'.$vars['node']->type);
    // Get the url_alias and make each item part of an array
    $url_alias = drupal_get_path_alias($_GET['q']);
    $split_url = explode('/', $url_alias);
    // Add the full path template pages
    // Insert 2nd to last to allow page--node--[nid] to be last
    $cumulative_path = '';
    foreach ($split_url as $path) {
      $cumulative_path .= '__' . $path;
      $path_name = 'page' . $cumulative_path;
      array_splice($vars['theme_hook_suggestions'], -1, 0, str_replace('-','_',$path_name));
    }
    // This does just the page name on its own & is considered more specific than the longest path
    // (because sometimes those get too long)
    // Also we don't want to do this if there were no paths on the URL
    // Again, add 2nd to last to preserve page--node--[nid] if we do add it in
    if (count($split_url) > 1) {
      $page_name = end($split_url);
      array_splice($vars['theme_hook_suggestions'], -1, 0, 'page__'.str_replace('-','_',$page_name));
    }
  }
}
  • page.tpl.php – All pages. This covers the front, taxonomy, views, and node displays if nothing is overriding it.
  • page–node–%.tpl.php – This is the same as using page.tpl.php and overrides it.
  • page–[content-type].tpl.php – All pages that contain a single node under a specific content type. This needs to be defined in a custom preprocess function as shown below. It preserves what I perceive to be a logical ordering by using array_splice to place it 3rd in line (or 2nd to last with the -1 parameter). This makes it so that you can still override page specific nodes (page–node–[nid].tpl.php).
  array_splice($vars['theme_hook_suggestions'], -1, 0, 'page__'.$vars['node']->type);
  • page–[path-1].tpl.php – These next three are examples of what the custom preprocess_page code does.
  • page–[path-1]–[path-2].tpl.php – This has all the paths now. You’re not limited to two, but your file names might get crazy if you have a lot of paths, long paths, or both.
  • page–[path-1]–[path-2]–[node-name].tpl.php – The final and most precise (overrides all prior since its more specific) includes the nodes title.
  $cumulative_path = '';
  foreach ($split_url as $path) {
    $cumulative_path .= '__' . $path;
    $path_name = 'page' . $cumulative_path;
    array_splice($vars['theme_hook_suggestions'], -1, 0, str_replace('-','_',$path_name));
  }
  • page–[node-name].tpl.php – This is kind of like page–[path-1]–[path-2]–[page-name].tpl.php (and probably preferable since that one can get really long. This is why I decided to make this one available separately. Personally I would rather use page–node–[nid].tpl.php than this. However if you have a TON of these templates, I can see how it would be easier to browse your template directory by a more readable name. If you end up changing of the title of your node however (or the url_alais for any of these) you’ll need to adjust your template names accordingly.
  if (count($split_url) > 1) {
    $page_name = end($split_url);
    array_splice($vars['theme_hook_suggestions'], -1, 0, 'page__'.str_replace('-','_',$page_name));
  }
  • page–node–[nid].tpl.php – The page used for a particular node ID.

Pages – Taxonomy

I’d like to make this section more elaborate with some custom ideas but that will have to wait till later. For now, these are the page taxonomy default template suggestions.

  • page–taxonomy.tpl.php – Generic. Any taxonomy page.
  • page–taxonomy–term–%.tpl.php – This is basically the same as using page–taxonomy.tpl.php and overrides it. I believe the only difference might be in the fact that you have $content in your term. This means you’ve filled out your description or have created a custom field entity. Then this template will try to load.
  • page–taxonomy–term–[tid].tpl.php – The page used for a particular term ID. Again, assuming you’ve filled out the description term (or added additional fields) when you created that term. Having a term title and path alone didn’t appear to call this template up when I did my testing.

Pages – Views

Views has its own template suggestions in which I wont get into here. On the page level, by default, the paths available to you are very much like those in which we’ve customized for page nodes above.

  • page–[path]
  • page–[path-1]–[path-2].tpl.php
  • page–[path-1]–[path-2]–[node-name].tpl.php

Page Node Example

For this example lets assume a few things:

  • You’re viewing a node with a content type of “article”
  • The path url_alias is /path-1/path-2/my-article/name
  • The node ID you’re viewing is 5

Your theme_hook_suggestions array will look something like this:

[theme_hook_suggestions] => Array
    (
        [0] => page__node
        [1] => page__node__%
        [2] => page__article
        [3] => page__path_1
        [4] => page__path_1__path-2
        [5] => page__path_1__path_2__my-article
        [6] => page__my_article
        [7] => page__node__5
    )

It’s important to note here that everything passed into theme_hook_suggestions has an underscore instead of a dash (even if its just one). They will be converted to dashes after they go through preprocessing. This is why with the suggestions as stated above you’d create the following templates (each additional one in this order one will override the last).

  • page–node.tpl.php
  • page–node–%.tpl.php
  • page–article.tpl.php
  • page–path-1.tpl.php
  • page–path-1–path-2.tpl.php
  • page–path-1–path-2–my-article.tpl.php
  • page–my-article.tpl.php
  • page–node–5.tpl.php

Node Templates

  • node.tpl.php – All nodes anywhere they’re displayed. This template is sometimes used several times on a page.tpl.php if multiple nodes are called. On a taxonomy page listing for example.
  • node–[content-type].tpl.php – All nodes that match being a particular content type.
  • node–[nid].tpl.php – The node template used anywhere

Experimentation & Testing

If you want to experiment with this yourself I’d install the Devel module and use this code to see what’s in the theme_hook_suggestions array:

function mytheme_preprocess_page(&$vars) {
  kpr($vars);
}

Useful Resources

A lot of my research on pulling this together is comprised from viewing these pages for some of the insights I got. A few extend further on what I tested here so they might be useful to you to browse.

Respond: Leave A Comment | Trackback URL

Entrupeners, Subscribe for the lastest tools, tips, and tutorials.


12 Responses to Drupal 7.0 Preprocess Page Templates – theme_hook_suggestions

  1. julian

    thank, your article was very helpfull!

  2. holy, that was a lot of coding tricks but for a non techie guy like me, this is useful, thanks alot.

  3. well your information about Drupal 7 is very helpfull, but i was wondering could we use same method in Joomla themes?

    • Joomla has its own template system which I’m not familiar with. But, based off my experience with WordPress and from what I’ve -heard- about Joomla, Drupal has the most robust and fine tuned theming abilities in my opinion.

      You can do the same stuff in any of them, its just a question of how much work it is to do what you want to do.

  4. Excellent – I was beating my head against a wall for hours due to the book I’m trying to learn Drupal from leaving this essential bit of info out. Thanks!

  5. Angelo

    Very very helpful… now I can theme my content-type pages.. thankss!!!

  6. brody

    how should i name my template for multiple blog posts?

    eg: all the posts under blog do not have the same template.. how do i add the blog template to blog and blog posts?

    http://www.mywebsite.com/blog/blog-post

    I need my template to work two levels down under the same content type.

    I do not want to create a template for each blog post.

    thanks.

    also is it a problem if if have autopath?

  7. THANK YOU! I’ve been searching everywhere and having no luck getting this to work (note that I’m using autopath). This worked like a charm.

    Thanks for sharing! *high-five*

  8. Who Dat?

    It seems to work for certain combos.

    This is what I got and what works but most combos don’t seem to work.

    D7/Ubercart 3

    Using path auto I have the site set up like this. I have two content types.

    Product (Ubercart) and
    Manufacturers (custom type)

    The UC vocabulary is called products and I have a view that draws this page. Under products vocabulary I have several terms. Two examples are Nutrients and Manufacturers. The nutrients term contains nodes of the content type Products and the Manufactures term contains nodes of the type Manufacturers.

    So these are the template files that work.

    page-products.tpl.php which works on this path. For the vocabulary/view with a path of products and for nodes of the type Product.

    websites.com/products
    and
    websites.com/products/nutrients/node
    websites.com/products/manufacturers/node

    Now these path revert to page.tpl.php and I’ve tried all combos to override them.
    websites.com/products/nutrients/node
    websites.com/products/manufacturers/node

    Any suggestion on how I should name the template files so I can over ride websites.com/products/nutrients/node and websites.com/products/manufacturers/node would be appreciated.

  9. Edoardo

    i have a clean d7 installation, multilingual, it-en,
    i create a test template called “test”,
    i just copied your script into the template.php file inside the test template,
    i changed “my_theme” with “test”,
    i create a page node and an article node,
    i create the files page.tpl.php and page-article.tpl.php (they are different)
    but the system show me ever the page.tpl .php.

    my path is http://mysite/drupal/it/articolo1
    i have to activate something in the administration?

    where i wrong?

    thank for your beaoutifoul tutorial.

    • Do you have the Devel Module installed? Use that to clear your theme registry so it picks up the new template. Or you can go to your current theme settings and re-save the current settings. That refreshes. Clear cache if you have that enabled too.

  10. Gaurav Shah

    I have enabled an overlay for non-admin users so can create some contents. When non-admin user go to node/add/xxx page, it opens in an overlay with my custom theme. Now, it looks like by default overlay loads up page.tpl.php file. How can i provide custom.tpl. file and remove unnecessary stuff such as header, primary menu etc. I really appreciate if anyone can help me on this.

Leave a Reply

Custom Theme by Rob Malon | Content & Design © 2010 - RobMalon.Com - Chicago, Illinois