Commit a0b6d650 authored by catch's avatar catch

Issue #891112 by Berdir, sun, Amitaibu, siharris, Cottser: Replace...

Issue #891112 by Berdir, sun, Amitaibu, siharris, Cottser: Replace theme_item_list()'s 'data' items with render elements.
parent a0de1621
......@@ -5384,27 +5384,6 @@ function drupal_pre_render_dropbutton($element) {
return $element;
}
/**
* Pre-render callback: Appends contents in #markup to #children.
*
* This needs to be a #pre_render callback, because eventually assigned
* #theme_wrappers will expect the element's rendered content in #children.
* Note that if also a #theme is defined for the element, then the result of
* the theme callback will override #children.
*
* @param $elements
* A structured array using the #markup key.
*
* @return
* The passed-in elements, but #markup appended to #children.
*
* @see drupal_render()
*/
function drupal_pre_render_markup($elements) {
$elements['#children'] = $elements['#markup'];
return $elements;
}
/**
* Renders the page, including all theming.
*
......@@ -5587,11 +5566,20 @@ function drupal_render(&$elements) {
// If #theme was not set and the element has children, render them now.
// This is the same process as drupal_render_children() but is inlined
// for speed.
if ($elements['#children'] == '') {
if ($elements['#children'] === '') {
foreach ($children as $key) {
$elements['#children'] .= drupal_render($elements[$key]);
}
}
// If #theme was not set, but the element has raw #markup, prepend the content
// in #markup to #children. #children may contain the rendered content
// supplied by #theme, or the rendered child elements, as processed above. If
// both #theme and #markup are set, then #theme is responsible for rendering
// the element. Eventually assigned #theme_wrappers will expect both the
// element's #markup and the rendered content of child elements in #children.
if (!isset($elements['#theme']) && isset($elements['#markup'])) {
$elements['#children'] = $elements['#markup'] . $elements['#children'];
}
// Let the theme functions in #theme_wrappers add markup around the rendered
// children.
......
......@@ -237,14 +237,14 @@ function theme_pager($variables) {
if ($pager_total[$element] > 1) {
if ($li_first) {
$items[] = array(
'class' => array('pager-first'),
'data' => $li_first,
'#wrapper_attributes' => array('class' => array('pager-first')),
'#markup' => $li_first,
);
}
if ($li_previous) {
$items[] = array(
'class' => array('pager-previous'),
'data' => $li_previous,
'#wrapper_attributes' => array('class' => array('pager-previous')),
'#markup' => $li_previous,
);
}
......@@ -252,16 +252,16 @@ function theme_pager($variables) {
if ($i != $pager_max) {
if ($i > 1) {
$items[] = array(
'class' => array('pager-ellipsis'),
'data' => '…',
'#wrapper_attributes' => array('class' => array('pager-ellipsis')),
'#markup' => '…',
);
}
// Now generate the actual pager piece.
for (; $i <= $pager_last && $i <= $pager_max; $i++) {
if ($i < $pager_current) {
$items[] = array(
'class' => array('pager-item'),
'data' => theme('pager_link', array(
'#wrapper_attributes' => array('class' => array('pager-item')),
'#markup' => theme('pager_link', array(
'text' => $i,
'page_new' => pager_load_array($i - 1, $element, $pager_page_array),
'element' => $element,
......@@ -272,14 +272,14 @@ function theme_pager($variables) {
}
if ($i == $pager_current) {
$items[] = array(
'class' => array('pager-current'),
'data' => $i,
'#wrapper_attributes' => array('class' => array('pager-current')),
'#markup' => $i,
);
}
if ($i > $pager_current) {
$items[] = array(
'class' => array('pager-item'),
'data' => theme('pager_link', array(
'#wrapper_attributes' => array('class' => array('pager-item')),
'#markup' => theme('pager_link', array(
'text' => $i,
'page_new' => pager_load_array($i - 1, $element, $pager_page_array),
'element' => $element,
......@@ -291,22 +291,22 @@ function theme_pager($variables) {
}
if ($i < $pager_max) {
$items[] = array(
'class' => array('pager-ellipsis'),
'data' => '…',
'#wrapper_attributes' => array('class' => array('pager-ellipsis')),
'#markup' => '…',
);
}
}
// End generation.
if ($li_next) {
$items[] = array(
'class' => array('pager-next'),
'data' => $li_next,
'#wrapper_attributes' => array('class' => array('pager-next')),
'#markup' => $li_next,
);
}
if ($li_last) {
$items[] = array(
'class' => array('pager-last'),
'data' => $li_last,
'#wrapper_attributes' => array('class' => array('pager-last')),
'#markup' => $li_last,
);
}
return '<h2 class="element-invisible">' . t('Pages') . '</h2>' . theme('item_list', array(
......
......@@ -937,6 +937,9 @@ function theme($hook, $variables = array()) {
}
$hook = $candidate;
}
// Save the original theme hook, so it can be supplied to theme variable
// preprocess callbacks.
$original_hook = $hook;
// If there's no implementation, check for more generic fallbacks. If there's
// still no implementation, log an error and return an empty string.
......@@ -996,6 +999,10 @@ function theme($hook, $variables = array()) {
elseif (!empty($info['render element'])) {
$variables += array($info['render element'] => array());
}
// Supply original caller info.
$variables += array(
'theme_hook_original' => $original_hook,
);
// Invoke the variable processors, if any. The processors may specify
// alternate suggestions for which hook's template/function to use. If the
......@@ -2121,20 +2128,61 @@ function theme_mark($variables) {
}
}
/**
* Preprocesses variables for theme_item_list().
*
* @param array $variables
* An associative array containing theme variables for theme_item_list().
* 'items' in variables will be processed to automatically inherit the
* variables of this list to any possibly contained nested lists that do not
* specify custom render properties. This allows callers to specify larger
* nested lists, without having to explicitly specify and repeat the render
* properties for all nested child lists.
*/
function template_preprocess_item_list(&$variables) {
foreach ($variables['items'] as &$item) {
// If the item value is an array, then it is a render array.
if (is_array($item)) {
// Determine whether there are any child elements in the item that are not
// fully-specified render arrays. If there are any, then the child
// elements present nested lists and we automatically inherit the render
// array properties of the current list to them.
foreach (element_children($item) as $key) {
$child = &$item[$key];
// If this child element does not specify how it can be rendered, then
// we need to inherit the render properties of the current list.
if (!isset($child['#type']) && !isset($child['#theme']) && !isset($child['#markup'])) {
// Since theme_item_list() supports both strings and render arrays as
// items, the items of the nested list may have been specified as the
// child elements of the nested list, instead of #items. For
// convenience, we automatically move them into #items.
if (!isset($child['#items'])) {
// This is the same condition as in element_children(), which cannot
// be used here, since it triggers an error on string values.
foreach ($child as $child_key => $child_value) {
if ($child_key[0] !== '#') {
$child['#items'][$child_key] = $child_value;
unset($child[$child_key]);
}
}
}
// Lastly, inherit the original theme variables of the current list.
$child['#theme'] = $variables['theme_hook_original'];
$child['#type'] = $variables['type'];
}
}
}
}
}
/**
* Returns HTML for a list or nested list of items.
*
* @param $variables
* An associative array containing:
* - items: A list of items to render. String values are rendered as is. Each
* item can also be an associative array containing:
* - data: The string content of the list item.
* - children: A list of nested child items to render that behave
* identically to 'items', but any non-numeric string keys are treated as
* HTML attributes for the child list that wraps 'children'.
* - type: The type of list to return (e.g. "ul", "ol").
* Any other key/value pairs are used as HTML attributes for the list item
* in 'data'.
* - items: A list of items to render. Allowed values are strings or
* render arrays. Render arrays can specify list item attributes in the
* #wrapper_attributes property.
* - title: The title of the list.
* - type: The type of list to return (e.g. "ul", "ol").
* - attributes: The attributes applied to the list element.
......@@ -2142,6 +2190,7 @@ function theme_mark($variables) {
function theme_item_list($variables) {
$items = $variables['items'];
$title = (string) $variables['title'];
// @todo 'type' clashes with '#type'. Rename to 'tag'.
$type = $variables['type'];
$list_attributes = $variables['attributes'];
......@@ -2151,40 +2200,15 @@ function theme_item_list($variables) {
$num_items = count($items);
$i = 0;
foreach ($items as $key => $item) {
foreach ($items as &$item) {
$i++;
$attributes = array();
if (is_array($item)) {
$value = '';
if (isset($item['data'])) {
$value .= $item['data'];
}
$attributes = array_diff_key($item, array('data' => 0, 'children' => 0, 'type' => 0));
// Append nested child list, if any.
if (isset($item['children'])) {
// HTML attributes for the outer list are defined in the 'attributes'
// theme variable, but not inherited by children. For nested lists,
// all non-numeric keys in 'children' are used as list attributes.
$child_list_attributes = array();
foreach ($item['children'] as $child_key => $child_item) {
if (is_string($child_key)) {
$child_list_attributes[$child_key] = $child_item;
unset($item['children'][$child_key]);
}
}
$value .= theme('item_list', array(
'items' => $item['children'],
'type' => (isset($item['type']) ? $item['type'] : $type),
'attributes' => $child_list_attributes,
));
if (isset($item['#wrapper_attributes'])) {
$attributes = $item['#wrapper_attributes'];
}
$item = drupal_render($item);
}
else {
$value = $item;
}
$attributes['class'][] = ($i % 2 ? 'odd' : 'even');
if ($i == 1) {
$attributes['class'][] = 'first';
......@@ -2192,8 +2216,7 @@ function theme_item_list($variables) {
if ($i == $num_items) {
$attributes['class'][] = 'last';
}
$output .= '<li' . new Attribute($attributes) . '>' . $value . '</li>';
$output .= '<li' . new Attribute($attributes) . '>' . $item . '</li>';
}
$output .= "</$type>";
}
......
......@@ -43,39 +43,82 @@ function testItemList() {
'id' => 'parentlist',
);
$variables['items'] = array(
// A plain string value forms an own item.
'a',
// Items can be fully-fledged render arrays with their own attributes.
array(
'data' => 'b',
'children' => array(
'c',
// Nested children may use additional attributes.
'#wrapper_attributes' => array(
'id' => 'item-id-b',
),
'#markup' => 'b',
'childlist' => array(
'#theme' => 'item_list',
'#attributes' => array('id' => 'blist'),
'#type' => 'ol',
'#items' => array(
'ba',
array(
'#markup' => 'bb',
'#wrapper_attributes' => array('class' => array('item-class-bb')),
),
),
),
),
// However, items can also be child #items.
array(
'#markup' => 'c',
'childlist' => array(
'#attributes' => array('id' => 'clist'),
'ca',
array(
'data' => 'd',
'class' => array('dee'),
'#markup' => 'cb',
'#wrapper_attributes' => array('class' => array('item-class-cb')),
'children' => array(
'cba',
'cbb',
),
),
// Any string key is treated as child list attribute.
'id' => 'childlist',
'cc',
),
// Any other keys are treated as item attributes.
'id' => 'bee',
'type' => 'ol',
),
// Use #markup to be able to specify #wrapper_attributes.
array(
'data' => 'e',
'id' => 'E',
'#markup' => 'd',
'#wrapper_attributes' => array('id' => 'item-id-d'),
),
// An empty item with attributes.
array(
'#wrapper_attributes' => array('id' => 'item-id-e'),
),
// Lastly, another plain string item.
'f',
);
$inner = '<div class="item-list"><ol id="childlist">';
$inner .= '<li class="odd first">c</li>';
$inner .= '<li class="dee even last">d</li>';
$inner .= '</ol></div>';
$inner_b = '<div class="item-list"><ol id="blist">';
$inner_b .= '<li class="odd first">ba</li>';
$inner_b .= '<li class="item-class-bb even last">bb</li>';
$inner_b .= '</ol></div>';
$inner_cb = '<div class="item-list"><ul>';
$inner_cb .= '<li class="odd first">cba</li>';
$inner_cb .= '<li class="even last">cbb</li>';
$inner_cb .= '</ul></div>';
$inner_c = '<div class="item-list"><ul id="clist">';
$inner_c .= '<li class="odd first">ca</li>';
$inner_c .= '<li class="item-class-cb even">cb' . $inner_cb . '</li>';
$inner_c .= '<li class="odd last">cc</li>';
$inner_c .= '</ul></div>';
$expected = '<div class="item-list">';
$expected .= '<h3>Some title</h3>';
$expected .= '<ul id="parentlist">';
$expected .= '<li class="odd first">a</li>';
$expected .= '<li id="bee" class="even">b' . $inner . '</li>';
$expected .= '<li id="E" class="odd last">e</li>';
$expected .= '<li id="item-id-b" class="even">b' . $inner_b . '</li>';
$expected .= '<li class="odd">c' . $inner_c . '</li>';
$expected .= '<li id="item-id-d" class="even">d</li>';
$expected .= '<li id="item-id-e" class="odd"></li>';
$expected .= '<li class="even last">f</li>';
$expected .= '</ul></div>';
$this->assertThemeOutput('item_list', $variables, $expected);
......
......@@ -504,7 +504,6 @@ function system_element_info() {
// Form structure.
$types['item'] = array(
'#markup' => '',
'#pre_render' => array('drupal_pre_render_markup'),
'#theme_wrappers' => array('form_element'),
);
$types['hidden'] = array(
......@@ -517,10 +516,9 @@ function system_element_info() {
);
$types['markup'] = array(
'#markup' => '',
'#pre_render' => array('drupal_pre_render_markup'),
);
$types['link'] = array(
'#pre_render' => array('drupal_pre_render_link', 'drupal_pre_render_markup'),
'#pre_render' => array('drupal_pre_render_link'),
);
$types['fieldset'] = array(
'#collapsible' => FALSE,
......@@ -4208,7 +4206,7 @@ function theme_exposed_filters($variables) {
if (isset($form['current'])) {
$items = array();
foreach (element_children($form['current']) as $key) {
$items[] = drupal_render($form['current'][$key]);
$items[] = $form['current'][$key];
}
$output .= theme('item_list', array('items' => $items, 'attributes' => array('class' => array('clearfix', 'current-filters'))));
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment