summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2009-02-03 18:55:32 +0000
committerDries Buytaert <dries@buytaert.net>2009-02-03 18:55:32 +0000
commit29c8e40e912f8975011224f824977b2353fe07b5 (patch)
treebb6a3341259232340b88ca120cf349c57404c13b /includes
parent607e9626d5af265b18e8319b156bb0fda3445cd4 (diff)
downloadbrdo-29c8e40e912f8975011224f824977b2353fe07b5.tar.gz
brdo-29c8e40e912f8975011224f824977b2353fe07b5.tar.bz2
- Patch #355236 by Frando: refactor drupal_render() theming.
Diffstat (limited to 'includes')
-rw-r--r--includes/common.inc211
-rw-r--r--includes/form.inc132
-rw-r--r--includes/locale.inc2
-rw-r--r--includes/theme.inc2
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']);
}