reverted: --- includes/common.inc 2010-10-03 20:17:25 +0000 +++ includes/common.inc 2010-10-03 05:11:16 +0000 @@ -5639,7 +5639,8 @@ if (is_int($property)) { $property = '#' . $attribute; } + // Do not overwrite already existing attributes. + if (isset($element[$property]) && !isset($element['#attributes'][$attribute])) { - if (isset($element[$property])) { $element['#attributes'][$attribute] = $element[$property]; } } diff -u includes/form.inc includes/form.inc --- includes/form.inc 2010-10-03 20:17:25 +0000 +++ includes/form.inc 2010-10-03 18:09:53 +0000 @@ -1162,6 +1162,19 @@ } } } + // Non-multiple select fields always have a value in HTML. If the user + // does not change the form, it will be the value of the first option. + // Because of this, form validation for the field will almost always + // pass, even if the user did not select anything. To work around this + // browser behavior, required select fields without a #default_value get + // an additional, first empty option. In case the submitted value is + // identical to the empty option's value, we reset the element's value + // to NULL to trigger the regular #required handling below. + // @see form_process_select() + elseif ($elements['#type'] == 'select' && !$elements['#multiple'] && $elements['#required'] && !isset($elements['#default_value']) && $elements['#value'] === $elements['#empty_value']) { + $elements['#value'] = NULL; + form_set_value($elements, NULL, $form_state); + } elseif (!isset($options[$elements['#value']])) { form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.')); watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR); @@ -1203,13 +1216,27 @@ } // Make sure a value is passed when the field is required. - // 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 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']))); + if (isset($elements['#needs_validation']) && $elements['#required']) { + // 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 integer 0, different than string + // '0', which could be a valid value. + $is_empty_multiple = (!count($elements['#value'])); + $is_empty_string = (is_string($elements['#value']) && drupal_strlen(trim($elements['#value'])) == 0); + $is_empty_value = ($elements['#value'] === 0); + if ($is_empty_multiple || $is_empty_string || $is_empty_value) { + // Although discouraged, a #title is not mandatory for form elements. In + // case there is no #title, we cannot set a form error message. + // Instead of setting no #title, form constructors are encouraged to set + // #title_display to 'invisible' to improve accessibility. + if (isset($elements['#title'])) { + form_error($elements, $t('!name field is required.', array('!name' => $elements['#title']))); + } + else { + form_error($elements); + } + } } // Call user-defined form level validators. @@ -2138,6 +2165,15 @@ return (isset($element['#default_value']) && is_array($element['#default_value'])) ? $element['#default_value'] : array(); } } + // Non-multiple select elements may have an empty option preprended to them + // (see form_process_select()). When this occurs, usually #empty_value is + // an empty string, but some forms set #empty_value to integer 0 or some + // other non-string constant. PHP receives all submitted form input as + // strings, but if the empty option is selected, set the value to match the + // empty value exactly. + elseif (isset($element['#empty_value']) && $input === (string) $element['#empty_value']) { + return $element['#empty_value']; + } else { return $input; } @@ -2258,6 +2294,72 @@ } /** + * Processes a select list form element. + * + * This process callback is mandatory for select fields, since all user agents + * automatically preselect the first available option of single (non-multiple) + * select lists. + * + * @param $element + * The form element to process. Properties used: + * - #multiple: (optional) Indicates whether one or more options can be + * selected. Defaults to FALSE. + * - #default_value: Must be NULL or not set in case there is no value for the + * element yet, in which case a first default option is inserted by default. + * Whether this first option is a valid option depends on whether the field + * is #required or not. + * - #required: (optional) Whether the user needs to select an option (TRUE) + * or not (FALSE). Defaults to FALSE. + * - #empty_option: (optional) The label to show for the first default option. + * By default, the label is automatically set to "- Please select -" for a + * required field and "- None -" for an optional field. + * - #empty_value: (optional) The value for the first default option, which is + * used to determine whether the user submitted a value or not. + * - If #required is TRUE, this defaults to '' (an empty string). + * - If #required is not TRUE and this value isn't set, then no extra option + * is added to the select control, leaving the control in a slightly + * illogical state, because there's no way for the user to select nothing, + * since all user agents automatically preselect the first available + * option. But people are used to this being the behavior of select + * controls. + * @todo Address the above issue in Drupal 8. + * - If #required is not TRUE and this value is set (most commonly to an + * empty string), then an extra option (see #empty_option above) + * representing a "non-selection" is added with this as its value. + * + * @see _form_validate() + */ +function form_process_select($element) { + // #multiple select fields need a special #name. + if ($element['#multiple']) { + $element['#attributes']['multiple'] = 'multiple'; + $element['#attributes']['name'] = $element['#name'] . '[]'; + } + // A non-#multiple select needs special handling to prevent user agents from + // preselecting the first option without intention. #multiple select lists do + // not get an empty option, as it would not make sense, user interface-wise. + else { + $required = $element['#required']; + // If the element is required and there is no #default_value, then add an + // empty option that will fail validation, so that the user is required to + // make a choice. Also, if there's a value for #empty_value or + // #empty_option, then add an option that represents emptiness. + if (($required && !isset($element['#default_value'])) || isset($element['#empty_value']) || isset($element['#empty_option'])) { + $element += array( + '#empty_value' => '', + '#empty_option' => $required ? t('- Select - ') : t('- None -'), + ); + // The empty option is prepended to #options and purposively not merged + // to prevent another option in #options mistakenly using the same value + // as #empty_value. + $empty_option = array($element['#empty_value'] => $element['#empty_option']); + $element['#options'] = $empty_option + $element['#options']; + } + } + return $element; +} + +/** * Returns HTML for a select form element. * * It is possible to group options together; to do this, change the format of @@ -2277,11 +2379,6 @@ element_set_attributes($element, array('id', 'name', 'size')); _form_set_class($element, array('form-select')); - if (!empty($element['#multiple'])) { - $element['#attributes']['multiple'] = 'multiple'; - $element['#attributes']['name'] .= '[]'; - } - return '' . form_select_options($element) . ''; } diff -u includes/install.core.inc includes/install.core.inc --- includes/install.core.inc 2010-10-03 20:17:25 +0000 +++ includes/install.core.inc 2010-10-03 18:01:56 +0000 @@ -1756,11 +1756,10 @@ ); $countries = country_get_list(); - $countries = array_merge(array('' => st('No default country')), $countries); $form['server_settings']['site_default_country'] = array( '#type' => 'select', '#title' => t('Default country'), - '#default_value' => variable_get('site_default_country', ''), + '#default_value' => variable_get('site_default_country', NULL), '#options' => $countries, '#description' => st('Select the default country for the site.'), '#weight' => 0, diff -u modules/aggregator/aggregator.processor.inc modules/aggregator/aggregator.processor.inc --- modules/aggregator/aggregator.processor.inc 2010-10-03 20:17:25 +0000 +++ modules/aggregator/aggregator.processor.inc 2010-10-03 18:01:56 +0000 @@ -70,7 +70,7 @@ function aggregator_form_aggregator_admin_form_alter(&$form, $form_state) { if (in_array('aggregator', variable_get('aggregator_processors', array('aggregator')))) { $info = module_invoke('aggregator', 'aggregator_process', 'info'); - $items = array(0 => t('none')) + drupal_map_assoc(array(3, 5, 10, 15, 20, 25), '_aggregator_items'); + $items = drupal_map_assoc(array(3, 5, 10, 15, 20, 25), '_aggregator_items'); $period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval'); $period[AGGREGATOR_CLEAR_NEVER] = t('Never'); @@ -90,8 +90,9 @@ $form['modules']['aggregator']['aggregator_summary_items'] = array( '#type' => 'select', - '#title' => t('Number of items shown in listing pages') , + '#title' => t('Number of items shown in listing pages'), '#default_value' => variable_get('aggregator_summary_items', 3), + '#empty_value' => 0, '#options' => $items, ); diff -u modules/block/block.admin.inc modules/block/block.admin.inc --- modules/block/block.admin.inc 2010-10-03 20:17:25 +0000 +++ modules/block/block.admin.inc 2010-10-03 18:01:56 +0000 @@ -85,9 +85,6 @@ $block_regions = system_region_list($theme, REGIONS_VISIBLE); } - // We always add a region for disabled blocks. - $block_regions += array(BLOCK_REGION_NONE => '<' . t('none') . '>'); - // Weights range from -delta to +delta, so delta should be at least half // of the amount of blocks present. This makes sure all blocks in the same // region get an unique weight. @@ -100,7 +97,8 @@ ); $form['block_regions'] = array( '#type' => 'value', - '#value' => $block_regions, + // Add a last region for disabled blocks. + '#value' => $block_regions + array(BLOCK_REGION_NONE => BLOCK_REGION_NONE), ); $form['blocks'] = array(); $form['#tree'] = TRUE; @@ -127,13 +125,14 @@ '#default_value' => $block['weight'], '#delta' => $weight_delta, '#title_display' => 'invisible', - '#title' => t('Weight for @block block', array('%block' => $block['info'])), + '#title' => t('Weight for @block block', array('@block' => $block['info'])), ); $form['blocks'][$key]['region'] = array( '#type' => 'select', - '#default_value' => $block['region'], + '#default_value' => $block['region'] != BLOCK_REGION_NONE ? $block['region'] : NULL, + '#empty_value' => BLOCK_REGION_NONE, '#title_display' => 'invisible', - '#title' => t('Region for @block block', array('%block' => $block['info'])), + '#title' => t('Region for @block block', array('@block' => $block['info'])), '#options' => $block_regions, ); $form['blocks'][$key]['configure'] = array( @@ -150,7 +149,7 @@ } } // Do not allow disabling the main system content block. - unset($form['blocks']['system_main']['region']['#options'][BLOCK_REGION_NONE]); + $form['blocks']['system_main']['region']['#required'] = TRUE; $form['actions'] = array( '#tree' => FALSE, @@ -302,9 +301,9 @@ $form['regions'][$key] = array( '#type' => 'select', '#title' => $theme->info['name'], - '#default_value' => (!empty($region) ? $region : BLOCK_REGION_NONE), - '#options' => array(BLOCK_REGION_NONE => t('')) + system_region_list($key, REGIONS_VISIBLE), - '#expandable' => ($key !== $theme_default), + '#default_value' => !empty($region) && $region != -1 ? $region : NULL, + '#empty_value' => BLOCK_REGION_NONE, + '#options' => system_region_list($key, REGIONS_VISIBLE), '#weight' => ($key == $theme_default ? 9 : 10), ); } @@ -657,7 +656,7 @@ $block = &$variables['form']['blocks'][$i]; // Fetch the region for the current block. - $region = $block['region']['#default_value']; + $region = (isset($block['region']['#default_value']) ? $block['region']['#default_value'] : BLOCK_REGION_NONE); // Set special classes needed for table drag and drop. $block['region']['#attributes']['class'] = array('block-region-select', 'block-region-' . $region); diff -u modules/field_ui/field_ui.admin.inc modules/field_ui/field_ui.admin.inc --- modules/field_ui/field_ui.admin.inc 2010-10-03 20:17:25 +0000 +++ modules/field_ui/field_ui.admin.inc 2010-10-03 18:01:56 +0000 @@ -304,7 +304,7 @@ t('Widget'), array('data' => t('Operations'), 'colspan' => 2), ), - '#parent_options' => array('' => t('')), + '#parent_options' => array(), '#regions' => array( 'main' => array(), 'add_new' => array('title' => ' '), @@ -432,8 +432,6 @@ $field_type_options = field_ui_field_type_options(); $widget_type_options = field_ui_widget_type_options(NULL, TRUE); if ($field_type_options && $widget_type_options) { - array_unshift($field_type_options, t('- Select a field type -')); - array_unshift($widget_type_options, t('- Select a widget -')); $name = '_add_new_field'; $table[$name] = array( '#attributes' => array('class' => array('draggable', 'tabledrag-leaf', 'add-new')), @@ -482,6 +480,7 @@ 'type' => array( '#type' => 'select', '#options' => $field_type_options, + '#empty_option' => t('- Select a field type -'), '#description' => t('Type of data to store.'), '#attributes' => array('class' => array('field-type-select')), '#prefix' => '
 
', @@ -489,6 +488,7 @@ 'widget_type' => array( '#type' => 'select', '#options' => $widget_type_options, + '#empty_option' => t('- Select a widget -'), '#description' => t('Form element to edit the data.'), '#attributes' => array('class' => array('widget-type-select')), '#cell_attributes' => array('colspan' => 3), @@ -500,7 +500,6 @@ // Additional row: add existing field. $existing_field_options = field_ui_existing_field_options($entity_type, $bundle); if ($existing_field_options && $widget_type_options) { - array_unshift($existing_field_options, t('- Select an existing field -')); $name = '_add_existing_field'; $table[$name] = array( '#attributes' => array('class' => array('draggable', 'tabledrag-leaf', 'add-new')), @@ -540,6 +539,7 @@ 'field_name' => array( '#type' => 'select', '#options' => $existing_field_options, + '#empty_option' => t('- Select an existing field -'), '#description' => t('Field to share'), '#attributes' => array('class' => array('field-select')), '#cell_attributes' => array('colspan' => 2), @@ -548,6 +548,7 @@ 'widget_type' => array( '#type' => 'select', '#options' => $widget_type_options, + '#empty_option' => t('- Select a widget -'), '#description' => t('Form element to edit the data.'), '#attributes' => array('class' => array('widget-type-select')), '#cell_attributes' => array('colspan' => 3), @@ -844,7 +845,7 @@ 'visible' => array('message' => t('No field is displayed.')), 'hidden' => array('title' => t('Hidden'), 'message' => t('No field is hidden.')), ), - '#parent_options' => array('' => t('')), + '#parent_options' => array(), '#attributes' => array( 'class' => array('field-ui-overview'), 'id' => 'field-display-overview', @@ -889,7 +890,6 @@ 'parent' => array( '#type' => 'select', '#options' => $table['#parent_options'], - '#default_value' => '', '#attributes' => array('class' => array('field-parent')), '#parents' => array('fields', $name, 'parent'), ), @@ -1043,7 +1043,6 @@ 'parent' => array( '#type' => 'select', '#options' => $table['#parent_options'], - '#default_value' => '', '#attributes' => array('class' => array('field-parent')), '#parents' => array('fields', $name, 'parent'), ), @@ -1704,7 +1703,6 @@ '#default_value' => !empty($instance['description']) ? $instance['description'] : '', '#rows' => 5, '#description' => t('Instructions to present to the user below this field on the editing form.
Allowed HTML tags: @tags', array('@tags' => _field_filter_xss_display_allowed_tags())), - '#required' => FALSE, '#weight' => 0, ); diff -u modules/image/image.admin.inc modules/image/image.admin.inc --- modules/image/image.admin.inc 2010-10-03 20:17:25 +0000 +++ modules/image/image.admin.inc 2010-10-03 18:01:56 +0000 @@ -116,7 +116,7 @@ } // Build the new image effect addition form and add it to the effect list. - $new_effect_options = array('' => t('Select a new effect')); + $new_effect_options = array(); foreach (image_effect_definitions() as $effect => $definition) { $new_effect_options[$effect] = check_plain($definition['label']); } @@ -128,6 +128,7 @@ $form['effects']['new']['new'] = array( '#type' => 'select', '#options' => $new_effect_options, + '#empty_option' => t('Select a new effect'), ); $form['effects']['new']['weight'] = array( '#type' => 'weight', @@ -290,11 +291,11 @@ $form_state['image_style'] = $style; $replacement_styles = array_diff_key(image_style_options(), array($style['name'] => '')); - $replacement_styles[''] = t('No replacement, just delete'); $form['replacement'] = array( '#title' => t('Replacement style'), '#type' => 'select', '#options' => $replacement_styles, + '#empty_option' => t('No replacement, just delete'), ); return confirm_form( diff -u modules/image/image.field.inc modules/image/image.field.inc --- modules/image/image.field.inc 2010-10-03 20:17:25 +0000 +++ modules/image/image.field.inc 2010-10-03 18:01:56 +0000 @@ -269,7 +269,7 @@ $form['preview_image_style'] = array( '#title' => t('Preview image style'), '#type' => 'select', - '#options' => array('' => '<' . t('no preview') . '>') + image_style_options(FALSE), + '#options' => image_style_options(FALSE), '#default_value' => $settings['preview_image_style'], '#description' => t('The preview image will be shown while editing the content.'), '#weight' => 15, @@ -418,16 +418,16 @@ $display = $instance['display'][$view_mode]; $settings = $display['settings']; - $image_styles = array('' => t('None (original image)')) + image_style_options(FALSE); + $image_styles = image_style_options(FALSE); $form['image_style'] = array( '#title' => t('Image style'), '#type' => 'select', '#default_value' => $settings['image_style'], + '#empty_option' => t('None (original image)'), '#options' => $image_styles, ); $link_types = array( - '' => t(''), 'content' => t('Content'), 'file' => t('File'), ); diff -u modules/locale/locale.admin.inc modules/locale/locale.admin.inc --- modules/locale/locale.admin.inc 2010-10-03 20:17:25 +0000 +++ modules/locale/locale.admin.inc 2010-10-03 18:01:56 +0000 @@ -854,7 +854,8 @@ $form['filters']['status'][$key] = array( '#title' => $filter['title'], '#type' => 'select', - '#multiple' => FALSE, + '#empty_value' => 'all', + '#empty_option' => $filter['options']['all'], '#size' => 0, '#options' => $filter['options'], ); diff -u modules/menu/menu.admin.inc modules/menu/menu.admin.inc --- modules/menu/menu.admin.inc 2010-10-03 20:17:25 +0000 +++ modules/menu/menu.admin.inc 2010-10-03 18:01:56 +0000 @@ -680,22 +680,22 @@ $menu_options = menu_get_menus(); $main = variable_get('menu_main_links_source', 'main-menu'); - $main_options = array_merge($menu_options, array('' => t('No Main links'))); $form['menu_main_links_source'] = array( '#type' => 'select', '#title' => t('Source for the Main links'), '#default_value' => variable_get('menu_main_links_source', 'main-menu'), - '#options' => $main_options, + '#empty_option' => t('No Main links'), + '#options' => $menu_options, '#tree' => FALSE, '#description' => t('Select what should be displayed as the Main links (typically at the top of the page).'), ); - $secondary_options = array_merge($menu_options, array('' => t('No Secondary links'))); $form['menu_secondary_links_source'] = array( '#type' => 'select', '#title' => t('Source for the Secondary links'), '#default_value' => variable_get('menu_secondary_links_source', 'user-menu'), - '#options' => $secondary_options, + '#empty_option' => t('No Secondary links'), + '#options' => $menu_options, '#tree' => FALSE, '#description' => t("Select the source for the Secondary links. An advanced option allows you to use the same source for both Main links (currently %main) and Secondary links: if your source menu has two levels of hierarchy, the top level menu links will appear in the Main links, and the children of the active link will appear in the Secondary links." , array('%main' => $main_options[$main])), ); diff -u modules/simpletest/tests/form.test modules/simpletest/tests/form.test --- modules/simpletest/tests/form.test 2010-10-03 20:17:25 +0000 +++ modules/simpletest/tests/form.test 2010-10-03 18:08:27 +0000 @@ -153,6 +153,70 @@ } /** + * Tests validation of #type 'select' elements. + */ + function testSelect() { + $form = $form_state = array(); + $form = form_test_select($form, $form_state); + $error = '!name field is required.'; + $this->drupalGet('form-test/select'); + + // Posting without any values should throw validation errors. + $this->drupalPost(NULL, array(), 'Submit'); + $this->assertNoText(t($error, array('!name' => $form['select']['#title']))); + $this->assertNoText(t($error, array('!name' => $form['select_required']['#title']))); + $this->assertNoText(t($error, array('!name' => $form['select_optional']['#title']))); + $this->assertNoText(t($error, array('!name' => $form['empty_value']['#title']))); + $this->assertNoText(t($error, array('!name' => $form['empty_value_one']['#title']))); + $this->assertText(t($error, array('!name' => $form['no_default']['#title']))); + $this->assertNoText(t($error, array('!name' => $form['no_default_optional']['#title']))); + $this->assertText(t($error, array('!name' => $form['no_default_empty_option']['#title']))); + $this->assertNoText(t($error, array('!name' => $form['no_default_empty_option_optional']['#title']))); + $this->assertText(t($error, array('!name' => $form['no_default_empty_value']['#title']))); + $this->assertText(t($error, array('!name' => $form['no_default_empty_value_one']['#title']))); + $this->assertNoText(t($error, array('!name' => $form['no_default_empty_value_optional']['#title']))); + $this->assertNoText(t($error, array('!name' => $form['multiple']['#title']))); + $this->assertNoText(t($error, array('!name' => $form['multiple_no_default']['#title']))); + $this->assertText(t($error, array('!name' => $form['multiple_no_default_required']['#title']))); + + // Post values for required fields. + $edit = array( + 'no_default' => 'three', + 'no_default_empty_option' => 'three', + 'no_default_empty_value' => 'three', + 'no_default_empty_value_one' => 'three', + 'multiple_no_default_required[]' => 'three', + ); + $this->drupalPost(NULL, $edit, 'Submit'); + $values = drupal_json_decode($this->drupalGetContent()); + + // Verify expected values. + $expected = array( + 'select' => 'one', + 'empty_value' => 'one', + 'empty_value_one' => 'one', + 'no_default' => 'three', + 'no_default_optional' => 'one', + 'no_default_optional_empty_value' => '', + 'no_default_empty_option' => 'three', + 'no_default_empty_option_optional' => '', + 'no_default_empty_value' => 'three', + 'no_default_empty_value_one' => 'three', + 'no_default_empty_value_optional' => 0, + 'multiple' => array('two' => 'two'), + 'multiple_no_default' => array(), + 'multiple_no_default_required' => array('three' => 'three'), + ); + foreach ($expected as $key => $value) { + $this->assertIdentical($values[$key], $value, t('@name: @actual is equal to @expected.', array( + '@name' => $key, + '@actual' => var_export($values[$key], TRUE), + '@expected' => var_export($value, TRUE), + ))); + } + } + + /** * Test handling of disabled elements. * * @see _form_test_disabled_elements() diff -u modules/simpletest/tests/form_test.module modules/simpletest/tests/form_test.module --- modules/simpletest/tests/form_test.module 2010-10-03 20:17:25 +0000 +++ modules/simpletest/tests/form_test.module 2010-10-03 18:07:00 +0000 @@ -92,6 +92,12 @@ 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); + $items['form-test/select'] = array( + 'title' => t('Select'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_select'), + 'access callback' => TRUE, + ); $items['form-test/disabled-elements'] = array( 'title' => t('Form test'), @@ -771,6 +777,115 @@ } /** + * Builds a form to test #type 'select' validation. + */ +function form_test_select($form, &$form_state) { + $base = array( + '#type' => 'select', + '#options' => drupal_map_assoc(array('one', 'two', 'three')), + ); + + $form['select'] = $base + array( + '#title' => '#default_value one', + '#default_value' => 'one', + ); + $form['select_required'] = $base + array( + '#title' => '#default_value one, #required', + '#required' => TRUE, + '#default_value' => 'one', + ); + $form['select_optional'] = $base + array( + '#title' => '#default_value one, not #required', + '#required' => FALSE, + '#default_value' => 'one', + ); + $form['empty_value'] = $base + array( + '#title' => '#default_value one, #required, #empty_value 0', + '#required' => TRUE, + '#default_value' => 'one', + '#empty_value' => 0, + ); + $form['empty_value_one'] = $base + array( + '#title' => '#default_value = #empty_value, #required', + '#required' => TRUE, + '#default_value' => 'one', + '#empty_value' => 'one', + ); + + $form['no_default'] = $base + array( + '#title' => 'No #default_value, #required', + '#required' => TRUE, + ); + $form['no_default_optional'] = $base + array( + '#title' => 'No #default_value, not #required', + '#required' => FALSE, + '#description' => 'Should result in "one", because it is not required and there is no #empty_value requested, so default browser behavior of preselecting first option is in effect.', + ); + $form['no_default_optional_empty_value'] = $base + array( + '#title' => 'No #default_value, not #required, #empty_value empty string', + '#empty_value' => '', + '#required' => FALSE, + '#description' => 'Should result in an empty string (due to #empty_value), because it is optional.', + ); + + $form['no_default_empty_option'] = $base + array( + '#title' => 'No #default_value, #required, #empty_option', + '#required' => TRUE, + '#empty_option' => '- Choose -', + ); + $form['no_default_empty_option_optional'] = $base + array( + '#title' => 'No #default_value, not #required, #empty_option', + '#empty_option' => '- Dismiss -', + '#description' => 'Should result in an empty string (default of #empty_value), because it is optional.', + ); + + $form['no_default_empty_value'] = $base + array( + '#title' => 'No #default_value, #required, #empty_value 0', + '#required' => TRUE, + '#empty_value' => 0, + '#description' => 'Should never result in 0.', + ); + $form['no_default_empty_value_one'] = $base + array( + '#title' => 'No #default_value, #required, #empty_value one', + '#required' => TRUE, + '#empty_value' => 'one', + '#description' => 'A mistakenly assigned #empty_value contained in #options should not be valid.', + ); + $form['no_default_empty_value_optional'] = $base + array( + '#title' => 'No #default_value, not #required, #empty_value 0', + '#required' => FALSE, + '#empty_value' => 0, + '#description' => 'Should result in 0, because it is optional.', + ); + + $form['multiple'] = $base + array( + '#title' => '#multiple, #default_value two', + '#default_value' => array('two'), + '#multiple' => TRUE, + ); + $form['multiple_no_default'] = $base + array( + '#title' => '#multiple, no #default_value', + '#multiple' => TRUE, + ); + $form['multiple_no_default_required'] = $base + array( + '#title' => '#multiple, #required, no #default_value', + '#required' => TRUE, + '#multiple' => TRUE, + ); + + $form['submit'] = array('#type' => 'submit', '#value' => 'Submit'); + return $form; +} + +/** + * Form submit handler for form_test_select(). + */ +function form_test_select_submit($form, &$form_state) { + drupal_json_output($form_state['values']); + exit(); +} + +/** * Build a form to test disabled elements. */ function _form_test_disabled_elements($form, &$form_state) { reverted: --- modules/system/system.admin.inc 2010-10-03 20:17:25 +0000 +++ modules/system/system.admin.inc 2010-10-03 00:47:51 +0000 @@ -2868,7 +2868,7 @@ actions_synchronize(); $actions = actions_list(); $actions_map = actions_actions_map($actions); + $options = array(); - $options = array(t('Choose an advanced action')); $unconfigurable = array(); foreach ($actions_map as $key => $array) { @@ -2940,9 +2940,7 @@ ); $form['parent']['action'] = array( '#type' => 'select', - '#default_value' => '', '#options' => $options, - '#description' => '', ); $form['parent']['actions'] = array('#type' => 'actions'); $form['parent']['actions']['submit'] = array( diff -u modules/system/system.module modules/system/system.module --- modules/system/system.module 2010-10-03 20:17:25 +0000 +++ modules/system/system.module 2010-10-03 18:01:56 +0000 @@ -399,9 +399,8 @@ ); $types['select'] = array( '#input' => TRUE, - '#size' => 0, '#multiple' => FALSE, - '#process' => array('ajax_process_form'), + '#process' => array('form_process_select', 'ajax_process_form'), '#theme' => 'select', '#theme_wrappers' => array('form_element'), ); reverted: --- modules/trigger/trigger.admin.inc 2010-10-03 20:17:25 +0000 +++ modules/trigger/trigger.admin.inc 2010-09-24 21:36:22 +0000 @@ -177,7 +177,6 @@ ); // List possible actions that may be assigned. if (count($options) != 0) { - array_unshift($options, t('Choose an action')); $form[$hook]['parent']['aid'] = array( '#type' => 'select', '#options' => $options, only in patch2: unchanged: --- modules/filter/filter.module 2010-09-29 20:12:51 +0000 +++ modules/filter/filter.module 2010-10-03 18:01:56 +0000 @@ -827,6 +827,7 @@ function filter_process_format($element) $element['format']['format'] = array( '#type' => 'select', '#title' => t('Text format'), + '#required' => TRUE, '#options' => $options, '#default_value' => $element['#format'], '#access' => count($formats) > 1,