summaryrefslogtreecommitdiff
path: root/includes/form.inc
diff options
context:
space:
mode:
Diffstat (limited to 'includes/form.inc')
-rw-r--r--includes/form.inc76
1 files changed, 53 insertions, 23 deletions
diff --git a/includes/form.inc b/includes/form.inc
index a6ca24515..ff918dc43 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -284,10 +284,7 @@ function form_state_defaults() {
}
/**
- * Retrieves a form, caches it and processes it with an empty $_POST.
- *
- * This function clears $_POST and passes the empty $_POST to the form_builder.
- * To preserve some parts from $_POST, pass them in $form_state.
+ * Retrieves a form, caches it and processes it again.
*
* If your AHAH callback simulates the pressing of a button, then your AHAH
* callback will need to do the same as what drupal_get_form would do when the
@@ -315,6 +312,13 @@ function form_state_defaults() {
* The newly built form.
*/
function drupal_rebuild_form($form_id, &$form_state, $form_build_id = NULL) {
+ // AJAX and other contexts may call drupal_rebuild_form() even when
+ // $form_state['rebuild'] isn't set, but _form_builder_handle_input_element()
+ // needs to distinguish a rebuild from an initial build in order to process
+ // user input correctly. Form constructors and form processing functions may
+ // also need to handle a rebuild differently than an initial build.
+ $form_state['rebuild'] = TRUE;
+
$form = drupal_retrieve_form($form_id, $form_state);
if (!isset($form_build_id)) {
@@ -331,13 +335,8 @@ function drupal_rebuild_form($form_id, &$form_state, $form_build_id = NULL) {
form_set_cache($form_build_id, $form, $form_state);
}
- // Clear out all post data, as we don't want the previous step's
- // data to pollute this one and trigger validate/submit handling,
- // then process the form for rendering.
- $form_state['input'] = array();
-
- // Also clear out all group associations as these might be different
- // when rerendering the form.
+ // Clear out all group associations as these might be different when
+ // re-rendering the form.
$form_state['groups'] = array();
// Do not call drupal_process_form(), since it would prevent the rebuilt form
@@ -409,10 +408,13 @@ function form_set_cache($form_build_id, $form, $form_state) {
* different $form_id values to the proper form constructor function. Examples
* may be found in node_forms(), search_forms(), and user_forms().
* @param $form_state
- * A keyed array containing the current state of the form. Most
- * important is the $form_state['values'] collection, a tree of data
- * used to simulate the incoming $_POST information from a user's
- * form submission.
+ * A keyed array containing the current state of the form. Most important is
+ * the $form_state['values'] collection, a tree of data used to simulate the
+ * incoming $_POST information from a user's form submission. If a key is not
+ * filled in $form_state['values'], then the default value of the respective
+ * element is used. To submit an unchecked checkbox or other control that
+ * browsers submit by not having a $_POST entry, include the key, but set the
+ * value to NULL.
* @param ...
* Any additional arguments are passed on to the functions called by
* drupal_form_submit(), including the unique form constructor function.
@@ -935,9 +937,11 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
*/
function form_execute_handlers($type, &$form, &$form_state) {
$return = FALSE;
+ // If there was a button pressed, use its handlers.
if (isset($form_state[$type . '_handlers'])) {
$handlers = $form_state[$type . '_handlers'];
}
+ // Otherwise, check for a form-level handler.
elseif (isset($form['#' . $type])) {
$handlers = $form['#' . $type];
}
@@ -1205,13 +1209,40 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
$value_callback = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value';
if ($form_state['programmed'] || ($form_state['process_input'] && (!isset($element['#access']) || $element['#access']))) {
+ // Get the input for the current element. NULL values in the input need to
+ // be explicitly distinguished from missing input. (see below)
$input = $form_state['input'];
+ $input_exists = TRUE;
foreach ($element['#parents'] as $parent) {
- $input = isset($input[$parent]) ? $input[$parent] : NULL;
+ if (is_array($input) && array_key_exists($parent, $input)) {
+ $input = $input[$parent];
+ }
+ else {
+ $input = NULL;
+ $input_exists = FALSE;
+ break;
+ }
+ }
+ // For browser-submitted forms, the submitted values do not contain values
+ // for certain elements (empty multiple select, unchecked checkbox).
+ // During initial form processing, we add explicit NULL values for such
+ // elements in $form_state['input']. When rebuilding the form, we can
+ // distinguish elements having NULL input from elements that were not part
+ // of the initially submitted form and can therefore use default values
+ // for the latter, if required. Programmatically submitted forms can
+ // submit explicit NULL values when calling drupal_form_submit(), so we do
+ // not modify $form_state['input'] for them.
+ if (!$input_exists && !$form_state['rebuild'] && !$form_state['programmed']) {
+ // We leverage the internal logic of form_set_value() to change the
+ // input values by passing $form_state['input'] instead of the usual
+ // $form_state['values']. In effect, this adds the necessary parent keys
+ // to $form_state['input'] and sets the element's input value to NULL.
+ _form_set_value($form_state['input'], $element, $element['#parents'], NULL);
+ $input_exists = TRUE;
}
- // If we have input for the current element, assign it to the #value property.
- if (!$form_state['programmed'] || isset($input)) {
- // Call #type_value to set the form value;
+ // If we have input for the current element, assign it to the #value
+ // property, optionally filtered through $value_callback.
+ if ($input_exists) {
if (function_exists($value_callback)) {
$element['#value'] = $value_callback($element, $input, $form_state);
}
@@ -1595,11 +1626,10 @@ function form_set_value($element, $value, &$form_state) {
}
/**
- * Helper function for form_set_value().
+ * Helper function for form_set_value() and _form_builder_handle_input_element().
*
- * We iterate over $parents and create nested arrays for them
- * in $form_state['values'] if needed. Then we insert the value into
- * the right array.
+ * We iterate over $parents and create nested arrays for them in $form_values if
+ * needed. Then we insert the value into the last parent key.
*/
function _form_set_value(&$form_values, $element, $parents, $value) {
$parent = array_shift($parents);