diff --git a/database/updates.inc b/database/updates.inc
index 8308c49b97e5f550b7877cbfa25f6b1ca7b757c6..48170254cfbf54f270a4c3884f07bb5751de3b29 100644
--- a/database/updates.inc
+++ b/database/updates.inc
@@ -80,7 +80,8 @@
   "2004-08-11" => "update_101",
   "2004-08-12" => "update_102",
   "2004-08-17" => "update_103",
-  "2004-08-19" => "update_104"
+  "2004-08-19" => "update_104",
+  "2004-09-14" => "update_105"
 );
 
 function update_32() {
@@ -1750,6 +1751,51 @@ function update_104() {
   return $ret;
 }
 
+function update_105() {
+  $ret = array();
+
+  $shadowupdates = db_query("SELECT nid,tid FROM {forum} WHERE shadow=0");
+  while ($shadowrecord = db_fetch_object($shadowupdates)) {
+    db_query("DELETE FROM term_node WHERE nid = %d AND tid <> %d", $shadowrecord->nid, $shadowrecord-tid);
+  }
+
+  $ret[] = update_sql("ALTER TABLE {forum} DROP shadow");
+  $ret[] = update_sql('ALTER TABLE node ADD INDEX node_status_type (status, type, nid)');
+
+  $ret[] = update_sql("CREATE TABLE node_comment_statistics (
+  nid int(10) unsigned NOT NULL auto_increment,
+  cid int(10) unsigned NOT NULL default '0',
+  last_comment_timestamp int(11) NOT NULL default '0',
+  last_comment_name varchar(60) default NULL,
+  last_comment_uid int(10) NOT NULL default '0',
+  comment_count int(10) unsigned NOT NULL default '0',
+  PRIMARY KEY (nid),
+  KEY node_comment_timestamp (last_comment_timestamp)
+) TYPE=MyISAM");
+  $ret[] = update_sql("INSERT INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) SELECT n.nid, 0, n.created, NULL, n.uid, 0 FROM {node} n");
+
+  $ret[] = update_sql("CREATE TABLE {forum_conv_temp} (
+    nid int(10) unsigned NOT NULL default '0',
+    cid int(10) unsigned NOT NULL default '0',
+    comment_count int(10) unsigned NOT NULL default '0',
+    PRIMARY KEY (nid)
+    )");
+
+  $ret[] = update_sql('INSERT INTO {forum_conv_temp} SELECT f.nid, MAX(c.cid), COUNT(c.nid) FROM {forum} f INNER JOIN {comments} c ON f.nid = c.nid WHERE c.status = 0 GROUP BY f.nid');
+
+  /* This would be faster but only works with MySQL 4.0.4 or higher
+  $ret[] = update_sql('UPDATE {node_comment_statistics} n, {forum_conv_temp} t, {comments} c SET n.comment_count = t.comment_count, n.last_comment_timestamp = c.timestamp, n.last_comment_name = c.name, n.last_comment_uid = c.uid, n.cid = t.cid WHERE t.cid = c.cid AND n.nid = t.nid');
+  */
+
+  $commentupdates = db_query("SELECT t.nid, t.cid, t.comment_count, c.timestamp, c.name, c.uid FROM {forum_conv_temp} t INNER JOIN {comments} c ON t.cid = c.cid");
+  while ($commentrecord = db_fetch_object($commentupdates)) {
+    db_query("UPDATE {node_comment_statistics} SET comment_count = %d, last_comment_timestamp = %d, last_comment_name = '%s', last_comment_uid = %d, cid = %d WHERE nid = %d", $commentrecord->comment_count, $commentrecord->timestamp, $commentrecord->name, $commentrecord->uid, $commentrecord->cid, $commentrecord->nid);
+  }
+
+  $ret[] = update_sql("DROP TABLE {forum_conv_temp}");
+
+  return $ret;
+}
 
 function update_sql($sql) {
   $edit = $_POST["edit"];
diff --git a/modules/comment.module b/modules/comment.module
index d3e9407a3dd7f810672a58c87eba1b6bf647cbc0..83ccf246853e02ebc7a4f22592dafb4dcb46f853 100644
--- a/modules/comment.module
+++ b/modules/comment.module
@@ -226,6 +226,7 @@ function comment_link($type, $node = 0, $main = 0) {
 
 /**
  * Implementation of hook_nodeapi().
+ *
  */
 function comment_nodeapi(&$node, $op, $arg = 0) {
   switch ($op) {
@@ -241,14 +242,20 @@ function comment_nodeapi(&$node, $op, $arg = 0) {
         return form_group(t('User comments'), $output);
       }
       break;
+    case 'load':
+      return db_fetch_array(db_query("SELECT last_comment_timestamp, last_comment_name, last_comment_name, comment_count, cid as last_comment_cid FROM {node_comment_statistics} WHERE nid = %d", $node->nid));
     case 'validate':
       if (!user_access('administer nodes')) {
         // Force default for normal users:
         $node->comment = variable_get("comment_$node->type", 2);
       }
       break;
+    case 'insert':
+      db_query('INSERT INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) VALUES (%d,0,%d,NULL,%d,0)', $node->nid, $node->created, $node->uid);
+      break;
     case 'delete':
-      db_query("DELETE FROM {comments} WHERE nid = '$node->nid'");
+      db_query('DELETE FROM {comments} WHERE nid = %d', $node->nid);
+      db_query('DELETE FROM {node_comment_statistics} WHERE nid = %d', $node->nid);
       break;
   }
 }
@@ -558,6 +565,8 @@ function comment_post($edit) {
         // user.
         db_query("UPDATE {comments} SET subject = '%s', comment = '%s' WHERE cid = %d AND uid = '$user->uid'", $edit['subject'], $edit['comment'], $edit["cid"]);
 
+        _comment_update_node_statistics($edit['nid']);
+
         // Allow modules to respond to the updating of a comment.
         module_invoke_all('comment', 'update', $edit);
 
@@ -664,8 +673,16 @@ function comment_post($edit) {
 
 
         $edit["cid"] = db_next_id("{comments}_cid");
+        $edit['timestamp'] = time();
+
+        if ($edit['uid'] = $user->uid) {
+          $edit['name'] = $user->name;
+        }
+
 
-        db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit["cid"], $edit["nid"], $edit["pid"], $user->uid, $edit['subject'], $edit['comment'], $_SERVER['REMOTE_ADDR'], time(), $status, $score, $users, $thread, $edit["name"], $edit['mail'], $edit["homepage"]);
+        db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit["cid"], $edit["nid"], $edit["pid"], $edit['uid'], $edit['subject'], $edit['comment'], $_SERVER['REMOTE_ADDR'], $edit['timestamp'], $status, $score, $users, $thread, $edit["name"], $edit['mail'], $edit["homepage"]);
+
+        _comment_update_node_statistics($edit['nid']);
 
         // Tell the other modules a new comment has been submitted.
         module_invoke_all('comment', 'insert', $edit);
@@ -985,9 +1002,12 @@ function comment_delete($cid) {
     // Delete comment and its replies.
     _comment_delete_thread($comment);
 
+    _comment_update_node_statistics($comment->nid);
+
     // Clear the cache so an anonymous user
     // can see his comment being added.
     cache_clear_all();
+
   }
 
   // Print a confirmation.
@@ -1326,7 +1346,7 @@ function comment_num_all($nid) {
   static $cache;
 
   if (!isset($cache[$nid])) {
-    $cache[$nid] = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE nid = %d AND status = 0', $nid));
+    $cache[$nid] = db_result(db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = %d', $nid));
   }
   return $cache[$nid];
 }
@@ -1631,9 +1651,12 @@ function _comment_delete_thread($comment) {
   db_query('DELETE FROM {comments} WHERE cid = %d', $comment->cid);
   watchdog('special', t('Comment: deleted %subject.', array('%subject' => "<em>$comment->subject</em>")));
 
+  module_invoke_all('comment', 'delete', $comment);
+
   // Delete the comment's replies:
-  $result = db_query('SELECT cid, subject FROM {comments} WHERE pid = %d', $comment->cid);
+  $result = db_query('SELECT c.*, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE pid = %d', $comment->cid);
   while ($comment = db_fetch_object($result)) {
+    $comment->name = $comment->registered_name ? $comment->registered_name : $comment->name;
     _comment_delete_thread($comment);
   }
 }
@@ -1666,4 +1689,22 @@ function _comment_per_page() {
   return drupal_map_assoc(array(10, 30, 50, 70, 90));
 }
 
+/**
+ * Updates the comment statistics for a given node.  This should be called any
+ * time a comment is added, deleted, or updated.
+ *
+ * The following fields are contained in the node_comment_statistics table.
+ * - cid: cid of the last comment to be created for the node.
+ * - last_comment_timestamp: the timestamp of the last comment for this node or the node create stamp if no comments exist for the node.
+ * - last_comment_name: the name of the anonymous poster for the last comment
+ * - last_comment_uid: the uid of the poster for the last comment for this node or the node authors uid if no comments exists for the node.
+ * - comment_count: the total number of comments on this node.
+ */
+function _comment_update_node_statistics($nid) {
+  $count = db_result(db_query('SELECT COUNT(c.cid) FROM {comments} c WHERE c.nid = %d AND c.status = 0', $nid));
+  $node = node_load(array('nid' => $nid));
+  $last_reply = db_fetch_object(db_query_range('SELECT c.cid, c.name, c.timestamp, c.uid FROM {comments} c WHERE c.nid = %d AND c.status = 0 ORDER BY c.cid DESC', $nid, 0, 1));
+  db_query("UPDATE {node_comment_statistics} n SET n.comment_count = %d, last_comment_timestamp = '%s', n.last_comment_name = '%s', n.last_comment_uid = %d WHERE n.nid = %d", $count, $last_reply ? $last_reply->timestamp : $node->created, $last_reply->name, $last_reply ? $last_reply->uid : $node->uid, $comment->nid);
+}
+
 ?>
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index d3e9407a3dd7f810672a58c87eba1b6bf647cbc0..83ccf246853e02ebc7a4f22592dafb4dcb46f853 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -226,6 +226,7 @@ function comment_link($type, $node = 0, $main = 0) {
 
 /**
  * Implementation of hook_nodeapi().
+ *
  */
 function comment_nodeapi(&$node, $op, $arg = 0) {
   switch ($op) {
@@ -241,14 +242,20 @@ function comment_nodeapi(&$node, $op, $arg = 0) {
         return form_group(t('User comments'), $output);
       }
       break;
+    case 'load':
+      return db_fetch_array(db_query("SELECT last_comment_timestamp, last_comment_name, last_comment_name, comment_count, cid as last_comment_cid FROM {node_comment_statistics} WHERE nid = %d", $node->nid));
     case 'validate':
       if (!user_access('administer nodes')) {
         // Force default for normal users:
         $node->comment = variable_get("comment_$node->type", 2);
       }
       break;
+    case 'insert':
+      db_query('INSERT INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) VALUES (%d,0,%d,NULL,%d,0)', $node->nid, $node->created, $node->uid);
+      break;
     case 'delete':
-      db_query("DELETE FROM {comments} WHERE nid = '$node->nid'");
+      db_query('DELETE FROM {comments} WHERE nid = %d', $node->nid);
+      db_query('DELETE FROM {node_comment_statistics} WHERE nid = %d', $node->nid);
       break;
   }
 }
@@ -558,6 +565,8 @@ function comment_post($edit) {
         // user.
         db_query("UPDATE {comments} SET subject = '%s', comment = '%s' WHERE cid = %d AND uid = '$user->uid'", $edit['subject'], $edit['comment'], $edit["cid"]);
 
+        _comment_update_node_statistics($edit['nid']);
+
         // Allow modules to respond to the updating of a comment.
         module_invoke_all('comment', 'update', $edit);
 
@@ -664,8 +673,16 @@ function comment_post($edit) {
 
 
         $edit["cid"] = db_next_id("{comments}_cid");
+        $edit['timestamp'] = time();
+
+        if ($edit['uid'] = $user->uid) {
+          $edit['name'] = $user->name;
+        }
+
 
-        db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit["cid"], $edit["nid"], $edit["pid"], $user->uid, $edit['subject'], $edit['comment'], $_SERVER['REMOTE_ADDR'], time(), $status, $score, $users, $thread, $edit["name"], $edit['mail'], $edit["homepage"]);
+        db_query("INSERT INTO {comments} (cid, nid, pid, uid, subject, comment, hostname, timestamp, status, score, users, thread, name, mail, homepage) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $edit["cid"], $edit["nid"], $edit["pid"], $edit['uid'], $edit['subject'], $edit['comment'], $_SERVER['REMOTE_ADDR'], $edit['timestamp'], $status, $score, $users, $thread, $edit["name"], $edit['mail'], $edit["homepage"]);
+
+        _comment_update_node_statistics($edit['nid']);
 
         // Tell the other modules a new comment has been submitted.
         module_invoke_all('comment', 'insert', $edit);
@@ -985,9 +1002,12 @@ function comment_delete($cid) {
     // Delete comment and its replies.
     _comment_delete_thread($comment);
 
+    _comment_update_node_statistics($comment->nid);
+
     // Clear the cache so an anonymous user
     // can see his comment being added.
     cache_clear_all();
+
   }
 
   // Print a confirmation.
@@ -1326,7 +1346,7 @@ function comment_num_all($nid) {
   static $cache;
 
   if (!isset($cache[$nid])) {
-    $cache[$nid] = db_result(db_query('SELECT COUNT(cid) FROM {comments} WHERE nid = %d AND status = 0', $nid));
+    $cache[$nid] = db_result(db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = %d', $nid));
   }
   return $cache[$nid];
 }
@@ -1631,9 +1651,12 @@ function _comment_delete_thread($comment) {
   db_query('DELETE FROM {comments} WHERE cid = %d', $comment->cid);
   watchdog('special', t('Comment: deleted %subject.', array('%subject' => "<em>$comment->subject</em>")));
 
+  module_invoke_all('comment', 'delete', $comment);
+
   // Delete the comment's replies:
-  $result = db_query('SELECT cid, subject FROM {comments} WHERE pid = %d', $comment->cid);
+  $result = db_query('SELECT c.*, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE pid = %d', $comment->cid);
   while ($comment = db_fetch_object($result)) {
+    $comment->name = $comment->registered_name ? $comment->registered_name : $comment->name;
     _comment_delete_thread($comment);
   }
 }
@@ -1666,4 +1689,22 @@ function _comment_per_page() {
   return drupal_map_assoc(array(10, 30, 50, 70, 90));
 }
 
+/**
+ * Updates the comment statistics for a given node.  This should be called any
+ * time a comment is added, deleted, or updated.
+ *
+ * The following fields are contained in the node_comment_statistics table.
+ * - cid: cid of the last comment to be created for the node.
+ * - last_comment_timestamp: the timestamp of the last comment for this node or the node create stamp if no comments exist for the node.
+ * - last_comment_name: the name of the anonymous poster for the last comment
+ * - last_comment_uid: the uid of the poster for the last comment for this node or the node authors uid if no comments exists for the node.
+ * - comment_count: the total number of comments on this node.
+ */
+function _comment_update_node_statistics($nid) {
+  $count = db_result(db_query('SELECT COUNT(c.cid) FROM {comments} c WHERE c.nid = %d AND c.status = 0', $nid));
+  $node = node_load(array('nid' => $nid));
+  $last_reply = db_fetch_object(db_query_range('SELECT c.cid, c.name, c.timestamp, c.uid FROM {comments} c WHERE c.nid = %d AND c.status = 0 ORDER BY c.cid DESC', $nid, 0, 1));
+  db_query("UPDATE {node_comment_statistics} n SET n.comment_count = %d, last_comment_timestamp = '%s', n.last_comment_name = '%s', n.last_comment_uid = %d WHERE n.nid = %d", $count, $last_reply ? $last_reply->timestamp : $node->created, $last_reply->name, $last_reply ? $last_reply->uid : $node->uid, $comment->nid);
+}
+
 ?>
diff --git a/modules/forum.module b/modules/forum.module
index 478827107da565ad9fb920ff0fb0bb5d517a3e47..4d726940016843ac5b5c7cd3421dd54052edd44f 100644
--- a/modules/forum.module
+++ b/modules/forum.module
@@ -122,9 +122,9 @@ function forum_block($op = 'list', $delta = 0) {
   }
   else {
     if (user_access('access content')) {
-      $content  = node_title_list(db_query_range("SELECT DISTINCT(n.nid), n.title, GREATEST(n.created, MAX(c.timestamp)) AS sort FROM {node} n ". node_access_join_sql() ." LEFT JOIN {comments} c ON n.nid = c.nid WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." GROUP BY n.nid, n.title, n.created ORDER BY sort DESC", 0, variable_get('forum_block_num', '5')), t('Active forum topics:'));
+      $content  = node_title_list(db_query_range("SELECT n.nid, n.title, l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid ". node_access_join_sql() ." WHERE n.status = 1 AND n.type='forum' AND ". node_access_where_sql() ." ORDER BY l.last_comment_timestamp DESC", 0, variable_get('forum_block_num', '5')), t('Active forum topics:'));
 
-      $content .= node_title_list(db_query_range("SELECT DISTINCT(n.nid), n.title FROM {node} n ". node_access_join_sql() ." WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." ORDER BY n.nid DESC", 0, variable_get('forum_block_num', '5')), t('New forum topics:'));
+      $content .= node_title_list(db_query_range("SELECT n.nid, n.title FROM {node} n ". node_access_join_sql() ." WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." ORDER BY n.nid DESC", 0, variable_get('forum_block_num', '5')), t('New forum topics:'));
 
       if ($content) {
         $content .= '<div class="more-link">'. l(t('more'), 'forum', array('title' => t('Read the latest forum topics.'))) .'</div>';
@@ -153,7 +153,7 @@ function forum_link($type, $node = 0, $main = 0) {
   if (!$main && $type == 'node' && $node->type == 'forum') {
     // get previous and next topic
 
-    $result = db_query('SELECT DISTINCT(n.nid), n.title, n.sticky, GREATEST(n.created, MAX(c.timestamp)) AS date_sort FROM {node} n '. node_access_join_sql() .' INNER JOIN {forum} f ON n.nid = f.nid LEFT JOIN {comments} c ON n.nid = c.nid WHERE n.nid = f.nid AND f.tid = %d AND n.status = 1 AND '. node_access_where_sql() .' GROUP BY n.nid, n.title, n.created ORDER BY n.sticky DESC, '. _forum_get_topic_order_sql(variable_get('forum_order', 1)), $node->tid);
+    $result = db_query("SELECT n.nid, n.title, n.sticky, l.comment_count, l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid " . node_access_join_sql() . " INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND n.type='forum' AND " . node_access_where_sql() . ' ORDER BY n.sticky DESC, '. _forum_get_topic_order_sql(variable_get('forum_order', 1)), $node->tid);
 
     while ($topic = db_fetch_object($result)) {
       if ($stop == 1) {
@@ -238,7 +238,6 @@ function forum_view(&$node, $teaser = FALSE, $page = FALSE) {
 function forum_validate(&$node) {
   // Make sure all fields are set properly:
   $node->icon = $node->icon ? $node->icon : '';
-  $node->shadow = $node->shadow ? $node->shadow : 0;
 
   if ($node->taxonomy) {
     // Extract the node's proper topic ID.
@@ -255,9 +254,23 @@ function forum_validate(&$node) {
         }
       }
     }
+    if ($node->tid && $node->shadow) {
+      $terms = array_keys(taxonomy_node_get_terms($node->nid));
+      if (!in_array($node->tid, $terms)) {
+        $terms[] = $node->tid;
+      }
+      $node->taxonomy = $terms;
+    }
   }
 }
 
+/**
+ * Implementation of hook_update().
+ */
+function forum_update($node) {
+  db_query('UPDATE {forum} SET tid = %d WHERE nid = %d', $node->tid, $node->nid);
+}
+
 /**
  * Implementation of hook_form().
  */
@@ -266,12 +279,16 @@ function forum_form(&$node) {
     // new topic
     $node->taxonomy[] = arg(3);
   }
+  else {
+    $node->taxonomy = array($node->tid);
+  }
 
   $output = implode('', taxonomy_node_form('forum', $node));
 
   if ($node->nid) {
     // if editing, give option to leave shadows
-    $output .= form_checkbox(t('Leave shadow copy'), 'shadow', 1, $node->shadow, t('If you move this topic, you can leave a link in the old forum to the new forum.'));
+    $shadow = (count(taxonomy_node_get_terms($node->nid)) > 1);
+    $output .= form_checkbox(t('Leave shadow copy'), 'shadow', 1, $shadow, t('If you move this topic, you can leave a link in the old forum to the new forum.'));
   }
 
   $output .= form_textarea(t('Body'), 'body', $node->body, 60, 10, '');
@@ -283,14 +300,8 @@ function forum_form(&$node) {
  * Implementation of hook_insert().
  */
 function forum_insert($node) {
-  db_query('INSERT INTO {forum} (nid, shadow, tid) VALUES (%d, %d, %d)', $node->nid, $node->shadow, $node->tid);
-}
-
-/**
- * Implementation of hook_update().
- */
-function forum_update($node) {
-  db_query('UPDATE {forum} SET shadow = %d, tid = %d WHERE nid = %d', $node->shadow, $node->tid, $node->nid);
+  global $user;
+  db_query('INSERT INTO {forum} (nid, tid) VALUES (%d, %d)', $node->nid, $node->tid);
 }
 
 /**
@@ -300,27 +311,11 @@ function forum_delete(&$node) {
   db_query('DELETE FROM {forum} WHERE nid = %d', $node->nid);
 }
 
-function _forum_num_comments($nid) {
-  $value = db_fetch_object(db_query('SELECT COUNT(cid) AS count FROM {comments} WHERE nid = %d AND status = 0', $nid));
-  return ($value) ? $value->count : 0;
-}
-
-function _forum_last_comment($nid) {
-  $value = db_fetch_object(db_query_range('SELECT timestamp FROM {comments} WHERE nid = %d AND status = 0 ORDER BY timestamp DESC', $nid, 0, 1));
-  return ($value) ? format_date($value->timestamp, 'small') : '&nbsp;';
-}
-
-function _forum_last_reply($nid) {
-  $value = db_fetch_object(db_query_range('SELECT c.timestamp, c.name AS anonymous_name, u.name, u.uid FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = %d AND c.status = 0 ORDER BY c.timestamp DESC', $nid, 0, 1));
-  if ($value) {
-    $value->name = $value->uid ? $value->name : $value->anonymous_name;
-  }
-  else {
-    $value = new StdClass();
-  }
-  return $value;
-}
-
+/**
+ * Formats a topic for display
+ *
+ * @TODO Give a better description. Not sure where this funciton is used yet.
+ */
 function _forum_format($topic) {
   if ($topic && $topic->timestamp) {
     return t('%time ago<br />by %author', array('%time' => format_interval(time() - $topic->timestamp), '%author' => format_name($topic)));
@@ -330,77 +325,85 @@ function _forum_format($topic) {
   }
 }
 
+/**
+ * Returns a list of all forums for a given taxonomy id
+ *
+ * Forum objects containt the following fields
+ * -num_topics Number of topics in the forum
+ * -num_posts Total number of posts in all topics
+ * -last_post Most recent post for the forum
+ *
+ * @param $tid
+ *   Taxonomy ID of the vocabulary that holds the forum list.
+ * @return
+ *   Array of object containing the forum information.
+ */
 function forum_get_forums($tid = 0) {
   if (!$tid) {
     $tid = 0;
   }
 
-  $cache = cache_get("forum:$tid");
+  $forums = array();
+  $_forums = taxonomy_get_tree(variable_get('forum_nav_vocabulary', ''), $tid);
 
-  if (empty($cache)) {
-    $forums = array();
-    $_forums = taxonomy_get_tree(variable_get('forum_nav_vocabulary', ''), $tid);
-    foreach ($_forums as $forum) {
-      if (in_array($forum->tid, variable_get('forum_containers', array()))) {
-        $forum->container = 1;
-      }
-      $forum->num_topics = _forum_num_topics($forum->tid);
-      $forum->num_posts = _forum_num_replies($forum->tid) + $forum->num_topics;
-      $forum->last_post = _forum_last_post($forum->tid);
-      $forums[$forum->tid] = $forum;
-    }
+  if (count($_forums)) {
 
-    cache_set("forum:$tid", serialize($forums), 1);
-  }
-  else {
-    $forums = unserialize($cache->data);
+    $counts = array();
+
+    $_counts = db_query("SELECT r.tid, COUNT(n.nid) AS topic_count, SUM(l.comment_count) AS comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {term_node} r ON n.nid = r.nid " . node_access_join_sql() . " WHERE n.status = 1 AND n.type = 'forum' AND " . node_access_where_sql() . " GROUP BY r.tid", $forum->tid);
+    while ($count = db_fetch_object($_counts)) {
+      $counts[$count->tid] = $count;
+    }
   }
 
-  return $forums;
-}
+  foreach ($_forums as $forum) {
+    if (in_array($forum->tid, variable_get('forum_containers', array()))) {
+      $forum->container = 1;
+    }
 
-function _forum_num_topics($term) {
-  return db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) FROM {forum} f INNER JOIN {node} n ON n.nid = f.nid '. node_access_join_sql() .' WHERE f.tid = %d AND n.status = 1 AND '. node_access_where_sql() .' AND f.shadow = 0', $term));
-}
+    if ($counts[$forum->tid]) {
+      $forum->num_topics = $counts[$forum->tid]->topic_count;
+      $forum->num_posts = $counts[$forum->tid]->topic_count + $counts[$forum->tid]->comment_count;
+    }
+    else {
+      $forum->num_topics = 0;
+      $forum->num_posts = 0;
+    }
+
+    // This query does not use full ANSI syntax since MySQL 3.x does not support
+    // table1 INNER JOIN table2 INNER JOIN table3 ON table2_criteria ON table3_criteria
+    // used to join node_comment_statistics to users
+    $topic = db_fetch_object(db_query_range('SELECT n.nid, l.last_comment_timestamp, IF(l.last_comment_uid, cu.name, l.last_comment_name) as last_comment_name, l.last_comment_uid FROM {node} n ' . node_access_join_sql() . ", {node_comment_statistics} l /*! USE INDEX (node_comment_timestamp) */, {users} cu, {term_node} r WHERE  n.nid = r.nid AND r.tid = %d AND n.status = 1 AND n.type = 'forum' AND l.last_comment_uid = cu.uid AND n.nid = l.nid AND " . node_access_where_sql() . ' ORDER BY l.last_comment_timestamp DESC', $forum->tid, 0, 1));
+    $last_post->timestamp = $topic->last_comment_timestamp;
+    $last_post->name = $topic->last_comment_name;
+    $last_post->uid = $topic->last_comment_uid;
+    $forum->last_post = $last_post;
 
-function _forum_num_replies($term) {
-  return db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) AS count FROM {comments} c INNER JOIN {node} n ON n.nid = c.nid '. node_access_join_sql() .' INNER JOIN {forum} f ON n.nid = f.nid WHERE f.tid = %d AND n.nid = f.nid AND n.nid = c.nid AND n.status = 1 AND '. node_access_where_sql() ." AND c.status = 0 AND n.type = 'forum'", $term));
+    $forums[$forum->tid] = $forum;
+  }
+
+  return $forums;
 }
 
 function _forum_topics_read($term, $uid) {
   // Calculate the number of topics the user has read. Assume all entries older
   // than NODE_NEW_LIMIT are read, and include the recent posts that user has
   // read.
-  $ancient = db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) FROM {forum} f INNER JOIN {node} n ON f.nid = n.nid '. node_access_join_sql() .' WHERE f.tid = %d AND n.status = 1 AND '. node_access_where_sql() .' AND n.created <= %d AND f.shadow = 0', $term, NODE_NEW_LIMIT));
-  $recent = db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) FROM {forum} f INNER JOIN {node} n ON f.nid = n.nid '. node_access_join_sql() .' INNER JOIN {history} h ON n.nid = h.nid WHERE n.status = 1 AND '. node_access_where_sql() .' AND f.tid = %d AND h.uid = %d AND n.created > %d AND f.shadow = 0', $term, $uid, NODE_NEW_LIMIT));
+  $ancient = db_result(db_query('SELECT COUNT(n.nid) FROM {node} n INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d '. node_access_join_sql() .' WHERE n.created <= %d AND n.status = 1 AND '. node_access_where_sql(), $term, NODE_NEW_LIMIT));
+  $recent = db_result(db_query('SELECT COUNT(n.nid) FROM {node} n '. node_access_join_sql() .' INNER JOIN {history} h ON n.nid = h.nid AND h.uid = %d INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND n.created > %d AND '. node_access_where_sql(), $uid, $term, NODE_NEW_LIMIT));
 
   return $ancient + $recent;
 }
 
-function _forum_last_post($term) {
-  $topic = db_fetch_object(db_query_range("SELECT DISTINCT(n.nid), n.created AS timestamp, u.name AS name, u.uid AS uid FROM {forum} f INNER JOIN {node} n ON n.nid = f.nid ". node_access_join_sql() ." INNER JOIN {users} u ON n.uid = u.uid WHERE f.tid = %d AND n.nid = f.nid AND n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." ORDER BY timestamp DESC", $term, 0, 1));
-  $reply = db_fetch_object(db_query_range("SELECT DISTINCT(n.nid), c.timestamp, c.name AS anonymous_name, u.name AS name, u.uid AS uid FROM {forum} f INNER JOIN {node} n ON n.nid = f.nid ". node_access_join_sql() ." INNER JOIN {comments} c ON n.nid = c.nid INNER JOIN {users} u ON c.uid = u.uid WHERE f.tid = %d AND n.nid = f.nid AND n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." AND c.status = 0 ORDER BY c.timestamp DESC", $term, 0, 1));
-  if ($reply) {
-    $reply->name = $reply->uid ? $reply->name : $reply->anonymous_name;
-  }
-  else {
-    $reply = new StdClass();
-  }
-
-  $value = ($topic->timestamp > $reply->timestamp) ? $topic : $reply;
-
-  return $value;
-}
-
 function forum_get_topics($tid, $sortby, $forum_per_page) {
   global $user, $forum_topic_list_header;
 
   $forum_topic_list_header = array(
     array('data' => '&nbsp;'),
     array('data' => t('Topic'), 'field' => 'n.title'),
-    array('data' => t('Replies'), 'field' => 'num_comments'),
+    array('data' => t('Replies'), 'field' => 'l.comment_count'),
     array('data' => t('Created'), 'field' => 'n.created'),
-    array('data' => t('Last reply'), 'field' => 'date_sort'),
+    array('data' => t('Last reply'), 'field' => 'l.last_comment_timestamp'),
   );
 
   $order = _forum_get_topic_order($sortby);
@@ -413,25 +416,22 @@ function forum_get_topics($tid, $sortby, $forum_per_page) {
   $term = taxonomy_get_term($tid);
   $check_tid = $tid ? "'". check_query($tid) ."'" : 'NULL';
 
-  // show topics with the correct tid, or in the forum but with shadow = 1
-  // @TODO: this is not ANSI SQL! ("user error: 'n.created' isn't in GROUP BY")
-  // @TODO: timestamp is a sql reserved word. are there more?
-  $sql = "SELECT DISTINCT(n.nid), n.title, n.sticky, u.name AS name, u.uid AS uid, n.created AS timestamp, GREATEST(n.created, MAX(c.timestamp)) AS date_sort, COUNT(c.nid) AS num_comments, n.comment AS comment_mode, f.tid FROM {node} n ". node_access_join_sql() ." INNER JOIN {term_node} r ON n.nid = r.nid INNER JOIN {users} u ON n.uid = u.uid LEFT JOIN {comments} c ON n.nid = c.nid INNER JOIN {forum} f ON n.nid = f.nid WHERE ((r.tid = $check_tid AND f.shadow = 1) OR f.tid = $check_tid) AND n.status = 1 AND ". node_access_where_sql() ." AND n.type = 'forum' GROUP BY n.nid, n.title, u.name, u.uid, n.created, n.comment, f.tid";
+  $sql = "SELECT n.nid, f.tid, n.title, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid, cu.name, l.last_comment_name) as last_comment_name, l.last_comment_uid, l.comment_count AS num_comments FROM {node} n ". node_access_join_sql() .", {node_comment_statistics} l, {users} cu, {term_node} r, {users} u, {forum} f WHERE n.status = 1 AND l.last_comment_uid = cu.uid AND n.nid = l.nid AND n.nid = r.nid AND r.tid = $check_tid AND n.uid = u.uid AND n.nid = f.nid AND ". node_access_where_sql();
   $sql .= tablesort_sql($forum_topic_list_header, 'n.sticky DESC,');
 
-  $sql_count = "SELECT COUNT(DISTINCT(n.nid)) FROM {node} n ". node_access_join_sql() ." INNER JOIN {forum} f ON n.nid = f.nid INNER JOIN {term_node} r ON n.nid = r.nid WHERE ( (r.tid = $check_tid AND f.shadow = 1) OR f.tid = $check_tid) AND n.status = 1 AND ". node_access_where_sql() ." AND n.type = 'forum'";
+  $sql_count = "SELECT COUNT(n.nid) FROM {node} n ". node_access_join_sql() ." INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = $check_tid WHERE n.status = 1 AND n.type = 'forum' AND ". node_access_where_sql();
 
   $result = pager_query($sql, $forum_per_page, 0, $sql_count);
 
   while ($topic = db_fetch_object($result)) {
     if ($user->uid) {
       // folder is new if topic is new or there are new comments since last visit
-      if ($topic->shadow > 0) {
+      if ($topic->tid != $tid) {
         $topic->new = 0;
       }
       else {
         $history = _forum_user_last_visit($topic->nid);
-        $topic->new_replies = db_result(db_query('SELECT COUNT(DISTINCT(c.nid)) FROM {node} n '. node_access_join_sql() .' INNER JOIN {comments} c ON n.nid = c.nid WHERE n.nid = %d AND n.status = 1 AND '. node_access_where_sql() .' AND c.status = 0 AND c.timestamp > %d', $topic->nid, $history));
+        $topic->new_replies = comment_num_new($topic->nid, $history);
         $topic->new = $topic->new_replies || ($topic->timestamp > $history);
       }
     }
@@ -441,21 +441,25 @@ function forum_get_topics($tid, $sortby, $forum_per_page) {
       $topic->new = 0;
     }
 
-    $topic->last_reply = _forum_last_reply($topic->nid);
+    if ($topic->num_comments > 0) {
+      $last_reply->timestamp = $topic->last_comment_timestamp;
+      $last_reply->name = $topic->last_comment_name;
+      $last_reply->uid = $topic->last_comment_uid;
+      $topic->last_reply = $last_reply;
+    }
     $topics[] = $topic;
   }
 
   return $topics;
 }
 
+/**
+ * Finds the first unread node for a given forum.
+ */
 function _forum_new($tid) {
   global $user;
-  $result = db_query("SELECT DISTINCT(n.nid) FROM {node} n, {history} h, {forum} f ". node_access_join_sql() ." WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." AND h.nid = n.nid AND f.nid = h.nid AND f.tid = %d AND h.uid = %d", $tid, $user->uid);
-  while ($r = db_fetch_object($result)) {
-    $read[] = $r->nid;
-  }
 
-  $nid = db_result(db_query_range("SELECT DISTINCT(n.nid) FROM {node} n ". node_access_join_sql() ." INNER JOIN {forum} f ON n.nid = f.nid WHERE n.type = 'forum' AND f.nid = n.nid AND n.status = 1 AND ". node_access_where_sql() ." AND f.tid = %d AND n.created > %d ". ($read ? 'AND NOT (n.nid IN ('. implode(',', $read) .')) ' : '') .'ORDER BY created', $tid, NODE_NEW_LIMIT, 0, 1));
+  $nid = db_result(db_query_range("SELECT n.nid FROM {node} n LEFT JOIN {history} h ON n.nid = h.hid AND h.uid = %d INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d " . node_access_join_sql() . " WHERE n.status = 1 AND n.type = 'forum' AND h.nid IS NULL AND n.created > %d AND " . node_access_where_sql() . " ORDER BY created", $user->uid, $tid, NODE_NEW_LIMIT, 0, 1));
 
   return $nid ? $nid : 0;
 }
@@ -704,16 +708,16 @@ function _forum_user_last_visit($nid) {
 function _forum_get_topic_order($sortby) {
   switch ($sortby) {
     case 1:
-      return array('field' => 'date_sort', 'sort' => 'desc');
+      return array('field' => 'l.last_comment_timestamp', 'sort' => 'desc');
       break;
     case 2:
-      return array('field' => 'date_sort', 'sort' => 'asc');
+      return array('field' => 'l.last_comment_timestamp', 'sort' => 'asc');
       break;
     case 3:
-      return array('field' => 'num_comments', 'sort' => 'desc');
+      return array('field' => 'l.comment_count', 'sort' => 'desc');
       break;
     case 4:
-      return array('field' => 'num_comments', 'sort' => 'asc');
+      return array('field' => 'l.comment_count', 'sort' => 'asc');
       break;
   }
 }
diff --git a/modules/forum/forum.module b/modules/forum/forum.module
index 478827107da565ad9fb920ff0fb0bb5d517a3e47..4d726940016843ac5b5c7cd3421dd54052edd44f 100644
--- a/modules/forum/forum.module
+++ b/modules/forum/forum.module
@@ -122,9 +122,9 @@ function forum_block($op = 'list', $delta = 0) {
   }
   else {
     if (user_access('access content')) {
-      $content  = node_title_list(db_query_range("SELECT DISTINCT(n.nid), n.title, GREATEST(n.created, MAX(c.timestamp)) AS sort FROM {node} n ". node_access_join_sql() ." LEFT JOIN {comments} c ON n.nid = c.nid WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." GROUP BY n.nid, n.title, n.created ORDER BY sort DESC", 0, variable_get('forum_block_num', '5')), t('Active forum topics:'));
+      $content  = node_title_list(db_query_range("SELECT n.nid, n.title, l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid ". node_access_join_sql() ." WHERE n.status = 1 AND n.type='forum' AND ". node_access_where_sql() ." ORDER BY l.last_comment_timestamp DESC", 0, variable_get('forum_block_num', '5')), t('Active forum topics:'));
 
-      $content .= node_title_list(db_query_range("SELECT DISTINCT(n.nid), n.title FROM {node} n ". node_access_join_sql() ." WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." ORDER BY n.nid DESC", 0, variable_get('forum_block_num', '5')), t('New forum topics:'));
+      $content .= node_title_list(db_query_range("SELECT n.nid, n.title FROM {node} n ". node_access_join_sql() ." WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." ORDER BY n.nid DESC", 0, variable_get('forum_block_num', '5')), t('New forum topics:'));
 
       if ($content) {
         $content .= '<div class="more-link">'. l(t('more'), 'forum', array('title' => t('Read the latest forum topics.'))) .'</div>';
@@ -153,7 +153,7 @@ function forum_link($type, $node = 0, $main = 0) {
   if (!$main && $type == 'node' && $node->type == 'forum') {
     // get previous and next topic
 
-    $result = db_query('SELECT DISTINCT(n.nid), n.title, n.sticky, GREATEST(n.created, MAX(c.timestamp)) AS date_sort FROM {node} n '. node_access_join_sql() .' INNER JOIN {forum} f ON n.nid = f.nid LEFT JOIN {comments} c ON n.nid = c.nid WHERE n.nid = f.nid AND f.tid = %d AND n.status = 1 AND '. node_access_where_sql() .' GROUP BY n.nid, n.title, n.created ORDER BY n.sticky DESC, '. _forum_get_topic_order_sql(variable_get('forum_order', 1)), $node->tid);
+    $result = db_query("SELECT n.nid, n.title, n.sticky, l.comment_count, l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid " . node_access_join_sql() . " INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND n.type='forum' AND " . node_access_where_sql() . ' ORDER BY n.sticky DESC, '. _forum_get_topic_order_sql(variable_get('forum_order', 1)), $node->tid);
 
     while ($topic = db_fetch_object($result)) {
       if ($stop == 1) {
@@ -238,7 +238,6 @@ function forum_view(&$node, $teaser = FALSE, $page = FALSE) {
 function forum_validate(&$node) {
   // Make sure all fields are set properly:
   $node->icon = $node->icon ? $node->icon : '';
-  $node->shadow = $node->shadow ? $node->shadow : 0;
 
   if ($node->taxonomy) {
     // Extract the node's proper topic ID.
@@ -255,9 +254,23 @@ function forum_validate(&$node) {
         }
       }
     }
+    if ($node->tid && $node->shadow) {
+      $terms = array_keys(taxonomy_node_get_terms($node->nid));
+      if (!in_array($node->tid, $terms)) {
+        $terms[] = $node->tid;
+      }
+      $node->taxonomy = $terms;
+    }
   }
 }
 
+/**
+ * Implementation of hook_update().
+ */
+function forum_update($node) {
+  db_query('UPDATE {forum} SET tid = %d WHERE nid = %d', $node->tid, $node->nid);
+}
+
 /**
  * Implementation of hook_form().
  */
@@ -266,12 +279,16 @@ function forum_form(&$node) {
     // new topic
     $node->taxonomy[] = arg(3);
   }
+  else {
+    $node->taxonomy = array($node->tid);
+  }
 
   $output = implode('', taxonomy_node_form('forum', $node));
 
   if ($node->nid) {
     // if editing, give option to leave shadows
-    $output .= form_checkbox(t('Leave shadow copy'), 'shadow', 1, $node->shadow, t('If you move this topic, you can leave a link in the old forum to the new forum.'));
+    $shadow = (count(taxonomy_node_get_terms($node->nid)) > 1);
+    $output .= form_checkbox(t('Leave shadow copy'), 'shadow', 1, $shadow, t('If you move this topic, you can leave a link in the old forum to the new forum.'));
   }
 
   $output .= form_textarea(t('Body'), 'body', $node->body, 60, 10, '');
@@ -283,14 +300,8 @@ function forum_form(&$node) {
  * Implementation of hook_insert().
  */
 function forum_insert($node) {
-  db_query('INSERT INTO {forum} (nid, shadow, tid) VALUES (%d, %d, %d)', $node->nid, $node->shadow, $node->tid);
-}
-
-/**
- * Implementation of hook_update().
- */
-function forum_update($node) {
-  db_query('UPDATE {forum} SET shadow = %d, tid = %d WHERE nid = %d', $node->shadow, $node->tid, $node->nid);
+  global $user;
+  db_query('INSERT INTO {forum} (nid, tid) VALUES (%d, %d)', $node->nid, $node->tid);
 }
 
 /**
@@ -300,27 +311,11 @@ function forum_delete(&$node) {
   db_query('DELETE FROM {forum} WHERE nid = %d', $node->nid);
 }
 
-function _forum_num_comments($nid) {
-  $value = db_fetch_object(db_query('SELECT COUNT(cid) AS count FROM {comments} WHERE nid = %d AND status = 0', $nid));
-  return ($value) ? $value->count : 0;
-}
-
-function _forum_last_comment($nid) {
-  $value = db_fetch_object(db_query_range('SELECT timestamp FROM {comments} WHERE nid = %d AND status = 0 ORDER BY timestamp DESC', $nid, 0, 1));
-  return ($value) ? format_date($value->timestamp, 'small') : '&nbsp;';
-}
-
-function _forum_last_reply($nid) {
-  $value = db_fetch_object(db_query_range('SELECT c.timestamp, c.name AS anonymous_name, u.name, u.uid FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = %d AND c.status = 0 ORDER BY c.timestamp DESC', $nid, 0, 1));
-  if ($value) {
-    $value->name = $value->uid ? $value->name : $value->anonymous_name;
-  }
-  else {
-    $value = new StdClass();
-  }
-  return $value;
-}
-
+/**
+ * Formats a topic for display
+ *
+ * @TODO Give a better description. Not sure where this funciton is used yet.
+ */
 function _forum_format($topic) {
   if ($topic && $topic->timestamp) {
     return t('%time ago<br />by %author', array('%time' => format_interval(time() - $topic->timestamp), '%author' => format_name($topic)));
@@ -330,77 +325,85 @@ function _forum_format($topic) {
   }
 }
 
+/**
+ * Returns a list of all forums for a given taxonomy id
+ *
+ * Forum objects containt the following fields
+ * -num_topics Number of topics in the forum
+ * -num_posts Total number of posts in all topics
+ * -last_post Most recent post for the forum
+ *
+ * @param $tid
+ *   Taxonomy ID of the vocabulary that holds the forum list.
+ * @return
+ *   Array of object containing the forum information.
+ */
 function forum_get_forums($tid = 0) {
   if (!$tid) {
     $tid = 0;
   }
 
-  $cache = cache_get("forum:$tid");
+  $forums = array();
+  $_forums = taxonomy_get_tree(variable_get('forum_nav_vocabulary', ''), $tid);
 
-  if (empty($cache)) {
-    $forums = array();
-    $_forums = taxonomy_get_tree(variable_get('forum_nav_vocabulary', ''), $tid);
-    foreach ($_forums as $forum) {
-      if (in_array($forum->tid, variable_get('forum_containers', array()))) {
-        $forum->container = 1;
-      }
-      $forum->num_topics = _forum_num_topics($forum->tid);
-      $forum->num_posts = _forum_num_replies($forum->tid) + $forum->num_topics;
-      $forum->last_post = _forum_last_post($forum->tid);
-      $forums[$forum->tid] = $forum;
-    }
+  if (count($_forums)) {
 
-    cache_set("forum:$tid", serialize($forums), 1);
-  }
-  else {
-    $forums = unserialize($cache->data);
+    $counts = array();
+
+    $_counts = db_query("SELECT r.tid, COUNT(n.nid) AS topic_count, SUM(l.comment_count) AS comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {term_node} r ON n.nid = r.nid " . node_access_join_sql() . " WHERE n.status = 1 AND n.type = 'forum' AND " . node_access_where_sql() . " GROUP BY r.tid", $forum->tid);
+    while ($count = db_fetch_object($_counts)) {
+      $counts[$count->tid] = $count;
+    }
   }
 
-  return $forums;
-}
+  foreach ($_forums as $forum) {
+    if (in_array($forum->tid, variable_get('forum_containers', array()))) {
+      $forum->container = 1;
+    }
 
-function _forum_num_topics($term) {
-  return db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) FROM {forum} f INNER JOIN {node} n ON n.nid = f.nid '. node_access_join_sql() .' WHERE f.tid = %d AND n.status = 1 AND '. node_access_where_sql() .' AND f.shadow = 0', $term));
-}
+    if ($counts[$forum->tid]) {
+      $forum->num_topics = $counts[$forum->tid]->topic_count;
+      $forum->num_posts = $counts[$forum->tid]->topic_count + $counts[$forum->tid]->comment_count;
+    }
+    else {
+      $forum->num_topics = 0;
+      $forum->num_posts = 0;
+    }
+
+    // This query does not use full ANSI syntax since MySQL 3.x does not support
+    // table1 INNER JOIN table2 INNER JOIN table3 ON table2_criteria ON table3_criteria
+    // used to join node_comment_statistics to users
+    $topic = db_fetch_object(db_query_range('SELECT n.nid, l.last_comment_timestamp, IF(l.last_comment_uid, cu.name, l.last_comment_name) as last_comment_name, l.last_comment_uid FROM {node} n ' . node_access_join_sql() . ", {node_comment_statistics} l /*! USE INDEX (node_comment_timestamp) */, {users} cu, {term_node} r WHERE  n.nid = r.nid AND r.tid = %d AND n.status = 1 AND n.type = 'forum' AND l.last_comment_uid = cu.uid AND n.nid = l.nid AND " . node_access_where_sql() . ' ORDER BY l.last_comment_timestamp DESC', $forum->tid, 0, 1));
+    $last_post->timestamp = $topic->last_comment_timestamp;
+    $last_post->name = $topic->last_comment_name;
+    $last_post->uid = $topic->last_comment_uid;
+    $forum->last_post = $last_post;
 
-function _forum_num_replies($term) {
-  return db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) AS count FROM {comments} c INNER JOIN {node} n ON n.nid = c.nid '. node_access_join_sql() .' INNER JOIN {forum} f ON n.nid = f.nid WHERE f.tid = %d AND n.nid = f.nid AND n.nid = c.nid AND n.status = 1 AND '. node_access_where_sql() ." AND c.status = 0 AND n.type = 'forum'", $term));
+    $forums[$forum->tid] = $forum;
+  }
+
+  return $forums;
 }
 
 function _forum_topics_read($term, $uid) {
   // Calculate the number of topics the user has read. Assume all entries older
   // than NODE_NEW_LIMIT are read, and include the recent posts that user has
   // read.
-  $ancient = db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) FROM {forum} f INNER JOIN {node} n ON f.nid = n.nid '. node_access_join_sql() .' WHERE f.tid = %d AND n.status = 1 AND '. node_access_where_sql() .' AND n.created <= %d AND f.shadow = 0', $term, NODE_NEW_LIMIT));
-  $recent = db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) FROM {forum} f INNER JOIN {node} n ON f.nid = n.nid '. node_access_join_sql() .' INNER JOIN {history} h ON n.nid = h.nid WHERE n.status = 1 AND '. node_access_where_sql() .' AND f.tid = %d AND h.uid = %d AND n.created > %d AND f.shadow = 0', $term, $uid, NODE_NEW_LIMIT));
+  $ancient = db_result(db_query('SELECT COUNT(n.nid) FROM {node} n INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d '. node_access_join_sql() .' WHERE n.created <= %d AND n.status = 1 AND '. node_access_where_sql(), $term, NODE_NEW_LIMIT));
+  $recent = db_result(db_query('SELECT COUNT(n.nid) FROM {node} n '. node_access_join_sql() .' INNER JOIN {history} h ON n.nid = h.nid AND h.uid = %d INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND n.created > %d AND '. node_access_where_sql(), $uid, $term, NODE_NEW_LIMIT));
 
   return $ancient + $recent;
 }
 
-function _forum_last_post($term) {
-  $topic = db_fetch_object(db_query_range("SELECT DISTINCT(n.nid), n.created AS timestamp, u.name AS name, u.uid AS uid FROM {forum} f INNER JOIN {node} n ON n.nid = f.nid ". node_access_join_sql() ." INNER JOIN {users} u ON n.uid = u.uid WHERE f.tid = %d AND n.nid = f.nid AND n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." ORDER BY timestamp DESC", $term, 0, 1));
-  $reply = db_fetch_object(db_query_range("SELECT DISTINCT(n.nid), c.timestamp, c.name AS anonymous_name, u.name AS name, u.uid AS uid FROM {forum} f INNER JOIN {node} n ON n.nid = f.nid ". node_access_join_sql() ." INNER JOIN {comments} c ON n.nid = c.nid INNER JOIN {users} u ON c.uid = u.uid WHERE f.tid = %d AND n.nid = f.nid AND n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." AND c.status = 0 ORDER BY c.timestamp DESC", $term, 0, 1));
-  if ($reply) {
-    $reply->name = $reply->uid ? $reply->name : $reply->anonymous_name;
-  }
-  else {
-    $reply = new StdClass();
-  }
-
-  $value = ($topic->timestamp > $reply->timestamp) ? $topic : $reply;
-
-  return $value;
-}
-
 function forum_get_topics($tid, $sortby, $forum_per_page) {
   global $user, $forum_topic_list_header;
 
   $forum_topic_list_header = array(
     array('data' => '&nbsp;'),
     array('data' => t('Topic'), 'field' => 'n.title'),
-    array('data' => t('Replies'), 'field' => 'num_comments'),
+    array('data' => t('Replies'), 'field' => 'l.comment_count'),
     array('data' => t('Created'), 'field' => 'n.created'),
-    array('data' => t('Last reply'), 'field' => 'date_sort'),
+    array('data' => t('Last reply'), 'field' => 'l.last_comment_timestamp'),
   );
 
   $order = _forum_get_topic_order($sortby);
@@ -413,25 +416,22 @@ function forum_get_topics($tid, $sortby, $forum_per_page) {
   $term = taxonomy_get_term($tid);
   $check_tid = $tid ? "'". check_query($tid) ."'" : 'NULL';
 
-  // show topics with the correct tid, or in the forum but with shadow = 1
-  // @TODO: this is not ANSI SQL! ("user error: 'n.created' isn't in GROUP BY")
-  // @TODO: timestamp is a sql reserved word. are there more?
-  $sql = "SELECT DISTINCT(n.nid), n.title, n.sticky, u.name AS name, u.uid AS uid, n.created AS timestamp, GREATEST(n.created, MAX(c.timestamp)) AS date_sort, COUNT(c.nid) AS num_comments, n.comment AS comment_mode, f.tid FROM {node} n ". node_access_join_sql() ." INNER JOIN {term_node} r ON n.nid = r.nid INNER JOIN {users} u ON n.uid = u.uid LEFT JOIN {comments} c ON n.nid = c.nid INNER JOIN {forum} f ON n.nid = f.nid WHERE ((r.tid = $check_tid AND f.shadow = 1) OR f.tid = $check_tid) AND n.status = 1 AND ". node_access_where_sql() ." AND n.type = 'forum' GROUP BY n.nid, n.title, u.name, u.uid, n.created, n.comment, f.tid";
+  $sql = "SELECT n.nid, f.tid, n.title, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid, cu.name, l.last_comment_name) as last_comment_name, l.last_comment_uid, l.comment_count AS num_comments FROM {node} n ". node_access_join_sql() .", {node_comment_statistics} l, {users} cu, {term_node} r, {users} u, {forum} f WHERE n.status = 1 AND l.last_comment_uid = cu.uid AND n.nid = l.nid AND n.nid = r.nid AND r.tid = $check_tid AND n.uid = u.uid AND n.nid = f.nid AND ". node_access_where_sql();
   $sql .= tablesort_sql($forum_topic_list_header, 'n.sticky DESC,');
 
-  $sql_count = "SELECT COUNT(DISTINCT(n.nid)) FROM {node} n ". node_access_join_sql() ." INNER JOIN {forum} f ON n.nid = f.nid INNER JOIN {term_node} r ON n.nid = r.nid WHERE ( (r.tid = $check_tid AND f.shadow = 1) OR f.tid = $check_tid) AND n.status = 1 AND ". node_access_where_sql() ." AND n.type = 'forum'";
+  $sql_count = "SELECT COUNT(n.nid) FROM {node} n ". node_access_join_sql() ." INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = $check_tid WHERE n.status = 1 AND n.type = 'forum' AND ". node_access_where_sql();
 
   $result = pager_query($sql, $forum_per_page, 0, $sql_count);
 
   while ($topic = db_fetch_object($result)) {
     if ($user->uid) {
       // folder is new if topic is new or there are new comments since last visit
-      if ($topic->shadow > 0) {
+      if ($topic->tid != $tid) {
         $topic->new = 0;
       }
       else {
         $history = _forum_user_last_visit($topic->nid);
-        $topic->new_replies = db_result(db_query('SELECT COUNT(DISTINCT(c.nid)) FROM {node} n '. node_access_join_sql() .' INNER JOIN {comments} c ON n.nid = c.nid WHERE n.nid = %d AND n.status = 1 AND '. node_access_where_sql() .' AND c.status = 0 AND c.timestamp > %d', $topic->nid, $history));
+        $topic->new_replies = comment_num_new($topic->nid, $history);
         $topic->new = $topic->new_replies || ($topic->timestamp > $history);
       }
     }
@@ -441,21 +441,25 @@ function forum_get_topics($tid, $sortby, $forum_per_page) {
       $topic->new = 0;
     }
 
-    $topic->last_reply = _forum_last_reply($topic->nid);
+    if ($topic->num_comments > 0) {
+      $last_reply->timestamp = $topic->last_comment_timestamp;
+      $last_reply->name = $topic->last_comment_name;
+      $last_reply->uid = $topic->last_comment_uid;
+      $topic->last_reply = $last_reply;
+    }
     $topics[] = $topic;
   }
 
   return $topics;
 }
 
+/**
+ * Finds the first unread node for a given forum.
+ */
 function _forum_new($tid) {
   global $user;
-  $result = db_query("SELECT DISTINCT(n.nid) FROM {node} n, {history} h, {forum} f ". node_access_join_sql() ." WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." AND h.nid = n.nid AND f.nid = h.nid AND f.tid = %d AND h.uid = %d", $tid, $user->uid);
-  while ($r = db_fetch_object($result)) {
-    $read[] = $r->nid;
-  }
 
-  $nid = db_result(db_query_range("SELECT DISTINCT(n.nid) FROM {node} n ". node_access_join_sql() ." INNER JOIN {forum} f ON n.nid = f.nid WHERE n.type = 'forum' AND f.nid = n.nid AND n.status = 1 AND ". node_access_where_sql() ." AND f.tid = %d AND n.created > %d ". ($read ? 'AND NOT (n.nid IN ('. implode(',', $read) .')) ' : '') .'ORDER BY created', $tid, NODE_NEW_LIMIT, 0, 1));
+  $nid = db_result(db_query_range("SELECT n.nid FROM {node} n LEFT JOIN {history} h ON n.nid = h.hid AND h.uid = %d INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d " . node_access_join_sql() . " WHERE n.status = 1 AND n.type = 'forum' AND h.nid IS NULL AND n.created > %d AND " . node_access_where_sql() . " ORDER BY created", $user->uid, $tid, NODE_NEW_LIMIT, 0, 1));
 
   return $nid ? $nid : 0;
 }
@@ -704,16 +708,16 @@ function _forum_user_last_visit($nid) {
 function _forum_get_topic_order($sortby) {
   switch ($sortby) {
     case 1:
-      return array('field' => 'date_sort', 'sort' => 'desc');
+      return array('field' => 'l.last_comment_timestamp', 'sort' => 'desc');
       break;
     case 2:
-      return array('field' => 'date_sort', 'sort' => 'asc');
+      return array('field' => 'l.last_comment_timestamp', 'sort' => 'asc');
       break;
     case 3:
-      return array('field' => 'num_comments', 'sort' => 'desc');
+      return array('field' => 'l.comment_count', 'sort' => 'desc');
       break;
     case 4:
-      return array('field' => 'num_comments', 'sort' => 'asc');
+      return array('field' => 'l.comment_count', 'sort' => 'asc');
       break;
   }
 }