As any Drupal developer has learned soon enough, Drupal's Form API is extremely powerful, but with that power come complexity. Building a complex Drupal form to look and behave the way you want is almost an art form in and of itself. This tutorial will discuss how to build a multi-step, #ajax driven Drupal 7 form with a non-JavaScript fallback. This tutorial assumes you have an understanding of the Form API in Drupal 7, but seeing as you have read this far, it's probably safe to assume that you do.

In this tutorial, we are going to look at a three-step form that collects an alphabetic value on step one, a numeric value on step 2, and an alphanumeric value on step 3. There will be back buttons, allowing the user to review their previous submissions, and on the final step, the values will be submitted and saved to the database.

So, let's begin. We will be creating a module called Ajax Form (ajax_form). So the first thing we need to do is implement hook_menu(), to create a path at which we can access our form:

function ajax_form_menu()
{
	$menu['ajax_form'] = array
	(
		'title' => 'Ajax multistep form example',
		'description' => 'Creates an ajax multistep form',
		'page callback' => 'drupal_get_form',
		'page arguments' => array('ajax_form_multistep_form'),
		'access callback' => TRUE,
	);
 
	return $menu;
}

In the above, we have mapped the path /ajax_form to the the Form API using drupal_get_form() as our callback, and setting our form definition as the function ajax_multistep_form(). Next, we need to define our form in this function:

ffunction ajax_form_multistep_form($form, &$form_state)
{
	// If $form_state['step'] is not set, we set it to 1
	$form_state['step'] = isset($form_state['step']) ? $form_state['step'] : 1;
 
	// Add a wrapper div that will be used by the Form API to update the form using AJAX
	$form['#prefix'] = '<div id="ajax_form_multistep_form">';
	$form['#suffix'] = '</div>';

The above is the beginning of our function definition. We are doing two things here. The first is we are determining what step of the multi-step form we are on. If $form_state['step'] is set, then we set it to itself. If it is not set, we set it ($form_state['step']) to 1, since we will be on the first step.

After that, we wrap the entire form in a div with a unique ID. This is the wrapper we will be using in order to dynamically reload the form on each step.

Continuing on with our function:

// Depending on which step of the form we are on, we output different form fields
switch($form_state['step'])
{
	// Step 1
	case 1:
		$default_value = '';
		if(isset($form_state['values']['step_1']))
		{
			$default_value = $form_state['values']['step_1'];
		}
		elseif(isset($form_state['storage']['step_1']))
		{
			$default_value = $form_state['storage']['step_1'];
		}
			$form['step_1'] = array
		(
			'#type' => 'textfield',
			'#title' => t('Enter something alphabetical'),
			'#required' => TRUE,
			'#default_value' => $default_value,
		);
		break;
 
	// Step 2
	case 2:
		$default_value = '';
		if(isset($form_state['values']['step_2']))
		{
			$default_value = $form_state['values']['step_2'];
		}
		elseif(isset($form_state['storage']['step_2']))
		{
			$default_value = $form_state['storage']['step_2'];
		}
		$form['step_2'] = array
		(
			'#type' => 'textfield',
			'#title' => t('Enter something numerical'),
			'#required' => TRUE,
			'#default_value' => $default_value,
		);
 
		break;
 
	// Step 3
	case 3:
		$default_value = '';
		if(isset($form_state['values']['step_3']))
		{
			$default_value = $form_state['values']['step_3'];
		}
		elseif(isset($form_state['storage']['step_3']))
		{
			$default_value = $form_state['storage']['step_3'];
		}
		$form['step_3'] = array
		(
			'#type' => 'textfield',
			'#title' => t('Enter something alphanumerical'),
			'#required' => TRUE,
			'#default_value' => $default_value,
		);
 
		break;
}

In this next step, we are using the current step of the form (that we determined in the previous piece of code) to determine which part of the form we will display. As can be seen in the switch() function, we have three potential cases - 1, 2 and 3, On step one, we create a textfield for the alphabetic value. On step two we create a textfield for the numeric value, and on step three we create a textfield for the alphanumeric value.

You'll notice that for each step, the default value was set to empty as a default. Next, we check if $form_state['values']['step_x'] is set, and if it is, we use that. If it is not set, then we instead use $form_state['storage']['step_x'] instead. The reasoning behind this is that as the user submits values, we will be storing them in $form_state['storage'] in our submit function. However, if the form fails validation, the values the user submitted will be stored in $form_state['values'], and in this case these will be more current than the values in $form_state['storage']. This example outlines the three different default values:

  1. The user first comes to the form, and there is no value to insert, as they have not submitted the form yet. in this case, the default value is an empty string.
  2. The user enters a value, and clicks the next button. We will save the values to $form_state['storage']['step_x'] at this point.
  3. The user realizes they want to change the value they submitted. So they click the back button. At this point, $form_state['values']['step_x'] will not be set, but $form_state['storage']['step_x'] is set. So we use the previously submitted value to populate the field.
  4. The user enters a new value and submits the form, but the submitted value fails validation. The form is rebuilt at this point, and $form_state['values']['step_x'] is now set containing the invalid value. This is now our most up-to-date value for the field, so we use this value, letting the user alter it as necessary.
  5. When the user submits the form again, the new value will be saved to $form_state['storage']['step_x'].

The final thing we need to add to our mutli-step form is the buttons:

// Create a container for our buttons
$form['buttons'] = array
(
	'#type' => 'container',
);
// If we are on step 1, we do not want a back button
if($form_state['step'] !== 1)
{
	$form['buttons']['back'] = array
	(
		'#type' => 'submit',
		'#value' => t('Back'),
		// Setting #limit_validation_errors to an empty array
		// ensures that the form fields are not validated when
		// the back button is used. Otherwise, we will get errors
		'#limit_validation_errors' => array(),
		// We need to create a #submit function for this button, or else
		// #limit_validation_errors has no effect. So we create a submit
		// function specifically for this submit button.
		'#submit' => array('ajax_form_multistep_form_back_submit'),
		'#ajax' => array
		(
			// We pass in the wrapper we created at the start of the form
			'wrapper' => 'ajax_form_multistep_form',
			// We pass a callback function we will use later to render the form for the user
			'callback' => 'ajax_form_multistep_form_ajax_callback',
		),
	);
}
// We only want a forward button if we are not on the last step of the form
if($form_state['step'] !== 3)
{
	$form['buttons']['forward'] = array
	(
		'#type' => 'submit',
		'#value' => t('Next'),
		'#ajax' => array
		(
			// We pass in the wrapper we created at the start of the form
			'wrapper' => 'ajax_form_multistep_form',
			// We pass a callback function we will use later to render the form for the user
			'callback' => 'ajax_form_multistep_form_ajax_callback',
		),
	);
}
// We only want a submit button if we are on the last step of the form
else
{
	$form['buttons']['submit'] = array
	(
		'#type' => 'submit',
		'#value' => t('Submit'),
		'#ajax' => array
		(
			// We pass in the wrapper we created at the start of the form
			'wrapper' => 'ajax_form_multistep_form',
			// We pass a callback function we will use later to render the form for the user
			'callback' => 'ajax_form_multistep_form_ajax_callback',
		),
	);
}
 
// We always need to return the form
return $form;

The first thing we do in the above code is create a container, $form_state['buttons']. This gives us a wrapper div around our buttons, which is benefiicial for theming purposes. The next thing we do is add a delete button, provided we are not on step 1. If we are on step 1, we do not want a back button, as there is nowhere to go back to.

Then, we determine whether or not we are on the last step. The reason for this is that for the first steps, we want to have a 'continue' button, allowing the user to proceed to the next step of the form. However, on the last step of the form, we want to change this to a submit button. We do this for two reasons. One, is that we want to give the user an indicator that they will be submitting the values at this point, and the other is so that we can target this submit button in our submit function (coming further down in the tutorial).

You'll notice that for our back button, we set #limit_validation_errors() to an empty array. This is to ensure that no fields are validated when we use our back button, as we are not submitting the values at this step, but rather purely navigating to the previous step of the form. In order for #limit_validation_errors to work, a #submit function is also required for the element. Otherwise #limit_validation_errors is ignored, and the form fields are validated. I will show this submit function further down in the tutorial.

So we have now built our form definition. The entire form definition is here:

function ajax_form_multistep_form($form, &$form_state)
{
	// If $form_state['step'] is not set, we set it to 1
	$form_state['step'] = isset($form_state['step']) ? $form_state['step'] : 1;
 
	// Add a wrapper div that will be used by the Form API to update the form using AJAX
	$form['#prefix'] = '<div id="ajax_form_multistep_form">';
	$form['#suffix'] = '</div>';
 
	// Depending on which step of the form we are on, we output different form fields
	switch($form_state['step'])
	{
		// Step 1
		case 1:
			$default_value = '';
			if(isset($form_state['values']['step_1']))
			{
				$default_value = $form_state['values']['step_1'];
			}
			elseif(isset($form_state['storage']['step_1']))
			{
				$default_value = $form_state['storage']['step_1'];
			}
 
			$form['step_1'] = array
			(
				'#type' => 'textfield',
				'#title' => t('Enter something alphabetical'),
				'#required' => TRUE,
				'#default_value' => $default_value,
			);
			break;
 
		// Step 2
		case 2:
			$default_value = '';
			if(isset($form_state['values']['step_2']))
			{
				$default_value = $form_state['values']['step_2'];
			}
			elseif(isset($form_state['storage']['step_2']))
			{
				$default_value = $form_state['storage']['step_2'];
			}
 
			$form['step_2'] = array
			(
				'#type' => 'textfield',
				'#title' => t('Enter something numerical'),
				'#required' => TRUE,
				'#default_value' => $default_value,
			);
 
			break;
 
		// Step 3
		case 3:
			$default_value = '';
			if(isset($form_state['values']['step_3']))
			{
				$default_value = $form_state['values']['step_3'];
			}
			elseif(isset($form_state['storage']['step_3']))
			{
				$default_value = $form_state['storage']['step_3'];
			}
 
			$form['step_3'] = array
			(
				'#type' => 'textfield',
				'#title' => t('Enter something alphanumerical'),
				'#required' => TRUE,
				'#default_value' => $default_value,
			);
 
			break;
	}
 
	// Create a container for our buttons
	$form['buttons'] = array
	(
		'#type' => 'container',
	);
	// If we are on step 1, we do not want a back button
	if($form_state['step'] !== 1)
	{
		$form['buttons']['back'] = array
		(
			'#type' => 'submit',
			'#value' => t('Back'),
			// Setting #limit_validation_errors to an empty array
			// ensures that the form fields are not validated when
			// the back button is used. Otherwise, we will get errors
			'#limit_validation_errors' => array(),
			// We need to create a #submit function for this button, or else
			// #limit_validation_errors has no effect. So we create a submit
			// function specifically for this submit button.
			'#submit' => array('ajax_form_multistep_form_back_submit'),
			'#ajax' => array
			(
				// We pass in the wrapper we created at the start of the form
				'wrapper' => 'ajax_form_multistep_form',
				// We pass a callback function we will use later to render the form for the user
				'callback' => 'ajax_form_multistep_form_ajax_callback',
			),
		);
	}
	// We only want a forward button if we are not on the last step of the form
	if($form_state['step'] !== 3)
	{
		$form['buttons']['forward'] = array
		(
			'#type' => 'submit',
			'#value' => t('Next'),
			'#ajax' => array
			(
				// We pass in the wrapper we created at the start of the form
				'wrapper' => 'ajax_form_multistep_form',
				// We pass a callback function we will use later to render the form for the user
				'callback' => 'ajax_form_multistep_form_ajax_callback',
			),
		);
	}
	// We only want a submit button if we are on the last step of the form
	else
	{
		$form['buttons']['submit'] = array
		(
			'#type' => 'submit',
			'#value' => t('Submit'),
			'#ajax' => array
			(
				// We pass in the wrapper we created at the start of the form
				'wrapper' => 'ajax_form_multistep_form',
				// We pass a callback function we will use later to render the form for the user
				'callback' => 'ajax_form_multistep_form_ajax_callback',
			),
		);
	}
 
	return $form;
}

Next, we will create our validation function. Our validation function needs to check that the first value is alphabetic, the second value is numeric, and the the third value is alphanumeric.

function ajax_form_multistep_form_validate($form, &$form_state)
{
	// First we set up a switch for each of our three steps
	switch($form_state['step'])
	{
		case "1":
			// Check that the first value is alphabetic
			if(!preg_match('/^[a-zA-Z]+$/', $form_state['values']['step_1']))
			{
				form_set_error('step_1', t('Alphabetic characters only please'));
			}
 
			break;
 
		case "2":
			// Check that the second value is numeric
			if(preg_match('/\D/', $form_state['values']['step_2']))
			{
				form_set_error('step_2', t('Numeric characters only please'));
			}
 
			break;			
 
		case "3":
			// Check that the third value is alphanumeric
			if(!preg_match('/^[a-zA-Z0-9]+$/', $form_state['values']['step_3']))
			{
				form_set_error('step_3', t('Alphanumeric characters only please'));
			}
 
			break;
 
	}
}

The above block of code is fairly straightforward. We check which step we are on, then validate the submitted values for that step. If the submitted value falls out of the allowed range of values, an error is thrown against the submitted value, and the user will be returned to the current step, being shown the error.

Our next step is to create the submit function for our back button. Remember that we needed to explicitly set this function in order for #limit_validation_errors to work.

function ajax_form_multistep_form_back_submit($form, &$form_state)
{
	$form_state['step']--;
 
	// Because this is a multi-step form, we always need to rebuild the form
	// on every step, even after submission. This allows our form to be completely
	// Ajaxified without ever requiring a page load.
	$form_state['rebuild'] = TRUE;
}

This function is quite simple. Since all we are doing with this button is moving to the previous step, we decrease the current step by one, and rebuild the form. The form always needs to be rebuilt for this AJAX functionality to work.

Next we need our general submission function. This function will handle our forward button as well as our submission. Note that we could theoretically spin the forward button off into a separate function, the same as we did with the back button, but since we are not using #limit_validation_errors on the forward button, it's unnecessary to spin it into a separate function. Here is the start of our submission function:

function ajax_form_multistep_form_submit($form, &$form_state)
{
	// First we determine which step we are on, and save the
	// submitted values to $form_state['storage']. This will
	// allow our submitted values to persist.
	$step = $form_state['step'];
	$form_state['storage']['step_' . $step] = $form_state['values']['step_' . $step];

The first thing we do is to save the submitted value to our $form_state['storage'] object. This allows our values to persist between different steps of the form. Since this is a simplified example, we are able to do this in one line of code for all three steps, but it's likely in a real-world form, you will want to create a switch($form_state['step']) and create separate blocks of code for each step. But, regardless of how you do it, you will want to save the values to $form_state['storage'], and ideally with the same key as the form element itself (so as to provide consistency and make your code easy to follow).

Next, we need to add code to handle our forward button, as well as our submit button:

// Check to see if the next/forward button was clicked
if(isset($form_state['values']['forward']) && $form_state['values']['op'] == $form_state['values']['forward'])
{
	// Increase the step by one, to move on to the next step
	$form_state['step']++;
}
// Check to see if the final step has been submitted
elseif(isset($form_state['values']['submit']) && $form_state['values']['op'] == $form_state['values']['submit'])
{
	// Here we will collect the data and show the submitted values to the user in a message.
	// In a real-world application of this form, you would use the submitted values to complete the
	// purpose of the form - save them to the database and/or send an email and/or display something
	// to the user etc. The following is just s simple example to show how the values can be used.
	$items = array($form_state['storage']['step_1'], $form_state['storage']['step_2'], $form_state['storage']['step_3']);
	drupal_set_message(t('You submitted the following values:!values', array('!values' => theme('item_list', array('items' => $items)))));
 
	// The form has been completed, so we want to return the user to step 1
	// as well as clear any saved values.
	$form_state['step'] = 1;
	$form_state['storage'] = array();
}
 
// As in ajax_form_multistep_form_back_submit(), we need to set
// $form_state['rebuild'] to TRUE, in able to ensure that our
// our form is rebuilt, allowing for the multi-step process
$form_state['rebuild'] = TRUE;

With the above piece of code, first we first check to see if the next/forward button was clicked, and if it is, we increment the step by one. If the forward button was not clicked, then it means our submit button was clicked (though we add a check to ensure this, bulletproofing our form). When the submit button was clicked, we do something with the values. Generally this will mean saving them to the database or something, but in this example we are simply displaying a message to the user showing them their submitted values. And finally, we rebuild the form, as we always need to do in order to make this an AJAX form.

We have now created our entire submit function, which can be seen in its entirety here:

function ajax_form_multistep_form_submit($form, &$form_state)
{
	// First we determine which step we are on, and save the
	// submitted values to $form_state['storage']. This will
	// allow our submitted values to persist.
	$step = $form_state['step'];
	$form_state['storage']['step_' . $step] = $form_state['values']['step_' . $step];
 
	// Check to see if the next/forward button was clicked
	if(isset($form_state['values']['forward']) && $form_state['values']['op'] == $form_state['values']['forward'])
	{
		// Increase the step by one, to move on to the next step
		$form_state['step']++;
	}
	// Check to see if the final step has been submitted
	elseif(isset($form_state['values']['submit']) && $form_state['values']['op'] == $form_state['values']['submit'])
	{
		// Here we will collect the data and show the submitted values to the user in a message.
		// In a real-world application of this form, you would use the submitted values to complete the
		// purpose of the form - save them to the database and/or send an email and/or display something
		// to the user etc. The following is just s simple example to show how the values can be used.
		$items = array($form_state['storage']['step_1'], $form_state['storage']['step_2'], $form_state['storage']['step_3']);
		drupal_set_message(t('You submitted the following values:!values', array('!values' => theme('item_list', array('items' => $items)))));
 
		// The form has been completed, so we want to return the user to step 1
		// as well as clear any saved values.
		$form_state['step'] = 1;
		$form_state['storage'] = array();
	}
 
	// As in ajax_form_multistep_form_back_submit(), we need to set
	// $form_state['rebuild'] to TRUE, in able to ensure that our
	// our form is rebuilt, allowing for the multi-step process
	$form_state['rebuild'] = TRUE;
}

And that leaves us with one final step - we need to create our #ajax callback function that we defined on all of our buttons (back, forward and submit). This function is as follows:

function ajax_form_multistep_form_ajax_callback($form, &$form_state)
{
	return $form;
}

It's a very simple function, as it returns the entire form each time the function is called. This allows for our mulitple steps.

So, you've come this far. The only thing left is the non-JavaScript fallback. So, what do we need to do to ensure this works without JavaScript? And the answer is: nothing. It already does. Disable JavaScript on your browser and try out the form, and you will see that due to the way it is set up with the buttons, this form will work equally well with or without javascript enabled on the user's browser.

Hopefully this can help you step up the forms on your Drupal site, creating a more user-friendly interface for your users.

Here is the entire code for this tutorial:

function ajax_form_menu()
{
	$menu['ajax_form'] = array
	(
		'title' => 'Ajax multistep form example',
		'description' => 'Creates an ajax multistep form',
		'page callback' => 'drupal_get_form',
		'page arguments' => array('ajax_form_multistep_form'),
		'access callback' => TRUE,
	);
 
	return $menu;
}
 
function ajax_form_multistep_form($form, &$form_state)
{
	// If $form_state['step'] is not set, we set it to 1
	$form_state['step'] = isset($form_state['step']) ? $form_state['step'] : 1;
 
	// Add a wrapper div that will be used by the Form API to update the form using AJAX
	$form['#prefix'] = '<div id="ajax_form_multistep_form">';
	$form['#suffix'] = '</div>';
 
	// Depending on which step of the form we are on, we output different form fields
	switch($form_state['step'])
	{
		// Step 1
		case 1:
			$default_value = '';
			if(isset($form_state['values']['step_1']))
			{
				$default_value = $form_state['values']['step_1'];
			}
			elseif(isset($form_state['storage']['step_1']))
			{
				$default_value = $form_state['storage']['step_1'];
			}
 
			$form['step_1'] = array
			(
				'#type' => 'textfield',
				'#title' => t('Enter something alphabetical'),
				'#required' => TRUE,
				'#default_value' => $default_value,
			);
			break;
 
		// Step 2
		case 2:
			$default_value = '';
			if(isset($form_state['values']['step_2']))
			{
				$default_value = $form_state['values']['step_2'];
			}
			elseif(isset($form_state['storage']['step_2']))
			{
				$default_value = $form_state['storage']['step_2'];
			}
 
			$form['step_2'] = array
			(
				'#type' => 'textfield',
				'#title' => t('Enter something numerical'),
				'#required' => TRUE,
				'#default_value' => $default_value,
			);
 
			break;
 
		// Step 3
		case 3:
			$default_value = '';
			if(isset($form_state['values']['step_3']))
			{
				$default_value = $form_state['values']['step_3'];
			}
			elseif(isset($form_state['storage']['step_3']))
			{
				$default_value = $form_state['storage']['step_3'];
			}
 
			$form['step_3'] = array
			(
				'#type' => 'textfield',
				'#title' => t('Enter something alphanumerical'),
				'#required' => TRUE,
				'#default_value' => $default_value,
			);
 
			break;
	}
 
	// Create a container for our buttons
	$form['buttons'] = array
	(
		'#type' => 'container',
	);
	// If we are on step 1, we do not want a back button
	if($form_state['step'] !== 1)
	{
		$form['buttons']['back'] = array
		(
			'#type' => 'submit',
			'#value' => t('Back'),
			// Setting #limit_validation_errors to an empty array
			// ensures that the form fields are not validated when
			// the back button is used. Otherwise, we will get errors
			'#limit_validation_errors' => array(),
			// We need to create a #submit function for this button, or else
			// #limit_validation_errors has no effect. So we create a submit
			// function specifically for this submit button.
			'#submit' => array('ajax_form_multistep_form_back_submit'),
			'#ajax' => array
			(
				// We pass in the wrapper we created at the start of the form
				'wrapper' => 'ajax_form_multistep_form',
				// We pass a callback function we will use later to render the form for the user
				'callback' => 'ajax_form_multistep_form_ajax_callback',
			),
		);
	}
	// We only want a forward button if we are not on the last step of the form
	if($form_state['step'] !== 3)
	{
		$form['buttons']['forward'] = array
		(
			'#type' => 'submit',
			'#value' => t('Next'),
			'#ajax' => array
			(
				// We pass in the wrapper we created at the start of the form
				'wrapper' => 'ajax_form_multistep_form',
				// We pass a callback function we will use later to render the form for the user
				'callback' => 'ajax_form_multistep_form_ajax_callback',
			),
		);
	}
	// We only want a submit button if we are on the last step of the form
	else
	{
		$form['buttons']['submit'] = array
		(
			'#type' => 'submit',
			'#value' => t('Submit'),
			'#ajax' => array
			(
				// We pass in the wrapper we created at the start of the form
				'wrapper' => 'ajax_form_multistep_form',
				// We pass a callback function we will use later to render the form for the user
				'callback' => 'ajax_form_multistep_form_ajax_callback',
			),
		);
	}
 
	return $form;
}
 
function ajax_form_multistep_form_validate($form, &$form_state)
{
	// First we set up a switch for each of our three steps
	switch($form_state['step'])
	{
		case "1":
			// Check that the first value is alphabetic
			if(!preg_match('/^[a-zA-Z]+$/', $form_state['values']['step_1']))
			{
				form_set_error('step_1', t('Alphabetic characters only please'));
			}
 
			break;
 
		case "2":
			// Check that the second value is numeric
			if(preg_match('/\D/', $form_state['values']['step_2']))
			{
				form_set_error('step_2', t('Numeric characters only please'));
			}
 
			break;			
 
		case "3":
			// Check that the third value is alphanumeric
			if(!preg_match('/^[a-zA-Z0-9]+$/', $form_state['values']['step_3']))
			{
				form_set_error('step_3', t('Alphanumeric characters only please'));
			}
 
			break;
 
	}
}
 
function ajax_form_multistep_form_back_submit($form, &$form_state)
{
	$form_state['step']--;
 
	// Because this is a multi-step form, we always need to rebuild the form
	// on every step, even after submission. This allows our form to be completely
	// Ajaxified without ever requiring a page load.
	$form_state['rebuild'] = TRUE;
}
 
function ajax_form_multistep_form_submit($form, &$form_state)
{
	// First we determine which step we are on, and save the
	// submitted values to $form_state['storage']. This will
	// allow our submitted values to persist.
	$step = $form_state['step'];
	$form_state['storage']['step_' . $step] = $form_state['values']['step_' . $step];
 
	// Check to see if the next/forward button was clicked
	if(isset($form_state['values']['forward']) && $form_state['values']['op'] == $form_state['values']['forward'])
	{
		// Increase the step by one, to move on to the next step
		$form_state['step']++;
	}
	// Check to see if the final step has been submitted
	elseif(isset($form_state['values']['submit']) && $form_state['values']['op'] == $form_state['values']['submit'])
	{
		// Here we will collect the data and show the submitted values to the user in a message.
		// In a real-world application of this form, you would use the submitted values to complete the
		// purpose of the form - save them to the database and/or send an email and/or display something
		// to the user etc. The following is just s simple example to show how the values can be used.
		$items = array($form_state['storage']['step_1'], $form_state['storage']['step_2'], $form_state['storage']['step_3']);
		drupal_set_message(t('You submitted the following values:!values', array('!values' => theme('item_list', array('items' => $items)))));
 
		// The form has been completed, so we want to return the user to step 1
		// as well as clear any saved values.
		$form_state['step'] = 1;
		$form_state['storage'] = array();
	}
 
	// As in ajax_form_multistep_form_back_submit(), we need to set
	// $form_state['rebuild'] to TRUE, in able to ensure that our
	// our form is rebuilt, allowing for the multi-step process
	$form_state['rebuild'] = TRUE;
}
 
function ajax_form_multistep_form_ajax_callback($form, &$form_state)
{
	return $form;
}