Custom Web Design Project Management Login

Newsletter Management Login

Back to CHROMATIC's Blog Home Page

This step by step illustrated Drupal tutorial will teach you how to create custom form overrides and element-specific validations with Drupal 6.

Drupal Tutorial: Form Overrides and Element-Specific Validations Image
Delicious Save to Delicious

Drupal Tutorial: Form Overrides and Element-Specific Validations

Chris | Wednesday, July 15th, 2009 24 comments

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 »

About the Author...

Chris is the Director of Web Development at CHROMATIC. He is a designer/developer born and raised in Chicago. He enjoys making things work almost as much as he enjoys making them look nice. Chris specializes in Drupal and standards-based websites. He loves running, tattoos, live music, and Obey T-shirts, drinks too much coffee, and named his puppy after a former Chicago Cub. He maintains a Drupal-focused blog at chrisfree.me

Like this post? What next?
Comments for this postComments are directly below (arrow image)

Leave a Comment

  1. Gosh. Quite hard. But it’s not impossible to be done. Thanks for the tutorial!

  2. Nice article Chris!

  3. Is there any reason you did not choose the legal module? http://drupal.org/project/legal

  4. Hey Gerry – Thanks for the tip. I didn’t use that module because I’d never heard of it before! My tutorial, although solves a specific problem, was also meant to help explain the process of overriding forms in Drupal as well as creating custom validations. These solutions can be used in many different ways. Thanks again!

    • Hi Chris, I found your tutorial very helpful. I was curious if you would be willing to post one on how to store user information into the mix ie. company, first name etc. I’m not sure how the best way to go about it would be. Either make my owe table… in .install or add to the user table in some way etc. Or maybe there some slick way to mix profiles with registration. I’m a big newb at Drupal and a mediocre PHPer but all on the up and up and your tutorial definitely helped a lot.

      Thanks Martin

      • Martin – Thanks for your comment. Glad to see this post is helping people out! On to your question, if you would like to have fields like (Company, First Name, etc) included in your user registration form, you can use the profile module. When you add a field, there is a checkbox option for “Visible in user registration form”. Selecting this should do the trick. Hope this helps!

  5. That was a very helpful post. Thanks for sharing.

  6. thank you !
    very useful
    …now on to finding tutorials on how to save this info to a database:)

  7. Wow — thank you sooooo much for this. Very helpful for exactly the small tweak I was looking to do.

  8. Chris Pall August 31, 2009

    Is this (form_alter) the correct method (I’m not sure if it’s even possible), for updating the title of the form for CCK based forms? For example, I’ve got a form for “Create Arbitrary Content Type” for the form title, but I’d rather have “Add Some Really Arbitrary Title” instead. (Using Drupal 6)

    • Chris Pall,

      Thanks for your comment. As for your question, ‘hook_form_alter()’ likely isn’t what you need. Since the Page Title has already been set by the time the form is prepared for rendering. I would suggest a simple preprocess override in template.php. For example, to alter the title of a particular page, you would use this function:

      function phptemplate_preprocess_page(&$vars, $hook) {
      // if some condition is true (you are viewing your node creation page)
      if ($vars['title'] == ‘Create Arbitrary Content Type’) {
      $vars['title'] = ‘My New Title’;
      }
      }

      Be sure to rebuild your menus after adding this to template.php. Hope this helps!

  9. Hi,

    I just need to make some simple changes to the help text under certain fields in the user registration form. For example, I’d like to add something under the username field where it reads:
    “Spaces are allowed; punctuation is not allowed except for periods, hyphens, and underscores.”

    Is there any easy way to do this without writing a custom module?

    thanks!

  10. nguyen hoang October 24, 2009

    Thanks for the great tutorial on form alter.
    I m using webform module. I want to include a default Country text field for all webform created.
    But the form_id is changed with the node id so i can’t use this value to add default form.
    When i look at webform.module, i see this “webform_node_form”, i tried this for the $form_id but no luck.
    Does webform have a global id?
    Can you advice me for a solution?
    If not, all i can do is to add code directly to webform.module.

  11. Manu Adam October 31, 2009

    Hi Chris, Thanks for your a great tutorial. I implemented your form override plan and it worked perfectly. I now prefer this to even legal module because your alteration is so nicely compact. I must say it is a very good work by you and the way you explained it step by step was superb. I tried to further add a few more items by adding some more codes in this form over ride module but I could not succeed. I basically wanted to get rid of those additional items in the forms which are unnecessary for the users in order to present to them a Compact form. For that I wanted to remove field groups like Menu, Path, Publishing Option, Book outline, Comment setting, revision information etc. for that.. I added additional line your form override module:
    function form_overrides_form_resume_node_form_alter(&$form, $form_state, $form_id) {
    if ($form['#id'] == ‘node-form’) {

    // Move all path options to publishing options
    if ($form['path']) {
    $form['options']['path'] = $form['path'];
    unset($form['path']);
    }

    if ($form['#id'] == ‘node-form’) {

    // Move all path options to publishing options
    if ($form['menu']) {
    $form['options']['menu'] = $form['menu'];
    unset($form['menu']);
    }
    }
    }
    Here i must explain that my form id is resume_node_form. I also tried : $form['menu']['#access'] = FALSE; in the place of unset. But it didnt work as I see all those irritating field groups still in the form. it will be very kind of you if you could guide me and tell what needs to be done to get rid of these unnecessary field groups — the best way to do so. Thanking you once again.

    • Manu – do your users need permission to create revisions, or alter menus? If not, removing their access permissions (admin/users/permissions) should remove those elements from the form. Otherwise, simply unsetting the fieldset variables should do the trick. Make sure to dump your cache after making these changes – Drupal caches these forms to improve load times. Hope this helps!

  12. Hello Chris, Many of my friends are so impressed by your skill and expertise that reading ur every new tutorial is a must do for them. As such I am a newb for Drupal. Though I’ve command on css and html, PHP remains a greek to me and so I am unable to use Drupal’s alter hook function for various customisation. It will be very sweet of you if you could guide me on certain aspects as I am trying to build a job site.
    For that I want two user registrations: One for Jobseeker and another for Employer with proper link in primary menu as “Post Resume” (for Jobseeker regn) and “Post Job” (for employer regn)
    I have created two content types: Resume and Job with added cck fields and connected both to Content profile with check on use on registration. Taking a cue from your reply to manu’s query, I’ve also restricted field permission for different roles.
    Now my problem is : how to create two different menus: Post Resume and Post job .. for different role types. Unless a different role is auto assigned from menu, it shows all the fields in user registations.
    I tried menu per role module. but it just hides menu and doesnt assign role.
    Earlier I had also tried Auto Assign Role module. But, though it is creating role based menu but showing just basic drupal registration form without those special fields for resume uploads or job descriptions for respective roles.
    I have tried many different modules and all sort of permutation combination, but still no success.
    Please guide me as to how to create two different type of registered users. Thanking you.

  13. Hello Chris, Hi, I am here again.. back like a bad coin, lol. Sorry to disturb again. But I just wanted to inform you that I solved my problem. I must say that Autoassign Role is a great module: very neat and user friendly. Earler I missed a very important point and failed to see that it has also created a role option in content type, which I didnt check. Once I checked it, it created very neat menu items directing users to different registration forms. Now I seek your help about one thing. These menu items: Post resume and Post Job are not visible when I login as admin (super user). But when I log out they become visible. Isn’t it possible that all menus are always visible and when a user (without proper permission) clicks there, he gets a page, telling him that: (say) the access to the page in his present role is denied and if he wants to carry on this task ..he should do such and such things…. How can I achieve this? Please guide me. Thanks again.

  14. Hi, I tried changing the #value of the submit button in my registration form since I think it’s ‘Create new account’ when what I wanted was ‘Create New Account’. After changing it, the form wasn’t functioning well. It didn’t display any errors but it didn’t also register the new account I’ve created.

    • Hi Arnold – Thanks for posting. If you are only overriding the #value of the submit button, it shouldn’t change anything else (validations, submit callbacks, etc.). Are you sure you haven’t overridden other parts of the form? I’d be happy to look at your code.

  15. Hi Chris,

    Awesome post. I’m in the process of modifying the user_register form on our site as we speak, we’re adding a survey to the process (which can also be accessed in other places), and it’s just nice to see someone adopt the same approach as me (the only thing I haven’t done yet is write an Admin screen to view the results of the survey). Just about to try and implement your element validation pointers, I’m in need of overriding the default validation and I think that’s what I need!

    • Hi Jamie – thanks for reading. I’m glad to see so many people have found this tutorial helpful. Let me know how it goes!

  16. OMG!!! Thank you for this write up, saved me on one of my current projects :) THANK YOU

  17. Hi Chris, thanks for the great post. It’s easy to understand for newbies like me. BTW, just wondering let’s say that I added a drop down field in the user registration page, and I would need to update the user_profile and send email to the admin based on the value of that drop down field. Is it possible?

    Cos I’m not sure where the form would go after the user clicked on “submit” in the registration page. And where will my drop down value be stored in.

Leave a comment for this post

  • Please keep comments related to topic. Whatever you do, don't spam!
  • Basic HTML tags are allowed:
    <a href> <abbr> <acronym> <blockquote> <code> <em> <strike> <strong>
  • Note: un-related or spam comments will be deleted. Also, we use the rel=nofollow tag, so there's even less of a reason to spam.

Trackback this postComments are directly below (arrow image)

Trackback this post

Back to top