Creating a module configuration (settings) page

Last modified: June 10, 2009 - 15:19

Main topic described: Module settings
Drupal hook used: hook_menu

Now that we have a working module, we'd like to make it better. If we have a site that has been around for a while, content from a week ago might not be as interesting as content from a year ago. Similarly, if we have a busy site, we might not want to display all the links to content created last week. So, let's create a configuration page for the administrator to adjust this information.

(Note: This was done using hook_settings in Drupal 4.7 which no longer exists in Drupal 5)

Create the configuration function

We'd like to configure how many links display in the block, so we'll create a form for the administrator to set the number of links. This is done in our function onthisdate_admin. Note that _admin is not a hook and we could have used whatever we wanted there.

<?php
function onthisdate_admin() {

 
$form['onthisdate_maxdisp'] = array(
   
'#type' => 'textfield',
   
'#title' => t('Maximum number of links'),
   
'#default_value' => variable_get('onthisdate_maxdisp', 3),
   
'#size' => 2,
   
'#maxlength' => 2,
   
'#description' => t("The maximum number of links to display in the block.")
  );

  return
system_settings_form($form);
}
?>

This function uses several powerful Drupal form handling features. We don't need to worry about creating an HTML text field or the form, as Drupal will do so for us for this settings page. We use variable_get to retrieve the value of the system configuration variable "onthisdate_maxdisp", and define the default value to be 3. We use an array ( named $form here ) to create the form which contains a textfield of size 2 ( #size ), accepting a maximum length of 2 characters ( #maxlength ). We also use the translate function of t(). The system_settings_form() function adds default buttons to a form and set its prefix.

Refer to Drupal Forms API Reference and
Drupal Forms API Quickstart Guide for more detailed information on what more you can do with Drupal Form API.

When you save a settings variable for any module, the variable (in our case, 'onthisdate_maxdisp') and the value is stored in the variables table. Programmatically, you can retrieve the values with the
variable_get('variable_name', default_value)
function.

Of course, we'll need to use the configuration value in our SQL SELECT, so we'll need to adjust our query statement in the onthisdate_block function. One way to do this is with a LIMIT value in our query:

<?php
  $limitnum
= variable_get('onthisdate_maxdisp', 3);
 
$query = "SELECT nid, title, created FROM " .
          
"{node} WHERE created >= '" . $start_time .
          
"' AND created <= '". $end_time . "' LIMIT " . $limitnum;
?>

However, this method may or may not translate across databases (really). Better to use one of Drupal's select methods. In this case, let's leave the original query the same, and call db_query_range:

<?php
  $limitnum
= variable_get("onthisdate_maxdisp", 3);

 
$query = "SELECT nid, title, created FROM " .
          
"{node} WHERE created >= %d " .
          
"AND created <= %d";

 
$queryResult = db_query_range($query, $start_time, $end_time, 0, $limitnum);
?>

Add the page to hook_menu

Once you have created the function with your settings, the page is added with a callback to the function which you specify in hook_menu. In hook_menu we will return an array which describes to Drupal which URL path to use, the title to display, the function to use and the permissions required.

We would like only administrators to be able to access this page, so we'll place the permissions check for the module here in hook_menu so that Drupal can itself check the appropriate permission. To minimize the number of permissions an administrator has to deal with, we're going to use the global administration permission for administrating our module instead of creating a new custom permission.

<?php
/**
* Implementation of hook_menu().
*/
function onthisdate_menu() {

 
$items = array();

 
$items[] = array(
   
'path' => 'admin/settings/onthisdate',
   
'title' => t('On this date module settings'),
   
'description' => t('Description of your On this date settings control'),
   
'callback' => 'drupal_get_form',
   
'callback arguments' => 'onthisdate_admin',
   
'access' => user_access('access administration pages'),
   
'type' => MENU_NORMAL_ITEM,
   );

  return
$items;
}
?>

You can test the settings page by editing the number of links displayed and noticing the block content adjusts accordingly. Navigate to the settings page: admin/settings/onthisdate or administer » site configuration » onthisdate. (If the page doesn't exist, you may have to disable and enable the module for the system to register the new settings page.) Adjust the number of links and save the configuration. Notice the number of links in the block adjusts accordingly.

Note: We don't have any validation with this input. If you enter "c" in the maximum number of links, you'll break the block.

More information on hook_menu:
Drupal 5 hook_menu

For 6.0: RobLayfield offers a hack:

Change the if ... else if to switch ... case and add two additional cases for the op variable:

1. case "configure" - into which you should move the items[] array code from the example above
2. case "save" - which saves the information back to the variable onthisdate_maxdisp

Finally, add an extra variable to the function call:

$edit = array()

which retrieves any posted form data into an array called edit (set to an empty array by default.)

The full re-written onthisdate_block function is:

<?php
/**
* Generate HTML for the onthisdate block
* @param op the operation from the URL
* @param delta offset
* @returns block HTML
*/
function onthisdate_block($op='list', $delta=0, $edit = array()) {
 
// listing of blocks, such as on the admin/block page
 
switch($op){

  case
"list":
   
$block[0]["info"] = t("On This Date");
    return
$block;
    break;
  
  case
"view":
 
// our block content
    // content variable that will be returned for display
   
$block_content = '';

   
// Get today's date
   
$today = getdate();

   
// calculate midnight one week ago
   
$start_time = mktime(0, 0, 0,$today['mon'],
                               (
$today['mday'] - 7), $today['year']);

   
// we want items that occur only on the day in question, so
    //calculate 1 day
   
$end_time = $start_time + 86400;
   
// 60 * 60 * 24 = 86400 seconds in a day
  
   
$limitnum = variable_get("onthisdate_maxdisp", 3);
  
   
$query = "SELECT nid, title, created FROM {node}" .
          
"{node} WHERE created >= %d " .
          
"AND created <= %d";
  
   
$queryResult = db_query_range($query, $start_time, $end_time, 0, $limitnum);

    while (
$links = db_fetch_object($queryResult)) {
     
$block_content .= l($links->title, 'node/'.$links->nid) . '<br />';
    }
   
// check to see if there was any content before setting up the block
   
if ($block_content == '') {
     
// no content from a week ago, return nothing.
     
return;
    }
   
// set up the block
   
$block['subject'] = 'On This Date';
   
$block['content'] = $block_content;
    return
$block;
    break;

    case
"configure":
   
// Compile a list of fields to show
   
$form['onthisdate_maxdisp'] = array(
       
'#type' => 'textfield',
       
'#title' => t('Maximum number of links'),
       
'#default_value' => variable_get('onthisdate_maxdisp', 3),
       
'#size' => 2,
       
'#maxlength' => 2,
       
'#description' => t("The maximum number of links to display in the block.")
    );
    return
$form;
   
// for some reason the core modules don't insert a break after the "configure" case

   
case "save":
     
variable_set('onthisdate_maxdisp', $edit['onthisdate_maxdisp']);
    break;
  }
}
?>

 
 

Drupal is a registered trademark of Dries Buytaert.