Project

General

Profile

Download (55 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
 * Definition of privacy levels
16
 */
17
define('TAXONPRIVACY_PRIVATE', 'Private');
18
define('TAXONPRIVACY_PUBLIC', 'Public');
19

    
20
/**
21
 * Implementation of hook_perm()
22
 *
23
 * Valid permissions for this module
24
 * @return array An array of valid permissions for the taxon_experts module
25
 */
26
function cdm_taxontree_perm() {
27
	return array(
28
    	'view private taxoninterests',
29
	//TODO which else permission are required?
30
	);
31
}
32

    
33
/**
34
 * Implementation of hook_menu()
35
 */
36
function cdm_taxontree_menu($may_cache) {
37

    
38
    $items = array();
39
    if ($may_cache) {
40
  
41
        $items[] = array(
42
          'path' => 'cdm_taxontree/set',
43
          'callback' => 'cdm_taxontree_set',
44
          'access' => true,
45
          'type' => MENU_CALLBACK,
46
        );
47
        
48
         $items[] = array(
49
    
50
          'path' => 'cdm_taxontree/filter',
51
          'callback' => 'cdm_taxontree_view_filter',
52
          'access' => true,
53
          'type' => MENU_CALLBACK,
54
        );
55
        
56
        $items[] = array(
57
          'path' => 'cdm_taxontree/taxonomy/children',
58
          'callback' => 'cdm_taxontree_taxonomy_children',
59
          'access' => true,
60
          'type' => MENU_CALLBACK,
61
        );
62
  
63
    } else {
64

    
65
      if(variable_get('cdm_taxontree_cache', 0)){
66
    		$items[] = array('path' => 'admin/settings/cdm_dataportal/clear_cache',
67
          'title' => t('Empty CDM Taxontree Cache'),
68
          'callback' => 'cdm_taxontree_cache_clear',
69
          'access' => user_access('administer cdm_dataportal'),
70
          'weight' => 2,
71
          'type' => MENU_NORMAL_ITEM,
72
    		);
73
      }
74
      
75
    }
76
    return $items;
77
}
78

    
79
function cdm_taxontree_cache_get($term){
80
  if(variable_get('cdm_taxontree_cache', 0)){
81
    return cache_get($term,'cache_cdm_taxontree');
82
  }
83
}
84

    
85
function cdm_taxontree_cache_set($term, $child_terms){
86
  if(variable_get('cdm_taxontree_cache', 0)){
87
    return cache_set($term,'cache_cdm_taxontree', serialize($child_terms));
88
  }
89
}
90

    
91
/**
92
 * function to empty the cdm_taxontree cache table
93
 *
94
 */
95
function cdm_taxontree_cache_clear() {
96

    
97
	// clear core tables
98
	$alltables = array('cache_cdm_taxontree');
99
	foreach ($alltables as $table) {
100
		cache_clear_all('*', $table, TRUE);
101
	}
102
	drupal_set_message('CDM Taxontree Cache cleared.');
103
	drupal_goto('admin/settings/cdm_dataportal');
104
}
105

    
106

    
107
/**
108
 * Implementation of hook_block()
109
 *
110
 * @param String $op
111
 * @param int $delta
112
 */
113
function cdm_taxontree_block($op='list', $delta=0, $edit = array()) {
114
  if ($op == "list") {
115
    $block['cdm_tree']["info"] = t('CDM Taxon Tree');
116
    $block['filters']["info"] = t('Active Filters');
117
		$block[1]["info"] = t('Drupal Taxonomy Tree');
118
    return $block;
119
  }
120
  else if ($op == 'view') {
121
    switch($delta){
122
      case 'cdm_tree':
123
        $block['subject'] = t('Taxon Tree');
124
        $taxonUuid_inFocus = _cdm_get_taxonuuid();
125
        $tree = cdm_taxontree_build_tree($taxonUuid_inFocus);
126
        $magicbox_enable = variable_get('cdm_taxontree_magicbox_enable', 0);
127
        $block['content'] = theme('cdm_taxontree_block', $tree, $delta, FALSE, 'cdm_taxontree_node_concept_switch');
128
        // java script 
129
        $verticalSroller = $magicbox_enable ? 'cdm_taxontree_scroller_x' : 'cdm_taxontree_scroller_xy';
130
        theme('cdm_taxontree_add_scripts');
131
        drupal_add_js('
132
        if (Drupal.jsEnabled) {
133
        $(document).ready(function()
134
        {
135
          $(\'ul.cdm_taxontree\').cdm_taxontree();
136
          $(\'div.'.$verticalSroller.'\').scrollTo($(\'.active\'), 400, {over:-3});});
137
        }', 'inline');
138
         return $block;
139
        
140
      case 'filters':
141
        $block['subject'] = t('Active Filters');
142
        $block['content'] = cdm_taxontree_view_filter('list');
143
        return $block;
144
        
145
      case 1:
146
        $block['subject'] = t('Taxonomy Tree');
147
        $term_inFocus = arg(0) == 'taxonomy' && arg(1) == 'term' ? arg(2) : 0;
148
        $tree = cdm_taxontree_build_tree($term_inFocus, true, variable_get('cdm_taxontree_block_1_vid', 0));
149
        $block['content'] = theme('cdm_taxontree_block', $tree, $delta, FALSE);
150
        theme('cdm_taxontree_add_scripts');
151
        drupal_add_js('
152
        if (Drupal.jsEnabled) {
153
        $(document).ready(function()
154
        {
155
          $(\'ul.cdm_taxontree\').cdm_taxontree();
156
          $(\'div.cdm_taxontree_scroller_x\').scrollTo($(\'.active\'), 400, {over:-3});});
157
        }', 'inline');
158

    
159
        return $block;
160
      }
161
    } else if ($op == 'configure') {
162
      switch($delta){
163
        case 1:
164
          $vocs = taxonomy_get_vocabularies();
165
          $options = array();
166
          foreach($vocs as $voc){
167
            $options[$voc->vid] = $voc->name;
168
          }
169
          $form['vid'] = array(
170
            '#type' => 'select',
171
            '#title' => t('Category'),
172
            '#default_value' => variable_get('cdm_taxontree_block_1_vid', 0),
173
            '#options' => $options,
174
          );
175
          return $form;
176
      }
177
    } else if ($op == 'save') {
178
      switch($delta){
179
        case 1:
180
         variable_set('cdm_taxontree_block_1_vid', $edit['vid']);
181
				return;
182
		}
183
	}
184
}
185

    
186
/**
187
 * Implementation of hook_help().
188
 *
189
 * @param unknown_type $section
190
 * @return unknown
191
 */
192
function cdm_taxontree_help($section) {
193
	switch ($section) {
194
		case 'admin/modules#description':
195
			return t('Defines a selection widget for massive taxonomy structures.');
196
	}
197
}
198

    
199
/**
200
 * Implementation of hook_field_info().
201
 */
202
function cdm_taxontree_field_info() {
203
	return array(
204
    'cdm_taxontree' => array('label' => 'CDM Taxontree'),
205
	);
206
}
207

    
208
/**
209
 * Implementation of hook_field()
210
 *
211
 */
212
function cdm_taxontree_field($op, &$node, $field, &$items, $teaser, $page) {
213
	switch ($op) {
214

    
215
		case 'view':
216
			$context = $teaser ? 'teaser' : 'full';
217
			$formatter = isset($field['display_settings'][$context]['format']) ? $field['display_settings'][$context]['format'] : 'default';
218
			foreach ($items as $delta => $item) {
219
				$items[$delta]['view'] = content_format($field, $item, $formatter, $node);
220
			}
221
			return theme('field', $node, $field, $items, $teaser, $page);
222
	}
223
}
224

    
225
/**
226
 * Implementation of hook_field_settings().
227
 */
228
function cdm_taxontree_field_settings($op, $field) {
229
	switch ($op) {
230
		case 'form':
231
			$form = array(
232
        '#theme' => 'cdm_taxontree_field_settings',
233
			);
234
			return $form;
235

    
236
		case 'save':
237
			break;
238

    
239
		case 'database columns':
240
			return array(
241
        'tid' => array('type' => 'int', 'length' => 10, 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
242
			);
243
			break;
244

    
245
		case 'filters':
246
			return array(
247
			 'default' => array(
248
       'operator' => 'views_handler_operator_like',
249
       'handler' => 'cdm_taxontree_views_filter_handler',
250
			),
251
			);
252
			break;
253

    
254
		case 'callbacks': //pairs up with cck_fullname_field::view
255
			return array(
256
        'view' => CONTENT_CALLBACK_CUSTOM,
257
			);
258
			break;
259

    
260
	}
261
}
262

    
263
/**
264
 * implementation of hook_widget_info()
265
 *
266
 */
267
function cdm_taxontree_widget_info(){
268
	$vocabularies = taxonomy_get_vocabularies();
269
	$vocabs = array();
270
	foreach ($vocabularies as $vid => $vocab) {
271
		$vocabs['cdm_taxontree_' .$vocab->vid] = array(
272
        'label' => $vocab->name,
273
        'field types' => array('cdm_taxontree'),
274
		);
275
	}
276
	return $vocabs;
277
}
278

    
279
/**
280
 * implementation of hook_widget()
281
 *
282
 */
283
function cdm_taxontree_widget($op, &$node, $field, &$node_field){
284
	switch($op){
285
		case 'prepare form values':
286
			// get posted values in both node edit and profile edit mode
287
			if ($_POST[$field['field_name']] || $_POST[$field['type_name'].'_node_form'][$field['field_name']]) {
288
				$node_field = ($_POST['form_id'] == 'user_edit') ?  $_POST[$field['type_name'].'_node_form'][$field['field_name']] : $_POST[$field['field_name']];
289
			}else{
290
				$node_field_transposed = content_transpose_array_rows_cols($node_field);
291
				$node_field = array();
292
				$node_field[0]['cdm_taxontree_select'] = $node_field_transposed['tid'];
293
			}
294
			return;
295

    
296
		case 'form':
297
			$form = array();
298
			$form[$field['field_name']] = array(
299
        '#tree' => TRUE,
300
        '#theme' => NULL,
301
        '#type' => 'markup',
302
        '#title' => t($field['widget']['label']),
303
        '#description' => t($field['widget']['description']),
304
        '#weight' => $field['widget']['weight'],
305
			);
306

    
307
			// add the form
308
			$delta = 0;
309
			_cdm_taxontree_widget_form($form[$field['field_name']][$delta], $field, $node_field);
310

    
311
			return $form;
312

    
313
		case 'validate':
314
			if($field['required'] && count($node_field[0]['cdm_taxontree_select']) < 1){
315
				form_set_error('cdm_taxontree_select', t('Please select at least one item from %type.', array('%type' => $field['widget']['label'])));
316
			}
317
         return;
318
      }
319
    }
320

    
321
function _cdm_taxontree_widget_form(&$form_item, $field = array(), $node_field = array(), $delta = 0) {
322

    
323
  // get vocabulary id
324
  $vid = substr($field['widget']['type'], strlen($field['type'])+1);
325

    
326
  // build the tree
327
  $tree = cdm_taxontree_build_tree(NULL, NULL, $vid);
328

    
329
  $options = array();
330
  if (!empty($node_field[$delta]['cdm_taxontree_select'])){
331
    $options = _cdm_taxontree_recreate_options($node_field[$delta]['cdm_taxontree_select']);
332
  }
333

    
334
  // prefix and suffix: render the taxon tree div structure around the select field
335
  $prefix .= '<div class="cdm_taxontree_widget">';
336
  // hide labels in filter view
337
  if(substr($field['field_name'],-7,7) !== '_filter'){
338
    $prefix .= '<div class="taxontree_header">';
339
    $prefix .= '<div class="field-label column-left">' . t($field['widget']['label']) . ':' . ($field['required'] == 1 ? ' <span class="form-required" title="' .t('This field is required.'). '">*</span>' : '') . '</div>';
340
    $prefix .= '<div class="field-label column-right">' . t('Your Selection') . ':</div>';
341
    $prefix .= '</div>';
342
  }
343
  $prefix .= '<div class="taxontree">';
344
  $prefix .= '<div class="cdm_taxontree_scroller_x">';
345
  $prefix .= '<div class="cdm_taxontree_container">';
346
  $prefix .= '<div class="cdm_taxontree_scroller_y">';
347
  // inject the taxon tree
348
  $prefix .= theme('cdm_taxontree',$tree, NULL, FALSE, 'cdm_taxontree_node_reference_widget', $field['field_name']);
349
  $prefix .= '</div></div></div></div>';
350
  $prefix .= '<div class="taxontree_devider"><span class="hidden">&gt;&gt;</span></div>';
351
  // this is the place where to put the select box without disturbing the scrollers
352
  $suffix = '</div>';
353

    
354
  // pull in the default value
355
  $default_value = (!empty($field['widget']['default_value'][$delta]['cdm_taxontree_select'])) ? $field['widget']['default_value'][$delta]['cdm_taxontree_select'] : array();
356
  // $default_show_childnodes = ($field['widget']['default_value']['include_childnodes']['include_childnodes'] === 'include_childnodes') ? 'checked="checked"' : '';
357
  $default_show_childnodes = 'checked="checked"';
358

    
359
  // add the select box and set to multiple, if appropriate
360
  $form_item['cdm_taxontree_select'] = array(
361
        '#tree' => TRUE,
362
        '#type' => 'select',
363
        '#default_value' => !empty($node_field[$delta]['cdm_taxontree_select']) ? $node_field[$delta]['cdm_taxontree_select'] : $default_value ,
364
        '#multiple' => $field['multiple'] ? TRUE : FALSE,
365
        '#options' => $options,
366
        '#size' => $field['multiple'] ? 12 : 2,
367
        '#prefix' => $prefix,
368
        '#suffix' => $suffix,
369
        '#attributes' => array('class' => 'taxontree_select'),
370
  );
371

    
372
  // add the scripts for multiple widgets, if not already present
373
  theme('cdm_taxontree_add_scripts');
374

    
375
  global $taxontree_script_present;
376
  if(!$taxontree_script_present[$field['field_name']]){
377
    drupal_add_js('$(document).ready(function() {$(\'ul.cdm_taxontree.' . $field['field_name'] . '\').cdm_taxontree(
378
      {
379
        widget:                 true,
380
        element_name:           \''.$field['field_name'] .'\',  //
381
        multiselect:            '.($field['multiple']?'true':'false').'         //
382
      }
383
      );});', 'inline');
384
    $taxontree_script_present[$field['field_name']] = TRUE;
385
  }
386

    
387
  // add flag for including child nodes if selected in settings
388
  // this is only tirggered in filter mode
389
  if($field['show_include_childnodes']){
390

    
391
    $default_value = (count($_GET)==1) ? $default_show_childnodes : ($field['include_childnodes_flag']) ? 'checked="checked"' : '';
392

    
393
    $include_label = 'Include Childnodes';
394
    $include_childnode_message = 'Include childnodes and parent node';
395
    // HACK! Replace the text to display as 'include childnodes text'
396
    if(substr($field['field_name'],-7,7) == '_filter'){
397
      $vocabulary = substr($field['widget']['type'],strlen($field['type'])+1);
398
      switch($vocabulary){
399

    
400
        case 8:
401
          $include_label = 'Include Childtaxa';
402
          $include_childnode_message = 'Check, if all child taxa of the selected taxa shall be included';
403
          break;
404
        case 3:
405
          $include_label = 'Include Subregions';
406
          $include_childnode_message = 'Check, if all subregions of the selected region should be included';
407
          break;
408

    
409
      }
410
    }
411

    
412
    $form_item['include_childnodes'] = array(
413
      '#tree' => TRUE,
414
      '#type' => 'markup',
415
      '#prefix' => '<div class="include-options">',
416
      '#value' => '<label class="field-label" for="' . $field['field_name'] . $delta . '-include">' . t($include_label) . '</label><div class="field-description">' . t($include_childnode_message) . '<input type="checkbox" name="'  .$field['field_name'] . $delta . '_include" id="' . $field['field_name'] . $delta . '-include" value="include_childnodes" ' . $default_value . '/></div>',
417
      '#suffix' => '</div>',
418
    );
419

    
420
  }
421

    
422
}
423

    
424
function cdm_taxontree_field_formatter_info() {
425
	return array(
426
    'default' => array(
427
      'label' => t('Default'),
428
      'field types' => array('cdm_taxontree'),
429
	),
430
    'link' => array(
431
      'label' => t('With link'),
432
      'field types' => array('cdm_taxontree'),
433
	),
434
	);
435
}
436

    
437

    
438

    
439
/*
440
 * formatters to prepare the correct links for taxa
441
 */
442
function cdm_taxontree_field_formatter($field, $item, $formatter, $node) {
443
	switch ($formatter) {
444
		case 'link':
445
			$term = taxonomy_get_term($item['tid']);
446
			$taxa = db_result(db_query('SELECT name FROM {vocabulary} WHERE vid = %d', $term->vid));
447
			switch($taxa){
448
				case 'Taxonomy':
449
					$link = 'interest_by_taxonomy/';
450
					break;
451
				case 'Georegion':
452
					$link = 'interest_by_georegion/';
453
					break;
454
				default:
455
					$link = 'taxonomy/term/';
456
			}
457
			return l($term->name, $link.$term->tid);
458

    
459
				default:
460
					$name = db_result(db_query('SELECT name FROM {term_data} WHERE tid = %d', $item['tid']));
461
					return $name;
462
	}
463
}
464

    
465
/**
466
 * Helper function to transpose a list of term ids into an
467
 * array with key = tid and value = term name;
468
 * used to recreate cdm_taxontree_widgets select list options.
469
 *
470
 * @param array $options
471
 * @return array transposed options
472
 */
473
function _cdm_taxontree_recreate_options($options = array()){
474
	$transposed = array();
475
	foreach($options as $key => $tid){
476
		if(is_numeric($tid)){
477
			$parents = taxonomy_get_parents_all($tid);
478
			$path = theme('cdm_taxontree_taxon_fullpath',array_reverse($parents),3);
479
			$transposed[$tid] = $path;
480
		}
481
	}
482
	return $transposed;
483
}
484

    
485
/*
486
 * implementation of hook_form_alter()
487
 *
488
 * used to replace the standard taxonomy selectbox
489
 * with our nice cdm_taxontree_widget form.
490
 *
491
 */
492
function cdm_taxontree_form_alter($form_id, &$form){
493

    
494
	if($form_id == 'views_filters')
495
	{
496
		global $user;
497
		$view = $form['view']['#value'];
498

    
499
		// find the cdm_taxontree_filter
500
		foreach($view->exposed_filter as $delta => $filter){
501
			if(preg_match('@^cdm_taxontree@',$filter['field'])){
502

    
503
				// simulate field settings to invoke the widget form correctly
504
				$field = array(
505
					'field_name' => 'cdm_taxontree_filter',
506
					'type' => 'cdm_taxontree',
507
				  'multiple' => $form['filter'.$delta]['#multiple'],
508
					'show_include_childnodes' => 1,
509
					'include_childnodes_flag' => $_GET['cdm_taxontree_filter' . $delta . '_include'] ? 1 : 0,
510
					'widget' => array(
511
						'type' => 'cdm_taxontree_' . $form['filter'.$delta]['#vocab'],
512
						'label' => $filter['label'],
513
				),
514
				);
515

    
516
				// simulate the node_field data from filter form values
517
				$filter_field = array();
518
				$filter_values = $form['filter'.$delta]['#default_value'];
519

    
520
				// transpose the filter values to a form understood by the widget
521
				if(is_array($filter_values)){
522
					foreach($filter_values as $key => $val){
523
						$filter_field[$delta]['cdm_taxontree_select'][] = $val;
524
					}
525
				}else if(is_numeric($key)){
526
					$filter_field[$delta]['cdm_taxontree_select'][] = $filter_values;
527
				}
528

    
529
				// create the widget
530
				$subform = array();
531
				_cdm_taxontree_widget_form($subform['cdm_taxontree_filter'], $field, $filter_field, $delta);
532

    
533
				// add some important attributes
534
				$subform['cdm_taxontree_filter']['cdm_taxontree_select']['#name'] = 'filter'.$delta;
535
				$subform['cdm_taxontree_filter']['cdm_taxontree_select']['#id'] = 'edit-filter'.$delta;
536

    
537
				// replace original form element with widget
538
				$form['filter'.$delta] = $subform['cdm_taxontree_filter'];
539

    
540
			}
541
		}
542
	}
543
}
544

    
545
/**
546
 * implementation of hook_filters_alter(), a *views modification*
547
 * to enable injection of more filter values
548
 *
549
 * @param object $view, needed to find cdm_taxontree filters by $delta
550
 * @param array $filters, incoming filter values
551
 * @return array $filters extended filter values
552
 */
553
function cdm_taxontree_views_filters_alter($view, $filters){
554

    
555
	if($view->name == 'search_interest'){
556
		// find the cdm_taxontree_filter
557
		foreach($view->exposed_filter as $delta => $filter){
558
			if(preg_match('@^cdm_taxontree@',$filter['field'])){
559

    
560
				// include child terms and parent term
561
				if($_GET['cdm_taxontree_filter' . $delta . '_include'] == TRUE && is_array($filters[$delta]['filter'])){
562
					foreach($filters[$delta]['filter'] as $key => $val){
563

    
564
						if(is_numeric($val)){
565
							$child_terms[] = $val;
566
						}
567
					}
568

    
569
					$term_collection = array();
570
					// get child terms from cache
571
					foreach($child_terms as $key => $term){
572
						if(!$children = cdm_taxontree_cache_get($term,'cache_cdm_taxontree')){
573
							// no data in cache? add some!
574
							$child_terms = _cdm_taxontree_get_all_children($child_terms,TRUE);
575
							// add parent term
576
							$parent_terms = taxonomy_get_parents($term);
577
							foreach($parent_terms as $key => $term_obj){
578
								$child_terms[$key] = $key;
579
							}
580

    
581
							cdm_taxontree_cache_set($term,'cache_cdm_taxontree',serialize($child_terms));
582
							$children = $child_terms;
583
						}
584
						else{
585
							$children = unserialize($children->data);
586
						}
587

    
588
						$term_collection = array_merge($term_collection, $children);
589
					}
590

    
591
					// re-add filter values
592
					$filters[$delta]['filter'] = $term_collection;
593

    
594
				}
595
			}
596
		}
597
	}
598
				
599
	/*
600
	 * React on taxonprivacy settings
601
	 * Remove taxon_privacy from taxonomy / filters, if user is allowed to view private taxon interests
602
	 */
603
	global $user;
604

    
605
	// show information only to roles with access to private interests
606
	if($user->uid && user_access('view private taxoninterests',$user)){
607
			foreach($view->filter as $delta => $filter){
608
				if(preg_match('@^term_node_@',$filter['field'])){
609
					$tid = intval(str_replace('.tid','',str_replace('term_node_','',$filter['field'])));
610
					if($tid == 9){
611
						unset($view->filter[$delta]);
612
					}
613
				}
614
			}
615
	}
616

    
617
	
618
	return $filters;
619
}
620

    
621
/**
622
 * Transforms an unpredictably and irregularly nested set of tids (as returned
623
 * from a taxonomy form) into a linear array of tids.
624
 * borrow from taxonomy_browser.module
625
 */
626
function _cdm_taxontree_get_all_children($tids = null, $include_children = false) {
627
	static $tid_list;
628

    
629
	if (isset($tids) && is_array($tids)) {
630
		$tid_list = array();
631
		foreach ($tids as $key => $tid) {
632
			if (!empty($tid)) {
633
				if (is_array($tid)) {
634
					foreach ($tid as $key2 => $tid2) {
635
						if (!empty($tid2)) {
636
							$tid_list[$tid2] = $tid2;
637
						}
638
					}
639
				}
640
				else {
641
					$tid_list[$tid] = $tid;
642
				}
643
			} /* end !empty */
644
		} /* end foreach */
645
	}
646

    
647
	if ($include_children) {
648
		foreach ($tid_list as $tid) {
649
			_cdm_taxontree_get_children($tid_list, $tid);
650
		}
651
	}
652

    
653
	return $tid_list;
654
}
655

    
656
function _cdm_taxontree_get_children(&$tid_list, $tid) {
657
	$child_nodes = taxonomy_get_children($tid);
658
	if (!empty($child_nodes)) {
659
		foreach ($child_nodes as $child_tid => $child_term) {
660
			$tid_list[$tid] = $tid;
661
			_cdm_taxontree_get_children($tid_list, $child_tid);
662
		}
663
	}
664
	else {
665
		$tid_list[$tid] = $tid;
666
	}
667
}
668

    
669
/**
670
 * Implematation of hook_views_tables()
671
 *
672
 *
673
 * @return onject views related db table data
674
 */
675
function cdm_taxontree_views_tables() {
676
	$tables = array();
677
	// field_name, type_name, widget_type
678
	$result = db_query("SELECT nfi.*, nf.db_storage FROM {node_field_instance} nfi INNER JOIN {node_field} nf ON nfi.field_name = nf.field_name WHERE widget_type LIKE 'cdm_taxontree_%'");
679
	while ($row = db_fetch_object($result)) {
680

    
681
		// Build the list of options
682
		$vid = substr($row->widget_type, 14); // 14 = strlen('cdm_taxontree)!
683

    
684
		$options = array();
685
		$tree = taxonomy_form($vid);
686
		foreach ($tree['#options'] as $index => $option) {
687
			if (is_object($option)) {
688
				foreach($option->option as $key => $value) {
689
					$options[$key] = $value;
690
				}
691
			}
692
		}
693

    
694
		// Rely on CCK to provide us with the correct table name.
695
		if ($row->db_storage == CONTENT_DB_STORAGE_PER_FIELD) {
696
			$table_name = _content_tablename($row->field_name, CONTENT_DB_STORAGE_PER_FIELD);
697
		}
698
		else {
699
			$table_name = _content_tablename($row->type_name, CONTENT_DB_STORAGE_PER_CONTENT_TYPE);
700
		}
701

    
702
		# Get the taxonomy multi select property
703
		$vocab = taxonomy_get_vocabulary($vid);
704
		$multiple = $vocab->multiple;
705

    
706
		$table = array(
707
      'name' => $table_name,
708
      'provider' => 'cdm_taxontree',
709
      'join' => array(
710
        'left' => array('table' => 'node', 'field' => 'vid',),
711
        'right' => array('field' => 'vid'),
712
		),
713
      'filters' => array(
714
		$row->field_name .'_tid' => array(
715
          'name' => t('CDM Taxontree: @field_name', array('@field_name' => $row->field_name)),
716
          'help' => t('Filter on @field_name terms.', array('@field_name' => $row->field_name)),
717
          'operator' => 'views_handler_operator_or',
718
					'handler' => 'cdm_taxontree_views_handler_filter_default',
719
          'value' => array(
720
          	'#type' => 'select',
721
		// pass the vocabulary id
722
						'#vocab' => $vid,
723
          	'#options' => $options,
724
          	'#multiple' => $multiple,
725
		),
726
		),
727
		),
728
		);
729
		$tables['cdm_taxontree'. $row->field_name] = $table;
730
	}
731
	return $tables;
732
}
733

    
734
function cdm_taxontree_views_handler_filter_default($op, $filter, $filterinfo, &$query) {
735
	$table = $filterinfo['table'];
736
	$field = $filterinfo['field'];
737

    
738
	if (is_array($filter['value']) && count($filter['value'])) {
739
		if ($filter['operator'] == 'OR' || $filter['operator'] == 'NOR') {
740
			$query->ensure_table($table);
741
			$where_args = array_merge(array($query->use_alias_prefix . $table, $field), $filter['value']);
742
			$placeholder = array_fill(0, count($filter['value']), '%s');
743
			if ($filter['operator'] == 'OR') {
744
				$query->add_where("%s.%s IN (". implode(", ", $placeholder) .")", $where_args);
745
			}
746
			else {
747
				$where_args[] = $where_args[0];
748
				$where_args[] = $where_args[1];
749
				$query->add_where("(%s.%s NOT IN (". implode(", ", $placeholder) .") OR %s.%s IS NULL)", $where_args);
750
			}
751
		}
752
		else {
753
			$howmany = count($filter['value']);
754
			$high_table = $query->add_table($table, true, $howmany);
755
			if (!$high_table) { // couldn't add the table
756
				return;
757
			}
758

    
759
			$table_num = $high_table - $howmany;
760
			foreach ($filter['value'] as $item) {
761
				$table_num++;
762
				$tn = $query->get_table_name($table, $table_num);
763
				$query->add_where("%s.%s = '%s'", $tn, $field, $item);
764
			}
765
		}
766
	}
767
	else {
768
		$query->ensure_table("$table");
769
		$query->add_where("%s.%s %s '%s'", $query->use_alias_prefix . $table, $field, $filter['operator'], $filter['value']);
770
	}
771
}
772

    
773
/**
774
 * implematation of hook_token_list()
775
 *
776
 */
777
function cdm_taxontree_token_list($type = 'all') {
778
	if ($type == 'field' || $type == 'all') {
779
		$tokens = array();
780

    
781
		$tokens['cdm_taxontree']['tid'] = t("Term ID - first item only.");
782
		$tokens['cdm_taxontree']['tid-all'] = t("Term ID - all items comma separated");
783
		$tokens['cdm_taxontree']['tid-n'] = t("Term ID - where n = the value to select if more than one value in the field.");
784
		$tokens['cdm_taxontree']['raw'] = t("Raw, unfiltered term - first item only");
785
		$tokens['cdm_taxontree']['raw-all'] = t("Raw, unfiltered terms - all items comma separated");
786
		$tokens['cdm_taxontree']['raw-n'] = t('Raw, unfiltered terms - where n = the value to select if more than one value in the field');
787
		$tokens['cdm_taxontree']['formatted'] = t("Formatted and filtered terms - first item only");
788
		$tokens['cdm_taxontree']['formatted-all'] = t("Formatted and filtered terms - all items with line break separated");
789
		$tokens['cdm_taxontree']['formatted-n'] = t('Formatted and filtered terms - where n = the value to select if more than one value in the field');
790

    
791
		return $tokens;
792
	}
793
}
794

    
795

    
796
/**
797
 * implementation of hook_token_values()
798
 *
799
 */
800
function cdm_taxontree_token_values($type, $object = NULL) {
801
	if ($type == 'field') {
802
		$i = 0;
803
		foreach ($object as $value) {
804
			$term = taxonomy_get_term($value['tid']);
805
			$values[] = $term->name;
806
			$views[] = $value['view'];
807
			$tokens['raw-' . $i] = $term->name;
808
			$tokens['formatted-' . $i] = $value['view'];
809
			$i++;
810
		}
811

    
812
		//for backwards compat
813
		$tokens['raw-all']  = implode(', ' , $values);
814
		$tokens['formatted-all'] = implode("\n", $views);
815

    
816
		return $tokens;
817
	}
818
}
819

    
820

    
821
function cdm_taxontree_set($key, $value){
822

    
823
  if(is_string($key)){
824
    $_SESSION['cdm']['taxontree'][$key] = $value;
825
  }
826

    
827
  if($_REQUEST['destination']){
828
    $destination = $_REQUEST['destination'];
829
    unset($_REQUEST['destination']);
830
    drupal_goto($destination);
831
  }
832
}
833

    
834

    
835
/**
836
 * Enter description here...
837
 *
838
 * @param UUID $secUuid
839
 * @return unknown
840
 */
841
function cdm_taxontree_secRefTitle_for($secUuid){
842
  
843
  $refSTO = cdm_api_secref_cache_get($secUuid);
844
  if($refSTO && isset($refSTO->fullCitation)){
845
    $cit = $refSTO->fullCitation;
846
  } else {
847
    $cit = '[no title for:'.$secUuid.']';
848
  }
849
  return $cit;
850
}
851

    
852

    
853

    
854
/**
855
 * Queries the Drupal database for the location of a certain block with the given $delta
856
 *
857
 * @param [String | Number] $delta
858
 * @return values (left | right | <empty>)
859
 */
860
function _get_block_region($delta){
861
  global $user, $theme_key;
862
  
863
  $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));
864
  return $result['region'];
865
}
866

    
867
/**
868
 * Enter description here...
869
 *
870
 * @return unknown
871
 */
872
function _get_compact_mode(){
873

    
874
  if(!isset($_SESSION['cdm']['taxontree']['compact_mode'])){
875
    $_SESSION['cdm']['taxontree']['compact_mode'] = 'expanded';
876
  }
877
  return  $_SESSION['cdm']['taxontree']['compact_mode'];
878
}
879

    
880
/**
881
 * Enter description here...
882
 *
883
 * @param unknown_type $str
884
 * @return unknown
885
 */
886
function _is_uuid($str){
887
  return strlen($str) == 36 && strpos($str, '-');
888
}
889

    
890
/**
891
 * Converts an array of drupal taxonomy terms into an
892
 * array of partially instantiated cdm tree nodes by adding the fields
893
 * relevant for tree node processing in cdm_taxontree
894
 *
895
 *
896
 * term       =>      cdm tree node
897
 * ------------------------------------
898
 * tid       ->     uuid
899
   name      ->     titleCache
900
                    taggedName
901
                    secUuid
902
                    isAccepted
903
                    taxonomicChildrenCount
904
                    alternativeConceptRefs
905
 *
906
 * @param unknown_type $terms
907
 */
908
function cdm_taxontree_terms2treenodes(&$terms){
909
  foreach($terms as &$term){
910
    $term->uuid = $term->tid;
911
    $term->titleCache = $term->name;
912
    $term->taxonomicChildrenCount = count(taxonomy_get_children($term->tid, $term->vid));
913
  }
914
  return $terms;
915
}
916

    
917
/**
918
 * Enter description here...
919
 *
920
 * @param unknown_type $tid
921
 * @param unknown_type $vid
922
 * @param unknown_type $theme
923
 */
924
function cdm_taxontree_taxonomy_children($tid, $vid, $theme){
925
  $args = func_get_args();
926
  $tid = array_shift($args);
927
  $vid = array_shift($args);
928
  $theme = array_shift($args);
929
  
930
  $children = cdm_taxontree_get_children($tid, $vid);
931
  $children = cdm_taxontree_terms2treenodes($children);
932
  array_unshift($args, $theme, $children);
933
  print call_user_func_array('theme', $args);
934
}
935

    
936
/**
937
 * Enter description here...
938
 *
939
 * @param unknown_type $vid
940
 * @param unknown_type $secRefUuuid
941
 * @return unknown
942
 */
943
function cdm_taxontree_get_root($vid = null, $secRefUuuid = null){
944
  if(is_numeric($vid)){
945
    // vid, $parent = 0, $depth = -1, $max_depth = NULL) {
946
    $terms = taxonomy_get_tree($vid, 0, -1, 1);
947
    return cdm_taxontree_terms2treenodes($terms);
948
  } else {
949
    return cdm_ws_taxonomy($secRefUuuid, null);
950
  }
951
}
952

    
953
/**
954
 * Enter description here...
955
 *
956
 * @param unknown_type $uuid
957
 * @param unknown_type $vid
958
 * @return unknown
959
 */
960
function cdm_taxontree_get_children($uuid, $vid = null){
961

    
962
  if(is_numeric($vid)){
963
    $terms = taxonomy_get_children($uuid, $vid);
964
    return cdm_taxontree_terms2treenodes($terms);
965
  } else {
966
    //FIXME replace $uuid by path of parent $uuids
967
    
968
    $secRefUuid = variable_get('cdm_secUuid_default', null);
969
    return cdm_ws_taxonomy($secRefUuid, $uuid);
970
  }
971
}
972

    
973
/**
974
 * Enter description here...
975
 *
976
 * @param unknown_type $uuid
977
 * @return unknown
978
 */
979
function cdm_taxontree_get_parents($uuid){
980

    
981
  if(!_is_uuid($uuid)){
982
    $terms = taxonomy_get_parents($uuid);
983
    array_push($terms, taxonomy_get_term($uuid));
984
    $terms = array_reverse($terms);
985
    return cdm_taxontree_terms2treenodes($terms);
986
  } else {
987
    //FIXME use view uuid once views are implemented
988
    //$secRef = _cdm_dataportal_currentSecRef_array();
989
    $secRefUuid = variable_get('cdm_secUuid_default', null);
990
    
991
    return cdm_ws_taxonomy_pathFromRoot($secRefUuid, $uuid);
992
  }
993
}
994

    
995
/**
996
 * builds a tree of TaxonNode instances, whereas the instances are extended by some fields:
997
 *
998
 *  - $node->filter: values ( 'on', 'excluded', 'included' )
999
 *  - $node->expanded: values ( 'expanded', 'collapsed' )
1000
 *    $node->focused: values ( TRUE, FALSE )
1001
 *
1002
 * @param unknown_type $taxonUuid
1003
 * @return unknown
1004
 */
1005
function cdm_taxontree_build_tree($taxonUuid = null, $hideOtherConcepts = true, $vid = null){
1006

    
1007
  // find the secRefUuid
1008
  $secRefUuid = null;
1009
  
1010
  
1011
  //FIXME HACK for testing
1012
  $secRefUuid = '06fd671f-1226-4e3b-beca-1959b3b32e20';
1013
        
1014
  if(is_null($vid)){
1015
    //TODO use instead _cdm_dataportal_currentSecRef_array() & _cdm_dataportal_set_currentSecRef
1016
    //TODO using the currentSecUuid causes problems in cichorieae in taraxacum, 
1017
    /*     thus it is temporary DISABLED..
1018
    if($taxonUuid){
1019
      //TODO poor performance here:
1020
      $taxon =  cdm_ws_get(CDM_WS_TAXON, $taxonUuid);
1021
      $secRefUuid = $taxon->sec->uuid;
1022
    }
1023
     end of DISABLED */
1024
  
1025
    if(!$secRefUuid){
1026
      $secRefUuid = variable_get('cdm_secUuid_default',false);
1027
    }
1028
    
1029
    $compact_tree = cdm_taxontree_filters_active() && _get_compact_mode() != 'expanded';
1030
  }
1031
  // compact_modes: 'expanded', 'compact', 'flattened'
1032

    
1033
  // get the root level
1034
  $root_tree = cdm_taxontree_get_root($vid, ($hideOtherConcepts ? $secRefUuid : null));
1035
//  if(!$root_tree || !is_array($root_tree)){
1036
//    return array();
1037
//  }
1038
  $root_tree = _cdm_resultset2nodelist($root_tree, cdm_taxontree_filters_active());
1039
  
1040

    
1041
  if(cdm_taxontree_filters_active()){
1042
    // the paths up to active filters are inactive in the user interface and
1043
    // thus cannot be browsed by expanding nodes
1044
    // therefore we need to build up the branches for all nodes which are set as filters
1045
    // the branches are merged with the root
1046
    foreach(cdm_taxontree_filters_get() as $uuid=>$filter){
1047
      $branch = cdm_taxontree_build_path($uuid, TRUE, ($compact_tree === false ? true :null));
1048
      $root_tree = _cdm_taxontree_merge($root_tree, $branch);
1049
    }
1050
  }
1051

    
1052
  // build the the branch for the focused node and merge it with the root
1053
  if($taxonUuid){
1054
    $branch = cdm_taxontree_build_path($taxonUuid, NULL, (cdm_taxontree_filters_active() ? NULL : TRUE), TRUE);
1055
    $root_tree = _cdm_taxontree_merge($root_tree, $branch);
1056
  }
1057

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

    
1061
  // flatten tree
1062
  if($compact_tree){
1063
    if( _get_compact_mode() == 'flattened'){
1064
      $root_tree = cdm_taxontree_flatten($root_tree);
1065
    } else if(_get_compact_mode() == 'compact') {
1066
      foreach($root_tree as $uuid => $node){
1067
        if( $node->filter == 'excluded' && !$node->children){
1068
          unset($root_tree[$uuid]);
1069
        }
1070
      }
1071
    }
1072
  }
1073

    
1074
  return $root_tree;
1075
}
1076

    
1077
/**
1078
 * Builds the specific branch path for $taxonUuid.
1079
 * The branch path reaches from the parent root node of $taxonUuid up to $taxonUuid.
1080
 *
1081
 * @param UUID $taxonUuid
1082
 * @param Boolean $is_filter_path whether the upmost node of this path is mapped by an active filter
1083
 * @param Boolean $is_expanded whether all nodes along the tree are expanded
1084
 * @param Boolean $is_focused whether to upper most element of this branch is set as filter
1085
 * @return a subtree
1086
 */
1087
function cdm_taxontree_build_path($taxonUuid, $is_filter_path = null, $is_expanded = null, $is_focused = FALSE){
1088

    
1089
  $branch_path = array();
1090
  
1091
  $parents = cdm_taxontree_get_parents($taxonUuid);
1092
  if(!$parents){
1093
    if($is_filter_path){
1094
      // remove invalid filter
1095
      cdm_taxontree_filters_remove($taxonUuid);
1096
    }
1097
    return false;
1098
  }
1099

    
1100
  $parents = _cdm_resultset2nodelist($parents, NULL);
1101
  $lastParent = null;
1102
  foreach($parents as $pnode){
1103
    $pnode->focused = false;
1104
    if($lastParent){
1105
      $pnode->children = array($lastParent->uuid => $lastParent);
1106
      if(!is_null($is_filter_path)){
1107
        $pnode->filter = ($is_filter_path ? 'excludes' : 'included');
1108
      }
1109
      if(!is_null($is_expanded)){
1110
        $pnode->expanded = ($is_expanded ? 'expanded' : 'collapsed');
1111
      }
1112
    } else {
1113
      // the uppermost node of branch
1114
      if(!is_null($is_filter_path)){
1115
        $pnode->filter = ($is_filter_path ? 'on' : 'includes');
1116
      }
1117
      // uppermost node is always expanded if it has children
1118
      $pnode->focused = $is_focused;
1119
      $pnode->expanded = ($pnode->taxonomicChildrenCount ? 'expanded' : 'collapsed');
1120
    }
1121
    $lastParent = $pnode;
1122
  }
1123
  $branch_path[$pnode->uuid] = $pnode;
1124
  return $branch_path;
1125
}
1126

    
1127
/**
1128
 * Performs two steps on each level of the tree:
1129
 *  1. reorder siblings except root (is expected to be ordered jet) alphabetically
1130
 *  2. populate children of expanded nodes  & propagate the filter attribute
1131
 *
1132
 * @param unknown_type $tree
1133
 * @return unknown
1134
 */
1135
function cdm_taxontree_populate($tree, $expand_excluded, $filter_default = null){
1136

    
1137
  if(!is_array($tree)){
1138
    return false;
1139
  }
1140
  foreach(array_keys($tree) as $uuid){
1141

    
1142
    if(!isset($tree[$uuid]->filter) && !is_null($filter_default)){
1143
      $tree[$uuid]->filter = $filter_default;
1144
    }
1145

    
1146
    if( $tree[$uuid]->expanded == 'expanded' && ($expand_excluded || $tree[$uuid]->filter != 'excluded')){
1147
      $children = cdm_taxontree_get_children($uuid, $tree[$uuid]->vid);
1148
      $children = _cdm_resultset2nodelist($children, ($tree[$uuid]->filter == 'excludes'));
1149

    
1150
      // store the children of the node for later processing
1151
      if(is_array($tree[$uuid]->children)){
1152
        $pnode_children = $tree[$uuid]->children;
1153
      } else {
1154
        $pnode_children = false;
1155
      }
1156
      // replace the children by the newly retrieved child nodes
1157
      $tree[$uuid]->children = $children;
1158
       
1159
      if($pnode_children){
1160
        // recurse into the childtree which was stored before
1161
        $pnode_children =  cdm_taxontree_populate($pnode_children, $expand_excluded ,$tree[$uuid]->filter);
1162
        // recombine
1163
        foreach($pnode_children as $childUuid=>$cnode){
1164
          $tree[$uuid]->children[$childUuid] = $cnode;
1165
        }
1166
      }
1167
    } else {
1168
      // reorder nodes which are not expanded, expanded nodes are reordered implicitly above
1169
      if(isset($tree[$uuid]->children) && count($tree[$uuid]->children) > 1) {
1170
        // copy the children into an array which can be sorted by its keys
1171
        $ordered = array();
1172
        foreach($tree[$uuid]->children as $cnode){
1173
          // concatenate full name and uid
1174
          $reordered[str_pad($cnode->titleCache, 255, '-').$cnode->uuid] = $cnode;
1175
        }
1176
        // sort
1177
        ksort($reordered);
1178
        // move the children back into the parent node
1179
        $tree[$uuid]->children = array();
1180
        foreach($reordered as $cnode){
1181
          $tree[$uuid]->children[$cnode->uuid] = $cnode;
1182
        }
1183
      }
1184
      $tree[$uuid]->children = cdm_taxontree_populate($tree[$uuid]->children, $expand_excluded, $tree[$uuid]->filter);
1185
    }
1186
  }
1187
  return $tree;
1188
}
1189

    
1190
/**
1191
 * Enter description here...
1192
 *
1193
 * @param unknown_type $tree
1194
 * @param unknown_type $new_root
1195
 * @return unknown
1196
 */
1197
function cdm_taxontree_flatten($tree, &$new_root = null){
1198
  if(!$new_root){
1199
    $new_root = array();
1200
  }
1201
  foreach($tree as $node){
1202
    if($node->filter == 'on'){
1203
      $new_root[$node->uuid] = $node;
1204
    } else if(is_array($node->children)){
1205
      cdm_taxontree_flatten($node->children, $new_root);
1206
    }
1207
  }
1208
  return $new_root;
1209
}
1210

    
1211

    
1212
/**
1213
 * Merge a branch into a tree whereas the tree dominated the branch except
1214
 * nodes having property filter set to "on". These always dominate
1215
 * nevertheless if they are in tree or branch.
1216
 *
1217
 * @param unknown_type $tree the dominant tree
1218
 * @param unknown_type $branch the tree to be merged in
1219
 * @return the merged $tree
1220
 */
1221
function _cdm_taxontree_merge($tree, $branch) {
1222

    
1223
  if(!$branch){
1224
    return $tree;
1225
  }
1226
  
1227
  foreach(array_keys($tree) as $uuid) {
1228
    // check if node exists in $branch
1229
    if(!empty($branch[$uuid])) {
1230
      // preserve filter property
1231
      if(isset($tree[$uuid]->filter) && !(isset($branch[$uuid]->filter) && $branch[$uuid]->filter == 'on') ){
1232
        $branch[$uuid]->filter = $tree[$uuid]->filter;
1233
      } else if(isset($branch[$uuid]->filter)){
1234
        $tree[$uuid]->filter = $branch[$uuid]->filter;
1235
      }
1236
      // preserve expanded property
1237
      if(isset($tree[$uuid]->expanded)){
1238
        $branch[$uuid]->expanded = $tree[$uuid]->expanded;
1239
      } else if(isset($branch[$uuid]->expanded)){
1240
        $tree[$uuid]->expanded = $branch[$uuid]->expanded;
1241
      }
1242
      // $Uuid exists check if the node in tree1 or tree2 contains children
1243
      if(is_array($branch[$uuid]->children) && is_array($tree[$uuid]->children)) {
1244
        // merge recursive
1245
        $tree[$uuid]->children = _cdm_taxontree_merge($tree[$uuid]->children, $branch[$uuid]->children);
1246
      } else if(is_array($branch[$uuid]->children)){
1247
        $tree[$uuid] =  $branch[$uuid];
1248
      }
1249
      unset($branch[$uuid]);
1250
    }
1251
  }
1252
  // append remaining items from branch to tree
1253
  foreach(array_keys($branch) as $uuid){
1254
    $tree[$uuid] =  $branch[$uuid];
1255
  }
1256
  return $tree;
1257
}
1258

    
1259

    
1260
/**
1261
 * Replaces the keys of an array of TreeNode instances
1262
 * by the $treeNode->uuid of the single array elements.
1263
 * An sets additional fields
1264
 *
1265
 * @param $resultset array of TreeNode instances as +returned by the cdm web service
1266
 * @param $excluded  whether the $resultset is included by a active filter. Is ignored if NULL.
1267
 * @param $expanded  whether the children of the nodes in the $resultset are expanded or not. Is ignored if NULL.
1268
 */
1269
function _cdm_resultset2nodelist($resultset, $excluded = null, $expanded = null){
1270

    
1271
  if(! is_array($resultset)) {
1272
    return false;
1273
  }
1274

    
1275
  $tree = array();
1276
  foreach($resultset as $treeNode){
1277
    if(!is_null($excluded)){
1278
      $treeNode->filter = ($excluded ? 'excluded': 'included');
1279
    }
1280
    if(!is_null($expanded)){
1281
      $treeNode->expanded = ($expanded ? 'expanded': 'collapsed');
1282
    }
1283
    $tree[$treeNode->uuid] = $treeNode;
1284
  }
1285
  return $tree;
1286
}
1287

    
1288

    
1289
// ------------------------ THEME --------------------------- //
1290

    
1291

    
1292
function theme_cdm_taxontree_add_scripts(){
1293
  $path_cdm_taxontree = drupal_get_path('module', 'cdm_taxontree');
1294
  $path_preferred_module = drupal_get_path('module', 'cdm_dataportal') ? drupal_get_path('module', 'cdm_dataportal') : $path_cdm_taxontree;
1295
  drupal_add_css($path_cdm_taxontree.'/cdm_taxontree.css');
1296
  drupal_add_js($path_preferred_module.'/js/jquery.dimensions.js');
1297
  drupal_add_js($path_cdm_taxontree.'/js/cdm_taxontree.js');
1298
  drupal_add_js($path_cdm_taxontree.'/js/jquery.scrollTo.js');
1299
}
1300

    
1301

    
1302
/**
1303
 * @param $tree 				the tree of TreeNode to be displayed
1304
 * @param $magicbox 			if true, the tree will be embedded into a set of div tags which allow the
1305
 *            					tree to expand and overlap other content. This is useful if the node titles are
1306
 *            					quite long or if the tree is nested deeply.
1307
 *            					If $magicbox ist set to the delta of the containing block the direction into which the
1308
 *            					box expands is dependent on the region in which the blockis located.
1309
 *            					See also Variable $left_expand_region in this function!
1310
 * @param $show_filter_switch 	The tree can offer buttons to add a node to a set of filters
1311
 *            					which can then be applied to the tree to limit the visible subtrees and thus
1312
 *            					to compact the tree. Three different compact modes are available.
1313
 * @param $tree_node_callback 	name of a callback method which will be called for each node
1314
 *            					in theme_cdm_taxontree_node(). The output of this callback, which takes the
1315
 *            					$node object as single arument, is appended to the end of the redered node.
1316
 */
1317
function theme_cdm_taxontree_block($tree, $delta, $magicbox = false, $show_filter_switch = false, $tree_node_callback = false){
1318

    
1319
   // THEMERS: change the line below according the specific regions of your theme
1320
   $left_expand_region = 'right';
1321
  
1322
  $out = '';
1323
  if(cdm_taxontree_filters_active()){
1324
    $out .= theme('cdm_taxontree_contoller', _get_compact_mode());
1325
  }
1326
  if($magicbox){
1327
    if(is_numeric($magicbox) || is_string($magicbox)){
1328
      $region = _get_block_region($magicbox);
1329
    }
1330
    // the magicbox expands to the right by default,
1331
    // if the class 'expand-left' to  the cdm_taxontree_scroller_x the box will expand to the left
1332
    $expand_direction = $region == 'right' ? 'expand-left' : '';
1333
    $out .= '<div class="cdm_taxontree_scroller_x '.$expand_direction.'"><div class="cdm_taxontree_container"><div class="cdm_taxontree_scroller_y">';
1334
  } else {
1335
    $out .= '<div class="cdm_taxontree_scroller_xy">';
1336
  }
1337
  
1338
  $out .= theme('cdm_taxontree', $tree, !cdm_taxontree_filters_active(), $show_filter_switch , $tree_node_callback);
1339
  
1340
  if($magicbox){
1341
    $out .= '</div></div></div>';
1342
  } else {
1343
    $out .= '</div>';
1344
  }
1345
  return $out;
1346
}
1347

    
1348
function theme_cdm_taxontree_contoller($compact_mode){
1349

    
1350
  static $modes = array('expanded', 'compact', 'flattened');
1351

    
1352
  $out = '<div class="settings">';
1353
  foreach($modes as $mode){
1354
    if($compact_mode == $mode){
1355
      $out .= t($mode);
1356
    } else {
1357
      $out .= l(t($mode), 'cdm_taxontree/set/compact_mode/'.$mode, array(), drupal_get_destination());
1358
    }
1359
    $out .= ' ';
1360
  }
1361

    
1362
  return $out.'</div>';
1363
}
1364

    
1365
function theme_cdm_taxontree($tree, $filterIncludes = null, $show_filter_switch = false, $tree_node_callback = false, $element_name = false){
1366

    
1367
  if(!is_array($tree)) {
1368
//    $out = '<ul class="cdm_taxontree">';
1369
//    $out .= '<li>----------------------------NO TREE---------------------------------------</li>';
1370
//    $out .= '</ul>';
1371
//    return $out;
1372
    return false;
1373
  }
1374

    
1375
  if(is_null($filterIncludes)){
1376
    // set $filterIncludes true if no filters are set.
1377
    $filterIncludes = !cdm_taxontree_filters_active();
1378
  }
1379

    
1380
	// append element name to get multiple taxontrees on one page working
1381
	$out = '<ul class="cdm_taxontree' . (($element_name) ? ' ' . $element_name : '') . '">';
1382
  foreach($tree as $node){
1383
    $out .= theme('cdm_taxontree_node', $node, $filterIncludes, $show_filter_switch, $tree_node_callback);
1384
  }
1385
  $out .= '</ul>';
1386
  return $out;
1387
}
1388

    
1389
function theme_cdm_taxontree_node($node, $filterIncludes, $show_filter_switch = false, $tree_node_callback = false){
1390

    
1391
  $is_leaf = !$node->taxonomicChildrenCount || $node->taxonomicChildrenCount == 0;
1392
  $is_expanded = isset($node->expanded) && $node->expanded = 'expanded';
1393
  
1394
  if($node->tid){
1395
    $node_name = $node->name;
1396
    $path = "taxonomy/term/".$node->tid;
1397
    // disable filterswitch
1398
    $show_filter_switch = false;
1399
    
1400
  } else if(module_exists('cdm_dataportal')){
1401
      $node_name = cdm_dataportal_shortname_of($node);
1402
      $path = cdm_dataportal_taxon_path($node->uuid);
1403
  } else {
1404
      $node_name = "module cdm_dataportal missing";
1405
      $path = "";
1406
  }
1407

    
1408
  if($filterIncludes){
1409
		$name = l($node_name, $path);
1410
		// no names for terms in filter widget; as discussed with A. Müller
1411
		//$name = '';
1412
    
1413
		$filter_class = 'filter_included';
1414
  } else {
1415
    if($node->filter == 'on') {
1416
      $name = l($node_name,  $path);
1417
      $filter_class = 'filter_on';
1418
    } else {
1419
      $name .= $node_name;
1420
      $filter_class = 'filter_excluded';
1421
    }
1422
  }
1423
  $nextLevelIncluded = $node->filter == 'on' || $filterIncludes;
1424

    
1425
  $ahah_url = false;
1426
  if(!$is_leaf && !$is_expanded && $filter_class != 'filter_excluded'){
1427
    if($node->tid){
1428
      $ahah_url = url('cdm_taxontree/taxonomy/children/'.$node->tid.'/'.$node->vid.'/cdm_taxontree/'.($nextLevelIncluded ? 1 : 0).'/'.($show_filter_switch ? 1 : 0).'/'.$tree_node_callback);
1429
    } else if(module_exists('cdm_dataportal')) {
1430
      
1431
      $ws_url = cdm_compose_url(cdm_ws_taxonomy_compose_resourcePath( $node->secUuid, $node->uuid));
1432
      $ahah_url = url('cdm_api/proxy/'.urlencode($ws_url).'/cdm_taxontree/'.($nextLevelIncluded ? 1 : 0).'/'.($show_filter_switch ? 1 : 0).'/'.$tree_node_callback);
1433
    }
1434
  }
1435

    
1436
  // list item
1437
  $out = '<li class="'
1438
  .($node->focused ? 'focused ' : '')
1439
  .($is_leaf ? 'leaf ':($is_expanded ?'expanded ':'collapsed '))
1440
  .$filter_class.'"'
1441
  .($ahah_url ? 'title="'.$ahah_url.'"' : '')
1442
  .'>';
1443

    
1444
  if($show_filter_switch){
1445
    // filter icon
1446
    $out .= theme('cdm_taxontree_node_filter_switch', $node, $filter_class);
1447
  }
1448

    
1449
  // taxon name
1450
  $out .= $name;
1451

    
1452
  // concept_switch or other theme callbacks
1453
  if($tree_node_callback){
1454
    $out .= theme($tree_node_callback, $node);
1455
  }
1456

    
1457
  if($node->children && is_array($node->children)){
1458
    $out .= theme('cdm_taxontree', $node->children, $nextLevelIncluded, $show_filter_switch, $tree_node_callback);
1459
  }
1460
  $out .= '</li>';
1461

    
1462
  return $out;
1463
}
1464

    
1465
function theme_cdm_taxontree_node_filter_switch(&$node, $filter_class){
1466
  if(!module_exists('cdm_dataportal')) {
1467
    return '';
1468
  }
1469
  
1470
  $out = '';
1471
  switch($filter_class){
1472
    case 'filter_included':
1473
      $filter_icon = 'visible_implicit_small.gif';
1474
      break;
1475
    case 'filter_excluded':
1476
      $filter_icon = 'invisible_small.gif';
1477
      break;
1478
    case 'filter_on':
1479
      $filter_icon = 'visible_small.gif';
1480
      break;
1481
  }
1482

    
1483
  $filter_op = $node->filter == 'on' ? 'remove' : 'add';
1484

    
1485
  $out .= '&nbsp;'
1486
  .l('<img src="'.drupal_get_path('module', 'cdm_taxontree').'/'.$filter_icon.'" alt="[f]" />',
1487
    'cdm_taxontree/filter/'.$filter_op.'/'.$node->uuid, array('class'=>'filter_'.$filter_op),
1488
    'destination='.cdm_dataportal_taxon_path($node->uuid),
1489
  null, false, true);
1490
   
1491
  return $out;
1492
}
1493

    
1494
function theme_cdm_taxontree_node_concept_switch(&$node){
1495
  $out = '';
1496

    
1497
  if(isset($node->alternativeConceptRefs[0])){
1498
    $out = l(
1499
      '<img src="'.drupal_get_path('module', 'cdm_taxontree').'/concept_switch.gif" alt="[-&gt;]" />',
1500
      'cdm_dataportal/taxon/alternative/'.$node->uuid,
1501
    array('rel'=>'cdm_dataportal/taxon/alternative/'.$node->uuid, 'class'=>'concept_switch'),
1502
    null, null, false, true);
1503
  }
1504
  return $out;
1505
}
1506

    
1507

    
1508

    
1509
/**
1510
 * Theme function that returns a <span> element containing the secundum of a given TreeNode> element
1511
 *
1512
 * @param array $node The <TreeNode> element as returned by the webservice
1513
 *
1514
 * @return String html formatted title of the secundum
1515
 */
1516
function theme_cdm_taxontree_node_reference(&$node){
1517

    
1518
  $secRefTitle = cdm_taxontree_secRefTitle_for($node->secUuid);
1519
  // encode any special characters
1520
  $secRefTitle = check_plain($secRefTitle);
1521
  $out = ' <span class="sec_ref widget_select" title="'.$secRefTitle.'" style="background-color:#'._uuid_to_rgbhex($node->secUuid).'" alt="'.$node->secUuid.'">'
1522
      .$secRefTitle.'</span>';
1523
  return $out;
1524
}
1525

    
1526

    
1527
/**
1528
 * theme function to display terms in the cd,_taxontree_widget with additional data.
1529
 *
1530
 */
1531
function theme_cdm_taxontree_node_reference_widget(&$node){
1532
	$title = ($node->titleCache) ? check_plain($node->titleCache) : check_plain($node->name);
1533
	$parents = taxonomy_get_parents_all($node->tid);
1534
	$path = theme('cdm_taxontree_taxon_fullpath',array_reverse($parents),3);
1535
	$out = ' <span class="sec_ref widget_select" title="' . (!empty($path) ? $path : $title) . '" alt="'.$node->tid.'">' . $title . '</span>';
1536
	return $out;
1537
}
1538

    
1539
/**
1540
 * theme function to replace term names with full path to term name
1541
 *
1542
 * @param array $tids path->to->term
1543
 * @param integer $max_levels, 0 is unlimited
1544
 * @return text representation of path->to->term
1545
 */
1546
function theme_cdm_taxontree_taxon_fullpath($tids = array(), $max_levels = 3){
1547
	$path = array();
1548
	foreach ($tids as $tid){
1549
		$path[] = $tid->name;
1550
	}
1551
	if(count($path) > $max_levels){
1552
		$abbr_path = array_slice($path,0,$max_levels-1);
1553
		array_push($abbr_path,'…');
1554
		array_push($abbr_path,array_pop($path));
1555
		$path = $abbr_path;
1556
	}
1557
	return implode(' » ',$path);
1558
}
1559

    
1560
// ----------------- FILTERS -------------------------- //
1561

    
1562
/**
1563
 * filters on children, overrides already set parent filters and vice versa
1564
 *
1565
 * @param STRING $op [add | remove] a taxon from the filtered taxa
1566
 * 					 TODO at the moment there is also a 'list' operation that displays all set filters and provides the ability to delete them.
1567
 * 						  This option depends on function is defined in cdm_dataportal. Problem is, that the dependency is the other way round.
1568
 * @param UUID $taxonUuid
1569
 * @return unknown
1570
 */
1571
function cdm_taxontree_view_filter($op, $taxonUuid = null){
1572
  
1573
  if(!isset($_SESSION['cdm']['filters'])){
1574
    $_SESSION['cdm']['filters'] = array();
1575
  }
1576
  if($taxonUuid || $op == 'list'){
1577
    switch($op){
1578
      case 'add':
1579
        cdm_taxontree_filters_add($taxonUuid);
1580
        break;
1581
      case 'remove':
1582
        cdm_taxontree_filters_remove($taxonUuid);
1583
        break;
1584
      case 'list':
1585
        //TODO put in cdm_dataportal_theme to decouple both modules by this!!!
1586
        $out = '<ul>';
1587
        foreach($_SESSION['cdm']['filters'] as $uuid=>$node){
1588
          $out .= '<li>'.cdm_dataportal_shortname_of($node).' '.l('[x]', 'cdm_dataportal/filter/remove/'.$uuid, array(), drupal_get_destination()).'</li>';
1589
        }
1590
        $out .= '</ul>';
1591
        return $out;
1592
    }
1593
  }
1594
  if($_REQUEST['destination']){
1595
    $destination = $_REQUEST['destination'];
1596
    unset($_REQUEST['destination']);
1597
    drupal_goto($destination);
1598
  }
1599
}
1600

    
1601
/**
1602
 * Filters are set in cdm_dataportal_view_filter().
1603
 * functions using filters should remove invalid filters
1604
 *
1605
 * @return true if any filter is active
1606
 */
1607
function cdm_taxontree_filters_active(){
1608
 return isset($_SESSION['cdm']['filters']) && count($_SESSION['cdm']['filters']) > 0;
1609
}
1610

    
1611
/**
1612
 * Filters are set in cdm_dataportal_view_filter().
1613
 *
1614
 * @return a reference on the filters array stored in the SESSION
1615
 */
1616
function &cdm_taxontree_filters_get(){
1617
  if(!isset($_SESSION['cdm']['filters'])){
1618
    $_SESSION['cdm']['filters'] = array();
1619
  }
1620
 return $_SESSION['cdm']['filters'];
1621
}
1622

    
1623
/**
1624
 * Adds a taxon to the filtered taxa array
1625
 *
1626
 * @param UUID $taxonUuid
1627
 */
1628
function cdm_taxontree_filters_add($taxonUuid){
1629

    
1630
  $sec = _cdm_dataportal_currentSecRef_array();
1631
  $parents = cdm_ws_taxonomy_pathFromRoot($sec['uuid'], $taxonUuid);
1632
  
1633
  $parents = array_reverse($parents);
1634
  
1635
  // pop off last element since this is the TreeNode object for $taxonUuid!
1636
  $this_node = array_pop($parents);
1637
  // will contain the uuid of the parent nodes excluding the $taxonUuid node itself
1638
  $parent_uuids = array();
1639
  
1640
  // children override parents rule: remove all parent filters,
1641
  foreach($parents as $pnode){
1642
    unset($_SESSION['cdm']['filters'][$pnode->uuid]);
1643
    $parent_uuids[] = $pnode->uuid;
1644
  }
1645
  
1646
  // search for potential children of this $taxonUuid
1647
  foreach($_SESSION['cdm']['filters'] as $uuid=>$node){
1648
    if(in_array($taxonUuid, $node->parentUuids)){
1649
      unset($_SESSION['cdm']['filters'][$node->uuid]);
1650
    }
1651
  }
1652
  // finally add this $taxonUuid as new filter
1653
  $this_node->parentUuids = $parent_uuids;
1654
  $_SESSION['cdm']['filters'][$taxonUuid] = $this_node;
1655
}
1656

    
1657
/**
1658
 * Unsets a taxon from the filtered taxa array
1659
 *
1660
 * @param UUID $taxonUuid
1661
 */
1662
function cdm_taxontree_filters_remove($taxonUuid){
1663
  unset($_SESSION['cdm']['filters'][$taxonUuid]);
1664
}
1665

    
1666

    
1667
// ------------------------ PRIVATE --------------------------- //
1668

    
1669
/**
1670
 * Analyses the current Drupal path.
1671
 * If a certain taxon was requested in the request, returns the UUID of that taxon.
1672
 * A stored UUID if no taxon was requested.
1673
 * TODO where does the dtored UUID come from?
1674
 *
1675
 * @return UUID
1676
 */
1677
function _cdm_get_taxonuuid(){
1678

    
1679
  //TODO make the path configurable
1680
  if (arg(0)=="cdm_dataportal" && arg(1)=="taxon" && arg(2)!==0){
1681
    $taxon_uuid = arg(2);
1682
  } else {
1683
    $taxon_uuid = $_SESSION['cdm_dataportal']['tree']['taxon_uuid'];
1684
  }
1685
  return $taxon_uuid;
1686
}
1687

    
1688
/**
1689
 * Converts a UUID into a hexadecimal RGB colour code.
1690
 *
1691
 * @param UUID $uuid
1692
 * @return Number hexadecimal
1693
 */
1694
function _uuid_to_rgbhex($uuid){
1695

    
1696
  $xfoot = _str_crossfoot($uuid);
1697
  $h = $xfoot / 255;
1698
  $h = $h - floor($h);
1699
  $RGB = _hsv_2_rgb($h, 0.45, 1);
1700
  return dechex($RGB['R']).dechex($RGB['G']).dechex($RGB['B']);
1701
}
1702

    
1703
/**
1704
 * Sums up ASCII values of the character in the given string.
1705
 *
1706
 * @param String $str
1707
 * @return Number
1708
 */
1709
function _str_crossfoot($str){
1710
  $xfoot = 0;
1711
  for($i=0; $i<strlen($str); $i++){
1712
    $xfoot = $xfoot + ord($str[$i]);
1713
  }
1714
  return $xfoot;
1715
}
1716

    
1717
/**
1718
 * Converts HSV colour codes into their RGB counterpart
1719
 *
1720
 * @param Number $H value 0-1
1721
 * @param Number $S value 0-1
1722
 * @param Number $V value 0-1
1723
 * @return array three values  0-255
1724
 */
1725
function _hsv_2_rgb($H, $S, $V) // HSV Values:Number 0-1
1726
{ //
1727
  $RGB = array();
1728
  
1729
  if($S == 0){
1730
    $R = $G = $B = $V * 255;
1731
  } else {
1732
    $var_H = $H * 6;
1733
    $var_i = floor( $var_H );
1734
    $var_1 = $V * ( 1 - $S );
1735
    $var_2 = $V * ( 1 - $S * ( $var_H - $var_i ) );
1736
    $var_3 = $V * ( 1 - $S * (1 - ( $var_H - $var_i ) ) );
1737
  
1738
    if ($var_i == 0) { $var_R = $V ; $var_G = $var_3 ; $var_B = $var_1 ; }
1739
    else if ($var_i == 1) { $var_R = $var_2 ; $var_G = $V ; $var_B = $var_1 ; }
1740
    else if ($var_i == 2) { $var_R = $var_1 ; $var_G = $V ; $var_B = $var_3 ; }
1741
    else if ($var_i == 3) { $var_R = $var_1 ; $var_G = $var_2 ; $var_B = $V ; }
1742
    else if ($var_i == 4) { $var_R = $var_3 ; $var_G = $var_1 ; $var_B = $V ; }
1743
    else { $var_R = $V ; $var_G = $var_1 ; $var_B = $var_2 ; }
1744
  
1745
    $R = $var_R * 255;
1746
    $G = $var_G * 255;
1747
    $B = $var_B * 255;
1748
  }
1749
  
1750
  $RGB['R'] = $R;
1751
  $RGB['G'] = $G;
1752
  $RGB['B'] = $B;
1753
  
1754
  return $RGB;
1755
}
(5-5/16)