diff --git a/core/includes/form.inc b/core/includes/form.inc
index a12fb34..2b40109 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -1727,6 +1727,7 @@ function form_builder($form_id, &$element, &$form_state) {
'#required' => FALSE,
'#attributes' => array(),
'#title_display' => 'before',
+ '#description_position' => 'after',
);
// Special handling if we're on the top level form element.
@@ -3837,7 +3838,9 @@ function theme_file($variables) {
* - 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.
+ * based on the optional #title_display property, and an optional #description,
+ * that can be output before or after the field depending on the
+ * #description_position property.
*
* The optional #title_display property can have these values:
* - before: The label is output before the element. This is the default.
@@ -3863,11 +3866,19 @@ function theme_file($variables) {
* but the parent element should have neither. Use this carefully because a
* field without an associated label can cause accessibility challenges.
*
+ * The optional #description_position property can have these values:
+ * - before: The description is output before the element.
+ * - after: The description is output after the element.
+ *
+ * If the #description property is not set, the description will not be output,
+ * regardless of the #description_position value.
+ * The default value of #description_position is 'after'.
+ *
* @param $variables
* An associative array containing:
* - element: An associative array containing the properties of the element.
- * Properties used: #title, #title_display, #description, #id, #required,
- * #children, #type, #name.
+ * Properties used: #title, #title_display, #description, #description_position,
+ * #id, #required, #children, #type, #name.
*
* @ingroup themeable
*/
@@ -3880,6 +3891,7 @@ function theme_form_element($variables) {
// may not necessarily have been processed by form_builder().
$element += array(
'#title_display' => 'before',
+ '#description_position' => 'after',
);
// Add element #id for #type 'item'.
@@ -3911,10 +3923,16 @@ function theme_form_element($variables) {
case 'before':
case 'invisible':
$output .= ' ' . theme('form_element_label', $variables);
+ if (!empty($element['#description']) && $element['#description_position'] == 'before') {
+ $output .= '
' . $element['#description'] . "
\n";
+ }
$output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
break;
case 'after':
+ if (!empty($element['#description']) && $element['#description_position'] == 'before') {
+ $output .= '' . $element['#description'] . "
\n";
+ }
$output .= ' ' . $prefix . $element['#children'] . $suffix;
$output .= ' ' . theme('form_element_label', $variables) . "\n";
break;
@@ -3926,7 +3944,7 @@ function theme_form_element($variables) {
break;
}
- if (!empty($element['#description'])) {
+ if (!empty($element['#description']) && $element['#description_position'] == 'after') {
$output .= '' . $element['#description'] . "
\n";
}
diff --git a/core/modules/simpletest/tests/form.test b/core/modules/simpletest/tests/form.test
index 70ebf86..ff3b6d8 100644
--- a/core/modules/simpletest/tests/form.test
+++ b/core/modules/simpletest/tests/form.test
@@ -653,6 +653,13 @@ class FormsElementsLabelsTestCase extends DrupalWebTestCase {
$elements = $this->xpath('//div[@id="form-test-textfield-title-suffix"]/preceding-sibling::div[contains(@class, \'form-item-form-textfield-test-title\')]');
$this->assertTrue(isset($elements[0]), t("Properly places the #suffix element before the form item."));
+
+ // Check #description placement depending on the #description_position value.
+ $elements = $this->xpath('//div[@class="form-item form-type-textfield form-item-form-textfield-test-description-after"]/input[@id="edit-form-textfield-test-description-after"]/following-sibling::div[@class="description"]');
+ $this->assertTrue(isset($elements[0]), t("Properly places the #description element after the form item."));
+
+ $elements = $this->xpath('//div[@class="form-item form-type-textfield form-item-form-textfield-test-description-before"]/input[@id="edit-form-textfield-test-description-before"]/preceding-sibling::div[@class="description"]');
+ $this->assertTrue(isset($elements[0]), t("Properly places the #description element before the form item."));
}
}
diff --git a/core/modules/simpletest/tests/form_test.module b/core/modules/simpletest/tests/form_test.module
index 0393b18..17dbe58 100644
--- a/core/modules/simpletest/tests/form_test.module
+++ b/core/modules/simpletest/tests/form_test.module
@@ -755,6 +755,20 @@ function form_label_test_form() {
'#type' => 'textfield',
);
+ $form['form_textfield_test_description_before'] = array(
+ '#type' =>'textfield',
+ '#title' => t('Textfield test for description before element'),
+ '#description' => t('Textfield test for description before element'),
+ '#description_position' => 'before'
+ );
+
+ $form['form_textfield_test_description_after'] = array(
+ '#type' =>'textfield',
+ '#title' => t('Textfield test for description after element'),
+ '#description' => t('Textfield test for description after element'),
+ '#description_position' => 'after'
+ );
+
return $form;
}
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 0684f26..5a6469d 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -226,6 +226,8 @@ function hook_cron_queue_info_alter(&$queues) {
* - "#submit": array of callback functions taking $form and $form_state.
* - "#title_display": optional string indicating if and how #title should be
* displayed, see theme_form_element() and theme_form_element_label().
+ * - "#description_position": optional string indicating how the #description should
+ * be displayed, see theme_form_element().
*
* @see hook_element_info_alter()
* @see system_element_info()