Index: pathauto.info
===================================================================
RCS file: /cvs/drupal/contributions/modules/pathauto/pathauto.info,v
retrieving revision 1.5
diff -u -r1.5 pathauto.info
--- pathauto.info	21 Mar 2009 00:24:28 -0000	1.5
+++ pathauto.info	4 Nov 2009 22:14:24 -0000
@@ -2,6 +2,13 @@
 name = Pathauto
 description = Provides a mechanism for modules to automatically generate aliases for the content they manage.
 dependencies[] = path
-dependencies[] = token
 suggests[] = path_redirect
-core = 6.x
+core = 7.x
+
+files[] = pathauto.admin.inc
+files[] = pathauto.inc
+files[] = pathauto.module
+files[] = pathauto_node.inc
+files[] = pathauto_taxonomy.inc
+files[] = pathauto_user.inc
+files[] = pathauto.install
Index: pathauto_node.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/pathauto/pathauto_node.inc,v
retrieving revision 1.48
diff -u -r1.48 pathauto_node.inc
--- pathauto_node.inc	28 Jun 2008 15:41:15 -0000	1.48
+++ pathauto_node.inc	4 Nov 2009 22:14:24 -0000
@@ -9,7 +9,7 @@
  */
 
 /**
- * Implementation of hook_pathauto().
+ * Implement hook_pathauto().
  */
 function node_pathauto($op) {
   switch ($op) {
@@ -19,17 +19,13 @@
       $settings['token_type'] = 'node';
       $settings['groupheader'] = t('Node path settings');
       $settings['patterndescr'] = t('Default path pattern (applies to all node types with blank patterns below)');
-      $settings['patterndefault'] = t('content/[title-raw]');
+      $settings['patterndefault'] = t('content/[node:title]');
       $settings['bulkname'] = t('Bulk generate aliases for nodes that are not aliased');
       $settings['bulkdescr'] = t('Generate aliases for all existing nodes which do not already have aliases.');
 
-      $patterns = token_get_list('node');
-      foreach ($patterns as $type => $pattern_set) {
-        if ($type != 'global') {
-          foreach ($pattern_set as $pattern => $description) {
-            $settings['placeholders']['['. $pattern .']'] = $description;
-          }
-        }
+      $tokens = token_info();
+      foreach ($tokens['tokens']['node'] as $key => $info) {
+        $settings['tokens']["[node:$key]"] = $info['description'];
       }
       $settings['supportsfeeds'] = 'feed';
 
@@ -39,15 +35,15 @@
       else {
         $languages = array();
       }
-      foreach (node_get_types('names') as $node_type => $node_name) {
-        if (variable_get('language_content_type_'. $node_type, 0) && count($languages)) {
+      foreach (node_type_get_names() as $node_type => $node_name) {
+        if (variable_get('language_content_type_' . $node_type, 0) && count($languages)) {
           $settings['patternitems'][$node_type] = t('Default path pattern for @node_type (applies to all @node_type node types with blank patterns below)', array('@node_type' => $node_name));
           foreach ($languages as $lang_code => $lang_name) {
             if (!empty($lang_code)) {
-              $settings['patternitems'][$node_type .'_'. $lang_code] = t('Pattern for all @node_type paths in @language', array('@node_type' => $node_name, '@language' => $lang_name));
+              $settings['patternitems'][$node_type . '_' . $lang_code] = t('Pattern for all @node_type paths in @language', array('@node_type' => $node_name, '@language' => $lang_name));
             }
             else {
-              $settings['patternitems'][$node_type .'_'. $lang_code] = t('Pattern for all language neutral @node_type paths', array('@node_type' => $node_name));
+              $settings['patternitems'][$node_type . '_' . $lang_code] = t('Pattern for all language neutral @node_type paths', array('@node_type' => $node_name));
             }
           }
         }
@@ -70,7 +66,7 @@
 
   // If there's a default pattern we assume all types might be updated.
   if (trim(variable_get('pathauto_node_pattern', ''))) {
-    $pattern_types = array_keys(node_get_types('names'));
+    $pattern_types = array_keys(node_type_get_types());
   }
   else {
     // Check first for a node specific pattern...
@@ -78,15 +74,15 @@
     if (module_exists('locale')) {
       $languages = array('' => t('Language neutral')) + locale_language_list('name');
     }
-    foreach (array_keys(node_get_types('names')) as $type) {
-      if (trim(variable_get('pathauto_node_'. $type .'_pattern', ''))) {
+    foreach (array_keys(node_type_get_types()) as $type) {
+      if (trim(variable_get('pathauto_node_' . $type . '_pattern', ''))) {
         $pattern_types[$type] = $type;
         continue;
       }
       // ...then for a node-language pattern.
-      if (variable_get('language_content_type_'. $type, 0) && $languages) {
+      if (variable_get('language_content_type_' . $type, 0) && $languages) {
         foreach ($languages as $lang_code => $lang_name) {
-          if (trim(variable_get('pathauto_node_'. $type .'_'. $lang_code .'_pattern', ''))) {
+          if (trim(variable_get('pathauto_node_' . $type . '_' . $lang_code . '_pattern', ''))) {
             $pattern_types[$type] = $type;
             continue 2;
           }
@@ -97,22 +93,22 @@
 
   $count = 0;
   if (count($pattern_types)) {
-    $query = "SELECT n.nid, n.vid, n.type, n.title, n.uid, n.created, n.language, alias.src, alias.dst FROM {node} n LEFT JOIN {url_alias} alias ON CONCAT('node/', CAST(n.nid AS CHAR)) = alias.src WHERE alias.src IS NULL AND n.type IN (". db_placeholders($pattern_types, 'varchar') .')';
-    $result = db_query_range($query, $pattern_types, 0, variable_get('pathauto_max_bulk_update', 50));
+    // @TODO We could rewrite this section to use node_load_multiple
+    $query = "SELECT n.nid, n.vid, n.type, n.title, n.uid, n.created, n.language, ua.source, ua.alias FROM {node} n LEFT JOIN {url_alias} ua ON CONCAT('node/', CAST(n.nid AS CHAR)) = ua.source WHERE ua.source IS NULL AND n.type IN (:pattern_types)";
+    $result = db_query_range($query, 0, variable_get('pathauto_max_bulk_update', 50), array(':pattern_types' => $pattern_types));
 
-    $placeholders = array();
-    while ($node_ref = db_fetch_object($result)) {
+    foreach ($result as $node_ref) {
       $node = node_load($node_ref->nid, NULL, TRUE);
-      $node->src = $node_ref->src;
-      $node->dst = $node_ref->dst;
+      $node->source = $node_ref->source;
+      $node->alias = $node_ref->alias;
       if (module_exists('taxonomy')) {
         // Must populate the terms for the node here for the category
         // placeholders to work
-        $node->taxonomy = array_keys(taxonomy_node_get_terms($node));
+        // @TODO Find the Drupal 7 alternative for this if necessary
+        //$node->taxonomy = array_keys(taxonomy_node_get_terms($node));
       }
-      $placeholders = pathauto_get_placeholders('node', $node);
-      $src = "node/$node->nid";
-      if (pathauto_create_alias('node', 'bulkupdate', $placeholders, $src, $node->nid, $node->type, $node->language)) {
+      $source = "node/$node->nid";
+      if (pathauto_create_alias('node', 'bulkupdate', $source, array('node' => $node), $node->type, $node->language)) {
         $count++;
       }
     }
Index: pathauto.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/pathauto/pathauto.inc,v
retrieving revision 1.54
diff -u -r1.54 pathauto.inc
--- pathauto.inc	22 Oct 2009 00:11:35 -0000	1.54
+++ pathauto.inc	4 Nov 2009 22:14:24 -0000
@@ -75,54 +75,54 @@
  * Copied from search.module's PREG_CLASS_SEARCH_EXCLUDE.
  */
 define('PREG_CLASS_ALNUM',
-'\x{0}-\x{2f}\x{3a}-\x{40}\x{5b}-\x{60}\x{7b}-\x{bf}\x{d7}\x{f7}\x{2b0}-'.
-'\x{385}\x{387}\x{3f6}\x{482}-\x{489}\x{559}-\x{55f}\x{589}-\x{5c7}\x{5f3}-'.
-'\x{61f}\x{640}\x{64b}-\x{65e}\x{66a}-\x{66d}\x{670}\x{6d4}\x{6d6}-\x{6ed}'.
-'\x{6fd}\x{6fe}\x{700}-\x{70f}\x{711}\x{730}-\x{74a}\x{7a6}-\x{7b0}\x{901}-'.
-'\x{903}\x{93c}\x{93e}-\x{94d}\x{951}-\x{954}\x{962}-\x{965}\x{970}\x{981}-'.
-'\x{983}\x{9bc}\x{9be}-\x{9cd}\x{9d7}\x{9e2}\x{9e3}\x{9f2}-\x{a03}\x{a3c}-'.
-'\x{a4d}\x{a70}\x{a71}\x{a81}-\x{a83}\x{abc}\x{abe}-\x{acd}\x{ae2}\x{ae3}'.
-'\x{af1}-\x{b03}\x{b3c}\x{b3e}-\x{b57}\x{b70}\x{b82}\x{bbe}-\x{bd7}\x{bf0}-'.
-'\x{c03}\x{c3e}-\x{c56}\x{c82}\x{c83}\x{cbc}\x{cbe}-\x{cd6}\x{d02}\x{d03}'.
-'\x{d3e}-\x{d57}\x{d82}\x{d83}\x{dca}-\x{df4}\x{e31}\x{e34}-\x{e3f}\x{e46}-'.
-'\x{e4f}\x{e5a}\x{e5b}\x{eb1}\x{eb4}-\x{ebc}\x{ec6}-\x{ecd}\x{f01}-\x{f1f}'.
-'\x{f2a}-\x{f3f}\x{f71}-\x{f87}\x{f90}-\x{fd1}\x{102c}-\x{1039}\x{104a}-'.
-'\x{104f}\x{1056}-\x{1059}\x{10fb}\x{10fc}\x{135f}-\x{137c}\x{1390}-\x{1399}'.
-'\x{166d}\x{166e}\x{1680}\x{169b}\x{169c}\x{16eb}-\x{16f0}\x{1712}-\x{1714}'.
-'\x{1732}-\x{1736}\x{1752}\x{1753}\x{1772}\x{1773}\x{17b4}-\x{17db}\x{17dd}'.
-'\x{17f0}-\x{180e}\x{1843}\x{18a9}\x{1920}-\x{1945}\x{19b0}-\x{19c0}\x{19c8}'.
-'\x{19c9}\x{19de}-\x{19ff}\x{1a17}-\x{1a1f}\x{1d2c}-\x{1d61}\x{1d78}\x{1d9b}-'.
-'\x{1dc3}\x{1fbd}\x{1fbf}-\x{1fc1}\x{1fcd}-\x{1fcf}\x{1fdd}-\x{1fdf}\x{1fed}-'.
-'\x{1fef}\x{1ffd}-\x{2070}\x{2074}-\x{207e}\x{2080}-\x{2101}\x{2103}-\x{2106}'.
-'\x{2108}\x{2109}\x{2114}\x{2116}-\x{2118}\x{211e}-\x{2123}\x{2125}\x{2127}'.
-'\x{2129}\x{212e}\x{2132}\x{213a}\x{213b}\x{2140}-\x{2144}\x{214a}-\x{2b13}'.
-'\x{2ce5}-\x{2cff}\x{2d6f}\x{2e00}-\x{3005}\x{3007}-\x{303b}\x{303d}-\x{303f}'.
-'\x{3099}-\x{309e}\x{30a0}\x{30fb}-\x{30fe}\x{3190}-\x{319f}\x{31c0}-\x{31cf}'.
-'\x{3200}-\x{33ff}\x{4dc0}-\x{4dff}\x{a015}\x{a490}-\x{a716}\x{a802}\x{a806}'.
-'\x{a80b}\x{a823}-\x{a82b}\x{d800}-\x{f8ff}\x{fb1e}\x{fb29}\x{fd3e}\x{fd3f}'.
-'\x{fdfc}-\x{fe6b}\x{feff}-\x{ff0f}\x{ff1a}-\x{ff20}\x{ff3b}-\x{ff40}\x{ff5b}-'.
+'\x{0}-\x{2f}\x{3a}-\x{40}\x{5b}-\x{60}\x{7b}-\x{bf}\x{d7}\x{f7}\x{2b0}-' .
+'\x{385}\x{387}\x{3f6}\x{482}-\x{489}\x{559}-\x{55f}\x{589}-\x{5c7}\x{5f3}-' .
+'\x{61f}\x{640}\x{64b}-\x{65e}\x{66a}-\x{66d}\x{670}\x{6d4}\x{6d6}-\x{6ed}' .
+'\x{6fd}\x{6fe}\x{700}-\x{70f}\x{711}\x{730}-\x{74a}\x{7a6}-\x{7b0}\x{901}-' .
+'\x{903}\x{93c}\x{93e}-\x{94d}\x{951}-\x{954}\x{962}-\x{965}\x{970}\x{981}-' .
+'\x{983}\x{9bc}\x{9be}-\x{9cd}\x{9d7}\x{9e2}\x{9e3}\x{9f2}-\x{a03}\x{a3c}-' .
+'\x{a4d}\x{a70}\x{a71}\x{a81}-\x{a83}\x{abc}\x{abe}-\x{acd}\x{ae2}\x{ae3}' .
+'\x{af1}-\x{b03}\x{b3c}\x{b3e}-\x{b57}\x{b70}\x{b82}\x{bbe}-\x{bd7}\x{bf0}-' .
+'\x{c03}\x{c3e}-\x{c56}\x{c82}\x{c83}\x{cbc}\x{cbe}-\x{cd6}\x{d02}\x{d03}' .
+'\x{d3e}-\x{d57}\x{d82}\x{d83}\x{dca}-\x{df4}\x{e31}\x{e34}-\x{e3f}\x{e46}-' .
+'\x{e4f}\x{e5a}\x{e5b}\x{eb1}\x{eb4}-\x{ebc}\x{ec6}-\x{ecd}\x{f01}-\x{f1f}' .
+'\x{f2a}-\x{f3f}\x{f71}-\x{f87}\x{f90}-\x{fd1}\x{102c}-\x{1039}\x{104a}-' .
+'\x{104f}\x{1056}-\x{1059}\x{10fb}\x{10fc}\x{135f}-\x{137c}\x{1390}-\x{1399}' .
+'\x{166d}\x{166e}\x{1680}\x{169b}\x{169c}\x{16eb}-\x{16f0}\x{1712}-\x{1714}' .
+'\x{1732}-\x{1736}\x{1752}\x{1753}\x{1772}\x{1773}\x{17b4}-\x{17db}\x{17dd}' .
+'\x{17f0}-\x{180e}\x{1843}\x{18a9}\x{1920}-\x{1945}\x{19b0}-\x{19c0}\x{19c8}' .
+'\x{19c9}\x{19de}-\x{19ff}\x{1a17}-\x{1a1f}\x{1d2c}-\x{1d61}\x{1d78}\x{1d9b}-' .
+'\x{1dc3}\x{1fbd}\x{1fbf}-\x{1fc1}\x{1fcd}-\x{1fcf}\x{1fdd}-\x{1fdf}\x{1fed}-' .
+'\x{1fef}\x{1ffd}-\x{2070}\x{2074}-\x{207e}\x{2080}-\x{2101}\x{2103}-\x{2106}' .
+'\x{2108}\x{2109}\x{2114}\x{2116}-\x{2118}\x{211e}-\x{2123}\x{2125}\x{2127}' .
+'\x{2129}\x{212e}\x{2132}\x{213a}\x{213b}\x{2140}-\x{2144}\x{214a}-\x{2b13}' .
+'\x{2ce5}-\x{2cff}\x{2d6f}\x{2e00}-\x{3005}\x{3007}-\x{303b}\x{303d}-\x{303f}' .
+'\x{3099}-\x{309e}\x{30a0}\x{30fb}-\x{30fe}\x{3190}-\x{319f}\x{31c0}-\x{31cf}' .
+'\x{3200}-\x{33ff}\x{4dc0}-\x{4dff}\x{a015}\x{a490}-\x{a716}\x{a802}\x{a806}' .
+'\x{a80b}\x{a823}-\x{a82b}\x{d800}-\x{f8ff}\x{fb1e}\x{fb29}\x{fd3e}\x{fd3f}' .
+'\x{fdfc}-\x{fe6b}\x{feff}-\x{ff0f}\x{ff1a}-\x{ff20}\x{ff3b}-\x{ff40}\x{ff5b}-' .
 '\x{ff65}\x{ff70}\x{ff9e}\x{ff9f}\x{ffe0}-\x{fffd}');
 
 /**
  * Check to see if there is already an alias pointing to a different item.
  *
  * @param $alias
- *   A string alias (i.e. dst).
- * @param $src
+ *   A string alias.
+ * @param $source
  *   A string that is the internal path.
  * @param $language
  *   A string indicating the path's language.
  * @return
  *   TRUE if an alias exists, FALSE if not.
  */
-function _pathauto_alias_exists($alias, $src, $language = '') {
-  $alias_pid = db_result(db_query_range("SELECT pid FROM {url_alias} WHERE dst = '%s' AND src <> '%s' AND language = '%s'", array($alias, $src, $language), 0, 1));
+function _pathauto_alias_exists($alias, $source, $language = '') {
+  $alias_pid = db_query_range("SELECT pid FROM {url_alias} WHERE alias = :alias AND source <> :source AND language = :lang", 0, 1, array(':alias' => $alias, ':source' => $source, ':lang' => $language))->fetchColumn();
   if (function_exists('path_redirect_delete')) {
     // Delete from path_redirect the exact same alias to the same node.
-    path_redirect_delete(array('path' => $alias, 'redirect' => $src));
+    path_redirect_delete(array('path' => $alias, 'redirect' => $source));
 
     // If there still is this alias used in path_redirect, then create a different alias
-    $redirect_rid = db_result(db_query_range("SELECT rid FROM {path_redirect} WHERE path = '%s'", $alias, 0, 1));
+    $redirect_rid = db_query_range("SELECT rid FROM {path_redirect} WHERE path = :path", 0, 1, array(':path' => $alias))->fetchColumn();
   }
   if ($alias_pid || !empty($redirect_rid)) {
     return TRUE;
@@ -142,13 +142,13 @@
  *   An array with the keys "pid" and "old_alias" containing
  *   the "pid" and old "alias", respectively, of the old alias.
  */
-function _pathauto_existing_alias_data($src) {
+function _pathauto_existing_alias_data($source) {
   $output = array(
     'pid' => '',
     'old_alias' => ''
   );
-  $result = db_query("SELECT pid, dst FROM {url_alias} WHERE src='%s'", $src);
-  if ($data = db_fetch_object($result)) {
+  $result = db_query("SELECT pid, alias FROM {url_alias} WHERE source = :source", array(':source' => $source));
+  if (($data = $result->fetchObject()) !== FALSE) {
     // The item is already aliased, check what to do...
     switch (variable_get('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE)) {
       // Replace old alias - remember the pid to update
@@ -157,7 +157,7 @@
         $output['pid'] = $data->pid;
       // Add new alias in addition to old one
       case PATHAUTO_UPDATE_ACTION_LEAVE:
-        $output['old_alias'] = $data->dst;
+        $output['old_alias'] = $data->alias;
         break;
       // Do nothing
       case PATHAUTO_UPDATE_ACTION_NO_NEW:
@@ -193,7 +193,7 @@
   $output = $string;
   $punctuation = pathauto_punctuation_chars();
   foreach ($punctuation as $name => $details) {
-    $action = variable_get('pathauto_punctuation_'. $name, PATHAUTO_PUNCTUATION_REMOVE);
+    $action = variable_get('pathauto_punctuation_' . $name, PATHAUTO_PUNCTUATION_REMOVE);
     if ($action != PATHAUTO_PUNCTUATION_DO_NOTHING) {
       // Slightly tricky inline if which either replaces with the separator or nothing
       $output = str_replace($details['value'], ($action ? $separator : ''), $output);
@@ -222,7 +222,7 @@
   }
 
   // Get rid of words that are on the ignore list
-  $ignore_re = '\b'. preg_replace('/,/', '\b|\b', variable_get('pathauto_ignore_words', $ignore_words)) .'\b';
+  $ignore_re = '\b' . preg_replace('/,/', '\b|\b', variable_get('pathauto_ignore_words', $ignore_words)) . '\b';
 
   if (function_exists('mb_eregi_replace')) {
     $output = mb_eregi_replace($ignore_re, '', $output);
@@ -237,11 +237,11 @@
   // In preparation for pattern matching,
   // escape the separator if and only if it is not alphanumeric.
   if (isset($separator)) {
-    if (preg_match('/^[^'. PREG_CLASS_ALNUM .']+$/uD', $separator)) {
+    if (preg_match('/^[^' . PREG_CLASS_ALNUM . ']+$/uD', $separator)) {
       $seppattern = $separator;
     }
     else {
-      $seppattern = '\\'. $separator;
+      $seppattern = '\\' . $separator;
     }
     // Trim any leading or trailing separators.
     $output = preg_replace("/^$seppattern+|$seppattern+$/", '', $output);
@@ -273,22 +273,22 @@
  * @param $op
  *   Operation being performed on the content being aliased
  *   ('insert', 'update', 'return', or 'bulkupdate').
- * @param $placeholders
- *   An array whose keys consist of the translated placeholders
- *   which appear in patterns (e.g., t('[title]')) and values are the
- *   actual values to be substituted into the pattern (e.g., $node->title).
- * @param $src
+ * @param $source
  *   The "real" URI of the content to be aliased (e.g., "node/$node->nid").
+ * @param $data
+ *   An associative array of objects needed to apply the alias' pattern, keyed
+ *   by their type.  Example: array('term' => $term, 'vocabulary' => $vocabulary).
  * @param $type
  *   For modules which provided pattern items in hook_pathauto(),
  *   the relevant identifier for the specific item to be aliased
  *   (e.g., $node->type).
  * @param $language
  *   A string specify the path's language.
+ *
  * @return
  *   The alias that was created.
  */
-function pathauto_create_alias($module, $op, $placeholders, $src, $entity_id, $type = NULL, $language = '') {
+function pathauto_create_alias($module, $op, $source, $data, $type = NULL, $language = '') {
   if (($op != 'bulkupdate') and variable_get('pathauto_verbose', FALSE) && user_access('notify of path changes')) {
     $verbose = TRUE;
   }
@@ -298,28 +298,28 @@
 
   // Retrieve and apply the pattern for this content type
   if (!empty($type)) {
-    $pattern = trim(variable_get('pathauto_'. $module .'_'. $type .'_'. $language .'_pattern', ''));
+    $pattern = trim(variable_get('pathauto_' . $module . '_' . $type . '_' . $language . '_pattern', ''));
     if (empty($pattern)) {
-      $pattern = trim(variable_get('pathauto_'. $module .'_'. $type .'_pattern', ''));
+      $pattern = trim(variable_get('pathauto_' . $module . '_' . $type . '_pattern', ''));
     }
   }
   if (empty($pattern)) {
-    $pattern = trim(variable_get('pathauto_'. $module .'_pattern', ''));
+    $pattern = trim(variable_get('pathauto_' . $module . '_pattern', ''));
   }
   // No pattern? Do nothing (otherwise we may blow away existing aliases...)
   if (empty($pattern)) {
     return '';
   }
 
-  if ($module == 'taxonomy') {
+  if ($module == 'taxonomy' && isset($data['term'])) {
     // Get proper path for term.
-    $term_path = taxonomy_term_path(taxonomy_get_term($entity_id));
-    if ($term_path != $src) {
+    $term_path = 'taxonomy/term/' . $data['term']->tid;
+    if ($term_path != $source) {
       // Quietly alias 'taxonomy/term/[tid]' with proper path for term.
-      $update_data = _pathauto_existing_alias_data($src);
-      _pathauto_set_alias($src, $term_path, $module, $entity_id, $update_data['pid'], FALSE, $update_data['old_alias'], $language);
+      $update_data = _pathauto_existing_alias_data($source);
+      _pathauto_set_alias($source, $term_path, $update_data['pid'], FALSE, $update_data['old_alias'], $language);
       // Set $src as proper path.
-      $src = $term_path;
+      $source = $term_path;
     }
   }
 
@@ -331,13 +331,13 @@
       // Do nothing
       return '';
     }
-    $update_data = _pathauto_existing_alias_data($src);
+    $update_data = _pathauto_existing_alias_data($source);
     $pid = $update_data['pid'];
     $old_alias = $update_data['old_alias'];
   }
 
-  // Replace the placeholders with the values provided by the module.
-  $alias = str_replace($placeholders['tokens'], $placeholders['values'], $pattern);
+  // Replace any tokens in the pattern.  Uses callback option to clean replacements. No sanitization.
+  $alias = token_replace($pattern, $data, array('sanitize' => FALSE, 'callback' => 'pathauto_clean_token_values', 'language' => (object)array('language' => $language)));
 
   // Two or more slashes should be collapsed into one
   $alias = preg_replace('/\/+/', '/', $alias);
@@ -353,9 +353,9 @@
   $alias = _pathauto_truncate_chars($alias, $maxlength, $separator);
 
   // If the alias already exists, generate a new, hopefully unique, variant
-  if (_pathauto_alias_exists($alias, $src, $language)) {
+  if (_pathauto_alias_exists($alias, $source, $language)) {
     $original_alias = $alias;
-    for ($i = 0; _pathauto_alias_exists(drupal_substr($alias, 0, $maxlength - strlen($i)) . $separator . $i, $src, $language); $i++) {
+    for ($i = 0; _pathauto_alias_exists(drupal_substr($alias, 0, $maxlength - drupal_strlen($i)) . $separator . $i, $source, $language); $i++) {
     }
     // Make room for the sequence number
     $alias = drupal_substr($alias, 0, $maxlength - drupal_strlen($i));
@@ -374,21 +374,22 @@
 
   // If $pid is NULL, a new alias is created - otherwise, the existing
   // alias for the designated src is replaced
-  _pathauto_set_alias($src, $alias, $module, $entity_id, $pid, $verbose, $old_alias, $language);
+  _pathauto_set_alias($source, $alias, $pid, $verbose, $old_alias, $language);
 
   // Also create a related feed alias if requested, and if supported
   // by the module
-  if (drupal_strlen(variable_get('pathauto_'. $module .'_applytofeeds', ''))) {
-    $feedappend = variable_get('pathauto_'. $module .'_applytofeeds', '');
+  if (drupal_strlen(variable_get('pathauto_' . $module . '_applytofeeds', ''))) {
+    $feedappend = variable_get('pathauto_' . $module . '_applytofeeds', '');
 
     // For forums and taxonomies, the src doesn't always form the base of the rss feed (ie. image galleries)
-    if ($module == 'taxonomy' || $module == 'forum') {
-      $update_data = _pathauto_existing_alias_data("taxonomy/term/$entity_id/$feedappend");
-      _pathauto_set_alias("taxonomy/term/$entity_id/$feedappend", "$alias/feed", $module, $entity_id, $update_data['pid'], $verbose, $update_data['old_alias'], $language);
+    if (($module == 'taxonomy' || $module == 'forum') && isset($data['term'])) {
+      $tid = $data['term']->tid;
+      $update_data = _pathauto_existing_alias_data("taxonomy/term/$tid/$feedappend");
+      _pathauto_set_alias("taxonomy/term/$tid/$feedappend", "$alias/feed", $update_data['pid'], $verbose, $update_data['old_alias'], $language);
     }
     else {
-      $update_data = _pathauto_existing_alias_data("$src/$feedappend");
-      _pathauto_set_alias("$src/$feedappend", "$alias/feed", $module, $entity_id, $update_data['pid'], $verbose, $update_data['old_alias'], $language);
+      $update_data = _pathauto_existing_alias_data("$source/$feedappend");
+      _pathauto_set_alias("$source/$feedappend", "$alias/feed", $update_data['pid'], $verbose, $update_data['old_alias'], $language);
     }
   }
 
@@ -416,9 +417,9 @@
 /**
  * Private function for Pathauto to create an alias.
  *
- * @param $src
+ * @param $source
  *   The internal path.
- * @param $dst
+ * @param $alias
  *   The visible externally used path.
  * @param $pid
  *   If the item is currently aliased, the pid for that item.
@@ -429,90 +430,71 @@
  * @param $language
  *   The path's language.
  */
-function _pathauto_set_alias($src, $dst, $entity_type, $entity_id, $pid = NULL, $verbose = FALSE, $old_alias = NULL, $language = '') {
+function _pathauto_set_alias($source, $alias, $pid = NULL, $verbose = FALSE, $old_alias = NULL, $language = '') {
   // Alert users that an existing callback cannot be overridden automatically
-  if (_pathauto_path_is_callback($dst)) {
+  if (_pathauto_path_is_callback($alias)) {
     if ($verbose && user_access('notify of path changes')) {
-      drupal_set_message(t('Ignoring alias %dst due to existing path conflict.', array('%dst' => $dst)));
+      drupal_set_message(t('Ignoring alias %alias due to existing path conflict.', array('%alias' => $alias)));
     }
     return;
   }
   // Alert users if they are trying to create an alias that is the same as the internal path
-  if ($src == $dst) {
+  if ($source == $alias) {
     if ($verbose && user_access('notify of path changes')) {
-      drupal_set_message(t('Ignoring alias %dst because it is the same as the internal path.', array('%dst' => $dst)));
+      drupal_set_message(t('Ignoring alias %alias because it is the same as the internal path.', array('%alias' => $alias)));
     }
     return;
   }
 
   // Skip replacing the current alias with an identical alias
-  if ($old_alias != $dst) {
-    path_set_alias($src, $dst, $pid, $language);
+  if ($old_alias != $alias) {
+    $path = array('source' => $source, 'alias' => $alias, 'pid' => $pid, 'language' => $language);
+    path_save($path);
 
     if (variable_get('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE) == PATHAUTO_UPDATE_ACTION_REDIRECT
         && function_exists('path_redirect_save')) {
       if (!empty($old_alias)) {
         $redirect = array(
           'path' => $old_alias,
-          'redirect' => $src,
+          'redirect' => $source,
         );
         path_redirect_save($redirect);
       }
     }
     if ($verbose && user_access('notify of path changes')) {
       if (!empty($redirect)) {
-        drupal_set_message(t('Created new alias %dst for %src, replacing %old_alias. %old_alias now redirects to %dst.', array('%dst' => $dst, '%src' => $src, '%old_alias' => $old_alias)));
+        drupal_set_message(t('Created new alias %alias for %source, replacing %old_alias. %old_alias now redirects to %alias.', array('%alias' => $alias, '%source' => $source, '%old_alias' => $old_alias)));
       }
       elseif ($pid) {
-        drupal_set_message(t('Created new alias %dst for %src, replacing %old_alias.', array('%dst' => $dst, '%src' => $src, '%old_alias' => $old_alias)));
+        drupal_set_message(t('Created new alias %alias for %source, replacing %old_alias.', array('%alias' => $alias, '%source' => $source, '%old_alias' => $old_alias)));
       }
       else {
-        drupal_set_message(t('Created new alias %dst for %src.', array('%dst' => $dst, '%src' => $src)));
+        drupal_set_message(t('Created new alias %alias for %source.', array('%alias' => $alias, '%source' => $source)));
       }
     }
   }
 }
 
 /**
- * Generalized function to get tokens across all Pathauto types.
- *
- * @param $object
- *   A user, node, or category object.
- * @return
- *   Tokens for that object formatted in the way that
- *   Pathauto expects to see them.
- */
-function pathauto_get_placeholders($type, $object) {
-  if (function_exists('token_get_values')) {
-    $full = token_get_values($type, $object, TRUE);
-    $tokens = token_prepare_tokens($full->tokens);
-    $values = pathauto_clean_token_values($full);
-    return array('tokens' => $tokens, 'values' => $values);
-  }
-  // TODO at some point try removing this and see if install profiles have problems again.
-  watchdog('Pathauto', 'It appears that you have installed Pathauto, which depends on Token, but Token is either not installed or not installed properly.');
-  return array('tokens' => array(), 'values' => array());
-}
-
-/**
  * Clean tokens so they are URL friendly.
  *
- * @param $full
- *   An array of token values that need to be "cleaned" for use in the URL.
- * @return
- *   An array of the cleaned tokens.
+ * @param $replacements
+ *   An array of token replacements that need to be "cleaned" for use in the URL.
+ * @param $data
+ *   An array of objects used to generate the replacements.
+ * @param $options
+ *   An array of options used to generate the replacements.
  */
-function pathauto_clean_token_values($full) {
-  foreach ($full->values as $key => $value) {
+function pathauto_clean_token_values(&$replacements, $data = array(), $options = array()) {
+  foreach ($replacements as $token => $value) {
     // If it's a "path" or "url friendly" token don't remove the "/" character
-    if (drupal_substr($full->tokens[$key], -4, 4) === 'path' || drupal_substr($full->tokens[$key], -8, 8) === 'path-raw' || drupal_substr($full->tokens[$key], -5, 5) === 'alias') {
-      $full->values[$key] = pathauto_cleanstring($value, FALSE);
+    if (drupal_substr($token, -4, 4) === 'path' || drupal_substr($token, -5, 5) === 'alias') {
+      $replacements[$token] = pathauto_cleanstring($value, FALSE);
     }
     else {
-      $full->values[$key] = pathauto_cleanstring($value);
+      $replacements[$token] = pathauto_cleanstring($value);
     }
   }
-  return $full->values;
 }
 
 /**
@@ -582,7 +564,7 @@
   if (drupal_strlen($string) > $length) {
     $string = drupal_substr($string, 0, $length + 1); // leave one more character
     if ($last_break = strrpos($string, $separator)) { // space exists AND is not on position 0
-      $string = substr($string, 0, $last_break);
+      $string = drupal_substr($string, 0, $last_break);
     }
     else {
       $string = drupal_substr($string, 0, $length);
Index: pathauto_user.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/pathauto/pathauto_user.inc,v
retrieving revision 1.31
diff -u -r1.31 pathauto_user.inc
--- pathauto_user.inc	11 Jul 2008 20:01:22 -0000	1.31
+++ pathauto_user.inc	4 Nov 2009 22:14:24 -0000
@@ -9,7 +9,7 @@
  */
 
 /**
- * Implementation of hook_pathauto() for user aliases.
+ * Implement hook_pathauto() for user aliases.
  */
 function user_pathauto($op) {
   switch ($op) {
@@ -19,14 +19,11 @@
       $settings['token_type'] = 'user';
       $settings['groupheader'] = t('User path settings');
       $settings['patterndescr'] = t('Pattern for user account page paths');
-      $settings['patterndefault'] = t('users/[user-raw]');
-      $patterns = token_get_list('user');
-      foreach ($patterns as $type => $pattern_set) {
-        if ($type != 'global') {
-          foreach ($pattern_set as $pattern => $description) {
-            $settings['placeholders']['['. $pattern .']'] = $description;
-          }
-        }
+      $settings['patterndefault'] = t('users/[user:name]');
+
+      $tokens = token_info();
+      foreach ($tokens['tokens']['user'] as $key => $info) {
+        $settings['tokens']["[user:$key]"] = $info['description'];
       }
 
       $settings['bulkname'] = t('Bulk generate aliases for users that are not aliased');
@@ -38,7 +35,7 @@
 }
 
 /**
- * Implementation of hook_pathauto() for blog aliases.
+ * Implement hook_pathauto() for blog aliases.
  */
 function blog_pathauto($op) {
   switch ($op) {
@@ -48,11 +45,13 @@
       $settings['token_type'] = 'user';
       $settings['groupheader'] = t('Blog path settings');
       $settings['patterndescr'] = t('Pattern for blog page paths');
-      $settings['patterndefault'] = t('blogs/[user-raw]');
-      $patterns = token_get_list('user');
-      foreach ($patterns['user'] as $pattern => $description) {
-        $settings['placeholders']['['. $pattern .']'] = $description;
+      $settings['patterndefault'] = t('blogs/[user:name]');
+
+      $tokens = token_info();
+      foreach ($tokens['tokens']['user'] as $key => $info) {
+        $settings['tokens']["[user:$key]"] = $info['description'];
       }
+
       $settings['supportsfeeds'] = 'feed';
       $settings['bulkname'] = t('Bulk generate aliases for blogs that are not aliased');
       $settings['bulkdescr'] = t('Generate aliases for all existing blog pages which do not already have aliases.');
@@ -63,7 +62,7 @@
 }
 
 /**
- * Implementation of hook_pathauto() for user-tracker aliases.
+ * Implement hook_pathauto() for user-tracker aliases.
  */
 function tracker_pathauto($op) {
   switch ($op) {
@@ -73,11 +72,13 @@
       $settings['token_type'] = 'user';
       $settings['groupheader'] = t('User-tracker path settings');
       $settings['patterndescr'] = t('Pattern for user-tracker page paths');
-      $settings['patterndefault'] = t('users/[user-raw]/track');
-      $patterns = token_get_list('user');
-      foreach ($patterns['user'] as $pattern => $description) {
-        $settings['placeholders']['['. $pattern .']'] = $description;
+      $settings['patterndefault'] = t('users/[user:name]/track');
+
+      $tokens = token_info();
+      foreach ($tokens['tokens']['user'] as $key => $info) {
+        $settings['tokens']["[user:$key]"] = $info['description'];
       }
+
       $settings['supportsfeeds'] = 'feed';
       $settings['bulkname'] = t('Bulk generate aliases for user-tracker paths that are not aliased');
       $settings['bulkdescr'] = t('Generate aliases for all existing user-tracker pages which do not already have aliases.');
@@ -88,7 +89,7 @@
 }
 
 /**
- * Implementation of hook_pathauto() for contact form aliases.
+ * Implement hook_pathauto() for contact form aliases.
  */
 function contact_pathauto($op) {
   switch ($op) {
@@ -98,11 +99,13 @@
       $settings['token_type'] = 'user';
       $settings['groupheader'] = t('User contact form path settings');
       $settings['patterndescr'] = t('Pattern for the user contact form paths');
-      $settings['patterndefault'] = t('users/[user-raw]/contact');
-      $patterns = token_get_list('user');
-      foreach ($patterns['user'] as $pattern => $description) {
-        $settings['placeholders']['['. $pattern .']'] = $description;
+      $settings['patterndefault'] = t('users/[user:name]/contact');
+
+      $tokens = token_info();
+      foreach ($tokens['tokens']['user'] as $key => $info) {
+        $settings['tokens']["[user:$key]"] = $info['description'];
       }
+
       $settings['bulkname'] = t('Bulk generate aliases for user contact form paths that are not aliased');
       $settings['bulkdescr'] = t('Generate aliases for all existing user contact form pages which do not already have aliases.');
       return (object) $settings;
@@ -115,15 +118,14 @@
  * Bulk generate aliases for all users without aliases.
  */
 function user_pathauto_bulkupdate() {
-  $query = "SELECT uid, name, src, dst FROM {users} LEFT JOIN {url_alias} ON CONCAT('user/', CAST(uid AS CHAR)) = src WHERE uid > 0 AND src IS NULL";
+  $query = "SELECT uid, name, source, alias FROM {users} LEFT JOIN {url_alias} ON CONCAT('user/', CAST(uid AS CHAR)) = source WHERE uid > 0 AND source IS NULL";
   $result = db_query_range($query, 0, variable_get('pathauto_max_bulk_update', 50));
 
   $count = 0;
   $placeholders = array();
-  while ($user = db_fetch_object($result)) {
-    $placeholders = pathauto_get_placeholders('user', $user);
-    $src = 'user/'. $user->uid;
-    if (pathauto_create_alias('user', 'bulkupdate', $placeholders, $src, $user->uid)) {
+  foreach ($result as $user) {
+    $source = 'user/' . $user->uid;
+    if (pathauto_create_alias('user', 'bulkupdate', $source, array('user' => $user))) {
       $count++;
     }
   }
@@ -137,15 +139,13 @@
  * Bulk generate aliases for all blogs without aliases.
  */
 function blog_pathauto_bulkupdate() {
-  $query = "SELECT uid, name, src, dst FROM {users} LEFT JOIN {url_alias} ON CONCAT('blog/', CAST(uid AS CHAR)) = src WHERE uid > 0 AND src IS NULL";
+  $query = "SELECT uid, name, source, alias FROM {users} LEFT JOIN {url_alias} ON CONCAT('blog/', CAST(uid AS CHAR)) = source WHERE uid > 0 AND source IS NULL";
   $result = db_query_range($query, 0, variable_get('pathauto_max_bulk_update', 50));
 
   $count = 0;
-  $placeholders = array();
-  while ($user = db_fetch_object($result)) {
-    $placeholders = pathauto_get_placeholders('user', $user);
-    $src = 'blog/'. $user->uid;
-    if (pathauto_create_alias('blog', 'bulkupdate', $placeholders, $src, $user->uid)) {
+  foreach ($result as $user) {
+    $source = 'blog/' . $user->uid;
+    if (pathauto_create_alias('blog', 'bulkupdate', $source, array('user' => $user))) {
       $count++;
     }
   }
@@ -161,15 +161,13 @@
 function tracker_pathauto_bulkupdate() {
   // We do the double CONCAT because Pgsql8.1 doesn't support more than three arguments to CONCAT
   // Hopefully some day we can remove that.
-  $query = "SELECT uid, name, src, dst FROM {users} LEFT JOIN {url_alias} ON CONCAT(CONCAT('user/', CAST(uid AS CHAR)), '/track') = src WHERE uid > 0 AND src IS NULL";
+  $query = "SELECT uid, name, source, alias FROM {users} LEFT JOIN {url_alias} ON CONCAT(CONCAT('user/', CAST(uid AS CHAR)), '/track') = source WHERE uid > 0 AND source IS NULL";
   $result = db_query_range($query, 0, variable_get('pathauto_max_bulk_update', 50));
 
   $count = 0;
-  $placeholders = array();
-  while ($user = db_fetch_object($result)) {
-    $placeholders = pathauto_get_placeholders('user', $user);
-    $src = 'user/'. $user->uid .'/track';
-    if (pathauto_create_alias('tracker', 'bulkupdate', $placeholders, $src, $user->uid)) {
+  foreach ($result as $user) {
+    $source = 'user/' . $user->uid . '/track';
+    if (pathauto_create_alias('tracker', 'bulkupdate', $source, array('user' => $user))) {
       $count++;
     }
   }
@@ -183,15 +181,13 @@
  * Bulk generate aliases for all users without aliases
  */
 function contact_pathauto_bulkupdate() {
-  $query = "SELECT uid, name, src, dst FROM {users} LEFT JOIN {url_alias} ON CONCAT(CONCAT('user/', CAST(uid AS CHAR)), '/contact') = src WHERE uid > 0 AND src IS NULL";
+  $query = "SELECT uid, name, source, alias FROM {users} LEFT JOIN {url_alias} ON CONCAT(CONCAT('user/', CAST(uid AS CHAR)), '/contact') = source WHERE uid > 0 AND source IS NULL";
   $result = db_query_range($query, 0, variable_get('pathauto_max_bulk_update', 50));
 
   $count = 0;
-  $placeholders = array();
-  while ($user = db_fetch_object($result)) {
-    $placeholders = pathauto_get_placeholders('user', $user);
-    $src = 'user/'. $user->uid .'/contact';
-    if (pathauto_create_alias('contact', 'bulkupdate', $placeholders, $src, $user->uid)) {
+  foreach ($result as $user) {
+    $source = 'user/' . $user->uid . '/contact';
+    if (pathauto_create_alias('contact', 'bulkupdate', $source, array('user' => $user))) {
       $count++;
     }
   }
Index: pathauto.admin.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/pathauto/pathauto.admin.inc,v
retrieving revision 1.20
diff -u -r1.20 pathauto.admin.inc
--- pathauto.admin.inc	13 Sep 2008 08:54:06 -0000	1.20
+++ pathauto.admin.inc	4 Nov 2009 22:14:24 -0000
@@ -17,12 +17,6 @@
 function pathauto_admin_settings() {
   _pathauto_include();
 
-  // Make sure they have token
-  if (!function_exists('token_get_list')) {
-    drupal_set_message(t('It appears that the <a href="@token_link">Token module</a> is not installed. Please ensure that you have it installed so that Pathauto can work properly. You may need to disable Pathauto and re-enable it to get Token enabled.', array('@token_link' => 'http://drupal.org/project/token')), 'error');
-    return;
-  }
-
   // Default words to ignore
   $ignore_words = array(
     'a', 'an', 'as', 'at', 'before', 'but', 'by', 'for', 'from', 'is', 'in',
@@ -122,7 +116,7 @@
   $transliteration_help = t('When a pattern includes certain characters (such as those with accents) should Pathauto attempt to transliterate them into the ASCII-96 alphabet? Transliteration is handled by the Transliteration module.');
   if (!module_exists('transliteration')) {
     $disable_transliteration = TRUE;
-    $transliteration_help .= ' <strong>'. t("This option is disabled on your site because the Transliteration module either isn't installed, or isn't installed properly.") .'</strong>';
+    $transliteration_help .= ' <strong>' . t("This option is disabled on your site because the Transliteration module either isn't installed, or isn't installed properly.") . '</strong>';
     // Make sure we don't try to use Transliteration if it isn't available
     variable_set('pathauto_transliterate', FALSE);
   }
@@ -160,10 +154,10 @@
 
   $punctuation = pathauto_punctuation_chars();
   foreach ($punctuation as $name => $details) {
-    $form['punctuation']['pathauto_punctuation_'. $name ] = array(
+    $form['punctuation']['pathauto_punctuation_' . $name ] = array(
       '#type' => 'select',
       '#title' => $details['name'],
-      '#default_value' => variable_get('pathauto_punctuation_'. $name, PATHAUTO_PUNCTUATION_REMOVE),
+      '#default_value' => variable_get('pathauto_punctuation_' . $name, PATHAUTO_PUNCTUATION_REMOVE),
       '#options' => array(
         PATHAUTO_PUNCTUATION_REMOVE => t('Remove'),
         PATHAUTO_PUNCTUATION_REPLACE => t('Replace by separator'),
@@ -184,7 +178,7 @@
     $patterndefault = $settings->patterndefault;
     $groupheader = $settings->groupheader;
     $supportsfeeds = isset($settings->supportsfeeds) ? $settings->supportsfeeds : NULL;
-    variable_set('pathauto_'. $module .'_supportsfeeds', $supportsfeeds);
+    variable_set('pathauto_' . $module . '_supportsfeeds', $supportsfeeds);
 
     $form[$module] = array(
       '#type' => 'fieldset',
@@ -196,7 +190,7 @@
     $group_weight++;
 
     // Prompt for the default pattern for this module
-    $variable = 'pathauto_'. $module .'_pattern';
+    $variable = 'pathauto_' . $module . '_pattern';
     $description = '';
     if (!count($_POST)) {
       $error = _pathauto_check_pattern(variable_get($variable, $patterndefault), $settings->token_type);
@@ -218,7 +212,7 @@
     // them up here
     if (isset($settings->patternitems)) {
       foreach ($settings->patternitems as $itemname => $itemlabel) {
-        $variable = 'pathauto_'. $module .'_'. $itemname .'_pattern';
+        $variable = 'pathauto_' . $module . '_' . $itemname . '_pattern';
         $description = '';
         if (!count($_POST)) {
           $error = _pathauto_check_pattern(variable_get($variable, $patterndefault), $settings->token_type);
@@ -240,9 +234,9 @@
     // Display the user documentation of placeholders supported by
     // this module, as a description on the last pattern
     $doc = "<dl>\n";
-    foreach ($settings->placeholders as $name => $description) {
-      $doc .= '<dt>'. $name .'</dt>';
-      $doc .= '<dd>'. $description .'</dd>';
+    foreach ($settings->tokens as $name => $description) {
+      $doc .= '<dt>' . $name . '</dt>';
+      $doc .= '<dd>' . $description . '</dd>';
     }
     $doc .= "</dl>\n";
     $form[$module]['token_help'] = array(
@@ -250,18 +244,17 @@
       '#type' => 'fieldset',
       '#collapsible' => TRUE,
       '#collapsed' => TRUE,
-      '#description' => t('Use -raw replacements for text to avoid problems with HTML entities.'),
     );
     $form[$module]['token_help']['help'] = array(
-      '#value' => $doc,
+      '#markup' => $doc,
     );
 
     // If the module supports bulk updates, offer the update action here
     if ($settings->bulkname) {
-      $variable = 'pathauto_'. $module .'_bulkupdate';
+      $variable = 'pathauto_' . $module . '_bulkupdate';
       if (variable_get($variable, FALSE)) {
         variable_set($variable, FALSE);
-        $function = $module .'_pathauto_bulkupdate';
+        $function = $module . '_pathauto_bulkupdate';
         call_user_func($function);
       }
       $form[$module][$variable] = array(
@@ -274,7 +267,7 @@
 
     // If the module supports feeds, offer to generate aliases for them
     if ($supportsfeeds) {
-      $variable = 'pathauto_'. $module .'_applytofeeds';
+      $variable = 'pathauto_' . $module . '_applytofeeds';
       $current = variable_get($variable, $supportsfeeds);
       // This checks for the old style from 2.0 and earlier. TODO: At some point we can drop that.
       if (is_numeric($current)) {
@@ -308,8 +301,7 @@
 /**
  * Helper function for pathauto_admin_settings().
  *
- * See if they are using all -raw tokens available
- * and if all the tokens are valid in the given context.
+ * See if all the tokens are valid in the given context.
  *
  * @param $pattern
  *   A string containing the pattern to be tested.
@@ -322,40 +314,25 @@
  */
 function _pathauto_check_pattern($pattern, $type) {
   // Hold items we've warned about so we only warn once per token
-  static $warned;
+  $warned = &drupal_static(__FUNCTION__);
   $return = FALSE;
 
   // Build up a set of all tokens in a format that's easy to search
-  $all_tokens = array();
-  $tokens = token_get_list($type);
-  foreach ($tokens as $actual_tokens) {
-    foreach (array_keys($actual_tokens) as $token) {
-      $all_tokens[$token] = $token;
-    }
-  }
+  $tokens = token_info();
+  $tokens = $tokens['tokens'][$type];
 
   // Now, search the pattern and evaluate its contents.
   $matches = array();
   if (preg_match_all('/\[[a-zA-z_\-]+?\]/i', $pattern, $matches)) {
     // Loop over each found token.
     foreach ($matches[0] as $id => $token) {
-      $token = str_replace('[', '', str_replace(']', '', $token));
+      $test = str_replace('[', '', str_replace(']', '', $token));
+      $test = drupal_substr($test, strpos($test, $type . ':'));
       // Check if the token is even valid in this context.
-      if (!array_key_exists($token, $all_tokens)) {
-        drupal_set_message(t('You are using the token [%token] which is not valid within the scope of tokens where you are using it.', array('%token' => $token)), 'error');
+      if (!array_key_exists($test, $tokens)) {
+        drupal_set_message(t('You are using the token [%token] which is not valid within the scope of tokens where you are using it.', array('%token' => $type . ':' . $test)), 'error');
         $return[] = $token;
       }
-      // Check if there is a raw companion that they should be using.
-      elseif (!preg_match('/\-raw/i', $token)) {
-        $raw_token = $token .'-raw';
-        if (array_key_exists($raw_token, $all_tokens)) {
-          if (!isset($warned) || !array_key_exists($token, $warned)) {
-            drupal_set_message(t('You are using the token [%token] which has a -raw companion available [%raw_token]. For Pathauto patterns you should use the -raw version of tokens unless you really know what you are doing. See the <a href="@pathauto-help">Pathauto help</a> for more details.', array('%token' => $token, '%raw_token' => $raw_token, '@pathauto-help' => url('admin/help/pathauto'))), 'error');
-            $warned[$token] = $token;
-          }
-          $return[] = $token;
-        }
-      }
     }
   }
 
@@ -381,7 +358,7 @@
   $punctuation = pathauto_punctuation_chars();
   foreach ($punctuation as $name => $details) {
     if ($details['value'] == $separator) {
-      $action = $form_state['values']['pathauto_punctuation_'. $name];
+      $action = $form_state['values']['pathauto_punctuation_' . $name];
       if ($action == PATHAUTO_PUNCTUATION_REMOVE) {
         drupal_set_message(t('You have configured the @name to be the separator and to be removed when encountered in strings. This can cause problems with your patterns and especially with the catpath and termpath patterns. You should probably set the action for @name to be "replace by separator".', array('@name' => $details['name'])), 'error');
       }
@@ -414,7 +391,7 @@
   );
 
   // First we do the "all" case
-  $total_count = db_result(db_query('SELECT count(1) FROM {url_alias}'));
+  $total_count = db_query('SELECT count(1) FROM {url_alias}')->fetchField();
   $form['delete']['all_aliases'] = array(
     '#type' => 'checkbox',
     '#title' => t('All aliases'),
@@ -425,7 +402,7 @@
   // Next, iterate over an array of objects/alias types which can be deleted and provide checkboxes
   $objects = module_invoke_all('path_alias_types');
   foreach ($objects as $internal_name => $label) {
-    $count = db_result(db_query("SELECT count(1) FROM {url_alias} WHERE src LIKE '%s%%'", $internal_name));
+    $count = db_query("SELECT count(1) FROM {url_alias} WHERE source LIKE :src", array(':src' => "$internal_name%"))->fetchField();
     $form['delete'][$internal_name] = array(
       '#type' => 'checkbox',
       '#title' => $label, // This label is sent through t() in the hard coded function where it is defined
@@ -435,7 +412,7 @@
   }
 
   // Warn them and give a button that shows we mean business
-  $form['warning'] = array('#value' => '<p>'. t('<strong>Note:</strong> there is no confirmation. Be sure of your action before clicking the "Delete aliases now!" button.<br />You may want to make a backup of the database and/or the url_alias table prior to using this feature.') .'</p>');
+  $form['warning'] = array('#value' => '<p>' . t('<strong>Note:</strong> there is no confirmation. Be sure of your action before clicking the "Delete aliases now!" button.<br />You may want to make a backup of the database and/or the url_alias table prior to using this feature.') . '</p>');
   $form['buttons']['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Delete aliases now!'),
@@ -456,12 +433,12 @@
       }
       $objects = module_invoke_all('path_alias_types');
       if (array_key_exists($key, $objects)) {
-        db_query("DELETE FROM {url_alias} WHERE src LIKE '%s%%'", $key);
+        db_query("DELETE FROM {url_alias} WHERE source LIKE :src", array(':src' => "$key%"));
         drupal_set_message(t('All of your %type path aliases have been deleted.', array('%type' => $objects[$key])));
       }
     }
   }
-  $form_state['redirect'] = 'admin/build/path/delete_bulk';
+  $form_state['redirect'] = 'admin/config/search/path/delete_bulk';
 }
 
 /**
@@ -486,6 +463,6 @@
     }
   }
   if ($wysiwyg_problem) {
-    return '<strong> '. t('You appear to be using a "WYSIWYG" editor which can cause problems if that editor runs on the Pathauto administrative settings page. Please be sure to disable these editors for all text boxes on this page. See the "WYSIWYG Conflicts" section of the README.txt file for more information.') .'</strong>';
+    return '<strong> ' . t('You appear to be using a "WYSIWYG" editor which can cause problems if that editor runs on the Pathauto administrative settings page. Please be sure to disable these editors for all text boxes on this page. See the "WYSIWYG Conflicts" section of the README.txt file for more information.') . '</strong>';
   }
 }
Index: pathauto_taxonomy.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/pathauto/pathauto_taxonomy.inc,v
retrieving revision 1.41
diff -u -r1.41 pathauto_taxonomy.inc
--- pathauto_taxonomy.inc	28 Aug 2008 16:14:06 -0000	1.41
+++ pathauto_taxonomy.inc	4 Nov 2009 22:14:24 -0000
@@ -9,24 +9,24 @@
  */
 
 /**
- * Implementation of hook_pathauto() for taxonomy module.
+ * Implement hook_pathauto() for taxonomy module.
  */
 function taxonomy_pathauto($op) {
   switch ($op) {
     case 'settings':
       $settings = array();
       $settings['module'] = 'taxonomy';
-      $settings['token_type'] = 'taxonomy';
+      $settings['token_type'] = 'term';
       $settings['groupheader'] = t('Taxonomy term path settings');
       $settings['patterndescr'] = t('Default path pattern (applies to all vocabularies with blank patterns below)');
-      $settings['patterndefault'] = t('category/[vocab-raw]/[catpath-raw]');
-      $patterns = token_get_list('taxonomy');
-      foreach ($patterns as $type => $pattern_set) {
-        if ($type != 'global') {
-          foreach ($pattern_set as $pattern => $description) {
-            $settings['placeholders']['['. $pattern .']'] = $description;
-          }
-        }
+      $settings['patterndefault'] = t('category/[vocabulary:title]/[term:catpath]');
+
+      $tokens = token_info();
+      foreach ($tokens['tokens']['term'] as $key => $info) {
+        $settings['tokens']["[term:$key]"] = $info['description'];
+      }
+      foreach ($tokens['tokens']['vocabulary'] as $key => $info) {
+        $settings['tokens']["[vocabulary:$key]"] = $info['description'];
       }
       $settings['supportsfeeds'] = '0/feed';
       $settings['bulkname'] = t('Bulk generate aliases for terms that are not aliased');
@@ -54,39 +54,47 @@
  * Generate aliases for all categories without aliases.
  */
 function taxonomy_pathauto_bulkupdate() {
-  // From all node types, only attempt to update those with patterns
-  $pattern_vids = array();
+  // Initialize db_or().
+  $pattern_or = db_or();
+
+  // From all node types, only attempt to update those with patterns.
   foreach (taxonomy_get_vocabularies() as $vid => $info) {
-    $pattern = trim(variable_get('pathauto_taxonomy_'. $vid .'_pattern', ''));
+    $pattern = trim(variable_get('pathauto_taxonomy_' . $vid . '_pattern', ''));
 
     // If it's not set, check the default
-    // TODO - if there's a default we shouldn't do this crazy where statement because all vocabs get aliases
-    // TODO - special casing to exclude the forum vid (and the images vid and...?)
+    // TODO - If there's a default we shouldn't do this crazy where statement because all vocabularies get aliases.
+    // TODO - Special casing to exclude the forum vid (and the images vid and...?).
     if (empty($pattern)) {
       $pattern = trim(variable_get('pathauto_taxonomy_pattern', ''));
     }
     if (!empty($pattern)) {
-      $pattern_vids[] = $vid;
-      if (empty($vid_where)) {
-        $vid_where = " AND (vid = '%s' ";
-      }
-      else {
-        $vid_where .= " OR vid = '%s'";
-      }
+      $pattern_or->condition('vid', $vid);
     }
   }
-  $vid_where .= ')';
 
-  // Exclude the forums and join all the args into one array so they can be passed to db_query
-  $forum_vid[] = variable_get('forum_nav_vocabulary', '');
-  $query_args = array_merge($forum_vid, $pattern_vids);
-  $query = "SELECT tid, vid, name, src, dst FROM {term_data} LEFT JOIN {url_alias} ON CONCAT('taxonomy/term/', CAST(tid AS CHAR)) = src WHERE src IS NULL AND vid <> %d ". $vid_where;
-  $result = db_query_range($query, $query_args, 0, variable_get('pathauto_max_bulk_update', 50));
+  // Set up the query object.
+  $query = db_select('taxonomy_term_data', 'td');
+  $query->leftJoin('url_alias', 'ua', "CONCAT('taxonomy/term/', CAST(tid AS CHAR)) = source");
+  $query->fields('td', array('tid', 'vid', 'name'));
+  $query->fields('ua', array('source', 'alias'));
+  $query->isNull('source');
+
+  // Exclude the forums.
+  $query->condition('vid', variable_get('forum_nav_vocabulary', ''), '<>');
+
+  // Add $pattern_or condition to query.
+  $query->condition($pattern_or);
+
+  // Add range to query.
+  $query->range(0, variable_get('pathauto_max_bulk_update', 50));
+
+  // Execute query.
+  $result = $query->execute();
 
   $count = 0;
   $placeholders = array();
-  while ($category = db_fetch_object($result)) {
-    $count += _taxonomy_pathauto_alias($category, 'bulkupdate');
+  foreach ($result as $term) {
+    $count += _taxonomy_pathauto_alias($term, 'bulkupdate');
   }
 
   drupal_set_message(format_plural($count,
@@ -100,22 +108,22 @@
  * @param $category
  *   A taxonomy object.
  */
-function _taxonomy_pathauto_alias($category, $op) {
+function _taxonomy_pathauto_alias($term, $op) {
   $count = 0;
-
-  $placeholders = pathauto_get_placeholders('taxonomy', $category);
+  $term = taxonomy_term_load($term->tid);
+  $vocabulary = taxonomy_vocabulary_load($term->vid);
 
   $forum_vid = variable_get('forum_nav_vocabulary', '');
   // If we're in a forum vocabulary, also create a forum container, forum, or forum topic alias.
-  if (module_exists('forum') && $forum_vid == (int)$category->vid) {
-    $src = 'forum/'. $category->tid;
-    if (pathauto_create_alias('forum', $op, $placeholders, $src, $category->tid, $category->vid)) {
+  if (module_exists('forum') && $forum_vid == (int)$term->vid) {
+    $source = 'forum/' . $term->tid;
+    if (pathauto_create_alias('forum', $op, $source, array('term' => $term, 'vocabulary' => $vocabulary), $term->vid)) {
       $count++;
     }
   }
   else {
-    $src = taxonomy_term_path($category);
-    if (pathauto_create_alias('taxonomy', $op, $placeholders, $src, $category->tid, $category->vid)) {
+    $source = 'taxonomy/term/' . $term->tid;
+    if (pathauto_create_alias('taxonomy', $op, $source, array('term' => $term, 'vocabulary' => $vocabulary), $term->vid)) {
       $count++;
     }
   }
@@ -123,8 +131,11 @@
 }
 
 /**
- * Implementation of hook_pathauto() for forum module.
+ * @TODO Port the Forum Implementation.
  */
+/**
+ * Implement hook_pathauto() for forum module.
+
 function forum_pathauto($op) {
   switch ($op) {
     case 'settings':
@@ -138,7 +149,7 @@
       foreach ($patterns as $type => $pattern_set) {
         if ($type != 'global') {
           foreach ($pattern_set as $pattern => $description) {
-            $settings['placeholders']['['. $pattern .']'] = $description;
+            $settings['placeholders']['[' . $pattern . ']'] = $description;
           }
         }
       }
@@ -149,23 +160,23 @@
     default:
       break;
   }
-}
+}*/
 
 /**
  * Generate aliases for all forums and forum containers without aliases.
- */
+
 function forum_pathauto_bulkupdate() {
   $forum_vid = variable_get('forum_nav_vocabulary', '');
-  $query = "SELECT tid, vid, name, src, dst FROM {term_data} LEFT JOIN {url_alias} ON CONCAT('forum/', CAST(tid AS CHAR)) = src WHERE vid = %d AND src IS NULL";
-  $result = db_query_range($query, $forum_vid, 0, variable_get('pathauto_max_bulk_update', 50));
+  $query = "SELECT tid, vid, name, source, alias FROM {taxonomy_term_data} LEFT JOIN {url_alias} ON CONCAT('forum/', CAST(tid AS CHAR)) = source WHERE vid = :vid AND source IS NULL";
+  $result = db_query_range($query, 0, variable_get('pathauto_max_bulk_update', 50), array(':vid' => $forum_vid));
 
   $count = 0;
   $placeholders = array();
-  while ($category = db_fetch_object($result)) {
+  foreach ($result as $category) {
     $count = _taxonomy_pathauto_alias($category, 'bulkupdate') + $count;
   }
 
   drupal_set_message(format_plural($count,
     'Bulk update of forums and forum containers completed, one alias generated.',
     'Bulk update of forums and forum containers completed, @count aliases generated.'));
-}
+}*/
Index: pathauto.install
===================================================================
RCS file: /cvs/drupal/contributions/modules/pathauto/pathauto.install,v
retrieving revision 1.14
diff -u -r1.14 pathauto.install
--- pathauto.install	17 Oct 2009 17:36:58 -0000	1.14
+++ pathauto.install	4 Nov 2009 22:14:24 -0000
@@ -9,14 +9,14 @@
  */
 
 /**
- * Implementation of hook_install().
+ * Implement hook_install().
  */
 function pathauto_install() {
   // Check to see if taxonomy module is enabled before we set those variables
   if (module_exists('taxonomy')) {
     variable_set('pathauto_modulelist', array('node', 'user', 'taxonomy'));
     variable_set('pathauto_taxonomy_supportsfeeds', '0/feed');
-    variable_set('pathauto_taxonomy_pattern', 'category/[vocab-raw]/[catpath-raw]');
+    variable_set('pathauto_taxonomy_pattern', 'category/[vocabulary:name]/[term:catpath]');
     variable_set('pathauto_taxonomy_bulkupdate', FALSE);
     variable_set('pathauto_taxonomy_applytofeeds', FALSE);
     variable_set('pathauto_taxonomy_2_pattern', '');
@@ -36,13 +36,13 @@
   variable_set('pathauto_node_forum_pattern', '');
   variable_set('pathauto_node_image_pattern', '');
   variable_set('pathauto_node_page_pattern', '');
-  variable_set('pathauto_node_pattern', 'content/[title-raw]');
+  variable_set('pathauto_node_pattern', 'content/[node:title]');
   variable_set('pathauto_node_story_pattern', '');
   variable_set('pathauto_punctuation_quotes', 0);
   variable_set('pathauto_separator', '-');
   variable_set('pathauto_update_action', '2');
   variable_set('pathauto_user_bulkupdate', FALSE);
-  variable_set('pathauto_user_pattern', 'users/[user-raw]');
+  variable_set('pathauto_user_pattern', 'users/[user:name]');
   variable_set('pathauto_user_supportsfeeds', NULL);
   variable_set('pathauto_verbose', FALSE);
   variable_set('pathauto_node_applytofeeds', '');
@@ -58,7 +58,7 @@
 }
 
 /**
- * Implementation of hook_uninstall().
+ * Implement hook_uninstall().
  */
 function pathauto_uninstall() {
   // Delete all the pathauto variables and then clear the variable cache
Index: pathauto.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/pathauto/pathauto.module,v
retrieving revision 1.126
diff -u -r1.126 pathauto.module
--- pathauto.module	17 Oct 2009 17:45:10 -0000	1.126
+++ pathauto.module	4 Nov 2009 22:14:24 -0000
@@ -19,31 +19,39 @@
  */
 
 /**
- * Implementation of hook_help().
+ * Implement hook_help().
  */
 function pathauto_help($path, $arg) {
   switch ($path) {
     case 'admin/help#pathauto':
-      $output = '<p>'. t('Provides a mechanism for modules to automatically generate aliases for the content they manage.') .'</p>';
-      $output .= '<h2>'. t('Settings') .'</h2>';
-      $output .= '<p>'. t('The <strong>maximum alias length</strong> and <strong>maximum component length</strong> values default to 100 and have a limit of 128 from Pathauto. This length is limited by the length of the "dst" column of the url_alias database table. The default database schema for this column is 128. If you set a length that is equal to that of the one set in the "dst" column it will cause problems in situations where the system needs to append additional words to the aliased URL. For example, URLs generated for feeds will have "/feed" added to the end. You should enter a value that is the length of the "dst" column minus the length of any strings that might get added to the end of the URL. The length of strings that might get added to the end of your URLs depends on which modules you have enabled and on your Pathauto settings. The recommended and default value is 100.') .'</p>';
-      $output .= '<p>'. t('<strong>Raw tokens</strong>: In Pathauto it is appropriate to use the -raw form of tokens. Paths are sent through a filtering system which ensures that raw user content is filtered. Failure to use -raw tokens can cause problems with the Pathauto punctuation filtering system.') .'</p>';
+      $output = '<p>' . t('Provides a mechanism for modules to automatically generate aliases for the content they manage.') . '</p>';
+      $output .= '<h2>' . t('Settings') . '</h2>';
+      $output .= '<p>' . t('The <strong>maximum alias length</strong> and <strong>maximum component length</strong> values default to 100 and have a limit of 128 from Pathauto. This length is limited by the length of the "alias" column of the url_alias database table. The default database schema for this column is 128. If you set a length that is equal to that of the one set in the "alias" column it will cause problems in situations where the system needs to append additional words to the aliased URL. For example, URLs generated for feeds will have "/feed" added to the end. You should enter a value that is the length of the "alias" column minus the length of any strings that might get added to the end of the URL. The length of strings that might get added to the end of your URLs depends on which modules you have enabled and on your Pathauto settings. The recommended and default value is 100.') . '</p>';
       return $output;
   }
 }
 
 /**
- * Implementation of hook_perm().
+ * Implement hook_permission().
  */
-function pathauto_perm() {
-  return array('administer pathauto', 'notify of path changes');
+function pathauto_permission() {
+  return array(
+    'administer pathauto' => array(
+      'title' => t('Administer pathauto'),
+      'description' => t('PLACEHOLDER'),
+    ),
+    'notify of path changes' => array(
+      'title' => t('Notify of Path Changes'),
+      'description' => t('PLACEHOILDER'),
+    ),
+  );
 }
 
 /**
- * Implementation of hook_menu().
+ * Implement hook_menu().
  */
 function pathauto_menu() {
-  $items['admin/build/path/pathauto'] = array(
+  $items['admin/config/search/path/pathauto'] = array(
     'title' => 'Automated alias settings',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('pathauto_admin_settings'),
@@ -54,12 +62,13 @@
     'file' => 'pathauto.admin.inc',
   );
 
-  $items['admin/build/path/delete_bulk'] = array(
+  $items['admin/config/search/path/delete_bulk'] = array(
     'title' => 'Delete aliases',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('pathauto_admin_delete'),
     'access arguments' => array('administer url aliases'),
     'type' => MENU_LOCAL_TASK,
+    'weight' => 11,
     'file' => 'pathauto.admin.inc',
   );
 
@@ -77,99 +86,119 @@
 }
 
 /**
- * Implementation of hook_token_values() for Pathauto specific tokens.
+ * Implement hook_tokens() for Pathauto specific tokens.
  */
-function pathauto_token_values($type, $object = NULL) {
+function pathauto_tokens($type, $tokens, array $data = array(), array $options = array()) {
+  $replacements = array();
   if (module_exists('taxonomy')) {
-    if ($type == 'taxonomy' || $type == 'node' || $type == 'all') {
+    if ($type == 'term' || $type == 'node') {
       _pathauto_include();
-      switch ($type) {
-        case 'node':
-          // Get the bookpathalias token
-          $values['bookpathalias'] = '';
-          // We're on a node and it's a book and it has a parent? Get the book path alias.
-          if (module_exists('book') && !empty($object->book['menu_name'])) {
-            for ($i = 1; $i < 10; $i++) {
-              $second_child = $i + 2;
-              if ($object->book["p$second_child"] === 0) {
-                $parent_node = db_result(db_query('SELECT link_path FROM {menu_links} WHERE mlid = %d', $object->book["p$i"]));
-                break;
-              }
-            }
-            $values['bookpathalias'] = drupal_get_path_alias($parent_node);
-          }
-
+      $sanitize = !empty($options['sanitize']);
+      $separator = variable_get('pathauto_separator', '-');
+      $catpath = $alias = '';
+
+      if (($type == 'node' && !empty($data['node'])) || ($type == 'term' && !empty($data['term']))) {
+        if ($type == 'node') {
+          $node = $data['node'];
           // Get taxonomy related data.
-          $vid = db_result(db_query_range("SELECT t.vid FROM {term_node} r INNER JOIN {term_data} t ON r.tid = t.tid INNER JOIN {vocabulary} v ON t.vid = v.vid WHERE r.vid = %d ORDER BY v.weight, t.weight, t.name", $object->vid, 0, 1));
-          $category = db_fetch_object(db_query_range("SELECT t.tid, t.name FROM {term_data} t INNER JOIN {term_node} r ON r.tid = t.tid WHERE t.vid = %d AND r.nid = %d ORDER BY weight", $vid, $object->nid, 0, 1));
-          $category->vid = $vid;
-          // In the realm of nodes these are terms, in the realm of Taxonomy, cats
-          $label = 'term';
-
-        case 'taxonomy':
-        default:
-          if (!isset($category)) {
-            $category = $object;
-          }
-          if (!isset($label)) {
-            $label = 'cat';
+          $vid = db_query_range("SELECT t.vid FROM {taxonomy_index} i INNER JOIN {taxonomy_term_data} t ON i.tid = t.tid INNER JOIN {taxonomy_vocabulary} v ON t.vid = v.vid WHERE i.nid = :nid ORDER BY v.weight, t.weight, t.name", 0, 1, array(':nid' => $node->nid))->fetchColumn();
+          $term = db_query_range("SELECT t.tid, t.name FROM {taxonomy_term_data} t INNER JOIN {taxonomy_index} i ON i.tid = t.tid WHERE t.vid = :vid AND i.nid = :nid ORDER BY weight", 0, 1, array(':vid' => $vid, ':nid' => $node->nid))->fetchObject();
+          $term->vid = $vid;
+        }
+        elseif ($type == 'term') {
+          $term = $data['term'];
+        }
+
+        if (isset($term->tid)) {
+          $parents = taxonomy_get_parents_all($term->tid);
+          array_shift($parents);
+          foreach ($parents as $parent) {
+            // Replace any '/' characters in individual terms which might create confusing URLs
+            $catpath = pathauto_cleanstring(check_plain(preg_replace('/\//', '', $parent->name))) . '/' . $catpath;
           }
-          if (isset($category->tid)) {
-            $separator = variable_get('pathauto_separator', '-');
-            $parents = taxonomy_get_parents_all($category->tid);
-            array_shift($parents);
-            $catpath = '';
-            $catpath_raw = '';
-            foreach ($parents as $parent) {
-              // Replace any / characters in individual terms which might create confusing URLs
-              $catpath = pathauto_cleanstring(check_plain(preg_replace('/\//', '', $parent->name))) .'/'. $catpath;
-              $catpath_raw = pathauto_cleanstring(preg_replace('/\//', '', $parent->name)) .'/'. $catpath_raw;
-            }
-            $values[$label .'path'] = $catpath .'/'. check_plain($category->name);
-            $values[$label .'path-raw'] = $catpath_raw .'/'. $category->name;
-            $values[$label .'alias'] = drupal_get_path_alias('taxonomy/term/'. $category->tid);
-            if (!strncasecmp($values[$label .'alias'], 'taxonomy', 8)) {
-              $values[$label .'alias'] = check_plain($category->name);
-            }
+
+          $catpath = $catpath . '/' . check_plain($term->name);
+          $alias = drupal_get_path_alias('taxonomy/term/' . $term->tid);
+          if (!strncasecmp($alias, 'taxonomy', 8)) {
+            $alias = check_plain($term->name);
           }
-          else { // Provide some defaults if they aren't set.
-            $values[$label .'path'] = '';
-            $values[$label .'path-raw'] = '';
-            $values[$label .'alias'] = '';
+        }
+
+        foreach ($tokens as $name => $original) {
+          switch ($name) {
+            case 'bookpathalias':
+              if (module_exists('book') && !empty($node->book['menu_name'])) {
+                for ($i = 1; $i < 10; $i++) {
+                  $second_child = $i + 2;
+                  if ($node->book["p$second_child"] === 0) {
+                    $parent_node = db_query('SELECT link_path FROM {menu_links} WHERE mlid = :mlid', array(':mlid' => $node->book["p$i"]))->fetchObject();
+                    break;
+                  }
+                }
+                $replacements[$original] = drupal_get_path_alias($parent_node);
+              }
+              else {
+                $replacements[$original] = '';
+              }
+              break;
+
+            case 'termpath':
+            case 'catpath':
+              $replacements[$original] = $catpath;
+              break;
+
+            case 'termalias':
+            case 'catalias':
+              $replacements[$original] = $alias;
+              break;
           }
+        }
       }
-      return $values;
     }
   }
+  return $replacements;
 }
 
 /**
- * Implementation of hook_token_list() for Pathauto specific tokens.
+ * Implement hook_token_info() for Pathauto specific tokens.
  */
-function pathauto_token_list($type = 'all') {
+function pathauto_token_info() {
   $tokens = array();
+
   if (module_exists('taxonomy')) {
-    if ($type == 'taxonomy' || $type == 'all') {
-      $tokens['taxonomy']['catpath'] = t('As [cat], but including its supercategories separated by /.');
-      $tokens['taxonomy']['catpath-raw'] = t('As [cat-raw], but including its supercategories separated by /. WARNING - raw user input.');
-      $tokens['taxonomy']['catalias'] = t('URL alias for the term.');
-    }
-    if ($type == 'node' || $type == 'all') {
-      $tokens['node']['termpath'] = t('As [term], but including its supercategories separated by /.');
-      $tokens['node']['termpath-raw'] = t('As [term-raw], but including its supercategories separated by /. WARNING - raw user input.');
-      $tokens['node']['termalias'] = t('URL alias for the term.');
-    }
-  }
-  if (module_exists('book')) {
-    if ($type == 'node' || $type == 'all') {
-      $tokens['node']['bookpathalias'] = t('URL alias for the parent book.');
+    $tokens['term']['catpath'] = array(
+      'name' => t("Node's Term Path"),
+      'description' => t('As [cat], but including its supercategories separated by /.'),
+    );
+    $tokens['term']['catalias'] = array(
+      'name' => t("Node's Term Alias"),
+      'description' => t('URL alias for the term.'),
+    );
+
+    $tokens['node']['termpath'] = array(
+      'name' => t('Term Path'),
+      'description' => t('As [term], but including its supercategories separated by /.'),
+    );
+    $tokens['node']['termalias'] = array(
+      'name' => t('Term Alias'),
+      'description' => t('URL alias for the term.'),
+    );
+
+    if (module_exists('book')) {
+      $tokens['node']['bookpathalias'] = array(
+        'name' => t('Book Path Alias'),
+        'description' => t('URL alias for the parent book.'),
+      );
     }
   }
-  return $tokens;
+
+  return array(
+    'tokens' => $tokens,
+  );
 }
 
 /**
- * Implementation of hook_path_alias_types().
+ * Implement hook_path_alias_types().
  *
  * Used primarily by the bulk delete form.
  */
@@ -194,77 +223,95 @@
 // Some node related functions.
 
 /**
- * Implementation of hook_nodeapi().
+ * Implement hook_node_presave().
  */
-function pathauto_nodeapi(&$node, $op, $teaser, $page) {
+function pathauto_node_presave($node) {
   if (module_exists('path')) {
-    switch ($op) {
-      case 'presave':
-        // About to be saved (before insert/update)
-        if (!empty($node->pathauto_perform_alias) && isset($node->old_alias)
-            && $node->path == '' && $node->old_alias != '') {
-          /**
-           * There was an old alias, but when pathauto_perform_alias was checked
-           * the javascript disabled the textbox which led to an empty value being
-           * submitted. Restoring the old path-value here prevents the Path module
-           * from deleting any old alias before Pathauto gets control.
-           */
-          $node->path = $node->old_alias;
-        }
-        break;
-      case 'insert':
-      case 'update':
-        _pathauto_include();
-        // Get the specific pattern or the default
-        if (variable_get('language_content_type_'. $node->type, 0)) {
-          $pattern = trim(variable_get('pathauto_node_'. $node->type .'_'. $node->language .'_pattern', FALSE));
-        }
-        if (empty($pattern)) {
-          $pattern = trim(variable_get('pathauto_node_'. $node->type .'_pattern', FALSE));
-          if (empty($pattern)) {
-            $pattern = trim(variable_get('pathauto_node_pattern', FALSE));
-          }
-        }
-        // Only do work if there's a pattern
-        if ($pattern) {
-          // Only create an alias if the checkbox was not provided or if the checkbox was provided and is checked
-          if (!isset($node->pathauto_perform_alias) || $node->pathauto_perform_alias) {
-            $placeholders = pathauto_get_placeholders('node', $node);
-            $src = "node/$node->nid";
-            $alias = pathauto_create_alias('node', $op, $placeholders, $src, $node->nid, $node->type, $node->language);
-          }
-        }
-        break;
-      case 'delete':
-        path_set_alias('node/'. $node->nid);
-        path_set_alias('node/'. $node->nid .'/feed');
-        break;
-      default:
-        break;
+    // About to be saved (before insert/update)
+    if (!empty($node->pathauto_perform_alias) && isset($node->old_alias)
+        && $node->path == '' && $node->old_alias != '') {
+      /**
+       * There was an old alias, but when pathauto_perform_alias was checked
+       * the javascript disabled the textbox which led to an empty value being
+       * submitted. Restoring the old path-value here prevents the Path module
+       * from deleting any old alias before Pathauto gets control.
+       */
+      $node->path = $node->old_alias;
+    }
+  }
+}
+
+/**
+ * Implement hook_node_insert().
+ */
+function pathauto_node_insert($node) {
+  _pathauto_node_insert_update($node, 'insert');
+}
+
+/**
+ * Implement hook_node_update().
+ */
+function pathauto_node_update($node) {
+  _pathauto_node_insert_update($node, 'update');
+}
+
+/**
+ * Internal helper for node insert/update duplicate functionality.
+ */
+function _pathauto_node_insert_update($node, $op) {
+  if (module_exists('path')) {
+    _pathauto_include();
+    // Get the specific pattern or the default
+    if (variable_get('language_content_type_' . $node->type, 0)) {
+      $pattern = trim(variable_get('pathauto_node_' . $node->type . '_' . $node->language . '_pattern', FALSE));
     }
+    if (empty($pattern)) {
+      $pattern = trim(variable_get('pathauto_node_' . $node->type . '_pattern', FALSE));
+      if (empty($pattern)) {
+        $pattern = trim(variable_get('pathauto_node_pattern', FALSE));
+      }
+    }
+    // Only do work if there's a pattern
+    if ($pattern) {
+      // Only create an alias if the checkbox was not provided or if the checkbox was provided and is checked
+      if (!isset($node->pathauto_perform_alias) || $node->pathauto_perform_alias) {
+        $source = "node/$node->nid";
+        $alias = pathauto_create_alias('node', $op, $source, array('node' => $node), $node->type, $node->language);
+      }
+    }
+  }
+}
+
+/**
+ * Implement hook_node_delete().
+ */
+function pathauto_node_delete($node) {
+  if (module_exists('path')) {
+    path_delete(array('source' => 'node/' . $node->nid));
+    path_delete(array('source' => 'node/' . $node->nid . '/feed'));
   }
 }
 
 /**
- * Implementation of hook_form_alter().
+ * Implement hook_form_alter().
  *
  * This allows alias creators to override Pathauto and specify their
  * own aliases (Pathauto will be invisible to other users). Inserted
  * into the path module's fieldset in the node form.
  */
-function pathauto_form_alter(&$form, $form_state, $form_id) {
-  // Process only node forms.
-  if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) {
+function pathauto_form_alter(&$form, &$form_state, $form_id) {
+  // Only do this for node forms
+  if (!empty($form['#node_edit_form'])) {
     $node = $form['#node'];
     $pattern = FALSE;
 
     // Find if there is an automatic alias pattern for this node type.
     if (isset($form['language'])) {
       $language = isset($form['language']['#value']) ? $form['language']['#value'] : $form['language']['#default_value'];
-      $pattern = trim(variable_get('pathauto_node_'. $form['type']['#value'] .'_'. $language .'_pattern', ''));
+      $pattern = trim(variable_get('pathauto_node_' . $form['type']['#value'] . '_' . $language . '_pattern', ''));
     }
     if (!$pattern) {
-      $pattern = trim(variable_get('pathauto_node_'. $form['type']['#value'] .'_pattern', ''));
+      $pattern = trim(variable_get('pathauto_node_' . $form['type']['#value'] . '_pattern', ''));
       if (!$pattern) {
         $pattern = trim(variable_get('pathauto_node_pattern', ''));
       }
@@ -278,8 +325,7 @@
           // alias that would be genereted by pathauto. If they are the same,
           // then keep the automatic alias enabled.
           _pathauto_include();
-          $placeholders = pathauto_get_placeholders('node', $node);
-          $pathauto_alias = pathauto_create_alias('node', 'return', $placeholders, "node/{$node->nid}", $node->nid, $node->type, $node->language);
+          $pathauto_alias = pathauto_create_alias('node', 'return', "node/{$node->nid}", array('node' => $node), $node->type, $node->language);
           $node->pathauto_perform_alias = isset($node->path) && $node->path == $pathauto_alias;
         }
         else {
@@ -290,7 +336,7 @@
 
       // Add JavaScript that will disable the path textfield when the automatic
       // alias checkbox is checked.
-      drupal_add_js(drupal_get_path('module', 'pathauto') .'/pathauto.js');
+      drupal_add_js(drupal_get_path('module', 'pathauto') . '/pathauto.js');
 
       $form['path']['pathauto_perform_alias'] = array(
         '#type' => 'checkbox',
@@ -301,7 +347,7 @@
       );
 
       if (user_access('administer pathauto')) {
-        $form['path']['pathauto_perform_alias']['#description'] .= ' '. t('To control the format of the generated aliases, see the <a href="@pathauto">automated alias settings</a>.', array('@pathauto' => url('admin/build/path/pathauto')));
+        $form['path']['pathauto_perform_alias']['#description'] .= ' ' . t('To control the format of the generated aliases, see the <a href="@pathauto">automated alias settings</a>.', array('@pathauto' => url('admin/build/path/pathauto')));
       }
 
       if ($node->pathauto_perform_alias && !empty($node->old_alias) && empty($node->path)) {
@@ -321,7 +367,7 @@
 }
 
 /**
- * Implementation of hook_node_operations().
+ * Implement hook_node_operations().
  */
 function pathauto_node_operations() {
   $operations = array(
@@ -341,10 +387,9 @@
  */
 function pathauto_node_operations_update($nodes) {
   _pathauto_include();
-  foreach ($nodes as $nid) {
-    $node = node_load($nid);
-    $placeholders = pathauto_get_placeholders('node', $node);
-    pathauto_create_alias('node', 'bulkupdate', $placeholders, "node/$node->nid", $node->nid, $node->type);
+  $nodes = node_load_multiple($nodes);
+  foreach ($nodes as $node) {
+    pathauto_create_alias('node', 'bulkupdate', "node/$node->nid", array('node' => $node), $node->type);
   }
 }
 
@@ -352,100 +397,123 @@
 // Taxonomy related functions.
 
 /**
- * Implementation of hook_taxonomy().
+ * Implement hook_taxonomy_term_insert().
  */
-function pathauto_taxonomy($op, $type, $object = NULL) {
-  switch ($type) {
-    case 'term':
-      switch ($op) {
-        case 'insert':
-        case 'update':
-          _pathauto_include();
-          // Use the category info to automatically create an alias
-          $category = (object) $object;
-          if ($category->name) {
-            $count = _taxonomy_pathauto_alias($category, $op);
-          }
+function pathauto_taxonomy_term_insert($term) {
+  // taxonomy.tokens.inc expects a description
+  if (!isset($term->description)) {
+    $term->description = NULL;
+  }
+  _pathauto_taxonomy_term_insert_update($term, 'insert');
+}
 
-          // For all children generate new alias (important if [catpath] used)
-          foreach (taxonomy_get_tree($category->vid, $category->tid) as $subcategory) {
-            $count = _taxonomy_pathauto_alias($subcategory, $op);
-          }
+/**
+ * Implement hook_taxonomy_term_update().
+ */
+function pathauto_taxonomy_term_update($term) {
+  _pathauto_taxonomy_term_insert_update($term, 'update');
+}
 
-          break;
-        case 'delete':
-          // If the category is deleted, remove the path aliases
-          $category = (object) $object;
-          path_set_alias('taxonomy/term/'. $category->tid);
-          path_set_alias(taxonomy_term_path($category));
-          path_set_alias('forum/'. $category->tid);
-          path_set_alias('taxonomy/term/'. $category->tid .'/0/feed');
-          break;
-        default:
-          break;
-      }
-      break;
-    default:
-      break;
+/**
+ * Internal helper for taxonomy term insert/update duplicate functionality
+ */
+function _pathauto_taxonomy_term_insert_update($term, $op) {
+  _pathauto_include();
+  if ($term->name) {
+    $count = _taxonomy_pathauto_alias($term, $op);
+  }
+
+  // For all children generate new alias (important if [catpath] used)
+  foreach (taxonomy_get_tree($term->vid, $term->tid) as $subterm) {
+    $count = _taxonomy_pathauto_alias($subterm, $op);
   }
 }
 
+/**
+ * Implement hook_taxonomy_term_delete().
+ */
+function pathauto_taxonomy_term_delete($term) {
+  path_delete(array('source' => 'taxonomy/term/' . $term->tid));
+  path_delete(array('source' => 'forum/' . $term->tid));
+  path_delete(array('source' => 'taxonomy/term/' . $term->tid . '/0/feed'));
+}
+
 //==============================================================================
-// User related functions.
+// User related functions. For users, trackers, and blogs.
 
 /**
- * Implementation of hook_user() for users, trackers, and blogs.
+ * Implement hook_user_insert().
  */
-function pathauto_user($op, &$edit, &$user, $category = FALSE) {
-  switch ($op) {
-    case 'insert':
-    case 'update':
-      _pathauto_include();
-      // Use the username to automatically create an alias
-      $pathauto_user = (object) array_merge((array) $user, $edit);
-      if ($user->name) {
-        $placeholders = pathauto_get_placeholders('user', $pathauto_user);
-        $src = 'user/'. $user->uid;
-        $alias = pathauto_create_alias('user', $op, $placeholders, $src, $user->uid);
-
-        if (module_exists('blog')) {
-          $new_user = drupal_clone($user);
-          if ($category == 'account') {
-            $new_user->roles = isset($edit['roles']) ? $edit['roles'] : array();
-            $new_user->roles[DRUPAL_AUTHENTICATED_RID] = t('authenticated user'); // Add this back
-          }
-          if (user_access('create blog entries', $new_user)) {
-            $src = 'blog/'. $user->uid;
-            $alias = pathauto_create_alias('blog', $op, $placeholders, $src, $user->uid);
-          }
-          else {
-            path_set_alias('blog/'. $user->uid);
-            path_set_alias('blog/'. $user->uid .'/feed');
-          }
-        }
-        if (module_exists('tracker')) {
-          $src = 'user/'. $user->uid .'/track';
-          $alias = pathauto_create_alias('tracker', $op, $placeholders, $src, $user->uid);
-        }
-        if (module_exists('contact')) {
-          $src = 'user/'. $user->uid .'/contact';
-          $alias = pathauto_create_alias('contact', $op, $placeholders, $src, $user->uid);
-        }
+function pathauto_user_insert(&$edit, $account, $category) {
+  _pathauto_user_insert_update($edit, $account, $category, 'insert');
+}
+
+/**
+ * Implement hook_user_update().
+ */
+function pathauto_user_update(&$edit, $account, $category) {
+  _pathauto_user_insert_update($edit, $account, $category, 'update');
+}
+
+/**
+ * Internal helper for user insert/update duplicate functionality.
+ */
+function _pathauto_user_insert_update(&$edit, $account, $category, $op) {
+  _pathauto_include();
+  // Use the username to automatically create an alias
+  $pathauto_user = (object) array_merge((array) $account, $edit);
+  if ($pathauto_user->name) {
+    $source = 'user/' . $pathauto_user->uid;
+    $alias = pathauto_create_alias('user', $op, $source, array('user' => $pathauto_user));
+
+    if (module_exists('blog')) {
+      $new_user = clone $pathauto_user;
+      if ($category == 'account') {
+        $new_user->roles = isset($edit['roles']) ? $edit['roles'] : array();
+        $new_user->roles[DRUPAL_AUTHENTICATED_RID] = t('authenticated user'); // Add this back
+      }
+      if (user_access('create blog entries', $new_user)) {
+        $source = 'blog/' . $new_user->uid;
+        $alias = pathauto_create_alias('blog', $op, $source, array('user' => $new_user));
       }
+      else {
+        path_delete(array('source' => 'blog/' . $new_user->uid));
+        path_delete(array('source' => 'blog/' . $new_user->uid . '/feed'));
+      }
+    }
+    if (module_exists('tracker')) {
+      $source = 'user/' . $new_user->uid . '/track';
+      $alias = pathauto_create_alias('tracker', $op, $source, array('user' => $new_user));
+    }
+    if (module_exists('contact')) {
+      $source = 'user/' . $new_user->uid . '/contact';
+      $alias = pathauto_create_alias('contact', $op, $source, array('user' => $new_user));
+    }
+  }
+}
+
+/**
+ * Implement hook_user_delete().
+ */
+function pathauto_user_cancel($edit, $account, $method) {
+  switch ($method) {
+    case 'user_cancel_block':
+    case 'user_cancel_block_unpublish':
+      // Don't remove aliases because the user may become active again later.
       break;
-    case 'delete':
-      // If the user is deleted, remove the path aliases
-      $user = (object) $user;
-      path_set_alias('user/'. $user->uid);
+
+    case 'user_cancel_reassign':
+    case 'user_cancel_delete':
+      // Remove the users's path aliases
+      $user = (object) $account;
+      path_delete(array('source' => 'user/' . $user->uid));
 
       // They may have enabled these modules and/or feeds when the user was created, so let's try to delete all of them
-      path_set_alias('blog/'. $user->uid);
-      path_set_alias('blog/'. $user->uid .'/feed');
-      path_set_alias('user/'. $user->uid .'/track');
-      path_set_alias('user/'. $user->uid .'/track/feed');
-      path_set_alias('user/'. $user->uid .'/contact');
-      break;
-    default:
+      path_delete(array('source' => 'blog/' . $user->uid));
+      path_delete(array('source' => 'blog/' . $user->uid . '/feed'));
+      path_delete(array('source' => 'user/' . $user->uid . '/track'));
+      path_delete(array('source' => 'user/' . $user->uid . '/track/feed'));
+      path_delete(array('source' => 'user/' . $user->uid . '/contact'));
       break;
   }
 }
Index: pathauto.js
===================================================================
RCS file: /cvs/drupal/contributions/modules/pathauto/pathauto.js,v
retrieving revision 1.5
diff -u -r1.5 pathauto.js
--- pathauto.js	18 Apr 2009 17:09:15 -0000	1.5
+++ pathauto.js	4 Nov 2009 22:14:24 -0000
@@ -1,23 +1,25 @@
 // $Id: pathauto.js,v 1.5 2009/04/18 17:09:15 freso Exp $
-if (Drupal.jsEnabled) {
-  $(document).ready(function() {
-    if ($("#edit-pathauto-perform-alias").size() && $("#edit-pathauto-perform-alias").attr("checked")) {
-      // Disable input and hide its description.
-      $("#edit-path").attr("disabled","disabled");
-      $("#edit-path-wrapper > div.description").hide(0);
-    }
-    $("#edit-pathauto-perform-alias").bind("click", function() {
-      if ($("#edit-pathauto-perform-alias").attr("checked")) {
-        // Auto-alias checked; disable input.
-        $("#edit-path").attr("disabled","disabled");
-        $("#edit-path-wrapper > div[class=description]").slideUp('fast');
+Drupal.behaviors.pathauto = {
+  attach: function(context, settings) {
+    (function ($) {
+      if ($("#edit-pathauto-perform-alias", context).size() && $("#edit-pathauto-perform-alias", context).attr("checked")) {
+        // Disable input and hide its description.
+        $("#edit-path-2", context).attr("disabled","disabled");
+        $("#edit-path-2", context).parent().find("div.description", context).hide(0);
       }
-      else {
-        // Auto-alias unchecked; enable input.
-        $("#edit-path").removeAttr("disabled");
-        $("#edit-path")[0].focus();
-        $("#edit-path-wrapper > div[class=description]").slideDown('fast');
-      }
-    });
-  });
-}
+      $("#edit-pathauto-perform-alias", context).bind("click", function() {
+        if ($("#edit-pathauto-perform-alias", context).attr("checked")) {
+          // Auto-alias checked; disable input.
+          $("#edit-path-2", context).attr("disabled","disabled");
+          $("#edit-path-2", context).parent().find("div.description", context).slideUp('fast');
+        }
+        else {
+          // Auto-alias unchecked; enable input.
+          $("#edit-path-2", context).removeAttr("disabled");
+          $("#edit-path-2", context)[0].focus();
+          $("#edit-path-2", context).parent().find("div.description", context).slideDown('fast');
+        }
+      });
+    })(jQuery);
+  }
+};
