/[drupal]/contributions/modules/taxonomy_dss/taxonomy_dss.module
ViewVC logotype

Contents of /contributions/modules/taxonomy_dss/taxonomy_dss.module

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


Revision 1.26 - (show annotations) (download) (as text)
Wed Jan 9 18:25:08 2008 UTC (22 months, 2 weeks ago) by moonray
Branch: MAIN
CVS Tags: DRUPAL-5--1-0, HEAD
Branch point for: DRUPAL-5
Changes since 1.25: +2 -2 lines
File MIME type: text/x-php
Tweak to breadcrumbs.
1 <?php
2 // $Id: taxonomy_dss.module,v 1.25 2007/08/31 17:17:59 moonray Exp $
3
4 /* Required patch to core (can we find a module specific workaround?):
5
6 Index: themes/engines/phptemplate/phptemplate.engine
7 ===================================================================
8 RCS file: /cvs/drupal/drupal/themes/engines/phptemplate/phptemplate.engine,v
9 retrieving revision 1.54
10 diff -b -U3 -r1.54 phptemplate.engine
11 --- themes/engines/phptemplate/phptemplate.engine 30 Dec 2006 20:59:11 -0000 1.54
12 +++ themes/engines/phptemplate/phptemplate.engine 25 Jan 2007 17:58:08 -0000
13 @@ -266,13 +266,27 @@
14 $taxonomy = array();
15 }
16
17 + // This seems like a bit of a hack, but it works
18 + if (isset($node->links['node_read_more'])) {
19 + $link = $node->links['node_read_more'];
20 +
21 + $href = isset($link['href']) ? $link['href'] : NULL;
22 + $query = isset($link['query']) ? $link['query'] : NULL;
23 + $fragment = isset($link['fragment']) ? $link['fragment'] : NULL;
24 +
25 + $node_url = url($href, $query, $fragment);
26 + }
27 + else {
28 + $node_url = url('node/'.$node->nid);
29 + }
30 +
31 $variables = array(
32 'content' => ($teaser && $node->teaser) ? $node->teaser : $node->body,
33 'date' => format_date($node->created),
34 'links' => $node->links ? theme('links', $node->links, array('class' => 'links inline')) : '',
35 'name' => theme('username', $node),
36 'node' => $node, // we pass the actual node to allow more customization
37 - 'node_url' => url('node/'. $node->nid),
38 + 'node_url' => $node_url,
39 'page' => $page,
40 'taxonomy' => $taxonomy,
41 'teaser' => $teaser,
42
43 */
44
45 /**
46 * @file
47 * Taxonomy Dynamic Site Structure uses taxonomy terms to create a flexible
48 * and dynamic site structure, or outline.
49 *
50 * TODO
51 * Figure out how to let this module be loaded last if it is enabled at the
52 * same time as taxonomy; the menu callback override doesn't work otherwise.
53 * Also, find every implode that only handles commas (,) and add ability to
54 * handle plusses (+).
55 */
56
57 /**
58 * Implementation of hook_menu().
59 */
60 function taxonomy_dss_menu($may_cache) {
61 $items = array();
62
63 if (!$may_cache) {
64 $items[] = array(
65 'path' => 'admin/content/taxonomy/settings',
66 'title' => t('Settings'),
67 'callback' => 'drupal_get_form',
68 'callback arguments' => array('taxonomy_dss_admin_settings'),
69 'access' => user_access('administer taxonomy'),
70 'weight' => 10,
71 'type' => MENU_LOCAL_TASK,
72 );
73
74 // Terrible hack to override taxonomy's callback function
75 // Couldn't find another way to accomplish this, though
76 $GLOBALS['_menu']['callbacks']['taxonomy/term']['callback'] = 'taxonomy_dss_term_page';
77
78 // Show node view and edit/add links for taxonomy pages
79 if (arg(0) == 'taxonomy' && arg(1) == 'term') {
80 $node = node_load(taxonomy_dss_get_nid(arg(2)));
81 $items[] = array(
82 'path' => 'taxonomy/term/'. arg(2),
83 'title' => t('View'),
84 'callback' => 'taxonomy_dss_term_page',
85 'callback arguments' => array(arg(2)),
86 'access' => user_access('access content') && ($node ? node_access('view', $node) : TRUE),
87 'type' => MENU_CALLBACK,
88 );
89 $items[] = array(
90 'path' => 'taxonomy/term/'. arg(2) .'/view',
91 'title' => t('View'),
92 'type' => MENU_DEFAULT_LOCAL_TASK,
93 'weight' => -10,
94 );
95 if ($node) {
96 $items[] = array(
97 'path' => 'taxonomy/term/'. arg(2) .'/outline',
98 'title' => t('Outline'),
99 'callback' => 'drupal_get_form',
100 'callback arguments' => array('book_outline', $node->nid),
101 'access' => user_access('outline posts in books'),
102 'type' => MENU_LOCAL_TASK,
103 'weight' => 2
104 );
105 $items[] = array(
106 'path' => 'taxonomy/term/'. arg(2) .'/edit',
107 'title' => t('Edit'),
108 'callback' => 'taxonomy_dss_page_edit',
109 'callback arguments' => array(arg(2), $node),
110 'access' => node_access('update', $node),
111 'weight' => 1,
112 'type' => MENU_LOCAL_TASK,
113 );
114 $items[] = array(
115 'path' => 'taxonomy/term/'. arg(2) .'/add',
116 'title' => t('Edit'),
117 'callback' => 'taxonomy_dss_not_available',
118 'access' => node_access('update', $node),
119 'weight' => 1,
120 'type' => MENU_CALLBACK,
121 );
122 $items[] = array(
123 'path' => 'taxonomy/term/'. arg(2) .'/delete',
124 'title' => t('Delete'),
125 'callback' => 'drupal_get_form',
126 'callback arguments' => array('node_delete_confirm', $node),
127 'access' => node_access('delete', $node),
128 'weight' => 1,
129 'type' => MENU_CALLBACK,
130 );
131 }
132 else {
133 $items[] = array(
134 'path' => 'taxonomy/term/'. arg(2) .'/add',
135 'title' => t('Edit'),
136 'callback' => 'taxonomy_dss_page_add',
137 'callback arguments' => array(arg(2)),
138 'access' => node_access('create', 'term'),
139 'weight' => 1,
140 'type' => MENU_LOCAL_TASK,
141 );
142 $items[] = array(
143 'path' => 'taxonomy/term/'. arg(2) .'/edit',
144 'title' => t('Edit'),
145 'callback' => 'taxonomy_dss_not_available',
146 'access' => node_access('create', 'term'),
147 'weight' => 1,
148 'type' => MENU_CALLBACK,
149 );
150 }
151 }
152 }
153
154 return $items;
155 }
156
157 /**
158 * Menu callback; presents the taxonomy settings page.
159 */
160 function taxonomy_dss_admin_settings() {
161 $form['toc_options'] = array(
162 '#type' => 'fieldset',
163 '#title' => t('Default outline options'),
164 '#collapsible' => TRUE,
165 '#description' => t('These are the default setting which can be overridden for each individual taxonomy terms set.'),
166 '#weight' => -2,
167 );
168 $form['toc_options']['taxonomy_dss_toc_hidden'] = array(
169 '#type' => 'checkbox',
170 '#title' => t('Hide outline'),
171 '#default_value' => variable_get('taxonomy_dss_toc_hidden', 0),
172 '#description' => t('Hide the table of contents for this set of terms.'),
173 );
174 $form['toc_options']['taxonomy_dss_toc_depth'] = array(
175 '#type' => 'select',
176 '#title' => t('Depth of outline'),
177 '#default_value' => variable_get('taxonomy_dss_toc_depth', 0),
178 '#options' => array(0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 10 => 10),
179 '#description' => t('Depth of the table of contents for this set of terms. Set this to 0 to reflect an infinite depth.'),
180 );
181 $form['toc_options']['taxonomy_dss_toc_nodecount'] = array(
182 '#type' => 'checkbox',
183 '#title' => t('Show assigned node count in outline'),
184 '#default_value' => variable_get('taxonomy_dss_toc_nodecount', 1),
185 '#description' => t('Toggles display of the number of assigned nodes available, for each item listed in the table of contents.'),
186 );
187
188 $form['node_options'] = array(
189 '#type' => 'fieldset',
190 '#title' => t('Default node listing options'),
191 '#collapsible' => TRUE,
192 '#collapsed' => FALSE,
193 '#weight' => -1,
194 );
195 $form['node_options']['taxonomy_dss_lst_hidden'] = array(
196 '#type' => 'checkbox',
197 '#title' => t('Hide node listing'),
198 '#default_value' => variable_get('taxonomy_dss_lst_hidden', 0),
199 '#description' => t('Hide the listed nodes in this set of terms.'),
200 );
201 $form['node_options']['taxonomy_dss_lst_teasers_hidden'] = array(
202 '#type' => 'checkbox',
203 '#title' => t('Hide teasers'),
204 '#default_value' => variable_get('taxonomy_dss_lst_teasers_hidden', 0),
205 '#description' => t('Hide the teaser texts for the listed nodes in this set of terms.'),
206 );
207
208 return system_settings_form($form);
209 }
210
211 /**
212 * Menu callback; presents the editing form associated with this set of terms,
213 * or redirects to delete confirmation.
214 */
215 function taxonomy_dss_page_edit($tid, $node) {
216 if ($_POST['op'] == t('Delete')) {
217 // Note: we redirect from taxonomy/term/tid/edit to taxonomy/term/tid/delete to make the tabs disappear.
218 if ($_REQUEST['destination']) {
219 $destination = drupal_get_destination();
220 unset($_REQUEST['destination']);
221 }
222 drupal_goto('taxonomy/term/'. $tid .'/delete', $destination);
223 }
224
225 $terms = taxonomy_dss_terms_parse_string($tid);
226
227 // Set page title
228 $title = check_plain(end($terms['names']));
229 drupal_set_title($title);
230
231 // Set breadcrumb
232 $breadcrumb = taxonomy_dss_generate_breadcrumb($terms['tids'], $terms['names']);
233 taxonomy_dss_set_breadcrumb($breadcrumb);
234
235 // Render editing form
236 return drupal_get_form($node->type .'_node_form', $node);
237 }
238
239 /**
240 * Menu callback; presents the add node form and associated a node with this
241 * set of terms.
242 */
243 function taxonomy_dss_page_add($tid) {
244 $type = 'term';
245
246 $terms = taxonomy_dss_terms_parse_string($tid);
247
248 // Set page title
249 $title = check_plain(end($terms['names']));
250 drupal_set_title($title);
251
252 // Set breadcrumb
253 $breadcrumb = taxonomy_dss_generate_breadcrumb($terms['tids'], $terms['names']);
254 taxonomy_dss_set_breadcrumb($breadcrumb);
255
256 // Initialize settings:
257 $node = array('uid' => $GLOBALS['user']->uid, 'name' => $GLOBALS['user']->name, 'type' => $type);
258
259 return drupal_get_form($type .'_node_form', $node);
260 }
261
262 /**
263 * Implementation of hook_link_alter().
264 *
265 * Adds query part to links in order to keep track of dynamic taxonomy based
266 * site structure.
267 */
268 function taxonomy_dss_link_alter(&$node, &$links) {
269 $process = FALSE;
270
271 if (arg(0) == 'taxonomy' && arg(1) == 'term' && isset($node->taxonomy)) {
272 $tids = taxonomy_dss_filter_tids($node->taxonomy, arg(2), TRUE);
273 $process = TRUE;
274 }
275 elseif (arg(0) == 'node' && isset($_GET['taxonomy'])) {
276 $tids = taxonomy_dss_filter_tids($node->taxonomy, $_GET['taxonomy'], TRUE);
277 $process = TRUE;
278 }
279 elseif (arg(0) == 'comment') {
280 }
281
282 if ($process) {
283 $query = (count($tids) ? 'taxonomy='. implode(',', $tids) : '');
284
285 foreach ($links as $module => $link) {
286 switch ($module) {
287 case 'comment_comments':
288 case 'comment_add':
289 case 'node_read_more':
290 if ($query) {
291 $links[$module]['query'] = (isset($link['query']) ? $link['query'] . '&' : '') . $query;
292 }
293 break;
294 }
295 }
296 }
297 }
298
299 /**
300 * Implementation of hook_comment().
301 *
302 * Redirects the page back to the proper taxonomy term set after adding
303 * a comment to a node.
304 */
305 function taxonomy_dss_comment(&$comment, $op) {
306 switch ($op) {
307 case 'form':
308 if (isset($_GET['taxonomy']) && !isset($comment['#attributes']['destination'])) {
309 $form['destination'] = array(
310 '#type' => 'hidden',
311 '#value' => 'node/'. $comment['nid']['#value'] .'?taxonomy='. $_GET['taxonomy'],
312 );
313 }
314 elseif (isset($comment['#attributes']['destination'])) {
315 $form['destination'] = array(
316 '#type' => 'hidden',
317 '#value' => $comment['#attributes']['destination'],
318 );
319 }
320 return $form;
321 break;
322 }
323 }
324
325 /**
326 * Implementation of hook_form_alter().
327 */
328 function taxonomy_dss_form_alter($form_id, &$form) {
329 switch ($form_id) {
330 case 'taxonomy_form_vocabulary':
331 $form['taxonomy_dss_hidden'] = array(
332 '#type' => 'checkboxes',
333 '#title' => t('Breadcrumb'),
334 '#default_value' => (taxonomy_dss_vocabulary_is_hidden($form['vid']['#value']) ? 1 : 0),
335 '#options' => array('1' => t('Don\'t show in breadcrumb trail')),
336 '#description' => t('The breadcrumb trail is built using terms from all vocabularies associated with the current node. The vocabulary\'s weight determines the order in which the vocabulary appears in the breadcrumb trail. If hidden, this vocabulary will not be used to build the breadcrumb trail. It is recommended to hide free tagging vocabularies.'),
337 '#weight' => 1,
338 );
339 break;
340
341 case 'term_node_form':
342 if (arg(0) == 'node' && arg(1) == 'add') {
343 $form = array();
344 $form[] = array(
345 '#value' => t('Term pages can only be created from a taxonomy term page.'),
346 );
347 }
348 elseif (arg(0) == 'taxonomy' && arg(1) == 'term' && (arg(3) == 'add' || arg(3) == 'edit')) {
349 // Add options for display of outline tree
350
351 $termset = taxonomy_dss_load(arg(2));
352
353 $form['toc_options'] = array(
354 '#type' => 'fieldset',
355 '#title' => t('Outline options'),
356 '#collapsible' => TRUE,
357 '#collapsed' => FALSE,
358 '#weight' => -2,
359 );
360 $form['toc_options']['hidden'] = array(
361 '#type' => 'checkbox',
362 '#title' => t('Hide term'),
363 '#default_value' => isset($node->hidden) ? $node->hidden : $termset->hidden,
364 '#description' => t('Don\'t display this item in the table of contents.'),
365 );
366 $form['toc_options']['toc_hidden'] = array(
367 '#type' => 'checkbox',
368 '#title' => t('Hide outline'),
369 '#default_value' => isset($node->toc_hidden) ? $node->toc_hidden : $termset->toc_hidden,
370 '#description' => t('Hide the table of contents for this set of terms.'),
371 );
372 $form['toc_options']['toc_depth'] = array(
373 '#type' => 'select',
374 '#title' => t('Depth of outline'),
375 '#default_value' => isset($node->toc_depth) ? $node->toc_depth : $termset->toc_depth,
376 '#options' => array(0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 10 => 10),
377 '#description' => t('Depth of the table of contents for this set of terms. Set this to 0 to reflect an infinite depth.'),
378 );
379 $form['toc_options']['toc_nodecount'] = array(
380 '#type' => 'checkbox',
381 '#title' => t('Show assigned node count in outline'),
382 '#default_value' => isset($node->toc_nodecount) ? $node->toc_nodecount : $termset->toc_nodecount,
383 '#description' => t('Toggles display of the number of assigned nodes available for each item listed in the table of contents.'),
384 );
385
386 $form['node_options'] = array(
387 '#type' => 'fieldset',
388 '#title' => t('Node listing options'),
389 '#collapsible' => TRUE,
390 '#collapsed' => FALSE,
391 '#weight' => -1,
392 );
393 $form['node_options']['lst_hidden'] = array(
394 '#type' => 'checkbox',
395 '#title' => t('Hide node listing'),
396 '#default_value' => isset($node->lst_hidden) ? $node->lst_hidden : $termset->lst_hidden,
397 '#description' => t('Hide the listed nodes in this set of terms.'),
398 );
399 $form['node_options']['lst_teasers_hidden'] = array(
400 '#type' => 'checkbox',
401 '#title' => t('Hide teasers'),
402 '#default_value' => isset($node->lst_teasers_hidden) ? $node->lst_teasers_hidden : $termset->lst_teasers_hidden,
403 '#description' => t('Hide the teaser texts for the listed nodes in this set of terms.'),
404 );
405
406 // Required to make this form unique to this set of terms. If omitted,
407 // drupal's form handler often thinks it's a duplicate submit for a
408 // previous 'add node' request.
409 $form['term_tid'] = array(
410 '#type' => 'hidden',
411 '#value' => arg(2),
412 );
413 }
414 else {
415 print "EDIT THIS PAGE!<br />\nAdd a taxonomy select dropdown menu.<br />\nOr, we can somehow disable access to this particular page from this particular spot?";
416 }
417 break;
418
419 case 'node_admin_nodes':
420 // Override edit destination for 'term' nodes on 'Content management' page
421 if (is_array($form['name'])) {
422 $keys = array_keys($form['name']);
423 foreach ($keys as $key) {
424 if ($form['name'][$key]['#value'] == 'Term') {
425 $form['operations'][$key]['#value'] = l(t('edit'), 'taxonomy/term/'. taxonomy_dss_get_tids($key) .'/edit', array(), drupal_get_destination());
426 }
427 }
428 }
429 break;
430 }
431 }
432
433 /**
434 * Implementation of hook_taxonomy().
435 */
436 function taxonomy_dss_taxonomy($op, $type, $object = NULL) {
437 if ($type == 'vocabulary') {
438 switch ($op) {
439 case 'delete':
440 db_query("DELETE FROM {taxonomy_dss_vocabulary} WHERE vid = %d", $object['vid']);
441 break;
442 case 'insert':
443 db_query("INSERT INTO {taxonomy_dss_vocabulary} (vid, hidden) VALUES (%d, %d)", $object['vid'], $object['taxonomy_dss_hidden']['1']);
444 break;
445 case 'update':
446 db_query("UPDATE {taxonomy_dss_vocabulary} SET hidden = %d WHERE vid = %d", $object['taxonomy_dss_hidden']['1'], $object['vid']);
447 break;
448 }
449 }
450 elseif ($type == 'term') {
451 switch ($op) {
452 case 'delete':
453 db_query("DELETE FROM {taxonomy_dss_term} WHERE tid = '%s'", $object['tid']);
454 // Actually, we need to delete every single page that this term appears in... ouch!
455 break;
456 }
457 }
458 }
459
460 /**
461 * Implementation of hook_nodeapi().
462 */
463 function taxonomy_dss_nodeapi(&$node, $op, $teaser, $page) {
464 $clean_url = (bool)variable_get('clean_url', '0');
465 $delimiter = ($clean_url == FALSE || strstr('?', $node->breadcrumb[$i]['path']) ? '&' : '?');
466
467 switch ($op) {
468 case 'insert':
469 if (arg(0) == 'taxonomy' && arg(1) == 'term' && arg(3) == 'add') {
470 // arg(2) needs to be cleaned, or we might have a security hole!
471
472 // Allow other modules to alter the data
473 $termset = taxonomy_dss_node_to_termset(arg(2), $node);
474 taxonomy_dss_invoke($termset, 'submit', $node);
475 $termset->serialized_data = serialize($termset->serial_data);
476
477 // Insert termset into database
478 db_query("INSERT INTO {taxonomy_dss_term} (tid, nid, hidden, toc_hidden, toc_depth, toc_nodecount, lst_hidden, lst_teasers_hidden, serialized_data) VALUES ('%s', %d, %d, %d, %d, %d, %d, %d, '%s')", $termset->tids, $termset->nid, $termset->hidden, $termset->toc_hidden, $termset->toc_depth, $termset->toc_nodecount, $termset->lst_hidden, $termset->lst_teasers_hidden, $termset->serialized_data);
479
480 // Allow other modules to alter the data
481 taxonomy_dss_invoke($termset, 'insert', $node);
482 }
483 break;
484
485 case 'update':
486 if (arg(0) == 'taxonomy' && arg(1) == 'term' && arg(3) == 'edit') {
487
488 // Allow other modules to alter the data
489 $termset = taxonomy_dss_node_to_termset(arg(2), $node);
490 taxonomy_dss_invoke($termset, 'submit', $node);
491 $termset->serialized_data = serialize($termset->serial_data);
492
493 // Update termset in database
494 db_query("UPDATE {taxonomy_dss_term} SET hidden = %d, toc_hidden = %d, toc_depth = %d, toc_nodecount = %d, lst_hidden = %d, lst_teasers_hidden = %d, serialized_data = '%s' WHERE tid = '%s'", $termset->hidden, $termset->toc_hidden, $termset->toc_depth, $termset->toc_nodecount, $termset->lst_hidden, $termset->lst_teasers_hidden, $termset->serialized_data, $termset->tids);
495
496 // Allow other modules to alter the data
497 taxonomy_dss_invoke($termset, 'update', $node);
498 }
499 break;
500
501 case 'delete':
502 db_query("DELETE FROM {taxonomy_dss_term} WHERE nid = %d", $node->nid);
503
504 // Allow other modules to alter the data
505 taxonomy_dss_invoke($termset, 'delete');
506 break;
507
508 case 'submit':
509 // If it's a term node, add a title
510 if (isset($node->nid)) {
511 // Updating existing node
512 if ($node->type == 'term' && $tids = taxonomy_dss_get_tids($node->nid)) {
513 $node->title = taxonomy_dss_get_title($tids);
514 }
515 }
516 elseif (arg(0) == 'taxonomy' && arg(1) == 'term' && arg(3) == 'add') {
517 // Creating new node
518 $node->title = taxonomy_dss_get_title(arg(2));
519 }
520
521 break;
522
523 case 'view':
524 if ($page) {
525 // Page view
526
527 // If this node is associated with a taxonomy term, jump!
528 if (arg(0) != 'taxonomy' && arg(1) != 'term' && $tid = taxonomy_dss_get_tids($node->nid)) {
529 drupal_goto('taxonomy/term/'.$tid);
530 }
531
532 // Fix book module links
533 $taxonomy = $_GET['taxonomy'] ? 'taxonomy='.$_GET['taxonomy'] : '';
534 if (module_exists('book') && isset($node->content['book_navigation']) && $taxonomy) {
535 // Hack the themed output from the book module's navigation
536 $node->content['book_navigation'] = preg_replace('/(node\/\d+)/', '${1}'. $delimiter.$taxonomy, $node->content['book_navigation']);
537 }
538
539 // Taxonomy terms seem to already be sorted by vocabulary weight.
540 $tids = taxonomy_dss_filter_tids($node->taxonomy, $_GET['taxonomy']);
541 $names = array();
542 foreach ($tids as $tid) {
543 $names[] = $node->taxonomy[$tid]->name;
544 }
545
546 $breadcrumb = taxonomy_dss_generate_breadcrumb($tids, $names);
547
548 // Integrate with book module
549 if (module_exists('book') && isset($node->breadcrumb)) {
550 $breadcrumb = array_merge($breadcrumb, $node->breadcrumb);
551 }
552 else {
553 $breadcrumb[] = array('path' => 'node/'. $node->nid, 'title' => $node->title);
554 }
555
556 menu_set_location($breadcrumb);
557 taxonomy_dss_set_breadcrumb($breadcrumb);
558 }
559 break;
560 case 'alter':
561 if ($page) {
562 // Page view
563
564 $taxonomy = $_GET['taxonomy'] ? 'taxonomy='.$_GET['taxonomy'] : '';
565
566 if (module_exists('book') && isset($node->breadcrumb) && $taxonomy) {
567 // Hack the breadcrumb to include the query
568 $breadcrumb = drupal_get_breadcrumb();
569 $offset = count($breadcrumb) - count($node->breadcrumb) + 1;
570 for ($i = $offset; $i < count($breadcrumb); $i++) {
571 $breadcrumb[$i] = str_replace($node->breadcrumb[$i - $offset]['path'], $node->breadcrumb[$i - $offset]['path'] . $delimiter . $taxonomy, $breadcrumb[$i]);
572 }
573 drupal_set_breadcrumb($breadcrumb);
574 }
575 }
576 break;
577 }
578 }
579
580 /**
581 * Is vocabulary hidden?
582 */
583 function taxonomy_dss_vocabulary_is_hidden($vid) {
584 static $vocabularies = array();
585
586 if (!isset($vocabularies[$vid])) {
587 $result = db_query("SELECT hidden FROM {taxonomy_dss_vocabulary} WHERE vid = %d", $vid);
588 $row = db_fetch_array($result);
589 if ($row && $row['hidden'] == 1) {
590 $vocabularies[$vid] = TRUE;
591 }
592 else {
593 $vocabularies[$vid] = FALSE;
594 }
595 }
596
597 return $vocabularies[$vid];
598 }
599
600 function taxonomy_dss_not_available() {
601 return t('n/a');
602 }
603
604 function taxonomy_dss_get_termset_data($tids) {
605 static $data = array();
606
607 if (!isset($data[$tids])) {
608 $result = db_query("SELECT nid, hidden, toc_hidden, toc_depth, toc_nodecount, lst_hidden, lst_teasers_hidden, serialized_data FROM {taxonomy_dss_term} WHERE tid = '%s'", $tids);
609 if ($row = db_fetch_object($result)) {
610 $row->serial_data = unserialize($row->serialized_data);
611 $data[$tids] = $row;
612 }
613 else {
614 $data[$tids] = new StdClass;
615 $data[$tids]->tids = $tids;
616 $data[$tids]->nid = 0;
617 $data[$tids]->hidden = 0;
618 $data[$tids]->toc_hidden = variable_get('taxonomy_dss_toc_hidden', 0);
619 $data[$tids]->toc_depth = variable_get('taxonomy_dss_toc_depth', 0);
620 $data[$tids]->toc_nodecount = variable_get('taxonomy_dss_toc_nodecount', 1);
621 $data[$tids]->lst_hidden = variable_get('taxonomy_dss_lst_hidden', 0);
622 $data[$tids]->lst_teasers_hidden = variable_get('taxonomy_dss_lst_teasers_hidden', 0);
623 $data[$tids]->serial_data = unserialize('');
624 }
625 }
626
627 return drupal_clone($data[$tids]);
628 }
629
630 function taxonomy_dss_term_is_hidden($tids) {
631 $termset = taxonomy_dss_load($tids);
632 return $termset->hidden;
633 }
634
635 function taxonomy_dss_get_nid($tids) {
636 $termset = taxonomy_dss_load($tids);
637 return $termset->nid;
638 }
639
640 function taxonomy_dss_get_tids($nid) {
641 $tids = 0;
642
643 $result = db_query("SELECT tid FROM {taxonomy_dss_term} WHERE nid = %d", $nid);
644 if ($row = db_fetch_array($result)) {
645 $tids = $row['tid'];
646 }
647 return $tids;
648 }
649
650 function taxonomy_dss_get_current_tids() {
651 $tids = array();
652 switch (arg(0)) {
653 case 'node':
654 if (is_numeric(arg(1))) {
655 $node = node_load(arg(1));
656 if (!$node) {
657 break;
658 }
659 $tids = taxonomy_dss_filter_tids($node->taxonomy, isset($_GET['taxonomy']) ? $_GET['taxonomy'] : '');
660 }
661 break;
662 case 'taxonomy':
663 if (arg(1) == 'term') {
664 $tids = explode(',', arg(2));
665 }
666 break;
667 case 'faq':
668 if (module_exists('faq') && module_exists('path_redirect')) {
669 $result = db_query("SELECT path FROM {path_redirect} WHERE redirect = '%s'", 'faq');
670 while ($row = db_fetch_array($result)) {
671 if (substr($row['path'], 0, 14) == 'taxonomy/term/') {
672 $names = array();
673 $tids = explode(',', substr($row['path'], 14));
674 }
675 }
676 }
677 break;
678 }
679
680 return $tids;
681 }
682
683 /**
684 * Extends taxonomy_terms_parse_string.
685 * Parses a comma or plus separated string of term IDs.
686 *
687 * @param $str_tids
688 * A string of term IDs, separated by plus or comma.
689 * comma (,) means AND.
690 * plus (+) means OR.
691 * @param $expand_parents
692 * (optional) Expands tids that have parents when TRUE. Defaults to FALSE.
693 *
694 * @return an associative array with an operator key (either 'and' or 'or'),
695 * a tids key containing an array of the term ids, and a names key
696 * containing the term names/titles.
697 */
698 function taxonomy_dss_terms_parse_string($str_tids, $expand_parents = FALSE) {
699 static $termsets = array(
700 'default' => array(),
701 'expanded' => array(),
702 );
703
704 if ($expand_parents === FALSE) {
705 if (isset($termsets['default'][$str_tids])) {
706 return $termsets['default'][$str_tids];
707 }
708 }
709 else {
710 if (isset($termsets['expanded'][$str_tids])) {
711 return $termsets['expanded'][$str_tids];
712 }
713 }
714
715 // Parse string
716 $terms = taxonomy_terms_parse_string($str_tids);
717 if ($terms['operator'] != 'and' && $terms['operator'] != 'or') {
718 drupal_not_found();
719 }
720
721 // Expand to include parents
722 if ($expand_parents) {
723 $tids = array();
724
725 foreach ($terms['tids'] as $tid) {
726 if ($parents = taxonomy_get_parents_all($tid)) {
727 while (count($parents)) {
728 $parent = array_pop($parents);
729 $tids[] = $parent->tid;
730 }
731 }
732 $tids[] = $tid;
733 }
734
735 $terms['tids'] = $tids;
736 }
737
738 // Retrieve the name for each term
739 if ($terms['tids']) {
740 $tids = array();
741 $names = array();
742
743 $result = db_query(db_rewrite_sql('SELECT t.tid, t.name, (%s) AS weight FROM {term_data} t WHERE t.tid IN (%s) ORDER BY weight', 't', 'tid'), _taxonomy_dss_sql_order_tids($terms['tids']), implode(',', $terms['tids']));
744 while ($term = db_fetch_object($result)) {
745 $tids[] = $term->tid;
746 $names[] = $term->name;
747 }
748
749 $terms['tids'] = $tids;
750 $terms['names'] = $names;
751 }
752
753 if ($expand_parents === FALSE) {
754 $termsets['default'][$str_tids] = $terms;
755 return $termsets['default'][$str_tids];
756 }
757 else {
758 $termsets['expanded'][$str_tids] = $terms;
759 return $termsets['expanded'][$str_tids];
760 }
761 }
762
763 /**
764 * Derives title from a comma or plus separated string of term IDs.
765 *
766 * @param $str_tids
767 * A string of term IDs, separated by plus or comma.
768 * comma (,) means AND.
769 * plus (+) means OR.
770 *
771 * @return the derived title string.
772 **/
773 function taxonomy_dss_get_title($str_tids) {
774 $terms = taxonomy_dss_terms_parse_string($str_tids, TRUE);
775 $title = implode(' › ', $terms['names']);
776
777 return $title;
778 }
779
780 /**
781 * Given an array of taxonomy terms, this function will validate it against
782 * a set of term IDs and strip out invalid ones. By default it will also strip
783 * out hidden terms.
784 *
785 * @param $taxonomy
786 * An array of taxonomy terms to be filtered.
787 * @param $tids
788 * An array of the valid taxonomy term IDs.
789 * @param $minimal
790 * (optional) When TRUE, this function will look at the weights of each term
791 * and its vocabulary, and remove the terms that are in the right order.
792 * Defaults to FALSE.
793 * @param $strip_hidden
794 * (optional) Strips hidden vocabularies when TRUE. Defaults to TRUE.
795 *
796 * @return
797 * An array of taxonomy term IDs, with hidden and excess terms stripped out.
798 */
799 function taxonomy_dss_filter_tids($taxonomy, $tids = array(), $minimal = FALSE, $strip_hidden = TRUE) {
800 // Strip out other terms from the same vocabulary
801 $strip_same_vocabulary = TRUE;
802
803 $return = array();
804
805 if (count($taxonomy) == 0) {
806 return $return;
807 }
808
809 if (!is_array($tids)) {
810 if ($tids) {
811 $terms = taxonomy_dss_terms_parse_string($tids);
812 $tids = $terms['tids'];
813 }
814 else {
815 $tids = array();
816 }
817 }
818
819 $vids = array();
820 $keys = array_keys($taxonomy);
821
822 // Filter out invalid and hidden tids
823 for ($i = 0; $i < count($tids); $i++) {
824 /*
825 // If the term's vocabulary is hidden, don't return it
826 if ($strip_hidden) {
827 // Is this a valid tid?
828 if (in_array($tids[$i], $keys)) {
829 $term = $taxonomy[$tids[$i]];
830
831 // Cache vocabulary data
832 if (!isset($vids[$term->vid])) {
833 $vids[$term->vid] = taxonomy_dss_vocabulary_is_hidden($term->vid);
834 }
835
836 // Only return tid if it's not hidden
837 if ($vids[$term->vid] == FALSE) {
838 $return[] = $term->tid;
839 }
840 }
841 }
842 else {
843 */
844 // Is this a valid tid?
845 if (in_array($tids[$i], $keys)) {
846 $term = $taxonomy[$tids[$i]];
847
848 if (!isset($vids[$term->vid])) {
849 if ($strip_hidden) {
850 $vids[$term->vid] = taxonomy_dss_vocabulary_is_hidden($term->vid);
851 }
852 else {
853 $vids[$term->vid] = FALSE;
854 }
855 }
856 $return[] = $tids[$i];
857 }
858 /*
859 }
860 */
861 }
862
863 // Filter out hidden tids, and add the remaining ones
864 for ($i = 0; $i < count($keys); $i++) {
865 if (!in_array($keys[$i], $return)) {
866 $term = $taxonomy[$keys[$i]];
867
868 if ($strip_hidden) {
869 // Cache vocabulary data
870 if (!isset($vids[$term->vid])) {
871 $vids[$term->vid] = taxonomy_dss_vocabulary_is_hidden($term->vid);
872 if ($strip_same_vocabulary == TRUE && $vids[$term->vid] == FALSE) {
873 $return[] = $term->tid;
874 }
875 }
876
877 // Only return tid if it's not hidden
878 if ($strip_same_vocabulary == FALSE && $vids[$term->vid] == FALSE) {
879 $return[] = $term->tid;
880 }
881 }
882 else {
883 if ($strip_same_vocabulary) {
884 if (!isset($vids[$term->vid])) {
885 $vids[$term->vid] = FALSE;
886 $return[] = $keys[$i];
887 }
888 }
889 else {
890 $return[] = $keys[$i];
891 }
892 }
893 }
894 }
895
896 // Filter out redundant tids (tids that are already in order of weight)
897 if ($minimal) {
898 for ($i = count($keys) -1; $i >= 0; $i--) {
899 if ($return[$i] == $keys[$i]) {
900 array_pop($return);
901 }
902 }
903 }
904
905 return $return;
906 }
907
908 function taxonomy_dss_select_children($tids) {
909 if (!is_array($tids) && is_numeric($tids)) {
910 $tids = array($tids);
911 }
912 if (count($tids) > 0) {
913 $joins = '';
914 $wheres = '';
915 foreach ($tids as $index => $tid) {
916 $joins .= ' INNER JOIN {term_node} tn'. $index .' ON n.nid = tn'. $index .'.nid';
917 $wheres .= ' AND tn'. $index .'.tid = '. $tid;
918 }
919 $sql = "SELECT DISTINCT(n.nid) FROM {node} n {$joins} WHERE n.status = 1 {$wheres} AND (SELECT COUNT(DISTINCT td0.vid) FROM {term_node} t LEFT JOIN {term_data} td0 ON t.tid = td0.tid WHERE t.nid = n.nid) > ". count($tids);
920
921 $sql = "SELECT tn.nid, tn.tid, td.vid, td.name, td.description, td.weight, v.weight AS vweight FROM {term_node} tn LEFT JOIN {term_data} td ON td.tid = tn.tid LEFT JOIN {vocabulary} v ON td.vid = v.vid WHERE tn.nid IN ({$sql}) AND td.vid NOT IN (SELECT td1.vid FROM {term_data} td1 WHERE td1.tid IN (". implode(',', $tids) .")) ORDER BY v.weight DESC, td.weight DESC, td.name DESC";
922
923 $sql = "SELECT DISTINCT r.tid, r.vid, r.name, r.description, r.weight, GROUP_CONCAT(DISTINCT r.tid ORDER BY r.vweight, r.weight, r.name SEPARATOR ',') AS tids FROM ({$sql}) r GROUP BY r.nid ORDER BY r.vweight, r.weight, r.name";
924
925 $result = db_query(db_rewrite_sql($sql, 'tn', 'randomfieldname'));
926 }
927
928 return $result;
929 }
930
931 function taxonomy_dss_get_tree_children($tids, &$result, $depth = 0) {
932 $children = array();
933
934 // Get all child terms
935 $child_terms = taxonomy_get_children(end($tids));
936
937 // Add all children from other vocabularies
938 if (db_num_rows($result) > 0) {
939 while ($term = db_fetch_object($result)) {
940 $child_terms[] = $term;
941 }
942 }
943
944 foreach ($child_terms as $child) {
945 // Create a new tids array
946 $mytids = $tids;
947
948 // Fix for child terms (not child terms from other vocabularies)
949 if (!isset($child->tids)) {
950 array_pop($mytids);
951 }
952
953 $child->nodes = array();
954
955 // Is child a sub of other child(ren)?
956 if (strpos($child->tids, ',') !== FALSE) {
957 $atids = explode(',', $child->tids);
958
959 $mychildren = &$children;
960 while (count($atids) > 1) {
961 $tid = array_shift($atids);
962 array_push($mytids, $tid);
963
964 if (!isset($mychildren[$tid])) {
965 // Fetch missing term
966 $term = taxonomy_get_term($tid);
967
968 $term->count = _taxonomy_dss_count_nodes($mytids);
969 // $term->children = array();
970 $term->nodes = array();
971 $term->pids = $mytids;
972 $term->link = 'taxonomy/term/'. implode(',', $mytids);
973 $term->hidden = taxonomy_dss_term_is_hidden(implode(',', $mytids));
974
975 $mychildren[$tid] = $term;
976 }
977
978 // Handle terms from the same vocabulary
979 $next_term = taxonomy_get_term($atids[0]);
980 if ($mychildren[$tid]->vid !== $next_term->vid) {
981 if (!isset($mychildren[$tid]->children)) {
982 $mychildren[$tid]->children = array();
983 }
984 $mychildren = &$mychildren[$tid]->children;
985 }
986 else {
987 array_pop($mytids);
988 }
989
990 }
991
992 // Add details
993 array_push($mytids, $child->tid);
994 $child->count = _taxonomy_dss_count_nodes($mytids);
995 $child->pids = $mytids;
996 $child->link = 'taxonomy/term/'. implode(',', $mytids);
997 $child->hidden = taxonomy_dss_term_is_hidden(implode(',', $mytids));
998
999 // Parent node should already be created
1000 $mychildren[$child->tid] = $child;
1001 }
1002 else {
1003 // Add details
1004 array_push($mytids, $child->tid);
1005 $child->count = _taxonomy_dss_count_nodes($mytids);
1006
1007 if (count($tids) == 1 || $child->count > 0) {
1008 $child->pids = $mytids;
1009 $child->link = 'taxonomy/term/'. implode(',', $mytids);
1010 $child->hidden = taxonomy_dss_term_is_hidden(implode(',', $mytids));
1011
1012 if (!isset($child->tids)) {
1013 $mychildren = taxonomy_dss_get_tree_children($mytids, taxonomy_dss_select_children($mytids), 0);
1014 if (count($mychildren)) {
1015 $child->children = $mychildren;
1016 }
1017 }
1018
1019 // Normal child, not a sub-child
1020 $children[$child->tid] = $child;
1021 }
1022 }
1023 }
1024
1025 // Expand terms to include parent terms
1026 _taxonomy_dss_expand_terms($children, $tids);
1027
1028 // Strip anything that's in a deeper level than requested
1029 if ($depth > 0) {
1030 _taxonomy_dss_limit_depth($children, $depth);
1031 }
1032
1033 // Sort terms by weight: first by vocabulary weight, then by individual term weights
1034 _taxonomy_dss_sort_tree($children);
1035
1036 return $children;
1037 }
1038
1039 /**
1040 * Replacement for taxonomy_select_nodes.
1041 * Finds all nodes that match selected taxonomy conditions.
1042 *
1043 * @param $tids
1044 * An array of term IDs to match.
1045 * @param $operator
1046 * How to interpret multiple IDs in the array. Can be "or" or "and".
1047 * @param $depth
1048 * How many levels deep to traverse the taxonomy tree. Can be a nonnegative
1049 * integer or "all".
1050 * @param $pager
1051 * Whether the nodes are to be used with a pager (the case on most Drupal
1052 * pages) or not (in an XML feed, for example).
1053 * @param $order
1054 * The order clause for the query that retrieve the nodes.
1055 * @return
1056 * A resource identifier pointing to the query results.
1057 */
1058 function taxonomy_dss_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $order = 'n.sticky DESC, n.created DESC', $exclusive = TRUE, $promoted = FALSE, $nodecount = 0) {
1059 if (count($tids) > 0) {
1060 // For each term ID, generate an array of descendant term IDs to the right depth.
1061 $descendant_tids = array();
1062 if ($depth === 'all') {
1063 $depth = NULL;
1064 }
1065 foreach ($tids as $index => $tid) {
1066 $term = taxonomy_get_term($tid);
1067 $tree = taxonomy_get_tree($term->vid, $tid, -1, $depth);
1068 $descendant_tids[] = array_merge(array($tid), array_map('_taxonomy_get_tid_from_term', $tree));
1069 }
1070
1071 $promote_sql = $promoted ? 'AND n.promote = 1 ' : '';
1072
1073 if ($operator == 'or') {
1074 $str_tids = implode(',', call_user_func_array('array_merge', $descendant_tids));
1075 $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 .') '. $promote_sql .'AND n.status = 1 ORDER BY '. $order;
1076 $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 .') '. $promote_sql .'AND n.status = 1';
1077 }
1078 else {
1079 $joins = '';
1080 $wheres = '';
1081 foreach ($descendant_tids as $index => $tids) {
1082 $joins .= ' INNER JOIN {term_node} tn'. $index .' ON n.nid = tn'. $index .'.nid';
1083 $wheres .= ' AND tn'. $index .'.tid IN ('. implode(',', $tids) .')';
1084 }
1085 if ($exclusive) {
1086 $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n '. $joins .' WHERE n.status = 1 '. $promote_sql . $wheres .' AND (SELECT COUNT(DISTINCT td0.vid) FROM {term_node} t LEFT JOIN {term_data} td0 ON t.tid = td0.tid WHERE t.nid = n.nid) = '. count($descendant_tids) .' ORDER BY '. $order;
1087 $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n '. $joins .' AND (SELECT COUNT(*) FROM {term_node} t WHERE t.nid = n.nid) = '. count($descendant_tids) .' WHERE n.status = 1 '. $promote_sql . $wheres;
1088 }
1089 else {
1090 $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n '. $joins .' WHERE n.status = 1 '. $promote_sql . $wheres .' ORDER BY '. $order;
1091 $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n '. $joins .' WHERE n.status = 1 '. $promote_sql . $wheres;
1092 }
1093 }
1094 // Once Taxonomy Acccess Control or any other acces control module gets
1095 // enabled, it tries to mess with the SQL unless you set the fieldname to
1096 // something it doesn't recognize.
1097 $sql = db_rewrite_sql($sql, 'tn', 'randomfieldname');
1098 $sql_count = db_rewrite_sql($sql_count, 'tn', 'randomfieldname');
1099
1100 if ($pager) {
1101 if ($nodecount == 0) {
1102 $nodecount = variable_get('default_nodes_main', 10);
1103 }
1104 $result = pager_query($sql, $nodecount, 0, $sql_count);
1105 }
1106 else {
1107 if ($nodecount == 0) {
1108 $nodecount = variable_get('feed_default_items', 10);
1109 }
1110 $result = db_query_range($sql, 0, $nodecount);
1111 }