Print a hierarchical list of taxonomy vocabulary terms on a page

Estimated reading time of this article: 4 minutes

Do you want to print a hierarchical list of taxonomy terms with corresponding feeds in Drupal 7?

If so, then put the following code in a module:

<?php
/**
 * Implements hook_menu().
 */
function YOUR_MODULE_menu() {
 
$items['termlisting'] = array(
   
'title' => 'Term listing',
   
'page callback' => '_YOUR_MODULE_kategorieliste',
   
'page arguments' => array('1'),
   
'access arguments' => array('access content'),
   
'type' => MENU_CALLBACK, // no automatic menu entry
 
);

  return
$items;
}

/**
 * Builds a nested list of taxonomy terms for a vocabulary.
 * Very simple, could be optimized.
 * Algorithm from http://drupal.org/node/223675#comment-900999
 */
function _YOUR_MODULE_vocabulary_tree($vid) {
 
$depth = 0;
 
$num_at_depth = 0;
 
$tree = taxonomy_get_tree($vid);

 
$markup = "<ul class=\"termlist\">\n<li class=\"clearfix\">";
  foreach (
$tree as $term) {
   
$diffdepth=0;
    if (
$term->depth > $depth) {
     
$markup .= "\n<ul>\n<li class=\"clearfix\">";
     
$depth = $term->depth;
     
$num_at_depth = 0;
    }
    if (
$term->depth < $depth) {
     
$diffdepth= $depth -$term->depth;
      while (
$diffdepth > 0){
       
$markup .= "</li>\n</ul>\n";
       
$diffdepth--;
      }
     
$depth = $term->depth;
    }
    if ((
$term->depth == $depth) && ($num_at_depth > 0)) {
       
$markup .= "</li>\n<li class=\"clearfix\">";
      }
   
$markup .= _YOUR_MODULE_build_term_with_feed($term->name, array($term->tid));
   
$num_at_depth++;
  }
 
$markup .= "</li>\n</ul>\n";
  return
$markup;
}

/**
 * Builds a page which prints hierarchically the kategory taxonomy terms.
 */
function _YOUR_MODULE_kategorieliste($vid = 2) {
 
$markup = '<p>Introduction text</p>';
 
$markup .= _YOUR_MODULE_vocabulary_tree($vid);
 
$markup .= "<h2>Combinations</h2>\n";
 
$markup .= "<ul class=\"termlist\">\n";
 
$markup .= "<li>" . _YOUR_MODULE_build_term_with_feed('Terms X+Y', array(19, 20)) . "</li>\n";
 
$markup .= "<li>" . _YOUR_MODULE_build_term_with_feed('Terms X+Y+ ...', array(14, 15, 18, 19, 20, 21, 16, 17)) . "</li>\n";
 
$markup .= "</ul>\n";

 
$build = array();
 
$build['terms'] = array(
   
'#weight' => -5,
   
'#prefix' => '<div class="term-listing">',
   
'#markup' => $markup,
   
'#suffix' => '</div>',
   );

  return
$build;
}

/**
 * Builds a link with parameter name with (count) and the corresponding feed.
 * @param $name
 *  Name of the link
 * @param $tids
 *  Array of tids
 * @return
 *  A link with parameter name with (count) and the corresponding feed.
 */
function _YOUR_MODULE_build_term_with_feed($name, $tids) {
  
$count = db_query("SELECT COUNT(nid) FROM {taxonomy_index} WHERE tid IN (:tids) ", array(':tids' => implode(',', $tids)))->fetchField();
 
$markup = l($name . ' (' . $count . ')', 'taxonomy/term/'. implode('+', $tids), array('attributes' => (array('class' => array('termtext')))));
 
$markup .= ' ' . YOUR_MODULE_feed_icon(array('url' => 'taxonomy/term/' . implode('+', $tids) . '/all/feed', 'title' => $name));
  return
$markup;
}

/**
 * Copy of theme_feed_icon which avoids double encoding of &
 * due to issue http://drupal.org/node/1211668.
 * Uses !feed-title instead of @feed-title for t().
 */
function YOUR_MODULE_feed_icon($variables) {
 
$text = t('Subscribe to !feed-title', array('!feed-title' => $variables['title']));
  if (
$image = theme('image', array('path' => 'misc/feed.png', 'width' => 16, 'height' => 16, 'alt' => $text))) {
    return
l($image, $variables['url'], array('html' => TRUE, 'attributes' => array('class' => array('feed-icon'), 'title' => $text)));
  }
}
?>

This code is based on http://drupal.org/node/223675#comment-900999.

Some styling:

.term-listing {
  margin-top: 14px;
}

.termlist .termtext {
  margin-right: 18px;
}

#page .term-listing a.feed-icon {
  bottom: -2px;
  border-bottom: none;
}

#block-block-1 a.feed-icon img {
  position: relative;
  bottom: -3px;
  border-bottom: none;
}