Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.973 diff -u -r1.973 node.module --- modules/node/node.module 31 Aug 2008 15:50:35 -0000 1.973 +++ modules/node/node.module 6 Sep 2008 03:14:12 -0000 @@ -1404,7 +1404,11 @@ $row[] = drupal_render($form['factors'][$key]); $rows[] = $row; } - $output .= theme('table', $header, $rows); + $table = array( + '#header' => $header, + '#rows' => $rows, + ) + _element_info('table'); + $output .= theme('table', $table); $output .= drupal_render($form); return $output; @@ -1795,17 +1799,17 @@ function node_page_default() { $result = pager_query(db_rewrite_sql('SELECT n.nid, n.sticky, n.created FROM {node} n WHERE n.promote = 1 AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC'), variable_get('default_nodes_main', 10)); - $output = ''; + $output = array(); $num_rows = FALSE; while ($node = db_fetch_object($result)) { - $output .= node_view(node_load($node->nid), 1); + $output[] = array('#markup' => node_view(node_load($node->nid), 1)); $num_rows = TRUE; } if ($num_rows) { $feed_url = url('rss.xml', array('absolute' => TRUE)); drupal_add_feed($feed_url, variable_get('site_name', 'Drupal') . ' ' . t('RSS')); - $output .= theme('pager', NULL, variable_get('default_nodes_main', 10)); + $output[] = array('#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10))); } else { $default_message = '

' . t('Welcome to your new Drupal website!') . '

'; @@ -1818,7 +1822,7 @@ $default_message .= ''; $default_message .= '

' . t('For more information, please refer to the help section, or the online Drupal handbooks. You may also post at the Drupal forum, or view the wide range of other support options available.', array('@help' => url('admin/help'), '@handbook' => 'http://drupal.org/handbooks', '@forum' => 'http://drupal.org/forum', '@support' => 'http://drupal.org/support')) . '

'; - $output = '
' . $default_message . '
'; + $output = array('#markup' => '
' . $default_message . '
'); } drupal_set_title(''); Index: modules/node/node.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.admin.inc,v retrieving revision 1.24 diff -u -r1.24 node.admin.inc --- modules/node/node.admin.inc 19 Jul 2008 19:04:24 -0000 1.24 +++ modules/node/node.admin.inc 6 Sep 2008 03:14:12 -0000 @@ -585,8 +585,11 @@ else { $rows[] = array(array('data' => t('No posts available.'), 'colspan' => '6')); } - - $output .= theme('table', $header, $rows); + $table = array( + '#header' => $header, + '#rows' => $rows, + ) + _element_info('table'); + $output .= theme('table', $table); if ($form['pager']['#markup']) { $output .= drupal_render($form['pager']); } Index: modules/node/content_types.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/node/content_types.inc,v retrieving revision 1.57 diff -u -r1.57 content_types.inc --- modules/node/content_types.inc 16 Jul 2008 21:59:27 -0000 1.57 +++ modules/node/content_types.inc 6 Sep 2008 03:14:12 -0000 @@ -42,7 +42,11 @@ $rows[] = array(array('data' => t('No content types available.'), 'colspan' => '5', 'class' => 'message')); } - return theme('table', $header, $rows); + return array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + ); } /** Index: modules/filter/filter.module =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v retrieving revision 1.224 diff -u -r1.224 filter.module --- modules/filter/filter.module 5 Sep 2008 09:25:52 -0000 1.224 +++ modules/filter/filter.module 6 Sep 2008 03:14:12 -0000 @@ -233,7 +233,11 @@ ); } } - $output .= theme('table', $header, $rows); + $table = array( + '#header' => $header, + '#rows' => $rows, + ) + _element_info('table'); + $output .= theme('table', $table); $output .= '

' . t('Most unusual characters can be directly entered without any problems.') . '

'; $output .= '

' . t('If you do encounter problems, try using HTML character entities. A common example looks like & for an ampersand & character. For a full list of entities see HTML\'s entities page. Some of the available characters include:', array('@html-entities' => 'http://www.w3.org/TR/html4/sgml/entities.html')) . '

'; @@ -253,7 +257,11 @@ array('data' => $entity[1], 'class' => 'get') ); } - $output .= theme('table', $header, $rows); + $table = array( + '#header' => $header, + '#rows' => $rows, + ) + _element_info('table'); + $output .= theme('table', $table); return $output; } } Index: modules/filter/filter.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.admin.inc,v retrieving revision 1.13 diff -u -r1.13 filter.admin.inc --- modules/filter/filter.admin.inc 24 Jul 2008 16:27:51 -0000 1.13 +++ modules/filter/filter.admin.inc 6 Sep 2008 03:14:12 -0000 @@ -80,8 +80,12 @@ unset($form[$id]); } } - $header = array(t('Name'), t('Roles'), t('Default'), t('Weight'), array('data' => t('Operations'), 'colspan' => 2)); - $output = theme('table', $header, $rows, array('id' => 'input-format-order')); + $table = array( + '#header' => array(t('Name'), t('Roles'), t('Default'), t('Weight'), array('data' => t('Operations'), 'colspan' => 2)), + '#rows' => $rows, + '#attributes' => array('id' => 'input-format-order'), + ) + _element_info('table'); + $output = theme('table', $table); $output .= drupal_render($form); drupal_add_tabledrag('input-format-order', 'order', 'sibling', 'input-format-order-weight'); @@ -375,7 +379,6 @@ * @ingroup themeable */ function theme_filter_admin_order($form) { - $header = array(t('Name'), t('Weight')); $rows = array(); foreach (element_children($form['names']) as $id) { // Don't take form control structures. @@ -387,8 +390,12 @@ ); } } - - $output = theme('table', $header, $rows, array('id' => 'filter-order')); + $table = array( + '#header' => array(t('Name'), t('Weight')), + '#rows' => $rows, + '#attributes' => array('id' => 'filter-order'), + ) + _element_info('table'); + $output = theme('table', $table); $output .= drupal_render($form); drupal_add_tabledrag('filter-order', 'order', 'sibling', 'filter-order-weight', NULL, NULL, FALSE); Index: modules/openid/openid.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/openid/openid.pages.inc,v retrieving revision 1.6 diff -u -r1.6 openid.pages.inc --- modules/openid/openid.pages.inc 14 Apr 2008 17:48:38 -0000 1.6 +++ modules/openid/openid.pages.inc 6 Sep 2008 03:14:12 -0000 @@ -47,8 +47,12 @@ $rows[] = array($identity->authname, l(t('Delete'), 'user/' . $account->uid . '/openid/delete/' . $identity->aid)); } - $output = theme('table', $header, $rows); - $output .= drupal_get_form('openid_user_add'); + $output['accounts'] = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + ); + $output['openid_user_add'] = drupal_get_form('openid_user_add'); return $output; } Index: modules/comment/comment.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.admin.inc,v retrieving revision 1.10 diff -u -r1.10 comment.admin.inc --- modules/comment/comment.admin.inc 16 Jul 2008 21:59:26 -0000 1.10 +++ modules/comment/comment.admin.inc 6 Sep 2008 03:14:12 -0000 @@ -167,8 +167,12 @@ else { $rows[] = array(array('data' => t('No comments available.'), 'colspan' => '6')); } + $table = array( + '#header' => $form['header']['#value'], + '#rows' => $rows, + ) + _element_info('table'); + $output .= theme('table', $table); - $output .= theme('table', $form['header']['#value'], $rows); if ($form['pager']['#markup']) { $output .= drupal_render($form['pager']); } Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.647 diff -u -r1.647 comment.module --- modules/comment/comment.module 21 Aug 2008 19:36:36 -0000 1.647 +++ modules/comment/comment.module 6 Sep 2008 03:14:12 -0000 @@ -981,7 +981,11 @@ $comments .= ''; } $output .= $comments; - $output .= theme('pager', NULL, $comments_per_page, 0); + $element = array( + '#type' => 'pager', + '#limit' => $comments_per_page, + ) + _element_info('pager'); + $output .= theme('pager', $element); } // If enabled, show new comment form if it's not already being displayed. @@ -1408,7 +1412,7 @@ * The form title. */ function comment_form_box($edit, $title = NULL) { - return theme('box', $title, drupal_get_form('comment_form', $edit, $title)); + return theme('box', $title, drupal_render(drupal_get_form('comment_form', $edit, $title))); } /** Index: includes/theme.maintenance.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.maintenance.inc,v retrieving revision 1.16 diff -u -r1.16 theme.maintenance.inc --- includes/theme.maintenance.inc 21 Aug 2008 19:36:36 -0000 1.16 +++ includes/theme.maintenance.inc 6 Sep 2008 03:14:12 -0000 @@ -164,7 +164,7 @@ drupal_set_header('Content-Type: text/html; charset=utf-8'); // Assign content and show message flag. - $variables['content'] = $content; + $variables['elements']['content'] = array('#value' => $content); $variables['show_messages'] = $show_messages; // The maintenance preprocess function is recycled here. template_preprocess_maintenance_page($variables); @@ -239,6 +239,8 @@ $head_title[] = variable_get('site_slogan', ''); } } + + $variables['content'] = &$variables['elements']['content']); $variables['head_title'] = implode(' | ', $head_title); $variables['base_path'] = base_path(); $variables['front_page'] = url(); Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.788 diff -u -r1.788 common.inc --- includes/common.inc 21 Aug 2008 19:36:36 -0000 1.788 +++ includes/common.inc 6 Sep 2008 03:14:12 -0000 @@ -47,20 +47,18 @@ * @param $region * A specified region to fetch content for. If NULL, all regions will be * returned. - * @param $delimiter - * Content to be inserted between exploded array elements. */ -function drupal_get_content($region = NULL, $delimiter = ' ') { +function drupal_get_content($region = NULL) { $content = drupal_set_content(); if (isset($region)) { if (isset($content[$region]) && is_array($content[$region])) { - return implode($delimiter, $content[$region]); + return $content[$region]; } } else { foreach (array_keys($content) as $region) { - if (is_array($content[$region])) { - $content[$region] = implode($delimiter, $content[$region]); + if (!is_array($content[$region])) { + $content[$region] = array(); } } return $content; @@ -327,8 +325,15 @@ drupal_maintenance_theme(); drupal_set_header('HTTP/1.1 503 Service unavailable'); drupal_set_title(t('Site offline')); - print theme('maintenance_page', filter_xss_admin(variable_get('site_offline_message', - t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')))))); + + $elements['page'] = array( + '#title' => drupal_get_title(), + '#theme' => 'maintenance_page', + '#content' => array( + '#markup' => filter_xss_admin(variable_get('site_offline_message', t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal'))))), + ), + ); + return $elements; } /** @@ -354,11 +359,11 @@ if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) { drupal_set_title(t('Page not found')); - $return = t('The requested page could not be found.'); + $return = array('#markup' => t('The requested page could not be found.')); } // To conserve CPU and bandwidth, omit the blocks. - print theme('page', $return, FALSE); + return drupal_build_page($return, FALSE, TRUE, FALSE); } /** @@ -383,9 +388,122 @@ if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) { drupal_set_title(t('Access denied')); - $return = t('You are not authorized to access this page.'); + $return = array('#markup' => t('You are not authorized to access this page.')); + } + return drupal_build_page($return, TRUE, TRUE, FALSE); +} + +/** + * Build a structured array with page content and theme parameters. + */ +function drupal_build_page($content, $show_blocks = TRUE, $show_messages = TRUE, $success = TRUE) { + if (!is_array($content)) { + $content = array('#markup' => $content); } - print theme('page', $return); + $elements = array( + '#title' => drupal_get_title(), + '#theme' => 'page', + '#show_blocks' => $show_blocks, + '#show_messages' => $show_messages, + '#success' => $success, + 'content' => $content, + ); + + global $theme; + + $regions = system_region_list($theme); + // Load all region content assigned via blocks. + foreach (array_keys($regions) as $region) { + if (!isset($elements[$region])) { + $elements[$region] = array(); + } + // Prevent left and right regions from rendering blocks when 'show_blocks' == FALSE. + if (!(!$show_blocks && ($region == 'left' || $region == 'right'))) { + $elements[$region]['blocks'] = array('#markup' => theme('blocks', $region)); + } + } + + // Alter any part of the page before it's rendered. + drupal_alter('page', $elements); + return page_elements_builder($elements); +} + +/** + * Adds any required properties non-form elements on the page. + * + * Similar to form_builder(). + */ +function page_elements_builder($elements) { + // Use element defaults. + if ((!empty($elements['#type'])) && ($info = _element_info($elements['#type']))) { + // Overlay $info onto $elements, retaining preexisting keys. + $elements += $info; + } + + // We start off assuming all elements are in the correct order. + $elements['#sorted'] = TRUE; + + // Recurse through all child elements. + $count = 0; + foreach (element_children($elements) as $key) { + // Assign a decimal placeholder weight to preserve original array order. + if (!isset($form[$key]['#weight'])) { + $elements[$key]['#weight'] = $count/1000; + } + else { + // If one of the child elements has a weight then we will need to sort + // later. + unset($elements['#sorted']); + } + if (empty($elements[$key]['#type']) || $elements[$key]['#type'] != 'form') { + $elements[$key] = page_elements_builder($elements[$key]); + } + $count++; + } + return $elements; +} + +function drupal_render_page($elements) { + $render_type = ''; + if (isset($_GET['render'])) { + $render_type = $_GET['render']; + } + // jQuery sets a HTTP_X_REQUESTED_WITH header of 'XMLHttpRequest'. + if (!$render_type) { + // Set the render type based on the request header. + if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { + $render_type = 'json'; + } + else { + $render_type = 'xhtml'; + } + } + switch ($render_type) { + case 'xhtml': + case 'html': + $render = 'drupal_render'; + break; + + default: + $render = 'drupal_' . $render_type . '_render'; + if (!drupal_function_exists($render)) { + $render = 'drupal_render'; + } + break; + } + + return $render($elements); +} + +/** + * Render the elements into simple XML. + */ +function drupal_xml_render($elements) { + return drupal_render($elements, 'theme_xml'); +} + +function render(&$element) { + echo drupal_render($element); } /** @@ -2723,10 +2841,13 @@ * * @param $elements * The structured array describing the data to be rendered. + * @param $callback + * Optional parameter defining which function to use to mediate conversion of + * each elemement to a string. Use the default, 'theme', for XHTML. * @return - * The rendered HTML. + * The rendered content (XHTML or other depending on $callback). */ -function drupal_render(&$elements) { +function drupal_render(&$elements, $callback = 'theme') { if (!isset($elements) || (isset($elements['#access']) && !$elements['#access'])) { return NULL; } @@ -2745,7 +2866,7 @@ if (isset($elements['#pre_render'])) { foreach ($elements['#pre_render'] as $function) { if (drupal_function_exists($function)) { - $elements = $function($elements); + $elements = $function($elements, $callback); } } } @@ -2776,7 +2897,7 @@ } unset($elements['#prefix'], $elements['#suffix']); - $content = theme($elements['#theme'], $elements); + $content = $callback($elements['#theme'], $elements); foreach (array('#type', '#prefix', '#suffix') as $key) { $elements[$key] = isset($previous[$key]) ? $previous[$key] : NULL; @@ -2785,7 +2906,7 @@ // Render each of the children using drupal_render and concatenate them. if (!isset($content) || $content === '') { foreach ($children as $key) { - $content .= drupal_render($elements[$key]); + $content .= drupal_render($elements[$key], $callback); } } } @@ -2795,7 +2916,8 @@ // 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); + // The default $callback is 'theme'. + $content = $callback(!empty($elements['#type']) ? $elements['#type'] : 'markup', $elements); $elements['#printed'] = TRUE; } @@ -2806,7 +2928,7 @@ if (isset($elements['#post_render'])) { foreach ($elements['#post_render'] as $function) { if (drupal_function_exists($function)) { - $content = $function($content, $elements); + $content = $function($content, $elements, $callback); } } } @@ -2816,6 +2938,139 @@ } } + +/** + * Renders elements of structured array tree, retaining the structure. + * + * Recursively iterates over each of the array elements, generating output. + * This function is usually called from within a another function. + * + * @param $elements + * The structured array describing the data to be rendered. + * @param $callback + * Optional parameter defining which funciton to use to mediate conversion of + * each elemement to a string. Use the default, 'theme', for HTML. + * @return + * The rendered HTML. + */ +function drupal_render_array(&$elements, $callback = 'theme') { + if (!isset($elements) || (isset($elements['#access']) && !$elements['#access'])) { + $elements = NULL; + } + + // 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; + } + } + + // 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 + // element is rendered into the final text. + if (isset($elements['#pre_render'])) { + foreach ($elements['#pre_render'] as $function) { + if (drupal_function_exists($function)) { + $elements = $function($elements, $callback); + } + } + } + + $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 (!$children && 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. + $elements['#printed'] = TRUE; + $elements['#type'] = 'markup'; + $elements['#value'] = ''; + + unset($elements['#prefix'], $elements['#suffix']); + + $args[] = $elements['#theme']; + $args[] = $elements; + if (!empty($elements['#theme arguments'])) { + foreach($elements['#theme arguments'] as $value) { + $args[] = $value; + } + } + // The default $callback is 'theme'. + $content = call_user_func_array($callback, $args); + + foreach (array('#type', '#prefix', '#suffix') as $key) { + $elements[$key] = isset($previous[$key]) ? $previous[$key] : NULL; + } + } + // Render each of the children using drupal_render_array + if (!isset($content) || $content === '') { + foreach ($children as $key) { + drupal_render_array($elements[$key], $callback); + } + } + } + + // Until now, we rendered the children, here we render the element itself + if (!isset($elements['#printed']) && !$children) { + // The default $callback is 'theme'. + $content = $callback(!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, $callback); + } + } + } + $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : ''; + $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : ''; + $elements['#value'] = $prefix . $content . $suffix; + } +} + +function element_property_preserve_in_array($key) { + switch ($key) { + case '#title': + case '#value': + case '#type': + return TRUE; + + default: + return FALSE; + } +} + +function drupal_json_render($elements) { + drupal_set_header('Content-Type: text/javascript; charset=utf-8'); + drupal_render_array($elements); + return drupal_to_js($elements); +} + +function drupal_printr_render($elements) { + drupal_set_header('Content-Type: text/plain; charset=utf-8'); + drupal_render_array($elements); + return print_r($elements, 1); +} + /** * Function used by uasort to sort structured arrays by weight. */ @@ -2866,18 +3121,18 @@ 'arguments' => array('text' => NULL) ), 'page' => array( - 'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE), + 'arguments' => array('elements' => NULL), 'template' => 'page', ), 'maintenance_page' => array( - 'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE), + 'arguments' => array('elements' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE), 'template' => 'maintenance-page', ), 'update_page' => array( - 'arguments' => array('content' => NULL, 'show_messages' => TRUE), + 'arguments' => array('elements' => NULL, 'show_messages' => TRUE), ), 'install_page' => array( - 'arguments' => array('content' => NULL), + 'arguments' => array('elements' => NULL), ), 'task_list' => array( 'arguments' => array('items' => NULL, 'active' => NULL), Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.433 diff -u -r1.433 theme.inc --- includes/theme.inc 2 Sep 2008 17:38:55 -0000 1.433 +++ includes/theme.inc 6 Sep 2008 03:14:12 -0000 @@ -479,6 +479,14 @@ } /** + * Dummy function for now. + */ +function theme_xml() { + $args = func_get_args(); + return call_user_func_array('theme', $args); +} + +/** * Generate the themed output. * * All requests for theme hooks must go through this function. It examines @@ -640,12 +648,9 @@ } if (isset($info['preprocess functions']) && is_array($info['preprocess functions'])) { - // This construct ensures that we can keep a reference through - // call_user_func_array. - $args = array(&$variables, $hook); foreach ($info['preprocess functions'] as $preprocess_function) { if (drupal_function_exists($preprocess_function)) { - call_user_func_array($preprocess_function, $args); + $preprocess_function($variables, $hook); } } } @@ -1281,7 +1286,13 @@ * @return * An HTML string representing the table. */ -function theme_table($header, $rows, $attributes = array(), $caption = NULL, $colgroups = array()) { +function theme_table($element) { + + $header = $element['#header']; + $rows = $element['#rows']; + $attributes = $element['#attributes']; + $caption = $element['#caption']; + $colgroups = $element['#colgroups']; // Add sticky headers, if applicable. if (count($header)) { @@ -1481,7 +1492,12 @@ * @return * A string containing the list output. */ -function theme_item_list($items = array(), $title = NULL, $type = 'ul', $attributes = array()) { +function theme_item_list($list) { + $items = $list['#items']; + $title = $list['#title']; + $type = $list['#list_type']; + $attributes = $list['#attributes']; + $output = '
'; if (isset($title)) { $output .= '

' . $title . '

'; @@ -1789,17 +1805,8 @@ global $theme; // Populate all block regions. $regions = system_region_list($theme); - // Load all region content assigned via blocks. foreach (array_keys($regions) as $region) { - // Prevent left and right regions from rendering blocks when 'show_blocks' == FALSE. - if (!(!$variables['show_blocks'] && ($region == 'left' || $region == 'right'))) { - $blocks = theme('blocks', $region); - } - else { - $blocks = ''; - } - // Assign region to a region variable. - isset($variables[$region]) ? $variables[$region] .= $blocks : $variables[$region] = $blocks; + $variables[$region] = &$variables['elements'][$region]; } // Set up layout variable. @@ -1826,6 +1833,8 @@ $head_title[] = variable_get('site_slogan', ''); } } + + $variables['content'] = &$variables['elements']['content']; $variables['head_title'] = implode(' | ', $head_title); $variables['base_path'] = base_path(); $variables['front_page'] = url(); @@ -1837,7 +1846,7 @@ $variables['language'] = $GLOBALS['language']; $variables['language']->dir = $GLOBALS['language']->direction ? 'rtl' : 'ltr'; $variables['logo'] = theme_get_setting('logo'); - $variables['messages'] = $variables['show_messages'] ? theme('status_messages') : ''; + $variables['messages'] = $variables['elements']['#show_messages'] ? theme('status_messages') : ''; $variables['mission'] = isset($mission) ? $mission : ''; $variables['main_menu'] = theme_get_setting('toggle_main_menu') ? menu_main_menu() : array(); $variables['secondary_menu'] = theme_get_setting('toggle_secondary_menu') ? menu_secondary_menu() : array(); @@ -1989,6 +1998,7 @@ */ function template_preprocess_block(&$variables) { static $block_counter = array(); + // All blocks get an independent counter for each region. if (!isset($block_counter[$variables['block']->region])) { $block_counter[$variables['block']->region] = 1; Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.281 diff -u -r1.281 form.inc --- includes/form.inc 17 Aug 2008 11:08:23 -0000 1.281 +++ includes/form.inc 6 Sep 2008 03:14:12 -0000 @@ -143,9 +143,10 @@ $form = drupal_rebuild_form($form_id, $form_state, $args); } - // If we haven't redirected to a new location by now, we want to - // render whatever form array is currently in hand. - return drupal_render_form($form_id, $form); + // If we haven't redirected to a new location by now, we want to prepare for + // rendering whatever form array is currently in hand. + drupal_set_form_theme($form_id, $form); + return $form; } /** @@ -587,7 +588,7 @@ * A string containing the path of the page to display when processing * is complete. */ -function drupal_render_form($form_id, &$form) { +function drupal_set_form_theme($form_id, &$form) { // Don't override #theme if someone already set it. if (!isset($form['#theme'])) { init_theme(); @@ -596,9 +597,6 @@ $form['#theme'] = $form_id; } } - - $output = drupal_render($form); - return $output; } /** Index: modules/menu/menu.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/menu/menu.admin.inc,v retrieving revision 1.32 diff -u -r1.32 menu.admin.inc --- modules/menu/menu.admin.inc 16 Jul 2008 21:59:27 -0000 1.32 +++ modules/menu/menu.admin.inc 6 Sep 2008 03:14:12 -0000 @@ -214,7 +214,12 @@ } $output = ''; if ($rows) { - $output .= theme('table', $header, $rows, array('id' => 'menu-overview')); + $table = array( + '#header' => $header, + '#rows' => $rows, + '#attributes' => array('id' => 'menu-overview'), + ) + _element_info('table'); + $output .= theme('table', $table); } $output .= drupal_render($form); return $output; Index: modules/user/user.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v retrieving revision 1.24 diff -u -r1.24 user.admin.inc --- modules/user/user.admin.inc 24 Jul 2008 16:28:52 -0000 1.24 +++ modules/user/user.admin.inc 6 Sep 2008 03:14:12 -0000 @@ -19,8 +19,8 @@ $output = drupal_get_form('user_multiple_delete_confirm'); } else { - $output = drupal_get_form('user_filter_form'); - $output .= drupal_get_form('user_admin_account'); + $output[] = drupal_get_form('user_filter_form'); + $output[] = drupal_get_form('user_admin_account'); } } return $output; @@ -606,7 +606,13 @@ } } $output = theme('system_compact_link'); - $output .= theme('table', $header, $rows, array('id' => 'permissions')); + $table = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + '#attributes' => array('id' => 'permissions'), + ); + $output .= theme('table', $table); $output .= drupal_render($form); return $output; } @@ -738,8 +744,12 @@ else { $rows[] = array(array('data' => t('No users available.'), 'colspan' => '7')); } - - $output .= theme('table', $header, $rows); + $table = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + ); + $output .= theme('table', $table); if ($form['pager']['#markup']) { $output .= drupal_render($form['pager']); } @@ -768,7 +778,12 @@ $rows[] = array(drupal_render($form['name']), array('data' => drupal_render($form['submit']), 'colspan' => 2)); $output = drupal_render($form); - $output .= theme('table', $header, $rows); + $table = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + ); + $output .= theme('table', $table); return $output; } Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.917 diff -u -r1.917 user.module --- modules/user/user.module 21 Aug 2008 19:36:39 -0000 1.917 +++ modules/user/user.module 6 Sep 2008 03:14:12 -0000 @@ -760,7 +760,7 @@ if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) { $block['subject'] = t('User login'); - $block['content'] = drupal_get_form('user_login_block'); + $block['content'] = drupal_render(drupal_get_form('user_login_block')); } return $block; Index: modules/blog/blog.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/blog/blog.pages.inc,v retrieving revision 1.10 diff -u -r1.10 blog.pages.inc --- modules/blog/blog.pages.inc 22 May 2008 19:27:13 -0000 1.10 +++ modules/blog/blog.pages.inc 6 Sep 2008 03:14:12 -0000 @@ -23,18 +23,21 @@ $items[] = t('You are not allowed to post a new blog entry.'); } - $output = theme('item_list', $items); + $output[] = array( + '#type' => 'item_list', + '#items' => $items + ); $result = pager_query(db_rewrite_sql("SELECT n.nid, n.sticky, n.created FROM {node} n WHERE n.type = 'blog' AND n.uid = %d AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC"), variable_get('default_nodes_main', 10), 0, NULL, $account->uid); $has_posts = FALSE; while ($node = db_fetch_object($result)) { - $output .= node_view(node_load($node->nid), 1); + $output[] = array('#markup' => node_view(node_load($node->nid), 1)); $has_posts = TRUE; } if ($has_posts) { - $output .= theme('pager', NULL, variable_get('default_nodes_main', 10)); + $output['pager'] = array('#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10))); } else { if ($account->uid == $user->uid) { @@ -55,25 +58,27 @@ function blog_page_last() { global $user; - $output = ''; $items = array(); if (user_access('edit own blog')) { $items[] = l(t('Create new blog entry.'), "node/add/blog"); } - $output = theme('item_list', $items); + $output['blog_links'] = array( + '#type' => 'item_list', + '#items' => $items + ); $result = pager_query(db_rewrite_sql("SELECT n.nid, n.created FROM {node} n WHERE n.type = 'blog' AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC"), variable_get('default_nodes_main', 10)); $has_posts = FALSE; while ($node = db_fetch_object($result)) { - $output .= node_view(node_load($node->nid), 1); + $output['nodes'][] = array('#markup' => node_view(node_load($node->nid), 1)); $has_posts = TRUE; } if ($has_posts) { - $output .= theme('pager', NULL, variable_get('default_nodes_main', 10)); + $output['pager'] = array('#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10))); } else { drupal_set_message(t('No blog entries have been created.')); Index: modules/aggregator/aggregator.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.pages.inc,v retrieving revision 1.16 diff -u -r1.16 aggregator.pages.inc --- modules/aggregator/aggregator.pages.inc 16 Aug 2008 21:13:32 -0000 1.16 +++ modules/aggregator/aggregator.pages.inc 6 Sep 2008 03:14:12 -0000 @@ -230,7 +230,11 @@ } } } - $output .= theme('table', array('', t('Categorize')), $rows); + $table = array( + '#header' => array('', t('Categorize')), + '#rows' => $rows, + ) + _element_info('table'); + $output .= theme('table', $table); $output .= drupal_render($form['submit']); $output .= drupal_render($form); Index: modules/aggregator/aggregator.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.admin.inc,v retrieving revision 1.14 diff -u -r1.14 aggregator.admin.inc --- modules/aggregator/aggregator.admin.inc 16 Aug 2008 14:48:17 -0000 1.14 +++ modules/aggregator/aggregator.admin.inc 6 Sep 2008 03:14:12 -0000 @@ -22,25 +22,33 @@ function aggregator_view() { $result = db_query('SELECT f.*, COUNT(i.iid) AS items FROM {aggregator_feed} f LEFT JOIN {aggregator_item} i ON f.fid = i.fid GROUP BY f.fid, f.title, f.url, f.refresh, f.checked, f.link, f.description, f.hash, f.etag, f.modified, f.image, f.block ORDER BY f.title'); - $output = '

' . t('Feed overview') . '

'; + $output['feed_overview'] = array('#markup' => '

' . t('Feed overview') . '

'); $header = array(t('Title'), t('Items'), t('Last update'), t('Next update'), array('data' => t('Operations'), 'colspan' => '3')); $rows = array(); while ($feed = db_fetch_object($result)) { $rows[] = array(l($feed->title, "aggregator/sources/$feed->fid"), format_plural($feed->items, '1 item', '@count items'), ($feed->checked ? t('@time ago', array('@time' => format_interval(time() - $feed->checked))) : t('never')), ($feed->checked ? t('%time left', array('%time' => format_interval($feed->checked + $feed->refresh - time()))) : t('never')), l(t('edit'), "admin/content/aggregator/edit/feed/$feed->fid"), l(t('remove items'), "admin/content/aggregator/remove/$feed->fid"), l(t('update items'), "admin/content/aggregator/update/$feed->fid")); } - $output .= theme('table', $header, $rows); + $output['feed_table'] = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + ); $result = db_query('SELECT c.cid, c.title, count(ci.iid) as items FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid GROUP BY c.cid, c.title ORDER BY title'); - $output .= '

' . t('Category overview') . '

'; + $output['category_overview'] = array('#markup' => '

' . t('Category overview') . '

'); $header = array(t('Title'), t('Items'), t('Operations')); $rows = array(); while ($category = db_fetch_object($result)) { $rows[] = array(l($category->title, "aggregator/categories/$category->cid"), format_plural($category->items, '1 item', '@count items'), l(t('edit'), "admin/content/aggregator/edit/category/$category->cid")); } - $output .= theme('table', $header, $rows); + $output['category_table'] = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + ); return $output; } Index: index.php =================================================================== RCS file: /cvs/drupal/drupal/index.php,v retrieving revision 1.95 diff -u -r1.95 index.php --- index.php 21 Aug 2008 19:36:35 -0000 1.95 +++ index.php 6 Sep 2008 03:14:11 -0000 @@ -20,19 +20,21 @@ if (is_int($return)) { switch ($return) { case MENU_NOT_FOUND: - drupal_not_found(); + $elements = drupal_not_found(); break; case MENU_ACCESS_DENIED: - drupal_access_denied(); + $elements = drupal_access_denied(); break; case MENU_SITE_OFFLINE: - drupal_site_offline(); + $elements = drupal_site_offline(); break; } } elseif (isset($return)) { // Print any value (including an empty string) except NULL or undefined: - print theme('page', $return); + $elements = drupal_build_page($return); } +echo drupal_render_page($elements); + drupal_page_footer(); Index: modules/path/path.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/path/path.admin.inc,v retrieving revision 1.10 diff -u -r1.10 path.admin.inc --- modules/path/path.admin.inc 21 Aug 2008 19:36:37 -0000 1.10 +++ modules/path/path.admin.inc 6 Sep 2008 03:14:12 -0000 @@ -13,7 +13,7 @@ */ function path_admin_overview($keys = NULL) { // Add the filter form above the overview table. - $output = drupal_get_form('path_admin_filter_form', $keys); + $output['path_admin_filter_form'] = drupal_get_form('path_admin_filter_form', $keys); // Enable language column if locale is enabled or if we have any alias with language $count = db_result(db_query("SELECT COUNT(*) FROM {url_alias} WHERE language != ''")); $multilanguage = (module_exists('locale') || $count); @@ -57,8 +57,12 @@ $rows[] = array(array('data' => $empty_message, 'colspan' => ($multilanguage ? 5 : 4))); } - $output .= theme('table', $header, $rows); - $output .= theme('pager', NULL, 50, 0); + $output['aliases'] = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + ); + $output['pager'] = array('#markup' => theme('pager', NULL, 50, 0)); return $output; } Index: modules/book/book.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/book/book.admin.inc,v retrieving revision 1.12 diff -u -r1.12 book.admin.inc --- modules/book/book.admin.inc 5 Jul 2008 06:00:51 -0000 1.12 +++ modules/book/book.admin.inc 6 Sep 2008 03:14:12 -0000 @@ -16,7 +16,11 @@ } $headers = array(t('Book'), t('Operations')); - return theme('table', $headers, $rows); + return array( + '#type' => 'table', + '#header' => $headers, + '#rows' => $rows, + ); } /** @@ -248,6 +252,11 @@ $rows[] = $row; } - return theme('table', $header, $rows, array('id' => 'book-outline')); + $table = array( + '#header' => $header, + '#rows' => $rows, + '#attributes' => array('id' => 'book-outline'), + ) + _element_info('table'); + return theme('table', $table); } Index: modules/system/system.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v retrieving revision 1.86 diff -u -r1.86 system.admin.inc --- modules/system/system.admin.inc 30 Aug 2008 09:49:43 -0000 1.86 +++ modules/system/system.admin.inc 6 Sep 2008 03:14:12 -0000 @@ -1120,8 +1120,12 @@ } $output .= drupal_get_form('system_ip_blocking_form'); - - $output .= theme('table', $header, $rows); + $table = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + ); + $output .= theme('table', $table); return $output; } @@ -1826,8 +1830,12 @@ $rows[] = array(check_plain($key), check_plain($data[$key]), $explanation); } } - - return theme('table', array(t('Variable'), t('Value'), t('Description')), $rows); + $table = array( + '#type' => 'table', + '#header' => array(t('Variable'), t('Value'), t('Description')), + '#rows' => $rows, + ); + return theme('table', $table); } /** @@ -2110,8 +2118,12 @@ $row[] = array('data' => $description, 'class' => 'description'); $rows[] = $row; } - - return theme('table', $form['#header'], $rows); + $table = array( + '#type' => 'table', + '#header' => $form['#header'], + '#rows' => $rows, + ); + return theme('table', $table); } /** @@ -2162,8 +2174,12 @@ if (empty($rows)) { $rows[] = array(array('data' => t('No modules are available to uninstall.'), 'colspan' => '3', 'align' => 'center', 'class' => 'message')); } - - $output = theme('table', $header, $rows); + $table = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + ); + $output = theme('table', $table); $output .= drupal_render($form); return $output; @@ -2186,8 +2202,12 @@ $rows[] = $row; } - $header = array(t('Screenshot'), t('Name'), t('Selected')); - $output = theme('table', $header, $rows); + $table = array( + '#type' => 'table', + '#header' => array(t('Screenshot'), t('Name'), t('Selected')), + '#rows' => $rows, + ); + $output = theme('table', $table); return $output; } @@ -2244,8 +2264,12 @@ $rows[] = $row; } - $header = array(t('Screenshot'), t('Name'), t('Version'), t('Enabled'), t('Default'), t('Operations')); - $output = theme('table', $header, $rows); + $table = array( + '#type' => 'table', + '#header' => array(t('Screenshot'), t('Name'), t('Version'), t('Enabled'), t('Default'), t('Operations')), + '#rows' => $rows, + ); + $output = theme('table', $table); $output .= drupal_render($form); return $output; } Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.616 diff -u -r1.616 system.module --- modules/system/system.module 5 Sep 2008 09:29:06 -0000 1.616 +++ modules/system/system.module 6 Sep 2008 03:14:12 -0000 @@ -323,6 +323,21 @@ '#input' => TRUE, ); + /** + * Common page elements. + */ + $type['table'] = array( + '#header' => array(), + '#rows' => array(), + '#caption' => NULL, + '#colgroups' => array(), + ); + + $type['item_list'] = array( + '#items' => array(), + '#title' => NULL, + '#list_type' => 'ul', + ); return $type; } Index: modules/dblog/dblog.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/dblog/dblog.admin.inc,v retrieving revision 1.8 diff -u -r1.8 dblog.admin.inc --- modules/dblog/dblog.admin.inc 19 Jul 2008 07:44:45 -0000 1.8 +++ modules/dblog/dblog.admin.inc 6 Sep 2008 03:14:12 -0000 @@ -51,7 +51,7 @@ WATCHDOG_EMERG => 'dblog-emerg', ); - $output = drupal_get_form('dblog_filter_form'); + $output['filter_form'] = drupal_get_form('dblog_filter_form'); $header = array( ' ', @@ -90,9 +90,16 @@ if (!$rows) { $rows[] = array(array('data' => t('No log messages available.'), 'colspan' => 6)); } - - $output .= theme('table', $header, $rows, array('id' => 'admin-dblog')); - $output .= theme('pager', NULL, 50, 0); + $output['messages_table'] = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + '#attributes' => array('id' => 'admin-dblog'), + ); + $output['pager'] = array( + '#type' => 'pager', + '#limit' => 50, + ); return $output; } @@ -118,8 +125,12 @@ if (empty($rows)) { $rows[] = array(array('data' => t('No log messages available.'), 'colspan' => 2)); } - - $output = theme('table', $header, $rows); + $table = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + ); + $output = theme('table', $table); $output .= theme('pager', NULL, 30, 0); return $output; @@ -171,8 +182,13 @@ $dblog->link, ), ); - $attributes = array('class' => 'dblog-event'); - $output = theme('table', array(), $rows, $attributes); + $table = array( + '#type' => 'table', + '#header' => array(), + '#rows' => $rows, + '#attributes' => array('class' => 'dblog-event'), + ); + $output = theme('table', $table); } return $output; } Index: themes/garland/page.tpl.php =================================================================== RCS file: /cvs/drupal/drupal/themes/garland/page.tpl.php,v retrieving revision 1.20 diff -u -r1.20 page.tpl.php --- themes/garland/page.tpl.php 28 Apr 2008 09:25:27 -0000 1.20 +++ themes/garland/page.tpl.php 6 Sep 2008 03:14:12 -0000 @@ -14,7 +14,7 @@ -
+
@@ -38,7 +38,7 @@ @@ -52,16 +52,16 @@
- +
- +