Project

General

Profile

Download (31.6 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
// $Id$
3

    
4
/*
5
 * @file
6
 * cdm_taxontree.module
7
 *
8
 * Copyright (C) 2007 EDIT
9
 * European Distributed Institute of Taxonomy
10
 * http://www.e-taxonomy.eu
11
 */
12

    
13

    
14

    
15
/**
16
 * Implementation of hook_menu()
17
 */
18
function cdm_taxontree_menu($may_cache) {
19

    
20
	$items = array();
21
	if ($may_cache) {
22

    
23
		$items[] = array(
24
          'path' => 'cdm_taxontree/set',
25
          'callback' => 'cdm_taxontree_set',
26
          'access' => true,
27
          'type' => MENU_CALLBACK,
28
		);
29

    
30
		$items[] = array(
31

    
32
          'path' => 'cdm_taxontree/filter',
33
          'callback' => 'cdm_taxontree_view_filter',
34
          'access' => true,
35
          'type' => MENU_CALLBACK,
36
		);
37

    
38
		$items[] = array(
39
          'path' => 'cdm_taxontree/taxonomy/children',
40
          'callback' => 'cdm_taxontree_taxonomy_children',
41
          'access' => true,
42
          'type' => MENU_CALLBACK,
43
		);
44

    
45
	} else {
46

    
47
		//      DISABLED since the taxontree-widged is no longer being used!!!
48
		//
49
		//      if(variable_get('cdm_taxontree_cache', 0)){
50
		//    		$items[] = array('path' => 'admin/settings/cdm_dataportal/clear_cache',
51
		//          'title' => t('Empty CDM Taxontree Cache'),
52
		//          'callback' => 'cdm_taxontree_cache_clear',
53
		//          'access' => user_access('administer cdm_dataportal'),
54
		//          'weight' => 2,
55
		//          'type' => MENU_NORMAL_ITEM,
56
		//    		);
57
		//      }
58

    
59
	}
60
	return $items;
61
}
62

    
63
/**
64
 * Implementation of hook_block()
65
 *
66
 * @param String $op
67
 * @param int $delta
68
 */
69
function cdm_taxontree_block($op='list', $delta=0, $edit = array()) {
70
	if ($op == "list") {
71
		$block['cdm_tree']["info"] = t('CDM taxon tree');
72
		$block['filters']["info"] = t('Active filters');
73
		$block[1]["info"] = t('Drupal taxonomy tree');
74
		return $block;
75
	}
76
	else if ($op == 'view') {
77
		switch($delta){
78
			case 'cdm_tree':
79
				$block['subject'] = t('Classification');
80
				$taxonUuid_inFocus = _cdm_get_taxonuuid();
81
				$tree = cdm_taxontree_build_tree($taxonUuid_inFocus);
82
				$magicbox_enable = variable_get('cdm_taxontree_magicbox_enable', 0);
83

    
84
				if(count(cdm_get_taxontrees_as_options()) > 1){
85
					$block['content'] = cdm_taxonomictree_selector();
86
				}
87
				$block['content'] .= theme('cdm_taxontree_block', $tree, $delta, FALSE, 'cdm_taxontree_node_concept_switch');
88
				// java script
89
				$verticalSroller = $magicbox_enable ? 'cdm_taxontree_scroller_x' : 'cdm_taxontree_scroller_xy';
90
				theme('cdm_taxontree_add_scripts');
91
				drupal_add_js('
92
        if (Drupal.jsEnabled) {
93
        $(document).ready(function()
94
        {
95
          $(\'ul.cdm_taxontree\').cdm_taxontree();
96
          $(\'div.'.$verticalSroller.'\').scrollTo($(\'.focused\'), 400, {over:-3});});
97
        }', 'inline');
98
				return $block;
99

    
100
			case 'filters':
101
				$block['subject'] = t('Active filters');
102
				$block['content'] = cdm_taxontree_view_filter('list');
103
				return $block;
104

    
105
			case 1:
106
				$block['subject'] = t('Taxonomy tree');
107
				$term_inFocus = arg(0) == 'taxonomy' && arg(1) == 'term' ? arg(2) : 0;
108
				$tree = cdm_taxontree_build_tree($term_inFocus, true, variable_get('cdm_taxontree_block_1_vid', 0));
109
				$block['content'] = theme('cdm_taxontree_block', $tree, $delta, FALSE);
110
				theme('cdm_taxontree_add_scripts');
111
				drupal_add_js('
112
        if (Drupal.jsEnabled) {
113
        $(document).ready(function()
114
        {
115
          $(\'ul.cdm_taxontree\').cdm_taxontree();
116
          $(\'div.cdm_taxontree_scroller_x\').scrollTo($(\'.active\'), 400, {over:-3});});
117
        }', 'inline');
118

    
119
				return $block;
120
		}
121
	} else if ($op == 'configure') {
122
		switch($delta){
123
			case 1:
124
				$vocs = taxonomy_get_vocabularies();
125
				$options = array();
126
				foreach($vocs as $voc){
127
					$options[$voc->vid] = $voc->name;
128
				}
129
				$form['vid'] = array(
130
            '#type' => 'select',
131
            '#title' => t('Category'),
132
            '#default_value' => variable_get('cdm_taxontree_block_1_vid', 0),
133
            '#options' => $options,
134
				);
135
				return $form;
136
		}
137
	} else if ($op == 'save') {
138
		switch($delta){
139
			case 1:
140
				variable_set('cdm_taxontree_block_1_vid', $edit['vid']);
141
				return;
142
		}
143
	}
144
}
145

    
146
/**
147
 * Implementation of hook_help().
148
 *
149
 * @param unknown_type $section
150
 * @return unknown
151
 */
152
function cdm_taxontree_help($section) {
153
	switch ($section) {
154
		case 'admin/modules#description':
155
			return t('Defines a selection widget for massive taxonomy structures.');
156
	}
157
}
158

    
159
/**
160
 * Implementation of hook_field_info().
161
 */
162
function cdm_taxontree_field_info() {
163
	return array(
164
    'cdm_taxontree' => array('label' => 'CDM Taxontree'),
165
	);
166
}
167

    
168

    
169

    
170
function cdm_taxontree_field_formatter_info() {
171
	return array(
172
    'default' => array(
173
      'label' => t('Default'),
174
      'field types' => array('cdm_taxontree'),
175
	),
176
    'link' => array(
177
      'label' => t('With link'),
178
      'field types' => array('cdm_taxontree'),
179
	),
180
	);
181
}
182

    
183

    
184

    
185
/*
186
 * formatters to prepare the correct links for taxa
187
 */
188
function cdm_taxontree_field_formatter($field, $item, $formatter, $node) {
189
	switch ($formatter) {
190
		case 'link':
191
			$term = taxonomy_get_term($item['tid']);
192
			$taxa = db_result(db_query('SELECT name FROM {vocabulary} WHERE vid = %d', $term->vid));
193
			switch($taxa){
194
				case 'Taxonomy':
195
					$link = 'interest_by_taxonomy/';
196
					break;
197
				case 'Georegion':
198
					$link = 'interest_by_georegion/';
199
					break;
200
				default:
201
					$link = 'taxonomy/term/';
202
			}
203
			return l($term->name, $link.$term->tid);
204

    
205
		default:
206
			$name = db_result(db_query('SELECT name FROM {term_data} WHERE tid = %d', $item['tid']));
207
			return $name;
208
	}
209
}
210

    
211

    
212
/**
213
 * Transforms an unpredictably and irregularly nested set of tids (as returned
214
 * from a taxonomy form) into a linear array of tids.
215
 * borrow from taxonomy_browser.module
216
 */
217
function _cdm_taxontree_get_all_children($tids = null, $include_children = false) {
218
	static $tid_list;
219

    
220
	if (isset($tids) && is_array($tids)) {
221
		$tid_list = array();
222
		foreach ($tids as $key => $tid) {
223
			if (!empty($tid)) {
224
				if (is_array($tid)) {
225
					foreach ($tid as $key2 => $tid2) {
226
						if (!empty($tid2)) {
227
							$tid_list[$tid2] = $tid2;
228
						}
229
					}
230
				}
231
				else {
232
					$tid_list[$tid] = $tid;
233
				}
234
			} /* end !empty */
235
		} /* end foreach */
236
	}
237

    
238
	if ($include_children) {
239
		foreach ($tid_list as $tid) {
240
			_cdm_taxontree_get_children($tid_list, $tid);
241
		}
242
	}
243

    
244
	return $tid_list;
245
}
246

    
247
function _cdm_taxontree_get_children(&$tid_list, $tid) {
248
	$child_nodes = taxonomy_get_children($tid);
249
	if (!empty($child_nodes)) {
250
		foreach ($child_nodes as $child_tid => $child_term) {
251
			$tid_list[$tid] = $tid;
252
			_cdm_taxontree_get_children($tid_list, $child_tid);
253
		}
254
	}
255
	else {
256
		$tid_list[$tid] = $tid;
257
	}
258
}
259

    
260
function cdm_taxontree_set($key, $value){
261

    
262
	if(is_string($key)){
263
		$_SESSION['cdm']['taxontree'][$key] = $value;
264
	}
265

    
266
	if($_REQUEST['destination']){
267
		$destination = $_REQUEST['destination'];
268
		unset($_REQUEST['destination']);
269
		drupal_goto($destination);
270
	}
271
}
272

    
273

    
274
/**
275
 * Enter description here...
276
 *
277
 * @param UUID $secUuid
278
 * @return unknown
279
 */
280
function cdm_taxontree_secRefTitle_for($secUuid){
281

    
282
	$reference = cdm_api_secref_cache_get($secUuid);
283
	if($reference){
284
		$cit = $reference->titleCache;
285
	} else {
286
		$cit = '[no title for:'.$secUuid.']';
287
	}
288
	return $cit;
289
}
290

    
291

    
292

    
293
/**
294
 * Queries the Drupal database for the location of a certain block with the given $delta
295
 *
296
 * @param [String | Number] $delta
297
 * @return values (left | right | <empty>)
298
 */
299
function _get_block_region($delta){
300
	global $user, $theme_key;
301

    
302
	$result = db_fetch_array(db_query("SELECT DISTINCT b.region FROM {blocks} b LEFT JOIN {blocks_roles} r ON b.module = r.module AND b.delta = r.delta WHERE b.theme = '%s' AND b.status = 1 AND (r.rid IN (%s) OR r.rid IS NULL) AND b.module = '%s' AND b.delta = '%s'", $theme_key, implode(',', array_keys($user->roles)), 'cdm_taxontree', $delta));
303
	return $result['region'];
304
}
305

    
306
/**
307
 * Enter description here...
308
 *
309
 * @return unknown
310
 */
311
function _get_compact_mode(){
312

    
313
	if(!isset($_SESSION['cdm']['taxontree']['compact_mode'])){
314
		$_SESSION['cdm']['taxontree']['compact_mode'] = 'expanded';
315
	}
316
	return  $_SESSION['cdm']['taxontree']['compact_mode'];
317
}
318

    
319

    
320
/**
321
 * Converts an array of drupal taxonomy terms into an
322
 * array of partially instantiated cdm tree nodes by adding the fields
323
 * relevant for tree node processing in cdm_taxontree
324
 *
325
 *
326
 * term       =>      cdm tree node
327
 * ------------------------------------
328
 * tid       ->     uuid
329
 name      ->     titleCache
330
 taggedName
331
 secUuid
332
 isAccepted
333
 taxonomicChildrenCount
334
 alternativeConceptRefs
335
 *
336
 * @param unknown_type $terms
337
 */
338
function cdm_taxontree_terms2treenodes(&$terms){
339
	foreach($terms as &$term){
340
		$term->uuid = $term->tid;
341
		$term->titleCache = $term->name;
342
		$term->taxonomicChildrenCount = count(taxonomy_get_children($term->tid, $term->vid));
343
	}
344
	return $terms;
345
}
346

    
347
/**
348
 * Enter description here...
349
 *
350
 * @param unknown_type $tid
351
 * @param unknown_type $vid
352
 * @param unknown_type $theme
353
 */
354
function cdm_taxontree_taxonomy_children($tid, $vid, $theme){
355
	$args = func_get_args();
356
	$tid = array_shift($args);
357
	$vid = array_shift($args);
358
	$theme = array_shift($args);
359

    
360
	$children = cdm_taxontree_get_children($tid, $vid);
361
	$children = cdm_taxontree_terms2treenodes($children);
362
	array_unshift($args, $theme, $children);
363
	print call_user_func_array('theme', $args);
364
}
365

    
366
/**
367
 * Enter description here...
368
 *
369
 * @param unknown_type $vid
370
 * @param unknown_type $secRefUuuid
371
 * @return unknown
372
 */
373
function cdm_taxontree_get_root($vid = null){
374
	if(is_numeric($vid)){
375
		// vid, $parent = 0, $depth = -1, $max_depth = NULL) {
376
		$terms = taxonomy_get_tree($vid, 0, -1, 1);
377
		return cdm_taxontree_terms2treenodes($terms);
378
	} else {
379
		return cdm_ws_taxonomy();
380
	}
381
}
382

    
383
/**
384
 * Enter description here...
385
 *
386
 * @param unknown_type $uuid
387
 * @param unknown_type $vid
388
 * @return unknown
389
 */
390
function cdm_taxontree_get_children($uuid, $vid = null){
391

    
392
	if(is_numeric($vid)){
393
		$terms = taxonomy_get_children($uuid, $vid);
394
		return cdm_taxontree_terms2treenodes($terms);
395
	} else {
396
		//FIXME replace $uuid by path of parent $uuids
397

    
398
		return cdm_ws_taxonomy($uuid);
399
	}
400
}
401

    
402
/**
403
 * Enter description here...
404
 *
405
 * @param unknown_type $uuid
406
 * @return unknown
407
 */
408
function cdm_taxontree_get_parents($uuid){
409

    
410
	if(!is_uuid($uuid)){
411
		// using Drupal taxonomy
412
		$terms = taxonomy_get_parents($uuid);
413
		array_push($terms, taxonomy_get_term($uuid));
414
		$terms = array_reverse($terms);
415
		return cdm_taxontree_terms2treenodes($terms);
416
	} else {
417
		// using cdm
418
		$terms = cdm_ws_taxonomy_pathFromRoot($uuid);
419
		if(!$terms){
420
			return;
421
		}
422
		$terms = array_reverse($terms);
423
		return $terms;
424
	}
425
}
426

    
427
/**
428
 * builds a tree of TaxonNode instances, whereas the instances are extended by some fields:
429
 *
430
 *  - $node->filter: values ( 'on', 'excluded', 'included' )
431
 *  - $node->expanded: values ( 'expanded', 'collapsed' )
432
 *    $node->focused: values ( TRUE, FALSE )
433
 *
434
 * @param unknown_type $taxonUuid
435
 * @return unknown
436
 */
437
function cdm_taxontree_build_tree($taxonUuid = null, $hideOtherConcepts = true, $vid = null){
438
	//TODO remove $hideOtherConcepts from method signature
439

    
440
	if(is_null($vid)){
441

    
442
		if($taxonUuid){
443
			$taxon =  cdm_ws_get(CDM_WS_PORTAL_TAXON, $taxonUuid);
444
		}
445

    
446
		$compact_tree = cdm_taxontree_filters_active() && _get_compact_mode() != 'expanded';
447
	}
448
	/* valid compact_modes: 'expanded', 'compact', 'flattened' */
449

    
450
	// get the root level
451
	$root_tree = cdm_taxontree_get_root($vid);
452
	//  if(!$root_tree || !is_array($root_tree)){
453
	//    return array();
454
	//  }
455
	$root_tree = _cdm_resultset2nodelist($root_tree, cdm_taxontree_filters_active());
456

    
457

    
458
	if(cdm_taxontree_filters_active()){
459
		// the paths up to active filters are inactive in the user interface and
460
		// thus cannot be browsed by expanding nodes
461
		// therefore we need to build up the branches for all nodes which are set as filters
462
		// the branches are merged with the root
463
		foreach(cdm_taxontree_filters_get() as $uuid=>$filter){
464
			$branch = cdm_taxontree_build_path($uuid, TRUE, ($compact_tree === false ? true :null));
465
			$root_tree = _cdm_taxontree_merge($root_tree, $branch);
466
		}
467
	}
468

    
469
	// build the the branch for the focused node and merge it with the root
470
	if($taxonUuid){
471
		$branch = cdm_taxontree_build_path($taxonUuid, NULL, (cdm_taxontree_filters_active() ? NULL : TRUE), TRUE);
472
		$root_tree = _cdm_taxontree_merge($root_tree, $branch);
473
	}
474

    
475
	//reorder siblings & populate expanded nodes with children and propagate the filter attribute
476
	$root_tree = cdm_taxontree_populate($root_tree, $compact_tree === false);
477

    
478
	// flatten tree
479
	if($compact_tree){
480
		if( _get_compact_mode() == 'flattened'){
481
			$root_tree = cdm_taxontree_flatten($root_tree);
482
		} else if(_get_compact_mode() == 'compact') {
483
			foreach($root_tree as $uuid => $node){
484
				if( $node->filter == 'excluded' && !$node->children){
485
					unset($root_tree[$uuid]);
486
				}
487
			}
488
		}
489
	}
490

    
491
	return $root_tree;
492
}
493

    
494
/**
495
 * Builds the specific branch path for $taxonUuid.
496
 * The branch path reaches from the parent root node of $taxonUuid up to $taxonUuid.
497
 *
498
 * @param UUID $taxonUuid
499
 * @param Boolean $is_filter_path whether the upmost node of this path is mapped by an active filter
500
 * @param Boolean $is_expanded whether all nodes along the tree are expanded
501
 * @param Boolean $is_focused whether to upper most element of this branch is set as filter
502
 * @return a subtree
503
 */
504
function cdm_taxontree_build_path($taxonUuid, $is_filter_path = null, $is_expanded = null, $is_focused = FALSE){
505

    
506
	$branch_path = array();
507

    
508
	$parents = cdm_taxontree_get_parents($taxonUuid);
509
	if(!$parents){
510
		if($is_filter_path){
511
			// remove invalid filter
512
			cdm_taxontree_filters_remove($taxonUuid);
513
		}
514
		return false;
515
	}
516

    
517
	$parents = _cdm_resultset2nodelist($parents, NULL);
518
	$lastParent = null;
519
	foreach($parents as $pnode){
520
		$pnode->focused = false;
521
		if($lastParent){
522
			$pnode->children = array($lastParent->taxonUuid => $lastParent);
523
			if(!is_null($is_filter_path)){
524
				$pnode->filter = ($is_filter_path ? 'excludes' : 'included');
525
			}
526
			if(!is_null($is_expanded)){
527
				$pnode->expanded = ($is_expanded ? 'expanded' : 'collapsed');
528
			}
529
		} else {
530
			// the uppermost node of branch
531
			if(!is_null($is_filter_path)){
532
				$pnode->filter = ($is_filter_path ? 'on' : 'includes');
533
			}
534
			// uppermost node is always expanded if it has children
535
			$pnode->focused = $is_focused;
536
			$pnode->expanded = ($pnode->taxonomicChildrenCount ? 'expanded' : 'collapsed');
537
		}
538
		$lastParent = $pnode;
539
	}
540
	$branch_path[$pnode->taxonUuid] = $pnode;
541
	return $branch_path;
542
}
543

    
544
/**
545
 * Performs two steps on each level of the tree:
546
 *  1. reorder siblings except root (is expected to be ordered jet) alphabetically
547
 *  2. populate children of expanded nodes  & propagate the filter attribute
548
 *
549
 * @param unknown_type $tree
550
 * @return unknown
551
 */
552
function cdm_taxontree_populate($tree, $expand_excluded, $filter_default = null){
553

    
554
	if(!is_array($tree)){
555
		return false;
556
	}
557
	foreach(array_keys($tree) as $uuid){
558

    
559
		if(!isset($tree[$uuid]->filter) && !is_null($filter_default)){
560
			$tree[$uuid]->filter = $filter_default;
561
		}
562

    
563
		if( $tree[$uuid]->expanded == 'expanded' && ($expand_excluded || $tree[$uuid]->filter != 'excluded')){
564
			$children = cdm_taxontree_get_children($uuid, $tree[$uuid]->vid);
565
			$children = _cdm_resultset2nodelist($children, ($tree[$uuid]->filter == 'excludes'));
566

    
567
			// store the children of the node for later processing
568
			if(is_array($tree[$uuid]->children)){
569
				$pnode_children = $tree[$uuid]->children;
570
			} else {
571
				$pnode_children = false;
572
			}
573
			// replace the children by the newly retrieved child nodes
574
			$tree[$uuid]->children = $children;
575

    
576
			if($pnode_children){
577
				// recurse into the childtree which was stored before
578
				$pnode_children =  cdm_taxontree_populate($pnode_children, $expand_excluded ,$tree[$uuid]->filter);
579
				// recombine
580
				foreach($pnode_children as $childUuid=>$cnode){
581
					$tree[$uuid]->children[$childUuid] = $cnode;
582
				}
583
			}
584
		} else {
585
			// reorder nodes which are not expanded, expanded nodes are reordered implicitly above
586
			if(isset($tree[$uuid]->children) && count($tree[$uuid]->children) > 1) {
587
				// copy the children into an array which can be sorted by its keys
588
				$ordered = array();
589
				foreach($tree[$uuid]->children as $cnode){
590
					// concatenate full name and uid
591
					$reordered[str_pad($cnode->titleCache, 255, '-').$cnode->taxonUuid] = $cnode;
592
				}
593
				// sort
594
				ksort($reordered);
595
				// move the children back into the parent node
596
				$tree[$uuid]->children = array();
597
				foreach($reordered as $cnode){
598
					$tree[$uuid]->children[$cnode->taxonUuid] = $cnode;
599
				}
600
			}
601
			$tree[$uuid]->children = cdm_taxontree_populate($tree[$uuid]->children, $expand_excluded, $tree[$uuid]->filter);
602
		}
603
	}
604
	return $tree;
605
}
606

    
607
/**
608
 * Enter description here...
609
 *
610
 * @param unknown_type $tree
611
 * @param unknown_type $new_root
612
 * @return unknown
613
 */
614
function cdm_taxontree_flatten($tree, &$new_root = null){
615
	if(!$new_root){
616
		$new_root = array();
617
	}
618
	foreach($tree as $node){
619
		if($node->filter == 'on'){
620
			$new_root[$node->taxonUuid] = $node;
621
		} else if(is_array($node->children)){
622
			cdm_taxontree_flatten($node->children, $new_root);
623
		}
624
	}
625
	return $new_root;
626
}
627

    
628

    
629
/**
630
 * Merge a branch into a tree whereas the tree dominated the branch except
631
 * nodes having property filter set to "on". These always dominate
632
 * nevertheless if they are in tree or branch.
633
 *
634
 * @param unknown_type $tree the dominant tree
635
 * @param unknown_type $branch the tree to be merged in
636
 * @return the merged $tree
637
 */
638
function _cdm_taxontree_merge($tree, $branch) {
639

    
640
	if(!$branch){
641
		return $tree;
642
	}
643

    
644
	foreach(array_keys($tree) as $uuid) {
645
		// check if node exists in $branch
646
		if(!empty($branch[$uuid])) {
647
			// preserve filter property
648
			if(isset($tree[$uuid]->filter) && !(isset($branch[$uuid]->filter) && $branch[$uuid]->filter == 'on') ){
649
				$branch[$uuid]->filter = $tree[$uuid]->filter;
650
			} else if(isset($branch[$uuid]->filter)){
651
				$tree[$uuid]->filter = $branch[$uuid]->filter;
652
			}
653
			// preserve expanded property
654
			if(isset($tree[$uuid]->expanded)){
655
				$branch[$uuid]->expanded = $tree[$uuid]->expanded;
656
			} else if(isset($branch[$uuid]->expanded)){
657
				$tree[$uuid]->expanded = $branch[$uuid]->expanded;
658
			}
659
			// $Uuid exists check if the node in tree1 or tree2 contains children
660
			if(is_array($branch[$uuid]->children) && is_array($tree[$uuid]->children)) {
661
				// merge recursive
662
				$tree[$uuid]->children = _cdm_taxontree_merge($tree[$uuid]->children, $branch[$uuid]->children);
663
			} else if(is_array($branch[$uuid]->children)){
664
				$tree[$uuid] =  $branch[$uuid];
665
			}
666
			unset($branch[$uuid]);
667
		}
668
	}
669
	// append remaining items from branch to tree
670
	foreach(array_keys($branch) as $uuid){
671
		$tree[$uuid] =  $branch[$uuid];
672
	}
673
	return $tree;
674
}
675

    
676

    
677
/**
678
 * Replaces the keys of an array of TreeNode instances
679
 * by the $treenode->taxonUuid of the single array elements.
680
 * An sets additional fields
681
 *
682
 * @param $resultset array of TreeNode instances as +returned by the cdm web service
683
 * @param $excluded  whether the $resultset is included by a active filter. Is ignored if NULL.
684
 * @param $expanded  whether the children of the nodes in the $resultset are expanded or not. Is ignored if NULL.
685
 */
686
function _cdm_resultset2nodelist($resultset, $excluded = null, $expanded = null){
687

    
688
	if(! is_array($resultset)) {
689
		return false;
690
	}
691

    
692
	$tree = array();
693
	foreach($resultset as $treeNode){
694
		if(!is_null($excluded)){
695
			$treeNode->filter = ($excluded ? 'excluded': 'included');
696
		}
697
		if(!is_null($expanded)){
698
			$treeNode->expanded = ($expanded ? 'expanded': 'collapsed');
699
		}
700
		$tree[$treeNode->taxonUuid] = $treeNode;
701
	}
702
	return $tree;
703
}
704

    
705

    
706
// ------------------------ THEME --------------------------- //
707

    
708

    
709
function theme_cdm_taxontree_add_scripts(){
710
	$path_cdm_taxontree = drupal_get_path('module', 'cdm_taxontree');
711
	$path_preferred_module = drupal_get_path('module', 'cdm_dataportal') ? drupal_get_path('module', 'cdm_dataportal') : $path_cdm_taxontree;
712
	drupal_add_css($path_cdm_taxontree.'/cdm_taxontree.css');
713
	drupal_add_js($path_preferred_module.'/js/jquery.dimensions.js');
714
	drupal_add_js($path_cdm_taxontree.'/js/cdm_taxontree.js');
715
	drupal_add_js($path_cdm_taxontree.'/js/jquery.scrollTo.js');
716
}
717

    
718

    
719
/**
720
 * @param $tree 				the tree of TreeNode to be displayed
721
 * @param $magicbox 			if true, the tree will be embedded into a set of div tags which allow the
722
 *            					tree to expand and overlap other content. This is useful if the node titles are
723
 *            					quite long or if the tree is nested deeply.
724
 *            					If $magicbox ist set to the delta of the containing block the direction into which the
725
 *            					box expands is dependent on the region in which the blockis located.
726
 *            					See also Variable $left_expand_region in this function!
727
 * @param $show_filter_switch 	The tree can offer buttons to add a node to a set of filters
728
 *            					which can then be applied to the tree to limit the visible subtrees and thus
729
 *            					to compact the tree. Three different compact modes are available.
730
 * @param $tree_node_callback 	name of a callback method which will be called for each node
731
 *            					in theme_cdm_taxontree_node(). The output of this callback, which takes the
732
 *            					$node object as single arument, is appended to the end of the redered node.
733
 */
734
function theme_cdm_taxontree_block($tree, $delta, $magicbox = false, $show_filter_switch = false, $tree_node_callback = false){
735

    
736
	// THEMERS: change the line below according the specific regions of your theme
737
	$left_expand_region = 'right';
738

    
739
	$out = '';
740
	if(cdm_taxontree_filters_active()){
741
		$out .= theme('cdm_taxontree_contoller', _get_compact_mode());
742
	}
743
	if($magicbox){
744
		if(is_numeric($magicbox) || is_string($magicbox)){
745
			$region = _get_block_region($magicbox);
746
		}
747
		// the magicbox expands to the right by default,
748
		// if the class 'expand-left' to  the cdm_taxontree_scroller_x the box will expand to the left
749
		$expand_direction = $region == 'right' ? 'expand-left' : '';
750
		$out .= '<div class="cdm_taxontree_scroller_x '.$expand_direction.'"><div class="cdm_taxontree_container"><div class="cdm_taxontree_scroller_y">';
751
	} else {
752
		$out .= '<div class="cdm_taxontree_scroller_xy">';
753
	}
754

    
755
	$out .= theme('cdm_taxontree', $tree, !cdm_taxontree_filters_active(), $show_filter_switch , $tree_node_callback);
756

    
757
	if($magicbox){
758
		$out .= '</div></div></div>';
759
	} else {
760
		$out .= '</div>';
761
	}
762
	return $out;
763
}
764

    
765
function theme_cdm_taxontree_contoller($compact_mode){
766

    
767
	static $modes = array('expanded', 'compact', 'flattened');
768

    
769
	$out = '<div class="settings">';
770
	foreach($modes as $mode){
771
		if($compact_mode == $mode){
772
			$out .= t($mode);
773
		} else {
774
			$out .= l(t($mode), 'cdm_taxontree/set/compact_mode/'.$mode, array(), drupal_get_destination());
775
		}
776
		$out .= ' ';
777
	}
778

    
779
	return $out.'</div>';
780
}
781

    
782
function theme_cdm_taxontree($tree, $filterIncludes = null, $show_filter_switch = false, $tree_node_callback = false, $element_name = false){
783

    
784
	if(!is_array($tree)) {
785
		//    $out = '<ul class="cdm_taxontree">';
786
		//    $out .= '<li>----------------------------NO TREE---------------------------------------</li>';
787
		//    $out .= '</ul>';
788
		//    return $out;
789
		return false;
790
	}
791

    
792
	if(is_null($filterIncludes)){
793
		// set $filterIncludes true if no filters are set.
794
		$filterIncludes = !cdm_taxontree_filters_active();
795
	}
796

    
797
	// append element name to get multiple taxontrees on one page working
798
	$out = '<ul class="cdm_taxontree' . (($element_name) ? ' ' . $element_name : '') . '">';
799
	foreach($tree as $node){
800
		$out .= theme('cdm_taxontree_node', $node, $filterIncludes, $show_filter_switch, $tree_node_callback);
801
	}
802
	$out .= '</ul>';
803
	return $out;
804
}
805

    
806
function theme_cdm_taxontree_node($node, $filterIncludes, $show_filter_switch = false, $tree_node_callback = false){
807

    
808
	$is_leaf = !$node->taxonomicChildrenCount || $node->taxonomicChildrenCount == 0;
809
	$is_expanded = isset($node->expanded) && $node->expanded = 'expanded';
810
/*
811
if($node->taxonUuid == '1a0bb171-9461-4713-b98d-d0a5f03dfcb1'){
812
	var_dump('ACHTUNG ##########!!!!!!!!!!!!');
813
}
814
*/
815
	if($node->tid){
816
		$node_name = $node->name;
817
		$path = "taxonomy/term/".$node->tid;
818
		// disable filterswitch
819
		$show_filter_switch = false;
820

    
821
	} else if(module_exists('cdm_dataportal')){
822
		$node_name = cdm_dataportal_shortname_of($node);
823
		$path = path_to_taxon($node->taxonUuid);
824
	} else {
825
		$node_name = "module cdm_dataportal missing";
826
		$path = "";
827
	}
828

    
829
	if($filterIncludes){
830
		$name = l($node_name, $path);
831
		// no names for terms in filter widget; as discussed with A. Müller
832
		//$name = '';
833

    
834
		$filter_class = 'filter_included';
835
	} else {
836
		if($node->filter == 'on') {
837
			$name = l($node_name,  $path);
838
			$filter_class = 'filter_on';
839
		} else {
840
			$name .= $node_name;
841
			$filter_class = 'filter_excluded';
842
		}
843
	}
844
	$nextLevelIncluded = $node->filter == 'on' || $filterIncludes;
845

    
846
	$ahah_url = false;
847
	if(!$is_leaf && !$is_expanded && $filter_class != 'filter_excluded'){
848
		if($node->tid){
849
			$ahah_url = url('cdm_taxontree/taxonomy/children/'.$node->tid.'/'.$node->vid.'/cdm_taxontree/'.($nextLevelIncluded ? 1 : 0).'/'.($show_filter_switch ? 1 : 0).'/'.$tree_node_callback);
850
		} else if(module_exists('cdm_dataportal')) {
851
			$ws_url = cdm_compose_taxonomy_path($node->taxonUuid);
852
			$ahah_url = url('cdm_api/proxy/'.urlencode($ws_url).'/cdm_taxontree/'.($nextLevelIncluded ? 1 : 0).'/'.($show_filter_switch ? 1 : 0).'/'.$tree_node_callback);
853
		}
854
	}
855

    
856
	// list item
857
	$out = '<li class="'
858
	.($node->focused ? 'focused ' : '')
859
	.($is_leaf ? 'leaf ':($is_expanded ?'expanded ':'collapsed '))
860
	.$filter_class.'"'
861
	.($ahah_url ? 'ref="'.$ahah_url.'"' : '')
862
	.'>';
863

    
864
	if($show_filter_switch){
865
		// filter icon
866
		$out .= theme('cdm_taxontree_node_filter_switch', $node, $filter_class);
867
	}
868

    
869
	// taxon name
870
	$out .= $name;
871

    
872
	// concept_switch or other theme callbacks
873
	if($tree_node_callback){
874
		$out .= theme($tree_node_callback, $node);
875
	}
876

    
877
/*
878
	foreach ($node->children as $element){
879
		$nameStr = '';
880
		foreach($element->taggedTitle as $tagtxt){
881
			if($tagtxt->type == 'name' || $tagtxt->type == 'rank'){
882
				$nameStr .= ($nameStr ? ' ' : '').$tagtxt->text;
883
			}
884
		}
885
		$nameStr = trim($nameStr);
886
		if (stristr(strtolower($tagtxt->text), 'incertae sedis') !== FALSE ||
887
		    stristr(strtolower($tagtxt->text), 'nomina excludenda') !== FALSE){
888
			var_dump('PREMIO');
889
			var_dump($element);
890
		}
891
	}
892
	*/
893

    
894
	if($node->children && is_array($node->children)){
895
		$out .= theme('cdm_taxontree', $node->children, $nextLevelIncluded, $show_filter_switch, $tree_node_callback);
896
	}
897
	$out .= '</li>';
898

    
899
	return $out;
900
}
901

    
902
function theme_cdm_taxontree_node_filter_switch(&$node, $filter_class){
903
	if(!module_exists('cdm_dataportal')) {
904
		return '';
905
	}
906

    
907
	$out = '';
908
	switch($filter_class){
909
		case 'filter_included':
910
			$filter_icon = 'visible_implicit_small.gif';
911
			break;
912
		case 'filter_excluded':
913
			$filter_icon = 'invisible_small.gif';
914
			break;
915
		case 'filter_on':
916
			$filter_icon = 'visible_small.gif';
917
			break;
918
	}
919

    
920
	$filter_op = $node->filter == 'on' ? 'remove' : 'add';
921

    
922
	$out .= '&nbsp;'
923
	.l('<img src="'.drupal_get_path('module', 'cdm_taxontree').'/'.$filter_icon.'" alt="[f]" />',
924
    'cdm_taxontree/filter/'.$filter_op.'/'.$node->taxonUuid, array('class'=>'filter_'.$filter_op),
925
    'destination='.path_to_taxon($node->taxonUuid),
926
	null, false, true);
927

    
928
	return $out;
929
}
930

    
931
/**
932
 *
933
 * @param unknown_type $node
934
 * @return unknown_type
935
 */
936
function theme_cdm_taxontree_node_concept_switch(&$node){
937
	$out = '';
938

    
939
	if(isset($node->alternativeConceptRefs[0])){
940
		$out = l(
941
      '<img src="'.drupal_get_path('module', 'cdm_taxontree').'/concept_switch.gif" alt="[-&gt;]" />',
942
      'cdm_dataportal/taxon/alternative/'.$node->taxonUuid,
943
		array('rel'=>'cdm_dataportal/taxon/alternative/'.$node->taxonUuid, 'class'=>'concept_switch'),
944
		null, null, false, true);
945
	}
946
	return $out;
947
}
948

    
949
// ----------------- FILTERS -------------------------- //
950

    
951
/**
952
 * filters on children, overrides already set parent filters and vice versa
953
 *
954
 * @param STRING $op [add | remove] a taxon from the filtered taxa
955
 * 					 TODO at the moment there is also a 'list' operation that displays all set filters and provides the ability to delete them.
956
 * 						  This option depends on function is defined in cdm_dataportal. Problem is, that the dependency is the other way round.
957
 * @param UUID $taxonUuid
958
 * @return unknown
959
 */
960
function cdm_taxontree_view_filter($op, $taxonUuid = null){
961

    
962
	if(!isset($_SESSION['cdm']['filters'])){
963
		$_SESSION['cdm']['filters'] = array();
964
	}
965
	if($taxonUuid || $op == 'list'){
966
		switch($op){
967
			case 'add':
968
				cdm_taxontree_filters_add($taxonUuid);
969
				break;
970
			case 'remove':
971
				cdm_taxontree_filters_remove($taxonUuid);
972
				break;
973
			case 'list':
974
				//TODO put in cdm_dataportal_theme to decouple both modules by this!!!
975
				$out = '<ul>';
976
				foreach($_SESSION['cdm']['filters'] as $uuid=>$node){
977
					$out .= '<li>'.cdm_dataportal_shortname_of($node).' '.l('[x]', 'cdm_dataportal/filter/remove/'.$uuid, array(), drupal_get_destination()).'</li>';
978
				}
979
				$out .= '</ul>';
980
				return $out;
981
		}
982
	}
983
	if($_REQUEST['destination']){
984
		$destination = $_REQUEST['destination'];
985
		unset($_REQUEST['destination']);
986
		drupal_goto($destination);
987
	}
988
}
989

    
990
/**
991
 * Filters are set in cdm_dataportal_view_filter().
992
 * functions using filters should remove invalid filters
993
 *
994
 * @return true if any filter is active
995
 */
996
function cdm_taxontree_filters_active(){
997
	return isset($_SESSION['cdm']['filters']) && count($_SESSION['cdm']['filters']) > 0;
998
}
999

    
1000
/**
1001
 * Filters are set in cdm_dataportal_view_filter().
1002
 *
1003
 * @return a reference on the filters array stored in the SESSION
1004
 */
1005
function &cdm_taxontree_filters_get(){
1006
	if(!isset($_SESSION['cdm']['filters'])){
1007
		$_SESSION['cdm']['filters'] = array();
1008
	}
1009
	return $_SESSION['cdm']['filters'];
1010
}
1011

    
1012
/**
1013
 * Adds a taxon to the filtered taxa array
1014
 *
1015
 * @param UUID $taxonUuid
1016
 */
1017
function cdm_taxontree_filters_add($taxonUuid){
1018

    
1019
	$parents = cdm_ws_taxonomy_pathFromRoot($taxonUuid);
1020

    
1021
	$parents = array_reverse($parents);
1022

    
1023
	// pop off last element since this is the TreeNode object for $taxonUuid!
1024
	$this_node = array_pop($parents);
1025
	// will contain the uuid of the parent nodes excluding the $taxonUuid node itself
1026
	$parent_uuids = array();
1027

    
1028
	// children override parents rule: remove all parent filters,
1029
	foreach($parents as $pnode){
1030
		unset($_SESSION['cdm']['filters'][$pnode->taxonUuid]);
1031
		$parent_uuids[] = $pnode->taxonUuid;
1032
	}
1033

    
1034
	// search for potential children of this $taxonUuid
1035
	foreach($_SESSION['cdm']['filters'] as $uuid=>$node){
1036
		if(in_array($taxonUuid, $node->parentUuids)){
1037
			unset($_SESSION['cdm']['filters'][$node->taxonUuid]);
1038
		}
1039
	}
1040
	// finally add this $taxonUuid as new filter
1041
	$this_node->parentUuids = $parent_uuids;
1042
	$_SESSION['cdm']['filters'][$taxonUuid] = $this_node;
1043
}
1044

    
1045
/**
1046
 * Unsets a taxon from the filtered taxa array
1047
 *
1048
 * @param UUID $taxonUuid
1049
 */
1050
function cdm_taxontree_filters_remove($taxonUuid){
1051
	unset($_SESSION['cdm']['filters'][$taxonUuid]);
1052
}
1053

    
1054

    
1055
// ------------------------ PRIVATE --------------------------- //
1056

    
1057
/**
1058
 * Analyses the current Drupal path.
1059
 * If a certain taxon was requested in the request, returns the UUID of that taxon.
1060
 * A stored UUID if no taxon was requested.
1061
 * TODO where does the stored UUID come from?
1062
 *
1063
 * @return UUID
1064
 */
1065
function _cdm_get_taxonuuid(){
1066

    
1067
	//TODO make the path configurable
1068
	if (arg(0)=="cdm_dataportal" && arg(1)=="taxon" && arg(2)!==0){
1069
		$taxon_uuid = arg(2);
1070
	} else {
1071
		$taxon_uuid = $_SESSION['cdm_dataportal']['tree']['taxon_uuid'];
1072
	}
1073
	return $taxon_uuid;
1074
}
1075

    
(6-6/18)