Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.174.2.13 diff -u -p -r1.174.2.13 form.inc --- includes/form.inc 27 Dec 2007 08:41:52 -0000 1.174.2.13 +++ includes/form.inc 6 Jun 2008 01:19:30 -0000 @@ -251,6 +251,14 @@ function drupal_process_form($form_id, & drupal_prepare_form($form_id, $form); if (($form['#programmed']) || (!empty($_POST) && (($_POST['form_id'] == $form_id)))) { drupal_validate_form($form_id, $form); + + // form_clean_id() maintains a cache of element IDs it has seen, + // so it can prevent duplicates. We want to be sure we reset that + // cache when a form is processed, so scenerios that result in + // the form being built behind the scenes and again for the + // browser don't increment all the element IDs needlessly. + form_clean_id(NULL, TRUE); + // 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. @@ -1430,7 +1438,7 @@ function theme_textfield($element) { function theme_form($element) { // Anonymous div to satisfy XHTML compliance. $action = $element['#action'] ? 'action="' . check_url($element['#action']) . '" ' : ''; - return '
\n
". $element['#children'] ."\n
\n"; + return '
\n
". $element['#children'] ."\n
\n"; } /** @@ -1585,15 +1593,43 @@ function _form_set_class(&$element, $cla } /** - * Remove invalid characters from an HTML ID attribute string. + * Prepare an HTML ID attribute string for a form item. + * + * Remove invalid characters and guarantee uniqueness. * * @param $id * The ID to clean. + * @param $flush + * If set to TRUE, the function will flush and reset the static array + * which is built to test the uniqueness of element IDs. This is only + * used if a form has completed the validation process. This parameter + * should never be set to TRUE if this function is being called to + * assign an ID to the #ID element. * @return * The cleaned ID. */ -function form_clean_id($id = NULL) { +function form_clean_id($id = NULL, $flush = FALSE) { + static $seen_ids = array(); + + if ($flush) { + $seen_ids = array(); + return; + } $id = str_replace(array('][', '_', ' '), '-', $id); + + // Ensure IDs are unique. The first occurrence is held but left alone. + // Subsequent occurrences get a number appended to them. This incrementing + // will almost certainly break code that relies on explicit HTML IDs in + // forms that appear more than once on the page, but the alternative is + // outputting duplicate IDs, which would break JS code and XHTML + // validity anyways. For now, it's an acceptable stopgap solution. + if (isset($seen_ids[$id])) { + $id = $id .'-'. $seen_ids[$id]++; + } + else { + $seen_ids[$id] = 1; + } + return $id; }