Project

General

Profile

Download (18.1 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
 * The contents of this file are subject to the Mozilla Public License Version 1.1
13
 * See LICENSE.TXT at the top of this package for the full license terms.
14
 */
15

    
16

    
17
/**
18
 * Implementation of hook_menu()
19
 */
20
function cdm_taxontree_menu($may_cache) {
21
  
22
  $items = array();
23
  if ($may_cache) {
24

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

    
32
  }
33
  return $items;
34
}
35

    
36

    
37
/**
38
 * Implementation of hook_block()
39
 *
40
 * @param String $op
41
 * @param int $delta
42
 */
43
function cdm_taxontree_block($op='list', $delta=0) {
44
  if ($op == "list") {
45
    $block[0]["info"] = t('CDM Taxon Tree');
46
    return $block;
47
  }
48
  else if ($op == 'view') {
49
    switch($delta){
50
      case 0:
51
        $block['subject'] = t('CDM Taxon Tree');
52
        $taxonUuid_inFocus = _cdm_get_taxonuuid();
53
        $tree = cdm_taxontree_build_tree($taxonUuid_inFocus);
54
        $block['content'] = theme('cdm_taxontree_block', $tree);
55
        drupal_add_css(drupal_get_path('module', 'cdm_taxontree').'/cdm_taxontree.css');
56
        drupal_add_js(drupal_get_path('module', 'cdm_taxontree').'/js/cdm_taxontree.js');
57
        if($taxonUuid_inFocus){
58
          drupal_add_js(drupal_get_path('module', 'cdm_taxontree').'/js/jquery.scrollTo.js');
59
          drupal_add_js(drupal_get_path('module', 'cdm_taxontree').'/js/jquery.dimensions.js');
60
          drupal_add_js('
61
      if (Drupal.jsEnabled) {
62
        $(document).ready(function() 
63
        {
64
          //var activeElement = $(\'div.cdm_taxontree_scroller_x .active\');
65
          $(\'div.cdm_taxontree_scroller_x\').scrollTo($(\'.active\'));});
66
        }', 'inline');
67
        }
68
        return $block;
69
    }
70
  }
71
}
72

    
73
function cdm_taxontree_set($key, $value){
74
  
75
  if(is_string($key)){
76
    $_SESSION['cdm']['taxontree'][$key] = $value; 
77
  }
78
  
79
  if($_REQUEST['destination']){
80
    $destination = $_REQUEST['destination'];
81
    unset($_REQUEST['destination']);
82
    drupal_goto($destination);
83
  }
84
}
85

    
86
function _get_compact_mode(){
87
  
88
  if(!isset($_SESSION['cdm']['taxontree']['compact_mode'])){
89
    $_SESSION['cdm']['taxontree']['compact_mode'] = 'expanded';
90
  }
91
  return  $_SESSION['cdm']['taxontree']['compact_mode'];
92
}
93

    
94
/**
95
 * builds a tree of TaxonNode instances, whereas the instances are extended by some fields:
96
 * 
97
 *  - $node->filter: values ( 'on', 'excluded', 'included' ) 
98
 *  - $node->expanded: values ( 'expanded', 'collapsed' ) 
99
 *    $node->focused: values ( TRUE, FALSE ) 
100
 *
101
 * @param unknown_type $taxonUuid
102
 * @return unknown
103
 */
104
function cdm_taxontree_build_tree($taxonUuid = null){
105

    
106
  // find the secRefUuid
107
  $secRefUuid = null;
108
  if($taxonUuid){
109
    //TODO poor performance here:
110
    $taxon =  cdm_ws_get(CDM_WS_TAXON, $taxonUuid);
111
    $secRefUuid = $taxon->sec->uuid;
112
  }
113

    
114
  if(!$secRefUuid){
115
    $secRef = _cdm_dataportal_currentSecRef_array();
116
    $secRefUuid = $secRef['uuid'];
117
  // compact_modes: 'expanded', 'compact', 'flattened'
118
  }
119
  $compact_tree = cdm_dataportal_filters_active() && _get_compact_mode() != 'expanded';
120
  
121
  // get the root level
122
  $root_tree = cdm_ws_get(CDM_WS_TREENODE_ROOT, $secRefUuid);
123
  $root_tree = _cdm_resultset2nodelist($root_tree, cdm_dataportal_filters_active());
124

    
125
  if(cdm_dataportal_filters_active()){
126
    // the paths up to active filters are inactive in the user interface and
127
    // thus cannot be browsed by expanding nodes
128
    // therefore we need to build up the branches for all nodes which are set as filters
129
    // the branches are merged with the root
130
    foreach(cdm_dataportal_filters_get() as $uuid=>$filter){
131
      $branch = cdm_taxontree_build_path($uuid, TRUE, $compact_tree === false);
132
      $root_tree = _cdm_taxontree_merge($root_tree, $branch);
133
    }
134
  }
135
  
136
  // build the the branch for the focused node and merge it with the root
137
  if($taxonUuid){
138
    $branch = cdm_taxontree_build_path($taxonUuid, NULL, (cdm_dataportal_filters_active() ? NULL : TRUE), TRUE);
139
    $root_tree = _cdm_taxontree_merge($root_tree, $branch);
140
  }
141
  
142
  //reorder siblings & populate expanded nodes with children and propagate the filter attribute
143
  $root_tree = cdm_taxontree_populate($root_tree, $compact_tree === false);
144
  
145
  // flatten tree 
146
  if($compact_tree && _get_compact_mode() == 'flattened'){
147
    $root_tree = cdm_taxontree_flatten($root_tree);
148
  }
149
  
150
  return $root_tree;
151
}
152

    
153
/**
154
 * Builds the specific branch path for $taxonUuid. 
155
 * The branch path reaches from the parent root node of $taxonUuid up to $taxonUuid.
156
 *
157
 * @param unknown_type $taxonUuid
158
 * @param $is_filter_path whether the upmost node of this path is mapped by an active filter 
159
 * @param $is_expanded whether all nodes along the tree are expanded 
160
 * @return a subtree
161
 */
162
function cdm_taxontree_build_path($taxonUuid, $is_filter_path = null, $is_expanded = null, $is_focused = FALSE){
163
  
164
  $branch_path = array();
165
  $parents = cdm_ws_get(CDM_WS_TREENODE_PARENTS, $taxonUuid);
166
  if(!$parents){
167
    return false;
168
  }
169
  //TODO reorder parents in JSON stubs and remove the if condition below
170
  if(variable_get('cdm_webservice_isStub', 0)){ 
171
    $parents = array_reverse($parents);
172
  }
173
  
174
  $parents = _cdm_resultset2nodelist($parents, NULL);
175
  $lastParent = null;
176
  foreach($parents as $pnode){
177
    $pnode->focused = false;
178
    if($lastParent){
179
      $pnode->children = array($lastParent->uuid => $lastParent);
180
      if(!is_null($is_filter_path)){
181
        $pnode->filter = ($is_filter_path ? 'excludes' : 'included');
182
      }
183
      if(!is_null($is_expanded)){
184
        $pnode->expanded = ($is_expanded ? 'expanded' : 'collapsed');
185
      }
186
    } else {
187
      // the uppermost node of branch
188
      if(!is_null($is_filter_path)){
189
        $pnode->filter = ($is_filter_path ? 'on' : 'includes');
190
      }
191
      // uppermost node is always expanded if it has children
192
      $pnode->focused = $is_focused;
193
      $pnode->expanded = ($pnode->hasChildren ? 'expanded' : 'collapsed');
194
    }
195
    $lastParent = $pnode;
196
  }
197
  $branch_path[$pnode->uuid] = $pnode;
198
  return $branch_path;
199
}
200

    
201
/**
202
 * Performs two steps on each level of the tree:
203
 *  1. reorder siblings except root (is expected to be ordered jet) alphabetically 
204
 *  2. populate children of expanded nodes  & propagate the filter attribute
205
 *
206
 * @param unknown_type $tree
207
 * @return unknown
208
 */
209
function cdm_taxontree_populate($tree, $expand_excluded, $filter_default = null){
210
  
211
  if(!is_array($tree)){
212
    return false;
213
  }
214
  foreach(array_keys($tree) as $uuid){
215
    
216
    if(!isset($tree[$uuid]->filter) && !is_null($filter_default)){
217
      $tree[$uuid]->filter = $filter_default;
218
    }
219
    
220
    if( $tree[$uuid]->expanded == 'expanded' && ($expand_excluded || $tree[$uuid]->filter != 'excluded')){
221
       $children = cdm_ws_get(CDM_WS_TREENODE_CHILDREN, $uuid);
222
       $children = _cdm_resultset2nodelist($children, ($tree[$uuid]->filter == 'excludes'));
223

    
224
       // store the children of the node for later processing
225
       if(is_array($tree[$uuid]->children)){
226
         $pnode_children = $tree[$uuid]->children;
227
       } else {
228
         $pnode_children = false;
229
       }
230
       // replace the children by the newly retrieved child nodes
231
       $tree[$uuid]->children = $children;
232
       
233
       if($pnode_children){         
234
         // recurse into the childtree which was stored before
235
         $pnode_children =  cdm_taxontree_populate($pnode_children, $expand_excluded ,$tree[$uuid]->filter);
236
         // recombine
237
         foreach($pnode_children as $childUuid=>$cnode){
238
           $tree[$uuid]->children[$childUuid] = $cnode;
239
         }
240
       }
241
    } else {
242
      // reorder nodes which are not expanded, expanded nodes are reordered implicitly above
243
      if(isset($tree[$uuid]->children) && count($tree[$uuid]->children) > 1) {
244
       // copy the children into an array which can be sorted by its keys
245
       $ordered = array();
246
       foreach($tree[$uuid]->children as $cnode){
247
         // concatenate full name and uid
248
         $reordered[str_pad($cnode->fullname, 255, '-').$cnode->uuid] = $cnode;
249
       }
250
       // sort
251
       ksort($reordered);
252
       // move the children back into the parent node
253
       $tree[$uuid]->children = array();
254
       foreach($reordered as $cnode){
255
         $tree[$uuid]->children[$cnode->uuid] = $cnode;
256
       }
257
      }
258
     $tree[$uuid]->children = cdm_taxontree_populate($tree[$uuid]->children, $expand_excluded, $tree[$uuid]->filter);
259
    }
260
  }
261
  return $tree;
262
}
263

    
264
function cdm_taxontree_flatten($tree, &$new_root = null){
265
  if(!$new_root){
266
    $new_root = array();
267
  }
268
  foreach($tree as $node){
269
    if($node->filter == 'on'){
270
      $new_root[$node->uuid] = $node;
271
    } else if(is_array($node->children)){
272
      cdm_taxontree_flatten($node->children, $new_root);      
273
    }
274
  }
275
  return $new_root;
276
}
277

    
278

    
279
/**
280
 * Merge a branch into a root tree
281
 *
282
 * @param unknown_type $tree
283
 * @param unknown_type $branch
284
 * @return the merged $tree
285
 */
286
function _cdm_taxontree_merge($tree, $branch) {
287
  
288
    if(!$branch){
289
      return $tree;
290
    }
291
      
292
    foreach(array_keys($tree) as $uuid) {       
293
        // check if node exists in $branch
294
        if(!empty($branch[$uuid])) {
295
            if(isset($tree[$uuid]->filter)){
296
               $branch[$uuid]->filter = $tree[$uuid]->filter;
297
            }
298
            if(isset($tree[$uuid]->expanded)){
299
               $branch[$uuid]->expanded = $tree[$uuid]->expanded;
300
            }
301
            // $Uuid exists check if the node in tree1 or tree2 contains children
302
            if(is_array($branch[$uuid]->children) && is_array($tree[$uuid]->children)) {
303
              // merge recursive
304
              $tree[$uuid]->children = _cdm_taxontree_merge($tree[$uuid]->children, $branch[$uuid]->children);
305
            } else if(is_array($branch[$uuid]->children)){
306
              $tree[$uuid] =  $branch[$uuid];
307
            }  
308
            unset($branch[$uuid]);
309
        } 
310
    }
311
    // append remaining items from branch to tree
312
    foreach(array_keys($branch) as $uuid){
313
      $tree[$uuid] =  $branch[$uuid];
314
    }
315
    return $tree;
316
}
317

    
318

    
319
/**
320
 * Replaces the keys of an array of TreeNode instances
321
 * by the $treeNode->uuid of the single array elements.
322
 * An sets additional fields
323
 *
324
 * @param $resultset array of TreeNode instances as +returned by the cdm web service
325
 * @param $excluded  whether the $resultset is included by a active filter. Is ignored if NULL.
326
 * @param $expanded  whether the children of the nodes in the $resultset are expanded or not. Is ignored if NULL.
327
 */
328
function _cdm_resultset2nodelist($resultset, $excluded = null, $expanded = null){
329

    
330
  if(! is_array($resultset)) {
331
    return false;
332
  }
333

    
334
  $tree = array();
335
  foreach($resultset as $treeNode){
336
    if(!is_null($excluded)){
337
      $treeNode->filter = ($excluded ? 'excluded': 'included');   
338
    }
339
   if(!is_null($expanded)){
340
      $treeNode->expanded = ($expanded ? 'expanded': 'collapsed');   
341
    }
342
    $tree[$treeNode->uuid] = $treeNode;
343
  }
344
  return $tree;
345
}
346

    
347

    
348
function theme_cdm_taxontree_block($tree){
349
    
350
  $out = '';
351
  if(cdm_dataportal_filters_active()){
352
        $out .= theme('cdm_taxontree_contoller', _get_compact_mode());
353
  }
354
  $out .= '<div class="cdm_taxontree_scroller_x"><div class="cdm_taxontree_container"><div class="cdm_taxontree_scroller_y"><div class="cdm_taxontree_border">'.theme('cdm_taxontree', $tree, !cdm_dataportal_filters_active(), TRUE, 'cdm_taxontree_node_concept_switch').'</div></div></div></div>';
355
  return $out;
356
}
357

    
358
function theme_cdm_taxontree_contoller($compact_mode){
359
 
360
  static $modes = array('expanded', 'compact', 'flattened');
361
  
362
  $out = '<div class="settings">';
363
  foreach($modes as $mode){
364
    if($compact_mode == $mode){
365
      $out .= t($mode);
366
    } else {
367
      $out .= l(t($mode), 'cdm_taxontree/set/compact_mode/'.$mode, array(), drupal_get_destination());
368
    }
369
    $out .= ' ';
370
  }
371
  
372
  return $out.'</div>';
373
}
374

    
375
function theme_cdm_taxontree($tree, $filterIncludes = null, $show_filter_switch = false, $tree_node_callback = false){
376
  
377
  if(!is_array($tree)) {
378
    return false;
379
  }
380
 
381
  if(is_null($filterIncludes)){
382
    // set $filterIncludes true if no filters are set.
383
    $filterIncludes = !cdm_dataportal_filters_active();
384
  }
385
  
386
  $out = '<ul class="cdm_taxontree">';
387
  foreach($tree as $node){
388
    $out .= theme('cdm_taxontree_node', $node, $filterIncludes, $show_filter_switch, $tree_node_callback);
389
  }
390
  $out .= '</ul>';
391
  return $out;
392
}
393

    
394
function theme_cdm_taxontree_node($node, $filterIncludes, $show_filter_switch = false, $tree_node_callback = false){
395

    
396
  $is_leaf = !$node->hasChildren || $node->hasChildren == 0;
397
  $is_expanded = isset($node->expanded) && $node->expanded = 'expanded';
398

    
399
  if($filterIncludes){
400
    $name = l(cdm_dataportal_shortname_of($node), cdm_dataportal_taxon_path($node->uuid));
401
    $filter_class = 'filter_included';
402
  } else {
403
     if($node->filter == 'on') {
404
      $name = l(cdm_dataportal_shortname_of($node), cdm_dataportal_taxon_path($node->uuid));
405
      $filter_class = 'filter_on';
406
    } else {
407
      $name .= cdm_dataportal_shortname_of($node);
408
      $filter_class = 'filter_excluded';
409
    }
410
  }
411
  $nextLevelIncluded = $node->filter == 'on' || $filterIncludes;
412

    
413
  $cdm_proxy_url = false;
414
  if(!$is_leaf && !$is_expanded && $filter_class != 'filter_excluded'){
415
    $ws_url = cdm_compose_url(CDM_WS_TREENODE_CHILDREN, array($node->uuid));
416
    $cdm_proxy_url = url('cdm_api/proxy/'.urlencode($ws_url).'/cdm_taxontree/'.($nextLevelIncluded ? 1 : 0).'/'.($show_filter_switch ? 1 : 0).'/'.$tree_node_callback);
417
  }
418

    
419
  // list item
420
  $out = '<li class="'
421
  .($node->focused ? 'focused ' : '')
422
  .($is_leaf ? 'leaf ':($is_expanded ?'expanded ':'collapsed '))
423
  .$filter_class.'"'
424
  .($cdm_proxy_url ? 'title="'.$cdm_proxy_url.'"' : '')
425
  .'>';
426
  
427
  if($show_filter_switch){
428
  // filter icon 
429
  $out .= theme('cdm_taxontree_node_filter_switch', $node, $filter_class);
430
  }
431
  
432
  // taxon name
433
  $out .= $name;
434
  
435
  // concept_switch or other theme callbacks
436
  if($tree_node_callback){
437
    $out .= theme($tree_node_callback, $node);
438
  }
439
  
440
  if(isset($node->children) && is_array($node->children)){
441
      $out .= theme('cdm_taxontree', $node->children, $nextLevelIncluded);
442
  }
443
  $out .= '</li>';
444
  
445
  return $out;
446
}
447

    
448
function theme_cdm_taxontree_node_filter_switch(&$node, $filter_class){
449
  $out = '';
450
  
451
  switch($filter_class){
452
    case 'filter_included':
453
      $filter_icon = 'visible_implicit_small.gif';
454
    break;
455
    case 'filter_excluded':
456
       $filter_icon = 'invisible_small.gif';
457
    break;
458
    case 'filter_on':
459
       $filter_icon = 'visible_small.gif';
460
    break;
461
  }
462
  
463
  $filter_op = $node->filter == 'on' ? 'remove' : 'add';
464

    
465
  $out .= '&nbsp;'
466
     .l('<img src="'.drupal_get_path('module', 'cdm_taxontree').'/'.$filter_icon.'" alt="[f]" />', 
467
    'cdm_dataportal/filter/'.$filter_op.'/'.$node->uuid, array('class'=>'filter_'.$filter_op), 
468
    'destination='.cdm_dataportal_taxon_path($node->uuid), 
469
     null, false, true);
470
     
471
  return $out;
472
}
473

    
474
function theme_cdm_taxontree_node_concept_switch(&$node){
475
  $out = '';
476
  
477
  if(isset($node->alternativeConceptRefs[0])){
478
    $out = l(
479
      '<img src="'.drupal_get_path('module', 'cdm_taxontree').'/concept_switch.gif" alt="[-&gt;]" />', 
480
      'cdm_dataportal/taxon/alternative/'.$node->uuid, 
481
      array('rel'=>'cdm_dataportal/taxon/alternative/'.$node->uuid, 'class'=>'concept_switch'), 
482
      null, null, false, true);
483
  }
484
  return $out;
485
}
486

    
487
function theme_cdm_taxontree_node_reference(&$node){
488
  
489
  $out = ' <span class="sec_ref widget_select" style="background-color:#'._uuid_to_rgbhex($node->secUuid).'" title="'.$node->secUuid.'">'.$node->secUuid.'</span>';
490
  return $out;
491
}
492

    
493
function cdm_taxontree_widget($default_uuid = false){
494
  
495
  drupal_add_js(drupal_get_path('module', 'cdm_taxontree').'/js/cdm_taxontree_widget.js');
496
  
497
  $out = '<div class="cdm_taxontree_widget">';
498
  $out .= '<div class="tree">'.theme('cdm_taxontree', cdm_taxontree_build_tree(), NULL, FALSE, 'cdm_taxontree_node_reference').'</div>';
499
  $out .= '<div class="selected_nodes"><label>'.t('Selected Nodes').'</label><ul class="listing"><li><input type="text" class="value_1232-stub" value="1232-stub" /></li></ul></div>';
500
  $out .= '</div>';
501
  return $out;
502
}
503

    
504

    
505
// --------------------------------------------------- //
506
 
507
function _uuid_to_rgbhex($uuid){
508
  
509
  $xfoot = _str_crossfoot($uuid);
510
  $h = $xfoot / 2 / 2;
511
  $h = $h - floor($h);
512
  $RGB = _hsv_2_rgb($h, 0.45, 1); 
513
  return dechex($RGB['R']).dechex($RGB['G']).dechex($RGB['B']);
514
}
515

    
516
function _str_crossfoot($str)
517
{
518
  $xfoot = 0;
519
  for($i=0; $i<strlen($str); $i++)
520
  {
521
    $xfoot = $xfoot + ord($str[$i]);
522
  }
523
  return $xfoot;
524
} 
525

    
526

    
527
function _cdm_get_taxonuuid(){
528

    
529
  //TODO make the path configurable
530
  if (arg(0)=="cdm_dataportal" && arg(1)=="taxon" && arg(2)!==0){
531
    $taxon_uuid = arg(2);
532
  } else {
533
    $taxon_uuid = $_SESSION['cdm_dataportal']['tree']['taxon_uuid'];
534
  }
535

    
536
  return $taxon_uuid;
537
}
538

    
539
function _hsv_2_rgb($H, $S, $V) // HSV Values:Number 0-1
540
{ // RGB Results:Number 0-255
541
  $RGB = array();
542
  
543
  if($S == 0)
544
  {
545
  $R = $G = $B = $V * 255;
546
  }
547
  else
548
  {
549
  $var_H = $H * 6;
550
  $var_i = floor( $var_H );
551
  $var_1 = $V * ( 1 - $S );
552
  $var_2 = $V * ( 1 - $S * ( $var_H - $var_i ) );
553
  $var_3 = $V * ( 1 - $S * (1 - ( $var_H - $var_i ) ) );
554
  
555
  if ($var_i == 0) { $var_R = $V ; $var_G = $var_3 ; $var_B = $var_1 ; }
556
  else if ($var_i == 1) { $var_R = $var_2 ; $var_G = $V ; $var_B = $var_1 ; }
557
  else if ($var_i == 2) { $var_R = $var_1 ; $var_G = $V ; $var_B = $var_3 ; }
558
  else if ($var_i == 3) { $var_R = $var_1 ; $var_G = $var_2 ; $var_B = $V ; }
559
  else if ($var_i == 4) { $var_R = $var_3 ; $var_G = $var_1 ; $var_B = $V ; }
560
  else { $var_R = $V ; $var_G = $var_1 ; $var_B = $var_2 ; }
561
  
562
  $R = $var_R * 255;
563
  $G = $var_G * 255;
564
  $B = $var_B * 255;
565
  }
566
  
567
  $RGB['R'] = $R;
568
  $RGB['G'] = $G;
569
  $RGB['B'] = $B;
570
  
571
  return $RGB;
572
}
(3-3/14)