by Matt Westgate on February 26, 2006 // Short URL

Take control of your Drupal theme

Want to create a front page that's styled differently from the rest of your site? Perhaps you need a separate admin theme? Or how about a login page which only shows the login block and nothing else? With a little PHP knowledge these problems are easy to solve.

Note: You must be using the PHPTemplate theme engine for your theme. An easy way to determine this is by looking for files ending in .tpl.php within your site's theme folder. PHPTemplate is compatible with Drupal 4.6 and up. As a matter of fact, it's the default theme engine for Drupal 4.7 since it combines the best of both worlds for designers and programmers. Designers get an easy way to manipulate HTML lightly sprinkled with PHP variables for dynamic content, and developers get a fast rendering template system that's a snap to extend. Here's what a template looks like using PHPTemplate.

<div class="node">
  <h2 class="title"><a href="/<?php print $node_url?>"><?php print $title?></a></h2>
  <span class="submitted"><?php print $submitted?></span>
  <span class="taxonomy"><?php print $terms?></span>
  <div class="content"><?php print $content?></div>
  <div class="links">&raquo; <?php print $links?></div>
</div>

Developers can override any of the above PHP variables or even add new ones to pass to the template for the designer to use. What most folks don't know is that for every template section, PHPTemplate looks for a special variable named template_file which stores the name of the file to execute. This is your key to conditionally loading different theme files.

Working with template_file

Intercepting PHPTemplates' default variables is accomplished through creating a new function named _phptemplate_variables() within your theme. Navigate to your site's current theme folder and look for a file called template.php. If that file doesn't exist, create it. Now add the following function:

<?php
/**
* Intercept template variables
*
* @param $hook
*   The name of the theme function being executed
* @param $vars
*   A sequential array of variables passed to the theme function.
*/
function _phptemplate_variables($hook, $vars = array()) {
  switch (
$hook) {
   
// more code here...
 
}

  return
$vars;
}
?>

This function is called by Drupal just after a template engine call is made. Examples of this include: loading the node template, the block template or what we want, the page template. The $hook parameter above is the name of the template section the system is calling (node, block, page, etc).

First the system assigns a bunch of default variables. In the case of page hook: $sidebar_left, $sidebar_right, $footer_message, $search_box, $title, $content, etc. If _phptemplate_variables() exists, any values set there will override the defaults, including your opportunity to change the name of the template file the system should be looking for. Let's take a look at our first example.

Separate Administration Theme

The admin area is known for its wide tables which can be difficult to style when they're found nowhere else on your main site. Many folks find themselves wishing for a separate theme. Here's how to do it.

<?php
function _phptemplate_variables($hook, $vars = array()) {
  switch (
$hook) {
    case
'page':
      if ((
arg(0) == 'admin')) {
       
$vars['template_file'] = 'page-admin';
      }
      break;
  }

  return
$vars;
}
?>

Here we're changing value of $vars['template_file'] from page.tpl.php to page-admin.tpl.php.

Let's build our new admin theme. You probably want to use the Blue Marine layout as the admin theme. If your already using Blue Marine for the rest of your site that's okay as you'll get that basic idea. Grab a copy of page.tpl.php from the Blue Marine theme and paste it into your theme folder and rename the file to page-admin.tpl.php. Next, we want this file to use a separate stylesheet, so copy the Blue Marine style.css file and rename it to admin-style.css. Finally edit page-admin.tpl.php so it knows about the new CSS file.

<?php //print $styles ?>
<style type="text/css" media="all">@import "<?php print base_path(). $directory; ?>/admin-style.css";</style>

Now navigate to your admin section and behold the marvels of PHPTemplate.

Custom Front Page

Here's how we do the special front page on Lullabot.com.

<?php
function _phptemplate_variables($hook, $vars = array()) {
  switch (
$hook) {
    case
'page':
      global
$user;
      if (
$vars['is_front']) {
       
$vars['template_file'] = 'page-index';
      }
      break;
  }

  return
$vars;
}
?>

Create a file called page-index.tpl.php and start styling away.

Stand-alone Login Page

Sometimes you want the login/register page to stand alone, with no other action for a user to take.

<?php
function _phptemplate_variables($hook, $vars = array()) {
  switch (
$hook) {
    case
'page':
      global
$user;
      if (
arg(0) == 'user'){
        if (
$user->uid == 0) {
         
$vars['template_file'] = 'page-login';
        }
        elseif (
arg(1) == 'login' || arg(1) == 'register' || arg(1) == 'password' ) {
         
$vars['template_file'] = 'page-login';
        }
      }
      break;
  }

  return
$vars;
}
?>

Create a page-login.tpl.php such as the following

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="<?php print $language ?>" xml:lang="<?php print $language ?>">

<head>
  <title><?php print $head_title ?></title>
  <?php print $head ?>
<?php print $styles ?>
</head>

<body<?php print $onload_attributes  ?>>
  <table border="0" cellpadding="0" cellspacing="0" id="header">
    <tr>
      <td id="logo">
        <?php if ($logo) { ?><a href="/<?php print $base_path ?>" title="<?php print t('Home') ?>"><img src="/<?php print $logo ?>" alt="<?php print t('Home') ?>" /></a><?php } ?>
        <?php if ($site_name) { ?><h1 class='site-name'><a href="/<?php print $base_path ?>" title="<?php print t('Home') ?>"><?php print $site_name ?></a></h1><?php } ?>
        <?php if ($site_slogan) { ?><div class='site-slogan'><?php print $site_slogan ?></div><?php } ?>
      </td>
      <td id="menu"></td>
    </tr>
    <tr>
      <td colspan="2"><div><?php print $header ?></div></td>
    </tr>
  </table>

<table border="0" cellpadding="0" cellspacing="0" id="content">
  <tr>
    <td valign="top">
      <div id="main">
        <?php //print $breadcrumb ?>
        <h1 class="title"><?php print $title ?></h1>
       <div class="tabs"><?php print $tabs ?></div>
        <div id="help"><?php print $help ?></div>
        <?php print $messages ?>

        <?php print $content; ?>
      </div>
    </td>
  </tr>
</table>

<?php print $closure ?>

</body>
</html>

Matt Westgate

Co-Founder & President

Want Matt Westgate to speak at your event? Contact us with the details and we’ll be in touch soon.

Comments

syawillim

Aaaahhhhhh hhhhaaaa

So that's how it's done, I some how expected it to be a little more difficult than that. But as with most things Drupal it's very straight forward once you know how. I did initially recieve an error message "Call to undefined function base_path()", and so changed the this: <?php print base_path(). $directory; ?> to this: <?php print $directory; ?> and away we go. Thanks! Reply

Marius

base_path

Yep, I had the same problem with base_path. Not sure where it's coming from, checked in 4.7 and there is no base_path there either. Reply

ted

Which version of Drupal?

The lastest CVS of Drupal 4.7 has base_path() defined in it. If you are using an older beta, or 4.6.5, base_path() won't work. This article is intended for 4.7, however with a few slight changes, it should work for 4.6.5 as well.

Reply

Marc

seperate admin hack breaks

When updating a page the path becomes: ?q=node/2/edit&destination=admin%2Fnode so then it displays the regular template instead of the page-admin.tpl.php Reply

Steve

template.php location

I'm using Drupal 6.5 with apache/php, clear_urls, and mysql. This problem occured for me as well. I relocated the _phptemplate_variables function in the engine template.php file instead of having one in the mytheme directory. That solved it. Hope it helps. Reply

Catherine

RE: template.php location

I had the same problem too with a blank page appearing and the url like this:

?q=node/2/edit&destination=admin%2Fnode

So I moved template.php up a level in the themes directory and it works.

Thanks for the tip!

Reply

Anonymous

Great discussion. Run into

Great discussion.

Run into the same problem, getting the white blank page and url of the kind: http://www.mysite.com/drupal/node?destination=

Moving template.php file up in the theme directory doesn’t solve the problem as Durpal doesn’t recognize the file and fallback to normal (that’s prior to _phptemplate_variables function) work.

The only way I managed to overcome and use the _phptemplate_variables function is by incorporating it into the phptemplate.engine file. But that’s an ugly solution.

Anyone knows what the nature of the problem at hand and how to solve it so template.php with _phptemplate_variables can be used?

Thanks in advance

Reply

BeechyBoy

Same Problem

I'm on Drupal 5.1 and keep getting a blank screen if a template.php file is created in the 'bluemarine' theme directory. I'm about to go into the actual PHPtemplate engine file and try the 'ugly solution' which I know will bugger everything up :s. Can I ask what the anonymous user above did in the PHPtemplate engine file to solve the issue? Many thanks,
Beechy Boy

Reply

bert Wynants

Great Article

I figured out a lot about phptemplate already but this helps and clarifies a lot more! Also thanks for the PodCast, it referred me to this article. Reply

arvana

How about themes for specific pages?

I've been trying to find a way to make it easy for a site admin to specify an alternate page style without having to do any template editing. The best idea I've tried was to use case switching in page.tpl.php based on (in my case) arg(0)=="store" and set a path alias such as "store/product1" so that the page gets the custom page theme. Unfortunately arg() doesn't seem to recognize path aliases, so it doesn't work. Any ideas of how this could be done? I've thought about taxonomy-based solutions, but can't figure out what URL would work, and I'm at a loss. Reply

arvana

got an answer

I got some help on this -- the issue that I was having is that my site has clean URL's turned on, so arg() doesn't get set. The alternative for clean URL's is this: <?php $uri_request_id = $_SERVER['REQUEST_URI']; $section = explode("/", $uri_request_id); switch ($section[1]) { case 'layout1': include 'page-layout1.tpl.php'; break; case 'layout2': include 'page-layout2.tpl.php'; break; default: include 'page-default.tpl.php'; } ?> $section[1] should be changed to $section[2] etc depending on which part of the URL you want to use to switch templates with. Reply

Michael Jones

The problem is not clean URLs...

...the problem is trying to call URL aliases. I've found out the hard way over the last week that if you try to call an alias as arg(whatever), it doesn't work. arg(whatever) still comes up as whatever it would be if you weren't using an alias.

Your code looks like it will work with aliases. Going to try it now. Will report back later if it works or not.

MJ

Reply

Michael Jones

Yep. That code works...

Yep. The code above works if you're trying to call aliases.

So, for clean urls and url aliases, you can use the following in your page.tpl.php (which works famously):

<?php

if ($is_front) {
    include
'page-index.tpl.php';
    return;
}

$uri_request_id = $_SERVER['REQUEST_URI'];
$section = explode("/", $uri_request_id);
switch (
$section[1]) {
    case
'admin': include 'page-admin.tpl.php'; break;
    case
'group': include 'page-group.tpl.php'; break;
    case
'question': include 'page-vocab.tpl.php'; break;
    case
'add': include 'page-addnode.tpl.php'; break;
    default: include
'page-default.tpl.php';
}

?>

That code deals with a custom front page as well as any other internal pages (based on where arg(0) would normally be in a clean url) you need. All you have to do is change up the case code to match what you need.

Now, I wish I knew of a way to use this new code with the _phptemplate_ function and put it in template.php where it belongs instead of in the page.tpl.php. Anyone want to take a shot at that? ;)

MJ

Reply

alienbrain

A slightly lighter..

That really helped me, great article. Though by checking phptemplate.engine, I'd say that a slightly lighter way to override the variables should look like: <?php function _phptemplate_variables($hook, $vars) { $myvars = array(); switch ($hook) { // more code here... } return $myvars; } ?> Reply

SoupStone

Parse error unexpected T_VARIABLE for _phptemplate_variables

Seeing as no one else is getting this, I'm probably doing something wrong. For some reason, in both Bluemarine and FriendsElectric I get a parse error with the admin-page code above. I've tried every trick I know to get rid of these errors... here is the code I've pasted into template.php exactly as above: function _phptemplate_variables ($hook, $vars = array()) { switch ($hook) { case 'page': if (arg(0) == 'admin') { $vars['template_file'] = 'page-admin'; } break; } return $vars; } Am I really doing something wrong, or is there something I'm not seeing. Thanks for posting this -- it exactly what I'm trying to do. Reply

SoupStone

First off, retyping (not

First off, retyping (not using copy/paste) helped. Here is the code I came up with that tends to work (sorry for answering my own question). function _phptemplate_variables ($hook,$vars) { switch($hook) { case 'page': if(arg(0) == 'admin') $vars['template_file'] = 'page-admin'; break; } return($vars); } Reply

Lawrence Sheed

Nice!

Nice - I was doing things the old fashioned way in my page.tpl.php

<?php
if ($is_front) {
  include(
'page-front.tpl.php');
  return;
}
?>

This is *much* more modular.

Would you recommend doing this for languages too?

eg - English template file / other language template file (Chinese for me mostly).

Right now I get the languages array from

<?php


$languages
= (function_exists('locale')) ? locale_supported_languages() : array('name' => array());
if (
$user->language == '') {
     
$user->language = key($languages['name']);
}
//(first in the array is the current site language)

/* Easy enough for me to switch pages using this  */

switch ($user->language ) {
case
'en':
  
//Load the english template
break;

case
'zh-hans':
//Load the chinese template
break;
}
?>

Comments?

Reply

Heine

Admin theme & blocks

Nice post. Small nitpick; on admin/blocks (block settings) you probably want to use the 'normal' theme, so you can see where the blocks are going.

<?php
if ((arg(0) == 'admin' && arg(1) != 'block')) {
?>

If you do want the admin theme again on admin/block/configure/etc. modify the statement further.

Reply

gollyg

I have passed extra

I have passed extra variables using php template in the past, but I am stumped on this now using drupal 4.7rc2. The $vars array is not available to me - a simple

print_r(get_defined_vars());

shows a few variables, but nothing that is very useful. Anyone else having problems with this and 4.7rc2? Or ideas on what might be happening?

Reply

pbarnett

Separate Administration Theme

Excellent idea, and it works for me.

I did find that due to the fact that we're just changing stylesheets, the logo image is still that of the original style.

You can fix that by adding

   $vars['logo'] = 'themes/your-theme-name/your-icon-name';

after
   $vars['template_file'] = 'page-admin';

in function _phptemplate_variables in template.php.

Reply

snarfblatt

Can't seem to get it to work

I created a 'page_index.tpl.php' file and 'template.php' file in my themes folder. The contents of the 'template.php' file is this:

<?php
/**
* Intercept template variables
*
* @param $hook
*   The name of the theme function being executed
* @param $vars
*   A sequential array of variables passed to the theme function.
*/
function _phptemplate_variables($hook, $vars = array()) {
  switch (
$hook) {
    case
'page':
        global
$user;
        if (
$vars['is_front']) {
           
$vars['template_file'] = 'page-index';
        }
        break;
  }
  return
$vars;
}
?>

When I go to the home page of my site.. I still get the default drupal home page, am I missing a step somewhere?

Reply

snarfblatt

Ok.. I figured out my

Ok.. I figured out my problem.. it was actually quite silly. I had the template file name incorrect, so nevermind.

Reply

xwebmaster

Login / Logout link in primary links.

Great page btw. Bookmarked it will revisit.
Is there a way to add a link which will show 'login' in primary links when user not logged in and 'logout' when user logge in. Will take user to a login page .

Reply

cypher543

Very cool guide! I

Very cool guide! I especially like the custom frontpage part. I've always wondered if it was possible to do that. :D

Reply

Will

admin skin

I'm using a custom admin skin and had to change the if line above to include the 'add content' pages, just adding an extra argument.

if ((arg(0) == 'admin') || (arg(1) == 'add') ) {

seems to be working so far.

thanks for the write up! Really helped.

Reply

Jani Tarvainen

Super

Your custom front page snippet was a lifesaver for a Drupal newbie. Thanks :)

Reply

Caleb G

Another option...

I found a great little contrib module for 4.7 called "Separate management theme for administrative pages" (that's one long project name, right) which let's someone just specify a different theme for paths/pages the designate in the same manner that one can make blocks hide/show.
http://drupal.org/project/manage

Reply

Mick

$content variable in the node.tpl.php page ?

Hello i was looking for some help on styling my node page..i've found all the articles on moving the blocks about and the variables available via the drupal.org site but can't find how to style the actual $content variable...driving me bonkers !!

can any1 help ?

cheers,

M

Reply

forsythe

Blank Page

I'm a Drupal newbie and am also getting a blank page for the login page. What am I doing wrong? I included the following in my template.php and created a file called page-login.tpl.php using the example HTML above. Please Help!!!

/**
* Intercept template variables
*
* @param $hook
*   The name of the theme function being executed
* @param $vars
*   A sequential array of variables passed to the theme function.
*/
function _phptemplate_variables($hook, $vars = array()) {
  switch ($hook) {
    // more code here...
  }

  return $vars;
}

function _phptemplate_variables($hook, $vars = array()) {
  switch ($hook) {
    case 'page':
      global $user;
      if (arg(0) == 'user'){
        if ($user->uid == 0) {
          $vars['template_file'] = 'page-login';
        }
        elseif (arg(1) == 'login' || arg(1) == 'register' || arg(1) == 'password' ) {
          $vars['template_file'] = 'page-login';
        }
      }
      break;
  }

  return $vars;
}
Reply

Markus Sandy

Blank Page

It may be because there are two copies of the function defined in your code and that is an error (look in you watchdog log files to see the message). Simply remove or rename the first one.

Reply

themegarden.org

Whenever your Drupal gives a

Whenever your Drupal gives a blank page, try to look at your web server's log files.
Usually (for apache), you should take a look in error_log file for PHP errors and warnings.

Reply

orangecoat-ciallella

Blank page on login and logout - Possible Fix = Trim whitespace

I was working in the template.php and started getting a blank page after each login and logout. It wasn't unless I reloaded the page the second time that the page would load correctly.

Per themegarden's suggestion, I looked at my Drupal error log to find the 'headers already sent' message, which usually means some extra whitespace is being output by PHP before the http headers are sent.

Sure enough, I found I had two new lines at the end of my template.php. Moral of the story, trim any whitespace outside the PHP tags.

Jim

Reply

Anonymous

styles.css

The functionality works great but it still loads the styles.css along with the additional admin-styles.css. is there a way of stopping it loading?

Reply

MikeyLikesIt

that would be great!

I've often wanted to style my dates like that, but haven't yet had the time to figure out how to customize the output of drupal to accomplish it.

Reply

Jahangir

settings.php

Another hack for having Separate Administration Theme would be to do the following in settings.php file

if(arg(0) == 'admin' | arg(0) == 'devel' | arg(1) == 'content' | arg(1) == 'blocks' | arg(1) == 'add' | arg(2) == 'edit')
{
$conf = array(
'site_name' => 'Admin Panel',
'theme_default' => 'garland',
'anonymous' => 'Visitor',
);
}

Reply

Anonymous

Adding a new theme

Hi,

I've downloaded a new theme from the drupal.org and I've placed the new theme directory under the Theme directory but it doesn't show up as one of the theme options. Do I need to do anything else?

Thanks for your help.

Reply

Hillary

Role-based Theme

This is a great tut. However, I'm still trying to dig through the code to figure out how to change the theme (really, just the site name and logo) based on the role perms a user belongs to.

Any suggestions? :)

Reply

Hillary

Role-based Theme: problem solved!

Just figured it out and thought I'd post my solution. I wanted to simply change the name of the site based on whether they had a certain user role. I added the following code to the _phptemplate_variables function in the template.php file within the template I'm using.

global $user;
if (in_array('Department Manager',$user->roles))
$vars['site_name'] = "Client Extranet";
Reply

Jessicakoh

One can use

One can use page-front.tpl.php for front page without hardcoding into tempate.php.

Duplicate page.tpl.php, rename it to above name.

This is applicable for D5 and above.

Reply

Drupal Museum

Great tutorial. It's good

Great tutorial. It's good to see some focus on Drupal theming. That's one area that seems to be lacking in the Drupal community, although I've been seeing more and more nice Drupal sites lately. Thanks for the post!

Reply

Steve

Stand-alone login not working

I tried implementing the stand-alone login using Drupal 6.2 but it's not working for me.

I'm using the Pixture module and pasted the code below into template.php but it seems like when I load the front page it just ignores the code.

I tried putting this into the page.tpl.php file like some of the comments suggestion, but didn't have much luck.

Is there something different I need to be doing in 6.x?

<?php
function _phptemplate_variables($hook, $vars = array()) {
  switch (
$hook) {
    case
'page':
      global
$user;
      if (
arg(0) == 'user'){
        if (
$user->uid == 0) {
         
$vars['template_file'] = 'page-login';
        }
        elseif (
arg(1) == 'login' || arg(1) == 'register' || arg(1) == 'password' ) {
         
$vars['template_file'] = 'page-login';
        }
      }
      break;
  }

  return
$vars;
}
?>
Reply

Steve

Thanks - updated code?

Thanks it seemed like it wasn't even calling the function! Those links are a great starting point, but I'm still having trouble.

I noticed that the Pixture template.php has a preprocess function - it looks like for anything that is a page. I tried to just insert part of the code from the article but it doesn't work. Any suggestions?

- Steve

/**
* Override or insert PHPTemplate variables into the templates.
*/
function phptemplate_preprocess_page(&$vars) {
  // Hook into color.module
  if (module_exists('color')) {
    _color_page_alter($vars);
  }

/*
Steve: Inserted this bit from the article but it doesn't seem to work. Had to take out the break. Not sure what to return?
*/

      if (arg(0) == 'user'){
        if ($user->uid == 0) {
          $vars['template_file'] = 'page-login';
        }
        elseif (arg(1) == 'login' || arg(1) == 'register' || arg(1) == 'password' ) {
          $vars['template_file'] = 'page-login';
        }
      }
      return;

}
Reply

Felipe Gaona

Custom Front Page

Hi

I was wondering if this tecniques still work in drupal 6x, I was able to create a new region now using the .info file in the theme acording to 6x specifications, but I haven't been able to show this region just in the front page with your code, It shows everywhere on the site.

I don't have much experience programmign php so I'm not sure if 6x requires some changes to your code for a custom front page.

Thanks

Felipe

Reply

Camilo

awesome!!!

Thank you!! this really helped me, i spent days! reading about how to make this and this explanations finally helped me! thank you!

Reply

Anonymous

Drupal producing 2x HTML/BODY code

I followed these instructions and got it all working except one part. On my "Custom-page.tpl.php" I have it stripped down to nothing but "Test", but whenever I visit the page in drupal, the normal header and footer code is still there, with my entire 'custom-page.tpl.php' inside the body tags. So now I have 2 .

Reply

eknowlton

I just spent about 45

I just spent about 45 minutes trying to figure out how to make your custom front page snippet to work with Drupal 6, and you know... Drupal 6 just happens to have page-front.tpl.php all figured out.

So it was all a waste of time. ha.
Just thought i'd let surfers that saw this know in case they get caught up with it too.

Reply

ash

any example of template_preprocess?

plz guyz if any one of you could give any example of doing the same thing in drupal 6 "template_preprocess"........plz, its really needed.
thx in advance.

Reply

Anonymous

thank you for your guide,

thank you for your guide, it's very helpful.

We're new man in Drupal themes market. We've got a new Drupal theme for free and really need comments from Drupal themes communities. Just go and download it for free? (http://symphonythemes.com/node/73) and tell us if you like it or dislike it, as well.

Thanks again, Matt

Reply