[ Index ] |
|
Code source de Drupal 5.3 |
1 <?php 2 // $Id: taxonomy.module,v 1.330.2.11 2007/09/12 07:49:36 drumm 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_link(). 18 * 19 * This hook is extended with $type = 'taxonomy terms' to allow themes to 20 * print lists of terms associated with a node. Themes can print taxonomy 21 * links with: 22 * 23 * if (module_exists('taxonomy')) { 24 * $terms = taxonomy_link('taxonomy terms', $node); 25 * print theme('links', $terms); 26 * } 27 */ 28 function taxonomy_link($type, $node = NULL) { 29 if ($type == 'taxonomy terms' && $node != NULL) { 30 $links = array(); 31 if (array_key_exists('taxonomy', $node)) { 32 foreach ($node->taxonomy as $term) { 33 $links['taxonomy_term_'. $term->tid] = array( 34 'title' => $term->name, 35 'href' => taxonomy_term_path($term), 36 'attributes' => array('rel' => 'tag', 'title' => strip_tags($term->description)) 37 ); 38 } 39 } 40 41 // We call this hook again because some modules and themes call taxonomy_link('taxonomy terms') directly 42 foreach (module_implements('link_alter') as $module) { 43 $function = $module .'_link_alter'; 44 $function($node, $links); 45 } 46 47 return $links; 48 } 49 } 50 51 /** 52 * For vocabularies not maintained by taxonomy.module, give the maintaining 53 * module a chance to provide a path for terms in that vocabulary. 54 * 55 * @param $term 56 * A term object. 57 * @return 58 * An internal Drupal path. 59 */ 60 61 function taxonomy_term_path($term) { 62 $vocabulary = taxonomy_get_vocabulary($term->vid); 63 if ($vocabulary->module != 'taxonomy' && $path = module_invoke($vocabulary->module, 'term_path', $term)) { 64 return $path; 65 } 66 return 'taxonomy/term/'. $term->tid; 67 } 68 69 /** 70 * Implementation of hook_menu(). 71 */ 72 function taxonomy_menu($may_cache) { 73 $items = array(); 74 75 if ($may_cache) { 76 $items[] = array('path' => 'admin/content/taxonomy', 77 'title' => t('Categories'), 78 'description' => t('Create vocabularies and terms to categorize your content.'), 79 'callback' => 'taxonomy_overview_vocabularies', 80 'access' => user_access('administer taxonomy')); 81 82 $items[] = array('path' => 'admin/content/taxonomy/list', 83 'title' => t('List'), 84 'type' => MENU_DEFAULT_LOCAL_TASK, 85 'weight' => -10); 86 87 $items[] = array('path' => 'admin/content/taxonomy/add/vocabulary', 88 'title' => t('Add vocabulary'), 89 'callback' => 'drupal_get_form', 90 'callback arguments' => array('taxonomy_form_vocabulary'), 91 'access' => user_access('administer taxonomy'), 92 'type' => MENU_LOCAL_TASK); 93 94 $items[] = array('path' => 'admin/content/taxonomy/edit/vocabulary', 95 'title' => t('Edit vocabulary'), 96 'callback' => 'taxonomy_admin_vocabulary_edit', 97 'access' => user_access('administer taxonomy'), 98 'type' => MENU_CALLBACK); 99 100 $items[] = array('path' => 'admin/content/taxonomy/edit/term', 101 'title' => t('Edit term'), 102 'callback' => 'taxonomy_admin_term_edit', 103 'access' => user_access('administer taxonomy'), 104 'type' => MENU_CALLBACK); 105 106 $items[] = array('path' => 'taxonomy/term', 107 'title' => t('Taxonomy term'), 108 'callback' => 'taxonomy_term_page', 109 'access' => user_access('access content'), 110 'type' => MENU_CALLBACK); 111 112 $items[] = array('path' => 'taxonomy/autocomplete', 113 'title' => t('Autocomplete taxonomy'), 114 'callback' => 'taxonomy_autocomplete', 115 'access' => user_access('access content'), 116 'type' => MENU_CALLBACK); 117 } 118 else { 119 if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'taxonomy' && is_numeric(arg(3))) { 120 $vid = arg(3); 121 $items[] = array('path' => 'admin/content/taxonomy/'. $vid, 122 'title' => t('List terms'), 123 'callback' => 'taxonomy_overview_terms', 124 'callback arguments' => array($vid), 125 'access' => user_access('administer taxonomy'), 126 'type' => MENU_CALLBACK); 127 128 $items[] = array('path' => 'admin/content/taxonomy/'. $vid .'/list', 129 'title' => t('List'), 130 'type' => MENU_DEFAULT_LOCAL_TASK, 131 'weight' => -10); 132 133 $items[] = array('path' => 'admin/content/taxonomy/'. $vid .'/add/term', 134 'title' => t('Add term'), 135 'callback' => 'drupal_get_form', 136 'callback arguments' => array('taxonomy_form_term', $vid), 137 'access' => user_access('administer taxonomy'), 138 'type' => MENU_LOCAL_TASK); 139 } 140 } 141 142 return $items; 143 } 144 145 /** 146 * List and manage vocabularies. 147 */ 148 function taxonomy_overview_vocabularies() { 149 $vocabularies = taxonomy_get_vocabularies(); 150 $rows = array(); 151 foreach ($vocabularies as $vocabulary) { 152 $types = array(); 153 foreach ($vocabulary->nodes as $type) { 154 $node_type = node_get_types('name', $type); 155 $types[] = $node_type ? check_plain($node_type) : check_plain($type); 156 } 157 $rows[] = array('name' => check_plain($vocabulary->name), 158 'type' => implode(', ', $types), 159 'edit' => l(t('edit vocabulary'), "admin/content/taxonomy/edit/vocabulary/$vocabulary->vid"), 160 'list' => l(t('list terms'), "admin/content/taxonomy/$vocabulary->vid"), 161 'add' => l(t('add terms'), "admin/content/taxonomy/$vocabulary->vid/add/term") 162 ); 163 } 164 if (empty($rows)) { 165 $rows[] = array(array('data' => t('No categories available.'), 'colspan' => '5')); 166 } 167 $header = array(t('Name'), t('Type'), array('data' => t('Operations'), 'colspan' => '3')); 168 169 return theme('table', $header, $rows, array('id' => 'taxonomy')); 170 } 171 172 /** 173 * Display a tree of all the terms in a vocabulary, with options to edit 174 * each one. 175 */ 176 function taxonomy_overview_terms($vid) { 177 $destination = drupal_get_destination(); 178 179 $header = array(t('Name'), t('Operations')); 180 $vocabulary = taxonomy_get_vocabulary($vid); 181 if (!$vocabulary) { 182 return drupal_not_found(); 183 } 184 185 drupal_set_title(check_plain($vocabulary->name)); 186 $start_from = $_GET['page'] ? $_GET['page'] : 0; 187 $total_entries = 0; // total count for pager 188 $page_increment = 25; // number of tids per page 189 $displayed_count = 0; // number of tids shown 190 191 if ($vocabulary->tags) { 192 // We are not calling taxonomy_get_tree because that might fail with a big 193 // number of tags in the freetagging vocabulary. 194 $results = pager_query(db_rewrite_sql('SELECT t.*, h.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'), $page_increment, 0, NULL, $vid); 195 while ($term = db_fetch_object($results)) { 196 $rows[] = array( 197 l($term->name, "taxonomy/term/$term->tid"), 198 l(t('edit'), "admin/content/taxonomy/edit/term/$term->tid", array(), $destination), 199 ); 200 } 201 } 202 else { 203 $tree = taxonomy_get_tree($vocabulary->vid); 204 foreach ($tree as $term) { 205 $total_entries++; // we're counting all-totals, not displayed 206 if (($start_from && ($start_from * $page_increment) >= $total_entries) || ($displayed_count == $page_increment)) { 207 continue; 208 } 209 $rows[] = array(str_repeat('--', $term->depth) .' '. l($term->name, "taxonomy/term/$term->tid"), l(t('edit'), "admin/content/taxonomy/edit/term/$term->tid", array(), $destination)); 210 $displayed_count++; // we're counting tids displayed 211 } 212 213 if (!$rows) { 214 $rows[] = array(array('data' => t('No terms available.'), 'colspan' => '2')); 215 } 216 217 $GLOBALS['pager_page_array'][] = $start_from; // FIXME 218 $GLOBALS['pager_total'][] = intval($total_entries / $page_increment) + 1; // FIXME 219 } 220 221 $output .= theme('table', $header, $rows, array('id' => 'taxonomy')); 222 if ($vocabulary->tags || $total_entries >= $page_increment) { 223 $output .= theme('pager', NULL, $page_increment); 224 } 225 226 return $output; 227 } 228 229 /** 230 * Display form for adding and editing vocabularies. 231 */ 232 function taxonomy_form_vocabulary($edit = array()) { 233 $form['name'] = array('#type' => 'textfield', 234 '#title' => t('Vocabulary name'), 235 '#default_value' => $edit['name'], 236 '#maxlength' => 255, 237 '#description' => t('The name for this vocabulary. Example: "Topic".'), 238 '#required' => TRUE, 239 ); 240 $form['description'] = array('#type' => 'textarea', 241 '#title' => t('Description'), 242 '#default_value' => $edit['description'], 243 '#description' => t('Description of the vocabulary; can be used by modules.'), 244 ); 245 $form['help'] = array('#type' => 'textfield', 246 '#title' => t('Help text'), 247 '#maxlength' => 255, 248 '#default_value' => $edit['help'], 249 '#description' => t('Instructions to present to the user when choosing a term.'), 250 ); 251 $form['nodes'] = array('#type' => 'checkboxes', 252 '#title' => t('Types'), 253 '#default_value' => $edit['nodes'], 254 '#options' => array_map('check_plain', node_get_types('names')), 255 '#description' => t('A list of node types you want to associate with this vocabulary.'), 256 '#required' => TRUE, 257 ); 258 $form['hierarchy'] = array('#type' => 'radios', 259 '#title' => t('Hierarchy'), 260 '#default_value' => $edit['hierarchy'], 261 '#options' => array(t('Disabled'), t('Single'), t('Multiple')), 262 '#description' => t('Allows <a href="@help-url">a tree-like hierarchy</a> between terms of this vocabulary.', array('@help-url' => url('admin/help/taxonomy', NULL, NULL, 'hierarchy'))), 263 ); 264 $form['relations'] = array('#type' => 'checkbox', 265 '#title' => t('Related terms'), 266 '#default_value' => $edit['relations'], 267 '#description' => t('Allows <a href="@help-url">related terms</a> in this vocabulary.', array('@help-url' => url('admin/help/taxonomy', NULL, NULL, 'related-terms'))), 268 ); 269 $form['tags'] = array('#type' => 'checkbox', 270 '#title' => t('Free tagging'), 271 '#default_value' => $edit['tags'], 272 '#description' => t('Content is categorized by typing terms instead of choosing from a list.'), 273 ); 274 $form['multiple'] = array('#type' => 'checkbox', 275 '#title' => t('Multiple select'), 276 '#default_value' => $edit['multiple'], 277 '#description' => t('Allows nodes to have more than one term from this vocabulary (always true for free tagging).'), 278 ); 279 $form['required'] = array('#type' => 'checkbox', 280 '#title' => t('Required'), 281 '#default_value' => $edit['required'], 282 '#description' => t('If enabled, every node <strong>must</strong> have at least one term in this vocabulary.'), 283 ); 284 $form['weight'] = array('#type' => 'weight', 285 '#title' => t('Weight'), 286 '#default_value' => $edit['weight'], 287 '#description' => t('In listings, the heavier vocabularies will sink and the lighter vocabularies will be positioned nearer the top.'), 288 ); 289 290 $form['submit'] = array('#type' => 'submit', '#value' => t('Submit')); 291 if ($edit['vid']) { 292 $form['delete'] = array('#type' => 'submit', '#value' => t('Delete')); 293 $form['vid'] = array('#type' => 'value', '#value' => $edit['vid']); 294 $form['module'] = array('#type' => 'value', '#value' => $edit['module']); 295 } 296 return $form; 297 } 298 299 /** 300 * Accept the form submission for a vocabulary and save the results. 301 */ 302 function taxonomy_form_vocabulary_submit($form_id, $form_values) { 303 // Fix up the nodes array to remove unchecked nodes. 304 $form_values['nodes'] = array_filter($form_values['nodes']); 305 switch (taxonomy_save_vocabulary($form_values)) { 306 case SAVED_NEW: 307 drupal_set_message(t('Created new vocabulary %name.', array('%name' => $form_values['name']))); 308 watchdog('taxonomy', t('Created new vocabulary %name.', array('%name' => $form_values['name'])), WATCHDOG_NOTICE, l(t('edit'), 'admin/content/taxonomy/edit/vocabulary/'. $form_values['vid'])); 309 break; 310 case SAVED_UPDATED: 311 drupal_set_message(t('Updated vocabulary %name.', array('%name' => $form_values['name']))); 312 watchdog('taxonomy', t('Updated vocabulary %name.', array('%name' => $form_values['name'])), WATCHDOG_NOTICE, l(t('edit'), 'admin/content/taxonomy/edit/vocabulary/'. $form_values['vid'])); 313 break; 314 } 315 return 'admin/content/taxonomy'; 316 } 317 318 function taxonomy_save_vocabulary(&$edit) { 319 $edit['nodes'] = empty($edit['nodes']) ? array() : $edit['nodes']; 320 321 if ($edit['vid'] && $edit['name']) { 322 db_query("UPDATE {vocabulary} SET name = '%s', description = '%s', help = '%s', multiple = %d, required = %d, hierarchy = %d, relations = %d, tags = %d, weight = %d, module = '%s' WHERE vid = %d", $edit['name'], $edit['description'], $edit['help'], $edit['multiple'], $edit['required'], $edit['hierarchy'], $edit['relations'], $edit['tags'], $edit['weight'], isset($edit['module']) ? $edit['module'] : 'taxonomy', $edit['vid']); 323 db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d", $edit['vid']); 324 foreach ($edit['nodes'] as $type => $selected) { 325 db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type); 326 } 327 module_invoke_all('taxonomy', 'update', 'vocabulary', $edit); 328 $status = SAVED_UPDATED; 329 } 330 else if ($edit['vid']) { 331 $status = taxonomy_del_vocabulary($edit['vid']); 332 } 333 else { 334 $edit['vid'] = db_next_id('{vocabulary}_vid'); 335 db_query("INSERT INTO {vocabulary} (vid, name, description, help, multiple, required, hierarchy, relations, tags, weight, module) VALUES (%d, '%s', '%s', '%s', %d, %d, %d, %d, %d, %d, '%s')", $edit['vid'], $edit['name'], $edit['description'], $edit['help'], $edit['multiple'], $edit['required'], $edit['hierarchy'], $edit['relations'], $edit['tags'], $edit['weight'], isset($edit['module']) ? $edit['module'] : 'taxonomy'); 336 foreach ($edit['nodes'] as $type => $selected) { 337 db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type); 338 } 339 module_invoke_all('taxonomy', 'insert', 'vocabulary', $edit); 340 $status = SAVED_NEW; 341 } 342 343 cache_clear_all(); 344 345 return $status; 346 } 347 348 /** 349 * Delete a vocabulary. 350 * 351 * @param $vid 352 * A vocabulary ID. 353 * @return 354 * Constant indicating items were deleted. 355 */ 356 function taxonomy_del_vocabulary($vid) { 357 $vocabulary = (array) taxonomy_get_vocabulary($vid); 358 359 db_query('DELETE FROM {vocabulary} WHERE vid = %d', $vid); 360 db_query('DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid); 361 $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid); 362 while ($term = db_fetch_object($result)) { 363 taxonomy_del_term($term->tid); 364 } 365 366 module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary); 367 368 cache_clear_all(); 369 370 return SAVED_DELETED; 371 } 372 373 function taxonomy_vocabulary_confirm_delete($vid) { 374 $vocabulary = taxonomy_get_vocabulary($vid); 375 376 $form['type'] = array('#type' => 'value', '#value' => 'vocabulary'); 377 $form['vid'] = array('#type' => 'value', '#value' => $vid); 378 $form['name'] = array('#type' => 'value', '#value' => $vocabulary->name); 379 return confirm_form($form, 380 t('Are you sure you want to delete the vocabulary %title?', 381 array('%title' => $vocabulary->name)), 382 'admin/content/taxonomy', 383 t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'), 384 t('Delete'), 385 t('Cancel')); 386 } 387 388 function taxonomy_vocabulary_confirm_delete_submit($form_id, $form_values) { 389 $status = taxonomy_del_vocabulary($form_values['vid']); 390 drupal_set_message(t('Deleted vocabulary %name.', array('%name' => $form_values['name']))); 391 watchdog('taxonomy', t('Deleted vocabulary %name.', array('%name' => $form_values['name'])), WATCHDOG_NOTICE); 392 return 'admin/content/taxonomy'; 393 } 394 395 function taxonomy_form_term($vocabulary_id, $edit = array()) { 396 $vocabulary = taxonomy_get_vocabulary($vocabulary_id); 397 drupal_set_title(check_plain($vocabulary->name)); 398 399 $form['name'] = array( 400 '#type' => 'textfield', 401 '#title' => t('Term name'), 402 '#default_value' => $edit['name'], 403 '#maxlength' => 255, 404 '#description' => t('The name of this term.'), 405 '#required' => TRUE); 406 407 $form['description'] = array( 408 '#type' => 'textarea', 409 '#title' => t('Description'), 410 '#default_value' => $edit['description'], 411 '#description' => t('A description of the term.')); 412 413 if ($vocabulary->hierarchy) { 414 $parent = array_keys(taxonomy_get_parents($edit['tid'])); 415 $children = taxonomy_get_tree($vocabulary_id, $edit['tid']); 416 417 // A term can't be the child of itself, nor of its children. 418 foreach ($children as $child) { 419 $exclude[] = $child->tid; 420 } 421 $exclude[] = $edit['tid']; 422 423 if ($vocabulary->hierarchy == 1) { 424 $form['parent'] = _taxonomy_term_select(t('Parent'), 'parent', $parent, $vocabulary_id, l(t('Parent term'), 'admin/help/taxonomy', NULL, NULL, 'parent') .'.', 0, '<'. t('root') .'>', $exclude); 425 } 426 elseif ($vocabulary->hierarchy == 2) { 427 $form['parent'] = _taxonomy_term_select(t('Parents'), 'parent', $parent, $vocabulary_id, l(t('Parent terms'), 'admin/help/taxonomy', NULL, NULL, 'parent') .'.', 1, '<'. t('root') .'>', $exclude); 428 } 429 } 430 431 if ($vocabulary->relations) { 432 $form['relations'] = _taxonomy_term_select(t('Related terms'), 'relations', array_keys(taxonomy_get_related($edit['tid'])), $vocabulary_id, NULL, 1, '<'. t('none') .'>', array($edit['tid'])); 433 } 434 435 $form['synonyms'] = array( 436 '#type' => 'textarea', 437 '#title' => t('Synonyms'), 438 '#default_value' => implode("\n", taxonomy_get_synonyms($edit['tid'])), 439 '#description' => t('<a href="@help-url">Synonyms</a> of this term, one synonym per line.', array('@help-url' => url('admin/help/taxonomy', NULL, NULL, 'synonyms')))); 440 $form['weight'] = array( 441 '#type' => 'weight', 442 '#title' => t('Weight'), 443 '#default_value' => $edit['weight'], 444 '#description' => t('In listings, the heavier terms will sink and the lighter terms will be positioned nearer the top.')); 445 $form['vid'] = array( 446 '#type' => 'value', 447 '#value' => $vocabulary->vid); 448 $form['submit'] = array( 449 '#type' => 'submit', 450 '#value' => t('Submit')); 451 452 if ($edit['tid']) { 453 $form['delete'] = array( 454 '#type' => 'submit', 455 '#value' => t('Delete')); 456 $form['tid'] = array( 457 '#type' => 'value', 458 '#value' => $edit['tid']); 459 } 460 else { 461 $form['destination'] = array('#type' => 'hidden', '#value' => $_GET['q']); 462 } 463 464 return $form; 465 } 466 467 /** 468 * Accept the form submission for a taxonomy term and save the result. 469 */ 470 function taxonomy_form_term_submit($form_id, $form_values) { 471 switch (taxonomy_save_term($form_values)) { 472 case SAVED_NEW: 473 drupal_set_message(t('Created new term %term.', array('%term' => $form_values['name']))); 474 watchdog('taxonomy', t('Created new term %term.', array('%term' => $form_values['name'])), WATCHDOG_NOTICE, l(t('edit'), 'admin/content/taxonomy/edit/term/'. $form_values['tid'])); 475 break; 476 case SAVED_UPDATED: 477 drupal_set_message(t('Updated term %term.', array('%term' => $form_values['name']))); 478 watchdog('taxonomy', t('Updated term %term.', array('%term' => $form_values['name'])), WATCHDOG_NOTICE, l(t('edit'), 'admin/content/taxonomy/edit/term/'. $form_values['tid'])); 479 break; 480 } 481 return 'admin/content/taxonomy'; 482 } 483 484 /** 485 * Helper function for taxonomy_form_term_submit(). 486 * 487 * @param $form_values 488 * @return 489 * Status constant indicating if term was inserted or updated. 490 */ 491 function taxonomy_save_term(&$form_values) { 492 if ($form_values['tid'] && $form_values['name']) { 493 db_query("UPDATE {term_data} SET name = '%s', description = '%s', weight = %d WHERE tid = %d", $form_values['name'], $form_values['description'], $form_values['weight'], $form_values['tid']); 494 $hook = 'update'; 495 $status = SAVED_UPDATED; 496 } 497 else if ($form_values['tid']) { 498 return taxonomy_del_term($form_values['tid']); 499 } 500 else { 501 $form_values['tid'] = db_next_id('{term_data}_tid'); 502 db_query("INSERT INTO {term_data} (tid, name, description, vid, weight) VALUES (%d, '%s', '%s', %d, %d)", $form_values['tid'], $form_values['name'], $form_values['description'], $form_values['vid'], $form_values['weight']); 503 $hook = 'insert'; 504 $status = SAVED_NEW; 505 } 506 507 db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $form_values['tid'], $form_values['tid']); 508 if ($form_values['relations']) { 509 foreach ($form_values['relations'] as $related_id) { 510 if ($related_id != 0) { 511 db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $form_values['tid'], $related_id); 512 } 513 } 514 } 515 516 db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $form_values['tid']); 517 if (!isset($form_values['parent']) || empty($form_values['parent'])) { 518 $form_values['parent'] = array(0); 519 } 520 if (is_array($form_values['parent'])) { 521 foreach ($form_values['parent'] as $parent) { 522 if (is_array($parent)) { 523 foreach ($parent as $tid) { 524 db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $tid); 525 } 526 } 527 else { 528 db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $parent); 529 } 530 } 531 } 532 else { 533 db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $form_values['parent']); 534 } 535 536 db_query('DELETE FROM {term_synonym} WHERE tid = %d', $form_values['tid']); 537 if ($form_values['synonyms']) { 538 foreach (explode ("\n", str_replace("\r", '', $form_values['synonyms'])) as $synonym) { 539 if ($synonym) { 540 db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $form_values['tid'], chop($synonym)); 541 } 542 } 543 } 544 545 if (isset($hook)) { 546 module_invoke_all('taxonomy', $hook, 'term', $form_values); 547 } 548 549 cache_clear_all(); 550 551 return $status; 552 } 553 554 /** 555 * Delete a term. 556 * 557 * @param $tid 558 * The term ID. 559 * @return 560 * Status constant indicating deletion. 561 */ 562 function taxonomy_del_term($tid) { 563 $tids = array($tid); 564 while ($tids) { 565 $children_tids = $orphans = array(); 566 foreach ($tids as $tid) { 567 // See if any of the term's children are about to be become orphans: 568 if ($children = taxonomy_get_children($tid)) { 569 foreach ($children as $child) { 570 // If the term has multiple parents, we don't delete it. 571 $parents = taxonomy_get_parents($child->tid); 572 if (count($parents) == 1) { 573 $orphans[] = $child->tid; 574 } 575 } 576 } 577 578 $term = (array) taxonomy_get_term($tid); 579 580 db_query('DELETE FROM {term_data} WHERE tid = %d', $tid); 581 db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $tid); 582 db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $tid, $tid); 583 db_query('DELETE FROM {term_synonym} WHERE tid = %d', $tid); 584 db_query('DELETE FROM {term_node} WHERE tid = %d', $tid); 585 586 module_invoke_all('taxonomy', 'delete', 'term', $term); 587 } 588 589 $tids = $orphans; 590 } 591 592 cache_clear_all(); 593 594 return SAVED_DELETED; 595 } 596 597 function taxonomy_term_confirm_delete($tid) { 598 $term = taxonomy_get_term($tid); 599 600 $form['type'] = array('#type' => 'value', '#value' => 'term'); 601 $form['name'] = array('#type' => 'value', '#value' => $term->name); 602 $form['tid'] = array('#type' => 'value', '#value' => $tid); 603 return confirm_form($form, 604 t('Are you sure you want to delete the term %title?', 605 array('%title' => $term->name)), 606 'admin/content/taxonomy', 607 t('Deleting a term will delete all its children if there are any. This action cannot be undone.'), 608 t('Delete'), 609 t('Cancel')); 610 } 611 612 function taxonomy_term_confirm_delete_submit($form_id, $form_values) { 613 taxonomy_del_term($form_values['tid']); 614 drupal_set_message(t('Deleted term %name.', array('%name' => $form_values['name']))); 615 watchdog('taxonomy', t('Deleted term %name.', array('%name' => $form_values['name'])), WATCHDOG_NOTICE); 616 return 'admin/content/taxonomy'; 617 } 618 619 /** 620 * Generate a form element for selecting terms from a vocabulary. 621 */ 622 function taxonomy_form($vid, $value = 0, $help = NULL, $name = 'taxonomy') { 623 $vocabulary = taxonomy_get_vocabulary($vid); 624 $help = ($help) ? $help : $vocabulary->help; 625 if ($vocabulary->required) { 626 $blank = 0; 627 } 628 else { 629 $blank = '<'. t('none') .'>'; 630 } 631 632 return _taxonomy_term_select(check_plain($vocabulary->name), $name, $value, $vid, $help, intval($vocabulary->multiple), $blank); 633 } 634 635 /** 636 * Generate a set of options for selecting a term from all vocabularies. 637 */ 638 function taxonomy_form_all($free_tags = 0) { 639 $vocabularies = taxonomy_get_vocabularies(); 640 $options = array(); 641 foreach ($vocabularies as $vid => $vocabulary) { 642 if ($vocabulary->tags && !$free_tags) { continue; } 643 $tree = taxonomy_get_tree($vid); 644 if ($tree && (count($tree) > 0)) { 645 $options[$vocabulary->name] = array(); 646 foreach ($tree as $term) { 647 $options[$vocabulary->name][$term->tid] = str_repeat('-', $term->depth) . $term->name; 648 } 649 } 650 } 651 return $options; 652 } 653 654 /** 655 * Return an array of all vocabulary objects. 656 * 657 * @param $type 658 * If set, return only those vocabularies associated with this node type. 659 */ 660 function taxonomy_get_vocabularies($type = NULL) { 661 if ($type) { 662 $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); 663 } 664 else { 665 $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')); 666 } 667 668 $vocabularies = array(); 669 $node_types = array(); 670 while ($voc = db_fetch_object($result)) { 671 $node_types[$voc->vid][] = $voc->type; 672 unset($voc->type); 673 $voc->nodes = $node_types[$voc->vid]; 674 $vocabularies[$voc->vid] = $voc; 675 } 676 677 return $vocabularies; 678 } 679 680 /** 681 * Implementation of hook_form_alter(). 682 * Generate a form for selecting terms to associate with a node. 683 */ 684 function taxonomy_form_alter($form_id, &$form) { 685 if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) { 686 $node = $form['#node']; 687 688 if (!isset($node->taxonomy)) { 689 if ($node->nid) { 690 $terms = taxonomy_node_get_terms($node->nid); 691 } 692 else { 693 $terms = array(); 694 } 695 } 696 else { 697 $terms = $node->taxonomy; 698 } 699 700 $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); 701 702 while ($vocabulary = db_fetch_object($c)) { 703 if ($vocabulary->tags) { 704 $typed_terms = array(); 705 foreach ($terms as $term) { 706 // Extract terms belonging to the vocabulary in question. 707 if ($term->vid == $vocabulary->vid) { 708 709 // Commas and quotes in terms are special cases, so encode 'em. 710 if (strpos($term->name, ',') !== FALSE || strpos($term->name, '"') !== FALSE) { 711 $term->name = '"'.str_replace('"', '""', $term->name).'"'; 712 } 713 714 $typed_terms[] = $term->name; 715 } 716 } 717 $typed_string = implode(', ', $typed_terms) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL); 718 719 if ($vocabulary->help) { 720 $help = $vocabulary->help; 721 } 722 else { 723 $help = t('A comma-separated list of terms describing this content. Example: funny, bungee jumping, "Company, Inc.".'); 724 } 725 $form['taxonomy']['tags'][$vocabulary->vid] = array('#type' => 'textfield', 726 '#title' => $vocabulary->name, 727 '#description' => $help, 728 '#required' => $vocabulary->required, 729 '#default_value' => $typed_string, 730 '#autocomplete_path' => 'taxonomy/autocomplete/'. $vocabulary->vid, 731 '#weight' => $vocabulary->weight, 732 '#maxlength' => 255, 733 ); 734 } 735 else { 736 // Extract terms belonging to the vocabulary in question. 737 $default_terms = array(); 738 foreach ($terms as $term) { 739 if ($term->vid == $vocabulary->vid) { 740 $default_terms[$term->tid] = $term; 741 } 742 } 743 $form['taxonomy'][$vocabulary->vid] = taxonomy_form($vocabulary->vid, array_keys($default_terms), $vocabulary->help); 744 $form['taxonomy'][$vocabulary->vid]['#weight'] = $vocabulary->weight; 745 $form['taxonomy'][$vocabulary->vid]['#required'] = $vocabulary->required; 746 } 747 } 748 if (is_array($form['taxonomy']) && !empty($form['taxonomy'])) { 749 if (count($form['taxonomy']) > 1) { // Add fieldset only if form has more than 1 element. 750 $form['taxonomy'] += array( 751 '#type' => 'fieldset', 752 '#title' => t('Categories'), 753 '#collapsible' => TRUE, 754 '#collapsed' => FALSE, 755 ); 756 } 757 $form['taxonomy']['#weight'] = -3; 758 $form['taxonomy']['#tree'] = TRUE; 759 } 760 } 761 } 762 763 /** 764 * Find all terms associated with the given node, within one vocabulary. 765 */ 766 function taxonomy_node_get_terms_by_vocabulary($nid, $vid, $key = 'tid') { 767 $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.nid = %d ORDER BY weight', 't', 'tid'), $vid, $nid); 768 $terms = array(); 769 while ($term = db_fetch_object($result)) { 770 $terms[$term->$key] = $term; 771 } 772 return $terms; 773 } 774 775 /** 776 * Find all terms associated with the given node, ordered by vocabulary and term weight. 777 */ 778 function taxonomy_node_get_terms($nid, $key = 'tid') { 779 static $terms; 780 781 if (!isset($terms[$nid][$key])) { 782 $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.nid = %d ORDER BY v.weight, t.weight, t.name', 't', 'tid'), $nid); 783 $terms[$nid][$key] = array(); 784 while ($term = db_fetch_object($result)) { 785 $terms[$nid][$key][$term->$key] = $term; 786 } 787 } 788 return $terms[$nid][$key]; 789 } 790 791 /** 792 * Make sure incoming vids are free tagging enabled. 793 */ 794 function taxonomy_node_validate(&$node) { 795 if ($node->taxonomy) { 796 $terms = $node->taxonomy; 797 if ($terms['tags']) { 798 foreach ($terms['tags'] as $vid => $vid_value) { 799 $vocabulary = taxonomy_get_vocabulary($vid); 800 if (!$vocabulary->tags) { 801 // see form_get_error $key = implode('][', $element['#parents']); 802 // on why this is the key 803 form_set_error("taxonomy][tags][$vid", t('The %name vocabulary can not be modified in this way.', array('%name' => $vocabulary->name))); 804 } 805 } 806 } 807 } 808 } 809 810 /** 811 * Save term associations for a given node. 812 */ 813 function taxonomy_node_save($nid, $terms) { 814 taxonomy_node_delete($nid); 815 816 // Free tagging vocabularies do not send their tids in the form, 817 // so we'll detect them here and process them independently. 818 if (isset($terms['tags'])) { 819 $typed_input = $terms['tags']; 820 unset($terms['tags']); 821 822 foreach ($typed_input as $vid => $vid_value) { 823 // This regexp allows the following types of user input: 824 // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar 825 $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; 826 preg_match_all($regexp, $vid_value, $matches); 827 $typed_terms = array_unique($matches[1]); 828 829 $inserted = array(); 830 foreach ($typed_terms as $typed_term) { 831 // If a user has escaped a term (to demonstrate that it is a group, 832 // or includes a comma or quote character), we remove the escape 833 // formatting so to save the term into the database as the user intends. 834 $typed_term = str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $typed_term)); 835 $typed_term = trim($typed_term); 836 if ($typed_term == "") { continue; } 837 838 // See if the term exists in the chosen vocabulary 839 // and return the tid; otherwise, add a new record. 840 $possibilities = taxonomy_get_term_by_name($typed_term); 841 $typed_term_tid = NULL; // tid match, if any. 842 foreach ($possibilities as $possibility) { 843 if ($possibility->vid == $vid) { 844 $typed_term_tid = $possibility->tid; 845 } 846 } 847 848 if (!$typed_term_tid) { 849 $edit = array('vid' => $vid, 'name' => $typed_term); 850 $status = taxonomy_save_term($edit); 851 $typed_term_tid = $edit['tid']; 852 } 853 854 // Defend against duplicate, differently cased tags 855 if (!isset($inserted[$typed_term_tid])) { 856 db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $typed_term_tid); 857 $inserted[$typed_term_tid] = TRUE; 858 } 859 } 860 } 861 } 862 863 if (is_array($terms)) { 864 foreach ($terms as $term) { 865 if (is_array($term)) { 866 foreach ($term as $tid) { 867 if ($tid) { 868 db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $tid); 869 } 870 } 871 } 872 else if (is_object($term)) { 873 db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term->tid); 874 } 875 else if ($term) { 876 db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term); 877 } 878 } 879 } 880 } 881 882 /** 883 * Remove associations of a node to its terms. 884 */ 885 function taxonomy_node_delete($nid) { 886 db_query('DELETE FROM {term_node} WHERE nid = %d', $nid); 887 } 888 889 /** 890 * Implementation of hook_node_type(). 891 */ 892 function taxonomy_node_type($op, $info) { 893 if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) { 894 db_query("UPDATE {vocabulary_node_types} SET type = '%s' WHERE type = '%s'", $info->type, $info->old_type); 895 } 896 elseif ($op == 'delete') { 897 db_query("DELETE FROM {vocabulary_node_types} WHERE type = '%s'", $info->type); 898 } 899 } 900 901 /** 902 * Find all term objects related to a given term ID. 903 */ 904 function taxonomy_get_related($tid, $key = 'tid') { 905 if ($tid) { 906 $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); 907 $related = array(); 908 while ($term = db_fetch_object($result)) { 909 $related[$term->$key] = $term; 910 } 911 return $related; 912 } 913 else { 914 return array(); 915 } 916 } 917 918 /** 919 * Find all parents of a given term ID. 920 */ 921 function taxonomy_get_parents($tid, $key = 'tid') { 922 if ($tid) { 923 $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); 924 $parents = array(); 925 while ($parent = db_fetch_object($result)) { 926 $parents[$parent->$key] = $parent; 927 } 928 return $parents; 929 } 930 else { 931 return array(); 932 } 933 } 934 935 /** 936 * Find all ancestors of a given term ID. 937 */ 938 function taxonomy_get_parents_all($tid) { 939 $parents = array(); 940 if ($tid) { 941 $parents[] = taxonomy_get_term($tid); 942 $n = 0; 943 while ($parent = taxonomy_get_parents($parents[$n]->tid)) { 944 $parents = array_merge($parents, $parent); 945 $n++; 946 } 947 } 948 return $parents; 949 } 950 951 /** 952 * Find all children of a term ID. 953 */ 954 function taxonomy_get_children($tid, $vid = 0, $key = 'tid') { 955 if ($vid) { 956 $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); 957 } 958 else { 959 $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); 960 } 961 $children = array(); 962 while ($term = db_fetch_object($result)) { 963 $children[$term->$key] = $term; 964 } 965 return $children; 966 } 967 968 /** 969 * Create a hierarchical representation of a vocabulary. 970 * 971 * @param $vid 972 * Which vocabulary to generate the tree for. 973 * 974 * @param $parent 975 * The term ID under which to generate the tree. If 0, generate the tree 976 * for the entire vocabulary. 977 * 978 * @param $depth 979 * Internal use only. 980 * 981 * @param $max_depth 982 * The number of levels of the tree to return. Leave NULL to return all levels. 983 * 984 * @return 985 * An array of all term objects in the tree. Each term object is extended 986 * to have "depth" and "parents" attributes in addition to its normal ones. 987 * Results are statically cached. 988 */ 989 function taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL) { 990 static $children, $parents, $terms; 991 992 $depth++; 993 994 // We cache trees, so it's not CPU-intensive to call get_tree() on a term 995 // and its children, too. 996 if (!isset($children[$vid])) { 997 $children[$vid] = array(); 998 999 $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); 1000 while ($term = db_fetch_object($result)) { 1001 $children[$vid][$term->parent][] = $term->tid; 1002 $parents[$vid][$term->tid][] = $term->parent; 1003 $terms[$vid][$term->tid] = $term; 1004 } 1005 } 1006 1007 $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth; 1008 if ($children[$vid][$parent]) { 1009 foreach ($children[$vid][$parent] as $child) { 1010 if ($max_depth > $depth) { 1011 $term = drupal_clone($terms[$vid][$child]); 1012 $term->depth = $depth; 1013 // The "parent" attribute is not useful, as it would show one parent only. 1014 unset($term->parent); 1015 $term->parents = $parents[$vid][$child]; 1016 $tree[] = $term; 1017 1018 if ($children[$vid][$child]) { 1019 $tree = array_merge($tree, taxonomy_get_tree($vid, $child, $depth, $max_depth)); 1020 } 1021 } 1022 } 1023 } 1024 1025 return $tree ? $tree : array(); 1026 } 1027 1028 /** 1029 * Return an array of synonyms of the given term ID. 1030 */ 1031 function taxonomy_get_synonyms($tid) { 1032 if ($tid) { 1033 $result = db_query('SELECT name FROM {term_synonym} WHERE tid = %d', $tid); 1034 while ($synonym = db_fetch_array($result)) { 1035 $synonyms[] = $synonym['name']; 1036 } 1037 return $synonyms ? $synonyms : array(); 1038 } 1039 else { 1040 return array(); 1041 } 1042 } 1043 1044 /** 1045 * Return the term object that has the given string as a synonym. 1046 */ 1047 function taxonomy_get_synonym_root($synonym) { 1048 return db_fetch_object(db_query("SELECT * FROM {term_synonym} s, {term_data} t WHERE t.tid = s.tid AND s.name = '%s'", $synonym)); 1049 } 1050 1051 /** 1052 * Count the number of published nodes classified by a term. 1053 * 1054 * @param $tid 1055 * The term's ID 1056 * 1057 * @param $type 1058 * The $node->type. If given, taxonomy_term_count_nodes only counts 1059 * nodes of $type that are classified with the term $tid. 1060 * 1061 * @return int 1062 * An integer representing a number of nodes. 1063 * Results are statically cached. 1064 */ 1065 function taxonomy_term_count_nodes($tid, $type = 0) { 1066 static $count; 1067 1068 if (!isset($count[$type])) { 1069 // $type == 0 always evaluates TRUE if $type is a string 1070 if (is_numeric($type)) { 1071 $result = db_query(db_rewrite_sql('SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 GROUP BY t.tid')); 1072 } 1073 else { 1074 $result = db_query(db_rewrite_sql("SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 AND n.type = '%s' GROUP BY t.tid"), $type); 1075 } 1076 while ($term = db_fetch_object($result)) { 1077 $count[$type][$term->tid] = $term->c; 1078 } 1079 } 1080 1081 foreach (_taxonomy_term_children($tid) as $c) { 1082 $children_count += taxonomy_term_count_nodes($c, $type); 1083 } 1084 return $count[$type][$tid] + $children_count; 1085 } 1086 1087 /** 1088 * Helper for taxonomy_term_count_nodes(). Used to find out 1089 * which terms are children of a parent term. 1090 * 1091 * @param $tid 1092 * The parent term's ID 1093 * 1094 * @return array 1095 * An array of term IDs representing the children of $tid. 1096 * Results are statically cached. 1097 * 1098 */ 1099 function _taxonomy_term_children($tid) { 1100 static $children; 1101 1102 if (!isset($children)) { 1103 $result = db_query('SELECT tid, parent FROM {term_hierarchy}'); 1104 while ($term = db_fetch_object($result)) { 1105 $children[$term->parent][] = $term->tid; 1106 } 1107 } 1108 return $children[$tid] ? $children[$tid] : array(); 1109 } 1110 1111 /** 1112 * Try to map a string to an existing term, as for glossary use. 1113 * 1114 * Provides a case-insensitive and trimmed mapping, to maximize the 1115 * likelihood of a successful match. 1116 * 1117 * @param name 1118 * Name of the term to search for. 1119 * 1120 * @return 1121 * An array of matching term objects. 1122 */ 1123 function taxonomy_get_term_by_name($name) { 1124 $db_result = db_query(db_rewrite_sql("SELECT t.tid, t.* FROM {term_data} t WHERE LOWER('%s') LIKE LOWER(t.name)", 't', 'tid'), trim($name)); 1125 $result = array(); 1126 while ($term = db_fetch_object($db_result)) { 1127 $result[] = $term; 1128 } 1129 1130 return $result; 1131 } 1132 1133 /** 1134 * Return the vocabulary object matching a vocabulary ID. 1135 * 1136 * @param $vid 1137 * The vocabulary's ID 1138 * 1139 * @return Object 1140 * The vocabulary object with all of its metadata. 1141 * Results are statically cached. 1142 */ 1143 function taxonomy_get_vocabulary($vid) { 1144 static $vocabularies = array(); 1145 1146 if (!array_key_exists($vid, $vocabularies)) { 1147 $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 ORDER BY v.weight, v.name', $vid); 1148 $node_types = array(); 1149 while ($voc = db_fetch_object($result)) { 1150 $node_types[] = $voc->type; 1151 unset($voc->type); 1152 $voc->nodes = $node_types; 1153 $vocabularies[$vid] = $voc; 1154 } 1155 } 1156 1157 return $vocabularies[$vid]; 1158 } 1159 1160 /** 1161 * Return the term object matching a term ID. 1162 * 1163 * @param $tid 1164 * A term's ID 1165 * 1166 * @return Object 1167 * A term object. Results are statically cached. 1168 */ 1169 function taxonomy_get_term($tid) { 1170 static $terms = array(); 1171 1172 if (!isset($terms[$tid])) { 1173 $terms[$tid] = db_fetch_object(db_query('SELECT * FROM {term_data} WHERE tid = %d', $tid)); 1174 } 1175 1176 return $terms[$tid]; 1177 } 1178 1179 function _taxonomy_term_select($title, $name, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) { 1180 $tree = taxonomy_get_tree($vocabulary_id); 1181 $options = array(); 1182 1183 if ($blank) { 1184 $options[0] = $blank; 1185 } 1186 if ($tree) { 1187 foreach ($tree as $term) { 1188 if (!in_array($term->tid, $exclude)) { 1189 $choice = new stdClass(); 1190 $choice->option = array($term->tid => str_repeat('-', $term->depth) . $term->name); 1191 $options[] = $choice; 1192 } 1193 } 1194 if (!$blank && !$value) { 1195 // required but without a predefined value, so set first as predefined 1196 $value = $tree[0]->tid; 1197 } 1198 } 1199 1200 return array('#type' => 'select', 1201 '#title' => $title, 1202 '#default_value' => $value, 1203 '#options' => $options, 1204 '#description' => $description, 1205 '#multiple' => $multiple, 1206 '#size' => $multiple ? min(9, count($options)) : 0, 1207 '#weight' => -15, 1208 '#theme' => 'taxonomy_term_select', 1209 ); 1210 } 1211 1212 /** 1213 * We use the default selection field for choosing terms. 1214 */ 1215 function theme_taxonomy_term_select($element) { 1216 return theme('select', $element); 1217 } 1218 1219 /** 1220 * Finds all nodes that match selected taxonomy conditions. 1221 * 1222 * @param $tids 1223 * An array of term IDs to match. 1224 * @param $operator 1225 * How to interpret multiple IDs in the array. Can be "or" or "and". 1226 * @param $depth 1227 * How many levels deep to traverse the taxonomy tree. Can be a nonnegative 1228 * integer or "all". 1229 * @param $pager 1230 * Whether the nodes are to be used with a pager (the case on most Drupal 1231 * pages) or not (in an XML feed, for example). 1232 * @param $order 1233 * The order clause for the query that retrieve the nodes. 1234 * @return 1235 * A resource identifier pointing to the query results. 1236 */ 1237 function taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $order = 'n.sticky DESC, n.created DESC') { 1238 if (count($tids) > 0) { 1239 // For each term ID, generate an array of descendant term IDs to the right depth. 1240 $descendant_tids = array(); 1241 if ($depth === 'all') { 1242 $depth = NULL; 1243 } 1244 foreach ($tids as $index => $tid) { 1245 $term = taxonomy_get_term($tid); 1246 $tree = taxonomy_get_tree($term->vid, $tid, -1, $depth); 1247 $descendant_tids[] = array_merge(array($tid), array_map('_taxonomy_get_tid_from_term', $tree)); 1248 } 1249 1250 if ($operator == 'or') { 1251 $str_tids = implode(',', call_user_func_array('array_merge', $descendant_tids)); 1252 $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n INNER JOIN {term_node} tn ON n.nid = tn.nid WHERE tn.tid IN ('. $str_tids .') AND n.status = 1 ORDER BY '. $order; 1253 $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n INNER JOIN {term_node} tn ON n.nid = tn.nid WHERE tn.tid IN ('. $str_tids .') AND n.status = 1'; 1254 } 1255 else { 1256 $joins = ''; 1257 $wheres = ''; 1258 foreach ($descendant_tids as $index => $tids) { 1259 $joins .= ' INNER JOIN {term_node} tn'. $index .' ON n.nid = tn'. $index .'.nid'; 1260 $wheres .= ' AND tn'. $index .'.tid IN ('. implode(',', $tids) .')'; 1261 } 1262 $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n '. $joins .' WHERE n.status = 1 '. $wheres .' ORDER BY '. $order; 1263 $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n '. $joins .' WHERE n.status = 1 '. $wheres; 1264 } 1265 $sql = db_rewrite_sql($sql); 1266 $sql_count = db_rewrite_sql($sql_count); 1267 if ($pager) { 1268 $result = pager_query($sql, variable_get('default_nodes_main', 10), 0, $sql_count); 1269 } 1270 else { 1271 $result = db_query_range($sql, 0, variable_get('feed_default_items', 10)); 1272 } 1273 } 1274 1275 return $result; 1276 } 1277 1278 /** 1279 * Accepts the result of a pager_query() call, such as that performed by 1280 * taxonomy_select_nodes(), and formats each node along with a pager. 1281 */ 1282 function taxonomy_render_nodes($result) { 1283 $output = ''; 1284 if (db_num_rows($result) > 0) { 1285 while ($node = db_fetch_object($result)) { 1286 $output .= node_view(node_load($node->nid), 1); 1287 } 1288 $output .= theme('pager', NULL, variable_get('default_nodes_main', 10), 0); 1289 } 1290 else { 1291 $output .= '<p>'. t('There are currently no posts in this category.') .'</p>'; 1292 } 1293 return $output; 1294 } 1295 1296 /** 1297 * Implementation of hook_nodeapi(). 1298 */ 1299 function taxonomy_nodeapi($node, $op, $arg = 0) { 1300 switch ($op) { 1301 case 'load': 1302 $output['taxonomy'] = taxonomy_node_get_terms($node->nid); 1303 return $output; 1304 case 'insert': 1305 taxonomy_node_save($node->nid, $node->taxonomy); 1306 break; 1307 case 'update': 1308 taxonomy_node_save($node->nid, $node->taxonomy); 1309 break; 1310 case 'delete': 1311 taxonomy_node_delete($node->nid); 1312 break; 1313 case 'validate': 1314 taxonomy_node_validate($node); 1315 break; 1316 case 'rss item': 1317 return taxonomy_rss_item($node); 1318 case 'update index': 1319 return taxonomy_node_update_index($node); 1320 } 1321 } 1322 1323 /** 1324 * Implementation of hook_nodeapi('update_index'). 1325 */ 1326 function taxonomy_node_update_index(&$node) { 1327 $output = array(); 1328 foreach ($node->taxonomy as $term) { 1329 $output[] = $term->name; 1330 } 1331 if (count($output)) { 1332 return '<strong>('. implode(', ', $output) .')</strong>'; 1333 } 1334 } 1335 1336 /** 1337 * Parses a comma or plus separated string of term IDs. 1338 * 1339 * @param $str_tids 1340 * A string of term IDs, separated by plus or comma. 1341 * comma (,) means AND 1342 * plus (+) means OR 1343 * 1344 * @return an associative array with an operator key (either 'and' 1345 * or 'or') and a tid key containing an array of the term ids. 1346 */ 1347 function taxonomy_terms_parse_string($str_tids) { 1348 $terms = array(); 1349 if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str_tids)) { 1350 $terms['operator'] = 'or'; 1351 // The '+' character in a query string may be parsed as ' '. 1352 $terms['tids'] = preg_split('/[+ ]/', $str_tids); 1353 } 1354 else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str_tids)) { 1355 $terms['operator'] = 'and'; 1356 $terms['tids'] = explode(',', $str_tids); 1357 } 1358 return $terms; 1359 } 1360 1361 1362 /** 1363 * Menu callback; displays all nodes associated with a term. 1364 */ 1365 function taxonomy_term_page($str_tids = '', $depth = 0, $op = 'page') { 1366 $terms = taxonomy_terms_parse_string($str_tids); 1367 if ($terms['operator'] != 'and' && $terms['operator'] != 'or') { 1368 drupal_not_found(); 1369 } 1370 1371 if ($terms['tids']) { 1372 $placeholders = implode(',', array_fill(0, count($terms['tids']), '%d')); 1373 $result = db_query(db_rewrite_sql('SELECT t.tid, t.name FROM {term_data} t WHERE t.tid IN ('. $placeholders .')', 't', 'tid'), $terms['tids']); 1374 $tids = array(); // we rebuild the $tids-array so it only contains terms the user has access to. 1375 $names = array(); 1376 while ($term = db_fetch_object($result)) { 1377 $tids[] = $term->tid; 1378 $names[] = $term->name; 1379 } 1380 1381 if ($names) { 1382 $title = check_plain(implode(', ', $names)); 1383 drupal_set_title($title); 1384 1385 switch ($op) { 1386 case 'page': 1387 // Build breadcrumb based on first hierarchy of first term: 1388 $current->tid = $tids[0]; 1389 $breadcrumbs = array(array('path' => $_GET['q'], 'title' => $names[0])); 1390 while ($parents = taxonomy_get_parents($current->tid)) { 1391 $current = array_shift($parents); 1392 $breadcrumbs[] = array('path' => 'taxonomy/term/'. $current->tid, 'title' => $current->name); 1393 } 1394 $breadcrumbs = array_reverse($breadcrumbs); 1395 menu_set_location($breadcrumbs); 1396 1397 $output = taxonomy_render_nodes(taxonomy_select_nodes($tids, $terms['operator'], $depth, TRUE)); 1398 drupal_add_feed(url('taxonomy/term/'. $str_tids .'/'. $depth .'/feed'), 'RSS - '. $title); 1399 return $output; 1400 break; 1401 1402 case 'feed': 1403 $term = taxonomy_get_term($tids[0]); 1404 $channel['link'] = url('taxonomy/term/'. $str_tids .'/'. $depth, NULL, NULL, TRUE); 1405 $channel['title'] = variable_get('site_name', 'Drupal') .' - '. $title; 1406 $channel['description'] = $term->description; 1407 1408 $result = taxonomy_select_nodes($tids, $terms['operator'], $depth, FALSE); 1409 node_feed($result, $channel); 1410 break; 1411 default: 1412 drupal_not_found(); 1413 } 1414 } 1415 else { 1416 drupal_not_found(); 1417 } 1418 } 1419 } 1420 1421 /** 1422 * Page to edit a vocabulary. 1423 */ 1424 function taxonomy_admin_vocabulary_edit($vid = NULL) { 1425 if ($_POST['op'] == t('Delete') || $_POST['confirm']) { 1426 return drupal_get_form('taxonomy_vocabulary_confirm_delete', $vid); 1427 } 1428 if ($vocabulary = (array)taxonomy_get_vocabulary($vid)) { 1429 return drupal_get_form('taxonomy_form_vocabulary', $vocabulary); 1430 } 1431 return drupal_not_found(); 1432 } 1433 1434 /** 1435 * Page to edit a vocabulary term. 1436 */ 1437 function taxonomy_admin_term_edit($tid) { 1438 if ($_POST['op'] == t('Delete') || $_POST['confirm']) { 1439 return drupal_get_form('taxonomy_term_confirm_delete', $tid); 1440 } 1441 if ($term = (array)taxonomy_get_term($tid)) { 1442 return drupal_get_form('taxonomy_form_term', $term['vid'], $term); 1443 } 1444 return drupal_not_found(); 1445 } 1446 1447 /** 1448 * Provides category information for RSS feeds. 1449 */ 1450 function taxonomy_rss_item($node) { 1451 $output = array(); 1452 foreach ($node->taxonomy as $term) { 1453 $output[] = array('key' => 'category', 1454 'value' => check_plain($term->name), 1455 'attributes' => array('domain' => url('taxonomy/term/'. $term->tid, NULL, NULL, TRUE))); 1456 } 1457 return $output; 1458 } 1459 1460 /** 1461 * Implementation of hook_help(). 1462 */ 1463 function taxonomy_help($section) { 1464 switch ($section) { 1465 case 'admin/help#taxonomy': 1466 $output = '<p>'. t('The taxonomy module is one of the most popular features because users often want to create categories to organize content by type. A simple example would be organizing a list of music reviews by musical genre.') .'</p>'; 1467 $output .= '<p>'. t('Taxonomy is the study of classification. The taxonomy module allows you to define vocabularies (sets of categories) which are used to classify content. The module supports hierarchical classification and association between terms, allowing for truly flexible information retrieval and classification. The taxonomy module allows multiple lists of categories for classification (controlled vocabularies) and offers the possibility of creating thesauri (controlled vocabularies that indicate the relationship of terms) and taxonomies (controlled vocabularies where relationships are indicated hierarchically). To view and manage the terms of each vocabulary, click on the associated <em>list terms</em> link. To delete a vocabulary and all its terms, choose <em>edit vocabulary.</em>') .'</p>'; 1468 $output .= '<p>'. t('A controlled vocabulary is a set of terms to use for describing content (known as descriptors in indexing lingo). Drupal allows you to describe each piece of content (blog, story, etc.) using one or many of these terms. For simple implementations, you might create a set of categories without subcategories, similar to Slashdot\'s sections. For more complex implementations, you might create a hierarchical list of categories.') .'</p>'; 1469 $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="@taxonomy">Taxonomy page</a>.', array('@taxonomy' => 'http://drupal.org/handbook/modules/taxonomy/')) .'</p>'; 1470 return $output; 1471 case 'admin/content/taxonomy': 1472 return '<p>'. t('The taxonomy module allows you to classify content into categories and subcategories; it allows multiple lists of categories for classification (controlled vocabularies) and offers the possibility of creating thesauri (controlled vocabularies that indicate the relationship of terms), taxonomies (controlled vocabularies where relationships are indicated hierarchically), and free vocabularies where terms, or tags, are defined during content creation. To view and manage the terms of each vocabulary, click on the associated <em>list terms</em> link. To delete a vocabulary and all its terms, choose "edit vocabulary".') .'</p>'; 1473 case 'admin/content/taxonomy/add/vocabulary': 1474 return '<p>'. t("When you create a controlled vocabulary you are creating a set of terms to use for describing content (known as descriptors in indexing lingo). Drupal allows you to describe each piece of content (blog, story, etc.) using one or many of these terms. For simple implementations, you might create a set of categories without subcategories. For more complex implementations, you might create a hierarchical list of categories.") .'</p>'; 1475 } 1476 } 1477 1478 /** 1479 * Helper function for array_map purposes. 1480 */ 1481 function _taxonomy_get_tid_from_term($term) { 1482 return $term->tid; 1483 } 1484 1485 /** 1486 * Helper function for autocompletion 1487 */ 1488 function taxonomy_autocomplete($vid, $string = '') { 1489 // The user enters a comma-separated list of tags. We only autocomplete the last tag. 1490 // This regexp allows the following types of user input: 1491 // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar 1492 $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; 1493 preg_match_all($regexp, $string, $matches); 1494 $array = $matches[1]; 1495 1496 // Fetch last tag 1497 $last_string = trim(array_pop($array)); 1498 if ($last_string != '') { 1499 $result = db_query_range(db_rewrite_sql("SELECT t.tid, t.name FROM {term_data} t WHERE t.vid = %d AND LOWER(t.name) LIKE LOWER('%%%s%%')", 't', 'tid'), $vid, $last_string, 0, 10); 1500 1501 $prefix = count($array) ? implode(', ', $array) .', ' : ''; 1502 1503 $matches = array(); 1504 while ($tag = db_fetch_object($result)) { 1505 $n = $tag->name; 1506 // Commas and quotes in terms are special cases, so encode 'em. 1507 if (strpos($tag->name, ',') !== FALSE || strpos($tag->name, '"') !== FALSE) { 1508 $n = '"'. str_replace('"', '""', $tag->name) .'"'; 1509 } 1510 $matches[$prefix . $n] = check_plain($tag->name); 1511 } 1512 print drupal_to_js($matches); 1513 exit(); 1514 } 1515 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Fri Nov 30 16:20:15 2007 | par Balluche grâce à PHPXref 0.7 |
![]() |