Project

General

Profile

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

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

    
33

    
34
/**
35
 * Provides the name render template to be used within the page elements identified the the $renderPath.
36
 *
37
 * The render templates arrays contains one or more name render templates to be used within the page elements identified the the
38
 * renderPath. The renderPath is the key of the subelements whereas the value is the name render template.
39
 *
40
 * The render paths used for a cdm_dataportal page can be visualized by supplying the HTTP query parameter RENDER_PATH=1.
41
 *
42
 * It will be tried to find  the best matching default RenderTemplate by stripping the dot separated render path
43
 * element by element. If no matching template is found the DEFAULT will be used:
44
 *
45
 * - related_taxon.heterotypicSynonymyGroup.taxon_page_synonymy
46
 * - related_taxon.heterotypicSynonymyGroup.taxon_page_synonymy
47
 * - related_taxon.heterotypicSynonymyGroup.taxon_page_synonymy
48
 *
49
 * A single render template can be used for multiple render paths. In this case the according key of the render templates
50
 * array element should be the list of these render paths concatenated by ONLY a comma character without any whitespace.
51
 *
52
 * A render template is an associative array. The keys of this array are referring to the keys as defined in the part
53
 * definitions array.
54
 * @see get_partDefinition($taxonNameType) for more information
55
 *
56
 * The value of the render template element must be set to TRUE in order to let this part being rendered.
57
 * The namePart, nameAuthorPart and referencePart can also hold an associative array with a single
58
 * element: array('#uri' => TRUE). The value of the #uri element will be replaced by the according
59
 * links if the parameters $nameLink or $refenceLink are set.
60
 *
61
 * @param string $render_path
62
 *   The render path can consist of multiple dot separated elements
63
 *   @see RenderHints::getRenderPath()
64
 * @param string $nameLink
65
 *   The link path or URL to be used for name parts if a link is forseen in the template
66
 *   matching the given $renderPath.
67
 * @param string $referenceLink
68
 *   The link path ot URL to be used for nomenclatural reference parts if a link is forseen
69
 *   in the template matching the given $renderPath.
70
 * @return array
71
 *   An associative array, the render template
72
 */
73
function get_nameRenderTemplate($render_path, $nameLink = NULL, $referenceLink = NULL) {
74

    
75
  static $default_render_templates = NULL;
76
  static $split_render_templates = NULL;
77

    
78

    
79
  if (!isset($default_render_templates)) {
80
    $default_render_templates = unserialize(CDM_NAME_RENDER_TEMPLATES_DEFAULT);
81
  }
82
  if($split_render_templates == NULL) {
83
    $render_templates = variable_get(CDM_NAME_RENDER_TEMPLATES, $default_render_templates);
84
    // needs to be converted to an array
85
    $render_templates = (object_to_array($render_templates));
86

    
87
    // separate render templates which are combined with a comma
88
    $split_render_templates = array();
89
    foreach($render_templates as $key => $template){
90
      if(strpos($key, ',')){
91
        foreach(explode(',', $key) as $path){
92
          $split_render_templates[$path] = $template;
93
        }
94
      } else {
95
        $split_render_templates[$key] = $template;
96
      }
97
    }
98
  }
99

    
100
  // get the base element of the renderPath
101
  if (($separatorPos = strpos($render_path, '.')) > 0) {
102
    $renderPath_base = substr($render_path, 0, $separatorPos);
103
  } else {
104
    $renderPath_base = $render_path;
105
  }
106

    
107
  $template = NULL;
108
  // 1. try to find a template using the render path base element
109
  if(array_key_exists($renderPath_base, $split_render_templates)){
110
    $template = (array)$split_render_templates[$renderPath_base];
111
  }
112

    
113
  // 2. Find best matching default RenderTemplate
114
  // by stripping the dot separated render path element by element
115
  // if no matching template is found the DEFAULT will be used.
116
  while (!is_array($template) && strlen($render_path) > 0) {
117
    foreach ($split_render_templates as $path => $t) {
118
      if ($path == $render_path) {
119
        $template = $t;
120
        break;
121
      }
122
    }
123
    // shorten by one element
124
    $render_path = substr($render_path, strrpos($render_path, '.') + 1, strlen($render_path));
125
  }
126

    
127

    
128
  // 3. Otherwise get default RenderTemplate from theme.
129
  if (!is_array($template)) {
130
    $template = $split_render_templates['#DEFAULT'];
131
  }
132

    
133
  // --- set the link uris to the according template fields if they exist
134
  if(isset($template['nameAuthorPart']) && isset($template['nameAuthorPart']['#uri'])) {
135
    if ($nameLink) {
136
      $template['nameAuthorPart']['#uri'] = $nameLink;
137
    }
138
    else {
139
      unset($template['nameAuthorPart']['#uri']);
140
    }
141
  }
142

    
143
  if ($nameLink && isset($template['namePart']['#uri'])) {
144
    $template['namePart']['#uri'] = $nameLink;
145
  }
146
  else {
147
    unset($template['namePart']['#uri']);
148
  }
149

    
150
  if ($referenceLink && isset($template['referencePart']['#uri'])) {
151
    $template['referencePart']['#uri'] = $referenceLink;
152
  }
153
  else {
154
    unset($template['referencePart']['#uri']);
155
  }
156

    
157
  return $template;
158
}
159

    
160
/**
161
 * The part definitions define the specific parts of which a rendered taxon name plus additional information will consist.
162
 *
163
 * A full taxon name plus additional information can consist of the following elements:
164
 *
165
 *   - name: the taxon name inclugin rank nbut without author
166
 *   - authorTeam:  The authors of a reference, also used in taxon names
167
 *   - authors:  The authors of a reference, also used in taxon names
168
 *   - reference: the nomenclatural reference,
169
 *   - microreference:  Volume, page number etc.
170
 *   - status:  The nomenclatural status of a name
171
 *   - description: name descriptions like protologues etc ...
172
 *
173
 * These elements are combined in the part definitions array to from the specific parts to be rendered.
174
 * Usually the following parts are formed:
175
 *
176
 * The name "Lapsana communis L., Sp. Pl.: 811. 1753" shall be an example here:
177
 *  - namePart: the name and rank (in example: "Lapsana communis")
178
 *  - authorshipPart: the author (in example: "L.")
179
 *  - nameAuthorPart: the combination of name and author part (in example: "Lapsana communis L.").
180
 *     This is useful for zoological names where the authorshipPart belongs to the name and both should
181
 *     be combined when a link to the taxon is rendered.
182
 *  - referencePart: the nomencaltural reference (in example: "Sp. Pl. 1753")
183
 *  - microreferencePart: usually the page number (in example ": 811.")
184
 *  - statusPart: the nomenclatorical status
185
 *  - descriptionPart:
186
 *
187
 * Each set of parts is dedicated to render a specific TaxonName type, the type names are used as keys for the
188
 * specific parts part definitions:
189
 *
190
 *  - BotanicalName
191
 *  - ZoologicalName
192
 *  - #DEFAULT:  covers ViralNames and general NonViralNames
193
 *
194
 * An example:
195
 * @code
196
 * array(
197
 *    'ZoologicalName' => array(
198
 *        'namePart' => array('name' => TRUE),
199
 *        'referencePart' => array('authorTeam' => TRUE),
200
 *        'microreferencePart' => array('microreference' => TRUE),
201
 *        'statusPart' => array('status' => TRUE),
202
 *        'descriptionPart' => array('description' => TRUE),
203
 *    ),
204
 *    'BotanicalName' => array(
205
 *        'namePart' => array(
206
 *            'name' => TRUE,
207
 *            'authors' => TRUE,
208
 *        ),
209
 *        'referencePart' => array(
210
 *            'reference' => TRUE,
211
 *            'microreference' => TRUE,
212
 *        ),
213
 *        'statusPart' => array('status' => TRUE),
214
 *        'descriptionPart' => array('description' => TRUE),
215
 *    ),
216
 *  );
217
 * @endcode
218
 *
219
 * @param object $taxonNameType
220
 *    A cdm TaxonNameType entity
221
 *
222
 */
223
function get_partDefinition($taxonNameType) {
224

    
225
  static $default_part_definitions = null;
226
  if (!isset($default_part_definitions)) {
227
    $default_part_definitions= unserialize(CDM_PART_DEFINITIONS_DEFAULT);
228
  }
229

    
230
  static $part_definitions = null;
231
  if (!isset($part_definitions)) {
232
    $part_definitions = object_to_array(variable_get(CDM_PART_DEFINITIONS, $default_part_definitions));
233
  }
234

    
235
  $dtype = nameTypeToDTYPE($taxonNameType);
236
  if (array_key_exists($taxonNameType, $part_definitions)) {
237
    return $part_definitions[$taxonNameType];
238
  } else if (array_key_exists($dtype, $part_definitions)) {
239
    return $part_definitions[$dtype];
240
  } else {
241
    return $part_definitions['#DEFAULT']; // covers ViralNames and general NonViralNames
242
  }
243

    
244
}
245

    
246

    
247
/**
248
 * Renders the markup for a CDM TaxonName instance.
249
 *
250
 * The layout of the name representation is configured by the
251
 * part_definitions and render_templates (see get_partDefinition() and
252
 * get_nameRenderTemplate())
253
 *
254
 * @param $taxon_name_or_taxon_base
255
 *    A cdm TaxonBase or TaxonName entity
256
 * @param $name_link
257
 *    URI to the taxon, @param $reference_link
258
 *    URI to the reference,
259
 * @param bool $show_annotations
260
 *    turns the display of annotations on
261
 * @param bool $is_type_designation
262
 *    To indicate that the supplied taxon name is a name type designation.
263
 * @param array $skiptags
264
 *    an array of name elements tags like 'name', 'rank' to skip. The name part
265
 *          'authors' will not ber affected by this filter. This part is managed though the render template
266
 *          mechanism.
267
 * @param bool $is_invalid
268
 *   Indicates that this taxon is invalid. In this case the name part will be shown in double quotes.
269
 *   This is useful when rendering taxon relation ships.
270
 *
271
 * @return string
272
 *  The markup for a taxon name.
273
 * @see path_to_taxon(), must be processed by url() before passing to this method
274
 * @see path_to_reference(), must be processed by url() before passing to this method
275
 */
276
function render_taxon_or_name($taxon_name_or_taxon_base, $name_link = NULL, $reference_link = NULL,
277
  $show_annotations = true, $is_type_designation = false, $skiptags = array(), $is_invalid = false) {
278

    
279
  $is_doubtful = false;
280
  $taxon_base = null;
281
  if($taxon_name_or_taxon_base->class == 'Taxon' || $taxon_name_or_taxon_base->class == 'Synonym'){
282
    $taxon_base = $taxon_name_or_taxon_base;
283
    if(isset($taxon_name_or_taxon_base->name)){
284
      $taxon_name = $taxon_name_or_taxon_base->name;
285
    } else {
286
      $taxon_name = cdm_ws_get(CDM_WS_TAXON . '/$0/name', array($taxon_name_or_taxon_base->uuid));
287
    }
288
    $is_doubtful = $taxon_name_or_taxon_base->doubtful;
289
    // use the TaxonBase.tagged_title so we have the secRef
290
    $tagged_title = $taxon_name_or_taxon_base->taggedTitle;
291
  } else {
292
    // assuming this is a TaxonName
293
    $taxon_name = $taxon_name_or_taxon_base;
294
    if(isset($taxon_name->taggedFullTitle)){
295
      $tagged_title = $taxon_name_or_taxon_base->taggedFullTitle;
296
    } else {
297
      $tagged_title = $taxon_name_or_taxon_base->taggedName;
298
    }
299
  }
300

    
301

    
302
  $renderTemplate = get_nameRenderTemplate(RenderHints::getRenderPath(), $name_link, $reference_link);
303
  $partDefinition = get_partDefinition($taxon_name->nameType);
304

    
305
  // Apply definitions to template.
306
  foreach ($renderTemplate as $part => $uri) {
307

    
308
    if (isset($partDefinition[$part])) {
309
      $renderTemplate[$part] = $partDefinition[$part];
310
    }
311
    if (is_array($uri) && isset($uri['#uri'])) {
312
      $renderTemplate[$part]['#uri'] = $uri['#uri'];
313
    }
314
  }
315

    
316
  $secref_tagged_text = tagged_text_extract_reference_and_detail($tagged_title);
317
  // taxon names will have the nomenclatural reference in the tagged full title:
318
  $nomref_tagged_text = tagged_text_extract_reference($tagged_title);
319
  $nom_status_tagged_text = tagged_text_extract_nomstatus($tagged_title);
320
  $appended_phrase_tagged_text = array(); // this is filled later
321

    
322
  normalize_tagged_text($tagged_title);
323

    
324
  $is_valid_tagged_title =
325
    isset($tagged_title)
326
    && is_array($tagged_title)
327
    && isset($tagged_title[0]->text)
328
    && is_string($tagged_title[0]->text)
329
    && $tagged_title[0]->text != ''
330
    && isset($tagged_title[0]->type);
331
  $lastAuthorElementString = FALSE;
332

    
333
  $name_encasement = $is_invalid ? '"' : '';
334
  $doubtful_marker = $is_doubtful ? '?&#8239;' : ''; // 	&#8239; =  NARROW NO-BREAK SPACE
335
  $doubtful_marker_markup = '';
336

    
337
  if($doubtful_marker){
338
    $doubtful_marker_markup = '<span class="doubtful">' . $doubtful_marker . '</span>';
339
    if($tagged_title[0]->text == '?' ){
340
      // remove the first tagged text element
341
      unset($tagged_title[0]);
342
    }
343
  }
344

    
345
  // split off all appendedPhrase item  from the end of the array (usually there only should  be one)
346
  while($tagged_title[count($tagged_title)-1]->type == "appendedPhrase"){
347
    $appended_phrase_tagged_text[] = array_pop($tagged_title);
348
  }
349

    
350
  // Got to use second entry as first one, see ToDo comment below ...
351
  if ($is_valid_tagged_title) {
352

    
353
    $taggedName = $tagged_title;
354
    $hasNamePart_with_Authors = isset($renderTemplate['namePart']) && isset($renderTemplate['namePart']['authors']);
355
    $hasNameAuthorPart_with_Authors = isset($renderTemplate['nameAuthorPart']) && isset($renderTemplate['nameAuthorPart']['authors']);
356

    
357

    
358
    if (!(($hasNamePart_with_Authors) || ($hasNameAuthorPart_with_Authors))) {
359
      // Find author and split off from name.
360
      // TODO expecting to find the author as the last element.
361
      /*
362
      if($taggedName[count($taggedName)- 1]->type == 'authors'){
363
        $authorTeam = $taggedName[count($taggedName)- 1]->text;
364
        unset($taggedName[count($taggedName)- 1]);
365
      }
366
      */
367

    
368
      // Remove all authors.
369
      $taggedNameNew = array();
370
      foreach ($taggedName as $element) {
371
        if ($element->type != 'authors') {
372
          $taggedNameNew[] = $element;
373
        }
374
        else {
375
          $lastAuthorElementString = $element->text;
376
        }
377
      }
378
      $taggedName = $taggedNameNew;
379
      unset($taggedNameNew);
380
    }
381
    $name = '<span class="' . $taxon_name->class . '">' . $doubtful_marker_markup . $name_encasement . cdm_tagged_text_to_markup($taggedName, $skiptags) . $name_encasement . '</span>';
382
  }
383
  else {
384
    // use titleCache instead
385
    $name = '<span class="' . $taxon_name->class . '_titleCache">' . $doubtful_marker_markup . $name_encasement . $taxon_name->titleCache . $name_encasement . '</span>';
386
  }
387

    
388

    
389
  if(isset($appended_phrase_tagged_text[0])){
390
    $name .= ' <span class="appended-phrase">'. cdm_tagged_text_to_markup($appended_phrase_tagged_text) . '</span>';
391
  }
392

    
393
  // Fill name into $renderTemplate.
394
  array_setr('name', $name , $renderTemplate);
395

    
396
  // Fill with authorTeam.
397
  /*
398
  if($authorTeam){
399
    $authorTeamHtml = ' <span class="authorTeam">'.$authorTeam.'</span>';
400
    array_setr('authorTeam', $authorTeamHtml, $renderTemplate);
401
  }
402
  */
403

    
404
  // Fill with reference.
405
  if (isset($renderTemplate['referencePart']) && !$is_type_designation) {
406

    
407
    $registrations = cdm_ws_get(CDM_WS_NAME, array($taxon_name->uuid, "registrations"));
408
    $registration_markup = render_registrations($registrations);
409

    
410
    // default separator
411
    $separator = '';
412

    
413
    // [Eckhard]:"Komma nach dem Taxonnamen ist grunsätzlich falsch,
414
    // Komma nach dem Autornamen ist überall dort falsch, wo ein "in" folgt."
415
    if (isset($renderTemplate['referencePart']['reference'])) {
416
      $microreference = NULL;
417
      if (isset($renderTemplate['referencePart']['microreference'])&& isset($taxon_name->nomenclaturalSource->citationMicroReference)) {
418
        $microreference = $taxon_name->nomenclaturalSource->citationMicroReference;
419
      }
420
      if(count($nomref_tagged_text) == 0 && isset($taxon_name->nomenclaturalSource->citation)){
421
        // TODO is this case still relevant? The tagged text should already contain all information!
422
        $citation = cdm_ws_getNomenclaturalReference($taxon_name->nomenclaturalSource->citation->uuid, $microreference);
423
        // Find preceding element of the reference.
424
        $precedingKey = get_preceding_contentElementKey('reference', $renderTemplate);
425
        if (str_beginsWith($citation, ", in")) {
426
          $citation = substr($citation, 2);
427
          $separator = ' ';
428
        }
429
        elseif (!str_beginsWith($citation, "in") && $precedingKey == 'authors') {
430
          $separator = ', ';
431
        } else {
432
          $separator = ' ';
433
        }
434
        $referenceArray['#separator'] = $separator;
435
        $referenceArray['#html'] = '<span class="reference">' . $citation . '</span>' . $registration_markup;
436
      } else {
437
        // this ist the case for taxon names
438
        $referenceArray['#html'] = cdm_tagged_text_to_markup($nomref_tagged_text);
439
      }
440

    
441

    
442
      array_setr('reference', $referenceArray, $renderTemplate);
443
    }
444

    
445
    // If authors have been removed from the name part the last named authorteam
446
    // should be added to the reference citation, otherwise, keep the separator
447
    // out of the reference.
448
    if (isset($renderTemplate['referencePart']['authors']) && $lastAuthorElementString) {
449
      // If the nomenclaturalReference citation is not included in the
450
      // reference part but display of the microreference
451
      // is wanted, append the microreference to the authorTeam.
452
      $citation = '';
453
      if (!isset($renderTemplate['referencePart']['reference']) && isset($renderTemplate['referencePart']['microreference'])) {
454
        $separator = ": ";
455
        $citation = $taxon_name->nomenclaturalMicroReference;
456
      }
457
      $referenceArray['#html'] = ' <span class="reference">' . $lastAuthorElementString . $separator . $citation . '</span>';
458
      array_setr('authors', $referenceArray, $renderTemplate);
459
    }
460
  }
461

    
462
  $is_reference_year = false;
463
  if (isset($renderTemplate['referenceYearPart']['reference.year'])) {
464
    if(isset($taxon_name->nomenclaturalSource->citation->datePublished)){
465
      $referenceArray['#html'] = ' <span class="reference">' . timePeriodToString($taxon_name->nomenclaturalSource->citation->datePublished) . '</span>';
466
      array_setr('reference.year', $referenceArray, $renderTemplate);
467
      $is_reference_year = true;
468
    }
469
  }
470

    
471
  // Fill with status.
472
  if(isset($renderTemplate['statusPart']['status'])){
473
    if (isset($nom_status_tagged_text[0])) {
474
        $tt_to_markup_options = array('html' => false);
475
        foreach ($nom_status_tagged_text as &$tt){
476
         if($tt->type == 'nomStatus'&& isset($tt->entityReference)) {
477
           $nom_status = cdm_ws_get(CDM_WS_NOMENCLATURALSTATUS, array($tt->entityReference->uuid));
478
           $nom_status_fkey = handle_nomenclatural_status_as_footnote($nom_status);
479
           $tt->text .= $nom_status_fkey;
480
           $tt_to_markup_options['html'] = true;
481
         }
482
        }
483
        array_setr(
484
          'status',
485
          '<span class="nomenclatural_status">' . cdm_tagged_text_to_markup($nom_status_tagged_text, array('postSeparator'), 'span', $tt_to_markup_options) . '</span>',
486
          $renderTemplate);
487
    }
488
  }
489

    
490
  if (isset($renderTemplate['secReferencePart'])){
491
    if(isset($secref_tagged_text[1])){
492
      $post_separator_markup = $is_reference_year ? '.': '';
493
      if(isset($nom_status_tagged_text[count($nom_status_tagged_text) - 1]) && ($nom_status_tagged_text[count($nom_status_tagged_text) - 1]->type ==  'postSeparator')){
494
        $post_separator_markup = cdm_tagged_text_to_markup(array($nom_status_tagged_text[count($nom_status_tagged_text) - 1 ]));
495
      };
496
      array_setr('secReference',
497
        $post_separator_markup
498
          . ' <span class="sec_reference">'
499
          . join('', cdm_tagged_text_values($secref_tagged_text))
500
          . '</span>', $renderTemplate);
501
    }
502
  }
503

    
504
  // Fill with protologues etc...
505
  $descriptionHtml = '';
506
  if (array_setr('description', TRUE, $renderTemplate)) {
507
    $descriptions = cdm_ws_get(CDM_WS_PORTAL_NAME_DESCRIPTIONS, $taxon_name->uuid);
508
    if($descriptions){
509
      foreach ($descriptions as $description) {
510
        if (!empty($description)) {
511
          foreach ($description->elements as $description_element) {
512
            $second_citation = '';
513
            if (isset($description_element->multilanguageText_L10n) && $description_element->multilanguageText_L10n->text) {
514
              if(isset($description_element->feature) && $description_element->feature->uuid == UUID_ADDITIONAL_PUBLICATION){
515
                $prefix =  '& ';
516
              } else {
517
                $prefix = '';
518
              }
519
              $second_citation = ' [' . $prefix . $description_element->multilanguageText_L10n->text . '].';
520
            }
521
            $descriptionHtml .= $second_citation;
522
            $descriptionHtml .= cdm_description_element_media(
523
                $description_element,
524
                array(
525
                  'application/pdf',
526
                  'image/png',
527
                  'image/jpeg',
528
                  'image/gif',
529
                  'text/html',
530
                )
531
            );
532

    
533
          }
534
        }
535
      }
536
    }
537
    array_setr('description', $descriptionHtml, $renderTemplate);
538
  }
539

    
540
  // Render.
541
  $out = '';
542
  if(isset($_REQUEST['RENDER_PATH'])){
543
    // developer option to show the render path with each taxon name
544
    $out .= '<span class="render-path">' . RenderHints::getRenderPath() . '</span>';
545
  }
546
  $out .= '<span class="' . html_class_attribute_ref($taxon_name_or_taxon_base)
547
    . '" data-cdm-ref="/name/' . $taxon_name->uuid . '" data-cdm-render-path="' . RenderHints::getRenderPath() .'">';
548

    
549
  foreach ($renderTemplate as $partName => $part) {
550
    $separator = '';
551
    $partHtml = '';
552
    $uri = FALSE;
553
    if (!is_array($part)) {
554
      continue;
555
    }
556
    if (isset($part['#uri']) && is_string($part['#uri'])) {
557
      $uri = $part['#uri'];
558
      unset($part['#uri']);
559
    }
560
    foreach ($part as $key => $content) {
561
      $html = '';
562
      if (is_array($content)) {
563
        $html = $content['#html'];
564
        if(isset($content['#separator'])) {
565
          $separator = $content['#separator'];
566
        }
567
      }
568
      elseif (is_string($content)) {
569
        $html = $content;
570
      }
571
      $partHtml .= '<span class="' . $key . '">' . $html . '</span>';
572
    }
573
    if ($uri) {
574
      // cannot use l() here since the #uri aleady should have been processed through uri() at this point
575
      $out .= $separator . '<a href="' . $uri . '" class="' . $partName . '">' . $partHtml . '</a>';
576

    
577
    }
578
    else {
579
      $out .= $separator . $partHtml;
580
    }
581
  }
582
  $out .= '</span>';
583
  if ($show_annotations) {
584
    if($taxon_base){
585
      $annotations_and_sources_taxon = handle_annotations_and_sources($taxon_base);
586
    }
587
    $annotations_and_sources_name = handle_annotations_and_sources($taxon_name);
588
    $footnote_keys = $annotations_and_sources_taxon['foot_note_keys'] .
589
      ($annotations_and_sources_taxon['foot_note_keys'] && $annotations_and_sources_name['foot_note_keys'] ? ',' : '') .
590
      $annotations_and_sources_name['foot_note_keys'];
591
    $out .= $footnote_keys;
592
  }
593
  return $out;
594
}
595

    
596

    
597

    
598
/**
599
 * Composes information for a registration from a dto object.
600
 *
601
 * Registrations which are not yet published are suppressed.
602
 *
603
 * @param $registration_dto
604
 * @param $with_citation
605
 *   Whether to show the citation.
606
 *
607
 * @return array
608
 *    A drupal render array with the elements:
609
 *    - 'name'
610
 *    - 'name-relations'
611
 *    - 'specimen_type_designations'
612
 *    - 'name_type_designations'
613
 *    - 'citation'
614
 *    - 'registration_date_and_institute'
615
 * @ingroup compose
616
 */
617
function compose_registration_dto_full($registration_dto, $with_citation = true)
618
{
619
  $render_array = array(
620
    '#prefix' => '<div class="registration">',
621
    '#suffix' => '</div>'
622
  );
623

    
624
  if(!(isset($registration_dto->identifier) && $registration_dto->status == 'PUBLISHED')){
625
    return $render_array;
626
  }
627

    
628
  $render_array['sub_headline'] = markup_to_render_array(join(", ", registration_types($registration_dto)),-10, '<h3 class="registration_type">' . t('Event: '), '</h3>' );
629
  $render_array['nomenclatural_act'] = array(
630
    '#weight' => 0,
631
    '#prefix' => '<div class="nomenclatural_act">',
632

    
633
    '#suffix' => '</div>'
634
  );
635

    
636
  $typified_name = null;
637

    
638
  // Nomenclatural act block element
639
  $last_footnote_listkey = RenderHints::setFootnoteListKey("nomenclatural_act");
640
  // name
641
  $name_relations = null;
642
  if($registration_dto->nameRef){
643
    $name = cdm_ws_get(CDM_WS_PORTAL_NAME, $registration_dto->nameRef->uuid);
644
    cdm_load_tagged_full_title($name);
645
    $render_array['nomenclatural_act']['published_name'] = markup_to_render_array('<div class="published-name">' . render_taxon_or_name($name, url(path_to_name($name->uuid))) . '</div>', 0);
646
    $name_relations = cdm_ws_fetch_all(str_replace("$0", $registration_dto->nameRef->uuid, CDM_WS_PORTAL_NAME_NAME_RELATIONS));
647
    // need to create the name relationships later, so that the foot notes are in correct order, see section // name relations
648
  } else {
649
    // in this case the registration must have a
650
    // typified name will be rendered later
651
    $typified_name = cdm_ws_get(CDM_WS_PORTAL_NAME, $registration_dto->typifiedNameRef->uuid);
652

    
653
  }
654

    
655
  // typedesignation in detail
656
  if(is_object($registration_dto->orderdTypeDesignationWorkingSets)) {
657
    $field_unit_uuids = array();
658
    $specimen_type_designation_refs = array();
659
    $name_type_designation_refs = array();
660
    foreach ((array)$registration_dto->orderdTypeDesignationWorkingSets as $workingset_ref => $obj) {
661
      $tokens = explode("#", $workingset_ref);
662
      $types_in_fieldunit = get_object_vars($obj); // convert into associative array
663

    
664
      if ($tokens[0] == 'NameTypeDesignation') {
665
        foreach ($types_in_fieldunit as $type_status => $entity_reference_list) {
666
          if(!isset($name_type_designation_refs[$type_status])){
667
            $name_type_designation_refs[$type_status]  = $entity_reference_list;
668
          } else {
669
            array_push($name_type_designation_refs[$type_status] ,$entity_reference_list);
670
          }
671
        }
672
      } else if ($tokens[0] == 'FieldUnit'){
673
        $field_unit_uuids[] = $tokens[1];
674
        foreach ($types_in_fieldunit as $type_status => $entity_reference_list) {
675
          if(!isset($specimen_type_designation_refs[$type_status])){
676
            $specimen_type_designation_refs[$type_status] =  $entity_reference_list;
677
          } else {
678
            array_push($specimen_type_designation_refs[$type_status], $entity_reference_list);
679
          }
680
        }
681
      } else {
682
        drupal_set_message("Unimplemented type: " . $tokens[0], 'error');
683
      }
684
    }
685
    // type designations which are in this nomenclatural act.
686
    if (count($name_type_designation_refs) > 0) {
687
      $render_array['nomenclatural_act']['name_type_designations'] = compose_name_type_designations($name_type_designation_refs);
688
      $render_array['nomenclatural_act']['name_type_designations']['#prefix'] = '<p class="name_type_designations">';
689
      $render_array['nomenclatural_act']['name_type_designations']['#suffix'] = '</p>';
690
      $render_array['nomenclatural_act']['name_type_designations']['#weight'] = 20;
691
    }
692
    if (count($field_unit_uuids) > 0) {
693
      $specimen_type_designations_array = compose_specimen_type_designations($specimen_type_designation_refs, true);
694
      $render_array['nomenclatural_act']['specimen_type_designations'] = $specimen_type_designations_array['type_designations'];
695
      $render_array['map'] = $specimen_type_designations_array['map'];
696
      $render_array['map']['#weight'] = $render_array['nomenclatural_act']['#weight'] + 20;
697
    }
698
  }
699

    
700
  // name relations
701
  if($name_relations){
702
    $render_array['nomenclatural_act']['name_relations'] = compose_name_relationships_list($name_relations, $registration_dto->nameRef->uuid, null);
703
    $render_array['nomenclatural_act']['name_relations']['#weight'] = 10;
704
  }
705

    
706
  // citation
707
  if ($with_citation) {
708
    $render_array['citation'] = markup_to_render_array(
709
      "<div class=\"citation nomenclatural_act_citation" . html_class_attribute_ref(new TypedEntityReference("Reference", $registration_dto->citationUuid)) . "\">"
710
      . "<span class=\"label\">published in: </span>"
711
      . $registration_dto->bibliographicInRefCitationString
712
      . l(custom_icon_font_markup('icon-interal-link-alt-solid', array('class' => array('superscript'))), path_to_reference($registration_dto->citationUuid), array('html' => true))
713
      . "</div>",
714
      $render_array['nomenclatural_act']['#weight'] + 10 );
715
  }
716

    
717
  $render_array['nomenclatural_act']['footnotes'] = markup_to_render_array(render_footnotes(),100);
718

    
719
  // END of nomenclatural act block
720
  RenderHints::setFootnoteListKey($last_footnote_listkey );
721

    
722
  if($typified_name){
723
    $render_array['typified_name'] = markup_to_render_array('<p class="typified-name">for ' . render_taxon_or_name($typified_name, url(path_to_name($typified_name->uuid))) . '</p>', 40);
724
  }
725

    
726
  // registration date and office
727
  $registration_date_insitute_markup = render_registration_date_and_institute($registration_dto);
728
  if($registration_date_insitute_markup){
729
    $render_array['registration_date_and_institute'] = markup_to_render_array(
730
      $registration_date_insitute_markup . '</p>',
731
      100);
732
  }
733

    
734
  $render_array['page_footnotes'] = markup_to_render_array(render_footnotes(), 110);
735

    
736
  return $render_array;
737
}
738

    
739

    
740
/**
741
 * Composes a compact representation for a registrationDTO object
742
 *
743
 * Registrations which are not yet published are suppressed.
744
 *
745
 * @param $registration_dto
746
 * @param $style string
747
 *   The style of how to compose the 'identifier' and 'registration_date_and_institute' part with the summary
748
 *   - 'citation': Similar to the arrearance of nomenclatural acts in print media
749
 *   - 'list-item' : style suitable for result lists etc
750
 *
751
 * @return array
752
 *    A drupal render array with the elements:
753
 *    - 'registration-metadata' when $style == 'list-item'
754
 *    - 'summary'
755
 * @ingroup compose
756
 */
757
function compose_registration_dto_compact($registration_dto, $style = 'citation', $tag_enclosing_summary = 'p')
758
{
759
  $render_array = array();
760
  $media_link_map = array();
761

    
762
  if(!(isset($registration_dto->identifier) && $registration_dto->status == 'PUBLISHED')){
763
    return $render_array;
764
  }
765

    
766
  $registration_date_insitute_markup = render_registration_date_and_institute($registration_dto, 'span');
767
  $itentifier_markup = l($registration_dto->identifier, path_to_registration($registration_dto->identifier), array('attributes' => array('class' => array('identifier'))));
768

    
769
  $tagged_text_options = array();
770
  if(isset($registration_dto->nameRef)){
771
    $tagged_text_options[] = array(
772
      'filter-type' => 'name',
773
      'prefix' => '<span class="registered_name">',
774
      'suffix' => '</span>',
775
    );
776
  } else {
777
    $tagged_text_options[] = array(
778
      'filter-type' => 'name',
779
      'prefix' => '<span class="referenced_typified_name">',
780
      'suffix' => '</span>',
781
    );
782
  }
783
  cdm_tagged_text_add_options($registration_dto->summaryTaggedText, $tagged_text_options);
784
  $taggged_text_expanded = cdm_tagged_text_expand_entity_references($registration_dto->summaryTaggedText);
785
  foreach ($taggged_text_expanded  as $tagged_text){
786
    if(isset($tagged_text->entityReference->type) && $tagged_text->entityReference->type == 'SpecimenTypeDesignation') {
787
      $mediaDTOs = cdm_ws_get('typedesignation/$0/media', array($tagged_text->entityReference->uuid));
788
      if(isset($mediaDTOs[0]->uri)){
789
        $media_url_key = '{link-' . $mediaDTOs[0]->uuid . '}';
790
        $tagged_text->text = str_replace('[icon]', '[icon]' . $media_url_key, $tagged_text->text);
791
        $media_link_map[$media_url_key] =  cdm_external_uri($mediaDTOs[0]->uri, true);
792
      }
793
    }
794
  }
795
  $registation_markup = cdm_tagged_text_to_markup($taggged_text_expanded);
796
  foreach($media_link_map as $media_url_key => $link){
797
    $registation_markup = str_replace($media_url_key, $link, $registation_markup);
798
  }
799
  if($style == 'citation') {
800
    $registation_markup = $registation_markup . ' ' . $itentifier_markup . ' ' . $registration_date_insitute_markup;
801
  } else {
802
    $render_array['registration-metadata'] = markup_to_render_array('<div class="registration-metadata">' . $itentifier_markup . ' ' . $registration_date_insitute_markup. "</div>", -10);
803
  }
804
  $render_array['summary'] = markup_to_render_array('<' . $tag_enclosing_summary . ' class="registration-summary">' . $registation_markup . '</' . $tag_enclosing_summary . '>', 0);
805

    
806
  return $render_array;
807
}
808

    
809

    
810
/**
811
 * Renders the registrationDate and institutionTitleCache of the $registration_dto as markup.
812
 *
813
 * @param $registration_dto
814
 * @return string
815
 *    The markup or an empty string
816
 */
817
function render_registration_date_and_institute($registration_dto, $enclosing_tag = 'p') {
818
  $registration_date_institute_markup = '';
819
  if ($registration_dto->registrationDate) {
820
    $date_string = format_datetime($registration_dto->registrationDate);
821
    if (isset($registration_dto->institutionTitleCache) && $registration_dto->institutionTitleCache) {
822
      $registration_date_institute_markup =
823
        t("Registration on @date in @institution", array(
824
          '@date' => $date_string,
825
          '@institution' => $registration_dto->institutionTitleCache,
826
        ));
827
    } else {
828
      $registration_date_institute_markup =
829
        t("Registration on @date", array(
830
          '@date' => $date_string
831
        ));
832
    }
833
    $registration_date_institute_markup = '<' .$enclosing_tag . ' class="registration-date-and-institute">'. $registration_date_institute_markup . '</' .$enclosing_tag . '>';
834
  }
835
  return $registration_date_institute_markup;
836
}
837

    
838

    
839
/**
840
 * @param $registrations
841
 * @return string
842
 */
843
function render_registrations($registrations)
844
{
845
  $registration_markup = '';
846
  $registration_markup_array = array();
847
  if ($registrations) {
848
    foreach ($registrations as $reg) {
849
      $registration_markup_array[] = render_registration($reg);
850
    }
851
    $registration_markup = " Registration" . (count($registration_markup_array) > 1 ? 's: ' : ': ')
852
      . join(', ', $registration_markup_array);
853
  }
854
  return $registration_markup;
855
}
856

    
857

    
858
/**
859
 * Renders a registration
860
 *
861
 * TODO replace by compose_registration_dto_compact
862
 * @param $registration
863
 */
864
function render_registration($registration){
865
  $markup = '';
866

    
867
  if(isset($registration->identifier) && $registration->status == 'PUBLISHED'){
868
    $office_class_attribute = '';
869
    if(isset($registration->institution->titleCache)){
870
      $office_class_attribute = registration_intitute_class_attribute($registration);
871
    }
872
    $markup = "<span class=\"registration $office_class_attribute\">" . l($registration->identifier, path_to_registration($registration->identifier)) . ', '
873
      .  preg_replace('/^([^T]*)(.*)$/', '${1}', $registration->registrationDate)
874
      . '</span>';
875
  }
876
  return $markup;
877
}
878

    
879
/**
880
 * @param $registration
881
 * @return string
882
 */
883
function registration_intitute_class_attribute($registration_dto)
884
{
885
  if(isset($registration_dto->institutionTitleCache)){
886
    $institutionTitleCache = $registration_dto->institutionTitleCache;
887
  } else {
888
    // fall back option to also support cdm entities
889
    $institutionTitleCache = @$registration_dto->institution->titleCache;
890
  }
891
  return $institutionTitleCache ? 'registration-' . strtolower(preg_replace('/[^a-zA-Z0-9]/', '-', $institutionTitleCache)) : '';
892
}
893

    
894

    
895
/**
896
 * Renders and array of CDM TypeDesignations
897
 *
898
 *  - NameTypeDesignation
899
 *  - SpecimenTypeDesignation
900
 *  - TextualTypeDesignation
901
 *
902
 * @param object $type_designations an array of cdm TypeDesignation entities
903
 *  to render
904
 * @param string $enclosing_tag the tag element type to enclose the whole list
905
 *  of type designation with. By default this DOM element is <ul>
906
 * @param string $element_tag the tag element type to be used for each
907
 *  type designation item.
908
 * @param bool $link_to_specimen_page whether a specimen in type designation element
909
 *  should be a link or not.
910
 *
911
 * @return string The markup.
912
 *
913
 * @InGroup Render
914
 */
915
function render_type_designations($type_designations, $enclosing_tag = 'ul', $element_tag =  'li', $link_to_specimen_page = true) {
916

    
917
  // need to add element to render path since type designations
918
  // need other name render template
919
  RenderHints::pushToRenderStack('typedesignations');
920

    
921
  $out = '<' . $enclosing_tag .' class="typeDesignations">';
922
  $specimen_type_designations = array();
923
  $name_type_designations = array();
924
  $textual_type_designations = array();
925
  $separator = ',';
926

    
927
  foreach ($type_designations as $type_designation) {
928
    switch ($type_designation->class) {
929
      case 'SpecimenTypeDesignation':
930
        $specimen_type_designations[] = $type_designation;
931
        break;
932
      case 'NameTypeDesignation':
933
        $name_type_designations[] = $type_designation;
934
        break;
935
      case 'TextualTypeDesignation':
936
        $textual_type_designations[] = $type_designation;
937
        break;
938
      default:  throw new Exception('Unknown type designation class: ' . $type_designation->class);
939
    }
940
  }
941

    
942
  // NameTypeDesignation ..................................
943
  if(!empty($name_type_designations)){
944
    usort($name_type_designations, "compare_type_designations_by_status");
945
    foreach($name_type_designations as $name_type_designation){
946
      if ($name_type_designation->notDesignated) {
947
        $out .= '<'. $element_tag .' class="' . html_class_attribute_ref($name_type_designation) . '">' .  type_designation_status_label_markup($name_type_designation)  . ': '
948
          . t('not designated') . '</'. $element_tag .'>';
949
      }
950
      elseif (isset($name_type_designation->typeName)) {
951
        $link_to_name_page = url(path_to_name($name_type_designation->typeName->uuid));
952
        $out .= '<'. $element_tag .' class="' . html_class_attribute_ref($name_type_designation) . '">' .  type_designation_status_label_markup($name_type_designation) ;
953

    
954
        if (!empty($name_type_designation->source->citation)) {
955
          $out .= type_designation_citation_layout($name_type_designation, $separator); // TODO type_designation_citation_layout() needs most probably to be replaced
956

    
957
        }
958
        $referenceUri = '';
959
        if (isset($name_type_designation->typeName->nomenclaturalSource->citation)) {
960
          $referenceUri = url(path_to_reference($name_type_designation->typeName->nomenclaturalSource->citation->uuid));
961
        }
962
        $out .= ': ' . render_taxon_or_name($name_type_designation->typeName, $link_to_name_page, $referenceUri, TRUE, TRUE);
963
      }
964
      RenderHints::setAnnotationsAndSourceConfig(annotations_and_sources_config_typedesignations());
965
      $annotations_and_sources = handle_annotations_and_sources($name_type_designation);
966
      $out .= $annotations_and_sources['foot_note_keys'];
967
    }
968
  } // END NameTypeDesignation
969

    
970
  // SpecimenTypeDesignation ...................................
971
  if (!empty($specimen_type_designations)) {
972
    usort($specimen_type_designations, "compare_specimen_type_designation");
973
    foreach ($specimen_type_designations as $specimen_type_designation) {
974
      $type_citation_markup = '';
975

    
976
      if (!empty($specimen_type_designation->source->citation)) {
977

    
978
        $citation_footnote_str = cdm_reference_markup($specimen_type_designation->source->citation, null, false, true);
979
        $author_team = cdm_ws_get(CDM_WS_REFERENCE_AUTHORTEAM, $specimen_type_designation->source->citation->uuid);
980

    
981
        if (!empty($author_team->titleCache)) {
982
          $year = @timePeriodToString($specimen_type_designation->source->citation->datePublished, true, 'YYYY');
983
          $authorteam_str = $author_team->titleCache . ($year ? ' ' : '') . $year;
984
          if ($authorteam_str == $specimen_type_designation->source->citation->titleCache) {
985
            $citation_footnote_str = '';
986
          }
987
        } else {
988
          $authorteam_str = $citation_footnote_str;
989
          // no need for a footnote in case in case it is used as replacement for missing author teams
990
          $citation_footnote_str = '';
991
        }
992

    
993
        // for being registered a typedesignation MUST HAVE a citation, so it is save to handle the
994
        // Registration output in if condition checking if the citation is present
995
        $registration_markup = render_registrations($specimen_type_designation->registrations);
996
        $citation_footnote_str .= ($citation_footnote_str ? ' ' : '') . $registration_markup;
997

    
998
        $footnote_key_markup = '';
999
        if ($citation_footnote_str) {
1000
          // footnotes should be rendered in the parent element so we
1001
          // are relying on the FootnoteListKey set there
1002
          $_fkey2 = FootnoteManager::addNewFootnote(RenderHints::getFootnoteListKey(), $citation_footnote_str);
1003
          $footnote_key_markup = render_footnote_key($_fkey2, $separator, TRUE);
1004
        }
1005

    
1006
        $type_citation_markup .= '&nbsp;(' . t('designated by') . '&nbsp;<span class="typeReference">' . $authorteam_str . '</span>';
1007
        if (!empty($specimen_type_designation->source->citationMicroReference)) {
1008
          $type_citation_markup .= ': ' . trim($specimen_type_designation->source->citationMicroReference);
1009
        }
1010
        $type_citation_markup .= $footnote_key_markup . ')';
1011

    
1012
      }
1013

    
1014

    
1015
      $out .= '<'. $element_tag .' class="' . html_class_attribute_ref($specimen_type_designation) . '">';
1016
      $out .= type_designation_status_label_markup($specimen_type_designation) . $type_citation_markup;
1017

    
1018

    
1019
      $derivedUnitFacadeInstance = null;
1020
      if (isset($specimen_type_designation->typeSpecimen)) {
1021
        $derivedUnitFacadeInstance = cdm_ws_get(CDM_WS_DERIVEDUNIT_FACADE, $specimen_type_designation->typeSpecimen->uuid);
1022
      }
1023

    
1024
      if (!empty($derivedUnitFacadeInstance->titleCache)) {
1025
        $specimen_markup = $derivedUnitFacadeInstance->titleCache;
1026
        if($link_to_specimen_page && isset($derivedUnitFacadeInstance->specimenLabel) && $derivedUnitFacadeInstance->specimenLabel){
1027
          $specimen_markup = str_replace($derivedUnitFacadeInstance->specimenLabel, l($derivedUnitFacadeInstance->specimenLabel, path_to_specimen($specimen_type_designation->typeSpecimen->uuid)), $specimen_markup);
1028
        }
1029
        RenderHints::setAnnotationsAndSourceConfig(annotations_and_sources_config_typedesignations());
1030
        $annotations_and_sources = handle_annotations_and_sources($derivedUnitFacadeInstance);
1031
        $out .= ': <span class="' . html_class_attribute_ref($specimen_type_designation->typeSpecimen) . '">'
1032
          . $specimen_markup
1033
          . '</span>'; // . ': ' . theme('cdm_specimen', array('specimenTypeDesignation' => $derivedUnitFacadeInstance));
1034
        if(!empty($derivedUnitFacadeInstance->preferredStableUri)){
1035
          $out .= ' ' . l($derivedUnitFacadeInstance->preferredStableUri, $derivedUnitFacadeInstance->preferredStableUri, array('absolute' => true));
1036
        }
1037
        $out .= $annotations_and_sources['foot_note_keys'];
1038
      }
1039
      $out .= '</'. $element_tag .'>';
1040
    }
1041
  } // END Specimen type designations
1042

    
1043
  // TextualTypeDesignation .........................
1044
  usort($textual_type_designations, 'compare_textual_type_designation');
1045
  if(!empty($textual_type_designations)) {
1046
      RenderHints::setAnnotationsAndSourceConfig([
1047
          // these settings differ from those provided by annotations_and_sources_config_typedesignations()
1048
          // TODO is this by purpose? please document the reason for the difference
1049
          'sources_as_content' => false, // as footnotes
1050
          'link_to_name_used_in_source' => false,
1051
          'link_to_reference' => true,
1052
          'add_footnote_keys' => true,
1053
          'bibliography_aware' => false
1054
        ]
1055
      );
1056
    foreach ($textual_type_designations as $textual_type_designation) {
1057
      $annotations_and_sources = handle_annotations_and_sources($textual_type_designation);
1058
      $encasement =  $textual_type_designation->verbatim ? '"' : '';
1059
      $out .= '<' . $element_tag . ' class="' . html_class_attribute_ref($textual_type_designation) . '">' . type_designation_status_label_markup(null)
1060
        . ': ' .  $encasement . trim($textual_type_designation->text_L10n->text) . $encasement .  $annotations_and_sources['foot_note_keys'] .'</' . $element_tag . '>';
1061
//      if(is_array( $annotations_and_sources['source_references'])){
1062
//        $citation_markup = join(', ', $annotations_and_sources['source_references']);
1063
//      }
1064
//      $out .= $citation_markup;
1065
    }
1066
  }
1067

    
1068
  // Footnotes for citations, collection acronyms.
1069
  // footnotes should be rendered in the parent element so we
1070
  // are relying on the FootnoteListKey set there
1071
  $_fkey = FootnoteManager::addNewFootnote(
1072
    RenderHints::getFootnoteListKey(),
1073
    (isset($derivedUnitFacadeInstance->collection->titleCache) ? $derivedUnitFacadeInstance->collection->titleCache : FALSE)
1074
  );
1075
  $out .= render_footnote_key($_fkey, $separator);
1076
  $out .= '</' . $enclosing_tag .'>';
1077

    
1078
  RenderHints::popFromRenderStack();
1079

    
1080
  return $out;
1081
}
1082

    
1083

    
1084
/**
1085
 * Composes the textual representation for the type designation of taxon name identified by the uuid in with a map for the location data.
1086
 *
1087
 * @param $taxon_name_uuid
1088
 * @param $show_specimen_details
1089
 * @return array
1090
 *    A drupal render array with the following elements:
1091
 *    - 'type_designations'
1092
 *    - 'map'
1093
 *    - 'specimens'
1094
 *
1095
 * @ingroup compose
1096
 */
1097
function compose_type_designations($taxon_name_uuid, $show_specimen_details = false)
1098
{
1099
  $render_array = array(
1100
    'type_designations' => array(),
1101
    'map' => array(),
1102
    );
1103
  $type_designations = cdm_ws_get(CDM_WS_PORTAL_NAME_TYPEDESIGNATIONS, $taxon_name_uuid);
1104
  if ($type_designations) {
1105
    usort($type_designations, 'compare_specimen_type_designation');
1106
    $render_array['type_designations'] = markup_to_render_array(
1107
      render_type_designations($type_designations, 'div', 'div')
1108
    );
1109

    
1110
    $render_array['map'] = compose_type_designations_map($type_designations);
1111
  }
1112
  return $render_array;
1113
}
1114

    
1115

    
1116
/**
1117
 * Composes the TypedEntityReference to name type designations passed as associatve array.
1118
 *
1119
 * @param $type_entity_refs_by_status array
1120
 *   an associative array of name type type => TypedEntityReference for name type designations as
1121
 *   produced by the eu.etaxonomy.cdm.api.service.name.TypeDesignationSetManager
1122
 *
1123
 * @ingroup compose
1124
 */
1125
function compose_name_type_designations($type_entity_refs_by_status){
1126
  $render_array = array();
1127
  $preferredStableUri = '';
1128
  foreach($type_entity_refs_by_status as $type_status => $name_type_entityRefs){
1129
    foreach ($name_type_entityRefs as $name_type_entity_ref){
1130
      $type_designation = cdm_ws_get(CDM_WS_TYPEDESIGNATION, array($name_type_entity_ref->uuid, 'preferredUri'));
1131
      $footnote_keys = '';
1132

    
1133
      if(isset($type_designation->typeSpecimen->preferredStableUri) && $type_designation->typeSpecimen->preferredStableUri){
1134
        $preferredStableUri = $type_designation->typeSpecimen->preferredStableUri;
1135
      }
1136
      // annotations and sources for the $derived_unit_facade_dto
1137
      RenderHints::setAnnotationsAndSourceConfig(annotations_and_sources_config_typedesignations());
1138
      $annotations_and_sources = handle_annotations_and_sources($name_type_entity_ref);
1139

    
1140
      $render_array[] = markup_to_render_array('<div class="name_type_designation ' . html_class_attribute_ref($name_type_entity_ref)  . '"><span class="type-status">'. ucfirst($type_status) . "</span>: "
1141
        . $name_type_entity_ref->label
1142
        . ($preferredStableUri ? " ". l($preferredStableUri,  $preferredStableUri) : '')
1143
        . $annotations_and_sources['foot_note_keys']
1144
        . '</div>');
1145
      }
1146
  }
1147
  return $render_array;
1148
}
1149

    
1150
/**
1151
 * Composes the specimen type designations with map from the the $type_entity_refs
1152
 *
1153
 * @param $type_entity_refs array
1154
 *   an associative array of specimen type type => TypedEntityReference for specimen type designations as
1155
 *   produced by the eu.etaxonomy.cdm.api.service.name.TypeDesignationSetManager
1156
 *
1157
 * @param $show_media_specimen
1158
 * @return array
1159
 *    A drupal render array with the following elements:
1160
 *    - 'type_designations'
1161
 *    - 'map'
1162
 *
1163
 * @ingroup compose
1164
 *
1165
 */
1166
function compose_specimen_type_designations($type_entity_refs, $show_media_specimen){
1167

    
1168
  $render_array = array();
1169

    
1170
  $type_designation_list = array();
1171
  uksort($type_entity_refs, "compare_type_designation_status_labels");
1172
  foreach($type_entity_refs as $type_status => $type_designation_entity_refs){
1173
    foreach($type_designation_entity_refs as $type_designation_entity_ref){
1174

    
1175
      $type_designation = cdm_ws_get(CDM_WS_PORTAL_TYPEDESIGNATION, array($type_designation_entity_ref->uuid));
1176
      $type_designation_list[] = $type_designation; // collect for the map
1177

    
1178
      $derived_unit_facade_dto = cdm_ws_get(CDM_WS_PORTAL_DERIVEDUNIT_FACADE, $type_designation->typeSpecimen->uuid);
1179
      // the media specimen is not contained in the $type_designation returned by CDM_PORTAL_TYPEDESIGNATION, so we need to fetch it separately
1180
      $mediaSpecimen = cdm_ws_get(CDM_WS_PORTAL_OCCURRENCE, array($type_designation->typeSpecimen->uuid, 'mediaSpecimen'));
1181

    
1182

    
1183
      $preferredStableUri = '';
1184
      $citation_markup = '';
1185
      $media = '';
1186

    
1187
      // annotations and sources for the $derived_unit_facade_dto
1188
      RenderHints::setAnnotationsAndSourceConfig(annotations_and_sources_config_typedesignations());
1189
      $annotations_and_sources = handle_annotations_and_sources($derived_unit_facade_dto);
1190
      $source_citations = $annotations_and_sources['source_references'];
1191
      $foot_note_keys = $annotations_and_sources['foot_note_keys'];
1192

    
1193
      // preferredStableUri
1194
      if(isset($type_designation->typeSpecimen->preferredStableUri) && $type_designation->typeSpecimen->preferredStableUri){
1195
        $preferredStableUri = $type_designation->typeSpecimen->preferredStableUri;
1196
      }
1197

    
1198
      if($show_media_specimen && $mediaSpecimen){
1199
        // compose output
1200
        // mediaURI
1201
        if(isset($mediaSpecimen->representations[0])) {
1202
          $gallery_settings = getGallerySettings(CDM_DATAPORTAL_SPECIMEN_GALLERY_NAME);
1203
          $captionElements = array(
1204
            '#uri' => t('open media'),
1205
            'elements' => array('-none-'),
1206
            'sources_as_content' => true
1207
          );
1208
          $media = compose_cdm_media_gallerie(array(
1209
            'mediaList' => array($mediaSpecimen),
1210
            'galleryName' => CDM_DATAPORTAL_TYPE_SPECIMEN_GALLERY_NAME . '_' . $type_designation_entity_ref->uuid,
1211
            'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
1212
            'cols' => $gallery_settings['cdm_dataportal_media_cols'],
1213
            'captionElements' => $captionElements,
1214
          ));
1215
        }
1216
        // citation and detail for the media specimen
1217
        RenderHints::setAnnotationsAndSourceConfig(annotations_and_sources_config_typedesignations());
1218
        $annotations_and_sources = handle_annotations_and_sources($mediaSpecimen);
1219
        if(is_array( $annotations_and_sources['source_references'])){
1220
          $source_citations = array_merge($source_citations, $annotations_and_sources['source_references']);
1221
        }
1222
        if($annotations_and_sources['foot_note_keys']){
1223
          $foot_note_keys .= ', ' . $annotations_and_sources['foot_note_keys'];
1224
        }
1225
      }
1226

    
1227
      $citation_markup = join(', ', $source_citations);
1228

    
1229
      $specimen_markup = $derived_unit_facade_dto->titleCache;
1230
      if(isset($derived_unit_facade_dto->specimenLabel) && $derived_unit_facade_dto->specimenLabel){
1231
        $specimen_markup = str_replace(
1232
          $derived_unit_facade_dto->specimenLabel,
1233
          l($derived_unit_facade_dto->specimenLabel, path_to_specimen($type_designation->typeSpecimen->uuid)), $specimen_markup);
1234
      }
1235

    
1236
      $type_designation_render_array = markup_to_render_array(
1237
        '<div class="type_designation_entity_ref ' . html_class_attribute_ref($type_designation_entity_ref)  . '">
1238
          <span class="type-status">' . ucfirst($type_status) . "</span>: "
1239
        . $specimen_markup . $foot_note_keys
1240
        . ($citation_markup ? ' '. $citation_markup : '')
1241
        . ($preferredStableUri ? " ". l($preferredStableUri,  $preferredStableUri) : '')
1242
        . $media
1243
        . '</div>');
1244

    
1245
      $render_array['type_designations'][] = $type_designation_render_array;
1246
    }
1247
  }
1248
  if(count($type_designation_list) > 0 ){
1249
    $render_array['map'] = compose_type_designations_map($type_designation_list);
1250
  } else {
1251
    $render_array['map'] = array();
1252
  }
1253
  return $render_array;
1254
}
1255

    
1256
/**
1257
 * Creates the markup for the given name relationship
1258
 *
1259
 * @param $name_rel the CDM NameRelationship entity
1260
 * @param $current_name_uuid
1261
 * @param $current_taxon_uuid
1262
 * @param $suppress_if_current_name_is_source // FIXME UNUSED !!!!
1263
 * @param $show_name_cache_only
1264
 *    The nameCache will be shown instead of the titleCache if this parameter is true.
1265
 * @return null|string
1266
 *    The markup or null
1267
 */
1268
function name_relationship_markup($name_rel, $current_name_uuid, $current_taxon_uuid, $show_name_cache_only = false){
1269

    
1270
  $relationship_markup = null;
1271

    
1272
  $current_name_is_toName = $current_name_uuid == $name_rel->toName->uuid;
1273

    
1274
  if($current_name_is_toName){
1275
    $name = $name_rel->fromName;
1276
  } else {
1277
    $name = $name_rel->toName;
1278
  }
1279

    
1280
  cdm_load_tagged_full_title($name);
1281

    
1282
  $highlited_synonym_uuid = isset ($name->taxonBases[0]->uuid) ? $name->taxonBases[0]->uuid : '';
1283
  if(!$show_name_cache_only){
1284
    $relationship_markup = render_taxon_or_name($name,
1285
      url(path_to_name($name->uuid, $current_taxon_uuid, $highlited_synonym_uuid, false))
1286
    );
1287
  } else {
1288
    $relationship_markup = l(
1289
      '<span class="' . html_class_attribute_ref($name) . '"">' . $name->nameCache . '</span>',
1290
      path_to_name($name->uuid, $current_taxon_uuid, $highlited_synonym_uuid, false),
1291
      array('html' => true)
1292
    );
1293
  }
1294

    
1295
  return $relationship_markup;
1296
}
1297

    
1298

    
1299
/**
1300
 * Composes an inline representation of selected name relationships
1301
 *
1302
 * The output of this function will be usually appended to taxon name representations.
1303
 * Only the following types are displayed: LATER_HOMONYM, TREATED_AS_LATER_HOMONYM, BLOCKING_NAME_FOR, ORTHOGRAPHIC_VARIANT
1304
 *
1305
 * LATER_HOMONYM, TREATED_AS_LATER_HOMONYM, BLOCKING_NAME_FOR are displayed as
1306
 * non {titleCache} nec {titleCache} nec {titleCache} whereas the related names
1307
 * are ordered alphabetically.
1308
 *
1309
 * ORTHOGRAPHIC_VARIANT is displayed as 'ort. var. {nameCache}'
1310
 *
1311
 * Related issues:
1312
 *   - https://dev.e-taxonomy.eu/redmine/issues/5697 "Show name conserved against as [non xxx]"
1313
 *   - https://dev.e-taxonomy.eu/redmine/issues/6678 "How to correctly show name relationship "orth. var." in dataportal"
1314
 *   - https://dev.e-taxonomy.eu/redmine/issues/5857
1315
 *   - https://dev.e-taxonomy.eu/redmine/issues/2001 "[Cichorieae Portal] Name Relationship -> blocking name are not shown"
1316
 *
1317
 * @param $name_relations
1318
 *    The list of CDM NameRelationsips
1319
 * @param $current_name_uuid
1320
 *    The Uuid of the name for which the relations are to be rendered, the current name will be hidden when
1321
 *    rendering the relation an only the other name is shown. Parameter is REQUIRED.
1322
 * @param $suppress_if_current_name_is_source
1323
 *    The display of the relation will be
1324
 *    suppressed is the current name is on the source of the relation edge.
1325
 *    That is if it is on the from side of the relation. Except for 'blocking name for' which is
1326
 *    an inverse relation. For this relation type the toName is taken in to account.
1327
 * @param $current_taxon_uuid
1328
 *    The taxon to be omitted from related taxa. This is only used to create links, see path_to_name()
1329
 * @return array
1330
 *    A drupal render array
1331
 *
1332
 * @ingroup Compose
1333
 */
1334
function compose_name_relationships_inline($name_relations, $current_name_uuid, $current_taxon_uuid, $suppress_if_current_name_is_source = true) {
1335

    
1336
  RenderHints::pushToRenderStack('homonym');
1337
  // the render stack element homonyms is being used in the default render templates !!!, see CDM_NAME_RENDER_TEMPLATES_DEFAULT
1338

    
1339
  $selected_name_rel_uuids = variable_get(CDM_NAME_RELATIONSHIP_INLINE_TYPES, unserialize(CDM_NAME_RELATIONSHIP_INLINE_TYPES_DEFAULT));
1340
  $name_rel_type_filter = array('direct' => array(), 'inverse' => array());
1341
  foreach ($selected_name_rel_uuids as $uuid){
1342
    $name_rel_type_filter['direct'][$uuid] = $uuid;
1343
    if($uuid != UUID_NAMERELATIONSHIPTYPE_MISSPELLING){
1344
      $name_rel_type_filter['inverse'][$uuid] = $uuid;
1345
    }
1346
  }
1347

    
1348
  $list_prefix = '<span class="name_relationships">[';
1349
  $list_suffix = ']</span>';
1350
  $item_prefix = '<span class="item">';
1351
  $item_suffix = '</span> ';
1352
  $render_array = compose_name_relationships($name_relations, $name_rel_type_filter, $current_name_uuid, $current_taxon_uuid, $list_prefix, $list_suffix, $item_prefix, $item_suffix);
1353

    
1354
  // remove the glue space from the last item element which has been added by the $item_suffix = '</span> '
1355
  $items_ctn = count($render_array['list']['items']);
1356
  if($items_ctn){
1357
    $render_array['list']['items'][$items_ctn - 1]['#suffix'] = '</span>';
1358
  }
1359

    
1360
  RenderHints::popFromRenderStack();
1361
  return $render_array;
1362
}
1363

    
1364
/**
1365
 * Composes an list representation of the name relationships.
1366
 *
1367
 * The output of this function will be usually appended to taxon name representations.
1368
 *
1369
 * Related issues:
1370
 *   - https://dev.e-taxonomy.eu/redmine/issues/5697 "Show name conserved against as [non xxx]"
1371
 *   - https://dev.e-taxonomy.eu/redmine/issues/6678 "How to correctly show name relationship "orth. var." in dataportal"
1372
 *   - https://dev.e-taxonomy.eu/redmine/issues/5857
1373
 *
1374
 * @param $name_relations
1375
 *    The list of CDM NameRelationsips
1376
 * @param $current_name_uuid
1377
 *    The Uuid of the name for which the relations are to be rendered, the current name will be hidden when
1378
 *    rendering the relation an only the other name is shown. Parameter is REQUIRED.
1379
 * @param $current_taxon_uuid
1380
 *    The taxon to be omitted from related taxa. This is only used to create links, see path_to_name()
1381
 * @return array
1382
 *    A drupal render array
1383
 *
1384
 * @ingroup Compose
1385
 */
1386
function compose_name_relationships_list($name_relations, $current_name_uuid, $current_taxon_uuid) {
1387

    
1388
  // $ordered_name_relation_type_uuids = array_keys(cdm_terms_by_type_as_option('NameRelationshipType', CDM_ORDER_BY_ORDER_INDEX_ASC));
1389

    
1390
  $key = 'name_relationships';
1391
  RenderHints::pushToRenderStack($key);
1392
  if(RenderHints::isUnsetFootnoteListKey()){
1393
    RenderHints::setFootnoteListKey($key);
1394
  }
1395
  // the render stack element homonyms is being used in the default render templates !!!, see CDM_NAME_RENDER_TEMPLATES_DEFAULT
1396

    
1397
  $selected_name_rel_uuids = variable_get(CDM_NAME_RELATIONSHIP_LIST_TYPES, cdm_vocabulary_as_defaults(UUID_NAME_RELATIONSHIP_TYPE));
1398
  $name_rel_type_filter = array('direct' => array(), 'inverse' => array());
1399
  foreach ($selected_name_rel_uuids as $uuid){
1400
    $name_rel_type_filter['direct'][$uuid] = $uuid;
1401
    $name_rel_type_filter['inverse'][$uuid] = $uuid;
1402
  }
1403

    
1404
  $list_prefix = '<div class="relationships_list name_relationships">';
1405
  $list_suffix = '</div>';
1406
  $item_prefix = '<div class="item">';
1407
  $item_suffix = '</div>';
1408

    
1409
  $render_array = compose_name_relationships($name_relations, $name_rel_type_filter, $current_name_uuid, $current_taxon_uuid, $list_prefix, $list_suffix, $item_prefix, $item_suffix);
1410

    
1411
  RenderHints::popFromRenderStack();
1412
  if(RenderHints::getFootnoteListKey() == $key) {
1413
    $render_array['footnotes'] = markup_to_render_array(render_footnotes(RenderHints::getFootnoteListKey()));
1414
    RenderHints::clearFootnoteListKey();
1415
  }
1416
  return $render_array;
1417
}
1418

    
1419
/**
1420
 * @param $name_relations
1421
 * @param $name_rel_type_filter
1422
 *   Associative array with two keys:
1423
 *   - 'direct': the relationship type uuids for the direct direction of the relation edge to be included
1424
 *   - 'inverse': the relationship type uuids for the direct direction of the relation edge to be included
1425
 * @param $current_name_uuid
1426
 * @param $current_taxon_uuid
1427
 * @param $list_prefix
1428
 * @param $list_suffix
1429
 * @param $item_prefix
1430
 * @param $item_suffix
1431
 * @return array
1432
 *
1433
 * @ingroup Compose
1434
 */
1435
function compose_name_relationships($name_relations, $name_rel_type_filter, $current_name_uuid, $current_taxon_uuid,
1436
                                    $list_prefix, $list_suffix, $item_prefix, $item_suffix)
1437
{
1438
  $non_nec_name_reltype_uuids = array(UUID_NAMERELATIONSHIPTYPE_LATER_HOMONYM,
1439
    UUID_NAMERELATIONSHIPTYPE_TREATED_AS_LATER_HOMONYM,
1440
    UUID_NAMERELATIONSHIPTYPE_CONSERVED_AGAINST,
1441
    UUID_NAMERELATIONSHIPTYPE_MISSPELLING,
1442
    UUID_NAMERELATIONSHIPTYPE_BLOCKING_NAME_FOR);
1443

    
1444
  $render_array = array(
1445
    'list' => array(
1446
      '#prefix' => $list_prefix,
1447
      '#suffix' => $list_suffix,
1448
      'items' => array()
1449
    ),
1450
    'footnotes' => array()
1451
  );
1452

    
1453
  if ($name_relations) {
1454

    
1455
    // remove all relations which are not selected in the settings and
1456
    // separate all LATER_HOMONYM, TREATED_AS_LATER_HOMONYM, BLOCKING_NAME_FOR relations and ORTHOGRAPHIC_VARIANTs
1457
    // for special handling
1458
    $filtered_name_rels = array();
1459
    $non_nec_name_rels = array();
1460
    $orthographic_variants = array();
1461
    foreach ($name_relations as $name_rel) {
1462
      $rel_type_uuid = $name_rel->type->uuid;
1463
      $is_inverse_relation = $current_name_uuid == $name_rel->toName->uuid;
1464
      if ((!$is_inverse_relation && isset($name_rel_type_filter['direct'][$rel_type_uuid]) && $name_rel_type_filter['direct'][$rel_type_uuid])
1465
        ||($is_inverse_relation && isset($name_rel_type_filter['inverse'][$rel_type_uuid]) && $name_rel_type_filter['inverse'][$rel_type_uuid])) {
1466

    
1467
        if (array_search($rel_type_uuid, $non_nec_name_reltype_uuids) !== false && (
1468
            $current_name_uuid == $name_rel->fromName->uuid && $rel_type_uuid != UUID_NAMERELATIONSHIPTYPE_BLOCKING_NAME_FOR
1469
            || $current_name_uuid == $name_rel->toName->uuid && $rel_type_uuid == UUID_NAMERELATIONSHIPTYPE_BLOCKING_NAME_FOR
1470
          )
1471
        ){
1472
          $non_nec_name_rels[] = $name_rel;
1473
        } else if (UUID_NAMERELATIONSHIPTYPE_ORTHOGRAPHIC_VARIANT == $rel_type_uuid) {
1474
          $orthographic_variants[] = $name_rel;
1475
        } else {
1476

    
1477
          $filtered_name_rels[] = $name_rel;
1478
        }
1479
      }
1480
    }
1481
    $name_relations = $filtered_name_rels;
1482

    
1483
    usort($name_relations, 'compare_name_relations_by_term_order_index');
1484

    
1485
    // compose
1486
    foreach ($name_relations as $name_rel) {
1487

    
1488
      $is_inverse_relation = $current_name_uuid == $name_rel->toName->uuid;
1489

    
1490
      $rel_footnote_key_markup = handle_name_relationship_as_footnote($name_rel);
1491
      $relationship_markup = name_relationship_markup($name_rel, $current_name_uuid, $current_taxon_uuid);
1492

    
1493
      $label = cdm_relationship_type_term_abbreviated_label($name_rel->type, $is_inverse_relation);
1494
      $symbol = cdm_relationship_type_term_symbol($name_rel->type, $is_inverse_relation);
1495
      $symbol_markup = '<span class="symbol" title="' . $label . '">' . $symbol . '</span>' . $rel_footnote_key_markup . ' ';
1496
      $relationship_markup = $symbol_markup . $relationship_markup;
1497
      if ($relationship_markup) {
1498
        $render_array['list']['items'][] = markup_to_render_array($relationship_markup,
1499
          null,
1500
          $item_prefix,
1501
          $item_suffix);
1502
      }
1503
    }
1504

    
1505
    // name relationships to be displayed as non nec
1506
    if (count($non_nec_name_rels) > 0) {
1507
      $non_nec_markup = '';
1508
      foreach ($non_nec_name_rels as $name_rel) {
1509
        $is_inverse_relation = $current_name_uuid == $name_rel->toName->uuid;
1510
        $rel_footnote_key_markup = handle_name_relationship_as_footnote($name_rel);
1511
        $relationship_markup = name_relationship_markup($name_rel, $current_name_uuid, $current_taxon_uuid);
1512
        $label = cdm_relationship_type_term_abbreviated_label($name_rel->type, $is_inverse_relation);
1513
        $symbol = $non_nec_markup ? ' nec ' : 'non';
1514
        $symbol_markup = '<span class="symbol" title="' . $label . '">' . $symbol . '</span>' . $rel_footnote_key_markup .  ' ';
1515
        $non_nec_markup .= $symbol_markup . $relationship_markup;
1516
      }
1517
      if ($non_nec_markup) {
1518
        $render_array['list']['items'][] = markup_to_render_array($non_nec_markup,
1519
          null,
1520
          $item_prefix,
1521
          $item_suffix);
1522
      }
1523
    }
1524

    
1525
    // orthographic variants
1526
    if (count($orthographic_variants) > 0) {
1527
      foreach ($orthographic_variants as $name_rel) {
1528

    
1529
        $is_inverse_relation = $current_name_uuid == $name_rel->toName->uuid;
1530
        $rel_footnote_key_markup = handle_name_relationship_as_footnote($name_rel);
1531
        $relationship_markup = name_relationship_markup($name_rel, $current_name_uuid, $current_taxon_uuid, TRUE);
1532
        $nomref_footnote_key_markup = handle_nomenclatural_reference_as_footnote($name_rel->toName);
1533
        $label = cdm_relationship_type_term_abbreviated_label($name_rel->type, $is_inverse_relation);
1534
        $symbol = cdm_relationship_type_term_symbol($name_rel->type, $is_inverse_relation);
1535
        $symbol_markup = '<span class="symbol" title="' . $label . '">' . $symbol . '</span>' . $rel_footnote_key_markup .  ' ';
1536
        $relationship_markup = $symbol_markup . $relationship_markup . $nomref_footnote_key_markup;
1537
      }
1538
      if (isset($relationship_markup) && $relationship_markup) {
1539
        $render_array['list']['items'][] = markup_to_render_array($relationship_markup,
1540
          null,
1541
          $item_prefix,
1542
          $item_suffix);
1543
      }
1544
    }
1545
  }
1546
  return $render_array;
1547
}
1548

    
1549

    
1550

    
1551
/**
1552
 * @param $taxon
1553
 * @return array
1554
 */
1555
function cdm_name_relationships_for_taxon($taxon)
1556
{
1557
  $from_name_relations = cdm_ws_get(CDM_WS_PORTAL_TAXON_FROM_NAMERELATIONS, $taxon->uuid);
1558
  $to_name_relations = cdm_ws_get(CDM_WS_PORTAL_TAXON_TO_NAMERELATIONS, $taxon->uuid);
1559
  $name_relations = array_merge($from_name_relations, $to_name_relations);
1560
  return $name_relations;
1561
}
1562

    
1563

    
1564
/**
1565
 * Recursively searches the array for the $key and sets the given value.
1566
 *
1567
 * @param mixed $key
1568
 *   Key to search for.
1569
 * @param mixed $value
1570
 *   Value to set.'
1571
 * @param array $array
1572
 *   Array to search in.
1573
 *
1574
 * @return bool
1575
 *   True if the key has been found.
1576
 */
1577
function &array_setr($key, $value, array &$array) {
1578
  $res = NULL;
1579
  foreach ($array as $k => &$v) {
1580
    if ($key == $k) {
1581
      $v = $value;
1582
      return $array;
1583
    }
1584
    elseif (is_array($v)) {
1585
      $innerArray = array_setr($key, $value, $v);
1586
      if ($innerArray) {
1587
        return $array;
1588
      }
1589
    }
1590
  }
1591
  return $res;
1592
}
1593

    
1594
/**
1595
 * @todo Please document this function.
1596
 * @see http://drupal.org/node/1354
1597
 */
1598
function &get_preceding_contentElement($contentElementKey, array &$renderTemplate) {
1599
  $res = NULL;
1600
  $precedingElement = NULL;
1601
  foreach ($renderTemplate as &$part) {
1602
    foreach ($part as $key => &$element) {
1603
      if ($key == $contentElementKey) {
1604
        return $precedingElement;
1605
      }
1606
      $precedingElement = $element;
1607
    }
1608
  }
1609
  return $res;
1610
}
1611

    
1612
/**
1613
 * @todo Please document this function.
1614
 * @see http://drupal.org/node/1354
1615
 */
1616
function &get_preceding_contentElementKey($contentElementKey, array &$renderTemplate) {
1617
  $res = NULL;
1618
  $precedingKey = NULL;
1619
  foreach ($renderTemplate as &$part) {
1620
    if (is_array($part)) {
1621
      foreach ($part as $key => &$element) {
1622
        if ($key == $contentElementKey) {
1623
          return $precedingKey;
1624
        }
1625
        if (!str_beginsWith($key, '#')) {
1626
          $precedingKey = $key;
1627
        }
1628
      }
1629
    }
1630
  }
1631
  return $res;
1632
}
1633

    
1634
function nameTypeToDTYPE($dtype){
1635
  static $nameTypeLabelMap = array(
1636
    "ICNB" => "BacterialName",
1637
    "ICNAFP" => "BotanicalName",
1638
    "ICNCP" => "CultivarPlantName",
1639
    "ICZN" => "ZoologicalName",
1640
    "ICVCN" => "ViralName",
1641
    "Any taxon name" => "TaxonName",
1642
    "NonViral" => "TaxonName",
1643
    "Fungus" => "BotanicalName",
1644
    "Plant" => "BotanicalName",
1645
    "Algae" => "BotanicalName",
1646
  );
1647
  return $nameTypeLabelMap[$dtype];
1648

    
1649
}
1650

    
1651

    
1652
function compare_name_relations_by_term_order_index($name_rel1, $name_rel2){
1653
  return compare_terms_by_order_index($name_rel1->type, $name_rel2->type);
1654
}
1655

    
1656
/**
1657
 * Provides an array with the different registration types covered by the passed registration.
1658
 *
1659
 * The labels in the returned array are translatable.
1660
 *
1661
 * See also https://dev.e-taxonomy.eu/redmine/issues/8016
1662
 *
1663
 * @param $registration_dto
1664
 * @return array
1665
 *    An array of the labels describing the different registration types covered by the passed registration.
1666
 */
1667
function registration_types($registration_dto){
1668
  $reg_type_labels = array();
1669
  if(isset($registration_dto->nameRef)){
1670
    $reg_type_labels["name"] = t("new name");
1671
    $reg_type_labels["taxon"] = t("new taxon");
1672
    $name_relations = cdm_ws_fetch_all(str_replace("$0", $registration_dto->nameRef->uuid, CDM_WS_PORTAL_NAME_NAME_RELATIONS));
1673
    $is_new_combination = true;
1674
    foreach($name_relations as $name_rel){
1675
      if(isset($name_rel->type->uuid)){
1676
        $name_is_from_name = $registration_dto->nameRef->uuid == $name_rel->fromName->uuid;
1677
        switch($name_rel->type->uuid) {
1678
          case UUID_NAMERELATIONSHIPTYPE_BASIONYM:
1679
            if(!$name_is_from_name){
1680
              $reg_type_labels["basionym"] = t("new combination");
1681
              $is_new_combination = true;
1682
            }
1683
            break;
1684
          case UUID_NAMERELATIONSHIPTYPE_REPLACED_SYNONYM:
1685
            if(!$name_is_from_name) {
1686
              $is_new_combination = true;
1687
            }
1688
            break;
1689
          case UUID_NAMERELATIONSHIPTYPE_VALIDATED_BY_NAME:
1690
            if(!$name_is_from_name) {
1691
              $reg_type_labels["validation"] = t("validation");
1692
            }
1693
            break;
1694
          case UUID_NAMERELATIONSHIPTYPE_ORTHOGRAPHIC_VARIANT:
1695
            if(!$name_is_from_name) {
1696
              $reg_type_labels["orth_var"] = t("orthographical correction");
1697
            }break;
1698
          default:
1699
            // NOTHING
1700
        }
1701
      }
1702
    }
1703
    if($is_new_combination){
1704
      unset($reg_type_labels["taxon"]);
1705
    }
1706
  }
1707
  if(isset($registration_dto->orderdTypeDesignationWorkingSets)){
1708
    $reg_type_labels[] = t("new nomenclatural type");
1709
  }
1710
  return $reg_type_labels;
1711
}
1712

    
1713
/**
1714
 * Collects and deduplicates the type designations associated with the passes synonyms.
1715
 *
1716
 * @param $synonymy_group
1717
 *    An array containing a homotypic or heterotypic group of names.
1718
 * @param $accepted_taxon_name_uuid
1719
 *    The uuid of the accepted taxon name. Optional parameter which is required when composing
1720
 *    the information for the homotypic group. In this case the accepted taxon is not included
1721
 *    in the $synonymy_group and must therefor passed in this second parameter.
1722
 *
1723
 * @return array
1724
 *    The type designations
1725
 */
1726
function type_designations_for_synonymy_group($synonymy_group, $accepted_taxon_name_uuid = null)
1727
{
1728
  if (count($synonymy_group) > 0) {
1729
    $name_uuid = array_pop($synonymy_group)->name->uuid;
1730
  } else {
1731
    $name_uuid = $accepted_taxon_name_uuid;
1732
  }
1733
  if ($name_uuid) {
1734
   $type_designations = cdm_ws_get(CDM_WS_PORTAL_NAME_TYPEDESIGNATIONS_IN_HOMOTYPICAL_GROUP, $name_uuid);
1735
    if ($type_designations) {
1736
      return $type_designations;
1737
    }
1738
  }
1739

    
1740
  return array();
1741
}
1742

    
1743

    
1744
/**
1745
 * Compares two SpecimenTypeDesignations
1746
 *
1747
 * @param object $a
1748
 *   A SpecimenTypeDesignation.
1749
 * @param object $b
1750
 *   SpecimenTypeDesignation.
1751
 */
1752
function compare_specimen_type_designation($a, $b) {
1753

    
1754
  $cmp_by_status = compare_type_designations_by_status($a,$b);
1755
  if($cmp_by_status !== 0){
1756
    return $cmp_by_status;
1757
  }
1758

    
1759
  $aQuantifier = FALSE;
1760
  $bQuantifier = FALSE;
1761
  if ($aQuantifier == $bQuantifier) {
1762
    // Sort alphabetically.
1763
    $a_text =  isset($a->typeSpecimen->titleCache) ? preg_replace('/[\[\]\"]/', '', $a->typeSpecimen->titleCache) : '';
1764
    $b_text =  isset($b->typeSpecimen->titleCache) ? preg_replace('/[\[\]\"]/', '', $b->typeSpecimen->titleCache) : '';
1765
    return strcasecmp($a_text, $b_text);
1766
  }
1767
  return ($aQuantifier < $bQuantifier) ? -1 : (($aQuantifier > $bQuantifier) ? 1 : 0);
1768
}
1769

    
1770
/**
1771
 * Compares the status of two TypeDesignations
1772
 *
1773
 * @param object $a
1774
 *   A TypeDesignation
1775
 * @param object $b
1776
 *   TypeDesignation
1777
 */
1778
function compare_type_designations_by_status($a, $b) {
1779
  $status_a = isset($a->typeStatus) ? $a->typeStatus : null;
1780
  $status_b = isset($b->typeStatus) ? $b->typeStatus : null;
1781
  return compare_type_designation_status($status_a, $status_b);
1782
}
1783

    
1784
/**
1785
 * Compares two TypeDesignationStatusBase
1786
 *
1787
 * @param object $a
1788
 *   A TypeDesignationStatusBase.
1789
 * @param object $b
1790
 *   TypeDesignationStatusBase.
1791
 */
1792
function compare_type_designation_status($a, $b) {
1793
  $type_status_order = type_status_order();
1794
  $aQuantifier = FALSE;
1795
  $bQuantifier = FALSE;
1796
  if (isset($a->label) && isset($b->label)) {
1797
    $aQuantifier = array_search($a->label, $type_status_order);
1798
    $bQuantifier = array_search($b->label, $type_status_order);
1799
  }
1800
  return ($aQuantifier < $bQuantifier) ? -1 : (($aQuantifier > $bQuantifier) ? 1 : 0);
1801
}
1802

    
1803
/**
1804
 * Compares the two TextualTypeDesignations
1805
 *
1806
 * @param object $a
1807
 *   A TextualTypeDesignations.
1808
 * @param object $b
1809
 *   TextualTypeDesignations.
1810
 */
1811
function compare_textual_type_designation($a, $b) {
1812

    
1813
  $cmp_by_status = compare_type_designations_by_status($a,$b);
1814
  if($cmp_by_status !== 0){
1815
    return $cmp_by_status;
1816
  }
1817

    
1818
  $aQuantifier = FALSE;
1819
  $bQuantifier = FALSE;
1820
  if ($aQuantifier == $bQuantifier) {
1821
    // Sort alphabetically.
1822
    $a_text =  isset($a->text_L10n->text) ? $a->text_L10n->text : '';
1823
    $b_text =  isset($b->text_L10n->text) ? $b->text_L10n->text : '';
1824
    return strcasecmp($a_text, $b_text);
1825
  }
1826
  return ($aQuantifier < $bQuantifier) ? -1 : (($aQuantifier > $bQuantifier) ? 1 : 0);
1827
}
1828

    
1829

    
1830
/**
1831
 * Compares two SpecimenTypeDesignation status labels
1832
 *
1833
 * @param string $a
1834
 *   A TypeDesignationStatus label.
1835
 * @param string $b
1836
 *   A TypeDesignationStatus label.
1837
 */
1838
function compare_type_designation_status_labels($a, $b) {
1839

    
1840
  $type_status_order = type_status_order();
1841

    
1842
  $aQuantifier = FALSE;
1843
  $bQuantifier = FALSE;
1844
  if (isset($a) && isset($b)) {
1845
    $aQuantifier = array_search($a, $type_status_order);
1846
    $bQuantifier = array_search($b, $type_status_order);
1847
  }
1848
  return ($aQuantifier < $bQuantifier) ? -1 : 1;
1849
}
1850

    
1851
/**
1852
 * @return array
1853
 */
1854
function type_status_order()
1855
{
1856
  /*
1857
    This is the desired sort order as of now: Holotype Isotype Lectotype
1858
    Isolectotype Syntype.
1859
    TODO Basically, what we are trying to do is, we define
1860
    an ordered array of TypeDesignation-states and use the index of this array
1861
    for comparison. This array has to be filled with the cdm- TypeDesignation
1862
    states and the order should be parameterisable inside the dataportal.
1863
    */
1864
  // Make that static for now.
1865
  $type_status_order = array(
1866
    'Epitype',
1867
    'Holotype',
1868
    'Isotype',
1869
    'Lectotype',
1870
    'Isolectotype',
1871
    'Syntype',
1872
    'Paratype'
1873
  );
1874
  return $type_status_order;
1875
}
1876

    
1877
/**
1878
 * Return HTML for the lectotype citation with the correct layout.
1879
 *
1880
 * This function prints the lectotype citation with the correct layout.
1881
 * Lectotypes are renderized in the synonymy tab of a taxon if they exist.
1882
 *
1883
 * @param mixed $typeDesignation
1884
 *   Object containing the lectotype citation to print.
1885
 *
1886
 * @return string
1887
 *   Valid html string.
1888
 */
1889
function type_designation_citation_layout($typeDesignation, $footnote_separator = ',') {
1890
  $res = '';
1891
  $citation = $typeDesignation->source->citation;
1892
  $pages = $typeDesignation->source->citationMicroReference;
1893
  if(isset($typeDesignation->typeStatus->uuid) && isset($typeDesignation->typeStatus->representation_L10n)) {
1894
    if ( $typeDesignation->typeStatus->uuid == UUID_NTD_ORIGINAL_DESIGNATION || $typeDesignation->typeStatus->uuid == UUID_NTD_MONOTYPY) {
1895
      $res = ' (' . $typeDesignation->typeStatus->representation_L10n . ')';
1896
      return $res;
1897
    }
1898
  }
1899

    
1900
  if ($citation) {
1901
    // $type = $typeDesignation_citation->type;
1902
    $year = isset($citation->datePublished->start) ? substr($citation->datePublished->start, 0, 4) : '';
1903
    $author = isset($citation->authorship->titleCache) ? $citation->authorship->titleCache : '';
1904
    $res .= ' (designated by ';
1905
    $res .= $author;
1906
    $res .= ($year ? ' ' . $year : '');
1907
    $res .= ($pages ? ': ' . $pages : '');
1908
    // $res .= ')';
1909

    
1910
    // footnotes should be rendered in the parent element so we
1911
    // are relying on the FootnoteListKey set there
1912
    $fkey_typeDesignation = FootnoteManager::addNewFootnote(RenderHints::getFootnoteListKey(), $citation->titleCache);
1913
    $res .= render_footnote_key($fkey_typeDesignation, $footnote_separator,TRUE) . ')';
1914
  }
1915
  return $res;
1916
}
1917

    
1918
/**
1919
 * Creates markup for the status of a type designation. In case the status or its representation is missing the label will be set to "Type"
1920
 *
1921
 * @param $type_designation
1922
 * @return string
1923
 */
1924
function type_designation_status_label_markup($type_designation)
1925
{
1926
  return '<span class="type-status">'
1927
    . ((isset($type_designation->typeStatus->representation_L10n)) ? ucfirst($type_designation->typeStatus->representation_L10n) : t('Type')) . '</span>'
1928
    ;
1929
}
(7-7/14)