I recently updated a D6 site hosted on Linux server with HS update. All was fine. When I brought that update onto my local Windows machine, running WAMP, I got a connection reset error. In debug, it showed as a socket error. When I traced the code, it was bugging out when loading the hs_taxonomy.module file. Removing that file allowed me to bring up the site normally. Looking at the file in Netbeans turned up a number of uninitialized variables. When I initialized each of them, I was able to load the site successfully. Linux must have something that forgives uninitialized variables; the initialization seems needed for Windows.

Here's the diffs:

# This patch file was generated by NetBeans IDE
# It uses platform neutral UTF-8 encoding and \n newlines.
--- D:\Stateside\sites\all\modules\hierarchical_select\modules\hs_taxonomy.module
+++ D:\Stateside\backups\hs\hierarchical_select\modules\hs_taxonomy.module
@@ -570,7 +570,7 @@
* Implementation of hook_hierarchical_select_config_info().
*/
function hs_taxonomy_hierarchical_select_config_info() {
- static $config_info = array();
+ static $config_info;

if (!isset($config_info)) {
$config_info = array();
@@ -613,8 +613,8 @@
* Implementation of hook_token_values().
*/
function hs_taxonomy_token_values($type, $object = NULL, $options = array()) {
- static $hs_vids = array();
- static $all_vids = array();
+ static $hs_vids;
+ static $all_vids;

$separator = variable_get('hs_taxonomy_separator', variable_get('pathauto_separator', '-'));

@@ -683,7 +683,6 @@
* Implementation of hook_token_list().
*/
function hs_taxonomy_token_list($type = 'all') {
- $tokens = array();
if ($type == 'node' || $type == 'all') {
$tokens['node']['save-lineage-termpath'] = t('Only use when you have enabled the "save lineage" setting of Hierarchical Select. Will show the term\'s parent terms separated by /.');
$tokens['node']['save-lineage-termpath-raw'] = t('As [save-linage-termpath]. WARNING - raw user input.');

Comments

ptitwolfy’s picture

Same issue. Fixed by applying this patch but without removing the "$tokens = array();" code in the last function.
Thanks

Marino Katalinic’s picture

my hs_taxonomy.module looks as if your patch has already been applied however I am experiencing same problems and getting connection reset error. Either I am not reading your patch information or my hs_taxonomy.module file correctly or something else is amiss

Here is the copy of my hs_taxonomy.module

<?php

/**
 * @file
 * Implementation of the Hierarchical Select API for the Taxonomy module.
 */


//----------------------------------------------------------------------------
// Drupal core hooks.

// Add per-vocabulary settings for Hierarchical Select.
function hs_taxonomy_form_taxonomy_form_vocabulary_alter(&$form, &$form_state) {
  module_load_include('inc', 'hierarchical_select', 'includes/common');

  $vid = (isset($form['vid'])) ? $form['vid']['#value'] : NULL;
  $vocabulary = ($vid) ? taxonomy_vocabulary_load($vid) : NULL;

  // Don't add the per-vocabulary settings for Hierarchical Select when the
  // vocabulary still needs to be created or is a freetagging vocabulary.
  if (!$vid || $vocabulary->tags) {
    return;
  }

  if (variable_get("taxonomy_hierarchical_select_$vid", 0)) {
    $form['settings']['tags']['#attributes']['disabled'] = TRUE;
    $form['settings']['tags']['#description'] = t(
      "This setting is irrelevant when you're using Hierarchical Select.
      <br />Use Hierarchical Select's %editability-settings instead.",
      array('%editability-settings' => t('Editability settings'))
    );
    $form['settings']['multiple']['#attributes']['disabled'] = TRUE;
    $form['settings']['multiple']['#description'] = t(
      "This setting is managed by the Hierarchical Select configuration, by
      the %enable-dropbox setting.",
      array('%enable-dropbox' => t('Enable the dropbox'))
    );
  }

  $split = array_search('weight', array_keys($form)) + 1;
  $first_part = array_slice($form, 0, $split);
  $second_part = array_slice($form, $split);
  $form = $first_part;

  $form['hierarchical_select_status'] = array(
    '#type' => 'checkbox',
    '#title' => '<strong>'. t('Use the Hierarchical Select form element for this vocabulary.') .'</strong>',
    '#default_value' => variable_get("taxonomy_hierarchical_select_$vid", 0),
    '#description' => t(
      'When checked, the %free_tagging and %multiple_values settings will
      be managed by the Hierarchical Select configuration.',
      array(
        '%free_tagging' => t('Free tagging'),
        '%multiple_values' => t('Multiple values'),
      )
    ),
  );

  // Add the Hierarchical Select config form.
  $module = 'hs_taxonomy';
  $params = array(
    'vid'                        => $vid,
    'exclude_tid'                => NULL,
    'root_term'                  => NULL,
    'entity_count_for_node_type' => NULL,
  );
  $config_id = "taxonomy-$vid";
  $defaults = array(
    // Enable the save_lineage setting by default if the multiple parents
    // vocabulary option is enabled.
    'save_lineage' => (int) ($vocabulary->hierarchy == 2),
    'editability' => array(
      'max_levels' => _hs_taxonomy_hierarchical_select_get_depth($vid),
    ),
  );
  $strings = array(
    'hierarchy'   => t('vocabulary'),
    'hierarchies' => t('vocabularies'),
    'item'        => t('term'),
    'items'       => t('terms'),
    'item_type'   => t('term type'),
    'entity'      => t('node'),
    'entities'    => t('nodes'),
  );
  $max_hierarchy_depth = _hs_taxonomy_hierarchical_select_get_depth($vid);
  $preview_is_required = $vocabulary->required;
  $form['hierarchical_select'] = hierarchical_select_common_config_form($module, $params, $config_id, $defaults, $strings, $max_hierarchy_depth, $preview_is_required);

  // The forum selection requires that only the deepest term is saved!
  // See http://drupal.org/node/241766#comment-808464.
  if ($vid == variable_get('forum_nav_vocabulary', -1)) {
    $form['hierarchical_select']['save_lineage']['#value'] = 0;
    $form['hierarchical_select']['save_lineage']['#attributes'] = array('disabled' => 'disabled');
    $form['hierarchical_select']['save_lineage']['#description'] .= '<br />'. t(
      'This is the vocabulary that will be used for forum navigation and it
      <strong>always</strong> requires the %dont_save_lineage setting to be
      set!',
      array('%dont_save_lineage' => t('Save only the deepest term'))
    );
  }

  // Add the the submit handler for the Hierarchical Select config form.
  $parents = array('hierarchical_select');
  $form['#submit'][] = 'hierarchical_select_common_config_form_submit';
  $form['#hs_common_config_form_parents'] = $parents;

  // Add a validate callback to override the freetagging and multiple select
  // settings if necessary.
  $form['#validate'][] = 'hierarchical_select_taxonomy_form_vocabulary_validate';
  $form['#submit'][]   = 'hierarchical_select_taxonomy_form_vocabulary_submit';

  // The original #submit callback(s) has/have to be executed afterwards.
  $form['#submit'] = array_merge($form['#submit'], $second_part['#submit']);

  $form += $second_part;
}

// The taxonomy term form.
function hs_taxonomy_form_taxonomy_form_term_alter(&$form, &$form_state) {
  // Don't alter the form when it's in confirmation mode.
  if (isset($form_state['confirm_delete']) || isset($form_state['confirm_parents'])) {
    return;
  }

  require_once(drupal_get_path('module', 'hierarchical_select') .'/includes/common.inc');

  // Build an appropriate config, that inherits the level_labels settings
  // from the vocabulary's Hierarchical Select config.
  $vid = $form['#vocabulary']['vid'];
  $vocabulary_config = hierarchical_select_common_config_get("taxonomy-$vid");
  $config = array(
    'module' => 'hs_taxonomy',
    'params' => array(
      'vid'         => $vid,
      'exclude_tid' => $form['#term']['tid'],
    ),
    'enforce_deepest' => 0,
    'save_lineage'    => 0,
    'level_labels' => $vocabulary_config['level_labels'],
    'dropbox' => array(
      'status'   => 1,
    ),
    'params' => array(
      'vid'         => isset($form['vid']['#value']) ? $form['vid']['#value'] : NULL,
      'exclude_tid' => isset($form['tid']['#value']) ? $form['tid']['#value'] : NULL,
      'root_term'   => TRUE,
    ),
  );

  // Use Hierarchical Select for selecting the parent term(s).
  $parent = isset($form['tid']['#value']) ? array_keys(taxonomy_get_parents($form['tid']['#value'])) : array();
  $form['advanced']['parent'] = array(
    '#type'   => 'hierarchical_select',
    '#config' => $config,
    // Copied from core.
    '#title'         => t('Parents'),
    '#description'   => t('Parent terms') .'.',
    '#weight'        => -15,
    '#default_value' => $parent,
  );
  $form['advanced']['parent']['#config']['dropbox']['title'] = t('All parent terms');

  // Use Hierarchical Select for selecting the related term(s).
  $related = isset($form['tid']['#value']) ? array_keys(taxonomy_get_related($form['tid']['#value'])) : array();
  $form['advanced']['relations'] = array(
    '#type'   => 'hierarchical_select',
    '#config' => $config,
    // Copied from core.
    '#title'         => t('Related terms'),
    '#weight'        => -15,
    '#default_value' => $related,
  );
  $form['advanced']['relations']['#config']['dropbox']['title'] = t('All related terms');
}

// The forum 'container' form.
function hs_taxonomy_form_forum_form_container_alter(&$form, &$form_state) {
  unset($form['parent'][0]['#options']);
  unset($form['parent'][0]['#theme']);
  unset($form['parent'][0]['#required']);
  $form['parent'][0]['#type'] = 'hierarchical_select';
  $form['parent'][0]['#config'] = array(
    'module'          => 'hs_taxonomy',
    'enforce_deepest' => 0,
    'save_lineage'    => 0,
    'params' => array(
      'vid'         => $form['vid']['#value'],
      'exclude_tid' => $form['tid']['#value'],
      'root_term'   => TRUE,
    ),
  );
}

// The forum 'forum' form.
function hs_taxonomy_form_forum_form_forum_alter(&$form, &$form_state) {
  forum_form_container($form, $form_state);
}

function hs_taxonomy_form($vid, $value = 0, $help = NULL) {
  require_once(drupal_get_path('module', 'hierarchical_select') .'/includes/common.inc');

  $vocabulary = taxonomy_vocabulary_load($vid);
  if (module_exists('i18ntaxonomy')) {
    $title = check_plain(tt("taxonomy:vocabulary:$vid:name", $vocabulary->name));
    $description = check_plain(($help) ? $help : tt("taxonomy:vocabulary:$vid:help", $vocabulary->help));
  }
  else {
    $title = check_plain($vocabulary->name);
    $description = check_plain(($help) ? $help : $vocabulary->help);
  }

  $form_item = array(
    '#type'          => 'hierarchical_select',
    '#config'        => array(
      'module' => 'hs_taxonomy',
      'params' => array(
        'vid'                        => $vid,
        'exclude_tid'                => NULL,
        'root_term'                  => NULL,
        'entity_count_for_node_type' => NULL,
      ),
    ),
    '#title'         => $title,
    '#default_value' => $value,
    '#description'   => $description,
    '#weight'        => -15,
  );

  hierarchical_select_common_config_apply($form_item, "taxonomy-$vid");

  return $form_item;
}

/**
 * Implementation of hook_form_alter().
 */
function hs_taxonomy_form_alter(&$form, $form_state, $form_id) {
  // Change the term selection of nodes. Only affects hierarchical
  // vocabularies.
  if (isset($form['type']) && isset($form['#node']) && (variable_get('taxonomy_override_selector', FALSE)) && $form['type']['#value'] .'_node_form' == $form_id) {
    $node = $form['#node'];

    if (!isset($node->taxonomy)) {
      $terms = empty($node->nid) ? array() : taxonomy_node_get_terms($node);
    }
    else {
      // After preview the terms must be converted to objects.
      reset($node->taxonomy);
      if (!is_object(current($node->taxonomy))) {
        $node->taxonomy = taxonomy_preview_terms($node);
      }
      $terms = $node->taxonomy;
    }

    $c = db_query(db_rewrite_sql("SELECT v.* FROM {vocabulary} v INNER JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s' ORDER BY v.weight, v.name", 'v', 'vid'), $node->type);

    while ($vocabulary = db_fetch_object($c)) {
      if ($vocabulary->tags) {
        if (isset($form_state['node_preview'])) {
          // Typed string can be changed by the user before preview,
          // so we just insert the tags directly as provided in the form.
          $typed_string = $node->taxonomy['tags'][$vocabulary->vid];
        }
        else {
          $typed_string = taxonomy_implode_tags($terms, $vocabulary->vid) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL);
        }
        if ($vocabulary->help) {
          $help = filter_xss($vocabulary->help);
        }
        else {
          $help = t('A comma-separated list of terms describing this content. Example: funny, bungee jumping, "Company, Inc.".');
        }
        $form['taxonomy']['tags'][$vocabulary->vid] = array('#type' => 'textfield',
          '#title' => $vocabulary->name,
          '#description' => $help,
          '#required' => $vocabulary->required,
          '#default_value' => $typed_string,
          '#autocomplete_path' => 'taxonomy/autocomplete/'. $vocabulary->vid,
          '#weight' => $vocabulary->weight,
          '#maxlength' => 255,
        );

        if (module_exists('i18ntaxonomy')) {
          $vid = $vocabulary->vid;
          // Special treatment for tags, add some help texts
          $type = i18ntaxonomy_vocabulary($vid);
          if ($type == I18N_TAXONOMY_LOCALIZE || $type == I18N_TAXONOMY_TRANSLATE) {
            $form['taxonomy']['tags'][$vid]['#title'] = i18nstrings_string("taxonomy:vocabulary:$vid:name", $form['taxonomy']['tags'][$vid]['#title']);
            $form['taxonomy']['tags'][$vid]['#description'] = i18nstrings("taxonomy:vocabulary:$vid:help", $form['taxonomy']['tags'][$vid]['#description']);
          }
          if ($type == I18N_TAXONOMY_LOCALIZE) {
            $form['taxonomy']['tags'][$vid]['#description'] .= ' '. t('This is a localizable vocabulary, so only terms in %language are allowed here.', array('%language' => language_default('name')));
          }
        }
      }
      else {
        // Just looking at $vocabulary->required is not sufficient: the forum
        // module does not correctly set $vocabulary->required and hence the
        // forum vocabulary does not have it set. It's made required through
        // some hardcoded piece of Forms API logic. Therefore we have to do
        // more extensive checks to decide to make the form item required.
        $required = (isset($form['taxonomy'][$vocabulary->vid]['#required'])) ? $form['taxonomy'][$vocabulary->vid]['#required'] : $vocabulary->required;
        // Extract terms belonging to the vocabulary in question.
        $default_terms = array();
        foreach ($terms as $term) {
          // Free tagging has no default terms and also no vid after preview.
          if (isset($term->vid) && $term->vid == $vocabulary->vid) {
            $default_terms[$term->tid] = $term;
          }
        }

        $form_function = variable_get("taxonomy_hierarchical_select_$vocabulary->vid", 0) ? 'hs_taxonomy_form' : 'taxonomy_form';

        if ($form_function == 'hs_taxonomy_form' && module_exists('i18ntaxonomy')) {
          $vid = $vocabulary->vid;
          if (is_numeric($vid) && i18ntaxonomy_vocabulary($vid) == I18N_TAXONOMY_LOCALIZE) {
            // Rebuild this vocabulary's form.
            $vocabulary = taxonomy_vocabulary_load($vid);
            // Extract terms belonging to the vocabulary in question.
            $default_terms = array();
            foreach ($terms as $term) {
              if ($term->vid == $vid) {
                $default_terms[$term->tid] = $term;
              }
            }
            $form['taxonomy'][$vid] = i18ntaxonomy_vocabulary_form($vocabulary->vid, array_keys($default_terms));
            $form['taxonomy'][$vid]['#weight'] = $vocabulary->weight;
            $form['taxonomy'][$vid]['#required'] = $vocabulary->required;
            $form['taxonomy'][$vid]['#description'] = i18nstrings("taxonomy:vocabulary:$vid:help", $vocabulary->help);
          }
          elseif (is_numeric($vid) && i18ntaxonomy_vocabulary($vid) == I18N_TAXONOMY_TRANSLATE) {
            // Rebuild this vocabulary's form.
            $vocabulary = taxonomy_vocabulary_load($vid);
            $form['taxonomy'][$vid]['#title'] = i18nstrings_string("taxonomy:vocabulary:$vid:name", $vocabulary->name);
            $form['taxonomy'][$vid]['#description'] = i18nstrings("taxonomy:vocabulary:$vid:help", $vocabulary->help);
          }
        }

        $form['taxonomy'][$vocabulary->vid] = $form_function($vocabulary->vid, array_keys($default_terms), filter_xss($vocabulary->help));
        $form['taxonomy'][$vocabulary->vid]['#weight'] = $vocabulary->weight;
        $form['taxonomy'][$vocabulary->vid]['#required'] = $required;
      }
    }
    if (!empty($form['taxonomy']) && is_array($form['taxonomy'])) {
      if (count($form['taxonomy']) > 1) {
        // Add fieldset only if form has more than 1 element.
        $form['taxonomy'] += array(
          '#type' => 'fieldset',
          '#title' => t('Vocabularies'),
          '#collapsible' => TRUE,
          '#collapsed' => FALSE,
        );
      }
      $form['taxonomy']['#weight'] = -3;
      $form['taxonomy']['#tree'] = TRUE;
    }
  }
}


//----------------------------------------------------------------------------
// Hierarchical Select hooks.

/**
 * Implementation of hook_hierarchical_select_params().
 */
function hs_taxonomy_hierarchical_select_params() {
  $params = array(
    'vid',
    'exclude_tid',                // Allows a term to be excluded (necessary for the taxonomy_form_term form).
    'root_term',                  // Displays a fake "<root>" term in the root level (necessary for the taxonomy_form-term form).
    'entity_count_for_node_type', // Restrict the entity count to a specific node type.
  );
  return $params;
}

/**
 * Implementation of hook_hierarchical_select_root_level().
 */
function hs_taxonomy_hierarchical_select_root_level($params) {
  $terms = _hs_taxonomy_hierarchical_select_get_tree($params['vid'], 0, -1, 1);

  // If the root_term parameter is enabled, then prepend a fake "<root>" term.
  if (isset($params['root_term']) && $params['root_term'] === TRUE) {
    $root_term = new StdClass();
    $root_term->tid = 0;
    $root_term->name = '<'. t('root') .'>';
    $terms = array_merge(array($root_term), $terms);
  }

  // Unset the term that's being excluded, if it is among the terms.
  if (isset($params['exclude_tid'])) {
    foreach ($terms as $key => $term) {
      if ($term->tid == $params['exclude_tid']) {
        unset($terms[$key]);
      }
    }
  }

  // If the Term Permissions module is installed, honor its settings.
  if (function_exists('term_permissions_allowed')) {
    global $user;
    foreach ($terms as $key => $term) {
      if (!term_permissions_allowed($term->tid, $user) ) {
        unset($terms[$key]);
      }
    }
  }

  return _hs_taxonomy_hierarchical_select_terms_to_options($terms);
}

/**
 * Implementation of hook_hierarchical_select_children().
 */
function hs_taxonomy_hierarchical_select_children($parent, $params) {
  if (isset($params['root_term']) && $params['root_term'] && $parent == 0) {
    return array();
  }

  $terms = taxonomy_get_children($parent, $params['vid']);

  // Unset the term that's being excluded, if it is among the children.
  unset($terms[$params['exclude_tid']]);

  // If the Term Permissions module is installed, honor its settings.
  if (function_exists('term_permissions_allowed')) {
    global $user;
    foreach ($terms as $key => $term) {
      if (!term_permissions_allowed($term->tid, $user) ) {
        unset($terms[$key]);
      }
    }
  }

  return _hs_taxonomy_hierarchical_select_terms_to_options($terms);
}

/**
 * Implementation of hook_hierarchical_select_lineage().
 */
function hs_taxonomy_hierarchical_select_lineage($item, $params) {
  $lineage = array();

  if (isset($params['root_term']) && $params['root_term'] && $item == 0) {
    return array(0);
  }

  $terms = array_reverse(hs_taxonomy_get_parents_all($item));
  foreach ($terms as $term) {
    $lineage[] = $term->tid;
  }
  return $lineage;
}

/**
 * Alternative version of taxonomy_get_parents_all(): instead of using all
 * parents of a term (i.e. when multiple parents are being used), only the
 * first is kept.
 */
function hs_taxonomy_get_parents_all($tid) {
  $parents = array();
  if ($tid) {
    $parents[] = taxonomy_get_term($tid);
    $n = 0;
    while ($parent = taxonomy_get_parents($parents[$n]->tid)) {
      $parents = array_merge($parents, array(reset($parent)));
      $n++;
    }
  }
  return $parents;
}

/**
 * Implementation of hook_hierarchical_select_valid_item().
 */
function hs_taxonomy_hierarchical_select_valid_item($item, $params) {
  if (isset($params['root_term']) && $params['root_term'] && $item == 0) {
    return TRUE;
  }
  else if (!is_numeric($item) || $item < 1 || (isset($params['exclude_tid']) && $item == $params['exclude_tid'])) {
    return FALSE;
  }
  $term = taxonomy_get_term($item);

  // If the Term Permissions module is installed, honor its settings.
  if (function_exists('term_permissions_allowed')) {
    global $user;
    if (!term_permissions_allowed($term->tid, $user) ) {
        return FALSE;
    }
  }

  return ($term->vid == $params['vid']);
}

/**
 * Implementation of hook_hierarchical_select_item_get_label().
 */
function hs_taxonomy_hierarchical_select_item_get_label($item, $params) {
  static $labels = array();

  if (!isset($labels[$item])) {
    $term = taxonomy_get_term($item);
    // Use the translated term when available!
    if (module_exists('i18ntaxonomy') && i18ntaxonomy_vocabulary($term->vid) == I18N_TAXONOMY_LOCALIZE) {
      $labels[$item] = tt("taxonomy:term:$term->tid:name", $term->name);
    }
    else {
      $labels[$item] = t($term->name);
    }
  }

  return $labels[$item];
}


/**
 * Implementation of hook_hierarchical_select_create_item().
 */
function hs_taxonomy_hierarchical_select_create_item($label, $parent, $params) {
  $form_state['values'] = array(
    'name'   => html_entity_decode($label, ENT_QUOTES),
    'parent' => $parent,
    'vid'    => $params['vid'],
  );
  $status = taxonomy_save_term($form_state['values']);

  if ($status == SAVED_NEW) {
    // Reset the cached tree.
    _hs_taxonomy_hierarchical_select_get_tree($params['vid'], 0, -1, 1, TRUE);

    // Retrieve the tid.
    $children = taxonomy_get_children($parent, $params['vid']);
    foreach ($children as $tid => $term) {
      if ($term->name == $label) {
        return $tid;
      }
    }
  }
  else {
    return FALSE;
  }
}

/**
 * Implementation of hook_hierarchical_select_entity_count().
 */
function hs_taxonomy_hierarchical_select_entity_count($item, $params) {
  // Allow restricting entity count by node type.
  if ($params['entity_count_for_node_type']){
    return hs_taxonomy_term_count_nodes($item, $params['entity_count_for_node_type']);
  }
  else {
    return hs_taxonomy_term_count_nodes($item);
  }
}

/**
 * Implementation of hook_hierarchical_select_implementation_info().
 */
function hs_taxonomy_hierarchical_select_implementation_info() {
  return array(
    'hierarchy type' => t('Taxonomy'),
    'entity type'    => t('Node'),
  );
}

/**
 * Implementation of hook_hierarchical_select_config_info().
 */
function hs_taxonomy_hierarchical_select_config_info() {
  static $config_info;

  if (!isset($config_info)) {
    $config_info = array();

    $content_types = node_get_types();
    $vocabularies = taxonomy_get_vocabularies();

    foreach ($vocabularies as $vid => $vocabulary) {
      if (variable_get("taxonomy_hierarchical_select_$vid", 0)) {
        // Collect the human-readable names of each content type for which this
        // vocabulary is used.
        $entities = array();
        foreach ($vocabulary->nodes as $content_type) {
          $entities[] = $content_types[$content_type]->name;
        }

        $config_id = "taxonomy-$vid";
        $config_info[$config_id] = array(
          'config_id'      => $config_id,
          'hierarchy type' => t('Taxonomy'),
          'hierarchy'      => t($vocabulary->name),
          'entity type'    => t('Node'),
          'entity'         => implode(', ', array_map('t', $entities)),
          'context type'   => t('Node form'),
          'context'        => '',
          'edit link'      => "admin/content/taxonomy/edit/vocabulary/$vid",
        );
      }
    }
  }

  return  $config_info;
}


//----------------------------------------------------------------------------
// Token hooks.

/**
 * Implementation of hook_token_values().
 */
function hs_taxonomy_token_values($type, $object = NULL, $options = array()) {
  static $hs_vids;
  static $all_vids;

  $separator = variable_get('hs_taxonomy_separator', variable_get('pathauto_separator', '-'));

  $values = array();
  switch ($type) {
    case 'node':
      $node = $object;

      // Default values.
      $values['save-lineage-termpath'] = $values['save-lineage-termpath-raw'] = '';

      // If $node->taxonomy doesn't exist, these tokens cannot be created!
      if (!is_object($node) || !isset($node->taxonomy) || !is_array($node->taxonomy)) {
        return $values;
      }

      // Find out which vocabularies are using Hierarchical Select.
      if (!isset($hs_vids)) {
        $hs_vids = array();
        $result = db_query("SELECT SUBSTRING(name, 30, 3) AS vid FROM {variable} WHERE name LIKE 'taxonomy_hierarchical_select_%' AND value LIKE 'i:1\;';");
        while ($o = db_fetch_object($result)) {
          $hs_vids[] = $o->vid;
        }
      }

      // Get a list of all existent vids, so we can generate an empty token
      // when a token is requested for a vocabulary that's not associated with
      // the current content type.
      if (!isset($all_vids)) {
        $all_vids = array();
        $result = db_query("SELECT vid FROM {vocabulary}");
        while ($row = db_fetch_object($result)) {
          $all_vids[] = $row->vid;
        }
      }

      // Generate the per-vid "save-lineage-termpath" tokens.
      foreach ($all_vids as $vid) {
        $terms = array();
        if (in_array($vid, $hs_vids) && isset($node->taxonomy[$vid])) {
          $selection = $node->taxonomy[$vid];
          $terms = _hs_taxonomy_token_termpath_for_vid($selection, $vid);
        }

        $terms_raw = $terms;
        $terms = array_map('check_plain', $terms);
        $values["save-lineage-termpath:$vid"] = !empty($options['pathauto']) ? $terms : implode($separator, $terms);
        $values["save-lineage-termpath-raw:$vid"] = !empty($options['pathauto']) ? $terms_raw : implode($separator, $terms_raw);
      }

      // We use the terms of the first vocabulary that uses Hierarchical
      // Select for the default "save-lineage-termpath" tokens.
      $vids = array_intersect(array_keys($node->taxonomy), $hs_vids);
      if (!empty($vids)) {
        $vid = $vids[0];
        $values['save-lineage-termpath'] = $values["save-lineage-termpath:$vid"];
        $values['save-lineage-termpath-raw'] = $values["save-lineage-termpath-raw:$vid"];
      }
      break;
  }

  return $values;
}

/**
 * Implementation of hook_token_list().
 */
function hs_taxonomy_token_list($type = 'all') {
  if ($type == 'node' || $type == 'all') {
    $tokens['node']['save-lineage-termpath'] = t('Only use when you have enabled the "save lineage" setting of Hierarchical Select. Will show the term\'s parent terms separated by /.');
    $tokens['node']['save-lineage-termpath-raw'] = t('As [save-linage-termpath]. WARNING - raw user input.');

    $tokens['node']['save-lineage-termpath:vid'] = t('Only has output when terms are present for the vocabulary with the specified vid. Only use when you have enabled the "save lineage" setting of Hierarchical Select. Will show the term\'s parent terms separated by /.');
    $tokens['node']['save-lineage-termpath-raw:vid'] = t('Only has output when terms are present for the vocabulary with the specified vid. As [save-linage-termpath]. WARNING - raw user input.');

    return $tokens;
  }
}

/**
 * Helper function for hs_taxonomy_token_values().
 */
function _hs_taxonomy_token_termpath_for_vid($selection, $vid) {
  $terms = array();
  $selection = (is_array($selection)) ? $selection : array($selection);

  // Generate the part we'll need of the Hierarchical Select configuration.
  $config = array(
    'module'       => 'hs_taxonomy',
    'save_lineage' => 1,
    'params' => array(
      'vid'         => $vid,
      'exclude_tid' => NULL,
      'root_term'   => NULL,
    ),
  );

  // Validate all items in the selection, if any.
  if (!empty($selection)) {
    foreach ($selection as $key => $item) {
      $valid = module_invoke($config['module'], 'hierarchical_select_valid_item', $selection[$key], $config['params']);
      if (!$valid) {
        unset($selection[$key]);
      }
    }
  }

  // Generate a dropbox out of the selection. This will automatically
  // calculate all lineages for us.
  // If the selection is empty, then the tokens will be as well.
  if (!empty($selection)) {
    $dropbox = _hierarchical_select_dropbox_generate($config, $selection);

    // If no lineages could be generated, these tokens cannot be created!
    if (empty($dropbox->lineages)) {
      return $terms;
    }

    // We pick the first lineage.
    $lineage = $dropbox->lineages[0];

    // Finally, we build the tokens.
    foreach ($lineage as $item) {
      $terms[] = $item['label'];
    }
  }

  return $terms;
}


//----------------------------------------------------------------------------
// FAPI callbacks.

/**
 * Additional validate callback for the taxonomy_form_vocabulary form.
 */
function hierarchical_select_taxonomy_form_vocabulary_validate($form, &$form_state) {
  // The "multiple select" setting doesn't exist for the forum vocabulary!
  if ($form_state['values']['hierarchical_select_status'] && isset($form['settings']['multiple'])) {
    // Enable Taxonomy's "multiple select" setting when:
    // - Hierarchical Select's "multiple select" setting is enabled, or:
    // - Hierarchical Select's "save term lineage" setting is enabled
    $multiple_select_enabled = ((isset($form_state['values']['hierarchical_select']['dropbox']['status']) && $form_state['values']['hierarchical_select']['dropbox']['status']) || $form_state['values']['hierarchical_select']['save_lineage']);
    form_set_value($form['settings']['multiple'], (int) $multiple_select_enabled, $form_state);
  }

  // If Hierarchical Select is enabled, disable freetagging.
  if ($form_state['values']['hierarchical_select_status']) {
    form_set_value($form['settings']['tags'], 0, $form_state);
  }
}

/**
 * Additional submit callback for the taxonomy_form_vocabulary form.
 */
function hierarchical_select_taxonomy_form_vocabulary_submit($form, &$form_state) {
  $vid = $form_state['values']['vid'];
  variable_set("taxonomy_hierarchical_select_$vid", $form_state['values']['hierarchical_select_status'], $form_state);
}


//----------------------------------------------------------------------------
// Private functions.

/**
 * Drupal core's taxonomy_get_tree() doesn't allow us to reset the cached
 * trees, which obviously causes problems when you create new items between
 * two calls to it.
 */
function _hs_taxonomy_hierarchical_select_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL, $reset = FALSE) {
  static $children, $parents, $terms;

  if ($reset) {
    $children = $parents = $terms = array();
  }

  $depth++;

  // We cache trees, so it's not CPU-intensive to call get_tree() on a term
  // and its children, too.
  if (!isset($children[$vid])) {
    $children[$vid] = array();

    $result = db_query(db_rewrite_sql('SELECT t.tid, t.*, parent FROM {term_data} t INNER JOIN  {term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d ORDER BY weight, name', 't', 'tid'), $vid);
    while ($term = db_fetch_object($result)) {
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
    }
  }

  $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
  if (isset($children[$vid][$parent])) {
    foreach ($children[$vid][$parent] as $child) {
      if ($max_depth > $depth) {
        $term = drupal_clone($terms[$vid][$child]);
        $term->depth = $depth;
        // The "parent" attribute is not useful, as it would show one parent only.
        unset($term->parent);
        $term->parents = $parents[$vid][$child];
        $tree[] = $term;

        if (isset($children[$vid][$child])) {
          $tree = array_merge($tree, _hs_taxonomy_hierarchical_select_get_tree($vid, $child, $depth, $max_depth));
        }
      }
    }
  }

  return isset($tree) ? $tree : array();
}

/**
 * Drupal core's taxonomy_term_count_nodes() is buggy. See
 * http://drupal.org/node/144969#comment-843000.
 */
function hs_taxonomy_term_count_nodes($tid, $type = 0) {
  static $count;

  $term = taxonomy_get_term($tid);
  $tree = _hs_taxonomy_hierarchical_select_get_tree($term->vid, $tid);
  $tids = array($tid);
  foreach ($tree as $descendant) {
    $tids[] = $descendant->tid;
  }

  if (!isset($count[$type][$tid])) {
    if (is_numeric($type)) {
      $count[$type][$tid] = db_result(db_query(db_rewrite_sql("SELECT COUNT(DISTINCT(n.nid)) AS count FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 AND t.tid IN (%s)"), implode(',', $tids)));
    }
    else {
      $count[$type][$tid] = db_result(db_query(db_rewrite_sql("SELECT COUNT(DISTINCT(n.nid)) AS count FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 AND n.type = '%s' AND t.tid IN (%s)"), $type, implode(',', $tids)));
    }
  }
  return $count[$type][$tid];
}

/**
 * Transform an array of terms into an associative array of options, for use
 * in a select form item.
 *
 * @param $terms
 *  An array of term objects.
 * @return
 *  An associative array of options, keys are tids, values are term names.
 */
function _hs_taxonomy_hierarchical_select_terms_to_options($terms) {
  $options = array();
  foreach ($terms as $key => $term) {
    // Use the translated term when available!
    if (module_exists('i18ntaxonomy') && isset($term->vid) && i18ntaxonomy_vocabulary($term->vid) == I18N_TAXONOMY_LOCALIZE) {
      $options[$term->tid] = tt("taxonomy:term:$term->tid:name", $term->name);
    }
    else {
      $options[$term->tid] = t($term->name);
    }
  }
  return $options;
}

/**
 * Get the depth of a vocabulary's tree.
 *
 * @param $vid
 *   A vocabulary id.
 * @return
 *   The depth of the vocabulary's tree.
 */
function _hs_taxonomy_hierarchical_select_get_depth($vid) {
  $depth = -99999;
  $tree = _hs_taxonomy_hierarchical_select_get_tree($vid);
  foreach ($tree as $term) {
    if ($term->depth > $depth) {
      $depth = $term->depth;
    }
  }
  return $depth;
}

Hopefully someone can help because I really need the hierarchical taxonomy to work on my site

kenorb’s picture

Status: Active » Needs review
gold’s picture

Status: Needs review » Closed (won't fix)

Tidying up the issue queue. The 6.x version is no longer supported. Only reopen this if it also applies to the 7.x.