summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2010-04-11 19:00:27 +0000
committerDries Buytaert <dries@buytaert.net>2010-04-11 19:00:27 +0000
commit33f0b0830baa8d584c8cce180fa2d8a0b94e39fd (patch)
tree00d6c31612eb275ac148abf678ff2a474e4fa44f /includes
parent98a3f2266d516a39c2731499af2840b0a2d500ef (diff)
downloadbrdo-33f0b0830baa8d584c8cce180fa2d8a0b94e39fd.tar.gz
brdo-33f0b0830baa8d584c8cce180fa2d8a0b94e39fd.tar.bz2
- Patch #426056 by effulgentsia, Berdir, sun, boombatower, tstoeckler: fixed server-side enforcement of #disabled is inconsistent.
Diffstat (limited to 'includes')
-rw-r--r--includes/form.inc78
1 files changed, 54 insertions, 24 deletions
diff --git a/includes/form.inc b/includes/form.inc
index 9518ef13a..93d0304b6 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -983,7 +983,7 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
// A simple call to empty() will not cut it here as some fields, like
// checkboxes, can return a valid value of '0'. Instead, check the
// length if it's a string, and the item count if it's an array.
- // An unchecked checkbox has a #value of numeric 0, different than string
+ // An unchecked checkbox has a #value of integer 0, different than string
// '0', which could be a valid value.
if (isset($elements['#needs_validation']) && $elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0) || $elements['#value'] === 0)) {
form_error($elements, $t('!name field is required.', array('!name' => $elements['#title'])));
@@ -1405,15 +1405,45 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
array_unshift($element['#parents'], $name);
}
+ // Setting #disabled to TRUE results in user input being ignored, regardless
+ // of how the element is themed or whether JavaScript is used to change the
+ // control's attributes. However, it's good UI to let the user know that input
+ // is not wanted for the control. HTML supports two attributes for this:
+ // http://www.w3.org/TR/html401/interact/forms.html#h-17.12. If a form wants
+ // to start a control off with one of these attributes for UI purposes only,
+ // but still allow input to be processed if it's sumitted, it can set the
+ // desired attribute in #attributes directly rather than using #disabled.
+ // However, developers should think carefully about the accessibility
+ // implications of doing so: if the form expects input to be enterable under
+ // some condition triggered by JavaScript, how would someone who has
+ // JavaScript disabled trigger that condition? Instead, developers should
+ // consider whether a multi-step form would be more appropriate (#disabled can
+ // be changed from step to step). If one still decides to use JavaScript to
+ // affect when a control is enabled, then it is best for accessibility for the
+ // control to be enabled in the HTML, and disabled by JavaScript on document
+ // ready.
if (!empty($element['#disabled'])) {
- $element['#attributes']['disabled'] = 'disabled';
+ if (!empty($element['#allow_focus'])) {
+ $element['#attributes']['readonly'] = 'readonly';
+ }
+ else {
+ $element['#attributes']['disabled'] = 'disabled';
+ }
}
// Set the element's #value property.
if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
$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']))) {
+ // With JavaScript or other easy hacking, input can be submitted even for
+ // elements with #access=FALSE or #disabled=TRUE. For security, these must
+ // not be processed. Forms that set #disabled=TRUE on an element do not
+ // expect input for the element, and even forms submitted with
+ // drupal_form_submit() must not be able to get around this. Forms that set
+ // #access=FALSE on an element usually allow access for some users, so forms
+ // submitted with drupal_form_submit() may bypass access restriction and be
+ // treated as high-privelege users instead.
+ if (empty($element['#disabled']) && ($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'];
@@ -1698,18 +1728,11 @@ function form_type_image_button_value($form, $input, $form_state) {
*/
function form_type_checkbox_value($element, $input = FALSE) {
if ($input !== FALSE) {
- if (empty($element['#disabled'])) {
- // Successful (checked) checkboxes are present with a value (possibly '0').
- // http://www.w3.org/TR/html401/interact/forms.html#successful-controls
- // For an unchecked checkbox, we return numeric 0, so we can explicitly
- // test for a value different than string '0'.
- return isset($input) ? $element['#return_value'] : 0;
- }
- else {
- // Disabled form controls are not submitted by the browser. Ignore any
- // submitted value and always return default.
- return $element['#default_value'];
- }
+ // Successful (checked) checkboxes are present with a value (possibly '0').
+ // http://www.w3.org/TR/html401/interact/forms.html#successful-controls
+ // For an unchecked checkbox, we return integer 0, so we can explicitly
+ // test for a value different than string '0'.
+ return isset($input) ? $element['#return_value'] : 0;
}
}
@@ -2294,9 +2317,12 @@ function form_process_radios($element) {
'#attributes' => $element['#attributes'],
'#parents' => $element['#parents'],
'#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)),
- '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
- '#disabled' => isset($element['#disabled']) ? $element['#disabled'] : NULL,
);
+ foreach (array('#ajax', '#disabled', '#allow_focus') as $property) {
+ if (isset($element[$property])) {
+ $element[$key][$property] = $element[$property];
+ }
+ }
}
}
}
@@ -2326,7 +2352,7 @@ function theme_checkbox($variables) {
$checkbox .= 'name="' . $element['#name'] . '" ';
$checkbox .= 'id="' . $element['#id'] . '" ' ;
$checkbox .= 'value="' . $element['#return_value'] . '" ';
- // Unchecked checkbox has #value of numeric 0.
+ // Unchecked checkbox has #value of integer 0.
if ($element['#value'] !== 0 && $element['#value'] == $element['#return_value']) {
$checkbox .= 'checked="checked" ';
}
@@ -2398,9 +2424,12 @@ function form_process_checkboxes($element) {
'#return_value' => $key,
'#default_value' => isset($value[$key]) ? $key : NULL,
'#attributes' => $element['#attributes'],
- '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
- '#disabled' => isset($element['#disabled']) ? $element['#disabled'] : NULL,
);
+ foreach (array('#ajax', '#disabled', '#allow_focus') as $property) {
+ if (isset($element[$property])) {
+ $element[$key][$property] = $element[$property];
+ }
+ }
}
}
}
@@ -2550,8 +2579,6 @@ function form_process_tableselect($element) {
'#return_value' => $key,
'#default_value' => isset($value[$key]) ? $key : NULL,
'#attributes' => $element['#attributes'],
- '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
- '#disabled' => isset($element['#disabled']) ? $element['#disabled'] : NULL,
);
}
else {
@@ -2566,10 +2593,13 @@ function form_process_tableselect($element) {
'#attributes' => $element['#attributes'],
'#parents' => $element['#parents'],
'#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)),
- '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
- '#disabled' => isset($element['#disabled']) ? $element['#disabled'] : NULL,
);
}
+ foreach (array('#ajax', '#disabled', '#allow_focus') as $property) {
+ if (isset($element[$property])) {
+ $element[$key][$property] = $element[$property];
+ }
+ }
}
}
}