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