[ Index ]
 

Code source de Drupal 5.3

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/modules/taxonomy/ -> taxonomy.module (source)

   1  <?php
   2  // $Id: taxonomy.module,v 1.330.2.11 2007/09/12 07:49:36 drumm Exp $
   3  
   4  /**
   5   * @file
   6   * Enables the organization of content into categories.
   7   */
   8  
   9  /**
  10   * Implementation of hook_perm().
  11   */
  12  function taxonomy_perm() {
  13    return array('administer taxonomy');
  14  }
  15  
  16  /**
  17   * Implementation of hook_link().
  18   *
  19   * This hook is extended with $type = 'taxonomy terms' to allow themes to
  20   * print lists of terms associated with a node. Themes can print taxonomy
  21   * links with:
  22   *
  23   * if (module_exists('taxonomy')) {
  24   *   $terms = taxonomy_link('taxonomy terms', $node);
  25   *   print theme('links', $terms);
  26   * }
  27   */
  28  function taxonomy_link($type, $node = NULL) {
  29    if ($type == 'taxonomy terms' && $node != NULL) {
  30      $links = array();
  31      if (array_key_exists('taxonomy', $node)) {
  32        foreach ($node->taxonomy as $term) {
  33          $links['taxonomy_term_'. $term->tid] = array(
  34            'title' => $term->name,
  35            'href' => taxonomy_term_path($term),
  36            'attributes' => array('rel' => 'tag', 'title' => strip_tags($term->description))
  37          );
  38        }
  39      }
  40  
  41      // We call this hook again because some modules and themes call taxonomy_link('taxonomy terms') directly
  42      foreach (module_implements('link_alter') as $module) {
  43        $function = $module .'_link_alter';
  44        $function($node, $links);
  45      }
  46  
  47      return $links;
  48    }
  49  }
  50  
  51  /**
  52   * For vocabularies not maintained by taxonomy.module, give the maintaining
  53   * module a chance to provide a path for terms in that vocabulary.
  54   *
  55   * @param $term
  56   *   A term object.
  57   * @return
  58   *   An internal Drupal path.
  59   */
  60  
  61  function taxonomy_term_path($term) {
  62    $vocabulary = taxonomy_get_vocabulary($term->vid);
  63    if ($vocabulary->module != 'taxonomy' && $path = module_invoke($vocabulary->module, 'term_path', $term)) {
  64      return $path;
  65    }
  66    return 'taxonomy/term/'. $term->tid;
  67  }
  68  
  69  /**
  70   * Implementation of hook_menu().
  71   */
  72  function taxonomy_menu($may_cache) {
  73    $items = array();
  74  
  75    if ($may_cache) {
  76      $items[] = array('path' => 'admin/content/taxonomy',
  77        'title' => t('Categories'),
  78        'description' => t('Create vocabularies and terms to categorize your content.'),
  79        'callback' => 'taxonomy_overview_vocabularies',
  80        'access' => user_access('administer taxonomy'));
  81  
  82      $items[] = array('path' => 'admin/content/taxonomy/list',
  83        'title' => t('List'),
  84        'type' => MENU_DEFAULT_LOCAL_TASK,
  85        'weight' => -10);
  86  
  87      $items[] = array('path' => 'admin/content/taxonomy/add/vocabulary',
  88        'title' => t('Add vocabulary'),
  89        'callback' => 'drupal_get_form',
  90        'callback arguments' => array('taxonomy_form_vocabulary'),
  91        'access' => user_access('administer taxonomy'),
  92        'type' => MENU_LOCAL_TASK);
  93  
  94      $items[] = array('path' => 'admin/content/taxonomy/edit/vocabulary',
  95        'title' => t('Edit vocabulary'),
  96        'callback' => 'taxonomy_admin_vocabulary_edit',
  97        'access' => user_access('administer taxonomy'),
  98        'type' => MENU_CALLBACK);
  99  
 100      $items[] = array('path' => 'admin/content/taxonomy/edit/term',
 101        'title' => t('Edit term'),
 102        'callback' => 'taxonomy_admin_term_edit',
 103        'access' => user_access('administer taxonomy'),
 104        'type' => MENU_CALLBACK);
 105  
 106      $items[] = array('path' => 'taxonomy/term',
 107        'title' => t('Taxonomy term'),
 108        'callback' => 'taxonomy_term_page',
 109        'access' => user_access('access content'),
 110        'type' => MENU_CALLBACK);
 111  
 112      $items[] = array('path' => 'taxonomy/autocomplete',
 113        'title' => t('Autocomplete taxonomy'),
 114        'callback' => 'taxonomy_autocomplete',
 115        'access' => user_access('access content'),
 116        'type' => MENU_CALLBACK);
 117    }
 118    else {
 119      if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'taxonomy' && is_numeric(arg(3))) {
 120        $vid = arg(3);
 121        $items[] = array('path' => 'admin/content/taxonomy/'. $vid,
 122          'title' => t('List terms'),
 123          'callback' => 'taxonomy_overview_terms',
 124          'callback arguments' => array($vid),
 125          'access' => user_access('administer taxonomy'),
 126          'type' => MENU_CALLBACK);
 127  
 128        $items[] = array('path' => 'admin/content/taxonomy/'. $vid .'/list',
 129          'title' => t('List'),
 130          'type' => MENU_DEFAULT_LOCAL_TASK,
 131          'weight' => -10);
 132  
 133        $items[] = array('path' => 'admin/content/taxonomy/'. $vid .'/add/term',
 134          'title' => t('Add term'),
 135          'callback' => 'drupal_get_form',
 136          'callback arguments' => array('taxonomy_form_term', $vid),
 137          'access' => user_access('administer taxonomy'),
 138          'type' => MENU_LOCAL_TASK);
 139      }
 140    }
 141  
 142    return $items;
 143  }
 144  
 145  /**
 146   * List and manage vocabularies.
 147   */
 148  function taxonomy_overview_vocabularies() {
 149    $vocabularies = taxonomy_get_vocabularies();
 150    $rows = array();
 151    foreach ($vocabularies as $vocabulary) {
 152      $types = array();
 153      foreach ($vocabulary->nodes as $type) {
 154        $node_type = node_get_types('name', $type);
 155        $types[] = $node_type ? check_plain($node_type) : check_plain($type);
 156      }
 157      $rows[] = array('name' => check_plain($vocabulary->name),
 158        'type' => implode(', ', $types),
 159        'edit' => l(t('edit vocabulary'), "admin/content/taxonomy/edit/vocabulary/$vocabulary->vid"),
 160        'list' => l(t('list terms'), "admin/content/taxonomy/$vocabulary->vid"),
 161        'add' => l(t('add terms'), "admin/content/taxonomy/$vocabulary->vid/add/term")
 162      );
 163    }
 164    if (empty($rows)) {
 165      $rows[] = array(array('data' => t('No categories available.'), 'colspan' => '5'));
 166    }
 167    $header = array(t('Name'), t('Type'), array('data' => t('Operations'), 'colspan' => '3'));
 168  
 169    return theme('table', $header, $rows, array('id' => 'taxonomy'));
 170  }
 171  
 172  /**
 173   * Display a tree of all the terms in a vocabulary, with options to edit
 174   * each one.
 175   */
 176  function taxonomy_overview_terms($vid) {
 177    $destination = drupal_get_destination();
 178  
 179    $header = array(t('Name'), t('Operations'));
 180    $vocabulary = taxonomy_get_vocabulary($vid);
 181    if (!$vocabulary) {
 182      return drupal_not_found();
 183    }
 184  
 185    drupal_set_title(check_plain($vocabulary->name));
 186    $start_from      = $_GET['page'] ? $_GET['page'] : 0;
 187    $total_entries   = 0;  // total count for pager
 188    $page_increment  = 25; // number of tids per page
 189    $displayed_count = 0;  // number of tids shown
 190  
 191    if ($vocabulary->tags) {
 192      // We are not calling taxonomy_get_tree because that might fail with a big
 193      // number of tags in the freetagging vocabulary.
 194      $results = pager_query(db_rewrite_sql('SELECT t.*, h.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'), $page_increment, 0, NULL, $vid);
 195      while ($term = db_fetch_object($results)) {
 196        $rows[] = array(
 197          l($term->name, "taxonomy/term/$term->tid"),
 198          l(t('edit'), "admin/content/taxonomy/edit/term/$term->tid", array(), $destination),
 199        );
 200      }
 201    }
 202    else {
 203      $tree = taxonomy_get_tree($vocabulary->vid);
 204      foreach ($tree as $term) {
 205        $total_entries++; // we're counting all-totals, not displayed
 206        if (($start_from && ($start_from * $page_increment) >= $total_entries) || ($displayed_count == $page_increment)) {
 207          continue;
 208        }
 209        $rows[] = array(str_repeat('--', $term->depth) .' '. l($term->name, "taxonomy/term/$term->tid"), l(t('edit'), "admin/content/taxonomy/edit/term/$term->tid", array(), $destination));
 210        $displayed_count++; // we're counting tids displayed
 211      }
 212  
 213      if (!$rows) {
 214        $rows[] = array(array('data' => t('No terms available.'), 'colspan' => '2'));
 215      }
 216  
 217      $GLOBALS['pager_page_array'][] = $start_from;  // FIXME
 218      $GLOBALS['pager_total'][] = intval($total_entries / $page_increment) + 1; // FIXME
 219    }
 220  
 221    $output .= theme('table', $header, $rows, array('id' => 'taxonomy'));
 222    if ($vocabulary->tags || $total_entries >= $page_increment) {
 223      $output .= theme('pager', NULL, $page_increment);
 224    }
 225  
 226    return $output;
 227  }
 228  
 229  /**
 230   * Display form for adding and editing vocabularies.
 231   */
 232  function taxonomy_form_vocabulary($edit = array()) {
 233    $form['name'] = array('#type' => 'textfield',
 234      '#title' => t('Vocabulary name'),
 235      '#default_value' => $edit['name'],
 236      '#maxlength' => 255,
 237      '#description' => t('The name for this vocabulary. Example: "Topic".'),
 238      '#required' => TRUE,
 239    );
 240    $form['description'] = array('#type' => 'textarea',
 241      '#title' => t('Description'),
 242      '#default_value' => $edit['description'],
 243      '#description' => t('Description of the vocabulary; can be used by modules.'),
 244    );
 245    $form['help'] = array('#type' => 'textfield',
 246      '#title' => t('Help text'),
 247      '#maxlength' => 255,
 248      '#default_value' => $edit['help'],
 249      '#description' => t('Instructions to present to the user when choosing a term.'),
 250    );
 251    $form['nodes'] = array('#type' => 'checkboxes',
 252      '#title' => t('Types'),
 253      '#default_value' => $edit['nodes'],
 254      '#options' => array_map('check_plain', node_get_types('names')),
 255      '#description' => t('A list of node types you want to associate with this vocabulary.'),
 256      '#required' => TRUE,
 257    );
 258    $form['hierarchy'] = array('#type' => 'radios',
 259      '#title' => t('Hierarchy'),
 260      '#default_value' => $edit['hierarchy'],
 261      '#options' => array(t('Disabled'), t('Single'), t('Multiple')),
 262      '#description' => t('Allows <a href="@help-url">a tree-like hierarchy</a> between terms of this vocabulary.', array('@help-url' => url('admin/help/taxonomy', NULL, NULL, 'hierarchy'))),
 263    );
 264    $form['relations'] = array('#type' => 'checkbox',
 265      '#title' => t('Related terms'),
 266      '#default_value' => $edit['relations'],
 267      '#description' => t('Allows <a href="@help-url">related terms</a> in this vocabulary.', array('@help-url' => url('admin/help/taxonomy', NULL, NULL, 'related-terms'))),
 268    );
 269    $form['tags'] = array('#type' => 'checkbox',
 270      '#title' => t('Free tagging'),
 271      '#default_value' => $edit['tags'],
 272      '#description' => t('Content is categorized by typing terms instead of choosing from a list.'),
 273    );
 274    $form['multiple'] = array('#type' => 'checkbox',
 275      '#title' => t('Multiple select'),
 276      '#default_value' => $edit['multiple'],
 277      '#description' => t('Allows nodes to have more than one term from this vocabulary (always true for free tagging).'),
 278    );
 279    $form['required'] = array('#type' => 'checkbox',
 280      '#title' => t('Required'),
 281      '#default_value' => $edit['required'],
 282      '#description' => t('If enabled, every node <strong>must</strong> have at least one term in this vocabulary.'),
 283    );
 284    $form['weight'] = array('#type' => 'weight',
 285      '#title' => t('Weight'),
 286      '#default_value' => $edit['weight'],
 287      '#description' => t('In listings, the heavier vocabularies will sink and the lighter vocabularies will be positioned nearer the top.'),
 288    );
 289  
 290    $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
 291    if ($edit['vid']) {
 292      $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
 293      $form['vid'] = array('#type' => 'value', '#value' => $edit['vid']);
 294      $form['module'] = array('#type' => 'value', '#value' => $edit['module']);
 295    }
 296    return $form;
 297  }
 298  
 299  /**
 300   * Accept the form submission for a vocabulary and save the results.
 301   */
 302  function taxonomy_form_vocabulary_submit($form_id, $form_values) {
 303    // Fix up the nodes array to remove unchecked nodes.
 304    $form_values['nodes'] = array_filter($form_values['nodes']);
 305    switch (taxonomy_save_vocabulary($form_values)) {
 306      case SAVED_NEW:
 307        drupal_set_message(t('Created new vocabulary %name.', array('%name' => $form_values['name'])));
 308        watchdog('taxonomy', t('Created new vocabulary %name.', array('%name' => $form_values['name'])), WATCHDOG_NOTICE, l(t('edit'), 'admin/content/taxonomy/edit/vocabulary/'. $form_values['vid']));
 309        break;
 310      case SAVED_UPDATED:
 311        drupal_set_message(t('Updated vocabulary %name.', array('%name' => $form_values['name'])));
 312        watchdog('taxonomy', t('Updated vocabulary %name.', array('%name' => $form_values['name'])), WATCHDOG_NOTICE, l(t('edit'), 'admin/content/taxonomy/edit/vocabulary/'. $form_values['vid']));
 313        break;
 314    }
 315    return 'admin/content/taxonomy';
 316  }
 317  
 318  function taxonomy_save_vocabulary(&$edit) {
 319    $edit['nodes'] = empty($edit['nodes']) ? array() : $edit['nodes'];
 320  
 321    if ($edit['vid'] && $edit['name']) {
 322      db_query("UPDATE {vocabulary} SET name = '%s', description = '%s', help = '%s', multiple = %d, required = %d, hierarchy = %d, relations = %d, tags = %d, weight = %d, module = '%s' WHERE vid = %d", $edit['name'], $edit['description'], $edit['help'], $edit['multiple'], $edit['required'], $edit['hierarchy'], $edit['relations'], $edit['tags'], $edit['weight'], isset($edit['module']) ? $edit['module'] : 'taxonomy', $edit['vid']);
 323      db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d", $edit['vid']);
 324      foreach ($edit['nodes'] as $type => $selected) {
 325        db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
 326      }
 327      module_invoke_all('taxonomy', 'update', 'vocabulary', $edit);
 328      $status = SAVED_UPDATED;
 329    }
 330    else if ($edit['vid']) {
 331      $status = taxonomy_del_vocabulary($edit['vid']);
 332    }
 333    else {
 334      $edit['vid'] = db_next_id('{vocabulary}_vid');
 335      db_query("INSERT INTO {vocabulary} (vid, name, description, help, multiple, required, hierarchy, relations, tags, weight, module) VALUES (%d, '%s', '%s', '%s', %d, %d, %d, %d, %d, %d, '%s')", $edit['vid'], $edit['name'], $edit['description'], $edit['help'], $edit['multiple'], $edit['required'], $edit['hierarchy'], $edit['relations'], $edit['tags'], $edit['weight'], isset($edit['module']) ? $edit['module'] : 'taxonomy');
 336      foreach ($edit['nodes'] as $type => $selected) {
 337        db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
 338      }
 339      module_invoke_all('taxonomy', 'insert', 'vocabulary', $edit);
 340      $status = SAVED_NEW;
 341    }
 342  
 343    cache_clear_all();
 344  
 345    return $status;
 346  }
 347  
 348  /**
 349   * Delete a vocabulary.
 350   *
 351   * @param $vid
 352   *   A vocabulary ID.
 353   * @return
 354   *   Constant indicating items were deleted.
 355   */
 356  function taxonomy_del_vocabulary($vid) {
 357    $vocabulary = (array) taxonomy_get_vocabulary($vid);
 358  
 359    db_query('DELETE FROM {vocabulary} WHERE vid = %d', $vid);
 360    db_query('DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid);
 361    $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
 362    while ($term = db_fetch_object($result)) {
 363      taxonomy_del_term($term->tid);
 364    }
 365  
 366    module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
 367  
 368    cache_clear_all();
 369  
 370    return SAVED_DELETED;
 371  }
 372  
 373  function taxonomy_vocabulary_confirm_delete($vid) {
 374    $vocabulary = taxonomy_get_vocabulary($vid);
 375  
 376    $form['type'] = array('#type' => 'value', '#value' => 'vocabulary');
 377    $form['vid'] = array('#type' => 'value', '#value' => $vid);
 378    $form['name'] = array('#type' => 'value', '#value' => $vocabulary->name);
 379    return confirm_form($form,
 380                    t('Are you sure you want to delete the vocabulary %title?',
 381                    array('%title' => $vocabulary->name)),
 382                    'admin/content/taxonomy',
 383                    t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'),
 384                    t('Delete'),
 385                    t('Cancel'));
 386  }
 387  
 388  function taxonomy_vocabulary_confirm_delete_submit($form_id, $form_values) {
 389    $status = taxonomy_del_vocabulary($form_values['vid']);
 390    drupal_set_message(t('Deleted vocabulary %name.', array('%name' => $form_values['name'])));
 391    watchdog('taxonomy', t('Deleted vocabulary %name.', array('%name' => $form_values['name'])), WATCHDOG_NOTICE);
 392    return 'admin/content/taxonomy';
 393  }
 394  
 395  function taxonomy_form_term($vocabulary_id, $edit = array()) {
 396    $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
 397    drupal_set_title(check_plain($vocabulary->name));
 398  
 399    $form['name'] = array(
 400      '#type' => 'textfield',
 401      '#title' => t('Term name'),
 402      '#default_value' => $edit['name'],
 403      '#maxlength' => 255,
 404      '#description' => t('The name of this term.'),
 405      '#required' => TRUE);
 406  
 407    $form['description'] = array(
 408      '#type' => 'textarea',
 409      '#title' => t('Description'),
 410      '#default_value' => $edit['description'],
 411      '#description' => t('A description of the term.'));
 412  
 413    if ($vocabulary->hierarchy) {
 414      $parent = array_keys(taxonomy_get_parents($edit['tid']));
 415      $children = taxonomy_get_tree($vocabulary_id, $edit['tid']);
 416  
 417      // A term can't be the child of itself, nor of its children.
 418      foreach ($children as $child) {
 419        $exclude[] = $child->tid;
 420      }
 421      $exclude[] = $edit['tid'];
 422  
 423      if ($vocabulary->hierarchy == 1) {
 424        $form['parent'] = _taxonomy_term_select(t('Parent'), 'parent', $parent, $vocabulary_id, l(t('Parent term'), 'admin/help/taxonomy', NULL, NULL, 'parent') .'.', 0, '<'. t('root') .'>', $exclude);
 425      }
 426      elseif ($vocabulary->hierarchy == 2) {
 427        $form['parent'] = _taxonomy_term_select(t('Parents'), 'parent', $parent, $vocabulary_id, l(t('Parent terms'), 'admin/help/taxonomy', NULL, NULL, 'parent') .'.', 1, '<'. t('root') .'>', $exclude);
 428      }
 429    }
 430  
 431    if ($vocabulary->relations) {
 432      $form['relations'] = _taxonomy_term_select(t('Related terms'), 'relations', array_keys(taxonomy_get_related($edit['tid'])), $vocabulary_id, NULL, 1, '<'. t('none') .'>', array($edit['tid']));
 433    }
 434  
 435    $form['synonyms'] = array(
 436      '#type' => 'textarea',
 437      '#title' => t('Synonyms'),
 438      '#default_value' => implode("\n", taxonomy_get_synonyms($edit['tid'])),
 439      '#description' => t('<a href="@help-url">Synonyms</a> of this term, one synonym per line.', array('@help-url' => url('admin/help/taxonomy', NULL, NULL, 'synonyms'))));
 440    $form['weight'] = array(
 441      '#type' => 'weight',
 442      '#title' => t('Weight'),
 443      '#default_value' => $edit['weight'],
 444      '#description' => t('In listings, the heavier terms will sink and the lighter terms will be positioned nearer the top.'));
 445    $form['vid'] = array(
 446      '#type' => 'value',
 447      '#value' => $vocabulary->vid);
 448    $form['submit'] = array(
 449      '#type' => 'submit',
 450      '#value' => t('Submit'));
 451  
 452    if ($edit['tid']) {
 453      $form['delete'] = array(
 454        '#type' => 'submit',
 455        '#value' => t('Delete'));
 456      $form['tid'] = array(
 457        '#type' => 'value',
 458        '#value' => $edit['tid']);
 459    }
 460    else {
 461      $form['destination'] = array('#type' => 'hidden', '#value' => $_GET['q']);
 462    }
 463  
 464    return $form;
 465  }
 466  
 467  /**
 468   * Accept the form submission for a taxonomy term and save the result.
 469   */
 470  function taxonomy_form_term_submit($form_id, $form_values) {
 471    switch (taxonomy_save_term($form_values)) {
 472      case SAVED_NEW:
 473        drupal_set_message(t('Created new term %term.', array('%term' => $form_values['name'])));
 474        watchdog('taxonomy', t('Created new term %term.', array('%term' => $form_values['name'])), WATCHDOG_NOTICE, l(t('edit'), 'admin/content/taxonomy/edit/term/'. $form_values['tid']));
 475        break;
 476      case SAVED_UPDATED:
 477        drupal_set_message(t('Updated term %term.', array('%term' => $form_values['name'])));
 478        watchdog('taxonomy', t('Updated term %term.', array('%term' => $form_values['name'])), WATCHDOG_NOTICE, l(t('edit'), 'admin/content/taxonomy/edit/term/'. $form_values['tid']));
 479        break;
 480    }
 481    return 'admin/content/taxonomy';
 482  }
 483  
 484  /**
 485   * Helper function for taxonomy_form_term_submit().
 486   *
 487   * @param $form_values
 488   * @return
 489   *   Status constant indicating if term was inserted or updated.
 490   */
 491  function taxonomy_save_term(&$form_values) {
 492    if ($form_values['tid'] && $form_values['name']) {
 493      db_query("UPDATE {term_data} SET name = '%s', description = '%s', weight = %d WHERE tid = %d", $form_values['name'], $form_values['description'], $form_values['weight'], $form_values['tid']);
 494      $hook = 'update';
 495      $status = SAVED_UPDATED;
 496    }
 497    else if ($form_values['tid']) {
 498      return taxonomy_del_term($form_values['tid']);
 499    }
 500    else {
 501      $form_values['tid'] = db_next_id('{term_data}_tid');
 502      db_query("INSERT INTO {term_data} (tid, name, description, vid, weight) VALUES (%d, '%s', '%s', %d, %d)", $form_values['tid'], $form_values['name'], $form_values['description'], $form_values['vid'], $form_values['weight']);
 503      $hook = 'insert';
 504      $status = SAVED_NEW;
 505    }
 506  
 507    db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $form_values['tid'], $form_values['tid']);
 508    if ($form_values['relations']) {
 509      foreach ($form_values['relations'] as $related_id) {
 510        if ($related_id != 0) {
 511          db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $form_values['tid'], $related_id);
 512        }
 513      }
 514    }
 515  
 516    db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $form_values['tid']);
 517    if (!isset($form_values['parent']) || empty($form_values['parent'])) {
 518      $form_values['parent'] = array(0);
 519    }
 520    if (is_array($form_values['parent'])) {
 521      foreach ($form_values['parent'] as $parent) {
 522        if (is_array($parent)) {
 523          foreach ($parent as $tid) {
 524            db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $tid);
 525          }
 526        }
 527        else {
 528          db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $parent);
 529        }
 530      }
 531    }
 532    else {
 533      db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $form_values['parent']);
 534    }
 535  
 536    db_query('DELETE FROM {term_synonym} WHERE tid = %d', $form_values['tid']);
 537    if ($form_values['synonyms']) {
 538      foreach (explode ("\n", str_replace("\r", '', $form_values['synonyms'])) as $synonym) {
 539        if ($synonym) {
 540          db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $form_values['tid'], chop($synonym));
 541        }
 542      }
 543    }
 544  
 545    if (isset($hook)) {
 546      module_invoke_all('taxonomy', $hook, 'term', $form_values);
 547    }
 548  
 549    cache_clear_all();
 550  
 551    return $status;
 552  }
 553  
 554  /**
 555   * Delete a term.
 556   *
 557   * @param $tid
 558   *   The term ID.
 559   * @return
 560   *   Status constant indicating deletion.
 561   */
 562  function taxonomy_del_term($tid) {
 563    $tids = array($tid);
 564    while ($tids) {
 565      $children_tids = $orphans = array();
 566      foreach ($tids as $tid) {
 567        // See if any of the term's children are about to be become orphans:
 568        if ($children = taxonomy_get_children($tid)) {
 569          foreach ($children as $child) {
 570            // If the term has multiple parents, we don't delete it.
 571            $parents = taxonomy_get_parents($child->tid);
 572            if (count($parents) == 1) {
 573              $orphans[] = $child->tid;
 574            }
 575          }
 576        }
 577  
 578        $term = (array) taxonomy_get_term($tid);
 579  
 580        db_query('DELETE FROM {term_data} WHERE tid = %d', $tid);
 581        db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $tid);
 582        db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $tid, $tid);
 583        db_query('DELETE FROM {term_synonym} WHERE tid = %d', $tid);
 584        db_query('DELETE FROM {term_node} WHERE tid = %d', $tid);
 585  
 586        module_invoke_all('taxonomy', 'delete', 'term', $term);
 587      }
 588  
 589      $tids = $orphans;
 590    }
 591  
 592    cache_clear_all();
 593  
 594    return SAVED_DELETED;
 595  }
 596  
 597  function taxonomy_term_confirm_delete($tid) {
 598    $term = taxonomy_get_term($tid);
 599  
 600    $form['type'] = array('#type' => 'value', '#value' => 'term');
 601    $form['name'] = array('#type' => 'value', '#value' => $term->name);
 602    $form['tid'] = array('#type' => 'value', '#value' => $tid);
 603    return confirm_form($form,
 604                    t('Are you sure you want to delete the term %title?',
 605                    array('%title' => $term->name)),
 606                    'admin/content/taxonomy',
 607                    t('Deleting a term will delete all its children if there are any. This action cannot be undone.'),
 608                    t('Delete'),
 609                    t('Cancel'));
 610  }
 611  
 612  function taxonomy_term_confirm_delete_submit($form_id, $form_values) {
 613    taxonomy_del_term($form_values['tid']);
 614    drupal_set_message(t('Deleted term %name.', array('%name' => $form_values['name'])));
 615    watchdog('taxonomy', t('Deleted term %name.', array('%name' => $form_values['name'])), WATCHDOG_NOTICE);
 616    return 'admin/content/taxonomy';
 617  }
 618  
 619  /**
 620   * Generate a form element for selecting terms from a vocabulary.
 621   */
 622  function taxonomy_form($vid, $value = 0, $help = NULL, $name = 'taxonomy') {
 623    $vocabulary = taxonomy_get_vocabulary($vid);
 624    $help = ($help) ? $help : $vocabulary->help;
 625    if ($vocabulary->required) {
 626      $blank = 0;
 627    }
 628    else {
 629      $blank = '<'. t('none') .'>';
 630    }
 631  
 632    return _taxonomy_term_select(check_plain($vocabulary->name), $name, $value, $vid, $help, intval($vocabulary->multiple), $blank);
 633  }
 634  
 635  /**
 636   * Generate a set of options for selecting a term from all vocabularies.
 637   */
 638  function taxonomy_form_all($free_tags = 0) {
 639    $vocabularies = taxonomy_get_vocabularies();
 640    $options = array();
 641    foreach ($vocabularies as $vid => $vocabulary) {
 642      if ($vocabulary->tags && !$free_tags) { continue; }
 643      $tree = taxonomy_get_tree($vid);
 644      if ($tree && (count($tree) > 0)) {
 645        $options[$vocabulary->name] = array();
 646        foreach ($tree as $term) {
 647          $options[$vocabulary->name][$term->tid] = str_repeat('-', $term->depth) . $term->name;
 648        }
 649      }
 650    }
 651    return $options;
 652  }
 653  
 654  /**
 655   * Return an array of all vocabulary objects.
 656   *
 657   * @param $type
 658   *   If set, return only those vocabularies associated with this node type.
 659   */
 660  function taxonomy_get_vocabularies($type = NULL) {
 661    if ($type) {
 662      $result = db_query(db_rewrite_sql("SELECT v.vid, v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s' ORDER BY v.weight, v.name", 'v', 'vid'), $type);
 663    }
 664    else {
 665      $result = db_query(db_rewrite_sql('SELECT v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid ORDER BY v.weight, v.name', 'v', 'vid'));
 666    }
 667  
 668    $vocabularies = array();
 669    $node_types = array();
 670    while ($voc = db_fetch_object($result)) {
 671      $node_types[$voc->vid][] = $voc->type;
 672      unset($voc->type);
 673      $voc->nodes = $node_types[$voc->vid];
 674      $vocabularies[$voc->vid] = $voc;
 675    }
 676  
 677    return $vocabularies;
 678  }
 679  
 680  /**
 681   * Implementation of hook_form_alter().
 682   * Generate a form for selecting terms to associate with a node.
 683   */
 684  function taxonomy_form_alter($form_id, &$form) {
 685    if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
 686      $node = $form['#node'];
 687  
 688      if (!isset($node->taxonomy)) {
 689        if ($node->nid) {
 690          $terms = taxonomy_node_get_terms($node->nid);
 691        }
 692        else {
 693          $terms = array();
 694        }
 695      }
 696      else {
 697        $terms = $node->taxonomy;
 698      }
 699  
 700      $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);
 701  
 702      while ($vocabulary = db_fetch_object($c)) {
 703        if ($vocabulary->tags) {
 704          $typed_terms = array();
 705          foreach ($terms as $term) {
 706            // Extract terms belonging to the vocabulary in question.
 707            if ($term->vid == $vocabulary->vid) {
 708  
 709              // Commas and quotes in terms are special cases, so encode 'em.
 710              if (strpos($term->name, ',') !== FALSE || strpos($term->name, '"') !== FALSE) {
 711                $term->name = '"'.str_replace('"', '""', $term->name).'"';
 712              }
 713  
 714              $typed_terms[] = $term->name;
 715            }
 716          }
 717          $typed_string = implode(', ', $typed_terms) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL);
 718  
 719          if ($vocabulary->help) {
 720            $help = $vocabulary->help;
 721          }
 722          else {
 723            $help = t('A comma-separated list of terms describing this content. Example: funny, bungee jumping, "Company, Inc.".');
 724          }
 725          $form['taxonomy']['tags'][$vocabulary->vid] = array('#type' => 'textfield',
 726            '#title' => $vocabulary->name,
 727            '#description' => $help,
 728            '#required' => $vocabulary->required,
 729            '#default_value' => $typed_string,
 730            '#autocomplete_path' => 'taxonomy/autocomplete/'. $vocabulary->vid,
 731            '#weight' => $vocabulary->weight,
 732            '#maxlength' => 255,
 733          );
 734        }
 735        else {
 736          // Extract terms belonging to the vocabulary in question.
 737          $default_terms = array();
 738          foreach ($terms as $term) {
 739            if ($term->vid == $vocabulary->vid) {
 740              $default_terms[$term->tid] = $term;
 741            }
 742          }
 743          $form['taxonomy'][$vocabulary->vid] = taxonomy_form($vocabulary->vid, array_keys($default_terms), $vocabulary->help);
 744          $form['taxonomy'][$vocabulary->vid]['#weight'] = $vocabulary->weight;
 745          $form['taxonomy'][$vocabulary->vid]['#required'] = $vocabulary->required;
 746        }
 747      }
 748      if (is_array($form['taxonomy']) && !empty($form['taxonomy'])) {
 749        if (count($form['taxonomy']) > 1) { // Add fieldset only if form has more than 1 element.
 750          $form['taxonomy'] += array(
 751            '#type' => 'fieldset',
 752            '#title' => t('Categories'),
 753            '#collapsible' => TRUE,
 754            '#collapsed' => FALSE,
 755          );
 756        }
 757        $form['taxonomy']['#weight'] = -3;
 758        $form['taxonomy']['#tree'] = TRUE;
 759      }
 760    }
 761  }
 762  
 763  /**
 764   * Find all terms associated with the given node, within one vocabulary.
 765   */
 766  function taxonomy_node_get_terms_by_vocabulary($nid, $vid, $key = 'tid') {
 767    $result = db_query(db_rewrite_sql('SELECT t.tid, t.* FROM {term_data} t INNER JOIN {term_node} r ON r.tid = t.tid WHERE t.vid = %d AND r.nid = %d ORDER BY weight', 't', 'tid'), $vid, $nid);
 768    $terms = array();
 769    while ($term = db_fetch_object($result)) {
 770      $terms[$term->$key] = $term;
 771    }
 772    return $terms;
 773  }
 774  
 775  /**
 776   * Find all terms associated with the given node, ordered by vocabulary and term weight.
 777   */
 778  function taxonomy_node_get_terms($nid, $key = 'tid') {
 779    static $terms;
 780  
 781    if (!isset($terms[$nid][$key])) {
 782      $result = db_query(db_rewrite_sql('SELECT t.* FROM {term_node} r INNER JOIN {term_data} t ON r.tid = t.tid INNER JOIN {vocabulary} v ON t.vid = v.vid WHERE r.nid = %d ORDER BY v.weight, t.weight, t.name', 't', 'tid'), $nid);
 783      $terms[$nid][$key] = array();
 784      while ($term = db_fetch_object($result)) {
 785        $terms[$nid][$key][$term->$key] = $term;
 786      }
 787    }
 788    return $terms[$nid][$key];
 789  }
 790  
 791  /**
 792   * Make sure incoming vids are free tagging enabled.
 793   */
 794  function taxonomy_node_validate(&$node) {
 795    if ($node->taxonomy) {
 796      $terms = $node->taxonomy;
 797      if ($terms['tags']) {
 798        foreach ($terms['tags'] as $vid => $vid_value) {
 799          $vocabulary = taxonomy_get_vocabulary($vid);
 800          if (!$vocabulary->tags) {
 801            // see form_get_error $key = implode('][', $element['#parents']);
 802            // on why this is the key
 803            form_set_error("taxonomy][tags][$vid", t('The %name vocabulary can not be modified in this way.', array('%name' => $vocabulary->name)));
 804          }
 805        }
 806      }
 807    }
 808  }
 809  
 810  /**
 811   * Save term associations for a given node.
 812   */
 813  function taxonomy_node_save($nid, $terms) {
 814    taxonomy_node_delete($nid);
 815  
 816    // Free tagging vocabularies do not send their tids in the form,
 817    // so we'll detect them here and process them independently.
 818    if (isset($terms['tags'])) {
 819      $typed_input = $terms['tags'];
 820      unset($terms['tags']);
 821  
 822      foreach ($typed_input as $vid => $vid_value) {
 823        // This regexp allows the following types of user input:
 824        // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar
 825        $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x';
 826        preg_match_all($regexp, $vid_value, $matches);
 827        $typed_terms = array_unique($matches[1]);
 828  
 829        $inserted = array();
 830        foreach ($typed_terms as $typed_term) {
 831          // If a user has escaped a term (to demonstrate that it is a group,
 832          // or includes a comma or quote character), we remove the escape
 833          // formatting so to save the term into the database as the user intends.
 834          $typed_term = str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $typed_term));
 835          $typed_term = trim($typed_term);
 836          if ($typed_term == "") { continue; }
 837  
 838          // See if the term exists in the chosen vocabulary
 839          // and return the tid; otherwise, add a new record.
 840          $possibilities = taxonomy_get_term_by_name($typed_term);
 841          $typed_term_tid = NULL; // tid match, if any.
 842          foreach ($possibilities as $possibility) {
 843            if ($possibility->vid == $vid) {
 844              $typed_term_tid = $possibility->tid;
 845            }
 846          }
 847  
 848          if (!$typed_term_tid) {
 849            $edit = array('vid' => $vid, 'name' => $typed_term);
 850            $status = taxonomy_save_term($edit);
 851            $typed_term_tid = $edit['tid'];
 852          }
 853  
 854          // Defend against duplicate, differently cased tags
 855          if (!isset($inserted[$typed_term_tid])) {
 856            db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $typed_term_tid);
 857            $inserted[$typed_term_tid] = TRUE;
 858          }
 859        }
 860      }
 861    }
 862  
 863    if (is_array($terms)) {
 864      foreach ($terms as $term) {
 865        if (is_array($term)) {
 866          foreach ($term as $tid) {
 867            if ($tid) {
 868              db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $tid);
 869            }
 870          }
 871        }
 872        else if (is_object($term)) {
 873          db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term->tid);
 874        }
 875        else if ($term) {
 876          db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term);
 877        }
 878      }
 879    }
 880  }
 881  
 882  /**
 883   * Remove associations of a node to its terms.
 884   */
 885  function taxonomy_node_delete($nid) {
 886    db_query('DELETE FROM {term_node} WHERE nid = %d', $nid);
 887  }
 888  
 889  /**
 890   * Implementation of hook_node_type().
 891   */
 892  function taxonomy_node_type($op, $info) {
 893    if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) {
 894      db_query("UPDATE {vocabulary_node_types} SET type = '%s' WHERE type = '%s'", $info->type, $info->old_type);
 895    }
 896    elseif ($op == 'delete') {
 897      db_query("DELETE FROM {vocabulary_node_types} WHERE type = '%s'", $info->type);
 898    }
 899  }
 900  
 901  /**
 902   * Find all term objects related to a given term ID.
 903   */
 904  function taxonomy_get_related($tid, $key = 'tid') {
 905    if ($tid) {
 906      $result = db_query('SELECT t.*, tid1, tid2 FROM {term_relation}, {term_data} t WHERE (t.tid = tid1 OR t.tid = tid2) AND (tid1 = %d OR tid2 = %d) AND t.tid != %d ORDER BY weight, name', $tid, $tid, $tid);
 907      $related = array();
 908      while ($term = db_fetch_object($result)) {
 909        $related[$term->$key] = $term;
 910      }
 911      return $related;
 912    }
 913    else {
 914      return array();
 915    }
 916  }
 917  
 918  /**
 919   * Find all parents of a given term ID.
 920   */
 921  function taxonomy_get_parents($tid, $key = 'tid') {
 922    if ($tid) {
 923      $result = db_query(db_rewrite_sql('SELECT t.tid, t.* FROM {term_data} t INNER JOIN {term_hierarchy} h ON h.parent = t.tid WHERE h.tid = %d ORDER BY weight, name', 't', 'tid'), $tid);
 924      $parents = array();
 925      while ($parent = db_fetch_object($result)) {
 926        $parents[$parent->$key] = $parent;
 927      }
 928      return $parents;
 929    }
 930    else {
 931      return array();
 932    }
 933  }
 934  
 935  /**
 936   * Find all ancestors of a given term ID.
 937   */
 938  function taxonomy_get_parents_all($tid) {
 939    $parents = array();
 940    if ($tid) {
 941      $parents[] = taxonomy_get_term($tid);
 942      $n = 0;
 943      while ($parent = taxonomy_get_parents($parents[$n]->tid)) {
 944        $parents = array_merge($parents, $parent);
 945        $n++;
 946      }
 947    }
 948    return $parents;
 949  }
 950  
 951  /**
 952   * Find all children of a term ID.
 953   */
 954  function taxonomy_get_children($tid, $vid = 0, $key = 'tid') {
 955    if ($vid) {
 956      $result = db_query(db_rewrite_sql('SELECT t.* FROM {term_data} t INNER JOIN {term_hierarchy} h ON h.tid = t.tid WHERE t.vid = %d AND h.parent = %d ORDER BY weight, name', 't', 'tid'), $vid, $tid);
 957    }
 958    else {
 959      $result = db_query(db_rewrite_sql('SELECT t.* FROM {term_data} t INNER JOIN {term_hierarchy} h ON h.tid = t.tid WHERE parent = %d ORDER BY weight, name', 't', 'tid'), $tid);
 960    }
 961    $children = array();
 962    while ($term = db_fetch_object($result)) {
 963      $children[$term->$key] = $term;
 964    }
 965    return $children;
 966  }
 967  
 968  /**
 969   * Create a hierarchical representation of a vocabulary.
 970   *
 971   * @param $vid
 972   *   Which vocabulary to generate the tree for.
 973   *
 974   * @param $parent
 975   *   The term ID under which to generate the tree. If 0, generate the tree
 976   *   for the entire vocabulary.
 977   *
 978   * @param $depth
 979   *   Internal use only.
 980   *
 981   * @param $max_depth
 982   *   The number of levels of the tree to return. Leave NULL to return all levels.
 983   *
 984   * @return
 985   *   An array of all term objects in the tree. Each term object is extended
 986   *   to have "depth" and "parents" attributes in addition to its normal ones.
 987   *   Results are statically cached.
 988   */
 989  function taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL) {
 990    static $children, $parents, $terms;
 991  
 992    $depth++;
 993  
 994    // We cache trees, so it's not CPU-intensive to call get_tree() on a term
 995    // and its children, too.
 996    if (!isset($children[$vid])) {
 997      $children[$vid] = array();
 998  
 999      $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);
1000      while ($term = db_fetch_object($result)) {
1001        $children[$vid][$term->parent][] = $term->tid;
1002        $parents[$vid][$term->tid][] = $term->parent;
1003        $terms[$vid][$term->tid] = $term;
1004      }
1005    }
1006  
1007    $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
1008    if ($children[$vid][$parent]) {
1009      foreach ($children[$vid][$parent] as $child) {
1010        if ($max_depth > $depth) {
1011          $term = drupal_clone($terms[$vid][$child]);
1012          $term->depth = $depth;
1013          // The "parent" attribute is not useful, as it would show one parent only.
1014          unset($term->parent);
1015          $term->parents = $parents[$vid][$child];
1016          $tree[] = $term;
1017  
1018          if ($children[$vid][$child]) {
1019            $tree = array_merge($tree, taxonomy_get_tree($vid, $child, $depth, $max_depth));
1020          }
1021        }
1022      }
1023    }
1024  
1025    return $tree ? $tree : array();
1026  }
1027  
1028  /**
1029   * Return an array of synonyms of the given term ID.
1030   */
1031  function taxonomy_get_synonyms($tid) {
1032    if ($tid) {
1033      $result = db_query('SELECT name FROM {term_synonym} WHERE tid = %d', $tid);
1034      while ($synonym = db_fetch_array($result)) {
1035        $synonyms[] = $synonym['name'];
1036      }
1037      return $synonyms ? $synonyms : array();
1038    }
1039    else {
1040      return array();
1041    }
1042  }
1043  
1044  /**
1045   * Return the term object that has the given string as a synonym.
1046   */
1047  function taxonomy_get_synonym_root($synonym) {
1048    return db_fetch_object(db_query("SELECT * FROM {term_synonym} s, {term_data} t WHERE t.tid = s.tid AND s.name = '%s'", $synonym));
1049  }
1050  
1051  /**
1052   * Count the number of published nodes classified by a term.
1053   *
1054   * @param $tid
1055   *   The term's ID
1056   *
1057   * @param $type
1058   *   The $node->type. If given, taxonomy_term_count_nodes only counts
1059   *   nodes of $type that are classified with the term $tid.
1060   *
1061   * @return int
1062   *   An integer representing a number of nodes.
1063   *   Results are statically cached.
1064   */
1065  function taxonomy_term_count_nodes($tid, $type = 0) {
1066    static $count;
1067  
1068    if (!isset($count[$type])) {
1069      // $type == 0 always evaluates TRUE if $type is a string
1070      if (is_numeric($type)) {
1071        $result = db_query(db_rewrite_sql('SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 GROUP BY t.tid'));
1072      }
1073      else {
1074        $result = db_query(db_rewrite_sql("SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 AND n.type = '%s' GROUP BY t.tid"), $type);
1075      }
1076      while ($term = db_fetch_object($result)) {
1077        $count[$type][$term->tid] = $term->c;
1078      }
1079    }
1080  
1081    foreach (_taxonomy_term_children($tid) as $c) {
1082      $children_count += taxonomy_term_count_nodes($c, $type);
1083    }
1084    return $count[$type][$tid] + $children_count;
1085  }
1086  
1087  /**
1088   * Helper for taxonomy_term_count_nodes(). Used to find out
1089   * which terms are children of a parent term.
1090   *
1091   * @param $tid
1092   *   The parent term's ID
1093   *
1094   * @return array
1095   *   An array of term IDs representing the children of $tid.
1096   *   Results are statically cached.
1097   *
1098   */
1099  function _taxonomy_term_children($tid) {
1100    static $children;
1101  
1102    if (!isset($children)) {
1103      $result = db_query('SELECT tid, parent FROM {term_hierarchy}');
1104      while ($term = db_fetch_object($result)) {
1105        $children[$term->parent][] = $term->tid;
1106      }
1107    }
1108    return $children[$tid] ? $children[$tid] : array();
1109  }
1110  
1111  /**
1112   * Try to map a string to an existing term, as for glossary use.
1113   *
1114   * Provides a case-insensitive and trimmed mapping, to maximize the
1115   * likelihood of a successful match.
1116   *
1117   * @param name
1118   *   Name of the term to search for.
1119   *
1120   * @return
1121   *   An array of matching term objects.
1122   */
1123  function taxonomy_get_term_by_name($name) {
1124    $db_result = db_query(db_rewrite_sql("SELECT t.tid, t.* FROM {term_data} t WHERE LOWER('%s') LIKE LOWER(t.name)", 't', 'tid'), trim($name));
1125    $result = array();
1126    while ($term = db_fetch_object($db_result)) {
1127      $result[] = $term;
1128    }
1129  
1130    return $result;
1131  }
1132  
1133  /**
1134   * Return the vocabulary object matching a vocabulary ID.
1135   *
1136   * @param $vid
1137   *   The vocabulary's ID
1138   *
1139   * @return Object
1140   *   The vocabulary object with all of its metadata.
1141   *   Results are statically cached.
1142   */
1143  function taxonomy_get_vocabulary($vid) {
1144    static $vocabularies = array();
1145  
1146    if (!array_key_exists($vid, $vocabularies)) {
1147      $result = db_query('SELECT v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE v.vid = %d ORDER BY v.weight, v.name', $vid);
1148      $node_types = array();
1149      while ($voc = db_fetch_object($result)) {
1150        $node_types[] = $voc->type;
1151        unset($voc->type);
1152        $voc->nodes = $node_types;
1153        $vocabularies[$vid] = $voc;
1154      }
1155    }
1156  
1157    return $vocabularies[$vid];
1158  }
1159  
1160  /**
1161   * Return the term object matching a term ID.
1162   *
1163   * @param $tid
1164   *   A term's ID
1165   *
1166   * @return Object
1167   *   A term object. Results are statically cached.
1168   */
1169  function taxonomy_get_term($tid) {
1170    static $terms = array();
1171  
1172    if (!isset($terms[$tid])) {
1173      $terms[$tid] = db_fetch_object(db_query('SELECT * FROM {term_data} WHERE tid = %d', $tid));
1174    }
1175  
1176    return $terms[$tid];
1177  }
1178  
1179  function _taxonomy_term_select($title, $name, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) {
1180    $tree = taxonomy_get_tree($vocabulary_id);
1181    $options = array();
1182  
1183    if ($blank) {
1184      $options[0] = $blank;
1185    }
1186    if ($tree) {
1187      foreach ($tree as $term) {
1188        if (!in_array($term->tid, $exclude)) {
1189          $choice = new stdClass();
1190          $choice->option = array($term->tid => str_repeat('-', $term->depth) . $term->name);
1191          $options[] = $choice;
1192        }
1193      }
1194      if (!$blank && !$value) {
1195        // required but without a predefined value, so set first as predefined
1196        $value = $tree[0]->tid;
1197      }
1198    }
1199  
1200    return array('#type' => 'select',
1201      '#title' => $title,
1202      '#default_value' => $value,
1203      '#options' => $options,
1204      '#description' => $description,
1205      '#multiple' => $multiple,
1206      '#size' => $multiple ? min(9, count($options)) : 0,
1207      '#weight' => -15,
1208      '#theme' => 'taxonomy_term_select',
1209    );
1210  }
1211  
1212  /**
1213   * We use the default selection field for choosing terms.
1214   */
1215  function theme_taxonomy_term_select($element) {
1216    return theme('select', $element);
1217  }
1218  
1219  /**
1220   * Finds all nodes that match selected taxonomy conditions.
1221   *
1222   * @param $tids
1223   *   An array of term IDs to match.
1224   * @param $operator
1225   *   How to interpret multiple IDs in the array. Can be "or" or "and".
1226   * @param $depth
1227   *   How many levels deep to traverse the taxonomy tree. Can be a nonnegative
1228   *   integer or "all".
1229   * @param $pager
1230   *   Whether the nodes are to be used with a pager (the case on most Drupal
1231   *   pages) or not (in an XML feed, for example).
1232   * @param $order
1233   *   The order clause for the query that retrieve the nodes.
1234   * @return
1235   *   A resource identifier pointing to the query results.
1236   */
1237  function taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $order = 'n.sticky DESC, n.created DESC') {
1238    if (count($tids) > 0) {
1239      // For each term ID, generate an array of descendant term IDs to the right depth.
1240      $descendant_tids = array();
1241      if ($depth === 'all') {
1242        $depth = NULL;
1243      }
1244      foreach ($tids as $index => $tid) {
1245        $term = taxonomy_get_term($tid);
1246        $tree = taxonomy_get_tree($term->vid, $tid, -1, $depth);
1247        $descendant_tids[] = array_merge(array($tid), array_map('_taxonomy_get_tid_from_term', $tree));
1248      }
1249  
1250      if ($operator == 'or') {
1251        $str_tids = implode(',', call_user_func_array('array_merge', $descendant_tids));
1252        $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n INNER JOIN {term_node} tn ON n.nid = tn.nid WHERE tn.tid IN ('. $str_tids .') AND n.status = 1 ORDER BY '. $order;
1253        $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n INNER JOIN {term_node} tn ON n.nid = tn.nid WHERE tn.tid IN ('. $str_tids .') AND n.status = 1';
1254      }
1255      else {
1256        $joins = '';
1257        $wheres = '';
1258        foreach ($descendant_tids as $index => $tids) {
1259          $joins .= ' INNER JOIN {term_node} tn'. $index .' ON n.nid = tn'. $index .'.nid';
1260          $wheres .= ' AND tn'. $index .'.tid IN ('. implode(',', $tids) .')';
1261        }
1262        $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n '. $joins .' WHERE n.status = 1 '. $wheres .' ORDER BY '. $order;
1263        $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n '. $joins .' WHERE n.status = 1 '. $wheres;
1264      }
1265      $sql = db_rewrite_sql($sql);
1266      $sql_count = db_rewrite_sql($sql_count);
1267      if ($pager) {
1268        $result = pager_query($sql, variable_get('default_nodes_main', 10), 0, $sql_count);
1269      }
1270      else {
1271        $result = db_query_range($sql, 0, variable_get('feed_default_items', 10));
1272      }
1273    }
1274  
1275    return $result;
1276  }
1277  
1278  /**
1279   * Accepts the result of a pager_query() call, such as that performed by
1280   * taxonomy_select_nodes(), and formats each node along with a pager.
1281  */
1282  function taxonomy_render_nodes($result) {
1283    $output = '';
1284    if (db_num_rows($result) > 0) {
1285      while ($node = db_fetch_object($result)) {
1286        $output .= node_view(node_load($node->nid), 1);
1287      }
1288      $output .= theme('pager', NULL, variable_get('default_nodes_main', 10), 0);
1289    }
1290    else {
1291      $output .= '<p>'. t('There are currently no posts in this category.') .'</p>';
1292    }
1293    return $output;
1294  }
1295  
1296  /**
1297   * Implementation of hook_nodeapi().
1298   */
1299  function taxonomy_nodeapi($node, $op, $arg = 0) {
1300    switch ($op) {
1301      case 'load':
1302       $output['taxonomy'] = taxonomy_node_get_terms($node->nid);
1303       return $output;
1304      case 'insert':
1305        taxonomy_node_save($node->nid, $node->taxonomy);
1306        break;
1307      case 'update':
1308        taxonomy_node_save($node->nid, $node->taxonomy);
1309        break;
1310      case 'delete':
1311        taxonomy_node_delete($node->nid);
1312        break;
1313      case 'validate':
1314        taxonomy_node_validate($node);
1315        break;
1316      case 'rss item':
1317        return taxonomy_rss_item($node);
1318      case 'update index':
1319        return taxonomy_node_update_index($node);
1320    }
1321  }
1322  
1323  /**
1324   * Implementation of hook_nodeapi('update_index').
1325   */
1326  function taxonomy_node_update_index(&$node) {
1327    $output = array();
1328    foreach ($node->taxonomy as $term) {
1329      $output[] = $term->name;
1330    }
1331    if (count($output)) {
1332      return '<strong>('. implode(', ', $output) .')</strong>';
1333    }
1334  }
1335  
1336  /**
1337   * Parses a comma or plus separated string of term IDs.
1338   *
1339   * @param $str_tids
1340   *   A string of term IDs, separated by plus or comma.
1341   *   comma (,) means AND
1342   *   plus (+) means OR
1343   *
1344   * @return an associative array with an operator key (either 'and'
1345   *   or 'or') and a tid key containing an array of the term ids.
1346   */
1347  function taxonomy_terms_parse_string($str_tids) {
1348    $terms = array();
1349    if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str_tids)) {
1350      $terms['operator'] = 'or';
1351      // The '+' character in a query string may be parsed as ' '.
1352      $terms['tids'] = preg_split('/[+ ]/', $str_tids);
1353    }
1354    else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str_tids)) {
1355      $terms['operator'] = 'and';
1356      $terms['tids'] = explode(',', $str_tids);
1357    }
1358    return $terms;
1359  }
1360  
1361  
1362  /**
1363   * Menu callback; displays all nodes associated with a term.
1364   */
1365  function taxonomy_term_page($str_tids = '', $depth = 0, $op = 'page') {
1366    $terms = taxonomy_terms_parse_string($str_tids);
1367    if ($terms['operator'] != 'and' && $terms['operator'] != 'or') {
1368      drupal_not_found();
1369    }
1370  
1371    if ($terms['tids']) {
1372      $placeholders = implode(',', array_fill(0, count($terms['tids']), '%d'));
1373      $result = db_query(db_rewrite_sql('SELECT t.tid, t.name FROM {term_data} t WHERE t.tid IN ('. $placeholders .')', 't', 'tid'), $terms['tids']);
1374      $tids = array(); // we rebuild the $tids-array so it only contains terms the user has access to.
1375      $names = array();
1376      while ($term = db_fetch_object($result)) {
1377        $tids[] = $term->tid;
1378        $names[] = $term->name;
1379      }
1380  
1381      if ($names) {
1382        $title = check_plain(implode(', ', $names));
1383        drupal_set_title($title);
1384  
1385        switch ($op) {
1386          case 'page':
1387            // Build breadcrumb based on first hierarchy of first term:
1388            $current->tid = $tids[0];
1389            $breadcrumbs = array(array('path' => $_GET['q'], 'title' => $names[0]));
1390            while ($parents = taxonomy_get_parents($current->tid)) {
1391              $current = array_shift($parents);
1392              $breadcrumbs[] = array('path' => 'taxonomy/term/'. $current->tid, 'title' => $current->name);
1393            }
1394            $breadcrumbs = array_reverse($breadcrumbs);
1395            menu_set_location($breadcrumbs);
1396  
1397            $output = taxonomy_render_nodes(taxonomy_select_nodes($tids, $terms['operator'], $depth, TRUE));
1398            drupal_add_feed(url('taxonomy/term/'. $str_tids .'/'. $depth .'/feed'), 'RSS - '. $title);
1399            return $output;
1400            break;
1401  
1402          case 'feed':
1403            $term = taxonomy_get_term($tids[0]);
1404            $channel['link'] = url('taxonomy/term/'. $str_tids .'/'. $depth, NULL, NULL, TRUE);
1405            $channel['title'] = variable_get('site_name', 'Drupal') .' - '. $title;
1406            $channel['description'] = $term->description;
1407  
1408            $result = taxonomy_select_nodes($tids, $terms['operator'], $depth, FALSE);
1409            node_feed($result, $channel);
1410            break;
1411          default:
1412            drupal_not_found();
1413        }
1414      }
1415      else {
1416        drupal_not_found();
1417      }
1418    }
1419  }
1420  
1421  /**
1422   * Page to edit a vocabulary.
1423   */
1424  function taxonomy_admin_vocabulary_edit($vid = NULL) {
1425    if ($_POST['op'] == t('Delete') || $_POST['confirm']) {
1426      return drupal_get_form('taxonomy_vocabulary_confirm_delete', $vid);
1427    }
1428    if ($vocabulary = (array)taxonomy_get_vocabulary($vid)) {
1429      return drupal_get_form('taxonomy_form_vocabulary', $vocabulary);
1430    }
1431    return drupal_not_found();
1432  }
1433  
1434  /**
1435   * Page to edit a vocabulary term.
1436   */
1437  function taxonomy_admin_term_edit($tid) {
1438    if ($_POST['op'] == t('Delete') || $_POST['confirm']) {
1439      return drupal_get_form('taxonomy_term_confirm_delete', $tid);
1440    }
1441    if ($term = (array)taxonomy_get_term($tid)) {
1442      return drupal_get_form('taxonomy_form_term', $term['vid'], $term);
1443    }
1444    return drupal_not_found();
1445  }
1446  
1447  /**
1448   * Provides category information for RSS feeds.
1449   */
1450  function taxonomy_rss_item($node) {
1451    $output = array();
1452    foreach ($node->taxonomy as $term) {
1453      $output[] = array('key'   => 'category',
1454                        'value' => check_plain($term->name),
1455                        'attributes' => array('domain' => url('taxonomy/term/'. $term->tid, NULL, NULL, TRUE)));
1456    }
1457    return $output;
1458  }
1459  
1460  /**
1461   * Implementation of hook_help().
1462   */
1463  function taxonomy_help($section) {
1464    switch ($section) {
1465      case 'admin/help#taxonomy':
1466        $output = '<p>'. t('The taxonomy module is one of the most popular features because users often want to create categories to organize content by type. A simple example would be organizing a list of music reviews by musical genre.') .'</p>';
1467        $output .= '<p>'. t('Taxonomy is the study of classification. The taxonomy module allows you to define vocabularies (sets of categories) which are used to classify content. The module supports hierarchical classification and association between terms, allowing for truly flexible information retrieval and classification. The taxonomy module allows multiple lists of categories for classification (controlled vocabularies) and offers the possibility of creating thesauri (controlled vocabularies that indicate the relationship of terms) and taxonomies (controlled vocabularies where relationships are indicated hierarchically). To view and manage the terms of each vocabulary, click on the associated <em>list terms</em> link. To delete a vocabulary and all its terms, choose <em>edit vocabulary.</em>') .'</p>';
1468        $output .= '<p>'. t('A controlled vocabulary is a set of terms to use for describing content (known as descriptors in indexing lingo). Drupal allows you to describe each piece of content (blog, story, etc.) using one or many of these terms. For simple implementations, you might create a set of categories without subcategories, similar to Slashdot\'s sections. For more complex implementations, you might create a hierarchical list of categories.') .'</p>';
1469        $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="@taxonomy">Taxonomy page</a>.', array('@taxonomy' => 'http://drupal.org/handbook/modules/taxonomy/')) .'</p>';
1470        return $output;
1471      case 'admin/content/taxonomy':
1472        return '<p>'. t('The taxonomy module allows you to classify content into categories and subcategories; it allows multiple lists of categories for classification (controlled vocabularies) and offers the possibility of creating thesauri (controlled vocabularies that indicate the relationship of terms), taxonomies (controlled vocabularies where relationships are indicated hierarchically), and free vocabularies where terms, or tags, are defined during content creation. To view and manage the terms of each vocabulary, click on the associated <em>list terms</em> link. To delete a vocabulary and all its terms, choose "edit vocabulary".') .'</p>';
1473      case 'admin/content/taxonomy/add/vocabulary':
1474        return '<p>'. t("When you create a controlled vocabulary you are creating a set of terms to use for describing content (known as descriptors in indexing lingo). Drupal allows you to describe each piece of content (blog, story, etc.) using one or many of these terms. For simple implementations, you might create a set of categories without subcategories. For more complex implementations, you might create a hierarchical list of categories.") .'</p>';
1475    }
1476  }
1477  
1478  /**
1479   * Helper function for array_map purposes.
1480   */
1481  function _taxonomy_get_tid_from_term($term) {
1482    return $term->tid;
1483  }
1484  
1485  /**
1486   * Helper function for autocompletion
1487   */
1488  function taxonomy_autocomplete($vid, $string = '') {
1489    // The user enters a comma-separated list of tags. We only autocomplete the last tag.
1490    // This regexp allows the following types of user input:
1491    // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar
1492    $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x';
1493    preg_match_all($regexp, $string, $matches);
1494    $array = $matches[1];
1495  
1496    // Fetch last tag
1497    $last_string = trim(array_pop($array));
1498    if ($last_string != '') {
1499      $result = db_query_range(db_rewrite_sql("SELECT t.tid, t.name FROM {term_data} t WHERE t.vid = %d AND LOWER(t.name) LIKE LOWER('%%%s%%')", 't', 'tid'), $vid, $last_string, 0, 10);
1500  
1501      $prefix = count($array) ? implode(', ', $array) .', ' : '';
1502  
1503      $matches = array();
1504      while ($tag = db_fetch_object($result)) {
1505        $n = $tag->name;
1506        // Commas and quotes in terms are special cases, so encode 'em.
1507        if (strpos($tag->name, ',') !== FALSE || strpos($tag->name, '"') !== FALSE) {
1508          $n = '"'. str_replace('"', '""', $tag->name) .'"';
1509        }
1510        $matches[$prefix . $n] = check_plain($tag->name);
1511      }
1512      print drupal_to_js($matches);
1513      exit();
1514    }
1515  }


Généré le : Fri Nov 30 16:20:15 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics