/[drupal]/contributions/sandbox/jjeff/taxonomynode/taxonomynode.module
ViewVC logotype

Contents of /contributions/sandbox/jjeff/taxonomynode/taxonomynode.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download) (as text)
Wed Nov 1 19:23:04 2006 UTC (3 years ago) by jjeff
Branch: MAIN
CVS Tags: HEAD
File MIME type: text/x-php
Module to associate nodes with a given term from a given vocabulary.
Node appears at top of taxonomy listing page for these terms.
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * This module allows nodes to be created that act as descriptions for taxonomy listing pages.
7 *
8 * @todo
9 * - add a default option to redirect node to the taxonomy listing
10 * - allow manipulation of the page title
11 *
12 */
13
14 /**
15 * Implementation of hook_help().
16 */
17 function taxonomynode_help($section) {
18 switch ($section) {
19 case 'admin/help#taxonomynode':
20 return t('TODO: Create admin help text.');
21 case 'admin/modules#description':
22 return t('Allows nodes to be created to act as a description for taxonomy terms.');
23 // OPTIONAL: Add additional cases for other paths that should display help text.
24 }
25 }
26
27
28 /**
29 * Implementation of hook_menu().
30 */
31 function taxonomynode_menu($may_cache) {
32 $access = user_access('administer nodes');
33
34 $items = array();
35
36 // hmmm... two tacts are possible...
37 // either create a menu item for the terms associated with a node
38 // or redirect to the node on the fly...
39
40 if ($may_cache) {
41
42 // create menu items for assigned items
43 $result = db_query('SELECT nid, args FROM {taxonomy_node}');
44 while($r = db_fetch_object($result)){
45
46 $items[] = array(
47 'path' => 'taxonomy/term/'. $r->args,
48 'title' => t('taxonomy term'),
49 'callback' => 'taxonomynode_page',
50 'callback arguments' => array($r->nid),
51 'access' => user_access('access content'),
52 'type' => MENU_CALLBACK,
53 );
54
55 $items[] = array(
56 'path' => 'taxonomy/term/'. $r->args .'/default',
57 'title' => t('taxonomy'),
58 'weight' => -2,
59 'access' => user_access('access content'),
60 'type' => MENU_DEFAULT_LOCAL_TASK,
61 );
62
63 $items[] = array(
64 'path' => 'taxonomy/term/'. $r->args .'/edit',
65 'title' => t('edit'),
66 'callback' => 'taxonomynode_term_redirect',
67 'callback arguments' => array($r->nid, 'edit'),
68 'weight' => 2,
69 'access' => $access,
70 'type' => MENU_LOCAL_TASK,
71 );
72
73 if(module_exist('taxonomy_menu')){
74 // taxonomy_menu uses a different menu structure
75 // let's hijack that too!
76
77 // first, let's decipher the argument and see if it's compatible with taxonomy_menu
78 if(!strstr($r->arg, '+') && !strstr($r->args, ',')){
79 $args = explode('/', $r->args);
80 if($term = taxonomy_get_term($args[0])) {
81 $vid = $term->vid;
82 $tid = $term->tid;
83
84 //now we whip through the taxonomy tree and find the tid
85
86 // this stuff is borrowed from taxonomy_menu.module
87 $path = 'taxonomy_menu/'. $vid;
88 $tree = taxonomy_get_tree($vid);
89
90 $old_depth = -1;
91 $old_path = $path;
92
93 foreach ($tree as $term) {
94 if ($term->depth <= $old_depth) {
95 $slashes_to_remove = $old_depth - $term->depth + 1;
96 for ($i = 0; $i < $slashes_to_remove; $i++) {
97 $old_path = substr($old_path, 0, strrpos($old_path, '/'));
98 }
99 }
100 $path = $old_path .'/'. $term->tid;
101 $old_depth = $term->depth;
102 $old_path = $path;
103 if($term->tid == $tid){
104
105 // but what if there are children??? <-- bug
106
107 $items[] = array(
108 'path' => $path,
109 'title' => t($term->name),
110 'weight' => $term->weight,
111 'callback' => 'taxonomynode_tm_page',
112 'callback arguments' => array($r->nid, $term->tid),
113 );
114 $items[] = array(
115 'path' => $path .'/default',
116 'title' => t('taxonomy'),
117 'weight' => -2,
118 'type' => MENU_DEFAULT_LOCAL_TASK,
119 );
120 $items[] = array(
121 'path' => $path .'/edit',
122 'title' => t('edit'),
123 'weight' => 0,
124 'callback' => 'taxonomynode_term_redirect',
125 'callback arguments' => array($r->nid, 'edit'),
126 'type' => MENU_LOCAL_TASK,
127 );
128 }
129 }
130
131
132 /*
133 foreach($tidpaths as $tidpath){
134 $t = implode('/'. $tidpath);
135
136
137 $items[] = array(
138 'path' => 'taxonomy_menu/'. $vid .'/'. $t,
139 'title' => t('taxonomy term'),
140 'callback' => 'taxonomynode_tm_page',
141 'callback arguments' => array($r->nid),
142 'access' => user_access('access content'),
143 'type' => MENU_CALLBACK,
144 );
145
146 $items[] = array(
147 'path' => 'taxonomy_menu/'. $vid .'/'. $t .'/default',
148 'title' => t('taxonomy'),
149 'weight' => -5,
150 'access' => $access,
151 'type' => MENU_DEFAULT_LOCAL_TASK,
152 );
153
154 $items[] = array(
155 'path' => 'taxonomy_menu/'. $vid .'/'. $t .'/edit',
156 'title' => t('edit'),
157 'callback' => 'taxonomynode_term_redirect',
158 'callback arguments' => array($r->nid, 'edit'),
159 'weight' => 2,
160 'access' => $access,
161 'type' => MENU_LOCAL_TASK,
162 );
163
164 }
165 */
166
167 }
168 }
169 }
170 }
171
172 $items[] = array(
173 'path' => 'taxonomynode/autocomplete',
174 'title' => t('taxonomy node autocomplete'),
175 'type' => MENU_CALLBACK,
176 'callback' => 'taxonomynode_autocomplete',
177 'access' => $access,
178 );
179
180
181 } else {
182 /*
183 if(arg(0) == 'taxonomy' && arg(1) == 'term' && arg(2)){
184 $match = arg(2);
185 if(arg(3)) {
186 $match .= '/'.arg(3);
187 }
188 $result = db_result(db_query('SELECT nid FROM {taxonomy_node} WHERE args = "%s"', arg(2)));
189 if ($result){
190 drupal_goto("node/". $result);
191 }
192 }
193 */
194
195 // add node tabs to taxonomy pages -- these could be cached
196 if (arg(0) == 'taxonomy' && arg(1) == 'term' && arg(2)){
197 $args = arg(2) . (arg(3) ? '/'. arg(3) : '');
198 if($nid = taxonomynode_find_nid($args)){
199
200
201
202 }
203 }
204
205 // if it's a node page
206 if(arg(0) == 'node' && is_numeric(arg(1))){
207 $node = node_load(arg(1));
208 // and it's got taxonomy_node information...
209 if (trim($node->taxonomy_node)){
210 // add "taxonomy" tab to node tabs
211 $items[] = array(
212 'path' => 'node/'. arg(1),
213 'title' => t('taxonomy'),
214 'type' => MENU_CALLBACK,
215 'weight' => -1,
216 'callback' => 'taxonomynode_redirect',
217 'access' => $access,
218 'callback arguments' => array(arg(1)),
219 );
220
221
222 $items[] = array(
223 'path' => 'node/'. arg(1) .'/view',
224 'title' => t('taxonomy'),
225 'type' => MENU_DEFAULT_LOCAL_TASK,
226 'weight' => -1,
227 'callback' => 'taxonomynode_redirect',
228 'access' => $access,
229 'callback arguments' => array(arg(1)),
230 );
231 }
232 }
233
234 }
235 return $items;
236 }
237
238
239 /**
240 * Implementation of hook_nodeapi().
241 */
242 function taxonomynode_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
243 $vocabs = variable_get('txnode_'. $node->type, array());
244 if(!empty($vocabs)){
245 switch ($op) {
246 case 'load':
247 $node->taxonomy_node = db_result(db_query('SELECT args FROM {taxonomy_node} WHERE nid = %d', $node->nid));
248 break;
249 case 'insert':
250 case 'update':
251 $node->taxonomy_node = trim(taxonomynode_evaluate_textfield($node->taxonomy_node));
252 if(!empty($node->taxonomy_node)){
253 db_query('DELETE FROM {taxonomy_node} WHERE nid = %d', $node->nid);
254 db_query('INSERT INTO {taxonomy_node} (nid, args) VALUES (%d, "%s")', $node->nid, $node->taxonomy_node);
255 menu_rebuild();
256 }
257 break;
258 case 'delete':
259 db_query('DELETE FROM {taxonomy_node} WHERE nid = %d', $node->nid);
260 break;
261 case 'execute':
262 $node->taxonomy_node = trim(taxonomynode_evaluate_textfield($node->taxonomy_node));
263 break;
264 case 'validate':
265 $node->taxonomy_node = trim(taxonomynode_evaluate_textfield($node->taxonomy_node));
266
267 if(!empty($node->taxonomy_node)){
268
269 // VALIDATE THAT IT'S AN ACTUAL TAXONOMY LISTING
270
271 $result = db_result(db_query('SELECT nid FROM {taxonomy_node} WHERE args = "%s"', $node->taxonomy_node));
272 if($result && $result != $node->nid){
273 // if this argument is being used by a different node already, return an error
274 form_set_error('taxonomy_node', t('The taxonomy argument "'. $node->taxonomy_node .'" is already being used by another node. Please remove that reference before submitting this. <a href="%href">Click here</a> to edit the existing node.', array('href' => url('node/'. $result))));
275 }
276 }
277 break;
278 case 'view':
279 $args = explode('/', $node->taxonomy_node);
280 //next line doesn't work... hmm...
281 //$node->body .= taxonomy_term_page($args[0], $args[1]);
282 break;
283 case 'fields':
284 // OPTIONAL: Return array of additional fields to save to the node table.
285 break;
286 case 'search result':
287 // OPTIONAL: Display extra node information with search result.
288 break;
289 case 'update index':
290 // OPTIONAL: Insert code to index additional node properties.
291 case 'print':
292 // OPTIONAL: Prepare node for printer-friendly view.
293 break;
294 }
295 }
296 }
297
298
299 function taxonomynode_form_alter($form_id, &$form){
300 if (isset($form['type'])) {
301 if ($form['type']['#value'] .'_node_settings' == $form_id) {
302
303 $vocab_objs = taxonomy_get_vocabularies();
304 foreach($vocab_objs as $vocab_obj){
305 $vocabs[$vocab_obj->vid] = $vocab_obj->name;
306 }
307
308 $form['workflow']['txnode_'. $form['type']['#value']] = array(
309 '#type' => 'checkboxes',
310 '#title' => t('Taxonomy Node'),
311 '#default_value' => variable_get('txnode_'. $form['type']['#value'], array()),
312 '#options' => $vocabs,
313 '#description' => t('To allow content of this type to appear as descriptions for taxonomy terms, select the vocabulary (or vocabularies) for which you would like to associate it.'),
314 );
315 }
316
317 $node = $form['#node'];
318 if ($form['type']['#value'] .'_node_form' == $form_id && user_access('administer nodes')) {
319 $vids = variable_get('txnode_'. $node->type, array());
320 if(!empty($vids)){
321
322 if(is_numeric($node->taxonomy_node)){
323 $term = taxonomy_get_term($node->taxonomy_node);
324 $default_value = $term->name;
325 } else {
326 $default_value = $node->taxonomy_node;
327 }
328
329
330 $form['taxonomy_node'] = array(
331 '#type' => 'fieldset',
332 '#title' => t('Describe Category'),
333 '#collapsible' => TRUE,
334 '#collapsed' => empty($default_value),
335 '#weight' => 8,
336 );
337
338 $form['taxonomy_node']['taxonomy_node'] = array(
339 '#type' => 'textfield',
340 '#title' => t('Category'),
341 '#autocomplete_path' => 'taxonomynode/autocomplete/'. implode(',', $vids),
342 '#default_value' => $default_value,
343 '#prefix' => t('<div>Use this entry as an introduction for the following category or categories.</div>'),
344 '#description' => t('Enter term name here and this node will appear as a description for the listing of items in this category. You can also assign this node to more complex taxonomy listings by entering the entire argument.<br />
345 Examples:<br />
346 - taxonomy/term/1 is a single term listing: enter either the term id or simply the <strong>name of term</strong><br/>
347 - taxonomy/term/2/all shows complete branch: enter "<strong>2/all</strong>"<br/>
348 - taxonomy/term/1,2,3 shows nodes containing ALL terms 1, 2, AND 3: enter "<strong>1,2,3</strong>"<br />
349 - taxonomy/term/1+2+3 shows nodes containing ANY of the terms 1, 2, OR 3: enter "<strong>1+2+3</strong>"'),
350 );
351
352 }
353 }
354
355 }
356 }
357
358 /**
359 * Callback for the taxonomy.module hijacks
360 *
361 * @param unknown_type $nid
362 * @return unknown
363 */
364 function taxonomynode_page($nid){
365 $node = node_load($nid);
366
367 if (arg(4)){
368 // if this is the feed page.
369 // just hand it off to the taxonomy function
370 if(arg(4) == 'feed'){
371 return taxonomy_term_page(arg(2), arg(3), arg(4));
372 }
373 else {
374 $taxonomy_output = taxonomy_term_page(arg(2), arg(3), arg(4));
375 }
376 } elseif (arg(3)) {
377 if(arg(3) == 'feed'){
378 return taxonomy_term_page(arg(2), arg(3));
379 }
380 else {
381 $taxonomy_output = taxonomy_term_page(arg(2), arg(3));
382 }
383 } else {
384 $taxonomy_output = taxonomy_term_page(arg(2));
385 }
386
387 $output = theme('taxonomynode_output', $node, $taxonomy_output);
388
389 return $output;
390
391 }
392
393 /**
394 * Callback for the taxonomy_menu.module hijacks
395 *
396 * @param unknown_type $nid
397 */
398 function taxonomynode_tm_page($nid, $tid){
399 //check to see if this is a submenu item
400 $i = 2;
401 while(arg($i) != $tid){
402 $i++;
403 }
404 $i++;
405 if(is_numeric(arg($i))){
406 $output = taxonomy_menu_page();
407 }
408 else {
409 // the good stuff
410 $node = node_load($nid);
411
412 $taxonomy_menu_output = taxonomy_menu_page();
413
414 $output = theme('taxonomynode_output', $node, $taxonomy_menu_output);
415
416 }
417
418 return $output;
419
420 }
421
422
423 /**
424 * Rewrite of node_page() allowing for arguments from places other than the url
425 *
426 * Should be able to be used as a complete drop-in replacement for node_page()
427 *
428 * @todo
429 * submit patch
430 */
431
432 /*
433 function taxonomynode_page($arg1 = NULL, $arg2 = NULL) {
434 if(is_null($arg1)){
435 $arg1 = arg(1);
436 }
437 if(is_null($arg2)){
438 $arg2 = arg(2);
439 }
440
441 $op = $arg1;
442
443 if (is_numeric($op)) {
444 $op = ($arg2 && !is_numeric($arg2)) ? $arg2 : 'view';
445 }
446
447 switch ($op) {
448 case 'view':
449 if (is_numeric($arg1)) {
450 $node = node_load($arg1);
451 if ($node->nid) {
452 drupal_set_title(check_plain($node->title));
453 return node_show($node, $arg2);
454 }
455 else if (db_result(db_query('SELECT nid FROM {node} WHERE nid = %d', $arg1))) {
456 drupal_access_denied();
457 }
458 else {
459 drupal_not_found();
460 }
461 }
462 break;
463 case 'add':
464 return node_add($arg2);
465 break;
466 case 'edit':
467 if ($_POST['op'] == t('Delete')) {
468 // Note: we redirect from node/uid/edit to node/uid/delete to make the tabs disappear.
469 if ($_REQUEST['destination']) {
470 $destination = drupal_get_destination();
471 unset($_REQUEST['destination']);
472 }
473 drupal_goto('node/'. $arg1 .'/delete', $destination);
474 }
475
476 if (is_numeric($arg1)) {
477 $node = node_load($arg1);
478 if ($node->nid) {
479 drupal_set_title(check_plain($node->title));
480 return node_form($node);
481 }
482 else if (db_result(db_query('SELECT nid FROM {node} WHERE nid = %d', $arg1))) {
483 drupal_access_denied();
484 }
485 else {
486 drupal_not_found();
487 }
488 }
489 break;
490 default:
491 drupal_set_title('');
492 return node_page_default();
493 }
494 }
495 */
496
497 /**
498 * Callback for autocompletion
499 */
500 function taxonomynode_autocomplete($vids, $string = '') {
501 //$vids = explode(",", $vids);
502
503 // The user enters a comma-separated list of tags. We only autocomplete the last tag.
504 // This regexp allows the following types of user input:
505 // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar
506 $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x';
507 preg_match_all($regexp, $string, $matches);
508 $array = $matches[1];
509
510 // Fetch last tag
511 $last_string = trim(array_pop($array));
512 if ($last_string != '') {
513 $result = db_query_range("SELECT name FROM {term_data} WHERE vid IN (%s) AND LOWER(name) LIKE LOWER('%%%s%%')", $vids, $last_string, 0, 10);
514
515 $prefix = count($array) ? implode(', ', $array) .', ' : '';
516
517 $matches = array();
518 while ($tag = db_fetch_object($result)) {
519 $n = $tag->name;
520 // Commas and quotes in terms are special cases, so encode 'em.
521 if (preg_match('/,/', $tag->name) || preg_match('/"/', $tag->name)) {
522 $n = '"'. preg_replace('/"/', '""', $tag->name) .'"';
523 }
524 $matches[$prefix . $n] = check_plain($tag->name);
525 }
526 print drupal_to_js($matches);
527 exit();
528 }
529 }
530
531 /**
532 * Return an array of all of the possible paths to get to a term
533 *
534 * @param unknown_type $tid
535 * @return unknown
536 */
537 function taxonomynode_brain_teaser($tid){
538 $result = db_query(db_rewrite_sql('SELECT t.tid 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);
539 while($r = db_fetch_object($result)){
540 $parents[$r->tid] = taxonomynode_brain_teaser($r->tid);
541 }
542 if(!empty($parents)){
543 $return = $parents;
544 }
545 else {
546 $return = $tid;
547 }
548 return $return;
549 }
550
551 function taxonomynode_brain_teaser_flat($tid, $parents = array()){
552 $array = taxonomynode_brain_teaser($tid);
553 return taxonomynode_brain_teaser_array_flatten($array);
554 }
555
556 function taxonomynode_brain_teaser_array_flatten($array){
557 static $paths = array();
558 $parents = array();
559 if(is_array($array)){
560 foreach($array as $key => $value){
561 if(is_array($value)){
562 $parents[] = $key;
563 $parents[] = array_merge(array($key), taxonomynode_brain_teaser_array_flatten($value));
564 }
565 else {
566 print_r($array);
567 print "<br /><br />";
568 }
569 }
570 }
571 return $parents;
572 }
573
574
575 function taxonomynode_get_parents_all($tid) {
576 $parents = array();
577 if ($tid) {
578 $parents[] = taxonomy_get_term($tid);
579 $n = 0;
580 while ($parent = taxonomy_get_parents($parents[$n]->tid)) {
581 $parents = array_merge($parents, $parent);
582 $n++;
583 }
584 }
585 return $parents;
586 }
587
588
589 function taxonomynode_evaluate_textfield($value){
590 // if first character is a number, assume that it's already converted
591 if(is_numeric($value{0})){
592 $return = $value;
593 }
594 else {
595 // allow for comma delimited terms, but list these as +
596 $values = explode(',', $value);
597 foreach($values as $value){
598 $value = trim($value);
599 $term = array_shift(taxonomy_get_term_by_name($value));
600 $tids[] = $term->tid;
601 }
602 // stick multiple categories together with "+"
603 // sort of counter-intuitive, but the + is going to be more common
604 $return = implode('+', $tids);
605 }
606 return $return;
607 }
608
609 function taxonomynode_redirect($nid){
610 $node = node_load($nid);
611 drupal_goto('taxonomy/term/'. $node->taxonomy_node);
612 }
613
614 function taxonomynode_term_redirect($nid, $op = FALSE){
615 drupal_goto('node/'. $nid . ($op ? '/'. $op : ''));
616 }
617
618 //not used?
619 function taxonomynode_find_nid($args){
620 return db_result(db_query('SELECT nid FROM {taxonomy_node} WHERE args = "%s"', $args));
621 }
622
623 /**
624 * Theme function for the taxonomy listing output with node-based intro
625 *
626 * @param $node
627 * the node object
628 *
629 * @param $taxonomy_output
630 * the rendered (paged) taxonomy listing - output from taxonomy_term_page()
631 *
632 * @return
633 * html
634 */
635 function theme_taxonomynode_output($node, $taxonomy_output){
636 theme('add_style', drupal_get_path('module', 'taxonomynode') .'/taxonomynode.css');
637 drupal_set_title($node->title);
638 $output = '<div class="taxonomy-description"><div class="inner-fix">'. node_view($node, FALSE, TRUE) .'</div></div>';
639 $output .= $taxonomy_output;
640 return $output;
641 }

  ViewVC Help
Powered by ViewVC 1.1.2