Project

General

Profile

Download (31.5 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, 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
		$taxon_in_current_tree = taxon_in_current_tree($taxonUuid);
472
		if ($taxon_in_current_tree) {
473
			$branch = cdm_taxontree_build_path($taxonUuid, NULL, (cdm_taxontree_filters_active() ? NULL : TRUE), TRUE);
474
			$root_tree = _cdm_taxontree_merge($root_tree, $branch);
475
		}
476
	}
477

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

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

    
494
	return $root_tree;
495
}
496

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

    
509
	$branch_path = array();
510

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

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

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

    
557
	if(!is_array($tree)){
558
		return false;
559
	}
560
	foreach(array_keys($tree) as $uuid){
561

    
562
		if(!isset($tree[$uuid]->filter) && !is_null($filter_default)){
563
			$tree[$uuid]->filter = $filter_default;
564
		}
565

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

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

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

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

    
631

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

    
643
	if(!$branch || !is_array($branch)){
644
		return $tree;
645
	}
646

    
647
	if(!is_array($tree)){
648
		return;
649
	}
650

    
651
	foreach(array_keys($tree) as $uuid) {
652
		// check if node exists in $branch
653
		if(!empty($branch[$uuid])) {
654
			// preserve filter property
655
			if(isset($tree[$uuid]->filter) && !(isset($branch[$uuid]->filter) && $branch[$uuid]->filter == 'on') ){
656
				$branch[$uuid]->filter = $tree[$uuid]->filter;
657
			} else if(isset($branch[$uuid]->filter)){
658
				$tree[$uuid]->filter = $branch[$uuid]->filter;
659
			}
660
			// preserve expanded property
661
			if(isset($tree[$uuid]->expanded)){
662
				$branch[$uuid]->expanded = $tree[$uuid]->expanded;
663
			} else if(isset($branch[$uuid]->expanded)){
664
				$tree[$uuid]->expanded = $branch[$uuid]->expanded;
665
			}
666
		  // preserve focused property
667
      if(isset($tree[$uuid]->focused)){
668
        $branch[$uuid]->focused = $tree[$uuid]->focused;
669
      } else if(isset($branch[$uuid]->focused)){
670
        $tree[$uuid]->focused = $branch[$uuid]->focused;
671
      }
672
			// $Uuid exists check if the node in tree1 or tree2 contains children
673
			if(is_array($branch[$uuid]->children) && is_array($tree[$uuid]->children)) {
674
				// merge recursive
675
				$tree[$uuid]->children = _cdm_taxontree_merge($tree[$uuid]->children, $branch[$uuid]->children);
676
			} else if(is_array($branch[$uuid]->children)){
677
				$tree[$uuid] =  $branch[$uuid];
678
			}
679
			unset($branch[$uuid]);
680
		}
681
	}
682
	// append remaining items from branch to tree
683
	foreach(array_keys($branch) as $uuid){
684
		$tree[$uuid] =  $branch[$uuid];
685
	}
686
	return $tree;
687
}
688

    
689

    
690
/**
691
 * Replaces the keys of an array of TreeNode instances
692
 * by the $treenode->taxonUuid of the single array elements.
693
 * An sets additional fields
694
 *
695
 * @param $resultset array of TreeNode instances as +returned by the cdm web service
696
 * @param $excluded  whether the $resultset is included by a active filter. Is ignored if NULL.
697
 * @param $expanded  whether the children of the nodes in the $resultset are expanded or not. Is ignored if NULL.
698
 */
699
function _cdm_resultset2nodelist($resultset, $excluded = null, $expanded = null){
700

    
701
	if(! is_array($resultset)) {
702
		return false;
703
	}
704

    
705
	$tree = array();
706
	foreach($resultset as $treeNode){
707
		if(!is_null($excluded)){
708
			$treeNode->filter = ($excluded ? 'excluded': 'included');
709
		}
710
		if(!is_null($expanded)){
711
			$treeNode->expanded = ($expanded ? 'expanded': 'collapsed');
712
		}
713
		$tree[$treeNode->taxonUuid] = $treeNode;
714
	}
715
	return $tree;
716
}
717

    
718

    
719
// ------------------------ THEME --------------------------- //
720

    
721

    
722
function theme_cdm_taxontree_add_scripts(){
723
	$path_cdm_taxontree = drupal_get_path('module', 'cdm_taxontree');
724
	$path_preferred_module = drupal_get_path('module', 'cdm_dataportal') ? drupal_get_path('module', 'cdm_dataportal') : $path_cdm_taxontree;
725
	drupal_add_css($path_cdm_taxontree.'/cdm_taxontree.css');
726
	drupal_add_js($path_preferred_module.'/js/jquery.dimensions.js');
727
	drupal_add_js($path_cdm_taxontree.'/js/cdm_taxontree.js');
728
	drupal_add_js($path_cdm_taxontree.'/js/jquery.scrollTo.js');
729
}
730

    
731

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

    
749
	// THEMERS: change the line below according the specific regions of your theme
750
	$left_expand_region = 'right';
751

    
752
	$out = '';
753
	if(cdm_taxontree_filters_active()){
754
		$out .= theme('cdm_taxontree_contoller', _get_compact_mode());
755
	}
756
	if($magicbox){
757
		if(is_numeric($magicbox) || is_string($magicbox)){
758
			$region = _get_block_region($magicbox);
759
		}
760
		// the magicbox expands to the right by default,
761
		// if the class 'expand-left' to  the cdm_taxontree_scroller_x the box will expand to the left
762
		$expand_direction = $region == 'right' ? 'expand-left' : '';
763
		$out .= '<div class="cdm_taxontree_scroller_x '.$expand_direction.'"><div class="cdm_taxontree_container"><div class="cdm_taxontree_scroller_y">';
764
	} else {
765
		$out .= '<div class="cdm_taxontree_scroller_xy">';
766
	}
767

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

    
770
	if($magicbox){
771
		$out .= '</div></div></div>';
772
	} else {
773
		$out .= '</div>';
774
	}
775
	return $out;
776
}
777

    
778
function theme_cdm_taxontree_contoller($compact_mode){
779

    
780
	static $modes = array('expanded', 'compact', 'flattened');
781

    
782
	$out = '<div class="settings">';
783
	foreach($modes as $mode){
784
		if($compact_mode == $mode){
785
			$out .= t($mode);
786
		} else {
787
			$out .= l(t($mode), 'cdm_taxontree/set/compact_mode/'.$mode, array(), drupal_get_destination());
788
		}
789
		$out .= ' ';
790
	}
791

    
792
	return $out.'</div>';
793
}
794

    
795
function theme_cdm_taxontree($tree, $filterIncludes = null, $show_filter_switch = false, $tree_node_callback = false, $element_name = false){
796

    
797
	if(!is_array($tree)) {
798
		//    $out = '<ul class="cdm_taxontree">';
799
		//    $out .= '<li>----------------------------NO TREE---------------------------------------</li>';
800
		//    $out .= '</ul>';
801
		//    return $out;
802
		return false;
803
	}
804

    
805
	if(is_null($filterIncludes)){
806
		// set $filterIncludes true if no filters are set.
807
		$filterIncludes = !cdm_taxontree_filters_active();
808
	}
809

    
810
	// append element name to get multiple taxontrees on one page working
811
	$out = '<ul class="cdm_taxontree' . (($element_name) ? ' ' . $element_name : '') . '">';
812
	foreach($tree as $node){
813
		$out .= theme('cdm_taxontree_node', $node, $filterIncludes, $show_filter_switch, $tree_node_callback);
814
	}
815
	$out .= '</ul>';
816
	return $out;
817
}
818

    
819
function theme_cdm_taxontree_node($node, $filterIncludes, $show_filter_switch = false, $tree_node_callback = false){
820

    
821
	$is_leaf = !$node->taxonomicChildrenCount || $node->taxonomicChildrenCount == 0;
822
	$is_expanded = isset($node->expanded) && $node->expanded = 'expanded';
823

    
824
	if($node->tid){
825
		$node_name = $node->name;
826
		$path = "taxonomy/term/".$node->tid;
827
		// disable filterswitch
828
		$show_filter_switch = false;
829

    
830
	} else if(module_exists('cdm_dataportal')){
831
		$node_name = cdm_dataportal_shortname_of($node);
832
		$path = path_to_taxon($node->taxonUuid);
833
	} else {
834
		$node_name = "module cdm_dataportal missing";
835
		$path = "";
836
	}
837

    
838
	if($filterIncludes){
839
		$name = l($node_name, $path);
840
		// no names for terms in filter widget; as discussed with A. Müller
841
		//$name = '';
842

    
843
		$filter_class = 'filter_included';
844
	} else {
845
		if($node->filter == 'on') {
846
			$name = l($node_name,  $path);
847
			$filter_class = 'filter_on';
848
		} else {
849
			$name .= $node_name;
850
			$filter_class = 'filter_excluded';
851
		}
852
	}
853
	$nextLevelIncluded = $node->filter == 'on' || $filterIncludes;
854

    
855
	$ahah_url = false;
856
	if(!$is_leaf && !$is_expanded && $filter_class != 'filter_excluded'){
857
		if($node->tid){
858
			$ahah_url = url('cdm_taxontree/taxonomy/children/'.$node->tid.'/'.$node->vid.'/cdm_taxontree/'.($nextLevelIncluded ? 1 : 0).'/'.($show_filter_switch ? 1 : 0).'/'.$tree_node_callback);
859
		} else if(module_exists('cdm_dataportal')) {
860
			$ws_url = cdm_compose_taxonomy_path($node->taxonUuid);
861
			$ahah_url = url('cdm_api/proxy/'.urlencode($ws_url).'/cdm_taxontree/'.($nextLevelIncluded ? 1 : 0).'/'.($show_filter_switch ? 1 : 0).'/'.$tree_node_callback);
862
		}
863
	}
864

    
865
	// list item
866
	$out = '<li class="'
867
	.($node->focused ? 'focused ' : '')
868
	.($is_leaf ? 'leaf ':($is_expanded ?'expanded ':'collapsed '))
869
	.$filter_class.'"'
870
	.($ahah_url ? 'ref="'.$ahah_url.'"' : '')
871
	.'>';
872

    
873

    
874
	if($show_filter_switch){
875
		// filter icon
876
		$out .= theme('cdm_taxontree_node_filter_switch', $node, $filter_class);
877
	}
878

    
879
	// taxon name
880
	$out .= $name;
881

    
882
	// concept_switch or other theme callbacks
883
	if($tree_node_callback){
884
		$out .= theme($tree_node_callback, $node);
885
	}
886

    
887
	if($node->children && is_array($node->children)){
888
		$out .= theme('cdm_taxontree', $node->children, $nextLevelIncluded, $show_filter_switch, $tree_node_callback);
889
	}
890
	$out .= '</li>';
891

    
892
	return $out;
893
}
894

    
895
function theme_cdm_taxontree_node_filter_switch(&$node, $filter_class){
896
	if(!module_exists('cdm_dataportal')) {
897
		return '';
898
	}
899

    
900
	$out = '';
901
	switch($filter_class){
902
		case 'filter_included':
903
			$filter_icon = 'visible_implicit_small.gif';
904
			break;
905
		case 'filter_excluded':
906
			$filter_icon = 'invisible_small.gif';
907
			break;
908
		case 'filter_on':
909
			$filter_icon = 'visible_small.gif';
910
			break;
911
	}
912

    
913
	$filter_op = $node->filter == 'on' ? 'remove' : 'add';
914

    
915
	$out .= '&nbsp;'
916
	.l('<img src="'.drupal_get_path('module', 'cdm_taxontree').'/'.$filter_icon.'" alt="[f]" />',
917
    'cdm_taxontree/filter/'.$filter_op.'/'.$node->taxonUuid, array('class'=>'filter_'.$filter_op),
918
    'destination='.path_to_taxon($node->taxonUuid),
919
	null, false, true);
920

    
921
	return $out;
922
}
923

    
924
/**
925
 *
926
 * @param unknown_type $node
927
 * @return unknown_type
928
 */
929
function theme_cdm_taxontree_node_concept_switch(&$node){
930
	$out = '';
931

    
932
	if(isset($node->alternativeConceptRefs[0])){
933
		$out = l(
934
      '<img src="'.drupal_get_path('module', 'cdm_taxontree').'/concept_switch.gif" alt="[-&gt;]" />',
935
      'cdm_dataportal/taxon/alternative/'.$node->taxonUuid,
936
		array('rel'=>'cdm_dataportal/taxon/alternative/'.$node->taxonUuid, 'class'=>'concept_switch'),
937
		null, null, false, true);
938
	}
939
	return $out;
940
}
941

    
942
// ----------------- FILTERS -------------------------- //
943

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

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

    
983
/**
984
 * Filters are set in cdm_dataportal_view_filter().
985
 * functions using filters should remove invalid filters
986
 *
987
 * @return true if any filter is active
988
 */
989
function cdm_taxontree_filters_active(){
990
	return isset($_SESSION['cdm']['filters']) && count($_SESSION['cdm']['filters']) > 0;
991
}
992

    
993
/**
994
 * Filters are set in cdm_dataportal_view_filter().
995
 *
996
 * @return a reference on the filters array stored in the SESSION
997
 */
998
function &cdm_taxontree_filters_get(){
999
	if(!isset($_SESSION['cdm']['filters'])){
1000
		$_SESSION['cdm']['filters'] = array();
1001
	}
1002
	return $_SESSION['cdm']['filters'];
1003
}
1004

    
1005
/**
1006
 * Adds a taxon to the filtered taxa array
1007
 *
1008
 * @param UUID $taxonUuid
1009
 */
1010
function cdm_taxontree_filters_add($taxonUuid){
1011

    
1012
	$parents = cdm_ws_taxonomy_pathFromRoot($taxonUuid);
1013

    
1014
	$parents = array_reverse($parents);
1015

    
1016
	// pop off last element since this is the TreeNode object for $taxonUuid!
1017
	$this_node = array_pop($parents);
1018
	// will contain the uuid of the parent nodes excluding the $taxonUuid node itself
1019
	$parent_uuids = array();
1020

    
1021
	// children override parents rule: remove all parent filters,
1022
	foreach($parents as $pnode){
1023
		unset($_SESSION['cdm']['filters'][$pnode->taxonUuid]);
1024
		$parent_uuids[] = $pnode->taxonUuid;
1025
	}
1026

    
1027
	// search for potential children of this $taxonUuid
1028
	foreach($_SESSION['cdm']['filters'] as $uuid=>$node){
1029
		if(in_array($taxonUuid, $node->parentUuids)){
1030
			unset($_SESSION['cdm']['filters'][$node->taxonUuid]);
1031
		}
1032
	}
1033
	// finally add this $taxonUuid as new filter
1034
	$this_node->parentUuids = $parent_uuids;
1035
	$_SESSION['cdm']['filters'][$taxonUuid] = $this_node;
1036
}
1037

    
1038
/**
1039
 * Unsets a taxon from the filtered taxa array
1040
 *
1041
 * @param UUID $taxonUuid
1042
 */
1043
function cdm_taxontree_filters_remove($taxonUuid){
1044
	unset($_SESSION['cdm']['filters'][$taxonUuid]);
1045
}
1046

    
1047

    
1048
// ------------------------ PRIVATE --------------------------- //
1049

    
1050
/**
1051
 * Analyses the current Drupal path.
1052
 * If a certain taxon was requested in the request, returns the UUID of that taxon.
1053
 * A stored UUID if no taxon was requested.
1054
 * TODO where does the stored UUID come from?
1055
 *
1056
 * @return UUID
1057
 */
1058
function _cdm_get_taxonuuid(){
1059

    
1060
	//TODO make the path configurable
1061
	if (arg(0)=="cdm_dataportal" && arg(1)=="taxon" && arg(2)!==0){
1062
		$taxon_uuid = arg(2);
1063
	} else {
1064
		$taxon_uuid = $_SESSION['cdm_dataportal']['tree']['taxon_uuid'];
1065
	}
1066
	return $taxon_uuid;
1067
}
1068

    
(6-6/18)