Making a custom block with an admin settings form in a Drupal module

Often while building a Drupal website, you'll need to create a custom Block containing HTML and content. Completely standard right? It's a problem when your client or the site maintainer needs to change some of that content. They are forced to wade through significant amounts of HTML to find that little bit of content to update. Instead, why don't you build a custom settings page? Your site maintainer gets a simple form to update and you get variables to output.

Note: This article builds on the template file method discussed in my previous article: Using theme hooks to display a tpl.php on a menu path.

What follows is a detailed method to:

  • Create a block - In code! Custom blocks cannot be version controlled and are therefore more difficult to maintain.
  • Create a template file - A template file is much easier than a PHP file for your Themer to modify.
  • Create an admin settings form - Don't hide the content settings in the Block configuration, and then the site maintainer doesn't need to learn about Blocks.

Let's start with a new module. Start the module name with your site short name followed by it's use. In this example case, I'm creating a footer block for "sitename" containing the copyright owner, dates, address and phone number.

sitename_footer.info

name = Example site - Custom footer block
description = "Creates a site footer block and provides an associated admin menu."
package = Example Site
core = 7.x
configure = admin/config/administration/sitename_footer

sitename_footer.module

<?php
 
/**
 * Implements hook_block_info().
 */
function sitename_footer_block_info() {
  $blocks['footer'] = array(
    'info' => t('Example Site - Footer'),
  );
  return $blocks;
}
 
/**
 * Implements hook_block_view().
 */
function sitename_footer_block_view($delta = '') {
  $block = array();
 
  // Depending on the specified delta return the correct block content.
  // Note: This switch is not required if you have only one block, you can
  // simply return the the block array
  switch ($delta) {
    case 'footer':
      // We don't want the block to have a title.
      $block['subject'] = '<none>';
      // Using a Renderable Array rather than a HTML-returning callback function.
      $block['content'] = array(
        // Specify the theme function to use.
        '#theme' => 'sitename_footer',
        // Provide variable values as specified in hook_theme.
        '#copyright' => check_plain(variable_get('sitename_footer_copyright', 'Company Name')),
        '#copyright_start' => check_plain(variable_get('sitename_footer_copyright_start', '1885')),
        '#copyright_end' => format_date(time(), 'custom' , 'Y'),
        '#phone' => check_plain(variable_get('sitename_footer_phone', '555-867-5309')),
        '#address' => check_plain(variable_get('sitename_footer_address', '123 Main St. North Pole')),
      );
      break;
  }
  return $block;
}
 
/**
 * Implements hook_theme().
 */
function sitename_footer_theme() {
  return array(
    'sitename_footer' => array(
      // Specify variables you'll be using in the template
      'variables' => array(
        // NULL is correct. Values set here are stored in Theme Registry.
        'copyright' => NULL,
        'copyright_start' => NULL,
        'copyright_end' => NULL,
        'phone' => NULL,
        'address' => NULL,
      ),
      // Use a template file called sitename_footer.tpl.php
      'template' => 'sitename_footer',
    ),
  );
}
 
/**
 * Implements hook_menu().
 */
function sitename_footer_menu() {
  $items['admin/config/administration/example_footer'] = array(
    'title' => 'Example site - footer',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('sitename_footer_configure'),
    // Use the default/standard site configuration permission.
    'access arguments' => array('administer site configuration'),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}
 
/**
 * Creates/returns a form to configure the block's variables.
 *
 * @param array $form
 * @param array $form_state
 * @return array
 */
function sitename_footer_configure($form, &$form_state) {
  $form['sitename_footer_copyright'] = array(
    '#type' => 'textfield',
    '#title' => t('Site copyright owner'),
    '#description' => t('The company/organization/person/entity to show as copyright holder.'),
    '#default_value' => variable_get('sitename_footer_copyright', 'Company Name'),
    '#size' => 60,
    '#maxlength' => 120,
    '#required' => TRUE,
  );
 
  $form['sitename_footer_copyright_start'] = array(
    '#type' => 'textfield',
    '#title' => t('Site copyright start date'),
    '#description' => t('The start date for copyright of the content; end date is assumed to be the current year.'),
    '#default_value' => variable_get('sitename_footer_copyright_start', '1955'), // Got it Marty?
    '#size' => 4,
    '#maxlength' => 4,
    '#required' => FALSE,
  );
  $form['sitename_footer_phone'] = array(
    '#type' => 'textfield',
    '#title' => t('Site phone number'),
    '#description' => t('The phone number of the copyright holder.'),
    '#default_value' => variable_get('sitename_footer_phone', '555-867-5309'),
    '#size' => 20,
    '#maxlength' => 20,
    '#required' => FALSE,
  );
  $form['sitename_footer_address'] = array(
    '#type' => 'textfield',
    '#title' => t('Site street address'),
    '#description' => t('The street address of the copyright holder.'),
    '#default_value' => variable_get('sitename_footer_address', '123 Main St. North Pole'),
    '#size' => 120,
    '#maxlength' => 120,
    '#required' => FALSE,
  );
 
  // Use system_settings_form() to automate saving configuration variables.
  return system_settings_form($form);
}

Now the file that will make your Themer love you.
sitename_footer.tpl.php:

<?php
/**
 * Displays the site footer with Copyright information.
 *
 * Template file allows for simple adjustment of HTML and CSS.
 */
?>
 
<span class="copyright">
  &copy;<?php echo $copyright_start;?> - <?php echo $copyright_end;?>
  <?php echo $copyright;?>
</span>
|
<span class="phone">
 <?php echo $phone;?>
</span>
|
<span class="address">
 <?php echo $address;?>
</span>

And lastly, for consistency and "following the rules" a hook_uninstall to delete the variables.
sitename_footer.install

<?php
/**
 * Implements hook_uninstall()
 *
 * Remove variables used.
 */
function sitename_footer_uninstall() {
  variable_del('sitename_footer_copyright');
  variable_del('sitename_footer_copyright_start');
  variable_del('sitename_footer_phone');
  variable_del('sitename_footer_address');
}

This example will work as is, but I suggest you expand and customize it specifically for your project. Enable the module, and place the block in your Footer region. Now check out that exciting HTML:

 
<span class="copyright">
  ©1885 - 2013 Company Name
</span>
|
<span class="phone">
 555-867-5309
</span>
|
<span class="address">
 123 Main St. North Pole
</span>

And how it it displays:

©1885 - 2013 Company Name | 555-867-5309 | 123 Main St. North Pole

External references:

Comments

A question about the function - sitename_footer_configure.
I can't remember there is a hook_configure function in D7.
I just know there is a function - hook_block_configure, and its argument is ($delta) only.

Do I understand your question correctly? I'm going to guess at two potential readings.

If the question is "Is sitename_footer_configure() is a valid Drupal hook?" No, it isn't. There is no reason for it to be. It is unrelated to the hook system. It is simply a logically named Form API generating function.

If the question is "Why not use hook_block_configure?" I specifically want these configuration options outside of the block configuration system. The block admin area is far too confusing and an area where you can completely break the site layout and display. Using the method described in the article, you don't need to give Administer Blocks to the content editor/creator roles.

That's great . You saved my time . thx a million .

Add new comment