diff --git a/includes/common.inc b/includes/common.inc
index af3371cc2f2f199efab2df91d9c59dc7f4fa0572..cd63bcc556b256f6b1ab2d0894294cd1927becd5 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -1570,3 +1570,116 @@ function drupal_cron_run() {
   // Return TRUE so other functions can check if it did run successfully
   return TRUE;
 }
+
+/**
+ * Renders HTML given a structured array tree. Recursively iterates over each
+ * of the array elements, generating HTML code. This function is usually
+ * called from within a another function, like drupal_get_form() or node_view().
+ *
+ * @param $elements
+ *   The structured array describing the data to be rendered.
+ * @return
+ *   The rendered HTML.
+ */
+function drupal_render(&$elements) {
+  if (!isset($elements)) {
+    return NULL;
+  }
+  $content = '';
+  uasort($elements, "_element_sort");
+  if (!isset($elements['#children'])) {
+    $children = element_children($elements);
+    /* Render all the children that use a theme function */
+    if (isset($elements['#theme']) && !$elements['#theme_used']) {
+      $elements['#theme_used'] = TRUE;
+
+      $previous_value = $elements['#value'];
+      $previous_type = $elements['#type'];
+      // If we rendered a single element, then we will skip the renderer.
+      if (empty($children)) {
+        $elements['#printed'] = TRUE;
+      }
+      else {
+        $elements['#value'] = '';
+      }
+      $elements['#type'] = 'markup';
+
+      $content = theme($elements['#theme'], $elements);
+
+      $elements['#value'] = $previous_value;
+      $elements['#type'] = $previous_type;
+      unset($elements['#prefix'], $elements['#suffix']);
+    }
+    /* 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 (isset($content) && $content !== '') {
+    $elements['#children'] = $content;
+  }
+
+  // Until now, we rendered the children, here we render the element itself
+  if (!isset($elements['#printed'])) {
+    $content = theme(($elements['#type']) ? $elements['#type']: 'markup', $elements);
+    $elements['#printed'] = TRUE;
+  }
+
+  if (isset($content) && $content !== '') {
+    $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : '';
+    $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
+    $content = $prefix . $content . $suffix;
+
+    if (isset($elements['#after_render'])) {
+      foreach ($elements['#after_render'] as $function) {
+        $function($elements, $content);
+      }
+    }
+
+    return $content;
+  }
+}
+
+/**
+ * Function used by uasort in drupal_render() to sort structured arrays
+ * by weight.
+ */
+function _element_sort($a, $b) {
+  $a_weight = (is_array($a) && isset($a['#weight'])) ? $a['#weight'] : 0;
+  $b_weight = (is_array($b) && isset($b['#weight'])) ? $b['#weight'] : 0;
+  if ($a_weight == $b_weight) {
+    return 0;
+  }
+  return ($a_weight < $b_weight) ? -1 : 1;
+}
+
+/**
+ * Check if the key is a property.
+ */
+function element_property($key) {
+  return $key[0] == '#';
+}
+
+/**
+ * Get properties of a structured array element. Properties begin with '#'.
+ */
+function element_properties($element) {
+  return array_filter(array_keys((array) $element), 'element_property');
+}
+
+/**
+ * Check if the key is a child.
+ */
+function element_child($key) {
+  return $key[0] != '#';
+}
+
+/**
+ * Get keys of a structured array tree element that are not properties
+ * (i.e., do not begin with '#').
+ */
+function element_children($element) {
+  return array_filter(array_keys((array) $element), 'element_child');
+}
\ No newline at end of file
diff --git a/includes/form.inc b/includes/form.inc
index 45df7f9fa1b7cb0466b9bc844879eef88ad5aa71..3fe49840610a0006c25b30bfcd9a8fc530fb1678 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -14,34 +14,6 @@
  * http://api.drupal.org/api/HEAD/file/developer/topics/forms_api.html
  */
 
-/**
- * Check if the key is a property.
- */
-function element_property($key) {
-  return $key[0] == '#';
-}
-
-/**
- * Get properties of a form tree element. Properties begin with '#'.
- */
-function element_properties($element) {
-  return array_filter(array_keys((array) $element), 'element_property');
-}
-
-/**
- * Check if the key is a child.
- */
-function element_child($key) {
-  return $key[0] != '#';
-}
-
-/**
- * Get keys of a form tree element that are not properties (i.e., do not begin with '#').
- */
-function element_children($element) {
-  return array_filter(array_keys((array) $element), 'element_child');
-}
-
 /**
  * Processes a form array and produces the HTML output of a form.
  * If there is input in the $_POST['edit'] variable, this function
@@ -263,7 +235,7 @@ function drupal_render_form($form_id, &$form, $callback = NULL) {
     }
   }
 
-  $output = form_render($form);
+  $output = drupal_render($form);
   return $output;
 }
 
@@ -588,82 +560,6 @@ function _form_set_value(&$form_values, $form, $parents, $value) {
   return $form;
 }
 
-/**
- * Renders a HTML form given a form tree. Recursively iterates over each of
- * the form elements, generating HTML code. This function is usually
- * called from within a theme. To render a form from within a module, use
- * drupal_get_form().
- *
- * @param $elements
- *   The form tree describing the form.
- * @return
- *   The rendered HTML form.
- */
-function form_render(&$elements) {
-  if (!isset($elements)) {
-    return NULL;
-  }
-  $content = '';
-  uasort($elements, "_form_sort");
-  if (!isset($elements['#children'])) {
-    $children = element_children($elements);
-    /* Render all the children that use a theme function */
-    if (isset($elements['#theme']) && !$elements['#theme_used']) {
-      $elements['#theme_used'] = TRUE;
-
-      $previous_value = $elements['#value'];
-      $previous_type = $elements['#type'];
-      // If we rendered a single element, then we will skip the renderer.
-      if (empty($children)) {
-        $elements['#printed'] = TRUE;
-      }
-      else {
-        $elements['#value'] = '';
-      }
-      $elements['#type'] = 'markup';
-
-      $content = theme($elements['#theme'], $elements);
-
-      $elements['#value'] = $previous_value;
-      $elements['#type'] = $previous_type;
-      unset($elements['#prefix'], $elements['#suffix']);
-    }
-    /* render each of the children using form_render and concatenate them */
-    if (!isset($content) || $content === '') {
-      foreach ($children as $key) {
-        $content .= form_render($elements[$key]);
-      }
-    }
-  }
-  if (isset($content) && $content !== '') {
-    $elements['#children'] = $content;
-  }
-
-  // Until now, we rendered the children, here we render the element itself
-  if (!isset($elements['#printed'])) {
-    $content = theme(($elements['#type']) ? $elements['#type']: 'markup', $elements);
-    $elements['#printed'] = TRUE;
-  }
-
-  if (isset($content) && $content !== '') {
-    $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : '';
-    $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
-    return $prefix . $content . $suffix;
-  }
-}
-
-/**
- * Function used by uasort in form_render() to sort form by weight.
- */
-function _form_sort($a, $b) {
-  $a_weight = (is_array($a) && isset($a['#weight'])) ? $a['#weight'] : 0;
-  $b_weight = (is_array($b) && isset($b['#weight'])) ? $b['#weight'] : 0;
-  if ($a_weight == $b_weight) {
-    return 0;
-  }
-  return ($a_weight < $b_weight) ? -1 : 1;
-}
-
 /**
  * Retrieve the default properties for the defined element type.
  */
diff --git a/includes/locale.inc b/includes/locale.inc
index 7fa3aa9c3ac3de427b825873206495620e7521cb..ff4232143fc7a24a2dcbe0bf9ef077cbf19565db 100644
--- a/includes/locale.inc
+++ b/includes/locale.inc
@@ -86,12 +86,12 @@ function theme_locale_admin_manage_screen($form) {
   foreach ($form['name'] as $key => $element) {
     // Do not take form control structures.
     if (is_array($element) && element_child($key)) {
-      $rows[] = array(check_plain($key), form_render($form['name'][$key]), form_render($form['enabled'][$key]), form_render($form['site_default'][$key]), ($key != 'en' ? form_render($form['translation'][$key]) : t('n/a')), ($key != 'en' ? l(t('delete'), 'admin/settings/locale/language/delete/'. $key) : ''));
+      $rows[] = array(check_plain($key), drupal_render($form['name'][$key]), drupal_render($form['enabled'][$key]), drupal_render($form['site_default'][$key]), ($key != 'en' ? drupal_render($form['translation'][$key]) : t('n/a')), ($key != 'en' ? l(t('delete'), 'admin/settings/locale/language/delete/'. $key) : ''));
     }
   }
   $header = array(array('data' => t('Code')), array('data' => t('English name')), array('data' => t('Enabled')), array('data' => t('Default')), array('data' => t('Translated')), array('data' => t('Operations')));
   $output = theme('table', $header, $rows);
-  $output .= form_render($form);
+  $output .= drupal_render($form);
 
   return $output;
 }
diff --git a/modules/aggregator/aggregator.module b/modules/aggregator/aggregator.module
index 8e7ab05d89d0af47acdb6fe8e5dff49f9173a3f6..0b7d3cbd299a4caa381e2d7787a3544ba727cb0d 100644
--- a/modules/aggregator/aggregator.module
+++ b/modules/aggregator/aggregator.module
@@ -1105,19 +1105,19 @@ function _aggregator_page_list($sql, $op, $header = '') {
 
 function theme_aggregator_page_list($form) {
   $output = '<div id="aggregator">';
-  $output .= form_render($form['header']);
+  $output .= drupal_render($form['header']);
   $rows = array();
   if ($form['items']) {
     foreach (element_children($form['items']) as $key) {
       if (is_array($form['items'][$key])) {
-        $rows[] = array(form_render($form['items'][$key]), array('data' => form_render($form['categories'][$key]), 'class' => 'categorize-item'));
+        $rows[] = array(drupal_render($form['items'][$key]), array('data' => drupal_render($form['categories'][$key]), 'class' => 'categorize-item'));
       }
     }
   }
   $output .= theme('table', array('', t('Categorize')), $rows);
-  $output .= form_render($form['submit']);
+  $output .= drupal_render($form['submit']);
   $output .= '</div>';
-  $output .= form_render($form);
+  $output .= drupal_render($form);
   return $output;
 }
 
diff --git a/modules/block/block.module b/modules/block/block.module
index 2b6d6b9481273973fa55de0cd6b936d9fbbb3357..c22667b6ec821c6d45a2ff4db2490001892f2f2d 100644
--- a/modules/block/block.module
+++ b/modules/block/block.module
@@ -320,16 +320,16 @@ function theme_block_admin_display($form) {
 
       // Generate block row
       $row = array(
-        array('data' => form_render($block['info']), 'class' => 'block'),
-        form_render($block['status']) . form_render($block['theme']),
-        form_render($block['weight']),
-        form_render($block['region'])
+        array('data' => drupal_render($block['info']), 'class' => 'block'),
+        drupal_render($block['status']) . drupal_render($block['theme']),
+        drupal_render($block['weight']),
+        drupal_render($block['region'])
       );
       if ($throttle) {
-        $row[] = form_render($block['throttle']);
+        $row[] = drupal_render($block['throttle']);
       }
-      $row[] = form_render($block['configure']);
-      $row[] = $block['delete'] ? form_render($block['delete']) : '';
+      $row[] = drupal_render($block['configure']);
+      $row[] = $block['delete'] ? drupal_render($block['delete']) : '';
       $rows[] = $row;
     }
   }
@@ -342,10 +342,10 @@ function theme_block_admin_display($form) {
   $header[] = array('data' => t('Operations'), 'colspan' => 2);
 
   $output = theme('table', $header, $rows, array('id' => 'blocks'));
-  $output .= form_render($form['submit']);
-  // Also render the form_id as there is no form_render($form) call (as form_render does not appear to handle the
+  $output .= drupal_render($form['submit']);
+  // Also render the form_id as there is no drupal_render($form) call (as drupal_render does not appear to handle the
   // multi-dimensional block form array very well).
-  $output .= form_render($form['form_id']);
+  $output .= drupal_render($form['form_id']);
 
   return $output;
 }
diff --git a/modules/blog/blog.module b/modules/blog/blog.module
index 4613c063429b79d4f56495b5fbcbd000dc5a3147..5e829a05c09709af2e32740fa157cdeee4153d77 100644
--- a/modules/blog/blog.module
+++ b/modules/blog/blog.module
@@ -235,7 +235,7 @@ function blog_form(&$node) {
 /**
  * Implementation of hook_view().
  */
-function blog_view(&$node, $teaser = FALSE, $page = FALSE) {
+function blog_view($node, $teaser = FALSE, $page = FALSE) {
   if ($page) {
     // Breadcrumb navigation
     $breadcrumb[] = array('path' => 'blog', 'title' => t('blogs'));
@@ -243,7 +243,7 @@ function blog_view(&$node, $teaser = FALSE, $page = FALSE) {
     $breadcrumb[] = array('path' => 'node/'. $node->nid);
     menu_set_location($breadcrumb);
   }
-  $node = node_prepare($node, $teaser);
+  return node_prepare($node, $teaser);
 }
 
 /**
diff --git a/modules/book/book.module b/modules/book/book.module
index f389b4f7e1e3184d2e0f425a36ca0ce8401cc483..a6c59fdf5f586b2399f74ecf90428c3b649665b0 100644
--- a/modules/book/book.module
+++ b/modules/book/book.module
@@ -439,16 +439,6 @@ function book_content($node, $teaser = FALSE) {
   return node_prepare($node, $teaser);
 }
 
-/**
- * Implementation of hook_view().
- *
- * If not displayed on the main page, we render the node as a page in the
- * book with extra links to the previous and next pages.
- */
-function book_view(&$node, $teaser = FALSE, $page = FALSE) {
-  $node = node_prepare($node, $teaser);
-}
-
 /**
  * Implementation of hook_nodeapi().
  *
@@ -472,7 +462,10 @@ function book_nodeapi(&$node, $op, $teaser, $page) {
           }
           $node->breadcrumb[] = array('path' => 'node/'. $node->nid);
 
-          $node->body .= theme('book_navigation', $node);
+          $node->content['book_navigation'] = array(
+            '#value' => theme('book_navigation', $node),
+            '#weight' => 100,
+          );
 
           if ($page) {
             menu_set_location($node->breadcrumb);
@@ -807,7 +800,7 @@ function book_node_visitor_html_pre($node, $depth, $nid) {
   $output .= "<h1 class=\"book-heading\">". check_plain($node->title) ."</h1>\n";
 
   if ($node->body) {
-    $output .= $node->body;
+    $output .= drupal_render($node->content);
   }
   return $output;
 }
@@ -868,8 +861,8 @@ function theme_book_admin_table($form) {
     $nid = $form[$key]['nid']['#value'];
     $pid = $form[0]['nid']['#value'];
     $rows[] = array(
-      '<div style="padding-left: '. (25 * $form[$key]['depth']['#value']) .'px;">'. form_render($form[$key]['title']) .'</div>',
-      form_render($form[$key]['weight']),
+      '<div style="padding-left: '. (25 * $form[$key]['depth']['#value']) .'px;">'. drupal_render($form[$key]['title']) .'</div>',
+      drupal_render($form[$key]['weight']),
       l(t('view'), 'node/'. $nid),
       l(t('edit'), 'node/'. $nid .'/edit'),
       l(t('delete'), 'node/'. $nid .'/delete', NULL, 'destination=admin/content/book'. (arg(3) == 'orphan' ? '/orphan' : '') . ($pid != $nid ? '/'.$pid : ''))
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index d66101c6dc8bd9d2bda399b07de49a5b467957b8..a6c14879dbd4a32cbe5e2512b7b1718a8f33f494 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -1085,15 +1085,15 @@ function comment_admin_overview_submit($form_id, $edit) {
 }
 
 function theme_comment_admin_overview($form) {
-  $output = form_render($form['options']);
+  $output = drupal_render($form['options']);
   if (isset($form['subject']) && is_array($form['subject'])) {
     foreach (element_children($form['subject']) as $key) {
       $row = array();
-      $row[] = form_render($form['comments'][$key]);
-      $row[] = form_render($form['subject'][$key]);
-      $row[] = form_render($form['username'][$key]);
-      $row[] = form_render($form['timestamp'][$key]);
-      $row[] = form_render($form['operations'][$key]);
+      $row[] = drupal_render($form['comments'][$key]);
+      $row[] = drupal_render($form['subject'][$key]);
+      $row[] = drupal_render($form['username'][$key]);
+      $row[] = drupal_render($form['timestamp'][$key]);
+      $row[] = drupal_render($form['operations'][$key]);
       $rows[] = $row;
     }
   }
@@ -1103,10 +1103,10 @@ function theme_comment_admin_overview($form) {
 
   $output .= theme('table', $form['header']['#value'], $rows);
   if ($form['pager']['#value']) {
-    $output .= form_render($form['pager']);
+    $output .= drupal_render($form['pager']);
   }
 
-  $output .= form_render($form);
+  $output .= drupal_render($form);
 
   return $output;
 }
@@ -1580,7 +1580,7 @@ function comment_controls($mode = COMMENT_MODE_THREADED_EXPANDED, $order = COMME
 
 function theme_comment_controls($form) {
   $output .= '<div class="container-inline">';
-  $output .=  form_render($form);
+  $output .=  drupal_render($form);
   $output .= '</div>';
   $output .= '<div class="description">'. t('Select your preferred way to display the comments and click "Save settings" to activate your changes.') .'</div>';
   return theme('box', t('Comment viewing options'), $output);
diff --git a/modules/filter/filter.module b/modules/filter/filter.module
index 2aafbe0ece327d74bc4bdf869d34904407767be4..75756f9d642a9a12464bef855a79ce56f4f57206 100644
--- a/modules/filter/filter.module
+++ b/modules/filter/filter.module
@@ -331,18 +331,18 @@ function theme_filter_admin_overview($form) {
   foreach ($form as $name => $element) {
     if (isset($element['roles']) && is_array($element['roles'])) {
       $rows[] = array(
-        form_render($form['default'][$element['id']['#value']]),
+        drupal_render($form['default'][$element['id']['#value']]),
         check_plain($name),
-        form_render($element['roles']),
-        form_render($element['configure']),
-        form_render($element['delete'])
+        drupal_render($element['roles']),
+        drupal_render($element['configure']),
+        drupal_render($element['delete'])
       );
       unset($form[$name]);
     }
   }
   $header = array(t('Default'), t('Name'), t('Roles'), array('data' => t('Operations'), 'colspan' => 2));
   $output = theme('table', $header, $rows);
-  $output .= form_render($form);
+  $output .= drupal_render($form);
 
   return $output;
 }
@@ -561,12 +561,12 @@ function theme_filter_admin_order($form) {
   foreach (element_children($form['names']) as $id) {
     // Don't take form control structures
     if (is_array($form['names'][$id])) {
-      $rows[] = array(form_render($form['names'][$id]), form_render($form['weights'][$id]));
+      $rows[] = array(drupal_render($form['names'][$id]), drupal_render($form['weights'][$id]));
     }
   }
 
   $output = theme('table', $header, $rows);
-  $output .= form_render($form);
+  $output .= drupal_render($form);
 
   return $output;
 }
diff --git a/modules/forum/forum.module b/modules/forum/forum.module
index 2c414ed0e215e3082881908fb10dca5d674f3db9..f2a4b47258cf57d6c3167c543b12b865fb63c595 100644
--- a/modules/forum/forum.module
+++ b/modules/forum/forum.module
@@ -313,8 +313,11 @@ function forum_view(&$node, $teaser = FALSE, $page = FALSE) {
   }
 
   $node = node_prepare($node, $teaser);
-
-  $node->body .= theme('forum_topic_navigation', $node);
+  $node->content['forum_navigation'] = array(
+    '#value' => theme('forum_topic_navigation', $node),
+    '#weight' => 100,
+  );
+  return $node;
 }
 
 /**
diff --git a/modules/node/node.module b/modules/node/node.module
index 9424cbb6e0ea3988633b0206f47ac0ecf1bfcc31..62e1ddab7ca7a066f25282dc166d2cb6693e2535 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -636,24 +636,6 @@ function node_save(&$node) {
 function node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE) {
   $node = (object)$node;
 
-  // Remove the delimiter (if any) that separates the teaser from the body.
-  // TODO: this strips legitimate uses of '<!--break-->' also.
-  $node->body = str_replace('<!--break-->', '', $node->body);
-
-  if ($node->log != '' && !$teaser) {
-    $node->body .= '<div class="log"><div class="title">'. t('Log') .':</div>'. filter_xss($node->log) .'</div>';
-  }
-
-  // The 'view' hook can be implemented to overwrite the default function
-  // to display nodes.
-  if (node_hook($node, 'view')) {
-    node_invoke($node, 'view', $teaser, $page);
-  }
-  else {
-    $node = node_prepare($node, $teaser);
-  }
-  // Allow modules to change $node->body before viewing.
-  node_invoke_nodeapi($node, 'view', $teaser, $page);
   if ($links) {
     $node->links = module_invoke_all('link', 'node', $node, !$page);
 
@@ -662,11 +644,18 @@ function node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE) {
       $function($node, $node->links);
     }
   }
-  // unset unused $node part so that a bad theme can not open a security hole
+
+  $node = node_build_content($node, $teaser, $page);
+
+  // Set the proper node part, then unset unused $node part so that a bad
+  // theme can not open a security hole.
+  $content = drupal_render($node->content);
   if ($teaser) {
+    $node->teaser = $content;
     unset($node->body);
   }
   else {
+    $node->body = $content;
     unset($node->teaser);
   }
 
@@ -674,16 +663,62 @@ function node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE) {
 }
 
 /**
- * Apply filters to a node in preparation for theming.
+ * Apply filters and build the node's standard elements.
  */
 function node_prepare($node, $teaser = FALSE) {
-  $node->readmore = (strlen($node->teaser) < strlen($node->body));
-  if ($teaser == FALSE) {
-    $node->body = check_markup($node->body, $node->format, FALSE);
+  $node->content['body'] = array(
+    '#value' => check_markup($teaser ? $node->teaser : $node->body, $node->format, FALSE),
+    '#weight' => 0,
+  );
+
+  if ($node->log != '' && !$teaser) {
+    $node->content['log_message'] = array(
+      '#value' => theme('node_log_message', filter_xss($node->log)),
+      '#weight' => 20,
+    );
+  }
+
+  if (strlen($node->teaser) < strlen($node->body)) {
+    $node->readmore = TRUE;
+  }
+
+  return $node;
+}
+
+/**
+ * Builds a structured array representing the node's content.
+ *
+ * @param $node
+ *   A node object.
+ * @param $teaser
+ *   Whether to display the teaser only, as on the main page.
+ * @param $page
+ *   Whether the node is being displayed by itself as a page.
+ *
+ * @return
+ *   An structured array containing the individual elements
+ *   of the node's body.
+ */
+function node_build_content($node, $teaser = FALSE, $page = FALSE) {
+  // Remove the delimiter (if any) that separates the teaser from the body.
+  // TODO: this strips legitimate uses of '<!--break-->' also.
+  $node->body = str_replace('<!--break-->', '', $node->body);
+
+  // The 'view' hook can be implemented to overwrite the default function
+  // to display nodes.
+  if (node_hook($node, 'view')) {
+    $node = node_invoke($node, 'view', $teaser, $page);
   }
   else {
-    $node->teaser = check_markup($node->teaser, $node->format, FALSE);
+    $node = node_prepare($node, $teaser);
   }
+
+  // Allow modules to make their own additions to the node.
+  node_invoke_nodeapi($node, 'view', $teaser, $page);
+
+  // Allow modules to modify the fully-built node.
+  node_invoke_nodeapi($node, 'alter', $teaser, $page);
+
   return $node;
 }
 
@@ -838,17 +873,10 @@ function node_search($op = 'search', $keys = NULL) {
       // Load results
       $results = array();
       foreach ($find as $item) {
+        // Build the node body.
         $node = node_load($item->sid);
-
-        // Get node output (filtered and with module-specific fields).
-        if (node_hook($node, 'view')) {
-          node_invoke($node, 'view', FALSE, FALSE);
-        }
-        else {
-          $node = node_prepare($node, FALSE);
-        }
-        // Allow modules to change $node->body before viewing.
-        node_invoke_nodeapi($node, 'view', FALSE, FALSE);
+        $node = node_build_content($node, FALSE, FALSE);
+        $node->body = drupal_render($node->content);
 
         // Fetch comments for snippet
         $node->body .= module_invoke('comment', 'nodeapi', $node, 'update index');
@@ -880,19 +908,19 @@ function node_user($op, &$edit, &$user) {
 }
 
 function theme_node_search_admin($form) {
-  $output = form_render($form['info']);
+  $output = drupal_render($form['info']);
 
   $header = array(t('Factor'), t('Weight'));
   foreach (element_children($form['factors']) as $key) {
     $row = array();
     $row[] = $form['factors'][$key]['#title'];
     unset($form['factors'][$key]['#title']);
-    $row[] = form_render($form['factors'][$key]);
+    $row[] = drupal_render($form['factors'][$key]);
     $rows[] = $row;
   }
   $output .= theme('table', $header, $rows);
 
-  $output .= form_render($form);
+  $output .= drupal_render($form);
   return $output;
 }
 
@@ -1268,9 +1296,9 @@ function node_filter_form() {
  */
 function theme_node_filter_form(&$form) {
   $output .= '<div id="node-admin-filter">';
-  $output .= form_render($form['filters']);
+  $output .= drupal_render($form['filters']);
   $output .= '</div>';
-  $output .= form_render($form);
+  $output .= drupal_render($form);
   return $output;
 }
 
@@ -1281,25 +1309,25 @@ function theme_node_filters(&$form) {
   $output .= '<ul>';
   if (sizeof($form['current'])) {
     foreach (element_children($form['current']) as $key) {
-      $output .= '<li>' . form_render($form['current'][$key]) . '</li>';
+      $output .= '<li>' . drupal_render($form['current'][$key]) . '</li>';
     }
   }
 
   $output .= '<li><dl class="multiselect">' . (sizeof($form['current']) ? '<dt><em>'. t('and') .'</em> '. t('where') .'</dt>' : '') . '<dd class="a">';
   foreach (element_children($form['filter']) as $key) {
-    $output .= form_render($form['filter'][$key]);
+    $output .= drupal_render($form['filter'][$key]);
   }
   $output .= '</dd>';
 
   $output .= '<dt>'. t('is') .'</dt>' . '<dd class="b">';
 
   foreach (element_children($form['status']) as $key) {
-    $output .= form_render($form['status'][$key]);
+    $output .= drupal_render($form['status'][$key]);
   }
   $output .= '</dd>';
 
   $output .= '</dl>';
-  $output .= '<div class="container-inline" id="node-admin-buttons">'. form_render($form['buttons']) .'</div>';
+  $output .= '<div class="container-inline" id="node-admin-buttons">'. drupal_render($form['buttons']) .'</div>';
   $output .= '</li></ul><br class="clear" />';
 
   return $output;
@@ -1414,16 +1442,16 @@ function theme_node_admin_nodes($form) {
   // Overview table:
   $header = array(NULL, t('Title'), t('Type'), t('Author'), t('Status'), t('Operations'));
 
-  $output .= form_render($form['options']);
+  $output .= drupal_render($form['options']);
   if (isset($form['title']) && is_array($form['title'])) {
     foreach (element_children($form['title']) as $key) {
       $row = array();
-      $row[] = form_render($form['nodes'][$key]);
-      $row[] = form_render($form['title'][$key]);
-      $row[] = form_render($form['name'][$key]);
-      $row[] = form_render($form['username'][$key]);
-      $row[] = form_render($form['status'][$key]);
-      $row[] = form_render($form['operations'][$key]);
+      $row[] = drupal_render($form['nodes'][$key]);
+      $row[] = drupal_render($form['title'][$key]);
+      $row[] = drupal_render($form['name'][$key]);
+      $row[] = drupal_render($form['username'][$key]);
+      $row[] = drupal_render($form['status'][$key]);
+      $row[] = drupal_render($form['operations'][$key]);
       $rows[] = $row;
     }
 
@@ -1434,10 +1462,10 @@ function theme_node_admin_nodes($form) {
 
   $output .= theme('table', $header, $rows);
   if ($form['pager']['#value']) {
-    $output .= form_render($form['pager']);
+    $output .= drupal_render($form['pager']);
   }
 
-  $output .= form_render($form);
+  $output .= drupal_render($form);
 
   return $output;
 }
@@ -1901,31 +1929,31 @@ function node_form_add_preview($form) {
 function theme_node_form($form) {
   $output = "\n<div class=\"node-form\">\n";
   if (isset($form['node_preview'])) {
-    $output .= form_render($form['node_preview']);
+    $output .= drupal_render($form['node_preview']);
   }
 
   // Admin form fields and submit buttons must be rendered first, because
   // they need to go to the bottom of the form, and so should not be part of
-  // the catch-all call to form_render().
+  // the catch-all call to drupal_render().
   $admin = '';
   if (isset($form['author'])) {
     $admin .= "    <div class=\"authored\">\n";
-    $admin .= form_render($form['author']);
+    $admin .= drupal_render($form['author']);
     $admin .= "    </div>\n";
   }
   if (isset($form['options'])) {
     $admin .= "    <div class=\"options\">\n";
-    $admin .= form_render($form['options']);
+    $admin .= drupal_render($form['options']);
     $admin .= "    </div>\n";
   }
-  $buttons = form_render($form['preview']);
-  $buttons .= form_render($form['submit']);
-  $buttons .= isset($form['delete']) ? form_render($form['delete']) : '';
+  $buttons = drupal_render($form['preview']);
+  $buttons .= drupal_render($form['submit']);
+  $buttons .= isset($form['delete']) ? drupal_render($form['delete']) : '';
 
   // Everything else gets rendered here, and is displayed before the admin form
   // field and the submit buttons.
   $output .= "  <div class=\"standard\">\n";
-  $output .= form_render($form);
+  $output .= drupal_render($form);
   $output .= "  </div>\n";
 
   if (!empty($admin)) {
@@ -2049,6 +2077,10 @@ function theme_node_preview($node) {
   return $output;
 }
 
+function theme_node_log_message($log) {
+  return '<div class="log"><div class="title">'. t('Log') .':</div>'. $log .'</div>';
+}
+
 function node_form_submit($form_id, $edit) {
   global $user;
 
@@ -2328,15 +2360,10 @@ function node_update_index() {
     $last_nid = $node->nid;
     $node = node_load($node->nid);
 
-    // Get node output (filtered and with module-specific fields).
-    if (node_hook($node, 'view')) {
-      node_invoke($node, 'view', FALSE, FALSE);
-    }
-    else {
-      $node = node_prepare($node, FALSE);
-    }
-    // Allow modules to change $node->body before viewing.
-    node_invoke_nodeapi($node, 'view', FALSE, FALSE);
+    // Build the node body.
+    $node = node_load($item->sid);
+    $node = node_build_content($node, FALSE, FALSE);
+    $node->body = drupal_render($node->content);
 
     $text = '<h1>'. check_plain($node->title) .'</h1>'. $node->body;
 
diff --git a/modules/poll/poll.module b/modules/poll/poll.module
index 97390c5b234bdf3f8da2eade162541422e69532f..9499a67649caa6e0a6c7097783f04ecfaa869b62 100644
--- a/modules/poll/poll.module
+++ b/modules/poll/poll.module
@@ -56,12 +56,11 @@ function poll_block($op = 'list', $delta = 0) {
         $poll = node_load(array('type' => 'poll', 'created' => $timestamp, 'status' => 1));
 
         if ($poll->nid) {
-          // poll_view() dumps the output into $poll->body.
-          poll_view($poll, 1, 0, 1);
+          $poll = poll_view($poll, TRUE, FALSE, TRUE);
         }
       }
       $block['subject'] = t('Poll');
-      $block['content'] = $poll->body;
+      $block['content'] = drupal_render($poll->content);
       return $block;
     }
   }
@@ -343,12 +342,12 @@ function theme_poll_view_voting($form) {
   $output .= '<div class="poll">';
   $output .= '  <div class="vote-form">';
   $output .= '    <div class="choices">';
-  $output .= form_render($form['choice']);
+  $output .= drupal_render($form['choice']);
   $output .= '    </div>';
-  $output .= form_render($form['nid']);
-  $output .= form_render($form['vote']);
+  $output .= drupal_render($form['nid']);
+  $output .= drupal_render($form['vote']);
   $output .= '  </div>';
-  $output .= form_render($form);
+  $output .= drupal_render($form);
   $output .= '</div>';
   return $output;
 }
@@ -541,14 +540,14 @@ function poll_cancel(&$node) {
  *   An extra parameter that adapts the hook to display a block-ready
  *   rendering of the poll.
  */
-function poll_view(&$node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
+function poll_view($node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
   global $user;
   $output = '';
 
   // Special display for side-block
   if ($block) {
     // No 'read more' link
-    $node->body = $node->teaser = '';
+    $node->readmore = FALSE;
 
     $links = module_invoke_all('link', 'node', $node, 1);
     $links[] = array('title' => t('older polls'), 'href' => 'poll', 'attributes' => array('title' => t('View the list of polls on this site.')));
@@ -560,13 +559,16 @@ function poll_view(&$node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
   }
 
   if ($node->allowvotes && ($block || arg(2) != 'results')) {
-    $output .= poll_view_voting($node, $teaser, $page, $block);
+    $node->content['body'] = array(
+      '#value' => poll_view_voting($node, $teaser, $page, $block),
+    );
   }
   else {
-    $output .= poll_view_results($node, $teaser, $page, $block);
+    $node->content['body'] = array(
+      '#value' => poll_view_results($node, $teaser, $page, $block),
+    );
   }
-
-  $node->body = $node->teaser = $output;
+  return $node;
 }
 
 /**
diff --git a/modules/search/search.module b/modules/search/search.module
index abf27a4658ea7bb99058e5037cf6fac509ac42ef..eb2aff9d073d2716b7c66df484edc02b28ef0c6a 100644
--- a/modules/search/search.module
+++ b/modules/search/search.module
@@ -1062,14 +1062,14 @@ function search_box_form_submit($form_id, $form_values) {
  * Theme the theme search form.
  */
 function theme_search_theme_form($form) {
-  return '<div id="search" class="container-inline">'. form_render($form) .'</div>';
+  return '<div id="search" class="container-inline">'. drupal_render($form) .'</div>';
 }
 
 /**
  * Theme the block search form.
  */
 function theme_search_block_form($form) {
-  return '<div class="container-inline">'. form_render($form) .'</div>';
+  return '<div class="container-inline">'. drupal_render($form) .'</div>';
 }
 
 /**
diff --git a/modules/system/system.module b/modules/system/system.module
index 7ad3a891ab8a02d408adcd3f62e085623a00195c..576702b019a28b145419133c03c8ee912eb95332 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -429,9 +429,9 @@ function theme_system_theme_select_form($form) {
   foreach (element_children($form) as $key) {
     $row = array();
     if (is_array($form[$key]['description'])) {
-      $row[] = form_render($form[$key]['screenshot']);
-      $row[] = form_render($form[$key]['description']);
-      $row[] = form_render($form['theme'][$key]);
+      $row[] = drupal_render($form[$key]['screenshot']);
+      $row[] = drupal_render($form[$key]['description']);
+      $row[] = drupal_render($form['theme'][$key]);
     }
     $rows[] = $row;
   }
@@ -1101,7 +1101,7 @@ function system_themes() {
       $form[$info->name]['operations'] = array('#type' => 'markup', '#value' => l(t('configure'), 'admin/build/themes/settings/' . $info->name) );
     }
     else {
-      // Dummy element for form_render. Cleaner than adding a check in the theme function.
+      // Dummy element for drupal_render. Cleaner than adding a check in the theme function.
       $form[$info->name]['operations'] = array();
     }
   }
@@ -1118,12 +1118,12 @@ function theme_system_themes($form) {
   foreach (element_children($form) as $key) {
     $row = array();
     if (is_array($form[$key]['description'])) {
-      $row[] = form_render($form[$key]['screenshot']);
-      $row[] = form_render($form[$key]['description']);
-      $row[] = array('data' => form_render($form['status'][$key]), 'align' => 'center');
+      $row[] = drupal_render($form[$key]['screenshot']);
+      $row[] = drupal_render($form[$key]['description']);
+      $row[] = array('data' => drupal_render($form['status'][$key]), 'align' => 'center');
       if ($form['theme_default']) {
-        $row[] = array('data' => form_render($form['theme_default'][$key]), 'align' => 'center');
-        $row[] = array('data' => form_render($form[$key]['operations']), 'align' => 'center');
+        $row[] = array('data' => drupal_render($form['theme_default'][$key]), 'align' => 'center');
+        $row[] = array('data' => drupal_render($form[$key]['operations']), 'align' => 'center');
       }
     }
     $rows[] = $row;
@@ -1131,7 +1131,7 @@ function theme_system_themes($form) {
 
   $header = array(t('Screenshot'), t('Name'), t('Enabled'), t('Default'), t('Operations'));
   $output = theme('table', $header, $rows);
-  $output .= form_render($form);
+  $output .= drupal_render($form);
   return $output;
 }
 
@@ -1216,12 +1216,12 @@ function system_modules() {
 function theme_system_modules($form) {
   foreach (element_children($form['name']) as $key) {
     $row = array();
-    $row[] = form_render($form['name'][$key]);
-    $row[] = form_render($form['description'][$key]);
-    $row[] = array('data' => form_render($form['status'][$key]), 'align' => 'center');
+    $row[] = drupal_render($form['name'][$key]);
+    $row[] = drupal_render($form['description'][$key]);
+    $row[] = array('data' => drupal_render($form['status'][$key]), 'align' => 'center');
 
     if (module_exist('throttle')) {
-      $row[] = array('data' => form_render($form['throttle'][$key]), 'align' => 'center');
+      $row[] = array('data' => drupal_render($form['throttle'][$key]), 'align' => 'center');
     }
     $rows[] = $row;
   }
@@ -1232,7 +1232,7 @@ function theme_system_modules($form) {
   }
 
   $output = theme('table', $header, $rows);
-  $output .= form_render($form);
+  $output .= drupal_render($form);
   return $output;
 }
 
diff --git a/modules/upload/upload.module b/modules/upload/upload.module
index 15edcd43cecd8cc8214281bafbb22f86201210ec..55f12f7f2d55691e839b1b5d016829c437d79f96 100644
--- a/modules/upload/upload.module
+++ b/modules/upload/upload.module
@@ -484,32 +484,36 @@ function upload_nodeapi(&$node, $op, $teaser) {
 
     case 'view':
       if (isset($node->files) && user_access('view uploaded files')) {
-        // Manipulate so that inline references work in preview
-        if (!variable_get('clean_url', 0)) {
-          $previews = array();
-          foreach ($node->files as $file) {
-            if (strpos($file->fid, 'upload') !== FALSE) {
-              $previews[] = $file;
-            }
+        // Add the attachments list to node body with a heavy
+        // weight to ensure they're below other elements
+        if (count($node->files)) {
+          if (!$teaser && user_access('view uploaded files')) {
+            $node->content['files'] = array(
+              '#value' => theme('upload_attachments', $node->files),
+              '#weight' => 50,
+            );
           }
-
-          // URLs to files being previewed are actually Drupal paths. When Clean
-          // URLs are disabled, the two do not match. We perform an automatic
-          // replacement from temporary to permanent URLs. That way, the author
-          // can use the final URL in the body before having actually saved (to
-          // place inline images for example).
-          foreach ($previews as $file) {
-            $old = file_create_filename($file->filename, file_create_path());
-            $new = url($old);
-            $node->body = str_replace($old, $new, $node->body);
-            $node->teaser = str_replace($old, $new, $node->teaser);
+          // Manipulate so that inline references work in preview
+          if (!variable_get('clean_url', 0)) {
+            $previews = array();
+            foreach ($node->files as $file) {
+              $file = (object)$file;
+              if (strpos($file->fid, 'upload') !== FALSE) {
+                $previews[] = $file;
+              }
+            }
+            // URLs to files being previewed are actually Drupal paths. When Clean
+            // URLs are disabled, the two do not match. We perform an automatic
+            // replacement from temporary to permanent URLs. That way, the author
+            // can use the final URL in the body before having actually saved (to
+            // place inline images for example).
+            foreach ($previews as $file) {
+              $old = file_create_filename($file->filename, file_create_path());
+              $node->content['#upload_urls'][$old] = url($old);
+            }
+            $node->content['#after_render'][] = 'upload_fix_preview_urls';
           }
         }
-
-        // Add the attachments list to node body
-        if (count($node->files) && !$teaser) {
-          $node->body .= theme('upload_attachments', $node->files);
-        }
       }
       break;
 
@@ -558,6 +562,18 @@ function upload_nodeapi(&$node, $op, $teaser) {
   }
 }
 
+function upload_fix_preview_urls($elements, &$content) {
+  if (is_array($elements['#upload_urls'])) {
+    $old_list = array();
+    $new_list = array();
+    foreach ($elements['#upload_urls'] as $old => $new) {
+      $old_list[] = $old;
+      $new_list[] = $new;
+    }
+    $content = str_replace($old_list, $new_list, $content);
+  }
+}
+
 /**
  * Displays file attachments in table
  */
@@ -797,14 +813,14 @@ function theme_upload_form_current(&$form) {
 
   foreach (element_children($form) as $key) {
     $row = array();
-    $row[] = form_render($form[$key]['remove']);
-    $row[] = form_render($form[$key]['list']);
-    $row[] = form_render($form[$key]['description']);
-    $row[] = form_render($form[$key]['size']);
+    $row[] = drupal_render($form[$key]['remove']);
+    $row[] = drupal_render($form[$key]['list']);
+    $row[] = drupal_render($form[$key]['description']);
+    $row[] = drupal_render($form[$key]['size']);
     $rows[] = $row;
   }
   $output = theme('table', $header, $rows);
-  $output .= form_render($form);
+  $output .= drupal_render($form);
   return $output;
 }
 
@@ -813,7 +829,7 @@ function theme_upload_form_current(&$form) {
  * Note: required to output prefix/suffix.
  */
 function theme_upload_form_new($form) {
-  $output = form_render($form);
+  $output = drupal_render($form);
   return $output;
 }
 
@@ -871,7 +887,7 @@ function upload_js() {
     $function('upload_js', $form);
   }
   $form = form_builder('upload_js', $form);
-  $output = theme('status_messages') . form_render($form);
+  $output = theme('status_messages') . drupal_render($form);
   // We send the updated file attachments form.
   print drupal_to_js(array('status' => TRUE, 'data' => $output));
   exit;
diff --git a/modules/user/user.module b/modules/user/user.module
index d49f7415de7e7cbcec898c78413eca71390492ce..2adbe0588985339291aec524660304b489305c48 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -1052,7 +1052,7 @@ function user_pass_submit($form_id, $form_values) {
 
 function theme_user_pass($form) {
   $output = '<p>'. t('Enter your username <strong><em>or</em></strong> your e-mail address.') .'</p>';
-  $output .= form_render($form);
+  $output .= drupal_render($form);
   return $output;
 }
 
@@ -1793,14 +1793,14 @@ function theme_user_admin_perm($form) {
       $row = array();
       // Module name
       if (is_numeric($key)) {
-        $row[] = array('data' => form_render($form['permission'][$key]), 'class' => 'module', 'colspan' => count($form['role_names']) + 1);
+        $row[] = array('data' => drupal_render($form['permission'][$key]), 'class' => 'module', 'colspan' => count($form['role_names']) + 1);
       // Permissions
       }
       else {
-        $row[] = array('data' => form_render($form['permission'][$key]), 'class' => 'permission');
+        $row[] = array('data' => drupal_render($form['permission'][$key]), 'class' => 'permission');
         foreach (element_children($form['checkboxes']) as $rid) {
           if (is_array($form['checkboxes'][$rid])) {
-            $row[] = array('data' => form_render($form['checkboxes'][$rid][$key]), 'align' => 'center', 'title' => t($key));
+            $row[] = array('data' => drupal_render($form['checkboxes'][$rid][$key]), 'align' => 'center', 'title' => t($key));
           }
         }
       }
@@ -1810,11 +1810,11 @@ function theme_user_admin_perm($form) {
   $header[] = (t('Permission'));
   foreach (element_children($form['role_names']) as $rid) {
     if (is_array($form['role_names'][$rid])) {
-      $header[] = form_render($form['role_names'][$rid]);
+      $header[] = drupal_render($form['role_names'][$rid]);
     }
   }
   $output = theme('table', $header, $rows, array('id' => 'permissions'));
-  $output .= form_render($form);
+  $output .= drupal_render($form);
   return $output;
 }
 
@@ -1904,7 +1904,7 @@ function theme_user_admin_new_role($form) {
       $rows[] = array($name, '<span class="disabled">'. t('locked') .'</span>');
     }
   }
-  $rows[] = array(form_render($form['name']), form_render($form['submit']));
+  $rows[] = array(drupal_render($form['name']), drupal_render($form['submit']));
 
   return theme('table', $header, $rows);
 }
@@ -1995,17 +1995,17 @@ function theme_user_admin_account($form) {
     t('Operations')
   );
 
-  $output .= form_render($form['options']);
+  $output .= drupal_render($form['options']);
   if (isset($form['name']) && is_array($form['name'])) {
     foreach (element_children($form['name']) as $key) {
       $rows[] = array(
-        form_render($form['accounts'][$key]),
-        form_render($form['name'][$key]),
-        form_render($form['status'][$key]),
-        form_render($form['roles'][$key]),
-        form_render($form['member_for'][$key]),
-        form_render($form['last_access'][$key]),
-        form_render($form['operations'][$key]),
+        drupal_render($form['accounts'][$key]),
+        drupal_render($form['name'][$key]),
+        drupal_render($form['status'][$key]),
+        drupal_render($form['roles'][$key]),
+        drupal_render($form['member_for'][$key]),
+        drupal_render($form['last_access'][$key]),
+        drupal_render($form['operations'][$key]),
       );
     }
   }
@@ -2015,10 +2015,10 @@ function theme_user_admin_account($form) {
 
   $output .= theme('table', $header, $rows);
   if ($form['pager']['#value']) {
-    $output .= form_render($form['pager']);
+    $output .= drupal_render($form['pager']);
   }
 
-  $output .= form_render($form);
+  $output .= drupal_render($form);
 
   return $output;
 }
diff --git a/modules/watchdog/watchdog.module b/modules/watchdog/watchdog.module
index c81b6e352e9ad156a2ec93cdeda0295a9f470479..05e738371cafa158aaf406381ff22faa09824075 100644
--- a/modules/watchdog/watchdog.module
+++ b/modules/watchdog/watchdog.module
@@ -152,7 +152,7 @@ function watchdog_overview() {
 }
 
 function theme_watchdog_form_overview($form) {
-  return '<div class="container-inline">'. form_render($form) .'</div>';
+  return '<div class="container-inline">'. drupal_render($form) .'</div>';
 }
 
 function watchdog_form_overview_submit($form_id, $form) {