Picture
Squiggy Rubio Drupal Developer & Themer
April 8, 2010

Drupal 6 provides many avenues to modify its appearance, including ways to theme a form. Recently I themed all node edit forms at once. I will share here how I did it.

The usual route to theme a node edit form, or any form for that matter, is to grab the form ID, which is used as a theme hook, and implement a hook_theme function in template.php in the theme.

function MYTHEME_theme() { return array(
    'blog_node_form' => array(
     
'arguments' => array('form' => NULL),
     
'template' => 'blog-node-form',
    )
  );
}

?>

This lets you alter the theme registry to add a template file for a specific form. There are many blog posts that go into more detail than I do here on how to theme forms in general.

The above is useful if I want to theme a blog content type or the user registration form, for example. Most sites have at least a few different content types. I wanted to make alterations to all node forms at once and use a single template file. This involved a slightly different approach.

Correction: This next step with the theme_registry_alter example is not necessary. See comments below this post.

There is a theme_node_form theme function in Drupal, which is called by all node forms. I wanted to replace or supersede this theme function with a template. I tried to do this with hook_theme, but it continued to pick up some parts of theme_node_form in addition to supporting my template file. So it called the form array twice, causing recursion; which is not what I expected. So instead I took a colleague's suggestion and altered the theme registry via hook_theme_registry_alter(). This seemed clean and simple enough.

This takes the node_form theme hook and changes it from a theme function to a template, in sites/all/themes/MYTHEME/template.php:

{C}function MYTHEME_theme_registry_alter(&$theme_registry) {
 
$theme_registry['node_form'] = array(
   
'template' => 'node-form',
   
'arguments' => array('form' => NULL)
  );
}

?>

This worked great. But I hear that hook_theme_registry_alter is intended for use in modules only? If you know why, please chime in with a comment. Or leave a comment on the theme registry for special cases handbook page.

And the fun part... Spiffing up the node edit form

Here is what I did. This creates two columns for the form and places all top level form fieldsets on the right.

Screenshot:

Screenshot of modified node edit form.

And here is how I did it...
{C}
Markup in the template sites/all/themes/MYTHEME/node-form.tpl.php:

  {C}print $form; ?>
  {C}print $buttons?>
  {C}print $fieldsets; ?>

A little CSS for the columns:

.node-form-left {
  float:left;
  margin-right:2%;
  width:640px;
}
.node-form-right {
  float:left;
  width:300px;
}

Peprocess function in sites/all/themes/MYTHEME/template.php:

{C}/**
* Preprocess for node-form.tpl.php
*/

function MYTHEME_preprocess_node_form(&$vars) {
  foreach(
$vars['form'] as $key => $value) {  
   if (
is_array($value) && $value['#type'] == 'fieldset') {
     
$vars['fieldsets'] .= drupal_render($vars['form'][$key]);
    }
  }
 
$vars['buttons'] = drupal_render($vars['form']['buttons']);
 
$vars['form'] = drupal_render($vars['form']);
}

?>

So what this does is create and prepare a few variables for my template. First it loops through the form array grabbing all fieldsets and puts them in $vars['fieldset'];. I then print this variable in the template. Similarly, it creates a buttons variable that grabs the submit buttons. There are a lot of ways to view the array, but I usually use print_r($vars['form']); in my preprocess function when I want to see what's happening behind the scenes.

As alternative route to do all this is with a theme overwrite function, which would look like this (I did not test this snippet!):

Update: See this comment for a version of this snippet that works.

 

/**
* Theme overwrite of theme_node_form();
* Note, this is UNTESTED CODE, but it should work.
*/

function MYTHEME_node_form($form) {
 
$output = '
';
 
$output .= drupal_render($form['buttons']);
 
$output .= drupal_render($form);
 
$output .= '
';
 
$output .= '
';
  foreach(
$form as $key => $value) {  
   if (
is_array($value) && $value['#type'] == 'fieldset') {
     
$output .= drupal_render($form[$key]);
    }
  }
 
$output .= '
';
  return
$output;
}

?>

So I could have used a theme overwrite instead of manually altering the theme registry. Theme functions are five times faster than using a template, so often it's recommended and preferable to use a theme function.

So there you go. That's how I themed all node edit forms with a template and preprocess function.