Index: INSTALL.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/INSTALL.txt,v
retrieving revision 1.9
diff -u -p -r1.9 INSTALL.txt
--- INSTALL.txt	14 Apr 2006 11:40:21 -0000	1.9
+++ INSTALL.txt	5 Aug 2009 04:44:03 -0000
@@ -3,67 +3,108 @@
 Requirements
 ------------
 
-This module requires the latest development version of Drupal (CVS HEAD).
+This module requires the corresponding stable version of Drupal.
+No other modules are required beyond that.
 
 
 Installation
 ------------
 
-1. Copy the category folder and its contents to the Drupal modules/ directory.
+1. If you're installing Category to an existing site, save backup copies
+   of your database and files first. Category module changes a lot of things,
+   and therefore it may be difficult to un-install Category properly later.
+
+2. Copy the category folder and its contents to the appropriate Drupal directory,
+   which is usually sites/all/modules/ or sites/your_site_name/modules/.
    Drupal should automatically detect it.
 
-2. Go to 'administer -> modules' and enable category (and category_menu,
-   category_display, category_export, and category_legacy, if applicable).
-   Note: for each module that you enable, all necessary database tables will
-   be automatically created, using Drupal's new .install file system. You no
-   longer need to run the category.mysql script (which is why this file no
-   longer exists).
+3. Go to 'Administer -> Modules' (admin/build/modules) and enable category
+   (and additional modules in the Category group, such as category_menu,
+   category_display, and others, if applicable).
 
-3. If you wish to use taxonomy-dependent Drupal modules (e.g. forum, image,
+4. If you wish to use taxonomy-dependent Drupal modules (e.g. forum, image,
    glossary) with this module, you must install the taxonomy wrapper module.
-   There is now a script available to perform this installation for you. To
-   run the script, go to 'administer -> settings -> category', and in the
-   'wrapper install / uninstall settings' box, click the 'install now' link
+   There is a script available to perform this installation for you. To
+   run the script, go to 'Administer -> Categories -> Wrapper modules'
+   (admin/content/category/wrappers), and click the 'install now' link
    for the taxonomy wrapper. If you experience errors with this script (usually
    due to file system permissions problems), see the instructions for
    installing the wrapper modules manually (available online).
 
-4. If you wish to use book-dependent Drupal modules (e.g. export_dxml) with
+5. If you wish to use book-dependent Drupal modules (e.g. export_dxml) with
    this module, you must install the book wrapper module. The procedure for
    doing this is similar to that for installing the taxonomy wrapper module.
-   To run the book wrapper installation script, go to 'administer -> settings
-   -> category', and in the 'wrapper install / uninstall settings' box, click
+   To run the book wrapper installation script, go to 'Administer -> Categories
+   -> Wrapper modules' (admin/content/category/wrappers), and click
    the 'install now' link for the book wrapper. If you experience errors with
    this script (usually due to file system permissions problems), see the
    instructions for installing the wrapper modules manually (available online).
 
-5. If you have existing taxonomy terms and vocabularies, or if you have
+6. If you have existing taxonomy terms and vocabularies, or if you have
    existing book nodes, you can import them as categories and containeris using
-   category_legacy. To do this, go to 'administer -> category legacy', and
+   category_legacy. To do this, go to 'Administer -> Category legacy', and
    follow the instructions on that page. It is recommended that you enable the
    taxonomy and book wrappers before performing an import. Additionally, using
    the taxonomy wrapper with existing taxonomy data is not recommended - either
-   import your existing taxonomy data first, disable the taxonomy wrapper, or
+   import your existing taxonomy data, disable the taxonomy wrapper, or
    delete your site's taxonomy data. You are able to leave the book module
    enabled, and continue to use your book data, if you wish (but as with
    taxonomy, it is recommended that you disable book).
 
-6. Go to 'administer -> access control' to configure access privileges for
-   this module.
+7. Go to 'Administer -> Permissions' (admin/user/permissions) to configure
+   access privileges for this module.
 
-7. Go to 'administer -> settings -> category' to change the settings for this
-   module. You might also want to change the node type settings for the
-   category and container node types, at 'administer -> settings -> content
-   types'.
+8. Settings for this module are basically located at 'Administer -> Categories'
+   (admin/content/category), but you'll notice that nearly all of the wide
+   range of options are actually set on per-container or per-node-type basis,
+   so you need to visit 'Administer -> Content types -> edit' (admin/content/
+   node-type/your_node_type), and especially your container's edit page, to
+   make all the choices.
 
-8. Go to 'administer -> categories' to start managing your categories and
+9. Go to 'Administer -> Categories' to start managing your categories and
    containers. You can create new categories and containers from here, or
-   you can go to 'create content -> category' or 'create content -> container'.
+   you can go to 'Create content -> Category' or 'Create content -> Container'.
+   As you'll be creating your categories and containers, you can adjust their
+   behavior to your needs through various settings found in the 'Category
+   information' or 'Container information' boxes.
 
-9. You can assign content to categories by configuring your containers to be
+10. You can assign content to categories by configuring your containers to be
    associated with various node types, and then creating or editing nodes of
    those types.
    Note: content with assigned categories will only have its categories
-   displayed if category_display is installed, and if your containers are
-   configured to show categories on assigned nodes, and if the taxonomy
-   wrapper is installed.
+   displayed, if the taxonomy wrapper is installed, and if category_display
+   is not set to hide them.
+
+11. You may create customized lists of content based on categories, if you
+   have installed the Views module (available at http://drupal.org/project/views),
+   and also use them for your categories through the category_views module.
+   To use Category data with Views, you need to install the taxonomy wrapper,
+   and choose taxonomy-based options in Views.
+
+
+Troubleshooting
+---------------
+
+If something doesn't work as you expected, check all your settings first.
+Most commonly, problems are solved by just reviewing the 'Container information'
+box (while editing the container), as the settings located there are changing
+behavior of all categories (and nodes to some extent) within that container.
+(Note that some settings are hidden, unless other settings make them relevant,
+so you really want to read the descriptions.)
+
+You should also check, whether all necessary modules (such as category_display,
+category_menu, and others) are enabled where needed, and the taxonomy and/or
+book wrapper modules installed if necessary.
+
+If you're experiencing problems with menu items, with taxonomy-based modules,
+or with various content lists (including TOC trees and Views integration),
+you might need to refresh your site's data through the category_resave module.
+(This might happen after larger changes of settings, done while content
+already exists on the site.)
+
+Always keep database backups, if un/installing modules, or performing large
+operations (such as category_resave do), so that you're able to revert the
+changes if something goes wrong.
+
+For further help, visit the sites http://category.greenash.net.au/ and/or
+http://drupal.org/project/category
Index: README.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/README.txt,v
retrieving revision 1.2
diff -u -p -r1.2 README.txt
--- README.txt	8 May 2006 13:38:25 -0000	1.2
+++ README.txt	5 Aug 2009 04:44:03 -0000
@@ -16,7 +16,7 @@ with categories. Features:
  - Extensible architecture allows other modules to build on the functionality
    of this 'base module'
  - Wrapper modules allow full backwards-compatibility with the taxonomy and
-   book modules (which this module is based upon), and thus with all other
+   book modules (which this module is based upon), and thus with most other
    modules that depend on them
 
 Note: this is a summary of the full feature list, which is maintained at http://category.greenash.net.au/features.
Index: UPGRADE.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/UPGRADE.txt,v
retrieving revision 1.1
diff -u -p -r1.1 UPGRADE.txt
--- UPGRADE.txt	22 Aug 2007 04:14:32 -0000	1.1
+++ UPGRADE.txt	5 Aug 2009 04:44:03 -0000
@@ -1,4 +1,12 @@
 Drupal 6.x:
  * "create categories", "create containers", "edit all categories", and "edit own categories" permissions are gone.
    Categories and containers no longer are special node types. Use the regular set of node permissions in the "node" section
-   to control access.
\ No newline at end of file
+   to control access.
+
+ * The category_display module now depends on category_menu. For some displays (such as the TOC tree)
+   to work properly, generated menu items must be enabled now (for containers and categories at a
+   minimum; you'll be able to put these into a hidden menu, so they don't show anywhere). You might need
+   to generate menu items for existing categories and/or nodes using the new category_resave module.
+
+ * The fieldset for custom menu links creation on node edit forms is suppressed now, as it's incompatible
+   with category_menu. Menu links should be created by either category_menu, or by the user - not both.
Index: category.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/category.admin.inc,v
retrieving revision 1.4
diff -u -p -r1.4 category.admin.inc
--- category.admin.inc	31 May 2009 10:16:11 -0000	1.4
+++ category.admin.inc	5 Aug 2009 04:44:04 -0000
@@ -114,6 +114,12 @@ function category_overview_containers_su
  */
 function theme_category_overview_containers($form) {
   $rows = array();
+
+  $cols = 4;
+  if (variable_get('category_container_hierarchy', 0) > 0) {
+    $cols++;
+  }
+
   foreach (element_children($form) as $key) {
     if (isset($form[$key]['name'])) {
       $container = &$form[$key];
@@ -121,10 +127,8 @@ function theme_category_overview_contain
       $row = array();
       $row[] = drupal_render($container['name']);
       $row[] = drupal_render($container['types']);
-      $cols = 4;
       if (variable_get('category_container_hierarchy', 0) > 0) {
         $row[] = drupal_render($container['parents']);
-        $cols++;
       }
       if (isset($form['submit'])) {
         $container['weight']['#attributes']['class'] = 'container-weight';
@@ -742,7 +746,6 @@ function category_add_category_page($cni
 function category_wrapper_admin_page() {
   $book_status = category_get_wrapper_status('book');
   $taxonomy_status = category_get_wrapper_status('taxonomy');
-  $menu_status = category_get_wrapper_status('menu');
   $form['book_wrapper'] = array(
     '#type' => 'item',
     '#title' => t('Book wrapper status'),
@@ -753,11 +756,6 @@ function category_wrapper_admin_page() {
     '#title' => t('Taxonomy wrapper status'),
     '#value' => theme('category_wrapper_status', 'taxonomy', $taxonomy_status),
   );
-  $form['menu_wrapper'] = array(
-    '#type' => 'item',
-    '#title' => t('Menu wrapper status'),
-    '#value' => theme('category_wrapper_status', 'menu', $menu_status),
-  );
 
   return $form;
 }
Index: category.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/category.inc,v
retrieving revision 1.70
diff -u -p -r1.70 category.inc
--- category.inc	21 Jul 2008 09:30:48 -0000	1.70
+++ category.inc	5 Aug 2009 04:44:04 -0000
@@ -18,31 +18,38 @@
  *   If set, return only those containers associated with this node type.
  */
 function category_get_containers($type = NULL) {
-  $type_sql = $type ? " WHERE nt.type = '%s'" : "";
-  $result = db_query(db_rewrite_sql("SELECT n.nid, n.title, cn.*, c.weight, c.depth, n.status, nt.type FROM {category_cont} cn INNER JOIN {category} c ON cn.cid = c.cid INNER JOIN {node} n ON cn.cid = n.nid LEFT JOIN {category_cont_node_types} nt ON cn.cid = nt.cid". $type_sql ." ORDER BY c.weight, n.title", 'n', 'nid'), $type);
+  $cache_key = 'c_get_conts'. $type;
+  $containers = category_cache_op('get', 0, $cache_key);
+  if (!isset($containers)) {
 
-  $containers = array();
-  $node_types = array();
-  while ($container = db_fetch_object($result)) {
-    // If no node types are associated with a container, the LEFT JOIN will
-    // return a NULL value for type.
-    if (isset($container->type)) {
-      $node_types[$container->cid][$container->type] = $container->type;
-      unset($container->type);
-      $container->nodes = $node_types[$container->cid];
-    }
-    elseif (!isset($container->nodes)) {
-      $container->nodes = array();
-    }
-
-    if (empty($container->admin_title)) {
-      $container->admin_title = '';
-      $container->has_admin_title = FALSE;
-    }
-    else {
-      $container->has_admin_title = TRUE;
+    $type_sql = $type ? " WHERE nt.type = '%s'" : "";
+    $result = db_query(db_rewrite_sql("SELECT n.nid, n.title, cn.*, c.weight, c.depth, n.status, nt.type FROM {category_cont} cn INNER JOIN {category} c ON cn.cid = c.cid INNER JOIN {node} n ON cn.cid = n.nid LEFT JOIN {category_cont_node_types} nt ON cn.cid = nt.cid". $type_sql ." ORDER BY c.weight, n.title", 'n', 'nid'), $type);
+
+    $containers = array();
+    $node_types = array();
+    while ($container = db_fetch_object($result)) {
+      // If no node types are associated with a container, the LEFT JOIN will
+      // return a NULL value for type.
+      if (isset($container->type)) {
+        $node_types[$container->cid][$container->type] = $container->type;
+        unset($container->type);
+        $container->nodes = $node_types[$container->cid];
+      }
+      elseif (!isset($container->nodes)) {
+        $container->nodes = array();
+      }
+
+      if (empty($container->admin_title)) {
+        $container->admin_title = '';
+        $container->has_admin_title = FALSE;
+      }
+      else {
+        $container->has_admin_title = TRUE;
+      }
+      $containers[$container->cid] = $container;
     }
-    $containers[$container->cid] = $container;
+
+    category_cache_op('set', 0, $cache_key, $containers);
   }
 
   return $containers;
@@ -52,10 +59,17 @@ function category_get_containers($type =
  * Find all categories associated to the given node, within one container.
  */
 function category_node_get_categories_by_container($node, $cnid, $key = 'cid') {
-  $result = db_query(db_rewrite_sql('SELECT c.*, n.title FROM {category} c INNER JOIN {category_node} r ON c.cid = r.cid INNER JOIN {node} n ON c.cid = n.nid WHERE c.cnid = %d AND r.vid = %d AND n.status = 1 ORDER BY c.weight'), $cnid, $node->vid);
-  $categories = array();
-  while ($category = db_fetch_object($result)) {
-    $categories[$category->$key] = $category;
+  $cache_key = 'c_node_get_cats_by_cont'. $node->vid .':'. $cnid .':'. $key;
+  $categories = category_cache_op('get', $node->nid, $cache_key);
+  if (!isset($categories)) {
+
+    $result = db_query(db_rewrite_sql('SELECT c.*, n.title FROM {category} c INNER JOIN {category_node} r ON c.cid = r.cid INNER JOIN {node} n ON c.cid = n.nid WHERE c.cnid = %d AND r.vid = %d AND n.status = 1 ORDER BY c.weight'), $cnid, $node->vid);
+    $categories = array();
+    while ($category = db_fetch_object($result)) {
+      $categories[$category->$key] = $category;
+    }
+
+    category_cache_op('set', $node->nid, $cache_key, $categories);
   }
   return $categories;
 }
@@ -63,20 +77,22 @@ function category_node_get_categories_by
 /**
  * Find all categories associated to the given node, ordered by container and category weight.
  */
-function category_node_get_categories($node, $key = 'cid', $reset = FALSE) {
-  static $categories = array();
+function category_node_get_categories($node, $key = 'cid') {
+  $cache_key = 'cat_node_get_cats'. $node->vid .':'. $key;
+  $categories = category_cache_op('get', $node->nid, $cache_key);
+  if (!isset($categories)) {
 
-  if (!isset($categories[$node->vid]) || $reset) {
-    $categories[$node->vid] = array();
+    $categories = array();
 
     $result = db_query(db_rewrite_sql('SELECT n.nid, nr.teaser AS description, r.vid AS node_id, c.*, n.title FROM {category} c INNER JOIN {category_node} r ON c.cid = r.cid INNER JOIN {category} cn ON c.cnid = cn.cid INNER JOIN {node} n ON c.cid = n.nid INNER JOIN {node_revisions} nr ON n.vid = nr.vid INNER JOIN {node} cnn ON cn.cid = cnn.nid WHERE n.status = 1 AND r.vid = %d ORDER BY cn.weight, cnn.title, c.weight, n.title', 'n', 'nid'), $node->vid);
 
     while ($category = db_fetch_object($result)) {
-      $categories[$node->vid][$category->$key] = $category;
+      $categories[$category->$key] = $category;
     }
-  }
 
-  return $categories[$node->vid];
+    category_cache_op('set', $node->nid, $cache_key, $categories);
+  }
+  return $categories;
 }
 
 /**
@@ -84,10 +100,17 @@ function category_node_get_categories($n
  */
 function category_get_related($cid, $key = 'cid') {
   if ($cid) {
-    $result = db_query(db_rewrite_sql('SELECT c.*, n.title, cid1, cid2 FROM {category_relation}, {category} c, {node} n WHERE (c.cid = cid1 OR c.cid = cid2) AND (cid1 = %d OR cid2 = %d) AND c.cid = n.nid AND c.cid != %d AND n.status = 1 ORDER BY c.weight, n.title'), $cid, $cid, $cid);
-    $related = array();
-    while ($category = db_fetch_object($result)) {
-      $related[$category->$key] = $category;
+    $cache_key = 'cat_get_related'. $key;
+    $related = category_cache_op('get', $cid, $cache_key);
+    if (!isset($related)) {
+
+      $result = db_query(db_rewrite_sql('SELECT c.*, n.title, cid1, cid2 FROM {category_relation}, {category} c, {node} n WHERE (c.cid = cid1 OR c.cid = cid2) AND (cid1 = %d OR cid2 = %d) AND c.cid = n.nid AND c.cid != %d AND n.status = 1 ORDER BY c.weight, n.title'), $cid, $cid, $cid);
+      $related = array();
+      while ($category = db_fetch_object($result)) {
+        $related[$category->$key] = $category;
+      }
+
+      category_cache_op('set', $cid, $cache_key, $related);
     }
     return $related;
   }
@@ -100,33 +123,26 @@ function category_get_related($cid, $key
  * Find all parents of a given category ID.
  * This function will return the parents in an indexed array sorted by the parents' weight and title.
  */
-function category_get_parents($cid, $key = 'cid', $distant = TRUE, $reset = FALSE) {
-  static $parents, $distant_parents;
-
+function category_get_parents($cid, $key = 'cid', $distant = TRUE) {
   if (!$cid) {
     return array();
   }
 
-  if (($distant && !isset($distant_parents)) || (!$distant && !isset($parents)) || $reset) {
+  $cache_key = 'cat_get_parents'. ($distant ? 'd' : 'n');
+  $parents = category_cache_op('get', 0, $cache_key);
+  if (!isset($parents)) {
+
     $distant_sql = $distant ? '' : ' AND (c.cnid = cc.cnid OR c.cnid = 0)';
     $result = db_query(db_rewrite_sql('SELECT c.*, h.cid AS child, n.title, cn.admin_title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.parent INNER JOIN {node} n ON c.cid = n.nid LEFT JOIN {category_cont} cn ON c.cid = cn.cid INNER JOIN {category} cc ON h.cid = cc.cid WHERE n.status = 1'. $distant_sql .' ORDER BY c.weight, n.title'), $cid);
-    if ($distant) {
-      $distant_parents = array();
-    }
-    else {
-      $parents = array();
-    }
+    $parents = array();
     while ($parent = db_fetch_object($result)) {
       if (empty($parent->admin_title)) {
         $parent->admin_title = $parent->title;
       }
-      if ($distant) {
-        $distant_parents[$parent->child][$parent->$key] = $parent;
-      }
-      else {
-        $parents[$parent->child][$parent->$key] = $parent;
-      }
+      $parents[$parent->child][$parent->$key] = $parent;
     }
+
+    category_cache_op('set', 0, $cache_key, $parents);
   }
 
   $no_parent = new StdClass();
@@ -137,7 +153,7 @@ function category_get_parents($cid, $key
   $no_parent->title = '';
   $no_parent->admin_title = '';
   $no_parent->child = $cid;
-  return $distant ? (isset($distant_parents[$cid]) ? $distant_parents[$cid] : array(0 => $no_parent)) : (isset($parents[$cid]) ? $parents[$cid] : array(0 => $no_parent));
+  return isset($parents[$cid]) ? $parents[$cid] : array(0 => $no_parent);
 }
 
 /**
@@ -159,26 +175,37 @@ function category_get_parents_all($cid, 
 /**
  * Find all children of a category ID.
  */
-function category_get_children($cid, $cnid = 0, $key = 'cid', $reset = FALSE) {
-  static $children;
+function category_get_children($cid, $cnid = 0, $key = 'cid') {
 
   if ($cnid) {
-    $result = db_query(db_rewrite_sql('SELECT c.*, n.title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.cid INNER JOIN {node} n ON c.cid = n.nid WHERE h.parent = %d AND n.status = 1 AND c.cnid = %d ORDER BY c.weight, n.title'), $cid, $cnid);
+    $cache_key = 'cat_get_children_cn'. $cnid .':'. $key;
+    $children = category_cache_op('get', $cid, $cache_key);
+    if (!isset($children)) {
+
+      $result = db_query(db_rewrite_sql('SELECT c.*, n.title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.cid INNER JOIN {node} n ON c.cid = n.nid WHERE h.parent = %d AND n.status = 1 AND c.cnid = %d ORDER BY c.weight, n.title'), $cid, $cnid);
+
+      $children = array();
+      while ($category = db_fetch_object($result)) {
+        $children[$category->$key] = $category;
+      }
 
-    $results = array();
-    while ($category = db_fetch_object($result)) {
-      $results[$category->$key] = $category;
+      category_cache_op('set', $cid, $cache_key, $children);
     }
-
-    return $results;
+    return $children;
   }
-  elseif (!isset($children) || $reset) {
+
+  $cache_key = 'cat_get_children_c0'. $key;
+  $children = category_cache_op('get', 0, $cache_key);
+  if (!isset($children)) {
+
     $result = db_query(db_rewrite_sql('SELECT c.*, h.parent, n.title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.cid INNER JOIN {node} n ON c.cid = n.nid WHERE n.status = 1 ORDER BY c.weight, n.title'), $cid);
 
     $children = array();
     while ($category = db_fetch_object($result)) {
       $children[$category->parent][$category->$key] = $category;
     }
+
+    category_cache_op('set', 0, $cache_key, $children);
   }
 
   return isset($children[$cid]) ? $children[$cid] : array();
@@ -209,8 +236,6 @@ function category_get_children($cid, $cn
  *   to have "depth" and "parents" attributes in addition to its normal ones.
  */
 function category_get_tree($cnid, $parent = NULL, $depth = -1, $max_depth = NULL, $distant = FALSE) {
-  static $children, $parents, $categories;
-
   if (empty($cnid) && $depth == -1) {
     $distant = TRUE;
   }
@@ -222,10 +247,20 @@ function category_get_tree($cnid, $paren
 
   // We cache trees, so it's not CPU-intensive to call get_tree() on a category
   // and its children, too.
-  if (!isset($children[$cnid])) {
+  $cache_key = 'cat_get_tree'. $depth .':'. ($distant ? 'd' : 'n');
+  $data = category_cache_op('get', $cnid, $cache_key);
+  if (isset($data)) {
+    $children = $data['children'];
+    $categories = $data['categories'];
+    $parents = $data['parents'];
+  }
+  else {
+
     $distant_sql = ($distant) ? '' : 'AND c.cnid = %d ';
 
-    $children[$cnid] = array();
+    $children = array();
+    $categories = array();
+    $parents = array();
     $result = db_query(db_rewrite_sql('SELECT c.cid, c.cnid, c.weight, c.depth, h.parent, n.title, cn.admin_title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.cid INNER JOIN {node} n ON c.cid = n.nid LEFT JOIN {category_cont} cn ON c.cid = cn.cid LEFT JOIN {category} c2 ON h.parent = c2.cid WHERE (c2.cid IS NOT NULL OR h.parent = 0) AND n.status = 1 '. $distant_sql .'ORDER BY c.weight, n.title', 'n', 'nid'), $cnid);
 
     while ($category = db_fetch_object($result)) {
@@ -236,29 +271,30 @@ function category_get_tree($cnid, $paren
         }
       }
 
-      $children[$cnid][$category->parent][] = $category->cid;
-      $categories[$cnid][$category->cid] = $category;
-      $parents[$cnid][$category->cid][] = $category->parent;
+      $children[$category->parent][] = $category->cid;
+      $categories[$category->cid] = $category;
+      $parents[$category->cid][] = $category->parent;
     }
+    category_cache_op('set', $cnid, $cache_key, array('children' => $children, 'categories' => $categories, 'parents' => $parents));
   }
 
-  $max_depth = (is_null($max_depth) && isset($children[$cnid])) ? count($children[$cnid]) : $max_depth;
+  $max_depth = (is_null($max_depth) && !empty($children)) ? count($children) : $max_depth;
   $tree = array();
-  if (!empty($children[$cnid]) && !empty($children[$cnid][$parent])) {
-    foreach ($children[$cnid][$parent] as $child) {
+  if (!empty($children) && !empty($children[$parent])) {
+    foreach ($children[$parent] as $child) {
       if ($max_depth > $depth) {
-        $cat = drupal_clone($categories[$cnid][$child]);
+        $cat = drupal_clone($categories[$child]);
         $cat->depth = $depth;
         // The "parent" attribute is not useful, as it would show one parent only.
         unset($cat->parent);
-        $cat->parents = $parents[$cnid][$child];
+        $cat->parents = $parents[$child];
 
         if (!$cat->cnid && empty($cat->admin_title)) {
           $cat->admin_title = $cat->title;
         }
         $tree[] = $cat;
 
-        if (!empty($children[$cnid][$child])) {
+        if (!empty($children[$child])) {
           $tree = array_merge($tree, category_get_tree($cnid, $child, $depth, $max_depth, $distant));
         }
       }
@@ -273,10 +309,17 @@ function category_get_tree($cnid, $paren
  */
 function category_get_synonyms($cid) {
   if ($cid) {
-    $synonyms = array();
-    $result = db_query('SELECT name FROM {category_synonym} WHERE cid = %d', $cid);
-    while ($synonym = db_fetch_array($result)) {
-      $synonyms[] = $synonym['name'];
+    $cache_key = 'cat_get_synonyms';
+    $synonyms = category_cache_op('get', $cid, $cache_key);
+    if (!isset($synonyms)) {
+
+      $synonyms = array();
+      $result = db_query('SELECT name FROM {category_synonym} WHERE cid = %d', $cid);
+      while ($synonym = db_fetch_array($result)) {
+        $synonyms[] = $synonym['name'];
+      }
+
+    category_cache_op('set', $cid, $cache_key, $synonyms);
     }
     return $synonyms;
   }
@@ -296,9 +339,9 @@ function category_get_synonym_root($syno
  * Given a category id, count the number of published nodes in it.
  */
 function category_category_count_nodes($cid, $type = 0) {
-  static $count;
-
-  if (!isset($count[$type])) {
+  $cache_key = 'cat_cat_count_nodes'. $type;
+  $count = category_cache_op('get', 0, $cache_key);
+  if (!isset($count)) {
     // $type == 0 always evaluates true if $type is a string
     if (is_numeric($type)) {
       $result = db_query(db_rewrite_sql('SELECT c.cid, COUNT(n.nid) AS cnt FROM {category_node} c INNER JOIN {node} n ON c.nid = n.nid WHERE n.status = 1 GROUP BY c.cid'));
@@ -307,27 +350,29 @@ function category_category_count_nodes($
       $result = db_query(db_rewrite_sql("SELECT c.cid, COUNT(n.nid) AS cnt FROM {category_node} c INNER JOIN {node} n ON c.nid = n.nid WHERE n.status = 1 AND n.type = '%s' GROUP BY c.cid"), $type);
     }
     while ($category = db_fetch_object($result)) {
-      $count[$type][$category->cid] = $category->cnt;
+      $count[$category->cid] = $category->cnt;
     }
+    category_cache_op('set', 0, $cache_key, $count);
   }
   $children_count = 0;
   foreach (_category_category_children($cid) as $c) {
     $children_count += category_category_count_nodes($c, $type);
   }
-  return $children_count + (isset($count[$type][$cid]) ? $count[$type][$cid] : 0);
+  return $children_count + (isset($count[$cid]) ? $count[$cid] : 0);
 }
 
 /**
  * Helper for category_category_count_nodes().
  */
 function _category_category_children($cid) {
-  static $children;
-
+  $cache_key = 'cat_cat_childr';
+  $children = category_cache_op('get', 0, $cache_key);
   if (!isset($children)) {
     $result = db_query('SELECT cid, parent FROM {category_hierarchy}');
     while ($category = db_fetch_object($result)) {
       $children[$category->parent][] = $category->cid;
     }
+    category_cache_op('set', 0, $cache_key, $children);
   }
   return isset($children[$cid]) ? $children[$cid] : array();
 }
@@ -362,15 +407,15 @@ function category_get_category_by_name($
  *
  * @return
  *   The container object with all of its metadata, if exists, NULL otherwise.
- *   Results are statically cached.
+ *   Results are cached.
  */
 function category_get_container($cnid) {
-  static $containers = array();
-
-  if (!isset($containers[$cnid])) {
+  $cache_key = 'cat_get_cont';
+  $container = category_cache_op('get', $cnid, $cache_key);
+  if (!isset($container)) {
     // Initialize so if this container does not exist, we have
     // that cached, and we will not try to load this later.
-    $containers[$cnid] = FALSE;
+    $container = FALSE;
     // Try to load the data and fill up the object.
     $result = db_query(db_rewrite_sql('SELECT n.nid, cn.*, n.title, c.weight, c.depth, ct.type, c.weight AS cont_weight, c.depth AS cont_depth FROM {category} c INNER JOIN {category_cont} cn ON c.cid = cn.cid INNER JOIN {node} n ON c.cid = n.nid LEFT JOIN {category_cont_node_types} ct ON c.cid = ct.cid WHERE c.cid = %d ORDER BY c.weight, n.title', 'n', 'nid'), $cnid);
     $node_types = array();
@@ -392,12 +437,13 @@ function category_get_container($cnid) {
         $cont->has_admin_title = TRUE;
       }
 
-      $containers[$cnid] = $cont;
+      $container = $cont;
     }
+    category_cache_op('set', $cnid, $cache_key, $container);
   }
 
   // Return NULL if this vocabulary does not exist.
-  return (isset($containers[$cnid]) ? $containers[$cnid] : NULL);
+  return (!empty($container) ? $container : NULL);
 }
 
 /**
@@ -405,20 +451,22 @@ function category_get_container($cnid) {
  *
  * @param $cid
  *   The node/category ID of the category to be retrieved.
- * @param $reset
- *   Whether or not to reset the cache of categories. Default is FALSE.
  *
  * @return
  *   Populated category object if the category is found, NULL otherwise.
  */
-function category_get_category($cid, $reset = FALSE) {
-  static $categories = array();
-
-  if (!isset($categories[$cid])) {
-    $categories[$cid] = db_fetch_object(db_query('SELECT n.nid, n.title, c.* FROM {category} c INNER JOIN {node} n ON c.cid = n.nid WHERE cid = %d', $cid));
+function category_get_category($cid) {
+  $cache_key = 'cat_get_cat';
+  $category = category_cache_op('get', $cid, $cache_key);
+  if (!isset($category)) {
+    $category = db_fetch_object(db_query('SELECT n.nid, n.title, c.* FROM {category} c INNER JOIN {node} n ON c.cid = n.nid WHERE cid = %d', $cid));
+    if (empty($category)) {
+      $category = FALSE;
+    }
+    category_cache_op('set', $cid, $cache_key, $category);
   }
 
-  return (isset($categories[$cid]) ? $categories[$cid] : NULL);
+  return (!empty($category) ? $category : NULL);
 }
 
 /**
@@ -490,8 +538,8 @@ function category_select_nodes($cids = a
 /**
  * Accepts the result of a pager_query() call, such as that performed by
  * category_select_nodes(), and formats each node along with a pager.
-*/
-function category_render_nodes($result, $category_display) {
+ */
+function category_render_nodes($result, $category_display = NULL) {
   $output = '';
   $has_rows = FALSE;
   while ($node = db_fetch_object($result)) {
@@ -534,3 +582,88 @@ function category_categories_parse_strin
   }
   return $categories;
 }
+
+/**
+ * Central caching function for all kinds of node-related category data,
+ * so that all data for a given node may be loaded by single query from
+ * cache table into static variable, and then returned by various API
+ * functions without any queries needed. The cache is stored per-node,
+ * and flushed only when given node is saved or deleted. The $nid of '0'
+ * is used for global summary-data, and flushed on every node save.
+ * While saving a category or container, we flush the whole cache, as
+ * this affects the whole hiearchy, and happens rarely. This should not
+ * be used for data affected by other factors (such as current user's
+ * permissions), unless the condition is incorporated into the cache key.
+ *
+ * @param op
+ *   Operation to be performed - either 'get', 'set', or 'flush'.
+ *
+ * @param nid
+ *   Node id to handle data for. 0 is general data, 'all' flushes all.
+ *
+ * @param key
+ *   Name of node-data subset to work with. (Usually an API function identificator.)
+ *
+ * @param data
+ *   Data for the 'set' operation (will get added to the existing per-node record).
+ *
+ * @return
+ *   Cached data, or NULL if none.
+ */
+function category_cache_op($op, $nid, $key = '', $data = NULL) {
+  static $cache;
+  if (!isset($cache)) {
+    $cache = array('n' => array(), 'g' => array());
+  }
+
+  // Flush cache if requested
+  if ($op == 'flush') {
+    if ($nid === 'all') {
+      // Total flush: Empty {cache_category} and static cache entirely
+      cache_clear_all('*', 'cache_category', TRUE);
+      $cache = array('n' => array(), 'g' => array());
+    }
+    else {
+      // Node flush: Delete entries for given node
+      cache_clear_all('n'. $nid, 'cache_category');
+      unset($cache['n'][$nid]);
+      // Delete also all entries for global data, because we don't really
+      // know how these might be affected.
+      cache_clear_all('g', 'cache_category', TRUE);
+      $cache['g'] = array();
+    }
+    return;
+  }
+
+  // For global data, we use the $key instead of $nid as identificator, for
+  // better granularity of data loading (as these are larger pieces). Both
+  // node/global parts of the cache are separated, to allow for all-global
+  // flush.
+  $part = ($nid == 0) ? 'g' : 'n';
+  $id = ($nid == 0) ? $key : $nid;
+
+  // Load existing record, if not available in static variable already.
+  if (!isset($cache[$part][$id])) {
+    $entry = cache_get($part . $id, 'cache_category');
+    if ($entry) {
+      $cache[$part][$id] = $entry->data;
+    }
+    else {
+      $cache[$part][$id] = array();
+    }
+  }
+
+  // Save new data if required.
+  if ($op == 'set') {
+    // Store serialized data, to ensure a fresh copy being always returned from
+    // static cache, without any references to the original (which might be
+    // further changed, after it got set to the cache). This is to avoid issues
+    // with PHP5 always passing and assigning objects as references, and/or with
+    // any nested by-reference elements.
+    $cache[$part][$id][$key] = serialize($data);
+    cache_set($part . $id, $cache[$part][$id], 'cache_category');
+  }
+
+  // Return data for given node/key combination.
+  return isset($cache[$part][$id][$key]) ? unserialize($cache[$part][$id][$key]) : NULL;
+}
Index: category.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/category.install,v
retrieving revision 1.21
diff -u -p -r1.21 category.install
--- category.install	31 May 2009 10:21:06 -0000	1.21
+++ category.install	5 Aug 2009 04:44:04 -0000
@@ -97,35 +97,35 @@ function _category_default_node_types() 
  */
 function category_schema() {
   $schema['category'] = array(
-    'description' => t('Stores category information.'),
+    'description' => 'Stores category information.',
     'fields' => array(
       'cid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t("Primary key: The category's {node}.nid."),
+        'description' => "Primary key: The category's {node}.nid.",
       ),
       'cnid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('The {category_cont}.cid of the container to which the category is assigned. (0 = category is a container)'),
+        'description' => 'The {category_cont}.cid of the container to which the category is assigned. (0 = category is a container)',
       ),
       'weight' => array(
         'type' => 'int',
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => t('The weight of this category in relation to other categories.'),
+        'description' => 'The weight of this category in relation to other categories.',
       ),
       'depth' => array(
         'type' => 'int',
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => t('The depth of child categories that are reflected in node listings for this category.'),
+        'description' => 'The depth of child categories that are reflected in node listings for this category.',
       ),
     ),
     'primary key' => array('cid'),
@@ -136,21 +136,21 @@ function category_schema() {
   );
 
   $schema['category_hierarchy'] = array(
-    'description' => t('Stores the hierarchical relationship between categories.'),
+    'description' => 'Stores the hierarchical relationship between categories.',
     'fields' => array(
       'cid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('Primary Key: The {category}.cid of the category.'),
+        'description' => 'Primary Key: The {category}.cid of the category.',
       ),
       'parent' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t("Primary Key: The {category}.cid of the category's parent. 0 indicates no parent."),
+        'description' => "Primary Key: The {category}.cid of the category's parent. 0 indicates no parent.",
       ),
     ),
     'primary key' => array('cid', 'parent'),
@@ -160,28 +160,28 @@ function category_schema() {
   );
 
   $schema['category_node'] = array(
-    'description' => t('Stores the relationship of categories to nodes.'),
+    'description' => 'Stores the relationship of categories to nodes.',
     'fields' => array(
       'nid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('Primary Key: The {node}.nid of the node.'),
+        'description' => 'The {node}.nid of the node.',
       ),
       'vid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('Primary Key: The {node}.vid of the node.'),
+        'description' => 'Primary Key: The {node}.vid of the node.',
       ),
       'cid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('Primary Key: The {category}.cid of a category assigned to the node.'),
+        'description' => 'Primary Key: The {category}.cid of a category assigned to the node.',
       ),
     ),
     'primary key' => array('cid', 'vid'),
@@ -192,26 +192,26 @@ function category_schema() {
   );
 
   $schema['category_relation'] = array(
-    'description' => t('Stores non-hierarchical relationships between categories.'),
+    'description' => 'Stores non-hierarchical relationships between categories.',
     'fields' => array(
       'crid' => array(
         'type' => 'serial',
         'not null' => TRUE,
-        'description' => t('Primary Key: Unique category relation ID.'),
+        'description' => 'Primary Key: Unique category relation ID.',
       ),
       'cid1' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('The {category}.cid of the first category in a relationship.'),
+        'description' => 'The {category}.cid of the first category in a relationship.',
       ),
       'cid2' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('The {category}.cid of the second category in a relationship.'),
+        'description' => 'The {category}.cid of the second category in a relationship.',
       ),
     ),
     'primary key' => array('crid'),
@@ -224,26 +224,26 @@ function category_schema() {
   );
 
   $schema['category_synonym'] = array(
-    'description' => t('Stores category synonyms.'),
+    'description' => 'Stores category synonyms.',
     'fields' => array(
       'csid' => array(
         'type' => 'serial',
         'not null' => TRUE,
-        'description' => t('Primary Key: Unique category synonym ID.'),
+        'description' => 'Primary Key: Unique category synonym ID.',
       ),
       'cid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('The {category}.cid of the category.'),
+        'description' => 'The {category}.cid of the category.',
       ),
       'name' => array(
         'type' => 'varchar',
         'length' => 255,
         'not null' => TRUE,
         'default' => '',
-        'description' => t('The name of the synonym.'),
+        'description' => 'The name of the synonym.',
       ),
     ),
     'primary key' => array('csid'),
@@ -254,35 +254,35 @@ function category_schema() {
   );
 
   $schema['category_cont'] = array(
-    'description' => t('Stores container information.'),
+    'description' => 'Stores container information.',
     'fields' => array(
       'cid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('Primary Key: The {category}.cid of the container.'),
+        'description' => 'Primary Key: The {category}.cid of the container.',
       ),
       'admin_title' => array(
         'type' => 'varchar',
         'length' => 255,
         'not null' => TRUE,
         'default' => '',
-        'description' => t('Title of the container for administrative purposes.'),
+        'description' => 'Title of the container for administrative purposes.',
       ),
       'help' => array(
         'type' => 'varchar',
         'length' => 255,
         'not null' => TRUE,
         'default' => '',
-        'description' => t('Help text to display for the container.'),
+        'description' => 'Help text to display for the container.',
       ),
       'module' => array(
         'type' => 'varchar',
         'length' => 255,
         'not null' => TRUE,
         'default' => '',
-        'description' => t('The module which created the container.'),
+        'description' => 'The module which created the container.',
       ),
       'relations' => array(
         'type' => 'int',
@@ -290,7 +290,7 @@ function category_schema() {
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => t('Whether or not related categories are enabled within the container. (0 = disabled, 1 = enabled)'),
+        'description' => 'Whether or not related categories are enabled within the container. (0 = disabled, 1 = enabled)',
       ),
       'synonyms' => array(
         'type' => 'int',
@@ -298,7 +298,7 @@ function category_schema() {
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => t('Whether or not synonyms for categories are enabled within the container. (0 = disabled, 1 = enabled)'),
+        'description' => 'Whether or not synonyms for categories are enabled within the container. (0 = disabled, 1 = enabled)',
       ),
       'hierarchy' => array(
         'type' => 'int',
@@ -306,7 +306,7 @@ function category_schema() {
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => t('The type of hierarchy allowed within the container. (0 = disabled, 1 = single, 2 = multiple)'),
+        'description' => 'The type of hierarchy allowed within the container. (0 = disabled, 1 = single, 2 = multiple)',
       ),
       'multiple' => array(
         'type' => 'int',
@@ -314,7 +314,7 @@ function category_schema() {
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => t('Whether or not multiple categories from this container may be assigned to a node. (0 = disabled, 1 = enabled)'),
+        'description' => 'Whether or not multiple categories from this container may be assigned to a node. (0 = disabled, 1 = enabled)',
       ),
       'required' => array(
         'type' => 'int',
@@ -322,7 +322,7 @@ function category_schema() {
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => t('Whether or not categories are required for nodes using this container. (0 = disabled, 1 = enabled)'),
+        'description' => 'Whether or not categories are required for nodes using this container. (0 = disabled, 1 = enabled)',
       ),
       'tags' => array(
         'type' => 'int',
@@ -330,7 +330,7 @@ function category_schema() {
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => t('Whether or not free tagging is enabled for the container. (0 = disabled, 1 = enabled)'),
+        'description' => 'Whether or not free tagging is enabled for the container. (0 = disabled, 1 = enabled)',
       ),
       'hidden_cont' => array(
         'type' => 'int',
@@ -338,14 +338,14 @@ function category_schema() {
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => t('Whether or not this container is hidden from your category hierarchy for end-users. (0 = disabled, 1 = enabled)'),
+        'description' => 'Whether or not this container is hidden from your category hierarchy for end-users. (0 = disabled, 1 = enabled)',
       ),
       'allowed_parent' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t("The {category}.cid of a container whose categories the categories in this container may have as parents. (0 = this container or a category within it is an allowed parent)"),
+        'description' => "The {category}.cid of a container whose categories the categories in this container may have as parents. (0 = this container or a category within it is an allowed parent)",
       ),
     ),
     'primary key' => array('cid'),
@@ -356,21 +356,21 @@ function category_schema() {
   );
 
   $schema['category_cont_node_types'] = array(
-    'description' => t("Stores which node types containers may be used with. (I.e. categories in container 'cid' may be used to tag content of type 'type'.)"),
+    'description' => "Stores which node types containers may be used with. (I.e. categories in container 'cid' may be used to tag content of type 'type'.)",
     'fields' => array(
       'cid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('Primary Key: the {category}.cid of the container.'),
+        'description' => 'Primary Key: the {category}.cid of the container.',
       ),
       'type' => array(
         'type' => 'varchar',
         'length' => 32,
         'not null' => TRUE,
         'default' => '',
-        'description' => t('The {node_type}.type of the node type for which the container may be used.'),
+        'description' => 'The {node_type}.type of the node type for which the container may be used.',
       ),
     ),
     'primary key' => array('cid', 'type'),
@@ -379,6 +379,9 @@ function category_schema() {
     ),
   );
 
+  $schema['cache_category'] = drupal_get_schema_unprocessed('system', 'cache');
+  $schema['cache_category']['description'] = 'Cache table for category module, to store various preprocessed data, both per-node and global.';
+
   return $schema;
 }
 
@@ -478,3 +481,81 @@ function category_update_6001() {
   }
   return $ret;
 }
+
+/**
+ * Uninstall and remove Menu wrapper module, if present. The wrapper only existed in
+ * 6.x releases 6.x-2.0-beta3 and below.
+ */
+function category_update_6002() {
+  $ret = array();
+
+  $wrapper_path = drupal_get_path('module', 'category') .'/wrappers/menu';
+  if (is_dir($wrapper_path)) {
+    $success = TRUE;
+    $files = array('menu.admin.inc', 'menu.info', 'menu.info.php', 'menu.install', 'menu.install.php', 'menu.module', 'menu.module.php');
+    foreach ($files as $file) {
+      $filepath = $wrapper_path .'/'. $file;
+      if (is_file($filepath) && !@unlink($filepath)) {
+        $success = FALSE;
+      }
+    }
+    if (!@rmdir($wrapper_path)) {
+      $success = FALSE;
+    }
+
+    drupal_flush_all_caches();
+    module_rebuild_cache();
+
+    if (!$success) {
+      drupal_set_message("The attempt to remove <em>$wrapper_path</em> directory and all it's contents failed. Please ensure that the webserver have write permissions to the <em>wrappers</em> directory and below, and re-run the Category module's update 6002. If that's not possible, you might need to remove the files manually.", 'error');
+    }
+    $ret[] = array('success' => $success, 'query' => "Uninstall Menu wrapper, and remove $wrapper_path directory.");
+  }
+
+  return $ret;
+}
+
+/**
+ * Add cache table.
+ */
+function category_update_6003() {
+  $ret = array();
+
+  // This is verbatim from system module (minus descriptions), to avoid call to
+  // drupal_get_schema_unprocessed() which may change in future, per schema/updates
+  // guidelines.
+  $schema = array(
+    'fields' => array(
+      'cid' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => ''),
+      'data' => array(
+        'type' => 'blob',
+        'not null' => FALSE,
+        'size' => 'big'),
+      'expire' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0),
+      'created' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0),
+      'headers' => array(
+        'type' => 'text',
+        'not null' => FALSE),
+      'serialized' => array(
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 0)
+      ),
+    'indexes' => array('expire' => array('expire')),
+    'primary key' => array('cid'),
+  );
+
+  db_create_table($ret, 'cache_category', $schema);
+  return $ret;
+}
Index: category.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/category.module,v
retrieving revision 1.161
diff -u -p -r1.161 category.module
--- category.module	31 May 2009 10:06:26 -0000	1.161
+++ category.module	5 Aug 2009 04:44:05 -0000
@@ -37,6 +37,13 @@ function category_theme() {
 }
 
 /**
+ * Implementation of hook_flush_caches()
+ */
+function category_flush_caches() {
+  return array('cache_category');
+}
+
+/**
  * Implementation of hook_link().
  *
  * This hook is extended with $type = 'categories' to allow themes to
@@ -376,6 +383,7 @@ function category_form_alter(&$form, $fo
     $node = $form['#node'];
 
     if (isset($node->category)) {
+      $form['#submit'][] = 'category_node_form_submit';
       _category_add_form_elements($form, $node);
       $form['category']['hierarchy']['pick-container'] = array(
         '#type' => 'submit',
@@ -562,6 +570,68 @@ function category_form_alter(&$form, $fo
 }
 
 /**
+ * Additional form submit handler for category/container node forms.
+ *
+ * Flattens and processes the $form_state['values']['category'] array
+ * on both submits and previews. This is needed because
+ * the 'category' fieldset on the node form has to be 'tree = TRUE', but
+ * we don't want to deal with the fieldset hierarchy all the time.
+ */
+function category_node_form_submit($form, &$form_state) {
+  $fieldsets = array(
+    'hierarchy' => TRUE,
+    'advanced' => TRUE,
+    'identification' => TRUE,
+    'content_types' => TRUE,
+    'tagging' => TRUE,
+    'distant' => TRUE,
+  );
+  foreach ($form_state['values']['category'] as $key => $value) {
+    if (isset($fieldsets[$key]) && is_array($value)) {
+      $form_state['values']['category'] += $value;
+      unset($form_state['values']['category'][$key]);
+    }
+  }
+
+  if (!empty($form_state['values']['category']['cid']) && $form_state['values']['category']['cid'] == 'new') {
+    unset($form_state['values']['category']['cid']);
+  }
+
+  // Load the new parent for distant-parent category selection.
+  if (!empty($form_state['values']['op'])) {
+    $containers = category_get_containers($node->type);
+    $is_valid_submission = FALSE;
+    foreach ($containers as $container) {
+      if ($form_state['values']['op'] == t('Update children of @parent', array('@parent' => $container->title))) {
+        $is_valid_submission = TRUE;
+      }
+    }
+
+    if ($is_valid_submission) {
+      foreach ($containers as $container) {
+        if (!empty($form_state['values']['categories'][$container->cid])) {
+          $post_categories = $form_state['values']['categories'][$container->cid];
+          if (!is_array($post_categories)) {
+            if (is_numeric($post_categories)) {
+              $post_categories = (int) $post_categories;
+              $node->categories[$post_categories] = category_get_category($post_categories);
+            }
+          }
+          else {
+            foreach ($post_categories as $post_category) {
+              if (is_numeric($post_category)) {
+                $post_category = (int) $post_category;
+                $node->categories[$post_category] = category_get_category($post_category);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
  * Generate a form element for selecting categories from a container.
  */
 function category_form($cnid, $value = 0, $help = NULL, $name = 'category',
@@ -753,6 +823,7 @@ function category_node_save($node, $cate
  */
 function category_node_delete($node) {
   db_query('DELETE FROM {category_node} WHERE nid = %d', $node->nid);
+  category_cache_op('flush', $node->nid);
 }
 
 /**
@@ -760,6 +831,7 @@ function category_node_delete($node) {
  */
 function category_node_delete_revision($node) {
   db_query('DELETE FROM {category_node} WHERE vid = %d', $node->vid);
+  category_cache_op('flush', $node->nid);
 }
 
 /**
@@ -826,14 +898,9 @@ function category_nodeapi(&$node, $op, $
     case 'prepare':
       $behavior = variable_get('category_behavior_'. $node->type, 0);
       if (!empty($behavior)) {
-        $container_nid = $_POST['category']['hierarchy']['container'];
-
         // Prepare defaults for the add/edit form.
         if (empty($node->category)) {
           $node->category = array();
-          if (!empty($container_nid) && is_numeric($container_nid)) {
-            $node->category['container'] = $container_nid;
-          }
           if (empty($node->nid) && isset($_GET['parent']) &&
           is_numeric($_GET['parent'])) {
             // Handle "Add child page" links:
@@ -860,10 +927,6 @@ function category_nodeapi(&$node, $op, $
           $node->category += _category_defaults(!empty($node->nid) ? $node->nid : 'new');
         }
         else {
-          _category_flatten_values($node);
-          if (!empty($container_nid) && is_numeric($container_nid)) {
-            $node->category['container'] = $container_nid;
-          }
           $node->category['container'] = empty($node->category['container']) ? $node->category['cnid'] : $node->category['container'];
           if (empty($node->category['container']) && !empty($node->category['parents']) && $behavior == 'container') {
             reset($node->category['parents']);
@@ -879,43 +942,6 @@ function category_nodeapi(&$node, $op, $
           $node->category['parent_depth_limit'] = (MENU_MAX_DEPTH - 1);
         }
         $node->category['behavior'] = $behavior;
-        if (empty($node->category['parents'])) {
-          $node->category['parents'] = array();
-        }
-        $node->category['parents'] = array_keys($node->category['parents']);
-      }
-
-      // Load the new parent for distant-parent category selection.
-      if (!empty($_POST['op'])) {
-        $containers = category_get_containers($node->type);
-        $is_valid_submission = FALSE;
-        foreach ($containers as $container) {
-          if ($_POST['op'] == t('Update children of @parent', array('@parent' => $container->title))) {
-            $is_valid_submission = TRUE;
-          }
-        }
-
-        if ($is_valid_submission) {
-          foreach ($containers as $container) {
-            if (!empty($_POST['categories'][$container->cid])) {
-              $post_categories = $_POST['categories'][$container->cid];
-              if (!is_array($post_categories)) {
-                if (is_numeric($post_categories)) {
-                  $post_categories = (int) $post_categories;
-                  $node->categories[$post_categories] = category_get_category($post_categories);
-                }
-              }
-              else {
-                foreach ($post_categories as $post_category) {
-                  if (is_numeric($post_category)) {
-                    $post_category = (int) $post_category;
-                    $node->categories[$post_category] = category_get_category($post_category);
-                  }
-                }
-              }
-            }
-          }
-        }
       }
       break;
 
@@ -923,7 +949,6 @@ function category_nodeapi(&$node, $op, $
     case 'update':
       $behavior = variable_get('category_behavior_'. $node->type, 0);
       if (!empty($behavior)) {
-        _category_flatten_values($node);
         if ($behavior == 'category') {
           category_save_category($node);
         }
@@ -1066,6 +1091,7 @@ function category_save_container(&$node)
     }
   }
 
+  category_cache_op('flush', 'all');
   return $status;
 }
 
@@ -1095,6 +1121,7 @@ function category_del_container($cid) {
     }
   }
 
+  category_cache_op('flush', 'all');
   return SAVED_DELETED;
 }
 
@@ -1210,6 +1237,7 @@ function category_save_category(&$node) 
     }
   }
 
+  category_cache_op('flush', 'all');
   return $status;
 }
 
@@ -1228,6 +1256,7 @@ function category_del_category($cid) {
   db_query('DELETE FROM {category_synonym} WHERE cid = %d', $cid);
   db_query('DELETE FROM {category_node} WHERE cid = %d', $cid);
 
+  category_cache_op('flush', 'all');
   return SAVED_DELETED;
 }
 
@@ -1345,7 +1374,13 @@ function _category_add_form_elements(&$f
   // Need this for AJAX.
   $form['#cache'] = TRUE;
   drupal_add_js("if (Drupal.jsEnabled) { $(document).ready(function() { $('#edit-category-hierarchy-pick-container').css('display', 'none'); }); }", 'inline');
-  $is_container = ($node->category['behavior'] == 'container');
+  $node->category['behavior'] = variable_get('category_behavior_'. $node->type, 0);
+  $is_container = ($node->category['behavior'] === 'container');
+
+  if (empty($node->category['parents'])) {
+    $node->category['parents'] = array();
+  }
+  $node->category['parents'] = array_keys($node->category['parents']);
 
   $form['category'] = array(
     '#type' => 'fieldset',
@@ -1356,7 +1391,7 @@ function _category_add_form_elements(&$f
     '#tree' => TRUE,
     '#attributes' => array('class' => 'category-form'),
   );
-  foreach (array('cid', 'original_container', 'parent_depth_limit') as $key) {
+  foreach (array('cid', 'original_container', 'parent_depth_limit', 'behavior') as $key) {
     $form['category'][$key] = array(
       '#type' => 'value',
       '#value' => $node->category[$key],
@@ -1603,15 +1638,15 @@ function category_form_category_submit($
   $container->parents = _category_flatten_parents(category_get_parents($container->cid));
 
   if (!$container->tags) {
-    if (empty($form_state['values']['category']['hierarchy']['parents']) || !is_array($form_state['values']['category']['hierarchy']['parents'])) {
-      $form_state['values']['category']['hierarchy']['parents'] = array();
+    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']['hierarchy']['parents']);
+    $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']['hierarchy']['parents']);
+      $first_parent = current($form_state['values']['category']['parents']);
       if (!empty($first_parent)) {
         if (category_is_root_parent($first_parent, $container->cid)) {
           $current_parent_count--;
@@ -1631,12 +1666,10 @@ function category_form_category_submit($
     // parents of every category in the container value.
     $new_container = NULL;
     if ($current_parent_count < $previous_parent_count && $current_parent_count < 2) {
-      $changed_category = (object) $form_state['values'];
-      _category_flatten_values($changed_category);
-      $changed_category = $changed_category->category;
+      $changed_category = $form_state['values']['category'];
       category_check_container_hierarchy((array) $container, $changed_category);
-      if ($form['#node']->category['container'] != $form_state['values']['category']['hierarchy']['container']) {
-        $new_container = category_get_container($form_state['values']['category']['hierarchy']['container']);
+      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);
       }
@@ -1672,7 +1705,6 @@ function category_form_category_submit($
  *   Rendered node listing, or FALSE if no listing should be displayed.
  */
 function category_node_listing($node) {
-  $nodes = category_select_nodes(array($node->category['cid']), 'or', $node->category['depth'], TRUE, TRUE);
   $is_container = empty($node->category['cnid']);
   $show_nodes = FALSE;
   $category_display = NULL;
@@ -1710,9 +1742,10 @@ function category_node_listing($node) {
     }
 
     if ($use_views) {
-      return category_views_render_nodes($nodes);
+      return category_views_render_nodes($node);
     }
     else {
+      $nodes = category_select_nodes(array($node->category['cid']), 'or', $node->category['depth'], TRUE, TRUE);
       return category_render_nodes($nodes, $category_display);
     }
   }
@@ -1738,7 +1771,7 @@ function category_help($path, $arg) {
       $output .= '<p>'. t('Use the list below to configure and review the containers defined on your site, and to manage the categories they contain. A container may (optionally) be tied to specific content types as shown in the <em>Type</em> column and, if so, will be displayed when creating or editing posts of that type. Multiple containers tied to the same content type will be displayed in the order shown below. To change the order of a container, grab a drag-and-drop handle under the <em>Name</em> column and drag it to a new location in the list. (Grab a handle by clicking and holding the mouse while hovering over a handle icon.) Remember that your changes will not be saved until you click the <em>Save</em> button at the bottom of the page.') .'</p>';
       return $output;
     case 'admin/content/category/wrappers':
-      $output = '<p>'. t('You can install and uninstall the taxonomy, book, and menu wrapper modules using the links below. It is very important that you have taxonomy, book, or menu (original or wrapper, whichever is installed) <strong>enabled</strong> on the <a href="@module-admin-page">module administration page</a> before performing an install or uninstall. Additionally, you should make sure that your web server has write permission on the file system, or the scripts may be denied access to rename the necessary files.', array('@module-admin-page' => url('admin/build/modules'))) .'</p>';
+      $output = '<p>'. t('You can install and uninstall the taxonomy and book wrapper modules using the links below. It is very important that you have taxonomy or book (original or wrapper, whichever is installed) <strong>enabled</strong> on the <a href="@module-admin-page">module administration page</a> before performing an install or uninstall. Additionally, you should make sure that your web server has write permission on the file system, or the scripts may be denied access to rename the necessary files.', array('@module-admin-page' => url('admin/build/modules'))) .'</p>';
       $output .= '<p>'. t('When performing an install, the scripts will look for the <code>modulename.module.php</code>, <code>modulename.info.php</code> and <code>modulename.install.php</code> files in the <code>wrappers/modulename</code> directory (in your category package), and it will rename them respectively to <code>modulename.module</code>, <code>modulename.info</code> and <code>modulename.install</code>. It will then update Drupal\'s module registry so that the new files are discovered. The reverse will happen when performing an uninstall. The script will determine the correct location of all necessary files automatically, based on where you have installed the category module.') .'</p>';
       $output .= '<p>'. t('After the operation that you invoke is completed, you will be returned to this page.') .'</p>';
       return $output;
@@ -1809,7 +1842,7 @@ function category_is_root_parent($parent
  * Determines whether or not the specified wrapper is enabled.
  *
  * @param $type
- *   The wrapper to check. Either 'taxonomy', 'book', or 'menu'. Default is 'taxonomy'.
+ *   The wrapper to check. Either 'taxonomy', or 'book'. Default is 'taxonomy'.
  *
  * @return
  *   Boolean value indicating status of the specified wrapper.
@@ -1861,32 +1894,6 @@ function _category_allowed_containers_op
 }
 
 /**
- * Helper function to flatten the $node->category array. This is needed because
- * the 'category' fieldset on the node form has to be 'tree = TRUE', but
- * we don't want to deal with the fieldset hierarchy all the time.
- */
-function _category_flatten_values(&$node) {
-  $fieldsets = array(
-    'hierarchy' => TRUE,
-    'advanced' => TRUE,
-    'identification' => TRUE,
-    'content_types' => TRUE,
-    'tagging' => TRUE,
-    'distant' => TRUE,
-  );
-  foreach ($node->category as $key => $value) {
-    if (isset($fieldsets[$key]) && is_array($value)) {
-      $node->category += $value;
-      unset($node->category[$key]);
-    }
-  }
-
-  if (!empty($node->category['cid']) && $node->category['cid'] == 'new') {
-    unset($node->category['cid']);
-  }
-}
-
-/**
  * Turns the return value of category_get_parents() into a simple array,
  * where both the keys and the values of each element are the node IDs of
  * the parent categories.
Index: category.pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/category.pages.inc,v
retrieving revision 1.5
diff -u -p -r1.5 category.pages.inc
--- category.pages.inc	5 Mar 2009 22:23:54 -0000	1.5
+++ category.pages.inc	5 Aug 2009 04:44:05 -0000
@@ -178,7 +178,7 @@ function category_wrapper($type, $op, $g
   $generic_error = ' '. t('Unable to perform the specified operation.');
 
   // Various validation checks
-  if (!($type == 'taxonomy' || $type == 'book' || $type == 'menu') || !($op == 'install' || $op == 'uninstall')) {
+  if (!($type == 'taxonomy' || $type == 'book') || !($op == 'install' || $op == 'uninstall')) {
     drupal_set_message(t('Invalid parameters supplied to wrapper install / uninstall script.') . $generic_error, 'error');
     drupal_goto($goto);
   }
Index: category_display/category_display.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/category_display/category_display.install,v
retrieving revision 1.9
diff -u -p -r1.9 category_display.install
--- category_display/category_display.install	21 Jul 2008 17:48:50 -0000	1.9
+++ category_display/category_display.install	5 Aug 2009 04:44:05 -0000
@@ -27,14 +27,14 @@ function category_display_uninstall() {
  */
 function category_display_schema() {
   $schema['category_display'] = array(
-    'description' => t('Stores display settings for containers.'),
+    'description' => 'Stores display settings for containers.',
     'fields' => array(
       'cid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('Primary Key: The {category}.cid of the container.'),
+        'description' => 'Primary Key: The {category}.cid of the container.',
       ),
       'navlinks' => array(
         'type' => 'int',
@@ -42,6 +42,7 @@ function category_display_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
+        'description' => 'Whether or not navigation links (prev/next/up) should be shown on category pages within this container.',
       ),
       'toc_for_cats' => array(
         'type' => 'int',
@@ -49,12 +50,14 @@ function category_display_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
+        'description' => 'Whether or not the Table-of-contents tree should be shown on category pages within this container.',
       ),
       'toc_depth' => array(
         'type' => 'int',
         'size' => 'small',
         'not null' => TRUE,
         'default' => 0,
+        'description' => 'Depth of the Table-of-contents tree (-1 for infinite).',
       ),
       'toc_nodecount' => array(
         'type' => 'int',
@@ -62,6 +65,7 @@ function category_display_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
+        'description' => 'Whether or not the Table-of-contents tree should show counts of assigned nodes.',
       ),
       'nodelinks' => array(
         'type' => 'int',
@@ -69,6 +73,7 @@ function category_display_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
+        'description' => 'Whether or not to show links to assigned categories on viewed nodes.',
       ),
       'show_listing' => array(
         'type' => 'int',
@@ -76,6 +81,7 @@ function category_display_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
+        'description' => 'Whether or not to show lists of assigned content on category pages within this container.',
       ),
       'emptymsg' => array(
         'type' => 'int',
@@ -83,6 +89,7 @@ function category_display_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
+        'description' => 'Whether or not to show a message, if no content is assigned to a category.',
       ),
     ),
     'primary key' => array('cid'),
Index: category_display/category_display.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/category_display/category_display.module,v
retrieving revision 1.39
diff -u -p -r1.39 category_display.module
--- category_display/category_display.module	31 May 2009 09:15:03 -0000	1.39
+++ category_display/category_display.module	5 Aug 2009 04:44:06 -0000
@@ -69,24 +69,14 @@ function category_display_nodeapi(&$node
 
     case 'prepare':
       $behavior = variable_get('category_behavior_'. $node->type, 0);
-      if (!empty($behavior)) {
-        $links_for_cats = $_POST['category']['menu']['links_for_cats'];
-        $toc_for_cats = $_POST['category']['display']['toc_for_cats'];
-        $show_listing = $_POST['category']['display']['show_listing'];
+      if (!empty($behavior) && $behavior == 'container') {
 
         // Prepare defaults for the add/edit form.
         if (empty($node->category_menu)) {
           $node->category_menu = array();
         }
-
-        if (isset($_POST['category']['menu']['links_for_cats'])) {
-          $node->category_menu['links_for_cats'] = $links_for_cats;
-        }
-        if (isset($_POST['category']['display']['toc_for_cats'])) {
-          $node->category_display['toc_for_cats'] = $toc_for_cats;
-        }
-        if (isset($_POST['category']['display']['show_listing'])) {
-          $node->category_display['show_listing'] = $show_listing;
+        if (empty($node->category_display)) {
+          $node->category_display = _category_display_defaults();
         }
       }
       break;
@@ -94,8 +84,6 @@ function category_display_nodeapi(&$node
     case 'insert':
     case 'update':
       $behavior = variable_get('category_behavior_'. $node->type, 0);
-      _category_display_flatten_values($node);
-
       if (!empty($behavior) && $behavior == 'container') {
         category_display_save_container($node);
       }
@@ -125,6 +113,7 @@ function category_display_form_alter(&$f
 
     if (!empty($behavior) && $behavior == 'container') {
       $form['category']['display'] = _category_display_form_elements($node);
+      $form['#submit'][] = 'category_display_node_form_submit';
     }
   }
 }
@@ -278,6 +267,31 @@ function _category_display_form_elements
 }
 
 /**
+ * Additional form submit handler for node forms.
+ *
+ * Flattens the $form_state['values']['category']['display'] array
+ * on both submits and previews.
+ */
+function category_display_node_form_submit($form, &$form_state) {
+  $fieldsets = array(
+    'display' => TRUE,
+  );
+  if (!empty($form_state['values']['category']) && is_array($form_state['values']['category'])) {
+    foreach ($form_state['values']['category'] as $key => $value) {
+      if (isset($fieldsets[$key]) && is_array($value)) {
+        $fieldset = 'category_'. $key;
+        if (empty($form_state['values'][$fieldset])) {
+          $form_state['values'][$fieldset] = array();
+        }
+        $form_state['values'][$fieldset] = $value + $form_state['values'][$fieldset];
+
+        unset($form_state['values']['category'][$key]);
+      }
+    }
+  }
+}
+
+/**
  * Implementation of hook_category_legacy().
  */
 function category_display_category_legacy($op, &$vocabularies, $edit = NULL) {
@@ -307,11 +321,19 @@ function category_display_category_legac
  *   The display settings for the specified node.
  */
 function category_display_get_container($nid) {
-  $result = db_query("SELECT * FROM {category_display} WHERE cid = %d", $nid);
+  $cache_key = 'cdisp_get_cont';
+  $container = category_cache_op('get', $nid, $cache_key);
+  if (!isset($container)) {
 
-  if ($container = db_fetch_object($result)) {
-    return $container;
+    $result = db_query("SELECT * FROM {category_display} WHERE cid = %d", $nid);
+    $container = db_fetch_object($result);
+    if (empty($container)) {
+      $container = (object) _category_display_defaults();
+    }
+
+    category_cache_op('set', $nid, $cache_key, $container);
   }
+  return $container;
 }
 
 /**
@@ -334,6 +356,7 @@ function category_display_save_container
     $status = SAVED_UPDATED;
   }
 
+  category_cache_op('flush', $node->nid);
   return $status;
 }
 
@@ -346,6 +369,22 @@ function category_display_save_container
  */
 function category_display_del_container($nid) {
   db_query('DELETE FROM {category_display} WHERE cid = %d', $nid);
+  category_cache_op('flush', $nid);
+}
+
+/**
+ * Helper function to provide default settings for new containers.
+ */
+function _category_display_defaults() {
+  return array(
+    'navlinks'      => 0,
+    'toc_for_cats'  => 0,
+    'toc_depth'     => 0,
+    'toc_nodecount' => 0,
+    'nodelinks'     => 1,
+    'show_listing'  => 1,
+    'emptymsg'      => 1,
+  );
 }
 
 /**
@@ -477,85 +516,93 @@ function category_display_next($book_lin
  * Format the menu links for the child pages of the current page.
  */
 function category_display_children($book_link, $container_info) {
-  $book_link_orig = $book_link;
+  $cache_key = 'cdisp_children'. $container_info->cid .':'. $book_link['mlid'];
+  $output = category_cache_op('get', 0, $cache_key);
+  if (!isset($output)) {
 
-  // If the TOC has more than one level, then we need to find a menu item with
-  // a specific depth. For example, if we want the TOC to be five levels deep,
-  // then we need to find a menu item at the fifth level of the hierarchy (or
-  // as close to it as possible). This menu item then gets passed to
-  // category_menu_tree_all_data(), so that it retrieves a menu hierarchy
-  // with the depth we need.
-  if ($container_info->toc_depth != 0 && isset($book_link)) {
-    if ($container_info->toc_depth == -1 || $container_info->toc_depth > MENU_MAX_DEPTH) {
-      $container_info->toc_depth = MENU_MAX_DEPTH;
-    }
-    $container_info->toc_depth += $book_link['depth'];
-    if ($book_link_new = _category_display_children_recurse($book_link, $container_info)) {
-      $book_link = $book_link_new;
+    $book_link_orig = $book_link;
+
+    // If the TOC has more than one level, then we need to find a menu item with
+    // a specific depth. For example, if we want the TOC to be five levels deep,
+    // then we need to find a menu item at the fifth level of the hierarchy (or
+    // as close to it as possible). This menu item then gets passed to
+    // category_menu_tree_all_data(), so that it retrieves a menu hierarchy
+    // with the depth we need.
+    if ($container_info->toc_depth != 0 && isset($book_link)) {
+      if ($container_info->toc_depth == -1 || $container_info->toc_depth > MENU_MAX_DEPTH) {
+        $container_info->toc_depth = MENU_MAX_DEPTH;
+      }
+      $container_info->toc_depth += $book_link['depth'];
+      if ($book_link_new = _category_display_children_recurse($book_link, $container_info)) {
+        $book_link = $book_link_new;
+      }
     }
-  }
 
-  $book_link = $book_link_orig;
+    $book_link = $book_link_orig;
 
-  $tree = array();
+    $tree = array();
 
-  if (isset($book_link)) {
-    $tree = category_menu_tree_all_data($book_link_orig['menu_name'], $book_link, $container_info->toc_depth);
-  }
-  // When no menu link is provided to this function, we're generating a TOC
-  // for a hidden container. This is typically useful for things such as a site
-  // map. All of the below code is for the special case of hidden containers
-  // that have no menu link.
-  else {
-    $children = category_get_children($container_info->cid);
-    $containers_map = array();
-    if (!empty($children)) {
-      foreach (array_keys($children) as $child_cid) {
-        $child_cnid = !empty($children[$child_cid]->cnid) ? $children[$child_cid]->cnid : $child_cid;
-        $child_menu = category_menu_get_container($child_cnid);
-        $child_display = category_display_get_container($child_cnid);
-        $child_menu_map = _category_display_children_get_menu_map($children[$child_cid], $child_cnid);
-
-        if ($container_info->toc_depth != 0) {
-          if ($container_info->toc_depth == -1 || $container_info->toc_depth > MENU_MAX_DEPTH) {
-            $container_info->toc_depth = MENU_MAX_DEPTH;
+    if (isset($book_link)) {
+      $tree = category_menu_tree_all_data($book_link_orig['menu_name'], $book_link, $container_info->toc_depth);
+    }
+    // When no menu link is provided to this function, we're generating a TOC
+    // for a hidden container. This is typically useful for things such as a site
+    // map. All of the below code is for the special case of hidden containers
+    // that have no menu link.
+    else {
+      $children = category_get_children($container_info->cid);
+      $containers_map = array();
+      if (!empty($children)) {
+        foreach (array_keys($children) as $child_cid) {
+          $child_cnid = !empty($children[$child_cid]->cnid) ? $children[$child_cid]->cnid : $child_cid;
+          $child_menu = category_menu_get_container($child_cnid);
+          $child_display = category_display_get_container($child_cnid);
+          $child_menu_map = _category_display_children_get_menu_map($children[$child_cid], $child_cnid);
+
+          if ($container_info->toc_depth != 0) {
+            if ($container_info->toc_depth == -1 || $container_info->toc_depth > MENU_MAX_DEPTH) {
+              $container_info->toc_depth = MENU_MAX_DEPTH;
+            }
+            $container_info->toc_depth += $child_menu_map['depth'];
+            $container_info->toc_depth--;
+            if ($child_menu_map_new = _category_display_children_recurse($child_menu_map, $container_info, 1, TRUE)) {
+              $child_menu_map = $child_menu_map_new;
+            }
           }
-          $container_info->toc_depth += $child_menu_map['depth'];
-          $container_info->toc_depth--;
-          if ($child_menu_map_new = _category_display_children_recurse($child_menu_map, $container_info, 1, TRUE)) {
-            $child_menu_map = $child_menu_map_new;
+          else {
+            $container_info->toc_depth = $child_menu_map['depth'];
+            $container_info->toc_depth--;
           }
-        }
-        else {
-          $container_info->toc_depth = $child_menu_map['depth'];
-          $container_info->toc_depth--;
-        }
 
-        if ($child_menu->links_for_cats && !isset($containers_map[$child_cnid])) {
-          $tree_new = category_menu_tree_all_data('category-menu-toc-'. $child_cnid, $child_menu_map, $container_info->toc_depth);
-          _category_display_children_prune_tagged_content($tree_new, $container_info);
-          $tree += $tree_new;
-          $containers_map[$child_cnid] = TRUE;
+          if ($child_menu->links_for_cats && !isset($containers_map[$child_cnid])) {
+            $tree_new = category_menu_tree_all_data('category-menu-toc-'. $child_cnid, $child_menu_map, $container_info->toc_depth);
+            _category_display_children_prune_tagged_content($tree_new, $container_info);
+            $tree += $tree_new;
+            $containers_map[$child_cnid] = TRUE;
+          }
         }
-      }
 
-      if (!$container_info->toc_depth) {
-        foreach (array_keys($tree) as $key) {
-          unset($tree[$key]['below']);
+        if (!$container_info->toc_depth) {
+          foreach (array_keys($tree) as $key) {
+            unset($tree[$key]['below']);
+          }
         }
       }
     }
-  }
 
-  // We've retrieved a menu hierarchy with the desired depth. Now we set the
-  // menu link back to what it was originally, and we reduce the hierarchy so
-  // that it doesn't include the parents of the current page, or any menu links
-  // for assigned content.
-  if (isset($book_link)) {
-    $tree = _category_display_children_reduce($tree, $book_link, $container_info);
-  }
+    // We've retrieved a menu hierarchy with the desired depth. Now we set the
+    // menu link back to what it was originally, and we reduce the hierarchy so
+    // that it doesn't include the parents of the current page, or any menu links
+    // for assigned content.
+    if (isset($book_link)) {
+      $tree = _category_display_children_reduce($tree, $book_link, $container_info);
+    }
 
-  return $tree ? ($container_info->toc_nodecount ? category_display_menu_tree_output($tree) : menu_tree_output($tree)) : '';
+    $output = $tree ? ($container_info->toc_nodecount ? category_display_menu_tree_output($tree) : menu_tree_output($tree)) : '';
+
+  category_cache_op('set', 0, $cache_key, $output);
+  }
+  return $output;
 }
 
 /**
@@ -656,7 +703,7 @@ function _category_display_children_prun
         preg_match('/^node\/(\d+)$/', $tree[$key]['link']['link_path'], $matches);
         if (!empty($matches[1]) && $type = db_result(db_query("SELECT type FROM {node} WHERE nid = %d", $matches[1]))) {
           $behavior = variable_get('category_behavior_'. $type, 0);
-          if (empty($behavior) && db_result(db_query("SELECT n.nid FROM {node} n INNER JOIN {category_node} cn ON n.nid = cn.nid WHERE n.type = '%s'", $type))) {
+          if (empty($behavior)) {
             $include_link = FALSE;
           }
         }
@@ -836,25 +883,3 @@ function theme_category_display_menu_ite
 
   return $output;
 }
-
-/**
- * Helper function to flatten the $node->category['display'] array.
- */
-function _category_display_flatten_values(&$node) {
-  $fieldsets = array(
-    'display' => TRUE,
-  );
-  if (!empty($node->category) && is_array($node->category)) {
-    foreach ($node->category as $key => $value) {
-      if (isset($fieldsets[$key]) && is_array($value)) {
-        $fieldset = 'category_'. $key;
-        if (empty($node->$fieldset)) {
-          $node->$fieldset = array();
-        }
-        $node->$fieldset += $value;
-
-        unset($node->category[$key]);
-      }
-    }
-  }
-}
Index: category_menu/category_menu.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/category_menu/category_menu.install,v
retrieving revision 1.7
diff -u -p -r1.7 category_menu.install
--- category_menu/category_menu.install	7 Feb 2009 09:01:16 -0000	1.7
+++ category_menu/category_menu.install	5 Aug 2009 04:44:06 -0000
@@ -16,22 +16,38 @@ function category_menu_install() {
 }
 
 /**
+ * Implementation of hook_disable().
+ */
+function category_menu_disable() {
+  drupal_set_message(t('The <em>category_menu</em> module is now disabled. This means that generated menu items are no longer maintained. In order to avoid future problems with outdated menu items, it\'s highly recommended to either enable <em>category_menu</em>, or <a href="!url">uninstall</a> it entirely (removes all generated menu items).', array('!url' => url('admin/build/modules/uninstall'))), 'warning');
+}
+
+/**
  * Implementation of hook_uninstall().
  */
 function category_menu_uninstall() {
   drupal_uninstall_schema('category_menu');
+
+  $types = node_get_types('names');
+  foreach ($types as $type => $name) {
+    variable_del('category_menu_links_'. $type);
+  }
+  // Remove all generated menu links, as these are no longer maintained,
+  // and user can't delete them (only just disable).
+  db_query("DELETE FROM {menu_links} WHERE module = 'category_menu'");
+  menu_rebuild();
 }
 
 function category_menu_schema() {
   $schema['category_menu'] = array(
-    'description' => t('Stores menu settings for containers.'),
+    'description' => 'Stores menu settings for containers.',
     'fields' => array(
       'cid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t('Primary Key: The {category}.cid of the container.'),
+        'description' => 'Primary Key: The {category}.cid of the container.',
       ),
       'links_for_cats' => array(
         'type' => 'int',
@@ -39,7 +55,7 @@ function category_menu_schema() {
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => t('Whether or not generation of menu links for categories is enabled within the container. (0 = disabled, 1 = enabled)'),
+        'description' => 'Whether or not generation of menu links for categories is enabled within the container. (0 = disabled, 1 = enabled)',
       ),
       'expanded_for_cats' => array(
         'type' => 'int',
@@ -47,14 +63,14 @@ function category_menu_schema() {
         'not null' => TRUE,
         'default' => 0,
         'size' => 'tiny',
-        'description' => t("Whether or not generated menu links for categories should be set to 'expanded' within the container. (0 = disabled, 1 = enabled)"),
+        'description' => "Whether or not generated menu links for categories should be set to 'expanded' within the container. (0 = disabled, 1 = enabled)",
       ),
       'plid_for_container' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t("The {menu_links}.mlid of a custom menu link to use as the parent of this container."),
+        'description' => "The {menu_links}.mlid of a custom menu link to use as the parent of this container.",
       ),
       'menu_name' => array(
         'type' => 'varchar',
@@ -68,21 +84,21 @@ function category_menu_schema() {
   );
 
   $schema['category_menu_map'] = array(
-  'description' => t('Stores mappings of containers, categories, and assigned nodes to generated menu links. Uniquely connects each such node to a link in {menu_links}'),
+  'description' => 'Stores mappings of containers, categories, and assigned nodes to generated menu links. Uniquely connects each such node to a link in {menu_links}',
     'fields' => array(
       'mlid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t("The node's {menu_links}.mlid."),
+        'description' => "The node's {menu_links}.mlid.",
       ),
       'nid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => t("The node's {node}.nid."),
+        'description' => "The node's {node}.nid.",
       ),
     ),
     'primary key' => array('mlid'),
@@ -142,3 +158,12 @@ function category_menu_update_6001() {
 
   return $ret;
 }
+
+/**
+ * Ensure correct weight for category_menu module.
+ */
+function category_menu_update_6002() {
+  $ret = array();
+  $ret[] = update_sql("UPDATE {system} SET weight = 8 WHERE name = 'category_menu'");
+  return $ret;
+}
Index: category_menu/category_menu.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/category_menu/category_menu.module,v
retrieving revision 1.54
diff -u -p -r1.54 category_menu.module
--- category_menu/category_menu.module	31 May 2009 10:24:23 -0000	1.54
+++ category_menu/category_menu.module	5 Aug 2009 04:44:06 -0000
@@ -71,43 +71,38 @@ function category_menu_nodeapi(&$node, $
 
     case 'prepare':
       $behavior = variable_get('category_behavior_'. $node->type, 0);
-      if (!empty($behavior)) {
-        $links_for_nodes = $_POST['category']['menu']['links_for_nodes'];
-        $container_has_parent_link = $_POST['category']['menu']['container_has_parent_link'];
-
+      if (!empty($behavior) && $behavior == 'container') {
         // Prepare defaults for the add/edit form.
         if (empty($node->category_menu)) {
           $node->category_menu = array();
         }
-
-        if (!empty($links_for_nodes) && is_array($links_for_nodes)) {
-          $links_for_nodes = array_intersect_key(node_get_types('names'), $links_for_nodes);
-          foreach (array_keys($links_for_nodes) as $node_type) {
-            $links_for_nodes[$node_type] = $node_type;
-          }
-          $node->category_menu['links_for_nodes'] = $links_for_nodes;
-        }
-        if (isset($_POST['category']['menu']['container_has_parent_link'])) {
-          $node->category_menu['container_has_parent_link'] = $container_has_parent_link;
-        }
-
-        if (!isset($node->category_menu['container_has_parent_link'])) {
-          $node->category_menu['container_has_parent_link'] = !empty($node->category_menu['plid_for_container']);
-        }
-        if (!empty($node->category_menu['plid_for_container'])) {
-          $item = db_fetch_array(db_query("SELECT m.*, ml.* FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = %d", $node->category_menu['plid_for_container']));
-          $node->category_menu['plid_value_for_container'] = $node->category_menu['plid_for_container'];
-          $node->category_menu['plid_for_container'] = $item['menu_name'] .':'. $node->category_menu['plid_for_container'];
-        }
       }
+      // No break: Remove menu-on-the-fly information retrieved by core menu module (just in case)
+
+    case 'presave':
+      // Remove any menu-on-the-fly information prior to saving a node,
+      // so that core menu module doesn't save any menu items.
+      unset($node->menu);
       break;
 
     case 'insert':
     case 'update':
       $behavior = variable_get('category_behavior_'. $node->type, 0);
-      _category_menu_flatten_values($node);
-
       if (!empty($behavior) && $behavior == 'container') {
+        // On updates of already populated containers, show a reminder to refresh
+        // site's data through category_resave, if critical menu settings changed.
+        if ($op == 'update' && db_fetch_array(db_query("SELECT * FROM {category} WHERE cnid = %d", $node->nid))) {
+          $old_settings = (array) category_menu_get_container($node->nid);
+          if (!empty($node->category_menu['links_for_nodes'])) {
+            $node->category_menu['links_for_nodes'] = array_filter($node->category_menu['links_for_nodes']);
+          }
+          foreach (array('links_for_cats', 'expanded_for_cats', 'menu_name', 'links_for_nodes') as $key) {
+            if (isset($old_settings[$key]) && $old_settings[$key] != $node->category_menu[$key]) {
+              drupal_set_message(t('Your changes affected behavior of generated menu links. To apply these changes properly, you need to refresh categories and/or nodes in this container now. The <em>category resave</em> module offers an easy way to perform this operation on existing content.'));
+              break;
+            }
+          }
+        }
         category_menu_save_container($node);
       }
 
@@ -116,7 +111,6 @@ function category_menu_nodeapi(&$node, $
 
     case 'delete':
       $behavior = variable_get('category_behavior_'. $node->type, 0);
-
       if (!empty($behavior) && $behavior == 'container') {
         category_menu_del_container($node->nid);
       }
@@ -127,13 +121,46 @@ function category_menu_nodeapi(&$node, $
 }
 
 /**
+ * Implementation of hook_menu_link_alter().
+ *
+ * Deals with external changes to maintained menu links before they are saved,
+ * either by merging the changes back into category system, or disallowing
+ * the changes to be saved.
+ */
+function category_menu_menu_link_alter(&$item, $menu) {
+  if ($item['module'] == 'category_menu' && empty($item['options']['category_menu_unlock']) && !empty($item['mlid']) && $old_item = db_fetch_array(db_query("SELECT * FROM {menu_links} WHERE mlid = %d", $item['mlid']))) {
+    // Disallow changes to these values, because we cannot keep them: Next
+    // edit of the node is going to overwrite them.
+    foreach (array('expanded', 'plid', 'link_title', 'hidden') as $key) {
+      if ($item[$key] != $old_item[$key]) {
+        $item[$key] = $old_item[$key];
+      }
+    }
+    // Merge new menu item weight back to category, so that reordering of menus work.
+    // This is split into two queries, to avoid PgSQL compatibility issues on JOINs.
+    if ($item['weight'] != $old_item['weight']) {
+      $node = db_fetch_array(db_query("SELECT * FROM {category_menu_map} WHERE mlid = %d", $item['mlid']));
+      if (!empty($node['nid'])) {
+        db_query("UPDATE {category} SET weight = %d WHERE cid = %d", $item['weight'], $node['nid']);
+        category_cache_op('flush', 'all');
+      }
+    }
+  }
+  unset($item['options']['category_menu_unlock']);
+}
+
+/**
  * Implementation of hook_form_alter().
  *
- * Adds the category menu settings to the container node form.
+ * Adds the category menu settings to the container node form,
+ * and alters menu overview screen for compatibility.
  */
 function category_menu_form_alter(&$form, $form_state, $form_id) {
   if (isset($form['type']) && isset($form['#node']) && $form['type']['#value']
   .'_node_form' == $form_id) {
+    // Remove the menu-on-the-fly fieldset added by core menu module.
+    unset($form['menu']);
+
     // Add elements to the node form
     $node = $form['#node'];
 
@@ -145,10 +172,21 @@ function category_menu_form_alter(&$form
         '#value' => $node->category_menu_map['mlid'],
       );
     }
+    $form['#submit'][] = 'category_menu_node_form_submit';
 
     $behavior = variable_get('category_behavior_'. $node->type, 0);
 
     if (!empty($behavior) && $behavior == 'container') {
+      // Prepare existing settings
+      if (!isset($node->category_menu['container_has_parent_link'])) {
+        $node->category_menu['container_has_parent_link'] = !empty($node->category_menu['plid_for_container']);
+      }
+      if (!empty($node->category_menu['plid_for_container'])) {
+        $item = db_fetch_array(db_query("SELECT m.*, ml.* FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = %d", $node->category_menu['plid_for_container']));
+        $node->category_menu['plid_value_for_container'] = $node->category_menu['plid_for_container'];
+        $node->category_menu['plid_for_container'] = $item['menu_name'] .':'. $node->category_menu['plid_for_container'];
+      }
+
       $form['category']['menu'] = array(
         '#type' => 'fieldset',
         '#title' => t('Menu links'),
@@ -197,20 +235,22 @@ function category_menu_form_alter(&$form
 
         $default_options = array();
         $taken_options = array();
-        if (!empty($node->category_menu['links_for_nodes'])) {
-          $default_options = $node->category_menu['links_for_nodes'];
-        }
-        else {
-          foreach (array_keys($nodes_options) as $type) {
-            $type_setting = variable_get('category_menu_links_'. $type, 0);
-            if (!empty($type_setting)) {
-              if ($type_setting == $node->nid) {
+        $is_preset = !empty($node->category_menu['links_for_nodes']);
+        foreach (array_keys($nodes_options) as $type) {
+          $type_setting = variable_get('category_menu_links_'. $type, 0);
+          $was_selected = ($is_preset && !empty($node->category_menu['links_for_nodes'][$type]));
+          if (!empty($type_setting)) {
+            if ($type_setting == $node->nid) {
+              if (!$is_preset || $was_selected) {
                 $default_options[$type] = $type;
               }
-              else {
-                $taken_options[$type] = $type;
-              }
             }
+            else {
+              $taken_options[$type] = $type;
+            }
+          }
+          else if ($was_selected) {
+            $default_options[$type] = $type;
           }
         }
         $category_link['links_for_nodes'] = $nodes_options;
@@ -301,6 +341,50 @@ function category_menu_form_alter(&$form
       }
     }
   }
+
+  // Grey-out incompatible settings for maintained menu items on the menu overview
+  // screen, and swap edit link to point to corresponding node edit.
+  if ($form_id == 'menu_overview_form') {
+    $title = t('To change this item, you need to edit the corresponding node.');
+    foreach ($form as $key => $row) {
+      if (!empty($row['#item']['module']) && $row['#item']['module'] == 'category_menu') {
+        $form[$key]['hidden']['#disabled'] = TRUE;
+        $form[$key]['expanded']['#disabled'] = TRUE;
+        $form[$key]['operations']['edit']['#value'] = l(t('edit node'), $row['#item']['link_path'] .'/edit', array('query' => drupal_get_destination(), 'attributes' => array('title' => $title)));
+      }
+    }
+  }
+}
+
+/**
+ * Additional form submit handler for node forms.
+ *
+ * Flattens and processes the $form_state['values']['category']['menu']
+ * and ...['menu_map'] arrays on both submits and previews.
+ */
+function category_menu_node_form_submit($form, &$form_state) {
+  $fieldsets = array(
+    'menu' => TRUE,
+    'menu_map' => TRUE,
+  );
+  if (!empty($form_state['values']['category']) && is_array($form_state['values']['category'])) {
+    foreach ($form_state['values']['category'] as $key => $value) {
+      if (isset($fieldsets[$key]) && is_array($value)) {
+        if (isset($value['plid_for_container']) && preg_match('/^[A-Za-z0-9\-_]+:[0-9]+$/', $value['plid_for_container'])) {
+          $value_parts = explode(':', $value['plid_for_container']);
+          $value['plid_for_container'] = $value_parts[1];
+        }
+
+        $fieldset = 'category_'. $key;
+        if (empty($form_state['values'][$fieldset])) {
+          $form_state['values'][$fieldset] = array();
+        }
+        $form_state['values'][$fieldset] = $value + $form_state['values'][$fieldset];
+
+        unset($form_state['values']['category'][$key]);
+      }
+    }
+  }
 }
 
 /**
@@ -498,20 +582,29 @@ function category_menu_category_legacy($
  *   The menu settings for the specified node.
  */
 function category_menu_get_container($nid) {
-  $result = db_query("SELECT * FROM {category_menu} WHERE cid = %d", $nid);
-
-  if ($container = db_fetch_object($result)) {
-    $links_for_nodes = array();
-    foreach (array_keys(node_get_types('names')) as $type) {
-      $links_setting = variable_get('category_menu_links_'. $type, 0);
-      if (!empty($links_setting) && $links_setting == $nid) {
-        $links_for_nodes[$type] = $type;
+  $cache_key = 'cmenu_get_cont';
+  $container = category_cache_op('get', $nid, $cache_key);
+  if (!isset($container)) {
+
+    $result = db_query("SELECT * FROM {category_menu} WHERE cid = %d", $nid);
+
+    if ($container = db_fetch_object($result)) {
+      $links_for_nodes = array();
+      foreach (array_keys(node_get_types('names')) as $type) {
+        $links_setting = variable_get('category_menu_links_'. $type, 0);
+        if (!empty($links_setting) && $links_setting == $nid) {
+          $links_for_nodes[$type] = $type;
+        }
       }
+      $container->links_for_nodes = $links_for_nodes;
+    }
+    else {
+      $container = (object) array();
     }
-    $container->links_for_nodes = $links_for_nodes;
 
-    return $container;
+    category_cache_op('set', $nid, $cache_key, $container);
   }
+  return $container;
 }
 
 /**
@@ -546,6 +639,7 @@ function category_menu_save_container(&$
     }
   }
 
+  category_cache_op('flush', 'all');
   return $status;
 }
 
@@ -565,6 +659,7 @@ function category_menu_del_container($ni
       variable_del('category_menu_links_'. $type);
     }
   }
+  category_cache_op('flush', 'all');
 }
 
 /**
@@ -585,12 +680,20 @@ function category_menu_map_load($mlid) {
  * during hook_nodeapi('load').
  */
 function category_menu_map_load_by_nid($nid) {
-  if ($item = db_fetch_array(db_query('SELECT ml.*, cmm.nid, m.* FROM {menu_links} ml INNER JOIN {category_menu_map} cmm ON cmm.mlid = ml.mlid LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE cmm.nid = %d', $nid))) {
+  $cache_key = 'cmenu_map_load_nid';
+  $item = category_cache_op('get', $nid, $cache_key);
+  if (!isset($item)) {
 
-    $item['options'] = unserialize($item['options']);
-    return $item;
+    if ($item = db_fetch_array(db_query('SELECT m.*, cmm.nid, ml.* FROM {menu_links} ml INNER JOIN {category_menu_map} cmm ON cmm.mlid = ml.mlid LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE cmm.nid = %d', $nid))) {
+      $item['options'] = unserialize($item['options']);
+    }
+    else {
+      $item = FALSE;
+    }
+
+    category_cache_op('set', $nid, $cache_key, $item);
   }
-  return FALSE;
+  return $item;
 }
 
 /**
@@ -650,6 +753,7 @@ function category_menu_map_save($node) {
     }
 
     $node->category_menu_map['expanded'] = $container_settings['expanded_for_cats'];
+    $node->category_menu_map['weight'] = $node->category['weight'];
   }
   // Logic for assigned node types.
   else {
@@ -692,6 +796,8 @@ function category_menu_map_save($node) {
     }
 
     $new_plid = $assigned_cat_menu_link['mlid'];
+    // Weight for nodes will be re-saved as is (if available), to keep possible
+    // menu order customizations.
   }
 
   $node->category_menu_map['menu_name'] = category_menu_menu_name($container_settings['menu_name'], $container_settings['cid']);
@@ -700,8 +806,10 @@ function category_menu_map_save($node) {
   $node->category_menu_map['parent_mismatch'] = FALSE;
   $node->category_menu_map['module'] = 'category_menu';
   $node->category_menu_map['plid'] = $new_plid;
+  $node->category_menu_map['hidden'] = (int) !$node->status;
   $menu_name = $node->category_menu_map['menu_name'];
 
+  $node->category_menu_map['options']['category_menu_unlock'] = TRUE;
   if (menu_link_save($node->category_menu_map)) {
     if ($new) {
       // Insert new.
@@ -739,7 +847,8 @@ function category_menu_map_save($node) {
  *   by the category hierarchy), or 0 if no suitable menu link was found.
  */
 function _category_menu_map_save_category($nid) {
-  $parents = category_get_parents($nid, 'cid', TRUE, TRUE);
+  category_cache_op('flush', 'all');
+  $parents = category_get_parents($nid, 'cid', TRUE);
   $parent = reset($parents);
 
   if (empty($parent->cid)) {
@@ -831,72 +940,83 @@ function category_menu_build_active_trai
  * children of bottom-level menu items.
  */
 function category_menu_tree_all_data($menu_name, &$item, $max_depth = NULL, $cache_prefix = 'category-menu-') {
-  // Start with a regular tree, based on the specified item, containing
-  // items local to the $menu_name menu.
-  $tree = _category_menu_tree_all_data($menu_name, $item, $cache_prefix, $max_depth);
-
-  // Next, find top-level items with defined parents, and extend the tree
-  // to include such parents and their ancestors.
-  $parent_link = reset($tree);
-  $parent_link_key = key($tree);
-  while ($parent_link['link']['plid']) {
-    $item['has_children'] = 1;
-    $ancestor_link = menu_link_load($parent_link['link']['plid']);
-    if ($ancestor_link['menu_name'] != $menu_name) {
-      $tree = _category_menu_tree_all_data($ancestor_link['menu_name'], $ancestor_link, $cache_prefix);
-      _category_menu_tree_above($tree, array(
-        'key' => $parent_link_key,
-        'value' => $parent_link,
-      ));
+  // Cache key employs current user's roles, to cover access check performed
+  // inside _category_menu_tree_all_data(). (TODO: Check whether that check is
+  // necessary, considering these are our own generated menu items.)
+  global $user;
+  $cache_key = 'cmenu_tree_all_data'. $menu_name .':'. $item[$mlid] .':'. $max_depth .':'. implode(':', $user->roles);
+  $tree = category_cache_op('get', 0, $cache_key);
+  if (!isset($tree)) {
+
+    // Start with a regular tree, based on the specified item, containing
+    // items local to the $menu_name menu.
+    $tree = _category_menu_tree_all_data($menu_name, $item, $cache_prefix, $max_depth);
+
+    // Next, find top-level items with defined parents, and extend the tree
+    // to include such parents and their ancestors.
+    $parent_link = reset($tree);
+    $parent_link_key = key($tree);
+    while ($parent_link['link']['plid']) {
+      $item['has_children'] = 1;
+      $ancestor_link = menu_link_load($parent_link['link']['plid']);
+      if ($ancestor_link['menu_name'] != $menu_name) {
+        $tree = _category_menu_tree_all_data($ancestor_link['menu_name'], $ancestor_link, $cache_prefix);
+        _category_menu_tree_above($tree, array(
+          'key' => $parent_link_key,
+          'value' => $parent_link,
+        ));
 
-      $parent_link = reset($tree);
-      $parent_link_key = key($tree);
-    }
-    else {
-      $parent_link['link']['plid'] = 0;
-    }
-  };
+        $parent_link = reset($tree);
+        $parent_link_key = key($tree);
+      }
+      else {
+        $parent_link['link']['plid'] = 0;
+      }
+    };
 
-  $query_nodetypes1 = '';
-  $query_nodetypes2 = '';
-  $args = array($item['mlid'], $menu_name);
-  if ($category = db_fetch_object(db_query('SELECT c.* FROM {category} c INNER JOIN {category_menu_map} cmm ON c.cid = cmm.nid WHERE cmm.mlid = %d', $item['mlid']))) {
-    $cnid = !empty($category->cnid) ? $category->cnid : $category->cid;
-    $node_types = array();
-    $query_nodetypes = db_query('SELECT type FROM {category_cont_node_types} WHERE cid = %d', $cnid);
+    $query_nodetypes1 = '';
+    $query_nodetypes2 = '';
+    $args = array($item['mlid'], $menu_name);
+    if ($category = db_fetch_object(db_query('SELECT c.* FROM {category} c INNER JOIN {category_menu_map} cmm ON c.cid = cmm.nid WHERE cmm.mlid = %d', $item['mlid']))) {
+      $cnid = !empty($category->cnid) ? $category->cnid : $category->cid;
+      $node_types = array();
+      $query_nodetypes = db_query('SELECT type FROM {category_cont_node_types} WHERE cid = %d', $cnid);
 
-    while ($node_type = db_fetch_object($query_nodetypes)) {
-      $node_types[] = 'category-menu-toc-'. $node_type->type;
-    }
+      while ($node_type = db_fetch_object($query_nodetypes)) {
+        $node_types[] = 'category-menu-toc-'. $node_type->type;
+      }
 
-    if (!empty($node_types)) {
-      $query_nodetypes1 = ' LEFT JOIN {category_menu_map} cmm ON ml.mlid = cmm.mlid LEFT JOIN {node} n ON cmm.nid = n.nid';
+      if (!empty($node_types)) {
+        $query_nodetypes1 = ' LEFT JOIN {category_menu_map} cmm ON ml.mlid = cmm.mlid LEFT JOIN {node} n ON cmm.nid = n.nid';
 
-      $placeholders = db_placeholders($node_types, 'varchar');
-      $query_nodetypes2 = ' AND ml.menu_name NOT IN('. $placeholders .')';
-      $args = array_merge($args, $node_types);
+        $placeholders = db_placeholders($node_types, 'varchar');
+        $query_nodetypes2 = ' AND ml.menu_name NOT IN('. $placeholders .')';
+        $args = array_merge($args, $node_types);
+      }
     }
-  }
 
-  $query = "SELECT m.*, ml.* FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path". $query_nodetypes1 ." WHERE ml.plid = %d AND ml.menu_name != '%s'". $query_nodetypes2;
+    $query = "SELECT m.*, ml.* FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path". $query_nodetypes1 ." WHERE ml.plid = %d AND ml.menu_name != '%s'". $query_nodetypes2;
 
-  if (isset($max_depth)) {
-    $query .= ' AND ml.depth < %d';
-    $args[] = $max_depth - 1;
-  }
-  // Finally, find distant children with the specified item as their parent,
-  // and extend the tree to include such children and their descendants.
-  $children = db_query($query, $args);
-  $child_tree_prev = array();
+    if (isset($max_depth)) {
+      $query .= ' AND ml.depth < %d';
+      $args[] = $max_depth - 1;
+    }
+    // Finally, find distant children with the specified item as their parent,
+    // and extend the tree to include such children and their descendants.
+    $children = db_query($query, $args);
+    $child_tree_prev = array();
 
-  while ($child_link = db_fetch_array($children)) {
-    $item['has_children'] = 1;
-    _menu_link_translate($child_link);
-    $child_tree = _category_menu_tree_all_data($child_link['menu_name'], $child_link, $cache_prefix);
-    if (!empty($child_tree) && $child_tree != $child_tree_prev) {
-      _category_menu_tree_below($tree, $child_tree, $item);
-      $child_tree_prev = $child_tree;
+    while ($child_link = db_fetch_array($children)) {
+      $item['has_children'] = 1;
+      _menu_link_translate($child_link);
+      $child_tree = _category_menu_tree_all_data($child_link['menu_name'], $child_link, $cache_prefix);
+      if (!empty($child_tree) && $child_tree != $child_tree_prev) {
+        _category_menu_tree_below($tree, $child_tree, $item);
+        $child_tree_prev = $child_tree;
+      }
     }
+
+  category_cache_op('set', 0, $cache_key, $tree);
   }
 
   return $tree;
@@ -1034,31 +1154,3 @@ function _category_menu_tree_above(&$tre
     }
   }
 }
-
-/**
- * Helper function to flatten the $node->category['menu'] array.
- */
-function _category_menu_flatten_values(&$node) {
-  $fieldsets = array(
-    'menu' => TRUE,
-    'menu_map' => TRUE,
-  );
-  if (!empty($node->category) && is_array($node->category)) {
-    foreach ($node->category as $key => $value) {
-      if (isset($fieldsets[$key]) && is_array($value)) {
-        if (isset($value['plid_for_container']) && preg_match('/^[A-Za-z0-9\-_]+:[0-9]+$/', $value['plid_for_container'])) {
-          $value_parts = explode(':', $value['plid_for_container']);
-          $value['plid_for_container'] = $value_parts[1];
-        }
-
-        $fieldset = 'category_'. $key;
-        if (empty($node->$fieldset)) {
-          $node->$fieldset = array();
-        }
-        $node->$fieldset += $value;
-
-        unset($node->category[$key]);
-      }
-    }
-  }
-}
Index: contrib/category_breadcrumb/category_breadcrumb.admin.inc
===================================================================
RCS file: contrib/category_breadcrumb/category_breadcrumb.admin.inc
diff -N contrib/category_breadcrumb/category_breadcrumb.admin.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ contrib/category_breadcrumb/category_breadcrumb.admin.inc	5 Aug 2009 04:44:06 -0000
@@ -0,0 +1,60 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Admin page callbacks for the category_breadcrumb module.
+ */
+
+
+/**
+ * The admin settings form
+ */
+function category_breadcrumb_admin_settings() {
+
+  $form['category_breadcrumb_amount'] = array(
+    '#type' => 'radios',
+    '#title' => t('For categorized nodes'),
+    '#default_value' => variable_get('category_breadcrumb_amount', 1),
+    '#options' => array(
+      '0' => t('Do nothing (keep standard menu-based breadcrumb)'),
+      '1' => t('Show just one breadcrumb (default)'),
+      '2' => t('Show multiple breadcrumbs'),
+      ),
+    '#description' => t("While enabled, this feature replaces any existing (menu-based) breadcrumbs on content (node) pages. One breadcrumb is a standard Drupal's format of breadcrumbs, but it can't show multiple paths (for nodes belonging to more categories), and so might fail to provide the expected backwards navigation in some cases. The Multiple breadcrumbs option allows for multiple paths to be shown, but it may be incompatible with some themes.")
+  );
+
+  $form['category_breadcrumb_catpages'] = array(
+    '#type' => 'radios',
+    '#title' => t('For category and container pages'),
+    '#default_value' => variable_get('category_breadcrumb_catpages', 1),
+    '#options' => array(
+      '0' => t('Do nothing (keep standard menu-based breadcrumb)'),
+      '1' => t('Show breadcrumb (default)'),
+      ),
+    '#description' => t('While enabled, this feature replaces any existing (menu-based) breadcrumbs on category and container pages.')
+  );
+
+  $form['category_breadcrumb_exclude'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Do not create breadcrumbs for'),
+    '#default_value' => variable_get('category_breadcrumb_exclude', 'forum,blog'),
+    '#size' => 60,
+    '#maxlength' => 200, 
+    '#description' => t("Enter comma-separated list of node-types to exclude from breadcrumb processing. 'forum' and 'blog' are recommended, to avoid compatibility problems. (If needed, names of other node types may be easily observed in HTML source of your pages as class names near top of the content.)")
+  );
+
+  $form['category_breadcrumb_title'] = array(
+    '#type' => 'radios',
+    '#title' => t('Add title of current node to the breadcrumb'),
+    '#default_value' => variable_get('category_breadcrumb_title', 0),
+    '#options' => array(
+      '0' => t('No title, show just path (default)'),
+      '1' => t('Title as plain text'),
+      '2' => t('Title as link'),
+      ),
+    '#description' => t("Drupal defines breadcrumbs with no title in it, so usually it's up to theme to add the currently viewed page's title (if desired), and this option should be disabled. But however, while using the Multiple breadcrumbs option, it might be better to enable the title here instead (removing it from your theme), so that the title may be added to each of the multiple paths. You can add title even if no breadcrumb is chosen above (i.e. adding to the standard Drupal's breadcrumb), but this won't work for any pages other than nodes.")
+  );
+
+  return system_settings_form($form);
+}
Index: contrib/category_breadcrumb/category_breadcrumb.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/contrib/category_breadcrumb/category_breadcrumb.info,v
retrieving revision 1.1
diff -u -p -r1.1 category_breadcrumb.info
--- contrib/category_breadcrumb/category_breadcrumb.info	5 Aug 2007 19:09:16 -0000	1.1
+++ contrib/category_breadcrumb/category_breadcrumb.info	5 Aug 2009 04:44:06 -0000
@@ -1,5 +1,6 @@
-; $Id: category_breadcrumb.info,v 1.1 2007/08/05 19:09:16 bdragon Exp $
+; $Id$
 name = Category Breadcrumb
 description = Allows breadcrumb-navigation on node-pages, based on Categories.
 package = Category
-dependencies = category
+dependencies[] = category
+core = 6.x
Index: contrib/category_breadcrumb/category_breadcrumb.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/contrib/category_breadcrumb/category_breadcrumb.module,v
retrieving revision 1.1
diff -u -p -r1.1 category_breadcrumb.module
--- contrib/category_breadcrumb/category_breadcrumb.module	5 Aug 2007 19:09:16 -0000	1.1
+++ contrib/category_breadcrumb/category_breadcrumb.module	5 Aug 2009 04:44:06 -0000
@@ -1,79 +1,37 @@
 <?php
-// $Id: category_breadcrumb.module,v 1.1 2007/08/05 19:09:16 bdragon Exp $
+// $Id$
 
 /**
  * @file
- * Create sensible breadcrumb-navigation on node-pages, based on Categories
+ * Create breadcrumb-navigation on node-pages, based on Categories
  */
 
 
 /*
 * Implementation of hook_menu (for admin settings only)
 */
-function category_breadcrumb_menu($may_cache) {
-  $items[] = array(
-    'path' => 'admin/settings/category_breadcrumb',
-    'title' => t('Category Breadcrumb'),
-    'description' => t('Configure breadcrumb-navigation for category-assigned nodes.'),
-    'callback' => 'drupal_get_form',
-    'callback arguments' => array('category_breadcrumb_admin_settings'),
-    'access' => user_access('administer site configuration'),
-    'type' => MENU_NORMAL_ITEM,
+function category_breadcrumb_menu() {
+  $items['admin/content/category/category_breadcrumb'] = array(
+    'title' => 'Breadcrumbs',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('category_breadcrumb_admin_settings'),
+    'access arguments' => array('administer site configuration'),
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'category_breadcrumb.admin.inc',
   );
   return $items;
 }
 
 
 /**
- * The admin settings form
+ * Implementation of hook_help().
  */
-function category_breadcrumb_admin_settings() {
-
-  $form['category_breadcrumb_amount'] = array(
-    '#type' => 'radios',
-    '#title' => t('For category-assigned nodes'),
-    '#default_value' => variable_get('category_breadcrumb_amount', 1),
-    '#options' => array(
-      '0' => t("Do nothing (use system's breadcrumb)"),
-      '1' => t('Show just one breadcrumb (default)'),
-      '2' => t('Show multiple breadcrumbs'),
-      ),
-    '#description' => t("One breadcrumb is exactly like system breadcrumbs, but if there are more categories assigned to the node, it shows only just one of the possible paths, confusing the user if he came from another. Multiple breadcrumbs shows the paths for all assigned categories, but may be incompatible with some themes.")
-  );
-
-  $form['category_breadcrumb_catpages'] = array(
-    '#type' => 'radios',
-    '#title' => t('For category and container pages'),
-    '#default_value' => variable_get('category_breadcrumb_catpages', 1),
-    '#options' => array(
-      '0' => t("Do nothing (use system's menu-based breadcrumb)"),
-      '1' => t('Show breadcrumb (default)'),
-      ),
-    '#description' => t("While enabled, this feature replaces any existing (menu-based) breadcrumbs on category- and container-pages.")
-  );
-
-  $form['category_breadcrumb_exclude'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Do not create breadcrumbs for'),
-    '#default_value' => variable_get('category_breadcrumb_exclude', 'forum,blog'),
-    '#size' => 60,
-    '#maxlength' => 200, 
-    '#description' => t("Enter comma-separated list of node-types to exclude from breadcrumb processing. 'forum' and 'blog' are recommended to avoid common problems. (More node types may be easily observed in HTML source of your pages as class names near top of the content.)")
-  );
-
-  $form['category_breadcrumb_title'] = array(
-    '#type' => 'radios',
-    '#title' => t('Add actual node-title to the breadcrumb'),
-    '#default_value' => variable_get('category_breadcrumb_title', 0),
-    '#options' => array(
-      '0' => t('No title, show just path (default)'),
-      '1' => t('Title as plain text'),
-      '2' => t('Title as link'),
-      ),
-    '#description' => t("Drupal defines the breadcrumb with no title in it, so usually it's up to theme to add the currently viewed page's title (or not), and this option should be disabled. But however, while using the Multiple breadcrumbs option, it might be better to enable the title here - and remove from your theme in turn - to have the title on each of the multiple breadcrumbs. You can add title even if no breadcrumb chosen above (i.e. adding to the system's breadcrumb), but this doesn't work for any pages other than nodes.")
-  );
-
-  return system_settings_form($form);
+function category_breadcrumb_help($path, $arg) {
+  switch ($path) {
+    case 'admin/content/category/category_breadcrumb':
+      $output = t("The category_breadcrumb module adds breadcrumb navigation to your category and container pages, as well as categorized contents (i.e. node pages), so that it reflects your site's hiearchy of categories rather than menu links. The settings below may be used to change the behavior of category-based breadcrumbs.");
+      return $output;
+  }
 }
 
 
@@ -88,17 +46,17 @@ function category_breadcrumb_nodeapi(&$n
     // Viewing a node as page, not front: Check for existing breadcrumbs & excluded node-types
     if (!drupal_set_breadcrumb() && !in_array($node->type, explode(',', variable_get('category_breadcrumb_exclude', 'forum,blog')))) {
 
-      // Handle category/container pages
-      if ($node->type == 'category_cat' or $node->type == 'category_cont') {
+      // Handle node types with category/container behavior
+      if (!empty($node->category) && !empty($node->category['behavior'])) {
         if (variable_get('category_breadcrumb_catpages', 1)) {
           $crumbs[] = _category_breadcrumb_build($node, $add_title);
         }
       }
-      // All other node-types: One breadcrumb per category assigned
+      // All other node-types are content: One breadcrumb per category assigned
       else {
         $amount = variable_get('category_breadcrumb_amount', 1); 
-        if ($node->category && $amount) {
-          foreach ($node->category as $category) {
+        if ($node->categories && $amount) {
+          foreach ($node->categories as $category) {
             $crumbs[] = _category_breadcrumb_build($node, $add_title, $category);
             if ($amount < 2)
               break;  // If no multiple breadcrumbs, stop after the first
@@ -115,7 +73,7 @@ function category_breadcrumb_nodeapi(&$n
 // all occurences of the separator, then allow Drupal to re-unite it with any separator wanted.
 
       // Multiple breadcrumbs as item-list, otherwise no change
-      $output = (count($crumbs) > 1) ? '<ul><li>'.implode('</li><li>', $crumbs).'</li></ul>' : $crumbs[0];
+      $output = (count($crumbs) > 1) ? '<ul><li>'. implode('</li><li>', $crumbs) .'</li></ul>' : $crumbs[0];
       drupal_set_breadcrumb(explode('»', $output));
     }
     // Even if no breadcrumb created, we may still want to append node-title for theming consistency
@@ -151,12 +109,23 @@ function _category_breadcrumb_build($nod
   }
 
   // Add further parents, until no more left
-  while ($parents = category_get_parents($cid, 'cid', FALSE)) {
-    $cid = $parents[0]->cid;
-    if(!$parents[0]->hidden_cont) {
-      $crumb[] = l($parents[0]->title, 'node/'.$cid);
+  do {
+    $parents = category_get_parents($cid, 'cid', FALSE);
+    $parent = reset($parents);
+    $cid = $parent->cid;
+    $hidden = 0;
+
+    if ($cid && !$parent->cnid) {
+      // This is a container. Since category_get_parents() doesn't fetch
+      // hidden_cont data in 6.x version, we need to check manually.
+      $data = db_fetch_object(db_query('SELECT hidden_cont FROM {category_cont} WHERE cid = %d', $cid));
+      $hidden = $data->hidden_cont;
     }
-  }
+
+    if ($cid && !$hidden) {
+      $crumb[] = l($parent->title, 'node/'.$cid);
+    }
+  } while ($cid);
 
   // Add 'Home' and join into string
   $crumb[] = l(t('Home'), variable_get('site_frontpage', 'node'));
Index: contrib/category_views/category_views.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/contrib/category_views/category_views.info,v
retrieving revision 1.1
diff -u -p -r1.1 category_views.info
--- contrib/category_views/category_views.info	5 Jan 2007 15:31:16 -0000	1.1
+++ contrib/category_views/category_views.info	5 Aug 2009 04:44:06 -0000
@@ -1,5 +1,7 @@
-; $Id: category_views.info,v 1.1 2007/01/05 15:31:16 jaza Exp $
+; $Id$
 name = Category views
 description = Renders node listings on category pages using a view.
 package = Category
-dependencies = category views
+dependencies[] = category
+dependencies[] = views
+core = 6.x
Index: contrib/category_views/category_views.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/contrib/category_views/category_views.install,v
retrieving revision 1.3
diff -u -p -r1.3 category_views.install
--- contrib/category_views/category_views.install	14 Jul 2007 20:39:08 -0000	1.3
+++ contrib/category_views/category_views.install	5 Aug 2009 04:44:07 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: category_views.install,v 1.3 2007/07/14 20:39:08 bdragon Exp $
+// $Id: category_views.install,v 1.2 2006/04/14 13:52:03 jaza Exp $
 
 /**
  * @file
@@ -8,6 +8,49 @@
  */
 
 /**
+ * Implementation of hook_schema().
+ */
+function category_views_schema() {
+  $schema['category_views'] = array(
+    'description' => 'Stores category_views settings for each container.',
+    'fields' => array(
+      'cid' => array(
+        'type' => 'int',
+        'unsigned' => true,
+        'not null' => true,
+        'default' => 0,
+        'description' => 'ID of the container {category_cont}.cid',
+      ),
+      'view_name' => array(
+        'type' => 'varchar',
+        'length' => '32',
+        'default' => '0',
+        'not null' => TRUE,
+        'description' => 'The unique name of the View for this container.',
+      ),
+      'display_cont' => array(
+        'type' => 'int',
+        'size' => 'tiny',
+        'unsigned' => true,
+        'not null' => true,
+        'default' => 0,
+        'description' => 'Whether the View should be used for this container itself',
+      ),
+      'display_cat' => array(
+        'type' => 'int',
+        'size' => 'tiny',
+        'unsigned' => true,
+        'not null' => true,
+        'default' => 0,
+        'description' => 'Whether the View should be used for categories in this container',
+      ),
+    ),
+    'primary key' => array('cid'),
+  );
+  return $schema;
+}
+
+/**
  * Implementation of hook_install().
  */
 function category_views_install() {
@@ -15,6 +58,13 @@ function category_views_install() {
 }
 
 /**
+ * Implementation of hook_uninstall().
+ */
+function category_views_uninstall() {
+  drupal_uninstall_schema('category_views');
+}
+
+/**
  * Install category_views table if it didn't already exist.
  */
 function category_views_update_1() {
@@ -31,4 +81,26 @@ function category_views_update_1() {
     ));
   }
   return $ret;
-}
\ No newline at end of file
+}
+
+/**
+ * Update to 6.x: As of Views 2, Identifier of a view changed from numeric ID to
+ * unique name (a string). There's no point in keeping old data in the column, as
+ * all views must be re-created on 5.x to 6.x upgrade anyway.
+ */
+function category_views_update_6000() {
+  $ret = array();
+
+  $new_field = array(
+    'type' => 'varchar',
+    'length' => '32',
+    'default' => '0',
+    'not null' => TRUE,
+    'description' => 'The unique name of the View for this container.',
+  );
+
+  db_drop_field($ret, 'category_views', 'view_id');
+  db_add_field($ret, 'category_views', 'view_name', $new_field);
+
+  return $ret;
+}
Index: contrib/category_views/category_views.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/contrib/category_views/category_views.module,v
retrieving revision 1.8
diff -u -p -r1.8 category_views.module
--- contrib/category_views/category_views.module	16 Jul 2007 03:40:21 -0000	1.8
+++ contrib/category_views/category_views.module	5 Aug 2009 04:44:07 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: category_views.module,v 1.8 2007/07/16 03:40:21 bdragon Exp $
+// $Id$
 
 /**
  * @file
@@ -9,8 +9,8 @@
 /**
  * Implementation of hook_help().
  */
-function category_views_help($section) {
-  switch ($section) {
+function category_views_help($path, $arg) {
+  switch ($path) {
     case 'admin/help#category_views':
       return t('<p>The category_views module renders node listings on category pages using a view. It requires the category module and the views module to be installed.</p>');
   }
@@ -22,91 +22,112 @@ function category_views_help($section) {
 function category_views_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
   switch ($op) {
     case 'load':
-      if (category_is_cat_or_cont($node->nid)) {
-        $type = category_node_get_type($node);
-        $is_cat = $type == 'category_cat';
-        if (!$is_cat) {
-          return (array)category_views_cont_load($node->nid);
-        }
-        else {
-          return (array)category_views_cont_load($node->cnid);
+      $behavior = variable_get('category_behavior_'. $node->type, 0);
+      if (!empty($behavior)) {
+        // This is a container or category. Load the view settings from database.
+        // No category information is available in the $node object yet.
+        $cache_key = 'cview_node_load';
+        $output = category_cache_op('get', $node->nid, $cache_key);
+        if (!isset($output)) {
+          if ($behavior === 'container') {
+            $result = db_query('SELECT * FROM {category_views} WHERE cid = %d', $node->nid);
+          }
+          else {
+            $result = db_query('SELECT v.* FROM {category_views} v INNER JOIN {category} c ON v.cid = c.cnid WHERE c.cid = %d', $node->nid);
+          }
+          $settings = db_fetch_array($result);
+          $output = array(
+            'view_for_cats' => $settings['view_name'],
+            'view_display' => array('cont' => $settings['display_cont'], 'cat' => $settings['display_cat']),
+          );
+          category_cache_op('set', $node->nid, $cache_key, $output);
         }
+        return $output;
       }
       break;
+
     case 'insert':
     case 'update':
-      $op = $_POST['op'];
-      $submit_values = array(
-        'insert' => t('Add to category outline'),
-        'update' => t('Update category outline'),
-        'delete' => t('Remove from category outline'),
-      );
-      if (in_array($op, $submit_values)) {
-        switch ($op) {
-          case $submit_values['insert']:
-          case $submit_values['update']:
-            category_views_cont_update($node);
-            break;
-          case $submit_values['delete']:
-            category_views_cont_delete($node->nid);
-            break;
+      $behavior = variable_get('category_behavior_'. $node->type, 0);
+      if ($behavior === 'container') {
+        // This is a container. Insert or update view settings in database.
+        $old_entry = db_fetch_array(db_query('SELECT * FROM {category_views} WHERE cid = %d', $node->nid));
+        if (!empty($old_entry)) {
+          db_query("UPDATE {category_views} SET view_name = '%s', display_cont = %d, display_cat = %d WHERE cid = %d", $node->view_for_cats, $node->view_display['cont'], $node->view_display['cat'], $node->nid);
         }
-      }
-      elseif (category_is_cat_or_cont($node->nid, TRUE)) {
-        $type = category_node_get_type($node);
-        $is_cat = $type == 'category_cat';
-        if (!$is_cat) {
-          category_views_cont_update($node);
+        else {
+          db_query("INSERT INTO {category_views} (cid, view_name, display_cont, display_cat) VALUES (%d, '%s', %d, %d)", $node->nid, $node->view_for_cats, $node->view_display['cont'], $node->view_display['cat']);
         }
       }
       break;
+
     case 'delete':
-      $type = category_node_get_type($node);
-      $is_cat = $type == 'category_cat';
-      if (!$is_cat) {
-        category_views_cont_delete($node->nid);
+      $behavior = variable_get('category_behavior_'. $node->type, 0);
+      if ($behavior === 'container') {
+        // This is a container being deleted. Delete view settings from database.
+        db_query('DELETE FROM {category_views} WHERE cid = %d', $node->nid);
       }
       break;
   }
 }
 
 /**
- * Implementation of hook_category().
+ * Implementation of hook_form_alter().
  */
-function category_views_category($op, $node = NULL) {
-  switch ($op) {
-    case 'form':
-      // Views settings
-      $form['category_views'] = array(
+function category_views_form_alter(&$form, $form_state, $form_id) {
+  if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) {
+    $node = $form['#node'];
+    if (isset($node->category) && $node->category['behavior'] === 'container') {
+
+      // Add views settings to container node form.
+      $form['category']['view'] = array(
         '#type' => 'fieldset',
         '#title' => t('Category view settings'),
         '#collapsible' => TRUE,
         '#collapsed' => TRUE,
+        '#weight' => 8,
       );
+      $form['#submit'][] = 'category_views_node_form_submit';
       $view_list = array(0 => '<'. t('none') .'>') + category_views_get_views();
       $options = array(
         'cont' => t('This container\'s page'),
         'cat' => t('Pages for categories in this container'),
       );
-      $form['category_views']['view_for_cats'] = array(
+      $form['category']['view']['view_for_cats'] = array(
         '#type' => 'select',
         '#title' => t('View for this container'),
         '#default_value' => isset($node->view_for_cats) ? $node->view_for_cats : 0,
         '#options' => $view_list,
         '#multiple' => FALSE,
-        '#description' => t('The view to use for rendering listings of assigned nodes, in this container and its categories. If you do not check either of the \'show view on\' boxes below, then it makes no difference whether or not you select a view here. Conversely, if you leave this setting at its default value (&lt;none&gt;), then it makes no difference whether or not you check either of the boxes below. If there are no views available, or if the currently available views are not adequate, you can !create-view (<em>warning:</em> clicking this link may cause you to leave this page, and to lose whatever information you have entered so far).', array('!create-view' => l(t('create a new view'), 'admin/views/add'))),
+        '#description' => t('The view to use for rendering listings of assigned nodes, in this container and its categories. If you do not check either of the \'show view on\' boxes below, then it makes no difference whether or not you select a view here. Conversely, if you leave this setting at its default value (&lt;none&gt;), then it makes no difference whether or not you check either of the boxes below. If there are no views available, or if the currently available views are not adequate, you can !create-view (<em>warning:</em> clicking this link may cause you to leave this page, and to lose whatever information you have entered so far).', array('!create-view' => l(t('create a new view'), 'admin/build/views/add'))),
       );
-      $form['category_views']['view_display'] = array(
+      $form['category']['view']['view_display'] = array(
         '#type' => 'checkboxes',
         '#title' => t('Show view on'),
-        '#default_value' => isset($node->view_display) ? $node->view_display : array(),
+        '#default_value' => array('cat' => empty($node->view_display['cat']) ? 0 : 'cat', 'cont' => empty($node->view_display['cont']) ? 0 : 'cont'),
         '#options' => $options,
         '#description' => t('Whether to use this view for rendering assigned node listings on this container\'s page, and/or on pages for categories in this container. The node listing will be passed to the view renderer, instead of to the default category module renderer.'),
       );
 
-      return $form;
-      break;
+    }
+  }
+}
+
+/**
+ * Additional form submit handler for node forms.
+ *
+ * Converts the $form_state['values']['category']['view'] array
+ * so that it will be merged back into node object.
+ */
+function category_views_node_form_submit($form, &$form_state) {
+  if (isset($form_state['values']['category']['view']['view_for_cats'])) {
+    $form_state['values']['view_for_cats'] = $form_state['values']['category']['view']['view_for_cats'];
+  }
+  if (isset($form_state['values']['category']['view']['view_display'])) {
+    $form_state['values']['view_display']['cont'] = (int) !empty($form_state['values']['category']['view']['view_display']['cont']);
+    $form_state['values']['view_display']['cat'] = (int) !empty($form_state['values']['category']['view']['view_display']['cat']);
   }
+  unset($form_state['values']['category']['view']);
 }
 
 /**
@@ -133,78 +154,28 @@ function category_views_category_legacy(
 }
 
 /**
- * Responds to the load of an existing container, by loading the container's
- * views settings from the database.
- *
- * @param $nid
- *   The node ID of the container being loaded.
+ * Fetches a list of all views usable for 'default' display.
  *
  * @return
- *   The views settings for the specified node.
- */
-function category_views_cont_load($nid) {
-  $container = new stdClass();
-  $sql = db_query("SELECT * FROM {category_views} WHERE cid = %d", $nid);
-  if ($result = db_fetch_object($sql)) {
-    $container->view_for_cats = $result->view_id;
-    if ($result->display_cont) $container->view_display['cont'] = 'cont';
-    if ($result->display_cat) $container->view_display['cat'] = 'cat';
-  }
-
-  return $container;
-}
-
-/**
- * Responds to the insert or update of an existing container, by updating the
- * container's export settings in the database.
- *
- * @param $node
- *   The container node being updated.
- */
-function category_views_cont_update($node) {
-  $node->cid = $node->nid;
-  $fields = array(
-    'cid',
-    'view_id',
-    'display_cont',
-    'display_cat',
-  );
-  $display_cont = $node->view_display['cont'] ? 1 : 0;
-  $display_cat = $node->view_display['cat'] ? 1 : 0;
-
-  $data_exists = (db_num_rows(db_query('SELECT * FROM {category_views} WHERE cid = %d', $node->nid)) > 0);
-  if ($data_exists) {
-    db_query('UPDATE {category_views} SET view_id = %d, display_cont = %d, display_cat = %d WHERE cid = %d', $node->view_for_cats, $display_cont, $display_cat, $node->nid);
-  }
-  else {
-    db_query('INSERT INTO {category_views} '. _category_container_insert($node, 1, $fields) .' VALUES (%d, %d, %d, %d)', $node->cid, $node->view_for_cats, $display_cont, $display_cat);
-  }
-}
-
-/**
- * Responds to the delete of an existing container, by deleting the container's
- * export settings in the database.
- *
- * @param $nid
- *   The node ID of the container being deleted.
- */
-function category_views_cont_delete($nid) {
-  db_query('DELETE FROM {category_views} WHERE cid = %d', $nid);
-}
-
-/**
- * Fetches a list of all views from the database.
- *
- * @return
- *   An array of views, where each key is the view's ID, and each value is its
+ *   An array of views, where each key is the view's name, and each value is its
  *   description (not its title).
  */
 function category_views_get_views() {
   $views = array();
 
-  $result = db_query("SELECT vid, description FROM {view_view} ORDER BY description");
-  while ($view = db_fetch_object($result)) {
-    $views[$view->vid] = $view->description;
+  // Get a list of all displays for all enabled views through the API, by searching
+  // for 'handler' setting, which is always there. We can't get the list directly
+  // from database, because some views may be stored in hook_views_default_views()
+  // implementations instead. (Views documentation suggests this practice for site
+  // specific modules too.)
+  $all_views = views_get_applicable_views('handler');
+  // Get only 'default' display for each view, and build form options including
+  // descriptions and tags.
+  foreach ($all_views as $result) {
+    list($view, $id) = $result;
+    if ($id == 'default') {
+      $views[$view->name] = $view->description . ($view->tag ? ' ('. $view->tag .')' : '');
+    }
   }
 
   return $views;
@@ -212,7 +183,7 @@ function category_views_get_views() {
 
 /**
  * Determine whether or not a view should be used for rendering the listing
- * of the specified node.
+ * of the specified node. This is called directly from category.module.
  *
  * @param $node
  *   The node being displayed.
@@ -223,15 +194,11 @@ function category_views_get_views() {
  *   Boolean TRUE if a view should be used, FALSE otherwise.
  */
 function category_views_is_visible($node, $type = NULL) {
-  if (!isset($type)) {
-    $type = category_node_get_type($node);
-  }
-
   if ($node->view_for_cats) {
-    if ($type == 'category_cont' && $node->view_display['cont']) {
+    if ($node->category['behavior'] === 'container' && $node->view_display['cont']) {
       return TRUE;
     }
-    elseif ($type == 'category_cat' && $node->view_display['cat']) {
+    elseif ($node->category['behavior'] === 'category' && $node->view_display['cat']) {
       return TRUE;
     }
   }
@@ -240,6 +207,7 @@ function category_views_is_visible($node
 
 /**
  * Retrieves a themed view of the specified category node.
+ * This is called directly from category.module.
  *
  * @param $node
  *   The category or container for which to generate the view output.
@@ -249,8 +217,12 @@ function category_views_is_visible($node
  *   another page (such as a category page).
  */
 function category_views_render_nodes($node) {
+  // We cannot use the simple views_embed_view() function, because the Exposed
+  // filters submit button won't point back to the category node in that case.
+  // We do the same manually here, adding the necessary path override.
   $view = views_get_view($node->view_for_cats);
-  $view->url = 'node/$arg';
-
-  return views_build_view('embed', $view, array($node->nid), $view->use_pager, $view->nodes_per_page);
+  if ($view) {
+    $view->override_path = 'node/%';
+    return $view->preview('default', array($node->nid));
+  }
 }
Index: contrib/category_views/category_views.schema
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/contrib/category_views/category_views.schema,v
retrieving revision 1.1
diff -u -p -r1.1 category_views.schema
--- contrib/category_views/category_views.schema	14 Jul 2007 20:39:08 -0000	1.1
+++ contrib/category_views/category_views.schema	5 Aug 2009 04:44:07 -0000
@@ -1,18 +0,0 @@
-<?php
-// $Id: category_views.schema,v 1.1 2007/07/14 20:39:08 bdragon Exp $
-
-/**
- * Implementation of hook_schema().
- */
-function category_views_schema() {
-  $schema['category_views'] = array(
-    'fields' => array(
-      'cid' => array('type' => 'int', 'unsigned' => true, 'not null' => true, 'default' => 0),
-      'view_id' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => true, 'not null' => true, 'default' => 0),
-      'display_cont' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => true, 'not null' => true, 'default' => 0),
-      'display_cat' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => true, 'not null' => true, 'default' => 0),
-    ),
-    'primary key' => array('cid'),
-  );
-  return $schema;
-}
Index: wrappers/menu/menu.admin.inc
===================================================================
RCS file: wrappers/menu/menu.admin.inc
diff -N wrappers/menu/menu.admin.inc
--- wrappers/menu/menu.admin.inc	10 Jul 2008 07:50:39 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,638 +0,0 @@
-<?php
-// $Id: menu.admin.inc,v 1.1 2008/07/10 07:50:39 jaza Exp $
-
-/**
- * @file
- * Administrative page callbaks for menu module.
- */
-
-/**
- * Menu callback which shows an overview page of all the custom menus and their descriptions.
- */
-function menu_overview_page() {
-  $result = db_query("SELECT * FROM {menu_custom} ORDER BY title");
-  $content = array();
-  while ($menu = db_fetch_array($result)) {
-    $menu['href'] = 'admin/build/menu-customize/'. $menu['menu_name'];
-    $menu['localized_options'] = array();
-    $content[] = $menu;
-  }
-  return theme('admin_block_content', $content);
-}
-
-/**
- * Form for editing an entire menu tree at once.
- *
- * Shows for one menu the menu items accessible to the current user and
- * relevant operations.
- */
-function menu_overview_form(&$form_state, $menu) {
-  global $menu_admin;
-  $sql = "
-    SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.*
-    FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
-    WHERE ml.menu_name = '%s'
-    ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC";
-  $result = db_query($sql, $menu['menu_name']);
-  $tree = menu_tree_data($result);
-  $node_links = array();
-  menu_tree_collect_node_links($tree, $node_links);
-  // We indicate that a menu administrator is running the menu access check.
-  $menu_admin = TRUE;
-  menu_tree_check_access($tree, $node_links);
-  $menu_admin = FALSE;
-
-  $form = _menu_overview_tree_form($tree);
-  $form['#menu'] =  $menu;
-  if (element_children($form)) {
-    $form['submit'] = array(
-      '#type' => 'submit',
-      '#value' => t('Save configuration'),
-    );
-  }
-  else {
-    $form['empty_menu'] = array('#value' => t('There are no menu items yet.'));
-  }
-  return $form;
-}
-
-/**
- * Recursive helper function for menu_overview_form().
- */
-function _menu_overview_tree_form($tree) {
-  static $form = array('#tree' => TRUE);
-  foreach ($tree as $data) {
-    $title = '';
-    $item = $data['link'];
-    // Don't show callbacks; these have $item['hidden'] < 0.
-    if ($item && $item['hidden'] >= 0) {
-      $mlid = 'mlid:'. $item['mlid'];
-      $form[$mlid]['#item'] = $item;
-      $form[$mlid]['#attributes'] = $item['hidden'] ? array('class' => 'menu-disabled') : array('class' => 'menu-enabled');
-      $form[$mlid]['title']['#value'] = l($item['title'], $item['href'], $item['localized_options']) . ($item['hidden'] ? ' ('. t('disabled') .')' : '');
-      $form[$mlid]['hidden'] = array(
-        '#type' => 'checkbox',
-        '#default_value' => !$item['hidden'],
-      );
-      $form[$mlid]['expanded'] = array(
-        '#type' => 'checkbox',
-        '#default_value' => $item['expanded'],
-      );
-      $form[$mlid]['weight'] = array(
-        '#type' => 'weight',
-        '#delta' => 50,
-        '#default_value' => isset($form_state[$mlid]['weight']) ? $form_state[$mlid]['weight'] : $item['weight'],
-      );
-      $form[$mlid]['mlid'] = array(
-        '#type' => 'hidden',
-        '#value' => $item['mlid'],
-      );
-      $form[$mlid]['plid'] = array(
-        '#type' => 'textfield',
-        '#default_value' => isset($form_state[$mlid]['plid']) ? $form_state[$mlid]['plid'] : $item['plid'],
-        '#size' => 6,
-      );
-      // Build a list of operations.
-      $operations = array();
-      $operations['edit'] = l(t('edit'), 'admin/build/menu/item/'. $item['mlid'] .'/edit');
-      // Only items created by the menu module can be deleted.
-      if ($item['module'] == 'menu' || $item['updated'] == 1) {
-        $operations['delete'] = l(t('delete'), 'admin/build/menu/item/'. $item['mlid'] .'/delete');
-      }
-      // Set the reset column.
-      elseif ($item['module'] == 'system' && $item['customized']) {
-        $operations['reset'] = l(t('reset'), 'admin/build/menu/item/'. $item['mlid'] .'/reset');
-      }
-
-      $form[$mlid]['operations'] = array();
-      foreach ($operations as $op => $value) {
-        $form[$mlid]['operations'][$op] = array('#value' => $value);
-      }
-    }
-
-    if ($data['below']) {
-      _menu_overview_tree_form($data['below']);
-    }
-  }
-  return $form;
-}
-
-/**
- * Submit handler for the menu overview form.
- *
- * This function takes great care in saving parent items first, then items
- * underneath them. Saving items in the incorrect order can break the menu tree.
- *
- * @see menu_overview_form()
- */
-function menu_overview_form_submit($form, &$form_state) {
-  // When dealing with saving menu items, the order in which these items are
-  // saved is critical. If a changed child item is saved before its parent,
-  // the child item could be saved with an invalid path past its immediate
-  // parent. To prevent this, save items in the form in the same order they
-  // are sent by $_POST, ensuring parents are saved first, then their children.
-  // See http://drupal.org/node/181126#comment-632270
-  $order = array_flip(array_keys($form['#post'])); // Get the $_POST order.
-  $form = array_merge($order, $form); // Update our original form with the new order.
-
-  $updated_items = array();
-  $fields = array('expanded', 'weight', 'plid');
-  foreach (element_children($form) as $mlid) {
-    if (isset($form[$mlid]['#item'])) {
-      $element = $form[$mlid];
-      // Update any fields that have changed in this menu item.
-      foreach ($fields as $field) {
-        if ($element[$field]['#value'] != $element[$field]['#default_value']) {
-          $element['#item'][$field] = $element[$field]['#value'];
-          $updated_items[$mlid] = $element['#item'];
-        }
-      }
-      // Hidden is a special case, the value needs to be reversed.
-      if ($element['hidden']['#value'] != $element['hidden']['#default_value']) {
-        $element['#item']['hidden'] = !$element['hidden']['#value'];
-        $updated_items[$mlid] = $element['#item'];
-      }
-    }
-  }
-
-  // Save all our changed items to the database.
-  foreach ($updated_items as $item) {
-    $item['customized'] = 1;
-    menu_link_save($item);
-  }
-}
-
-/**
- * Theme the menu overview form into a table.
- *
- * @ingroup themeable
- */
-function theme_menu_overview_form($form) {
-  drupal_add_tabledrag('menu-overview', 'match', 'parent', 'menu-plid', 'menu-plid', 'menu-mlid', TRUE, MENU_MAX_DEPTH - 1);
-  drupal_add_tabledrag('menu-overview', 'order', 'sibling', 'menu-weight');
-
-  $header = array(
-    t('Menu item'),
-    array('data' => t('Enabled'), 'class' => 'checkbox'),
-    array('data' => t('Expanded'), 'class' => 'checkbox'),
-    t('Weight'),
-    array('data' => t('Operations'), 'colspan' => '3'),
-  );
-
-  $rows = array();
-  foreach (element_children($form) as $mlid) {
-    if (isset($form[$mlid]['hidden'])) {
-      $element = &$form[$mlid];
-      // Build a list of operations.
-      $operations = array();
-      foreach (element_children($element['operations']) as $op) {
-        $operations[] = drupal_render($element['operations'][$op]);
-      }
-      while (count($operations) < 2) {
-        $operations[] = '';
-      }
-
-      // Add special classes to be used for tabledrag.js.
-      $element['plid']['#attributes']['class'] = 'menu-plid';
-      $element['mlid']['#attributes']['class'] = 'menu-mlid';
-      $element['weight']['#attributes']['class'] = 'menu-weight';
-
-      // Change the parent field to a hidden. This allows any value but hides the field.
-      $element['plid']['#type'] = 'hidden';
-
-      $row = array();
-      $row[] = theme('indentation', $element['#item']['depth'] - 1) . drupal_render($element['title']);
-      $row[] = array('data' => drupal_render($element['hidden']), 'class' => 'checkbox');
-      $row[] = array('data' => drupal_render($element['expanded']), 'class' => 'checkbox');
-      $row[] = drupal_render($element['weight']) . drupal_render($element['plid']) . drupal_render($element['mlid']);
-      $row = array_merge($row, $operations);
-
-      $row = array_merge(array('data' => $row), $element['#attributes']);
-      $row['class'] = !empty($row['class']) ? $row['class'] .' draggable' : 'draggable';
-      $rows[] = $row;
-    }
-  }
-  $output = '';
-  if ($rows) {
-    $output .= theme('table', $header, $rows, array('id' => 'menu-overview'));
-  }
-  $output .= drupal_render($form);
-  return $output;
-}
-
-/**
- * Menu callback; Build the menu link editing form.
- */
-function menu_edit_item(&$form_state, $type, $item, $menu) {
-
-  $form['menu'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Menu settings'),
-    '#collapsible' => FALSE,
-    '#tree' => TRUE,
-    '#weight' => -2,
-    '#attributes' => array('class' => 'menu-item-form'),
-    '#item' => $item,
-  );
-  if ($type == 'add' || empty($item)) {
-    // This is an add form, initialize the menu link.
-    $item = array('link_title' => '', 'mlid' => 0, 'plid' => 0, 'menu_name' => $menu['menu_name'], 'weight' => 0, 'link_path' => '', 'options' => array(), 'module' => 'menu', 'expanded' => 0, 'hidden' => 0, 'has_children' => 0);
-  }
-  foreach (array('link_path', 'mlid', 'module', 'has_children', 'options') as $key) {
-    $form['menu'][$key] = array('#type' => 'value', '#value' => $item[$key]);
-  }
-  // Any item created or edited via this interface is considered "customized".
-  $form['menu']['customized'] = array('#type' => 'value', '#value' => 1);
-  $form['menu']['original_item'] = array('#type' => 'value', '#value' => $item);
-
-  $path = $item['link_path'];
-  if (isset($item['options']['query'])) {
-    $path .= '?'. $item['options']['query'];
-  }
-  if (isset($item['options']['fragment'])) {
-    $path .= '#'. $item['options']['fragment'];
-  }
-  if ($item['module'] == 'menu') {
-    $form['menu']['link_path'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Path'),
-      '#default_value' => $path,
-      '#description' => t('The path this menu item links to. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')),
-      '#required' => TRUE,
-    );
-    $form['delete'] = array(
-      '#type' => 'submit',
-      '#value' => t('Delete'),
-      '#access' => $item['mlid'],
-      '#submit' => array('menu_item_delete_submit'),
-      '#weight' => 10,
-    );
-  }
-  else {
-    $form['menu']['_path'] = array(
-      '#type' => 'item',
-      '#title' => t('Path'),
-      '#description' => l($item['link_title'], $item['href'], $item['options']),
-    );
-  }
-  $form['menu']['link_title'] = array('#type' => 'textfield',
-    '#title' => t('Menu link title'),
-    '#default_value' => $item['link_title'],
-    '#description' => t('The link text corresponding to this item that should appear in the menu.'),
-    '#required' => TRUE,
-  );
-  $form['menu']['description'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Description'),
-    '#default_value' => isset($item['options']['attributes']['title']) ? $item['options']['attributes']['title'] : '',
-    '#rows' => 1,
-    '#description' => t('The description displayed when hovering over a menu item.'),
-  );
-  $form['menu']['enabled'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Enabled'),
-    '#default_value' => !$item['hidden'],
-    '#description' => t('Menu items that are not enabled will not be listed in any menu.'),
-  );
-  $form['menu']['expanded'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Expanded'),
-    '#default_value' => $item['expanded'],
-    '#description' => t('If selected and this menu item has children, the menu will always appear expanded.'),
-  );
-
-  // Generate a list of possible parents (not including this item or descendants).
-  $options = menu_parent_options(menu_get_menus(), $item);
-  $default = $item['menu_name'] .':'. $item['plid'];
-  if (!isset($options[$default])) {
-    $default = 'navigation:0';
-  }
-  $form['menu']['parent'] = array(
-    '#type' => 'select',
-    '#title' => t('Parent item'),
-    '#default_value' => $default,
-    '#options' => $options,
-    '#description' => t('The maximum depth for an item and all its children is fixed at !maxdepth. Some menu items may not be available as parents if selecting them would exceed this limit.', array('!maxdepth' => MENU_MAX_DEPTH)),
-    '#attributes' => array('class' => 'menu-title-select'),
-  );
-  $form['menu']['weight'] = array(
-    '#type' => 'weight',
-    '#title' => t('Weight'),
-    '#delta' => 50,
-    '#default_value' => $item['weight'],
-    '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
-  );
-  $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
-
-
-  return $form;
-}
-
-/**
- * Validate form values for a menu link being added or edited.
- */
-function menu_edit_item_validate($form, &$form_state) {
-  $item = &$form_state['values']['menu'];
-  $normal_path = drupal_get_normal_path($item['link_path']);
-  if ($item['link_path'] != $normal_path) {
-    drupal_set_message(t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array('%link_path' => $item['link_path'], '%normal_path' => $normal_path)));
-    $item['link_path'] = $normal_path;
-  }
-  if (!menu_path_is_external($item['link_path'])) {
-    $parsed_link = parse_url($item['link_path']);
-    if (isset($parsed_link['query'])) {
-      $item['options']['query'] = $parsed_link['query'];
-    }
-    if (isset($parsed_link['fragment'])) {
-      $item['options']['fragment'] = $parsed_link['fragment'];
-    }
-    if ($item['link_path'] != $parsed_link['path']) {
-      $item['link_path'] = $parsed_link['path'];
-    }
-  }
-  if (!trim($item['link_path']) || !menu_valid_path($item)) {
-    form_set_error('link_path', t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $item['link_path'])));
-  }
-}
-
-/**
- * Submit function for the delete button on the menu item editing form.
- */
-function menu_item_delete_submit($form, &$form_state) {
-  $form_state['redirect'] = 'admin/build/menu/item/'. $form_state['values']['menu']['mlid'] .'/delete';
-}
-
-/**
- * Process menu and menu item add/edit form submissions.
- */
-function menu_edit_item_submit($form, &$form_state) {
-  $item = $form_state['values']['menu'];
-
-  // The value of "hidden" is the opposite of the value
-  // supplied by the "enabled" checkbox.
-  $item['hidden'] = (int) !$item['enabled'];
-  unset($item['enabled']);
-
-  $item['options']['attributes']['title'] = $item['description'];
-  list($item['menu_name'], $item['plid']) = explode(':', $item['parent']);
-  if (!menu_link_save($item)) {
-    drupal_set_message(t('There was an error saving the menu link.'), 'error');
-  }
-  $form_state['redirect'] = 'admin/build/menu-customize/'. $item['menu_name'];
-}
-
-/**
- * Menu callback; Build the form that handles the adding/editing of a custom menu.
- */
-function menu_edit_menu(&$form_state, $type, $menu = array()) {
-  if ($type == 'edit') {
-    $form['menu_name'] = array('#type' => 'value', '#value' => $menu['menu_name']);
-    $form['#insert'] = FALSE;
-    $form['delete'] = array(
-      '#type' => 'submit',
-      '#value' => t('Delete'),
-      '#access' => !in_array($menu['menu_name'], menu_list_system_menus()),
-      '#submit' => array('menu_custom_delete_submit'),
-      '#weight' => 10,
-    );
-  }
-  else {
-    $menu = array('menu_name' => '', 'title' => '', 'description' => '');
-    $form['menu_name'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Menu name'),
-      '#maxsize' => MENU_MAX_MENU_NAME_LENGTH_UI,
-      '#description' => t('The machine-readable name of this menu. This text will be used for constructing the URL of the <em>menu overview</em> page for this menu. This name must contain only lowercase letters, numbers, and hyphens, and must be unique.'),
-      '#required' => TRUE,
-    );
-    $form['#insert'] = TRUE;
-  }
-  $form['#title'] = $menu['title'];
-  $form['title'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Title'),
-    '#default_value' => $menu['title'],
-    '#required' => TRUE,
-  );
-  $form['description'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Description'),
-    '#default_value' => $menu['description'],
-  );
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  return $form;
-}
-
-/**
- * Submit function for the 'Delete' button on the menu editing form.
- */
-function menu_custom_delete_submit($form, &$form_state) {
-  $form_state['redirect'] = 'admin/build/menu-customize/'. $form_state['values']['menu_name'] .'/delete';
-}
-
-/**
- * Menu callback; check access and get a confirm form for deletion of a custom menu.
- */
-function menu_delete_menu_page($menu) {
-  // System-defined menus may not be deleted.
-  if (in_array($menu['menu_name'], menu_list_system_menus())) {
-    drupal_access_denied();
-    return;
-  }
-  return drupal_get_form('menu_delete_menu_confirm', $menu);
-}
-
-/**
- * Build a confirm form for deletion of a custom menu.
- */
-function menu_delete_menu_confirm(&$form_state, $menu) {
-  $form['#menu'] = $menu;
-  $caption = '';
-  $num_links = db_result(db_query("SELECT COUNT(*) FROM {menu_links} WHERE menu_name = '%s'", $menu['menu_name']));
-  if ($num_links) {
-    $caption .= '<p>'. format_plural($num_links, '<strong>Warning:</strong> There is currently 1 menu item in %title. It will be deleted (system-defined items will be reset).', '<strong>Warning:</strong> There are currently @count menu items in %title. They will be deleted (system-defined items will be reset).', array('%title' => $menu['title'])) .'</p>';
-  }
-  $caption .= '<p>'. t('This action cannot be undone.') .'</p>';
-  return confirm_form($form, t('Are you sure you want to delete the custom menu %title?', array('%title' => $menu['title'])), 'admin/build/menu-customize/'. $menu['menu_name'], $caption, t('Delete'));
-}
-
-/**
- * Delete a custom menu and all items in it.
- */
-function menu_delete_menu_confirm_submit($form, &$form_state) {
-  $menu = $form['#menu'];
-  $form_state['redirect'] = 'admin/build/menu';
-  // System-defined menus may not be deleted - only menus defined by this module.
-  if (in_array($menu['menu_name'], menu_list_system_menus())  || !db_result(db_query("SELECT COUNT(*) FROM {menu_custom} WHERE menu_name = '%s'", $menu['menu_name']))) {
-    return;
-  }
-  // Reset all the menu links defined by the system via hook_menu.
-  $result = db_query("SELECT * FROM {menu_links} ml INNER JOIN {menu_router} m ON ml.router_path = m.path WHERE ml.menu_name = '%s' AND ml.module = 'system' ORDER BY m.number_parts ASC", $menu['menu_name']);
-  while ($item = db_fetch_array($result)) {
-    menu_reset_item($item);
-  }
-  // Delete all links to the overview page for this menu.
-  $result = db_query("SELECT mlid FROM {menu_links} ml WHERE ml.link_path = '%s'", 'admin/build/menu-customize/'. $menu['menu_name']);
-  while ($m = db_fetch_array($result)) {
-    menu_link_delete($m['mlid']);
-  }
-  // Delete all the links in the menu and the menu from the list of custom menus.
-  db_query("DELETE FROM {menu_links} WHERE menu_name = '%s'", $menu['menu_name']);
-  db_query("DELETE FROM {menu_custom} WHERE menu_name = '%s'", $menu['menu_name']);
-  // Delete all the blocks for this menu.
-  db_query("DELETE FROM {blocks} WHERE module = 'menu' AND delta = '%s'", $menu['menu_name']);
-  db_query("DELETE FROM {blocks_roles} WHERE module = 'menu' AND delta = '%s'", $menu['menu_name']);
-  menu_cache_clear_all();
-  cache_clear_all();
-  $t_args = array('%title' => $menu['title']);
-  drupal_set_message(t('The custom menu %title has been deleted.', $t_args));
-  watchdog('menu', 'Deleted custom menu %title and all its menu items.', $t_args, WATCHDOG_NOTICE);
-}
-
-/**
- * Validates the human and machine-readable names when adding or editing a menu.
- */
-function menu_edit_menu_validate($form, &$form_state) {
-  $item = $form_state['values'];
-  if (preg_match('/[^a-z0-9-]/', $item['menu_name'])) {
-    form_set_error('menu_name', t('The menu name may only consist of lowercase letters, numbers, and hyphens.'));
-  }
-  if (strlen($item['menu_name']) > MENU_MAX_MENU_NAME_LENGTH_UI) {
-    form_set_error('menu_name', format_plural(MENU_MAX_MENU_NAME_LENGTH_UI, "The menu name can't be longer than 1 character.", "The menu name can't be longer than @count characters."));
-  }
-  if ($form['#insert']) {
-    // We will add 'menu-' to the menu name to help avoid name-space conflicts.
-    $item['menu_name'] = 'menu-'. $item['menu_name'];
-    if (db_result(db_query("SELECT menu_name FROM {menu_custom} WHERE menu_name = '%s'", $item['menu_name'])) ||
-      db_result(db_query_range("SELECT menu_name FROM {menu_links} WHERE menu_name = '%s'", $item['menu_name'], 0, 1))) {
-      form_set_error('menu_name', t('The menu already exists.'));
-    }
-  }
-}
-
-/**
- * Submit function for adding or editing a custom menu.
- */
-function menu_edit_menu_submit($form, &$form_state) {
-  $menu = $form_state['values'];
-  $path = 'admin/build/menu-customize/';
-  if ($form['#insert']) {
-    // Add 'menu-' to the menu name to help avoid name-space conflicts.
-    $menu['menu_name'] = 'menu-'. $menu['menu_name'];
-    $link['link_title'] = $menu['title'];
-    $link['link_path'] = $path . $menu['menu_name'];
-    $link['router_path'] = $path .'%';
-    $link['module'] = 'menu';
-    $link['plid'] = db_result(db_query("SELECT mlid FROM {menu_links} WHERE link_path = '%s' AND module = '%s'", 'admin/build/menu', 'system'));
-    menu_link_save($link);
-    db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('%s', '%s', '%s')", $menu['menu_name'], $menu['title'], $menu['description']);
-  }
-  else {
-    db_query("UPDATE {menu_custom} SET title = '%s', description = '%s' WHERE menu_name = '%s'", $menu['title'], $menu['description'], $menu['menu_name']);
-    $result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = '%s'", $path . $menu['menu_name']);
-    while ($m = db_fetch_array($result)) {
-      $link = menu_link_load($m['mlid']);
-      $link['link_title'] = $menu['title'];
-      menu_link_save($link);
-    }
-  }
-  $form_state['redirect'] = $path . $menu['menu_name'];
-}
-
-/**
- * Menu callback; Check access and present a confirm form for deleting a menu link.
- */
-function menu_item_delete_page($item) {
-  // Links defined via hook_menu may not be deleted. Updated items are an
-  // exception, as they can be broken.
-  if ($item['module'] == 'system' && !$item['updated']) {
-    drupal_access_denied();
-    return;
-  }
-  return drupal_get_form('menu_item_delete_form', $item);
-}
-
-/**
- * Build a confirm form for deletion of a single menu link.
- */
-function menu_item_delete_form(&$form_state, $item) {
-  $form['#item'] = $item;
-  return confirm_form($form, t('Are you sure you want to delete the custom menu item %item?', array('%item' => $item['link_title'])), 'admin/build/menu-customize/'. $item['menu_name']);
-}
-
-/**
- * Process menu delete form submissions.
- */
-function menu_item_delete_form_submit($form, &$form_state) {
-  $item = $form['#item'];
-  menu_link_delete($item['mlid']);
-  $t_args = array('%title' => $item['link_title']);
-  drupal_set_message(t('The menu item %title has been deleted.', $t_args));
-  watchdog('menu', 'Deleted menu item %title.', $t_args, WATCHDOG_NOTICE);
-  $form_state['redirect'] = 'admin/build/menu-customize/'. $item['menu_name'];
-}
-
-/**
- * Menu callback; reset a single modified item.
- */
-function menu_reset_item_confirm(&$form_state, $item) {
-  $form['item'] = array('#type' => 'value', '#value' => $item);
-  return confirm_form($form, t('Are you sure you want to reset the item %item to its default values?', array('%item' => $item['link_title'])), 'admin/build/menu-customize/'. $item['menu_name'], t('Any customizations will be lost. This action cannot be undone.'), t('Reset'));
-}
-
-/**
- * Process menu reset item form submissions.
- */
-function menu_reset_item_confirm_submit($form, &$form_state) {
-  $item = $form_state['values']['item'];
-  $new_item = menu_reset_item($item);
-  drupal_set_message(t('The menu item was reset to its default settings.'));
-  $form_state['redirect'] = 'admin/build/menu-customize/'. $new_item['menu_name'];
-}
-
-/**
- * Menu callback; Build the form presenting menu configuration options.
- */
-function menu_configure() {
-  $form['intro'] = array(
-    '#type' => 'item',
-    '#value' => t('The menu module allows on-the-fly creation of menu links in the content authoring forms. The following option sets the default menu in which a new link will be added.'),
-  );
-
-  $menu_options = menu_get_menus();
-  $form['menu_default_node_menu'] = array(
-    '#type' => 'select',
-    '#title' => t('Default menu for content'),
-    '#default_value' => variable_get('menu_default_node_menu', 'primary-links'),
-    '#options' => $menu_options,
-    '#description' => t('Choose the menu to be the default in the menu options in the content authoring form.'),
-  );
-
-  $primary = variable_get('menu_primary_links_source', 'primary-links');
-  $primary_options = array_merge($menu_options, array('' => t('No primary links')));
-  $form['menu_primary_links_source'] = array(
-    '#type' => 'select',
-    '#title' => t('Source for the primary links'),
-    '#default_value' => $primary,
-    '#options' => $primary_options,
-    '#tree' => FALSE,
-    '#description' => t('Select what should be displayed as the primary links.'),
-  );
-
-  $secondary_options = array_merge($menu_options, array('' => t('No secondary links')));
-  $form["menu_secondary_links_source"] = array(
-    '#type' => 'select',
-    '#title' => t('Source for the secondary links'),
-    '#default_value' => variable_get('menu_secondary_links_source', 'secondary-links'),
-    '#options' => $secondary_options,
-    '#tree' => FALSE,
-    '#description' => t('Select what should be displayed as the secondary links. You can choose the same menu for secondary links as for primary links (currently %primary). If you do this, the children of the active primary menu link will be displayed as secondary links.', array('%primary' => $primary_options[$primary])),
-  );
-
-  return system_settings_form($form);
-}
-
Index: wrappers/menu/menu.info.php
===================================================================
RCS file: wrappers/menu/menu.info.php
diff -N wrappers/menu/menu.info.php
--- wrappers/menu/menu.info.php	5 Mar 2009 22:23:55 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,5 +0,0 @@
-; $Id: menu.info.php,v 1.1 2009/03/05 22:23:55 jaza Exp $
-name = Wrapper: menu
-description = Wrapper module that is identical to the Drupal core menu module, except that it lacks menu-on-the-fly functionality, which is incompatible with category_menu.
-package = Category
-core = 6.x
Index: wrappers/menu/menu.install.php
===================================================================
RCS file: wrappers/menu/menu.install.php
diff -N wrappers/menu/menu.install.php
--- wrappers/menu/menu.install.php	5 Mar 2009 22:23:55 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,58 +0,0 @@
-<?php
-// $Id: menu.install.php,v 1.1 2009/03/05 22:23:55 jaza Exp $
-
-/**
- * Implementation of hook_install().
- */
-function menu_install() {
-  // Create tables.
-  drupal_install_schema('menu');
-  
-  $t = get_t();
-  db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('%s', '%s', '%s')", 'navigation', $t('Navigation'), $t('The navigation menu is provided by Drupal and is the main interactive menu for any site. It is usually the only menu that contains personalized links for authenticated users, and is often not even visible to anonymous users.'));
-  db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('%s', '%s', '%s')", 'primary-links', $t('Primary links'), $t('Primary links are often used at the theme layer to show the major sections of a site. A typical representation for primary links would be tabs along the top.'));
-  db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('%s', '%s', '%s')", 'secondary-links', $t('Secondary links'), $t('Secondary links are often used for pages like legal notices, contact details, and other secondary navigation items that play a lesser role than primary links'));
-}
-
-/**
- * Implementation of hook_uninstall().
- */
-function menu_uninstall() {
-  // Remove tables.
-  drupal_uninstall_schema('menu');
-  menu_rebuild();
-}
-
-/**
- * Implementation of hook_schema().
- */
-function menu_schema() {
-  $schema['menu_custom'] = array(
-    'description' => t('Holds definitions for top-level custom menus (for example, Primary Links).'),
-    'fields' => array(
-      'menu_name' => array(
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => t('Primary Key: Unique key for menu. This is used as a block delta so length is 32.'),
-      ),
-      'title' => array(
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => t('Menu title; displayed at top of block.'),
-      ),
-      'description' => array(
-        'type' => 'text',
-        'not null' => FALSE,
-        'description' => t('Menu description.'),
-      ),
-    ),
-    'primary key' => array('menu_name'),
-  );
-
-  return $schema;
-}
-
Index: wrappers/menu/menu.module.php
===================================================================
RCS file: wrappers/menu/menu.module.php
diff -N wrappers/menu/menu.module.php
--- wrappers/menu/menu.module.php	5 Mar 2009 22:23:55 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,337 +0,0 @@
-<?php
-// $Id: menu.module.php,v 1.1 2009/03/05 22:23:55 jaza Exp $
-
-/**
- * @file
- * Wrapper module that is identical to the Drupal core menu module, except
- * that it lacks menu-on-the-fly functionality, which is incompatible with
- * category_menu.
- */
-
-/**
- * Maximum length of menu name as entered by the user. Database length is 32
- * and we add a menu- prefix.
- */
-define('MENU_MAX_MENU_NAME_LENGTH_UI', 27);
-
-/**
- * Implementation of hook_help().
- */
-function menu_help($path, $arg) {
-  switch ($path) {
-    case 'admin/help#menu':
-      $output = '<p>'. t("The menu module provides an interface to control and customize Drupal's powerful menu system. Menus are a hierarchical collection of links, or menu items, used to navigate a website, and are positioned and displayed using Drupal's flexible block system. By default, three menus are created during installation: <em>Navigation</em>, <em>Primary links</em>, and <em>Secondary links</em>. The <em>Navigation</em> menu contains most links necessary for working with and navigating your site, and is often displayed in either the left or right sidebar. Most Drupal themes also provide support for <em>Primary links</em> and <em>Secondary links</em>, by displaying them in either the header or footer of each page. By default, <em>Primary links</em> and <em>Secondary links</em> contain no menu items but may be configured to contain custom menu items specific to your site.") .'</p>';
-      $output .= '<p>'. t('The <a href="@menu">menus page</a> displays all menus currently available on your site. Select a menu from this list to add or edit a menu item, or to rearrange items within the menu. Create new menus using the <a href="@add-menu">add menu page</a> (the block containing a new menu must also be enabled on the <a href="@blocks">blocks administration page</a>).', array('@menu' => url('admin/build/menu'), '@add-menu' => url('admin/build/menu/add'), '@blocks' => url('admin/build/block'))) .'</p>';
-      $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@menu">Menu module</a>.', array('@menu' => 'http://drupal.org/handbook/modules/menu/')) .'</p>';
-      return $output;
-    case 'admin/build/menu':
-      return '<p>'. t('Menus are a collection of links (menu items) used to navigate a website. The menus currently available on your site are displayed below. Select a menu from this list to manage its menu items.') .'</p>';
-    case 'admin/build/menu/add':
-      return '<p>'. t('Enter the name for your new menu. Remember to enable the newly created block in the <a href="@blocks">blocks administration page</a>.', array('@blocks' => url('admin/build/block'))) .'</p>';
-    case 'admin/build/menu-customize/%':
-      return '<p>'. t('To rearrange menu items, grab a drag-and-drop handle under the <em>Menu item</em> column and drag the items (or group of items) to a new location in the list. (Grab a handle by clicking and holding the mouse while hovering over a handle icon.) Remember that your changes will not be saved until you click the <em>Save configuration</em> button at the bottom of the page.') .'</p>';
-    case 'admin/build/menu/item/add':
-      return '<p>'. t('Enter the title and path for your new menu item.') .'</p>';
-  }
-}
-
-/**
- * Implementation of hook_perm().
- */
-function menu_perm() {
-  return array('administer menu');
-}
-
-/**
- * Implementation of hook_menu().
- */
-function menu_menu() {
-  $items['admin/build/menu'] = array(
-    'title' => 'Menus',
-    'description' => "Control your site's navigation menu, primary links and secondary links. as well as rename and reorganize menu items.",
-    'page callback' => 'menu_overview_page',
-    'access callback' => 'user_access',
-    'access arguments' => array('administer menu'),
-    'file' => 'menu.admin.inc',
-  );
-
-  $items['admin/build/menu/list'] = array(
-    'title' => 'List menus',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-    'weight' => -10,
-    'file' => 'menu.admin.inc',
-  );
-  $items['admin/build/menu/add'] = array(
-    'title' => 'Add menu',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('menu_edit_menu', 'add'),
-    'access arguments' => array('administer menu'),
-    'type' => MENU_LOCAL_TASK,
-    'file' => 'menu.admin.inc',
-  );
-  $items['admin/build/menu/settings'] = array(
-    'title' => 'Settings',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('menu_configure'),
-    'access arguments' => array('administer menu'),
-    'type' => MENU_LOCAL_TASK,
-    'weight' => 5,
-    'file' => 'menu.admin.inc',
-  );
-  $items['admin/build/menu-customize/%menu'] = array(
-    'title' => 'Customize menu',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('menu_overview_form', 3),
-    'title callback' => 'menu_overview_title',
-    'title arguments' => array(3),
-    'access arguments' => array('administer menu'),
-    'type' => MENU_CALLBACK,
-    'file' => 'menu.admin.inc',
-  );
-  $items['admin/build/menu-customize/%menu/list'] = array(
-    'title' => 'List items',
-    'weight' => -10,
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-    'file' => 'menu.admin.inc',
-  );
-  $items['admin/build/menu-customize/%menu/add'] = array(
-    'title' => 'Add item',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('menu_edit_item', 'add', NULL, 3),
-    'access arguments' => array('administer menu'),
-    'type' => MENU_LOCAL_TASK,
-    'file' => 'menu.admin.inc',
-  );
-  $items['admin/build/menu-customize/%menu/edit'] = array(
-    'title' => 'Edit menu',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('menu_edit_menu', 'edit', 3),
-    'access arguments' => array('administer menu'),
-    'type' => MENU_LOCAL_TASK,
-    'file' => 'menu.admin.inc',
-  );
-  $items['admin/build/menu-customize/%menu/delete'] = array(
-    'title' => 'Delete menu',
-    'page callback' => 'menu_delete_menu_page',
-    'page arguments' => array(3),
-    'access arguments' => array('administer menu'),
-    'type' => MENU_CALLBACK,
-    'file' => 'menu.admin.inc',
-  );
-  $items['admin/build/menu/item/%menu_link/edit'] = array(
-    'title' => 'Edit menu item',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('menu_edit_item', 'edit', 4, NULL),
-    'access arguments' => array('administer menu'),
-    'type' => MENU_CALLBACK,
-    'file' => 'menu.admin.inc',
-  );
-  $items['admin/build/menu/item/%menu_link/reset'] = array(
-    'title' => 'Reset menu item',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('menu_reset_item_confirm', 4),
-    'access arguments' => array('administer menu'),
-    'type' => MENU_CALLBACK,
-    'file' => 'menu.admin.inc',
-  );
-  $items['admin/build/menu/item/%menu_link/delete'] = array(
-    'title' => 'Delete menu item',
-    'page callback' => 'menu_item_delete_page',
-    'page arguments' => array(4),
-    'access arguments' => array('administer menu'),
-    'type' => MENU_CALLBACK,
-    'file' => 'menu.admin.inc',
-  );
-
-  return $items;
-}
-
-/**
- * Implemenation of hook_theme().
- */
-function menu_theme() {
-  return array(
-    'menu_overview_form' => array(
-      'file' => 'menu.admin.inc',
-      'arguments' => array('form' => NULL),
-    ),
-  );
-}
-
-/**
- * Implementation of hook_enable()
- *
- *  Add a link for each custom menu.
- */
-function menu_enable() {
-  menu_rebuild();
-  $link = db_fetch_array(db_query("SELECT mlid AS plid, menu_name from {menu_links} WHERE link_path = 'admin/build/menu' AND module = 'system'"));
-  $link['router_path'] = 'admin/build/menu-customize/%';
-  $link['module'] = 'menu';
-  $result = db_query("SELECT * FROM {menu_custom}");
-  while ($menu = db_fetch_array($result)) {
-    $link['mlid'] = 0;
-    $link['link_title'] = $menu['title'];
-    $link['link_path'] = 'admin/build/menu-customize/'. $menu['menu_name'];
-    if (!db_result(db_query("SELECT mlid FROM {menu_links} WHERE link_path = '%s' AND plid = %d", $link['link_path'], $link['plid']))) {
-      menu_link_save($link);
-    }
-  }
-  menu_cache_clear_all();
-}
-
-/**
- * Title callback for the menu overview page and links.
- */
-function menu_overview_title($menu) {
-  return $menu['title'];
-}
-
-/**
- * Load the data for a single custom menu.
- */
-function menu_load($menu_name) {
-  return db_fetch_array(db_query("SELECT * FROM {menu_custom} WHERE menu_name = '%s'", $menu_name));
-}
-
-/**
- * Return a list of menu items that are valid possible parents for the given menu item.
- *
- * @param $menus
- *   An array of menu names and titles, such as from menu_get_menus().
- * @param $item
- *   The menu item for which to generate a list of parents.
- *   If $item['mlid'] == 0 then the complete tree is returned.
- * @return
- *   An array of menu link titles keyed on the a string containing the menu name
- *   and mlid. The list excludes the given item and its children.
- */
-function menu_parent_options($menus, $item) {
-  // The menu_links table can be practically any size and we need a way to
-  // allow contrib modules to provide more scalable pattern choosers.
-  // hook_form_alter is too late in itself because all the possible parents are
-  // retrieved here, unless menu_override_parent_selector is set to TRUE.
-  if (variable_get('menu_override_parent_selector', FALSE)) {
-    return array();
-  }
-  // If the item has children, there is an added limit to the depth of valid parents.
-  if (isset($item['parent_depth_limit'])) {
-    $limit = $item['parent_depth_limit'];
-  }
-  else {
-    $limit = _menu_parent_depth_limit($item);
-  }
-
-  foreach ($menus as $menu_name => $title) {
-    $tree = menu_tree_all_data($menu_name, NULL);
-    $options[$menu_name .':0'] = '<'. $title .'>';
-    _menu_parents_recurse($tree, $menu_name, '--', $options, $item['mlid'], $limit);
-  }
-  return $options;
-}
-
-/**
- * Recursive helper function for menu_parent_options().
- */
-function _menu_parents_recurse($tree, $menu_name, $indent, &$options, $exclude, $depth_limit) {
-  foreach ($tree as $data) {
-    if ($data['link']['depth'] > $depth_limit) {
-      // Don't iterate through any links on this level.
-      break;
-    }
-    if ($data['link']['mlid'] != $exclude && $data['link']['hidden'] >= 0) {
-      $title = $indent .' '. truncate_utf8($data['link']['title'], 30, TRUE, FALSE);
-      if ($data['link']['hidden']) {
-        $title .= ' ('. t('disabled') .')';
-      }
-      $options[$menu_name .':'. $data['link']['mlid']] = $title;
-      if ($data['below']) {
-        _menu_parents_recurse($data['below'], $menu_name, $indent .'--', $options, $exclude, $depth_limit);
-      }
-    }
-  }
-}
-
-/**
- * Reset a system-defined menu item.
- */
-function menu_reset_item($item) {
-  $router = menu_router_build();
-  $new_item = _menu_link_build($router[$item['router_path']]);
-  foreach (array('mlid', 'has_children') as $key) {
-    $new_item[$key] = $item[$key];
-  }
-  menu_link_save($new_item);
-  return $new_item;
-}
-
-/**
- * Implementation of hook_block().
- */
-function menu_block($op = 'list', $delta = 0) {
-  $menus = menu_get_menus();
-  // The Navigation menu is handled by the user module.
-  unset($menus['navigation']);
-  if ($op == 'list') {
-    $blocks = array();
-    foreach ($menus as $name => $title) {
-      // Default "Navigation" block is handled by user.module.
-      $blocks[$name]['info'] = check_plain($title);
-      // Menu blocks can't be cached because each menu item can have
-      // a custom access callback. menu.inc manages its own caching.
-      $blocks[$name]['cache'] = BLOCK_NO_CACHE;
-    }
-    return $blocks;
-  }
-  else if ($op == 'view') {
-    $data['subject'] = check_plain($menus[$delta]);
-    $data['content'] = menu_tree($delta);
-    return $data;
-  }
-}
-
-/**
- * Find the depth limit for items in the parent select.
- */
-function _menu_parent_depth_limit($item) {
-  return MENU_MAX_DEPTH - 1 - (($item['mlid'] && $item['has_children']) ? menu_link_children_relative_depth($item) : 0);
-}
-
-/**
- * Decompose the selected menu parent option into the menu_name and plid.
- */
-function menu_node_form_submit($form, &$form_state) {
-  list($form_state['values']['menu']['menu_name'], $form_state['values']['menu']['plid']) = explode(':', $form_state['values']['menu']['parent']);
-}
-
-/**
- * Return an associative array of the custom menus names.
- *
- * @param $all
- *   If FALSE return only user-added menus, or if TRUE also include
- *   the menus defined by the system.
- * @return
- *   An array with the machine-readable names as the keys, and human-readable
- *   titles as the values.
- */
-function menu_get_menus($all = TRUE) {
-  $system_menus = menu_list_system_menus();
-  $sql = 'SELECT * FROM {menu_custom}';
-  if (!$all) {
-    $sql .= ' WHERE menu_name NOT IN ('. implode(',', array_fill(0, count($system_menus), "'%s'")) .')';
-  }
-  $sql .= ' ORDER BY title';
-  $result = db_query($sql, $system_menus);
-  $rows = array();
-  while ($r = db_fetch_array($result)) {
-    $rows[$r['menu_name']] = $r['title'];
-  }
-  return $rows;
-}
-
-/**
- * Function to check the status of the wrapper.
- */
-function menu_wrapper_is_enabled() {
-  return TRUE;
-}
Index: wrappers/taxonomy/taxonomy.js
===================================================================
RCS file: wrappers/taxonomy/taxonomy.js
diff -N wrappers/taxonomy/taxonomy.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wrappers/taxonomy/taxonomy.js	5 Aug 2009 04:44:07 -0000
@@ -0,0 +1 @@
+ 
\ No newline at end of file
Index: wrappers/taxonomy/taxonomy.module.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/category/wrappers/taxonomy/taxonomy.module.php,v
retrieving revision 1.2
diff -u -p -r1.2 taxonomy.module.php
--- wrappers/taxonomy/taxonomy.module.php	31 May 2009 10:12:53 -0000	1.2
+++ wrappers/taxonomy/taxonomy.module.php	5 Aug 2009 04:44:07 -0000
@@ -828,7 +828,6 @@ function taxonomy_nodeapi($node, $op, $a
     case 'update':
       $behavior = variable_get('category_behavior_'. $node->type, 0);
       if (!empty($behavior) && empty($node->category['is_legacy'])) {
-        _category_flatten_values($node);
         if ($behavior == 'category') {
           taxonomy_save_term(_taxonomy_category_into_term($node), TRUE);
         }
@@ -844,7 +843,6 @@ function taxonomy_nodeapi($node, $op, $a
     case 'delete':
       $behavior = variable_get('category_behavior_'. $node->type, 0);
       if (!empty($behavior) && empty($node->category['is_legacy'])) {
-        _category_flatten_values($node);
         if ($behavior == 'category') {
           taxonomy_del_term($node->nid, TRUE);
         }
