Drupal and Front-End Web Developer

Managing custom block assignments in Drupal with automated fallbacks

Sunday, October 9th, 2011

(Updated 2011-11-12) One of the most frequent feature requests I hear when working on Drupal sites is for special case pages that need a specific set of blocks applied to them. This, of course, can (sometimes/usually, depending on the demands) be managed through the standard Blocks interface or (probably with better luck) using Context, but when things start to get down to the “every page needs to allow for it’s own custom set” scenario things get ugly pretty quickly.

So here’s a bit of cross-module hackery that I’ve put together as a little demonstration of a way to try to handle doing this but allowing for a fallback to regular Blocks or Context (and really, if you’re not using Context go read up on that first, it’s awesome) so there can be some automation when customization isn’t required.

The Modules we’ll be abusing…

  • Block Reference – This lets us add a Field to our content types for selecting blocks that we want to allow users to associate with a piece of content.
  • CCK Blocks – This lets us take a Field from one of our content types and break it out into a Block rather than including it in the regular node body. (Right now on version 7.x-1.0 you’ll also need to apply the patch that’s attached to comment #24 on the “Cck block display settings override basic node settings” issue.)
  • Block Theme – Lets us create additional .tpl.phpfiles for block templates and provides a pulldown on the block creation/editing screen to select which template a Block should be rendered using. We don’t really need this after all for this example of stuff, but I still find it to be a helpful module for cutting down on the number of stray template files you need to keep lying around.

The basic idea…

  1. For a content type (in the example here I just used the default pagecontent type from a default Drupal 7 install) we’re going to add a Block Reference field that lets us pull Blocks into a Field as part of a node.
  2. We’re going to use CCK Blocks to take that field from Block Reference and break it back out into a Block (separate from the body of the node).
  3. In the theme (in this case a quick fakey theme that just extends from Bartik) we’re going to add a new region for custom sidebar content and keep a default field for automated assignment.
  4. In page.tpl.phpwhere it would normally just check for our sidebar we’re going to check if there’s a custom sidebar, if there is a custom sidebar then we’re going to display that and hide the default one.

Ready, Set…

First things first, get yourself a Drupal install up and running (if you don’t know how to do that, this isn’t a good starting point for you). Then go download the modules we talked about up above. It’s okay, I’ll wait…

Got ‘em? Good. Apply the patch to cck_blocksand then go ahead and enable the three of them for your site.

Next we can head on over to our pagecontent type and add a new field. In our case we’re going to name it “Right Rail Blocks” (I’ve never been sure how calling the sidebars “rails” wound up being the norm where I work, but it’s just what I’m used to at this point, roll with it.) Go ahead and give it a name and set it to be of the type “Block Reference”. For the interface widget I’ve chosen to with a select list with drag and drop sorting. This way people can choose which Blocks to display and what order to display them in.

Once you click save you’ll get taken to some basic config options (as you’d no doubt expect if you’ve ever created a custom field before). From here you can define which Blocks you’re going to allow the users to select from based on what Module provides them. This isn’t a perfect system (since it’s all or nothing by Module and not a controllable subset of specific Blocks) but we’ll wave our hands for the moment and maybe try coming back to that one in the future someday (in the meantime declare it a “content governance” issue). For now, this is what we’ve got and it’s a heck of a lot closer to what we were hoping for than the default Blocks system so just roll with it for now.

For our example we’re just going to stick with blocks that are made using the default Blocks system. (But if we had other blocks that were provided by Views or custom Modules we could enable those here as well.)

After saving those settings we’re taken to the broader settings configuration screen, again, probably not a new screen for people who are used to doing Drupal stuff.

The primary things of note that we’re going to do here are:

  1. Under “Right Rail Blocks Field Settings” set the number of blocks that the user will be able to select. I’ve gone with 4 here for my example.
  2. Enable this Field to be displayed as a Block through the CCK Blocks module. We do this by checking “Enable” in the “Provide block for this field” section (which is usually way at the end right above the save button).

Next up we need to cruise over to the “Manage Display” section for our content type.

First off we’re going to want to set our Block Reference field to not be displayed as part of the default rendering mode. (Since that would defeat the purpose and all. You did use that patch file earlier, right? If you didn’t this won’t actually work and you’ll sit around for a couple hours swearing incoherently at Drupal. Or something, you know, if you’re that kind of person.) Go ahead and Save that.

You should also have a “CCK Blocks” build mode that shows up and you should go enable the Block Reference field to show up there. Set it to hide the label and just show the default content. Save that too and then we can get down to it.

Next up, we go back to basics. Head over to the Blocks interface and make yourself some blocks! I made three for testing things with.

Then we’ll make our default set of what goes into the right rail. I’ve just done it here using the usual Blocks interface but you can do it in Context as well. Just go ahead and assign your testing blocks into the usual sidebar_secondregion.

So you have a basis for comparison, you might want to make a sample pagenode (skipping over the blockreferencesection entirely for the moment).

Now, I mentioned it in passing before but I didn’t go into a lot of detail on it, but I went ahead and made a Bartik sub-theme that keeps all the default regions and adds one new one sidebar_second_custom. In the really real world I’d probably just be using a custom theme anyway and would have two regions to be using in it, but for speed on a demo I went the sub-theme route. There’s really nothing interesting about it, but it deserves an extra note: If you don’t see a “Sidebar Second Custom” region in Bartik, that’s why!

Anyway, let’s make another pagethat we can use for testing our custom block assignments on. As you’ll notice there’s a section on the node edit form now that lets us select from available blocks and sort them using drag and drop. Go ahead and choose a couple of your demonstration blocks and change up the order from the defaults.

Removed 2011-11-12: We need to make a little bit of a change in our page.tpl.phpfor where our right rail gets rendered out to the screen. Typically you would just check for any content in $page['sidebar_second']and if it was there output your wrapper markup and then the content. In our case we’re now going to need to check for content in $page['sidebar_second_custom']as well. Then we can output our markup for the layout of the page, and then either the content of our custom set of blocks, or the default.

Update 2011-11-12: Yeah, so, uh, don’t bother doing that. It was a tolerable way to get the logic in somewhere, but in reality it’s sort of like shoving Controller logic into a View. (A real MVC View, that is, not, like, Views.) Anyway, it’s possible to do some voodoo in your template.phpfile instead to be comparing if there’s supposed to be something in our custom region or not, and if so it overwrites the contents of the default region with the custom one. After all, that’s pretty much what template.php is there for. For me it came out something like this…

/**
 * Implements theme_preprocess_page().
 */
function MYTHEMENAME_preprocess_page(&$variables) {
  if (!empty($variables['page']['sidebar_second_custom'])) {
    $variables['page']['sidebar_second'] = $variables['page']['sidebar_second_custom'];
  } 
}

Next up we need to head over to the blocks page and assign our cck_blocksBlock into our custom sidebar region. The naming on these could be better, but it flags things by field name so you can usually tell what’s what.

Now we can go back to our page with the custom block assignments and bask in the warm glow of…

…blocks embedded in a block. D'oh!

At this point you have a few options:

  1. The always popular “swear incoherently”, though after doing a lot of research I have to say this doesn’t actually change the markup at all.
  2. Create a custom Block template override that removes all the wrapper markup and just spits out the content.
  3. Create a custom Block template that you can enable using blockthemeand then can reuse across multiple blocks if you need to. (This will work just fine and was the original way I walked through things here, but in the context of explaining just doing block assignments it kind of clouds what’s going on.)

The code that would go into the template file for both 2 and 3 is the same, it’s really just the naming convention that would be different. For #2 it would be block--cck-blocks.tpl.php. For using it with blockthemeit’s something like block--blocktheme--myblockname.tpl.php.

(Pardon the filename in the screenshot here, it should be block--cck-blocks.tpl.php.)

After that, head back over to your node with custom blocks assigned and VOILA! You should get something like this showing your custom blocks.

The fine print…

Like I kind of alluded to earlier, this method isn’t perfect. It doesn’t give us much in the way of restriction on block selections or placements beyond in the broadest of strokes. But it does give greater control on a node-by-node level than Context or the regular Blocks system does. And in a way that (I think) is a lot easier to explain to an end-user over trying to explain either block assignments or Context.

Changelog

2011-11-12

I’ve made some changes above to reflect moving the region checking and output logic into template.phprather than directly in any of the page template files. This should help keep things a little bit cleaner in the long run.

I also changed the section about applying a block template using blockthemesince it’s not really necessary.

Need a developer?

Good news, I'm available! You can try taking a look at my resume to get a feel for the kinds of things I know how to do.

I'm reading...

Listening to...

Individual Member of the Drupal Association