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