[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

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

   1  <?php
   2  // $Id: taxonomy.module,v 1.414.2.16 2010/08/06 11:10:57 goba 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_theme().
  18   */
  19  function taxonomy_theme() {
  20    return array(
  21      'taxonomy_term_select' => array(
  22        'arguments' => array('element' => NULL),
  23      ),
  24      'taxonomy_term_page' => array(
  25        'arguments' => array('tids' => array(), 'result' => NULL),
  26      ),
  27      'taxonomy_overview_vocabularies' => array(
  28        'arguments' => array('form' => array()),
  29      ),
  30      'taxonomy_overview_terms' => array(
  31        'arguments' => array('form' => array()),
  32      ),
  33    );
  34  }
  35  
  36  /**
  37   * Implementation of hook_link().
  38   *
  39   * This hook is extended with $type = 'taxonomy terms' to allow themes to
  40   * print lists of terms associated with a node. Themes can print taxonomy
  41   * links with:
  42   *
  43   * if (module_exists('taxonomy')) {
  44   *   $terms = taxonomy_link('taxonomy terms', $node);
  45   *   print theme('links', $terms);
  46   * }
  47   */
  48  function taxonomy_link($type, $node = NULL) {
  49    if ($type == 'taxonomy terms' && $node != NULL) {
  50      $links = array();
  51      // If previewing, the terms must be converted to objects first.
  52      if (isset($node->build_mode) && $node->build_mode == NODE_BUILD_PREVIEW) {
  53        $node->taxonomy = taxonomy_preview_terms($node);
  54      }
  55      if (!empty($node->taxonomy)) {
  56        foreach ($node->taxonomy as $term) {
  57          // During preview the free tagging terms are in an array unlike the
  58          // other terms which are objects. So we have to check if a $term
  59          // is an object or not.
  60          if (is_object($term)) {
  61            $links['taxonomy_term_'. $term->tid] = array(
  62              'title' => $term->name,
  63              'href' => taxonomy_term_path($term),
  64              'attributes' => array('rel' => 'tag', 'title' => strip_tags($term->description))
  65            );
  66          }
  67          // Previewing free tagging terms; we don't link them because the
  68          // term-page might not exist yet.
  69          else {
  70            foreach ($term as $free_typed) {
  71              $typed_terms = drupal_explode_tags($free_typed);
  72              foreach ($typed_terms as $typed_term) {
  73                $links['taxonomy_preview_term_'. $typed_term] = array(
  74                  'title' => $typed_term,
  75                );
  76              }
  77            }
  78          }
  79        }
  80      }
  81  
  82      // We call this hook again because some modules and themes
  83      // call taxonomy_link('taxonomy terms') directly.
  84      drupal_alter('link', $links, $node);
  85  
  86      return $links;
  87    }
  88  }
  89  
  90  /**
  91   * For vocabularies not maintained by taxonomy.module, give the maintaining
  92   * module a chance to provide a path for terms in that vocabulary.
  93   *
  94   * @param $term
  95   *   A term object.
  96   * @return
  97   *   An internal Drupal path.
  98   */
  99  function taxonomy_term_path($term) {
 100    $vocabulary = taxonomy_vocabulary_load($term->vid);
 101    if ($vocabulary->module != 'taxonomy' && $path = module_invoke($vocabulary->module, 'term_path', $term)) {
 102      return $path;
 103    }
 104    return 'taxonomy/term/'. $term->tid;
 105  }
 106  
 107  /**
 108   * Implementation of hook_menu().
 109   */
 110  function taxonomy_menu() {
 111    $items['admin/content/taxonomy'] = array(
 112      'title' => 'Taxonomy',
 113      'description' => 'Manage tagging, categorization, and classification of your content.',
 114      'page callback' => 'drupal_get_form',
 115      'page arguments' => array('taxonomy_overview_vocabularies'),
 116      'access arguments' => array('administer taxonomy'),
 117      'file' => 'taxonomy.admin.inc',
 118    );
 119  
 120    $items['admin/content/taxonomy/list'] = array(
 121      'title' => 'List',
 122      'type' => MENU_DEFAULT_LOCAL_TASK,
 123      'weight' => -10,
 124    );
 125  
 126    $items['admin/content/taxonomy/add/vocabulary'] = array(
 127      'title' => 'Add vocabulary',
 128      'page callback' => 'drupal_get_form',
 129      'page arguments' => array('taxonomy_form_vocabulary'),
 130      'access arguments' => array('administer taxonomy'),
 131      'type' => MENU_LOCAL_TASK,
 132      'parent' => 'admin/content/taxonomy',
 133      'file' => 'taxonomy.admin.inc',
 134    );
 135  
 136    $items['admin/content/taxonomy/edit/vocabulary/%taxonomy_vocabulary'] = array(
 137      'title' => 'Edit vocabulary',
 138      'page callback' => 'taxonomy_admin_vocabulary_edit',
 139      'page arguments' => array(5),
 140      'access arguments' => array('administer taxonomy'),
 141      'type' => MENU_CALLBACK,
 142      'file' => 'taxonomy.admin.inc',
 143    );
 144  
 145    $items['admin/content/taxonomy/edit/term'] = array(
 146      'title' => 'Edit term',
 147      'page callback' => 'taxonomy_admin_term_edit',
 148      'access arguments' => array('administer taxonomy'),
 149      'type' => MENU_CALLBACK,
 150      'file' => 'taxonomy.admin.inc',
 151    );
 152  
 153    $items['taxonomy/term/%'] = array(
 154      'title' => 'Taxonomy term',
 155      'page callback' => 'taxonomy_term_page',
 156      'page arguments' => array(2),
 157      'access arguments' => array('access content'),
 158      'type' => MENU_CALLBACK,
 159      'file' => 'taxonomy.pages.inc',
 160    );
 161  
 162    $items['taxonomy/autocomplete'] = array(
 163      'title' => 'Autocomplete taxonomy',
 164      'page callback' => 'taxonomy_autocomplete',
 165      'access arguments' => array('access content'),
 166      'type' => MENU_CALLBACK,
 167      'file' => 'taxonomy.pages.inc',
 168    );
 169    $items['admin/content/taxonomy/%taxonomy_vocabulary'] = array(
 170      'title' => 'List terms',
 171      'page callback' => 'drupal_get_form',
 172      'page arguments' => array('taxonomy_overview_terms', 3),
 173      'access arguments' => array('administer taxonomy'),
 174      'type' => MENU_CALLBACK,
 175      'file' => 'taxonomy.admin.inc',
 176    );
 177  
 178    $items['admin/content/taxonomy/%taxonomy_vocabulary/list'] = array(
 179      'title' => 'List',
 180      'type' => MENU_DEFAULT_LOCAL_TASK,
 181      'weight' => -10,
 182    );
 183  
 184    $items['admin/content/taxonomy/%taxonomy_vocabulary/add/term'] = array(
 185      'title' => 'Add term',
 186      'page callback' => 'taxonomy_add_term_page',
 187      'page arguments' => array(3),
 188      'access arguments' => array('administer taxonomy'),
 189      'type' => MENU_LOCAL_TASK,
 190      'parent' => 'admin/content/taxonomy/%taxonomy_vocabulary',
 191      'file' => 'taxonomy.admin.inc',
 192    );
 193  
 194    return $items;
 195  }
 196  
 197  function taxonomy_save_vocabulary(&$edit) {
 198    $edit['nodes'] = empty($edit['nodes']) ? array() : $edit['nodes'];
 199  
 200    if (!isset($edit['module'])) {
 201      $edit['module'] = 'taxonomy';
 202    }
 203  
 204    if (!empty($edit['vid']) && !empty($edit['name'])) {
 205      drupal_write_record('vocabulary', $edit, 'vid');
 206      db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d", $edit['vid']);
 207      foreach ($edit['nodes'] as $type => $selected) {
 208        db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
 209      }
 210      module_invoke_all('taxonomy', 'update', 'vocabulary', $edit);
 211      $status = SAVED_UPDATED;
 212    }
 213    else if (!empty($edit['vid'])) {
 214      $status = taxonomy_del_vocabulary($edit['vid']);
 215    }
 216    else {
 217      drupal_write_record('vocabulary', $edit);
 218      foreach ($edit['nodes'] as $type => $selected) {
 219        db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
 220      }
 221      module_invoke_all('taxonomy', 'insert', 'vocabulary', $edit);
 222      $status = SAVED_NEW;
 223    }
 224  
 225    cache_clear_all();
 226  
 227    return $status;
 228  }
 229  
 230  /**
 231   * Delete a vocabulary.
 232   *
 233   * @param $vid
 234   *   A vocabulary ID.
 235   * @return
 236   *   Constant indicating items were deleted.
 237   */
 238  function taxonomy_del_vocabulary($vid) {
 239    $vocabulary = (array) taxonomy_vocabulary_load($vid);
 240  
 241    db_query('DELETE FROM {vocabulary} WHERE vid = %d', $vid);
 242    db_query('DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid);
 243    $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
 244    while ($term = db_fetch_object($result)) {
 245      taxonomy_del_term($term->tid);
 246    }
 247  
 248    module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
 249  
 250    cache_clear_all();
 251  
 252    return SAVED_DELETED;
 253  }
 254  
 255  /**
 256   * Dynamically check and update the hierarachy flag of a vocabulary.
 257   *
 258   * Checks the current parents of all terms in a vocabulary and updates the
 259   * vocabularies hierarchy setting to the lowest possible level. A hierarchy with
 260   * no parents in any of its terms will be given a hierarchy of 0. If terms
 261   * contain at most a single parent, the vocabulary will be given a hierarchy of
 262   * 1. If any term contain multiple parents, the vocabulary will be given a
 263   * hieararchy of 2.
 264   *
 265   * @param $vocabulary
 266   *   An array of the vocabulary structure.
 267   * @param $changed_term
 268   *   An array of the term structure that was updated.
 269   */
 270  function taxonomy_check_vocabulary_hierarchy($vocabulary, $changed_term) {
 271    $tree = taxonomy_get_tree($vocabulary['vid']);
 272    $hierarchy = 0;
 273    foreach ($tree as $term) {
 274      // Update the changed term with the new parent value before comparision.
 275      if ($term->tid == $changed_term['tid']) {
 276        $term = (object)$changed_term;
 277        $term->parents = $term->parent;
 278      }
 279      // Check this term's parent count.
 280      if (count($term->parents) > 1) {
 281        $hierarchy = 2;
 282        break;
 283      }
 284      elseif (count($term->parents) == 1 && 0 !== array_shift($term->parents)) {
 285        $hierarchy = 1;
 286      }
 287    }
 288    if ($hierarchy != $vocabulary['hierarchy']) {
 289      $vocabulary['hierarchy'] = $hierarchy;
 290      taxonomy_save_vocabulary($vocabulary);
 291    }
 292  
 293    return $hierarchy;
 294  }
 295  
 296  /**
 297   * Helper function for taxonomy_form_term_submit().
 298   *
 299   * @param $form_state['values']
 300   * @return
 301   *   Status constant indicating if term was inserted or updated.
 302   */
 303  function taxonomy_save_term(&$form_values) {
 304    $form_values += array(
 305      'description' => '',
 306      'weight' => 0
 307    );
 308  
 309    if (!empty($form_values['tid']) && $form_values['name']) {
 310      drupal_write_record('term_data', $form_values, 'tid');
 311      $hook = 'update';
 312      $status = SAVED_UPDATED;
 313    }
 314    else if (!empty($form_values['tid'])) {
 315      return taxonomy_del_term($form_values['tid']);
 316    }
 317    else {
 318      drupal_write_record('term_data', $form_values);
 319      $hook = 'insert';
 320      $status = SAVED_NEW;
 321    }
 322  
 323    db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $form_values['tid'], $form_values['tid']);
 324    if (!empty($form_values['relations'])) {
 325      foreach ($form_values['relations'] as $related_id) {
 326        if ($related_id != 0) {
 327          db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $form_values['tid'], $related_id);
 328        }
 329      }
 330    }
 331  
 332    db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $form_values['tid']);
 333    if (!isset($form_values['parent']) || empty($form_values['parent'])) {
 334      $form_values['parent'] = array(0);
 335    }
 336    if (is_array($form_values['parent'])) {
 337      foreach ($form_values['parent'] as $parent) {
 338        if (is_array($parent)) {
 339          foreach ($parent as $tid) {
 340            db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $tid);
 341          }
 342        }
 343        else {
 344          db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $parent);
 345        }
 346      }
 347    }
 348    else {
 349      db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $form_values['parent']);
 350    }
 351  
 352    db_query('DELETE FROM {term_synonym} WHERE tid = %d', $form_values['tid']);
 353    if (!empty($form_values['synonyms'])) {
 354      foreach (explode ("\n", str_replace("\r", '', $form_values['synonyms'])) as $synonym) {
 355        if ($synonym) {
 356          db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $form_values['tid'], chop($synonym));
 357        }
 358      }
 359    }
 360  
 361    if (isset($hook)) {
 362      module_invoke_all('taxonomy', $hook, 'term', $form_values);
 363    }
 364  
 365    cache_clear_all();
 366  
 367    return $status;
 368  }
 369  
 370  /**
 371   * Delete a term.
 372   *
 373   * @param $tid
 374   *   The term ID.
 375   * @return
 376   *   Status constant indicating deletion.
 377   */
 378  function taxonomy_del_term($tid) {
 379    $tids = array($tid);
 380    while ($tids) {
 381      $children_tids = $orphans = array();
 382      foreach ($tids as $tid) {
 383        // See if any of the term's children are about to be become orphans:
 384        if ($children = taxonomy_get_children($tid)) {
 385          foreach ($children as $child) {
 386            // If the term has multiple parents, we don't delete it.
 387            $parents = taxonomy_get_parents($child->tid);
 388            if (count($parents) == 1) {
 389              $orphans[] = $child->tid;
 390            }
 391          }
 392        }
 393  
 394        $term = (array) taxonomy_get_term($tid);
 395  
 396        db_query('DELETE FROM {term_data} WHERE tid = %d', $tid);
 397        db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $tid);
 398        db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $tid, $tid);
 399        db_query('DELETE FROM {term_synonym} WHERE tid = %d', $tid);
 400        db_query('DELETE FROM {term_node} WHERE tid = %d', $tid);
 401  
 402        module_invoke_all('taxonomy', 'delete', 'term', $term);
 403      }
 404  
 405      $tids = $orphans;
 406    }
 407  
 408    cache_clear_all();
 409  
 410    return SAVED_DELETED;
 411  }
 412  
 413  /**
 414   * Generate a form element for selecting terms from a vocabulary.
 415   *
 416   * @param $vid
 417   *   The vocabulary ID to generate a form element for.
 418   * @param $value
 419   *   The existing value of the term(s) in this vocabulary to use by default.
 420   * @param $help
 421   *   Optional help text to use for the form element. If specified, this value
 422   *   MUST be properly sanitized and filtered (e.g. with filter_xss_admin() or
 423   *   check_plain() if it is user-supplied) to prevent XSS vulnerabilities. If
 424   *   omitted, the help text stored with the vocaulary (if any) will be used.
 425   * @return
 426   *   An array describing a form element to select terms for a vocabulary.
 427   *
 428   * @see _taxonomy_term_select()
 429   * @see filter_xss_admin()
 430   */
 431  function taxonomy_form($vid, $value = 0, $help = NULL, $name = 'taxonomy') {
 432    $vocabulary = taxonomy_vocabulary_load($vid);
 433    $help = ($help) ? $help : filter_xss_admin($vocabulary->help);
 434  
 435    if (!$vocabulary->multiple) {
 436      $blank = ($vocabulary->required) ? t('- Please choose -') : t('- None selected -');
 437    }
 438    else {
 439      $blank = ($vocabulary->required) ? 0 : t('- None -');
 440    }
 441  
 442    return _taxonomy_term_select(check_plain($vocabulary->name), $name, $value, $vid, $help, intval($vocabulary->multiple), $blank);
 443  }
 444  
 445  /**
 446   * Generate a set of options for selecting a term from all vocabularies.
 447   */
 448  function taxonomy_form_all($free_tags = 0) {
 449    $vocabularies = taxonomy_get_vocabularies();
 450    $options = array();
 451    foreach ($vocabularies as $vid => $vocabulary) {
 452      if ($vocabulary->tags && !$free_tags) { continue; }
 453      $tree = taxonomy_get_tree($vid);
 454      if ($tree && (count($tree) > 0)) {
 455        $options[$vocabulary->name] = array();
 456        foreach ($tree as $term) {
 457          $options[$vocabulary->name][$term->tid] = str_repeat('-', $term->depth) . $term->name;
 458        }
 459      }
 460    }
 461    return $options;
 462  }
 463  
 464  /**
 465   * Return an array of all vocabulary objects.
 466   *
 467   * @param $type
 468   *   If set, return only those vocabularies associated with this node type.
 469   */
 470  function taxonomy_get_vocabularies($type = NULL) {
 471    if ($type) {
 472      $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);
 473    }
 474    else {
 475      $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'));
 476    }
 477  
 478    $vocabularies = array();
 479    $node_types = array();
 480    while ($voc = db_fetch_object($result)) {
 481      // If no node types are associated with a vocabulary, the LEFT JOIN will
 482      // return a NULL value for type.
 483      if (isset($voc->type)) {
 484        $node_types[$voc->vid][$voc->type] = $voc->type;
 485        unset($voc->type);
 486        $voc->nodes = $node_types[$voc->vid];
 487      }
 488      elseif (!isset($voc->nodes)) {
 489        $voc->nodes = array();
 490      }
 491      $vocabularies[$voc->vid] = $voc;
 492    }
 493  
 494    return $vocabularies;
 495  }
 496  
 497  /**
 498   * Implementation of hook_form_alter().
 499   * Generate a form for selecting terms to associate with a node.
 500   * We check for taxonomy_override_selector before loading the full
 501   * vocabulary, so contrib modules can intercept before hook_form_alter
 502   *  and provide scalable alternatives.
 503   */
 504  function taxonomy_form_alter(&$form, $form_state, $form_id) {
 505    if (isset($form['type']) && isset($form['#node']) && (!variable_get('taxonomy_override_selector', FALSE)) && $form['type']['#value'] .'_node_form' == $form_id) {
 506      $node = $form['#node'];
 507  
 508      if (!isset($node->taxonomy)) {
 509        $terms = empty($node->nid) ? array() : taxonomy_node_get_terms($node);
 510      }
 511      else {
 512        // After a preview or form reload, the terms must be converted to objects.
 513        reset($node->taxonomy);
 514        if (!is_object(current($node->taxonomy))) {
 515          $node->taxonomy = taxonomy_preview_terms($node);
 516        }
 517        $terms = $node->taxonomy;
 518      }
 519  
 520      $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);
 521  
 522      while ($vocabulary = db_fetch_object($c)) {
 523        if ($vocabulary->tags) {
 524          if (isset($form_state['node_preview'])) {
 525            // Typed string can be changed by the user before preview,
 526            // so we just insert the tags directly as provided in the form.
 527            $typed_string = $node->taxonomy['tags'][$vocabulary->vid];
 528          }
 529          else {
 530            $typed_string = taxonomy_implode_tags($terms, $vocabulary->vid) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL);
 531          }
 532          if ($vocabulary->help) {
 533            $help = filter_xss_admin($vocabulary->help);
 534          }
 535          else {
 536            $help = t('A comma-separated list of terms describing this content. Example: funny, bungee jumping, "Company, Inc.".');
 537          }
 538          $form['taxonomy']['tags'][$vocabulary->vid] = array('#type' => 'textfield',
 539            '#title' => $vocabulary->name,
 540            '#description' => $help,
 541            '#required' => $vocabulary->required,
 542            '#default_value' => $typed_string,
 543            '#autocomplete_path' => 'taxonomy/autocomplete/'. $vocabulary->vid,
 544            '#weight' => $vocabulary->weight,
 545            '#maxlength' => 1024,
 546          );
 547        }
 548        else {
 549          // Extract terms belonging to the vocabulary in question.
 550          $default_terms = array();
 551          foreach ($terms as $term) {
 552            // Free tagging has no default terms and also no vid after preview.
 553            if (isset($term->vid) && $term->vid == $vocabulary->vid) {
 554              $default_terms[$term->tid] = $term;
 555            }
 556          }
 557          $form['taxonomy'][$vocabulary->vid] = taxonomy_form($vocabulary->vid, array_keys($default_terms), filter_xss_admin($vocabulary->help));
 558          $form['taxonomy'][$vocabulary->vid]['#weight'] = $vocabulary->weight;
 559          $form['taxonomy'][$vocabulary->vid]['#required'] = $vocabulary->required;
 560        }
 561      }
 562      if (!empty($form['taxonomy']) && is_array($form['taxonomy'])) {
 563        if (count($form['taxonomy']) > 1) {
 564          // Add fieldset only if form has more than 1 element.
 565          $form['taxonomy'] += array(
 566            '#type' => 'fieldset',
 567            '#title' => t('Vocabularies'),
 568            '#collapsible' => TRUE,
 569            '#collapsed' => FALSE,
 570          );
 571        }
 572        $form['taxonomy']['#weight'] = -3;
 573        $form['taxonomy']['#tree'] = TRUE;
 574      }
 575    }
 576  }
 577  
 578  /**
 579   * Helper function to convert terms after a preview.
 580   *
 581   * After preview the tags are an array instead of proper objects. This function
 582   * converts them back to objects with the exception of 'free tagging' terms,
 583   * because new tags can be added by the user before preview and those do not
 584   * yet exist in the database. We therefore save those tags as a string so
 585   * we can fill the form again after the preview.
 586   */
 587  function taxonomy_preview_terms($node) {
 588    $taxonomy = array();
 589    if (isset($node->taxonomy)) {
 590      foreach ($node->taxonomy as $key => $term) {
 591        unset($node->taxonomy[$key]);
 592        // A 'Multiple select' and a 'Free tagging' field returns an array.
 593        if (is_array($term)) {
 594          foreach ($term as $tid) {
 595            if ($key == 'tags') {
 596              // Free tagging; the values will be saved for later as strings
 597              // instead of objects to fill the form again.
 598              $taxonomy['tags'] = $term;
 599            }
 600            else {
 601              $taxonomy[$tid] = taxonomy_get_term($tid);
 602            }
 603          }
 604        }
 605        // A 'Single select' field returns the term id.
 606        elseif ($term) {
 607          $taxonomy[$term] = taxonomy_get_term($term);
 608        }
 609      }
 610    }
 611    return $taxonomy;
 612  }
 613  
 614  /**
 615   * Find all terms associated with the given node, within one vocabulary.
 616   */
 617  function taxonomy_node_get_terms_by_vocabulary($node, $vid, $key = 'tid') {
 618    $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.vid = %d ORDER BY weight', 't', 'tid'), $vid, $node->vid);
 619    $terms = array();
 620    while ($term = db_fetch_object($result)) {
 621      $terms[$term->$key] = $term;
 622    }
 623    return $terms;
 624  }
 625  
 626  /**
 627   * Find all terms associated with the given node, ordered by vocabulary and term weight.
 628   */
 629  function taxonomy_node_get_terms($node, $key = 'tid') {
 630    static $terms;
 631  
 632    if (!isset($terms[$node->vid][$key])) {
 633      $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.vid = %d ORDER BY v.weight, t.weight, t.name', 't', 'tid'), $node->vid);
 634      $terms[$node->vid][$key] = array();
 635      while ($term = db_fetch_object($result)) {
 636        $terms[$node->vid][$key][$term->$key] = $term;
 637      }
 638    }
 639    return $terms[$node->vid][$key];
 640  }
 641  
 642  /**
 643   * Make sure incoming vids are free tagging enabled.
 644   */
 645  function taxonomy_node_validate(&$node) {
 646    if (!empty($node->taxonomy)) {
 647      $terms = $node->taxonomy;
 648      if (!empty($terms['tags'])) {
 649        foreach ($terms['tags'] as $vid => $vid_value) {
 650          $vocabulary = taxonomy_vocabulary_load($vid);
 651          if (empty($vocabulary->tags)) {
 652            // see form_get_error $key = implode('][', $element['#parents']);
 653            // on why this is the key
 654            form_set_error("taxonomy][tags][$vid", t('The %name vocabulary can not be modified in this way.', array('%name' => $vocabulary->name)));
 655          }
 656        }
 657      }
 658    }
 659  }
 660  
 661  /**
 662   * Save term associations for a given node.
 663   */
 664  function taxonomy_node_save($node, $terms) {
 665  
 666    taxonomy_node_delete_revision($node);
 667  
 668    // Free tagging vocabularies do not send their tids in the form,
 669    // so we'll detect them here and process them independently.
 670    if (isset($terms['tags'])) {
 671      $typed_input = $terms['tags'];
 672      unset($terms['tags']);
 673  
 674      foreach ($typed_input as $vid => $vid_value) {
 675        $typed_terms = drupal_explode_tags($vid_value);
 676  
 677        $inserted = array();
 678        foreach ($typed_terms as $typed_term) {
 679          // See if the term exists in the chosen vocabulary
 680          // and return the tid; otherwise, add a new record.
 681          $possibilities = taxonomy_get_term_by_name($typed_term);
 682          $typed_term_tid = NULL; // tid match, if any.
 683          foreach ($possibilities as $possibility) {
 684            if ($possibility->vid == $vid) {
 685              $typed_term_tid = $possibility->tid;
 686            }
 687          }
 688  
 689          if (!$typed_term_tid) {
 690            $edit = array('vid' => $vid, 'name' => $typed_term);
 691            $status = taxonomy_save_term($edit);
 692            $typed_term_tid = $edit['tid'];
 693          }
 694  
 695          // Defend against duplicate, differently cased tags
 696          if (!isset($inserted[$typed_term_tid])) {
 697            db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $typed_term_tid);
 698            $inserted[$typed_term_tid] = TRUE;
 699          }
 700        }
 701      }
 702    }
 703  
 704    if (is_array($terms)) {
 705      foreach ($terms as $term) {
 706        if (is_array($term)) {
 707          foreach ($term as $tid) {
 708            if ($tid) {
 709              db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $tid);
 710            }
 711          }
 712        }
 713        else if (is_object($term)) {
 714          db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $term->tid);
 715        }
 716        else if ($term) {
 717          db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $term);
 718        }
 719      }
 720    }
 721  }
 722  
 723  /**
 724   * Remove associations of a node to its terms.
 725   */
 726  function taxonomy_node_delete($node) {
 727    db_query('DELETE FROM {term_node} WHERE nid = %d', $node->nid);
 728  }
 729  
 730  /**
 731   * Remove associations of a node to its terms.
 732   */
 733  function taxonomy_node_delete_revision($node) {
 734    db_query('DELETE FROM {term_node} WHERE vid = %d', $node->vid);
 735  }
 736  
 737  /**
 738   * Implementation of hook_node_type().
 739   */
 740  function taxonomy_node_type($op, $info) {
 741    if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) {
 742      db_query("UPDATE {vocabulary_node_types} SET type = '%s' WHERE type = '%s'", $info->type, $info->old_type);
 743    }
 744    elseif ($op == 'delete') {
 745      db_query("DELETE FROM {vocabulary_node_types} WHERE type = '%s'", $info->type);
 746    }
 747  }
 748  
 749  /**
 750   * Find all term objects related to a given term ID.
 751   */
 752  function taxonomy_get_related($tid, $key = 'tid') {
 753    if ($tid) {
 754      $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);
 755      $related = array();
 756      while ($term = db_fetch_object($result)) {
 757        $related[$term->$key] = $term;
 758      }
 759      return $related;
 760    }
 761    else {
 762      return array();
 763    }
 764  }
 765  
 766  /**
 767   * Find all parents of a given term ID.
 768   */
 769  function taxonomy_get_parents($tid, $key = 'tid') {
 770    if ($tid) {
 771      $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);
 772      $parents = array();
 773      while ($parent = db_fetch_object($result)) {
 774        $parents[$parent->$key] = $parent;
 775      }
 776      return $parents;
 777    }
 778    else {
 779      return array();
 780    }
 781  }
 782  
 783  /**
 784   * Find all ancestors of a given term ID.
 785   */
 786  function taxonomy_get_parents_all($tid) {
 787    $parents = array();
 788    if ($tid) {
 789      $parents[] = taxonomy_get_term($tid);
 790      $n = 0;
 791      while ($parent = taxonomy_get_parents($parents[$n]->tid)) {
 792        $parents = array_merge($parents, $parent);
 793        $n++;
 794      }
 795    }
 796    return $parents;
 797  }
 798  
 799  /**
 800   * Find all children of a term ID.
 801   */
 802  function taxonomy_get_children($tid, $vid = 0, $key = 'tid') {
 803    if ($vid) {
 804      $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);
 805    }
 806    else {
 807      $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);
 808    }
 809    $children = array();
 810    while ($term = db_fetch_object($result)) {
 811      $children[$term->$key] = $term;
 812    }
 813    return $children;
 814  }
 815  
 816  /**
 817   * Create a hierarchical representation of a vocabulary.
 818   *
 819   * @param $vid
 820   *   Which vocabulary to generate the tree for.
 821   *
 822   * @param $parent
 823   *   The term ID under which to generate the tree. If 0, generate the tree
 824   *   for the entire vocabulary.
 825   *
 826   * @param $depth
 827   *   Internal use only.
 828   *
 829   * @param $max_depth
 830   *   The number of levels of the tree to return. Leave NULL to return all levels.
 831   *
 832   * @return
 833   *   An array of all term objects in the tree. Each term object is extended
 834   *   to have "depth" and "parents" attributes in addition to its normal ones.
 835   *   Results are statically cached.
 836   */
 837  function taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL) {
 838    static $children, $parents, $terms;
 839  
 840    $depth++;
 841  
 842    // We cache trees, so it's not CPU-intensive to call get_tree() on a term
 843    // and its children, too.
 844    if (!isset($children[$vid])) {
 845      $children[$vid] = array();
 846  
 847      $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);
 848      while ($term = db_fetch_object($result)) {
 849        $children[$vid][$term->parent][] = $term->tid;
 850        $parents[$vid][$term->tid][] = $term->parent;
 851        $terms[$vid][$term->tid] = $term;
 852      }
 853    }
 854  
 855    $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
 856    $tree = array();
 857    if ($max_depth > $depth && !empty($children[$vid][$parent])) {
 858      foreach ($children[$vid][$parent] as $child) {
 859        $term = drupal_clone($terms[$vid][$child]);
 860        $term->depth = $depth;
 861        // The "parent" attribute is not useful, as it would show one parent only.
 862        unset($term->parent);
 863        $term->parents = $parents[$vid][$child];
 864        $tree[] = $term;
 865        if (!empty($children[$vid][$child])) {
 866          $tree = array_merge($tree, taxonomy_get_tree($vid, $child, $depth, $max_depth));
 867        }
 868      }
 869    }
 870  
 871    return $tree;
 872  }
 873  
 874  /**
 875   * Return an array of synonyms of the given term ID.
 876   */
 877  function taxonomy_get_synonyms($tid) {
 878    if ($tid) {
 879      $synonyms = array();
 880      $result = db_query('SELECT name FROM {term_synonym} WHERE tid = %d', $tid);
 881      while ($synonym = db_fetch_array($result)) {
 882        $synonyms[] = $synonym['name'];
 883      }
 884      return $synonyms;
 885    }
 886    else {
 887      return array();
 888    }
 889  }
 890  
 891  /**
 892   * Return the term object that has the given string as a synonym.
 893   */
 894  function taxonomy_get_synonym_root($synonym) {
 895    return db_fetch_object(db_query("SELECT * FROM {term_synonym} s, {term_data} t WHERE t.tid = s.tid AND s.name = '%s'", $synonym));
 896  }
 897  
 898  /**
 899   * Count the number of published nodes classified by a term.
 900   *
 901   * @param $tid
 902   *   The term's ID
 903   *
 904   * @param $type
 905   *   The $node->type. If given, taxonomy_term_count_nodes only counts
 906   *   nodes of $type that are classified with the term $tid.
 907   *
 908   * @return int
 909   *   An integer representing a number of nodes.
 910   *   Results are statically cached.
 911   */
 912  function taxonomy_term_count_nodes($tid, $type = 0) {
 913    static $count;
 914  
 915    if (!isset($count[$type])) {
 916      // $type == 0 always evaluates TRUE if $type is a string
 917      if (is_numeric($type)) {
 918        $result = db_query(db_rewrite_sql('SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.vid = n.vid WHERE n.status = 1 GROUP BY t.tid'));
 919      }
 920      else {
 921        $result = db_query(db_rewrite_sql("SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.vid = n.vid WHERE n.status = 1 AND n.type = '%s' GROUP BY t.tid"), $type);
 922      }
 923      $count[$type] = array();
 924      while ($term = db_fetch_object($result)) {
 925        $count[$type][$term->tid] = $term->c;
 926      }
 927    }
 928    $children_count = 0;
 929    foreach (_taxonomy_term_children($tid) as $c) {
 930      $children_count += taxonomy_term_count_nodes($c, $type);
 931    }
 932    return $children_count + (isset($count[$type][$tid]) ? $count[$type][$tid] : 0);
 933  }
 934  
 935  /**
 936   * Helper for taxonomy_term_count_nodes(). Used to find out
 937   * which terms are children of a parent term.
 938   *
 939   * @param $tid
 940   *   The parent term's ID
 941   *
 942   * @return array
 943   *   An array of term IDs representing the children of $tid.
 944   *   Results are statically cached.
 945   *
 946   */
 947  function _taxonomy_term_children($tid) {
 948    static $children;
 949  
 950    if (!isset($children)) {
 951      $result = db_query('SELECT tid, parent FROM {term_hierarchy}');
 952      while ($term = db_fetch_object($result)) {
 953        $children[$term->parent][] = $term->tid;
 954      }
 955    }
 956    return isset($children[$tid]) ? $children[$tid] : array();
 957  }
 958  
 959  /**
 960   * Try to map a string to an existing term, as for glossary use.
 961   *
 962   * Provides a case-insensitive and trimmed mapping, to maximize the
 963   * likelihood of a successful match.
 964   *
 965   * @param name
 966   *   Name of the term to search for.
 967   *
 968   * @return
 969   *   An array of matching term objects.
 970   */
 971  function taxonomy_get_term_by_name($name) {
 972    $db_result = db_query(db_rewrite_sql("SELECT t.tid, t.* FROM {term_data} t WHERE LOWER(t.name) = LOWER('%s')", 't', 'tid'), trim($name));
 973    $result = array();
 974    while ($term = db_fetch_object($db_result)) {
 975      $result[] = $term;
 976    }
 977  
 978    return $result;
 979  }
 980  
 981  /**
 982   * Return the vocabulary object matching a vocabulary ID.
 983   *
 984   * @param $vid
 985   *   The vocabulary's ID
 986   * @param $reset
 987   *   Whether to reset the internal taxonomy_vocabulary_load cache.
 988   *
 989   * @return
 990   *   The vocabulary object with all of its metadata, if exists, FALSE otherwise.
 991   *   Results are statically cached.
 992   */
 993  function taxonomy_vocabulary_load($vid, $reset = FALSE) {
 994    static $vocabularies = array();
 995  
 996    if ($reset) {
 997      $vocabularies = array();
 998    }
 999  
1000    if (!isset($vocabularies[$vid])) {
1001      // Initialize so if this vocabulary does not exist, we have
1002      // that cached, and we will not try to load this later.
1003      $vocabularies[$vid] = FALSE;
1004      // Try to load the data and fill up the object.
1005      $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', $vid);
1006      $node_types = array();
1007      while ($voc = db_fetch_object($result)) {
1008        if (!empty($voc->type)) {
1009          $node_types[$voc->type] = $voc->type;
1010        }
1011        unset($voc->type);
1012        $voc->nodes = $node_types;
1013        $vocabularies[$vid] = $voc;
1014      }
1015    }
1016  
1017    // Return FALSE if this vocabulary does not exist.
1018    return !empty($vocabularies[$vid]) ? $vocabularies[$vid] : FALSE;
1019  }
1020  
1021  /**
1022   * Return the term object matching a term ID.
1023   *
1024   * @param $tid
1025   *   A term's ID
1026   * @param $reset
1027   *   Whether to reset the internal taxonomy_get_term cache.
1028   *
1029   * @return Object
1030   *   A term object. Results are statically cached.
1031   */
1032  function taxonomy_get_term($tid, $reset = FALSE) {
1033    static $terms = array();
1034  
1035    if ($reset) {
1036      $terms = array();
1037    }
1038  
1039    if (!isset($terms[$tid])) {
1040      $terms[$tid] = db_fetch_object(db_query('SELECT * FROM {term_data} WHERE tid = %d', $tid));
1041    }
1042  
1043    return $terms[$tid];
1044  }
1045  
1046  /**
1047   * Create a select form element for a given taxonomy vocabulary.
1048   *
1049   * NOTE: This function expects input that has already been sanitized and is
1050   * safe for display. Callers must properly sanitize the $title and
1051   * $description arguments to prevent XSS vulnerabilities.
1052   *
1053   * @param $title
1054   *   The title of the vocabulary. This MUST be sanitized by the caller.
1055   * @param $name
1056   *   Ignored.
1057   * @param $value
1058   *   The currently selected terms from this vocabulary, if any.
1059   * @param $vocabulary_id
1060   *   The vocabulary ID to build the form element for.
1061   * @param $description
1062   *   Help text for the form element. This MUST be sanitized by the caller.
1063   * @param $multiple
1064   *   Boolean to control if the form should use a single or multiple select.
1065   * @param $blank
1066   *   Optional form choice to use when no value has been selected.
1067   * @param $exclude
1068   *   Optional array of term ids to exclude in the selector.
1069   * @return
1070   *   A FAPI form array to select terms from the given vocabulary.
1071   *
1072   * @see taxonomy_form()
1073   * @see taxonomy_form_term()
1074   */
1075  function _taxonomy_term_select($title, $name, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) {
1076    $tree = taxonomy_get_tree($vocabulary_id);
1077    $options = array();
1078  
1079    if ($blank) {
1080      $options[''] = $blank;
1081    }
1082    if ($tree) {
1083      foreach ($tree as $term) {
1084        if (!in_array($term->tid, $exclude)) {
1085          $choice = new stdClass();
1086          $choice->option = array($term->tid => str_repeat('-', $term->depth) . $term->name);
1087          $options[] = $choice;
1088        }
1089      }
1090    }
1091  
1092    return array('#type' => 'select',
1093      '#title' => $title,
1094      '#default_value' => $value,
1095      '#options' => $options,
1096      '#description' => $description,
1097      '#multiple' => $multiple,
1098      '#size' => $multiple ? min(9, count($options)) : 0,
1099      '#weight' => -15,
1100      '#theme' => 'taxonomy_term_select',
1101    );
1102  }
1103  
1104  /**
1105   * Format the selection field for choosing terms
1106   * (by deafult the default selection field is used).
1107   *
1108   * @ingroup themeable
1109   */
1110  function theme_taxonomy_term_select($element) {
1111    return theme('select', $element);
1112  }
1113  
1114  /**
1115   * Finds all nodes that match selected taxonomy conditions.
1116   *
1117   * @param $tids
1118   *   An array of term IDs to match.
1119   * @param $operator
1120   *   How to interpret multiple IDs in the array. Can be "or" or "and".
1121   * @param $depth
1122   *   How many levels deep to traverse the taxonomy tree. Can be a nonnegative
1123   *   integer or "all".
1124   * @param $pager
1125   *   Whether the nodes are to be used with a pager (the case on most Drupal
1126   *   pages) or not (in an XML feed, for example).
1127   * @param $order
1128   *   The order clause for the query that retrieve the nodes.
1129   * @return
1130   *   A resource identifier pointing to the query results.
1131   */
1132  function taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $order = 'n.sticky DESC, n.created DESC') {
1133    if (count($tids) > 0) {
1134      // For each term ID, generate an array of descendant term IDs to the right depth.
1135      $descendant_tids = array();
1136      if ($depth === 'all') {
1137        $depth = NULL;
1138      }
1139      foreach ($tids as $index => $tid) {
1140        $term = taxonomy_get_term($tid);
1141        $tree = taxonomy_get_tree($term->vid, $tid, -1, $depth);
1142        $descendant_tids[] = array_merge(array($tid), array_map('_taxonomy_get_tid_from_term', $tree));
1143      }
1144  
1145      if ($operator == 'or') {
1146        $args = call_user_func_array('array_merge', $descendant_tids);
1147        $placeholders = db_placeholders($args, 'int');
1148        $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n INNER JOIN {term_node} tn ON n.vid = tn.vid WHERE tn.tid IN ('. $placeholders .') AND n.status = 1 ORDER BY '. $order;
1149        $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n INNER JOIN {term_node} tn ON n.vid = tn.vid WHERE tn.tid IN ('. $placeholders .') AND n.status = 1';
1150      }
1151      else {
1152        $joins = '';
1153        $wheres = '';
1154        $args = array();
1155        foreach ($descendant_tids as $index => $tids) {
1156          $joins .= ' INNER JOIN {term_node} tn'. $index .' ON n.vid = tn'. $index .'.vid';
1157          $wheres .= ' AND tn'. $index .'.tid IN ('. db_placeholders($tids, 'int') .')';
1158          $args = array_merge($args, $tids);
1159        }
1160        $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n '. $joins .' WHERE n.status = 1 '. $wheres .' ORDER BY '. $order;
1161        $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n '. $joins .' WHERE n.status = 1 '. $wheres;
1162      }
1163      $sql = db_rewrite_sql($sql);
1164      $sql_count = db_rewrite_sql($sql_count);
1165      if ($pager) {
1166        $result = pager_query($sql, variable_get('default_nodes_main', 10), 0, $sql_count, $args);
1167      }
1168      else {
1169        $result = db_query_range($sql, $args, 0, variable_get('feed_default_items', 10));
1170      }
1171    }
1172  
1173    return $result;
1174  }
1175  
1176  /**
1177   * Accepts the result of a pager_query() call, such as that performed by
1178   * taxonomy_select_nodes(), and formats each node along with a pager.
1179   */
1180  function taxonomy_render_nodes($result) {
1181    $output = '';
1182    $has_rows = FALSE;
1183    while ($node = db_fetch_object($result)) {
1184      $output .= node_view(node_load($node->nid), 1);
1185      $has_rows = TRUE;
1186    }
1187    if ($has_rows) {
1188      $output .= theme('pager', NULL, variable_get('default_nodes_main', 10), 0);
1189    }
1190    else {
1191      $output .= '<p>'. t('There are currently no posts in this category.') .'</p>';
1192    }
1193    return $output;
1194  }
1195  
1196  /**
1197   * Implementation of hook_nodeapi().
1198   */
1199  function taxonomy_nodeapi($node, $op, $arg = 0) {
1200    switch ($op) {
1201      case 'load':
1202        $output['taxonomy'] = taxonomy_node_get_terms($node);
1203        return $output;
1204  
1205      case 'insert':
1206        if (!empty($node->taxonomy)) {
1207          taxonomy_node_save($node, $node->taxonomy);
1208        }
1209        break;
1210  
1211      case 'update':
1212        if (!empty($node->taxonomy)) {
1213          taxonomy_node_save($node, $node->taxonomy);
1214        }
1215        break;
1216  
1217      case 'delete':
1218        taxonomy_node_delete($node);
1219        break;
1220  
1221      case 'delete revision':
1222        taxonomy_node_delete_revision($node);
1223        break;
1224  
1225      case 'validate':
1226        taxonomy_node_validate($node);
1227        break;
1228  
1229      case 'rss item':
1230        return taxonomy_rss_item($node);
1231  
1232      case 'update index':
1233        return taxonomy_node_update_index($node);
1234    }
1235  }
1236  
1237  /**
1238   * Implementation of hook_nodeapi('update_index').
1239   */
1240  function taxonomy_node_update_index(&$node) {
1241    $output = array();
1242    foreach ($node->taxonomy as $term) {
1243      $output[] = $term->name;
1244    }
1245    if (count($output)) {
1246      return '<strong>('. implode(', ', $output) .')</strong>';
1247    }
1248  }
1249  
1250  /**
1251   * Parses a comma or plus separated string of term IDs.
1252   *
1253   * @param $str_tids
1254   *   A string of term IDs, separated by plus or comma.
1255   *   comma (,) means AND
1256   *   plus (+) means OR
1257   *
1258   * @return an associative array with an operator key (either 'and'
1259   *   or 'or') and a tid key containing an array of the term ids.
1260   */
1261  function taxonomy_terms_parse_string($str_tids) {
1262    $terms = array('operator' => '', 'tids' => array());
1263    if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str_tids)) {
1264      $terms['operator'] = 'or';
1265      // The '+' character in a query string may be parsed as ' '.
1266      $terms['tids'] = preg_split('/[+ ]/', $str_tids);
1267    }
1268    else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str_tids)) {
1269      $terms['operator'] = 'and';
1270      $terms['tids'] = explode(',', $str_tids);
1271    }
1272    return $terms;
1273  }
1274  
1275  /**
1276   * Provides category information for RSS feeds.
1277   */
1278  function taxonomy_rss_item($node) {
1279    $output = array();
1280    foreach ($node->taxonomy as $term) {
1281      $output[] = array('key'   => 'category',
1282                        'value' => $term->name,
1283                        'attributes' => array('domain' => url('taxonomy/term/'. $term->tid, array('absolute' => TRUE))));
1284    }
1285    return $output;
1286  }
1287  
1288  /**
1289   * Implementation of hook_help().
1290   */
1291  function taxonomy_help($path, $arg) {
1292    switch ($path) {
1293      case 'admin/help#taxonomy':
1294        $output = '<p>'. t('The taxonomy module allows you to categorize content using various systems of classification. Free-tagging vocabularies are created by users on the fly when they submit posts (as commonly found in blogs and social bookmarking applications). Controlled vocabularies allow for administrator-defined short lists of terms as well as complex hierarchies with multiple relationships between different terms. These methods can be applied to different content types and combined together to create a powerful and flexible method of classifying and presenting your content.') .'</p>';
1295        $output .= '<p>'. t('For example, when creating a recipe site, you might want to classify posts by both the type of meal and preparation time. A vocabulary for each allows you to categorize using each criteria independently instead of creating a tag for every possible combination.') .'</p>';
1296        $output .= '<p>'. t('Type of Meal: <em>Appetizer, Main Course, Salad, Dessert</em>') .'</p>';
1297        $output .= '<p>'. t('Preparation Time: <em>0-30mins, 30-60mins, 1-2 hrs, 2hrs+</em>') .'</p>';
1298        $output .= '<p>'. t("Each taxonomy term (often called a 'category' or 'tag' in other systems) automatically provides lists of posts and a corresponding RSS feed. These taxonomy/term URLs can be manipulated to generate AND and OR lists of posts classified with terms. In our recipe site example, it then becomes easy to create pages displaying 'Main courses', '30 minute recipes', or '30 minute main courses and appetizers' by using terms on their own or in combination with others. There are a significant number of contributed modules which you to alter and extend the behavior of the core module for both display and organization of terms.") .'</p>';
1299        $output .= '<p>'. t("Terms can also be organized in parent/child relationships from the admin interface. An example would be a vocabulary grouping countries under their parent geo-political regions. The taxonomy module also enables advanced implementations of hierarchy, for example placing Turkey in both the 'Middle East' and 'Europe'.") .'</p>';
1300        $output .= '<p>'. t('The taxonomy module supports the use of both synonyms and related terms, but does not directly use this functionality. However, optional contributed or custom modules may make full use of these advanced features.') .'</p>';
1301        $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@taxonomy">Taxonomy module</a>.', array('@taxonomy' => 'http://drupal.org/handbook/modules/taxonomy/')) .'</p>';
1302        return $output;
1303      case 'admin/content/taxonomy':
1304        $output = '<p>'. t("The taxonomy module allows you to categorize your content using both tags and administrator defined terms. It is a flexible tool for classifying content with many advanced features. To begin, create a 'Vocabulary' to hold one set of terms or tags. You can create one free-tagging vocabulary for everything, or separate controlled vocabularies to define the various properties of your content, for example 'Countries' or 'Colors'.") .'</p>';
1305        $output .= '<p>'. t('Use the list below to configure and review the vocabularies defined on your site, or to list and manage the terms (tags) they contain. A vocabulary may (optionally) be tied to specific content types as shown in the <em>Type</em> column and, if so, will be displayed when creating or editing posts of that type. Multiple vocabularies tied to the same content type will be displayed in the order shown below. To change the order of a vocabulary, grab a drag-and-drop handle under the <em>Name</em> column and drag it to a new location in the list. (Grab a handle by clicking and holding the mouse while hovering over a handle icon.) Remember that your changes will not be saved until you click the <em>Save</em> button at the bottom of the page.') .'</p>';
1306        return $output;
1307      case 'admin/content/taxonomy/%':
1308        $vocabulary = taxonomy_vocabulary_load($arg[3]);
1309        if ($vocabulary->tags) {
1310          return '<p>'. t('%capital_name is a free-tagging vocabulary. To change the name or description of a term, click the <em>edit</em> link next to the term.', array('%capital_name' => drupal_ucfirst($vocabulary->name))) .'</p>';
1311        }
1312        switch ($vocabulary->hierarchy) {
1313          case 0:
1314            return '<p>'. t('%capital_name is a flat vocabulary. You may organize the terms in the %name vocabulary by using the handles on the left side of the table. To change the name or description of a term, click the <em>edit</em> link next to the term.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name)) .'</p>';
1315          case 1:
1316            return '<p>'. t('%capital_name is a single hierarchy vocabulary. You may organize the terms in the %name vocabulary by using the handles on the left side of the table. To change the name or description of a term, click the <em>edit</em> link next to the term.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name)) .'</p>';
1317          case 2:
1318            return '<p>'. t('%capital_name is a multiple hierarchy vocabulary. To change the name or description of a term, click the <em>edit</em> link next to the term. Drag and drop of multiple hierarchies is not supported, but you can re-enable drag and drop support by editing each term to include only a single parent.', array('%capital_name' => drupal_ucfirst($vocabulary->name))) .'</p>';
1319        }
1320      case 'admin/content/taxonomy/add/vocabulary':
1321        return '<p>'. t('Define how your vocabulary will be presented to administrators and users, and which content types to categorize with it. Tags allows users to create terms when submitting posts by typing a comma separated list. Otherwise terms are chosen from a select list and can only be created by users with the "administer taxonomy" permission.') .'</p>';
1322    }
1323  }
1324  
1325  /**
1326   * Helper function for array_map purposes.
1327   */
1328  function _taxonomy_get_tid_from_term($term) {
1329    return $term->tid;
1330  }
1331  
1332  /**
1333   * Implodes a list of tags of a certain vocabulary into a string.
1334   *
1335   * @see drupal_explode_tags()
1336   */
1337  function taxonomy_implode_tags($tags, $vid = NULL) {
1338    $typed_tags = array();
1339    foreach ($tags as $tag) {
1340      // Extract terms belonging to the vocabulary in question.
1341      if (is_null($vid) || $tag->vid == $vid) {
1342  
1343        // Commas and quotes in tag names are special cases, so encode 'em.
1344        if (strpos($tag->name, ',') !== FALSE || strpos($tag->name, '"') !== FALSE) {
1345          $tag->name = '"'. str_replace('"', '""', $tag->name) .'"';
1346        }
1347  
1348        $typed_tags[] = $tag->name;
1349      }
1350    }
1351    return implode(', ', $typed_tags);
1352  }
1353  
1354  /**
1355   * Implementation of hook_hook_info().
1356   */
1357  function taxonomy_hook_info() {
1358    return array(
1359      'taxonomy' => array(
1360        'taxonomy' => array(
1361          'insert' => array(
1362            'runs when' => t('After saving a new term to the database'),
1363          ),
1364          'update' => array(
1365            'runs when' => t('After saving an updated term to the database'),
1366          ),
1367          'delete' => array(
1368            'runs when' => t('After deleting a term')
1369          ),
1370        ),
1371      ),
1372    );
1373  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7