How to create a themable, templated custom block

December 08, 2011

On a drupal 6 site, I wanted to create a custom block, but I also wanted to make sure that it would be easily themable. For some reason I couldn’t find a good example of how to do this, so here goes.

For logged out users, the block would show the login form, but it would also show an extra menu. For logged-in users, it would show a different menu and some additional content.

I created a custom accountblock.module to do all this, and defined the menus using the admin UI.

First, as with any custom block, implement hook_block to declare the block:

function accountblock_block($op = 'list', $delta = 0, $edit = array()) {
 switch ($op) {
   case 'list':
    $blocks['accountblock'] = array('info' => t('Combined login and my account block'));
    return $blocks;
      break; 
   case 'view':
     if ($delta =='accountblock') {
        $block['content'] = theme('accountblock_contents');
     }
     return $block;
         break; 
 }
} 

Notice how the block content returns theme(‘accountblock_contents’). This means we can open up our function (accountblock_contents) to the theme layer, as long as we use hook_theme to define the themable content, and make it templated:

function accountblock_theme() {
  return array( 'accountblock_contents' => array(
    'template' => 'accountblock_contents',
    'arguments' => array(),
    ),
  );
}

Then a preprocess function to expose the appropriate content to the themer:

function template_preprocess_accountblock_contents(&$vars) {
  global $user; 

  // content for logged in user
  if($user->uid) {
    $vars['accountblock_link_title'] = 'My Account'; 
    // personalise the block 
    if ($user->first_name && $user->last_name) {
     $vars['accountblock_link_title'] .= ' - '. $user->first_name. ' '.$user->last_name;
   }

   // add the account menu for logged in users
   $vars['accountblock_body'] = menu_tree_output(menu_tree_all_data('menu-myaccount'));
 }
 else {
    $vars['accountblock_link_title'] = 'My Account - Log In';
    $vars['accountblock_body'] = drupal_get_form('user_login_block'); 
    $vars['accountblock_body'] .= menu_tree_output(menu_tree_all_data('menu-myaccount-loggedout'));
  }
}

Then accountblock_contents.tpl.php is nice and simple for a designer to look at, without any unpleasant PHP logic:

<div class="login right">
  <a class="secondary" href="/user"><?php print $accountblock_link_title; ?></a>
</div> 
<div class="my-account"> 
  <a class="close" href="#">Close</a> 
  <p class="header"><?php print $accountblock_link_title; ?></p> 
  <?php print $accountblock_body;?> 
</div>