Index: modules/taxonomy/taxonomy.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.test,v
retrieving revision 1.68
diff -u -p -r1.68 taxonomy.test
--- modules/taxonomy/taxonomy.test	10 Jan 2010 22:56:51 -0000	1.68
+++ modules/taxonomy/taxonomy.test	12 Jan 2010 01:25:45 -0000
@@ -33,6 +33,7 @@ class TaxonomyWebTestCase extends Drupal
   function createTerm($vocabulary) {
     $term = new stdClass();
     $term->name = $this->randomName();
+    $term->description = $this->randomName();
     $term->vid = $vocabulary->vid;
     taxonomy_term_save($term);
     return $term;
@@ -962,3 +963,112 @@ class TaxonomyNodeFilterTestCase extends
     $this->assertNoText($node->title, t('Article node not found with search filtering by term 2.'));
   }
 }
+
+/**
+ * Test taxonomy token replacement in strings.
+ */
+class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Taxonomy token replacement',
+      'description' => 'Generates text using placeholders for dummy content to check taxonomy token replacement.',
+      'group' => 'Taxonomy',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access'));
+    $this->drupalLogin($this->admin_user);    
+    $this->vocabulary = $this->createVocabulary();
+    $this->langcode = LANGUAGE_NONE;
+
+    $this->instance = array(
+      'field_name' => 'taxonomy_' . $this->vocabulary->machine_name,
+      'bundle' => 'article',
+      'object_type' => 'node',
+      'widget' => array(
+        'type' => 'options_select',
+      ),
+      'display' => array(
+        'full' => array(
+          'type' => 'taxonomy_term_reference_link',
+        ),
+      ),
+    );
+    field_create_instance($this->instance);
+  }
+  
+  /**
+   * Creates some terms and a node, then tests the tokens generated from them.
+   */
+  function testTaxonomyTokenReplacement() {
+    // Create two taxonomy terms.
+    $term1 = $this->createTerm($this->vocabulary);
+    $term2 = $this->createTerm($this->vocabulary);
+
+    // Edit $term2, setting $term1 as parent.
+    $edit = array();
+    $edit['parent[]'] = array($term1->tid);
+    $this->drupalPost('taxonomy/term/' . $term2->tid . '/edit', $edit, t('Save'));
+
+    // Create node with term2.
+    $edit = array();
+    $node = $this->drupalCreateNode(array('type' => 'article'));
+    $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).
+    $source  = '[term:tid]';               // ID of term2.
+    $source .= '[term:vid]';               // Vocabulary ID of term2.
+    $source .= '[term:name]';              // Name of term2.
+    $source .= '[term:description]';       // Description of term2.
+    $source .= '[term:url]';               // URL of term2.
+    $source .= '[term:node-count]';        // # of nodes tagged with term2.
+    $source .= '[term:parent:name]';       // Name of term1 (chained).
+    $source .= '[term:vocabulary:name]';   // Name of vocabulary (chained).
+    
+    $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]';         // ID of vocabulary.
+    $source .= '[vocabulary:name]';        // Name of vocabulary.
+    $source .= '[vocabulary:description]'; // Description of vocabulary.
+    $source .= '[vocabulary:node-count]';  // # of nodes tagged with vocabulary.
+    $source .= '[vocabulary:term-count]';  // # of terms in vocabulary.
+
+    $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.'));
+  }
+}
Index: modules/taxonomy/taxonomy.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.tokens.inc,v
retrieving revision 1.5
diff -u -p -r1.5 taxonomy.tokens.inc
--- modules/taxonomy/taxonomy.tokens.inc	4 Dec 2009 16:49:47 -0000	1.5
+++ modules/taxonomy/taxonomy.tokens.inc	12 Jan 2010 01:25:46 -0000
@@ -119,12 +119,14 @@ function taxonomy_tokens($type, $tokens,
           break;
 
         case 'url':
-          $replacements[$original] = url('taxonomy/term/' . $term, array('absolute' => TRUE));
+          $replacements[$original] = url('taxonomy/term/' . $term->tid, array('absolute' => TRUE));
           break;
 
         case 'node-count':
-          $sql = "SELECT COUNT (1) FROM {taxonomy_term_node} tn WHERE tn.tid = :tid";
-          $count = db_query($sql, array(':tid' => $term->tid))->fetchField();
+          $query = db_select('taxonomy_index');
+          $query->condition('tid', $term->tid);
+          $query->addTag('term_node_count');
+          $count = $query->countQuery()->execute()->fetchField();
           $replacements[$original] = $count;
           break;
 
@@ -171,14 +173,20 @@ function taxonomy_tokens($type, $tokens,
           break;
 
         case 'term-count':
-          $sql = "SELECT COUNT (1) FROM {taxonomy_term_data} td WHERE td.vid = :vid";
-          $count = db_query($sql, array(':vid' => $vocabulary->vid))->fetchField();
+          $query = db_select('taxonomy_term_data');
+          $query->condition('vid', $vocabulary->vid);
+          $query->addTag('vocabulary_term_count');
+          $count = $query->countQuery()->execute()->fetchField();
           $replacements[$original] = $count;
           break;
 
         case 'node-count':
-          $sql = "SELECT COUNT (1) FROM {taxonomy_term_node} tn LEFT JOIN {taxonomy_term_data} td ON tn.tid = td.tid WHERE td.vid = :vid";
-          $count = db_query($sql, array(':vid' => $vocabulary->vid))->fetchField();
+          $query = db_select('taxonomy_index', 'ti');
+          $query->addExpression('COUNT(DISTINCT ti.nid)');
+          $query->leftJoin('taxonomy_term_data', 'td', 'ti.tid = td.tid');
+          $query->condition('td.vid', $vocabulary->vid);
+          $query->addTag('vocabulary_node_count');
+          $count = $query->execute()->fetchField();
           $replacements[$original] = $count;
           break;
       }
