[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

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

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


Generated: Mon Jul 9 18:01:44 2012 Cross-referenced by PHPXref 0.7