source: sipes/cord/modules/taxonomy/taxonomy.module @ b354002

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

se agrego el directorio del cord

  • Propiedad mode establecida a 100755
File size: 47.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 *
257 * Checks the current parents of all terms in a vocabulary and updates the
258 * vocabularies hierarchy setting to the lowest possible level. A hierarchy with
259 * no parents in any of its terms will be given a hierarchy of 0. If terms
260 * contain at most a single parent, the vocabulary will be given a hierarchy of
261 * 1. If any term contain multiple parents, the vocabulary will be given a
262 * hieararchy of 2.
263 *
264 * @param $vocabulary
265 *   An array of the vocabulary structure.
266 * @param $changed_term
267 *   An array of the term structure that was updated.
268 */
269function taxonomy_check_vocabulary_hierarchy($vocabulary, $changed_term) {
270  $tree = taxonomy_get_tree($vocabulary['vid']);
271  $hierarchy = 0;
272  foreach ($tree as $term) {
273    // Update the changed term with the new parent value before comparision.
274    if ($term->tid == $changed_term['tid']) {
275      $term = (object)$changed_term;
276      $term->parents = $term->parent;
277    }
278    // Check this term's parent count.
279    if (count($term->parents) > 1) {
280      $hierarchy = 2;
281      break;
282    }
283    elseif (count($term->parents) == 1 && 0 !== array_shift($term->parents)) {
284      $hierarchy = 1;
285    }
286  }
287  if ($hierarchy != $vocabulary['hierarchy']) {
288    $vocabulary['hierarchy'] = $hierarchy;
289    taxonomy_save_vocabulary($vocabulary);
290  }
291
292  return $hierarchy;
293}
294
295/**
296 * Helper function for taxonomy_form_term_submit().
297 *
298 * @param $form_state['values']
299 * @return
300 *   Status constant indicating if term was inserted or updated.
301 */
302function taxonomy_save_term(&$form_values) {
303  $form_values += array(
304    'description' => '',
305    'weight' => 0
306  );
307
308  if (!empty($form_values['tid']) && $form_values['name']) {
309    drupal_write_record('term_data', $form_values, 'tid');
310    $hook = 'update';
311    $status = SAVED_UPDATED;
312  }
313  else if (!empty($form_values['tid'])) {
314    return taxonomy_del_term($form_values['tid']);
315  }
316  else {
317    drupal_write_record('term_data', $form_values);
318    $hook = 'insert';
319    $status = SAVED_NEW;
320  }
321
322  db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $form_values['tid'], $form_values['tid']);
323  if (!empty($form_values['relations'])) {
324    foreach ($form_values['relations'] as $related_id) {
325      if ($related_id != 0) {
326        db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $form_values['tid'], $related_id);
327      }
328    }
329  }
330
331  db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $form_values['tid']);
332  if (!isset($form_values['parent']) || empty($form_values['parent'])) {
333    $form_values['parent'] = array(0);
334  }
335  if (is_array($form_values['parent'])) {
336    foreach ($form_values['parent'] as $parent) {
337      if (is_array($parent)) {
338        foreach ($parent as $tid) {
339          db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $tid);
340        }
341      }
342      else {
343        db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $parent);
344      }
345    }
346  }
347  else {
348    db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $form_values['parent']);
349  }
350
351  db_query('DELETE FROM {term_synonym} WHERE tid = %d', $form_values['tid']);
352  if (!empty($form_values['synonyms'])) {
353    foreach (explode ("\n", str_replace("\r", '', $form_values['synonyms'])) as $synonym) {
354      if ($synonym) {
355        db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $form_values['tid'], chop($synonym));
356      }
357    }
358  }
359
360  if (isset($hook)) {
361    module_invoke_all('taxonomy', $hook, 'term', $form_values);
362  }
363
364  cache_clear_all();
365
366  return $status;
367}
368
369/**
370 * Delete a term.
371 *
372 * @param $tid
373 *   The term ID.
374 * @return
375 *   Status constant indicating deletion.
376 */
377function taxonomy_del_term($tid) {
378  $tids = array($tid);
379  while ($tids) {
380    $children_tids = $orphans = array();
381    foreach ($tids as $tid) {
382      // See if any of the term's children are about to be become orphans:
383      if ($children = taxonomy_get_children($tid)) {
384        foreach ($children as $child) {
385          // If the term has multiple parents, we don't delete it.
386          $parents = taxonomy_get_parents($child->tid);
387          if (count($parents) == 1) {
388            $orphans[] = $child->tid;
389          }
390        }
391      }
392
393      $term = (array) taxonomy_get_term($tid);
394
395      db_query('DELETE FROM {term_data} WHERE tid = %d', $tid);
396      db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $tid);
397      db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $tid, $tid);
398      db_query('DELETE FROM {term_synonym} WHERE tid = %d', $tid);
399      db_query('DELETE FROM {term_node} WHERE tid = %d', $tid);
400
401      module_invoke_all('taxonomy', 'delete', 'term', $term);
402    }
403
404    $tids = $orphans;
405  }
406
407  cache_clear_all();
408
409  return SAVED_DELETED;
410}
411
412/**
413 * Generate a form element for selecting terms from a vocabulary.
414 *
415 * @param $vid
416 *   The vocabulary ID to generate a form element for.
417 * @param $value
418 *   The existing value of the term(s) in this vocabulary to use by default.
419 * @param $help
420 *   Optional help text to use for the form element. If specified, this value
421 *   MUST be properly sanitized and filtered (e.g. with filter_xss_admin() or
422 *   check_plain() if it is user-supplied) to prevent XSS vulnerabilities. If
423 *   omitted, the help text stored with the vocaulary (if any) will be used.
424 * @return
425 *   An array describing a form element to select terms for a vocabulary.
426 *
427 * @see _taxonomy_term_select()
428 * @see filter_xss_admin()
429 */
430function taxonomy_form($vid, $value = 0, $help = NULL, $name = 'taxonomy') {
431  $vocabulary = taxonomy_vocabulary_load($vid);
432  $help = ($help) ? $help : filter_xss_admin($vocabulary->help);
433
434  if (!$vocabulary->multiple) {
435    $blank = ($vocabulary->required) ? t('- Please choose -') : t('- None selected -');
436  }
437  else {
438    $blank = ($vocabulary->required) ? 0 : t('- None -');
439  }
440
441  return _taxonomy_term_select(check_plain($vocabulary->name), $name, $value, $vid, $help, intval($vocabulary->multiple), $blank);
442}
443
444/**
445 * Generate a set of options for selecting a term from all vocabularies.
446 */
447function taxonomy_form_all($free_tags = 0) {
448  $vocabularies = taxonomy_get_vocabularies();
449  $options = array();
450  foreach ($vocabularies as $vid => $vocabulary) {
451    if ($vocabulary->tags && !$free_tags) { continue; }
452    $tree = taxonomy_get_tree($vid);
453    if ($tree && (count($tree) > 0)) {
454      $options[$vocabulary->name] = array();
455      foreach ($tree as $term) {
456        $options[$vocabulary->name][$term->tid] = str_repeat('-', $term->depth) . $term->name;
457      }
458    }
459  }
460  return $options;
461}
462
463/**
464 * Return an array of all vocabulary objects.
465 *
466 * @param $type
467 *   If set, return only those vocabularies associated with this node type.
468 */
469function taxonomy_get_vocabularies($type = NULL) {
470  if ($type) {
471    $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);
472  }
473  else {
474    $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'));
475  }
476
477  $vocabularies = array();
478  $node_types = array();
479  while ($voc = db_fetch_object($result)) {
480    // If no node types are associated with a vocabulary, the LEFT JOIN will
481    // return a NULL value for type.
482    if (isset($voc->type)) {
483      $node_types[$voc->vid][$voc->type] = $voc->type;
484      unset($voc->type);
485      $voc->nodes = $node_types[$voc->vid];
486    }
487    elseif (!isset($voc->nodes)) {
488      $voc->nodes = array();
489    }
490    $vocabularies[$voc->vid] = $voc;
491  }
492
493  return $vocabularies;
494}
495
496/**
497 * Implementation of hook_form_alter().
498 * Generate a form for selecting terms to associate with a node.
499 * We check for taxonomy_override_selector before loading the full
500 * vocabulary, so contrib modules can intercept before hook_form_alter
501 *  and provide scalable alternatives.
502 */
503function taxonomy_form_alter(&$form, $form_state, $form_id) {
504  if (isset($form['type']) && isset($form['#node']) && (!variable_get('taxonomy_override_selector', FALSE)) && $form['type']['#value'] .'_node_form' == $form_id) {
505    $node = $form['#node'];
506
507    if (!isset($node->taxonomy)) {
508      $terms = empty($node->nid) ? array() : taxonomy_node_get_terms($node);
509    }
510    else {
511      // After a preview or form reload, the terms must be converted to objects.
512      reset($node->taxonomy);
513      if (!is_object(current($node->taxonomy))) {
514        $node->taxonomy = taxonomy_preview_terms($node);
515      }
516      $terms = $node->taxonomy;
517    }
518
519    $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);
520
521    while ($vocabulary = db_fetch_object($c)) {
522      if ($vocabulary->tags) {
523        if (isset($form_state['node_preview'])) {
524          // Typed string can be changed by the user before preview,
525          // so we just insert the tags directly as provided in the form.
526          $typed_string = $node->taxonomy['tags'][$vocabulary->vid];
527        }
528        else {
529          $typed_string = taxonomy_implode_tags($terms, $vocabulary->vid) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL);
530        }
531        if ($vocabulary->help) {
532          $help = filter_xss_admin($vocabulary->help);
533        }
534        else {
535          $help = t('A comma-separated list of terms describing this content. Example: funny, bungee jumping, "Company, Inc.".');
536        }
537        $form['taxonomy']['tags'][$vocabulary->vid] = array('#type' => 'textfield',
538          '#title' => $vocabulary->name,
539          '#description' => $help,
540          '#required' => $vocabulary->required,
541          '#default_value' => $typed_string,
542          '#autocomplete_path' => 'taxonomy/autocomplete/'. $vocabulary->vid,
543          '#weight' => $vocabulary->weight,
544          '#maxlength' => 1024,
545        );
546      }
547      else {
548        // Extract terms belonging to the vocabulary in question.
549        $default_terms = array();
550        foreach ($terms as $term) {
551          // Free tagging has no default terms and also no vid after preview.
552          if (isset($term->vid) && $term->vid == $vocabulary->vid) {
553            $default_terms[$term->tid] = $term;
554          }
555        }
556        $form['taxonomy'][$vocabulary->vid] = taxonomy_form($vocabulary->vid, array_keys($default_terms), filter_xss_admin($vocabulary->help));
557        $form['taxonomy'][$vocabulary->vid]['#weight'] = $vocabulary->weight;
558        $form['taxonomy'][$vocabulary->vid]['#required'] = $vocabulary->required;
559      }
560    }
561    if (!empty($form['taxonomy']) && is_array($form['taxonomy'])) {
562      if (count($form['taxonomy']) > 1) {
563        // Add fieldset only if form has more than 1 element.
564        $form['taxonomy'] += array(
565          '#type' => 'fieldset',
566          '#title' => t('Vocabularies'),
567          '#collapsible' => TRUE,
568          '#collapsed' => FALSE,
569        );
570      }
571      $form['taxonomy']['#weight'] = -3;
572      $form['taxonomy']['#tree'] = TRUE;
573    }
574  }
575}
576
577/**
578 * Helper function to convert terms after a preview.
579 *
580 * After preview the tags are an array instead of proper objects. This function
581 * converts them back to objects with the exception of 'free tagging' terms,
582 * because new tags can be added by the user before preview and those do not
583 * yet exist in the database. We therefore save those tags as a string so
584 * we can fill the form again after the preview.
585 */
586function taxonomy_preview_terms($node) {
587  $taxonomy = array();
588  if (isset($node->taxonomy)) {
589    foreach ($node->taxonomy as $key => $term) {
590      unset($node->taxonomy[$key]);
591      // A 'Multiple select' and a 'Free tagging' field returns an array.
592      if (is_array($term)) {
593        foreach ($term as $tid) {
594          if ($key == 'tags') {
595            // Free tagging; the values will be saved for later as strings
596            // instead of objects to fill the form again.
597            $taxonomy['tags'] = $term;
598          }
599          else {
600            $taxonomy[$tid] = taxonomy_get_term($tid);
601          }
602        }
603      }
604      // A 'Single select' field returns the term id.
605      elseif ($term) {
606        $taxonomy[$term] = taxonomy_get_term($term);
607      }
608    }
609  }
610  return $taxonomy;
611}
612
613/**
614 * Find all terms associated with the given node, within one vocabulary.
615 */
616function taxonomy_node_get_terms_by_vocabulary($node, $vid, $key = 'tid') {
617  $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);
618  $terms = array();
619  while ($term = db_fetch_object($result)) {
620    $terms[$term->$key] = $term;
621  }
622  return $terms;
623}
624
625/**
626 * Find all terms associated with the given node, ordered by vocabulary and term weight.
627 */
628function taxonomy_node_get_terms($node, $key = 'tid') {
629  static $terms;
630
631  if (!isset($terms[$node->vid][$key])) {
632    $result = db_query(db_rewrite_sql('SELECT t.* FROM {term_node} r INNER JOIN {term_data} t ON r.tid = t.tid INNER JOIN {vocabulary} v ON t.vid = v.vid WHERE r.vid = %d ORDER BY v.weight, t.weight, t.name', 't', 'tid'), $node->vid);
633    $terms[$node->vid][$key] = array();
634    while ($term = db_fetch_object($result)) {
635      $terms[$node->vid][$key][$term->$key] = $term;
636    }
637  }
638  return $terms[$node->vid][$key];
639}
640
641/**
642 * Make sure incoming vids are free tagging enabled.
643 */
644function taxonomy_node_validate(&$node) {
645  if (!empty($node->taxonomy)) {
646    $terms = $node->taxonomy;
647    if (!empty($terms['tags'])) {
648      foreach ($terms['tags'] as $vid => $vid_value) {
649        $vocabulary = taxonomy_vocabulary_load($vid);
650        if (empty($vocabulary->tags)) {
651          // see form_get_error $key = implode('][', $element['#parents']);
652          // on why this is the key
653          form_set_error("taxonomy][tags][$vid", t('The %name vocabulary can not be modified in this way.', array('%name' => $vocabulary->name)));
654        }
655      }
656    }
657  }
658}
659
660/**
661 * Save term associations for a given node.
662 */
663function taxonomy_node_save($node, $terms) {
664
665  taxonomy_node_delete_revision($node);
666
667  // Free tagging vocabularies do not send their tids in the form,
668  // so we'll detect them here and process them independently.
669  if (isset($terms['tags'])) {
670    $typed_input = $terms['tags'];
671    unset($terms['tags']);
672
673    foreach ($typed_input as $vid => $vid_value) {
674      $typed_terms = drupal_explode_tags($vid_value);
675
676      $inserted = array();
677      foreach ($typed_terms as $typed_term) {
678        // See if the term exists in the chosen vocabulary
679        // and return the tid; otherwise, add a new record.
680        $possibilities = taxonomy_get_term_by_name($typed_term);
681        $typed_term_tid = NULL; // tid match, if any.
682        foreach ($possibilities as $possibility) {
683          if ($possibility->vid == $vid) {
684            $typed_term_tid = $possibility->tid;
685          }
686        }
687
688        if (!$typed_term_tid) {
689          $edit = array('vid' => $vid, 'name' => $typed_term);
690          $status = taxonomy_save_term($edit);
691          $typed_term_tid = $edit['tid'];
692        }
693
694        // Defend against duplicate, differently cased tags
695        if (!isset($inserted[$typed_term_tid])) {
696          db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $typed_term_tid);
697          $inserted[$typed_term_tid] = TRUE;
698        }
699      }
700    }
701  }
702
703  if (is_array($terms)) {
704    foreach ($terms as $term) {
705      if (is_array($term)) {
706        foreach ($term as $tid) {
707          if ($tid) {
708            db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $tid);
709          }
710        }
711      }
712      else if (is_object($term)) {
713        db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $term->tid);
714      }
715      else if ($term) {
716        db_query('INSERT INTO {term_node} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $term);
717      }
718    }
719  }
720}
721
722/**
723 * Remove associations of a node to its terms.
724 */
725function taxonomy_node_delete($node) {
726  db_query('DELETE FROM {term_node} WHERE nid = %d', $node->nid);
727}
728
729/**
730 * Remove associations of a node to its terms.
731 */
732function taxonomy_node_delete_revision($node) {
733  db_query('DELETE FROM {term_node} WHERE vid = %d', $node->vid);
734}
735
736/**
737 * Implementation of hook_node_type().
738 */
739function taxonomy_node_type($op, $info) {
740  if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) {
741    db_query("UPDATE {vocabulary_node_types} SET type = '%s' WHERE type = '%s'", $info->type, $info->old_type);
742  }
743  elseif ($op == 'delete') {
744    db_query("DELETE FROM {vocabulary_node_types} WHERE type = '%s'", $info->type);
745  }
746}
747
748/**
749 * Find all term objects related to a given term ID.
750 */
751function taxonomy_get_related($tid, $key = 'tid') {
752  if ($tid) {
753    $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);
754    $related = array();
755    while ($term = db_fetch_object($result)) {
756      $related[$term->$key] = $term;
757    }
758    return $related;
759  }
760  else {
761    return array();
762  }
763}
764
765/**
766 * Find all parents of a given term ID.
767 */
768function taxonomy_get_parents($tid, $key = 'tid') {
769  if ($tid) {
770    $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);
771    $parents = array();
772    while ($parent = db_fetch_object($result)) {
773      $parents[$parent->$key] = $parent;
774    }
775    return $parents;
776  }
777  else {
778    return array();
779  }
780}
781
782/**
783 * Find all ancestors of a given term ID.
784 */
785function taxonomy_get_parents_all($tid) {
786  $parents = array();
787  if ($tid) {
788    $parents[] = taxonomy_get_term($tid);
789    $n = 0;
790    while ($parent = taxonomy_get_parents($parents[$n]->tid)) {
791      $parents = array_merge($parents, $parent);
792      $n++;
793    }
794  }
795  return $parents;
796}
797
798/**
799 * Find all children of a term ID.
800 */
801function taxonomy_get_children($tid, $vid = 0, $key = 'tid') {
802  if ($vid) {
803    $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);
804  }
805  else {
806    $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);
807  }
808  $children = array();
809  while ($term = db_fetch_object($result)) {
810    $children[$term->$key] = $term;
811  }
812  return $children;
813}
814
815/**
816 * Create a hierarchical representation of a vocabulary.
817 *
818 * @param $vid
819 *   Which vocabulary to generate the tree for.
820 *
821 * @param $parent
822 *   The term ID under which to generate the tree. If 0, generate the tree
823 *   for the entire vocabulary.
824 *
825 * @param $depth
826 *   Internal use only.
827 *
828 * @param $max_depth
829 *   The number of levels of the tree to return. Leave NULL to return all levels.
830 *
831 * @return
832 *   An array of all term objects in the tree. Each term object is extended
833 *   to have "depth" and "parents" attributes in addition to its normal ones.
834 *   Results are statically cached.
835 */
836function taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL) {
837  static $children, $parents, $terms;
838
839  $depth++;
840
841  // We cache trees, so it's not CPU-intensive to call get_tree() on a term
842  // and its children, too.
843  if (!isset($children[$vid])) {
844    $children[$vid] = array();
845
846    $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);
847    while ($term = db_fetch_object($result)) {
848      $children[$vid][$term->parent][] = $term->tid;
849      $parents[$vid][$term->tid][] = $term->parent;
850      $terms[$vid][$term->tid] = $term;
851    }
852  }
853
854  $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
855  $tree = array();
856  if ($max_depth > $depth && !empty($children[$vid][$parent])) {
857    foreach ($children[$vid][$parent] as $child) {
858      $term = drupal_clone($terms[$vid][$child]);
859      $term->depth = $depth;
860      // The "parent" attribute is not useful, as it would show one parent only.
861      unset($term->parent);
862      $term->parents = $parents[$vid][$child];
863      $tree[] = $term;
864      if (!empty($children[$vid][$child])) {
865        $tree = array_merge($tree, taxonomy_get_tree($vid, $child, $depth, $max_depth));
866      }
867    }
868  }
869
870  return $tree;
871}
872
873/**
874 * Return an array of synonyms of the given term ID.
875 */
876function taxonomy_get_synonyms($tid) {
877  if ($tid) {
878    $synonyms = array();
879    $result = db_query('SELECT name FROM {term_synonym} WHERE tid = %d', $tid);
880    while ($synonym = db_fetch_array($result)) {
881      $synonyms[] = $synonym['name'];
882    }
883    return $synonyms;
884  }
885  else {
886    return array();
887  }
888}
889
890/**
891 * Return the term object that has the given string as a synonym.
892 */
893function taxonomy_get_synonym_root($synonym) {
894  return db_fetch_object(db_query("SELECT * FROM {term_synonym} s, {term_data} t WHERE t.tid = s.tid AND s.name = '%s'", $synonym));
895}
896
897/**
898 * Count the number of published nodes classified by a term.
899 *
900 * @param $tid
901 *   The term's ID
902 *
903 * @param $type
904 *   The $node->type. If given, taxonomy_term_count_nodes only counts
905 *   nodes of $type that are classified with the term $tid.
906 *
907 * @return int
908 *   An integer representing a number of nodes.
909 *   Results are statically cached.
910 */
911function taxonomy_term_count_nodes($tid, $type = 0) {
912  static $count;
913
914  if (!isset($count[$type])) {
915    // $type == 0 always evaluates TRUE if $type is a string
916    if (is_numeric($type)) {
917      $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'));
918    }
919    else {
920      $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);
921    }
922    $count[$type] = array();
923    while ($term = db_fetch_object($result)) {
924      $count[$type][$term->tid] = $term->c;
925    }
926  }
927  $children_count = 0;
928  foreach (_taxonomy_term_children($tid) as $c) {
929    $children_count += taxonomy_term_count_nodes($c, $type);
930  }
931  return $children_count + (isset($count[$type][$tid]) ? $count[$type][$tid] : 0);
932}
933
934/**
935 * Helper for taxonomy_term_count_nodes(). Used to find out
936 * which terms are children of a parent term.
937 *
938 * @param $tid
939 *   The parent term's ID
940 *
941 * @return array
942 *   An array of term IDs representing the children of $tid.
943 *   Results are statically cached.
944 *
945 */
946function _taxonomy_term_children($tid) {
947  static $children;
948
949  if (!isset($children)) {
950    $result = db_query('SELECT tid, parent FROM {term_hierarchy}');
951    while ($term = db_fetch_object($result)) {
952      $children[$term->parent][] = $term->tid;
953    }
954  }
955  return isset($children[$tid]) ? $children[$tid] : array();
956}
957
958/**
959 * Try to map a string to an existing term, as for glossary use.
960 *
961 * Provides a case-insensitive and trimmed mapping, to maximize the
962 * likelihood of a successful match.
963 *
964 * @param name
965 *   Name of the term to search for.
966 *
967 * @return
968 *   An array of matching term objects.
969 */
970function taxonomy_get_term_by_name($name) {
971  $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));
972  $result = array();
973  while ($term = db_fetch_object($db_result)) {
974    $result[] = $term;
975  }
976
977  return $result;
978}
979
980/**
981 * Return the vocabulary object matching a vocabulary ID.
982 *
983 * @param $vid
984 *   The vocabulary's ID
985 * @param $reset
986 *   Whether to reset the internal taxonomy_vocabulary_load cache.
987 *
988 * @return
989 *   The vocabulary object with all of its metadata, if exists, FALSE otherwise.
990 *   Results are statically cached.
991 */
992function taxonomy_vocabulary_load($vid, $reset = FALSE) {
993  static $vocabularies = array();
994
995  if ($reset) {
996    $vocabularies = array();
997  }
998
999  if (!isset($vocabularies[$vid])) {
1000    // Initialize so if this vocabulary does not exist, we have
1001    // that cached, and we will not try to load this later.
1002    $vocabularies[$vid] = FALSE;
1003    // Try to load the data and fill up the object.
1004    $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);
1005    $node_types = array();
1006    while ($voc = db_fetch_object($result)) {
1007      if (!empty($voc->type)) {
1008        $node_types[$voc->type] = $voc->type;
1009      }
1010      unset($voc->type);
1011      $voc->nodes = $node_types;
1012      $vocabularies[$vid] = $voc;
1013    }
1014  }
1015
1016  // Return FALSE if this vocabulary does not exist.
1017  return !empty($vocabularies[$vid]) ? $vocabularies[$vid] : FALSE;
1018}
1019
1020/**
1021 * Return the term object matching a term ID.
1022 *
1023 * @param $tid
1024 *   A term's ID
1025 * @param $reset
1026 *   Whether to reset the internal taxonomy_get_term cache.
1027 *
1028 * @return Object
1029 *   A term object. Results are statically cached.
1030 */
1031function taxonomy_get_term($tid, $reset = FALSE) {
1032  static $terms = array();
1033
1034  if ($reset) {
1035    $terms = array();
1036  }
1037
1038  if (!isset($terms[$tid])) {
1039    $terms[$tid] = db_fetch_object(db_query('SELECT * FROM {term_data} WHERE tid = %d', $tid));
1040  }
1041
1042  return $terms[$tid];
1043}
1044
1045/**
1046 * Create a select form element for a given taxonomy vocabulary.
1047 *
1048 * NOTE: This function expects input that has already been sanitized and is
1049 * safe for display. Callers must properly sanitize the $title and
1050 * $description arguments to prevent XSS vulnerabilities.
1051 *
1052 * @param $title
1053 *   The title of the vocabulary. This MUST be sanitized by the caller.
1054 * @param $name
1055 *   Ignored.
1056 * @param $value
1057 *   The currently selected terms from this vocabulary, if any.
1058 * @param $vocabulary_id
1059 *   The vocabulary ID to build the form element for.
1060 * @param $description
1061 *   Help text for the form element. This MUST be sanitized by the caller.
1062 * @param $multiple
1063 *   Boolean to control if the form should use a single or multiple select.
1064 * @param $blank
1065 *   Optional form choice to use when no value has been selected.
1066 * @param $exclude
1067 *   Optional array of term ids to exclude in the selector.
1068 * @return
1069 *   A FAPI form array to select terms from the given vocabulary.
1070 *
1071 * @see taxonomy_form()
1072 * @see taxonomy_form_term()
1073 */
1074function _taxonomy_term_select($title, $name, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) {
1075  $tree = taxonomy_get_tree($vocabulary_id);
1076  $options = array();
1077
1078  if ($blank) {
1079    $options[''] = $blank;
1080  }
1081  if ($tree) {
1082    foreach ($tree as $term) {
1083      if (!in_array($term->tid, $exclude)) {
1084        $choice = new stdClass();
1085        $choice->option = array($term->tid => str_repeat('-', $term->depth) . $term->name);
1086        $options[] = $choice;
1087      }
1088    }
1089  }
1090
1091  return array('#type' => 'select',
1092    '#title' => $title,
1093    '#default_value' => $value,
1094    '#options' => $options,
1095    '#description' => $description,
1096    '#multiple' => $multiple,
1097    '#size' => $multiple ? min(9, count($options)) : 0,
1098    '#weight' => -15,
1099    '#theme' => 'taxonomy_term_select',
1100  );
1101}
1102
1103/**
1104 * Format the selection field for choosing terms
1105 * (by deafult the default selection field is used).
1106 *
1107 * @ingroup themeable
1108 */
1109function theme_taxonomy_term_select($element) {
1110  return theme('select', $element);
1111}
1112
1113/**
1114 * Finds all nodes that match selected taxonomy conditions.
1115 *
1116 * @param $tids
1117 *   An array of term IDs to match.
1118 * @param $operator
1119 *   How to interpret multiple IDs in the array. Can be "or" or "and".
1120 * @param $depth
1121 *   How many levels deep to traverse the taxonomy tree. Can be a nonnegative
1122 *   integer or "all".
1123 * @param $pager
1124 *   Whether the nodes are to be used with a pager (the case on most Drupal
1125 *   pages) or not (in an XML feed, for example).
1126 * @param $order
1127 *   The order clause for the query that retrieve the nodes.
1128 * @return
1129 *   A resource identifier pointing to the query results.
1130 */
1131function taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $order = 'n.sticky DESC, n.created DESC') {
1132  if (count($tids) > 0) {
1133    // For each term ID, generate an array of descendant term IDs to the right depth.
1134    $descendant_tids = array();
1135    if ($depth === 'all') {
1136      $depth = NULL;
1137    }
1138    foreach ($tids as $index => $tid) {
1139      $term = taxonomy_get_term($tid);
1140      $tree = taxonomy_get_tree($term->vid, $tid, -1, $depth);
1141      $descendant_tids[] = array_merge(array($tid), array_map('_taxonomy_get_tid_from_term', $tree));
1142    }
1143
1144    if ($operator == 'or') {
1145      $args = call_user_func_array('array_merge', $descendant_tids);
1146      $placeholders = db_placeholders($args, 'int');
1147      $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;
1148      $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';
1149    }
1150    else {
1151      $joins = '';
1152      $wheres = '';
1153      $args = array();
1154      foreach ($descendant_tids as $index => $tids) {
1155        $joins .= ' INNER JOIN {term_node} tn'. $index .' ON n.vid = tn'. $index .'.vid';
1156        $wheres .= ' AND tn'. $index .'.tid IN ('. db_placeholders($tids, 'int') .')';
1157        $args = array_merge($args, $tids);
1158      }
1159      $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n '. $joins .' WHERE n.status = 1 '. $wheres .' ORDER BY '. $order;
1160      $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n '. $joins .' WHERE n.status = 1 '. $wheres;
1161    }
1162    $sql = db_rewrite_sql($sql);
1163    $sql_count = db_rewrite_sql($sql_count);
1164    if ($pager) {
1165      $result = pager_query($sql, variable_get('default_nodes_main', 10), 0, $sql_count, $args);
1166    }
1167    else {
1168      $result = db_query_range($sql, $args, 0, variable_get('feed_default_items', 10));
1169    }
1170  }
1171
1172  return $result;
1173}
1174
1175/**
1176 * Accepts the result of a pager_query() call, such as that performed by
1177 * taxonomy_select_nodes(), and formats each node along with a pager.
1178 */
1179function taxonomy_render_nodes($result) {
1180  $output = '';
1181  $has_rows = FALSE;
1182  while ($node = db_fetch_object($result)) {
1183    $output .= node_view(node_load($node->nid), 1);
1184    $has_rows = TRUE;
1185  }
1186  if ($has_rows) {
1187    $output .= theme('pager', NULL, variable_get('default_nodes_main', 10), 0);
1188  }
1189  else {
1190    $output .= '<p>'. t('There are currently no posts in this category.') .'</p>';
1191  }
1192  return $output;
1193}
1194
1195/**
1196 * Implementation of hook_nodeapi().
1197 */
1198function taxonomy_nodeapi($node, $op, $arg = 0) {
1199  switch ($op) {
1200    case 'load':
1201      $output['taxonomy'] = taxonomy_node_get_terms($node);
1202      return $output;
1203
1204    case 'insert':
1205      if (!empty($node->taxonomy)) {
1206        taxonomy_node_save($node, $node->taxonomy);
1207      }
1208      break;
1209
1210    case 'update':
1211      if (!empty($node->taxonomy)) {
1212        taxonomy_node_save($node, $node->taxonomy);
1213      }
1214      break;
1215
1216    case 'delete':
1217      taxonomy_node_delete($node);
1218      break;
1219
1220    case 'delete revision':
1221      taxonomy_node_delete_revision($node);
1222      break;
1223
1224    case 'validate':
1225      taxonomy_node_validate($node);
1226      break;
1227
1228    case 'rss item':
1229      return taxonomy_rss_item($node);
1230
1231    case 'update index':
1232      return taxonomy_node_update_index($node);
1233  }
1234}
1235
1236/**
1237 * Implementation of hook_nodeapi('update_index').
1238 */
1239function taxonomy_node_update_index(&$node) {
1240  $output = array();
1241  foreach ($node->taxonomy as $term) {
1242    $output[] = $term->name;
1243  }
1244  if (count($output)) {
1245    return '<strong>('. implode(', ', $output) .')</strong>';
1246  }
1247}
1248
1249/**
1250 * Parses a comma or plus separated string of term IDs.
1251 *
1252 * @param $str_tids
1253 *   A string of term IDs, separated by plus or comma.
1254 *   comma (,) means AND
1255 *   plus (+) means OR
1256 *
1257 * @return an associative array with an operator key (either 'and'
1258 *   or 'or') and a tid key containing an array of the term ids.
1259 */
1260function taxonomy_terms_parse_string($str_tids) {
1261  $terms = array('operator' => '', 'tids' => array());
1262  if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str_tids)) {
1263    $terms['operator'] = 'or';
1264    // The '+' character in a query string may be parsed as ' '.
1265    $terms['tids'] = preg_split('/[+ ]/', $str_tids);
1266  }
1267  else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str_tids)) {
1268    $terms['operator'] = 'and';
1269    $terms['tids'] = explode(',', $str_tids);
1270  }
1271  return $terms;
1272}
1273
1274/**
1275 * Provides category information for RSS feeds.
1276 */
1277function taxonomy_rss_item($node) {
1278  $output = array();
1279  foreach ($node->taxonomy as $term) {
1280    $output[] = array('key'   => 'category',
1281                      'value' => $term->name,
1282                      'attributes' => array('domain' => url('taxonomy/term/'. $term->tid, array('absolute' => TRUE))));
1283  }
1284  return $output;
1285}
1286
1287/**
1288 * Implementation of hook_help().
1289 */
1290function taxonomy_help($path, $arg) {
1291  switch ($path) {
1292    case 'admin/help#taxonomy':
1293      $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>';
1294      $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>';
1295      $output .= '<p>'. t('Type of Meal: <em>Appetizer, Main Course, Salad, Dessert</em>') .'</p>';
1296      $output .= '<p>'. t('Preparation Time: <em>0-30mins, 30-60mins, 1-2 hrs, 2hrs+</em>') .'</p>';
1297      $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>';
1298      $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>';
1299      $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>';
1300      $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>';
1301      return $output;
1302    case 'admin/content/taxonomy':
1303      $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>';
1304      $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>';
1305      return $output;
1306    case 'admin/content/taxonomy/%':
1307      $vocabulary = taxonomy_vocabulary_load($arg[3]);
1308      if ($vocabulary->tags) {
1309        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>';
1310      }
1311      switch ($vocabulary->hierarchy) {
1312        case 0:
1313          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>';
1314        case 1:
1315          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>';
1316        case 2:
1317          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>';
1318      }
1319    case 'admin/content/taxonomy/add/vocabulary':
1320      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>';
1321  }
1322}
1323
1324/**
1325 * Helper function for array_map purposes.
1326 */
1327function _taxonomy_get_tid_from_term($term) {
1328  return $term->tid;
1329}
1330
1331/**
1332 * Implodes a list of tags of a certain vocabulary into a string.
1333 *
1334 * @see drupal_explode_tags()
1335 */
1336function taxonomy_implode_tags($tags, $vid = NULL) {
1337  $typed_tags = array();
1338  foreach ($tags as $tag) {
1339    // Extract terms belonging to the vocabulary in question.
1340    if (is_null($vid) || $tag->vid == $vid) {
1341
1342      // Commas and quotes in tag names are special cases, so encode 'em.
1343      if (strpos($tag->name, ',') !== FALSE || strpos($tag->name, '"') !== FALSE) {
1344        $tag->name = '"'. str_replace('"', '""', $tag->name) .'"';
1345      }
1346
1347      $typed_tags[] = $tag->name;
1348    }
1349  }
1350  return implode(', ', $typed_tags);
1351}
1352
1353/**
1354 * Implementation of hook_hook_info().
1355 */
1356function taxonomy_hook_info() {
1357  return array(
1358    'taxonomy' => array(
1359      'taxonomy' => array(
1360        'insert' => array(
1361          'runs when' => t('After saving a new term to the database'),
1362        ),
1363        'update' => array(
1364          'runs when' => t('After saving an updated term to the database'),
1365        ),
1366        'delete' => array(
1367          'runs when' => t('After deleting a term')
1368        ),
1369      ),
1370    ),
1371  );
1372}
Nota: Vea TracBrowser para ayuda de uso del navegador del repositorio.