Landscape

Drupal Tutorial: Form Overrides and Element-Specific Validations

Last week, I was working on some Drupal form customizations and validations for a client’s site (launching soon) and thought I’d share how you can easily alter Drupal forms. That is, if you know how and where to tap in.

Basically, I needed to add a “Terms of Agreement” section to the default User Registration form that ships with Drupal 6 and user.module.  In addition, the client wanted the ability to edit the “Terms of Agreement” text whenever they needed to. This text needed to be embedded right within the form, so a simple block or popup wouldn’t do. I was going to have to write a module.

I mapped out exactly what my custom module was going to need to do:

  1. Add a “Terms of Agreement” fieldset/textarea to the User Registration form
  2. Add validation to that same form to ensure that the new user has agreed to the terms before allowing registration
  3. Add an administration panel to Drupal that will allow the client to change the “Terms of Agreement” text

Here's what the end result will look like:

Custom Form Overrides with Drupal 6 Preview

Let's jump right in!

Create the module files/directory

I like to keep all of my custom modules in a separate directory from all of my contributed modules. So I install all of my contributed modules (Views, CCK, etc.) at sites/all/modules and all of my custom modules at sites/all/modules/custom. This serves a dual purpose: First, it lets anyone working on our team know that the modules inside this directory are indeed custom and written by CHROMATIC. Second, if we ever hand off the site to another development team or directly to a client’s IT team, this will help them keep things straight.

I’m going to call this custom module Form Overrides since we’ll be doing just that. So I’ll create a new directory called form_overrides within our custom folder.  Inside this new directory, I’ll create two files:

  1. form_overrides.info: this simple file tells Drupal about our module – the name, what versions its written for and what it does.
  2. form_overrides.module: where all the Drupal magic happens.

Finish up the .info file

Here’s the content for our .info file – explanation following:

; $Id$
name = Custom Form Overrides
description = Adding some custom form overrides.
core = 6.x

The first line ”: $Id$” is a CVS identification tag – it’s not super important because we won’t be submitting this to the Drupal community, but it’s still good practice. The ”name” and ”description” lines tell Drupal the human-readable name/description of our module as it will be displayed on the Module Configuration page. Finally, the ”core” line tells Drupal which versions this module will work with. At this point, it’s safe to navigate on over to Admin > Build > Modules and enable your module. Remember, it should be installed at the following path: sites/all/modules/custom/form_overrides.

Writing the module

Let’s start with adding our new fields to the form. First, we need to figure out which form we’ll be overriding. Head on over to user/register on your Drupal site. Use firebug to inspect the the HTML for the form. You should see some hidden form fields – we’re looking for the one with the name=”form_id” – this element holds the form’s unique identifier which we'll be using in our module.

drupal-tutorial-Custom form overrides and element-specific validations with Drupal 6-form-id-firebug

<input type="hidden" value="user_register" id="edit-user-register" name="form_id" />

Now that we know the form id of the form we’ll be overriding, we can get started leveraging that with some of Drupal’s magic. The hook we’ll be using is: hook_form_alter(). This little nugget of awesomeness let’s you override and alter any form that drupal is rendering before it is actually returned to the browser. Exactly what we need!

Here’s how to do start, explanation to follow: (add this to form_overrides.module)

<?php
/**
*  Implementation of hook_form_alter().
*/
function form_overrides_form_alter(&$form, $form_state, $form_id) {
  switch($form_id) {
      case 'user_register': // the value we stole from the rendered form
        // your customizations go here
          drupal_set_message('Hey, we\'ve tapped into this form!');
      break;
  }
}
?>

To summarize here, we’ve used the hook_form_alter (passing the $form, $form_state, $form_id values) to tap into Drupal’s pre-processing of the form. We switch on the $form_id (this is the unique ID of the form) to alter our specific form: user_register’. If you’ve got everything working, you should see our message appear at the top of the registration form.  You'll probably need to log out to access the registration form, dump your Drupal cache before doing so.

NOTE: Alternatively, you could use a function named: form_overrides_form_user_register_alter(). Drupal will let you create specific functions for each form you want to override by using the following naming convention:

  • module name + ‘form’ + form id value + ‘alter’

Now, let’s start customizing this sucker.

Overriding/Adding to the User Registration Form

We're going to add a new fieldset, called “Terms of Agreement” and within that, I’ll add a textarea to house the actual agreement. Finally, I’ll add a checkbox with the label, “I have read and agree to the Terms of Agreement” above.

Here’s how it’s done:

<?php
function form_overrides_form_alter(&$form, $form_state, $form_id) {
  switch($form_id) {
      case 'user_register': // the value we stole from the rendered form
        // add the fieldset
        $form['terms'] = array(
            '#type' => 'fieldset',
              '#title' => t('Terms of Agreement'),
              '#weight' => 10,
              '#collapsible' => FALSE,
              '#collapsed' => FALSE,
        );

        // add the textare that will hold our text
        $form['terms']['terms_text'] = array(
            '#value' => '
       '.t('Terms text goes here....').'
',
            '#weight' => 0,
        );

        // add the checkbox
        $form['terms']['terms_checkbox'] = array(
              '#type' => 'checkbox',
              '#title' => t('I have read and agree to the \'Terms of Agreement\' above.'),
            );

      break;
  }
}
?>

This should add each of the elements we need to the form. You may need to empty your site’s cache if caching is turned on to see these changes take effect. Next, we'll add some element-specific validation so that the new user can’t register for the site with agreeing to the terms (clicking the checkbox). After that, all we need to do is add a page where privileged users can edit the value of the site's Terms of Agreement.

Adding Element-Specific Validation to our altered form

Now that we’ve got all of our new elements added to the User Registration form, we need to add some “element-specific” validation. Drupal allows you to declare your own validation functions on specific elements. You simply declare your custom function when you declare your element, like so:

$form['terms']['terms_checkbox'] = array(
  '#type' => 'checkbox',
  '#title' => t('I have read and agree to the \'Terms of Agreement\' above.'),
  '#element_validate' => array('form_overrides_toa_validate'),
);

This just tells Drupal to run our new validation function (form_overrides_toa_validate) in addition to the existing validation for this form. This works great because we don’t have to alter anything from the user.module to make our new functionality work. Now all we need to is declare our new validation function:

function form_overrides_toa_validate($element, &$form_state) {
  if(empty($element['#value'])) {
    form_error($element, t('You must agree to the Terms of Agreement.'));
  }
}

When you submit the form (without clicking the checkbox), you should see our new error. Success!

Element-Specific Validation with Drupal 6 error preview

Creating a custom Administrative Panel

The final part of this tutorial is how to create your own admin panel. Here’s what we’re trying to accomplish:

  1. Create a page that allows users to alter the “Terms of Agreement’ text on the registration form
  2. Restrict this new page to privileged users (custom permissions)
  3. Pull the data saved from this page into our altered user registration form.

We’ll start by adding the new permissions to the Drupal permissions system. This is easily achieved by using hook_perm:

function form_overrides_perm() {
  return array('administer site terms of agreement');
}

This little snippet will add a granular permission to the “Permissions” page. This will allow you to grant this custom permission to whatever roles you prefer. Now that we’ve registered our new permissions, we can define our new page. We do this using hook_menu:

function form_overrides_menu() {
  $items['admin/settings/terms-customization'] = array(
    'title' => 'Terms of Agreement Customization',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('form_overrides_toa_settings'),
    'access arguments'  => array('administer site terms of agreement'),
    'file' => 'form_overrides.admin.inc',
    'weight' => 10,
  );
  return $items;
}

This registers a new path (‘admin/settings/terms-customization’) to the Drupal menu system, declares the title, the function to run when accessed (drupal_get_form), arguments to pass along (name of our custom form), access arguments (our new permissions) as well as the file where our new page’s form declaration lives. Phew!

We’ll need to create that new file now. Add a new file titled form_overrides.admin.inc to our form_overrides directory:

// $Id$

/* return an admin settings form */
function form_overrides_toa_settings() {
  $form['form_overrrides_toa'] = array(
    '#type' => 'textarea',
    '#title' => t('Terms of Agreement'),
    '#default_value' => variable_get('form_overrrides_toa', t('Enter your Terms of Agreement here.')),
    '#description' => t('The text will be displayed on the user registration form'),
    '#cols' => 60,
    '#rows' => 5,
  );
  return system_settings_form($form);
}

All this file does is declare a new form, with one textarea. It uses the core function system_settings_form(), which takes care of the form processing and storing of the submitted values in your Drupal database. Its a great tool for saving arbitrary values for your modules or themes. After rebuilding your menus (visit the module admin page, or use the devel module) you should now have a new admin panel, accessible via this path: admin/settings/terms-customization

Now that we have our custom panel working, all we need to do is grab our saved value and add it to our textarea on the User Registration form. We can access our saved variable with Drupal’s variable_get() function. Replace our original textbox declaration with this updated one:

  $form['terms']['terms_text'] = array(
    '#value' => '
'.variable_get('form_overrrides_toa', 'Terms of Agreement Text').'
',
    '#weight' => 0,
  );

Now, whatever text is entered and saved with our admin panel, will appear on our altered User Registration form! Pretty neat, huh?

Hopefully this unintentionally long tutorial will help you the next time you need to change one of Drupal’s forms, or when you need to create your own administrative section.  I have attached source code for my module below. Happy Drupaling!

Download Source Module » | Learn more about Drupal »

Related Posts & Presentations