Tuesday, January 29, 2013

A Block to the Head (in Drupal 7)

This is going to be a super specific & technical post, because it took me way too long to figure out how to do this & someone else could probably stand to benefit from it. For the tl;dr skip to the code section.

The Scenario

In general, the <head> of your HTML document is not editable in Drupal. The <title> changes to reflect what page you're on, sure, & tags are added or removed by modules frequently, but you can't tweak the markup in the same way you can the contents of a block or main content. For the most part, that's fine; 90% of the <head> should stay the same from page to page, such as site styles & must-have <meta> tags like viewport. If you need to change something site-wide, that can be done through theme templates. But sometimes that's not enough; what if I want to add different <meta> tags to different pages? Or load variegated builds of Modernizr on variegated pages? Or use a special font on a set of pages? There are numerous reasons why you might want a specific subset of pages to have a bit of custom markup; in short, you want to put one of Drupal's blocks in the document's <head>. Without further ado, here's how you do that.

The Code

In yourtheme.info, find the list of regions & add one for your new <head> region:
regions[html_head] = HTML Head
The name that you'll use in the code has to be a valid PHP variable name, so no spaces.

In your theme's template.php, add the following to the yourtheme_preprocess_html function:
$variables[ 'html_head' ] = block_get_blocks_by_region( 'html_head' );
If you're using a subtheme without a template.php, you can create it & write the function like this:
function mastertheme_subtheme_preprocess_html(&$variables) {
  /* so that html_head is available in html.tpl.php */
  $variables[ 'html_head' ] = block_get_blocks_by_region( 'html_head' );
}

In html.tpl.php, your theme's HTML template, find the spot where you want the block's markup to be inserted & add:
<?php if ( $html_head ): ?>
  <?php print render( $html_head ); ?>
<?php endif; ?>
If you're using a subtheme where html.tpl.php doesn't exist, copy the master theme's html.tpl.php into the templates directory & then add the above.

Right now, that would be enough to insert some code into the <head> but the code will likely be wrapped in problematic tags that don't belong in the <head> like <div> & <section>. To get rid of that junk, we create a new region-specific block template that only prints out the contents of the block & nothing else. Create a block--html-head.tpl.php file & put it in the templates directory with the following line as its only contents:
<?php print $content; ?>
Now clear the cache & add a block to the new region; it should appear in your site's <head>.

If you've named your block something other than "html_head" then you'll need to change the references throughout, but this should work for any Drupal 7 site. Note that if you rename a region (or perhaps otherwise screw with its templates? not entirely clear to me), all the blocks you had previously assigned to it become unassigned. That was what wasted the majority of my time; I couldn't understand why my block wasn't showing up when the $html_head variable should have been available but the block had been unassigned during my shenanigans.

The References

The Drupal Answers thread Printing regions in html.tpl.php provided most of the special sauce for this one, specifically the idea to store the return value of block_get_blocks_by_region in a variable that's accessible later when html.tpl.php runs.
The template_preprocess_html, block_get_blocks_by_region, & html.tpl.php API documents all provide useful reference material.
Lastly, the drupal_add_html_head function appears to provide another avenue to the same destination. However, it's much more convenient to store markup in a block. I also want to write straight HTML & not Drupal's weird "renderable array" content, which is what the function takes as a parameter.

5 comments:

  1. it must be regions[html_head], not region[html_head] in the yourtheme.info

    ReplyDelete
    Replies
    1. Yes, you're right about that. Thanks, I'll fix the post.

      Delete
  2. It helped to add google analytics for multi-domain drupal environment.
    Very useful post. Thanks.

    ReplyDelete
  3. Спасибо огромное! Помогло вставить пользовательские данные для оформления конкретной страницы. Теги пришлось удалять путем создания шаблонов для views.

    ReplyDelete