Want to get Lullabot article, videocast, and podcast announcements delivered right to your in-box?
Let us know your email address (we won't share it) and we'll let you know when anything exciting happens.
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">» <?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>Comments on this post will automatically be closed three months from the original post date.



RSS Feed



Comments
very helpful, thank you.
very helpful, thank you.
Very Helpful Information
Thanks Matt, I had an idea of this because of the Civicspace theme but your post made it completely clear now.
Regards!
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!
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.
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.
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
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.
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!
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
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
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.
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.
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.
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
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
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:
<?phpfunction _phptemplate_variables($hook, $vars) {
$myvars = array();
switch ($hook) {
// more code here...
}
return $myvars;
}
?>
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.
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);
}
Nice!
Nice - I was doing things the old fashioned way in my page.tpl.php
<?phpif ($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?
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.
<?phpif ((arg(0) == 'admin' && arg(1) != 'block')) {
?>
If you do want the admin theme again on admin/block/configure/etc. modify the statement further.
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?
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_variablesintemplate.php.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?
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.
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 .
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
I get a completely blank
I get a completely blank page when following these directions. :(
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.
Super
Your custom front page snippet was a lifesaver for a Drupal newbie. Thanks :)
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
$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
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;
}
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.
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.
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
Don't use closing php tag
Actually an even easier way to avoid this is to leave off the closing php tag. This is part of the Drupal coding standards and the detailed PHP Code tags section points to the original discussion that led to this decision.
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?
Event Styling?
Any chance we could get a tutorial on how the heck you go the event.module to output start and end dates that look like this?
http://www.lullabot.com/workshop/theming/providence-ri-2007
I sort of see how to do it, but don't really get how you got the date to be separated.
Thanks.
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.
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',
);
}
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.
do not see theme after loading it
I uploaded a new theme to the theme folder but I do not see it on the admin so I can set it up, what else do I need to do?
Jose
thanks
hey thanks for the tutorial, it helped me a lot
thanks
i am working on custom theme for my site and this tutorial gave me a good start
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? :)
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";
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.
inpAK
Ive Been Looking At THis Site,
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!
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;
}
?>
The theme system changed in 6.x
The _phptemplate_variables function no longer exists in Drupal 6. It has been replaced with the preprocess function. You should read up on the D6 theme system in the d.o handbook, http://drupal.org/node/173880.
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;
}
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
awesome!!!
Thank you!! this really helped me, i spent days! reading about how to make this and this explanations finally helped me! thank you!
Adding Page number on Pager Page Title
How to adding page number on Pager page Title?
Just like this
[site name] page #
please help me
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 .
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.
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.
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