Drupal Colonoscopy

Q: How many Drupal themers does it take to perform a custom field label colonectomy? A: You don't want to know.

Our journey began with what seemed to be a simple request: *How do you remove the colons from the end of field labels on a Drupal 6 site?

Should be simple, right? A CSS tweak, a function override in the theme, maybe a hook_form_alter -- standard stuff. No big whoop. Well, it was not as straightforward as we would have hoped. But, after some code snippet borrowing, a foray into regex-land, many theme function override attempts and one major "aha" moment, we've finally arrived at a solution we can all get behind (and understand).

Not all fields are created equally

I'll skip right to the punchline. The main thing to realize is that the HTML output for various fields is not all created equally or in the same place. Fields provided by Drupal core have their own theme function-- theme_form_element-- an explanation of which can be found on the Drupal API site

Take a look at the theme_form_element code on http://api.drupal.org (every Drupal themer's best friend). This code, if copied into your theme's template.php file can be manipulated to produce the output you desire:

<?php
function theme_form_element($element, $value) {
  // This is also used in the installer, pre-database setup.
  $t = get_t();

  $output = '<div class="form-item"';
  if (!empty($element['#id'])) {
    $output .= ' id="'. $element['#id'] .'-wrapper"';
  }
  $output .= ">\n";
  $required = !empty($element['#required']) ? '<span class="form-required" title="'. $t('This field is required.') .'">*</span>' : '';

  if (!empty($element['#title'])) {
    $title = $element['#title'];
    if (!empty($element['#id'])) {
      $output .= ' <label for="'. $element['#id'] .'">'. $t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) ."</label>\n";
    }
    else {
      $output .= ' <label>'. $t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) ."</label>\n";
    }
  }

  $output .= " $value\n";

  if (!empty($element['#description'])) {
    $output .= ' <div class="description">'. $element['#description'] ."</div>\n";
  }

  $output .= "</div>\n";

  return $output;
}
?>

Turns out, removing the colons from fields that originate from Drupal core modules is pretty simple. Notice there are two places where there is HTML output for field labels. Each one contains the value '!title:' That colon, in each place, is what you want to be rid of. Copy this function into your custom theme's template.php file. Rename it to be [YOUR_THEME_NAME]_form_element. Delete the two colons. Save your template.php file.

Done and done, right? Kinda, but not entirely...

CCK field theming 101

Because, if you are like the rest of us, you are creating your own content types and fields using the CCK module. The theme_form_element override does not affect any of your custom-created CCK fields. A little digging and we figured this one out.

The key revelation was the discovery that the CCK module comes packaged with its own theme folder. Who knew? Inside this folder is a file named content-field.tpl.php This file produces the HTML output for CCK fields-- in much the same way that your custom theme's node.tpl.php produces the HTML for your nodes and page.tpl.php produces the HTML for your pages.

To override the default output of this template file, leave the content-content-field.tpl.php field in the CCK module's theme folder, but also put a copy of this file into your custom theme's folder, in the same directory with your other template files. Keep the content-field.tpl.php filename the same to allow the override. If your CCK module files are not accessible to you (for example in a multi-site managed hosting environment), you can accomplish the same goal simply by downloading a copy of the CCK module from its project page: http://www.drupal.org/project/cck. I've also attached a copy of the most recent version of this file (as of 3/17/09) to this tip. Download this file below if you need it; or view/copy the relevant code here if you'd like to follow along:

<?php if (!$field_empty) : ?>
<div class="field field-type-<?php print $field_type_css ?> field-<?php print $field_name_css ?>">
  <?php if ($label_display == 'above') : ?>
    <div class="field-label"><?php print t($label) ?>&nbsp;</div>
  <?php endif;?>
  <div class="field-items">
    <?php $count = 1;
    foreach ($items as $delta => $item) :
      if (!$item['empty']) : ?>
        <div class="field-item <?php print ($count % 2 ? 'odd' : 'even') ?>">
          <?php if ($label_display == 'inline') { ?>
            <div class="field-label-inline<?php print($delta ? '' : '-first')?>">
              <?php print t($label) ?>what about one field</div>
          <?php } ?>
          <?php print $item['view'] ?>
        </div>
      <?php $count++;
      endif;
    endforeach;?>
  </div>
</div>
<?php endif; ?>

Once you have this template file in place in your custom theme folder, to follow our example task of removing field label colons, find the lines that print the field label(s)-- denoted by the $label variable. Notice the ": " which prints a colon and a non-breaking space. By deleting the ":", your surgical procedure to remove custom field label colons will be complete. Done and done, right? Sure, but why stop here?

Getting specific-- refining your CCK field theming for particular situations

This is just the beginning of what you can accomplish using this theming method. Fairly obviously, besides removing colons, you can impact the HTML in any way you wish by altering the code in this overriding template file. The commented-out text at the top of the file gives you important information about the variables you have access to:

<?php
// $Id: content-field.tpl.php,v 1.1.2.5 2008/11/03 12:46:27 yched Exp $

/**
 * @file content-field.tpl.php
 * Default theme implementation to display the value of a field.
 *
 * Available variables:
 * - $node: The node object.
 * - $field: The field array.
 * - $items: An array of values for each item in the field array.
 * - $teaser: Whether this is displayed as a teaser.
 * - $page: Whether this is displayed as a page.
 * - $field_name: The field name.
 * - $field_type: The field type.
 * - $field_name_css: The css-compatible field name.
 * - $field_type_css: The css-compatible field type.
 * - $label: The item label.
 * - $label_display: Position of label display, inline, above, or hidden.
 * - $field_empty: Whether the field has any valid value.
 *
 * Each $item in $items contains:
 * - 'view' - the themed view for that item
 *
 * @see template_preprocess_field()
 */
?>

Note, however, that this content-field.tpl.php template file, named as it is, themes the output of all CCK fields, globally across custom content types. You can become much more powerful when you take control of Drupal and CCK naming conventions and create more specifically named template files.

With a copy of content-field.tpl.php in your custom theme's directory (whether you've modified it or not) you have the ability to theme very specific field situations:

For example, making sure you do have a copy of content-field.tpl.php in your theme folder (it's important to have this file and have it named content-field.tpl.php, if you wish to theme only one specific custom field, you can add an additional template file named: content-field-[FIELD_NAME].tpl.php. For example, if the machine readable name of your field is field_custom_field, the name of your template file should be: content-field-field_custom_field.tpl.php. The same code from content-field.tpl.php will work as the base content for this file as well.

Refine this method further to isolate the impact to all the fields within a specific content type by adding a template file named: content-field-[CONTENT_TYPE].tpl.php. Or impact any single field within a specific content type with a template file named: content-field-[FIELD_NAME]-[CONTENT_TYPE].tpl.php

With this theming method at your disposal, you have quite a bit of control over CCK fields, if you determine such control is necessary. Remember, always use this power wisely and well.

Upcoming

It's nice to have a helping hand once in awhile. Each week, we make space available for you to bring your laptop in and work on your project. If you need help, we're around for questions, and so are the other lab hours participants.

It doesn't matter how smart our staff is, how much we know or how much we grow: it's just not possible for one company to be an expert in everything. Rather than pretending to have all the answers, we foster a thriving ecosystem of smart people who get a lot done by learning from each other.