You are here

taxonomy_menu_block.module in Taxonomy menu block 7

Taxonomy Menu Block module allows you to make menu blocks out of your taxonomies in a very performant way.

File

taxonomy_menu_block.module
View source
<?php

/**
 * @file
 * Taxonomy Menu Block module allows you to make menu blocks out of your
 * taxonomies in a very performant way.
 */

/**
 * Implements hook_menu().
 */
function taxonomy_menu_block_menu() {

  // Add block form.
  $items['admin/structure/block/add-taxonomy-menu-block'] = array(
    'title' => 'Add taxonomy menu block',
    'description' => 'Add a new taxonomy menu block.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_menu_block_add_block_form',
    ),
    'access arguments' => array(
      'administer blocks',
    ),
    'type' => MENU_LOCAL_ACTION,
    'file' => 'taxonomy_menu_block.admin.inc',
  );

  // Display "add taxonomy menu block" link on block overview page.
  $default_theme = variable_get('theme_default', 'bartik');
  foreach (list_themes() as $key => $theme) {
    if ($key != $default_theme) {
      $items['admin/structure/block/list/' . $key . '/add-taxonomy-menu-block'] = array(
        'title' => 'Add taxonomy menu block',
        'description' => 'Add a new taxonomy menu block.',
        'page callback' => 'drupal_get_form',
        'page arguments' => array(
          'taxonomy_menu_block_add_block_form',
        ),
        'access arguments' => array(
          'administer blocks',
        ),
        'type' => MENU_LOCAL_ACTION,
        'file' => 'taxonomy_menu_block.admin.inc',
      );
    }
  }

  // Delete block.
  $items['admin/structure/block/delete-taxonomy-menu-block'] = array(
    'title' => 'Delete taxonomy menu block',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_menu_block_delete',
    ),
    'access arguments' => array(
      'administer blocks',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'taxonomy_menu_block.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_form_alter().
 */
function taxonomy_menu_block_form_alter(&$form, &$form_state, $form_id) {
  switch ($form_id) {
    case 'taxonomy_menu_block_add_block_form':
      taxonomy_menu_block_form_options($form, $form_state);
      break;
    case 'block_admin_configure':
      if ($form['module']['#value'] == 'taxonomy_menu_block') {
        taxonomy_menu_block_form_options($form, $form_state);
        $form['#submit'][] = 'taxonomy_menu_block_edit_block';
        $form['#validate'][] = 'taxonomy_menu_block_add_block_form_validate';
      }
      break;
    case 'block_admin_display_form':

      // Display a "delete" link next to each taxonomy menu block.
      foreach (variable_get('taxonomy_menu_block_ids', array()) as $delta) {
        $form['blocks']['taxonomy_menu_block_' . $delta]['delete'] = array(
          '#type' => 'link',
          '#title' => t('delete'),
          '#href' => 'admin/structure/block/delete-taxonomy-menu-block/' . $delta,
        );
      }
      break;
  }
}

/**
 * Implements hook_theme().
 */
function taxonomy_menu_block_theme() {
  return array(
    'taxonomy_menu_block' => array(
      'variables' => array(
        'items' => NULL,
        'config' => NULL,
      ),
    ),
  );
}

/**
 * Implements hook_block_info().
 */
function taxonomy_menu_block_block_info() {
  $blocks = array();
  $deltas = variable_get('taxonomy_menu_block_ids', array());
  foreach ($deltas as $delta) {
    $name = taxonomy_menu_block_get_name($delta);
    $blocks[$delta]['info'] = $name;
    $blocks[$delta]['cache'] = DRUPAL_CACHE_PER_PAGE;
  }
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function taxonomy_menu_block_block_view($delta = '') {
  $config = taxonomy_menu_block_get_config($delta);
  $data = taxonomy_menu_block_build($config);
  return $data;
}

/**
 * Function to build our tree.
 *
 * @param array $config
 *   An array of configuration options that specifies how to build the
 *   taxonomy tree
 *   - admin_title: Administrative title to use on the block admin page
 *   - vid: Taxonomy vocabulary id
 *   - parent: What kind of tree we will build
 *       0: Display the whole tree
 *       1: Fixed parent: display only a branch of tree, parent is given
 *       2: Dynamic tree: will look at currently viewed page, find an active tid
 *          and display the children of that tid
 *   - parent_fixed: The fixed parent tid in case parent is set to 1
 *   - parent_dynamic: Determines at what depth we need to find the parent tid
 *     in case parent is set to 2
 *   - depth: Until what depth we need to render our tree.
 *   - home_link: Whether to add a link to the frontpage at the beginning of the
 *     menu or not.
 *
 * @return array
 *   Array that contains one value: content, which is the fully rendered HTML
 *   of our tree
 */
function taxonomy_menu_block_build($config) {

  // Initialize.
  $tree = array();
  $parent = NULL;
  $data['content'] = '';
  $data['subject'] = '';

  // Get tree.
  $taxonomy = taxonomy_menu_block_get_taxonomy($config['vid']);
  $tree = $taxonomy['tree'];

  // Find active tid.
  $tid = taxonomy_menu_block_find_active_tid($tree, $config['vid']);

  // Let other modules alter the active tid.
  drupal_alter('taxonomy_menu_block_active_tid', $tid, $config);

  // If we have a dynamic parent and no active tid, bail out.
  if ($config['parent'] == '2' && !$tid) {
    return;
  }

  // Add the active trail.
  $tree = taxonomy_menu_block_add_active_trail($tree, $tid);

  // Set parent for fixed parent blocks.
  if ($config['parent'] == '1') {
    $parent = $config['parent_fixed'];
  }

  // If we have a dynamic parent, find the parent tid.
  if ($config['parent'] == '2') {
    if ($config['parent_dynamic'] != '0') {

      // Loop over tree and find the highest item in active trail, taking into
      // account our specified depth.
      foreach ($tree as $key => $term) {
        if ($term['active_trail'] != '0' && $term['depth'] == $config['parent_dynamic'] - 1) {
          $parent = $key;
        }
      }
    }
    else {
      $parent = $tid;

      // Store depth for later use.
      $parent_depth = $tree[$parent]['depth'];
    }

    // Since we know we have an active tid, we should have a parent.
    if (!$parent) {
      return;
    }
  }

  // Set title of block. Do it now when we still have the full tree, in case of
  // a valid parent.
  if ($parent) {
    $data['subject'] = check_plain($tree[$parent]['name']);
  }
  else {
    $data['subject'] = check_plain($taxonomy['name']);
  }

  // Trim tree to one branch.
  if ($parent) {

    // Loop over tree, store all items that have higher depth than parent.
    // First item that has the same depth means new branch of tree.
    $start = FALSE;
    $branch = array();
    foreach ($tree as $key => $term) {

      // Stop storing once we hit a term with same depth as $parent.
      if ($term['depth'] <= $tree[$parent]['depth']) {
        $start = FALSE;
      }
      if ($start == TRUE) {
        $branch[$key] = $term;
      }

      // Only start storing from when we hit $parent.
      if ($key == $parent && $start != TRUE) {
        $start = TRUE;
      }
    }

    // An empty array means that we don't have any children for this branch.
    if ($branch) {
      $tree = $branch;
    }
    else {
      return;
    }
  }

  // If we have to add the number of nodes attached to each term.
  if ($config['nodes']) {
    $tree = taxonomy_menu_block_nodes($tree, $config);
  }

  // If we have to remove the terms with no nodes attached.
  if ($config['hide_empty']) {
    $tree = taxonomy_menu_block_hide_empty($tree, $config);
  }

  // Depth trim. Always do this last as to not lose the active trail.
  if ($config['depth'] != '0') {
    if ($config['parent'] == 2 && $config['parent_dynamic'] == 0) {

      // Depth is relative to the parent term's depth.
      $max_depth = $parent_depth + $config['depth'];
    }
    else {

      // Depth is relative to the root. Substract one of our max depth
      // because Drupal considers 0 to be the root level.
      $max_depth = $config['depth'] - 1;
    }
    $temp_tree = array();
    foreach ($tree as $tid => $term) {
      if ($term['depth'] <= $max_depth) {
        $temp_tree[$tid] = $term;
      }
    }
    if ($temp_tree) {
      $tree = $temp_tree;
    }
    else {
      return;
    }
  }

  // Last check. If we for some reason still don't have a built tree, bail.
  if (empty($tree)) {
    return;
  }

  // Add link to frontpage.
  if ($config['home_link']) {
    $home_link = array(
      // Give it an impossible key so it doesn't get mistaken for a parent tid.
      '-1' => array(
        'name' => t('Home'),
        'path' => '<front>',
        'depth' => '0',
        'parents' => array(
          '0' => $parent ? $parent : '0',
        ),
        'active_trail' => drupal_is_front_page() ? '2' : '0',
      ),
    );

    // Add to beginning of $tree array.
    $tree = $home_link + $tree;
  }

  // Let other modules alter the taxonomy tree data after all the processing has
  // been done.
  drupal_alter('taxonomy_menu_block', $tree, $config);

  // Nest tree.
  // If we have a parent, pass along parent.
  if ($parent) {
    $tree = taxonomy_menu_block_nest_tree($tree, $parent);
  }
  else {
    $tree = taxonomy_menu_block_nest_tree($tree);
  }

  // Theme tree.
  $data['content'] = array(
    '#items' => $tree,
    '#config' => $config,
    '#theme' => 'taxonomy_menu_block__' . $config['delta'],
  );
  return $data;
}

/**
 * Returns the configuration for the requested block delta.
 *
 * @param string $delta
 *   The delta that uniquely identifies the block in the block system. If
 *   not specified, the default configuration will be returned.
 *
 * @return array
 *   An associated array of configuration options.
 */
function taxonomy_menu_block_get_config($delta = NULL) {

  // Initialize.
  $config = array(
    'delta' => $delta,
    'admin_title' => '',
    'vid' => 0,
    'parent' => 0,
    'parent_fixed' => 0,
    'parent_dynamic' => 1,
    'depth' => 0,
    'home_link' => 0,
    'nodes' => 0,
    'nodes_aggregation' => 0,
    'hide_empty' => 0,
    'ctype' => array(),
  );

  // Get the block configuration options.
  if ($delta) {
    $config = variable_get("taxonomy_menu_block_{$delta}", array());
  }
  return $config;
}

/**
 * Function to get block name.
 */
function taxonomy_menu_block_get_name($delta) {
  $name = t('Taxonomy Menu Block');
  $config = taxonomy_menu_block_get_config($delta);
  if (!empty($config['admin_title'])) {
    $name = $name . ' (' . $config['admin_title'] . ')';
  }
  return $name;
}

/**
 * Get taxonomy tree & name, taking into account i18n.
 */
function taxonomy_menu_block_get_taxonomy($vid) {
  if (module_exists('i18n_taxonomy')) {
    global $language;
    $lang = $language->language;

    // Get cache. Format of cache id: module_name-vid-language.
    $cache = cache_get('taxonomy_menu_block-' . $vid . '-' . $lang);
    if ($cache) {
      $taxonomy = $cache->data;
    }
    else {
      $taxonomy = array();
      $vocab = taxonomy_vocabulary_load($vid);
      $taxonomy['name'] = i18n_taxonomy_vocabulary_name($vocab, $lang);
      $tree = i18n_taxonomy_get_tree($vid, $lang);

      // If data is empty, we have a vocabulary that is set to Translate or
      // Fixed but none of the terms are actually translated
      // (in the current language).
      // Or the vacobulary is set to localize.
      if (empty($tree)) {
        $tree = i18n_taxonomy_localize_terms(i18n_taxonomy_get_tree($vid, 'und'));
      }
      $taxonomy['tree'] = taxonomy_menu_block_structure_tree($tree, $vid, $lang);
      $cache = cache_set('taxonomy_menu_block-' . $vid . '-' . $lang, $taxonomy);
    }
  }
  else {
    $cache = cache_get('taxonomy_menu_block-' . $vid);
    if (!$cache) {
      $taxonomy = array();
      $vocab = taxonomy_vocabulary_load($vid);
      $taxonomy['name'] = $vocab->name;
      $tree = taxonomy_get_tree($vid);
      $taxonomy['tree'] = taxonomy_menu_block_structure_tree($tree, $vid);
      $cache = cache_set('taxonomy_menu_block-' . $vid, $taxonomy);
    }
    else {
      $taxonomy = $cache->data;
    }
  }
  return $taxonomy;
}

/**
 * Structure tree with only the info we need.
 */
function taxonomy_menu_block_structure_tree($data, $vid, $lang = NULL) {
  $tree = array();
  foreach ($data as $term) {

    // If one term in a localized taxonomy isn't translated all terms get
    // returned without a depth or a parent, giving us unusable results.
    // We leave those out here.
    if (isset($term->depth)) {
      $branch['name'] = $term->name;

      // Get path here as it requires an extra db query.
      // This way we get it in cache.
      $branch['path'] = drupal_get_path_alias('taxonomy/term/' . $term->tid, $lang);
      $branch['depth'] = $term->depth;
      $branch['parents'] = $term->parents;
      $branch['active_trail'] = '0';

      // Set alias option to true so we don't have to query for the alias every
      // time, as this is cached anyway.
      $branch['link_options'] = array(
        'alias' => TRUE,
      );
      $tree[$term->tid] = $branch;
    }
  }

  // Let other modules alter the tree data before it gets cached.
  drupal_alter('taxonomy_menu_block_tree', $tree, $vid);
  return $tree;
}

/**
 * Get currently active tid.
 */
function taxonomy_menu_block_find_active_tid($tree, $vid) {
  $active = NULL;

  // Check on what kind of page we are.
  $current = menu_get_item();

  // Return if we have no access
  if (!empty($current) && !$current['access']) {
    return $active;
  }

  // If we're on a NODE page.
  if (!empty($current) && $current['path'] == 'node/%') {
    $term_reference_fields = array();

    // Load all defined fields.
    $fields = field_info_fields();

    // Loop over them and pull out the taxonomy term fields that are attached
    // to nodes.
    foreach ($fields as $key => $field) {
      if ($field['type'] == 'taxonomy_term_reference' && $field['deleted'] == '0' && isset($field['bundles']['node'])) {
        foreach ($field['bundles']['node'] as $bundle) {
          $term_reference_fields[$bundle][] = $key;
        }
      }
    }
    $node = $current['page_arguments'][0];

    // Check if the current node has a term reference field.
    if (isset($term_reference_fields[$node->type])) {
      foreach ($term_reference_fields[$node->type] as $term_reference_field) {
        $term_reference_field = $node->{$term_reference_field};
        if (!empty($term_reference_field[LANGUAGE_NONE])) {

          // Only the last value will be selected, also for fields that allow
          // multiple values. It doesn't make sense to have more than one
          // active trail/items in a menu.
          $tid = end($term_reference_field[LANGUAGE_NONE]);
          $tid = $tid['tid'];

          // Check if the term actually belongs to the vocabulary we need to
          // display (node can have more than one term reference field).
          if (isset($tree[$tid])) {
            $active = $tid;
          }
        }
      }
    }
  }

  // If we're on a TAXONOMY page.
  if (!empty($current) && $current['path'] == 'taxonomy/term/%') {
    $tid = end($current['original_map']);

    // Check if the current tid is in our tree.
    if (isset($tid) && isset($tree[$tid])) {
      $active = $tid;
    }
  }
  return $active;
}

/**
 * Add active trail.
 *
 * Options:
 *  0 = not active. Has been set as default in taxonomy_menu_block_get_tree()
 *  1 = active trail
 *  2 = active
 */
function taxonomy_menu_block_add_active_trail($tree, $tid = NULL) {

  // If we have an active page, add the trail.
  if ($tid) {

    // Set current page as active.
    $tree[$tid]['active_trail'] = '2';

    // Add active trail to the parent(s).
    $parent = $tree[$tid]['parents'][0];
    for ($i = 0; $i < $tree[$tid]['depth']; $i++) {
      $tree[$parent]['active_trail'] = '1';
      $parent = $tree[$parent]['parents'][0];
    }
  }
  return $tree;
}

/**
 * Attach nr. of nodes to each term, plus the optional node options.
 *
 * @param type $tree
 * @param type $config
 * @return type
 */
function taxonomy_menu_block_nodes($tree, $config) {

  // Get the node count.
  $tids = array_keys($tree);
  $nodes = taxonomy_menu_block_get_nodes($tids, $config['vid'], $config['ctype']);

  // Add the "normal" node count.
  foreach ($tree as $tid => $term) {
    $tree[$tid]['nodes'] = 0;
    if (isset($nodes[$tid])) {
      $tree[$tid]['nodes'] = $nodes[$tid];
    }
  }

  // Add the aggregation node count.
  if ($config['nodes_aggregation']) {
    foreach ($tree as $tid => $term) {

      // Only aggregate if we have a parent and if the term is present in the
      // current tree.
      $parent_tid = $term['parents'][0];
      if (isset($tree[$parent_tid]) && $parent_tid != '0') {
        $tree[$parent_tid]['nodes'] = $tree[$parent_tid]['nodes'] + $term['nodes'];
      }
    }
  }
  return $tree;
}

/**
 * Delete the terms with no nodes attached to them.
 *
 * @param type $tree
 * @param type $config
 */
function taxonomy_menu_block_hide_empty($tree, $config) {

  // Get the node count.
  $tids = array_keys($tree);
  $nodes = taxonomy_menu_block_get_nodes($tids, $config['vid'], $config['ctype']);

  // Create helper array with subsequent keys.
  $helper = array();
  foreach ($tree as $tid => $term) {
    $temp = array(
      'tid' => $tid,
      'depth' => $term['depth'],
    );
    $helper[] = $temp;
  }
  $branches = array();
  foreach ($helper as $i => $term) {

    // If the current term has any nodes, set any branches to which this term
    // may belong to to non-delete
    if (isset($nodes[$term['tid']])) {
      foreach ($branches as $b => $branch) {
        $branches[$b]['delete'] = FALSE;
      }
    }

    // Get key for next term.
    $key = $i + 1;

    // If we have only one term or at the end of our array.
    if (!isset($helper[$key])) {
      if (!isset($nodes[$term['tid']])) {
        unset($tree[$term['tid']]);
      }
    }
    else {

      // Get next term.
      $next = $helper[$key];

      // If current and next term have same depth.
      if ($term['depth'] == $next['depth']) {
        if (!isset($nodes[$term['tid']])) {
          unset($tree[$term['tid']]);
        }
      }

      // If next term is higher in depth, we have a new branch.
      if ($term['depth'] < $next['depth']) {

        // If we have no nodes, set parent of branch ready for deletion.
        if (isset($nodes[$term['tid']])) {
          $branches[$term['tid']] = array(
            'tid' => $term['tid'],
            'delete' => FALSE,
          );
        }
        else {
          $branches[$term['tid']] = array(
            'tid' => $term['tid'],
            'delete' => TRUE,
          );
        }
      }

      // If next term is lower in depth, we are at the end of a branch.
      if ($term['depth'] > $next['depth']) {

        // Find out how many branches were closed.
        $depth = $term['depth'] - $next['depth'];
        for ($i = 0; $i < $depth; $i++) {

          // Get last open branch.
          $branch = array_pop($branches);
          if ($branch['delete']) {
            unset($tree[$branch['tid']]);
          }
        }

        // End of branch always means the current term is not a branch itself.
        if (!isset($nodes[$term['tid']])) {
          unset($tree[$term['tid']]);
        }
      }
    }
  }

  // Check remaining branches
  foreach ($branches as $b => $branch) {
    if ($branch['delete']) {
      unset($tree[$branch['tid']]);
    }
  }
  return $tree;
}

/**
 * Get the number of nodes attached to the terms of a vocabulary.
 *
 * We try to keep this as performant as possible by doing one query for all the
 * terms in a vocabulary, which we will then cache.
 */
function taxonomy_menu_block_get_nodes($tids, $vid, $ctype) {
  $nodes = array();
  if (!empty($ctype)) {
    $ctypestring = implode('-', $ctype);
  }

  // First check if our vocabulary is set to localize. If this is the case
  // nodes in different languages will be attached to the same tid.
  if (module_exists('i18n_taxonomy') && i18n_taxonomy_vocabulary_mode($vid) == 1) {
    global $language;
    $lang = $language->language;
    if (!empty($ctype)) {
      $cache_identifier = 'taxonomy_menu_block-' . $vid . '-nodes-' . $lang . '-' . $ctypestring;
    }
    else {
      $cache_identifier = 'taxonomy_menu_block-' . $vid . '-nodes-' . $lang;
    }

    // First check if we have the result in cache already.
    $cache = cache_get($cache_identifier);
    if ($cache) {
      $nodes = $cache->data;
    }
    else {

      // Query for nodes attached to every term of the tree.
      // Later we'll sift out the node counts we need from this global result.
      $query = db_select('taxonomy_index', 'ti');
      $query
        ->fields('ti', array(
        'tid',
      ));
      $query
        ->join('node', 'n', 'ti.nid = n.nid');
      $query
        ->join('taxonomy_term_data', 'ttd', 'ti.tid = ttd.tid');
      $query
        ->condition('ttd.vid', $vid, '=');
      $query
        ->condition('n.language', $lang, '=');
      $query
        ->groupBy('ti.tid');
      if (!empty($ctype)) {
        $or = db_or();
        foreach ($ctype as $type) {
          $or
            ->condition('n.type', $type, '=');
        }
        $query
          ->condition($or);
      }
      $query
        ->addExpression('COUNT(ti.tid)', 'count');
      $nodes = $query
        ->execute()
        ->fetchAllKeyed();
      $cache = cache_set($cache_identifier, $nodes);
    }
  }
  else {
    if (!empty($ctype)) {
      $cache_identifier = 'taxonomy_menu_block-' . $vid . '-nodes' . '-' . $ctypestring;
    }
    else {
      $cache_identifier = 'taxonomy_menu_block-' . $vid . '-nodes';
    }
    $cache = cache_get($cache_identifier);
    if ($cache) {
      $nodes = $cache->data;
    }
    else {

      // Here we don't need to take language in account. Every term has its own
      // tid.
      $query = db_select('taxonomy_index', 'ti');
      $query
        ->fields('ti', array(
        'tid',
      ));
      $query
        ->join('taxonomy_term_data', 'ttd', 'ti.tid = ttd.tid');
      $query
        ->condition('ttd.vid', $vid, '=');
      $query
        ->groupBy('ti.tid');
      if (!empty($ctype)) {
        $query
          ->join('node', 'n', 'ti.nid = n.nid');
        $or = db_or();
        foreach ($ctype as $type) {
          $or
            ->condition('n.type', $type, '=');
        }
        $query
          ->condition($or);
      }
      $query
        ->addExpression('COUNT(ti.tid)', 'count');
      $nodes = $query
        ->execute()
        ->fetchAllKeyed();
      $cache = cache_set($cache_identifier, $nodes);
    }
  }

  // Find the same values of our two arrays
  $tids = array_flip($tids);
  $nodes = array_intersect_key($nodes, $tids);
  return $nodes;
}

/**
 * Nest tree.
 */
function taxonomy_menu_block_nest_tree($tree, $root = '0') {
  $nested_tree = array();

  // Loop over the tree and search for direct children of the root.
  foreach ($tree as $tid => $term) {

    // Direct child is found.
    if ($term['parents'][0] == $root) {

      // Remove item from tree (we don't need to traverse this again).
      unset($tree[$tid]);

      // Append the child into result array and parse its children.
      $nested_tree[$tid] = $term;
      $nested_tree[$tid]['children'] = taxonomy_menu_block_nest_tree($tree, $tid);
    }
  }
  return $nested_tree;
}

/**
 * Generate markup for our list.
 */
function theme_taxonomy_menu_block($variables) {
  $tree = $variables['items'];
  $config = $variables['config'];
  $num_items = count($tree);
  $i = 0;
  $output = '<ul>';
  foreach ($tree as $tid => $term) {
    $i++;

    // Add classes.
    $attributes = array();
    if ($i == 1) {
      $attributes['class'][] = 'first';
    }
    if ($i == $num_items) {
      $attributes['class'][] = 'last';
    }
    if ($term['active_trail'] == '1') {
      $attributes['class'][] = 'active-trail';
    }
    if ($term['active_trail'] == '2') {
      $attributes['class'][] = 'active';
    }
    if (!empty($term['children'])) {
      $attributes['class'][] = 'has-children';
    }

    // Alter link text if we have to display the nodes attached.
    if (isset($term['nodes'])) {
      $term['name'] = $term['name'] . ' (' . $term['nodes'] . ')';
    }
    $output .= '<li' . drupal_attributes($attributes) . '>' . l($term['name'], $term['path'], $term['link_options']);
    if (!empty($term['children'])) {
      $output .= theme('taxonomy_menu_block__' . $config['delta'], array(
        'items' => $term['children'],
        'config' => $config,
      ));
    }
    $output .= '</li>';
  }
  $output .= '</ul>';
  return $output;
}

/**
 * Implements hook_entity_insert().
 */
function taxonomy_menu_block_entity_insert($entity, $type) {
  switch ($type) {
    case 'taxonomy_term':

      // Clear cache for this vocabulary.
      cache_clear_all('taxonomy_menu_block-' . $entity->vid, 'cache', TRUE);
      break;
  }
}

/**
 * Implements hook_entity_update().
 */
function taxonomy_menu_block_entity_update($entity, $type) {
  switch ($type) {
    case 'taxonomy_vocabulary':
    case 'taxonomy_term':

      // Clear cache for this vocabulary.
      cache_clear_all('taxonomy_menu_block-' . $entity->vid, 'cache', TRUE);
      break;
  }
}

/**
 * Implements hook_entity_delete().
 */
function taxonomy_menu_block_entity_delete($entity, $type) {
  switch ($type) {
    case 'taxonomy_vocabulary':
    case 'taxonomy_term':

      // Clear cache for this vocabulary.
      cache_clear_all('taxonomy_menu_block-' . $entity->vid, 'cache', TRUE);
      break;
  }
}

/**
 * Implements hook_path_alias_types().
 */
function taxonomy_menu_block_path_alias_types() {

  // Clear cache. There's no way of knowing if taxonomy paths have been deleted
  // so we clear all TMB caches.
  cache_clear_all('taxonomy_menu_block-', 'cache', TRUE);
}

/**
 * Expand normal Block add/edit form.
 */
function taxonomy_menu_block_form_options(&$form, &$form_state) {

  // Off-load to another file to keep back-end functions in one place.
  module_load_include('inc', 'taxonomy_menu_block', 'taxonomy_menu_block.admin');
  return _taxonomy_menu_block_form_options($form, $form_state);
}

/**
 * Edit function.
 */
function taxonomy_menu_block_edit_block($form, &$form_state) {

  // Off-load to another file to keep back-end functions in one place.
  module_load_include('inc', 'taxonomy_menu_block', 'taxonomy_menu_block.admin');
  return _taxonomy_menu_block_edit_block($form, $form_state);
}

/**
 * Validate function.
 */
function taxonomy_menu_block_add_block_form_validate($form, &$form_state) {

  // Off-load to another file to keep back-end functions in one place.
  module_load_include('inc', 'taxonomy_menu_block', 'taxonomy_menu_block.admin');
  return _taxonomy_menu_block_add_block_form_validate($form, $form_state);
}

Functions

Namesort descending Description
taxonomy_menu_block_add_active_trail Add active trail.
taxonomy_menu_block_add_block_form_validate Validate function.
taxonomy_menu_block_block_info Implements hook_block_info().
taxonomy_menu_block_block_view Implements hook_block_view().
taxonomy_menu_block_build Function to build our tree.
taxonomy_menu_block_edit_block Edit function.
taxonomy_menu_block_entity_delete Implements hook_entity_delete().
taxonomy_menu_block_entity_insert Implements hook_entity_insert().
taxonomy_menu_block_entity_update Implements hook_entity_update().
taxonomy_menu_block_find_active_tid Get currently active tid.
taxonomy_menu_block_form_alter Implements hook_form_alter().
taxonomy_menu_block_form_options Expand normal Block add/edit form.
taxonomy_menu_block_get_config Returns the configuration for the requested block delta.
taxonomy_menu_block_get_name Function to get block name.
taxonomy_menu_block_get_nodes Get the number of nodes attached to the terms of a vocabulary.
taxonomy_menu_block_get_taxonomy Get taxonomy tree & name, taking into account i18n.
taxonomy_menu_block_hide_empty Delete the terms with no nodes attached to them.
taxonomy_menu_block_menu Implements hook_menu().
taxonomy_menu_block_nest_tree Nest tree.
taxonomy_menu_block_nodes Attach nr. of nodes to each term, plus the optional node options.
taxonomy_menu_block_path_alias_types Implements hook_path_alias_types().
taxonomy_menu_block_structure_tree Structure tree with only the info we need.
taxonomy_menu_block_theme Implements hook_theme().
theme_taxonomy_menu_block Generate markup for our list.