diff options
author | Dries Buytaert <dries@buytaert.net> | 2009-02-03 18:55:32 +0000 |
---|---|---|
committer | Dries Buytaert <dries@buytaert.net> | 2009-02-03 18:55:32 +0000 |
commit | 29c8e40e912f8975011224f824977b2353fe07b5 (patch) | |
tree | bb6a3341259232340b88ca120cf349c57404c13b /includes | |
parent | 607e9626d5af265b18e8319b156bb0fda3445cd4 (diff) | |
download | brdo-29c8e40e912f8975011224f824977b2353fe07b5.tar.gz brdo-29c8e40e912f8975011224f824977b2353fe07b5.tar.bz2 |
- Patch #355236 by Frando: refactor drupal_render() theming.
Diffstat (limited to 'includes')
-rw-r--r-- | includes/common.inc | 211 | ||||
-rw-r--r-- | includes/form.inc | 132 | ||||
-rw-r--r-- | includes/locale.inc | 2 | ||||
-rw-r--r-- | includes/theme.inc | 2 |
4 files changed, 170 insertions, 177 deletions
diff --git a/includes/common.inc b/includes/common.inc index 09bd204e4..5dfc001ca 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -3217,7 +3217,7 @@ function drupal_alter($type, &$data) { */ function drupal_get_page($content = NULL) { // Initialize page array with defaults. @see hook_elements() - 'page' element. - $page = _element_info('page'); + $page = element_info('page'); $page['content'] = is_array($content) ? $content : array('main' => array('#markup' => $content)); return $page; @@ -3253,6 +3253,28 @@ function drupal_render_page($page) { * Renders HTML given a structured array tree. * * Recursively iterates over each of the array elements, generating HTML code. + * + * HTML generation is controlled by two properties containing theme functions, + * #theme and #theme_wrapper. + * + * #theme is the theme function called first. If it is set and the element has any + * children, they have to be rendered there. For elements that are not allowed + * to have any children, e.g. buttons or textfields, it can be used to render + * the element itself. If #theme is not present and the element has children, + * they are rendered and concatenated into a string by drupal_render_children(). + * + * The theme function in #theme_wrapper will be called after #theme has run. It + * can be used to add further markup around the rendered children, e.g. fieldsets + * add the required markup for a fieldset around their rendered child elements. + * A wrapper theme function always has to include the element's #children property + * in its output, as this contains the rendered children. + * + * For example, for the form element type, by default only the #theme_wrapper + * property is set, which adds the form markup around the rendered child elements + * of the form. This allows you to set the #theme property on a specific form to + * a custom theme function, giving you complete control over the placement of the + * form's children while not at all having to deal with the form markup itself. + * * This function is usually called from within a another function, like * drupal_get_form() or node_view(). Elements are sorted internally using * uasort(). Since this is expensive, when passing already sorted elements to @@ -3267,15 +3289,27 @@ function drupal_render_page($page) { function drupal_render(&$elements) { // Early-return nothing if user does not have access. if (!isset($elements) || (isset($elements['#access']) && !$elements['#access'])) { - return NULL; + return; + } + + // Do not print elements twice. + if (isset($elements['#printed']) && $elements['#printed']) { + return; } - // If the default values for this element haven't been loaded yet, populate + // If the default values for this element have not been loaded yet, populate // them. - if (!isset($elements['#defaults_loaded']) || !$elements['#defaults_loaded']) { - if ((!empty($elements['#type'])) && ($info = _element_info($elements['#type']))) { - $elements += $info; - } + if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) { + $elements += element_info($elements['#type']); + } + else { + $elements += element_basic_defaults(); + } + + // If #markup is not empty and no theme function is set, use theme_markup. + // This allows to specify just #markup on an element without setting the #type. + if (!empty($elements['#markup']) && empty($elements['#theme'])) { + $elements['#theme'] = 'markup'; } // Make any final changes to the element before it is rendered. This means @@ -3297,68 +3331,63 @@ function drupal_render(&$elements) { $elements['#sorted'] = TRUE; } - $content = ''; - $elements += array('#title' => NULL, '#description' => NULL); - if (!isset($elements['#children'])) { - $children = element_children($elements); - // Render all the children that use a theme function. - if (isset($elements['#theme']) && empty($elements['#theme_used'])) { - $elements['#theme_used'] = TRUE; - - $previous = array(); - foreach (array('#type', '#prefix', '#suffix') as $key) { - $previous[$key] = isset($elements[$key]) ? $elements[$key] : NULL; - } - // If we rendered a single element, then we will skip the renderer. - if (empty($children)) { - $elements['#printed'] = TRUE; - } - else { - $elements['#markup'] = ''; - } - - unset($elements['#type'], $elements['#prefix'], $elements['#suffix']); - $content = theme($elements['#theme'], $elements); - - foreach (array('#type', '#prefix', '#suffix') as $key) { - $elements[$key] = isset($previous[$key]) ? $previous[$key] : NULL; - } - } - // Render each of the children using drupal_render and concatenate them. - if (!isset($content) || $content === '') { - foreach ($children as $key) { - $content .= drupal_render($elements[$key]); - } - } + $elements['#children'] = ''; + // Call the element's #theme function if it is set. Then any children of the + // element have to be rendered there. + if (isset($elements['#theme'])) { + $elements['#children'] = theme($elements['#theme'], $elements); } - if (isset($content) && $content !== '') { - $elements['#children'] = $content; + // If #theme was not set and the element has children, render them now + // using drupal_render_children(). + if ($elements['#children'] == '') { + $elements['#children'] = drupal_render_children($elements); } - // Until now, we rendered the children, here we render the element itself - if (!isset($elements['#printed'])) { - $content = theme(!empty($elements['#type']) ? $elements['#type'] : 'markup', $elements); - $elements['#printed'] = TRUE; + // Let the theme function in #theme_wrapper add markup around the rendered + // children. + if (!empty($elements['#theme_wrapper'])) { + $elements['#children'] = theme($elements['#theme_wrapper'], $elements); } - if (isset($content) && $content !== '') { - // Filter the outputted content and make any last changes before the - // content is sent to the browser. The changes are made on $content - // which allows the output'ed text to be filtered. - if (isset($elements['#post_render'])) { - foreach ($elements['#post_render'] as $function) { - if (drupal_function_exists($function)) { - $content = $function($content, $elements); - } + // Filter the outputted content and make any last changes before the + // content is sent to the browser. The changes are made on $content + // which allows the output'ed text to be filtered. + if (isset($elements['#post_render'])) { + foreach ($elements['#post_render'] as $function) { + if (drupal_function_exists($function)) { + $elements['#children'] = $function($elements['#children'], $elements); } } - $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : ''; - $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : ''; - $content = $prefix . $content . $suffix; - // Store the rendered content, so higher level elements can reuse it. - $elements['#content'] = $content; - return $content; } + + $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : ''; + $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : ''; + + $elements['#printed'] = TRUE; + return $prefix . $elements['#children'] . $suffix; +} + +/** + * Render children of an element and concatenate them. + * + * This renders all children of an element using drupal_render() and then + * joins them together into a single string. + * + * @param $element + * The structured array whose children shall be rendered. + * @param $children_keys + * If the keys of the element's children are already known, they can be passed + * in to save another run of element_children(). + */ +function drupal_render_children($element, $children_keys = NULL) { + if ($children_keys === NULL) { + $children_keys = element_children($element); + } + $output = ''; + foreach ($children_keys as $key) { + $output .= drupal_render($element[$key]); + } + return $output; } /** @@ -3374,6 +3403,44 @@ function element_sort($a, $b) { } /** + * Retrieve the default properties for the defined element type. + */ +function element_info($type, $refresh = NULL) { + static $cache; + + if (!isset($cache) || $refresh) { + $basic_defaults = element_basic_defaults(); + $cache = array(); + foreach (module_implements('elements') as $module) { + $elements = module_invoke($module, 'elements'); + if (isset($elements) && is_array($elements)) { + $cache = array_merge_recursive($cache, $elements); + } + } + if (!empty($cache)) { + foreach ($cache as $element_type => $info) { + $cache[$element_type] = array_merge_recursive($basic_defaults, $info); + $cache[$element_type]['#type'] = $element_type; + } + } + } + + return $cache[$type]; +} + +/** + * Retrieve the basic default properties that are common to all elements. + */ +function element_basic_defaults() { + return array( + '#description' => '', + '#title' => '', + '#attributes' => array(), + '#required' => FALSE, + ); +} + +/** * Function used by uasort to sort structured arrays by weight, without the property weight prefix. */ function drupal_sort_weight($a, $b) { @@ -3410,7 +3477,13 @@ function element_child($key) { * Get keys of a structured array tree element that are not properties (i.e., do not begin with '#'). */ function element_children($element) { - return array_filter(array_keys((array) $element), 'element_child'); + $keys = array(); + foreach(array_keys($element) as $key) { + if ($key[0] !== '#') { + $keys[] = $key; + } + } + return $keys; } /** @@ -3564,24 +3637,15 @@ function drupal_common_theme() { 'radios' => array( 'arguments' => array('element' => NULL), ), - 'password_confirm' => array( - 'arguments' => array('element' => NULL), - ), 'date' => array( 'arguments' => array('element' => NULL), ), - 'item' => array( - 'arguments' => array('element' => NULL), - ), 'checkbox' => array( 'arguments' => array('element' => NULL), ), 'checkboxes' => array( 'arguments' => array('element' => NULL), ), - 'submit' => array( - 'arguments' => array('element' => NULL), - ), 'button' => array( 'arguments' => array('element' => NULL), ), @@ -3591,9 +3655,6 @@ function drupal_common_theme() { 'hidden' => array( 'arguments' => array('element' => NULL), ), - 'token' => array( - 'arguments' => array('element' => NULL), - ), 'textfield' => array( 'arguments' => array('element' => NULL), ), @@ -3616,7 +3677,7 @@ function drupal_common_theme() { 'arguments' => array('element' => NULL), ), 'form_element' => array( - 'arguments' => array('element' => NULL, 'value' => NULL), + 'arguments' => array('element' => NULL), ), ); } diff --git a/includes/form.inc b/includes/form.inc index 23973c8a0..5e911ed63 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -512,7 +512,7 @@ function drupal_prepare_form($form_id, &$form, &$form_state) { $form['#id'] = form_clean_id($form_id); } - $form += _element_info('form'); + $form += element_info('form'); $form += array('#tree' => FALSE, '#parents' => array()); if (!isset($form['#validate'])) { @@ -861,7 +861,7 @@ function form_builder($form_id, $form, &$form_state) { $form['#processed'] = FALSE; // Use element defaults. - if ((!empty($form['#type'])) && ($info = _element_info($form['#type']))) { + if ((!empty($form['#type'])) && ($info = element_info($form['#type']))) { // Overlay $info onto $form, retaining preexisting keys in $form. $form += $info; } @@ -1348,35 +1348,6 @@ function _form_set_value(&$form_values, $form_item, $parents, $value) { } } -/** - * Retrieve the default properties for the defined element type. - */ -function _element_info($type, $refresh = NULL) { - static $cache; - - $basic_defaults = array( - '#description' => NULL, - '#attributes' => array(), - '#required' => FALSE, - ); - if (!isset($cache) || $refresh) { - $cache = array(); - foreach (module_implements('elements') as $module) { - $elements = module_invoke($module, 'elements'); - if (isset($elements) && is_array($elements)) { - $cache = array_merge_recursive($cache, $elements); - } - } - if (sizeof($cache)) { - foreach ($cache as $element_type => $info) { - $cache[$element_type] = array_merge_recursive($basic_defaults, $info); - } - } - } - - return $cache[$type]; -} - function form_options_flatten($array, $reset = TRUE) { static $return; @@ -1419,7 +1390,7 @@ function theme_select($element) { $size = $element['#size'] ? ' size="' . $element['#size'] . '"' : ''; _form_set_class($element, array('form-select')); $multiple = $element['#multiple']; - return theme('form_element', $element, '<select name="' . $element['#name'] . '' . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) . ' id="' . $element['#id'] . '" ' . $size . '>' . form_select_options($element) . '</select>'); + return '<select name="' . $element['#name'] . '' . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple="multiple" ' : '') . drupal_attributes($element['#attributes']) . ' id="' . $element['#id'] . '" ' . $size . '>' . form_select_options($element) . '</select>'; } function form_select_options($element, $choices = NULL) { @@ -1555,8 +1526,7 @@ function theme_radio($element) { $output = '<label class="option" for="' . $element['#id'] . '">' . $output . ' ' . $element['#title'] . '</label>'; } - unset($element['#title']); - return theme('form_element', $element, $output); + return $output; } /** @@ -1576,28 +1546,8 @@ function theme_radios($element) { $class .= ' ' . $element['#attributes']['class']; } $element['#children'] = '<div class="' . $class . '">' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>'; - if ($element['#title'] || $element['#description']) { - unset($element['#id']); - return theme('form_element', $element, $element['#children']); - } - else { - return $element['#children']; - } -} -/** - * Format a password_confirm item. - * - * @param $element - * An associative array containing the properties of the element. - * Properties used: title, value, id, required, error. - * @return - * A themed HTML string representing the form item. - * - * @ingroup themeable - */ -function theme_password_confirm($element) { - return theme('form_element', $element, $element['#children']); + return $element['#children']; } /** @@ -1665,7 +1615,7 @@ function password_confirm_validate($form, &$form_state) { * @ingroup themeable */ function theme_date($element) { - return theme('form_element', $element, '<div class="container-inline">' . $element['#children'] . '</div>'); + return '<div class="container-inline">' . drupal_render_children($element) . '</div>'; } /** @@ -1881,6 +1831,8 @@ function form_process_input_format($element) { // We need to break references, otherwise form_builder recurses infinitely. $element['value'] = (array)$element; $element['#type'] = 'markup'; + $element['#theme'] = NULL; + $element['#theme_wrapper'] = NULL; $element['format'] = filter_form($element['#text_format'], 1, $element_parents); // We need to clear the #text_format from the new child otherwise we @@ -1977,21 +1929,6 @@ function form_process_ahah($element) { } /** - * Format a form item. - * - * @param $element - * An associative array containing the properties of the element. - * Properties used: title, value, description, required, error - * @return - * A themed HTML string representing the form item. - * - * @ingroup themeable - */ -function theme_item($element) { - return theme('form_element', $element, $element['#markup'] . (!empty($element['#children']) ? $element['#children'] : '')); -} - -/** * Format a checkbox. * * @param $element @@ -2016,8 +1953,7 @@ function theme_checkbox($element) { $checkbox = '<label class="option" for="' . $element['#id'] . '">' . $checkbox . ' ' . $element['#title'] . '</label>'; } - unset($element['#title']); - return theme('form_element', $element, $checkbox); + return $checkbox; } /** @@ -2036,13 +1972,21 @@ function theme_checkboxes($element) { $class .= ' ' . $element['#attributes']['class']; } $element['#children'] = '<div class="' . $class . '">' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>'; + + return $element['#children']; +} + +/** + * Add form_element theming to an element if title or desription is set. + * + * This is used as a pre render function for checkboxes and radios. + */ +function form_pre_render_conditional_form_element($element) { if ($element['#title'] || $element['#description']) { unset($element['#id']); - return theme('form_element', $element, $element['#children']); - } - else { - return $element['#children']; + $element['#theme_wrapper'] = 'form_element'; } + return $element; } function form_process_checkboxes($element) { @@ -2090,7 +2034,7 @@ function theme_tableselect($element) { $row = array(); // Render the checkbox / radio element. - $row[] = $element[$key]['#content']; + $row[] = drupal_render($element[$key]); // As theme_table only maps header and row columns by order, create the // correct order by iterating over the header fields. @@ -2244,15 +2188,6 @@ function theme_hidden($element) { } /** - * Format a form token. - * - * @ingroup themeable - */ -function theme_token($element) { - return theme('hidden', $element); -} - -/** * Format a textfield. * * @param $element @@ -2286,8 +2221,7 @@ function theme_textfield($element) { if (isset($element['#field_suffix'])) { $output .= ' <span class="field-suffix">' . $element['#field_suffix'] . '</span>'; } - - return theme('form_element', $element, $output) . $extra; + return $output . $extra; } /** @@ -2337,7 +2271,7 @@ function theme_textarea($element) { } _form_set_class($element, $class); - return theme('form_element', $element, '<textarea cols="' . $element['#cols'] . '" rows="' . $element['#rows'] . '" name="' . $element['#name'] . '" id="' . $element['#id'] . '" ' . drupal_attributes($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>'); + return '<textarea cols="' . $element['#cols'] . '" rows="' . $element['#rows'] . '" name="' . $element['#name'] . '" id="' . $element['#id'] . '" ' . drupal_attributes($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>'; } /** @@ -2355,7 +2289,7 @@ function theme_textarea($element) { */ function theme_markup($element) { - return (isset($element['#markup']) ? $element['#markup'] : '') . (isset($element['#children']) ? $element['#children'] : ''); + return (!empty($element['#markup']) ? $element['#markup'] : '') . drupal_render_children($element); } /** @@ -2375,7 +2309,7 @@ function theme_password($element) { _form_set_class($element, array('form-text')); $output = '<input type="password" name="' . $element['#name'] . '" id="' . $element['#id'] . '" ' . $maxlength . $size . drupal_attributes($element['#attributes']) . ' />'; - return theme('form_element', $element, $output); + return $output; } /** @@ -2388,7 +2322,7 @@ function form_process_weight($element) { $element['#options'] = $weights; $element['#type'] = 'select'; $element['#is_weight'] = TRUE; - $element += _element_info('select'); + $element += element_info('select'); return $element; } @@ -2415,7 +2349,7 @@ function form_process_weight($element) { */ function theme_file($element) { _form_set_class($element, array('form-file')); - return theme('form_element', $element, '<input type="file" name="' . $element['#name'] . '"' . ($element['#attributes'] ? ' ' . drupal_attributes($element['#attributes']) : '') . ' id="' . $element['#id'] . '" size="' . $element['#size'] . "\" />\n"); + return '<input type="file" name="' . $element['#name'] . '"' . ($element['#attributes'] ? ' ' . drupal_attributes($element['#attributes']) : '') . ' id="' . $element['#id'] . '" size="' . $element['#size'] . "\" />\n"; } /** @@ -2423,15 +2357,13 @@ function theme_file($element) { * * @param element * An associative array containing the properties of the element. - * Properties used: title, description, id, required - * @param $value - * The form element's data. + * Properties used: title, description, id, required, children * @return * A string representing the form element. * * @ingroup themeable */ -function theme_form_element($element, $value) { +function theme_form_element($element) { // This is also used in the installer, pre-database setup. $t = get_t(); @@ -2442,7 +2374,7 @@ function theme_form_element($element, $value) { $output .= ">\n"; $required = !empty($element['#required']) ? '<span class="form-required" title="' . $t('This field is required.') . '">*</span>' : ''; - if (!empty($element['#title'])) { + if (!empty($element['#title']) && empty($element['#form_element_skip_title'])) { $title = $element['#title']; if (!empty($element['#id'])) { $output .= ' <label for="' . $element['#id'] . '">' . $t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n"; @@ -2452,7 +2384,7 @@ function theme_form_element($element, $value) { } } - $output .= " $value\n"; + $output .= " " . $element['#children'] . "\n"; if (!empty($element['#description'])) { $output .= ' <div class="description">' . $element['#description'] . "</div>\n"; diff --git a/includes/locale.inc b/includes/locale.inc index 74b6efc7d..c5fcb592b 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -96,7 +96,7 @@ function theme_locale_languages_overview_form($form) { } $header = array(array('data' => t('English name')), array('data' => t('Native name')), array('data' => t('Code')), array('data' => t('Direction')), array('data' => t('Enabled')), array('data' => t('Default')), array('data' => t('Weight')), array('data' => t('Operations'))); $output = theme('table', $header, $rows, array('id' => 'language-order')); - $output .= drupal_render($form); + $output .= drupal_render_children($form); drupal_add_tabledrag('language-order', 'order', 'sibling', 'language-order-weight'); diff --git a/includes/theme.inc b/includes/theme.inc index 57989cc5b..606beebf1 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -1584,7 +1584,7 @@ function theme_item_list($items = array(), $title = NULL, $type = 'ul', $attribu */ function theme_list($elements) { // Populate any missing array elements with their defaults. - $elements += _element_info('list'); + $elements += element_info('list'); return theme('item_list', $elements['#items'], $elements['#title'], $elements['#list_type'], $elements['#attributes']); } |