UPDATE: The code has been updated so that if you add extra cck fields into the block content type the query_alter should still function properly.

Blocks are a fundamental part of any good Drupal website. How though, does one give a client access to change the block contents without giving them "administer blocks"?

We have used various techniques but our CTO, Gord, recently came up with a great idea. His idea was to create a view with a group block displays using a content type "block." This content type is given a few cck fields"weight," "region" and "pages". The view sorts by "weight" and uses "pages" as an argument. A block display exists for each region.

To load this view we create a custom block that displays the view programatically. The custom block passes the currently viewed page to the view as an argument. Any block nodes with that path set in their "pages" field will be returned.

You can see on the left image that with some customization, this system supports wildcards such as about/* and the options <front> and <all>.

How is this done? Let's look at the code used in this module.

Firstly we use hook_block()to define a set of 5 blocks. One issue using this technique is there is the potential that on every page load there is a query for every block assigned a region. We don't want this, so we give our custom blocks caching with BLOCK_CACHE_PER_PAGE. Since these blocks are used differently on each page, this will allow them to be cached while ensuring each page has a chance to be execute its query.

The next image shows the view setting we use for this technique. We create a block display for each custom block created with hook_block(). Each display corresponds to a region. These regions are not initially assignedthey first need to be dragged into their appropriate regionsthen the available options within the "region" cck field can be renamed to match. For example, if the block "Dynamic Block Region 1" was assigned to the tertiary region, this title should be renamed "Tertiary Region" within the "region" cck field. Blocks that aren't assigned any region can be deleted from the "region" field.

Once we have the view, the block content type and our blocks assigned, we are most of the way there. The only things not supported at this stage are wildcards and the <all> page. To incorporate these, we use hook_views_query_alter() as follows:

<?php function northstudio_block_system_views_query_alter(&$view, &$query) {   if ($view->name == 'ns_block_system') {         $num = count($query->where[0]['args']);     $path = $query->where[0]['args'][($num – 1)];     $query->where[0]['args'][$num] = '<all>';     if (strpos($path,'/')) {       $parts = explode('/', $path);       $num_parts = count($parts);       for ($i=($num + 1); $i<($num_parts + $num);$i++) {         if ($last_part) {           $part = $last_part.$parts[($i – ($num + 1))].'/';         } else {           $part = $parts[($i – ($num + 1))].'/';         }         $query->where[0]['args'][$i] = $part. '*';         $last_part = $part;       }     } else {       $num_parts = 1;     }     $query_parts = array();     for ($i=0;$i<($num_parts + 1);$i++) {       $query_parts[] = "'%s'";     }     $query->where[0]['clauses'][($num – 1)] = "node_data_field_block_pages.field_block_pages_value IN (".implode(',',$query_parts).")";   } } ?>

The main purpose of this function is to recursively go through the current path and return all portions with a wildcard. For example, if the block was triggered from about/staff/bob/history, the query_alter would send the following to the view as arguments:

  • <all>
  • about/staff/bob/history
  • about/staff/bob/*
  • about/staff/*
  • about/*

The last function we use is hook_preprocess_block(). What we do here is remove the title portion of the block. This code may depend on your block.tpl.php file and therefore may need to be adjusted. Removing this bit of html ensures the block is not displayed when the view returns nothing.

We have packaged up this code in a features module. Here is a list of installation instructions:

  1. Upload and enable the module.
  2. Move the custom blocks into the required regions.
  3. Edit the block content type "region" fieldchange the names of the regions to reflect the actual regions used.
  4. Important: In order to not have these nodes appearing in search results the content type should be edited as follows: Uncheck all options under Workflow settings and disable commenting. The nodes do not need to be published to be displayed as blocks. Features do not yet export these options so for now they need to be done manually.

The module follows below. If you have any questions just post a comment.