diff --git a/core/includes/common.inc b/core/includes/common.inc index 08ce2ab..55661d9 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -7031,6 +7031,9 @@ function drupal_common_theme() { 'form' => array( 'render element' => 'element', ), + 'form_error_message' => array( + 'render element' => 'element', + ), 'textarea' => array( 'render element' => 'element', ), diff --git a/core/includes/form.inc b/core/includes/form.inc index b5a13c5..e934c80 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -858,7 +858,7 @@ function drupal_process_form($form_id, &$form, &$form_state) { // browser don't increment all the element IDs needlessly. drupal_static_reset('drupal_html_id'); - if ($form_state['submitted'] && !form_get_errors() && !$form_state['rebuild']) { + if ($form_state['submitted'] && !$form_state['rebuild'] && !form_get_errors()) { // Execute form submit handlers. form_execute_handlers('submit', $form, $form_state); @@ -902,9 +902,16 @@ function drupal_process_form($form_id, &$form, &$form_state) { // Set a flag to indicate the the form has been processed and executed. $form_state['executed'] = TRUE; + // Occassionally a form will set an error in its submit handler so we make + // sure to display those errors here before the form is redirected. + form_display_errors($form); + // Redirect the form based on values in $form_state. drupal_redirect_form($form_state); } + else { + form_display_errors($form); + } // Don't rebuild or cache form submissions invoked via drupal_form_submit(). if (!empty($form_state['programmed'])) { @@ -948,6 +955,33 @@ function drupal_process_form($form_id, &$form, &$form_state) { } /** + * Displays the given form's errors. + * + * @param $form + * An associative array containing the structure of the form. + */ +function form_display_errors($form) { + if ($errors = form_get_errors()) { + $error_links = array(); + foreach ($errors as $key => $error) { + $element = form_get_element($key, $form); + if ($element) { + $title = isset($element['#title']) ? $element['#title'] : $key; + $error_links[] = l($element['#title'], '', array('fragment' => 'edit-' . str_replace('_', '-', $key), 'external' => TRUE)); + } + else { + drupal_set_message($error, 'error'); + unset($errors[$key]); + } + } + + if (!empty($error_links)) { + drupal_set_message(format_plural(count($error_links), '1 error has been found', '@count errors have been found') . ': ' . implode(', ', $error_links), 'error'); + } + } +} + +/** * Prepares a structured form array. * * Adds required elements, executes any hook_form_alter functions, and @@ -1163,6 +1197,7 @@ function drupal_validate_form($form_id, &$form, &$form_state) { $values[$name] = $button_value; } } + $form_state['values'] = $values; } } @@ -1587,9 +1622,6 @@ function form_set_error($name = NULL, $message = '', $limit_validation_errors = } if ($record) { $form[$name] = $message; - if ($message) { - drupal_set_message($message, 'error'); - } } } @@ -1622,11 +1654,13 @@ function form_get_errors() { function form_get_error($element) { $form = form_set_error(); $parents = array(); - foreach ($element['#parents'] as $parent) { - $parents[] = $parent; - $key = implode('][', $parents); - if (isset($form[$key])) { - return $form[$key]; + if (isset($element['#parents'])) { + foreach ($element['#parents'] as $parent) { + $parents[] = $parent; + $key = implode('][', $parents); + if (isset($form[$key])) { + return $form[$key]; + } } } } @@ -1921,6 +1955,37 @@ function form_builder($form_id, &$element, &$form_state) { } /** + * Given a form and an element key, this function returns the element no matter + * how deep within the form array the key exists. If the key is not found an + * empty array is returned. + * + * @param string $element_key - The key to search for. + * + * @param array $form - A structured form array to search. + * + * @return Array + */ +function form_get_element($element_key, $form) { + $element = array(); + foreach (element_children($form) as $key) { + if ($key === $element_key) { + $element = $form[$key]; + break; + } + else { + if (is_array($form[$key])) { + $element = form_get_element($element_key, $form[$key]); + if (!empty($element)) { + break; + } + } + } + } + + return $element; +} + +/** * Adds the #name and #value properties of an input element before rendering. */ function _form_builder_handle_input_element($form_id, &$element, &$form_state) { @@ -4266,6 +4331,11 @@ function theme_form_element($variables) { '#title_display' => 'before', ); + // @todo: figure out a better place & better way to grab this error so that it + // a) doesnt need this parent check + // b) happens before the theme layer + $variables['errors'] = form_get_error($element); + // Add element #id for #type 'item'. if (isset($element['#markup']) && !empty($element['#id'])) { $attributes['id'] = $element['#id']; @@ -4282,8 +4352,17 @@ function theme_form_element($variables) { if (!empty($element['#attributes']['disabled'])) { $attributes['class'][] = 'form-disabled'; } + // Add a class if an error exists. + if (!empty($variables['errors'])) { + $attributes['class'][] = 'form-error'; + } $output = '' . "\n"; + // Display any error messages. + if ($variables['errors']) { + $output .= ' ' . theme('form_error_message', $variables); + } + // If #title is not set, we don't display any label or required marker. if (!isset($element['#title'])) { $element['#title_display'] = 'none'; @@ -4399,6 +4478,26 @@ function theme_form_element_label($variables) { } /** + * Returns HTML for an inline error associated with a specific form element. + * + * @param $variables + * An associative array containing: + * - element: An associative array containing the properties of the element. + * Properties used: error. + * - errors: The errors associated with the current element as returned by + * form_get_error($element). + * + * @ingroup themeable + */ +function theme_form_error_message($variables) { + $output = '
'; + $output .= '' . t('Error') . ': ' . $variables['errors'] . ''; + $output .= '
'; + + return $output; +} + +/** * Sets a form element's class attribute. * * Adds 'required' and 'error' classes as needed. diff --git a/core/modules/system/system.theme-rtl.css b/core/modules/system/system.theme-rtl.css index 03c93f3..a8521b8 100644 --- a/core/modules/system/system.theme-rtl.css +++ b/core/modules/system/system.theme-rtl.css @@ -39,6 +39,14 @@ th { } /** + * Markup generated by Form API. + */ +div.form-error-message { + background-position: right top; + padding-right: 30px; +} + +/** * Collapsible fieldsets. */ .js fieldset.collapsible > legend .fieldset-legend { diff --git a/core/modules/system/system.theme.css b/core/modules/system/system.theme.css index d5f41e1..af79ad3 100644 --- a/core/modules/system/system.theme.css +++ b/core/modules/system/system.theme.css @@ -75,6 +75,18 @@ td.active { /** * Markup generated by Form API. */ +div.form-error { + background-color: #fef5f1; + border: 1px solid #ed541d; + color: #8c2e0b; + padding: 10px; +} +div.form-error-message { + background: no-repeat left top url(../../misc/message-24-error.png); /* LTR */ + margin-bottom: 10px; + min-height: 25px; + padding-left: 30px; /* LTR */ +} .form-item, .form-actions { margin-top: 1em;