diff options
Diffstat (limited to 'includes/common.inc')
-rw-r--r-- | includes/common.inc | 211 |
1 files changed, 136 insertions, 75 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), ), ); } |