Drupal Theming

Preparing for Theming Work

Overview

The purpose of theming is to generate the final HTML for display.

Modules

There are several modules that make Theme development easier.

Rebuilding Theme Cache

Shutting off caching does not affect the theme cache, therefore you either need to

  1. clear all caches each time something changes with your theme, or
  2. check “rebuild the theme registry on every page load” using the Devel module.

Theming Engine

The default theming engine in Drupal 7 is PHPTemplate. Before we get to that, let’s back up a second and talk about what theming really is. The point is to allow the designer to layout and structure the HTML in any manner they want. For example, I have a list of primary navigation links: Home, Products, Services, and About Us. Some designers may need those structured as:

<nav class="primary">
	<ul>
		<li><a href="index.php">Home</a></li>
		<li><a href="Products.php">Products</a></li>
		<li><a href="Services.php">Services</a></li>
		<li><a href="About-Us.php">About Us</a></li>
	</ul>
</nav>

A different designer/website may need it structured as:

<nav class="primary">
	<a href="index.php">Home</a>
	<a href="Products.php">Products</a>
	<a href="Services.php">Services</a>
	<a href="About-Us.php">About Us</a>
</nav>

The way Drupal provides this flexibility is by passing content and metadata to the theme engine. In this example, the content is probably an array of Page names and their associated links. The metadata could be styling cues such as "create a verital list of links" vs. horizontal. This combination of content and metadata is called a Render Array in Drupal parlance.

Renderable arrays have two kinds of key/value pairs: properties and children. Properties have keys starting with '#' and their values influence how the array will be rendered. Children are all elements whose keys do not start with a '#'. Their values should be renderable arrays themselves, which will be rendered during the rendering of the parent array. The markup provided by the children is typically inserted into the markup generated by the parent array.

Methods for Theming

There are two ways to convert Render Arrays into actual HTML markup—Template Files and Template Functions.

Tip: Print out available variables

Print the variables array

<?php
print '<pre>';
print_r(get_defined_vars());
print '</pre>';
?>

Print the variables array with the HTML markup

<?php
print '<pre>';
print htmlspecialchars(print_r(get_defined_vars(), TRUE), ENT_QUOTES);
print '</pre>';
?>

Template Files

Purposely keeping your Template Files simple will serve you well. Specifically, the tipple files (*.tpl.php) should contain HTML tags and very basic PHP. You PHP should be limited to:

You can use some if statements in the tipple file to conditionally display information based on the value of variables. Some are global variables and are available for every single tipple file to use. They are:

$is_admin, $logged_in, $is_front, $directory, $user, $language, $theme_hook_suggestions, $title_prefix, and $title_suffix.

Others are defined by various modules.

The naming standard for the tipple files controls which one is used to perform the actual theming. See How to Override Template Files for more information.

Template Functions

Like tipple files, the name of the function will control which theming function is used to generate the final HTML. The naming syntax is theme_themeHookName() where themeHookName is defined by hook_theme(). For example,

function myModule_theme() {
	return array(
		'sdf' => '

 

Many standard theme functions can be found in /includes/theme.inc

You can find a list of themeable functions on the Drupal.org site.

theme(themeFunction, variables)

The theme() function will kick-off the specified theming function to generate the final HTML based on the variables passed into theme(). That said, it is recommended that you call drupal_render() instead of theme().

*.tpl.php Files — How to Override Template Files

The template file that Drupal chooses to use is based on what is getting themed. It will use the most specific Template Suggestion it can find. When looking at the “Theming Blocks” and “Theming Nodes” sections below you will notice that each of those modules (yeah, that core functionality of Drupal is just a module, not unlike a custom module you might build) includes a default implementation of a template file (block.tpl.php and node.tpl.php). Those template files are used to style that type of content, unless a theme developer overrides it with their own version of a *.tpl.php file.

Theming Blocks

For example, let’s say Drupal is attempting to theme a block. It will look in this order:

  1. block--module--delta.tpl.php (a delta is just a specific block created via the UI or by a module).
  2. block--module.tpl.php (block--block.tpl.php applies to blocks created in the UI.)
  3. block--region.tpl.php
  4. block.tpl.php (if this is in your theme directory it’ll be used, otherwise it’ll use the default found at /modules/block/block.tpl.php)

This process is great for tweaking your theme, but what about tweaking the display for a block created by a custom module? It may be possible using hook_theme_registry_alter(), but honestly, it just may not be worth going through the trouble. Just shove your styling into the current theme instead.

Also one more example with Views, if you have a block created by views with a view name "front_news" and display id "block_1" then the theme hook suggestion would be: block--views--front-news-block-1.tpl.php (notice, when you have underscores in a display id or in a view name - you have to transform them in to a single dash.)

Theming Nodes

Theming nodes uses the same process as Blocks, except the names are:

  1. node--nodeID.tpl.php
  2. node--nodeType.tpl.php
  3. node.tpl.php (if this is in your theme directory it’ll be used, otherwise it’ll use the default found at /modules/node/node.tpl.php)

Theming Overall Structure of a Page

There are two template files that control the overall structure of a page (kinda like Dreamweaver templates) — html.tpl.php and page.tpl.php. It is pretty rare (I think) to modify the default html.tpl.php file. page.tpl.php, on the other hand, is used to control the visible structure of your page and will definitely need to be created for a custom theme.

For a list of overrides and when they are used, check out page 307 of DGD7. To whet your appetite:

Template Suggestions

Drupal sets some default Template Suggestions for you automatically, but as a module developer, we are free to add to the list of suggestions. The Template Suggestions are defined in template_preprocess_hook, so for Blocks, you can find it in template_preprocess_block().

General Appearance and Settings

Global Theme Settings

Drupal comes with some theme settings that can be configured in the administrative interface. This is where most of the site identity assets are defined, as well as a couple of other miscellaneous settings. A Global Settings page located at admin/appearance/settingscontains these settings. When global settings are saved, the settings apply to all themes. Each theme also has its own Settings page accessible via a Settings link located next to each enabled theme on the admin/appearance page. When theme settings are applied on an individual theme's Settings page, they override the global settings. The following sections will detail what each of these are and where you'll encounter them in your themes.

Quite a few of these settings determine whether or not variables are populated and therefore printed in template files. The settings pictured inFigure 15-6 represent the defaults provided by Drupal. These can be overridden by themes by defining features in the theme's .info file, which is discussed further in the "Defining Theme Metadata" section. When specifying features in .info files, you'll need to make sure you include all the features you want to support, as having just one will override all of the defaults provided by Drupal. The following is a quick reference of these settings as they'd be entered in a .info file:


features[] = logo
features[] = name 
features[] = slogan 
features[] = favicon 
features[] = main_menu 
features[] = secondary_menu 
features[] = node_user_picture 
features[] = comment_user_picture 
features[] = comment_user_verification

Image from book 
Figure 15-6: The Global Settings page

Logo

By default, Drupal will look for a file named logo.png in the root of the theme directory. There is also an option to specify a path to a different file to use for the logo, as well as the ability to upload a logo to use. When the Logo checkbox is checked, a variable called $logois populated with the path to the logo, which will be available in page.tpl.php. If unchecked, the logo will not print.

Name and Slogan

The site name is defined during the installation process. Both the site name and slogan can be changed on theadmin/config/system/site-information page. On the theme's Settings page, you can toggle their visibility. Both are available inpage.tpl.php as $site_name and $site_slogan.

Shortcut Icon

The shortcut icon, also known as the favicon, is the small Drupal icon that appears in the address bar, bookmarks, and tabs of most browsers. Like the logo, the shortcut icon's visibility can be toggled and a custom file can be used. The default file is misc/favicon.ico.

User Pictures in Posts and Comments

These settings control whether or not the variables $user_picture in node.tpl.php and $picture in comment.tpl.php are populated, and therefore whether or not the pictures are displayed when viewing nodes and comments.

User Verification Status in Comments

This option will display "(Not verified)" next to the user name for users that do not have a verified account. This text is defined intemplate_preprocess_username() and printed in theme_username() as $variables['extra']. See the "Preprocess and Process Functions" and "Theme Functions" sections to learn how to change this.

Main and Secondary Menus

When the checkboxes for the Main and Secondary menus are checked, $main_menu and $secondary_menu variables are populated for use by page.tpl.php with arrays containing the menu links for each menu. On the Menu Settings page, located at admin/structure/menu/settings, you can choose which menu is used for each. By default, the Main menu, which can be managed at admin/structure/menu/manage/main-menu, is used as the source that populates $main_menu. The default menu for the source of the Secondary menu is the User menu, which can be managed at admin/structure/menu/manage/user-menu.

These are simple, one-level menus, generated by the theme_links() function in page.tpl.php. This makes them hard to use when styling complex navigation designs. Because complex navigation is often required, many theme developers create regions for navigation and use blocks to output their menus instead of using these menus. We highly recommend the Menu Block module (http://drupal.org/project/menu_block), which allows you to do pretty much anything you'll ever need to do with menus very easily.

 

Pre-Process and Process Functions

Before a page is sent to the theming engine, most of the variable content is locked up in Render Arrays. Through the use of Alter and Preprocess functions, you can modify those arrays, or add and delete content. When writing a preprocess function it uses the naming scheme:

function module_preprocess_themeHookName(&$variables) { ...

where module is the internal name of your module and themeHookName is (the Hook Suggestion name???).

 

CSS

One way to add external stylesheets is through a preprocess function on theme_preprocess_html(). Example:

// ADD REFERENCE TO EXTERNAL STYLESHEET
	function bartiksub_preprocess_html(&$variables) {
	drupal_add_css('http://fonts.googleapis.com/css?family=News+Cycle', array('type' => 'external'));
}

Javascript

Like adding external CSS, the only way to get an external Javascript file into Drupal is via the preprocess_html hook.

// ADD REFERENCE TO EXTERNAL SCRIPT
	function bartiksub_preprocess_html(&$variables) {
	drupal_add_js('http://cdnjs.cloudflare.com/ajax/libs/modernizr/2.0.6/modernizr.min.js', 'external');
}

Attaching libraries in render arrays

The #attached property allows loading of CSS, JavaScript, libraries or custom types. Specify an array of type => value pairs, where the type (most often 'css', 'js', or 'library') determines the loading technique and the value provides the options specified to the loader function. Example:

$form['#attached']['css'] = array(drupal_get_path('module', 'ajax_example') . '/ajax_example.css', );
$form['#attached']['js'] = array(  drupal_get_path('module', 'ajax_example') . '/ajax_example.js', );

Regions

Regions are generated via <?php print render($page['custom']); ?>

Default Regions

Drupal core defines nine regions for themes to utilize programmatically by default. The code in Listing 15-2 duplicates the default core regions in .info file format. Like most theme layer implementations, the reason themes define regions is because they want to modify or add to the defaults. Until a theme defines its own regions, Drupal will use the defaults. This means that if the default regions are sufficient for your design, you will not need to define regions in your theme's .info file.

Listing 15-2: Drupal's Nine Predefined Theme Regions in Chronological Order

 regions[page_top] = Page Top 
regions[header] = Header 
regions[highlighted] = Highlighted 
regions[help] = Help 
regions[content] = Content 
regions[sidebar_first] = Sidebar First 
regions[sidebar_second] = Sidebar Second 
regions[footer] = Footer 
regions[page_bottom] = Page Bottom 

However, including this code in your theme’s .info file to begin with is a good practice. Once you define a single region in your theme, it will override core defaults, so having the full list of defaults and commenting out regions that you have disabled (instead of deleting or omitting them entirely) is a good way to keep track of what you're doing with them. You will need some of these regions, namely thepage_top, content, and page_bottom regions. These are required and must be printed in every Drupal theme to maintain a properly functioning site. An example of how one might organize regions in an .info file, taking defaults into account, is shown in Listing 15-3.

Listing 15-3: An Example of Region Implementation in a Theme's .info File

; CORE REGIONS - DISABLED 
;regions[highlighted] = Highlighted 
;regions[help] = Help 
;regions[header] = Header 
;regions[footer] = Footer 
 
; CORE REGIONS - REQUIRED 
regions[page_top] = Page Top 
regions[content] = Content 
regions[page_bottom] = Page Bottom  

; CORE REGIONS 
regions[sidebar_first] = Sidebar First 
regions[sidebar_second] = Sidebar Second  

; CUSTOM REGIONS 
regions[my_custom_region] = My Custom Region

 

Showing Images

PHP Functions

url() Makes a safe URL

base_path() Returns the base location of the Drupal install. It will always include the trailing slash. In most cases, this function will always return just a “/” because most installs have Drupal installed to a top level directory of the webroot. Note: Do not combine with path_to_theme() because that function apparently already takes into account the base.

path_to_theme() Returns the directory of the current theme. Example:

<img src="<?php echo url(path_to_theme(); ?>"/img/myPic.jpg’); alt="">