When we made the move from Drupal 5 to 6, there were some nice improvements to the theme system. Preprocess functions, better sub-theming and the theme registry were all great of additions to the system. These and others helped extend what could be done with themes and made the Drupal system as a whole more powerful. How could it get any better? With the fast approaching Drupal 7 code freeze this September, it is a good time to get a sneek peek.
.info file changes
Blocks and regions have been a integral part of Drupal. Being able to configure and place content via an administration interface allows for a very flexible system. The Drupal developers have taken notice of this and expanded the scope of what can be done with these elements. To compliment this change the theme has gained more configurable content placement, more default regions and a new ability.
To start, $content is now a block and there needs to be a place for it in any theme. This means that Drupal 7 now has it's first ever required region called, appropriately, "content". If you decide to declare any regions in your theme's .info file, this one must be included in order for your theme to be selectable.
There are two more recommended regions to add to your custom themes: "help" and "highlight". The "help" region is to accommodate help now being a block element. This also means your help content will be contained in a block.tpl.php template. By default, a theme will output the help text to this region, but that can be changed in blocks administration. The "highlight" region is a replacement for the former mission statement functionality in prior versions of Drupal. Now if someone wants to place a mission statement on the page, a new block will need to be created. Eliminating either of these regions will not cause your site to break, but it will certainly decrease it's default functionality.
With this expanded use of the block system, a site can have a lot of regions available. A lot of these regions are only used for very specific modules and do not need to be muddying up the blocks administration. The solution to this is the new hidden region designation.
Implementing a hidden region is pretty simple. You declare a new region just as any other (ex. regions[secret] = Super secret region). You then need to add it to the list of hidden regions with hidden_region (ex. regions_hidden[] = secret). All together, you should have something like the following:
regions[content] = Content
regions[help] = Help
regions[page_top] = Page top
regions[page_bottom] = Page bottom
regions[secret] = Super secret region
regions_hidden[] = secretNow that you know what a hidden region is, it is time to mention two new predefined hidden regions that you may notice in the example above: "page_top" and "page_bottom". If you have to redefine these regions, there is no need to add these to regions_hidden[] since they are predesignated in the system. These are in place to accommodate some new core blocks that are non-configurable. "page_top" is for placement of the new Administration toolbar. In your template, it should be output just inside the <body> tag, above all other markup. "page_bottom" is a replacement for the former $closure variable and functions the same.
In Drupal 6, themes that do not have any stylesheets or javascript files defined automatically get assigned a style.css stylesheet and script.css javascript file. This seems all well and good, but it really caused problems when creating a subtheme based on one that used the same files. If the subtheme also is not define any of these files, it would be assigned the same defaults. This caused another feature of Drupal to activate intended to make style and javascript overrides simpler: any style and javascript files of the same name would be removed in favor of the current theme's. For that reason, in Drupal 7 all stylesheets and script files now must be defined in the theme's .info file.
Template changes
There are now wildcard template files. As an example, if you wanted to override the page template when displaying a user, you simply add a page-user.tpl.php to your theme. The problem here is that this override also overrides the user login page. Something more specific is needed, but generalized so that it applies to all user pages.
For this reason, Drupal 7 introduces wildcard templates to solve this issue. For example, to solve the above conundrum, simply add a page-user-%.tpl.php file to the theme. The "%" is a placeholder for any Drupal system id (ex. node, profile, term, etc) and is based on the default url for the element. So a page template for any node, but not the default Drupal front page of /node, is page-node-%.tpl.php. This function expands beyond the page template and can be very useful.
box.tpl.php is now gone. This template file was used to wrap the comment for for nodes. Retheming the form itself is the proper approach now.
There are also a significant amount of changes to the variables available to page.tpl.php. These changes will compliment additional functionality and change in how a page is structured.
It has been talked about for a while and it is finally coming to fruition: RDFa support for Drupal is in version 7. Since this technology requires elements in the markup of the site, there are some pieces that will need added. A big change is the default doctype for Drupal will be changing. In Drupal 6, the recommended doctype was as follows:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">To comply with the RDFa specification, it will need to be a bit different.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
"http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">To further compliment the document type, there are changes to the html and head tags also. First, the lang attribute is to be replaced by xml:lang. The same $language->language gets placed in the value. Namespaces are a crucial part of this change, and they are added with the new $rdf_namespaces variable. Simply place this at the end of the html tag. Finally, the head tag needs GRDDL information added to the profile attribute. This is done with another new variable, $grddl_profile.
When all of that is added, you will have this:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" dir="<?php print $language->dir ?>"
<?php print $rdf_namespaces ?>>
<head profile="<?php print $grddl_profile ?>">There are some changes to familiar variables too. The $footer_message variable are no longer needed for one. Other changes are purely in name only and functionality is the same:
$leftis now$sidebar_first.$rightis now$sidebar_second.$primary_linksis now$main_menu.$secondary_linksis now$secondary_menu.$pictureis now$user_picture.
Granular theming of content
One of the big changes in Drupal 7 is the addition of fields in core. This not only brings much of the power of CCK, but also allows the system more control over how the fields get displayed. With CCK in drupal 6, getting granular output involved long strings of text to output a portion of an array. There were even security risks involved due to uncleaned data. This is now simplified and secured with the render(), show() and hide() functions.
An example is the easiest way to show how the system works. Previously, node content was output with a simple <?php print $content; ?>. Now $content is an array containing all fields related to the node. The render() function takes all of this data and renders it to a proper string for output. So if all you wanted to do is show a simple, complete dump of the node fields, you would use <?php print render($content); ?> instead. This would be missing the real advantage, though.
How is this "granular"? There is another facet to this function and that is that individual elements can be output. For example, if you wanted to output just the links in a node template you would add <?php print render($content['links']); ?> to it. As a bonus, this small piece of content will be flagged as already printed. This means you can use <?php print render($content); ?> afterwards and it will output everything but the links portion.
The show() and hide() functions are simple helpers. show() is used to flag already printed content as unprinted. This will be handy for when you want an element printed a second time. hide() is just the opposite. It is used to prevent a specific element from being output when render() is run on a more generic element.
The new node.tpl.php gives a good example how this can be used. Here is an excerpt of the important part:
<div class="content">
<?php
// We hide the comments and links now so that we can render them later.
hide($content['comments']);
hide($content['links']);
print render($content);
?>
</div>
<?php print render($content['links']); ?>
<?php print render($content['comments']); ?>
If there wasn't enough fun to be had with this new system, you now have the ability to include special styling or javascript with individual elements. In the variable containing the content, simply add a value to the #attached_css or #attached_js arrays.
Currently attached styles and scripts cannot be used in a page template. This is because that drupal_add_css() and drupal_add_js() are used in this process and these functions do not work properly in the template either. There is work going on to attempt to fix this, but as of this writing, the fix has not been committed.
Granular theming is actively being used in node, comment and profile templates. We will most likely see an expansion of the implementation before the code freeze in September. The process is not limited to these areas though and can be used in other facets of Drupal as long as the data is structured properly. For instance, elements of a simple theme function could be assembled into an array and output in the same manner.
Id and Class changes
One change comes in the form of a class array. Previously, the accepted way to add dynamic classes was to embed messy logic to the template file. For example, here is how the old Drupal 6 Garland theme node.tpl.php added classes:
<div id="node-<?php print $node->nid; ?>" class="node<?php if ($sticky) { print ' sticky'; } ?><?php if (!$status) { print ' node-unpublished'; } ?>">
<?php // Extra markup removed for brevity. ?>
</div>
The new Drupal 7 version is noticeably much cleaner:
<div id="node-<?php print $node->nid; ?>" class="<?php print $classes ?>">
<?php // Extra markup removed for brevity. ?>
</div>
To add more classes to the template, you could just add them after <?php print $classes ?>, but it is now recommended to add them to the classes array in a preprocess function like so:
<?php
function mytheme_preprocess_node(&$vars) {
// Add a striping class.
$vars['classes_array'][] = 'node-' . $vars['zebra'];
}
?>This becomes especially nice for keeping a theme tidy, as there is less of a need for extra template files whose sole purpose is to add a new class. (Studio theme pack users will possibly find this method somewhat familiar.)
The class formerly known as .clear-block is now known as .clearfix. This is the more common name for the method introduced by Position is Everything. This is a simple change that will make the Drupal theme layer more inviting to newcomers familiar with the tactic.
New to Drupal and joining .clearfix in the predefined style toolbox are .element-hidden and .element-invisible. These provide ways to hide content on a site in an accessible fashion. Each is made to handle a different situation when it comes to omitting content from view. The .element-hidden class is intended for immediately hiding an element on page load, but still allowing for javascript effects to open it. This class is best added with javascript and not hard-code it into your templates. The .element-invisible is intended to hide the content on full-featured browsers, but still keep it accessible to screen readers.
There are also changes to some of the typical block ids as outlined here on the upgrade handbook page. This is in and of itself not a big deal since this sort of thing is subjective and easily overridden with a template override. The thing to take note of here is that these ids are primarily derived from the $delta variable. This means that the information contained within is more semantic and less cryptic. This is a good thing to come to core.
Advanced theming changes
In order to fix some inheritance issues and confusions, all function names in the theme must match the theme name. Previously we could declare a new theme function of the name phptemplate_preprocess_node(). This was the recommended method for base themes, but it was discovered that this led to functions being run twice and other oddities. The recommendation then changed to naming all functions after the theme. This is now a must in Drupal 7.
Preprocess was a great addition in Drupal 6 and led to much cleaner template files. Now those functions get a sibling call process functions. These functions work in much the same way. They are fired after it's complimentary preprocess function and are intended to finalize any data changes that happened earlier. As an example, if you add a class to the $vars['classes'] in mytheme_preprocess_node($vars), it will get rendered into a single string for node.tpl.php in a later process function.
People liked preprocess so much with their template files, the same has also been brought to the simple theme functions. This is complete with the new process function functionality. This will hopefully bring more abstraction of logic and display to the theme function and make them easier to understand for PHP novices. This is a very recent addition at the time of this post, so the effect has yet to be seen completely.
The drupal_add_js() and drupal_add_css() functions have gained some extra kick. The option for adding an external file has been added to both. Now instead of adding a commonly used library to your own file system, you can take advantage of services like Google's AJAX libraries for remote hosting.
These two functions have also gained weight support. This will be especially handy for creating a theme that used a CSS framework or reset stylesheet. It normally best to have this type of styling loaded before any other styles, and adding a weight to the stylesheet will make that process much easier. The final addition is for drupal_add_css() only: the ability to add inline styles. drupal_add_js() has had this functionality for some time, and now it can be used for styles. This will come into play when attempting to style an element that has a dynamic size.
The final change is that jQuery UI is now part of the core distribution. It is a welcome inclusion since it allows more graphical sheen without extra modules. There are more tools for a themer to reliably take advantage of and will allow for some more interesting effects.
Getting involved
This is just a summary of what is currently done. There is a lot more items that need work and it is not hard to get involved. Do not fear the issue queue. Even if all you can give is input, please give it. There are tags available for both design review and themer review in addition to the theme system component. There are definitely more issues if you dig deeper, but these are good places to start.
- Tags: code, Drupal, Drupal 7, theming, Design 4 Drupal









Comments
Francesco Levorato writes:
Hi there and thanks for the writeup!
I see an incogrouency under the "Advanced theming changes" chapter where you write:
"As an example, if you add a class to the $vars['classes'] in mytheme_preprocess_node($vars), it will get rendered into a single string for node.tpl.php in a later process function.".
I guess you actually meant to write $vars['classes_array'] you mentioned above?
Have fun,
Francesco
Brian Morris writes:
Al... Thanks for the excellent write-up. A lot of welcome changes in Drupal 7. Looking forward to all of them!!
johnvsc writes:
Killer write up!
This is a great, consolidated assessment of all the important things to be aware of.
Bookmarked and, i am sure, frequently referenced!
Post new comment