--- pathauto.module	Sun Apr 10 19:01:59 2005
+++ pathauton2.module	Sun Apr 10 23:23:49 2005
@@ -32,12 +32,15 @@
         ** almost certainly appear immediately after the path alias field 
         */
         if (user_access('create url aliases')) {
-          $output = t('If the <b>Path alias</b> is left blank, an alias '.
+          $output = t('<div class="form-item"><div class="description">'.
+	    'If the <b>Path alias</b> is left blank, an alias '.
             'will be automatically generated from the title.');
           if (user_access('administer pathauto')) {
             $output .= t(' To control the format of the generated alias, '.
-              'see the <a href="admin/settings/pathauto">pathauto settings</a>.');
-          }
+              'see the <a href="admin/settings/pathauto">pathauto settings</a>.</div></div>');
+          } else {
+	    $output .= '</div></div>';
+	  }
           return $output;
         }
         break;
@@ -71,6 +74,10 @@
     _pathauto_bulkupdate();
   }
 
+  if (variable_get('pathauto_cat_bulkupdate', FALSE)) {
+    _pathauto_cat_bulkupdate();
+  }
+
   // Generate the form
   // %%% NOTE: should validate separator as legal URI character
   $items .= form_textfield(t('Separator'), 'pathauto_separator',  
@@ -79,7 +86,11 @@
 
   $items .= form_checkbox(t('Create feed aliases'), 'pathauto_applytofeed', 1,
     variable_get('pathauto_applytofeed', FALSE), t('Also generate aliases for RSS feeds.'));
-    
+
+  
+  $output .= form_group(t('General settings'), $items, t(''));
+
+  $items ='';
   $items .= form_textfield('Default path pattern (applies to all types with blank patterns below)',
                            'pathauto_pattern',
                            variable_get('pathauto_pattern','[title]'),
@@ -120,6 +131,9 @@
         $description .= '<dd>'.t("The vocabulary that the page's first category belongs to.");
         $description .= '<dt>'.'[cat]'.'</dt>';
         $description .= '<dd>'.t('The first category that the page belongs to.');
+        $description .= '<dt>'.'[catalias]'.'</dt>';
+	$description .= '<dd>'.t('The alias for the first category that the page belongs to. '.
+	  'This is useful for long category names.  You must set up the url alias for the category first.');
         $description .= '<dt>'.'[catpath]'.'</dt>';
         $description .= '<dd>'.t('As category, but including its supercategories.').'</dd>';
     }
@@ -164,13 +178,62 @@
         '</dl>'
       );
 
-  $output .= form_group(t('Format settings'), $items, $description);
+  $output .= form_group(t('Node path settings'), $items, $description);
 
-  // %%% Should only show when there are unaliased nodes
-  $items = '';
-  $items .= form_checkbox(t('Bulk update'), 'pathauto_bulkupdate', 1, FALSE, 
+
+  if (module_exist('taxonomy')) {
+    $vocabularies = taxonomy_get_vocabularies();
+    if (sizeof($vocabularies) > 0) {
+      $items  = '';
+      $items .= form_textfield('Default path pattern (applies to all vocabularies with blank patterns below)',
+                               'pathauto_cat_pattern',
+                               variable_get('pathauto_cat_pattern','[vocab]/[catpath]'),
+                               65,
+                               128,
+                               NULL,
+                               NULL,
+                               FALSE);
+
+
+      foreach ($vocabularies as $vocab) {
+        $items .= form_textfield('Pattern for all '.$vocab->name.' paths',
+                                 $vocab->name.'_pathauto_cat_pattern',
+                                 variable_get($vocab->name.'_pathauto_cat_pattern',''),
+                                 65,
+                                 128,
+                                 '',
+                                 NULL,
+                                 FALSE);
+      }
+
+      $description  = '';
+      $description .= '<dt>'.'[vocab]'.'</dt>';
+      $description .= '<dd>'.t("The vocabulary that the page's first category belongs to.");
+      $description .= '<dt>'.'[catname]'.'</dt>';
+      $description .= '<dd>'.t('The name of the category.');
+      $description .= '<dt>'.'[catpath]'.'</dt>';
+      $description .= '<dd>'.t('As category, but including its supercategories.').'</dd>';
+      $description .= '<dt>'.'[cid]'.'</dt>';
+      $description .= '<dd>'.t('The id number of the category.</dd>');
+     
+      $output .= form_group(t('Category path settings'), $items, $description);
+    }
+  }
+
+// %%% Should only show when there are unaliased nodetems ='';
+
+  $items  = '';
+  $items .= form_checkbox(t('Bulk update node paths'), 'pathauto_bulkupdate', 1, FALSE, 
     t('Generate aliases for all existing nodes which do not already have aliases.'));
   variable_set('pathauto_bulkupdate', FALSE);
+  if (module_exist('taxonomy')) {
+    $vocabularies = taxonomy_get_vocabularies();
+    if (sizeof($vocabularies) > 0) {
+      $items .= form_checkbox(t('Bulk update category paths'), 'pathauto_cat_bulkupdate', 1, FALSE,
+        t('Generate aliases for all existing categories which do not already have aliases.'));
+      variable_set('pathauto_cat_bulkupdate', FALSE);
+    }
+  }
   $output .= form_group(t('Actions'), $items, t(''));
   
   return $output;
@@ -230,10 +293,16 @@
       $vid = $term->vid;
       $vocabulary = taxonomy_get_vocabulary($vid);
       $placeholders['[vocab]'] = _pathauto_cleanstring($vocabulary->name, $separator);
+
+      $placeholders['[catalias]'] = drupal_get_path_alias('taxonomy/term/' . $node->taxonomy[0]);
+      if (!strncasecmp($placeholders['[catalias]'], 'taxonomy', 8)) {
+        $placeholders['[catalias]'] = $placeholders['[cat]'];
+      }
     } else {
       $placeholders['[cat]'] = '';
       $placeholders['[catpath]'] = '';
       $placeholders['[vocab]'] = '';
+      $palceholders['[catalias]'] = '';
     }
     
     // Node type info
@@ -319,6 +388,9 @@
   }
 }
 
+/**
+ * Implementation of hook_taxonomy().
+ */
 function pathauto_taxonomy($op, $type, $object) {
   switch ($type) {
     case 'vocabulary':
@@ -336,10 +408,25 @@
     case 'term':
       switch ($op) {
         case 'insert':
+          /*
+          ** If there's no explicit alias use the
+          ** category name to automatically create an alias
+          */
+          _pathauto_generate_cat_alias($object);
           break;
         case 'delete':
+	  /*
+	   ** If the category is deleted, remove the path aliases
+	   **
+           */
+	  _pathauto_remove_cat_alias($object);
           break;
         case 'update':
+          /*
+          ** If there's no explicit alias use the
+          ** category name to automatically create an alias
+          */
+	  _pathauto_generate_cat_alias($object);
           break;
         default:
           break;
@@ -349,5 +436,90 @@
       break;
   }
 }
+
+// Generate an alias from a category's name
+function _pathauto_generate_cat_alias($term) {
+  $category = array2object($term); 
+
+  if ($category->name) {
+    $separator = variable_get('pathauto_separator', '_');
+
+    // First transform the title
+    $catname = _pathauto_cleanstring($category->name, $separator);
+
+    // Now get all the placeholder data
+    $placeholders = array();
+    $placeholders['[catname]'] = $catname;
+
+    $catpath = '';
+    $parents = taxonomy_get_parents_all($category->tid);
+    foreach ($parents as $parent) {
+      $catpath = _pathauto_cleanstring($parent->name, $separator) . '/' . $catpath;
+    }
+    $placeholders['[catpath]'] = $catpath;
+
+    $vid = $category->vid;
+    $vocabulary = taxonomy_get_vocabulary($vid);
+    $placeholders['[vocab]'] = _pathauto_cleanstring($vocabulary->name, $separator);
+
+    $placeholders['[cid]'] = $category->tid;
+
+    
+
+    // Retrieve and apply the pattern
+    $pattern = strtolower(variable_get($placeholders['[vocab]'].'_pathauto_cat_pattern', ''));
+    if (!trim($pattern)) {
+      $pattern = strtolower(variable_get('pathauto_cat_pattern', '[vocab]/[catpath]'));
+    }
+    $alias = str_replace(array_keys($placeholders), $placeholders, $pattern);
+    $alias = strtolower($alias);
+    // Two or more slashes should be collapsed into one
+    $alias = preg_replace("/\/+/", "/", $alias);
+    // Trim any leading or trailing slashes
+    $alias = preg_replace("/^\/|\/+$/", "", $alias);
+
+    $src = "taxonomy/term/". $category->tid;
+    // If the alias already exists, generate a new variant
+    if (_pathauto_alias_exists($alias, $src)) {
+      for ($i=0; _pathauto_alias_exists($alias.$separator.$i, $src); $i++) {
+      }
+      $alias = $alias.$separator.$i;
+    }
+    path_set_alias($src, $alias);
+    if (variable_get('pathauto_applytofeed', FALSE)) {
+      path_set_alias("$src/feed", "$alias/feed");
+    }
+  }
+  return $alias;
+}
+
+// Generate aliases for all categories without aliases
+function _pathauto_cat_bulkupdate() {
+  $query = 'SELECT tid,vid,name,src,dst FROM {term_data} '.
+    "LEFT JOIN {url_alias} ON CONCAT('taxonomy/term/', tid) = src ".
+    'WHERE dst is null';
+  $result = db_query($query);
+  $category = db_fetch_object($result);
+  if (!$category) {
+    drupal_set_message('No unaliased catagories to update.');
+    return;
+  }
+
+  while ($category) {
+    _pathauto_generate_cat_alias($category);
+    $category = db_fetch_object($result);
+  }
+}
+
+// Remove aliase when a category is deleted
+function _pathauto_remove_cat_alias($term) {
+  $category = array2object($term);
+
+  db_query("DELETE FROM {url_alias} WHERE src = CONCAT('taxonomy/term/', %d)", $category->tid);
+  if (variable_get('pathauto_applytofeed', FALSE)) {
+    db_query("DELETE FROM {url_alias} WHERE src =  CONCAT('taxonomy/term/', %d, '/feed')", $category->tid);
+  }
+}
+
 
 ?>
