The purpose of theming is to generate the final HTML for display.
There are several modules that make Theme development easier.
Using the Theme Developer Module
Of course, when you're first starting out with Drupal, you'll need to get an idea of where the code is located and what you need to override in the first place. The Theme Developer module (http://drupal.org/project/devel_themer) is the perfect tool to help you figure this out. Once enabled, a checkbox will appear in the bottom right corner of the page. When clicked, a semi-transparent, resizable, and draggable window appears in the top right corner of the page. You can then move it around and click on any element of the page and the window will populate with all the information you need to know—and more
Shutting off caching does not affect the theme cache, therefore you either need to
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.
There are two ways to convert Render Arrays into actual HTML markup—Template Files and Template Functions.
Print the variables array
Print the variables array with the HTML markup
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:
print $title;
print t("Hello world");
hide($content['links']);
render($content);
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.
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.
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()
.
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.
For example, let’s say Drupal is attempting to theme a block. It will look in this order:
block--module--delta.tpl.php
(a delta is just a specific block created via the UI or by a module).block--module.tpl.php
(block--block.tpl.php applies to blocks created in the UI.)block--region.tpl.php
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 uses the same process as Blocks, except the names are:
node--nodeID.tpl.php
node--nodeType.tpl.php
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)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:
page--directory--subDirectory.tpl.php
page--directory--%.tpl.php
page--directory.tpl.php
(ex: page--node.tpl.php for any node)page--front.tpl.php
page.tpl.php
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()
.
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
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.
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.
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.
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.
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.
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.
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???).
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')); }
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'); }
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 are generated via <?php print render($page['custom']); ?>
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.
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.
; 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
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="">