I'm in the process of porting Advanced Forum to Drupal 6.x and am trying to take advantage of the new theme system. One of the things I wanted to do was get rid of the requirement to copy the forum theme to the site theme directory. My goal was to make it so the whole thing could be run right out of the module directory but allow any template copied to the user's theme to override what's in the module. merlinofchaos helped me quite a bit and told me about hook_theme_registry_alter(), which isn't documented much yet. I decided to write up what I learned today in this article. Keep in mind that this is written by someone who has only really dug into the new theme system in the last few days, but merlinofchaos gave it a look over and he thought it looked ok. If you have comments on the article, I'd like to hear from you, but please no support questions. I barely understand this myself. ;)
---
The theme system in D6 does an excellent job of handling template files which will work well for most people. If you need very specific control over the theme registry, however, there is hook_theme_registry_alter(). This hook lets you get in and modify the registry directly. It has a paramater, $theme_registry, which holds the whole registry. To see what's in it, you can use something like this:
<?php
MODULENAME_theme_registry_alter($theme_registry) {
// Assuming you have the devel module installed
dsm($theme_registry);
}
?>This will give you a nicely formatted listing of the entire array to check out. By modifying this array, you can affect how template files are handled.
Example 1
Let's say you want to provide a node template in your module but you want the user of your module to be able to override that template in their theme. How do you do that? Well, you start out with:
<?php
function MODULENAME_preprocess_node(&$vars) {
if ($some_condition_is_met) {
$vars['template_files'][] = 'special-template-name';
}
}
?>That works great for the special-template-name.tpl in the user's theme directory, but what if they don't want to put it in their theme? How do you get it to default to the one in your module? You might think you can just add in the path to the module before that, but that doesn't work. the template_files variable won't accept paths outside of the theme. So, the trick is to convince the registry that your module is a theme. That's where the hook comes in.
<?php
function MODULENAME_theme_registry_alter($theme_registry) {
// Remove the first path under 'node' which is the one for the
// module that created the template
$originalpath = array_shift($theme_registry[$template]['theme paths']);
// Get the path to your module
$modulepath = drupal_get_path('module', 'MODULENAME');
// Stick the original path and then your module path back on top
array_unshift($theme_registry[$template]['theme paths'], $originalpath, $modulepath);
}
?>Now the theme registry will look first to the user's theme for the template file and then to your module directory.
Example 2
You can use MODULENAME_preprocess_ITEM() to add in variables before they go to the template file. This can be used on your own theme items or existing ones. In the case of existing ones, your variables are merged into the ones made by the original preprocess code. This is a great feature if you just want to add to it. But what if you don't want the other preprocess code to run at all? hook_theme_registry_alter() to the rescue again.
<?php
function MODULENAME_theme_registry_alter($theme_registry) {
foreach ($theme_registry['TEMPLATE_TO_OVERRIDE']['preprocess functions'] as $key => $value) {
if ($value == 'PREPROCESS_FUNCTION_TO_OVERRIDE') {
unset($theme_registry['TEMPLATE_TO_OVERRIDE']['preprocess functions'][$key]);
}
}
?>By doing a foreach, you wipe out only the preprocess function you don't want to run and leave any others in the chain, such as your own.