
Sometimes, you need to change the HTML markup output by some module, and you want your changes to keep whichever theme you will use. The need came up for me recently when I had to provide special markup for pages that display a webform. I had to quickly put together a module for a client, a module that would provide a template file, which I decided to name (arbitrarily) page-webform.tpl.php.
The situation may arise for you as well, so I will share my technique with you. Say you want to provide a template file for all pages that display one node of a content type with machine-readable name CONTENT_TYPE_NAME. And say you want to use a module to provide such template. Say you decide to name your module special_page.
You start with creating a DOT info file for your module. The name of which would be in our case special_page.info.
;$Id$ name = Special Page description = Provides a template file for my content type CONTENT_TYPE_NAME. core = 6.x
Then you go about creating your DOT module file, ie: special_page.module.
<?php // $Id$ /** * @file * Module that provides a special template for pages that show * ONE node of content type CONTENT_TYPE_NAME. */
You probably should implement HOOK_help() in your module, but I will skip that, and cut the chase to what we really need.
At this point, you need to tell the theme system to use your template, and to use it only in certain situations. Your 'situations' will differ from mine. In my case, I wanted the theme system to use my template on a node page, hence any page with path node/nid, yet only on pages that show the node in view 'mode', so I did not want the template to be used when the path is node/nid/edit for example. There are many ways to skin the cat here. I decided to use the fact that a $node object is passed to the page.tpl.php template only when the page is a node page, as you will see in the following code snippet. Here, I will make use of a preprocess function to pass on to the theme system a suggestion about a new module-supplied template.
function special_page_preprocess_page(&$variables) { // If this is a node page (not a list of nodes page) and // the node is shown in 'view' mode rather than 'edit' or whatever. if (isset($variables['node']) && (arg(2) === NULL)) { // If the content type of that one node is 'CONTENT_TYPE_NAME'. if ($variables['node']->type == 'CONTENT_TYPE_NAME') { $variables['template_file'] = 'page-CONTENT_TYPE_NAME'; } } }
Then feel free to create such template file. As a reminder, no need to add the tpl.php extension to the value you assign to $variables['template_file']. Also, that name can be anything, I am just following conventions here by prefixing with page, as in page-SOMETHING.tpl.php. Make sure that the name you provide here matches the name of your template file.
Then, you are faced with a small problem: the template file will need to be placed in the theme folder in order to be picked up by Drupal's theme system. But you don't want that. So what to do? Here comes a situation where we can use the module hook HOOK_theme_registry_alter(). For the theme hook you want to provide special theming for (hook in theme parlance here), you will have to tell Drupal Hey, Drupal, please look in my module folder over here, you may find a template file you will need.
EDIT: As of Drupal 6.7, this last step should no longer be necessary. See this page in the Theming Guide on Drupal.org for details. I haven't had the chance to re-test my module without the following snippet.
function special_page_theme_registry_alter(&$theme_registry) { $theme_hook = 'page'; // my hook name // Get the path to this module $modulepath = drupal_get_path('module', 'special_page'); // Add the module path on top in the array of paths array_unshift($theme_registry[$theme_hook]['theme paths'], $modulepath); // dsm($theme_registry[$theme_hook]['theme paths']); }
And you are done.
What's more...
The hook function HOOK_theme_registry_alter() is called only when the theme registry is rebuilt, that is, when you clear your Drupal cache. (It is called for all modules that implement it of course.)
The hook is NOT called at every page refresh :-)
What a relief.
To test this, uncomment the dsm() call.
Comments
Redemption
The Drupal theme system has redeemed itself in my book.
Great write-up. Thank you very much for taking the time.
Drupal 6 is just great with theming
I dot not want to go back, Abuchanan.
Perfect!
Exactly what I needed. Thanks.
Thanks for the thanks
Glad this could help you, Frank, and best of luck with your development.
This looks like a great module: http://drupal.org/project/custompage
Ah, now I get it, you can
Ah, now I get it, you can inject the custom template into the theme system but without tying the template to a particular theme or having to copy it to multiple theme folders (so for example you can allow users to select their own theme and the template will always still be available). I guess that's what you were saying in the first sentence! D6 subthemes are great e.g. for adding a template to a single theme but this is another level of flexibility.
Thanks for another insightful post!
Yeah, my goal is better explained in your comment
That's it exactly.
Hello, what if the page I
Hello, what if the page I wanna style is a menu callback? how do i do it?
Menu callback
That menu callback function returns some output, ie: markup. Well, that output is probably generated with a theming function. If it is, then it is business as usual: create a template, and use the method described above.
what if...
hi, excellent tutorial. Only one question, your code works like a charm, but the module removes any possibility to the user to modify the .tpl.php from the theme directory.
What if i should only need to add a piece of text, or a div on the $content variable without theming the output? Should it be possible to do it from the module?
Thanks in advance
Adding to $content
For this, you need to implement the module hook HOOK_nodeapi() in your module. Use $op = 'view'.
You will find more explanation on this here.
Great.
Just what I was looking for. This should be in the Drupal docs!
thanks
Hi
I was looking for this for a couple hours :) finally. Thanks a lot.
Glad you found it
:-)
Since drupal 6.9, the need
Since drupal 6.9, the need to put the template file inside the theme dir is no more. it can now reside in the module dir and drupal will take a peek there.
http://drupal.org/node/223430
http://drupal.org/node/223430
6.7, i lied!
That's fabulous!
And extremely important. I will update the tutorial.
Thank you very much!
Very useful
Very useful
Thanks
This was very helpful!
Nice tutorial..
New method in drupal 6 for theming as compared to drupal 5.
good articles always here...
Thanks
Custom theme hook
I find that this works if using MYMODULE_preprocess_page() but if I'm preprocessing a custom theme hook, the override does not apply and the template in my modules direcotry is still sued instead of the one copied into my theme's directory.
Does this only work with MYMODULE_preprocess_page()?
Following up, naming
Following up, naming conventions seem to be the cause of my issue.
Brian
That last step where we edit the theme registry is no longer necessary. Read the tutorial again. All you have to do is register a theming function for your custom hook, and, when you do, don't forget to specify the name of the template you will use.
thanks
This is informational and came in handy for me. Thanks for the input here.
Ah
This site is a real winner
I had a bit of trouble theming drupal from the module layer myself. I actually had to pass the task on to my lecturer at the time.
Keep up the great posts.
Simon
Then enable the special_page module
Haha then you enable the special_page module you just created :) That was five minutes I'm not getting back. I stuck a div with a string in my page-something.tpl.php thinking that would just overwrite my page content but it didn't and I can't see anything happening. What have I done wrong?
have a .info and .module copy and pasted into a special_page folder replaced CONTENT_TYPE_NODE with page in the if-statement and changed the template_file to 'page-something'
I then enabled the module (eventually), refreshed the cache, made a page and viewed it. I then refreshed the cache a couple more times. dsm has me entering the if-statement but nothing happens.
function special_page_preprocess_page(&$variables) {
// If this is a node page (not a list of nodes page) and
// the node is shown in 'view' mode rather than 'edit' or whatever.
if (isset($variables['node']) && (arg(2) === NULL)) {
// If the content type of that one node is 'CONTENT_TYPE_NAME'.
if ($variables['node']->type == 'page') {
$variables['template_file'] = 'page-something';
dsm($variables);
}
}
}
//my .tpl.php has (maybe that's not enough)
What the what!!!
then I'm assuming it's drupal_render(something) to show the content with the new template??? Sorry I'm an utter newb with drupal.
No idea
Actually I have no idea what I have to type to print out the $content var in the .tpl.php
Geez, thanks
Caroline, thanks for the detailed instructions. I'm helping my wife with her drupal site and your steps really came in handy!
Thank you...
Thank you, Edward! :)
new comer of drupal
I am learning to use drupal to setup my another commercial website in order to save time and money.
useful information thanks
Great Technique Caroline
I have a couple of Drupal blogs I am playing with and tutorials like these are helping greatly. I swear you drupal module developers are genius - I have saved mad money by not taking any drupal college courses, but instead of running across blogs like this one.
Thank you for everything,
Jerry Chan :)
Had some issues...
I couldn't get the template file to work as set in special_page_preoprocess_page() until I changed
$variables['template_file'] = 'page-CONTENT_TYPE_NAME';
to
$variables['template_file'][] = 'page-CONTENT_TYPE_NAME';
Interestingly, in both cases the dsm returned the same values. Something else in the mix I guess. I still had to use the theme_registry_alter code as well.
This was in Drupal 6.14.
Thx for the article. I had no clue about the the theme registry alter function's existence.
Post new comment