Project

General

Profile

Download (18.8 KB) Statistics
| Branch: | Tag: | Revision:
1 b7a20282 Andreas Kohlbecker
<?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 df6226aa Andreas Kohlbecker
  static $NULL_AUTHORTEAM = 'NULL_AUTHORTEAM';
49
50 b7a20282 Andreas Kohlbecker
  if (!$taxonRelationships) {
51
    return null;
52
  }
53
54 df6226aa Andreas Kohlbecker
  RenderHints::pushToRenderStack('taxon_relationships');
55
  $footnoteListKey = 'taxon_relationships';
56 b7a20282 Andreas Kohlbecker
  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 df6226aa Andreas Kohlbecker
69 b7a20282 Andreas Kohlbecker
      if ($taxonRelation->type->uuid == UUID_MISAPPLIED_NAME_FOR || $taxonRelation->type->uuid == UUID_INVALID_DESIGNATION_FOR) {
70
71 df6226aa Andreas Kohlbecker
        RenderHints::pushToRenderStack('misapplied_name_for'); // TODO the render path string should in future come from $taxonRelation->type->...
72
73 b7a20282 Andreas Kohlbecker
        $name = $taxonRelation->fromTaxon->name->titleCache;
74
75
        if(isset($taxonRelation->fromTaxon->sec)) {
76
          // taxa not always are have a sec reference (e.g. doubtful taxa)
77
          $authorteam = cdm_ws_get(CDM_WS_REFERENCE_AUTHORTEAM, $taxonRelation->fromTaxon->sec->uuid);
78
          $authorteam = $authorteam->titleCache;
79 df6226aa Andreas Kohlbecker
          if(!$authorteam){
80
            $authorteam = $NULL_AUTHORTEAM;
81
          }
82 b7a20282 Andreas Kohlbecker
        }
83
84
        if (!isset($misapplied[$name])) {
85
          // Render the first name found as representative for all others.
86
          $misapplied[$name]['out'] = cdm_related_taxon($taxonRelation->fromTaxon, UUID_MISAPPLIED_NAME_FOR);
87
        }
88
        else {
89 df6226aa Andreas Kohlbecker
          // We need to add the anchors for all of the other misapplied names not
90 b7a20282 Andreas Kohlbecker
          // being rendered explicitly.
91
          $misapplied[$name]['out'] = uuid_anchor($taxonRelation->fromTaxon->uuid, $misapplied[$name]['out']);
92
        }
93
94
        // Collect all authors for this fullname.
95
        if (isset($authorteam)) {
96
          $misapplied[$name]['authorteam'][$authorteam] = '';
97
          $joinedAuthorTeams[$authorteam] = 'sensu ' . theme('cdm_reference', array('reference' => $taxonRelation->fromTaxon->sec));
98
        }
99 df6226aa Andreas Kohlbecker
100 b7a20282 Andreas Kohlbecker
      }
101
      else {
102 df6226aa Andreas Kohlbecker
        RenderHints::pushToRenderStack('other_taxon_relationship');
103 b7a20282 Andreas Kohlbecker
        // All relationsship types but misapplied_name_for
104
        // invalid_designation_for.
105
        $taxon_relationships_lines[] = cdm_taxonRelationship($taxonRelation, TRUE, _is_invers_taxonRelationship($taxonRelation, $focusedTaxon));
106
      }
107 df6226aa Andreas Kohlbecker
108
      RenderHints::popFromRenderStack();
109 b7a20282 Andreas Kohlbecker
    }
110
  }
111
112
  // Sort the joinedAuthorTeams and create footnotes and footnotekeys.
113
  ksort($joinedAuthorTeams);
114
  foreach ($joinedAuthorTeams as $authorteam => $sensuCitation) {
115
    $footnoteKey = FootnoteManager::addNewFootnote($footnoteListKey, $sensuCitation);
116 df6226aa Andreas Kohlbecker
117
      $joinedAuthorTeams[$authorteam] = '<span class="sensu">'
118
        . ($authorteam != $NULL_AUTHORTEAM ? 'sensu '. $authorteam : '')
119
        . theme('cdm_footnote_key', array('footnoteKey' => $footnoteKey))
120
        . '</span>';
121
122 b7a20282 Andreas Kohlbecker
  }
123
124
  // ---- Generate output ---- //
125
126
  $out = '<div class="taxon-relationships">';
127
  if (is_array($misapplied) && count($misapplied) > 0) {
128
    $out .= '<ul class="misapplied">';
129
    foreach ($misapplied as $misapplied_name) {
130
131
      $out .= '<li class="synonym"><span class="misapplied">' . $misapplied_name['out'] . ' </span>';
132
133
      if (isset($misapplied_name['authorteam'])) {
134
        // Fill authors with the renderedFootnoteKey and sorting 'em.
135
        foreach ($misapplied_name['authorteam'] as $authorteam => &$renderedFootnoteKey) {
136
          $renderedFootnoteKey = $joinedAuthorTeams[$authorteam];
137
        }
138
        ksort($misapplied_name['authorteam']);
139
        $out .= join('; ', $misapplied_name['authorteam']);
140
      }
141
      $out .= '</li>';
142
    }
143
    $out .= '</ul>';
144
  }
145
146
  if (isset($taxon_relationships_lines) && is_array($taxon_relationships_lines) && count($taxon_relationships_lines) > 0) {
147
    $out .= '<ul class="taxonRelationships">';
148
    foreach ($taxon_relationships_lines as $taxon_relationship_line) {
149
      $out .= '<li class="synonym">' . $taxon_relationship_line . '</li>';
150
    }
151
    $out .= '</ul>';
152
  }
153
154
  $footnotes = theme('cdm_footnotes', array('footnoteListKey' => $footnoteListKey, 'enclosingTag' => 'li'));
155
  $footnotes .= theme('cdm_annotation_footnotes', array('footnoteListKey' => $footnoteListKey, 'enclosingTag' => 'li'));
156
157
// AK: why splitting footnotes at the sensu string ??? this is weired and hacky
158
//     TODO remove below dead code
159
//   $tr_footnotes_exploded = explode('sensu', $tr_footnotes);
160
//   $tr_footnotes_aux = '';
161
//   foreach ($tr_footnotes_exploded as $element) {
162
//     $tr_footnotes_aux .= $element;
163
//   }
164
165
  $out .= '<ul class="footnotes">' . $footnotes . '</ul>';
166
167
  $out .= '</div>';
168
169
  RenderHints::popFromRenderStack();
170
  return $out;
171
}
172
173
174
/**
175
 * Renders a representation of the given taxon relationship.
176
 *
177
 * According name relationships are also being rendered.
178
 *
179
 * @param unknown_type $taxonRelationship
180
 * @param boolean $doLinkTaxon
181
 *     whether to create a link to the related taxon
182
 * @param boolean $inverse
183
 *     whether the $taxonRelationship should be treaded as invers relation
184
 *
185
 * @return void|string
186
 */
187
function cdm_taxonRelationship($taxonRelationship, $doLinkTaxon = FALSE, $inverse = FALSE) {
188
189
  // Validate object.
190
  if (!(isset($taxonRelationship->toTaxon) && isset($taxonRelationship->fromTaxon) && isset($taxonRelationship->type))) {
191
    return null;
192
  }
193
194
  $taxonRelationType = $taxonRelationship->type;
195
196
  if ($inverse) {
197
    $toTaxon = $taxonRelationship->fromTaxon;
198
    $relsign = $taxonRelationType->inverseRepresentation_L10n_abbreviatedLabel;
199
    $reltype_representation = $taxonRelationType->inverseRepresentation_L10n;
200
  }
201
  else {
202
    $toTaxon = $taxonRelationship->toTaxon;
203
    $relsign = $taxonRelationType->representation_L10n_abbreviatedLabel;
204
    $reltype_representation = $taxonRelationType->representation_L10n;
205
  }
206
207
  return cdm_related_taxon($toTaxon, NULL, $relsign, $reltype_representation, $taxonRelationship->doubtful, $doLinkTaxon);
208
}
209
210
/**
211
 * Renders a representation of the given taxon relationship.
212
 *
213
 * According name relationships are also being rendered.
214
 *
215
 * @param $taxon
216
 *  The CDM TaxonBase entity
217
 * @param $reltype_uuid
218
 *  The UUID of the TaxonRelationshipType
219
 * @param $relsign
220
 *  Optional. Can be  used to override the internal decision strategy on finding a suitable icon for the relationship
221
 * @param $reltype_representation
222
 *   Optional: Defines the value for the title attribute of the html element enclosing the relsign
223
 * @param $doubtful
224
 *   TODO
225
 * @param $doLinkTaxon
226
 *   The taxon will be rendered as clickable link when true.
227
 *
228
 * @return string
229
 *   Markup for the taxon relationship.
230
 */
231
function cdm_related_taxon($taxon, $reltype_uuid = NULL, $relsign = NULL, $reltype_representation = NULL, $doubtful=false, $doLinkTaxon = FALSE) {
232
  static $relsign_homo = '≡';
233
  static $relsign_hetero = '=';
234
  static $relsign_invalid = '&ndash;';
235
  static $nom_status_invalid_type_uuids =  array(
236
    UUID_NOMENCLATURALSTATUS_TYPE_INVALID,
237
    UUID_NOMENCLATURALSTATUS_TYPE_NUDUM,
238
    UUID_NOMENCLATURALSTATUS_TYPE_COMBINATIONINVALID,
239
    UUID_NOMENCLATURALSTATUS_TYPE_PROVISIONAL
240
  );
241
242
  // 'taxonRelationships';
243
  $footnoteListKey = NULL;
244
245
  $skip_tags = array();
246
247
  $is_invalid = false;
248
249
  if (!$relsign) {
250
251
    switch ($reltype_uuid) {
252
      case UUID_HETEROTYPIC_SYNONYM_OF:
253
      case UUID_SYNONYM_OF:
254
        $relsign = $relsign_hetero;
255
        break;
256
257
      case UUID_HOMOTYPIC_SYNONYM_OF:
258
        $relsign = $relsign_homo;
259
        break;
260
261
      case UUID_MISAPPLIED_NAME_FOR:
262
      case UUID_INVALID_DESIGNATION_FOR:
263
        $skip_tags[] = 'authors';
264
        $is_invalid = true;
265
        $relsign = $relsign_invalid;
266
267
        break;
268
269
      default:
270
        $relsign = $relsign_invalid;
271
    }
272
273
  }
274
275
  if($doubtful) {
276
    $relsign = '?' . $relsign;
277
  }
278
279
  /*
280
  Names with status invalid or nudum are to be displayed with the
281
  $relsign_invalid, these names appear at the end of all names in their
282
  homotypic group (ordered correctly by the java cdm_lib).
283
  */
284
  if (isset($taxon->name->status) && is_array($taxon->name->status)) {
285
    foreach ($taxon->name->status as $status) {
286
      if (in_array($status->type->uuid , $nom_status_invalid_type_uuids)) {
287
        $relsign = $relsign_invalid;
288
        break;
289
      }
290
    }
291
  }
292
293
  // Now rendering starts ..
294
  RenderHints::pushToRenderStack('related_taxon');
295
296
  if (isset($taxon->name->nomenclaturalReference)) {
297
    $referenceUri = url(path_to_reference($taxon->name->nomenclaturalReference->uuid));
298
  }
299
  $taxonUri = '';
300
  if ($doLinkTaxon) {
301
    $taxonUri = url(path_to_taxon($taxon->uuid, "synonymy"));
302
  }
303
  // Printing the taxonName and the handling the special case of annotations.
304
  if (!isset($referenceUri)) {
305
    $referenceUri = FALSE;
306
  }
307
  $out_taxon_part = render_taxon_or_name($taxon, $taxonUri, $referenceUri, TRUE, FALSE, $skip_tags, $is_invalid);
308
  $taxon_footnotes = theme('cdm_annotations_as_footnotekeys',
309
    array('cdmBase_list' => array(
310
      $taxon->name,
311
      $taxon,
312
    ),
313
      'footnote_list_key' => $footnoteListKey)
314
  );
315
316
  $homonyms = cdm_name_relationships_of($taxon);
317
318
  $out = '<span class="relation_sign" title="' . $reltype_representation . '">' . $relsign . '</span>'
319
    . $out_taxon_part . $taxon_footnotes . ' '  . $homonyms;
320
321
  $out = uuid_anchor($taxon->uuid, $out);
322
323
  RenderHints::popFromRenderStack();
324
325
  return $out;
326
}
327
328 87b304a7 Andreas Kohlbecker
329
/**
330
 * Creates markup for a taxon which is the accepted of another one
331
 *
332
 * @param $accepted_for_uuid
333
 *   The uuid of the accepted taxon
334
 */
335
function cdm_accepted_for($accepted_for_uuid) {
336
337
  if(!is_uuid($accepted_for_uuid)){
338
    return '';
339
  }
340
341
  RenderHints::pushToRenderStack('acceptedFor');
342
  $out = '';
343
344
  $synonym = cdm_ws_get(CDM_WS_PORTAL_TAXON, $accepted_for_uuid);
345
  if ($synonym) {
346
    $out .= '<span class="acceptedFor">';
347
    $out .= t('is accepted for ');
348
    if (isset($synonym->name->nomenclaturalReference)) {
349
      $referenceUri = url(path_to_reference($synonym->name->nomenclaturalReference->uuid));
350
    }
351
    $out .= render_taxon_or_name($synonym->name, NULL, $referenceUri);
352
    $out .= theme('cdm_annotations_as_footnotekeys', array('cdmBase_list' => $synonym));
353
    $out .= '</span>';
354
  }
355
  RenderHints::popFromRenderStack();
356
  return $out;
357
}
358
359
/**
360
 * Compose function for a list of taxa.
361
 *
362
 * This function is for example used toi display search results or the taxa for a taxon name in the name page.
363
 *
364
 * @param $taxon_list array
365
 *   The list of CDM Taxon entities. e.g. The records array as contained in a pager object.
366
 * @param $freetext_search_results array
367
 * @param $show_classification boolean
368
 *
369
 * @ingroup compose
370
 *
371
 */
372
function compose_list_of_taxa($taxon_list, $freetext_search_results = array(), $show_classification = false) {
373
374
  $unclassified_snippet = '<span class="unclassified">' . t('unclassified') . '</span>';
375
376
  RenderHints::pushToRenderStack('list_of_taxa');
377
378
  $gallery_settings = getGallerySettings(CDM_DATAPORTAL_SEARCH_GALLERY_NAME);
379
380
  $showMedia_taxa = $gallery_settings['cdm_dataportal_show_taxon_thumbnails'];
381
  $showMedia_synonyms = $gallery_settings['cdm_dataportal_show_synonym_thumbnails'];
382
  $searched_in_classification = cdm_dataportal_searched_in_classification();
383
  $searched_in_classification_uuid = null;
384
  if(isset($searched_in_classification->uuid)){
385
    $searched_in_classification_uuid = $searched_in_classification->uuid;
386
  }
387
388
  // .. Well, for sure not as performant as before, but better than nothing.
389
  $synonym_uuids = array();
390
  $misappied_uuids = array();
391
  foreach ($taxon_list as $taxon) {
392
    if ($taxon->class == "Synonym") {
393
      if (!array_key_exists($taxon->uuid, $synonym_uuids)) {
394
        $synonym_uuids[$taxon->uuid] = $taxon->uuid;
395
      }
396
    }
397
    elseif (!_cdm_dataportal_acceptedByCurrentView($taxon)) {
398
      // Assuming that it is a misappied name, will be further examined below.
399
      $misappied_uuids[$taxon->uuid] = $taxon->uuid;
400
    }
401
  }
402
403
  // Batch service not jet implemented:
404
  // $table_of_accepted = cdm_ws_property(CDM_WS_PORTAL_TAXON_ACCEPTED,
405
  // join(',', $synonym_uuids));
406
  // thus ...
407
  $table_of_accepted = array();
408
409
  foreach ($synonym_uuids as $relatedUuid) {
410
    $table_of_accepted[$relatedUuid] = cdm_ws_get(CDM_WS_PORTAL_TAXON_ACCEPTED, array(
411
      $relatedUuid,
412
      $searched_in_classification_uuid,
413
    ));
414
  }
415
416
  foreach ($misappied_uuids as $relatedUuid) {
417
    $taxonRelations = cdm_ws_get(CDM_WS_PORTAL_TAXON_RELATIONS, array(
418
      $relatedUuid,
419
    ));
420
    foreach ($taxonRelations as $relation) {
421
      if ($relation->type->uuid == UUID_MISAPPLIED_NAME_FOR && _cdm_dataportal_acceptedByCurrentView($relation->toTaxon)) {
422
        $table_of_accepted[$relatedUuid][] = $relation->toTaxon;
423
      }
424
    }
425
  }
426
427
  $out = '<ul class="cdm_names" style="background-image: none;">';
428
  $itemCnt = -1;
429
  foreach ($taxon_list as $taxon) {
430
    $itemCnt++;
431
    if (isset($table_of_accepted[$taxon->uuid])) {
432
      // Its a synonym or misapplied name.
433
      $is_synonym = isset($synonym_uuids[$taxon->uuid]); //TODO better use the $taxon->class attribute?
434
      $taxon_type = $is_synonym ? "Synonym" : "misapplied-name";
435
436
      $acceptedTaxa = $table_of_accepted[$taxon->uuid];
437
438
      if (count($acceptedTaxa) == 1) {
439
440
        $acceptedTaxon = $acceptedTaxa[0];
441
        $taxonUri = uri_to_synonym($taxon->uuid, $acceptedTaxon->uuid, 'synonymy');
442
        $referenceUri = '';
443
        if (isset($acceptedTaxon->name->nomenclaturalReference)) {
444
          $referenceUri = url(path_to_reference($acceptedTaxon->name->nomenclaturalReference->uuid));
445
        }
446
        $taxon_or_name = $is_synonym ? $taxon->name : $taxon;
447
        // $taxon_or_name this is a trick to suppress the sec reference for sysnonyms
448
        // supplying the name will cause render_taxon_or_name() to not show the sec reference
449
        $out .= '<li class="' . $taxon_type . '">' . render_taxon_or_name($taxon_or_name, $taxonUri, $referenceUri);
450
        if ($show_classification) {
451
          $classifications = get_classifications_for_taxon($taxon);
452
          $classification_titles = array();
453
          foreach ($classifications as $classification) {
454
            if (isset($classification->titleCache)) {
455
              $classification_titles[] = $classification->titleCache;
456
            }
457
          }
458
          if(count($classification_titles) == 0){
459
            $classification_titles[] = $unclassified_snippet;
460
          }
461
          $out .= ' : <span class="classifications">' . implode(', ', $classification_titles) . '</span>';
462
        }
463
        $out .= theme('cdm_annotations_as_footnotekeys', array('cdmBase_list' => $taxon));
464
        if ($showMedia_synonyms) {
465
          $out .= theme('cdm_taxon_list_thumbnails', array('taxon' => $acceptedTaxon));
466
        }
467
      }
468
      else {
469
470
        // TODO avoid using Ajax in the cdm_dynabox .... why?
471
        // TODO add media.
472
        $out .= cdm_dynabox(
473
          $taxon->uuid,
474
          render_taxon_or_name($taxon->name, NULL, NULL, FALSE),
475
          cdm_compose_url(CDM_WS_PORTAL_TAXON_ACCEPTED,
476
            array(
477
              $taxon->uuid,
478
              $searched_in_classification_uuid
479
            )
480
          ),
481
          'cdm_list_of_taxa',
482
          'show accepted taxa of this ' . $taxon_type,
483
          array('li', 'ul'),
484
          array('class' => array($taxon_type))
485
        );
486
      }
487
    }
488
    else {
489
      // Its a Taxon.
490
      $taxonUri = url(path_to_taxon($taxon->uuid));
491
      $referenceUri = '';
492
      if (isset($taxon->name->nomenclaturalReference)) {
493
        $referenceUri = url(path_to_reference($taxon->name->nomenclaturalReference->uuid));
494
      }
495
      $out .= '<li class="Taxon">' . render_taxon_or_name($taxon, $taxonUri, $referenceUri);
496
      if ($show_classification) {
497
        $classifications = get_classifications_for_taxon($taxon);
498
        $classification_titles = array();
499
        foreach ($classifications as $classification) {
500
          if (isset($classification->titleCache)) {
501
            $classification_titles[] = $classification->titleCache;
502
          }
503
        }
504
        if(count($classification_titles) == 0){
505
          $classification_titles[] = $unclassified_snippet;
506
        }
507
        $out .= ' : <span class="classifications">' . implode(', ', $classification_titles) . '</span>';
508
      }
509
      $out .= theme('cdm_annotations_as_footnotekeys', array('cdmBase_list' => $taxon));
510
511
      if ($showMedia_taxa) {
512
        $out .= theme('cdm_taxon_list_thumbnails', array('taxon' => $taxon));
513
      }
514
    }
515
516
    /*
517
     * the score field will be empty in case of MultiTermQueries like
518
     * WildcardQueries, since these are  constant score by default
519
     * since Lucene 2.9
520
     */
521
    if(isset($freetext_search_results[$itemCnt]) && $freetext_search_results[$itemCnt]->score && $freetext_search_results[$itemCnt]->maxScore){
522
      $percentage =  ( $freetext_search_results[$itemCnt]->score / $freetext_search_results[$itemCnt]->maxScore ) * 100;
523
      $out .= '<div class="score-bar"><div class="score-bar-indicator" style="width:' . $percentage .'% "></div></div>';
524
      $out .= '<div class="score-bar-value">' . number_format($percentage, 2) .'%</div>';
525
    }
526
527
    // Render highlighted fragments, these are made available by free text
528
    // searches.
529
    if (isset($freetext_search_results[$itemCnt]->fieldHighlightMap)) {
530
      $field_fragments = (array) $freetext_search_results[$itemCnt]->fieldHighlightMap;
531
      if (count($field_fragments) > 0) {
532
        $fragments_out = '';
533
        foreach ($field_fragments as $fieldName => $fragments) {
534
          $fragments_out .= '... <span class="' . $fieldName. '">' . filter_xss(join(" ... ", $fragments), array('b') ) . '</span>';
535
        }
536
        $out .= '<div class="fragment_highlight">' . $fragments_out . ' ...</div>';
537
      }
538
    }
539
540
    $out .= '</li>';
541
  }
542
543
  $out .= '</ul>';
544
  RenderHints::popFromRenderStack();
545
546
  return markup_to_render_array($out); // TODO create render array of all list items in function
547
}