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