Print a hierarchical list of taxonomy vocabulary terms on a page

Estimated reading time of this article: 3 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:

/**
 * 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;
}