diff -urp --strip-trailing-cr ../category/category.module ./category.module
--- ../category/category.module	2009-08-05 06:52:53.000000000 +0200
+++ ./category.module	2009-09-25 23:28:09.000000000 +0200
@@ -1135,42 +1135,39 @@ function category_del_container($cid) {
  * 1. If any category contains multiple parents, the container will be given a
  * hieararchy of 2.
  *
- * @param $container
- *   An array of the container structure.
- * @param $changed_category
- *   An array of the category structure that was updated.
- */
-function category_check_container_hierarchy($container, $changed_category) {
-  $tree = category_get_tree($container['cid']);
-  $hierarchy = 0;
-  foreach ($tree as $category) {
-    // Update the changed category with the new parent value before comparision.
-    if ($category->cid == $changed_category['cid']) {
-      $category = (object)$changed_category;
-    }
-    // Check this term's parent count.
-    if (count($category->parents) > 1) {
-      $hierarchy = 2;
-      break;
-    }
-    elseif (count($category->parents) == 1) {
-      $first_parent = array_shift($category->parents);
-      if (!empty($first_parent)) {
-        if (!category_is_root_parent($first_parent, $container['cid'])) {
-          $hierarchy = 1;
-        }
-      }
-    }
-  }
-  if ($hierarchy != $container['hierarchy']) {
-    $container['hierarchy'] = $hierarchy;
+ * Note: We're not inheriting the core taxonomy code here, because our workflow
+ * is different in that we save category data in hook_nodeapi() rather than in a
+ * form submit handler, so we can't use form values. Also the code is simplified.
+ *
+ * @param $cnid
+ *   The container to be updated.
+ * @param $hierarchy
+ *   Known minimum hierarchy suggested by a recently changed category (if any),
+ *   to help avoiding an expensive query. This is not a final value, but it's
+ *   needed to increase the hierarchy from initial zero. Values higher than 2
+ *   will be reduced.
+ */
+function category_update_container_hierarchy($cnid, $hierarchy = 0) {
+  $container = category_get_container($cnid);
+
+  if ($hierarchy < $container->hierarchy) {
+    // Retrieve the new value for hierarchy field afresh, as the biggest of all
+    // parent counts for categories in this container, reduced to maximum value
+    // of 2. Exclude the root (container) parent, if there's none other.
+    $result = db_fetch_array(db_query('SELECT h.cid, COUNT(h.parent) AS parents, MAX(ABS(h.parent - %d)) AS not_cont_only FROM {category_hierarchy} h INNER JOIN {category} c ON h.cid = c.cid WHERE c.cnid = %d GROUP BY h.cid ORDER BY parents DESC, not_cont_only DESC LIMIT 1', $cnid, $cnid));
+    $hierarchy = $result['not_cont_only'] ? $result['parents'] : 0;
+  }
+  $hierarchy = min($hierarchy, 2);
+
+  // Update the container, if we got a different hierarchy.
+  if ($hierarchy != $container->hierarchy) {
+    $container->hierarchy = $hierarchy;
     $node = new StdClass();
-    $node->nid = $container['cid'];
-    $node->category = $container;
+    $node->nid = $container->cid;
+    $node->category = (array) $container;
     category_save_container($node);
+    // TODO: Allow Taxonomy wrapper to take an action, too.
   }
-
-  return $hierarchy;
 }
 
 /**
@@ -1179,13 +1176,21 @@ function category_check_container_hierar
  */
 function category_save_category(&$node) {
   $new = empty($node->category['cid']);
+
+  // Before we save anything, retrieve the container this category used to
+  // be in previously, if any.
+  $old_cnid = NULL;
+  if (!$new) {
+    $old_category = category_get_category($node->nid);
+    $old_cnid = $old_category->cnid;
+  }
+
   $node->category['cid'] = $node->nid;
 
   if (empty($node->category['cnid'])) {
     $node->category['cnid'] = $node->category['container'];
   }
-  if (!isset($node->category['parents']) ||
-  empty($node->category['parents'])) {
+  if (empty($node->category['parents'])) {
     $node->category['parents'] = array($node->category['cnid']);
   }
 
@@ -1202,18 +1207,22 @@ function category_save_category(&$node) 
     $status = SAVED_UPDATED;
   }
 
+  $parents = array();
   if (is_array($node->category['parents'])) {
     foreach ($node->category['parents'] as $parent) {
       if (is_array($parent)) {
         foreach ($parent as $cid) {
           db_query('INSERT INTO {category_hierarchy} (cid, parent) VALUES (%d, %d)', $node->category['cid'], $cid);
+          $parents[$cid] = TRUE;
         }
       }
       else if (is_object($parent)) {
         db_query('INSERT INTO {category_hierarchy} (cid, parent) VALUES (%d, %d)', $node->category['cid'], $parent->cid);
+        $parents[$parent->cid] = TRUE;
       }
       else {
         db_query('INSERT INTO {category_hierarchy} (cid, parent) VALUES (%d, %d)', $node->category['cid'], $parent);
+        $parents[$parent] = TRUE;
       }
     }
   }
@@ -1237,6 +1246,16 @@ function category_save_category(&$node) 
     }
   }
 
+  // Update the hierarchy flag on affected container(s).
+  if (!empty($old_cnid) && $node->category['cnid'] != $old_cnid) {
+    category_update_container_hierarchy($old_cnid);
+  }
+  if (count($parents) == 1) {
+    // The container (root) parent doesn't count, if there's none other.
+    unset($parents[$node->category['cnid']]);
+  }
+  category_update_container_hierarchy($node->category['cnid'], count($parents));
+
   category_cache_op('flush', 'all');
   return $status;
 }
@@ -1250,12 +1269,16 @@ function category_save_category(&$node) 
  *   Status constant indicating deletion.
  */
 function category_del_category($cid) {
+  $old_category = category_get_category($cid);
+
   db_query('DELETE FROM {category} WHERE cid = %d', $cid);
   db_query('DELETE FROM {category_hierarchy} WHERE cid = %d', $cid);
   db_query('DELETE FROM {category_relation} WHERE cid1 = %d OR cid2 = %d', $cid, $cid);
   db_query('DELETE FROM {category_synonym} WHERE cid = %d', $cid);
   db_query('DELETE FROM {category_node} WHERE cid = %d', $cid);
 
+  category_update_container_hierarchy($old_category->cnid);
+
   category_cache_op('flush', 'all');
   return SAVED_DELETED;
 }
@@ -1625,71 +1648,6 @@ function _category_add_form_elements(&$f
       '#default_value' => implode("\n", category_get_synonyms($node->category['cid'])),
       '#description' => t('Synonyms of this category, one synonym per line.'),
     );
-    $form['#submit'][] = 'category_form_category_submit';
-  }
-}
-
-/**
- * Submit handler to insert or update a category. Updates the
- * hierarchy setting for the container of the category being updated.
- */
-function category_form_category_submit($form, &$form_state) {
-  $container = category_get_container($form['#node']->category['container']);
-  $container->parents = _category_flatten_parents(category_get_parents($container->cid));
-
-  if (!$container->tags) {
-    if (empty($form_state['values']['category']['parents']) || !is_array($form_state['values']['category']['parents'])) {
-      $form_state['values']['category']['parents'] = array();
-    }
-
-    $current_parent_count = count($form_state['values']['category']['parents']);
-    $previous_parent_count = count($form['#node']->category['parents']);
-
-    if ($current_parent_count == 1) {
-      $first_parent = current($form_state['values']['category']['parents']);
-      if (!empty($first_parent)) {
-        if (category_is_root_parent($first_parent, $container->cid)) {
-          $current_parent_count--;
-        }
-      }
-    }
-    if ($previous_parent_count == 1) {
-      $first_parent = current($form['#node']->category['parents']);
-      if (!empty($first_parent)) {
-        if (category_is_root_parent($first_parent, $container->cid)) {
-          $previous_parent_count--;
-        }
-      }
-    }
-
-    // If the number of parents has been reduced to one or none, do a check on the
-    // parents of every category in the container value.
-    $new_container = NULL;
-    if ($current_parent_count < $previous_parent_count && $current_parent_count < 2) {
-      $changed_category = $form_state['values']['category'];
-      category_check_container_hierarchy((array) $container, $changed_category);
-      if ($form['#node']->category['container'] != $form_state['values']['category']['container']) {
-        $new_container = category_get_container($form_state['values']['category']['container']);
-        $new_container->parents = _category_flatten_parents(category_get_parents($new_container->cid));
-        category_check_container_hierarchy((array) $new_container, $changed_category);
-      }
-    }
-    // If we've increased the number of parents and this is a single or flat
-    // hierarchy, update the container immediately.
-    elseif ($current_parent_count > $previous_parent_count && $container->hierarchy < 2) {
-      $container->hierarchy = $current_parent_count == 1 ? 1 : 2;
-      $node = new StdClass();
-      $node->nid = $container->cid;
-      $node->category = (array) $container;
-      category_save_container($node);
-      if (!empty($new_container)) {
-        $new_container->hierarchy = $current_parent_count == 1 ? 1 : 2;
-        $node = new StdClass();
-        $node->nid = $new_container->cid;
-        $node->category = (array) $new_container;
-        category_save_container($node);
-      }
-    }
   }
 }
 
