07. Create a module configuration (settings) page

Last modified: March 31, 2007 - 01:35

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.

A module's configuration is set up with the settings hook. We would like only administrators to be able to access this page, so we'll do our first permissions check of the module here:

<?php
/**
* Module configuration settings
* @return settings HTML or deny access
*/
function onthisdate_settings() { 
 
// only administrators can access this module
 
if (!user_access("administer onthisdate")) {
    return
message_access(); 
  }
}
?>

If you want to tie your modules permissions to the permissions of another module, you can use that module's permission string instead of defining a specific permission string for this module. The "access content" permission is a good one to check if the user can view the content on your site:

<?php
 
...
 
// check the user has content access
 
if (!user_access("access content")) {
    return
message_access();
  }

  ...
?>

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:

<?php
 
...
 
// check the user has administration access
 
if (!user_access('access administration pages')) {
    return
message_access();
  }
  ...
?>

This example is actually a bit artificial, as settings hook pages are available only to users with the 'access administration pages' permissions - this check isn't technically needed. As you'll see later in the tutorial, page access is most easily controlled in the _menu hook. For this tutorial example, however, we'll leave in the permissions check.

Now, 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:

For 4.5-4.6 modules, use the form_textfield function:

<?php
function onthisdate_settings() {
 
// only administrators can access this function
 
if (!user_access('access administration pages')) {
    return
message_access();
  }
 
$output .= form_textfield(t("Maximum number of links"), "onthisdate_maxdisp",
            
variable_get("onthisdate_maxdisp", "3"), 2, 2,
            
t("The maximum number of links to display in the block."));  return $output;}
}
?>

For 4.7 modules, use the updated form API:

<?php
function onthisdate_settings() {
 
// only administrators can access this module
 
if (!user_access('access administration pages')) {
    return
message_access();
  }
 
$form['onthisdate_maxdisp'] = array('#type' => 'textfield', '#title' => t('Maximum number of links'), '#default_value' => variable_get('onthisdate_maxdisp', 3), '#description' => t("The maximum number of links to display in the block."), '#maxlength' => '2', '#size' => '2');
  return
$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 the form_textfield function to create the form and a text box of size 2, accepting a maximum length of 2 characters. We also use the translate function of t(). There are other form functions that will automatically create the HTML form elements for use. For now, we'll just use the form_textfield() function for 4.5 - 4.6 nodes, and the forms API for 4.7 and later.

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 Drupals 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 >= '" . $start_time .
          
"' AND created <= '". $end_time;

  
$queryResult = db_query_range($query, 1, $limitnum);
?>

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 » settings » 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 about the settings hook:

Note: the settings hook will no longer be used in Drupal 4.8/5.0. See the documentation on hook_menu for how to create a menu item.

Download the code so far, (4.7 version) renaming to onthisdate.module before saving in your Drupal installation.

Confusion

elucify - August 30, 2005 - 16:00

In the text above, when you say

If you want to tie your modules permissions to the permissions of another module, you can use that module's permission string. The "access content" permission is a good one to check if the user can view the content on your site:

Is this one topic or two?

  • If it's one topic, then the example supposedly shows how to use the permissions of another module. But which one?
  • If it's two topics, then this part wants to be two paragraphs: one about sharing permissions between modules, and one about (I guess) checking if the user has read access to the content.

    As written, the first line says you can "use that modules permission string", but provides no example of how to do so. The second sentence is simply confusing. Does it mean this:

    If this block is only visible by certain users (administrators, for example), be sure to check the "access content" permission.

    Or does it mean something else? As phrased, it sounds like:

    It's a good idea to check the "access content" permission if the user can access content from your site.

    Which makes no sense to me at all. If the user can't access content, then what are you checking?

____
Mark Johnson
Silver Spring, MD

Clarification

dunghopper - September 20, 2006 - 22:32

If you want to tie your modules permissions to the permissions of another module, you can use that module's permission string. The "access content" permission is a good one to check if the user can view the content on your site:

Is this one topic or two?

It's really one topic, but that topic may not be expressely stated. So I'll try to state it: "You are not limited to only checking permission strings that are specific to your module."

The statement you quoted contained two more specific explanations of this general idea:
1) You may use permission strings from other modules (and it may be relevant to note that this creates a dependant relationship between your module and the 'other') and
2) You can use even more general, system-wide permission strings (such as "access content") which aren't connected to any module.

As written, the first line says you can "use that modules permission string", but provides no example of how to do so.

Example:
if ( !user_access("administer another_module") ) . . .
where "administer another_module" is a valid permission string defined by another_module. All permission strings used by all enabled modules for your site are listed at Administer >> Access Control >> Permissions.

The second sentence is simply confusing... As phrased, it sounds like:
"It's a good idea to check the "access content" permission if the user can access content from your site."
Which makes no sense to me at all. If the user can't access content, then what are you checking?

The second sentence, more clearly:

If you want to know whether the user has general permission to view content on your site, the "access content" permission string is a good one to check.

message_access() deprecated (4.7)

dlr - February 17, 2006 - 13:16

USE THIS instead of message_access to give access denied message

Benjamin Melançon - June 10, 2007 - 01:20

Using a simple text string message with return inside hook_settings as suggested by another commenter caused a pretty little fatal error ("Cannot use string offset as an array"). From pathauto 4.7:

<?php
function pathauto_settings() {
 
// Restrict administration of this module
 
if (!user_access('administer pathauto')) {
   
$form['error'] = array('#type' => 'item',
     
'#title' => t('You are not authorized to access the pathauto settings.'));
    return
$form;
  }
?>

ben, Agaric Design Collective

Superfluous buttons

suleyman - July 7, 2008 - 17:25

This method leaves the "Save configuration" and "Reset to defaults" buttons below the error message though.

drupal_get_form()

Haudrauf - July 3, 2006 - 02:07

It was unclear to me if I needed to use drupal_get_form() or not, because you saw them in those examples:
http://api.drupal.org/api/head/file/developer/topics/forms_api.html

There, you WOULD NEED drupal_get_form() if you get to your forms page with a menu hook like:

$items[] = array(
'path' => 'admin/settings/reservation',
'title' => t('Reservation Form Settings'),
'callback' => 'reservation_settings',
'access' => user_access('administer reservation'),
);

But that's not the sense of having a hook_settings() when you get to it by hook_menu() function items.
Just remove the above menu hook code and drupal will handle the path "admin/settings/yourpage" alright by itself.

So http://api.drupal.org/api/head/function/hook_settings is right in case you don't use a menu hook.

Code provided doesn't include the new query

oscarda - September 17, 2006 - 11:54

First of all many thanks for the great tutorial! : ) My small contribution:

The code in the link [Download the code so far, (4.7 version)] does not include the new lines that set the limit of the rows returned.
You may have to add manually the new lines provided:

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

 
$query = "SELECT nid, title, created FROM " .
          
"{node} WHERE created >= '" . $start_time .
          
"' AND created <= '". $end_time;

  
$queryResult = db_query_range($query, 1, $limitnum);
?>

Cheers,
Oscar

One other small modification

martron - October 11, 2006 - 18:46

In drupal 4.7.3 on my system db_query_range seems to begin counting from zero, so in order for the limit to work the queryresult line should look like:

<?php
$queryResult
= db_query_range($query, 0, $limitnum);  // Note the 0 instead of 1 as the second parameter
?>

 
 

Drupal is a registered trademark of Dries Buytaert.