So, I thought I would share with the Drupal community some code I wrote to create a browsable tree view of vocabularies in a taxonomy that are all single level. After days of searching (without any luck) for a module that would create a tree structure of my single level vocabularies, I wrote some code that dynamically creates the tree structure.

So why not just create the vocabularies in a tree structure to begin with? I think this is the norm for many sites, but the nodes in my site do not have a clear tree hierarchy that is agreed upon by all of the people that use my site, as well as if you want to change the tree structure (with many nodes already added) it's not that easy. Plus, I wanted the tree structure to be dynamic. In fact, I want to be able to offer different trees to accommodate different users.

The method to creating the tree structure is to define the vocabulary hierarchy using the vocabulary ids (4,8,6,2: where these are the vid's of the vocabularies I want to create a tree for). Then, traverse the vocabularies, in the above order, using the terms as folders, and the nodes as the tree leaves. The nodes in the sub terms/folders must be present in the parent folder. Thus, creating a tree view of the nodes across single level vocabularies.

Here is the code in action:

http://www.videobjj.com/?q=tree

I know how to program in PHP but not a Drupal guru, so the code is written using a view, then using template.php to generate the node listing for the view page. I am sure the code can be easily modified into a module. The beauty of the code, is at any point I can change the vocabulary order, and create a different tree structure without ever having to modify the taxonomy vocabulary hierarchies. The down side, is that the tree is programmaticly driven, meaning the more vocabularies (terms,nodes), the more resources are required to generate the tree. However, the code is recursive and as fast as I can make it.

If you are interested in the code, let me know and I will send it to you. Send me an email via the contact button from the site above. If enough people drive interest, I will try to make a module out of it.

I am thinking that others have had to be as frustrated as me trying to figure this out... Maybe it will help someone...

John

Comments

xmacinfo’s picture

I think you did a very good job the way you made your tree using Views and template.php code. So you might not need to create a module for this. Why not explain here how you did manage to create the tree with single level vocabularies. Other users might help you on enhancing the code.

You might also consider publishing a step by step documentation in the Drupal.org How To section: http://drupal.org/handbook/customization/howto

If your code in template is too large, you might consider creating a module. But if you do so, make sure it will still use Views.

g051798’s picture

I've had a few requests already for the code, and based on xmacinfo's suggestion, here's the code and how to use it. I was thinking that maybe someone else could have a look at the code and make any updates to refine or improve it. One suggestion to make it better is:

* Currently, it builds the tree on all the nodes that are in the vocabulary definition. However, it could be updated to only create the tree based on the nodes filtered from the view in the selected vocabularies.

The code is based on the javascript "Simple Tree Menu" at http://www.dynamicdrive.com/dynamicindex1/navigate1.htm

You will need to download the above JS code and place in your theme directory (or in any location the js files can be read). In my case, simply created a "simpletree" directory in my theme directory:

# mkdir sites/all/themes/barlow/simpletree

Here's a directory listing of the files you will need in simpletree folder:

# ls
closed.gif list.gif open.gif simpletree.css simpletreemenu.js

You will also need to update your "page,tpl.php" file in your theme directory to add the necessary java script code to initialize the js files:

# vi page.tpl.php
Add the following between the head tags (be sure to change it where simpletree lives in your path) :

<script type="text/javascript" src="/sites/all/themes/barlow/simpletree/simpletreemenu.js">

/***********************************************
* Simple Tree Menu- Dynamic Drive DHTML code library (www.dynamicdrive.com)
* This notice MUST stay intact for legal use
* Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
***********************************************/

</script>
<link rel="stylesheet" type="text/css" href="/sites/all/themes/barlow/simpletree/simpletree.css" />

Now, create a view called "tree". This will simple provide a page to create the tree structure in. It doesn't actually use (yet) any nodes filtered.

* Click Admin/Views/Add
* Name: Tree
* Click Page
* Click Check box for "Provide Page View"
* URL: tree
* Uncheck "Use Pager"
* Click "Save"

Now, you simply theme your view by add add the following code to your template.php file in your theme folder:

function phptemplate_views_view_nodes_tree($view, $nodes, $type) {

  # Define vocabulary tree
  $vocabTree=array(11,9,2,3);

  # Output (expand/contract) controls 
  $output="<a href=\"javascript:ddtreemenu.flatten('treemenu', 'expand')\"> Expand All</a> | <a href=\"javascript:ddtreemenu.flatten('treemenu', 'contact')\"> Contract All</a> <ul id=\"treemenu\" class=\"treeview\">";

  # Call recursive function to build tree
  $output.=bldTree($vocabTree,7,FALSE,NULL,NULL); 

  # Init java script
  $output.="</ul><script type=\"text/javascript\"> ddtreemenu.createTree(\"treemenu\", true, 10) </script>";

  return($output);
}



function bldTree($vcbs,$leaf,$loop,$nds=array(),$trms=array()) {
  $flag=TRUE;
  foreach ($vcbs as $vid) {

  # Only iterate vocabulary list once (1 loop)
  if ($loop) {break;}

  # Get vocabulary ID
  $vObj=taxonomy_get_vocabulary($vid);
  
    # Get terms associated with vocabulary
    $terms = taxonomy_get_tree($vid);

    # Loop through each vocabulary term to create folders
    foreach ($terms as $term) {
    
      $tid=$term->tid;
      $tnds=array();
      $tname=$term->name;

      # Get nodes associated with term
      $result=taxonomy_select_nodes(array($term->tid),'or','all',FALSE);
      $numRows=mysql_num_rows($result);

      if (!$numRows) { continue; }

      # Build array of all nodes associated with term
      while ($row=mysql_fetch_array($result)) {
        $nid=$row['nid'];
        $tnds[]=$nid;
      }

      # If term(folder) has nodes, only list nodes if contained in parent term(folder)
      if (count($nds)) {
        $tnds=array_intersect($tnds,$nds);  
      }

      # If no nodes from term are contained in parent term(s), then skip
      if (!count($tnds)) { continue; }

      # Build list of terms traversed so far
      if (!$loop) { $trms[]=$tname; }

      # Generate unordered list
      $output.="<li>$tname<ul>\n";

      # Recursively traverse through vocabulary list to generate tree structure
      $vocab=$vcbs;
      array_shift($vocab);
      $cnt=count($vocab);
      if ($cnt) { 
        $output.=bldTree($vocab,$leaf,0,$tnds,$trms); 
      }
  
      # Create folder for all sub-folder nodes
      $tmp="";
      if ($flag && $cnt) {
        foreach($trms as $tm) {
          $tmp.="$tm, ";
        }
        $str=rtrim($tmp,", ");
        $output.="<li>All $str Videos<ul>\n";
        array_pop($trms);
      }

      # Create node list for folder
      foreach ($tnds as $tnd) {
        # Get term that describes leaf node from vocabulary id (appended to title of node in tree)
        if($leaf) {
          $tecs=taxonomy_node_get_terms_by_vocabulary($tnd,$leaf);
          foreach($tecs as $tec) {
            break;
          }
        }
        $nd=node_load($tnd);
        $output.="<li>\n";
        if ($leaf) {
          $output.=l($nd->title . " (" . $tec->name . ")","node/" . $nd->nid);
        } else {
          $output.=l($nd->title,"node/" . $nd->nid);
        }
        $output.="</li>\n";
      }

      # Close html list
      $output.="</ul></li>\n";
      if ($flag && $cnt) {
        $output.="</ul></li>\n";
      }
    }
    $flag=FALSE;
    $loop=TRUE;
  }
  return($output);
}

Make changes to the code above to work for your environment:

In the "function phptemplate_views_view_nodes_tree($view, $nodes, $type)" function, you will want to define the tree using the vocabulary id's of your taxonomy. In this case, vocabulary "11" is the top folder in the tree, followed by "9", etc...

# Define vocabulary tree
$vocabTree=array(11,9,2,3);

Also, in my case, I wanted to have an additional description (vocabulary term) when displaying the titile of the node. The second argument of the bldTree function is the vocabulary ID of the vocabulary for the nodes that contains a description. In other words, for my site, every node is assigned a term in vocabulary 7 that helps to describe the node. This term is then appended to the node title in the tree. To not use this feature, change the 7 below to 0:

# Call recursive function to build tree
$output.=bldTree($vocabTree,7,FALSE,NULL,NULL);

That's it! Hopefully this make sense...

If you do get it working, please post here that you got it working. If people start to use it, i will try to create a how to page once the code is more mature.

John

g051798’s picture

It turns out that it was easy to integrate with views, and use the nodes filtered from views by just sending the array of nodes to the recursive function. I have also made a few minor changes to the code to create a category heading per vocabulary folder, I also included a parameter that allows the node to display the vote count (in my case jrating).

I have also updated my page (http://www.videobjj.com/?q=tree) to show 2 separate trees as well as a filter that only shows nodes that have 3 stars or higher for that tree. To get this working you have to setup the views to work with tabs(each tab is a view). Each view must have a corresponding function in template.php to theme it. In my case, I have four views with four functions in template.php:

function phptemplate_views_view_nodes_tree($view, $nodes, $type) {

  # Create array of nodes from view
  foreach ($nodes as $obj) { $nds[]=$obj->nid; }

  # Define vocabulary tree
  $vocabTree=array(11,9,2,3);

  # Output (expand/contract) controls 
  $output="<a href=\"javascript:ddtreemenu.flatten('treemenu', 'expand')\"> Expand All</a> | <a href=\"javascript:ddtreemenu.flatten('treemenu', 'contact')\"> Contract All</a> <ul id=\"treemenu\" class=\"treeview\">";

  # Call recursive function to build tree
  $output.=bldTree($vocabTree,7,FALSE,$nds,NULL,TRUE); 

  # Init java script
  $output.="</ul><script type=\"text/javascript\"> ddtreemenu.createTree(\"treemenu\", true, 10) </script>";

  return($output);
}

function phptemplate_views_view_nodes_treeRating($view, $nodes, $type) {

  # Create array of nodes from view
  foreach ($nodes as $obj) { $nds[]=$obj->nid; }

  # Define vocabulary tree
  $vocabTree=array(11,9,2,3);

  # Output (expand/contract) controls
  $output="<a href=\"javascript:ddtreemenu.flatten('treemenu', 'expand')\"> Expand All</a> | <a href=\"javascript:ddtreemenu.flatten('treemenu', 'contact')\"> Contract All</a> <ul id=\"treemenu\" class=\"treeview\">";

  # Call recursive function to build tree
  $output.=bldTree($vocabTree,7,FALSE,$nds,NULL,TRUE);

  # Init java script
  $output.="</ul><script type=\"text/javascript\"> ddtreemenu.createTree(\"treemenu\", true, 10) </script>";

  return($output);
}


function phptemplate_views_view_nodes_tree2($view, $nodes, $type) {
  # Create array of nodes from view
  foreach ($nodes as $obj) { $nds[]=$obj->nid; }

  # Define vocabulary tree
  $vocabTree=array(3,2,7);

  # Output (expand/contract) controls
  $output="<a href=\"javascript:ddtreemenu.flatten('treemenu2', 'expand')\"> Expand All</a> | <a href=\"javascript:ddtreemenu.flatten('treemenu2', 'contact')\"> Contract All</a> <ul id=\"treemenu2\" class=\"treeview\">";

  # Call recursive function to build tree
  $output.=bldTree($vocabTree,7,FALSE,$nds,NULL,TRUE);

  # Init java script
  $output.="</ul><script type=\"text/javascript\"> ddtreemenu.createTree(\"treemenu2\", true, 10) </script>";

  return($output);
}


function phptemplate_views_view_nodes_tree2Rating($view, $nodes, $type) {
  # Create array of nodes from view
  foreach ($nodes as $obj) { $nds[]=$obj->nid; }

  # Define vocabulary tree
  $vocabTree=array(3,2,7);

  # Output (expand/contract) controls
  $output="<a href=\"javascript:ddtreemenu.flatten('treemenu2', 'expand')\"> Expand All</a> | <a href=\"javascript:ddtreemenu.flatten('treemenu2', 'contact')\"> Contract All</a> <ul id=\"treemenu2\" class=\"treeview\">";

  # Call recursive function to build tree
  $output.=bldTree($vocabTree,7,FALSE,$nds,NULL,TRUE);

  # Init java script
  $output.="</ul><script type=\"text/javascript\"> ddtreemenu.createTree(\"treemenu2\", true, 10) </script>";

  return($output);
}

Alot of the code in each function above is just repeated for each view. Thus, it would be better to have the repeatable code in one single function then called in each view theme function. But, it works for now...

Also, note, I added the last argument (TRUE) to the bldTree function that means append the vote for the node as part of the description of the leaf nodes in the tree.

Also, note, the second tree is identified as "treemenu2".

Finally, Here's the updated code for the main bldTree function in template.php (append this code just below the above code in template.php):


function bldTree($vcbs,$leaf,$loop,$nds=array(),$trms=array(),$vote) {
  $flag=TRUE;
  $flag2=TRUE;
  foreach ($vcbs as $vid) {

  # Only iterate vocabulary list once (1 loop)
  if ($loop) {break;}

  # Get vocabulary ID
  $vObj=taxonomy_get_vocabulary($vid);
  $vname=$vObj->name;
  
    # Get terms associated with vocabulary
    $terms = taxonomy_get_tree($vid);

    # Loop through each vocabulary term to create folders
    foreach ($terms as $term) {
    
      $tid=$term->tid;
      $tnds=array();
      $tname=$term->name;

      # Get nodes associated with term
      $result=taxonomy_select_nodes(array($term->tid),'or','all',FALSE);
      $numRows=mysql_num_rows($result);

      if (!$numRows) { continue; }

      # Build array of all nodes associated with term
      while ($row=mysql_fetch_array($result)) {
        $nid=$row['nid'];
        $tnds[]=$nid;
      }

      # If term(folder) has nodes, only list nodes if contained in parent term(folder)
      if (count($nds)) {
        $tnds=array_intersect($tnds,$nds);  
      }

      # If no nodes from term are contained in parent term(s), then skip
      if (!count($tnds)) { continue; }

      # Build list of terms traversed so far
      if (!$loop) { $trms[]=$tname; }

      if ($flag2) {
        $output.="<strong>$vname</strong><br>";
      }
      # Generate unordered list
      $output.="<li>$tname<ul>\n";

      # Recursively traverse through vocabulary list to generate tree structure
      $vocab=$vcbs;
      array_shift($vocab);
      $cnt=count($vocab);
      if ($cnt) { 
        $output.=bldTree($vocab,$leaf,0,$tnds,$trms,$vote); 
      }
  
      # Create folder for all sub-folder nodes
      $tmp="";
      if ($flag && $cnt) {
        foreach($trms as $tm) {
          $tmp.="$tm, ";
        }
        $str=rtrim($tmp,", ");
        $output.="<li>All $str Videos<ul>\n";
        array_pop($trms);
      }

      # Create node list for folder
      foreach ($tnds as $tnd) {
        # Get term that describes leaf node from vocabulary id (appended to title of node in tree)
        if($leaf) {
          $tecs=taxonomy_node_get_terms_by_vocabulary($tnd,$leaf);
          foreach($tecs as $tec) {
            break;
          }
        }
        $nd=node_load($tnd);
        $output.="<li>\n";
        if ($leaf) {
          $output.=l($nd->title . " (" . $tec->name . ")","node/" . $nd->nid);
        } else {
          $output.=l($nd->title,"node/" . $nd->nid);
        }
        if ($vote) {
          $output.=" - " . round($nd->jrating_mean_rating[rating]) . " stars";
        }
        $output.="</li>\n";
      }

      # Close html list
      $output.="</ul></li>\n";
      if ($flag && $cnt) {
        $output.="</ul></li>\n";
      }
      $flag2=FALSE;
    }
    $flag=FALSE;
    $loop=TRUE;
  }
  return($output);
}

John

g051798’s picture

I have it working with cache for much faster performance. I also added subroutine for all views instead of copy/paste. Let me know if you would like the update of code. Don't want to repost unless someone wants it...

xmacinfo’s picture

Please do.

I'd be interested to know how you do this.

g051798’s picture

So, building the tree on every visit was time consuming especially on my very old server. Decided to use a file based cache that gets built on a daily basis. Basically, if user browses the tree and its more than 24 hours old, it will rebuild the tree and cache it. You will need to update the putCache and getCache functions to write to a writable directory on your server. Also, i minimized the views functions to call a function which builds the tree. This avoided duplicating the same code over and over for each view tab. I haven't spent a lot of time on the updated code, so I am sure there is some error checking that could be added but it is working fine in my environment for the past couple of days.

Here's the update of the code:


function phptemplate_views_view_nodes_tree($view, $nodes, $type) {
  $vocabTree=array(11,2,3);
  return processTree($nodes,"tree",$vocabTree);
}

function phptemplate_views_view_nodes_treeRating($view, $nodes, $type) {
  $vocabTree=array(11,2,3);
  return processTree($nodes,"treeRating",$vocabTree);
}

function phptemplate_views_view_nodes_treeFavs($view, $nodes, $type) {
  $vocabTree=array(11,2,3);
  return processTree($nodes,"treeFavs",$vocabTree);
}


function phptemplate_views_view_nodes_tree2($view, $nodes, $type) {
  $vocabTree=array(3,2,7);
  return processTree($nodes,"tree2",$vocabTree);
}


function phptemplate_views_view_nodes_tree2Rating($view, $nodes, $type) {
  $vocabTree=array(3,2,7);
  return processTree($nodes,"tree2Rating",$vocabTree);
}

function phptemplate_views_view_nodes_tree2Favs($view, $nodes, $type) {
  $vocabTree=array(3,2,7);
  return processTree($nodes,"tree2Favs",$vocabTree);
}


function processTree($nodes,$name,$vocabTree) {
   # Get cache if exists
  if($cache=getCache($name)) {
    $cache.="<br>Note:  Using cached tree for better performance.  Tree built daily.";
    return $cache;
  }

  # Create array of nodes from view
  foreach ($nodes as $obj) { $nds[]=$obj->nid; }

  $count=count($nds);
  $output.="

<br>Use the tabs above to choose which directory tree is easier to browse.  The \"Stars\" and \"Favorites\" tabs filter the videos in the tree by number of Stars and Favorites respectively. <br><br>Number of videos in this tree: <strong>$count</strong><br><br>";

  # Output (expand/contract) controls
  $output.="<a href=\"javascript:ddtreemenu.flatten('treemenu', 'expand')\"> Expand All</a> | <a href=\"javascript:ddtreemenu.flatten('treemenu', 'contact')\"> Contract All</a> <ul id=\"treemenu\" class=\"treeview\">";

  # Call recursive function to build tree
  $output.=bldTree($vocabTree,7,FALSE,$nds,NULL,TRUE);

  # Init java script
  $output.="</ul><script type=\"text/javascript\"> ddtreemenu.createTree(\"treemenu\", true, 10) </script>";

  # Write output to cache
  putCache("$name",$output);

  return($output); 
}


function getCache($name) {
  $file="/tmp/$name.cache";
  if (!file_exists($file)) {
    return FALSE;
  }
  $size=filesize($file);
  if ($size==0) {
    return FALSE;
  }
  if (!is_readable($file)) {
    return FALSE;
  }
  $handle = fopen($file, 'r');
  $ftime=filemtime($file);
  $stime=strtotime("-1 day");
  if ($ftime > $stime) {
    $cache = fread($handle, $size);
    fclose($handle);
    return $cache;
  } else {
    return FALSE;
  }
}

function putCache($name,$output) {
  $file="/tmp/$name.cache";
  if (file_exists($file)) {
    if (!is_writeable($file)) {
      return FALSE;
    }
  }
  $handle = fopen($file, 'w');
  fwrite($handle, $output);
  fclose($handle);
}



function bldTree($vcbs,$leaf,$loop,$nds=array(),$trms=array(),$vote) {

  $flag=TRUE;
  $flag2=TRUE;
  foreach ($vcbs as $vid) {

  # Only iterate vocabulary list once (1 loop)
  if ($loop) {break;}

  # Get vocabulary ID
  $vObj=taxonomy_get_vocabulary($vid);
  $vname=$vObj->name;
  
    # Get terms associated with vocabulary
    $terms = taxonomy_get_tree($vid);

    # Loop through each vocabulary term to create folders
    foreach ($terms as $term) {
    
      $tid=$term->tid;
      $tnds=array();
      $tname=$term->name;

      # Get nodes associated with term
      $result=taxonomy_select_nodes(array($term->tid),'or','all',FALSE);
      $numRows=mysql_num_rows($result);

      if (!$numRows) { continue; }

      # Build array of all nodes associated with term
      while ($row=mysql_fetch_array($result)) {
        $nid=$row['nid'];
        $tnds[]=$nid;
      }

      # If term(folder) has nodes, only list nodes if contained in parent term(folder)
      if (count($nds)) {
        $tnds=array_intersect($tnds,$nds);  
      }

      # If no nodes from term are contained in parent term(s), then skip
      if (!count($tnds)) { continue; }

      # Build list of terms traversed so far
      if (!$loop) { $trms[]=$tname; }

      if ($flag2) {
        $output.="<strong>$vname</strong><br>";
      }
      # Generate unordered list
      $output.="<li>$tname<ul>\n";

      # Recursively traverse through vocabulary list to generate tree structure
      $vocab=$vcbs;
      array_shift($vocab);
      $cnt=count($vocab);
      if ($cnt) { 
        $output.=bldTree($vocab,$leaf,0,$tnds,$trms,$vote); 
      }
  
      # Create folder for all sub-folder nodes
      $tmp="";
      if ($flag && $cnt) {
        foreach($trms as $tm) {
          $tmp.="$tm, ";
        }
        $str=rtrim($tmp,", ");
        $output.="<li>All $str Videos<ul>\n";
        array_pop($trms);
      }

      # Create node list for folder
      foreach ($tnds as $tnd) {
        # Get term that describes leaf node from vocabulary id (appended to title of node in tree)
        if($leaf) {
          $tecs=taxonomy_node_get_terms_by_vocabulary($tnd,$leaf);
          foreach($tecs as $tec) {
            break;
          }
        }
        $nd=node_load($tnd);
        $output.="<li>\n";
        if ($leaf) {
          $output.=l($nd->title . " (" . $tec->name . ")","node/" . $nd->nid);
          $str=var_export($nd,TRUE);
          if (preg_match("/No Gi/",$str)) {
            $output.=" - No Gi";
          } else {
            $output.=" - Gi";
          }
        } else {
          $output.=l($nd->title,"node/" . $nd->nid);
        }
        if ($vote) {
          $output.=" - " . round($nd->jrating_mean_rating[rating]) . " stars";
        }
        $output.="</li>\n";
      }

      # Close html list
      $output.="</ul></li>\n";
      if ($flag && $cnt) {
        $output.="</ul></li>\n";
      }
      $flag2=FALSE;
    }
    $flag=FALSE;
    $loop=TRUE;
  }
  return($output);
}

wchen’s picture

The taxonomy tree is pretty cool. However it's not working with Drupal 6.x. (I guess g051798 are using 5.x)
The main difference:
1. Drupal 6.x has View2.
2. the taxonomy module is modified.
3. I am using Multiple vocabulary.

The tweak I made:
1. create a file views-view-unformatted--taxonomy-tree.tpl.php and put it under the default theme folder.
2. put the following code in the file:

# Define vocabulary tree
  $vocabTree=array(2, 2);

# Output (expand/contract) controls
  $output="<a href=\"javascript:ddtreemenu.flatten('treemenu', 'expand')\"> Expand All</a> | <a href=\"javascript:ddtreemenu.flatten('treemenu', 'contact')\"> Contract All</a> <ul id=\"treemenu\" class=\"treeview\">";

# Call recursive function to build tree
  $output.=batchbldTree($vocabTree,0);

# Init java script
  $output.="</ul><script type=\"text/javascript\"> ddtreemenu.createTree(\"treemenu\", true, 10) </script>";

  print $output;

function batchbldTree($vcbs,$leaf) {
  $output='';
  foreach ($vcbs as $vid) {
    $output.=bldTree($vid,0);
  }
  return($output);
}

function bldTree($vid,$leaf) {
# Get vocabulary ID
    $vObj=taxonomy_vocabulary_load($vid);

# Get terms associated with vocabulary
    $terms = taxonomy_get_tree($vid);
    $curr_depth=-1;

# Loop through each vocabulary term to create folders
    foreach ($terms as $term) {
      $tid=$term->tid;
      $tnds=array();
      $tname=$term->name;
# Get nodes associated with term
      $result=taxonomy_select_nodes(array($term->tid),'or','all',FALSE);

# Build array of all nodes associated with term
      $has_rows = FALSE;
      while ($node = db_fetch_object($result)) {
        $tnds[]=$node->nid;
        $has_rows = TRUE;
      }

      if(!$has_rows){continue;}

# Close previous html list
      for($i=0; $i<$curr_depth-$term->depth+1; $i++){
        $output.="</ul></li>\n";
      }
      $curr_depth = $term->depth;

# Generate unordered list
      $output.="<li>$tname(".count($tnds).")<ul>\n";

# Create folder for all sub-folder nodes
      $show_all_nodes = FALSE;
      if($nextterm=next($terms)){
        prev($terms);
        if($nexterm->depth >= $term->depth){
          $show_all_nodes = TRUE;
        }
      }
      if ($show_all_nodes) {
        $output.="<li>All $tname Nodes<ul>\n";
      }

# Create node list for folder
      foreach ($tnds as $tnd) {
# Get term that describes leaf node from vocabulary id (appended to title of node in tree)
        if($leaf) {
          $tecs=taxonomy_node_get_terms_by_vocabulary($tnd,$leaf);
          foreach($tecs as $tec) {
            break;
          }
        }
        $nd=node_load($tnd);
        $output.="<li>\n";
        if ($leaf) {
          $output.=l($nd->title . " (" . $tec->name . ")","node/" . $nd->nid);
        } else {
          $output.=l($nd->title,"node/" . $nd->nid);
        }
        $output.="</li>\n";
      }

# Close html list
      if ($show_all_nodes) {
        $output.="</ul></li>\n";
      }
    }

# Close previous html list
    for($i=0; $i<$curr_depth+1; $i++){
      $output.="</ul></li>\n";
    }

  return($output);
}

Note:
1. I changed bldTree function so it's not recursive any more.
2. BatchbldTree will loop through all the vocabularies.
3. taxonomy_get_vocabulary() is replaced by taxonomy_vocabulary_load().
4. taxonomy_select_nodes() results is objects instead of arrary. so mysql_num_rows(), mysql_fetch_array() will give an error message like "warning: mysql_num_rows(): supplied argument is not a valid MySQL result resource".
5. I haven't tried the cache feature.
6. I am still working on this and I will report any new progress.

g051798’s picture

wchen,

Looks good. Do you have a demo of it working. You are correct, I am using version 5.x. I don't have 6.x yet so not able to see your code working. I do have a couple of questions. Are your vocabularies single level or do you have vocabs in hierarchy? Will it work for vocabs that have more than one level? Your example code has a tree of vocabs 2,2 which are the same. Will it work for diff vocabs. I think it will but just curious.

Looking forward to your updates as you progress.
John

wchen’s picture

Hi, John,
Unfortunately I can't setup a demo site. I am using hierarchy vocabularies and it works fine. The only reason that vocabs 2,2 is I only had one vocabulary at that time. I did more test with multiple vocabularies and it works fine.
You have done a great job and I am happy that I can contribute a little bit.