User login

Use hook_theme for new theming functions and templates; use hook_theme_registry_alter to take over existing ones

Everyone else has figured this out by now, but loudly proclaiming ignorance (as soon as I know I am ignorant) is a proud U.S. of A. tradition (albeit ordinarily done with less self-awareness).

Apologies if you had to click to read this, because the whole insight is in the page title: Use hook_theme for new theming functions and templates; use hook_theme_registry_alter to take over existing ones.

Agaric will demonstrate this with a negative example and a positive example.

Negative (that is, incorrect) example

WRONG, so very very wrong:

<?php
function examplemodule_theme() {
  return array(
    'page' => array(
      'template' => 'page',
      'arguments' => array(
        'variables' => array(),
      ),
    ),
  );
}
?>

Again, the above is WRONG. Do not use hook_theme for existing templates such as page, node, comment, etc. (For adding entries to the registry for elements your module or theme creates itself, you should use hook_theme.)

In the example of badness above, it will somehow replace the real page hook with your own, and unless you also re-implement the pre-process function, it will give you empty pages– apparently all the variables except the content variable! No idea how this works, why it turns out that way, but it just confirms how terribly wrong that is.

Positive (that is, correct) example

We want hook_theme_registry_alter, as described by Caroline Schnapp in theming D6 from the module layer:

<?php
function examplemodule_theme_registry_alter(&$theme_registry) {
  // Path to template file (Agaric's module has a theme directory, not required)
  $path = drupal_get_path('module', 'examplemodule') . '/theme';
  // Add the module path on top in the array of paths for the theme hook page
  array_unshift($theme_registry['page']['theme paths'], $path);
}
?>

Actually doing this proved much more complicated for Agaric. I know history will judge me harshly, but this is what I ended up with that actually worked:

<?php
/**
 * Implementation of hook_theme_registry_alter().
 */
function scf_theme_registry_alter(&$theme_registry) {
  // names of hooks handled by scf
  $theme_hooks = array(
    'block',
    'comment',
    'comment_wrapper',
    'node',
    'forums',
    'forum_list',
    'forum_topic_list',
    'forum_icon',
    'forum_submitted',
//    'forum_topic_navigation'  (not currently handled)
    'page',
  );
  // Get the path to this module
  $path = drupal_get_path('module', 'scf') . '/theme';
  // Add the path paths
  foreach ($theme_hooks AS $theme_hook) {
    if (!scf_au_stringpart_in_array($theme_registry[$theme_hook]['theme paths'], 'themes')) {
      // if there is no path with 'themes' in the theme registry
      // then replace the template path with our module path
      // if we add this when the theme is in charge, it throws errors
      // that oddly mungle the theme path plus the module template path
      // but without a theme directory being called first, this works
      // and this really is necessary for everything above
     
      // This just gets crazier and crazier.  Must be wrong, but it works.
      // Transform _ in hook to - to match template naming scheme
      $tpl = strtr($theme_hook, '_', '-');
      // this isn't documented, but it has proven necessary
      $theme_registry[$theme_hook]['template'] = $path . '/' . $tpl;
    }
    array_unshift($theme_registry[$theme_hook]['theme paths'], $path);
//  setting the specific chosen path isn't necessary   
//  $theme_registry[$theme_hook]['theme path'] = $path . '/' . $theme_hook;
  }
}

function scf_au_stringpart_in_array($haystack, $needle) {
  foreach ($haystack AS $bale) {
    $pos = strpos($bale, $needle);
    if (FALSE !== $pos && 0 !== $pos) {
      return TRUE;
    }
  }
  return FALSE;
}
?>

Resources Used by Agaric

More links than you would want to dare shake a stick at, unless you happen to have an army of stick-shakers to back you up.

Resolution

Searched words: 
Drupal 6 theme page template Drupal 6 theme page.tpl.php hook_theme page.tpl.php

Comments

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

If what

If what you describe is the case, that's not the intention. The goal is that any something.tpl.php placed in your theme directory would override anything defined here. Worst case scenario you need to implement hook_theme in your theme template.php or this function can be modified/extended to add the theme directory path at higher priority than the module directory path it is adding on.

Good job

Thank you. After reading the Drupal documentation and your homepage I'm ready to go.

Keep up!

Post new comment

The content of this field is kept private and will not be shown publicly.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • You can use Markdown syntax to format and style the text. Also see Markdown Extra for tables, footnotes, and more.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <blockquote> <small> <h2> <h3> <h4> <h5> <h6> <sub> <sup> <p> <br> <strike> <table> <tr> <td> <thead> <th> <tbody> <tt> <output>
  • Lines and paragraphs break automatically.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.