Index: modules/comment/comment.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.test,v
retrieving revision 1.73
diff -u -p -r1.73 comment.test
--- modules/comment/comment.test	31 Mar 2010 20:05:06 -0000	1.73
+++ modules/comment/comment.test	6 Apr 2010 00:07:31 -0000
@@ -1263,3 +1263,102 @@ class CommentContentRebuild extends Comm
     $this->assertFalse(isset($built_content['test_property']), t('Comment content was emptied before being built.'));
   }
 }
+
+/**
+ * Test comment token replacement in strings.
+ */
+class CommentTokenReplaceTestCase extends CommentHelperCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Comment token replacement',
+      'description' => 'Generates text using placeholders for dummy content to check comment token replacement.',
+      'group' => 'Comment',
+    );
+  }
+
+  /**
+   * Creates a comment, then tests the tokens generated from it.
+   */
+  function testCommentTokenReplacement() {
+    global $language;
+    $url_options = array(
+      'absolute' => TRUE,
+      'language' => $language,
+    );
+
+    $this->drupalLogin($this->admin_user);
+
+    // Set comment variables.
+    $this->setCommentSubject(TRUE);
+
+    // Create a node and a comment.
+    $node = $this->drupalCreateNode(array('type' => 'article'));
+    $parent_comment = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
+
+    // Post a reply to the comment.
+    $this->drupalGet('comment/reply/' . $node->nid . '/' . $parent_comment->id);
+    $child_comment = $this->postComment(NULL, $this->randomName(), $this->randomName());
+    $comment = comment_load($child_comment->id);
+    $comment->homepage = 'http://example.org/';
+
+    // Add HTML to ensure that sanitation of some fields tested directly.
+    $comment->subject = '<blink>Blinking Comment</blink>';
+
+    // Generate and test sanitized tokens.
+    $tests = array();
+    $tests['[comment:cid]'] = $comment->cid;
+    $tests['[comment:pid]'] = $comment->pid;
+    $tests['[comment:nid]'] = $comment->nid;
+    $tests['[comment:uid]'] = $comment->uid;
+    $tests['[comment:hostname]'] = check_plain($comment->hostname);
+    $tests['[comment:name]'] = filter_xss($comment->name);
+    $tests['[comment:mail]'] = check_plain($this->admin_user->mail);
+    $tests['[comment:homepage]'] = filter_xss_bad_protocol($comment->homepage);
+    $tests['[comment:title]'] = filter_xss($comment->subject);
+    $tests['[comment:body]'] = $comment->comment_body[LANGUAGE_NONE][0]['safe_value'];
+    $tests['[comment:url]'] = url('comment/' . $comment->cid, $url_options + array('fragment' => 'comment-' . $comment->cid));
+    $tests['[comment:edit-url]'] = url('comment/' . $comment->cid . '/edit', $url_options);
+    $tests['[comment:created:since]'] = format_interval(REQUEST_TIME - $comment->created, 2, $language->language);
+    $tests['[comment:changed:since]'] = format_interval(REQUEST_TIME - $comment->changed, 2, $language->language);
+    $tests['[comment:parent:title]'] = check_plain($parent_comment->subject);
+    $tests['[comment:node:title]'] = check_plain($node->title);
+    $tests['[comment:author:name]'] = check_plain($this->admin_user->name);
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('comment' => $comment), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Sanitized comment token %token replaced.', array('%token' => $input)));
+    }
+
+    // Generate and test unsanitized tokens.
+    $tests['[comment:hostname]'] = $comment->hostname;
+    $tests['[comment:name]'] = $comment->name;
+    $tests['[comment:mail]'] = $this->admin_user->mail;
+    $tests['[comment:homepage]'] = $comment->homepage;
+    $tests['[comment:title]'] = $comment->subject;
+    $tests['[comment:body]'] = $comment->comment_body[LANGUAGE_NONE][0]['value'];
+    $tests['[comment:parent:title]'] = $parent_comment->subject;
+    $tests['[comment:node:title]'] = $node->title;
+    $tests['[comment:author:name]'] = $this->admin_user->name;
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('comment' => $comment), array('language' => $language, 'sanitize' => FALSE));
+      $this->assertFalse(strcmp($output, $expected), t('Unsanitized comment token %token replaced.', array('%token' => $input)));
+    }
+
+    // Load node so comment_count gets computed.
+    $node = node_load($node->nid);
+
+    // Generate comment tokens for the node (it has 2 comments, both new).
+    $tests = array();
+    $tests['[node:comment-count]'] = 2;
+    $tests['[node:comment-count-new]'] = 2;
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('node' => $node), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Node comment token %token replaced.', array('%token' => $input)));
+    }
+  }
+}
Index: modules/comment/comment.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.tokens.inc,v
retrieving revision 1.10
diff -u -p -r1.10 comment.tokens.inc
--- modules/comment/comment.tokens.inc	9 Jan 2010 21:54:00 -0000	1.10
+++ modules/comment/comment.tokens.inc	6 Apr 2010 00:07:31 -0000
@@ -82,6 +82,11 @@ function comment_token_info() {
     'description' => t("The date the comment was posted."),
     'type' => 'date',
   );
+  $comment['changed'] = array(
+    'name' => t("Date changed"),
+    'description' => t("The date the comment was most recently updated."),
+    'type' => 'date',
+  );
   $comment['parent'] = array(
     'name' => t("Parent"),
     'description' => t("The comment's parent, if comment threading is active."),
@@ -133,6 +138,10 @@ function comment_tokens($type, $tokens, 
           $replacements[$original] = $comment->cid;
           break;
 
+        case 'pid':
+          $replacements[$original] = $comment->pid;
+          break;
+
         case 'nid':
           $replacements[$original] = $comment->nid;
           break;
@@ -141,10 +150,6 @@ function comment_tokens($type, $tokens, 
           $replacements[$original] = $comment->uid;
           break;
 
-        case 'pid':
-          $replacements[$original] = $comment->pid;
-          break;
-
         // Poster identity information for comments
         case 'hostname':
           $replacements[$original] = $sanitize ? check_plain($comment->hostname) : $comment->hostname;
@@ -175,7 +180,7 @@ function comment_tokens($type, $tokens, 
           break;
 
         case 'body':
-          $replacements[$original] = $sanitize ? check_markup($comment->comment, $comment->format, '', TRUE) : $comment->comment;
+          $replacements[$original] = $sanitize ? $comment->comment_body[LANGUAGE_NONE][0]['safe_value'] : $comment->comment_body[LANGUAGE_NONE][0]['value'];
           break;
 
         // Comment related URLs.
@@ -185,6 +190,7 @@ function comment_tokens($type, $tokens, 
           break;
 
         case 'edit-url':
+          $url_options['fragment'] = NULL;
           $replacements[$original] = url('comment/' . $comment->cid . '/edit', $url_options);
           break;
 
@@ -226,6 +232,10 @@ function comment_tokens($type, $tokens, 
       $replacements += token_generate('date', $date_tokens, array('date' => $comment->created), $options);
     }
 
+    if ($date_tokens = token_find_with_prefix($tokens, 'changed')) {
+      $replacements += token_generate('date', $date_tokens, array('date' => $comment->changed), $options);
+    }
+
     if (($parent_tokens = token_find_with_prefix($tokens, 'parent')) && $parent = comment_load($comment->pid)) {
       $replacements += token_generate('comment', $parent_tokens, array('comment' => $parent), $options);
     }
Index: modules/file/tests/file.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/file/tests/file.test,v
retrieving revision 1.13
diff -u -p -r1.13 file.test
--- modules/file/tests/file.test	27 Mar 2010 05:52:50 -0000	1.13
+++ modules/file/tests/file.test	6 Apr 2010 00:07:31 -0000
@@ -568,3 +568,79 @@ class FileFieldPathTestCase extends File
     $this->assertTrue($result, $message);
   }
 }
+
+/**
+ * Test file token replacement in strings.
+ */
+class FileTokenReplaceTestCase extends FileFieldTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File token replacement',
+      'description' => 'Generates text using placeholders for dummy content to check file token replacement.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Creates a file, then tests the tokens generated from it.
+   */
+  function testFileTokenReplacement() {
+    global $language;
+    $url_options = array(
+      'absolute' => TRUE,
+      'language' => $language,
+    );
+
+    // Create file field.
+    $type_name = 'article';
+    $field_name = 'field_' . strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name);
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    $test_file = $this->getTestFile('text');
+
+    // Create a new node with the uploaded file.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+    // Load the node and the file.
+    $node = node_load($nid);
+    $file = (object) $node->{$field_name}[LANGUAGE_NONE][0];
+    $file->description = 'File description.';
+
+    // Generate and test sanitized tokens.
+    $tests = array();
+    $tests['[file:fid]'] = $file->fid;
+    $tests['[file:uid]'] = $file->uid;
+    $tests['[file:name]'] = check_plain($file->filename);
+    $tests['[file:description]'] = filter_xss($file->description);
+    $tests['[file:path]'] = filter_xss($file->uri);
+    $tests['[file:mime]'] = filter_xss($file->filemime);
+    $tests['[file:size]'] = format_size($file->filesize);
+    $tests['[file:url]'] = url(file_create_url($file->uri), $url_options);
+    $tests['[file:timestamp]'] = format_date($file->timestamp, 'medium', '', NULL, $language->language);
+    $tests['[file:timestamp:short]'] = format_date($file->timestamp, 'short', '', NULL, $language->language);
+    $tests['[file:owner]'] = $this->admin_user->name;
+    $tests['[file:owner:uid]'] = $this->admin_user->uid;
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('file' => $file), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Sanitized file token %token replaced.', array('%token' => $input)));
+    }
+
+    // Generate and test unsanitized tokens.
+    $tests['[file:name]'] = $file->filename;
+    $tests['[file:description]'] = $file->description;
+    $tests['[file:path]'] = $file->uri;
+    $tests['[file:mime]'] = $file->filemime;
+    $tests['[file:size]'] = format_size($file->filesize);
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('file' => $file), array('language' => $language, 'sanitize' => FALSE));
+      $this->assertFalse(strcmp($output, $expected), t('Unsanitized file token %token replaced.', array('%token' => $input)));
+    }
+  }
+}
Index: modules/node/node.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.test,v
retrieving revision 1.78
diff -u -p -r1.78 node.test
--- modules/node/node.test	31 Mar 2010 20:05:06 -0000	1.78
+++ modules/node/node.test	6 Apr 2010 00:07:31 -0000
@@ -1599,3 +1599,78 @@ class NodeQueryAlter extends DrupalWebTe
     }
   }
 }
+
+/**
+ * Test node token replacement in strings.
+ */
+class NodeTokenReplaceTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Node token replacement',
+      'description' => 'Generates text using placeholders for dummy content to check node token replacement.',
+      'group' => 'Node',
+    );
+  }
+
+  /**
+   * Creates a node, then tests the tokens generated from it.
+   */
+  function testNodeTokenReplacement() {
+    global $language;
+    $url_options = array(
+      'absolute' => TRUE,
+      'language' => $language,
+    );
+
+    // Create a user and a node.
+    $account = $this->drupalCreateUser();
+    $settings = array(
+      'type' => 'article',
+      'uid' => $account->uid,
+      'title' => '<blink>Blinking Text</blink>',
+      'body' => array(LANGUAGE_NONE => array(array('value' => $this->randomName(32), 'summary' => $this->randomName(16)))),
+    );
+    $node = $this->drupalCreateNode($settings);
+
+    // Load node so that the body and summary fields are structured properly. 
+    $node = node_load($node->nid);
+
+    // Generate and test sanitized tokens.
+    $tests = array();
+    $tests['[node:nid]'] = $node->nid;
+    $tests['[node:vid]'] = $node->vid;
+    $tests['[node:tnid]'] = $node->tnid;
+    $tests['[node:uid]'] = $node->uid;
+    $tests['[node:type]'] = 'article';
+    $tests['[node:type-name]'] = 'Article';
+    $tests['[node:title]'] = check_plain($node->title);
+    $tests['[node:body]'] = $node->body[LANGUAGE_NONE][0]['safe_value'];
+    $tests['[node:summary]'] = $node->body[LANGUAGE_NONE][0]['safe_summary'];
+    $tests['[node:language]'] = check_plain($node->language);
+    $tests['[node:url]'] = url('node/' . $node->nid, $url_options);
+    $tests['[node:edit-url]'] = url('node/' . $node->nid . '/edit', $url_options);
+    $tests['[node:author:name]'] = check_plain($account->name);
+    $tests['[node:created:since]'] = format_interval(REQUEST_TIME - $node->created, 2, $language->language);
+    $tests['[node:changed:since]'] = format_interval(REQUEST_TIME - $node->changed, 2, $language->language);
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('node' => $node), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Sanitized node token %token replaced.', array('%token' => $input)));
+    }
+
+    // Generate and test unsanitized tokens.
+    $tests['[node:title]'] = $node->title;
+    $tests['[node:body]'] = $node->body[LANGUAGE_NONE][0]['value'];
+    $tests['[node:summary]'] = $node->body[LANGUAGE_NONE][0]['summary'];
+    $tests['[node:language]'] = $node->language;
+    $tests['[node:author:name]'] = $account->name;
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('node' => $node), array('language' => $language, 'sanitize' => FALSE));
+      $this->assertFalse(strcmp($output, $expected), t('Unsanitized node token %token replaced.', array('%token' => $input)));
+    }
+  }
+}
Index: modules/node/node.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.tokens.inc,v
retrieving revision 1.11
diff -u -p -r1.11 node.tokens.inc
--- modules/node/node.tokens.inc	14 Jan 2010 05:53:55 -0000	1.11
+++ modules/node/node.tokens.inc	6 Apr 2010 00:07:31 -0000
@@ -129,8 +129,13 @@ function node_tokens($type, $tokens, arr
           $replacements[$original] = $node->uid;
           break;
 
-        case 'name':
-          $replacements[$original] = $sanitize ? check_plain($node->name) : $node->name;
+        case 'type':
+          $replacements[$original] = $sanitize ? check_plain($node->type) : $node->type;
+          break;
+
+        case 'type-name':
+          $type_name = node_type_get_name($node);
+          $replacements[$original] = $sanitize ? check_plain($type_name) : $type_name;
           break;
 
         case 'title':
@@ -139,7 +144,7 @@ function node_tokens($type, $tokens, arr
 
         case 'body':
           if (!empty($node->body)) {
-            $replacements[$original] = $sanitize ? $node->body[LANGUAGE_NONE][0]['safe'] : $node->body[LANGUAGE_NONE][0]['value'];
+            $replacements[$original] = $sanitize ? $node->body[LANGUAGE_NONE][0]['safe_value'] : $node->body[LANGUAGE_NONE][0]['value'];
           }
           break;
 
@@ -149,15 +154,6 @@ function node_tokens($type, $tokens, arr
           }
           break;
 
-        case 'type':
-          $replacements[$original] = $sanitize ? check_plain($node->type) : $node->type;
-          break;
-
-        case 'type-name':
-          $type_name = node_type_get_name($node);
-          $replacements[$original] = $sanitize ? check_plain($type_name) : $type_name;
-          break;
-
         case 'language':
           $replacements[$original] = $sanitize ? check_plain($node->language) : $node->language;
           break;
Index: modules/poll/poll.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.test,v
retrieving revision 1.31
diff -u -p -r1.31 poll.test
--- modules/poll/poll.test	26 Mar 2010 18:58:12 -0000	1.31
+++ modules/poll/poll.test	6 Apr 2010 00:07:32 -0000
@@ -457,3 +457,92 @@ class PollVoteCheckHostname extends Poll
     $this->assertTrue(empty($elements), t("%user is not able to vote again.", array('%user' => $this->web_user1->name)));
   }
 }
+
+/**
+ * Test poll token replacement in strings.
+ */
+class PollTokenReplaceTestCase extends PollTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Poll token replacement',
+      'description' => 'Generates text using placeholders for dummy content to check poll token replacement.',
+      'group' => 'Poll',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('poll');
+  }
+
+  /**
+   * Creates a poll, then tests the tokens generated from it.
+   */
+  function testPollTokenReplacement() {
+    global $language;
+
+    // Craete a poll with three choices.
+    $title = $this->randomName();
+    $choices = $this->_generateChoices(3);
+    $poll_nid = $this->pollCreate($title, $choices, FALSE);
+    $this->drupalLogout();
+
+    // Create four users and have each of them vote.
+    $vote_user1 = $this->drupalCreateUser(array('vote on polls', 'access content'));
+    $this->drupalLogin($vote_user1);
+    $edit = array(
+      'choice' => '1',
+    );
+    $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
+    $this->drupalLogout();
+
+    $vote_user2 = $this->drupalCreateUser(array('vote on polls', 'access content'));
+    $this->drupalLogin($vote_user2);
+    $edit = array(
+      'choice' => '1',
+    );
+    $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
+    $this->drupalLogout();
+
+    $vote_user3 = $this->drupalCreateUser(array('vote on polls', 'access content'));
+    $this->drupalLogin($vote_user3);
+    $edit = array(
+      'choice' => '2',
+    );
+    $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
+    $this->drupalLogout();
+
+    $vote_user4 = $this->drupalCreateUser(array('vote on polls', 'access content'));
+    $this->drupalLogin($vote_user4);
+    $edit = array(
+      'choice' => '3',
+    );
+    $this->drupalPost('node/' . $poll_nid, $edit, t('Vote'));
+    $this->drupalLogout();
+
+    $poll = node_load($poll_nid, NULL, TRUE);
+
+    // Generate and test sanitized tokens.
+    $tests = array();
+    $tests['[node:poll-votes]'] = 4;
+    $tests['[node:poll-winner]'] = filter_xss($poll->choice[1]['chtext']);
+    $tests['[node:poll-winner-votes]'] = 2;
+    $tests['[node:poll-winner-percent]'] = 50;
+    $tests['[node:poll-duration]'] = format_interval($poll->runtime, 1, $language->language);
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('node' => $poll), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Sanitized poll token %token replaced.', array('%token' => $input)));
+    }
+
+    // Generate and test unsanitized tokens.
+    $tests['[node:poll-winner]'] = $poll->choice[1]['chtext'];
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('node' => $poll), array('language' => $language, 'sanitize' => FALSE));
+      $this->assertFalse(strcmp($output, $expected), t('Unsanitized poll token %token replaced.', array('%token' => $input)));
+    }
+  }
+}
Index: modules/poll/poll.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.tokens.inc,v
retrieving revision 1.4
diff -u -p -r1.4 poll.tokens.inc
--- modules/poll/poll.tokens.inc	7 Jan 2010 20:55:49 -0000	1.4
+++ modules/poll/poll.tokens.inc	6 Apr 2010 00:07:32 -0000
@@ -41,6 +41,14 @@ function poll_token_info() {
  */
 function poll_tokens($type, $tokens, array $data = array(), array $options = array()) {
   $sanitize = !empty($options['sanitize']);
+  if (isset($options['language'])) {
+    $url_options['language'] = $options['language'];
+    $language_code = $options['language']->language;
+  }
+  else {
+    $language_code = NULL;
+  }
+
   $replacements = array();
 
   if ($type == 'node' && !empty($data['node']) && $data['node']->type == 'poll') {
@@ -65,12 +73,18 @@ function poll_tokens($type, $tokens, arr
           if (isset($winner)) {
             $replacements[$original] = $sanitize ? filter_xss($winner['chtext']) : $winner['chtext'];
           }
+          else {
+            $replacements[$original] = '';
+          }
           break;
 
         case 'poll-winner-votes':
           if (isset($winner)) {
             $replacements[$original] = $winner['chvotes'];
           }
+          else {
+            $replacements[$original] = '';
+          }
           break;
 
         case 'poll-winner-percent':
@@ -78,6 +92,9 @@ function poll_tokens($type, $tokens, arr
             $percent = ($winner['chvotes'] / $total_votes) * 100;
             $replacements[$original] = number_format($percent, 0);
           }
+          else {
+            $replacements[$original] = '';
+          }
           break;
 
         case 'poll-duration':
Index: modules/statistics/statistics.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/statistics/statistics.test,v
retrieving revision 1.16
diff -u -p -r1.16 statistics.test
--- modules/statistics/statistics.test	30 Jan 2010 07:59:25 -0000	1.16
+++ modules/statistics/statistics.test	6 Apr 2010 00:07:32 -0000
@@ -312,3 +312,47 @@ class StatisticsAdminTestCase extends Dr
     $this->assertFalse($result, t('Daycounter is zero.'));
   }
 }
+
+/**
+ * Test statistics token replacement in strings.
+ */
+class StatisticsTokenReplaceTestCase extends StatisticsTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Statistics token replacement',
+      'description' => 'Generates text using placeholders for dummy content to check statistics token replacement.',
+      'group' => 'Statistics',
+    );
+  }
+
+  /**
+   * Creates a node, then tests the statistics tokens generated from it.
+   */
+  function testStatisticsTokenReplacement() {
+    global $language;
+
+    // Create user and node.
+    $user = $this->drupalCreateUser(array('create page content'));
+    $this->drupalLogin($user);
+    $node = $this->drupalCreateNode(array('type' => 'page', 'uid' => $user->uid));
+
+    // Hit the node.
+    $this->drupalGet('node/' . $node->nid);
+    $statistics = statistics_get($node->nid);
+
+    // Generate and test tokens.
+    $tests = array();
+    $tests['[node:total-count]'] = 1;
+    $tests['[node:day-count]'] = 1;
+    $tests['[node:last-view]'] = format_date($statistics['timestamp']);
+    $tests['[node:last-view:short]'] = format_date($statistics['timestamp'], 'short');
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('node' => $node), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Statistics token %token replaced.', array('%token' => $input)));
+    }
+  }
+}
Index: modules/statistics/statistics.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/statistics/statistics.tokens.inc,v
retrieving revision 1.3
diff -u -p -r1.3 statistics.tokens.inc
--- modules/statistics/statistics.tokens.inc	4 Dec 2009 16:49:47 -0000	1.3
+++ modules/statistics/statistics.tokens.inc	6 Apr 2010 00:07:32 -0000
@@ -10,11 +10,11 @@
  * Implements hook_token_info().
  */
 function statistics_token_info() {
-  $node['views'] = array(
+  $node['total-count'] = array(
     'name' => t("Number of views"),
     'description' => t("The number of visitors who have read the node."),
   );
-  $node['day-views'] = array(
+  $node['day-count'] = array(
     'name' => t("Views today"),
     'description' => t("The number of visitors who have read the node today."),
   );
@@ -40,13 +40,13 @@ function statistics_tokens($type, $token
     $node = $data['node'];
 
     foreach ($tokens as $name => $original) {
-      if ($name == 'views') {
+      if ($name == 'total-count') {
         $statistics = statistics_get($node->nid);
-        $replacements[$original] = $statistics['totalviews'];
+        $replacements[$original] = $statistics['totalcount'];
       }
-      elseif ($name == 'views-today') {
+      elseif ($name == 'day-count') {
         $statistics = statistics_get($node->nid);
-        $replacements[$original] = $statistics['dayviews'];
+        $replacements[$original] = $statistics['daycount'];
       }
       elseif ($name == 'last-view') {
         $statistics = statistics_get($node->nid);
Index: modules/system/system.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.test,v
retrieving revision 1.118
diff -u -p -r1.118 system.test
--- modules/system/system.test	31 Mar 2010 19:10:39 -0000	1.118
+++ modules/system/system.test	6 Apr 2010 00:07:32 -0000
@@ -1480,6 +1480,77 @@ class TokenReplaceTestCase extends Drupa
     $generated = token_generate('node', $raw_tokens, array('node' => $node), array('sanitize' => FALSE));
     $this->assertFalse(strcmp($generated['[node:title]'], $node->title), t('Unsanitized token generated properly.'));
   }
+
+  /**
+   * Tests the generation of all system site information tokens.
+   */
+  function testSystemSiteTokenReplacement() {
+    global $language;
+    $url_options = array(
+      'absolute' => TRUE,
+      'language' => $language,
+    );
+
+    // Set a few site variables.
+    variable_set('site_name', '<strong>Drupal<strong>');
+    variable_set('site_slogan', '<blink>Slogan</blink>');
+    variable_set('site_mission', '<em>Mission</em>');
+
+    // Generate and test sanitized tokens.
+    $tests = array();
+    $tests['[site:name]'] = check_plain(variable_get('site_name', 'Drupal'));
+    $tests['[site:slogan]'] = check_plain(variable_get('site_slogan', ''));
+    $tests['[site:mission]'] = filter_xss(variable_get('site_mission', ''));
+    $tests['[site:mail]'] = 'simpletest@example.com';
+    $tests['[site:url]'] = url('<front>', $url_options);
+    $tests['[site:url-brief]'] = preg_replace('!^https?://!', '', url('<front>', $url_options));
+    $tests['[site:login-url]'] = url('user', $url_options);
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array(), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Sanitized system site information token %token replaced.', array('%token' => $input)));
+    }
+
+    // Generate and test unsanitized tokens.
+    $tests['[site:name]'] = variable_get('site_name', 'Drupal');
+    $tests['[site:slogan]'] = variable_get('site_slogan', '');
+    $tests['[site:mission]'] = variable_get('site_mission', '');
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array(), array('language' => $language, 'sanitize' => FALSE));
+      $this->assertFalse(strcmp($output, $expected), t('Unsanitized system site information token %token replaced.', array('%token' => $input)));
+    }
+  }
+
+  /**
+   * Tests the generation of all system date tokens.
+   */
+  function testSystemDateTokenReplacement() {
+    global $language;
+
+    // Set time to one hour before request.
+    $date = REQUEST_TIME - 3600;
+
+    // Generate and test tokens.
+    $tests = array();
+    $tests['[date:short]'] = format_date($date, 'short', '', NULL, $language->language);
+    $tests['[date:medium]'] = format_date($date, 'medium', '', NULL, $language->language);
+    $tests['[date:long]'] = format_date($date, 'long', '', NULL, $language->language);
+    $tests['[date:custom:m/j/Y]'] = format_date($date, 'custom', 'm/j/Y', NULL, $language->language);
+    $tests['[date:since]'] = format_interval((REQUEST_TIME - $date), 2, $language->language);
+    $tests['[date:raw]'] = filter_xss($date);
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('date' => $date), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Date token %token replaced.', array('%token' => $input)));
+    }
+  }
 }
 
 class InfoFileParserTestCase extends DrupalUnitTestCase {
Index: modules/system/system.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.tokens.inc,v
retrieving revision 1.8
diff -u -p -r1.8 system.tokens.inc
--- modules/system/system.tokens.inc	30 Jan 2010 07:59:25 -0000	1.8
+++ modules/system/system.tokens.inc	6 Apr 2010 00:07:32 -0000
@@ -93,10 +93,6 @@ function system_token_info() {
     'name' => t("User ID"),
     'description' => t("The unique ID of the user who owns the file."),
   );
-  $file['nid'] = array(
-    'name' => t("Node ID"),
-    'description' => t("The unique ID of the node the file is attached to."),
-  );
   $file['name'] = array(
     'name' => t("File name"),
     'description' => t("The name of the file on disk."),
@@ -107,7 +103,7 @@ function system_token_info() {
   );
   $file['path'] = array(
     'name' => t("Path"),
-    'description' => t("The location of the file on disk."),
+    'description' => t("The location of the file relative to Drupal root."),
   );
   $file['mime'] = array(
     'name' => t("MIME type"),
@@ -117,7 +113,7 @@ function system_token_info() {
     'name' => t("File size"),
     'description' => t("The size of the file, in kilobytes."),
   );
-  $file['path'] = array(
+  $file['url'] = array(
     'name' => t("URL"),
     'description' => t("The web-accessible URL for the file."),
   );
@@ -126,11 +122,6 @@ function system_token_info() {
     'description' => t("The date the file was most recently changed."),
     'type' => 'date',
   );
-  $file['node'] = array(
-    'name' => t("Node"),
-    'description' => t("The node the file is attached to."),
-    'type' => 'date',
-  );
   $file['owner'] = array(
     'name' => t("Owner"),
     'description' => t("The user who originally uploaded the file."),
@@ -155,6 +146,7 @@ function system_tokens($type, $tokens, a
   if (isset($language)) {
     $url_options['language'] = $language;
   }
+  $langcode = (isset($language) ? $language->language : NULL);
   $sanitize = !empty($options['sanitize']);
 
   $replacements = array();
@@ -203,14 +195,9 @@ function system_tokens($type, $tokens, a
     else {
       $date = $data['date'];
     }
-    $langcode = (isset($language) ? $language->language : NULL);
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
-        case 'raw':
-          $replacements[$original] = filter_xss($date);
-          break;
-
         case 'short':
           $replacements[$original] = format_date($date, 'short', '', NULL, $langcode);
           break;
@@ -226,6 +213,10 @@ function system_tokens($type, $tokens, a
         case 'since':
           $replacements[$original] = format_interval((REQUEST_TIME - $date), 2, $langcode);
           break;
+
+        case 'raw':
+          $replacements[$original] = filter_xss($date);
+          break;
       }
     }
 
@@ -250,10 +241,6 @@ function system_tokens($type, $tokens, a
           $replacements[$original] = $file->uid;
           break;
 
-        case 'nid':
-          $replacements[$original] = $file->nid;
-          break;
-
         // Essential file data
         case 'name':
           $replacements[$original] = $sanitize ? check_plain($file->filename) : $file->filename;
@@ -264,7 +251,7 @@ function system_tokens($type, $tokens, a
           break;
 
         case 'path':
-          $replacements[$original] = $sanitize ? filter_xss($file->filepath) : $file->filepath;
+          $replacements[$original] = $sanitize ? filter_xss($file->uri) : $file->uri;
           break;
 
         case 'mime':
@@ -276,39 +263,27 @@ function system_tokens($type, $tokens, a
           break;
 
         case 'url':
-          $replacements[$original] = url(file_create_url($file->filepath), $url_options);
+          $replacements[$original] = url(file_create_url($file->uri), $url_options);
           break;
 
         // These tokens are default variations on the chained tokens handled below.
-        case 'node':
-          if ($nid = $file->nid) {
-            $node = node_load($file->nid);
-            $replacements[$original] = $sanitize ? filter_xss($node->title) : $node->title;
-          }
-          break;
-
         case 'timestamp':
-          $replacements[$original] = format_date($file->timestamp, 'medium', '', NULL, (isset($language) ? $language->language : NULL));
+          $replacements[$original] = format_date($file->timestamp, 'medium', '', NULL, $langcode);
           break;
 
         case 'owner':
           $account = user_load($file->uid);
-          $replacements[$original] = $sanitize ? filter_xss($user->name) : $user->name;
+          $replacements[$original] = $sanitize ? filter_xss($account->name) : $account->name;
           break;
       }
     }
 
-    if ($node_tokens = token_find_with_prefix($tokens, 'node')) {
-      $node = node_load($file->nid);
-      $replacements += token_generate('node', $node_tokens, array('node' => $node), $language, $sanitize);
-    }
-
     if ($date_tokens = token_find_with_prefix($tokens, 'timestamp')) {
-      $replacements += token_generate('date', $date_tokens, array('date' => $file->timestamp), $language, $sanitize);
+      $replacements += token_generate('date', $date_tokens, array('date' => $file->timestamp), $options);
     }
 
     if (($owner_tokens = token_find_with_prefix($tokens, 'owner')) && $account = user_load($file->uid)) {
-      $replacements += token_generate('user', $owner_tokens, array('user' => $account), $language, $sanitize);
+      $replacements += token_generate('user', $owner_tokens, array('user' => $account), $options);
     }
   }
 
Index: modules/taxonomy/taxonomy.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.test,v
retrieving revision 1.75
diff -u -p -r1.75 taxonomy.test
--- modules/taxonomy/taxonomy.test	27 Mar 2010 05:52:50 -0000	1.75
+++ modules/taxonomy/taxonomy.test	6 Apr 2010 00:07:32 -0000
@@ -911,12 +911,15 @@ class TaxonomyTokenReplaceTestCase exten
    * Creates some terms and a node, then tests the tokens generated from them.
    */
   function testTaxonomyTokenReplacement() {
+    global $language;
+
     // Create two taxonomy terms.
     $term1 = $this->createTerm($this->vocabulary);
     $term2 = $this->createTerm($this->vocabulary);
 
     // Edit $term2, setting $term1 as parent.
     $edit = array();
+    $edit['name'] = '<blink>Blinking Text</blink>';
     $edit['parent[]'] = array($term1->tid);
     $this->drupalPost('taxonomy/term/' . $term2->tid . '/edit', $edit, t('Save'));
 
@@ -926,57 +929,59 @@ class TaxonomyTokenReplaceTestCase exten
     $edit[$this->instance['field_name'] . '[' . $this->langcode . '][]'] = $term2->tid;
     $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
 
-    // Generate term token strings (before and after replacement) for term2.
-    $source  = '[term:tid]';
-    $source .= '[term:vid]';
-    $source .= '[term:name]';
-    $source .= '[term:description]';
-    $source .= '[term:url]';
-    $source .= '[term:node-count]';
-    $source .= '[term:parent:name]';
-    $source .= '[term:vocabulary:name]';
-
-    $target  = $term2->tid;
-    $target .= $term2->vid;
-    $target .= check_plain($term2->name);
-    $target .= check_markup($term2->description, $term2->format);
-    $target .= url('taxonomy/term/' . $term2->tid, array('absolute' => TRUE));
-    $target .= 1;
-    $target .= check_plain($term1->name);
-    $target .= check_plain($this->vocabulary->name);
-
-    $result = token_replace($source, array('term' => $term2));
-    $this->assertEqual(strcmp($target, $result), 0, t('Taxonomy term placeholder tokens replaced.'));
-
-    // Generate vocabulary token strings (before and after replacement).
-    $source = '[vocabulary:vid]';
-    $source .= '[vocabulary:name]';
-    $source .= '[vocabulary:description]';
-    $source .= '[vocabulary:node-count]';
-    $source .= '[vocabulary:term-count]';
-
-    $target = $this->vocabulary->vid;
-    $target .= check_plain($this->vocabulary->name);
-    $target .= filter_xss($this->vocabulary->description);
-    $target .= 1;
-    $target .= 2;
-
-    $result = token_replace($source, array('vocabulary' => $this->vocabulary));
-    $this->assertEqual(strcmp($target, $result), 0, t('Taxonomy vocabulary placeholder tokens replaced.'));
-
-    // Check that the results of token_generate are sanitized properly. This
-    // does NOT test the cleanliness of every token -- just that the $sanitize
-    // flag is being passed properly through the call stack and being handled
-    // correctly by a 'known' token, [term:name].
-    $edit = array();
-    $edit['name'] = '<blink>Blinking Text</blink>';
-    $this->drupalPost('taxonomy/term/' . $term2->tid . '/edit', $edit, t('Save'));
-
-    $raw_tokens = array('name' => '[term:name]');
-    $generated = token_generate('term', $raw_tokens, array('term' => $term2));
-    $this->assertEqual(strcmp($generated['[term:name]'], check_plain($term2->name)), 0, t('Token sanitized.'));
-
-    $generated = token_generate('term', $raw_tokens, array('term' => $term2), array('sanitize' => FALSE));
-    $this->assertEqual(strcmp($generated['[term:name]'], $term2->name), 0, t('Unsanitized token generated properly.'));
+    // Generate and test sanitized tokens.
+    $tests = array();
+    $tests['[term:tid]'] = $term2->tid;
+    $tests['[term:vid]'] = $term2->vid;
+    $tests['[term:name]'] = check_plain($term2->name);
+    $tests['[term:description]'] = check_markup($term2->description, $term2->format);
+    $tests['[term:url]'] = url('taxonomy/term/' . $term2->tid, array('absolute' => TRUE));
+    $tests['[term:node-count]'] = 1;
+    $tests['[term:parent:name]'] = check_plain($term1->name);
+    $tests['[term:vocabulary:name]'] = check_plain($this->vocabulary->name);
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('term' => $term2), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
+    }
+
+    // Generate and test unsanitized tokens.
+    $tests['[term:name]'] = $term2->name;
+    $tests['[term:description]'] = $term2->description;
+    $tests['[term:parent:name]'] = $term1->name;
+    $tests['[term:vocabulary:name]'] = $this->vocabulary->name;
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('term' => $term2), array('language' => $language, 'sanitize' => FALSE));
+      $this->assertFalse(strcmp($output, $expected), t('Unsanitized taxonomy term token %token replaced.', array('%token' => $input)));
+    }
+
+    // Generate and test sanitized tokens.
+    $tests = array();
+    $tests['[vocabulary:vid]'] = $this->vocabulary->vid;
+    $tests['[vocabulary:name]'] = check_plain($this->vocabulary->name);
+    $tests['[vocabulary:description]'] = filter_xss($this->vocabulary->description);
+    $tests['[vocabulary:node-count]'] = 1;
+    $tests['[vocabulary:term-count]'] = 2;
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Sanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
+    }
+
+    // Generate and test unsanitized tokens.
+    $tests['[vocabulary:name]'] = $this->vocabulary->name;
+    $tests['[vocabulary:description]'] = $this->vocabulary->description;
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $language, 'sanitize' => FALSE));
+      $this->assertFalse(strcmp($output, $expected), t('Unsanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
+    }
   }
 }
Index: modules/user/user.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.test,v
retrieving revision 1.88
diff -u -p -r1.88 user.test
--- modules/user/user.test	26 Mar 2010 22:24:31 -0000	1.88
+++ modules/user/user.test	6 Apr 2010 00:07:32 -0000
@@ -1521,3 +1521,67 @@ class UserRoleAdminTestCase extends Drup
   }
 }
 
+/**
+ * Test user token replacement in strings.
+ */
+class UserTokenReplaceTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'User token replacement',
+      'description' => 'Generates text using placeholders for dummy content to check user token replacement.',
+      'group' => 'User',
+    );
+  }
+
+  /**
+   * Creates a user, then tests the tokens generated from it.
+   */
+  function testUserTokenReplacement() {
+    global $language;
+    $url_options = array(
+      'absolute' => TRUE,
+      'language' => $language,
+    );
+
+    // Create two users and log them in one after another.
+    $user1 = $this->drupalCreateUser(array());
+    $user2 = $this->drupalCreateUser(array());
+    $this->drupalLogin($user1);
+    $this->drupalLogout();
+    $this->drupalLogin($user2);
+
+    $account = user_load($user1->uid);
+    global $user;
+
+    // Generate and test sanitized tokens.
+    $tests = array();
+    $tests['[user:uid]'] = $account->uid;
+    $tests['[user:name]'] = filter_xss($account->name);
+    $tests['[user:mail]'] = check_plain($account->mail);
+    $tests['[user:url]'] = url("user/$account->uid", $url_options);
+    $tests['[user:edit-url]'] = url("user/$account->uid/edit", $url_options);
+    $tests['[user:last-login]'] = format_date($account->login, 'medium', '', NULL, $language->language);
+    $tests['[user:last-login:short]'] = format_date($account->login, 'short', '', NULL, $language->language);
+    $tests['[user:created]'] = format_date($account->created, 'medium', '', NULL, $language->language);
+    $tests['[user:created:short]'] = format_date($account->created, 'short', '', NULL, $language->language);
+    $tests['[current-user:name]'] = check_plain($user->name);
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('user' => $account), array('language' => $language));
+      $this->assertFalse(strcmp($output, $expected), t('Sanitized user token %token replaced.', array('%token' => $input)));
+    }
+
+    // Generate and test unsanitized tokens.
+    $tests['[user:name]'] = $account->name;
+    $tests['[user:mail]'] = $account->mail;
+    $tests['[current-user:name]'] = $user->name;
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('user' => $account), array('language' => $language, 'sanitize' => FALSE));
+      $this->assertFalse(strcmp($output, $expected), t('Unsanitized user token %token replaced.', array('%token' => $input)));
+    }
+  }
+}
