? drushrc.php
? mw.patch
? modules/simpletest/mw.patch
Index: modules/book/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.module,v
retrieving revision 1.466
diff -u -F^f -r1.466 book.module
--- modules/book/book.module	1 Jul 2008 20:52:55 -0000	1.466
+++ modules/book/book.module	16 Jul 2008 05:58:35 -0000
@@ -485,6 +485,8 @@ function _book_update_outline(&$node) {
         book_update_bid($node->book);
       }
     }
+    // Completely clear the node_load() cache since this change can affect many nodes.
+    cache_clear_all('*', 'cache_node', TRUE);
 
     return TRUE;
   }
Index: modules/book/book.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.pages.inc,v
retrieving revision 1.7
diff -u -F^f -r1.7 book.pages.inc
--- modules/book/book.pages.inc	15 May 2008 21:19:24 -0000	1.7
+++ modules/book/book.pages.inc	16 Jul 2008 05:58:35 -0000
@@ -211,6 +211,9 @@ function book_remove_form_submit($form, 
     // Only allowed when this is not a book (top-level page).
     menu_link_delete($node->book['mlid']);
     db_query('DELETE FROM {book} WHERE nid = %d', $node->nid);
+
+    // Completely clear the node_load() cache since this change can affect many nodes.
+    cache_clear_all('*', 'cache_node', TRUE);
     drupal_set_message(t('The post has been removed from the book.'));
   }
   $form_state['redirect'] = 'node/' . $node->nid;
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.642
diff -u -F^f -r1.642 comment.module
--- modules/comment/comment.module	3 Jul 2008 17:57:03 -0000	1.642
+++ modules/comment/comment.module	16 Jul 2008 05:58:35 -0000
@@ -729,6 +729,7 @@ function comment_save($edit) {
       _comment_update_node_statistics($edit['nid']);
       // Clear the cache so an anonymous user can see his comment being added.
       cache_clear_all();
+      cache_clear_all($edit['nid'], 'cache_node');
 
       // Explain the approval queue if necessary, and then
       // redirect the user to the node he's commenting on.
@@ -1814,6 +1815,8 @@ function _comment_update_node_statistics
     $node = db_fetch_object(db_query("SELECT uid, created FROM {node} WHERE nid = %d", $nid));
     db_query("UPDATE {node_comment_statistics} SET comment_count = 0, last_comment_timestamp = %d, last_comment_name = '', last_comment_uid = %d WHERE nid = %d", $node->created, $node->uid, $nid);
   }
+  // Clear the node_load cache.
+  cache_clear_all($nid, 'cache_node');
 }
 
 /**
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.967
diff -u -F^f -r1.967 node.module
--- modules/node/node.module	26 May 2008 17:12:55 -0000	1.967
+++ modules/node/node.module	16 Jul 2008 05:58:36 -0000
@@ -557,6 +557,7 @@ function node_type_delete($type) {
  */
 function node_type_update_nodes($old_type, $type) {
   db_query("UPDATE {node} SET type = '%s' WHERE type = '%s'", $type, $old_type);
+  cache_clear_all('*', 'cache_node', TRUE);
   return db_affected_rows();
 }
 
@@ -714,6 +715,21 @@ function node_invoke_nodeapi(&$node, $op
 /**
  * Load a node object from the database.
  *
+ * This function provides two levels of caching. First, a static variable 
+ * cache stores all nodes loaded in this request. Use the $reset argument 
+ * to invalidate it. Second, the cache API based cache persists loaded nodes across 
+ * page requests until invalidated with cache_clear_all('*', 'cache_node', TRUE). To wipe 
+ * cache for a specific node: cache_clear_all($nid, 'cache_node');
+ * 
+ * If the load operation was cached previously, we load the data from
+ * that cache, and no hook_nodeapi('load') implementations are called. That means,
+ * that we expect all load implementations to return the same data and do
+ * not use any conditions on the user, language or anything else,
+ * which would limit what is alter in the node_load cache.
+ *
+ * Modules that wish to prevent a node from being cached should set
+ * the node's 'load_cache' property to FALSE.
+ *
  * @param $param
  *   Either the nid of the node or an array of conditions to match against in the database query
  * @param $revision
@@ -735,7 +751,14 @@ function node_load($param = array(), $re
   $arguments = array();
   if (is_numeric($param)) {
     if ($cachable) {
-      // Is the node statically cached?
+      if (!isset($nodes[$param])) {
+        if ($cache = cache_get($param, 'cache_node')) {
+          $nodes[$param] = $cache->data;
+        }
+      }
+
+      // Either the node was statically cached or we loaded from the
+      // cache_node table.
       if (isset($nodes[$param])) {
         return is_object($nodes[$param]) ? clone $nodes[$param] : $nodes[$param];
       }
@@ -780,6 +803,11 @@ function node_load($param = array(), $re
   }
 
   if ($node && $node->nid) {
+
+    // Default behavior is to cache the node. Modules that don't want
+    // a node to be cached can set this flag to FALSE. 
+    $node->load_cache = TRUE;
+
     // Call the node specific callback (if any) and piggy-back the
     // results to the node or overwrite some values.
     if ($extra = node_invoke($node, 'load')) {
@@ -793,8 +821,12 @@ function node_load($param = array(), $re
         $node->$key = $value;
       }
     }
-    if ($cachable) {
+    
+    // We can only cache when a nid is given, otherwise the conditions are
+    // too dynamic to be cacheable.
+    if ($cachable && is_numeric($param) && $node->load_cache === TRUE) {
       $nodes[$node->nid] = is_object($node) ? clone $node : $node;
+      cache_set($param, $nodes[$node->nid], 'cache_node');
     }
   }
 
@@ -967,6 +999,9 @@ function node_save(&$node) {
 
   // Clear the page and block caches.
   cache_clear_all();
+
+  // Clear the node load cache for this node.
+  cache_clear_all($node->nid, 'cache_node');
 }
 
 /**
@@ -1004,6 +1039,8 @@ function node_delete($nid) {
 
     // Clear the page and block caches.
     cache_clear_all();
+    // Clear the node load cache for this node.
+    cache_clear_all($node->nid, 'cache_node');
 
     // Remove this node from the search index if needed.
     if (function_exists('search_wipe')) {
@@ -1376,6 +1413,7 @@ function node_user($op, &$edit, &$user) 
   if ($op == 'delete') {
     db_query('UPDATE {node} SET uid = 0 WHERE uid = %d', $user->uid);
     db_query('UPDATE {node_revisions} SET uid = 0 WHERE uid = %d', $user->uid);
+    cache_clear_all('*', 'cache_node', TRUE);
   }
 }
 
Index: modules/node/node.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.test,v
retrieving revision 1.4
diff -u -F^f -r1.4 node.test
--- modules/node/node.test	26 Jun 2008 11:40:07 -0000	1.4
+++ modules/node/node.test	16 Jul 2008 05:58:36 -0000
@@ -79,7 +79,7 @@
     // Confirm that revisions revert properly.
     $this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/revert", array(), t('Revert'));
     $this->assertRaw(t('@type %title has been reverted back to the revision from %revision-date.',
-                        array('@type' => 'Page', '%title' => $nodes[1]->title,
+                        array('@type' => node_get_types('name', $nodes[1]->type), '%title' => $nodes[1]->title,
                               '%revision-date' => format_date($nodes[1]->revision_timestamp))), t('Revision reverted.'));
     $reverted_node = node_load($node->nid);
     $this->assertTrue(($nodes[1]->body == $reverted_node->body), t('Node reverted correctly.'));
Index: modules/poll/poll.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.module,v
retrieving revision 1.268
diff -u -F^f -r1.268 poll.module
--- modules/poll/poll.module	15 May 2008 20:55:58 -0000	1.268
+++ modules/poll/poll.module	16 Jul 2008 05:58:36 -0000
@@ -123,7 +123,19 @@ function poll_menu() {
  * Callback function to see if a node is acceptable for poll menu items.
  */
 function _poll_menu_access($node, $perm, $inspect_allowvotes) {
-  return user_access($perm) && ($node->type == 'poll') && ($node->allowvotes || !$inspect_allowvotes);
+  if (user_access($perm) && ($node->type == 'poll')) {
+    if ($inspect_allowvotes) {
+      // Load allow_votes into $node.
+      poll_allow_votes($node);
+      return $node->allowvotes;
+    }
+    else {
+      return TRUE;
+    }
+  }
+  else {
+    return FALSE;
+  }
 }
 
 /**
@@ -451,23 +463,6 @@ function poll_load($node) {
     $poll->choice[$choice['chid']] = $choice;
   }
 
-  // Determine whether or not this user is allowed to vote.
-  $poll->allowvotes = FALSE;
-  if (user_access('vote on polls') && $poll->active) {
-    if ($user->uid) {
-      $result = db_fetch_object(db_query('SELECT chid FROM {poll_votes} WHERE nid = %d AND uid = %d', $node->nid, $user->uid));
-    }
-    else {
-      $result = db_fetch_object(db_query("SELECT chid FROM {poll_votes} WHERE nid = %d AND hostname = '%s'", $node->nid, ip_address()));
-    }
-    if (isset($result->chid)) {
-      $poll->vote = $result->chid;
-    }
-    else {
-      $poll->vote = -1;
-      $poll->allowvotes = TRUE;
-    }
-  }
   return $poll;
 }
 
@@ -526,6 +521,38 @@ function poll_delete($node) {
 }
 
 /**
+ * Determine whether or not the current user is allowed to vote.
+ *
+ * @param object $node 
+ *   A node object
+ * @return
+ *   Inject the 'allow_votes' and 'vote'  elements into $node. Since this is user dependant,
+ *   we don't perform this during poll_load().
+ */
+function poll_allow_votes(&$node) {
+  global $user;
+  
+  if (!isset($node->allow_votes)) {
+    $node->allowvotes = FALSE;
+    if (user_access('vote on polls') && $node->active) {
+      if ($user->uid) {
+        $result = db_fetch_object(db_query('SELECT chid FROM {poll_votes} WHERE nid = %d AND uid = %d', $node->nid, $user->uid));
+      }
+      else {
+        $result = db_fetch_object(db_query("SELECT chid FROM {poll_votes} WHERE nid = %d AND hostname = '%s'", $node->nid, ip_address()));
+      }
+      if (isset($result->chid)) {
+        $node->vote = $result->chid;
+      }
+      else {
+        $node->vote = -1;
+        $node->allowvotes = TRUE;
+      }
+    }
+  }
+}
+
+/**
  * Implementation of hook_view().
  *
  * @param $block
@@ -536,6 +563,8 @@ function poll_view($node, $teaser = FALS
   global $user;
   $output = '';
 
+  poll_allow_votes($node);
+  
   // Special display for side-block
   if ($block) {
     // No 'read more' link
@@ -647,6 +676,7 @@ function poll_vote($form, &$form_state) 
   db_query("UPDATE {poll_choices} SET chvotes = chvotes + 1 WHERE chid = %d", $choice);
 
   cache_clear_all();
+  cache_clear_all($node->nid, 'cache_node');
   drupal_set_message(t('Your vote was recorded.'));
 
   // Return the user to whatever page they voted from.
@@ -811,6 +841,9 @@ function poll_cancel($form, &$form_state
   $node = node_load($form['#nid']);
   global $user;
 
+  // Subtract from the votes.
+  poll_allow_votes($node);
+  
   if ($user->uid) {
     db_query('DELETE FROM {poll_votes} WHERE nid = %d and uid = %d', $node->nid, $user->uid);
   }
@@ -818,8 +851,8 @@ function poll_cancel($form, &$form_state
     db_query("DELETE FROM {poll_votes} WHERE nid = %d and hostname = '%s'", $node->nid, ip_address());
   }
 
-  // Subtract from the votes.
   db_query("UPDATE {poll_choices} SET chvotes = chvotes - 1 WHERE chid = %d", $node->vote);
+  cache_clear_all($node->nid, 'cache_node');
 }
 
 /**
Index: modules/poll/poll.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.pages.inc,v
retrieving revision 1.6
diff -u -F^f -r1.6 poll.pages.inc
--- modules/poll/poll.pages.inc	15 May 2008 20:55:58 -0000	1.6
+++ modules/poll/poll.pages.inc	16 Jul 2008 05:58:36 -0000
@@ -33,7 +33,6 @@ function poll_votes($node) {
 
   $header[] = array('data' => t('Visitor'), 'field' => 'u.name');
   $header[] = array('data' => t('Vote'), 'field' => 'pv.chorder');
-  $header[] = array('data' => t('Vote'), 'field' => 'pc.weight');
 
   $result = pager_query("SELECT pv.chid, pv.uid, pv.hostname, u.name FROM {poll_votes} pv INNER JOIN {poll_choices} pc ON pv.chid = pc.chid LEFT JOIN {users} u ON pv.uid = u.uid WHERE pv.nid = %d". tablesort_sql($header), 20, 0, NULL, $node->nid);
   $rows = array();
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.256
diff -u -F^f -r1.256 system.install
--- modules/system/system.install	1 Jul 2008 20:36:40 -0000	1.256
+++ modules/system/system.install	16 Jul 2008 05:58:37 -0000
@@ -612,6 +612,8 @@ function system_schema() {
   $schema['cache_menu']['description'] = t('Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.');
   $schema['cache_registry'] = $schema['cache'];
   $schema['cache_registry']['description'] = t('Cache table for the code registry system to remember what code files need to be loaded on any given page.');
+  $schema['cache_node'] = $schema['cache'];
+  $schema['cache_node']['description'] = t('Cache table for the node system to store node_load() data.');
 
   $schema['files'] = array(
     'description' => t('Stores information for uploaded files.'),
@@ -3030,6 +3032,61 @@ function system_update_7009() {
 }
 
 /**
+ * Create a cache_node table.
+ */
+function system_update_7010() {
+  $ret = array();
+  $schema['cache_node'] = array(
+    'description' => t('Cache table used to store the output of node_load.'),
+    'fields' => array(
+      'cid' => array(
+        'description' => t('Primary Key: Unique cache ID.'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'data' => array(
+        'description' => t('A collection of data to cache.'),
+        'type' => 'blob',
+        'not null' => FALSE,
+        'size' => 'big',
+      ),
+      'expire' => array(
+        'description' => t('A Unix timestamp indicating when the cache entry should expire, or 0 for never.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'created' => array(
+        'description' => t('A Unix timestamp indicating when the cache entry was created.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'headers' => array(
+        'description' => t('Any custom HTTP headers to be added to cached data.'),
+        'type' => 'text',
+        'not null' => FALSE,
+      ),
+      'serialized' => array(
+        'description' => t('A flag to indicate whether content is serialized (1) or not (0).'),
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'expire' => array('expire'),
+    ),
+    'primary key' => array('cid'),
+  );
+  db_create_table($ret, 'cache_node', $schema['cache_node']);
+  return $ret;
+}
+
+/**
  * @} End of "defgroup updates-6.x-to-7.x"
  * The next series of updates should start at 8000.
  */
Index: modules/translation/translation.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/translation/translation.module,v
retrieving revision 1.27
diff -u -F^f -r1.27 translation.module
--- modules/translation/translation.module	6 May 2008 12:18:51 -0000	1.27
+++ modules/translation/translation.module	16 Jul 2008 05:58:37 -0000
@@ -217,6 +217,7 @@ function translation_nodeapi(&$node, $op
           db_query("UPDATE {node} SET tnid = %d, translate = %d WHERE nid = %d", $tnid, 0, $node->translation_source->nid);
         }
         db_query("UPDATE {node} SET tnid = %d, translate = %d WHERE nid = %d", $tnid, 0, $node->nid);
+        translation_clear_node_cache($tnid);
       }
       break;
 
@@ -228,6 +229,7 @@ function translation_nodeapi(&$node, $op
           // This is the source node, asking to mark all translations outdated.
           db_query("UPDATE {node} SET translate = 1 WHERE tnid = %d AND nid != %d", $node->tnid, $node->nid);
         }
+        translation_clear_node_cache($node->tnid);
       }
       break;
 
@@ -257,6 +259,22 @@ function translation_remove_from_set($no
         db_query('UPDATE {node} SET tnid = %d WHERE tnid = %d', $new_tnid, $node->tnid);
       }
     }
+    translation_clear_node_cache($node->tnid);
+  }
+}
+
+/**
+ * Clear node_load cache for all nodes in the translation set, so we have
+ * the proper translation set information in every node.
+ * 
+ * @param $tnid
+ *   The translation source nid of the translation set, the identifier
+ *   of the node used to derive all translations in the set.
+ */
+function translation_clear_node_cache($tnid) {
+
+  foreach (translation_node_get_translations($tnid) as $node) {
+    cache_clear_all($node->nid, 'cache_node');
   }
 }
 
@@ -284,7 +302,7 @@ function translation_node_get_translatio
         $translations[$tnid][$node->language] = $node;
       }
     }
-    return $translations[$tnid];
+    return isset($translations[$tnid]) ? $translations[$tnid] : array();
   }
 }
 
Index: modules/translation/translation.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/translation/translation.test,v
retrieving revision 1.3
diff -u -F^f -r1.3 translation.test
--- modules/translation/translation.test	30 May 2008 07:30:53 -0000	1.3
+++ modules/translation/translation.test	16 Jul 2008 05:58:37 -0000
@@ -52,7 +52,7 @@
     $node_trans_title = 'Test Traduccion ' . $this->randomName();
     $node_trans = $this->createTranslation($node->nid, $node_trans_title, 'Nodo cuerpo.', 'es');
 
-    // Update origninal and mark translation as outdated.
+    // Update original and mark translation as outdated.
     $edit = array();
     $edit['body'] = 'Node body. Additional Text.';
     $edit['translation[retranslate]'] = TRUE;
@@ -126,7 +126,7 @@
   }
 
   /**
-   * Create a translation for the specified page in the specified language.
+   * Create a translation for the specified node in the specified language.
    *
    * @param integer $nid Node id of page to create translation for.
    * @param string $title Title of page in specified language.
