Template suggestions based on Path alias

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
jfinkel's picture

I have written a module for my own use and wonder 1) if I have reinvented the wheel and 2) if others would find this useful. Thanks in advance for feedback.

As we know, we can create a unique template for the front page: page--front.tpl.php. But let's say we have an entire subsection of the site we want to theme with a template. Or perhaps just one specific page in a subsection. There is no easy way to do this, and using node ids in the template name is just poor programming practice.

Assume this site structure:

Front
  -- Trees
  ---- Deciduous
  ------- How to plant
  ---- Non-Deciduous
  ------- How to plant

  -- Plants
  ----- Flowering

With my module activated, I can now theme any page or subsection.
For example, /trees/Deciduous/How-to-plant can be themed by:

page--trees__deciduous__how-to-plant.tpl.php
page--trees__deciduous.tpl.php
page--trees.tpl.php
page.tpl.php

The suggestions are created by looking up the alias created by the Path module. They will be examined in this order, so that the most specific template will be used, just as we would expect.

So, is this reinventing the wheel? Would others find it useful?

Comments

YES! AWESOME

mauritsivs's picture

ive been searching the entire drupal site and google for a way to have this function...
i was thinking i was missing something because this appears to be a standard function in drupal 6?

anyway, i would LOVE to get my hands on your module.

is this also aplicable to Nodes only?

hariharasuthan_mk's picture

just for sample plz refer this
custom.module


function custom_menu() {
  $items = array();
  $items['playworkvideo'] = array(
    'title' => 'playworkvideo',
    'description' => "playworkvideo",
    'page callback' => 'playworkvideo',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['news'] = array(
    'title' => 'News',
    'description' => "Latest News",
    'page callback' => 'latest_news',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );  
  
  return $items;
}


function playworkvideo() {
  global $base_url;
  $filepath = $_POST['fid'];
  if($filepath) {  
   print "";
  }
  exit;
}

function latest_news() {
  $latestnews = theme('latest_work');
  return $latestnews;
}

function custom_theme() {
    return array (
      'latest_work' => array (
      'template' => 'latestwork',
      'arguments' => array('form' => NULL),
      ),      
    );  
}

reply:YES! AWESOME

hariharasuthan_mk's picture

in the below playworkvideo is url , you can use that as (playworkvideo.html or playworkvideo)

it will call function ,in theme folder create .tpl file corresponding to the function , so u can get this

function custom_menu() {
  $items = array();
  $items['playworkvideo'] = array(
    'title' => 'playworkvideo',
    'description' => "playworkvideo",
    'page callback' => 'playworkvideo',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['news'] = array(
    'title' => 'News',
    'description' => "Latest News",
    'page callback' => 'latest_news',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );  
  
  return $items;
}


function playworkvideo() {
  global $base_url;
  $filepath = $_POST['fid'];
  if($filepath) {  
   print "";
  }
  exit;
}

function latest_news() {
  $latestnews = theme('latest_work');
  return $latestnews;
}

function custom_theme() {
    return array (
      'latest_work' => array (
      'template' => 'latestwork',
      'arguments' => array('form' => NULL),
      ),      
    );  
}

Here is how I do it.

jfinkel's picture

I created a module called alias_suggestion and put this single function in the alias_suggestion.module file. With this method you do not have to add a menu hook for every page you want to theme. It just works for every page.

It depends on Path being activated.

<?
* Author: Joel R Finkel (Systems by Design of Illinois)
* Date: 2011-02-27

* Enables developers to override page.tpl.php:
*  <site>/{alias} can be themed with page--{alias}.tpl.php
*
* Examples:
www.mysite.org/tos can be themed with
*      page--tos.tpl.php
*
www.mysite.org/tos/sub1 can be themed with
*      page--tos__sub1.tpl.php
       page--tos.tpl.php
*
www.mysite.org/tos/sub1/sub2 can be themed with
*      page--tos_sub1__sub2.tpl.php
*      page--tos__sub1.tpl.php
*      page--tos.tpl.php
*/

function alias_suggestion_preprocess_page(&$vars, $hook)
{
  // only do this for page-type nodes and only if Path module exists
  if (module_exists('path') && isset($vars['node']) && $vars['node']->type == 'page')
  {
    // look up the alias from the url_alias table
    $source = 'node/' .$vars['node']->nid;
    $alias = db_query("SELECT alias FROM {url_alias} WHERE source = '$source'")->fetchField();

    if ($alias != '')
    {
      // build a suggestion for every possibility
      $parts = explode('/', $alias);
      $suggestion = '';
      foreach ($parts as $part)
      {
        if ($suggestion == '')
        {
          // first suggestion gets prefaced with 'page--'
          $suggestion .= "page--$part";
        }
        else
        {
          // subsequent suggestions get appended
          $suggestion .= "__$part";
        }
        // add the suggestion to the array
        $vars['theme_hook_suggestions'][] = $suggestion;
      }
    }
  }
}

Here is the alias_suggestion.info file

;
name = Path Alias Suggestions
description = Add template suggestions based on Path alias
package = Other
version = VERSION
core = 7.x
dependencies[] = path

Hope this is useful.

change -- to __

aboodred1's picture

I was not able to make this module to work until I changed -- to __ in the following code

          // first suggestion gets prefaced with 'page--'
          $suggestion .= "page__$part";

thanks! unfortunately it

mauritsivs's picture

thanks!

unfortunately it doesnt seem to work for me...

hmm

jfinkel's picture

Please confirm that you turned this into a module, placed it the the site/all/modules directory, and activated it. Also, I think it will only work with Drupal 7.

It works

boltforge's picture

Hey Joel,

Reviving a dead post, but just wanted to confirm that this does indeed work. Thanks!

Matt

I like this idea because

othermachines's picture

I like this idea because occasionally I end up with a lot of if conditionals in page.tpl.php, which can get messy. But what happens if a site admin changes a title (possibly breaking the alias) or wants to add a page without re-theming?

Good point

jfinkel's picture

In that case, I guess you could always revert to using page--node-[nid].tpl.php for a specific page, as the nid will not change if the path changes. This is not a solution for every use case. But I think it will work for many.

Looks like I have reinvented the wheel. I Just found this: http://drupal.org/node/139766

Great idea

dax444's picture

I love it. I don't think you are reinventing the wheel since the link has no D7 instructions. I however could not get this to work either.

Right at the beginning I did notice where you have

<?
* Author

I think it should be:

<?php
/* Author

Even with those changes though I can't get it to work. I created a module and the info file, loaded it up into sites/all/modules and enabled it. I still can't get it to work though. Any thoughts you have would be great.

<?php

jfinkel's picture

The need for <?php (as opposed to just the <?) is dependent on how PHP is set up. <?php will always work. So I suppose I should have used that.

As coded, this will only work for nodes that are Basic Page content type. Perhaps this is the issue.

The easiest way to see what the template suggestions are is to install and enable the Devel Themer module. Use it to verify that the template in question is in the list.

Sort of works

Jon Betts's picture

if I create a template called page--newsroom__press-releases.tpl.php, this works for example.com/newsroom/press-releases but not example.com/newsroom/press-releases/nodetitle

so close...

So you want a custom template

Garrett Albright's picture

So you want a custom template for the page display of particular node types, then? That's relatively easy - just create a template named node-[type].tpl.php for Drupal 6, or node__[type].tpl.php for Drupal 7 (note that's two underscores).

No, maybe my example wasn't

Jon Betts's picture

No, maybe my example wasn't clear, but I've got a node type, but within that node type is a taxonomy used to categorize the node type. I use that to generate the aliases. So I have a node type of news and within that I've got press-release, letters-to-the-editer, etc. Each of those types display different info or the same field differently, so I'd like to be able to generate template suggestions based on path since the path is based on the nodetype/tax term., e.g. news/letters, news/press as examples.

EDIT: Actually, been away from this for a while and realized that I need this type of functionality for nodes and not pages which this is particular thread is about, so nevermind...

new module zip file

dzigner's picture

Hi
I created the 2 files, .module and .info. But when I upload the zip file containing that module, it says, there is no .info file. I have checked, and it is there.
I have Drupal7, is there something missing?
thanks

page templates

dzigner's picture

Hi
The module does not seem to work for me. I could upload/install/activate the module, but my page seems to take the page.tpl.php template and not the one I made.
For instance, I have as my top menu items - home, services, solutions etc. Now, the home page has the page--front.tpl.php page, and that works fine. For services, I made the page--services.tpl.php page, and added the alias services to node/1 url. But still the services page takes page.tpl.php template only.
Please help if you can.
thanks

Clear the cache

jfinkel's picture

Make sure you clear the cache.

Try hook_process_page

Michael Phipps's picture

I did try the module that was suggested, but it did not work for me.

In my case, I wanted to use page--admin.tpl.php template for any page under the path admin/%. I originally tried page--admin--%.tpl.php but it wasn't working because I use aliased paths.

I had set up aliases like this:
node/add/blog --> admin/my-app/add-blog

But what was happening was the alias admin/my-app/add-blog was actually being seen as the path node/add/blog, so the page--admin--%.tpl.php template didn't count as a suggestion.

To solve this I used the following code in hook_process_page() in my template.php

<?php
function THEME_process_page(&$vars){
$alias = explode('/',drupal_get_path_alias(request_uri()));
  if(
$alias[1]== "admin") {
   
$vars['theme_hook_suggestions'][] = 'page__admin';
  }
}
?>

What the code does is get the alias rather than the path. I check that the alias starts with 'admin' and if it does, tell drupal to display the page using the page--admin.tpl.php that way anything in admin/whatever/i/like uses that template.

Michael Phipps

http://michaelphipps.com

hook_process_page works!

RoastBeats's picture

Just confirming that Michael's hook_process_page suggestion works. Just make sure you know how your $alias array is breaking apart with a quick print_r. I made the mistake of testing on a devel server with a different uri an couldn't figure out why it wasn't working.

I believe this can be

fmizzell's picture

I believe this can be accomplished with the ThemeKey module, but you will have to create a theme for each area that you would like unique theming on.

Thank you

nylin's picture

I did something like this on my last site, but this was a little bit more complex than mine, I didn't care about the different types of levels for every path. Thank you for your great work man, wonderful :)

Rendering theme template with URL alias

shangan.23's picture

Theme template can be overridden with URL alias in the following way.

  1. Preprocessor function in theme template file
  2. Get the URL alias and choose the template file.

Explanation with code is given in the below link,
http://mydons.com/how-to-create-a-template-for-page-created-in-drupal-ad...

Fixed version

maniqui's picture

The version posted by the author didn't work for me.
First, there is a missing / in the second line (reported previously).

Second, it wasn't working for URLs with hyphens (my-section/my-page), as the string added to the theme_hook_suggestions array would look like this: page--my-section__my-page, which for some reason, at least on my installation, just fails: the corresponding template is never found/applied.

The way I fixed it was by replacing hyphens (-) to underscores (_) just before the string is added to the array. I've also applied this fix posted previously which may look unnecessary (although it's more correct to keep it fixed) as, as said, the hyphens are finally replaced with underscores just before adding them to the array.

This is the final code:

<?
/*
* Author: Joel R Finkel (Systems by Design of Illinois)
* Date: 2011-02-27

* Enables developers to override page.tpl.php:
*  <site>/{alias} can be themed with page--{alias}.tpl.php
*
* Examples:
www.mysite.org/tos can be themed with
*      page--tos.tpl.php
*
www.mysite.org/tos/sub1 can be themed with
*      page--tos__sub1.tpl.php
       page--tos.tpl.php
*
www.mysite.org/tos/sub1/sub2 can be themed with
*      page--tos_sub1__sub2.tpl.php
*      page--tos__sub1.tpl.php
*      page--tos.tpl.php
*/

function alias_suggestion_preprocess_page(&$vars, $hook)
{
  // only do this for page-type nodes and only if Path module exists
  if (module_exists('path') && isset($vars['node']) && $vars['node']->type == 'page')
  {
    // look up the alias from the url_alias table
    $source = 'node/' .$vars['node']->nid;
    $alias = db_query("SELECT alias FROM {url_alias} WHERE source = '$source'")->fetchField();

    if ($alias != '')
    {
      // build a suggestion for every possibility
      $parts = explode('/', $alias);
      $suggestion = '';
      foreach ($parts as $part)
      {
        if ($suggestion == '')
        {
          // first suggestion gets prefaced with 'page__'
          $suggestion .= "page__$part";
        }
        else
        {
          // subsequent suggestions get appended
          $suggestion .= "__$part";
        }
        // convert hyphens to underscores.
        $suggestion =  str_replace('-', '_', $suggestion);
        // add the suggestion to the array
        $vars['theme_hook_suggestions'][] = $suggestion;
      }
    }
  }
}

This is a diff output:

--- alias_suggestion.module.orig 2013-01-16 20:58:35.512218325 -0300
+++ alias_suggestion.module 2013-01-16 21:01:58.972219357 -0300
@@ -1,4 +1,5 @@
<?
+/*
  * Author: Joel R Finkel (Systems by Design of Illinois)
  * Date: 2011-02-27
 
@@ -37,14 +38,16 @@
       {
         if ($suggestion == '')
         {
-          // first suggestion gets prefaced with 'page--'
-          $suggestion .= "page--$part";
+          // first suggestion gets prefaced with 'page__'
+          $suggestion .= "page__$part";
         }
         else
         {
           // subsequent suggestions get appended
           $suggestion .= "__$part";
         }
+        // convert hyphens to underscores.
+        $suggestion =  str_replace('-', '_', $suggestion);
         // add the suggestion to the array
         $vars['theme_hook_suggestions'][] = $suggestion;
       }

Hope this helps someone out there.

Tried but wasn't able to get

Roar's picture

Tried but wasn't able to get this to work properly on my site.. activated the module, created a page with the URL alias "dashboard" and created a "page--dashboard.tpl.php" template file.

Nothing happened. That page simply defaulted to use page.tpl.php

But interestingly your module worked when I deleted the URL alias entirely.

So I could actually see my custom "page--dashboard.tpl.php" template in use after deleting the URL alias and then subsequently loading "mysite.com/dashboard". But meanwhile the page title Drupal generates is "Page not found" of course because the "dashboard" alias did not exist.

So it works... but only when the URL alias doesn't exist.

Weird! Any ideas?

Thanks Maniqui!

harntrox's picture

Putting the preprocess_page hook within the theme template file along with maniqui's final code within the module finally got this to work for me. Steps:

  1. Create the module. Use maniqui's top section of code in the .module file.
  2. Install the module.
  3. Include the preprocess_page hook within your theme template file. (code below).
  4. Create a page using the 'Basic Page' content type on drupal e.g about-us.
  5. Create a php file for this page in your templates directory e.g page--about-us.tpl
  6. Clear Cache!

<?php
  
function YOURTHEME_preprocess_page(&$vars) {
     if (isset(
$vars['node']->type)) {
     
$vars['theme_hook_suggestions'][] = 'page__' . $vars['node']->type;
     }
   }
?>

Slight modification to fixed version

jessicachan's picture

I made a slight modification to the code from maniqui my site had multiple URL aliases for the node I was trying to theme, and it was making a theme suggestion for the wrong alias.

This modification will loop through all the url aliases stored for that node id and match it to the current path in order to make the theme suggestion, so you know that node/999 being pointed to by /my-new-path will always be themable with page--my-new-path.tpl.php, even if node/999 is pointed to by /my-other-path and /my-other-other-path.

<?
/*
* Author: Joel R Finkel (Systems by Design of Illinois)
* Date: 2011-02-27

* Enables developers to override page.tpl.php:
*  <site>/{alias} can be themed with page--{alias}.tpl.php
*
* Examples:
www.mysite.org/tos can be themed with
*      page--tos.tpl.php
*
www.mysite.org/tos/sub1 can be themed with
*      page--tos__sub1.tpl.php
       page--tos.tpl.php
*
www.mysite.org/tos/sub1/sub2 can be themed with
*      page--tos__sub1__sub2.tpl.php
*      page--tos__sub1.tpl.php
*      page--tos.tpl.php

* Additional Modifications: Jessica Chan (jessicachanstudios.com)
* Date: 2013-11-11

* Noticed that it was only taking the first url alias it found, but what if
* multiple URL aliases referenced the same Node ID? Wanted a 1:1
* relationship so modified it so that it looped through all the query
* results and only added a theme suggestion if the current path matched
* the alias.

* E.g.: For node/736 with 2 aliases pointing to it, alias-1 and alias-2,
* http://example.com/alias-1 can be themed with page--alias-1.tpl.php and
* http://example.com/alias-2 can be themed with page--alias-2.tpl.php
*
*/

function alias_suggest_preprocess_page(&$vars, $hook)
{
  // only do this for page-type nodes and only if Path module exists
  if (module_exists('path') && isset($vars['node']) && $vars['node']->type == 'page')
  {
    // look up the alias from the url_alias table
    $source = 'node/' .$vars['node']->nid;
    $alias = db_query("SELECT alias FROM {url_alias} WHERE source = '$source'");

    // store all the results in an array
    $all_results = $alias->fetchAll();

    // store the length of this array
    $results_length = count($all_results);

    // if an alias is found
    if ($alias != '')
    {

      for ($i=0; $i<$results_length; $i++) {
       
        // for each url alias found
        $each_alias = $all_results[$i]->alias;
        $current_path = request_path();

        // compare it to the current path. if they match
        if ($each_alias = $current_path) {

        // build a suggestion for every possibility
        $parts = explode('/', $each_alias);
        $suggestion = '';
        foreach ($parts as $part)
          {
            if ($suggestion == '')
            {
              // first suggestion gets prefaced with 'page__'
              $suggestion .= "page__$part";
            }
            else
            {
              // subsequent suggestions get appended
              $suggestion .= "__$part";
            }
            // convert hyphens to underscores.
            $suggestion =  str_replace('-', '_', $suggestion);
            // add the suggestion to the array
            $vars['theme_hook_suggestions'][] = $suggestion;
          }
        }
        }
      }
    }
}

Thanks harntrox

rameezamaqsood's picture

It works fine for me. Thanks for the instructions.

If you use the above code,

13rac1's picture

If you use the above code, please start it with the correct PHP syntax:

<?php

instead of
<?