diff --git includes/common.inc includes/common.inc
index 58b0845..e2545e5 100644
--- includes/common.inc
+++ includes/common.inc
@@ -42,6 +42,8 @@ define('JS_THEME', 100);
 
 /**
  * Set content for a specified region.
+ * 
+ * @todo: Drop this in favor of drupal_decorate_page. It's already only used in maintance theme mode.
  *
  * @param $region
  *   Page region the content is assigned to.
@@ -60,6 +62,8 @@ function drupal_set_content($region = NULL, $data = NULL) {
 /**
  * Get assigned content.
  *
+ * @todo: Drop this in favor of drupal_decorate_page. It's already only used in maintance theme mode.
+ *
  * @param $region
  *   A specified region to fetch content for. If NULL, all regions will be
  *   returned.
@@ -301,8 +305,7 @@ function drupal_get_destination() {
  * Drupal will ensure that messages set by drupal_set_message() and other
  * session data are written to the database before the user is redirected.
  *
- * This function ends the request; use it rather than a print theme('page')
- * statement in your menu callback.
+ * This function ends the request; use it rather than returning an array in your menu callback.
  *
  * @param $path
  *   A Drupal path or a full URL.
@@ -385,11 +388,12 @@ function drupal_not_found() {
 
   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);
+  drupal_decorate_page(array('#show_blocks' => FALSE));
+  print drupal_render_page($return);
 }
 
 /**
@@ -414,9 +418,10 @@ function drupal_access_denied() {
 
   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.'));
   }
-  print theme('page', $return);
+  
+  print drupal_render_page($return);
 }
 
 /**
@@ -821,7 +826,11 @@ function _drupal_log_error($error, $fatal = FALSE) {
     drupal_set_header($_SERVER['SERVER_PROTOCOL'] . ' Service unavailable');
     drupal_set_title(t('Error'));
     if (!defined('MAINTENANCE_MODE') && drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL) {
-      print theme('page', t('The website encountered an unexpected error. Please try again later.'), FALSE);
+      // To conserve CPU and bandwidth, omit the blocks.
+      drupal_decorate_page(array('#show_blocks' => FALSE));
+      print drupal_render_page(array(
+        '#markup' => t('The website encountered an unexpected error. Please try again later.'),
+      ));
     }
     else {
       print theme('maintenance_page', t('The website encountered an unexpected error. Please try again later.'), FALSE);
@@ -3176,6 +3185,56 @@ function drupal_alter($type, &$data) {
 }
 
 /**
+ * Renders the page and adds all theming.
+ * 
+ * @param $content
+ *   A string or array representing the content of the page.
+ */
+function drupal_render_page($content) {
+   // The theme system might not yet be initialized.
+  init_theme();
+  global $theme;
+  // Initialize page array with defaults.
+  $page = _element_info('page');
+  $page['#type'] = 'page';
+ 
+  // Set the main content.
+  $page['content'] = is_array($content) ? $content : array('#markup' => $content);
+  
+  // Include decorations.
+  $page = array_merge($page, drupal_decorate_page());
+  
+  // Allow final alteration.
+  drupal_alter('page', $page);
+  
+  // Ensure that we don't have any empty regions.
+  foreach (system_region_list($theme) as $region_key => $region_name) {
+    if(empty($page[$region_key])) {
+      $page[$region_key] = array('#markup' => '');
+    }
+  }
+
+  print drupal_render($page);
+}
+
+/**
+ * Add elements to the page.
+ *
+ * The passed in array is usually keyed by valid regions, as otherwise content
+ * will probably not be themed automatically.
+ *
+ * @param $decorations
+ *   A renderable array that will later on be merged into the main page array.
+ * @return
+ *   The collected decorations.
+ */
+function drupal_decorate_page($decorations = array()) {
+  static $page = array();
+  $page += $decorations;
+  return $page;
+}
+
+/**
  * Renders HTML given a structured array tree.
  *
  * Recursively iterates over each of the array elements, generating HTML code.
@@ -3246,7 +3305,17 @@ function drupal_render(&$elements) {
     // Render each of the children using drupal_render and concatenate them.
     if (!isset($content) || $content === '') {
       foreach ($children as $key) {
-        $content .= drupal_render($elements[$key]);
+        // If #concatenate_children is set to FALSE, the rendered children of the
+        // element are not concatenated into a single string, but instead kept as
+        // a keyed array of rendered string in the #children property of the parent
+        // element. The parent's element theming function is then responsible for
+        // printing out all elements in the #children array.
+        if (isset($elements['#concatenate_children']) && $elements['#concatenate_children'] == FALSE) {
+          $content[$key] = drupal_render($elements[$key]);
+        }
+        else {
+          $content .= drupal_render($elements[$key]);
+        }
       }
     }
   }
@@ -3339,7 +3408,7 @@ function drupal_common_theme() {
       'arguments' => array('text' => NULL)
     ),
     'page' => array(
-      'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE),
+      'arguments' => array('content' => NULL),
       'template' => 'page',
     ),
     'maintenance_page' => array(
@@ -3398,6 +3467,9 @@ function drupal_common_theme() {
     'item_list' => array(
       'arguments' => array('items' => array(), 'title' => NULL, 'type' => 'ul', 'attributes' => NULL),
     ),
+    'list' => array(
+      'arguments' => array('elements' => NULL),
+    ),
     'more_help_link' => array(
       'arguments' => array('url' => NULL),
     ),
diff --git includes/theme.inc includes/theme.inc
index 37d8652..e297904 100644
--- includes/theme.inc
+++ includes/theme.inc
@@ -1571,6 +1571,28 @@ function theme_item_list($items = array(), $title = NULL, $type = 'ul', $attribu
 }
 
 /**
+ * Return a themed list of items from a drupal_render() style array.
+ *
+ * @param $elements
+ *   An array consisting of the following keys:
+ *     - #items. An array of items as expected by theme('item_list'). Mandatory.
+ *     - #title. A title which prints above the list. Optional.
+ *     - #list_type. The type of list to return. Defaults to "ul".
+ *     - #attributes. An array of attributes as expected by theme('item_list'). Optional.
+ * @return
+ *   A string containing the list output.
+ */
+function theme_list($elements) {
+  // Populate any missing array elements with their defaults.
+  $elements += array(
+    '#title' => '',
+    '#list_type' => 'ul',
+    '#attributes' => array(),
+    '#items' => array(),
+  );
+  return theme('item_list', $elements['#items'], $elements['#title'], $elements['#list_type'], $elements['#attributes']);
+}
+/**
  * Returns code that emits the 'more help'-link.
  */
 function theme_more_help_link($url) {
@@ -1633,30 +1655,6 @@ function theme_closure($main = 0) {
 }
 
 /**
- * Return a set of blocks available for the current user.
- *
- * @param $region
- *   Which set of blocks to retrieve.
- * @return
- *   A string containing the themed blocks for this region.
- */
-function theme_blocks($region) {
-  $output = '';
-
-  if ($list = block_list($region)) {
-    foreach ($list as $key => $block) {
-      // $key == <i>module</i>_<i>delta</i>
-      $output .= theme('block', $block);
-    }
-  }
-
-  // Add any content assigned to this region through drupal_set_content() calls.
-  $output .= drupal_get_content($region);
-
-  return $output;
-}
-
-/**
  * Format a username.
  *
  * @param $object
@@ -1825,27 +1823,21 @@ function template_preprocess(&$variables, $hook) {
  * @see page.tpl.php
  */
 function template_preprocess_page(&$variables) {
+  // Move some variables to the top level for themer convenience and template cleanliness.
+  $variables['show_blocks'] = $variables['content']['#show_blocks'];
+  $variables['show_messages'] = $variables['content']['#show_messages'];
+  
+  // Import children of the content array as variables.
+  // This contains the main 'content' and the regions (with rendered blocks).
+  $content = $variables['content']['#children'];
+  foreach ($content as $key => $child) {
+    $variables[$key] = $child;
+  }
   // Add favicon
   if (theme_get_setting('toggle_favicon')) {
     drupal_set_html_head('<link rel="shortcut icon" href="' . check_url(theme_get_setting('favicon')) . '" type="image/x-icon" />');
   }
 
-  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;
-  }
-
   // Set up layout variable.
   $variables['layout'] = 'none';
   if (!empty($variables['left'])) {
@@ -1883,8 +1875,8 @@ function template_preprocess_page(&$variables) {
   $variables['logo']              = theme_get_setting('logo');
   $variables['messages']          = $variables['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();
+  $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();
   $variables['search_box']        = (theme_get_setting('toggle_search') ? drupal_get_form('search_theme_form') : '');
   $variables['site_name']         = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : '');
   $variables['site_slogan']       = (theme_get_setting('toggle_slogan') ? variable_get('site_slogan', '') : '');
@@ -1977,6 +1969,8 @@ function template_preprocess_page(&$variables) {
  * @see node.tpl.php
  */
 function template_preprocess_node(&$variables) {
+  $variables['teaser'] = &$variables['elements']['teaser'];
+  $variables['node'] = &$variables['elements']['node'];
   $node = $variables['node'];
 
   $variables['date']      = format_date($node->created);
@@ -2042,6 +2036,7 @@ function template_preprocess_node(&$variables) {
  */
 function template_preprocess_block(&$variables) {
   static $block_counter = array();
+  $variables['block'] = $variables['block']['#block'];
   // All blocks get an independent counter for each region.
   if (!isset($block_counter[$variables['block']->region])) {
     $block_counter[$variables['block']->region] = 1;
diff --git index.php index.php
index e9cdef1..0eeb3aa 100644
--- index.php
+++ index.php
@@ -21,7 +21,7 @@ require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 $return = menu_execute_active_handler();
 
-// Menu status constants are integers; page content is a string.
+// Menu status constants are integers; page content is a string or array.
 if (is_int($return)) {
   switch ($return) {
     case MENU_NOT_FOUND:
@@ -36,8 +36,8 @@ if (is_int($return)) {
   }
 }
 elseif (isset($return)) {
-  // Print any value (including an empty string) except NULL or undefined:
-  print theme('page', $return);
+  // Print anything besides a menu constant, assuming it's not NULL or undefined.
+  drupal_render_page($return);
 }
 
 drupal_page_footer();
diff --git modules/block/block.admin.inc modules/block/block.admin.inc
index 2feb425..358c98c 100644
--- modules/block/block.admin.inc
+++ modules/block/block.admin.inc
@@ -369,7 +369,8 @@ function template_preprocess_block_admin_display_form(&$variables) {
 
   foreach ($block_regions as $key => $value) {
     // Highlight regions on page to provide visual reference.
-    drupal_set_content($key, '<div class="block-region">' . $value . '</div>');
+    $decoration[$key]['region_title'] = array('#markup' => '<div class="block-region">' . $value . '</div>');
+    drupal_decorate_page($decoration);
     // Initialize an empty array for the region.
     $variables['block_listing'][$key] = array();
   }
diff --git modules/block/block.module modules/block/block.module
index 85bc4e5..62d3a1e 100644
--- modules/block/block.module
+++ modules/block/block.module
@@ -226,6 +226,47 @@ function block_block_view($delta = 0, $edit = array()) {
 }
 
 /**
+ * Implementation of hook_page_alter().
+ *
+ * Render blocks into their regions.
+ */
+function block_page_alter($page) {
+  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 ($page['#show_blocks'] || ($region != 'left' && $region != 'right')) {
+      $page[$region]['blocks'] = block_get_blocks_by_region($region);
+    }
+  }
+}
+
+/**
+ * Get a renderable array of a region containing all enabled blocks.
+ *
+ * @param $region
+ *   The requested region.
+ */
+function block_get_blocks_by_region($region) {
+  $weight = 0;
+  $output = array();
+  if ($list = block_list($region)) {
+    foreach ($list as $key => $block) {
+      $output[$key] = array(
+        '#theme' => 'block', 
+        '#block' => $block, 
+        '#weight' => $weight
+      );
+      $weight++;
+    }
+  }
+
+  return $output;
+}
+
+/**
  * Update the 'block' DB table with the blocks currently exported by modules.
  *
  * @return
diff --git modules/blog/blog.pages.inc modules/blog/blog.pages.inc
index a48c432..4308ae5 100644
--- modules/blog/blog.pages.inc
+++ modules/blog/blog.pages.inc
@@ -23,18 +23,20 @@ function blog_page_user($account) {
     $items[] = t('You are not allowed to post a new blog entry.');
   }
 
-  $output = theme('item_list', $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);
-    $has_posts = TRUE;
-  }
-
-  if ($has_posts) {
-    $output .= theme('pager', NULL, variable_get('default_nodes_main', 10));
+  $page['blog_actions'] = array(
+    '#items' => $items,
+    '#theme' => 'list',
+    '#weight' => -1,
+  );
+
+  $nids = pager_query(db_rewrite_sql("SELECT n.nid 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)->fetchCol();
+  if (!empty($nids)) {
+    $nodes = node_load_multiple($nids);
+    $page += node_build_multiple($nodes);
+    $page['pager'] = array(
+      '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)),
+      '#weight' => count($nodes),
+    );
   }
   else {
     if ($account->uid == $user->uid) {
@@ -46,7 +48,7 @@ function blog_page_user($account) {
   }
   drupal_add_feed(url('blog/' . $account->uid . '/feed'), t('RSS - !title', array('!title' => $title)));
 
-  return $output;
+  return $page;
 }
 
 /**
@@ -56,31 +58,33 @@ function blog_page_last() {
   global $user;
 
   $output = '';
-  $items = array();
+  $page = array();
 
   if (user_access('edit own blog')) {
     $items[] = l(t('Create new blog entry.'), "node/add/blog");
+    $page['blog_actions'] = array(
+      '#items' => $items,
+      '#theme' => 'list',
+      '#weight' => -1,
+    );
   }
 
-  $output = theme('item_list', $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);
-    $has_posts = TRUE;
-  }
+  $nids = pager_query(db_rewrite_sql("SELECT n.nid 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))->fetchCol();
 
-  if ($has_posts) {
-    $output .= theme('pager', NULL, variable_get('default_nodes_main', 10));
+  if (!empty($nids)) {
+    $nodes = node_load_multiple($nids);
+    $page += node_build_multiple($nodes);
+    $page['pager'] = array(
+      '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)),
+      '#weight' => count($nodes),
+    );
   }
   else {
     drupal_set_message(t('No blog entries have been created.'));
   }
   drupal_add_feed(url('blog/feed'), t('RSS - blogs'));
 
-  return $output;
+  return $page;
 }
 
 /**
diff --git modules/comment/comment.module modules/comment/comment.module
index 58e8315..f196ee4 100644
--- modules/comment/comment.module
+++ modules/comment/comment.module
@@ -1628,7 +1628,7 @@ function comment_form_add_preview($form, &$form_state) {
   }
   else {
     $suffix = empty($form['#suffix']) ? '' : $form['#suffix'];
-    $form['#suffix'] = $suffix . node_view($node);
+    $form['#suffix'] = $suffix . drupal_render(node_view($node));
     $edit['pid'] = 0;
   }
 
diff --git modules/comment/comment.pages.inc modules/comment/comment.pages.inc
index d36d04f..a656835 100644
--- modules/comment/comment.pages.inc
+++ modules/comment/comment.pages.inc
@@ -92,7 +92,7 @@ function comment_reply($node, $pid = NULL) {
       }
       // This is the case where the comment is in response to a node. Display the node.
       elseif (user_access('access content')) {
-        $output .= node_view($node);
+        $output .= drupal_render(node_view($node));
       }
 
       // Should we show the reply box?
diff --git modules/node/node.api.php modules/node/node.api.php
index 4a8636a..75937df 100644
--- modules/node/node.api.php
+++ modules/node/node.api.php
@@ -169,7 +169,7 @@ function hook_node_operations() {
  * @return
  *   None.
  */
-function hook_nodeapi_alter($node, $teaser, $page) {
+function hook_nodeapi_alter($node, $teaser) {
 }
 
 /**
diff --git modules/node/node.module modules/node/node.module
index d2d3771..8cec3c7 100644
--- modules/node/node.module
+++ modules/node/node.module
@@ -96,7 +96,7 @@ function node_help($path, $arg) {
 function node_theme() {
   return array(
     'node' => array(
-      'arguments' => array('node' => NULL, 'teaser' => FALSE, 'page' => FALSE),
+      'arguments' => array('elements' => NULL),
       'template' => 'node',
     ),
     'node_list' => array(
@@ -1123,24 +1123,28 @@ function node_delete($nid) {
 }
 
 /**
- * Generate a display of the given node.
+ * Generate an array for rendering the given node.
  *
  * @param $node
  *   A node array or node object.
  * @param $teaser
  *   Whether to display the teaser only or the full form.
- * @param $links
- *   Whether or not to display node links. Links are omitted for node previews.
  *
  * @return
- *   An HTML representation of the themed node.
+ *   An array as expected by drupal_render().
  */
 function node_view($node, $teaser = FALSE) {
   $node = (object)$node;
 
   $node = node_build_content($node, $teaser);
-
-  return theme('node', $node, $teaser);
+  
+  $render = $node->content;
+  $render += array(
+    '#theme' => 'node',
+    'node' => $node,
+    'teaser' => $teaser,
+  );
+  return $render;
 }
 
 /**
@@ -1208,19 +1212,17 @@ function node_build_content($node, $teaser = FALSE) {
 }
 
 /**
- * Generate a page displaying a single node.
+ * Generate an array which displays a single node.
  */
 function node_show($node, $message = FALSE) {
   if ($message) {
     drupal_set_title(t('Revision of %title from %date', array('%title' => $node->title, '%date' => format_date($node->revision_timestamp))), PASS_THROUGH);
   }
 
-  $output = node_view($node, FALSE, TRUE);
-
   // Update the history table, stating that this user viewed this node.
   node_tag_new($node->nid);
 
-  return $output;
+  return node_view($node, FALSE);
 }
 
 /**
@@ -1872,20 +1874,42 @@ function node_feed($nids = FALSE, $channel = array()) {
 }
 
 /**
+ * Construct a drupal_render() style array from an array of loaded nodes.
+ *
+ * @param array $nodes
+ *   An array of nodes as returned by node_load_multiple().
+ * @param boolean $teaser
+ *   Display nodes into teaser view or full view.
+ * @param integer $weight
+ *   An integer representing the weight of the first node in the list. 
+ * @return 
+ *   An array in the format expected by drupal_render().
+ */
+function node_build_multiple($nodes, $teaser = TRUE, $weight = 0) {
+  $render = array();
+  foreach ($nodes as $node) {
+    $render["nid_$node->nid"] = node_view($node, $teaser);
+    $render["nid_$node->nid"]['#weight'] = $weight;
+    $weight++;
+  }
+  return $render;
+}
+
+/**
  * Menu callback; Generate a listing of promoted nodes.
  */
 function node_page_default() {
   $nids = pager_query(db_rewrite_sql('SELECT n.nid 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))->fetchCol();
   if (!empty($nids)) {
     $nodes = node_load_multiple($nids);
-    $output = '';
-    foreach ($nodes as $node) {
-      $output .= node_view($node, TRUE);
-    }
+    $page = node_build_multiple($nodes);
 
     $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));
+    $page['pager'] = array(
+      '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)),
+      '#weight' => count($nodes),
+    );
   }
   else {
     $default_message = '<h1 class="title">' . t('Welcome to your new Drupal website!') . '</h1>';
@@ -1898,11 +1922,11 @@ function node_page_default() {
     $default_message .= '</ol>';
     $default_message .= '<p>' . t('For more information, please refer to the <a href="@help">help section</a>, or the <a href="@handbook">online Drupal handbooks</a>. You may also post at the <a href="@forum">Drupal forum</a>, or view the wide range of <a href="@support">other support options</a> available.', array('@help' => url('admin/help'), '@handbook' => 'http://drupal.org/handbooks', '@forum' => 'http://drupal.org/forum', '@support' => 'http://drupal.org/support')) . '</p>';
 
-    $output = '<div id="first-time">' . $default_message . '</div>';
+    $page = array('#markup' => '<div id="first-time">' . $default_message . '</div>');
   }
   drupal_set_title('');
 
-  return $output;
+  return $page;
 }
 
 /**
@@ -2930,7 +2954,7 @@ function node_unpublish_by_keyword_action_submit($form, $form_state) {
  */
 function node_unpublish_by_keyword_action($node, $context) {
   foreach ($context['keywords'] as $keyword) {
-    if (strstr(node_view(clone $node), $keyword) || strstr($node->title, $keyword)) {
+    if (strstr(drupal_render(node_view(clone $node)), $keyword) || strstr($node->title, $keyword)) {
       $node->status = 0;
       watchdog('action', 'Set @type %title to unpublished.', array('@type' => node_get_types('name', $node), '%title' => $node->title));
       break;
diff --git modules/node/node.pages.inc modules/node/node.pages.inc
index 76e28b1..511c042 100644
--- modules/node/node.pages.inc
+++ modules/node/node.pages.inc
@@ -415,12 +415,12 @@ function theme_node_preview($node) {
   if ($preview_trimmed_version) {
     drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "&lt;!--break--&gt;" (without the quotes) to fine-tune where your post gets split.</span>'));
     $output .= '<h3>' . t('Preview trimmed version') . '</h3>';
-    $output .= node_view(clone $node, 1, FALSE);
+    $output .= drupal_render(node_view(clone $node, TRUE));
     $output .= '<h3>' . t('Preview full version') . '</h3>';
-    $output .= node_view($node, 0, FALSE);
+    $output .= drupal_render(node_view($node, FALSE));
   }
   else {
-    $output .= node_view($node, 0, FALSE);
+    $output .= drupal_render(node_view($node, FALSE));
   }
   $output .= "</div>\n";
 
diff --git modules/system/system.admin.inc modules/system/system.admin.inc
index e3cc5bc..382091f 100644
--- modules/system/system.admin.inc
+++ modules/system/system.admin.inc
@@ -1900,7 +1900,7 @@ function system_batch_page() {
   elseif (isset($output)) {
     // Force a page without blocks or messages to
     // display a list of collected messages later.
-    print theme('page', $output, FALSE, FALSE);
+    return $output;
   }
 }
 
diff --git modules/system/system.api.php modules/system/system.api.php
index 278dca9..3e9bc22 100644
--- modules/system/system.api.php
+++ modules/system/system.api.php
@@ -180,6 +180,28 @@ function hook_js_alter(&$javascript) {
 }
 
 /**
+ * Perform alterations before a page is rendered.
+ *
+ * Use this hook when you want to add/remove elements at the page level. If are 
+ * making changes to forms or nodes or users, use their hook_OBJECT_alter() hook instead.
+ *
+ * @param $page
+ *   Nested array of render elements that comprise the page.
+ * @return
+ *   None.
+ */
+function hook_page_alter($page) {
+  if (menu_get_object()) {
+    // We are on a node detail page. Append a standard disclaimer.
+    $page['disclaimer'] = array(
+      '#markup' => t('Opinions expressed on this belong to the author, and not to Acme, Inc.'),
+      '#weight' => 25,
+    );
+  }
+}
+
+
+/**
  * Perform alterations before a form is rendered.
  *
  * One popular use of this hook is to add form elements to the node form. When
diff --git modules/system/system.module modules/system/system.module
index 4261399..6aed00e 100644
--- modules/system/system.module
+++ modules/system/system.module
@@ -236,6 +236,14 @@ function system_elements() {
     '#method' => 'post',
     '#action' => request_uri(),
   );
+  
+  // @see drupal_render_page().
+  $type['page'] = array(
+    '#show_messages' => TRUE,
+    '#show_blocks' => TRUE,
+    '#value' => NULL,
+    '#concatenate_children' => FALSE,
+  );
 
   /**
    * Input elements.
diff --git modules/taxonomy/taxonomy.module modules/taxonomy/taxonomy.module
index 5d4a468..32c5f49 100644
--- modules/taxonomy/taxonomy.module
+++ modules/taxonomy/taxonomy.module
@@ -26,9 +26,6 @@ function taxonomy_theme() {
     'taxonomy_term_select' => array(
       'arguments' => array('element' => NULL),
     ),
-    'taxonomy_term_page' => array(
-      'arguments' => array('tids' => array(), 'result' => NULL),
-    ),
     'taxonomy_overview_vocabularies' => array(
       'arguments' => array('form' => array()),
     ),
@@ -1223,7 +1220,7 @@ function theme_taxonomy_term_select($element) {
  * @param $order
  *   The order clause for the query that retrieve the nodes.
  * @return
- *   A resource identifier pointing to the query results.
+ *   An array node IDs.
  */
 function taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $order = 'n.sticky DESC, n.created DESC') {
   if (count($tids) > 0) {
@@ -1241,7 +1238,7 @@ function taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $p
     if ($operator == 'or') {
       $args = call_user_func_array('array_merge', $descendant_tids);
       $placeholders = db_placeholders($args, 'int');
-      $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n INNER JOIN {term_node} tn ON n.vid = tn.vid WHERE tn.tid IN (' . $placeholders . ') AND n.status = 1 ORDER BY ' . $order;
+      $sql = 'SELECT DISTINCT(n.nid) FROM {node} n INNER JOIN {term_node} tn ON n.vid = tn.vid WHERE tn.tid IN (' . $placeholders . ') AND n.status = 1 ORDER BY ' . $order;
       $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n INNER JOIN {term_node} tn ON n.vid = tn.vid WHERE tn.tid IN (' . $placeholders . ') AND n.status = 1';
     }
     else {
@@ -1253,44 +1250,20 @@ function taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $p
         $wheres .= ' AND tn' . $index . '.tid IN (' . db_placeholders($tids, 'int') . ')';
         $args = array_merge($args, $tids);
       }
-      $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n ' . $joins . ' WHERE n.status = 1 ' . $wheres . ' ORDER BY ' . $order;
+      $sql = 'SELECT DISTINCT(n.nid) FROM {node} n ' . $joins . ' WHERE n.status = 1 ' . $wheres . ' ORDER BY ' . $order;
       $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n ' . $joins . ' WHERE n.status = 1 ' . $wheres;
     }
     $sql = db_rewrite_sql($sql);
     $sql_count = db_rewrite_sql($sql_count);
     if ($pager) {
-      $result = pager_query($sql, variable_get('default_nodes_main', 10), 0, $sql_count, $args);
+      $nids = pager_query($sql, variable_get('default_nodes_main', 10), 0, $sql_count, $args)->fetchCol();
     }
     else {
-      $result = db_query_range($sql, $args, 0, variable_get('feed_default_items', 10));
+      $nids = db_query_range($sql, $args, 0, variable_get('feed_default_items', 10))->fetchCol();
     }
   }
 
-  return $result;
-}
-
-/**
- * Accepts the result of a pager_query() call, such as that performed by
- * taxonomy_select_nodes(), and formats each node along with a pager.
- */
-function taxonomy_render_nodes($result) {
-  $output = '';
-  $nids = array();
-  foreach ($result as $record) {
-    $nids[] = $record->nid;
-  }
-  if (!empty($nids)) {
-    $nodes = node_load_multiple($nids);
-
-    foreach ($nodes as $node) {
-      $output .= node_view($node, 1);
-    }
-    $output .= theme('pager', NULL, variable_get('default_nodes_main', 10), 0);
-  }
-  else {
-    $output .= '<p>' . t('There are currently no posts in this category.') . '</p>';
-  }
-  return $output;
+  return $nids;
 }
 
 /**
diff --git modules/taxonomy/taxonomy.pages.inc modules/taxonomy/taxonomy.pages.inc
index 1e448d0..0c5144e 100644
--- modules/taxonomy/taxonomy.pages.inc
+++ modules/taxonomy/taxonomy.pages.inc
@@ -42,12 +42,44 @@ function taxonomy_term_page($terms, $depth = 0, $op = 'page') {
           $breadcrumb[] = l(t('Home'), NULL);
           $breadcrumb = array_reverse($breadcrumb);
           drupal_set_breadcrumb($breadcrumb);
-
-          $output = theme('taxonomy_term_page', $tids, taxonomy_select_nodes($tids, $terms['operator'], $depth, TRUE));
           drupal_add_feed(url('taxonomy/term/' . $str_tids . '/' . $depth . '/feed'), 'RSS - ' . $title);
-          return $output;
-          break;
+          
+          if ($nids = taxonomy_select_nodes($tids, $terms['operator'], $depth, TRUE)) {
+            drupal_add_css(drupal_get_path('module', 'taxonomy') . '/taxonomy.css');
+            $page = array();
+
+            $nodes = node_load_multiple($nids);
+            $page += node_build_multiple($nodes);
+            $page['pager'] = array(
+              '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)),
+              '#weight' => count($nodes),
+            );
+          }
+          else {
+            $page['no_posts'] = array(
+              '#prefix' => '<p>',
+              '#markup' => t('There are currently no posts in this category.'),
+              '#suffix' => '</p>',
+            );
+          }
 
+          // Only display the description if we have a single term, to avoid clutter and confusion.
+          if (count($tids) == 1) {
+            $term = taxonomy_term_load($tids[0]);
+            $description = $term->description;
+
+            // Check that a description is set.
+            if (!empty($description)) {
+              $page['term_description'] = array(
+                '#markup' => filter_xss_admin($description),
+                '#weight' => -1,
+                '#prefix' => '<div class="taxonomy-term-description">',
+                '#suffix' => '</div>',
+              );
+            }
+          }
+          return $page;
+          break;
         case 'feed':
           $channel['link'] = url('taxonomy/term/' . $str_tids . '/' . $depth, array('absolute' => TRUE));
           $channel['title'] = variable_get('site_name', 'Drupal') . ' - ' . $title;
@@ -58,13 +90,9 @@ function taxonomy_term_page($terms, $depth = 0, $op = 'page') {
             $channel['description'] = $term->description;
           }
 
-          $result = taxonomy_select_nodes($tids, $terms['operator'], $depth, FALSE);
-          $items = array();
-          while ($row = db_fetch_object($result)) {
-            $items[] = $row->nid;
-          }
+          $nids = taxonomy_select_nodes($tids, $terms['operator'], $depth, FALSE);
 
-          node_feed($items, $channel);
+          node_feed($nids, $channel);
           break;
 
         default:
@@ -78,38 +106,6 @@ function taxonomy_term_page($terms, $depth = 0, $op = 'page') {
 }
 
 /**
- * Render a taxonomy term page HTML output.
- *
- * @param $tids
- *   An array of term ids.
- * @param $result
- *   A pager_query() result, such as that performed by taxonomy_select_nodes().
- *
- * @ingroup themeable
- */
-function theme_taxonomy_term_page($tids, $result) {
-  drupal_add_css(drupal_get_path('module', 'taxonomy') . '/taxonomy.css');
-  $output = '';
-
-  // Only display the description if we have a single term, to avoid clutter and confusion.
-  if (count($tids) == 1) {
-    $term = taxonomy_term_load($tids[0]);
-    $description = $term->description;
-
-    // Check that a description is set.
-    if (!empty($description)) {
-      $output .= '<div class="taxonomy-term-description">';
-      $output .= filter_xss_admin($description);
-      $output .= '</div>';
-    }
-  }
-
-  $output .= taxonomy_render_nodes($result);
-
-  return $output;
-}
-
-/**
  * Page to edit a vocabulary term.
  */
 function taxonomy_term_edit($term) {
diff --git modules/upload/upload.test modules/upload/upload.test
index 11c7e98..4cd4973 100644
--- modules/upload/upload.test
+++ modules/upload/upload.test
@@ -53,7 +53,7 @@ class UploadTestCase extends DrupalWebTestCase {
     
     // Assure that the attachment link appears on teaser view and has correct count.
     $node = node_load($node->nid);
-    $teaser = node_view($node, TRUE);
+    $teaser = drupal_render(node_view($node, TRUE));
     $this->assertTrue(strpos($teaser, format_plural(2, '1 attachment', '@count attachments')), 'Attachments link found on node teaser.');
 
     // Fetch db record and use fid to rename and delete file.
