Custom theming forms

This section outlines how to theme forms (CCK, user, etc) throughout Drupal 6.
Since http://drupal.org/node/101092 is quite out of date, and to my knowledge there are no comparable guides, I'm working on creating up to date guides on theming various forms in Drupal. Since it's the one I'm working on for myself right now, the first I will do is the My Account form (user/#/edit).

Much of this information comes from the excellent Drupal Dojo videos which introduce the method for preparing theme template files, as well as theming forms--you should absolutely watch them:

Theming CCK input forms with hook_form_alter

There's a helpful guide on drupal.org on how to theme CCK input forms. The approach of this guide gives you complete control over how every field and aspect of the form will be rendered, by means of creating a template file for the form and starting the form from a blank slate, separately re-adding every desired element back into the form.

Note that the original guide was written (I believe) for Drupal 4.7, contains notes regarding Drupal 5, and there is info about Drupal 6 adjustments in the comments of the page. Perhaps I will try updating that guide for Drupal 6 myself some time when I get a chance.

If your needs for adjustments are minor enough to not warrant manually reassembling the whole form one field at a time, then sometimes it may be simpler to go with a different approach, where you just override aspects of the form using hook_form_alter() (in a custom module) or hook_theme() (in template.php).

Here are several excellent resources for information on how to modify forms either using template.php or within a simple custom module:

You can do much of this via template.php, though most often (and in the below example), I use my own simple custom module for full access to all the possibilities. Here's a lesson I wrote on How to create a simple Drupal 6 module in three easy steps. The above Lullabot link mentions some details on whether to choose template.php or a module. Another reason I use a module is since I've set my module to have a very heavy weight value, causing it to override just about anything any other module has already done.

Here's an example which modifies the input form specifically for Forum nodes in a variety of minor ways (again, this is part of my own customsite module):

<?php
function customsite_form_alter(&$form, &$form_state, $form_id) {
  switch (
$form_id) {

    case
'forum_node_form':
     
// dsm($form);
     
      // Remove the Split teaser button. Reference: drupal.org/node/225955#comment-1095643
     
$form['body_field']['teaser_include'] = array(
       
'#type' => 'value',
       
'#value' => TRUE
     
);
     
     
// Remove the Menu fieldset. Forum topics never go in the menu.
     
unset($form['menu']);
     
     
// Start form wrapper box 1.
     
$form['title']['#prefix'] = '<div class="form-box">';
     
     
// Remove the "Body" label from above the main body textarea.
     
unset($form['body_field']['body']['#title']);
     
     
// Set the Attachments weight to show right below the Body.
     
$form['attachments']['#weight'] = -3;
     
     
// End form wrapper box 1. Include the pre-existing closing div as well.
     
$form['attachments']['#suffix'] = '</div></div>';
     
     
// Set the Taxonomy weight to show right below the File Attachments.
     
$form['taxonomy']['#weight'] = -2;
    
     
// Make the Categories taxonomy fieldset non-collapsible.
     
$form['taxonomy']['#collapsible'] = FALSE
    break;
  }
 
 
// Method to affect all node forms
 
if ($form['#id'] == 'node-form') {
   
  }
}
?>

A few things to note:

  1. Don't include the <?php and ?> tags, which are only shown here to trigger the code formatting/colors.
  2. About the 6th line down you see the commented out dsm($form); function. If you have Devel module installed, un-comment this line as needed and it will add an incredibly useful "widget" to the top of the page (prints in the $messages area) that lets you easily browse through the guts of the form. Don't uncomment the line if you don't have Devel module enabled though or it will wsod (white screen of death). The widget has access control, so non-privileged visitors will not see it, just you. If you prefer you can alternately change it to dpr($form); which will add a nicely formatted print_r instead. If you click around in the dsm() widget and compare to the code snippets here, you'll quickly understand how they are put together and how you can add your own.
  3. You can modify any form this way. Every form in Drupal has a unique Form ID. You can find the ID if you view source on the page's output and search for "form_id" (see the Lullabot article for an example).
  4. Above and below 2 specific elements of the form I've added a #prefix and a #suffix. This lets you append your own custom text or HTML above or below a form element. In this case I'm using this to wrap a certain set of fields with a div, so I can style them all together. You can add prefixes and suffixes in as many spots in the form as needed.
  5. At the bottom I've included an empty spot "Method to affect all node forms" which uses a snippet I found inside another module that allows you to effect multiple node types at once (whereas the first instance at the top affects only the "forum_node_form" form ID). I haven't thoroughly tested this out, but including it as it is likely useful.

Theming the user account page

This is an example of creating a template file to custom theme the Edit Account page in Drupal (user/#/edit). For the sake of an example of what "could" be done, the template is split into a two column layout, with parts of the form in each column. Also various adjustments and changes are made to some of the form elements, such as rewording a description, changing some of the fieldset titles, adjusting the size of some of the form fields, and renaming the submit and delete buttons.

This code example assumes several settings are enabled on your Drupal site, including: User pictures, signatures, and the permission to select alternate themes (although the UID 1 admin will still be able to select themes here in any case).

Depending on the modules you have installed, additional variables for the fields produced by those modules will need to be prepared in the preprocess function, and added to the user-profile-form.tpl.php in order to show up. As needed, you can uncomment the dsm($vars['form']); line if you have Devel module installed, which will make it much easier to visually browse through the contents of the form and find the names of elements you want to include.

The <?php print drupal_render($form); ?> line at the bottom of user-profile-form.tpl.php is very important. There are a variety of hidden form element which must be included in order for the form to function correctly. Adding this line at the bottom will include the remaining part of the form that you haven't already manually separated out for custom theming (both hidden fields, as well as any regular form content which you haven't separated out manually). So long as no required fields are shown at the bottom here, it's possible to wrap the area with a div set to display: hidden if you wish to hide them. Setting this part of the form to be hidden if required fields exist in it will result in the form being impossible to submit.

Much of this information comes from the excellent Drupal Dojo videos which introduce the method for preparing theme template files, as well as theming forms -- you should absolutely watch them:

Code for template.php

Add this code to template.php, changing both instances of YOURTHEME to the actual name of your theme:

/**
* Implementation of hook_theme().
*/
function YOURTHEME_theme() {
  return array(
    'user_profile_form' => array(
      'arguments' => array('form' => NULL),
      'template' => 'user-profile-form',
    ),
  );
}


/**
* Preprocess user profile form.
*/
function YOURTHEME_preprocess_user_profile_form(&$vars) {

  // Uncomment the following line if Devel module is enabled, to view the contents of the form.
  // dsm($vars['form']);

  // Change the help text for specific form elements.
  $vars['form']['account']['name']['#description'] = t('Custom description regarding the Username.');

  // Adjust the titles of several fieldsets.
  $vars['form']['picture']['#title'] = t('Your user picture / avatar');
  $vars['form']['timezone']['#title'] = t('Time zone');
  unset($vars['form']['timezone']['timezone']['#title']);

  // Set several elements that by default have collapsed fieldsets to expanded and non-collapsible.
  $vars['form']['theme_select']['themes']['#collapsible'] = FALSE;
  $vars['form']['picture']['#collapsible'] = FALSE;
  $vars['form']['contact']['#collapsible'] = FALSE;
  $vars['form']['timezone']['#collapsible'] = FALSE;

  // Adjust the size of several fields to fit better in 2 columns.
  $vars['form']['account']['name']['#size'] = 25;
  $vars['form']['account']['mail']['#size'] = 25;
  $vars['form']['picture']['picture_upload']['#size'] = 40;
  $vars['form']['signature_settings']['signature']['#cols'] = 50;

  // Rename the Save and Delete buttons to be more clear.
  $vars['form']['submit']['#value'] = t('Save profile');
  $vars['form']['delete']['#value'] = t('Delete account');


  // Prepare all of the desired form elements as variables, to be used in user-profile-form.tpl.php.
  // Everything before this part is optional.
  $vars['account'] = drupal_render($vars['form']['account']);
  $vars['theme_select'] = drupal_render($vars['form']['theme_select']);
  $vars['picture'] = drupal_render($vars['form']['picture']);
  $vars['signature_settings'] = drupal_render($vars['form']['signature_settings']);
  $vars['contact'] = drupal_render($vars['form']['contact']);
  $vars['timezone'] = drupal_render($vars['form']['timezone']);
  $vars['submit'] = drupal_render($vars['form']['submit']);
  $vars['delete'] = drupal_render($vars['form']['delete']);

}

Code for style.css

Add the following to style.css:

/**
* Custom form layout.
*/
#user-profile-form legend {
  font-size: 1.2em;
}

#user-profile-form .form-column-1,
#user-profile-form .form-column-2 {
  float: left;
  width: 45%;
}

#user-profile-form .form-column-1 {
  margin: 0 22px 0 0;
}

Code for user-profile-form.tpl.php

Create a new file in your theme's directory called user-profile-form.tpl.php and paste in the following code:

<div id="user-profile-form">

  <div class="form-column-1">
    <?php print $account; ?>
   
    <?php print $contact; ?>

    <?php print $timezone; ?>
  </div>
 
 
  <div class="form-column-2">
    <?php print $theme_select; ?>
   
    <?php print $picture; ?>
   
    <?php print $signature_settings; ?>   
  </div>
 
  <?php print $submit; ?>
  <?php print $delete; ?>
 
  <?php print drupal_render($form); ?>

</div>

After you add all the code and files, you will need to rebuild the theme registry in order for Drupal to load the new template file. The easiest way to do this is using Admin Menu module (top left icon) or the Devel block offered by Devel module. If you don't have either of these (though you should) then you can rebuild the theme registry by either 1) Visiting the main Themes page and saving it without any changes, 2) Visiting the Modules page, or 3) Going to Administer > Site configuration > Performance and clicking the "Clear cached data" button.