Commit 699afdd1 authored by Dries's avatar Dries
Browse files

- Patch #690980 by seutje, sun, seanyo, aaroncouch: disabled form elements not properly rendered.

parent 137011df
...@@ -2368,15 +2368,16 @@ function theme_fieldset($variables) { ...@@ -2368,15 +2368,16 @@ function theme_fieldset($variables) {
*/ */
function theme_radio($variables) { function theme_radio($variables) {
$element = $variables['element']; $element = $variables['element'];
$element['#attributes']['type'] = 'radio';
$element['#attributes']['name'] = $element['#name'];
$element['#attributes']['id'] = $element['#id'];
$element['#attributes']['value'] = $element['#return_value'];
if (check_plain($element['#value']) == $element['#return_value']) {
$element['#attributes']['checked'] = 'checked';
}
_form_set_class($element, array('form-radio')); _form_set_class($element, array('form-radio'));
$output = '<input type="radio" ';
$output .= 'id="' . $element['#id'] . '" ';
$output .= 'name="' . $element['#name'] . '" ';
$output .= 'value="' . $element['#return_value'] . '" ';
$output .= (check_plain($element['#value']) == $element['#return_value']) ? ' checked="checked" ' : ' ';
$output .= drupal_attributes($element['#attributes']) . ' />';
return $output; return '<input' . drupal_attributes($element['#attributes']) . ' />';
} }
/** /**
...@@ -2602,19 +2603,17 @@ function form_process_radios($element) { ...@@ -2602,19 +2603,17 @@ function form_process_radios($element) {
function theme_checkbox($variables) { function theme_checkbox($variables) {
$element = $variables['element']; $element = $variables['element'];
$t = get_t(); $t = get_t();
_form_set_class($element, array('form-checkbox')); $element['#attributes']['type'] = 'checkbox';
$checkbox = '<input '; $element['#attributes']['name'] = $element['#name'];
$checkbox .= 'type="checkbox" '; $element['#attributes']['id'] = $element['#id'];
$checkbox .= 'name="' . $element['#name'] . '" '; $element['#attributes']['value'] = $element['#return_value'];
$checkbox .= 'id="' . $element['#id'] . '" ' ;
$checkbox .= 'value="' . $element['#return_value'] . '" ';
// Unchecked checkbox has #value of integer 0. // Unchecked checkbox has #value of integer 0.
if ($element['#value'] !== 0 && $element['#value'] == $element['#return_value']) { if ($element['#value'] !== 0 && $element['#value'] == $element['#return_value']) {
$checkbox .= 'checked="checked" '; $element['#attributes']['checked'] = 'checked';
} }
$checkbox .= drupal_attributes($element['#attributes']) . ' />'; _form_set_class($element, array('form-checkbox'));
return $checkbox; return '<input' . drupal_attributes($element['#attributes']) . ' />';
} }
/** /**
...@@ -3057,9 +3056,18 @@ function theme_submit($variables) { ...@@ -3057,9 +3056,18 @@ function theme_submit($variables) {
*/ */
function theme_button($variables) { function theme_button($variables) {
$element = $variables['element']; $element = $variables['element'];
$element['#attributes']['type'] = 'submit';
if (!empty($element['#name'])) {
$element['#attributes']['name'] = $element['#name'];
}
$element['#attributes']['id'] = $element['#id'];
$element['#attributes']['value'] = $element['#value'];
$element['#attributes']['class'][] = 'form-' . $element['#button_type']; $element['#attributes']['class'][] = 'form-' . $element['#button_type'];
if (!empty($element['#attributes']['disabled'])) {
$element['#attributes']['class'][] = 'form-button-disabled';
}
return '<input type="submit" ' . (empty($element['#name']) ? '' : 'name="' . $element['#name'] . '" ') . 'id="' . $element['#id'] . '" value="' . check_plain($element['#value']) . '" ' . drupal_attributes($element['#attributes']) . " />\n"; return '<input' . drupal_attributes($element['#attributes']) . ' />';
} }
/** /**
...@@ -3074,15 +3082,24 @@ function theme_button($variables) { ...@@ -3074,15 +3082,24 @@ function theme_button($variables) {
*/ */
function theme_image_button($variables) { function theme_image_button($variables) {
$element = $variables['element']; $element = $variables['element'];
$element['#attributes']['type'] = 'submit';
$element['#attributes']['name'] = $element['#name'];
if (!empty($element['#value'])) {
$element['#attributes']['value'] = $element['#value'];
}
$element['#attributes']['id'] = $element['#id'];
$element['#attributes']['src'] = file_create_url($element['#src']);
if (!empty($element['#title'])) {
$element['#attributes']['alt'] = $element['#title'];
$element['#attributes']['title'] = $element['#title'];
}
$element['#attributes']['class'][] = 'form-' . $element['#button_type']; $element['#attributes']['class'][] = 'form-' . $element['#button_type'];
if (!empty($element['#attributes']['disabled'])) {
$element['#attributes']['class'][] = 'form-button-disabled';
}
return '<input type="image" name="' . $element['#name'] . '" ' . return '<input' . drupal_attributes($element['#attributes']) . ' />';
(!empty($element['#value']) ? ('value="' . check_plain($element['#value']) . '" ') : '') .
'id="' . $element['#id'] . '" ' .
drupal_attributes($element['#attributes']) .
' src="' . file_create_url($element['#src']) . '" ' .
(!empty($element['#title']) ? 'alt="' . check_plain($element['#title']) . '" title="' . check_plain($element['#title']) . '" ' : '' ) .
"/>\n";
} }
/** /**
...@@ -3097,7 +3114,11 @@ function theme_image_button($variables) { ...@@ -3097,7 +3114,11 @@ function theme_image_button($variables) {
*/ */
function theme_hidden($variables) { function theme_hidden($variables) {
$element = $variables['element']; $element = $variables['element'];
return '<input type="hidden" name="' . $element['#name'] . '" id="' . $element['#id'] . '" value="' . check_plain($element['#value']) . "\" " . drupal_attributes($element['#attributes']) . " />\n"; $element['#attributes']['type'] = 'hidden';
$element['#attributes']['name'] = $element['#name'];
$element['#attributes']['id'] = $element['#id'];
$element['#attributes']['value'] = $element['#value'];
return '<input' . drupal_attributes($element['#attributes']) . " />\n";
} }
/** /**
...@@ -3113,20 +3134,33 @@ function theme_hidden($variables) { ...@@ -3113,20 +3134,33 @@ function theme_hidden($variables) {
*/ */
function theme_textfield($variables) { function theme_textfield($variables) {
$element = $variables['element']; $element = $variables['element'];
$size = empty($element['#size']) ? '' : ' size="' . $element['#size'] . '"'; $element['#attributes']['type'] = 'text';
$maxlength = empty($element['#maxlength']) ? '' : ' maxlength="' . $element['#maxlength'] . '"'; $element['#attributes']['name'] = $element['#name'];
$class = array('form-text'); $element['#attributes']['id'] = $element['#id'];
$extra = ''; $element['#attributes']['value'] = $element['#value'];
$output = ''; if (!empty($element['#size'])) {
$element['#attributes']['size'] = $element['#size'];
}
if (!empty($element['#maxlength'])) {
$element['#attributes']['maxlength'] = $element['#maxlength'];
}
_form_set_class($element, array('form-text'));
$extra = '';
if ($element['#autocomplete_path'] && drupal_valid_path($element['#autocomplete_path'])) { if ($element['#autocomplete_path'] && drupal_valid_path($element['#autocomplete_path'])) {
drupal_add_js('misc/autocomplete.js'); drupal_add_js('misc/autocomplete.js');
$class[] = 'form-autocomplete'; $element['#attributes']['class'][] = 'form-autocomplete';
$extra = '<input class="autocomplete" type="hidden" id="' . $element['#id'] . '-autocomplete" value="' . check_url(url($element['#autocomplete_path'], array('absolute' => TRUE))) . '" disabled="disabled" />';
$attributes = array();
$attributes['type'] = 'hidden';
$attributes['id'] = $element['#id'] . '-autocomplete';
$attributes['value'] = url($element['#autocomplete_path'], array('absolute' => TRUE));
$attributes['disabled'] = 'disabled';
$attributes['class'][] = 'autocomplete';
$extra = '<input' . drupal_attributes($attributes) . ' />';
} }
_form_set_class($element, $class);
$output .= '<input type="text"' . $maxlength . ' name="' . $element['#name'] . '" id="' . $element['#id'] . '"' . $size . ' value="' . check_plain($element['#value']) . '"' . drupal_attributes($element['#attributes']) . ' />'; $output = '<input' . drupal_attributes($element['#attributes']) . ' />';
return $output . $extra; return $output . $extra;
} }
...@@ -3143,14 +3177,16 @@ function theme_textfield($variables) { ...@@ -3143,14 +3177,16 @@ function theme_textfield($variables) {
*/ */
function theme_form($variables) { function theme_form($variables) {
$element = $variables['element']; $element = $variables['element'];
// Anonymous div to satisfy XHTML compliance. if (!empty($element['#action'])) {
$action = $element['#action'] ? 'action="' . check_url($element['#action']) . '" ' : ''; $element['#attributes']['action'] = drupal_strip_dangerous_protocols($element['#action']);
}
$element['#attributes']['method'] = $element['#method'];
if (empty($element['#attributes']['accept-charset'])) { if (empty($element['#attributes']['accept-charset'])) {
$element['#attributes']['accept-charset'] = "UTF-8"; $element['#attributes']['accept-charset'] = "UTF-8";
} }
$element['#attributes']['id'] = $element['#id'];
return '<form '. $action .' method="'. $element['#method'] .'" id="'. $element['#id'] .'"'. drupal_attributes($element['#attributes']) .">\n<div>". $element['#children'] ."\n</div></form>\n"; // Anonymous DIV to satisfy XHTML compliance.
return '<form' . drupal_attributes($element['#attributes']) . '><div>' . $element['#children'] . '</div></form>';
} }
/** /**
...@@ -3166,10 +3202,16 @@ function theme_form($variables) { ...@@ -3166,10 +3202,16 @@ function theme_form($variables) {
*/ */
function theme_textarea($variables) { function theme_textarea($variables) {
$element = $variables['element']; $element = $variables['element'];
$element['#attributes']['name'] = $element['#name'];
$element['#attributes']['id'] = $element['#id'];
$element['#attributes']['value'] = $element['#value'];
$element['#attributes']['cols'] = $element['#cols'];
$element['#attributes']['rows'] = $element['#rows'];
_form_set_class($element, array('form-textarea'));
$wrapper_attributes = array( $wrapper_attributes = array(
'class' => array('form-textarea-wrapper'), 'class' => array('form-textarea-wrapper'),
); );
$class = array('form-textarea');
// Add resizable behavior. // Add resizable behavior.
if (!empty($element['#resizable'])) { if (!empty($element['#resizable'])) {
...@@ -3178,10 +3220,7 @@ function theme_textarea($variables) { ...@@ -3178,10 +3220,7 @@ function theme_textarea($variables) {
} }
$output = '<div' . drupal_attributes($wrapper_attributes) . '>'; $output = '<div' . drupal_attributes($wrapper_attributes) . '>';
$output .= '<textarea' . drupal_attributes($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>';
_form_set_class($element, $class);
$output .= '<textarea cols="' . $element['#cols'] . '" rows="' . $element['#rows'] . '" name="' . $element['#name'] . '" id="' . $element['#id'] . '" ' . drupal_attributes($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>';
$output .= '</div>'; $output .= '</div>';
return $output; return $output;
} }
...@@ -3199,12 +3238,19 @@ function theme_textarea($variables) { ...@@ -3199,12 +3238,19 @@ function theme_textarea($variables) {
*/ */
function theme_password($variables) { function theme_password($variables) {
$element = $variables['element']; $element = $variables['element'];
$size = $element['#size'] ? ' size="' . $element['#size'] . '" ' : ''; $element['#attributes']['type'] = 'password';
$maxlength = $element['#maxlength'] ? ' maxlength="' . $element['#maxlength'] . '" ' : ''; $element['#attributes']['name'] = $element['#name'];
$element['#attributes']['id'] = $element['#id'];
$element['#attributes']['value'] = $element['#value'];
if (!empty($element['#size'])) {
$element['#attributes']['size'] = $element['#size'];
}
if (!empty($element['#maxlength'])) {
$element['#attributes']['maxlength'] = $element['#maxlength'];
}
_form_set_class($element, array('form-text')); _form_set_class($element, array('form-text'));
$output = '<input type="password" name="' . $element['#name'] . '" id="' . $element['#id'] . '" ' . $maxlength . $size . drupal_attributes($element['#attributes']) . ' />';
return $output; return '<input' . drupal_attributes($element['#attributes']) . ' />';
} }
/** /**
...@@ -3237,17 +3283,30 @@ function form_process_weight($element) { ...@@ -3237,17 +3283,30 @@ function form_process_weight($element) {
*/ */
function theme_file($variables) { function theme_file($variables) {
$element = $variables['element']; $element = $variables['element'];
$element['#attributes']['type'] = 'file';
$element['#attributes']['name'] = $element['#name'];
$element['#attributes']['id'] = $element['#id'];
if (!empty($element['#size'])) {
$element['#attributes']['size'] = $element['#size'];
}
_form_set_class($element, array('form-file')); _form_set_class($element, array('form-file'));
return '<input type="file" name="' . $element['#name'] . '"' . ($element['#attributes'] ? ' ' . drupal_attributes($element['#attributes']) : '') . ' id="' . $element['#id'] . '" size="' . $element['#size'] . "\" />\n";
return '<input' . drupal_attributes($element['#attributes']) . ' />';
} }
/** /**
* Returns HTML for a form element. * Returns HTML for a form element.
* *
* Each form element is wrapped in a DIV with #type and #name classes. In * Each form element is wrapped in a DIV container having the following CSS
* addition to the element itself, the div contains a label before or after * classes:
* the element based on the optional #title_display property. After the label * - form-item: Generic for all form elements.
* and fields this function outputs the optional element #description. * - form-type-#type: The internal element #type.
* - form-item-#name: The internal form element #name (usually derived from the
* $form structure and set via form_builder()).
* - form-disabled: Only set if the form element is #disabled.
*
* In addition to the element itself, the DIV contains a label for the element
* based on the optional #title_display property, and an optional #description.
* *
* The optional #title_display property can have these values: * The optional #title_display property can have these values:
* - before: The label is output before the element. This is the default. * - before: The label is output before the element. This is the default.
...@@ -3298,6 +3357,10 @@ function theme_form_element($variables) { ...@@ -3298,6 +3357,10 @@ function theme_form_element($variables) {
if (!empty($element['#name'])) { if (!empty($element['#name'])) {
$attributes['class'][] = 'form-item-' . strtr($element['#name'], array(' ' => '-', '_' => '-', '[' => '-', ']' => '')); $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"; $output = '<div' . drupal_attributes($attributes) . '>' . "\n";
// If #title is not set, we don't display any label or required marker. // If #title is not set, we don't display any label or required marker.
......
...@@ -335,7 +335,7 @@ class ProfileTestAutocomplete extends ProfileTestCase { ...@@ -335,7 +335,7 @@ class ProfileTestAutocomplete extends ProfileTestCase {
$this->setProfileField($field, $field['value']); $this->setProfileField($field, $field['value']);
// Set some html for what we want to see in the page output later. // Set some html for what we want to see in the page output later.
$autocomplete_html = '<input class="autocomplete" type="hidden" id="' . drupal_html_id('edit-' . $field['form_name'] . '-autocomplete') . '" value="' . url('profile/autocomplete/' . $field['fid'], array('absolute' => TRUE)) . '" disabled="disabled" />'; $autocomplete_html = '<input type="hidden" id="' . drupal_html_id('edit-' . $field['form_name'] . '-autocomplete') . '" value="' . url('profile/autocomplete/' . $field['fid'], array('absolute' => TRUE)) . '" disabled="disabled" class="autocomplete" />';
$field_html = '<input type="text" maxlength="255" name="' . $field['form_name'] . '" id="' . drupal_html_id('edit-' . $field['form_name']) . '" size="60" value="' . $field['value'] . '" class="form-text form-autocomplete required" />'; $field_html = '<input type="text" maxlength="255" name="' . $field['form_name'] . '" id="' . drupal_html_id('edit-' . $field['form_name']) . '" size="60" value="' . $field['value'] . '" class="form-text form-autocomplete required" />';
// Check that autocompletion html is found on the user's profile edit page. // Check that autocompletion html is found on the user's profile edit page.
......
...@@ -193,7 +193,7 @@ class FormsTestCase extends DrupalWebTestCase { ...@@ -193,7 +193,7 @@ class FormsTestCase extends DrupalWebTestCase {
// All the elements should be marked as disabled, including the ones below // All the elements should be marked as disabled, including the ones below
// the disabled container. // the disabled container.
$this->assertEqual(count($disabled_elements), 20, t('The correct elements have the disabled property in the HTML code.')); $this->assertEqual(count($disabled_elements), 32, t('The correct elements have the disabled property in the HTML code.'));
$this->drupalPost(NULL, $edit, t('Submit')); $this->drupalPost(NULL, $edit, t('Submit'));
$returned_values['hijacked'] = drupal_json_decode($this->content); $returned_values['hijacked'] = drupal_json_decode($this->content);
...@@ -211,7 +211,12 @@ class FormsTestCase extends DrupalWebTestCase { ...@@ -211,7 +211,12 @@ class FormsTestCase extends DrupalWebTestCase {
function assertFormValuesDefault($values, $form) { function assertFormValuesDefault($values, $form) {
foreach (element_children($form) as $key) { foreach (element_children($form) as $key) {
if (isset($form[$key]['#default_value'])) { if (isset($form[$key]['#default_value'])) {
$expected_value = $form[$key]['#default_value']; if (isset($form[$key]['#expected_value'])) {
$expected_value = $form[$key]['#expected_value'];
}
else {
$expected_value = $form[$key]['#default_value'];
}
if ($key == 'checkboxes_multiple') { if ($key == 'checkboxes_multiple') {
// Checkboxes values are not filtered out. // Checkboxes values are not filtered out.
...@@ -225,6 +230,65 @@ class FormsTestCase extends DrupalWebTestCase { ...@@ -225,6 +230,65 @@ class FormsTestCase extends DrupalWebTestCase {
} }
} }
/**
* Verify markup for disabled form elements.
*
* @see _form_test_disabled_elements()
*/
function testDisabledMarkup() {
$this->drupalGet('form-test/disabled-elements');
$form_state = array();
$form = _form_test_disabled_elements(array(), $form_state);
$type_map = array(
'textarea' => 'textarea',
'select' => 'select',
'weight' => 'select',
'date' => 'select',
);
foreach ($form as $name => $item) {
// Skip special #types.
if (!isset($item['#type']) || in_array($item['#type'], array('hidden', 'text_format'))) {
continue;
}
// Setup XPath and CSS class depending on #type.
if (in_array($item['#type'], array('image_button', 'button', 'submit'))) {
$path = "//!type[contains(@class, :div-class) and @value=:value]";
$class = 'form-button-disabled';
}
else {
// starts-with() required for checkboxes.
$path = "//div[contains(@class, :div-class)]/descendant::!type[starts-with(@name, :name)]";
$class = 'form-disabled';
}
// Replace DOM element name in $path according to #type.
$type = 'input';
if (isset($type_map[$item['#type']])) {
$type = $type_map[$item['#type']];
}
$path = strtr($path, array('!type' => $type));
// Verify that the element exists.
$element = $this->xpath($path, array(
':name' => check_plain($name),
':div-class' => $class,
':value' => isset($item['#value']) ? $item['#value'] : '',
));
$this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => $item['#type'])));
}
// Verify special element #type text-format.
$element = $this->xpath('//div[contains(@class, :div-class)]/descendant::textarea[@name=:name]', array(
':name' => 'text_format[value]',
':div-class' => 'form-disabled',
));
$this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => 'text_format[value]')));
$element = $this->xpath('//div[contains(@class, :div-class)]/descendant::select[@name=:name]', array(
':name' => 'text_format[format]',
':div-class' => 'form-disabled',
));
$this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => 'text_format[format]')));
}
/** /**
* Test Form API protections against input forgery. * Test Form API protections against input forgery.
* *
......
...@@ -882,6 +882,64 @@ function _form_test_disabled_elements($form, &$form_state) { ...@@ -882,6 +882,64 @@ function _form_test_disabled_elements($form, &$form_state) {
); );
} }
// Text format.
$form['text_format'] = array(
'#type' => 'text_format',
'#title' => 'Text format',
'#disabled' => TRUE,
'#default_value' => 'Text value',
'#format' => 1,
'#expected_value' => array(
'value' => 'Text value',
'format' => 1,
),
'#test_hijack_value' => array(
'value' => 'HIJACK',
'format' => 2,
),
);
// Password fields.
$form['password'] = array(
'#type' => 'password',
'#title' => 'Password',
'#disabled' => TRUE,
);
$form['password_confirm'] = array(
'#type' => 'password_confirm',
'#title' => 'Password confirm',
'#disabled' => TRUE,
);
// Files.
$form['file'] = array(
'#type' => 'file',
'#title' => 'File',
'#disabled' => TRUE,
);
$form['managed_file'] = array(
'#type' => 'managed_file',
'#title' => 'Managed file',
'#disabled' => TRUE,
);
// Buttons.
$form['image_button'] = array(
'#type' => 'image_button',
'#value' => 'Image button',
'#disabled' => TRUE,
);
$form['button'] = array(
'#type' => 'button',
'#value' => 'Button',
'#disabled' => TRUE,
);
$form['submit_disabled'] = array(
'#type' => 'submit',
'#value' => 'Submit',
'#disabled' => TRUE,
);
$form['submit'] = array( $form['submit'] = array(
'#type' => 'submit', '#type' => 'submit',
'#value' => t('Submit'), '#value' => t('Submit'),
......
...@@ -627,6 +627,13 @@ div.teaser-checkbox .form-item, ...@@ -627,6 +627,13 @@ div.teaser-checkbox .form-item,
.form-item label.option input { .form-item label.option input {
vertical-align: middle; vertical-align: middle;
} }
.form-disabled input.form-autocomplete,
.form-disabled input.form-text,
.form-disabled textarea.form-textarea,
.form-disabled select.form-select {
background-color: #eee;
color: #777;
}
/* Filter */ /* Filter */
.filter-wrapper { .filter-wrapper {
...@@ -699,6 +706,13 @@ input.form-submit:active { ...@@ -699,6 +706,13 @@ input.form-submit:active {
border-color: #555; border-color: #555;
text-shadow: #222 0px -1px 0px; text-shadow: #222 0px -1px 0px;
} }
input.form-button-disabled,
input.form-button-disabled:active {
background: #eee none;
border-color: #eee;
text-shadow: none;
color: #999;
}
form input#edit-delete { form input#edit-delete {
background: #eee; background: #eee;
border-color: #fff #ddd #ccc; border-color: #fff #ddd #ccc;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment