Project

General

Profile

Download (13.3 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').'/cdm_taxontree.js');
57
        return $block;
58
    }
59
  }
60
}
61

    
62
function cdm_taxontree_set($key, $value){
63
  
64
  if(is_string($key)){
65
    $_SESSION['cdm']['taxontree'][$key] = $value; 
66
  }
67
  
68
  if($_REQUEST['destination']){
69
    $destination = $_REQUEST['destination'];
70
    unset($_REQUEST['destination']);
71
    drupal_goto($destination);
72
  }
73
}
74

    
75
function _is_compact_mode(){
76
  return isset($_SESSION['cdm']['taxontree']['compact']) && $_SESSION['cdm']['taxontree']['compact'];
77
}
78

    
79
/**
80
 * builds a tree of TaxonNode instances, whereas the instances are extended by some fields:
81
 * 
82
 *  - $node->filter: values ( 'on', 'excluded', 'included' ) 
83
 *  - $node->expanded: values ( 'expanded', 'collapsed' ) 
84
 *    $node->focused: values ( TRUE, FALSE ) 
85
 *
86
 * @param unknown_type $taxonUuid
87
 * @return unknown
88
 */
89
function cdm_taxontree_build_tree($taxonUuid){
90

    
91
  // find the secRefUuid
92
  $secRefUuid = null;
93
  if($taxonUuid){
94
    //TODO poor performance here:
95
    $taxon =  cdm_ws_get(CDM_WS_TAXON, $taxonUuid);
96
    $secRefUuid = $taxon->sec->uuid;
97
  }
98

    
99
  if(!$secRefUuid){
100
    $secRef = _cdm_dataportal_currentSecRef_array();
101
    $secRefUuid = $secRef['uuid'];
102
  }
103

    
104
  $compact_tree = (cdm_dataportal_filters_active() && _is_compact_mode() ? 'hidden' : false);
105
  
106
  // get the root level
107
  $root_tree = cdm_ws_get(CDM_WS_TREENODE_ROOT, $secRefUuid);
108
  $root_tree = _cdm_resultset2nodelist($root_tree, cdm_dataportal_filters_active());
109

    
110
  if(cdm_dataportal_filters_active()){
111
    // the paths up to active filters are inactive in the user interface and
112
    // thus cannot be browsed by expanding nodes
113
    // therefore we need to build up the branches for all nodes which are set as filters
114
    // the branches are merged with the root
115
    foreach(cdm_dataportal_filters_get() as $uuid=>$filter){
116
      $branch = cdm_taxontree_build_path($uuid, TRUE, $compact_tree === false);
117
      $root_tree = _cdm_taxontree_merge($root_tree, $branch);
118
    }
119
  }
120
  
121
  // build the the branch for the focused node and merge it with the root
122
  if($taxonUuid){
123
    $branch = cdm_taxontree_build_path($taxonUuid, NULL, (cdm_dataportal_filters_active() ? NULL : TRUE), TRUE);
124
    $root_tree = _cdm_taxontree_merge($root_tree, $branch);
125
  }
126
  
127
  //reorder siblings & populate expanded nodes with children and propagate the filter attribute
128
  $root_tree = cdm_taxontree_populate($root_tree);
129
  
130
  // flatten tree 
131
  /*
132
  if($compact_tree == 'flat'){
133
    $root_tree = cdm_taxontree_flatten($root_tree);
134
  }
135
  */
136
  
137
  return $root_tree;
138
}
139

    
140
/**
141
 * Builds the specific branch path for $taxonUuid. 
142
 * The branch path reaches from the parent root node of $taxonUuid up to $taxonUuid.
143
 *
144
 * @param unknown_type $taxonUuid
145
 * @param $is_filter_path whether the upmost node of this path is mapped by an active filter 
146
 * @param $is_expanded whether all nodes along the tree are expanded 
147
 * @return a subtree
148
 */
149
function cdm_taxontree_build_path($taxonUuid, $is_filter_path = null, $is_expanded = null, $is_focused = FALSE){
150
  
151
  $branch_path = array();
152
  $parents = cdm_ws_get(CDM_WS_TREENODE_PARENTS, $taxonUuid);
153
  //TODO improve performance by changing the web service and thus making the next line below obsolete:
154
  $parents = array_reverse($parents);
155
  $parents = _cdm_resultset2nodelist($parents, NULL);
156
  $lastParent = null;
157
  foreach($parents as $pnode){
158
    $pnode->focused = false;
159
    if($lastParent){
160
      $pnode->children = array($lastParent->uuid => $lastParent);
161
      if(!is_null($is_filter_path)){
162
        $pnode->filter = ($is_filter_path ? 'excludes' : 'included');
163
      }
164
      if(!is_null($is_expanded)){
165
        $pnode->expanded = ($is_expanded ? 'expanded' : 'collapsed');
166
      }
167
    } else {
168
      // the uppermost node of branch
169
      if(!is_null($is_filter_path)){
170
        $pnode->filter = ($is_filter_path ? 'on' : 'includes');
171
      }
172
      // uppermost node is always expanded if it has children
173
      $pnode->focused = $is_focused;
174
      $pnode->expanded = ($pnode->hasChildren ? 'expanded' : 'collapsed');
175
    }
176
    $lastParent = $pnode;
177
  }
178
  $branch_path[$pnode->uuid] = $pnode;
179
  return $branch_path;
180
}
181

    
182
/**
183
 * Performs two steps on each level of the tree:
184
 *  1. reorder siblings except root (is expected to be ordered jet) alphabetically 
185
 *  2. populate children of expanded nodes  & propagate the filter attribute
186
 *
187
 * @param unknown_type $tree
188
 * @return unknown
189
 */
190
function cdm_taxontree_populate($tree, $filter_default = null){
191
  
192
  foreach(array_keys($tree) as $uuid){
193
    
194
    if(!isset($tree[$uuid]->filter) && !is_null($filter_default)){
195
      $tree[$uuid]->filter = $filter_default;
196
    }
197
    
198
    if( $tree[$uuid]->expanded == 'expanded' && $tree[$uuid]->filter != 'excluded'){
199
       $children = cdm_ws_get(CDM_WS_TREENODE_CHILDREN, $uuid);
200
       $children = _cdm_resultset2nodelist($children, ($tree[$uuid]->filter == 'excludes'));
201

    
202
       // store the children of the node for later processing
203
       if(isset($tree[$uuid]->children)){
204
         $pnode_children = $tree[$uuid]->children;
205
       } else {
206
         $pnode_children = false;
207
       }
208
       // replace the children by the newly retrieved child nodes
209
       $tree[$uuid]->children = $children;
210
       
211
       if($pnode_children){         
212
         // recurse into the childtree which was stored before
213
         $pnode_children =  cdm_taxontree_populate($pnode_children, $tree[$uuid]->filter);
214
         // recombine
215
         foreach($pnode_children as $childUuid=>$cnode){
216
           $tree[$uuid]->children[$childUuid] = $cnode;
217
         }
218
       }
219
    } else {
220
      // reorder nodes which are not expanded, expanded nodes are reordered implicitly above
221
      if(isset($tree[$uuid]->children) && count($tree[$uuid]->children) > 1) {
222
       // copy the children into an array which can be sorted by its keys
223
       $ordered = array();
224
       foreach($tree[$uuid]->children as $cnode){
225
         // concatenate full name and uid
226
         $reordered[str_pad($cnode->fullname, 255, '-').$cnode->uuid] = $cnode;
227
       }
228
       // sort
229
       ksort($reordered);
230
       // move the children back into the parent node
231
       $tree[$uuid]->children = array();
232
       foreach($reordered as $cnode){
233
         $tree[$uuid]->children[$cnode->uuid] = $cnode;
234
       }
235
      }
236
     $tree[$uuid]->children =  cdm_taxontree_populate($tree[$uuid]->children, $tree[$uuid]->filter);
237
    }
238
  }
239
  return $tree;
240
}
241

    
242
/**
243
 * Merge a branch into a root tree
244
 *
245
 * @param unknown_type $tree
246
 * @param unknown_type $branch
247
 * @return the merged $tree
248
 */
249
function _cdm_taxontree_merge($tree, $branch) {
250
      
251
    foreach(array_keys($tree) as $uuid) {       
252
        // check if node exists in $branch
253
        if(!empty($branch[$uuid])) {
254
            if(isset($tree[$uuid]->filter)){
255
               $branch[$uuid]->filter = $tree[$uuid]->filter;
256
            }
257
            if(isset($tree[$uuid]->expanded)){
258
               $branch[$uuid]->expanded = $tree[$uuid]->expanded;
259
            }
260
            // $Uuid exists check if the node in tree1 or tree2 contains children
261
            if(is_array($branch[$uuid]->children) && is_array($tree[$uuid]->children)) {
262
              // merge recursive
263
              $tree[$uuid]->children = _cdm_taxontree_merge($tree[$uuid]->children, $branch[$uuid]->children);
264
            } else if(is_array($branch[$uuid]->children)){
265
              $tree[$uuid] =  $branch[$uuid];
266
            }  
267
            unset($branch[$uuid]);
268
        } 
269
    }
270
    // append remaining items from branch to tree
271
    foreach(array_keys($branch) as $uuid){
272
      $tree[$uuid] =  $branch[$uuid];
273
    }
274
    return $tree;
275
}
276

    
277
function theme_cdm_taxontree_block($tree){
278
    
279
  $out = '';
280
  if(cdm_dataportal_filters_active()){
281
        $out = '<div class="settings">'.l((_is_compact_mode()? t('expand tree'):t('compact tree')), 'cdm_taxontree/set/compact/'.(_is_compact_mode() ? 0:1), array(), drupal_get_destination()).'</div>';
282
  }
283
  $out .= theme('cdm_taxontree', $tree, !cdm_dataportal_filters_active());
284
  return $out;
285
}
286

    
287
function theme_cdm_taxontree($tree, $filterIncludes = null){
288
  
289
  if(!is_array($tree)) {
290
    return false;
291
  }
292
 
293
  if(is_null($filterIncludes)){
294
    // set $filterIncludes true if no filters are set.
295
    $filterIncludes = !cdm_dataportal_filters_active();
296
  }
297
  
298
  $out = '<ul class="cdm_taxontree">';
299
  foreach($tree as $node){
300
    $out .= theme('cdm_taxontree_node', $node, $filterIncludes);
301
  }
302
  $out .= '</ul>';
303
  return $out;
304
}
305

    
306
function theme_cdm_taxontree_node($node, $filterIncludes){
307
  //TODO clean up function
308
  $is_leaf = !$node->hasChildren || $node->hasChildren == 0;
309
  
310
  $is_expanded = isset($node->expanded) && $node->expanded = 'expanded';
311

    
312

    
313
  if($filterIncludes){
314
    $name = l(cdm_dataportal_shortname_of($node), cdm_dataportal_taxon_path($node->uuid));
315
    $filter_icon = 'visible_implicit_small.gif';
316
    $filter_class = 'filter_included';
317
  } else {
318
     if($node->filter == 'on') {
319
      $name = l(cdm_dataportal_shortname_of($node), cdm_dataportal_taxon_path($node->uuid));
320
      $filter_icon = 'visible_small.gif';
321
      $filter_class = 'filter_on';
322
    } else {
323
      $filter_icon = 'invisible_small.gif';
324
      $name .= cdm_dataportal_shortname_of($node);
325
      $filter_class = 'filter_excluded';
326
    }
327
  }
328
  $nextLevelIncluded = $node->filter == 'on' || $filterIncludes;
329

    
330
  $cdm_proxy_url = false;
331
  if(!$is_leaf && !$is_expanded && $filter_class != 'filter_excluded'){
332
    $ws_url = cdm_compose_url(CDM_WS_TREENODE_CHILDREN, array($node->uuid));
333
    $cdm_proxy_url = url('cdm_api/proxy/'.urlencode($ws_url).'/cdm_taxontree/'.($nextLevelIncluded ? 1 : 0));
334
  }
335

    
336
  $filter_op = $node->filter == 'on' ? 'remove' : 'add';
337
  
338
  // list item
339
  $out = '<li class="'
340
  .($node->focused ? 'focused ' : '')
341
  .($is_leaf ? 'leaf ':($is_expanded ?'expanded ':'collapsed '))
342
  .$filter_class.'"'
343
  .($cdm_proxy_url ? 'title="'.$cdm_proxy_url.'"' : '')
344
  .'>';
345
  
346
  // filter icon 
347
  $out .= '&nbsp;'
348
     .l('<img src="'.drupal_get_path('module', 'cdm_taxontree').'/'.$filter_icon.'" alt="[f]" />', 
349
    'cdm_dataportal/filter/'.$filter_op.'/'.$node->uuid, array('class'=>'filter_'.$filter_op), 
350
    'destination='.cdm_dataportal_taxon_path($node->uuid), 
351
     null, false, true);
352
  
353
  // taxon name
354
  $out .= $name;
355
  
356
  // concept_switch
357
  if(isset($node->alternativeConceptRefs[0])){
358
    $out .= l(
359
      '<img src="'.drupal_get_path('module', 'cdm_taxontree').'/concept_switch.gif" alt="[-&gt;]" />', 
360
      'cdm_dataportal/taxon/alternative/'.$node->uuid, 
361
      array('rel'=>'cdm_dataportal/taxon/alternative/'.$node->uuid, 'class'=>'concept_switch'), 
362
      null, null, false, true);
363
  }
364
  
365
  if(isset($node->children) && is_array($node->children)){
366
      $out .= theme('cdm_taxontree', $node->children, $nextLevelIncluded);
367
  }
368
  $out .= '</li>';
369
  
370
  return $out;
371
}
372

    
373
/**
374
 * Replaces the keys of an array of TreeNode instances
375
 * by the $treeNode->uuid of the single array elements.
376
 * An sets additional fields
377
 *
378
 * @param $resultset array of TreeNode instances as +returned by the cdm web service
379
 * @param $excluded  whether the $resultset is included by a active filter. Is ignored if NULL.
380
 * @param $expanded  whether the children of the nodes in the $resultset are expanded or not. Is ignored if NULL.
381
 */
382
function _cdm_resultset2nodelist($resultset, $excluded = null, $expanded = null){
383

    
384
  if(! is_array($resultset)) {
385
    return false;
386
  }
387

    
388
  $tree = array();
389
  foreach($resultset as $treeNode){
390
    if(!is_null($excluded)){
391
      $treeNode->filter = ($excluded ? 'excluded': 'included');   
392
    }
393
   if(!is_null($expanded)){
394
      $treeNode->expanded = ($expanded ? 'expanded': 'collapsed');   
395
    }
396
    $tree[$treeNode->uuid] = $treeNode;
397
  }
398
  return $tree;
399
}
400

    
401

    
402
function _cdm_get_taxonuuid(){
403

    
404
  //TODO make the path configurable
405
  if (arg(0)=="cdm_dataportal" && arg(1)=="taxon" && arg(2)!==0){
406
    $taxon_uuid = arg(2);
407
  } else {
408
    $taxon_uuid = $_SESSION['cdm_dataportal']['tree']['taxon_uuid'];
409
  }
410

    
411
  return $taxon_uuid;
412
}
(4-4/14)