Index: nodehierarchy.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/nodehierarchy/nodehierarchy.module,v
retrieving revision 1.12.2.22
diff -u -p -r1.12.2.22 nodehierarchy.module
--- nodehierarchy.module	26 Sep 2010 04:03:16 -0000	1.12.2.22
+++ nodehierarchy.module	20 Nov 2010 05:48:01 -0000
@@ -320,8 +320,7 @@ function nodehierarchy_menu_overview_for
 
   }
 
-  // Set the form handlers so the behaviour is the same as the regular menu form.
-  $form['#submit'][] = 'menu_overview_form_submit';
+  $form['#submit'][] = 'nodehierarchy_menu_form_submit';
   $form['#validate'][] = 'menu_overview_form_validate';
   return $form;
 }
@@ -366,7 +365,7 @@ function nodehierarchy_form_menu_edit_it
     $form['menu']['menu_name']['#weight'] = 20;
     $form['menu']['weight']['#weight'] = 30;
 
-    $form['#submit'] = array_merge(array('nodehierarchy_form_menu_edit_item_submit'), $form['#submit']);
+    $form['#submit'] = array('nodehierarchy_menu_form_submit');
   }
 }
 
@@ -379,6 +378,150 @@ function nodehierarchy_form_menu_edit_it
 }
 
 /**
+ * This is used for the submit of both nodehierarchy_menu_overview_form (the
+ * hierarchical display of the menu) and nodehierarchy_form_menu_edit_item_alter
+ * (editing a single menu item at a time).
+ */
+function nodehierarchy_menu_form_submit($form, &$form_state) {
+  switch ($form['#id']) {
+    case 'nodehierarchy-menu-overview-form':
+      // The following is from the menu_overview_form_submit function with the
+      // actual menu_link_saved stripped out.
+      //
+      // 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'];
+              /**
+               * In menu_overview_form_submit, 'customized' is set in the
+               * foreach loop for saving the menu item. Setting it earlier
+               * avoids having to calculate it later.
+               */
+              $updated_items[$mlid]['customized'] = 1;
+            }
+          }
+          // 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'];
+          }
+        }
+      }
+      $updated_items = array_values($updated_items);
+    break;
+    
+    case 'menu-edit-item':
+      $updated_items = array($form_state['values']['menu']);
+      $form_state['redirect'] = 'admin/build/menu-customize/'. $form_state['values']['menu']['menu_name'];
+    break;
+  }
+
+  $batch = array(
+    'operations' => array(
+      array('_nodehierarchy_menu_form_submit_batch', array($updated_items))
+    ),
+    'finished' => '_nodehierarchy_menu_form_submit_batch_finished',
+    'title' => 'Updating Menu Items'
+  );
+  batch_set($batch);
+}
+
+function _nodehierarchy_menu_form_submit_batch($updated_items, &$context) {
+  if (!isset($context['sandbox']['progress'])) {
+    $context['sandbox']['progress'] = 0;
+    $context['sandbox']['current_node'] = 0;
+    $context['sandbox']['max'] = count($updated_items);
+  }
+
+  $pathauto_exists = module_exists('pathauto');
+  if ($pathauto_exists) {
+    _pathauto_include();
+  }
+
+  $limit = 3;
+  for ($i = 0; $i < $limit; $i++) {
+    if ($context['sandbox']['progress'] == $context['sandbox']['max']) {
+      break;
+    }
+
+    $item = $updated_items[$context['sandbox']['progress']];
+    if ($pathauto_exists) {
+      /**
+       * Using Pathauto, determine if a node's alias is custom or not.
+       */
+      $not_custom_alias = FALSE;
+
+      if ($item['module'] == 'nodehierarchy' && $item['router_path'] == 'node/%') {
+        $nid = substr($item['link_path'], 5);
+        if (is_numeric($nid)) {
+          $node = node_load($nid);
+          $node->old_alias = $node->path;
+
+          $placeholders = pathauto_get_placeholders('node', $node);
+          $pathauto_alias = pathauto_create_alias('node', 'return', $placeholders, "node/{$node->nid}", $node->nid, $node->type, $node->language);
+          $not_custom_alias = ($node->path == $pathauto_alias);
+        }
+      }
+    }
+
+    /**
+     * Save the menu item. If the item's alias has changed, this will overwrite it.
+     */
+    if (!menu_link_save($item)) {
+      drupal_set_message(t('There was an error saving the menu link "%title".'), array('%title' => $item['link_title']), 'error');
+    } else {
+      $context['results'][] = $item['link_title'];
+    }
+
+    if ($pathauto_exists) {
+      /**
+       * If the alias is not custom, invoked pathauto. If the menu item has been
+       * moved, this will update the alias. If the menu item has a custom alias,
+       * assume the custom alias is still desired and don't do anything.
+       */
+      if ($not_custom_alias) {
+        // Run directly so we can pass $node as a reference
+        pathauto_nodeapi($node, 'update');
+      }
+      nodehierarchy_recursive_pathauto_update($node);
+    }
+    
+    $context['sandbox']['progress']++;
+  }
+
+  $context['message'] = t('Now processing %title', array('%title' => $item['link_title']));
+
+  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+  }
+}
+
+function _nodehierarchy_menu_form_submit_batch_finished($success, $results, $operations) {
+  if ($success) {
+    $message = count($results) .' menu items updated.';
+  } else {
+    $error_operation = reset($operations);
+    $message = t('An error occurred while processing %error_operation with arguments: @arguments', array('%error_operation' => $error_operation[0], '@arguments' => print_r($error_operation[1], TRUE)));
+  }
+  drupal_set_message($message);
+}
+
+/**
  * Theme the menu overview form into a table respecting the node hierarchy rules.
  *
  * @ingroup themeable
@@ -630,6 +773,15 @@ function nodehierarchy_update_node(&$nod
   if (user_access('edit all node parents') || ($node->uid == $user->uid && user_access('edit own node parents'))) {
     _nodehierarchy_save_node($node);
   }
+  
+  if (module_exists('pathauto')) {
+    /**
+     * Manually run pathauto_nodeapi first to make sure the recursive updates
+     * have the right alias.
+     */
+    pathauto_nodeapi($node, 'update');
+    nodehierarchy_recursive_pathauto_update($node);
+  }
 }
 
 /**
@@ -701,6 +853,114 @@ function _nodehierarchy_save_node(&$node
 }
 
 /**
+ * If necessary, update the children nodes with the pathauto-generated alias.
+ * Called from both nodeapi (update only) and through the menu system.
+ */
+function nodehierarchy_recursive_pathauto_update($node) {
+  /**
+   * If $node's old_alias or path are not set, or if $node's old_alias and
+   * path are the same, stop.
+   */
+  if (!isset($node->path) || !isset($node->old_alias)) { return; }
+  if ($node->old_alias == $node->path) { return; }
+
+  /**
+   * Nodehierarchy currently will return invalid data if the node does not have 
+   * any descendants. Check to make sure that the node's mlid is not zero.
+   */
+  $mlid = _nodehierarchy_get_node_mlid($node->nid);
+  if ($mlid === FALSE) { return; }
+
+  // Get all descendants of the node
+  $node_descendants = _nodehierarchy_get_children_menu_links($node->nid);
+  if (empty($node_descendants)) { return; }
+
+  // Convert the $node_descendants array into one that the batch API can understand
+  $all_children = array();
+  _nodehierarchy_get_all_children($all_children, $node_descendants);
+
+  /*
+   * Pass the current's node aliases in order to calculate if the pass is
+   * automatically aliased or not.
+   */
+  $path_info = array(
+    'old_alias' => $node->old_alias,
+    'path'      => $node->path
+  );
+
+  // Update the children aliases using the Batch API.
+  $batch = array(
+    'operations' => array(
+      array('_nodehierarchy_pathauto_update_nodes', array($all_children, $path_info))
+    ),
+    'finished' => '_nodehierarchy_pathauto_update_nodes_finished',
+    'title' => 'Updating Paths of Children Nodes'
+  );
+  batch_set($batch);
+}
+
+function _nodehierarchy_pathauto_update_nodes($nodes, $path_info, &$context) {
+  if (!isset($context['sandbox']['progress'])) {
+    $context['sandbox']['progress'] = 0;
+    $context['sandbox']['current_node'] = 0;
+    $context['sandbox']['max'] = count($nodes);
+  }
+
+  _pathauto_include();
+
+  $limit = 3;
+  for ($i = 0; $i < $limit; $i++) {
+    if ($context['sandbox']['progress'] == $context['sandbox']['max']) {
+      break;
+    }
+
+    $node = node_load($nodes[$context['sandbox']['progress']]['nid']);
+    $match_old_alias = strpos($node->path, $path_info['old_alias']);
+
+    // If the root parent's old alias (passed via $path_info) matches the beginning
+    // of the current node's alias, consider the alias automatically set and update.
+    if (is_numeric($match_old_alias) && $match_old_alias == '0') {
+      $placeholders = pathauto_get_placeholders('node', $node);
+      $pathauto_alias = pathauto_create_alias('node', 'return', $placeholders, "node/{$node->nid}", $node->nid, $node->type, $node->language);
+      pathauto_nodeapi($node, 'update');
+      $context['results'][] = check_plain($node->title);
+    }
+
+    $context['sandbox']['progress']++;
+    $context['sandbox']['current_node'] = $node->nid;
+    $context['message'] = t('Now processing %node', array('%node' => $node->title));
+  }
+
+  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+  }
+}
+
+function _nodehierarchy_pathauto_update_nodes_finished($success, $results, $operations) {
+  if ($success) {
+    $message = count($results) .' child node paths updated.';
+  } else {
+    $error_operation = reset($operations);
+    $message = t('An error occurred while processing %error_operation with arguments: @arguments', array('%error_operation' => $error_operation[0], '@arguments' => print_r($error_operation[1], TRUE)));
+  }
+  drupal_set_message($message);
+}
+
+/**
+ * Get all children of a node as an array of nodes
+ */
+function _nodehierarchy_get_all_children(&$all_children, $nodes) {
+  foreach($nodes as $node) {
+    $all_children[] = $node;
+    $node_descendants = _nodehierarchy_get_children_menu_links($node['nid']);
+
+    if (!empty($node_descendants)) {
+      _nodehierarchy_get_all_children($all_children, $node_descendants);
+    }
+  }
+}
+
+/**
  * Load a node's menu links when the node is loaded.
  */
 function nodehierarchy_load_node($node) {
