
Achievements 7.x-1.4: Using #theme_wrappers
The Drupal Achievements module offers the ability to create achievements and badges similar to systems seen on Xbox 360, Playstation 3, Foursquare, Gowalla, GetGlue, and more. For a Drupal site, this could mean commenting a certain number of times, starting a forum topic, visiting the site every day of the week, or anything else that can be tracked and coded. The recently released 7.x-1.4 update focuses on changes that make theming easier.
One of those changes was implementing #theme_wrappers. Like many other modules, Achievements has various HTML displays that are essentially containers of things: a group of unlocked achievements, a group of categorized achievements, etc. For stronger theming, one usually wraps those in an extra <div> so that CSS folks can control the container's display. In the past, I've usually accomplished this with:
$build['achievements'] = array(
'#prefix' => '<div class="achievement-groups">',
'#suffix' => '</div>',
);
Simliar code to the above exists in Drupal core (book.module, field.module, etc.) so I never gave it a second thought. However, for a themer, the above is problematic because there's no way to tweak that HTML without dipping into a hook_page_alter() and rewriting or munging the #prefix entirely. Since I've been away from Drupal theming for so long, I never knew there was a stronger alternative by using #theme_wrappers. Not surprisingly, Drupal core uses this pattern alongside the "bad" #prefix version, so a good contender for a "Novice" patch would likely be "Replace all uses of #prefix divs with #theme_wrappers".
Like other theme functions and files, #theme_wrappers requires a hook_theme() definition:
function achievements_theme() {
return array(
'achievement_groups_wrapper' => array(
'render element' => 'element',
),
);
}
In this case, we'll use a regular ol' theme function for the callback:
/**
* Default theme for the wrapper around a user's achievements page.
*
* @param $variables
* An associative array containing:
* - element: A render containing the user's achievements page.
*/
function theme_achievement_groups_wrapper($variables) {
return '<div id="achievement-groups">' . $variables['element']['#children'] . '</div>';
}
And finally, we can tweak the above $build render array to use it:
$build['achievements'] = array(
'#theme_wrappers' => array('achievement_groups_wrapper'),
);
In this case, the resultant HTML will be exactly the same, but now a themer can more easily override it just by defining THEMENAME_achievement_groups_wrapper(). This is a far cheaper method (mentally and performant-wise) than having to futz with hook_page_alter().

