Chapter Three LLC

theming

HOWTO: Utilize Drupal 6-style "preprocess" theme architecture in Drupal 5

Josh Koenig

Once of the biggest advances “under the hood” in Drupal 6 is the addition of the preprocess architecture to the theme layer. This is part and parcel with the deeper embedding of template files, and together they render Drupal 6 the most flexible and powerful release yet in terms of theme and design.

For those of us who still do a fair amount of work with the 5.x branch, it’s easy to be envious of those lucky enough to have these new tools at their disposal. But resist that cardinal sin! This quick HOWTO explains how to quickly turn your theme into a node-preprocessing machine, which has great benefits in terms of elegant architecture, and also future-proofing your work for the eventual Drupal 6.0 migration.

Bulding off _phptemplate_vars()

Possibly the most powerful programmatic tool in the Drupal themer’s toolkit in 5.0 is the _phptemplate_vars() function. We’ve talked before about to use this to use different template files under different circumstances. Overall, this function is a great way to make all those minor changes that are needed so that a site can have truly top-notch appearance and user-interface.

However, once your theme gets to be very complex, this function can easily become an overloaded beast of logical branches. Each new node type or special case creating new complexity, and maintaining that as your site grows can quickly become untenable.

Fear not, though. You can also use this function to implement a simple preprocessing architecture that will help you customize the data in all your node types without creating a mass of template files, and set the table for 6.0. Here’s how:

<?php
function _phptemplate_variables($hook, $vars = array()) {
  switch (
$hook) {
    case
'node':
     
$node = $vars['node']; // handy shorthand
     
$preprocess = 'phptemplate_'. $node->type .'_node_vars';
      if (
function_exists($preprocess)) {
       
call_user_func($preprocess, $vars);
      }
      break;
    case
'page':
    

 
}
}

function
phptemplate_blog_node_vars(&$vars) {
 
// your custom preprocessing here
 
$node = $vars['node']; // handy shorthand
 
drupal_add_css($vars['directory']. '/custom_blog_style.css'); 
 
$vars['submitted'] = t('Blogged by !name on !date', array('!name' => l($node->name, 'user/'. $node->uid), '!date' => format_date($node->created, 'custom', 'm-d-Y')));
}
?>

Note that you don’t need to use phptemplate_ as the prefix here; indeed it may be advisable to use your theme_name.

What this function does is quickly check for the existence of a node-specific preprocessing function, and if it exists passes through the $vars array by reference.

In this case we detect that a blog node is being rendered, and take the opportunity to include a final custom stylesheet form our theme, as well as altering the standard $submitted var.

The possibilities here are endless, and in “enterprise theming” situations where you may be dealing with upwards of 25 (or even 100!) node types, having this kind of programatic structure in your theme is invaluable for keeping things clean, elegant and extensible.

Good luck, and happy drupal theming!

The Day You Dress Yourself: The New Chapter Three Website

Matt Cheney

There comes a time in the life of every young child when begin to dress yourself. No longer are you content with the dorky collared shirt your mother laid out for you each morning or the matching shoes you and your siblings wore because it was just easier that way. Instead, you yearn for a little class, a little flash, and for the older kids some san francisco style.

In this spirit, for our first year and a half of doing business, Chapter Three has rolled with a functional and utilitarian website design created by, well, Josh’s mother. A great graphic designer in her own right, Josh’s mother helped us with an initial design and it helped kick off our successful business.

However, with no disrespect to Jeremy Bentham or Josh’s mother, we recently put our collective heads together with the inspired graphic artist Monica Katzenell and are launching a new website. The new design is a little sleeker and gives a lot more information throughout the site about who we are as a company, the projects we have done, and the way we have participated in the Drupal community.


To make it all happen, on a technical level we make moderately liberal use of the Drupal block system and created several of our pages with the soon to be dominate Drupal content layout system called Panels module. Much of the image resizing is done through Imagecache and there is some neat integration with the Bio Module to generate our About Page listing and store biographical about everyone who works for the company.

Now that we are all dressed up, anyone want to dance?

Drupal 6.0: More Designer-Friendly Than Ever!

Josh Koenig

With the release of Drupal 6.0, there have been major steps forward in the theme layer. Two of the most important are the standardization of template files and their associated pre-process functions, and the addition of theme.info files which allow the overriding of whole core stylesheets.

This Sunday I gave a 45 minute overview lesson on these topics for The Drupal Dojo. There will be more later, but in brief I think with this core advance, all that remains for Drupal to be a truly designer-friendly platform is better documentation of best practices.

Check out the screencast for details.

HOWTO: Fully Theme and Customize the Drupal User Registration Form

Matt Cheney

Just a little to the left please. Flip it around. Put that on top of this. Call it by a different name. It is the little changes, that seem trivial and small, that often end up being real headaches to make and support our clients in making. Do we really want to try to build capacity with clients by teaching them to adjust #weight in hook_form_alter?

The Drupal Theming System is pretty powerful and, when done right, can offer a good avenue for our clients and their staff to edit, modify, and change their own website content. Its a lot easier to modify HTML files than Drupal module files.

A good example of where this kind of process is needed is on the user registration page. There are a lot of little bits of language and ordering to change and add, but to do so in Drupal module code can get a little hairy. Observe our technique to abstract the user/register form into a flat template file (while maintaing most of the other good Drupal goodness).

Step One: Create a theme override in your module code for the user/register form that executes a _phptemplate_callback to use a separate template file.

<?php
function theme_user_register($form) {
 
$vars = array();
 
$output _phptemplate_callback('user_registration_form', $vars);
 
$output .= drupal_render($form);
  return
$output;
}
?>

Step Two: Expand the theme override function made in step one to remove the titles and descriptions Drupal provides for the form elements. We do this in the theme function (instead of in a hook_form_alter) to preserve the original field titles so they can be used as part of any error messages coming out of form validation.

<?php
 
foreach($form as $key => $value) { // loop through top level
   
if (is_array($form[$key])) {
     
$form[$key]['#title'] = '';
     
$form[$key]['#description'] = '';
      foreach(
$form[$key] as $key2 => $value2) { // loop through second level
       
if (is_array($form[$key][$key2])) {
         
$form[$key][$key2]['#title'] = '';
         
$form[$key][$key2]['#description'] = '';
        }
      }
    }
  }
?>

Step Three: Create "rendered" versions of each of the form elements and set them as variables that can be passed to the template file.

Note: This can also be done with a generic foreach loop (similar to the one in step two) that renders each form element automatically.

<?php
 
// Set up the Vars Array
 
$vars = array();

 
// Render Specific Fields You Want on Your Registration Form
  // note - the specific location of the element in the form array varies
 
$vars['name_element'] = drupal_render($form['account']['name']);
 
$vars['mail_element'] = drupal_render($form['account']['mail']);
 
// continue for each field you want...

  // Don't Forget the Submit Button 
 
$vars['submit_button'] = drupal_render($form['submit']);

?>

Step Four: Create a template file in your site's theme directory to build the user/register form with the customized variables we defined in step three.

Note: This file needs to be the same name as specified in the _phptemplate_callback (example: user_registration_form.tpl.php).

<div class="user-register-element">
  <label>Enter a screen name:</label>
  <div class="user-register-element-input">
    <?php print $name_element; ?>
  </div>
  <div class="user-register-element-description">
    Screen names can be up to 13 characters in length.
  </div>
</div>

<div class="user-register-element">
  <label>Enter an Email:</label>
  <div class="user-register-element-input">
    <?php print $mail_element; ?>
  </div>
  <div class="user-register-element-description">
    Emails must be valid.
  </div>
</div>

<?php // continue on for each rendered form element ?>

The drupal magic here is that the user registration form is now uniquely customizable by anyone who can edit the theme template. This allows for customized "prompts" for each profile field element, without changing the site-wide field name in admin/user/profile, and it allows for customization of the username and email titles and descriptions.

This technique will need to be modified to support external modules that modify the user/register form like LoginToboggan. It also needs to take into account things like "required" fieldstates.

Navigation Usability Goodness: Menu Trails Module

Josh Koenig

Songbird Starts Walking

Navigation. Without it, we’d all be lost. And yet, for many of us power-users site navigation can be something of an afterthought, with “a useful set of links” falling somewhere below “hackable urls” on our personal list of priorities. But if you’re going to try and win over the browsing public, you’d better have a good menu system.

When dealing with simple brochureware or blog sites, Drupal core’s menu module provides everything you need right out of the box. For design-heavy sites with a simple navigation tree, workable menus can be baked direct ly into the theme. However, keeping users oriented becomes increasingly important, and increasingly difficult, as your web-empire spreads and sprawls beyond what menu.module provides and what a designer can code into a theme file.

With that in mind, Chapter Three is happy to announce Menu Trails module, developed with sponsorship from the uber-cool cats (uber-flatulent raptors?) at Songbird.

Songbirdnest.com is exhibit A of a growing web-empire, making use of Bugzilla, Trac, and Drupal to manage the online life of their open-source media player/browser mashup. With several sections, sub-sections and more in their site plan, we realized it was time to transcent theme-based tweaks and add some common-sense usability to Drupal’s menu system:

  • Menu Trails implements primary/secondary links which keep the current menu trail “active” or highlighted. A handy snippet ready to go into your template.php is included.
  • The module provides a means of broadly categorizing nodes (by type or taxonomy) as falling “under” an existing menu item. These nodes are not added to the menu tree (keeping the menu admin system sane) but they will trigger the functionality above — preserving navigation state for the user — when viewed.

How is this useful? Compare:

Before:
Before Menu Trails

After
After Menu Trails

Makes a big difference, right? After talking to pwonlan about this functionality in IRC, the theme layer part — giving parent menu items a CSS class so they can be highlighted — will likely be in Drupal core for version 6.0.

For now the module is checked into CVS. We’re going to be doing a bit more tuning, and I want to see how feasible it would be to integrate somehow with views, so that nodes can be assigned to fall “under” any view which generates a menu item. I expect to package a 5.0 release later this week. Stay tuned to the project page for more on this.

Songbird Finds a Tree!

HOWTO: Write Cleaner tpl.php Files

Farsheed Hamidi-Toosi

The PHPTemplate system in Drupal is a powerful theming system. However, because it is written in PHP it is easy to abuse since it is possible to directly inject PHP logic into the templates. This often creates the effect of HTML-IN-PHP which is not good.



HTML-IN-PHP:

<?php
print '<h2 class="title">'. $title .'</h2>';
?>

PHP-IN-HTML:

<h2 class="title">
<?php print $title ?>
</h2>

Which do you think a themer who understands HTML/CSS will understand better? Of course the second, because it is mostly HTML and the themer can modify the HTML tags quite easily.

Conditional Statements

Another example of this is with logic statements.

HTML-IN-PHP:

<?php
global $user;
if (
$user->uid) { ?>


<h2 class="title">You are now logged in!</h2>

<?php } ?>

PHP-IN-HTML

<?php global $user; ?>
<?php if ($user->uid): ?>

<h2 class="title">You are now logged in!</h2>

<?php endif; ?>

See how the second example is easier to read? It’s also broken up into small pieces that a themer can move around quite easily. Having open and closing braces for an if statement is really hard to follow. But if(conditional): and endif; statements are easy to read, and “chunkify” or “modularize” the text in a clear way.

<?php if ($test == TRUE): ?>

<!— Block of HTML to printout if TRUE —>

<?php endif; ?>

PHPTemplate variables

The last thing that can make your life and the themer’s life easier is to pass variables to the template files using phptemplate_variables. A tpl.php really should be mostly simple conditional statements and print statements. If you have PHP code spanning multiple lines or are working with arrays or something, you can probably pull that code out of the tpl.php and put it in template.php.

Example Node.tpl.php with too much PHP

<?php
if ($node->field_related[0]['nid']) {
 
$related_cck_node = node_load($node->field_related[0]['nid']);
  print
'<div class="related-cck-link">';
  print
l($related_cck_node->title, 'node/' . $related_cck_node->nid);
  print
'</div>';
}
?>

Why is all that logic in the tpl.php? You can move that out into template.php.

Using Template.php to add variables to your tpl.php files

<?php
function _phptemplate_variables($hook, $vars = array()) {
  switch(
$hook){
    case
'node':
      if (
$vars['node']->field_related[0]['nid']) {
       
$related_cck_node = node_load($vars['node']->field_related[0]['nid']);
       
$vars['related_cck_link'] = l($related_cck_node->title, 'node/' . $related_cck_node->nid);
      } 
      break;
  }
  return
$vars;
}
?>

Now the node.tpl.php file will get a new variable called $related_cck_link which you can easily print out.

Example Node.tpl.php file with logic removed

<?php if ($related_cck_link): ?>
  <div class="related-cck-link">
    <?php print $related_cck_link ?>
  </div>
<?php endif; ?>

HOWTO: Quickly Truncate a Block of Text

Farsheed Hamidi-Toosi

When theming things I run into situations where a block of text is too long and I need a really quick way to truncate text and append an ellipses or custom trailing text. The built in drupal teaser truncates text in a much more logical manner (watches out for html tags and the like) while this php snippet is simpler and meant to run on pure text. You can do strip_tags() if you want to remove all the html tags before running this code on your piece of text. I often just drop this into my template.php and use it in various spots in the theme.

<?php
/**
* Truncate the string if it is beyond a certain $length and append with an ellipses or custom text
* $length is the number of characters allowed before truncating
* $append is appended to the truncated string
*/
function your_theme_custom_truncate($string = '', $length = 30, $append = '&#8230;') {
  return
strlen($string) > $length ? trim(substr($string, 0, $length)) . $append : $string;
}
?>

Before:

Suspendisse potenti. Ut tempus auctor libero. Aliquam molestie dolor quis lectus. Curabitur et erat eget lorem nonummy ultrices. Mauris interdum. Etiam imperdiet viverra purus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Mauris sem mauris, feugiat at, dictum vitae, placerat nec, odio. Nulla fringilla. Morbi justo nulla, commodo quis, ultricies a, eleifend eu, ante. Morbi eget erat eu ante tincidunt interdum. Nullam at est. Nulla ultrices pede vitae sapien bibendum bibendum. Quisque turpis enim, ullamcorper at, fermentum quis, vulputate quis, libero. In facilisis, dui eget accumsan molestie, justo metus tempor quam, eu tempor ipsum eros a urna. Donec aliquet. Curabitur condimentum volutpat augue. Praesent bibendum commodo enim.

After:

Suspendisse potenti. Ut tempus auctor libero. Aliquam molestie dolor quis lectus. Curabitur
et erat eget lorem nonummy ul…

Syndicate content