Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.843 diff -u -F^f -r1.843 common.inc --- includes/common.inc 8 Jan 2009 19:09:49 -0000 1.843 +++ includes/common.inc 10 Jan 2009 02:38:15 -0000 @@ -3200,14 +3200,30 @@ function drupal_render(&$elements) { if (!isset($elements) || (isset($elements['#access']) && !$elements['#access'])) { return NULL; } + + // Never render elements twice. + // To force rerendering an element, unset #rendered_content. + if (isset($elements['#rendered_content'])) { + return $elements['#rendered_content']; + } // If the default values for this element haven't 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 (empty($elements['#defaults_loaded']) && isset($elements['#type'])) { + $elements += _element_info($elements['#type']); + } + $elements += array('#title' => NULL, '#description' => NULL); + + // BACKWARD COMPATIBILITY: + // I did not yet convert the actual elements and form_alters to the new structure. + // TODO: Remove this, of course. + if (!isset($elements['#theme_children']) && isset($elements['#theme'])) { + $elements['#theme_children'] = $elements['#theme']; } + if (empty($elements['#theme_wrapper'])) { + $elements['#theme_wrapper'][] = !empty($elements['#type']) ? $elements['#type'] : 'markup'; + } + // END BACKWARD COMPATIBILITY. // Make any final changes to the element before it is rendered. This means // that the $element or the children can be altered or corrected before the @@ -3220,70 +3236,65 @@ function drupal_render(&$elements) { } } - $content = ''; // Either the elements did not go through form_builder or one of the children // has a #weight. if (!isset($elements['#sorted'])) { uasort($elements, "element_sort"); } - $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]); - } - } + // Render all the element's children into a string. + // If #theme_children is set, this theme function will be called which is then + // responsible to render all children. If #theme_children is not set, the element's + // children will be concatenated into a string by drupal_render_children. + if (isset($elements['#theme_children'])) { + $elements['#children'] = theme($elements['#theme_children'], $elements); } - if (isset($content) && $content !== '') { - $elements['#children'] = $content; + else { + $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; - } - - 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); - } + + // Let all theme functions in #theme_wrapper add markup around the rendered + // children. + foreach ($elements['#theme_wrapper'] as $theme_wrapper) { + // TODO: Would propably be nicer to just pass in $element['#children'] as a $content + // or $children argument, but that would require a change to all form element theme + // functions so I left it as it was for now. + $elements['#children'] = theme($theme_wrapper, $elements); + } + + $content = $elements['#children']; + + // 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); } } - $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : ''; - $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : ''; - return $prefix . $content . $suffix; } + + $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : ''; + $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : ''; + + $elements['#rendered_content'] = $prefix . $content . $suffix; + return $elements['#rendered_content']; +} + + +/** + * 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. + */ +function drupal_render_children($element) { + $output = ''; + $children = element_children($element); + foreach ($children as $key) { + $output .= drupal_render($element[$key]); + } + return $output; } /** Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.310 diff -u -F^f -r1.310 form.inc --- includes/form.inc 30 Dec 2008 16:43:14 -0000 1.310 +++ includes/form.inc 10 Jan 2009 02:38:16 -0000 @@ -1347,15 +1347,21 @@ function _form_set_value(&$form_values, /** * Retrieve the default properties for the defined element type. + * @todo + * Remove leading underscore and move to common.inc? It's required by + * drupal_render. */ function _element_info($type, $refresh = NULL) { static $cache; $basic_defaults = array( '#description' => NULL, + '#title' => NULL, '#attributes' => array(), '#required' => FALSE, + '#theme_wrapper' => array(), ); + if (!isset($cache) || $refresh) { $cache = array(); foreach (module_implements('elements') as $module) { @@ -2139,7 +2145,6 @@ function theme_textfield($element) { if (isset($element['#field_suffix'])) { $output .= ' ' . $element['#field_suffix'] . ''; } - return theme('form_element', $element, $output) . $extra; }