updated core to 7.58 (right after the site was hacked)

This commit is contained in:
2018-04-20 23:48:40 +02:00
parent 18f4aba146
commit 9344a61b61
711 changed files with 99690 additions and 480 deletions

View File

@@ -0,0 +1,12 @@
name = Form example
description = Examples of using the Drupal Form API.
package = Example modules
core = 7.x
files[] = form_example.test
; Information added by Drupal.org packaging script on 2017-01-10
version = "7.x-1.x-dev"
core = "7.x"
project = "examples"
datestamp = "1484076787"

View File

@@ -0,0 +1,234 @@
<?php
/**
* @file
* Examples demonstrating the Drupal Form API.
*/
/**
* @defgroup form_example Example: Form API
* @ingroup examples
* @{
* Examples demonstrating the Drupal Form API.
*
* The Form Example module is a part of the Examples for Developers Project
* and provides various Drupal Form API Examples. You can download and
* experiment with this code at the
* @link http://drupal.org/project/examples Examples for Developers project page. @endlink
*/
/**
* Implements hook_menu().
*
* Here we set up the URLs (menu entries) for the
* form examples. Note that most of the menu items
* have page callbacks and page arguments set, with
* page arguments set to be functions in external files.
*/
function form_example_menu() {
$items = array();
$items['examples/form_example'] = array(
'title' => 'Form Example',
'page callback' => 'form_example_intro',
'access callback' => TRUE,
'expanded' => TRUE,
);
$items['examples/form_example/tutorial'] = array(
'title' => 'Form Tutorial',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_1'),
'access callback' => TRUE,
'description' => 'A set of ten tutorials',
'file' => 'form_example_tutorial.inc',
'type' => MENU_NORMAL_ITEM,
);
$items['examples/form_example/tutorial/1'] = array(
'title' => '#1',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_1'),
'access callback' => TRUE,
'description' => 'Tutorial 1: Simplest form',
'type' => MENU_DEFAULT_LOCAL_TASK,
'file' => 'form_example_tutorial.inc',
);
$items['examples/form_example/tutorial/2'] = array(
'title' => '#2',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_2'),
'access callback' => TRUE,
'description' => 'Tutorial 2: Form with a submit button',
'type' => MENU_LOCAL_TASK,
'file' => 'form_example_tutorial.inc',
);
$items['examples/form_example/tutorial/3'] = array(
'title' => '#3',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_3'),
'access callback' => TRUE,
'description' => 'Tutorial 3: Fieldsets',
'type' => MENU_LOCAL_TASK,
'file' => 'form_example_tutorial.inc',
);
$items['examples/form_example/tutorial/4'] = array(
'title' => '#4',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_4'),
'access callback' => TRUE,
'description' => 'Tutorial 4: Required fields',
'type' => MENU_LOCAL_TASK,
'file' => 'form_example_tutorial.inc',
);
$items['examples/form_example/tutorial/5'] = array(
'title' => '#5',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_5'),
'access callback' => TRUE,
'description' => 'Tutorial 5: More element attributes',
'type' => MENU_LOCAL_TASK,
'file' => 'form_example_tutorial.inc',
);
$items['examples/form_example/tutorial/6'] = array(
'title' => '#6',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_6'),
'access callback' => TRUE,
'description' => 'Tutorial 6: Form with a validate handler',
'type' => MENU_LOCAL_TASK,
'file' => 'form_example_tutorial.inc',
);
$items['examples/form_example/tutorial/7'] = array(
'title' => '#7',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_7'),
'access callback' => TRUE,
'description' => 'Tutorial 7: Form with a submit handler',
'type' => MENU_LOCAL_TASK,
'file' => 'form_example_tutorial.inc',
);
$items['examples/form_example/tutorial/8'] = array(
'title' => '#8',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_8'),
'access callback' => TRUE,
'description' => 'Tutorial 8: Basic multistep form',
'type' => MENU_LOCAL_TASK,
'file' => 'form_example_tutorial.inc',
);
$items['examples/form_example/tutorial/9'] = array(
'title' => '#9',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_9'),
'access callback' => TRUE,
'description' => 'Tutorial 9: Form with dynamically added new fields',
'type' => MENU_LOCAL_TASK,
'file' => 'form_example_tutorial.inc',
'weight' => 9,
);
$items['examples/form_example/tutorial/10'] = array(
'title' => '#10',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_10'),
'access callback' => TRUE,
'description' => 'Tutorial 10: Form with file upload',
'type' => MENU_LOCAL_TASK,
'file' => 'form_example_tutorial.inc',
'weight' => 10,
);
$items['examples/form_example/tutorial/11'] = array(
'title' => '#11',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_11'),
'access callback' => TRUE,
'description' => 'Tutorial 11: generating a confirmation form',
'type' => MENU_LOCAL_TASK,
'file' => 'form_example_tutorial.inc',
'weight' => 11,
);
$items['examples/form_example/tutorial/11/confirm/%'] = array(
'title' => 'Name Confirmation',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_tutorial_11_confirm_name', 5),
'access callback' => TRUE,
'description' => 'Confirmation form for tutorial 11. Generated using the confirm_form function',
'file' => 'form_example_tutorial.inc',
);
$items['examples/form_example/states'] = array(
'title' => '#states example',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_states_form'),
'access callback' => TRUE,
'description' => 'How to use the #states attribute in FAPI',
'file' => 'form_example_states.inc',
);
$items['examples/form_example/wizard'] = array(
'title' => 'Extensible wizard example',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_wizard'),
'access callback' => TRUE,
'description' => 'A general approach to a wizard multistep form.',
'file' => 'form_example_wizard.inc',
);
$items['examples/form_example/element_example'] = array(
'title' => 'Element example',
'page callback' => 'drupal_get_form',
'page arguments' => array('form_example_element_demo_form'),
'access callback' => TRUE,
'file' => 'form_example_elements.inc',
'weight' => 100,
);
return $items;
}
/**
* Page callback for our general info page.
*/
function form_example_intro() {
$markup = t('The form example module provides a tutorial, extensible multistep example, an element example, and a #states example');
return array('#markup' => $markup);
}
/**
* Implements hook_help().
*/
function form_example_help($path, $arg) {
switch ($path) {
case 'examples/form_example/tutorial':
// TODO: Update the URL.
$help = t('This form example tutorial for Drupal 7 is the code from the <a href="http://drupal.org/node/262422">Handbook 10-step tutorial</a>');
break;
case 'examples/form_example/element_example':
$help = t('The Element Example shows how modules can provide their own Form API element types. Four different element types are demonstrated.');
break;
}
if (!empty($help)) {
return '<p>' . $help . '</p>';
}
}
/**
* Implements hook_element_info().
*
* To keep the various pieces of the example together in external files,
* this just returns _form_example_elements().
*/
function form_example_element_info() {
require_once 'form_example_elements.inc';
return _form_example_element_info();
}
/**
* Implements hook_theme().
*
* The only theme implementation is by the element example. To keep the various
* parts of the example together, this actually returns
* _form_example_element_theme().
*/
function form_example_theme($existing, $type, $theme, $path) {
require_once 'form_example_elements.inc';
return _form_example_element_theme($existing, $type, $theme, $path);
}
/**
* @} End of "defgroup form_example".
*/

View File

@@ -0,0 +1,288 @@
<?php
/**
* @file
* Test file for form_example module.
*/
/**
* Default test case for the form_example module.
*
* @ingroup form_example
*/
class FormExampleTestCase extends DrupalWebTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Form Example',
'description' => 'Various tests on the form_example module.' ,
'group' => 'Examples',
);
}
/**
* Enable modules.
*/
public function setUp() {
parent::setUp('form_example');
}
/**
* Test each tutorial.
*/
public function testTutorials() {
// Tutorial #1
$this->drupalGet('examples/form_example/tutorial');
$this->assertText(t('#9'));
// #2
$this->drupalPost('examples/form_example/tutorial/2', array('name' => t('name')), t('Submit'));
// #4
$this->drupalPost('examples/form_example/tutorial/4',
array('first' => t('firstname'), 'last' => t('lastname')), t('Submit'));
$this->drupalPost('examples/form_example/tutorial/4', array(), t('Submit'));
$this->assertText(t('First name field is required'));
$this->assertText(t('Last name field is required'));
// #5
$this->drupalPost('examples/form_example/tutorial/5',
array('first' => t('firstname'), 'last' => t('lastname')), t('Submit'));
$this->assertText(t('Please enter your first name'));
$this->drupalPost('examples/form_example/tutorial/4', array(), t('Submit'));
$this->assertText(t('First name field is required'));
$this->assertText(t('Last name field is required'));
// #6
$this->drupalPost(
'examples/form_example/tutorial/6',
array(
'first' => t('firstname'),
'last' => t('lastname'),
'year_of_birth' => 1955,
),
t('Submit'));
$this->assertNoText(t('Enter a year between 1900 and 2000'));
$this->drupalPost(
'examples/form_example/tutorial/6',
array(
'first' => t('firstname'),
'last' => t('lastname'),
'year_of_birth' => 1855,
),
t('Submit')
);
$this->assertText(t('Enter a year between 1900 and 2000'));
// #7
$this->drupalPost(
'examples/form_example/tutorial/7',
array(
'first' => t('firstname'),
'last' => t('lastname'),
'year_of_birth' => 1955,
),
t('Submit')
);
$this->assertText(t('The form has been submitted. name="firstname lastname", year of birth=1955'));
$this->drupalPost(
'examples/form_example/tutorial/7',
array(
'first' => t('firstname'),
'last' => t('lastname'),
'year_of_birth' => 1855,
),
t('Submit')
);
$this->assertText(t('Enter a year between 1900 and 2000'));
// Test tutorial #8.
$this->drupalPost(
'examples/form_example/tutorial/8',
array(
'first' => t('firstname'),
'last' => t('lastname'),
'year_of_birth' => 1955,
),
t('Next >>')
);
$this->drupalPost(NULL, array('color' => t('green')), t('<< Back'));
$this->drupalPost(NULL, array(), t('Next >>'));
$this->drupalPost(NULL, array('color' => t('red')), t('Submit'));
$this->assertText(t('The form has been submitted. name="firstname lastname", year of birth=1955'));
$this->assertText(t('And the favorite color is red'));
// #9
$url = 'examples/form_example/tutorial/9';
for ($i = 1; $i <= 4; $i++) {
if ($i > 1) {
// Later steps of multistep form take NULL.
$url = NULL;
}
$this->drupalPost(
$url,
array(
"name[$i][first]" => "firstname $i",
"name[$i][last]" => "lastname $i",
"name[$i][year_of_birth]" => 1950 + $i,
),
t('Add another name')
);
$this->assertText(t('Name #@num', array('@num' => $i + 1)));
}
// Now remove the last name added (#5).
$this->drupalPost(NULL, array(), t('Remove latest name'));
$this->assertNoText("Name #5");
$this->drupalPost(NULL, array(), t('Submit'));
$this->assertText('Form 9 has been submitted');
for ($i = 1; $i <= 4; $i++) {
$this->assertText(t('@num: firstname @num lastname @num (@year)', array('@num' => $i, '@year' => 1950 + $i)));
}
// #10
$url = 'examples/form_example/tutorial/10';
$this->drupalPost($url, array(), t('Submit'));
$this->assertText(t('No file was uploaded.'));
// Get sample images.
$images = $this->drupalGetTestFiles('image');
foreach ($images as $image) {
$this->drupalPost($url, array('files[file]' => drupal_realpath($image->uri)), t('Submit'));
$this->assertText(t('The form has been submitted and the image has been saved, filename: @filename.', array('@filename' => $image->filename)));
}
// #11: Confirmation form.
// Try to submit without a name.
$url = 'examples/form_example/tutorial/11';
$this->drupalPost($url, array(), t('Submit'));
$this->assertText('Name field is required.');
// Verify that we can enter a name and get the confirmation form.
$this->drupalPost(
$url,
array('name' => t('name 1')), t('Submit')
);
$this->assertText(t('Is this really your name?'));
$this->assertFieldById('edit-name', 'name 1');
// Check the 'yes' button.
$confirmation_text = t("Confirmation form submission recieved. According to your submission your name is '@name'", array('@name' => 'name 1'));
$url = 'examples/form_example/tutorial/11/confirm/name%201';
$this->drupalPost($url, array(), t('This is my name'));
$this->assertText($confirmation_text);
// Check the 'no' button.
$this->drupalGet($url);
$this->clickLink(t('Nope, not my name'));
$this->assertNoText($confirmation_text);
}
/**
* Test Wizard tutorial.
*
* @TODO improve this using drupal_form_submit
*/
public function testWizard() {
// Check if the wizard is there.
$this->drupalGet('examples/form_example/wizard');
$this->assertText(t('Extensible wizard example'));
$first_name = $this->randomName(8);
$last_name = $this->randomName(8);
$city = $this->randomName(8);
$aunts_name = $this->randomName(8);
// Submit the first step of the wizard.
$options = array(
'first_name' => $first_name,
'last_name' => $last_name,
);
$this->drupalPost('examples/form_example/wizard', $options, t('Next'));
// A label city is created, and two buttons appear, Previous and Next.
$this->assertText(t('Hint: Do not enter "San Francisco", and do not leave this out.'));
// Go back to the beginning and verify that the value is there.
$this->drupalPost(NULL, array(), t('Previous'));
$this->assertFieldByName('first_name', $first_name);
$this->assertFieldByName('last_name', $last_name);
// Go next. We should keep our values.
$this->drupalPost(NULL, array(), t('Next'));
$this->assertText(t('Hint: Do not enter "San Francisco", and do not leave this out.'));
// Try "San Francisco".
$this->drupalPost(NULL, array('city' => 'San Francisco'), t('Next'));
$this->assertText(t('You were warned not to enter "San Francisco"'));
// Try the real city.
$this->drupalPost(NULL, array('city' => $city), t('Next'));
// Enter the Aunt's name, but then the previous button.
$this->drupalPost(NULL, array('aunts_name' => $aunts_name), t('Previous'));
$this->assertFieldByName('city', $city);
// Go to first step and re-check all fields.
$this->drupalPost(NULL, array(), t('Previous'));
$this->assertFieldByName('first_name', $first_name);
$this->assertFieldByName('last_name', $last_name);
// Re-check second step.
$this->drupalPost(NULL, array(), t('Next'));
$this->assertText(t('Hint: Do not enter "San Francisco", and do not leave this out.'));
$this->assertFieldByName('city', $city);
// Re-check third step.
$this->drupalPost(NULL, array(), t('Next'));
$this->assertFieldByName('aunts_name', $aunts_name);
// Press finish and check for correct values.
$this->drupalPost(NULL, array(), t('Finish'));
$this->assertRaw(t('[first_name] =&gt; @first_name', array('@first_name' => $first_name)));
$this->assertRaw(t('[last_name] =&gt; @last_name', array('@last_name' => $last_name)));
$this->assertRaw(t('[city] =&gt; @city', array('@city' => $city)));
$this->assertRaw(t('[aunts_name] =&gt; @aunts_name', array('@aunts_name' => $aunts_name)));
}
/**
* Test the element_example form for correct behavior.
*/
public function testElementExample() {
// Make one basic POST with a set of values and check for correct responses.
$edit = array(
'a_form_example_textfield' => $this->randomName(),
'a_form_example_checkbox' => TRUE,
'a_form_example_element_discrete[areacode]' => sprintf('%03d', rand(0, 999)),
'a_form_example_element_discrete[prefix]' => sprintf('%03d', rand(0, 999)),
'a_form_example_element_discrete[extension]' => sprintf('%04d', rand(0, 9999)),
'a_form_example_element_combined[areacode]' => sprintf('%03d', rand(0, 999)),
'a_form_example_element_combined[prefix]' => sprintf('%03d', rand(0, 999)),
'a_form_example_element_combined[extension]' => sprintf('%04d', rand(0, 9999)),
);
$this->drupalPost('examples/form_example/element_example', $edit, t('Submit'));
$this->assertText(t('a_form_example_textfield has value @value', array('@value' => $edit['a_form_example_textfield'])));
$this->assertText(t('a_form_example_checkbox has value 1'));
$this->assertPattern(t('/areacode.*!areacode/', array('!areacode' => $edit['a_form_example_element_discrete[areacode]'])));
$this->assertPattern(t('/prefix.*!prefix/', array('!prefix' => $edit['a_form_example_element_discrete[prefix]'])));
$this->assertPattern(t('/extension.*!extension/', array('!extension' => $edit['a_form_example_element_discrete[extension]'])));
$this->assertText(t('a_form_example_element_combined has value @value', array('@value' => $edit['a_form_example_element_combined[areacode]'] . $edit['a_form_example_element_combined[prefix]'] . $edit['a_form_example_element_combined[extension]'])));
// Now flip the checkbox and check for correct behavior.
$edit['a_form_example_checkbox'] = FALSE;
$this->drupalPost('examples/form_example/element_example', $edit, t('Submit'));
$this->assertText(t('a_form_example_checkbox has value 0'));
}
}

View File

@@ -0,0 +1,531 @@
<?php
/**
* @file
* This is an example demonstrating how a module can define custom form and
* render elements.
*
* Form elements are already familiar to anyone who uses Form API. They share
* history with render elements, which are explained in the
* @link render_example.module Render Example @endlink. Examples
* of core form elements are 'textfield', 'checkbox' and 'fieldset'. Drupal
* utilizes hook_elements() to define these FAPI types, and this occurs in
* the core function system_elements().
*
* Each form element has a #type value that determines how it is treated by
* the Form API and how it is ultimately rendered into HTML.
* hook_element_info() allows modules to define new element types, and tells
* the Form API what default values they should automatically be populated with.
*
* By implementing hook_element_info() in your own module, you can create custom
* form (or render) elements with their own properties, validation and theming.
*
* In this example, we define a series of elements that range from trivial
* (a renamed textfield) to more advanced (a telephone number field with each
* portion separately validated).
*
* Since each element can use arbitrary properties (like #process or #dropthis)
* it can be quite complicated to figure out what all the properties actually
* mean. This example won't undertake the exhaustive task of explaining them
* all, as that would probably be impossible.
*/
/**
* @todo: Some additional magic things to explain:
* - #process and process callback (and naming) (in forms)
* - #value and value callback (and naming of the above)
* - #theme and #theme_wrappers
* - What is #return_value?
* - system module provides the standard default elements.
* - What are all the things that can be defined in hook_element_info() and
* where do the defaults come from?
* - Form elements that have a type that has a matching type in the element
* array created by hook_element_info() get those items merged with them.
* - #value_callback is called first by form_builder(). Its job is to figure
* out what the actual value of the element, using #default_value or whatever.
* - #process is then called to allow changes to the whole element (like adding
* child form elements.)
* - #return_value: chx: you need three different values for form API. You need
* the default value (#default_value), the value for the element if it gets
* checked )#return_value) and then #value which is either 0 or the
* #return_value
*/
/**
* Utility function providing data for form_example_element_info().
*
* This defines several new form element types.
*
* - form_example_textfield: This is actually just a textfield, but provides
* the new type. If more were to be done with it a theme function could be
* provided.
* - form_example_checkbox: Nothing more than a regular checkbox, but uses
* an alternate theme function provided by this module.
* - form_example_phonenumber_discrete: Provides a North-American style
* three-part phonenumber where the value of the phonenumber is managed
* as an array of three parts.
* - form_example_phonenumber_combined: Provides a North-American style
* three-part phonenumber where the actual value is managed as a 10-digit
* string and only broken up into three parts for the user interface.
*
* form_builder() has significant discussion of #process and #value_callback.
* See also hook_element_info().
*
* system_element_info() contains the Drupal default element types, which can
* also be used as examples.
*/
function _form_example_element_info() {
// form_example_textfield is a trivial element based on textfield that
// requires only a definition and a theme function. In this case we provide
// the theme function using the parent "textfield" theme function, but it
// would by default be provided in hook_theme(), by a "form_example_textfield"
// theme implementation, provided by default by the function
// theme_form_example_textfield(). Note that the 'form_example_textfield'
// element type is completely defined here. There is no further code required
// for it.
$types['form_example_textfield'] = array(
// #input = TRUE means that the incoming value will be used to figure out
// what #value will be.
'#input' => TRUE,
// Use theme('textfield') to format this element on output.
'#theme' => array('textfield'),
// Do not provide autocomplete.
'#autocomplete_path' => FALSE,
// Allow theme('form_element') to control the markup surrounding this
// value on output.
'#theme_wrappers' => array('form_element'),
);
// form_example_checkbox is mostly a copy of the system-defined checkbox
// element.
$types['form_example_checkbox'] = array(
// This is an HTML <input>.
'#input' => TRUE,
// @todo: Explain #return_value.
'#return_value' => TRUE,
// Our #process array will use the standard process functions used for a
// regular checkbox.
'#process' => array('form_process_checkbox', 'ajax_process_form'),
// Use theme('form_example_checkbox') to render this element on output.
'#theme' => 'form_example_checkbox',
// Use theme('form_element') to provide HTML wrappers for this element.
'#theme_wrappers' => array('form_element'),
// Place the title after the element (to the right of the checkbox).
// This attribute affects the behavior of theme_form_element().
'#title_display' => 'after',
// We use the default function name for the value callback, so it does not
// have to be listed explicitly. The pattern for the default function name
// is form_type_TYPENAME_value().
// '#value_callback' => 'form_type_form_example_checkbox_value',
);
// This discrete phonenumber element keeps its values as the separate elements
// area code, prefix, extension.
$types['form_example_phonenumber_discrete'] = array(
// #input == TRUE means that the form value here will be used to determine
// what #value will be.
'#input' => TRUE,
// #process is an array of callback functions executed when this element is
// processed. Here it provides the child form elements which define
// areacode, prefix, and extension.
'#process' => array('form_example_phonenumber_discrete_process'),
// Validation handlers for this element. These are in addition to any
// validation handlers that might.
'#element_validate' => array('form_example_phonenumber_discrete_validate'),
'#autocomplete_path' => FALSE,
'#theme_wrappers' => array('form_example_inline_form_element'),
);
// Define form_example_phonenumber_combined, which combines the phone
// number into a single validated text string.
$types['form_example_phonenumber_combined'] = array(
'#input' => TRUE ,
'#process' => array('form_example_phonenumber_combined_process'),
'#element_validate' => array('form_example_phonenumber_combined_validate'),
'#autocomplete_path' => FALSE,
'#value_callback' => 'form_example_phonenumber_combined_value',
'#default_value' => array(
'areacode' => '',
'prefix' => '',
'extension' => '',
),
'#theme_wrappers' => array('form_example_inline_form_element'),
);
return $types;
}
/**
* Value callback for form_example_phonenumber_combined.
*
* Builds the current combined value of the phone number only when the form
* builder is not processing the input.
*
* @param array $element
* Form element.
* @param array $input
* Input.
* @param array $form_state
* Form state.
*
* @return array
* The modified element.
*/
function form_example_phonenumber_combined_value(&$element, $input = FALSE, $form_state = NULL) {
if (!$form_state['process_input']) {
$matches = array();
$match = preg_match('/^(\d{3})(\d{3})(\d{4})$/', $element['#default_value'], $matches);
if ($match) {
// Get rid of the "all match" element.
array_shift($matches);
list($element['areacode'], $element['prefix'], $element['extension']) = $matches;
}
}
return $element;
}
/**
* Value callback for form_example_checkbox element type.
*
* Copied from form_type_checkbox_value().
*
* @param array $element
* The form element whose value is being populated.
* @param mixed $input
* The incoming input to populate the form element. If this is FALSE, meaning
* there is no input, the element's default value should be returned.
*
* @return int
* The value represented by the form element.
*/
function form_type_form_example_checkbox_value($element, $input = FALSE) {
if ($input === FALSE) {
return isset($element['#default_value']) ? $element['#default_value'] : 0;
}
else {
return isset($input) ? $element['#return_value'] : 0;
}
}
/**
* Process callback for the discrete version of phonenumber.
*/
function form_example_phonenumber_discrete_process($element, &$form_state, $complete_form) {
// #tree = TRUE means that the values in $form_state['values'] will be stored
// hierarchically. In this case, the parts of the element will appear in
// $form_state['values'] as
// $form_state['values']['<element_name>']['areacode'],
// $form_state['values']['<element_name>']['prefix'],
// etc. This technique is preferred when an element has member form
// elements.
$element['#tree'] = TRUE;
// Normal FAPI field definitions, except that #value is defined.
$element['areacode'] = array(
'#type' => 'textfield',
'#size' => 3,
'#maxlength' => 3,
'#value' => $element['#value']['areacode'],
'#required' => TRUE,
'#prefix' => '(',
'#suffix' => ')',
);
$element['prefix'] = array(
'#type' => 'textfield',
'#size' => 3,
'#maxlength' => 3,
'#required' => TRUE,
'#value' => $element['#value']['prefix'],
);
$element['extension'] = array(
'#type' => 'textfield',
'#size' => 4,
'#maxlength' => 4,
'#value' => $element['#value']['extension'],
);
return $element;
}
/**
* Validation handler for the discrete version of the phone number.
*
* Uses regular expressions to check that:
* - the area code is a three digit number.
* - the prefix is numeric 3-digit number.
* - the extension is a numeric 4-digit number.
*
* Any problems are shown on the form element using form_error().
*/
function form_example_phonenumber_discrete_validate($element, &$form_state) {
if (isset($element['#value']['areacode'])) {
if (0 == preg_match('/^\d{3}$/', $element['#value']['areacode'])) {
form_error($element['areacode'], t('The area code is invalid.'));
}
}
if (isset($element['#value']['prefix'])) {
if (0 == preg_match('/^\d{3}$/', $element['#value']['prefix'])) {
form_error($element['prefix'], t('The prefix is invalid.'));
}
}
if (isset($element['#value']['extension'])) {
if (0 == preg_match('/^\d{4}$/', $element['#value']['extension'])) {
form_error($element['extension'], t('The extension is invalid.'));
}
}
return $element;
}
/**
* Process callback for the combined version of the phonenumber element.
*/
function form_example_phonenumber_combined_process($element, &$form_state, $complete_form) {
// #tree = TRUE means that the values in $form_state['values'] will be stored
// hierarchically. In this case, the parts of the element will appear in
// $form_state['values'] as
// $form_state['values']['<element_name>']['areacode'],
// $form_state['values']['<element_name>']['prefix'],
// etc. This technique is preferred when an element has member form
// elements.
$element['#tree'] = TRUE;
// Normal FAPI field definitions, except that #value is defined.
$element['areacode'] = array(
'#type' => 'textfield',
'#size' => 3,
'#maxlength' => 3,
'#required' => TRUE,
'#prefix' => '(',
'#suffix' => ')',
);
$element['prefix'] = array(
'#type' => 'textfield',
'#size' => 3,
'#maxlength' => 3,
'#required' => TRUE,
);
$element['extension'] = array(
'#type' => 'textfield',
'#size' => 4,
'#maxlength' => 4,
'#required' => TRUE,
);
$matches = array();
$match = preg_match('/^(\d{3})(\d{3})(\d{4})$/', $element['#default_value'], $matches);
if ($match) {
// Get rid of the "all match" element.
array_shift($matches);
list($element['areacode']['#default_value'], $element['prefix']['#default_value'], $element['extension']['#default_value']) = $matches;
}
return $element;
}
/**
* Phone number validation function for the combined phonenumber.
*
* Uses regular expressions to check that:
* - the area code is a three digit number
* - the prefix is numeric 3-digit number
* - the extension is a numeric 4-digit number
*
* Any problems are shown on the form element using form_error().
*
* The combined value is then updated in the element.
*/
function form_example_phonenumber_combined_validate($element, &$form_state) {
$lengths = array(
'areacode' => 3,
'prefix' => 3,
'extension' => 4,
);
foreach ($lengths as $member => $length) {
$regex = '/^\d{' . $length . '}$/';
if (!empty($element['#value'][$member]) && 0 == preg_match($regex, $element['#value'][$member])) {
form_error($element[$member], t('@member is invalid', array('@member' => $member)));
}
}
// Consolidate into the three parts into one combined value.
$value = $element['areacode']['#value'] . $element['prefix']['#value'] . $element['extension']['#value'];
form_set_value($element, $value, $form_state);
return $element;
}
/**
* Called by form_example_theme() to provide hook_theme().
*
* This is kept in this file so it can be with the theme functions it presents.
* Otherwise it would get lonely.
*/
function _form_example_element_theme() {
return array(
'form_example_inline_form_element' => array(
'render element' => 'element',
'file' => 'form_example_elements.inc',
),
'form_example_checkbox' => array(
'render element' => 'element',
'file' => 'form_example_elements.inc',
),
);
}
/**
* Themes a custom checkbox.
*
* This doesn't actually do anything, but is here to show that theming can
* be done here.
*/
function theme_form_example_checkbox($variables) {
$element = $variables['element'];
return theme('checkbox', $element);
}
/**
* Formats child form elements as inline elements.
*/
function theme_form_example_inline_form_element($variables) {
$element = $variables['element'];
// Add element #id for #type 'item'.
if (isset($element['#markup']) && !empty($element['#id'])) {
$attributes['id'] = $element['#id'];
}
// Add element's #type and #name as class to aid with JS/CSS selectors.
$attributes['class'] = array('form-item');
if (!empty($element['#type'])) {
$attributes['class'][] = 'form-type-' . strtr($element['#type'], '_', '-');
}
if (!empty($element['#name'])) {
$attributes['class'][] = 'form-item-' . strtr($element['#name'],
array(
' ' => '-',
'_' => '-',
'[' => '-',
']' => '',
)
);
}
// Add a class for disabled elements to facilitate cross-browser styling.
if (!empty($element['#attributes']['disabled'])) {
$attributes['class'][] = 'form-disabled';
}
$output = '<div' . drupal_attributes($attributes) . '>' . "\n";
// If #title is not set, we don't display any label or required marker.
if (!isset($element['#title'])) {
$element['#title_display'] = 'none';
}
$prefix = isset($element['#field_prefix']) ? '<span class="field-prefix">' . $element['#field_prefix'] . '</span> ' : '';
$suffix = isset($element['#field_suffix']) ? ' <span class="field-suffix">' . $element['#field_suffix'] . '</span>' : '';
switch ($element['#title_display']) {
case 'before':
$output .= ' ' . theme('form_element_label', $variables);
$output .= ' ' . '<div class="container-inline">' . $prefix . $element['#children'] . $suffix . "</div>\n";
break;
case 'invisible':
case 'after':
$output .= ' ' . $prefix . $element['#children'] . $suffix;
$output .= ' ' . theme('form_element_label', $variables) . "\n";
break;
case 'none':
case 'attribute':
// Output no label and no required marker, only the children.
$output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
break;
}
if (!empty($element['#description'])) {
$output .= ' <div class="description">' . $element['#description'] . "</div>\n";
}
$output .= "</div>\n";
return $output;
}
/**
* Form content for examples/form_example/element_example.
*
* Simple form to demonstrate how to use the various new FAPI elements
* we've defined.
*/
function form_example_element_demo_form($form, &$form_state) {
$form['a_form_example_textfield'] = array(
'#type' => 'form_example_textfield',
'#title' => t('Form Example textfield'),
'#default_value' => variable_get('form_example_textfield', ''),
'#description' => t('form_example_textfield is a new type, but it is actually uses the system-provided functions of textfield'),
);
$form['a_form_example_checkbox'] = array(
'#type' => 'form_example_checkbox',
'#title' => t('Form Example checkbox'),
'#default_value' => variable_get('form_example_checkbox', FALSE),
'#description' => t('Nothing more than a regular checkbox but with a theme provided by this module.'),
);
$form['a_form_example_element_discrete'] = array(
'#type' => 'form_example_phonenumber_discrete',
'#title' => t('Discrete phone number'),
'#default_value' => variable_get(
'form_example_element_discrete',
array(
'areacode' => '999',
'prefix' => '999',
'extension' => '9999',
)
),
'#description' => t('A phone number : areacode (XXX), prefix (XXX) and extension (XXXX). This one uses a "discrete" element type, one which stores the three parts of the telephone number separately.'),
);
$form['a_form_example_element_combined'] = array(
'#type' => 'form_example_phonenumber_combined',
'#title' => t('Combined phone number'),
'#default_value' => variable_get('form_example_element_combined', '0000000000'),
'#description' => t('form_example_element_combined one uses a "combined" element type, one with a single 10-digit value which is broken apart when needed.'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Submit handler for form_example_element_demo_form().
*/
function form_example_element_demo_form_submit($form, &$form_state) {
// Exclude unnecessary elements.
unset($form_state['values']['submit'], $form_state['values']['form_id'], $form_state['values']['op'], $form_state['values']['form_token'], $form_state['values']['form_build_id']);
foreach ($form_state['values'] as $key => $value) {
variable_set($key, $value);
drupal_set_message(
t('%name has value %value',
array(
'%name' => $key,
'%value' => print_r($value, TRUE),
)
)
);
}
}

View File

@@ -0,0 +1,296 @@
<?php
/**
* @file
* An example of how to use the new #states Form API element, allowing
* dynamic form behavior with very simple setup.
*/
/**
* States demo form.
*
* This form shows off the #states system by dynamically showing parts of the
* form based on the state of other parts.
*
* @ingroup form_example
*
* The basic idea is that you add a #states property to the element which is
* to be changed based on some action elsewhere on the form. The #states
* property lists a change which is to be made, and under what conditions
* that change should be made.
*
* For example, in the 'tests_taken' form element below we have:
* @code
* '#states' => array(
* 'visible' => array(
* ':input[name="student_type"]' => array('value' => 'high_school'),
* ),
* ),
* @endcode
* Meaning that the element is to be made visible when the condition is met.
* The condition is a combination of a jQuery selector (which selects the
* element we want to test) and a condition for that element. In this case,
* the condition is whether the return value of the 'student_type' element is
* 'high_school'. If it is, this element will be visible.
*
* So the syntax is:
* @code
* '#states' => array(
* 'action_to_take_on_this_form_element' => array(
* 'jquery_selector_for_another_element' => array(
* 'condition_type' => value,
* ),
* ),
* ),
* @endcode
*
* If you need an action to take place only when two different conditions are
* true, then you add both of those conditions to the action. See the
* 'country_writein' element below for an example.
*
* Note that the easiest way to select a textfield, checkbox, or select is with
* the
* @link http://api.jquery.com/input-selector/ ':input' jquery shortcut @endlink,
* which selects any any of those.
*
* There are examples below of changing or hiding an element when a checkbox
* is checked, when a textarea is filled, when a select has a given value.
*
* See drupal_process_states() for full documentation.
*
* @see forms_api_reference.html
*/
function form_example_states_form($form, &$form_state) {
$form['student_type'] = array(
'#type' => 'radios',
'#options' => array(
'high_school' => t('High School'),
'undergraduate' => t('Undergraduate'),
'graduate' => t('Graduate'),
),
'#title' => t('What type of student are you?'),
);
$form['high_school'] = array(
'#type' => 'fieldset',
'#title' => t('High School Information'),
// This #states rule says that the "high school" fieldset should only
// be shown if the "student_type" form element is set to "High School".
'#states' => array(
'visible' => array(
':input[name="student_type"]' => array('value' => 'high_school'),
),
),
);
// High school information.
$form['high_school']['tests_taken'] = array(
'#type' => 'checkboxes',
'#options' => drupal_map_assoc(array(t('SAT'), t('ACT'))),
'#title' => t('What standardized tests did you take?'),
// This #states rule says that this checkboxes array will be visible only
// when $form['student_type'] is set to t('High School').
// It uses the jQuery selector :input[name=student_type] to choose the
// element which triggers the behavior, and then defines the "High School"
// value as the one that triggers visibility.
'#states' => array(
// Action to take.
'visible' => array(
':input[name="student_type"]' => array('value' => 'high_school'),
),
),
);
$form['high_school']['sat_score'] = array(
'#type' => 'textfield',
'#title' => t('Your SAT score:'),
'#size' => 4,
// This #states rule limits visibility to when the $form['tests_taken']
// 'SAT' checkbox is checked."
'#states' => array(
// Action to take.
'visible' => array(
':input[name="tests_taken[SAT]"]' => array('checked' => TRUE),
),
),
);
$form['high_school']['act_score'] = array(
'#type' => 'textfield',
'#title' => t('Your ACT score:'),
'#size' => 4,
// Set this element visible if the ACT checkbox above is checked.
'#states' => array(
// Action to take.
'visible' => array(
':input[name="tests_taken[ACT]"]' => array('checked' => TRUE),
),
),
);
// Undergrad information.
$form['undergraduate'] = array(
'#type' => 'fieldset',
'#title' => t('Undergraduate Information'),
// This #states rule says that the "undergraduate" fieldset should only
// be shown if the "student_type" form element is set to "Undergraduate".
'#states' => array(
'visible' => array(
':input[name="student_type"]' => array('value' => 'undergraduate'),
),
),
);
$form['undergraduate']['how_many_years'] = array(
'#type' => 'select',
'#title' => t('How many years have you completed?'),
// The options here are integers, but since all the action here happens
// using the DOM on the client, we will have to use strings to work with
// them.
'#options' => array(
1 => t('One'),
2 => t('Two'),
3 => t('Three'),
4 => t('Four'),
5 => t('Lots'),
),
);
$form['undergraduate']['comment'] = array(
'#type' => 'item',
'#description' => t("Wow, that's a long time."),
'#states' => array(
'visible' => array(
// Note that '5' must be used here instead of the integer 5.
// The information is coming from the DOM as a string.
':input[name="how_many_years"]' => array('value' => '5'),
),
),
);
$form['undergraduate']['school_name'] = array(
'#type' => 'textfield',
'#title' => t('Your college or university:'),
);
$form['undergraduate']['school_country'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(array(t('UK'), t('Other'))),
'#title' => t('In what country is your college or university located?'),
);
$form['undergraduate']['country_writein'] = array(
'#type' => 'textfield',
'#size' => 20,
'#title' => t('Please enter the name of the country where your college or university is located.'),
// Only show this field if school_country is set to 'Other'.
'#states' => array(
// Action to take: Make visible.
'visible' => array(
':input[name="school_country"]' => array('value' => t('Other')),
),
),
);
$form['undergraduate']['thanks'] = array(
'#type' => 'item',
'#description' => t('Thanks for providing both your school and your country.'),
'#states' => array(
// Here visibility requires that two separate conditions be true.
'visible' => array(
':input[name="school_country"]' => array('value' => t('Other')),
':input[name="country_writein"]' => array('filled' => TRUE),
),
),
);
$form['undergraduate']['go_away'] = array(
'#type' => 'submit',
'#value' => t('Done with form'),
'#states' => array(
// Here visibility requires that two separate conditions be true.
'visible' => array(
':input[name="school_country"]' => array('value' => t('Other')),
':input[name="country_writein"]' => array('filled' => TRUE),
),
),
);
// Graduate student information.
$form['graduate'] = array(
'#type' => 'fieldset',
'#title' => t('Graduate School Information'),
// This #states rule says that the "graduate" fieldset should only
// be shown if the "student_type" form element is set to "Graduate".
'#states' => array(
'visible' => array(
':input[name="student_type"]' => array('value' => 'graduate'),
),
),
);
$form['graduate']['more_info'] = array(
'#type' => 'textarea',
'#title' => t('Please describe your graduate studies'),
);
$form['graduate']['info_provide'] = array(
'#type' => 'checkbox',
'#title' => t('Check here if you have provided information above'),
'#disabled' => TRUE,
'#states' => array(
// Mark this checkbox checked if the "more_info" textarea has something
// in it, if it's 'filled'.
'checked' => array(
':input[name="more_info"]' => array('filled' => TRUE),
),
),
);
$form['average'] = array(
'#type' => 'textfield',
'#title' => t('Enter your average'),
// To trigger a state when the same controlling element can have more than
// one possible value, put all values in a higher-level array.
'#states' => array(
'visible' => array(
':input[name="student_type"]' => array(
array('value' => 'high_school'),
array('value' => 'undergraduate'),
),
),
),
);
$form['expand_more_info'] = array(
'#type' => 'checkbox',
'#title' => t('Check here if you want to add more information.'),
);
$form['more_info'] = array(
'#type' => 'fieldset',
'#title' => t('Additional Information'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
// Expand the expand_more_info fieldset if the box is checked.
'#states' => array(
'expanded' => array(
':input[name="expand_more_info"]' => array('checked' => TRUE),
),
),
);
$form['more_info']['feedback'] = array(
'#type' => 'textarea',
'#title' => t('What do you have to say?'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit your information'),
);
return $form;
}
/**
* Submit handler for form_example_states_form().
*/
function form_example_states_form_submit($form, &$form_state) {
drupal_set_message(t('Submitting values: @values', array('@values' => var_export($form_state['values'], TRUE))));
}

View File

@@ -0,0 +1,934 @@
<?php
/**
* @file
* This is the Form API Tutorial from the handbook.
*
* It goes through several form examples of increasing complexity to demonstrate
* Drupal 7 Form API.
*
* Links are provided inline for the related handbook pages.
*
* @see http://drupal.org/node/262422
*/
/**
* Main Form tutorial page.
*
* @see form_example_tutorial_1()
* @see form_example_tutorial_2()
* @see form_example_tutorial_3()
* @see form_example_tutorial_4()
* @see form_example_tutorial_5()
* @see form_example_tutorial_6()
* @see form_example_tutorial_7()
* @see form_example_tutorial_8()
* @see form_example_tutorial_9()
* @see form_example_tutorial_10()
*
* @ingroup form_example
*/
function form_example_tutorial() {
return t('This is a set of form tutorials tied to the <a href="http://drupal.org/node/262422">Drupal handbook</a>.');
}
/**
* Tutorial Example 1.
*
* This first form function is from the
* @link http://drupal.org/node/717722 Form Tutorial handbook page @endlink
*
* It just creates a very basic form with a textfield.
*
* This function is called the "form constructor function". It builds the form.
* It takes a two arguments, $form and $form_state, but if drupal_get_form()
* sends additional arguments, they will be provided after $form_state.
*
* @ingroup form_example
*/
function form_example_tutorial_1($form, &$form_state) {
$form['description'] = array(
'#type' => 'item',
'#title' => t('A form with nothing but a textfield'),
);
// This is the first form element. It's a textfield with a label, "Name"
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
);
return $form;
}
/**
* This is Example 2, a basic form with a submit button.
*
* @see http://drupal.org/node/717726
* @ingroup form_example
*/
function form_example_tutorial_2($form, &$form_state) {
$form['description'] = array(
'#type' => 'item',
'#title' => t('A simple form with a submit button'),
);
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
);
// Adds a simple submit button that refreshes the form and clears its
// contents. This is the default behavior for forms.
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
/**
* Example 3: A basic form with fieldsets.
*
* We establish a fieldset element and then place two text fields within
* it, one for a first name and one for a last name. This helps us group
* related content.
*
* Study the code below and you'll notice that we renamed the array of the first
* and last name fields by placing them under the $form['name']
* array. This tells Form API these fields belong to the $form['name'] fieldset.
*
* @ingroup form_example
*/
function form_example_tutorial_3($form, &$form_state) {
$form['description'] = array(
'#type' => 'item',
'#title' => t('A form with a fieldset'),
);
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
);
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
/**
* Example 4: Basic form with required fields.
*
* @ingroup form_example
*/
function form_example_tutorial_4($form, &$form_state) {
$form['description'] = array(
'#type' => 'item',
'#title' => t('A form with required fields'),
);
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
// Make the fieldset collapsible.
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
// Make these fields required.
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#required' => TRUE,
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#required' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
/**
* Example 5: Basic form with additional element attributes.
*
* This demonstrates additional attributes of text form fields.
*
* See the
* @link http://api.drupal.org/api/file/developer/topics/forms_api.html complete form reference @endlink
*
* @ingroup form_example
*/
function form_example_tutorial_5($form, &$form_state) {
$form['description'] = array(
'#type' => 'item',
'#title' => t('A form with additional attributes'),
'#description' => t('This one adds #default_value and #description'),
);
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#required' => TRUE,
'#default_value' => "First name",
'#description' => "Please enter your first name.",
'#size' => 20,
'#maxlength' => 20,
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#required' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
/**
* Example 6: A basic form with a validate handler.
*
* From http://drupal.org/node/717736
* @see form_example_tutorial_6_validate()
*
* @ingroup form_example
*/
function form_example_tutorial_6($form, &$form_state) {
$form['description'] = array(
'#type' => 'item',
'#title' => t('A form with a validation handler'),
);
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#required' => TRUE,
'#default_value' => "First name",
'#description' => "Please enter your first name.",
'#size' => 20,
'#maxlength' => 20,
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#required' => TRUE,
);
// New form field added to permit entry of year of birth.
// The data entered into this field will be validated with
// the default validation function.
$form['year_of_birth'] = array(
'#type' => 'textfield',
'#title' => "Year of birth",
'#description' => 'Format is "YYYY"',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
/**
* Validation handler for Tutorial 6.
*
* Now we add a handler/function to validate the data entered into the
* "year of birth" field to make sure it's between the values of 1900
* and 2000. If not, it displays an error. The value report is
* $form_state['values'] (see http://drupal.org/node/144132#form-state).
*
* Notice the name of the function. It is simply the name of the form
* followed by '_validate'. This is always the name of the default validation
* function. An alternate list of validation functions could have been provided
* in $form['#validate'].
*
* @see form_example_tutorial_6()
*
* @ingroup form_example
*/
function form_example_tutorial_6_validate($form, &$form_state) {
$year_of_birth = $form_state['values']['year_of_birth'];
if ($year_of_birth && ($year_of_birth < 1900 || $year_of_birth > 2000)) {
form_set_error('year_of_birth', t('Enter a year between 1900 and 2000.'));
}
}
/**
* Example 7: With a submit handler.
*
* From the handbook page:
* http://drupal.org/node/717740
*
* @see form_example_tutorial_7_validate()
* @see form_example_tutorial_7_submit()
*
* @ingroup form_example
*/
function form_example_tutorial_7($form, &$form_state) {
$form['description'] = array(
'#type' => 'item',
'#title' => t('A form with a submit handler'),
);
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#required' => TRUE,
'#default_value' => "First name",
'#description' => "Please enter your first name.",
'#size' => 20,
'#maxlength' => 20,
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#required' => TRUE,
);
$form['year_of_birth'] = array(
'#type' => 'textfield',
'#title' => "Year of birth",
'#description' => 'Format is "YYYY"',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
/**
* Validation function for form_example_tutorial_7().
*
* @ingroup form_example
*/
function form_example_tutorial_7_validate($form, &$form_state) {
$year_of_birth = $form_state['values']['year_of_birth'];
if ($year_of_birth && ($year_of_birth < 1900 || $year_of_birth > 2000)) {
form_set_error('year_of_birth', t('Enter a year between 1900 and 2000.'));
}
}
/**
* Submit function for form_example_tutorial_7().
*
* Adds a submit handler/function to our form to send a successful
* completion message to the screen.
*
* @ingroup form_example
*/
function form_example_tutorial_7_submit($form, &$form_state) {
drupal_set_message(t('The form has been submitted. name="@first @last", year of birth=@year_of_birth',
array(
'@first' => $form_state['values']['first'],
'@last' => $form_state['values']['last'],
'@year_of_birth' => $form_state['values']['year_of_birth'],
)
));
}
/**
* Example 8: A simple multistep form with a Next and a Back button.
*
* Handbook page: http://drupal.org/node/717750.
*
* For more extensive multistep forms, see
* @link form_example_wizard.inc form_example_wizard.inc @endlink
*
*
* Adds logic to our form builder to give it two pages.
* The @link ajax_example_wizard AJAX Example's Wizard Example @endlink
* gives an AJAX version of this same idea.
*
* @see form_example_tutorial_8_page_two()
* @see form_example_tutorial_8_page_two_back()
* @see form_example_tutorial_8_page_two_submit()
* @see form_example_tutorial_8_next_submit()
* @see form_example_tutorial.inc
*
* @ingroup form_example
*/
function form_example_tutorial_8($form, &$form_state) {
// Display page 2 if $form_state['page_num'] == 2
if (!empty($form_state['page_num']) && $form_state['page_num'] == 2) {
return form_example_tutorial_8_page_two($form, $form_state);
}
// Otherwise we build page 1.
$form_state['page_num'] = 1;
$form['description'] = array(
'#type' => 'item',
'#title' => t('A basic multistep form (page 1)'),
);
$form['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#description' => "Please enter your first name.",
'#size' => 20,
'#maxlength' => 20,
'#required' => TRUE,
'#default_value' => !empty($form_state['values']['first']) ? $form_state['values']['first'] : '',
);
$form['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#default_value' => !empty($form_state['values']['last']) ? $form_state['values']['last'] : '',
);
$form['year_of_birth'] = array(
'#type' => 'textfield',
'#title' => "Year of birth",
'#description' => 'Format is "YYYY"',
'#default_value' => !empty($form_state['values']['year_of_birth']) ? $form_state['values']['year_of_birth'] : '',
);
$form['next'] = array(
'#type' => 'submit',
'#value' => 'Next >>',
'#submit' => array('form_example_tutorial_8_next_submit'),
'#validate' => array('form_example_tutorial_8_next_validate'),
);
return $form;
}
/**
* Returns the form for the second page of form_example_tutorial_8().
*
* @ingroup form_example
*/
function form_example_tutorial_8_page_two($form, &$form_state) {
$form['description'] = array(
'#type' => 'item',
'#title' => t('A basic multistep form (page 2)'),
);
$form['color'] = array(
'#type' => 'textfield',
'#title' => t('Favorite color'),
'#required' => TRUE,
'#default_value' => !empty($form_state['values']['color']) ? $form_state['values']['color'] : '',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#submit' => array('form_example_tutorial_8_page_two_submit'),
);
$form['back'] = array(
'#type' => 'submit',
'#value' => t('<< Back'),
'#submit' => array('form_example_tutorial_8_page_two_back'),
// We won't bother validating the required 'color' field, since they
// have to come back to this page to submit anyway.
'#limit_validation_errors' => array(),
);
return $form;
}
/**
* Validate handler for the next button on first page.
*
* @ingroup form_example
*/
function form_example_tutorial_8_next_validate($form, &$form_state) {
$year_of_birth = $form_state['values']['year_of_birth'];
if ($year_of_birth && ($year_of_birth < 1900 || $year_of_birth > 2000)) {
form_set_error('year_of_birth', t('Enter a year between 1900 and 2000.'));
}
}
/**
* Submit handler for form_example_tutorial_8() next button.
*
* Capture the values from page one and store them away so they can be used
* at final submit time.
*
* @ingroup form_example
*/
function form_example_tutorial_8_next_submit($form, &$form_state) {
// Values are saved for each page.
// to carry forward to subsequent pages in the form.
// and we tell FAPI to rebuild the form.
$form_state['page_values'][1] = $form_state['values'];
if (!empty($form_state['page_values'][2])) {
$form_state['values'] = $form_state['page_values'][2];
}
// When form rebuilds, it will look at this to figure which page to build.
$form_state['page_num'] = 2;
$form_state['rebuild'] = TRUE;
}
/**
* Back button handler submit handler.
*
* Since #limit_validation_errors = array() is set, values from page 2
* will be discarded. We load the page 1 values instead.
*
* @ingroup form_example
*/
function form_example_tutorial_8_page_two_back($form, &$form_state) {
$form_state['values'] = $form_state['page_values'][1];
$form_state['page_num'] = 1;
$form_state['rebuild'] = TRUE;
}
/**
* The page 2 submit handler.
*
* This is the final submit handler. Gather all the data together and output
* it in a drupal_set_message().
*
* @ingroup form_example
*/
function form_example_tutorial_8_page_two_submit($form, &$form_state) {
// Normally, some code would go here to alter the database with the data
// collected from the form. Instead sets a message with drupal_set_message()
// to validate that the code worked.
$page_one_values = $form_state['page_values'][1];
drupal_set_message(t('The form has been submitted. name="@first @last", year of birth=@year_of_birth',
array(
'@first' => $page_one_values['first'],
'@last' => $page_one_values['last'],
'@year_of_birth' => $page_one_values['year_of_birth'],
)
));
if (!empty($page_one_values['first2'])) {
drupal_set_message(t('Second name: name="@first @last", year of birth=@year_of_birth',
array(
'@first' => $page_one_values['first2'],
'@last' => $page_one_values['last2'],
'@year_of_birth' => $page_one_values['year_of_birth2'],
)
));
}
drupal_set_message(t('And the favorite color is @color', array('@color' => $form_state['values']['color'])));
// If we wanted to redirect on submission, set $form_state['redirect']. For
// simple redirects, the value can be a string of the path to redirect to. For
// example, to redirect to /node, one would specify the following:
//
// $form_state['redirect'] = 'node';
//
// For more complex redirects, this value can be set to an array of options to
// pass to drupal_goto(). For example, to redirect to /foo?bar=1#baz, one
// would specify the following:
//
// @code
// $form_state['redirect'] = array(
// 'foo',
// array(
// 'query' => array('bar' => 1),
// 'fragment' => 'baz',
// ),
// );
// @endcode
//
// The first element in the array is the path to redirect to, and the second
// element in the array is the array of options. For more information on the
// available options, see http://api.drupal.org/url.
}
/**
* Example 9: A form with a dynamically added new fields.
*
* This example adds default values so that when the form is rebuilt,
* the form will by default have the previously-entered values.
*
* From handbook page http://drupal.org/node/717746.
*
* @see form_example_tutorial_9_add_name()
* @see form_example_tutorial_9_remove_name()
* @see form_example_tutorial_9_submit()
* @see form_example_tutorial_9_validate()
*
* @ingroup form_example
*/
function form_example_tutorial_9($form, &$form_state) {
// We will have many fields with the same name, so we need to be able to
// access the form hierarchically.
$form['#tree'] = TRUE;
$form['description'] = array(
'#type' => 'item',
'#title' => t('A form with dynamically added new fields'),
);
if (empty($form_state['num_names'])) {
$form_state['num_names'] = 1;
}
// Build the number of name fieldsets indicated by $form_state['num_names']
for ($i = 1; $i <= $form_state['num_names']; $i++) {
$form['name'][$i] = array(
'#type' => 'fieldset',
'#title' => t('Name #@num', array('@num' => $i)),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['name'][$i]['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#description' => t("Enter first name."),
'#size' => 20,
'#maxlength' => 20,
'#required' => TRUE,
);
$form['name'][$i]['last'] = array(
'#type' => 'textfield',
'#title' => t('Enter Last name'),
'#required' => TRUE,
);
$form['name'][$i]['year_of_birth'] = array(
'#type' => 'textfield',
'#title' => t("Year of birth"),
'#description' => t('Format is "YYYY"'),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
// Adds "Add another name" button.
$form['add_name'] = array(
'#type' => 'submit',
'#value' => t('Add another name'),
'#submit' => array('form_example_tutorial_9_add_name'),
);
// If we have more than one name, this button allows removal of the
// last name.
if ($form_state['num_names'] > 1) {
$form['remove_name'] = array(
'#type' => 'submit',
'#value' => t('Remove latest name'),
'#submit' => array('form_example_tutorial_9_remove_name'),
// Since we are removing a name, don't validate until later.
'#limit_validation_errors' => array(),
);
}
return $form;
}
/**
* Submit handler for "Add another name" button on form_example_tutorial_9().
*
* $form_state['num_names'] tells the form builder function how many name
* fieldsets to build, so here we increment it.
*
* All elements of $form_state are persisted, so there's no need to use a
* particular key, like the old $form_state['storage']. We can just use
* $form_state['num_names'].
*
* @ingroup form_example
*/
function form_example_tutorial_9_add_name($form, &$form_state) {
// Everything in $form_state is persistent, so we'll just use
// $form_state['add_name']
$form_state['num_names']++;
// Setting $form_state['rebuild'] = TRUE causes the form to be rebuilt again.
$form_state['rebuild'] = TRUE;
}
/**
* Submit handler for "Remove name" button on form_example_tutorial_9().
*
* @ingroup form_example
*/
function form_example_tutorial_9_remove_name($form, &$form_state) {
if ($form_state['num_names'] > 1) {
$form_state['num_names']--;
}
// Setting $form_state['rebuild'] = TRUE causes the form to be rebuilt again.
$form_state['rebuild'] = TRUE;
}
/**
* Validate function for form_example_tutorial_9().
*
* Adds logic to validate the form to check the validity of the new fields,
* if they exist.
*
* @ingroup form_example
*/
function form_example_tutorial_9_validate($form, &$form_state) {
for ($i = 1; $i <= $form_state['num_names']; $i++) {
$year_of_birth = $form_state['values']['name'][$i]['year_of_birth'];
if ($year_of_birth && ($year_of_birth < 1900 || $year_of_birth > 2000)) {
form_set_error("name][$i][year_of_birth", t('Enter a year between 1900 and 2000.'));
}
}
}
/**
* Submit function for form_example_tutorial_9().
*
* @ingroup form_example
*/
function form_example_tutorial_9_submit($form, &$form_state) {
$output = t("Form 9 has been submitted.");
for ($i = 1; $i <= $form_state['num_names']; $i++) {
$output .= t("@num: @first @last (@date)...",
array(
'@num' => $i,
'@first' => $form_state['values']['name'][$i]['first'],
'@last' => $form_state['values']['name'][$i]['last'],
'@date' => $form_state['values']['name'][$i]['year_of_birth'],
)
) . ' ';
}
drupal_set_message($output);
}
/**
* Example 10: A form with a file upload field.
*
* This example allows the user to upload a file to Drupal which is stored
* physically and with a reference in the database.
*
* @see form_example_tutorial_10_submit()
* @see form_example_tutorial_10_validate()
*
* @ingroup form_example
*/
function form_example_tutorial_10($form_state) {
// If you are familiar with how browsers handle files, you know that
// enctype="multipart/form-data" is required. Drupal takes care of that, so
// you don't need to include it yourself.
$form['file'] = array(
'#type' => 'file',
'#title' => t('Image'),
'#description' => t('Upload a file, allowed extensions: jpg, jpeg, png, gif'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Validate handler for form_example_tutorial_10().
*
* @ingroup form_example
*/
function form_example_tutorial_10_validate($form, &$form_state) {
$file = file_save_upload('file', array(
// Validates file is really an image.
'file_validate_is_image' => array(),
// Validate extensions.
'file_validate_extensions' => array('png gif jpg jpeg'),
));
// If the file passed validation:
if ($file) {
// Move the file into the Drupal file system.
if ($file = file_move($file, 'public://')) {
// Save the file for use in the submit handler.
$form_state['storage']['file'] = $file;
}
else {
form_set_error('file', t("Failed to write the uploaded file to the site's file folder."));
}
}
else {
form_set_error('file', t('No file was uploaded.'));
}
}
/**
* Submit handler for form_example_tutorial_10().
*
* @ingroup form_example
*/
function form_example_tutorial_10_submit($form, &$form_state) {
$file = $form_state['storage']['file'];
// We are done with the file, remove it from storage.
unset($form_state['storage']['file']);
// Make the storage of the file permanent.
$file->status = FILE_STATUS_PERMANENT;
// Save file status.
file_save($file);
// Set a response to the user.
drupal_set_message(t('The form has been submitted and the image has been saved, filename: @filename.', array('@filename' => $file->filename)));
}
/**
* Example 11: adding a confirmation form.
*
* This example generates a simple form that, when submitted, directs
* the user to a confirmation form generated using the confirm_form function.
* It asks the user to verify that the name they input was correct
*
* @see form_example_tutorial_11_submit()
*
* @ingroup form_example
*/
function form_example_tutorial_11($form, &$form_state) {
// This form is identical to the one in example 2 except for one thing: We are
// adding an #action tag to direct the form submission to a confirmation page.
$form['description'] = array(
'#type' => 'item',
'#title' => t('A set of two forms that demonstrate the confirm_form function. This form has an explicit action to direct the form to a confirmation page'),
);
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
'#required' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
/**
* Submit function for form_example_tutorial_11().
*
* Adds a submit handler/function to our form to redirect
* the user to a confirmation page.
*
* @ingroup form_example
*/
function form_example_tutorial_11_submit($form, &$form_state) {
// Simple submit function that changes the redirect of the form based on the
// value of the name field.
$name = $form_state['values']['name'];
$form_state['redirect'] = 'examples/form_example/tutorial/11/confirm/' . urlencode($name);
}
/**
* Example 11: A form generated with confirm_form().
*
* This function generates the confirmation form using the confirm_form()
* function. If confirmed, it sets a drupal message to demonstrate it's success.
*
* @param string $name
* The urlencoded name entered by the user.
*
* @see form_example_tutorial_11_confirm_name_submit()
*
* @ingroup form_example
*/
function form_example_tutorial_11_confirm_name($form, $form_state, $name) {
// confirm_form() returns a complete form array for confirming an action.
// It has 7 arguments: $form, $question, $path, $description, $yes, $no, and
// $name.
// - $form: Additional elements to add to the form that will be available in
// the submit handler.
// - $question: What is the user confirming? This will be the title of the
// page.
// - $path: Where should the page go if the user hits cancel?
// - $description = NULL: Additional text to display.
// - $yes = NULL: Anchor text for the confirmation button. Defaults to
// t('Confirm').
// - $no = NULL: Anchor text for the cancel link. Defaults to t('Cancel').
// - $name = 'confirm': The internal name used to refer to the confirmation
// item.
// First we make a textfield for our user's name. confirm_form() allows us to
// Add form elements to the confirmation form, so we'll take advangage of
// that.
$user_name_text_field = array(
'name' => array(
'#type' => 'textfield',
// We don't want the user to be able to edit their name here.
'#disabled' => TRUE,
'#title' => t('Your name:'),
'#value' => urldecode($name),
),
);
// The question to ask the user.
$confirmation_question = t('Is this really your name?');
// If the user clicks 'no,' they're sent to this path.
$cancel_path = 'examples/form_example/tutorial/11';
// Some helpful descriptive text.
$description = t('Please verify whether or not you have input your name correctly. If you verify you will be sent back to the form and a message will be set. Otherwise you will be sent to the same page but with no message.');
// These are the text for our yes and no buttons.
$yes_button = t('This is my name');
$no_button = t('Nope, not my name');
// The name Form API will use to refer to our confirmation form.
$confirm_name = 'confirm_example';
// Finally, call confirm_form() with our information, and then return the form
// array it gives us.
return confirm_form(
$user_name_text_field,
$confirmation_question,
$cancel_path,
$description,
$yes_button,
$no_button,
$confirm_name
);
}
/**
* Submit function for form_example_tutorial_11_confirm_form().
*
* Adds a submit handler/function to the confirmation form
* if this point is reached the submission has been confirmed
* so we will set a message to demonstrate the success.
*
* @ingroup form_example
*/
function form_example_tutorial_11_confirm_name_submit($form, &$form_state) {
drupal_set_message(t("Confirmation form submission recieved. According to your submission your name is '@name'", array("@name" => $form_state['values']['name'])));
$form_state['redirect'] = 'examples/form_example/tutorial/11';
}

View File

@@ -0,0 +1,325 @@
<?php
/**
* @file
* Extensible wizard form example.
*/
/**
* Extensible wizard form example.
*
* This is an example of a multistep form using a wizard style. It will include
* the 'Previous' and 'Next' buttons when required, and a 'Finish' button at the
* last stage of the form submission.
*
* This example is an extensible skeleton that can include (even
* programmatically) more steps. The demonstration form includes three steps,
* each step having its own validation functions.
*
* How to extend this example:
* - Steps are defined in the _form_example_steps() function. Include or alter
* the steps as you require.
* - For each step, implement the corresponding 'form' function (see
* 'form_example_wizard_personal_info' for the first step in this example.)
* Each step is a regular form, and the wizard collects all the values of the
* included forms.
* - Optionally, you may include custom validation functions using the regular
* validation hook (formname_validate). The wizard uses these validation
* functions for each step.
* - The most important customization step is to change the submit handler and
* do whatever you want with the collected information. In this case, the
* example just shows the collected values in the various steps.
* @ingroup form_example
*/
/**
* Returns the list of steps and their associated forms.
*
* This has been separated to clarify and easy the understanding of this
* example. You should edit this function to include the steps your
* wizard/multistep form requires.
*
* @return array
* List of steps and their forms.
*
* @ingroup form_example
*/
function _form_example_steps() {
return array(
1 => array(
'form' => 'form_example_wizard_personal_info',
),
2 => array(
'form' => 'form_example_wizard_location_info',
),
3 => array(
'form' => 'form_example_wizard_other_info',
),
);
}
/**
* The primary formbuilder function for the wizard form.
*
* This is the form that you should call with drupal_get_form() from your code,
* and it will include the rest of the step forms defined. You are not required
* to change this function, as this will handle all the step actions for you.
*
* This form has two defined submit handlers to process the different steps:
* - Previous: handles the way to get back one step in the wizard.
* - Next: handles each step form submission,
*
* The third handler, the finish button handler, is the default form_submit
* handler used to process the information.
*
* You are not required to change the next or previous handlers, but you must
* change the form_example_wizard_submit handler to perform the operations you
* need on the collected information.
*
* @ingroup form_example
*/
function form_example_wizard($form, &$form_state) {
// Initialize a description of the steps for the wizard.
if (empty($form_state['step'])) {
$form_state['step'] = 1;
// This array contains the function to be called at each step to get the
// relevant form elements. It will also store state information for each
// step.
$form_state['step_information'] = _form_example_steps();
}
$step = &$form_state['step'];
drupal_set_title(t('Extensible Wizard: Step @step', array('@step' => $step)));
// Call the function named in $form_state['step_information'] to get the
// form elements to display for this step.
$form = $form_state['step_information'][$step]['form']($form, $form_state);
// Show the 'previous' button if appropriate. Note that #submit is set to
// a special submit handler, and that we use #limit_validation_errors to
// skip all complaints about validation when using the back button. The
// values entered will be discarded, but they will not be validated, which
// would be annoying in a "back" button.
if ($step > 1) {
$form['prev'] = array(
'#type' => 'submit',
'#value' => t('Previous'),
'#name' => 'prev',
'#submit' => array('form_example_wizard_previous_submit'),
'#limit_validation_errors' => array(),
);
}
// Show the Next button only if there are more steps defined.
if ($step < count($form_state['step_information'])) {
// The Next button should be included on every step.
$form['next'] = array(
'#type' => 'submit',
'#value' => t('Next'),
'#name' => 'next',
'#submit' => array('form_example_wizard_next_submit'),
);
}
else {
// Just in case there are no more steps, we use the default submit handler
// of the form wizard. Call this button Finish, Submit, or whatever you
// want to show. When this button is clicked, the
// form_example_wizard_submit handler will be called.
$form['finish'] = array(
'#type' => 'submit',
'#value' => t('Finish'),
);
}
// Include each validation function defined for the different steps.
if (function_exists($form_state['step_information'][$step]['form'] . '_validate')) {
$form['next']['#validate'] = array($form_state['step_information'][$step]['form'] . '_validate');
}
return $form;
}
/**
* Submit handler for the "previous" button.
*
* This function:
* - Stores away $form_state['values']
* - Decrements the step counter
* - Replaces $form_state['values'] with the values from the previous state.
* - Forces form rebuild.
*
* You are not required to change this function.
*
* @ingroup form_example
*/
function form_example_wizard_previous_submit($form, &$form_state) {
$current_step = &$form_state['step'];
$form_state['step_information'][$current_step]['stored_values'] = $form_state['input'];
if ($current_step > 1) {
$current_step--;
$form_state['values'] = $form_state['step_information'][$current_step]['stored_values'];
}
$form_state['rebuild'] = TRUE;
}
/**
* Submit handler for the 'next' button.
*
* This function:
* - Saves away $form_state['values']
* - Increments the step count.
* - Replace $form_state['values'] from the last time we were at this page
* or with array() if we haven't been here before.
* - Force form rebuild.
*
* You are not required to change this function.
*
* @ingroup form_example
*/
function form_example_wizard_next_submit($form, &$form_state) {
$current_step = &$form_state['step'];
$form_state['step_information'][$current_step]['stored_values'] = $form_state['values'];
if ($current_step < count($form_state['step_information'])) {
$current_step++;
if (!empty($form_state['step_information'][$current_step]['stored_values'])) {
$form_state['values'] = $form_state['step_information'][$current_step]['stored_values'];
}
else {
$form_state['values'] = array();
}
// Force rebuild with next step.
$form_state['rebuild'] = TRUE;
return;
}
}
/**
* The previous code was a 'skeleton' of a multistep wizard form. You are not
* required to change a line on the previous code (apart from defining your own
* steps in the _form_example_steps() function.
*
* All the code included from here is the content of the wizard, the steps of
* the form.
*
* First, let's show the defined steps for the wizard example.
* @ingroup form_example
*/
/**
* Returns form elements for the 'personal info' page of the wizard.
*
* This is the first step of the wizard, asking for two textfields: first name
* and last name.
*
* @ingroup form_example
*/
function form_example_wizard_personal_info($form, &$form_state) {
$form = array();
$form['first_name'] = array(
'#type' => 'textfield',
'#title' => t('First Name'),
'#default_value' => !empty($form_state['values']['first_name']) ? $form_state['values']['first_name'] : '',
);
$form['last_name'] = array(
'#type' => 'textfield',
'#title' => t('Last Name'),
'#default_value' => !empty($form_state['values']['last_name']) ? $form_state['values']['last_name'] : '',
);
return $form;
}
/**
* Returns form elements for the 'location info' page of the wizard.
*
* This is the second step of the wizard. This step asks for a textfield value:
* a City. This step also includes a validation declared later.
*
* @ingroup form_example
*/
function form_example_wizard_location_info($form, &$form_state) {
$form = array();
$form['city'] = array(
'#type' => 'textfield',
'#title' => t('City'),
'#description' => t('Hint: Do not enter "San Francisco", and do not leave this out.'),
'#required' => TRUE,
'#default_value' => !empty($form_state['values']['city']) ? $form_state['values']['city'] : '',
);
return $form;
}
/**
* Custom validation form for the 'location info' page of the wizard.
*
* This is the validation function for the second step of the wizard.
* The city cannot be empty or be "San Francisco".
*
* @ingroup form_example
*/
function form_example_wizard_location_info_validate($form, &$form_state) {
if ($form_state['values']['city'] == 'San Francisco') {
form_set_error('city', t('You were warned not to enter "San Francisco"'));
}
}
/**
* Returns form elements for the 'other info' page of the wizard.
*
* This is the third and last step of the example wizard.
*
* @ingroup form_example
*/
function form_example_wizard_other_info($form, &$form_state) {
$form = array();
$form['aunts_name'] = array(
'#type' => 'textfield',
'#title' => t("Your first cousin's aunt's Social Security number"),
'#default_value' => !empty($form_state['values']['aunts_name']) ? $form_state['values']['aunts_name'] : '',
);
return $form;
}
/**
* Wizard form submit handler.
*
* This function:
* - Saves away $form_state['values']
* - Process all the form values.
*
* And now comes the magic of the wizard, the function that should handle all
* the inputs from the user on each different step.
*
* This demonstration handler just do a drupal_set_message() with the
* information collected on each different step of the wizard.
*
* @ingroup form_example
*/
function form_example_wizard_submit($form, &$form_state) {
$current_step = &$form_state['step'];
$form_state['step_information'][$current_step]['stored_values'] = $form_state['values'];
// In this case we've completed the final page of the wizard, so process the
// submitted information.
drupal_set_message(t('This information was collected by this wizard:'));
foreach ($form_state['step_information'] as $index => $value) {
// Remove FAPI fields included in the values (form_token, form_id and
// form_build_id. This is not required, you may access the values using
// $value['stored_values'] but I'm removing them to make a more clear
// representation of the collected information as the complete array will
// be passed through drupal_set_message().
unset($value['stored_values']['form_id']);
unset($value['stored_values']['form_build_id']);
unset($value['stored_values']['form_token']);
// Now show all the values.
drupal_set_message(t('Step @num collected the following values: <pre>@result</pre>',
array(
'@num' => $index,
'@result' => print_r($value['stored_values'], TRUE),
)
));
}
}