comparison modules/taxonomy/taxonomy.module @ 1:c1f4ac30525a 6.0

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