source: sipes/cord/modules/taxonomy/taxonomy.module @ 8a8efa8

stableversion-3.0
Last change on this file since 8a8efa8 was d7a822e, checked in by José Gregorio Puentes <jpuentes@…>, 8 años ago

se agrego el directorio del cord

  • Propiedad mode establecida a 100755
File size: 49.4 KB
Línea 
1<?php
2
3/**
4 * @file
5 * Enables the organization of content into categories.
6 */
7
8/**
9 * Implementation of hook_perm().
10 */
11function taxonomy_perm() {
12  return array('administer taxonomy');
13}
14
15/**
16 * Implementation of hook_theme().
17 */
18function 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 */
47function 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 */
98function 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 */
109function 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
196function 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 */
237function 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 */
273function 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 */
306function 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 */
381function 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 */
434function 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 */
451function 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 */
473function 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 */
507function 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 */
590function 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 */
620function 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 */
632function 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 */
652function 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 */
671function 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 */
736function 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 */
743function 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 */
750function 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 */
762function 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 */
779function 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 */
796function 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 */
812function 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 */
848function 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 */
928function 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 */
945function 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 */
963function 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 */
998function _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 */
1022function 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 */
1044function 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 */
1083function 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 */
1126function _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 */
1161function 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 */
1183function 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 */
1231function 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 */
1250function 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 */
1291function 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 */
1312function 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 */
1329function 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 */
1342function 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 */
1379function _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 */
1388function 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 */
1408function 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}
Nota: Vea TracBrowser para ayuda de uso del navegador del repositorio.