diff --git a/core/includes/form.inc b/core/includes/form.inc index 87f8edf..0d8398a 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -9,6 +9,7 @@ use Drupal\Component\Utility\Number; use Drupal\Component\Utility\String; use Drupal\Component\Utility\Url; +use Drupal\Component\Utility\Xss; use Drupal\Core\Database\Database; use Drupal\Core\Language\Language; use Drupal\Core\Template\Attribute; @@ -744,7 +745,7 @@ function form_options_flatten($array) { } /** - * Processes a select list form element. + * Processes a select list element. * * This process callback is mandatory for select fields, since all user agents * automatically preselect the first available option of single (non-multiple) @@ -812,26 +813,27 @@ function form_process_select($element) { } /** - * Returns HTML for a select form element. + * Prepares variables for select element templates. + * + * Default template: select.html.twig. * * It is possible to group options together; to do this, change the format of * $options to an associative array in which the keys are group labels, and the * values are associative arrays in the normal $options format. * - * @param $variables + * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. * Properties used: #title, #value, #options, #description, #extra, * #multiple, #required, #name, #attributes, #size. - * - * @ingroup themeable */ -function theme_select($variables) { +function template_preprocess_select(&$variables) { $element = $variables['element']; element_set_attributes($element, array('id', 'name', 'size')); _form_set_attributes($element, array('form-select')); - return '' . form_select_options($element) . ''; + $variables['options'] = form_select_options($element); + $variables['attributes'] = $element['#attributes']; } /** @@ -933,94 +935,79 @@ function form_get_options($element, $key) { } /** - * Returns HTML for a fieldset form element and its children. + * Prepares variables for fieldset element templates. * - * @param $variables + * Default template: fieldset.html.twig. + * + * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. * Properties used: #attributes, #children, #collapsed, #description, #id, * #title, #value. - * - * @ingroup themeable */ -function theme_fieldset($variables) { +function template_preprocess_fieldset(&$variables) { $element = $variables['element']; element_set_attributes($element, array('id')); _form_set_attributes($element, array('form-wrapper')); - if (!empty($element['#description'])) { - $description_id = $element['#attributes']['id'] . '--description'; - $element['#attributes']['aria-describedby'] = $description_id; - } - - $legend_attributes = array(); - if (isset($element['#title_display']) && $element['#title_display'] == 'invisible') { - $legend_attributes['class'][] = 'visually-hidden'; - } + $variables['attributes'] = $element['#attributes']; - $output = ''; if (!empty($element['#title'])) { - // Always wrap fieldset legends in a SPAN for CSS positioning. - $output .= '' . $element['#title'] . ''; + $variables['title'] = $element['#title']; + if (isset($element['#title_display']) && $element['#title_display'] == 'invisible') { + $variables['title_attributes']['class'] = array('visually-hidden'); + } } - $output .= '
'; - if (!empty($element['#description'])) { - $attributes = array('class' => 'fieldset-description', 'id' => $description_id); - $output .= '' . $element['#description'] . '
'; + if (!empty($element['#description'])) { + $variables['description'] = $element['#description']; + $description_id = $element['#attributes']['id'] . '--description'; + $variables['description_attributes'] = new Attribute(array( + 'id' => $description_id, + 'class' => array('fieldset-description'), + 'aria-describedby' => $description_id, + )); } - $output .= $element['#children']; - if (isset($element['#value'])) { - $output .= $element['#value']; - } - $output .= ''; - $output .= "\n"; - return $output; + + $variables['children'] = $element['#children']; + $variables['value'] = (isset($element['#value'])) ? $element['#value'] : NULL; } /** - * Returns HTML for a details form element and its children. + * Prepares variables for details element templates. * - * @param $variables + * Default template: details.html.twig. + * + * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. - * Properties used: #attributes, #children, #collapsed, #description, #id, - * #title, #value. + * Properties used: #attributes, #children, #collapsed, #collapsible, + * #description, #id, #title, #value. * * @ingroup themeable */ -function theme_details($variables) { +function template_preprocess_details(&$variables) { $element = $variables['element']; - element_set_attributes($element, array('id')); - _form_set_attributes($element, array('form-wrapper')); - $output = ''; + $variables['attributes'] = $element['#attributes']; + $variables['summary_attributes'] = new Attribute(array()); if (!empty($element['#title'])) { - $summary_attributes = new Attribute(array( - 'role' => 'button', - )); + $variables['summary_attributes']['role'] = 'button'; if (!empty($element['#attributes']['id'])) { - $summary_attributes['aria-controls'] = $element['#attributes']['id']; + $variables['summary_attributes']['aria-controls'] = $element['#attributes']['id']; } - $summary_attributes['aria-expanded'] = empty($element['#attributes']['open']) ? FALSE : TRUE; - $summary_attributes['aria-pressed'] = $summary_attributes['aria-expanded']; - $output .= '' . $element['#title'] . ''; - } - $output .= '
'; - if (!empty($element['#description'])) { - $output .= '
' . $element['#description'] . '
'; - } - $output .= $element['#children']; - if (isset($element['#value'])) { - $output .= $element['#value']; + $variables['summary_attributes']['aria-expanded'] = empty($element['#attributes']['open']) ? FALSE : TRUE; + $variables['summary_attributes']['aria-pressed'] = $variables['summary_attributes']['aria-expanded']; } - $output .= '
'; - $output .= "\n"; - return $output; + + $variables['title'] = (!empty($element['#title'])) ? $element['#title'] : ''; + $variables['description'] = (!empty($element['#description'])) ? $element['#description'] : ''; + $variables['children'] = (isset($element['#children'])) ? $element['#children'] : ''; + $variables['value'] = (isset($element['#value'])) ? $element['#value'] : ''; } /** - * Prepares a #type 'radio' render element for theme_input(). + * Adds form-specific attributes to a 'radio' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -1032,7 +1019,7 @@ function theme_details($variables) { * all the attributes. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__radio'. */ function form_pre_render_radio($element) { $element['#attributes']['type'] = 'radio'; @@ -1047,34 +1034,34 @@ function form_pre_render_radio($element) { } /** - * Returns HTML for a set of radio button form elements. + * Prepares variables for radios templates. * - * @param $variables + * Default template: radios.html.twig. + * + * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. - * Properties used: #title, #value, #options, #description, #required, - * #attributes, #children. - * - * @ingroup themeable + * Properties used: #attributes, #id, #children. */ -function theme_radios($variables) { +function template_preprocess_radios(&$variables) { $element = $variables['element']; - $attributes = array(); if (isset($element['#id'])) { - $attributes['id'] = $element['#id']; - } - $attributes['class'] = 'form-radios'; - if (!empty($element['#attributes']['class'])) { - $attributes['class'] .= ' ' . implode(' ', $element['#attributes']['class']); + $variables['attributes']['id'] = $element['#id']; } + $variables['attributes']['class'] = (!empty($element['#attributes']['class'])) ? $element['#attributes']['class'] : array(); + $variables['attributes']['class'][] = 'form-radios'; if (isset($element['#attributes']['title'])) { - $attributes['title'] = $element['#attributes']['title']; + $variables['attributes']['title'] = $element['#attributes']['title']; } - return '' . (!empty($element['#children']) ? $element['#children'] : '') . ''; + + $variables['children'] = !empty($element['#children']) ? $element['#children'] : ''; + + // Remove the disabled attribute on the wrapper. + unset($variables['attributes']['disabled']); } /** - * Expand a password_confirm field into two text boxes. + * Expands a password_confirm field into two text boxes. */ function form_process_password_confirm($element) { $element['pass1'] = array( @@ -1127,29 +1114,30 @@ function password_confirm_validate($element, &$element_state) { } /** - * Returns HTML for an #date form element. + * Adds form-specific attributes to a 'date' #type element. * * Supports HTML5 types of 'date', 'datetime', 'datetime-local', and 'time'. * Falls back to a plain textfield. Used as a sub-element by the datetime * element type. * - * @param array $variables - * An associative array containing: - * - element: An associative array containing the properties of the element. - * Properties used: #title, #value, #options, #description, #required, - * #attributes, #id, #name, #type, #min, #max, #step, #value, #size. + * @param array $element + * An associative array containing the properties of the element. + * Properties used: #title, #value, #options, #description, #required, + * #attributes, #id, #name, #type, #min, #max, #step, #value, #size. * - * @ingroup themeable + * Note: The input "name" attribute needs to be sanitized before output, which + * is currently done by initializing Drupal\Core\Template\Attribute with + * all the attributes. + * + * @return array + * The $element with prepared variables ready for #theme 'input__date'. */ -function theme_date($variables) { - $element = $variables['element']; - if (empty($element['attribute']['type'])) { - $element['attribute']['type'] = 'date'; - } - element_set_attributes($element, array('id', 'name', 'type', 'min', 'max', 'step', 'value', 'size')); - _form_set_attributes($element, array('form-' . $element['attribute']['type'])); +function form_pre_render_date($element) { + $element['#attributes']['type'] = 'date'; + element_set_attributes($element, array('id', 'name', 'min', 'max', 'step', 'value', 'size')); + _form_set_attributes($element, array('form-date')); - return ''; + return $element; } /** @@ -1165,7 +1153,7 @@ function weight_value(&$form) { } /** - * Expands a radios element into individual radio elements. + * Expands a #type 'radios' element into individual radio elements. */ function form_process_radios($element) { if (count($element['#options']) > 0) { @@ -1201,7 +1189,7 @@ function form_process_radios($element) { } /** - * Prepares a #type 'checkbox' render element for theme_input(). + * Adds form-specific attributes to a 'checkbox' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -1209,7 +1197,7 @@ function form_process_radios($element) { * #attributes, #checked. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__checkbox'. */ function form_pre_render_checkbox($element) { $element['#attributes']['type'] = 'checkbox'; @@ -1225,33 +1213,44 @@ function form_pre_render_checkbox($element) { } /** - * Returns HTML for a set of checkbox form elements. + * Prepares variables for checkboxes templates. * - * @param $variables + * Default template: checkboxes.html.twig. + * + * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. * Properties used: #children, #attributes. - * - * @ingroup themeable */ -function theme_checkboxes($variables) { +function template_preprocess_checkboxes(&$variables) { $element = $variables['element']; - $attributes = array(); + if (isset($element['#id'])) { - $attributes['id'] = $element['#id']; + $variables['attributes']['id'] = $element['#id']; } - $attributes['class'][] = 'form-checkboxes'; + $variables['attributes']['class'] = array(); + $variables['attributes']['class'][] = 'form-checkboxes'; if (!empty($element['#attributes']['class'])) { - $attributes['class'] = array_merge($attributes['class'], $element['#attributes']['class']); + if (is_array($element['#attributes']['class'])) { + foreach ($element['#attributes']['class'] as $class) { + $variables['attributes']['class'][] = $class; + } + } + else { + $variables['attributes']['class'][] = $element['#attributes']['class']; + } } if (isset($element['#attributes']['title'])) { - $attributes['title'] = $element['#attributes']['title']; + $variables['attributes']['title'] = $element['#attributes']['title']; } - return '' . (!empty($element['#children']) ? $element['#children'] : '') . ''; + $variables['children'] = (!empty($element['#children'])) ? $element['#children'] : ''; + + // Remove the disabled attribute on the wrapper. + unset($variables['attributes']['disabled']); } /** - * Adds form element theming to an element if its title or description is set. + * Adds form element themeing to an element if its title or description is set. * * This is used as a pre render function for checkboxes and radios. */ @@ -1484,7 +1483,9 @@ function form_process_container($element, &$form_state) { } /** - * Returns HTML for a table with radio buttons or checkboxes. + * Prepares a 'tableselect' #type element for rendering. + * + * Adds a column of radio buttons or checkboxes for each row of a table. * * @param $variables * An associative array containing: @@ -1522,11 +1523,8 @@ function form_process_container($element, &$form_state) { * '#empty' => t('No content available.'), * ); * @endcode - * - * @ingroup themeable */ -function theme_tableselect($variables) { - $element = $variables['element']; +function form_pre_render_tableselect(array $element) { $rows = array(); $header = $element['#header']; if (!empty($element['#options'])) { @@ -1566,7 +1564,7 @@ function theme_tableselect($variables) { // checkboxes/radios in the first table column. if ($element['#js_select']) { // Add a "Select all" checkbox. - drupal_add_library('system', 'drupal.tableselect'); + $element['#attached']['library'][] = array('system', 'drupal.tableselect'); array_unshift($header, array('class' => array('select-all'))); } else { @@ -1575,7 +1573,10 @@ function theme_tableselect($variables) { array_unshift($header, ''); } } - return theme('table', array('header' => $header, 'rows' => $rows, 'empty' => $element['#empty'], 'attributes' => $element['#attributes'])); + $element['#header'] = $header; + $element['#rows'] = $rows; + + return $element; } /** @@ -1667,7 +1668,6 @@ function form_process_tableselect($element) { * The processed element. * * @see form_process_tableselect() - * @see theme_tableselect() */ function form_process_table($element, &$form_state) { if ($element['#tableselect']) { @@ -1960,7 +1960,7 @@ function form_process_group(&$element, &$form_state) { } /** - * Adds form element theming to details. + * Adds form-specific attributes to a 'details' #type element. * * @param $element * An associative array containing the properties and children of the @@ -1970,11 +1970,13 @@ function form_process_group(&$element, &$form_state) { * The modified element. */ function form_pre_render_details($element) { - // The .form-wrapper class is required for #states to treat details like - // containers. + element_set_attributes($element, array('id')); if (!isset($element['#attributes']['class'])) { $element['#attributes']['class'] = array(); } + // The .form-wrapper class is required for #states to treat details like + // containers. + _form_set_attributes($element, array('form-wrapper')); // Collapsible details. $element['#attached']['library'][] = array('system', 'drupal.collapse'); @@ -2108,18 +2110,20 @@ function form_pre_render_vertical_tabs($element) { } /** - * Returns HTML for an element's children details as vertical tabs. + * Prepares an element's children details for vertical tabs templates. * - * @param $variables + * Default template: vertical-tabs.html.twig. + * + * @param array $variables * An associative array containing: * - element: An associative array containing the properties and children of * the details element. Properties used: #children. - * - * @ingroup themeable */ -function theme_vertical_tabs($variables) { - $element = $variables['element']; - return '
' . $element['#children'] . '
'; +function template_preprocess_vertical_tabs(&$variables) { + $variables['attributes'] = $variables['element']['#attributes'] + array( + 'data-vertical-tabs-panes' => TRUE, + ); + $variables['children'] = $variables['element']['#children']; } /** @@ -2166,37 +2170,20 @@ function form_process_autocomplete($element, &$form_state) { } /** - * Preprocesses variables for theme_input(). - * - * @param array $variables - * An associative array containing: - * - element: An associative array containing the properties of the element. + * Prepares variables for input templates. * - * @ingroup themeable - */ -function template_preprocess_input(&$variables) { - $element = $variables['element']; - $variables['attributes'] = new Attribute($element['#attributes']); -} - -/** - * Returns HTML for an input form element. + * Default template: input.html.twig. * * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. - * Properties used: #attributes. - * - * @ingroup themeable */ -function theme_input($variables) { - $element = $variables['element']; - $attributes = $variables['attributes']; - return '' . drupal_render_children($element); +function template_preprocess_input(&$variables) { + $variables['attributes'] = $variables['element']['#attributes']; } /** - * Prepares a #type 'button' render element for theme_input(). + * Adds form-specific attributes to a 'button' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -2206,7 +2193,7 @@ function theme_input($variables) { * styles the following button_types appropriately: 'primary', 'danger'. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__button'. */ function form_pre_render_button($element) { $element['#attributes']['type'] = 'submit'; @@ -2227,7 +2214,7 @@ function form_pre_render_button($element) { } /** - * Prepares a #type 'image_button' render element for theme_input(). + * Adds form-specific attributes to an 'image_button' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -2237,7 +2224,7 @@ function form_pre_render_button($element) { * styles the following button_types appropriately: 'primary', 'danger'. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__image_button'. */ function form_pre_render_image_button($element) { $element['#attributes']['type'] = 'image'; @@ -2264,14 +2251,14 @@ function form_pre_render_image_button($element) { } /** - * Prepares a #type 'hidden' render element for theme_input(). + * Adds form-specific attributes to a 'hidden' #type element. * * @param array $element * An associative array containing the properties of the element. * Properties used: #name, #value, #attributes. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__hidden'. */ function form_pre_render_hidden($element) { $element['#attributes']['type'] = 'hidden'; @@ -2281,7 +2268,7 @@ function form_pre_render_hidden($element) { } /** - * Prepares a #type 'textfield' render element for theme_input(). + * Adds form-specific attributes to a 'textfield' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -2289,7 +2276,7 @@ function form_pre_render_hidden($element) { * #placeholder, #required, #attributes. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__textfield'. */ function form_pre_render_textfield($element) { $element['#attributes']['type'] = 'text'; @@ -2300,7 +2287,7 @@ function form_pre_render_textfield($element) { } /** - * Prepares a #type 'email' render element for theme_input(). + * Adds form-specific attributes to an 'email' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -2308,7 +2295,7 @@ function form_pre_render_textfield($element) { * #placeholder, #required, #attributes. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__email'. */ function form_pre_render_email($element) { $element['#attributes']['type'] = 'email'; @@ -2333,7 +2320,7 @@ function form_validate_email(&$element, &$form_state) { } /** - * Prepares a #type 'tel' render element for theme_input(). + * Adds form-specific attributes to a 'tel' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -2341,7 +2328,7 @@ function form_validate_email(&$element, &$form_state) { * #placeholder, #required, #attributes. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__tel'. */ function form_pre_render_tel($element) { $element['#attributes']['type'] = 'tel'; @@ -2352,7 +2339,7 @@ function form_pre_render_tel($element) { } /** - * Prepares a #type 'number' render element for theme_input(). + * Adds form-specific attributes to a 'number' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -2360,7 +2347,7 @@ function form_pre_render_tel($element) { * #required, #attributes, #step, #size. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__number'. */ function form_pre_render_number($element) { $element['#attributes']['type'] = 'number'; @@ -2371,7 +2358,7 @@ function form_pre_render_number($element) { } /** - * Prepares a #type 'range' render element for theme_input(). + * Adds form-specific attributes to a 'range' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -2379,7 +2366,7 @@ function form_pre_render_number($element) { * #step. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__range'. */ function form_pre_render_range($element) { $element['#attributes']['type'] = 'range'; @@ -2460,7 +2447,7 @@ function form_type_range_value($element, $input = FALSE) { } /** - * Prepares a #type 'url' render element for theme_input(). + * Adds form-specific attributes to a 'url' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -2468,7 +2455,7 @@ function form_type_range_value($element, $input = FALSE) { * #placeholder, #required, #attributes. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__url'. */ function form_pre_render_url($element) { $element['#attributes']['type'] = 'url'; @@ -2479,7 +2466,7 @@ function form_pre_render_url($element) { } /** - * Prepares a #type 'search' render element for theme_input(). + * Adds form-specific attributes to a 'search' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -2487,7 +2474,7 @@ function form_pre_render_url($element) { * #placeholder, #required, #attributes. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__search'. */ function form_pre_render_search($element) { $element['#attributes']['type'] = 'search'; @@ -2534,14 +2521,14 @@ function form_validate_color(&$element, &$form_state) { } /** - * Prepares a #type 'color' render element for theme_input(). + * Adds form-specific attriutes to a 'color' #type element. * * @param array $element * An associative array containing the properties of the element. * Properties used: #title, #value, #description, #attributes. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__color'. */ function form_pre_render_color($element) { $element['#attributes']['type'] = 'color'; @@ -2552,61 +2539,59 @@ function form_pre_render_color($element) { } /** - * Returns HTML for a form. + * Prepares variables for form templates. * - * @param $variables + * Default template: form.html.twig. + * + * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. * Properties used: #action, #method, #attributes, #children - * - * @ingroup themeable */ -function theme_form($variables) { +function template_preprocess_form(&$variables) { $element = $variables['element']; + element_set_attributes($element, array('method', 'id')); if (isset($element['#action'])) { $element['#attributes']['action'] = Url::stripDangerousProtocols($element['#action']); } - element_set_attributes($element, array('method', 'id')); if (empty($element['#attributes']['accept-charset'])) { $element['#attributes']['accept-charset'] = "UTF-8"; } - // Anonymous DIV to satisfy XHTML compliance. - return '
' . $element['#children'] . '
'; + $variables['attributes'] = $element['#attributes']; + $variables['children'] = $element['#children']; } /** - * Returns HTML for a textarea form element. + * Prepares variables for textarea form element templates. * - * @param $variables + * Default template: textarea.html.twig. + * + * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. * Properties used: #title, #value, #description, #rows, #cols, * #placeholder, #required, #attributes, #resizable - * - * @ingroup themeable */ -function theme_textarea($variables) { +function template_preprocess_textarea(&$variables) { $element = $variables['element']; element_set_attributes($element, array('id', 'name', 'rows', 'cols', 'placeholder')); _form_set_attributes($element, array('form-textarea')); - $wrapper_attributes = array( + $variables['wrapper_attributes'] = new Attribute(array( 'class' => array('form-textarea-wrapper'), - ); + )); // Add resizable behavior. if (!empty($element['#resizable'])) { $element['#attributes']['class'][] = 'resize-' . $element['#resizable']; } - $output = ''; - $output .= '' . String::checkPlain($element['#value']) . ''; - $output .= ''; - return $output; + $variables['attributes'] = $element['#attributes']; + $variables['value'] = String::checkPlain($element['#value']); } /** - * Prepares a #type 'password' render element for theme_input(). + * Adds form-specific attributes to a 'password' #type element. * * @param array $element * An associative array containing the properties of the element. @@ -2614,7 +2599,7 @@ function theme_textarea($variables) { * #placeholder, #required, #attributes. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__password'. */ function form_pre_render_password($element) { $element['#attributes']['type'] = 'password'; @@ -2652,7 +2637,7 @@ function form_process_weight($element) { } /** - * Prepares a #type 'file' render element for theme_input(). + * Adds form-specific attributes to a 'file' #type element. * * For assistance with handling the uploaded file correctly, see the API * provided by file.inc. @@ -2663,7 +2648,7 @@ function form_process_weight($element) { * #attributes. * * @return array - * The $element with prepared variables ready for theme_input(). + * The $element with variables prepared for #theme 'input__file'. */ function form_pre_render_file($element) { $element['#attributes']['type'] = 'file'; @@ -2722,109 +2707,84 @@ function form_process_file($element) { * but the parent element should have neither. Use this carefully because a * field without an associated label can cause accessibility challenges. * - * @param $variables + * @param array $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. - * - * @ingroup themeable */ -function theme_form_element($variables) { - $element = &$variables['element']; +function template_preprocess_form_element(&$variables) { + $element = $variables['element']; // This function is invoked as theme wrapper, but the rendered form element // may not necessarily have been processed by form_builder(). $element += array( '#title_display' => 'before', ); - + // If #title is not set, we don't display any label or required marker. + if (!isset($element['#title'])) { + $element['#title_display'] = 'none'; + } + // We call it a label, though. + $variables['label_display'] = $element['#title_display']; + // Label returns empty string in some cases. + $variables['label'] = array('#theme' => 'form_element_label', '#element' => $element); // Take over any #wrapper_attributes defined by the element. // @todo Temporary hack for #type 'item'. // @see http://drupal.org/node/1829202 - if (isset($element['#wrapper_attributes'])) { - $attributes = $element['#wrapper_attributes']; - } - // Add element #id for #type 'item'. - if (isset($element['#markup']) && !empty($element['#id'])) { - $attributes['id'] = $element['#id']; - } + $variables['attributes'] = (isset($element['#wrapper_attributes']) ? $element['#wrapper_attributes'] : array('class' => array())); // Add element's #type and #name as class to aid with JS/CSS selectors. - $attributes['class'][] = 'form-item'; + $variables['attributes']['class'][] = 'form-item'; if (!empty($element['#type'])) { - $attributes['class'][] = 'form-type-' . strtr($element['#type'], '_', '-'); + $variables['attributes']['class'][] = 'form-type-' . strtr($element['#type'], '_', '-'); } if (!empty($element['#name'])) { - $attributes['class'][] = 'form-item-' . strtr($element['#name'], array(' ' => '-', '_' => '-', '[' => '-', ']' => '')); + $variables['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'; + $variables['attributes']['class'][] = 'form-disabled'; } - $output = '' . "\n"; - - // If #title is not set, we don't display any label or required marker. - if (!isset($element['#title'])) { - $element['#title_display'] = 'none'; - } - $prefix = isset($element['#field_prefix']) ? '' . $element['#field_prefix'] . ' ' : ''; - $suffix = isset($element['#field_suffix']) ? ' ' . $element['#field_suffix'] . '' : ''; - - switch ($element['#title_display']) { - case 'before': - case 'invisible': - $output .= ' ' . theme('form_element_label', $variables); - $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n"; - break; - - case 'after': - $output .= ' ' . $prefix . $element['#children'] . $suffix; - $output .= ' ' . theme('form_element_label', $variables) . "\n"; - break; - - case 'none': - case 'attribute': - // Output no label and no required marker, only the children. - $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n"; - break; - } - - if (!empty($element['#description'])) { - $attributes = array('class' => 'description'); + // Add element #id for #type 'item'. + if (isset($element['#markup']) && !empty($element['#id'])) { + $variables['attributes']['id'] = $element['#id']; + } + $variables['prefix'] = isset($element['#field_prefix']) ? $element['#field_prefix'] : ''; + $variables['suffix'] = isset($element['#field_suffix']) ? $element['#field_suffix'] : ''; + $variables['children'] = isset($element['#children']) ? $element['#children'] : NULL; + $variables['description'] = !empty($element['#description']) ? $element['#description'] : ''; + if ($variables['description']) { + $variables['description_attributes'] = new Attribute(array('class' => 'description')); if (!empty($element['#id'])) { - $attributes['id'] = $element['#id'] . '--description'; + $variables['description_attributes']['id'] = $element['#id'] . '--description'; } - $output .= '' . $element['#description'] . "\n"; } - - $output .= "\n"; - - return $output; } /** - * Returns HTML for a marker for required form elements. + * Prepares variables for form element required marker templates. * - * @param $variables + * Default template: form-element-required.html.twig. + * + * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. - * - * @ingroup themeable */ -function theme_form_required_marker($variables) { - $attributes = array( +function template_preprocess_form_required_marker(&$variables) { + $variables['attributes'] = array( 'class' => 'form-required', 'aria-hidden' => 'true', ); - return '*'; } /** - * Returns HTML for a form element label and required marker. + * Prepares variables for form element label templates. + * + * Default template: form-element-label.html.twig. * * Form element labels include the #title and a #required marker. The label is * associated with the element itself by the element #id. Labels may appear - * before or after elements, depending on theme_form_element() and + * before or after elements, depending on form_element template and * #title_display. * * This function will not be called for elements with no labels, depending on @@ -2832,44 +2792,72 @@ function theme_form_required_marker($variables) { * this function will output no label (''). For required elements that have an * empty #title, this will output the required marker alone within the label. * The label will use the #id to associate the marker with the field that is - * required. That is especially important for screenreader users to know - * which field is required. + * required. That is especially important for screenreader users to know which + * field is required. * - * @param $variables + * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. * Properties used: #required, #title, #id, #value, #description. - * - * @ingroup themeable */ -function theme_form_element_label($variables) { - $element = $variables['element']; +function template_preprocess_form_element_label(&$variables) { + // @todo: Figure out how to do this differently (calling from form_element). + $element = $variables['element']['#element']; - // If title and required marker are both empty, output no label. - if ((!isset($element['#title']) || $element['#title'] === '') && empty($element['#required'])) { - return ''; + // If title and required marker are both empty, prepare no label. + if (empty($element['#required']) && (!isset($element['#title']) || $element['#title'] === '')) { + $variables['label'] = $variables['required'] = $variables['attributes'] = NULL; } - - // If the element is required, a required marker is appended to the label. - $required = !empty($element['#required']) ? theme('form_required_marker', array('element' => $element)) : ''; - - $title = filter_xss_admin($element['#title']); - - $attributes = array(); - // Style the label as class option to display inline with the element. - if ($element['#title_display'] == 'after') { - $attributes['class'] = 'option'; - } - // Show label only to screen readers to avoid disruption in visual flows. - elseif ($element['#title_display'] == 'invisible') { - $attributes['class'] = 'visually-hidden'; + else { + // If the element is required, a required marker is appended to the label. + $variables['label'] = (isset($element['#title'])) ? Xss::filterAdmin($element['#title']) : NULL; + $variables['required'] = (!empty($element['#required'])) ? array('#theme' => 'form_required_marker', '#element' => $element) : ''; + // Associate the label with the field it is for. + if (!empty($element['#id'])) { + $variables['attributes']['for'] = $element['#id']; + } + // Add label positioning classes. + if (!empty($element['#title_display'])) { + $variables['attributes']['class'] = array(); + // Style the label as class 'option' to display inline with the element. + // @todo: Use a more semantic class name like 'layout-inline-after'? + if ($element['#title_display'] == 'after') { + $variables['attributes']['class'][] = 'option'; + } + // Show label only to screen readers to avoid disruption in visual flows. + elseif ($element['#title_display'] == 'invisible') { + $variables['attributes']['class'][] = 'visually-hidden'; + } + } } +} - if (!empty($element['#id'])) { - $attributes['for'] = $element['#id']; +/** + * Prepares variables for exposed filters templates. + * + * Default template: exposed-filters.html.twig. + * + * @param array $variables + * An associative array containing: + * - form: An associative array containing the structure of the form. + */ +function template_preprocess_exposed_filters(&$variables) { + $form = $variables['form']; + + $variables['items'] = array(); + if (isset($form['current'])) { + $items = array(); + foreach (element_children($form['current']) as $key) { + $items[] = $form['current'][$key]; + } + $variables['items'][] = array( + '#theme' => 'item_list', + '#items' => $items, + '#attributes' => array( + 'class' => array('clearfix', 'current-filters'), + ), + ); } - - return '' . t('!title!required', array('!title' => $title, '!required' => $required)) . ''; } /** diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 726503e..c426b22 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1926,20 +1926,16 @@ function theme_indentation($variables) { } /** - * Returns HTML to wrap child elements in a container. + * Prepares variables for container templates. * - * Used for grouped form items. Can also be used as a #theme_wrapper for any - * renderable element, to surround it with a
and add attributes such as - * classes or an HTML id. + * Default template: container.html.twig. * - * @param $variables + * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. * Properties used: #id, #attributes, #children. - * - * @ingroup themeable */ -function theme_container($variables) { +function template_preprocess_container(&$variables) { $element = $variables['element']; // Ensure #attributes is set. $element += array('#attributes' => array()); @@ -1954,7 +1950,8 @@ function theme_container($variables) { $element['#attributes']['class'][] = 'form-wrapper'; } - return '' . $element['#children'] . '
'; + $variables['children'] = $element['#children']; + $variables['attributes'] = $element['#attributes']; } /** @@ -2690,48 +2687,55 @@ function drupal_common_theme() { // From form.inc. 'input' => array( 'render element' => 'element', + 'template' => 'input', ), 'select' => array( 'render element' => 'element', + 'template' => 'select', ), 'fieldset' => array( 'render element' => 'element', + 'template' => 'fieldset', ), 'details' => array( 'render element' => 'element', + 'template' => 'details', ), 'radios' => array( 'render element' => 'element', - ), - 'date' => array( - 'render element' => 'element', + 'template' => 'radios', ), 'checkboxes' => array( 'render element' => 'element', + 'template' => 'checkboxes', ), 'form' => array( 'render element' => 'element', + 'template' => 'form', ), 'textarea' => array( 'render element' => 'element', - ), - 'tableselect' => array( - 'render element' => 'element', + 'template' => 'textarea', ), 'form_element' => array( 'render element' => 'element', + 'template' => 'form-element', ), 'form_required_marker' => array( 'render element' => 'element', + 'template' => 'form-required-marker', ), 'form_element_label' => array( 'render element' => 'element', + 'template' => 'form-element-label', ), 'vertical_tabs' => array( 'render element' => 'element', + 'template' => 'vertical-tabs', ), 'container' => array( 'render element' => 'element', + 'template' => 'container', ), ); } diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateTest.php index 62fa90c..3f07bfe 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateTest.php @@ -167,7 +167,9 @@ function testUpdateImportSourceRemote() { $this->drupalGet('admin/reports/translations/check'); // Check the status on the Available translation status page. - $this->assertRaw('', 'German language found'); + $elements = $this->xpath('//label[@class="visually-hidden" and @for="edit-langcodes-de" and text()=:value]', array(':value' => 'Update German')); + $this->assertTrue(!empty($elements), 'German language found'); + $this->assertText('Updates for: Contributed module one, Contributed module two, Custom module one, Locale test', 'Updates found'); $this->assertText('Contributed module one (' . format_date($this->timestamp_now, 'html_date') . ')', 'Updates for Contrib module one'); $this->assertText('Contributed module two (' . format_date($this->timestamp_new, 'html_date') . ')', 'Updates for Contrib module two'); diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/RenderElementTypesTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/RenderElementTypesTest.php index 9ba4bf4..a618409 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/RenderElementTypesTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderElementTypesTest.php @@ -50,7 +50,7 @@ function testContainer() { '#type' => 'container', '#markup' => 'foo', ), - 'expected' => '
foo
', + 'expected' => '
foo
' . "\n", ), // Container with a class. array( @@ -62,7 +62,7 @@ function testContainer() { 'class' => 'bar', ), ), - 'expected' => '
foo
', + 'expected' => '
foo
' . "\n", ), // Container with children. array( @@ -73,7 +73,7 @@ function testContainer() { '#markup' => 'foo', ), ), - 'expected' => '
foo
', + 'expected' => '
foo
' . "\n", ), ); diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php index 6563339..857cd09 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php @@ -94,7 +94,7 @@ function testRequiredFields() { $elements['file']['empty_values'] = $empty_strings; // Regular expression to find the expected marker on required elements. - $required_marker_preg = '@