Skip to content
Snippets Groups Projects
taxonomy.module 52.3 KiB
Newer Older
Dries Buytaert's avatar
Dries Buytaert committed
<?php
Kjartan Mannes's avatar
Kjartan Mannes committed
// $Id$
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * @file
 * Enables the organization of content into categories.
 */

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Implement hook_permission().
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function taxonomy_permission() {
    'administer taxonomy' => array(
      'title' => t('Administer taxonomy'),
      'description' => t('Manage taxonomy vocabularies and terms.'),
    ),
  foreach (taxonomy_get_vocabularies() as $vocabulary) {
    $permissions += array(
      'edit terms in ' . $vocabulary->vid => array(
        'title' => t('Edit terms in %vocabulary', array('%vocabulary' => $vocabulary->name)),
        'description' => t('Edit terms in the %vocabulary vocabulary.', array('%vocabulary' => $vocabulary->name)),
      ),
    );
    $permissions += array(
       'delete terms in ' . $vocabulary->vid => array(
         'title' => t('Delete terms in %vocabulary', array('%vocabulary' => $vocabulary->name)),
         'description' => t('Delete terms in the %vocabulary vocabulary.', array('%vocabulary' => $vocabulary->name)),
      ),
    );
  }
  return $permissions;
Kjartan Mannes's avatar
Kjartan Mannes committed
}
Dries Buytaert's avatar
Dries Buytaert committed

  $return = array(
    'taxonomy_term' => array(
      'controller class' => 'TaxonomyTermController',
      'base table' => 'taxonomy_term_data',
      'fieldable' => TRUE,
      'object keys' => array(
        'id' => 'tid',
        'bundle' => 'vocabulary_machine_name',
      ),
      'bundle keys' => array(
        'bundle' => 'machine_name',
      ),
      'bundles' => array(),
  foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocabulary) {
    $return['taxonomy_term']['bundles'][$machine_name] = array(
      'label' => $vocabulary->name,
      'admin' => array(
        'path' => 'admin/structure/taxonomy/%taxonomy_vocabulary',
        'real path' => 'admin/structure/taxonomy/' . $vocabulary->vid,
        'bundle argument' => 3,
        'access arguments' => array('administer taxonomy'),
      ),
    );
  }
  $return['taxonomy_vocabulary'] = array(
    'label' => t('Taxonomy vocabulary'),
    'controller class' => 'TaxonomyVocabularyController',
    'base table' => 'taxonomy_vocabulary',
    'object keys' => array(
      'id' => 'vid',
    ),
    'fieldable' => FALSE,
  );

/**
 * Return nodes attached to a term across all field instances.
 *
 * This function requires taxonomy module to be maintaining its own tables,
 * and will return an empty array if it is not. If using other field storage
 * methods alternatives methods for listing terms will need to be used.
 *
 * @param $term
 *   The term object.
 * @param $pager
 *   Boolean to indicate whether a pager should be used.
 * @order
 *   An array of fields and directions.
 *
 * @return
 *   An array of nids matching the query.
 */
function taxonomy_select_nodes($term, $pager = TRUE, $order = array('t.sticky' => 'DESC', 't.created' => 'DESC')) {
  if (!variable_get('taxonomy_maintain_index_table', TRUE)) {
    return array();
  }
  $query = db_select('taxonomy_index', 't');
  $query->addTag('node_access');
  if ($pager) {
    $count_query = clone $query;
    $count_query->addExpression('COUNT(t.nid)');

    $query = $query
      ->extend('PagerDefault')
      ->limit(variable_get('default_nodes_main', 10));
    $query->setCountQuery($count_query);
  }
  else {
    $query->range(0, variable_get('feed_default_items', 10));
  }
  $query->condition('tid', $term->tid );
  $query->addField('t', 'nid');
  $query->addField('t', 'tid');
  foreach ($order as $field => $direction) {
    $query->orderBy($field, $direction);
    // ORDER BY fields need to be loaded too, assume they are in the form
    // table_alias.name
    list($table_alias, $name) = explode('.', $field);
    $query->addField($table_alias, $name);
  }
  return $query->execute()->fetchCol();
}

/**
 * Implement hook_field_build_modes();
 *
 * @TODO: build mode for display as a field (when attached to nodes etc.).
 */
function taxonomy_field_build_modes($obj_type) {
  $modes = array();
  if ($obj_type == 'term') {
    $modes = array(
      'full' => t('Taxonomy term page'),
    );
  }
  return $modes;
}

 */
function taxonomy_theme() {
  return array(
    'taxonomy_term_select' => array(
      'arguments' => array('element' => NULL),
    'taxonomy_overview_vocabularies' => array(
      'arguments' => array('form' => array()),
    ),
    'taxonomy_overview_terms' => array(
      'arguments' => array('form' => array()),
    ),
    'taxonomy_autocomplete' => array(
      'arguments' => array('element' => NULL),
    ),
/**
 * For vocabularies not maintained by taxonomy.module, give the maintaining
 * module a chance to provide a path for terms in that vocabulary.
 *
 * @param $term
 *   A term object.
 * @return
 *   An internal Drupal path.
 */
function taxonomy_term_path($term) {
  $vocabulary = taxonomy_vocabulary_load($term->vid);
  if ($vocabulary->module != 'taxonomy' && $path = module_invoke($vocabulary->module, 'term_path', $term)) {
    return $path;
  }
  return 'taxonomy/term/' . $term->tid;
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
    'title' => 'Taxonomy',
    'description' => 'Manage tagging, categorization, and classification of your content.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('taxonomy_overview_vocabularies'),
    'access arguments' => array('administer taxonomy'),
  $items['admin/structure/taxonomy/list'] = array(
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );

    'page callback' => 'drupal_get_form',
    'page arguments' => array('taxonomy_form_vocabulary'),
    'access arguments' => array('administer taxonomy'),
  $items['taxonomy/term/%taxonomy_term'] = array(
    'title callback' => 'taxonomy_term_title',
    'title arguments' => array(2),
    'page callback' => 'taxonomy_term_page',
    'page arguments' => array(2),
    'access arguments' => array('access content'),
  $items['taxonomy/term/%taxonomy_term/view'] = array(
    'title' => 'View',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  $items['taxonomy/term/%taxonomy_term/edit'] = array(
    'title' => 'Edit term',
    'page callback' => 'taxonomy_term_edit',
    'access callback' => 'taxonomy_term_edit_access',
    'access arguments' => array(2),
  $items['taxonomy/term/%taxonomy_term/feed'] = array(
    'title' => 'Taxonomy term',
    'title callback' => 'taxonomy_term_title',
    'title arguments' => array(2),
    'page callback' => 'taxonomy_term_feed',
    'page arguments' => array(2),
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
    'page callback' => 'taxonomy_autocomplete',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  $items['admin/structure/taxonomy/%taxonomy_vocabulary'] = array(
    'title' => 'Vocabulary', // this is replaced by callback
    'page callback' => 'drupal_get_form',
    'page arguments' => array('taxonomy_form_vocabulary', 3),
    'title callback' => 'taxonomy_admin_vocabulary_title_callback',
    'title arguments' => array(3),
    'access arguments' => array('administer taxonomy'),
    'type' => MENU_CALLBACK,
  $items['admin/structure/taxonomy/%taxonomy_vocabulary/edit'] = array(
  $items['admin/structure/taxonomy/%taxonomy_vocabulary/list'] = array(
    'title' => 'List terms',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('taxonomy_overview_terms', 3),
    'access arguments' => array('administer taxonomy'),
    'type' => MENU_LOCAL_TASK,
  $items['admin/structure/taxonomy/%taxonomy_vocabulary/list/add'] = array(
    'page callback' => 'drupal_get_form',
    'page arguments' => array('taxonomy_form_term', 3),
    'access arguments' => array('administer taxonomy'),
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
  return $items;
}
Dries Buytaert's avatar
Dries Buytaert committed

/**
 * Return edit access for a given term.
 */
function taxonomy_term_edit_access($term) {
  return user_access("edit terms in $term->vid") || user_access('administer taxonomy');
}

/**
 * Return the vocabulary name given the vocabulary object.
 */
function taxonomy_admin_vocabulary_title_callback($vocabulary) {
  return check_plain($vocabulary->name);
}

/**
 * Save a vocabulary given a vocabulary object.
function taxonomy_vocabulary_save($vocabulary) {
  if (!empty($vocabulary->name)) {
    // Prevent leading and trailing spaces in vocabulary names.
    $vocabulary->name = trim($vocabulary->name);
  }
Dries Buytaert's avatar
 
Dries Buytaert committed

  if (!isset($vocabulary->module)) {
    $vocabulary->module = 'taxonomy';
  if (!empty($vocabulary->vid) && !empty($vocabulary->name)) {
    $status = drupal_write_record('taxonomy_vocabulary', $vocabulary, 'vid');
    module_invoke_all('taxonomy_vocabulary_update', $vocabulary);
Kjartan Mannes's avatar
Kjartan Mannes committed
  }
    $status = drupal_write_record('taxonomy_vocabulary', $vocabulary);
    field_attach_create_bundle($vocabulary->machine_name);
    taxonomy_vocabulary_create_field($vocabulary);
    module_invoke_all('taxonomy_vocabulary_insert', $vocabulary);
Kjartan Mannes's avatar
Kjartan Mannes committed
  }
Dries Buytaert's avatar
 
Dries Buytaert committed

  cache_clear_all();
  entity_get_controller('taxonomy_vocabulary')->resetCache();
Dries Buytaert's avatar
 
Dries Buytaert committed

Kjartan Mannes's avatar
Kjartan Mannes committed
}
Dries Buytaert's avatar
Dries Buytaert committed

/**
 * Delete a vocabulary.
 *
 * @param $vid
 *   A vocabulary ID.
 * @return
 *   Constant indicating items were deleted.
 */
  $vocabulary = (array) taxonomy_vocabulary_load($vid);
Dries Buytaert's avatar
 
Dries Buytaert committed

  db_delete('taxonomy_vocabulary')
    ->condition('vid', $vid)
    ->execute();
  $result = db_query('SELECT tid FROM {taxonomy_term_data} WHERE vid = :vid', array(':vid' => $vid))->fetchCol();
  foreach ($result as $tid) {
    taxonomy_term_delete($tid);
Dries Buytaert's avatar
Dries Buytaert committed
  }
Dries Buytaert's avatar
 
Dries Buytaert committed

  field_attach_delete_bundle($vocabulary['machine_name']);
Dries Buytaert's avatar
 
Dries Buytaert committed
  module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
  cache_clear_all();
  entity_get_controller('taxonomy_vocabulary')->resetCache();
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
}

 * Dynamically check and update the hierarchy flag of a vocabulary.
 * Checks the current parents of all terms in a vocabulary and updates the
 * vocabularies hierarchy setting to the lowest possible level. A hierarchy with
 * no parents in any of its terms will be given a hierarchy of 0. If terms
 * contain at most a single parent, the vocabulary will be given a hierarchy of
 * 1. If any term contain multiple parents, the vocabulary will be given a
 * @param $changed_term
 *   An array of the term structure that was updated.
 */
function taxonomy_check_vocabulary_hierarchy($vocabulary, $changed_term) {
  $hierarchy = 0;
  foreach ($tree as $term) {
    // Update the changed term with the new parent value before comparison.
    if ($term->tid == $changed_term['tid']) {
      $term = (object)$changed_term;
      $term->parents = $term->parent;
    }
    // Check this term's parent count.
    if (count($term->parents) > 1) {
      $hierarchy = 2;
      break;
    }
    elseif (count($term->parents) == 1 && 0 !== array_shift($term->parents)) {
      $hierarchy = 1;
    }
  }
  if ($hierarchy != $vocabulary->hierarchy) {
    $vocabulary->hierarchy = $hierarchy;
    taxonomy_vocabulary_save($vocabulary);
/**
 * Create a default field when a vocabulary is created.
 *
 * @param $vocabulary
 *   A taxonomy vocabulary object.
 */
function taxonomy_vocabulary_create_field($vocabulary) {
  $field = array(
    'field_name' => 'taxonomy_' . $vocabulary->machine_name,
    'type' => 'taxonomy_term',
    // Set cardinality to unlimited so that select
    // and autocomplete widgets behave as normal.
    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
    'settings' => array(
      'allowed_values' => array(
        array(
          'vid' => $vocabulary->vid,
          'parent' => 0,
        ),
      ),
    ),
  );
  field_create_field($field);
}

 * Save a term object to the database.
 * @param $term
 *  A term object.
 * @return
 *   Status constant indicating if term was inserted or updated.
 */
function taxonomy_term_save($term) {
  if ($term->name) {
    // Prevent leading and trailing spaces in term names.
    $term->name = trim($term->name);
  }
  if (!isset($term->vocabulary_machine_name)) {
    $vocabulary = taxonomy_vocabulary_load($term->vid);
    $term->vocabulary_machine_name = $vocabulary->machine_name;
  }

  field_attach_presave('taxonomy_term', $term);
  if (!empty($term->tid) && $term->name) {
    $status = drupal_write_record('taxonomy_term_data', $term, 'tid');
    field_attach_update('taxonomy_term', $term);
    module_invoke_all('taxonomy_term_update', $term);
Kjartan Mannes's avatar
Kjartan Mannes committed
  }
  else {
    $status = drupal_write_record('taxonomy_term_data', $term);
    field_attach_insert('taxonomy_term', $term);
    module_invoke_all('taxonomy_term_insert', $term);
Kjartan Mannes's avatar
Kjartan Mannes committed
  }
  db_delete('taxonomy_term_hierarchy')
    ->condition('tid', $term->tid)
    ->execute();
Dries Buytaert's avatar
Dries Buytaert committed

  if (!isset($term->parent) || empty($term->parent)) {
    $term->parent = array(0);
Kjartan Mannes's avatar
Kjartan Mannes committed
  }
  if (!is_array($term->parent)) {
    $term->parent = array($term->parent);
  }
  $query = db_insert('taxonomy_term_hierarchy')
    ->fields(array('tid', 'parent'));
  if (is_array($term->parent)) {
    foreach ($term->parent as $parent) {
      if (is_array($parent)) {
        foreach ($parent as $tid) {
        $query->values(array(
          'tid' => $term->tid,
          'parent' => $parent
        ));
Dries Buytaert's avatar
Dries Buytaert committed
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
  }
Dries Buytaert's avatar
Dries Buytaert committed

  db_delete('taxonomy_term_synonym')
    ->condition('tid', $term->tid)
    ->execute();
  if (!empty($term->synonyms)) {
    $query = db_insert('taxonomy_term_synonym')
      ->fields(array('tid', 'name'));
    foreach (explode ("\n", str_replace("\r", '', $term->synonyms)) as $synonym) {
Dries Buytaert's avatar
 
Dries Buytaert committed
      if ($synonym) {
        $query->values(array(
          'tid' => $term->tid,
          'name' => rtrim($synonym)
        ));
Dries Buytaert's avatar
 
Dries Buytaert committed
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
    }
Dries Buytaert's avatar
Dries Buytaert committed
  }
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
  cache_clear_all();
Dries Buytaert's avatar
 
Dries Buytaert committed

Kjartan Mannes's avatar
Kjartan Mannes committed
}
Dries Buytaert's avatar
Dries Buytaert committed

/**
 * Delete a term.
 *
 * @param $tid
 *   The term ID.
 * @return
 *   Status constant indicating deletion.
 */
function taxonomy_term_delete($tid) {
  $tids = array($tid);
  while ($tids) {
    $children_tids = $orphans = array();
    foreach ($tids as $tid) {
      // See if any of the term's children are about to be become orphans:
      if ($children = taxonomy_get_children($tid)) {
        foreach ($children as $child) {
          // If the term has multiple parents, we don't delete it.
          $parents = taxonomy_get_parents($child->tid);
          if (count($parents) == 1) {
            $orphans[] = $child->tid;
          }
        }
      }
Dries Buytaert's avatar
 
Dries Buytaert committed

      $term = taxonomy_term_load($tid);
Dries Buytaert's avatar
 
Dries Buytaert committed

      db_delete('taxonomy_term_data')
        ->condition('tid', $tid)
        ->execute();
      db_delete('taxonomy_term_hierarchy')
        ->condition('tid', $tid)
        ->execute();
      db_delete('taxonomy_term_synonym')
        ->condition('tid', $tid)
        ->execute();
Dries Buytaert's avatar
 
Dries Buytaert committed

      field_attach_delete('taxonomy_term', $term);
      module_invoke_all('taxonomy_term_delete', $term);
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
  cache_clear_all();
Dries Buytaert's avatar
 
Dries Buytaert committed
}

/**
 * Clear all static cache variables for terms..
 */
function taxonomy_terms_static_reset() {
  drupal_static_reset('taxonomy_term_count_nodes');
  drupal_static_reset('taxonomy_get_tree');
  drupal_static_reset('taxonomy_get_synonym_root');
  entity_get_controller('taxonomy_term')->resetCache();
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Generate a form element for selecting terms from a vocabulary.
 *
 * @param $vid
 *   The vocabulary ID to generate a form element for
 * @param $value
 *   The existing value of the term(s) in this vocabulary to use by default.
 * @param $help
 *   Optional help text to use for the form element. If specified, this value
 *   MUST be properly sanitized and filtered (e.g. with filter_xss_admin() or
 *   check_plain() if it is user-supplied) to prevent XSS vulnerabilities. If
 *   omitted, the help text stored with the vocaulary (if any) will be used.
 * @return
 *   An array describing a form element to select terms for a vocabulary.
 *
 * @see _taxonomy_term_select()
 * @see filter_xss_admin()
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function taxonomy_form($vid, $value = 0, $help = NULL) {
  $vocabulary = taxonomy_vocabulary_load($vid);
  $help = ($help) ? $help : filter_xss_admin($vocabulary->help);
Dries Buytaert's avatar
 
Dries Buytaert committed

  return _taxonomy_term_select(check_plain($vocabulary->name), $value, $vid, $help, $blank);
Kjartan Mannes's avatar
Kjartan Mannes committed
}
Dries Buytaert's avatar
Dries Buytaert committed

 * Generate a set of options for selecting a term from all vocabularies.
  $vocabularies = taxonomy_get_vocabularies();
  $options = array();
  foreach ($vocabularies as $vid => $vocabulary) {
    $tree = taxonomy_get_tree($vid);
        $options[$vocabulary->name][$term->tid] = str_repeat('-', $term->depth) . $term->name;
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Return an array of all vocabulary objects.
 *
 * @param $type
 *   If set, return only those vocabularies associated with this node type.
 */
function taxonomy_get_vocabularies() {
  return taxonomy_vocabulary_load_multiple(FALSE, array());
Kjartan Mannes's avatar
Kjartan Mannes committed
}
Dries Buytaert's avatar
Dries Buytaert committed

/**
 * Get names for all taxonomy vocabularies.
 *
 * @return
 *   An array of vocabulary ids, names, machine names, keyed by machine name.
 */
function taxonomy_vocabulary_get_names() {
  $names = db_query('SELECT name, machine_name, vid FROM {taxonomy_vocabulary}')->fetchAllAssoc('machine_name');
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Find all parents of a given term ID.
 */
function taxonomy_get_parents($tid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
  if ($tid) {
    $query = db_select('taxonomy_term_data', 't');
    $query->join('taxonomy_term_hierarchy', 'h', 'h.parent = t.tid');
    $query->addTag('term_access');

    $result = $query
      ->fields('t')
      ->condition('h.tid', $tid)
      ->orderBy('weight')
      ->orderBy('name')
      ->execute();
Kjartan Mannes's avatar
Kjartan Mannes committed
    $parents = array();
Kjartan Mannes's avatar
Kjartan Mannes committed
      $parents[$parent->$key] = $parent;
Dries Buytaert's avatar
 
Dries Buytaert committed
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
    return $parents;
Dries Buytaert's avatar
Dries Buytaert committed
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
  else {
    return array();
  }
}
Dries Buytaert's avatar
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Find all ancestors of a given term ID.
 */
function taxonomy_get_parents_all($tid) {
Dries Buytaert's avatar
 
Dries Buytaert committed
  $parents = array();
  if ($term = taxonomy_term_load($tid)) {
    $parents[] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
    $n = 0;
    while ($parent = taxonomy_get_parents($parents[$n]->tid)) {
      $parents = array_merge($parents, $parent);
      $n++;
    }
  }
  return $parents;
}

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Find all children of a term ID.
 */
function taxonomy_get_children($tid, $vid = 0, $key = 'tid') {
  $query = db_select('taxonomy_term_data', 't');
  $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
  $query->addTag('term_access');

  $query
    ->fields('t')
    ->condition('parent', $tid)
    ->orderBy('weight')
    ->orderBy('name');
Kjartan Mannes's avatar
Kjartan Mannes committed
  if ($vid) {
Kjartan Mannes's avatar
Kjartan Mannes committed
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
  $children = array();
Kjartan Mannes's avatar
Kjartan Mannes committed
    $children[$term->$key] = $term;
  }
  return $children;
}
Dries Buytaert's avatar
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Create a hierarchical representation of a vocabulary.
 *
 * @param $vid
 *   Which vocabulary to generate the tree for.
 * @param $parent
 *   The term ID under which to generate the tree. If 0, generate the tree
 *   for the entire vocabulary.
Dries Buytaert's avatar
 
Dries Buytaert committed
 * @param $max_depth
 *   The number of levels of the tree to return. Leave NULL to return all levels.
Dries Buytaert's avatar
 
Dries Buytaert committed
 * @return
 *   An array of all term objects in the tree. Each term object is extended
 *   to have "depth" and "parents" attributes in addition to its normal ones.
 *   Results are statically cached.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $depth = -1) {
  $children = &drupal_static(__FUNCTION__, array());
  $parents = &drupal_static(__FUNCTION__ . 'parents', array());
  $terms = &drupal_static(__FUNCTION__ . 'terms', array());
Kjartan Mannes's avatar
Kjartan Mannes committed
  $depth++;
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
  // 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();
    $parents[$vid] = array();
    $terms[$vid] = array();
Dries Buytaert's avatar
 
Dries Buytaert committed

    $query = db_select('taxonomy_term_data', 't');
    $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
    $query->addTag('term_access');

    $result = $query
      ->fields('t')
      ->fields('h', array('parent'))
      ->condition('t.vid', $vid)
      ->orderBy('weight')
      ->orderBy('name')
      ->execute();
    foreach ($result as $term) {
Dries Buytaert's avatar
 
Dries Buytaert committed
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
Dries Buytaert's avatar
Dries Buytaert committed
    }
  }
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
  $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
  if ($max_depth > $depth && !empty($children[$vid][$parent])) {
Dries Buytaert's avatar
Dries Buytaert committed
    foreach ($children[$vid][$parent] as $child) {
      $term = 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 (!empty($children[$vid][$child])) {
        $tree = array_merge($tree, taxonomy_get_tree($vid, $child, $max_depth, $depth));
Dries Buytaert's avatar
 
Dries Buytaert committed
      }
Dries Buytaert's avatar
 
Dries Buytaert committed
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
  }
Dries Buytaert's avatar
 
Dries Buytaert committed

Kjartan Mannes's avatar
Kjartan Mannes committed
}
Dries Buytaert's avatar
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Return an array of synonyms of the given term ID.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
function taxonomy_get_synonyms($tid) {
  if ($tid) {
    $synonyms = array();
    return db_query('SELECT name FROM {taxonomy_term_synonym} WHERE tid = :tid', array(':tid' => $tid))->fetchCol();
Dries Buytaert's avatar
Dries Buytaert committed
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
  else {
    return array();
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
}
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Return the term object that has the given string as a synonym.
 *
 * @param $synonym
 *   The string to compare against.
 * @return
 *   A term object, or FALSE if no matching term is found.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function taxonomy_get_synonym_root($synonym) {
  $synonyms = &drupal_static(__FUNCTION__, array());
    $synonyms[$synonym] = db_query("SELECT * FROM {taxonomy_term_synonym} s, {taxonomy_term_data} t WHERE t.tid = s.tid AND s.name = :name", array(':name' => $synonym))->fetch();
Kjartan Mannes's avatar
Kjartan Mannes committed
}
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
Dries Buytaert's avatar
 
Dries Buytaert committed
 * Try to map a string to an existing term, as for glossary use.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
Dries Buytaert's avatar
 
Dries Buytaert committed
 * Provides a case-insensitive and trimmed mapping, to maximize the
 * likelihood of a successful match.
 *
 * @param name
 *   Name of the term to search for.
 *
 * @return
 *   An array of matching term objects.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function taxonomy_get_term_by_name($name) {
  return taxonomy_term_load_multiple(array(), array('name' => trim($name)));
Dries Buytaert's avatar
 
Dries Buytaert committed
}

/**
 * Controller class for taxonomy terms.
 *
 * This extends the DrupalDefaultEntityController class. Only alteration is
 * that we match the condition on term name case-independently.
 */
class TaxonomyTermController extends DrupalDefaultEntityController {
  protected $type;
  public function load($ids = array(), $conditions = array()) {
    if (isset($conditions['type'])) {
      $this->type = $conditions['type'];
      unset($conditions['type']);
  protected function buildQuery() {
    parent::buildQuery();
    // When name is passed as a condition use LIKE.
    if (isset($this->conditions['name'])) {
      $conditions = &$this->query->conditions();
      foreach ($conditions as $key => $condition) {
        if ($condition['field'] == 'base.name') {
          $conditions[$key]['operator'] = 'LIKE';
        }
    // Add the machine name field from the {taxonomy_vocabulary} table.
    $this->query->innerJoin('taxonomy_vocabulary', 'v', 'base.vid = v.vid');
    $this->query->addField('v', 'machine_name', 'vocabulary_machine_name');
  }

  protected function cacheGet($ids, $conditions = array()) {
    $terms = parent::cacheGet($ids, $conditions);
    // Name matching is case insensitive, note that with some collations
    // LOWER() and drupal_strtolower() may return different results.
    foreach ($terms as $term) {
      $term_values = (array) $term;
      if (isset($this->conditions['name']) && drupal_strtolower($this->conditions['name'] != drupal_strtolower($term_values['name']))) {
        unset($terms[$term->tid]);
      }
/**
 * Controller class for taxonomy vocabularies.
 *
 * This extends the DrupalDefaultEntityController class, adding required
 * special handling for taxonomy vocabulary objects.
 */
class TaxonomyVocabularyController extends DrupalDefaultEntityController {
  protected function buildQuery() {
    parent::buildQuery();
    $this->query->orderBy('base.weight');
    $this->query->orderBy('base.name');
  }
  protected function attachLoad(&$records) {
    foreach ($records as $record) {
      // If no node types are associated with a vocabulary, the LEFT JOIN will
      // return a NULL value for type.
      $queried_vocabularies[$record->vid] = $record;
    }
    $records = $queried_vocabularies;
    parent::attachLoad($records);
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
}
Dries Buytaert's avatar
Dries Buytaert committed

/**
 * Load multiple taxonomy terms based on certain conditions.
 *
 * This function should be used whenever you need to load more than one term
 * from the database. Terms are loaded into memory and will not require
 * database access if loaded again during the same page request.
 *
 * @param $tids
 *  An array of taxonomy term IDs.
 * @param $conditions
 *  An array of conditions to add to the query.
 *
 * @return
 *  An array of term objects, indexed by tid.
 */
function taxonomy_term_load_multiple($tids = array(), $conditions = array()) {
  return entity_load('taxonomy_term', $tids, $conditions);
}
/**
 * Load multiple taxonomy vocabularies based on certain conditions.
 *
 * This function should be used whenever you need to load more than one
 * vocabulary from the database. Terms are loaded into memory and will not
 * require database access if loaded again during the same page request.
 *
 * @see entity_load()
 *
 * @param $vids
 *  An array of taxonomy vocabulary IDs, or FALSE to load all vocabularies.
 * @param $conditions
 *  An array of conditions to add to the query.
 *
 * @return
 *  An array of vocabulary objects, indexed by vid.
 */
function taxonomy_vocabulary_load_multiple($vids = array(), $conditions = array()) {
  return entity_load('taxonomy_vocabulary', $vids, $conditions);
}
/**
 * Return the vocabulary object matching a vocabulary ID.
 *
 * @param $vid
 *   The vocabulary's ID.
 *
 * @return
 *   The vocabulary object with all of its metadata, if exists, FALSE otherwise.
 *   Results are statically cached.
 */
function taxonomy_vocabulary_load($vid) {
  return reset(taxonomy_vocabulary_load_multiple(array($vid)));
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Return the term object matching a term ID.
 *   A term object. Results are statically cached.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
  $term = taxonomy_term_load_multiple(array($tid), array());
  return $term ? $term[$tid] : FALSE;
/**
 * Create a select form element for a given taxonomy vocabulary.
 *
 * NOTE: This function expects input that has already been sanitized and is
 * safe for display. Callers must properly sanitize the $title and
 * $description arguments to prevent XSS vulnerabilities.
 *
 * @param $title
 *   The title of the vocabulary. This MUST be sanitized by the caller.
 * @param $value
 *   The currently selected terms from this vocabulary, if any.
 * @param $vocabulary_id
 *   The vocabulary ID to build the form element for.
 * @param $description
 *   Help text for the form element. This MUST be sanitized by the caller.
 * @param $multiple
 *   Boolean to control if the form should use a single or multiple select.
 * @param $blank
 *   Optional form choice to use when no value has been selected.
 * @param $exclude
 *   Optional array of term ids to exclude in the selector.
 * @return
 *   A FAPI form array to select terms from the given vocabulary.
 *
 * @see taxonomy_form()
 * @see taxonomy_form_term()
 */
function _taxonomy_term_select($title, $value, $vocabulary_id, $description, $blank, $exclude = array()) {
Dries Buytaert's avatar
 
Dries Buytaert committed
  $tree = taxonomy_get_tree($vocabulary_id);
Dries Buytaert's avatar
Dries Buytaert committed
  if ($blank) {