Newer
Older
/**
* @defgroup form Form generation
* @{

Dries Buytaert
committed
* Functions to enable the processing and display of HTML forms.

Dries Buytaert
committed
* Drupal uses these functions to achieve consistency in its form processing and
* presentation, while simplifying code and reducing the amount of HTML that
* must be explicitly generated by modules.
*
* The drupal_get_form() function handles retrieving, processing, and
* displaying a rendered HTML form for modules automatically. For example:
*
* // display the user registration form
* $output = drupal_get_form('user_register');
*
* Forms can also be built and submitted programmatically without any user input
* using the drupal_execute() function. Pass in the id of the form, the values to
* submit to the form, and any parameters needed by the form's builder function.
* For example:

Dries Buytaert
committed
*
* // register a new user
* $values['name'] = 'robo-user';
* $values['mail'] = 'robouser@example.com';
* $values['pass'] = 'password';
* drupal_execute('user_register', $values);

Dries Buytaert
committed
*
* // Create a new node
* $node = array('type' => 'story');
* $values['title'] = 'My node';
* $values['body'] = 'This is the body text!';
* $values['name'] = 'robo-user';
* drupal_execute('story_node_form', $values, $node);
*
* Calling form_get_errors() after execution will return an array of any
* validation errors encountered.

Dries Buytaert
committed
*
* For information on the format of the structured arrays used to define forms,
* and more detailed explanations of the Form API workflow, see the reference at
* http://api.drupal.org/api/HEAD/file/developer/topics/forms_api_reference.html

Dries Buytaert
committed
* and the quickstart guide at
* http://api.drupal.org/api/HEAD/file/developer/topics/forms_api.html

Dries Buytaert
committed
* Retrieves a form from a builder function, passes it on for
* processing, and renders the form or redirects to its destination
* as appropriate. In multi-step form scenerios, it handles properly
* processing the values using the previous step's form definition,
* then rendering the requested step for display.

Dries Buytaert
committed
* The unique string identifying the desired form. If a function
* with that name exists, it is called to build the form array.
* Modules that need to generate the same form (or very similar forms)
* using different $form_ids can implement hook_forms(), which maps
* different $form_id values to the proper form building function. Examples
* may be found in node_forms(), search_forms(), and user_forms().
* @param ...
* Any additional arguments needed by the form building function.
* @return
* The rendered form.
*/
function drupal_get_form($form_id) {
// In multi-step form scenerios, the incoming $_POST values are not
// necessarily intended for the current form. We need to build
// a copy of the previously built form for validation and processing,
// then go on to the one that was requested if everything works.
$form_build_id = md5(mt_rand());
if (isset($_POST['form_build_id']) && isset($_SESSION['form'][$_POST['form_build_id']]) && $_POST['form_id'] == $form_id) {
// There's a previously stored multi-step form. We should handle
// IT first.
$stored = TRUE;
$args = $_SESSION['form'][$_POST['form_build_id']];
$form = call_user_func_array('drupal_retrieve_form', $args);
}
else {
// We're coming in fresh; build things as they would be. If the
// form's #multistep flag is set, store the build parameters so
// the same form can be reconstituted for validation.
$args = func_get_args();
$form = call_user_func_array('drupal_retrieve_form', $args);
if (isset($form['#multistep']) && $form['#multistep']) {
$_SESSION['form'][$form_build_id] = $args;
$form['#build_id'] = $form_build_id;
}
$stored = FALSE;
}
// Process the form, submit it, and store any errors if necessary.
drupal_process_form($args[0], $form);
if ($stored && !form_get_errors()) {
// If it's a stored form and there were no errors, we processed the
// stored form successfully. Now we need to build the form that was
// actually requested. We always pass in the current $_POST values
// to the builder function, as values from one stage of a multistep
// form can determine how subsequent steps are displayed.
$args = func_get_args();
$args[] = $_POST;
$form = call_user_func_array('drupal_retrieve_form', $args);
unset($_SESSION['form'][$_POST['form_build_id']]);
if (isset($form['#multistep']) && $form['#multistep']) {
$_SESSION['form'][$form_build_id] = $args;
$form['#build_id'] = $form_build_id;
}
drupal_prepare_form($args[0], $form);
}
return drupal_render_form($args[0], $form);

Dries Buytaert
committed
}
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/**
* Retrieves a form using a form_id, populates it with $form_values,
* processes it, and returns any validation errors encountered. This
* function is the programmatic counterpart to drupal_get_form().
*
* @param $form_id
* The unique string identifying the desired form. If a function
* with that name exists, it is called to build the form array.
* Modules that need to generate the same form (or very similar forms)
* using different $form_ids can implement hook_forms(), which maps
* different $form_id values to the proper form building function. Examples
* may be found in node_forms(), search_forms(), and user_forms().
* @param $form_values
* An array of values mirroring the values returned by a given form
* when it is submitted by a user.
* @param ...
* Any additional arguments needed by the form building function.
* @return
* Any form validation errors encountered.
*/
function drupal_execute($form_id, $form_values) {
$args = func_get_args();
$form_id = array_shift($args);
$form_values = array_shift($args);
array_unshift($args, $form_id);
if (isset($form_values)) {
$form = call_user_func_array('drupal_retrieve_form', $args);
$form['#post'] = $form_values;
return drupal_process_form($form_id, $form);
}
}

Dries Buytaert
committed
/**
* Retrieves the structured array that defines a given form.
*
* @param $form_id
* The unique string identifying the desired form. If a function
* with that name exists, it is called to build the form array.
* Modules that need to generate the same form (or very similar forms)
* using different $form_ids can implement hook_forms(), which maps
* different $form_id values to the proper form building function.
* @param ...
* Any additional arguments needed by the form building function.
*/
function drupal_retrieve_form($form_id) {
static $forms;

Dries Buytaert
committed
// We save two copies of the incoming arguments: one for modules to use
// when mapping form ids to builder functions, and another to pass to
// the builder function itself. We shift out the first argument -- the
// $form_id itself -- from the list to pass into the builder function,
// since it's already known.

Dries Buytaert
committed
$args = func_get_args();

Dries Buytaert
committed
$saved_args = $args;

Dries Buytaert
committed
array_shift($args);

Dries Buytaert
committed
// We first check to see if there's a function named after the $form_id.
// If there is, we simply pass the arguments on to it to get the form.

Dries Buytaert
committed
if (!function_exists($form_id)) {

Dries Buytaert
committed
// In cases where many form_ids need to share a central builder function,
// such as the node editing form, modules can implement hook_forms(). It
// maps one or more form_ids to the correct builder functions.
//
// We cache the results of that hook to save time, but that only works
// for modules that know all their form_ids in advance. (A module that
// adds a small 'rate this comment' form to each comment in a list
// would need a unique form_id for each one, for example.)
//
// So, we call the hook if $forms isn't yet populated, OR if it doesn't
// yet have an entry for the requested form_id.
if (!isset($forms) || !isset($forms[$form_id])) {
$forms = module_invoke_all('forms', $saved_args);

Dries Buytaert
committed
}
$form_definition = $forms[$form_id];
if (isset($form_definition['callback arguments'])) {
$args = array_merge($form_definition['callback arguments'], $args);
}
if (isset($form_definition['callback'])) {
$callback = $form_definition['callback'];
}
}

Dries Buytaert
committed
// If $callback was returned by a hook_forms() implementation, call it.
// Otherwise, call the function named after the form id.
$form = call_user_func_array(isset($callback) ? $callback : $form_id, $args);
// We store the original function arguments, rather than the final $arg
// value, so that form_alter functions can see what was originally
// passed to drupal_retrieve_form(). This allows the contents of #parameters
// to be saved and passed in at a later date to recreate the form.

Dries Buytaert
committed
$form['#parameters'] = $saved_args;
return $form;

Dries Buytaert
committed
}
/**
* This function is the heart of form API. The form gets built, validated and in
* appropriate cases, submitted.
*
* @param $form_id
* The unique string identifying the current form.
* @param $form
* An associative array containing the structure of the form.

Dries Buytaert
committed
* @return
* The path to redirect the user to upon completion.

Dries Buytaert
committed
function drupal_process_form($form_id, &$form) {

Steven Wittens
committed
global $form_values, $form_submitted, $user, $form_button_counter;
static $saved_globals = array();

Dries Buytaert
committed
// In some scenerios, this function can be called recursively. Pushing any pre-existing
// $form_values and form submission data lets us start fresh without clobbering work done
// in earlier recursive calls.
array_push($saved_globals, array($form_values, $form_submitted, $form_button_counter));

Steven Wittens
committed
$form_button_counter = array(0, 0);

Dries Buytaert
committed
drupal_prepare_form($form_id, $form);
if (($form['#programmed']) || (!empty($_POST) && (($_POST['form_id'] == $form_id) || ($_POST['form_id'] == $form['#base'])))) {

Dries Buytaert
committed
drupal_validate_form($form_id, $form);

Dries Buytaert
committed
// IE does not send a button value when there is only one submit button (and no non-submit buttons)
// and you submit by pressing enter.
// In that case we accept a submission without button values.

Dries Buytaert
committed
if ((($form['#programmed']) || $form_submitted || (!$form_button_counter[0] && $form_button_counter[1])) && !form_get_errors()) {
$redirect = drupal_submit_form($form_id, $form);
if (!$form['#programmed']) {
drupal_redirect_form($form, $redirect);
}

Dries Buytaert
committed
}
}

Dries Buytaert
committed
// We've finished calling functions that alter the global values, so we can
// restore the ones that were there before this function was called.

Dries Buytaert
committed
list($form_values, $form_submitted, $form_button_counter) = array_pop($saved_globals);

Dries Buytaert
committed
return $redirect;

Dries Buytaert
committed
}
/**
* Prepares a structured form array by adding required elements,
* executing any hook_form_alter functions, and optionally inserting
* a validation token to prevent tampering.
*
* @param $form_id
* A unique string identifying the form for validation, submission,
* theming, and hook_form_alter functions.
* @param $form
* An associative array containing the structure of the form.
*/

Dries Buytaert
committed
function drupal_prepare_form($form_id, &$form) {

Dries Buytaert
committed
$form['#type'] = 'form';

Dries Buytaert
committed
if (!isset($form['#post'])) {
$form['#post'] = $_POST;
$form['#programmed'] = FALSE;
}
else {
$form['#programmed'] = TRUE;
}
// In multi-step form scenerios, this id is used to identify
// a unique instance of a particular form for retrieval.
if (isset($form['#build_id'])) {
$form['form_build_id'] = array(
'#type' => 'hidden',
'#value' => $form['#build_id'],
'#id' => $form['#build_id'],
'#name' => 'form_build_id',
);
}

Dries Buytaert
committed
// If $base is set, it is used in place of $form_id when constructing validation,
// submission, and theming functions. Useful for mapping many similar or duplicate
// forms with different $form_ids to the same processing functions.
if (isset($form['#base'])) {
$base = $form['#base'];
}

Dries Buytaert
committed
if (isset($form['#token'])) {
// If the page cache is on and an anonymous user issues a GET request,
// unset the token because the token in the cached page would not match,
// because the token is based on the session ID.

Gerhard Killesreiter
committed
if (variable_get('cache', 0) && !$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET') {
unset($form['#token']);

Gerhard Killesreiter
committed
else {
// Make sure that a private key is set:
if (!variable_get('drupal_private_key', '')) {
variable_set('drupal_private_key', mt_rand());
}

Gerhard Killesreiter
committed
$form['form_token'] = array('#type' => 'hidden', '#default_value' => md5(session_id() . $form['#token'] . variable_get('drupal_private_key', '')));
}

Dries Buytaert
committed
if (isset($form_id)) {

Neil Drumm
committed
$form['form_id'] = array('#type' => 'hidden', '#value' => $form_id, '#id' => str_replace('_', '-', "edit-$form_id"));
if (!isset($form['#id'])) {
$form['#id'] = $form_id;
}
$form += _element_info('form');
if (!isset($form['#validate'])) {
if (function_exists($form_id .'_validate')) {
$form['#validate'] = array($form_id .'_validate' => array());

Dries Buytaert
committed
elseif (function_exists($base .'_validate')) {
$form['#validate'] = array($base .'_validate' => array());

Dries Buytaert
committed
if (!isset($form['#submit'])) {
if (function_exists($form_id .'_submit')) {
// we set submit here so that it can be altered but use reference for
// $form_values because it will change later

Gerhard Killesreiter
committed
$form['#submit'] = array($form_id .'_submit' => array());

Dries Buytaert
committed
elseif (function_exists($base .'_submit')) {
$form['#submit'] = array($base .'_submit' => array());
foreach (module_implements('form_alter') as $module) {
$function = $module .'_form_alter';

Dries Buytaert
committed
$function($form_id, $form);
$form = form_builder($form_id, $form);

Dries Buytaert
committed
/**
* Validates user-submitted form data from a global variable using
* the validate functions defined in a structured form array.
*
* @param $form_id
* A unique string identifying the form for validation, submission,
* theming, and hook_form_alter functions.
* @param $form
* An associative array containing the structure of the form.
*
*/

Dries Buytaert
committed
function drupal_validate_form($form_id, $form) {
static $validated_forms = array();
if (isset($validated_forms[$form_id])) {
return;
}

Dries Buytaert
committed
// If the session token was set by drupal_prepare_form(), ensure that it

Dries Buytaert
committed
// matches the current user's session

Dries Buytaert
committed
if (isset($form['#token'])) {
if ($form_values['form_token'] != md5(session_id() . $form['#token'] . variable_get('drupal_private_key', ''))) {
// setting this error will cause the form to fail validation
form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));
_form_validate($form, $form_id);
$validated_forms[$form_id] = TRUE;

Dries Buytaert
committed
/**
* Processes user-submitted form data from a global variable using
* the submit functions defined in a structured form array.
*
* @param $form_id
* A unique string identifying the form for validation, submission,
* theming, and hook_form_alter functions.
* @param $form
* An associative array containing the structure of the form.
* @return
* A string containing the path of the page to display when processing
* is complete.
*
*/

Dries Buytaert
committed
function drupal_submit_form($form_id, $form) {

Gerhard Killesreiter
committed
global $form_values;
$default_args = array($form_id, &$form_values);

Dries Buytaert
committed
if (isset($form['#submit'])) {
foreach ($form['#submit'] as $function => $args) {
if (function_exists($function)) {

Gerhard Killesreiter
committed
$args = array_merge($default_args, (array) $args);
// Since we can only redirect to one page, only the last redirect will work
$redirect = call_user_func_array($function, $args);
if (isset($redirect)) {
$goto = $redirect;
}
return $goto;

Dries Buytaert
committed
/**
* Renders a structured form array into themed HTML.
*
* @param $form_id
* A unique string identifying the form for validation, submission,
* theming, and hook_form_alter functions.
* @param $form
* An associative array containing the structure of the form.
* @return
* A string containing the path of the page to display when processing
* is complete.
*
*/

Dries Buytaert
committed
function drupal_render_form($form_id, &$form) {

Dries Buytaert
committed
// Don't override #theme if someone already set it.

Dries Buytaert
committed
if (isset($form['#base'])) {
$base = $form['#base'];
}

Dries Buytaert
committed
if (!isset($form['#theme'])) {
if (theme_get_function($form_id)) {
$form['#theme'] = $form_id;
}

Dries Buytaert
committed
elseif (theme_get_function($base)) {
$form['#theme'] = $base;

Dries Buytaert
committed
}
}
if (isset($form['#pre_render'])) {
foreach ($form['#pre_render'] as $function) {
if (function_exists($function)) {
$function($form_id, $form);
}
}
}

Dries Buytaert
committed
$output = drupal_render($form);

Dries Buytaert
committed
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
return $output;
}
/**
* Redirect the user to a URL after a form has been processed.
*
* @param $form
* An associative array containing the structure of the form.
* @param $redirect
* An optional string containing the destination path to redirect
* to if none is specified by the form.
*
*/
function drupal_redirect_form($form, $redirect = NULL) {
if (isset($redirect)) {
$goto = $redirect;
}
if (isset($form['#redirect'])) {
$goto = $form['#redirect'];
}
if ($goto !== FALSE) {
if (is_array($goto)) {
call_user_func_array('drupal_goto', $goto);
}
elseif (!isset($goto)) {
drupal_goto($_GET['q']);
}
else {
drupal_goto($goto);
}
}
}
function _form_validate($elements, $form_id = NULL) {

Dries Buytaert
committed
// Recurse through all children.
foreach (element_children($elements) as $key) {
if (isset($elements[$key]) && $elements[$key]) {
_form_validate($elements[$key]);
}
}
/* Validate the current input */
if (!$elements['#validated']) {
if (isset($elements['#needs_validation'])) {
// An empty textfield returns '' so we use empty(). An empty checkbox
// and a textfield could return '0' and empty('0') returns TRUE so we
// need a special check for the '0' string.
if ($elements['#required'] && empty($elements['#value']) && $elements['#value'] !== '0') {
form_error($elements, t('!name field is required.', array('!name' => $elements['#title'])));
// Add legal choice check if element has #options. Can be skipped, but then you must validate your own element.
if (isset($elements['#options']) && isset($elements['#value']) && !isset($elements['#DANGEROUS_SKIP_CHECK'])) {
if ($elements['#type'] == 'select') {
$options = form_options_flatten($elements['#options']);
}
else {
$options = $elements['#options'];
}
if (is_array($elements['#value'])) {
$value = $elements['#type'] == 'checkboxes' ? array_keys(array_filter($elements['#value'])) : $elements['#value'];
foreach ($value as $v) {
if (!isset($options[$v])) {
form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
watchdog('form', t('Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'])), WATCHDOG_ERROR);
elseif (!isset($options[$elements['#value']])) {
form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
watchdog('form', t('Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'])), WATCHDOG_ERROR);
}
}
// User-applied checks.
foreach ($elements['#validate'] as $function => $args) {
$args = array_merge(array($elements), $args);
// for the full form we hand over a copy of $form_values
if (isset($form_id)) {
$args = array_merge(array($form_id, $GLOBALS['form_values']), $args);
if (function_exists($function)) {
call_user_func_array($function, $args);

Dries Buytaert
committed
$elements['#validated'] = TRUE;
/**
* File an error against a form element. If the name of the element is
* edit[foo][bar] then you may pass either foo or foo][bar as $name
* foo will set an error for all its children.
*/

Dries Buytaert
committed
function form_set_error($name = NULL, $message = '') {
static $form = array();
if (isset($name) && !isset($form[$name])) {
$form[$name] = $message;

Dries Buytaert
committed
if ($message) {
drupal_set_message($message, 'error');
}
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
}
return $form;
}
/**
* Return an associative array of all errors.
*/
function form_get_errors() {
$form = form_set_error();
if (!empty($form)) {
return $form;
}
}
/**
* Return the error message filed against the form with the specified name.
*/
function form_get_error($element) {
$form = form_set_error();
$key = $element['#parents'][0];
if (isset($form[$key])) {
return $form[$key];
}
$key = implode('][', $element['#parents']);
if (isset($form[$key])) {
return $form[$key];
}
}
/**
* Flag an element as having an error.
*/

Dries Buytaert
committed
function form_error(&$element, $message = '') {

Dries Buytaert
committed
$element['#error'] = TRUE;
form_set_error(implode('][', $element['#parents']), $message);
* Adds some required properties to each form element, which are used
* internally in the form api. This function also automatically assigns
* the value property from the $edit array, provided the element doesn't
* already have an assigned value.
*
* @param $form_id

Dries Buytaert
committed
* A unique string identifying the form for validation, submission,
* theming, and hook_form_alter functions.
* @param $form
* An associative array containing the structure of the form.
function form_builder($form_id, $form) {

Steven Wittens
committed
global $form_values, $form_submitted, $form_button_counter;
// Initialize as unprocessed.
$form['#processed'] = FALSE;
/* Use element defaults */

Dries Buytaert
committed
if ((!empty($form['#type'])) && ($info = _element_info($form['#type']))) {

Dries Buytaert
committed
// overlay $info onto $form, retaining preexisting keys in $form
$form += $info;
}
if (isset($form['#input']) && $form['#input']) {
if (!isset($form['#name'])) {
$name = array_shift($form['#parents']);
$form['#name'] = $name;
if ($form['#type'] == 'file') {
// to make it easier to handle $_FILES in file.inc, we place all
// file fields in the 'files' array. Also, we do not support
// nested file names
$form['#name'] = 'files['. $form['#name'] .']';
}
elseif (count($form['#parents'])) {
$form['#name'] .= '['. implode('][', $form['#parents']) .']';
}
array_unshift($form['#parents'], $name);
}
if (!isset($form['#id'])) {
$form['#id'] = 'edit-'. implode('-', $form['#parents']);
if (isset($form['#disabled']) && $form['#disabled']) {
$form['#attributes']['disabled'] = 'disabled';
}
if (!isset($form['#value']) && !array_key_exists('#value', $form)) {
if (($form['#programmed']) || ((!isset($form['#access']) || $form['#access']) && isset($form['#post']) && ($form['#post']['form_id'] == $form_id))) {
$edit = $form['#post'];
foreach ($form['#parents'] as $parent) {
$edit = isset($edit[$parent]) ? $edit[$parent] : NULL;
}

Neil Drumm
committed
if (!$form['#programmed'] || isset($edit)) {
switch ($form['#type']) {
case 'checkbox':
$form['#value'] = !empty($edit) ? $form['#return_value'] : 0;
break;
case 'select':
if (isset($form['#multiple']) && $form['#multiple']) {
if (isset($edit) && is_array($edit)) {
$form['#value'] = drupal_map_assoc($edit);
}
else {
$form['#value'] = array();
}

Neil Drumm
committed
elseif (isset($edit)) {
$form['#value'] = $edit;

Neil Drumm
committed
break;
case 'textfield':
if (isset($edit)) {
// Equate $edit to the form value to ensure it's marked for validation
$edit = str_replace(array("\r", "\n"), '', $edit);
$form['#value'] = $edit;
}
break;
default:
if (isset($edit)) {
$form['#value'] = $edit;
}
}
// Mark all posted values for validation
if ((isset($form['#value']) && $form['#value'] === $edit) || (isset($form['#required']) && $form['#required'])) {
$form['#needs_validation'] = TRUE;
}

Dries Buytaert
committed
}
}
if (!isset($form['#value'])) {
$function = $form['#type'] . '_value';
if (function_exists($function)) {
$function($form);
}
else {
$form['#value'] = $form['#default_value'];
}

Dries Buytaert
committed
}

Dries Buytaert
committed
}

Steven Wittens
committed
if (isset($form['#executes_submit_callback'])) {
// Count submit and non-submit buttons
$form_button_counter[$form['#executes_submit_callback']]++;
// See if a submit button was pressed
if (isset($form['#post'][$form['#name']]) && $form['#post'][$form['#name']] == $form['#value']) {
$form_submitted = $form_submitted || $form['#executes_submit_callback'];
// In most cases, we want to use form_set_value() to manipulate the global variables.
// In this special case, we want to make sure that the value of this element is listed
// in $form_variables under 'op'.
$form_values[$form['#name']] = $form['#value'];

Dries Buytaert
committed
// Allow for elements to expand to multiple elements, e.g. radios, checkboxes and files.
if (isset($form['#process']) && !$form['#processed']) {
foreach ($form['#process'] as $process => $args) {
if (function_exists($process)) {
$args = array_merge(array($form), array($edit), $args);
$form = call_user_func_array($process, $args);

Dries Buytaert
committed
$form['#processed'] = TRUE;

Dries Buytaert
committed
// Set the $form_values key that gets passed to validate and submit.
// We call this after #process gets called so that #process has a
// chance to update #value if desired.
if (isset($form['#input']) && $form['#input']) {
form_set_value($form, $form['#value']);

Dries Buytaert
committed
}
// Recurse through all child elements.
$count = 0;
foreach (element_children($form) as $key) {

Dries Buytaert
committed
$form[$key]['#post'] = $form['#post'];
$form[$key]['#programmed'] = $form['#programmed'];
if (!isset($form[$key]['#tree'])) {
$form[$key]['#tree'] = $form['#tree'];
}

Dries Buytaert
committed
// deny access to child elements if parent is denied
if (isset($form['#access']) && !$form['#access']) {
$form[$key]['#access'] = FALSE;
}
// don't squash existing parents value
if (!isset($form[$key]['#parents'])) {
// Check to see if a tree of child elements is present. If so, continue down the tree if required.
$form[$key]['#parents'] = $form[$key]['#tree'] && $form['#tree'] ? array_merge($form['#parents'], array($key)) : array($key);

Dries Buytaert
committed
// Assign a decimal placeholder weight to preserve original array order
if (!isset($form[$key]['#weight'])) {
$form[$key]['#weight'] = $count/1000;
}
$form[$key] = form_builder($form_id, $form[$key]);

Gerhard Killesreiter
committed
if (isset($form['#after_build']) && !isset($form['#after_build_done'])) {
foreach ($form['#after_build'] as $function) {
if (function_exists($function)) {
$form = $function($form, $form_values);
}
}
$form['#after_build_done'] = TRUE;
* Use this function to make changes to form values in the form validate
* phase, so they will be available in the submit phase in $form_values.
*
* Specifically, if $form['#parents'] is array('foo', 'bar')
* and $value is 'baz' then this function will make
* $form_values['foo']['bar'] to be 'baz'.
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
*
* @param $form
* The form item. Keys used: #parents, #value
* @param $value
* The value for the form item.
*/
function form_set_value($form, $value) {
global $form_values;
_form_set_value($form_values, $form, $form['#parents'], $value);
}
/**
* Helper function for form_set_value().
*
* We iterate of $parents and create nested arrays for them
* in $form_values if needed. Then we insert the value in
* the right array.
*/
function _form_set_value(&$form_values, $form, $parents, $value) {
$parent = array_shift($parents);
if (empty($parents)) {
$form_values[$parent] = $value;
}
else {
if (!isset($form_values[$parent])) {
$form_values[$parent] = array();
}
_form_set_value($form_values[$parent], $form, $parents, $value);
}
return $form;
}
/**
* Retrieve the default properties for the defined element type.
*/
function _element_info($type, $refresh = NULL) {

Dries Buytaert
committed
'#description' => NULL,
'#attributes' => array(),
'#required' => FALSE,
'#parents' => array()
if (!isset($cache) || $refresh) {
$cache = array();
foreach (module_implements('elements') as $module) {
$elements = module_invoke($module, 'elements');

Dries Buytaert
committed
if (isset($elements) && is_array($elements)) {
$cache = array_merge_recursive($cache, $elements);
}
}
if (sizeof($cache)) {
foreach ($cache as $element_type => $info) {
$cache[$element_type] = array_merge_recursive($basic_defaults, $info);
}
}
}
return $cache[$type];
}
function form_options_flatten($array, $reset = TRUE) {
static $return;
if ($reset) {
$return = array();
}
foreach ($array as $key => $value) {
if (is_array($value)) {
form_options_flatten($value, FALSE);
}
else {
$return[$key] = 1;
}
}
return $return;
}
/**
* Format a dropdown menu or scrolling selection box.
*
* @param $element
* An associative array containing the properties of the element.

Dries Buytaert
committed
* Properties used: title, value, options, description, extra, multiple, required
* @return
* A themed HTML string representing the form element.
*
* It is possible to group options together; to do this, change the format of
* $options to an associative array in which the keys are group labels, and the
* values are associative arrays in the normal $options format.
*/
function theme_select($element) {
$select = '';
$size = $element['#size'] ? ' size="' . $element['#size'] . '"' : '';

Gerhard Killesreiter
committed
_form_set_class($element, array('form-select'));
$multiple = isset($element['#multiple']) && $element['#multiple'];
return theme('form_element', $element, '<select name="'. $element['#name'] .''. ($multiple ? '[]' : '') .'"'. ($multiple ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) .' id="'. $element['#id'] .'" '. $size .'>'. form_select_options($element) .'</select>');

Dries Buytaert
committed
}
function form_select_options($element, $choices = NULL) {
if (!isset($choices)) {
$choices = $element['#options'];
}
// array_key_exists() accommodates the rare event where $element['#value'] is NULL.
// isset() fails in this situation.
$value_valid = isset($element['#value']) || array_key_exists('#value', $element);
$value_is_array = is_array($element['#value']);

Dries Buytaert
committed
$options = '';
foreach ($choices as $key => $choice) {

Dries Buytaert
committed
$options .= '<optgroup label="'. $key .'">';
$options .= form_select_options($element, $choice);
$options .= '</optgroup>';

Dries Buytaert
committed
$key = (string)$key;
if ($value_valid && ($element['#value'] == $key || ($value_is_array && in_array($key, $element['#value'])))) {
$selected = ' selected="selected"';
}
else {
$selected = '';
}

Dries Buytaert
committed
$options .= '<option value="'. $key .'"'. $selected .'>'. check_plain($choice) .'</option>';

Dries Buytaert
committed
return $options;
}
/**
* Format a group of form items.
*
* @param $element
* An associative array containing the properties of the element.
* Properties used: attributes, title, value, description, children, collapsible, collapsed
* @return
* A themed HTML string representing the form item group.
*/
function theme_fieldset($element) {

Dries Buytaert
committed
if ($element['#collapsible']) {
drupal_add_js('misc/collapse.js');
if (!isset($element['#attributes']['class'])) {
$element['#attributes']['class'] = '';
}

Dries Buytaert
committed
$element['#attributes']['class'] .= ' collapsible';
if ($element['#collapsed']) {
$element['#attributes']['class'] .= ' collapsed';
return '<fieldset' . drupal_attributes($element['#attributes']) .'>' . ($element['#title'] ? '<legend>'. $element['#title'] .'</legend>' : '') . ($element['#description'] ? '<div class="description">'. $element['#description'] .'</div>' : '') . $element['#children'] . $element['#value'] . "</fieldset>\n";
}
/**
* Format a radio button.
*
* @param $element
* An associative array containing the properties of the element.

Dries Buytaert
committed
* Properties used: required, return_value, value, attributes, title, description
* @return
* A themed HTML string representing the form item group.
*/
function theme_radio($element) {
_form_set_class($element, array('form-radio'));
$output = '<input type="radio" ';

Dries Buytaert
committed
$output .= 'name="' . $element['#name'] .'" ';
$output .= 'value="'. $element['#return_value'] .'" ';
$output .= ($element['#value'] == $element['#return_value']) ? ' checked="checked" ' : ' ';
$output .= drupal_attributes($element['#attributes']) .' />';
if (!is_null($element['#title'])) {
$output = '<label class="option">'. $output .' '. $element['#title'] .'</label>';
unset($element['#title']);
return theme('form_element', $element, $output);
}
/**
* Format a set of radio buttons.
*
* @param $element
* An associative array containing the properties of the element.

Dries Buytaert
committed
* Properties used: title, value, options, description, required and attributes.
* @return
* A themed HTML string representing the radio button set.
*/
function theme_radios($element) {
$element['#children'] = '<div class="form-radios">'. $element['#children'] .'</div>';

Dries Buytaert
committed
if ($element['#title'] || $element['#description']) {
unset($element['#id']);
return theme('form_element', $element, $element['#children']);

Dries Buytaert
committed
return $element['#children'];
/**
* Format a password_confirm item.
*
* @param $element
* An associative array containing the properties of the element.
* Properties used: title, value, id, required, error.
* @return
* A themed HTML string representing the form item.
*/
function theme_password_confirm($element) {
return theme('form_element', $element, $element['#children']);
}

Gerhard Killesreiter
committed
/*
* Expand a password_confirm field into two text boxes.
*/
function expand_password_confirm($element) {
$element['pass1'] = array(
'#type' => 'password',
'#title' => t('Password'),
'#value' => $element['#value']['pass1'],
);
$element['pass2'] = array(
'#type' => 'password',