Index: modules/taxonomy/taxonomy.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v
retrieving revision 1.605
diff -u -p -r1.605 taxonomy.module
--- modules/taxonomy/taxonomy.module	11 Sep 2010 06:03:12 -0000	1.605
+++ modules/taxonomy/taxonomy.module	18 Sep 2010 18:27:06 -0000
@@ -682,6 +682,7 @@ function taxonomy_terms_static_reset() {
   drupal_static_reset('taxonomy_get_tree');
   drupal_static_reset('taxonomy_get_tree:parents');
   drupal_static_reset('taxonomy_get_tree:terms');
+  drupal_static_reset('taxonomy_get_tree:term_entities');
   drupal_static_reset('taxonomy_get_parents');
   drupal_static_reset('taxonomy_get_parents_all');
   drupal_static_reset('taxonomy_get_children');
@@ -803,20 +804,21 @@ function taxonomy_get_children($tid, $vi
  *   for the entire vocabulary.
  * @param $max_depth
  *   The number of levels of the tree to return. Leave NULL to return all levels.
- * @param $depth
- *   Internal use only.
+ * @param $list_only
+ *   If TRUE, terms only contain data from the taxonomy_term_data and
+ *   taxonomy_term_hierarchy table. Otherwise full term entites are returned,
+ *   but this requires more execution time and memory consumption.
  *
  * @return
  *   An array of all term objects in the tree. Each term object is extended
  *   to have "depth" and "parents" attributes in addition to its normal ones.
  *   Results are statically cached.
  */
-function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $depth = -1) {
+function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $list_only = TRUE) {
   $children = &drupal_static(__FUNCTION__, array());
   $parents = &drupal_static(__FUNCTION__ . ':parents', array());
   $terms = &drupal_static(__FUNCTION__ . ':terms', array());
-
-  $depth++;
+  $term_entities = &drupal_static(__FUNCTION__ . ':term_entities', array());
 
   // We cache trees, so it's not CPU-intensive to call get_tree() on a term
   // and its children, too.
@@ -827,31 +829,72 @@ function taxonomy_get_tree($vid, $parent
 
     $query = db_select('taxonomy_term_data', 't');
     $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
-    $query->addField('t', 'tid');
-    $query->addField('h', 'parent');
-    $query->condition('t.vid', $vid);
-    $query->addTag('term_access');
-    $query->orderBy('t.weight');
-    $query->orderBy('t.name');
-    $tid_parents = $query->execute()->fetchAllKeyed();
-    $terms[$vid] = taxonomy_term_load_multiple(array_keys($tid_parents));
-
-    foreach ($tid_parents as $tid => $parent_tid) {
-      $children[$vid][$parent_tid][] = $tid;
-      $parents[$vid][$tid][] = $parent_tid;
+    $result = $query
+      ->addTag('translatable')
+      ->addTag('term_access')
+      ->fields('t')
+      ->fields('h', array('parent'))
+      ->condition('t.vid', $vid)
+      ->orderBy('weight')
+      ->orderBy('name')
+      ->execute();
+
+    foreach ($result as $term) {
+      $children[$vid][$term->parent][] = $term->tid;
+      $parents[$vid][$term->tid][] = $term->parent;
+      $terms[$vid][$term->tid] = $term;
     }
   }
 
+  // Load full entities, if necessary.
+  if (!$list_only && !isset($term_entities[$vid])) {
+    $term_entities[$vid] = taxonomy_term_load_multiple(array_keys($terms[$vid]));
+  }
+
   $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
   $tree = array();
-  if ($max_depth > $depth && !empty($children[$vid][$parent])) {
-    foreach ($children[$vid][$parent] as $child) {
-      $term = clone $terms[$vid][$child];
-      $term->depth = $depth;
-      $term->parents = $parents[$vid][$child];
-      $tree[] = $term;
-      if (!empty($children[$vid][$child])) {
-        $tree = array_merge($tree, taxonomy_get_tree($vid, $child, $max_depth, $depth));
+  $process_parents = array();
+  $process_parents[] = $parent;
+  while (count($process_parents)) {
+    $parent = array_pop($process_parents);
+    $depth = count($process_parents);
+    if ($max_depth > $depth && !empty($children[$vid][$parent])) {
+      $has_children = FALSE;
+      $child = current($children[$vid][$parent]);
+      do {
+        if (empty($child)) {
+          break;
+        }
+        $term = $list_only ? $terms[$vid][$child] : $term_entities[$vid][$child];
+        if (count($parents[$vid][$term->tid]) > 1) {
+          // We have a term with multi parents here,
+          // clone the term, so that the depth attribute remains correct.
+          $term = clone $term;
+        }
+        $term->depth = $depth;
+        unset($term->parent);
+        $term->parents = $parents[$vid][$term->tid];
+        $tree[] = $term;
+        if (!empty($children[$vid][$term->tid])) {
+          $has_children = TRUE;
+
+          // We have to continue with the current parent later
+          // and use $term for the next iteration.
+          $process_parents[] = $parent;
+          $process_parents[] = $term->tid;
+
+          // Reset pointers for child lists because we step in there more often with multi parents.
+          reset($children[$vid][$term->tid]);
+          // Move pointer so that we get the correct term the next time.
+          next($children[$vid][$parent]);
+          break;
+        }
+      } while ($child = next($children[$vid][$parent]));
+
+      if (!$has_children) {
+        // We processed all terms in this hierarchy-level
+        // reset pointer so that this function works the next time it gets called.
+        reset($children[$vid][$parent]);
       }
     }
   }
