Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

cdm-dataportal / modules / cdm_dataportal / includes / taxon.inc @ 146fef89

History | View | Annotate | Download (18.8 KB)

1
<?php
2

    
3
/**
4
 * @file
5
 * Functions for dealing with CDM entities from the package model.taxon
6
 *
7
 * @copyright
8
 *   (C) 2007-2016 EDIT
9
 *   European Distributed Institute of Taxonomy
10
 *   http://www.e-taxonomy.eu
11
 *
12
 *   The contents of this module are subject to the Mozilla
13
 *   Public License Version 1.1.
14
 * @see http://www.mozilla.org/MPL/MPL-1.1.html
15
 *
16
 * @author
17
 *   - Andreas Kohlbecker <a.kohlbecker@BGBM.org>
18
 */
19

    
20
/**
21
 * @defgroup compose Compose functions
22
 * @{
23
 * Functions which are composing Drupal render arrays
24
 *
25
 * The cdm_dataportal module needs to compose rather complex render arrays from
26
 * the data returned by the CDM REST service. The compose functions are
27
 * responsible for creating the render arrays.
28
 *
29
 * All these functions are also implementations of the compose_hook()
30
 * which is used in the proxy_content() function.
31
 * @}
32
 */
33

    
34

    
35
/**
36
 * Returns HTML for misapplied names and invalid designations.
37
 *
38
 * Both relation types are currently treated the same!
39
 *
40
 * @param taxonRelationships
41
 * @param focusedTaxon
42
 *
43
 * @return string
44
 *    the rendered html
45
 */
46
function cdm_taxonRelationships($taxonRelationships, $focusedTaxon){
47

    
48
  static $NULL_AUTHORTEAM = 'NULL_AUTHORTEAM';
49

    
50
  if (!$taxonRelationships) {
51
    return null;
52
  }
53

    
54
  RenderHints::pushToRenderStack('taxon_relationships');
55
  $footnoteListKey = 'taxon_relationships';
56
  RenderHints::setFootnoteListKey($footnoteListKey);
57

    
58
  $misapplied = array();
59
  $joinedAuthorTeams = array();
60

    
61
  $taxon_relationship_types = variable_get(CDM_TAXON_RELATIONSHIP_TYPES, unserialize(CDM_TAXON_RELATIONSHIP_TYPES_DEFAULT));
62

    
63
  // Aggregate misapplied names having the same fullname:
64
  foreach ($taxonRelationships as $taxonRelation) {
65

    
66
    if (in_array($taxonRelation->type->uuid, $taxon_relationship_types)) {
67

    
68

    
69
      if ($taxonRelation->type->uuid == UUID_MISAPPLIED_NAME_FOR || $taxonRelation->type->uuid == UUID_INVALID_DESIGNATION_FOR) {
70

    
71
        RenderHints::pushToRenderStack('misapplied_name_for'); // TODO the render path string should in future come from $taxonRelation->type->...
72

    
73
        $name = $taxonRelation->fromTaxon->name->titleCache;
74

    
75
        $authorteam = $NULL_AUTHORTEAM;
76
        if(isset($taxonRelation->fromTaxon->sec)) {
77
          // taxa not always are have a sec reference (e.g. doubtful taxa)
78
          $authorteam = cdm_ws_get(CDM_WS_REFERENCE_AUTHORTEAM, $taxonRelation->fromTaxon->sec->uuid);
79
          $authorteam = $authorteam->titleCache;
80
        }
81

    
82
        if (!isset($misapplied[$name])) {
83
          // Render the first name found as representative for all others.
84
          $misapplied[$name]['out'] = cdm_related_taxon($taxonRelation->fromTaxon, UUID_MISAPPLIED_NAME_FOR);
85
        }
86
        else {
87
          // We need to add the anchors for all of the other misapplied names not
88
          // being rendered explicitly.
89
          $misapplied[$name]['out'] = uuid_anchor($taxonRelation->fromTaxon->uuid, $misapplied[$name]['out']);
90
        }
91

    
92
        // Collect all authors for this fullname.
93
        if (isset($authorteam) && $authorteam != $NULL_AUTHORTEAM) {
94
          $misapplied[$name]['authorteam'][$authorteam] = '';
95
          $joinedAuthorTeams[$authorteam] = 'sensu ' . theme('cdm_reference', array('reference' => $taxonRelation->fromTaxon->sec));
96
        }
97

    
98
      }
99
      else {
100
        RenderHints::pushToRenderStack('other_taxon_relationship');
101
        // All relationsship types but misapplied_name_for
102
        // invalid_designation_for.
103
        $taxon_relationships_lines[] = cdm_taxonRelationship($taxonRelation, TRUE, _is_invers_taxonRelationship($taxonRelation, $focusedTaxon));
104
      }
105

    
106
      RenderHints::popFromRenderStack();
107
    }
108
  }
109

    
110
  // Sort the joinedAuthorTeams and create footnotes and footnotekeys.
111
  ksort($joinedAuthorTeams);
112
  $author_team_cnt = 0;
113
  foreach ($joinedAuthorTeams as $authorteam => $sensuCitation) {
114
    $footnoteKey = FootnoteManager::addNewFootnote($footnoteListKey, $sensuCitation);
115

    
116
    $sensu = ++$author_team_cnt == 0 ? '' : 'sensu ';
117

    
118
    $joinedAuthorTeams[$authorteam] =  sprintf(
119
        '<span class="sensu">%s%s%s</span>',
120
        $sensu,
121
        ($authorteam != $NULL_AUTHORTEAM ? $authorteam : ''),
122
        theme('cdm_footnote_key', array('footnoteKey' => $footnoteKey))
123
      );
124
  }
125

    
126
  // ---- Generate output ---- //
127

    
128
  $out = '<div class="taxon-relationships">';
129
  if (is_array($misapplied) && count($misapplied) > 0) {
130
    $out .= '<ul class="misapplied">';
131
    foreach ($misapplied as $misapplied_name) {
132

    
133
      $out .= '<li class="synonym"><span class="misapplied">' . $misapplied_name['out'] . ' </span>';
134

    
135
      if (isset($misapplied_name['authorteam'])) {
136
        // Fill authors with the renderedFootnoteKey and sorting 'em.
137
        foreach ($misapplied_name['authorteam'] as $authorteam => &$renderedFootnoteKey) {
138
          $renderedFootnoteKey = $joinedAuthorTeams[$authorteam];
139
        }
140
        ksort($misapplied_name['authorteam']);
141
        $out .= join('; ', $misapplied_name['authorteam']);
142
      }
143
      $out .= '</li>';
144
    }
145
    $out .= '</ul>';
146
  }
147

    
148
  if (isset($taxon_relationships_lines) && is_array($taxon_relationships_lines) && count($taxon_relationships_lines) > 0) {
149
    $out .= '<ul class="taxonRelationships">';
150
    foreach ($taxon_relationships_lines as $taxon_relationship_line) {
151
      $out .= '<li class="synonym">' . $taxon_relationship_line . '</li>';
152
    }
153
    $out .= '</ul>';
154
  }
155

    
156
  $footnotes = theme('cdm_footnotes', array('footnoteListKey' => $footnoteListKey, 'enclosingTag' => 'li'));
157
  $footnotes .= theme('cdm_annotation_footnotes', array('footnoteListKey' => $footnoteListKey, 'enclosingTag' => 'li'));
158

    
159
// AK: why splitting footnotes at the sensu string ??? this is weired and hacky
160
//     TODO remove below dead code
161
//   $tr_footnotes_exploded = explode('sensu', $tr_footnotes);
162
//   $tr_footnotes_aux = '';
163
//   foreach ($tr_footnotes_exploded as $element) {
164
//     $tr_footnotes_aux .= $element;
165
//   }
166

    
167
  $out .= '<ul class="footnotes">' . $footnotes . '</ul>';
168

    
169
  $out .= '</div>';
170

    
171
  RenderHints::popFromRenderStack();
172
  return $out;
173
}
174

    
175

    
176
/**
177
 * Renders a representation of the given taxon relationship.
178
 *
179
 * According name relationships are also being rendered.
180
 *
181
 * @param unknown_type $taxonRelationship
182
 * @param boolean $doLinkTaxon
183
 *     whether to create a link to the related taxon
184
 * @param boolean $inverse
185
 *     whether the $taxonRelationship should be treaded as invers relation
186
 *
187
 * @return void|string
188
 */
189
function cdm_taxonRelationship($taxonRelationship, $doLinkTaxon = FALSE, $inverse = FALSE) {
190

    
191
  // Validate object.
192
  if (!(isset($taxonRelationship->toTaxon) && isset($taxonRelationship->fromTaxon) && isset($taxonRelationship->type))) {
193
    return null;
194
  }
195

    
196
  $taxonRelationType = $taxonRelationship->type;
197

    
198
  if ($inverse) {
199
    $toTaxon = $taxonRelationship->fromTaxon;
200
    $relsign = $taxonRelationType->inverseRepresentation_L10n_abbreviatedLabel;
201
    $reltype_representation = $taxonRelationType->inverseRepresentation_L10n;
202
  }
203
  else {
204
    $toTaxon = $taxonRelationship->toTaxon;
205
    $relsign = $taxonRelationType->representation_L10n_abbreviatedLabel;
206
    $reltype_representation = $taxonRelationType->representation_L10n;
207
  }
208

    
209
  return cdm_related_taxon($toTaxon, NULL, $relsign, $reltype_representation, $taxonRelationship->doubtful, $doLinkTaxon);
210
}
211

    
212
/**
213
 * Renders a representation of the given taxon relationship.
214
 *
215
 * According name relationships are also being rendered.
216
 *
217
 * @param $taxon
218
 *  The CDM TaxonBase entity
219
 * @param $reltype_uuid
220
 *  The UUID of the TaxonRelationshipType
221
 * @param $relsign
222
 *  Optional. Can be  used to override the internal decision strategy on finding a suitable icon for the relationship
223
 * @param $reltype_representation
224
 *   Optional: Defines the value for the title attribute of the html element enclosing the relsign
225
 * @param $doubtful
226
 *   TODO
227
 * @param $doLinkTaxon
228
 *   The taxon will be rendered as clickable link when true.
229
 *
230
 * @return string
231
 *   Markup for the taxon relationship.
232
 */
233
function cdm_related_taxon($taxon, $reltype_uuid = NULL, $relsign = NULL, $reltype_representation = NULL, $doubtful=false, $doLinkTaxon = FALSE) {
234
  static $relsign_homo = '≡';
235
  static $relsign_hetero = '=';
236
  static $relsign_invalid = '&ndash;';
237
  static $nom_status_invalid_type_uuids =  array(
238
    UUID_NOMENCLATURALSTATUS_TYPE_INVALID,
239
    UUID_NOMENCLATURALSTATUS_TYPE_NUDUM,
240
    UUID_NOMENCLATURALSTATUS_TYPE_COMBINATIONINVALID,
241
    UUID_NOMENCLATURALSTATUS_TYPE_PROVISIONAL
242
  );
243

    
244
  // 'taxonRelationships';
245
  $footnoteListKey = NULL;
246

    
247
  $skip_tags = array();
248

    
249
  $is_invalid = false;
250

    
251
  if (!$relsign) {
252

    
253
    switch ($reltype_uuid) {
254
      case UUID_HETEROTYPIC_SYNONYM_OF:
255
      case UUID_SYNONYM_OF:
256
        $relsign = $relsign_hetero;
257
        break;
258

    
259
      case UUID_HOMOTYPIC_SYNONYM_OF:
260
        $relsign = $relsign_homo;
261
        break;
262

    
263
      case UUID_MISAPPLIED_NAME_FOR:
264
      case UUID_INVALID_DESIGNATION_FOR:
265
        $skip_tags[] = 'authors';
266
        $is_invalid = true;
267
        $relsign = $relsign_invalid;
268

    
269
        break;
270

    
271
      default:
272
        $relsign = $relsign_invalid;
273
    }
274

    
275
  }
276

    
277
  if($doubtful) {
278
    $relsign = '?' . $relsign;
279
  }
280

    
281
  /*
282
  Names with status invalid or nudum are to be displayed with the
283
  $relsign_invalid, these names appear at the end of all names in their
284
  homotypic group (ordered correctly by the java cdm_lib).
285
  */
286
  if (isset($taxon->name->status) && is_array($taxon->name->status)) {
287
    foreach ($taxon->name->status as $status) {
288
      if (in_array($status->type->uuid , $nom_status_invalid_type_uuids)) {
289
        $relsign = $relsign_invalid;
290
        break;
291
      }
292
    }
293
  }
294

    
295
  // Now rendering starts ..
296
  RenderHints::pushToRenderStack('related_taxon');
297

    
298
  if (isset($taxon->name->nomenclaturalReference)) {
299
    $referenceUri = url(path_to_reference($taxon->name->nomenclaturalReference->uuid));
300
  }
301
  $taxonUri = '';
302
  if ($doLinkTaxon) {
303
    $taxonUri = url(path_to_taxon($taxon->uuid, "synonymy"));
304
  }
305
  // Printing the taxonName and the handling the special case of annotations.
306
  if (!isset($referenceUri)) {
307
    $referenceUri = FALSE;
308
  }
309
  $out_taxon_part = render_taxon_or_name($taxon, $taxonUri, $referenceUri, TRUE, FALSE, $skip_tags, $is_invalid);
310
  $taxon_footnotes = theme('cdm_annotations_as_footnotekeys',
311
    array('cdmBase_list' => array(
312
      $taxon->name,
313
      $taxon,
314
    ),
315
      'footnote_list_key' => $footnoteListKey)
316
  );
317

    
318
  $homonyms = cdm_name_relationships_of($taxon);
319

    
320
  $out = '<span class="relation_sign" title="' . $reltype_representation . '">' . $relsign . '</span>'
321
    . $out_taxon_part . $taxon_footnotes . ' '  . $homonyms;
322

    
323
  $out = uuid_anchor($taxon->uuid, $out);
324

    
325
  RenderHints::popFromRenderStack();
326

    
327
  return $out;
328
}
329

    
330

    
331
/**
332
 * Creates markup for a taxon which is the accepted of another one
333
 *
334
 * @param $accepted_for_uuid
335
 *   The uuid of the accepted taxon
336
 */
337
function cdm_accepted_for($accepted_for_uuid) {
338

    
339
  if(!is_uuid($accepted_for_uuid)){
340
    return '';
341
  }
342

    
343
  RenderHints::pushToRenderStack('acceptedFor');
344
  $out = '';
345

    
346
  $synonym = cdm_ws_get(CDM_WS_PORTAL_TAXON, $accepted_for_uuid);
347
  if ($synonym) {
348
    $out .= '<span class="acceptedFor">';
349
    $out .= t('is accepted for ');
350
    if (isset($synonym->name->nomenclaturalReference)) {
351
      $referenceUri = url(path_to_reference($synonym->name->nomenclaturalReference->uuid));
352
    }
353
    $out .= render_taxon_or_name($synonym->name, NULL, $referenceUri);
354
    $out .= theme('cdm_annotations_as_footnotekeys', array('cdmBase_list' => $synonym));
355
    $out .= '</span>';
356
  }
357
  RenderHints::popFromRenderStack();
358
  return $out;
359
}
360

    
361
/**
362
 * Compose function for a list of taxa.
363
 *
364
 * This function is for example used toi display search results or the taxa for a taxon name in the name page.
365
 *
366
 * @param $taxon_list array
367
 *   The list of CDM Taxon entities. e.g. The records array as contained in a pager object.
368
 * @param $freetext_search_results array
369
 * @param $show_classification boolean
370
 *
371
 * @ingroup compose
372
 *
373
 */
374
function compose_list_of_taxa($taxon_list, $freetext_search_results = array(), $show_classification = false) {
375

    
376
  $unclassified_snippet = '<span class="unclassified">' . t('unclassified') . '</span>';
377

    
378
  RenderHints::pushToRenderStack('list_of_taxa');
379

    
380
  $gallery_settings = getGallerySettings(CDM_DATAPORTAL_SEARCH_GALLERY_NAME);
381

    
382
  $showMedia_taxa = $gallery_settings['cdm_dataportal_show_taxon_thumbnails'];
383
  $showMedia_synonyms = $gallery_settings['cdm_dataportal_show_synonym_thumbnails'];
384
  $searched_in_classification = cdm_dataportal_searched_in_classification();
385
  $searched_in_classification_uuid = null;
386
  if(isset($searched_in_classification->uuid)){
387
    $searched_in_classification_uuid = $searched_in_classification->uuid;
388
  }
389

    
390
  // .. Well, for sure not as performant as before, but better than nothing.
391
  $synonym_uuids = array();
392
  $misappied_uuids = array();
393
  foreach ($taxon_list as $taxon) {
394
    if ($taxon->class == "Synonym") {
395
      if (!array_key_exists($taxon->uuid, $synonym_uuids)) {
396
        $synonym_uuids[$taxon->uuid] = $taxon->uuid;
397
      }
398
    }
399
    elseif (!_cdm_dataportal_acceptedByCurrentView($taxon)) {
400
      // Assuming that it is a misappied name, will be further examined below.
401
      $misappied_uuids[$taxon->uuid] = $taxon->uuid;
402
    }
403
  }
404

    
405
  // Batch service not jet implemented:
406
  // $table_of_accepted = cdm_ws_property(CDM_WS_PORTAL_TAXON_ACCEPTED,
407
  // join(',', $synonym_uuids));
408
  // thus ...
409
  $table_of_accepted = array();
410

    
411
  foreach ($synonym_uuids as $relatedUuid) {
412
    $table_of_accepted[$relatedUuid] = cdm_ws_get(CDM_WS_PORTAL_TAXON_ACCEPTED, array(
413
      $relatedUuid,
414
      $searched_in_classification_uuid,
415
    ));
416
  }
417

    
418
  foreach ($misappied_uuids as $relatedUuid) {
419
    $taxonRelations = cdm_ws_get(CDM_WS_PORTAL_TAXON_RELATIONS, array(
420
      $relatedUuid,
421
    ));
422
    foreach ($taxonRelations as $relation) {
423
      if ($relation->type->uuid == UUID_MISAPPLIED_NAME_FOR && _cdm_dataportal_acceptedByCurrentView($relation->toTaxon)) {
424
        $table_of_accepted[$relatedUuid][] = $relation->toTaxon;
425
      }
426
    }
427
  }
428

    
429
  $out = '<ul class="cdm_names" style="background-image: none;">';
430
  $itemCnt = -1;
431
  foreach ($taxon_list as $taxon) {
432
    $itemCnt++;
433
    if (isset($table_of_accepted[$taxon->uuid])) {
434
      // Its a synonym or misapplied name.
435
      $is_synonym = isset($synonym_uuids[$taxon->uuid]); //TODO better use the $taxon->class attribute?
436
      $taxon_type = $is_synonym ? "Synonym" : "misapplied-name";
437

    
438
      $acceptedTaxa = $table_of_accepted[$taxon->uuid];
439

    
440
      if (count($acceptedTaxa) == 1) {
441

    
442
        $acceptedTaxon = $acceptedTaxa[0];
443
        $taxonUri = uri_to_synonym($taxon->uuid, $acceptedTaxon->uuid, 'synonymy');
444
        $referenceUri = '';
445
        if (isset($acceptedTaxon->name->nomenclaturalReference)) {
446
          $referenceUri = url(path_to_reference($acceptedTaxon->name->nomenclaturalReference->uuid));
447
        }
448
        $taxon_or_name = $is_synonym ? $taxon->name : $taxon;
449
        // $taxon_or_name this is a trick to suppress the sec reference for sysnonyms
450
        // supplying the name will cause render_taxon_or_name() to not show the sec reference
451
        $out .= '<li class="' . $taxon_type . '">' . render_taxon_or_name($taxon_or_name, $taxonUri, $referenceUri);
452
        if ($show_classification) {
453
          $classifications = get_classifications_for_taxon($taxon);
454
          $classification_titles = array();
455
          foreach ($classifications as $classification) {
456
            if (isset($classification->titleCache)) {
457
              $classification_titles[] = $classification->titleCache;
458
            }
459
          }
460
          if(count($classification_titles) == 0){
461
            $classification_titles[] = $unclassified_snippet;
462
          }
463
          $out .= ' : <span class="classifications">' . implode(', ', $classification_titles) . '</span>';
464
        }
465
        $out .= theme('cdm_annotations_as_footnotekeys', array('cdmBase_list' => $taxon));
466
        if ($showMedia_synonyms) {
467
          $out .= theme('cdm_taxon_list_thumbnails', array('taxon' => $acceptedTaxon));
468
        }
469
      }
470
      else {
471

    
472
        // TODO avoid using Ajax in the cdm_dynabox .... why?
473
        // TODO add media.
474
        $out .= cdm_dynabox(
475
          $taxon->uuid,
476
          render_taxon_or_name($taxon->name, NULL, NULL, FALSE),
477
          cdm_compose_url(CDM_WS_PORTAL_TAXON_ACCEPTED,
478
            array(
479
              $taxon->uuid,
480
              $searched_in_classification_uuid
481
            )
482
          ),
483
          'cdm_list_of_taxa',
484
          'show accepted taxa of this ' . $taxon_type,
485
          array('li', 'ul'),
486
          array('class' => array($taxon_type))
487
        );
488
      }
489
    }
490
    else {
491
      // Its a Taxon.
492
      $taxonUri = url(path_to_taxon($taxon->uuid));
493
      $referenceUri = '';
494
      if (isset($taxon->name->nomenclaturalReference)) {
495
        $referenceUri = url(path_to_reference($taxon->name->nomenclaturalReference->uuid));
496
      }
497
      $out .= '<li class="Taxon">' . render_taxon_or_name($taxon, $taxonUri, $referenceUri);
498
      if ($show_classification) {
499
        $classifications = get_classifications_for_taxon($taxon);
500
        $classification_titles = array();
501
        foreach ($classifications as $classification) {
502
          if (isset($classification->titleCache)) {
503
            $classification_titles[] = $classification->titleCache;
504
          }
505
        }
506
        if(count($classification_titles) == 0){
507
          $classification_titles[] = $unclassified_snippet;
508
        }
509
        $out .= ' : <span class="classifications">' . implode(', ', $classification_titles) . '</span>';
510
      }
511
      $out .= theme('cdm_annotations_as_footnotekeys', array('cdmBase_list' => $taxon));
512

    
513
      if ($showMedia_taxa) {
514
        $out .= theme('cdm_taxon_list_thumbnails', array('taxon' => $taxon));
515
      }
516
    }
517

    
518
    /*
519
     * the score field will be empty in case of MultiTermQueries like
520
     * WildcardQueries, since these are  constant score by default
521
     * since Lucene 2.9
522
     */
523
    if(isset($freetext_search_results[$itemCnt]) && $freetext_search_results[$itemCnt]->score && $freetext_search_results[$itemCnt]->maxScore){
524
      $percentage =  ( $freetext_search_results[$itemCnt]->score / $freetext_search_results[$itemCnt]->maxScore ) * 100;
525
      $out .= '<div class="score-bar"><div class="score-bar-indicator" style="width:' . $percentage .'% "></div></div>';
526
      $out .= '<div class="score-bar-value">' . number_format($percentage, 2) .'%</div>';
527
    }
528

    
529
    // Render highlighted fragments, these are made available by free text
530
    // searches.
531
    if (isset($freetext_search_results[$itemCnt]->fieldHighlightMap)) {
532
      $field_fragments = (array) $freetext_search_results[$itemCnt]->fieldHighlightMap;
533
      if (count($field_fragments) > 0) {
534
        $fragments_out = '';
535
        foreach ($field_fragments as $fieldName => $fragments) {
536
          $fragments_out .= '... <span class="' . $fieldName. '">' . filter_xss(join(" ... ", $fragments), array('b') ) . '</span>';
537
        }
538
        $out .= '<div class="fragment_highlight">' . $fragments_out . ' ...</div>';
539
      }
540
    }
541

    
542
    $out .= '</li>';
543
  }
544

    
545
  $out .= '</ul>';
546
  RenderHints::popFromRenderStack();
547

    
548
  return markup_to_render_array($out); // TODO create render array of all list items in function
549
}
550

    
Add picture from clipboard (Maximum size: 40 MB)