The Theme System's Architecture
The theme system was briefly introduced in the first chapter. In this chapter, we will start out with a more detailed overview of the theme system's architecture.
With each major release of Drupal, the theme system has enjoyed significant revision, and version 6 of Drupal is no exception. The new release of Drupal has introduced theme hook registration, theme .info
files, and other features. We will make use of these new features in this chapter.
The goal of most theming systems—and Drupal's is no exception—is to separate the design elements from the processing logic. Doing so achieves two important (and related) goals:
- A new look and feel can be created without the need to re-write any of the processing code. This makes the theme creator's life easier—one need not possess detailed knowledge of Drupal's internals to change the way the site looks.
- Program logic is easier to maintain. When code isn't cluttered with layout elements (like HTML tags and CSS properties), the code can be cleaner and more readable. This, in turn, makes it easier to make changes to the code base.
To achieve this separation of the user-interface layer from the underlying processing logic, Drupal employs a complex theme system, which makes use of the module architecture we discussed in the previous chapters.
There are three main elements of the theming system that we will cover here: themes, theme engines, and hooks. These work in cooperation with the Drupal core system, and also with individual modules (which make use of hooks to interact with the template system).
The basic architecture, as we look at the theme system, is illustrated in the following figure.
Themes provide the look and feel for the user interface. Typically, they require an underlying theme engine as an interface between the Drupal core and the theme templates. Hooks, considered in the context of the theme system, provide a convenient method for modules and the theme system to interact.
Note
Not all themes use a theme engine. For example, the Chameleon theme, included in Drupal 6 by default, bypasses the theme engine layer. Instead, it implements the appropriate theme hooks in a PHP file (drupal/themes/chameleon/chameleon.theme
). These hooks simply return chunks of HTML. Additional styling is done with CSS.
Theme Templates
Of the three main elements of the theming system, the themes are the easiest to understand.
Themes are usually composed of a collection of templates, plus a handful of supporting files like images, CSS files, and occasionally JavaScript files as well. Here, we will focus on templates using the PHPTemplate engine. We will use CSS when we create a theme later in the chapter.
A template provides layout information for particular pieces of data—elements like blocks or nodes. So when a typical Drupal page is rendered, several templates are involved (one for the overall page layout, one for node content, one for block content, and so on). Often, templates format data as HTML, though there is no specific requirement that HTML be the target output language.
Most Drupal templates are written in the PHPTemplate"template language", which is actually just a specially formatted PHP file with access to a limited number of Drupal variables.
Following is an example template from Drupal's Garland theme, one of the templates included by default in Drupal 6. This template is stored in themes/garland/block.tpl.php:
<?php // $Id: block.tpl.php,v 1.3 2007/08/07 08:39:36 goba Exp $ ?> <div id="block-<?php print $block->module .'-'. $block->delta; ?>" class="clear-block block block-<?php print $block->module ?>"> <?php if (!empty($block->subject)): ?> <h2><?php print $block->subject ?></h2> <?php endif;?> <div class="content"><?php print $block->content ?></div> </div>
The above code is the block template in its entirety. What exactly does this template do? It themes blocks. This template is used every time block content is rendered using the Garland theme.
Usually, blocks are embedded inside a complete page. Thus, the HTML returned from this block would be inserted into a larger HTML document. But when is this template used?
In the last chapter, we created a module that returned block content. We implemented hook_block()
to create a block that displayed a bookshelf from Goodreads. When Drupal renders that block (that is, when it calls goodreads_block('view'))
, it passes the object returned from the block into the theme system as the variable $block
.
The theme system, based on user configuration, determines which template engine and template files or functions should be used to render the content.
In this case, the theme system hands the block data ($block
) over to the template that we have just seen, named block.tpl.php
.
As you saw in this snippet, the attributes of the $block
object are used to populate the template. This information is not only used for displaying textual content, but also for creating identifiers, such as HTML id
and class
attributes used by JavaScript and CSS to manipulate the block on the client side. (We will look at CSS later in this chapter and JavaScript in Chapter 5.)
The structure of the template should be familiar to any PHP developer. It is composed of HTML elements with PHP code fragments inserted where needed.
For the moment, let's take a quick look at the portion of the template that displays the subject and content of a block.
<?php if (!empty($block->subject)): ?> <h2><?php print $block->subject ?></h2> <?php endif;?> <div class="content"><?php print $block->content ?></div>
In the above snippet, lines 1 to 3 determine whether a subject exists. If it does exist, it is displayed. The last line displays the content.
This is a good example of a typical template work. There is very little programming logic. The little logic that is there is usually limited to checking for the existence of content, or perhaps looping through some content. All other data manipulation is done before the template is called.
To get a good idea of how this template works in action, let's consider a simple module named goodadvice
with a block hook that looks as follows:
/**
* Implementation of hook_block
*/
function goodadvice_block($op='list' , $delta=0, $edit=array()) {
switch ($op) {
case 'list':
$blocks[0]['info'] = t('A Little Advice...');
return $blocks;
case 'view': $advice = "I'd rather have a bottle in front of me ". "than a frontal lobotomy."; $blocks['subject'] = t('A Little Advice...'); $blocks['content'] = t($advice); return $blocks;
}
}
The highlighted portion in the above snippet shows the case that would be executed when a block is requested for viewing.
If the above block hook is executed with $op
set to'view'
, it will return an array that looks as follows:
array( 'subject' => 'A little Advice...' 'content' => 'I'd rather have a bottle in front of me than a frontal labotomy.' );
When the results are rendered through the block template we saw earlier, the result will look as follows:
<div id="block-goodadvice-0" class="clear-block block block-goodadvice"> <h2>A Little Advice...</h2> <div class="content">I'd rather have a bottle in front of me than a frontal lobotomy.</div> </div>
Accessed from a browser, where all of the Garland theme's stylesheets are applied, it looks like this:
As you can see, some of the block data was used to generate an ID (block-goodadvice-0
) and a class name (block-goodadvice
). Then, the $block->subject
and $block->content
information was used to render the title and text of the block.
Templates like the one we just examined make up the core of most themes. But themes are also composed of stylesheets, images, and other auxiliary files. We will look at creating a custom theme, complete with templates, in the second part of this chapter, Creating a Custom Theme. But before we go there, we need to look at the engine that drives template rendering.
Theme Engines
In the above section, we looked at a template written as a PHPTemplate
. This is one particular template "language." There are other template languages as well, such as the well-known PHP Smarty template language (http://smarty.php.net/ ) and the Xtemplate language used by default in older Drupal 4 versions. Is it possible to use one of these template languages instead of the default PHPTemplate?
The short answer is yes. New template languages can be supported by creating a custom chunk of code that implements the appropriate hooks and handles passing information from Drupal modules into the appropriate template or templates. This chunk of code is not, as we might expect, implemented as a module. Instead, it is implemented as a theme engine.
A theme engine is a special set of files (resembling, in some ways, a module) that handles rendering of data, usually with templates. Themes are written for particular theme engines. For example, one theme may use the PHPTemplate engine, while another uses the Smarty template engine. But due to the nature of the theme code and template files, it is difficult (and not good practice) to use multiple theme engines for one theme.
Custom theme engines can be built and packaged, though installing them is a manual procedure that must be done at the command line. We won't cover building a custom theme engine in this book. If you are interested in developing an engine, the Smarty theme engine (http://drupal.org/project/smarty) is a good starting point for learning to create one.
Note
Other template engines
A list of alternative template engines is maintained on the Drupal website. At the time of this writing, there are only three: Xtemplate, Smarty, and Plain PHP: http://drupal.org/node/176129.
In this book, we will use only of the PHPTemplate engine for the following reasons:
- It is the default theme engine, and the only one packaged with Drupal 6.
- Since templates are written in plain old PHP, there is no learning curve to pick up the template language.
- It is deeply integrated with Drupal, and we can accomplish more with less effort than it would take to implement similar features with other template engines.
Along with these, the PHPTemplate engine has no external dependencies and no configuration external to Drupal.
Theme Hooks
The last major piece of Drupal's theme system is the theme-specific hook support. In the last two chapters we've already discussed hooks at some length. Hooks are used as a sort of callback mechanism used to facilitate inter-module interaction with a high degree of flexibility.
The theme system, too, makes use of hooks for this purpose. It does so in some interesting ways:
- Themes and theme engines implement hooks for various purposes such as registering, handling a particular request for display, and so on.
- Template files do, to a certain extent, behave like hooks. Just as Drupal searches for hook functions, it may, depending on the configuration, search for template files that follow certain naming conventions, and load those instead of executing a hook.
- Modules may implement theming hooks either to make certain data available for formatting by the theme system, or to perform some amount of theming themselves. For example, the
syslog
module included with Drupal 6 makes use of some theming hooks in order to format log file output.
We will make use of some theme hooks later in this chapter. Also, as we work on our templates in the next section, we will see several theme hooks in action.
Note
Most other chapters in this book make use of theme hooks, too. In coming chapters, you will see such hooks used to add default theming to new content and even to theme non-HTML content like email messages ( Chapter 8).
At this point, we've covered the basic architecture of the theme system. We spent a little more time on themes themselves—especially templates. This introduction will serve as the bedrock for our coming work.
Next, we will create our first theme.