Project

General

Profile

Download (81.9 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 $split_render_templates = NULL;
76

    
77
  if($split_render_templates == NULL) {
78
    $render_templates = variable_get(NameRenderConfiguration::CDM_NAME_RENDER_TEMPLATES, NameRenderConfiguration::CDM_NAME_RENDER_TEMPLATES_DEFAULT);
79
    // needs to be converted to an array
80
    $render_templates = (object_to_array($render_templates));
81

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

    
95
  // get the base element of the renderPath
96
  if (($separatorPos = strpos($render_path, '.')) > 0) {
97
    $renderPath_base = substr($render_path, 0, $separatorPos);
98
  } else {
99
    $renderPath_base = $render_path;
100
  }
101

    
102
  $template = NULL;
103
  // 1. try to find a template using the render path base element
104
  if(array_key_exists($renderPath_base, $split_render_templates)){
105
    $template = (array)$split_render_templates[$renderPath_base];
106
  }
107

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

    
122

    
123
  // 3. Otherwise get default RenderTemplate from theme.
124
  if (!is_array($template)) {
125
    $template = $split_render_templates['#DEFAULT'];
126
  }
127

    
128
  // --- set the link uris to the according template fields if they exist
129
  if(isset($template['nameAuthorPart']) && isset($template['nameAuthorPart']['#uri'])) {
130
    if ($nameLink) {
131
      $template['nameAuthorPart']['#uri'] = $nameLink;
132
    }
133
    else {
134
      unset($template['nameAuthorPart']['#uri']);
135
    }
136
  }
137

    
138
  if ($nameLink && isset($template['namePart']['#uri'])) {
139
    $template['namePart']['#uri'] = $nameLink;
140
  }
141
  else {
142
    unset($template['namePart']['#uri']);
143
  }
144

    
145
  if ($referenceLink && isset($template['referencePart']['#uri'])) {
146
    $template['referencePart']['#uri'] = $referenceLink;
147
  }
148
  else {
149
    unset($template['referencePart']['#uri']);
150
  }
151

    
152
  return $template;
153
}
154

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

    
220
  static $part_definitions = null;
221
  if (!isset($part_definitions)) {
222
    $part_definitions = object_to_array(variable_get(NameRenderConfiguration::CDM_PART_DEFINITIONS, NameRenderConfiguration::CDM_PART_DEFINITIONS_DEFAULT));
223
  }
224

    
225
  $dtype = nameTypeToDTYPE($taxonNameType);
226
  if (array_key_exists($taxonNameType, $part_definitions)) {
227
    return $part_definitions[$taxonNameType];
228
  } else if (array_key_exists($dtype, $part_definitions)) {
229
    return $part_definitions[$dtype];
230
  } else {
231
    return $part_definitions['#DEFAULT']; // covers ViralNames and general NonViralNames
232
  }
233

    
234
}
235

    
236

    
237
/**
238
 * Renders the markup for a CDM TaxonName instance.
239
 *
240
 * The layout of the name representation is configured by the
241
 * part_definitions and render_templates (see get_partDefinition() and
242
 * get_nameRenderTemplate())
243
 *
244
 * @param $taxon_name_or_taxon_base
245
 *    A cdm TaxonBase or TaxonName entity
246
 * @param $name_link
247
 *    URI to the taxon, @param $reference_link
248
 *    URI to the reference,
249
 * @param bool $show_taxon_name_annotations
250
 *    Enable the display of footnotes for annotations on the taxon and name
251
 *    (default: true)
252
 * @param bool $is_type_designation
253
 *    To indicate that the supplied taxon name is a name type designation.
254
 *    (default: false)
255
 * @param array $skip_render_template_parts
256
 *    The render template elements matching these part names will bes skipped.
257
 *    This parameter should only be used in rare cases like for suppressing
258
 *    the sec reference of synonyms. Normally the configuration of the
259
 *    name appearance should only be done via the render templates themselves. (default: [])
260
 * @param bool $is_invalid
261
 *   Indicates that this taxon is invalid. In this case the name part will be shown in double quotes.
262
 *   This is useful when rendering taxon relation ships. (default: false)
263
 *
264
 * @return string
265
 *  The markup for a taxon name.
266
 * @see path_to_taxon(), must be processed by url() before passing to this method
267
 * @see path_to_reference(), must be processed by url() before passing to this method
268
 */
269
function render_taxon_or_name($taxon_name_or_taxon_base, $name_link = NULL, $reference_link = NULL,
270
  $show_taxon_name_annotations = true, $is_type_designation = false, $skip_render_template_parts = [], $is_invalid = false) {
271

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

    
295

    
296
  $renderTemplate = get_nameRenderTemplate(RenderHints::getRenderPath(), $name_link, $reference_link);
297
  $partDefinition = get_partDefinition($taxon_name->nameType);
298

    
299
  // Apply definitions to template.
300
  foreach ($renderTemplate as $part => $uri) {
301

    
302
    if (isset($partDefinition[$part])) {
303
      $renderTemplate[$part] = $partDefinition[$part];
304
    }
305
    if (is_array($uri) && isset($uri['#uri'])) {
306
      $renderTemplate[$part]['#uri'] = $uri['#uri'];
307
    }
308
  }
309

    
310
  foreach($skip_render_template_parts as $part){
311
    unset($renderTemplate[$part]);
312
  }
313

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

    
320
  normalize_tagged_text($tagged_title);
321

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

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

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

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

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

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

    
355

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

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

    
386

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

    
391
  // Fill name into $renderTemplate.
392
  array_setr('name', $name , $renderTemplate);
393

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

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

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

    
408
    // default separator
409
    $separator = '';
410

    
411
    // [Eckhard]:"Komma nach dem Taxonnamen ist grundsätzlich falsch,
412
    // Komma nach dem Autornamen ist überall dort falsch, wo ein "in" folgt."
413
    if (isset($renderTemplate['referencePart']['reference'])) {
414
      $microreference = NULL;
415
      if (isset($renderTemplate['referencePart']['microreference'])&& isset($taxon_name->nomenclaturalSource->citationMicroReference)) {
416
        $microreference = $taxon_name->nomenclaturalSource->citationMicroReference;
417
      }
418
      if(count($nomref_tagged_text) == 0 && isset($taxon_name->nomenclaturalSource->citation->title)){
419
        $citation = $taxon_name->nomenclaturalSource->citation->title; // using only the title here since the authors are already addded to the string
420
        if(isset($taxon_name->nomenclaturalSource->citationMicroReference) && $taxon_name->nomenclaturalSource->citationMicroReference){
421
          $citation .= ': ' . $taxon_name->nomenclaturalSource->citationMicroReference;
422
        }
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_to_markup_options['html'] = true;
480
         }
481
        }
482
        array_setr(
483
          'status',
484
          '<span class="nomenclatural_status">' . cdm_tagged_text_to_markup($nom_status_tagged_text, array('postSeparator'), 'span', $tt_to_markup_options) . '</span>',
485
          $renderTemplate);
486
    }
487
  }
488

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

    
503
  // Fill with protologues etc...
504
  $protologue_links = [];
505
  if (array_setr('description', TRUE, $renderTemplate)) {
506
    $external_links = cdm_ws_get(CDM_WS_NAME_PROTOLOGUE_LINKS, $taxon_name->uuid);
507
    if($external_links){
508
      foreach ($external_links as $link) {
509
        if (!empty($link->uri)) {
510
          // for the link see also cdm_external_uri()
511
          $protologue_links[] = ' ' . l(font_awesome_icon_markup('fa-book'), $link->uri, ['html' => true]);
512
          }
513
        }
514
      }
515
    array_setr('description', join(', ', $protologue_links), $renderTemplate);
516
    array_replace_keyr('description', 'protologue', $renderTemplate); // in preparation for #9319
517
  }
518

    
519
  // Render.
520
  $out = '';
521
  if(isset($_REQUEST['RENDER_PATH'])){
522
    // developer option to show the render path with each taxon name
523
    $out .= '<span class="render-path">' . RenderHints::getRenderPath() . '</span>';
524
  }
525
  $out .= '<span class="' . html_class_attribute_ref($taxon_name_or_taxon_base)
526
    . '" data-cdm-ref="/name/' . $taxon_name->uuid . '" data-cdm-render-path="' . RenderHints::getRenderPath() .'">';
527

    
528
  foreach ($renderTemplate as $partName => $part) {
529
    $separator = '';
530
    $partHtml = '';
531
    $uri = FALSE;
532
    if (!is_array($part)) {
533
      continue;
534
    }
535
    if (isset($part['#uri']) && is_string($part['#uri'])) {
536
      $uri = $part['#uri'];
537
      unset($part['#uri']);
538
    }
539
    foreach ($part as $part => $content) {
540
      $html = '';
541
      if (is_array($content)) {
542
        $html = $content['#html'];
543
        if(isset($content['#separator'])) {
544
          $separator = $content['#separator'];
545
        }
546
      }
547
      elseif (is_string($content)) {
548
        $html = $content;
549
      }
550
      if($html){ // skip empty elements
551
        $partHtml .= '<span class="' . $part . '">' . $html . '</span>';
552
      }
553
    }
554
    if ($uri) {
555
      // cannot use l() here since the #uri already should have been processed through uri() at this point
556
      $out .= $separator . '<a href="' . $uri . '" class="' . $partName . '">' . $partHtml . '</a>';
557

    
558
    }
559
    else {
560
      $out .= $separator . $partHtml;
561
    }
562
  }
563
  $out .= '</span>';
564

    
565
  $annotations_and_sources = new AnnotationsAndSources();
566
  if($nom_status_fkey){
567
    // the nomenclatural status footnote key refers to the source citation
568
    $annotations_and_sources->addFootNoteKey($nom_status_fkey);
569
  }
570
  if ($show_taxon_name_annotations) {
571
    if($taxon_base){
572
      $annotations_and_sources = handle_annotations_and_sources($taxon_base,
573
        null, null, $annotations_and_sources);
574
    }
575
    $annotations_and_sources = handle_annotations_and_sources($taxon_name,
576
      null, null, $annotations_and_sources);
577
  }
578
  $out .= $annotations_and_sources->footNoteKeysMarkup();
579
  return $out;
580
}
581

    
582

    
583

    
584
/**
585
 * Composes information for a registration from a dto object.
586
 *
587
 * Registrations which are not yet published are suppressed.
588
 *
589
 * @param $registration_dto
590
 * @param $with_citation
591
 *   Whether to show the citation.
592
 *
593
 * @return array
594
 *    A drupal render array with the elements:
595
 *    - 'name'
596
 *    - 'name-relations'
597
 *    - 'specimen_type_designations'
598
 *    - 'name_type_designations'
599
 *    - 'citation'
600
 *    - 'registration_date_and_institute'
601
 * @ingroup compose
602
 */
603
function compose_registration_dto_full($registration_dto, $with_citation = true)
604
{
605
  $render_array = array(
606
    '#prefix' => '<div class="registration">',
607
    '#suffix' => '</div>'
608
  );
609

    
610
  if(!(isset($registration_dto->identifier) && $registration_dto->status == 'PUBLISHED')){
611
    return $render_array;
612
  }
613

    
614
  $render_array['sub_headline'] = markup_to_render_array(join(", ", registration_types($registration_dto)),-10, '<h3 class="registration_type">' . t('Event: '), '</h3>' );
615
  $render_array['nomenclatural_act'] = array(
616
    '#weight' => 0,
617
    '#prefix' => '<div class="nomenclatural_act">',
618

    
619
    '#suffix' => '</div>'
620
  );
621

    
622
  $typified_name = null;
623

    
624
  // Nomenclatural act block element
625
  $last_footnote_listkey = RenderHints::setFootnoteListKey("nomenclatural_act");
626
  // name
627
  $name_relations = null;
628
  if(isset($registration_dto->nameRef) && $registration_dto->nameRef){
629
    $name = cdm_ws_get(CDM_WS_PORTAL_NAME, $registration_dto->nameRef->uuid);
630
    cdm_load_tagged_full_title($name);
631
    $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);
632
    $name_relations = cdm_ws_fetch_all(str_replace("$0", $registration_dto->nameRef->uuid, CDM_WS_PORTAL_NAME_NAME_RELATIONS));
633
    // need to create the name relationships later, so that the foot notes are in correct order, see section // name relations
634
  } else {
635
    // in this case the registration must have a
636
    // typified name will be rendered later
637
    $typified_name = cdm_ws_get(CDM_WS_PORTAL_NAME, $registration_dto->typifiedNameRef->uuid);
638

    
639
  }
640

    
641
  // typedesignation in detail
642
  if(is_object($registration_dto->orderedTypeDesignationWorkingSets)) {
643
    $field_unit_uuids = array();
644
    $specimen_type_designation_refs = array();
645
    $name_type_designation_refs = array();
646
    foreach ((array)$registration_dto->orderedTypeDesignationWorkingSets as $workingset_ref => $obj) {
647
      $tokens = explode("#", $workingset_ref);
648
      $types_in_fieldunit = get_object_vars($obj); // convert into associative array
649

    
650
      if ($tokens[0] == 'NameTypeDesignation') {
651
        foreach ($types_in_fieldunit as $type_status => $entity_reference_list) {
652
          if(!isset($name_type_designation_refs[$type_status])){
653
            $name_type_designation_refs[$type_status]  = $entity_reference_list;
654
          } else {
655
            array_push($name_type_designation_refs[$type_status] ,$entity_reference_list);
656
          }
657
        }
658
      } else if ($tokens[0] == 'FieldUnit'){
659
        $field_unit_uuids[] = $tokens[1];
660
        foreach ($types_in_fieldunit as $type_status => $entity_reference_list) {
661
          if(!isset($specimen_type_designation_refs[$type_status])){
662
            $specimen_type_designation_refs[$type_status] =  $entity_reference_list;
663
          } else {
664
            array_push($specimen_type_designation_refs[$type_status], $entity_reference_list);
665
          }
666
        }
667
      } else {
668
        drupal_set_message("Unimplemented type: " . $tokens[0], 'error');
669
      }
670
    }
671
    // type designations which are in this nomenclatural act.
672
    if (count($name_type_designation_refs) > 0) {
673
      $render_array['nomenclatural_act']['name_type_designations'] = compose_name_type_designations($name_type_designation_refs);
674
      $render_array['nomenclatural_act']['name_type_designations']['#prefix'] = '<p class="name_type_designations">';
675
      $render_array['nomenclatural_act']['name_type_designations']['#suffix'] = '</p>';
676
      $render_array['nomenclatural_act']['name_type_designations']['#weight'] = 20;
677
    }
678
    if (count($field_unit_uuids) > 0) {
679
      $specimen_type_designations_array = compose_specimen_type_designations($specimen_type_designation_refs, true);
680
      $render_array['nomenclatural_act']['specimen_type_designations'] = $specimen_type_designations_array['type_designations'];
681
      $render_array['map'] = $specimen_type_designations_array['map'];
682
      $render_array['map']['#weight'] = $render_array['nomenclatural_act']['#weight'] + 20;
683
    }
684
  }
685

    
686
  // name relations
687
  if($name_relations){
688
    $render_array['nomenclatural_act']['name_relations'] = compose_name_relationships_list($name_relations, $registration_dto->nameRef->uuid, null);
689
    $render_array['nomenclatural_act']['name_relations']['#weight'] = 10;
690
  }
691

    
692
  // citation
693
  if ($with_citation) {
694
    $render_array['citation'] = markup_to_render_array(
695
      "<div class=\"citation nomenclatural_act_citation" . html_class_attribute_ref(new TypedEntityReference("Reference", $registration_dto->citationUuid)) . "\">"
696
      . "<span class=\"label\">published in: </span>"
697
      . $registration_dto->bibliographicInRefCitationString
698
      . l(custom_icon_font_markup('icon-interal-link-alt-solid', array('class' => array('superscript'))), path_to_reference($registration_dto->citationUuid), array('html' => true))
699
      . "</div>",
700
      $render_array['nomenclatural_act']['#weight'] + 10 );
701
  }
702

    
703
  $render_array['nomenclatural_act']['footnotes'] = markup_to_render_array(render_footnotes(),100);
704

    
705
  // END of nomenclatural act block
706
  RenderHints::setFootnoteListKey($last_footnote_listkey );
707

    
708
  if($typified_name){
709
    $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);
710
  }
711

    
712
  // registration date and office
713
  $registration_date_insitute_markup = render_registration_date_and_institute($registration_dto);
714
  if($registration_date_insitute_markup){
715
    $render_array['registration_date_and_institute'] = markup_to_render_array(
716
      $registration_date_insitute_markup . '</p>',
717
      100);
718
  }
719

    
720
  $render_array['page_footnotes'] = markup_to_render_array(render_footnotes(), 110);
721

    
722
  return $render_array;
723
}
724

    
725

    
726
/**
727
 * Composes a compact representation for a registrationDTO object
728
 *
729
 * Registrations which are not yet published are suppressed.
730
 *
731
 * @param $registration_dto
732
 * @param $style string
733
 *   The style of how to compose the 'identifier' and 'registration_date_and_institute' part with the summary
734
 *   - 'citation': Similar to the arrearance of nomenclatural acts in print media
735
 *   - 'list-item' : style suitable for result lists etc
736
 *
737
 * @return array
738
 *    A drupal render array with the elements:
739
 *    - 'registration-metadata' when $style == 'list-item'
740
 *    - 'summary'
741
 * @ingroup compose
742
 */
743
function compose_registration_dto_compact($registration_dto, $style = 'citation', $tag_enclosing_summary = 'p')
744
{
745
  $render_array = array();
746
  $media_link_map = array();
747

    
748
  if(!(isset($registration_dto->identifier) && $registration_dto->status == 'PUBLISHED')){
749
    return $render_array;
750
  }
751

    
752
  $registration_date_institute_markup = render_registration_date_and_institute($registration_dto, 'span');
753
  $identifier_markup = render_link_to_registration($registration_dto->identifier);
754

    
755
  $tagged_text_options = array();
756
  if(isset($registration_dto->nameRef)){
757
    $tagged_text_options[] = array(
758
      'filter-type' => 'name',
759
      'prefix' => '<span class="registered_name">',
760
      'suffix' => '</span>',
761
    );
762
  } else {
763
    $tagged_text_options[] = array(
764
      'filter-type' => 'name',
765
      'prefix' => '<span class="referenced_typified_name">',
766
      'suffix' => '</span>',
767
    );
768
  }
769
  cdm_tagged_text_add_options($registration_dto->summaryTaggedText, $tagged_text_options);
770
  tagged_text_extract_reference($registration_dto->summaryTaggedText);
771
  $tagged_text_expanded = cdm_tagged_text_expand_entity_references($registration_dto->summaryTaggedText);
772
  foreach ($tagged_text_expanded  as $tagged_text){
773
    if(isset($tagged_text->entityReference->type) && $tagged_text->entityReference->type == 'SpecimenTypeDesignation') {
774
      $mediaDTOs = cdm_ws_get('typedesignation/$0/media', array($tagged_text->entityReference->uuid));
775
      if(isset($mediaDTOs[0]->uri)){
776
        $media_url_key = '{link-' . $mediaDTOs[0]->uuid . '}';
777
        $tagged_text->text = str_replace('[icon]', '[icon]' . $media_url_key, $tagged_text->text);
778
        $media_link_map[$media_url_key] =  cdm_external_uri($mediaDTOs[0]->uri, true);
779
      }
780
    }
781
  }
782
  $registation_markup = cdm_tagged_text_to_markup($tagged_text_expanded);
783
  foreach($media_link_map as $media_url_key => $link){
784
    $registation_markup = str_replace($media_url_key, $link, $registation_markup);
785
  }
786
  if($style == 'citation') {
787
    $registation_markup = $registation_markup . ' ' . $identifier_markup . ' ' . $registration_date_institute_markup;
788
  } else {
789
    $render_array['registration-metadata'] = markup_to_render_array('<div class="registration-metadata">' . $identifier_markup . ' ' . $registration_date_institute_markup. "</div>", -10);
790
  }
791
  $render_array['summary'] = markup_to_render_array('<' . $tag_enclosing_summary . ' class="registration-summary">' . $registation_markup . '</' . $tag_enclosing_summary . '>', 0);
792

    
793
  return $render_array;
794
}
795

    
796
/**
797
 * Renders the registrationDate and institutionTitleCache of the $registration_dto as markup.
798
 *
799
 * @param $registration_dto
800
 * @return string
801
 *    The markup or an empty string
802
 */
803
function render_registration_date_and_institute($registration_dto, $enclosing_tag = 'p') {
804
  $registration_date_institute_markup = '';
805
  if ($registration_dto->registrationDate) {
806
    $date_string = format_datetime($registration_dto->registrationDate);
807
    if (isset($registration_dto->institutionTitleCache) && $registration_dto->institutionTitleCache) {
808
      $registration_date_institute_markup =
809
        t("Registration on @date in @institution", array(
810
          '@date' => $date_string,
811
          '@institution' => $registration_dto->institutionTitleCache,
812
        ));
813
    } else {
814
      $registration_date_institute_markup =
815
        t("Registration on @date", array(
816
          '@date' => $date_string
817
        ));
818
    }
819
    $registration_date_institute_markup = '<' .$enclosing_tag . ' class="registration-date-and-institute">'. $registration_date_institute_markup . '</' .$enclosing_tag . '>';
820
  }
821
  return $registration_date_institute_markup;
822
}
823

    
824

    
825
/**
826
 * @param $registrations
827
 * @return string
828
 */
829
function render_registrations($registrations)
830
{
831
  $registration_markup = '';
832
  $registration_markup_array = array();
833
  if ($registrations) {
834
    foreach ($registrations as $reg) {
835
      $registration_markup_array[] = render_registration($reg);
836
    }
837
    $registration_markup = " Registration" . (count($registration_markup_array) > 1 ? 's: ' : ': ')
838
      . join(', ', $registration_markup_array);
839
  }
840
  return $registration_markup;
841
}
842

    
843

    
844
/**
845
 * Renders a registration
846
 *
847
 * TODO replace by compose_registration_dto_compact
848
 * @param $registration
849
 */
850
function render_registration($registration){
851
  $markup = '';
852

    
853
  if(isset($registration->identifier) && $registration->status == 'PUBLISHED'){
854
    $office_class_attribute = '';
855
    if(isset($registration->institution->titleCache)){
856
      $office_class_attribute = registration_institution_class_attribute($registration);
857
    }
858
    $markup = "<span class=\"registration $office_class_attribute\">" . render_link_to_registration($registration->identifier) . ', '
859
      .  preg_replace('/^([^T]*)(.*)$/', '${1}', $registration->registrationDate)
860
      . '</span>';
861
  }
862
  return $markup;
863
}
864

    
865
/**
866
 * @param $registration
867
 * @return string
868
 */
869
function registration_institution_class_attribute($registration_dto)
870
{
871
  if(isset($registration_dto->institutionTitleCache)){
872
    $institutionTitleCache = $registration_dto->institutionTitleCache;
873
  } else {
874
    // fall back option to also support cdm entities
875
    $institutionTitleCache = @$registration_dto->institution->titleCache;
876
  }
877
  return $institutionTitleCache ? 'registration-' . strtolower(preg_replace('/[^a-zA-Z0-9]/', '-', $institutionTitleCache)) : '';
878
}
879

    
880

    
881
/**
882
 * Renders and array of CDM TypeDesignations
883
 *
884
 *  - NameTypeDesignation
885
 *  - SpecimenTypeDesignation
886
 *  - TextualTypeDesignation
887
 *
888
 * @param object $type_designations an array of cdm TypeDesignation entities
889
 *  to render
890
 * @param string $enclosing_tag the tag element type to enclose the whole list
891
 *  of type designation with. By default this DOM element is <ul>
892
 * @param string $element_tag the tag element type to be used for each
893
 *  type designation item.
894
 * @param bool $link_to_specimen_page whether a specimen in type designation element
895
 *  should be a link or not.
896
 *
897
 * @return string The markup.
898
 *
899
 * @InGroup Render
900
 */
901
function render_type_designations($type_designations, $enclosing_tag = 'ul', $element_tag =  'li', $link_to_specimen_page = true) {
902

    
903
  // need to add element to render path since type designations
904
  // need other name render template
905
  RenderHints::pushToRenderStack('typedesignations');
906

    
907
  $out = '<' . $enclosing_tag .' class="typeDesignations">';
908
  $specimen_type_designations = array();
909
  $name_type_designations = array();
910
  $textual_type_designations = array();
911
  $separator = ',';
912

    
913
  foreach ($type_designations as $type_designation) {
914
    switch ($type_designation->class) {
915
      case 'SpecimenTypeDesignation':
916
        $specimen_type_designations[] = $type_designation;
917
        break;
918
      case 'NameTypeDesignation':
919
        $name_type_designations[] = $type_designation;
920
        break;
921
      case 'TextualTypeDesignation':
922
        $textual_type_designations[] = $type_designation;
923
        break;
924
      default:  throw new Exception('Unknown type designation class: ' . $type_designation->class);
925
    }
926
  }
927

    
928
  // NameTypeDesignation ..................................
929
  if(!empty($name_type_designations)){
930
    usort($name_type_designations, "compare_type_designations_by_status");
931
    foreach($name_type_designations as $name_type_designation){
932
      if ($name_type_designation->notDesignated) {
933
        $out .= '<'. $element_tag .' class="' . html_class_attribute_ref($name_type_designation) . '">' .  type_designation_status_label_markup($name_type_designation)  . ': '
934
          . t('not designated') . '</'. $element_tag .'>';
935
      }
936
      elseif (isset($name_type_designation->typeName)) {
937
        $link_to_name_page = url(path_to_name($name_type_designation->typeName->uuid));
938
        $out .= '<'. $element_tag .' class="' . html_class_attribute_ref($name_type_designation) . '">' .  type_designation_status_label_markup($name_type_designation) ;
939

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

    
943
        }
944
        $referenceUri = '';
945
        if (isset($name_type_designation->typeName->nomenclaturalSource->citation)) {
946
          $referenceUri = url(path_to_reference($name_type_designation->typeName->nomenclaturalSource->citation->uuid));
947
        }
948
        $out .= ': ' . render_taxon_or_name($name_type_designation->typeName, $link_to_name_page, $referenceUri, TRUE, TRUE);
949
      }
950
      RenderHints::setAnnotationsAndSourceConfig(annotations_and_sources_config_typedesignations());
951
      $annotations_and_sources = handle_annotations_and_sources($name_type_designation);
952
      $out .= $annotations_and_sources->footNoteKeysMarkup();
953
    }
954
  } // END NameTypeDesignation
955

    
956
  // SpecimenTypeDesignation ...................................
957
  if (!empty($specimen_type_designations)) {
958
    usort($specimen_type_designations, "compare_specimen_type_designation");
959
    foreach ($specimen_type_designations as $specimen_type_designation) {
960
      $type_citation_markup = '';
961

    
962
      if (!empty($specimen_type_designation->source->citation)) {
963

    
964
        $citation_footnote_str = cdm_reference_markup($specimen_type_designation->source->citation, null, false, true);
965
        $author_team = cdm_ws_get(CDM_WS_REFERENCE_AUTHORTEAM, $specimen_type_designation->source->citation->uuid);
966

    
967
        if (!empty($author_team->titleCache)) {
968
          $year = @timePeriodToString($specimen_type_designation->source->citation->datePublished, true, 'YYYY');
969
          $authorteam_str = $author_team->titleCache . ($year ? ' ' : '') . $year;
970
          if ($authorteam_str == $specimen_type_designation->source->citation->titleCache) {
971
            $citation_footnote_str = '';
972
          }
973
        } else {
974
          $authorteam_str = $citation_footnote_str;
975
          // no need for a footnote in case in case it is used as replacement for missing author teams
976
          $citation_footnote_str = '';
977
        }
978

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

    
984
        $footnote_key_markup = '';
985
        if ($citation_footnote_str) {
986
          // footnotes should be rendered in the parent element so we
987
          // are relying on the FootnoteListKey set there
988
          $_fkey2 = FootnoteManager::addNewFootnote(RenderHints::getFootnoteListKey(), $citation_footnote_str);
989
          $footnote_key_markup = render_footnote_key($_fkey2, $separator, TRUE);
990
        }
991

    
992
        $type_citation_markup .= '&nbsp;(' . t('designated by') . '&nbsp;<span class="typeReference">' . $authorteam_str . '</span>';
993
        if (!empty($specimen_type_designation->source->citationMicroReference)) {
994
          $type_citation_markup .= ': ' . trim($specimen_type_designation->source->citationMicroReference);
995
        }
996
        $type_citation_markup .= $footnote_key_markup . ')';
997

    
998
      }
999

    
1000

    
1001
      $out .= '<'. $element_tag .' class="' . html_class_attribute_ref($specimen_type_designation) . '">';
1002
      $out .= type_designation_status_label_markup($specimen_type_designation) . $type_citation_markup;
1003

    
1004

    
1005
      $derivedUnitFacadeInstance = null;
1006
      if (isset($specimen_type_designation->typeSpecimen)) {
1007
        $derivedUnitFacadeInstance = cdm_ws_get(CDM_WS_DERIVEDUNIT_FACADE, $specimen_type_designation->typeSpecimen->uuid);
1008
      }
1009

    
1010
      if (!empty($derivedUnitFacadeInstance->titleCache)) {
1011
        $specimen_markup = $derivedUnitFacadeInstance->titleCache;
1012
        if($link_to_specimen_page && isset($derivedUnitFacadeInstance->specimenLabel) && $derivedUnitFacadeInstance->specimenLabel){
1013
          $specimen_markup = str_replace($derivedUnitFacadeInstance->specimenLabel, l($derivedUnitFacadeInstance->specimenLabel, path_to_specimen($specimen_type_designation->typeSpecimen->uuid)), $specimen_markup);
1014
        }
1015
        RenderHints::setAnnotationsAndSourceConfig(annotations_and_sources_config_typedesignations());
1016
        $annotations_and_sources = handle_annotations_and_sources($derivedUnitFacadeInstance);
1017
        $out .= ': <span class="' . html_class_attribute_ref($specimen_type_designation->typeSpecimen) . '">'
1018
          . $specimen_markup
1019
          . '</span>'; // . ': ' . theme('cdm_specimen', array('specimenTypeDesignation' => $derivedUnitFacadeInstance));
1020
        if(!empty($derivedUnitFacadeInstance->preferredStableUri)){
1021
          $out .= ' ' . l($derivedUnitFacadeInstance->preferredStableUri, $derivedUnitFacadeInstance->preferredStableUri, array('absolute' => true));
1022
        }
1023
        $out .= $annotations_and_sources->footNoteKeysMarkup();
1024
      }
1025
      $out .= '</'. $element_tag .'>';
1026
    }
1027
  } // END Specimen type designations
1028

    
1029
  // TextualTypeDesignation .........................
1030
  usort($textual_type_designations, 'compare_textual_type_designation');
1031
  if(!empty($textual_type_designations)) {
1032
      RenderHints::setAnnotationsAndSourceConfig([
1033
          // these settings differ from those provided by annotations_and_sources_config_typedesignations()
1034
          // TODO is this by purpose? please document the reason for the difference
1035
          'sources_as_content' => false, // as footnotes
1036
          'link_to_name_used_in_source' => false,
1037
          'link_to_reference' => true,
1038
          'add_footnote_keys' => true,
1039
          'bibliography_aware' => false
1040
        ]
1041
      );
1042
    foreach ($textual_type_designations as $textual_type_designation) {
1043
      $annotations_and_sources = handle_annotations_and_sources($textual_type_designation);
1044
      $encasement =  $textual_type_designation->verbatim ? '"' : '';
1045
      $out .= '<' . $element_tag . ' class="' . html_class_attribute_ref($textual_type_designation) . '">' . type_designation_status_label_markup(null)
1046
        . ': ' .  $encasement . trim($textual_type_designation->text_L10n->text) . $encasement .  $annotations_and_sources->footNoteKeysMarkup() .'</' . $element_tag . '>';
1047
//      if($annotations_and_sources->hasSourceReferences())){
1048
//        $citation_markup = join(', ', getSourceReferences());
1049
//      }
1050
//      $out .= $citation_markup;
1051
    }
1052
  }
1053

    
1054
  // Footnotes for citations, collection acronyms.
1055
  // footnotes should be rendered in the parent element so we
1056
  // are relying on the FootnoteListKey set there
1057
  $_fkey = FootnoteManager::addNewFootnote(
1058
    RenderHints::getFootnoteListKey(),
1059
    (isset($derivedUnitFacadeInstance->collection->titleCache) ? $derivedUnitFacadeInstance->collection->titleCache : FALSE)
1060
  );
1061
  $out .= render_footnote_key($_fkey, $separator);
1062
  $out .= '</' . $enclosing_tag .'>';
1063

    
1064
  RenderHints::popFromRenderStack();
1065

    
1066
  return $out;
1067
}
1068

    
1069
/**
1070
 * Creates markup for a list of SpecimenTypedesignationDTO
1071
 *
1072
 * @param array $specimen_typedesignation_dtos
1073
 *  The SpecimenTypedesignationDTOs to be rendered
1074
 *
1075
 * @param bool $show_type_specimen
1076
 *
1077
 * @param string $enclosing_tag
1078
 * @param string $element_tag
1079
 *
1080
 * @return string
1081
 *   The markup.
1082
 */
1083
function render_specimen_typedesignation_dto($specimen_typedesignation_dtos, $show_type_specimen = FALSE, $enclosing_tag = 'ul', $element_tag = 'li'){
1084

    
1085
  // need to add element to render path since type designations
1086
  // need other name render template
1087
  RenderHints::pushToRenderStack('typedesignations');
1088

    
1089
  $out = '<' . $enclosing_tag .' class="typeDesignations">';
1090
  $separator = ',';
1091

    
1092
  if (!empty($specimen_typedesignation_dtos)) {
1093
    // usort($specimen_type_designations, "compare_specimen_type_designation"); // FIXME create compare function for SpecimenTypedesignationDTO
1094
    foreach ($specimen_typedesignation_dtos as $std_dto) {
1095
      $type_citation_markup = '';
1096

    
1097
      if (!empty($std_dto->source->citation)) {
1098

    
1099
        $citation_footnote_str = cdm_reference_markup($std_dto->source->citation, null, false, true);
1100
        $author_team = cdm_ws_get(CDM_WS_REFERENCE_AUTHORTEAM, $std_dto->source->citation->uuid);
1101

    
1102
        if (!empty($author_team->titleCache)) {
1103
          $year = @timePeriodToString($std_dto->source->citation->datePublished, true, 'YYYY');
1104
          $authorteam_str = $author_team->titleCache . ($year ? ' ' : '') . $year;
1105
          if ($authorteam_str == $std_dto->source->citation->titleCache) {
1106
            $citation_footnote_str = '';
1107
          }
1108
        } else {
1109
          $authorteam_str = $citation_footnote_str;
1110
          // no need for a footnote in case in case it is used as replacement for missing author teams
1111
          $citation_footnote_str = '';
1112
        }
1113

    
1114
        // for being registered a typedesignation MUST HAVE a citation, so it is save to handle the
1115
        // Registration output in the conditional block which has been checked that the citation is present
1116
        $registration_markup = render_registrations($std_dto->registrations);
1117
        $citation_footnote_str .= ($citation_footnote_str ? ' ' : '') . $registration_markup;
1118

    
1119
        $footnote_key_markup = '';
1120
        if ($citation_footnote_str) {
1121
          // footnotes should be rendered in the parent element so we
1122
          // are relying on the FootnoteListKey set there
1123
          $_fkey2 = FootnoteManager::addNewFootnote(RenderHints::getFootnoteListKey(), $citation_footnote_str);
1124
          $footnote_key_markup = render_footnote_key($_fkey2, $separator, TRUE);
1125
        }
1126

    
1127
        $type_citation_markup .= '&nbsp;(' . t('designated by') . '&nbsp;<span class="typeReference">' . $authorteam_str . '</span>';
1128
        if (!empty($std_dto->source->citationMicroReference)) {
1129
          $type_citation_markup .= ': ' . trim($std_dto->source->citationMicroReference);
1130
        }
1131
        $type_citation_markup .= $footnote_key_markup . ')';
1132
      }
1133

    
1134
      $out .= '<'. $element_tag .' class="' . html_class_attribute_ref($std_dto) . '">';
1135
      $out .= type_designation_dto_status_label_markup($std_dto) . $type_citation_markup;
1136

    
1137
      if($show_type_specimen){
1138
        $derivedUnitFacadeInstance = null;
1139
        if (isset($std_dto->typeSpecimen)) {
1140
          $derivedUnitFacadeInstance = cdm_ws_get(CDM_WS_DERIVEDUNIT_FACADE, $std_dto->typeSpecimen->uuid);
1141
        }
1142

    
1143
        if (!empty($derivedUnitFacadeInstance->titleCache)) {
1144
          $specimen_markup = $derivedUnitFacadeInstance->titleCache;
1145
          if(isset($derivedUnitFacadeInstance->specimenLabel) && $derivedUnitFacadeInstance->specimenLabel){
1146
            $specimen_markup = str_replace($derivedUnitFacadeInstance->specimenLabel, l($derivedUnitFacadeInstance->specimenLabel, path_to_specimen($std_dto->typeSpecimen->uuid)), $specimen_markup);
1147
          }
1148
          RenderHints::setAnnotationsAndSourceConfig(annotations_and_sources_config_typedesignations());
1149
          $annotations_and_sources = handle_annotations_and_sources($derivedUnitFacadeInstance);
1150
          $out .= ': <span class="' . html_class_attribute_ref($std_dto->typeSpecimen) . '">'
1151
            . $specimen_markup
1152
            . '</span>'; // . ': ' . theme('cdm_specimen', array('specimenTypeDesignation' => $derivedUnitFacadeInstance));
1153
          if(!empty($derivedUnitFacadeInstance->preferredStableUri)){
1154
            $out .= ' ' . l($derivedUnitFacadeInstance->preferredStableUri, $derivedUnitFacadeInstance->preferredStableUri, array('absolute' => true));
1155
          }
1156
          $out .= $annotations_and_sources->footNoteKeysMarkup();
1157
        }
1158
      }
1159

    
1160
      $out .= '</'. $element_tag .'>';
1161
    }
1162
    RenderHints::popFromRenderStack();
1163
  }
1164
  return $out;
1165
}
1166

    
1167

    
1168
/**
1169
 * Composes the textual representation for the type designation of taxon name identified by the uuid in with a map for the location data.
1170
 *
1171
 * @param $taxon_name_uuid
1172
 * @param $show_specimen_details
1173
 * @return array
1174
 *    A drupal render array with the following elements:
1175
 *    - 'type_designations'
1176
 *    - 'map'
1177
 *    - 'specimens'
1178
 *
1179
 * @ingroup compose
1180
 */
1181
function compose_type_designations($taxon_name_uuid, $show_specimen_details = false)
1182
{
1183
  $render_array = array(
1184
    'type_designations' => array(),
1185
    'map' => array(),
1186
    );
1187
  $type_designations = cdm_ws_get(CDM_WS_PORTAL_NAME_TYPEDESIGNATIONS, $taxon_name_uuid);
1188
  if ($type_designations) {
1189
    usort($type_designations, 'compare_specimen_type_designation');
1190
    $render_array['type_designations'] = markup_to_render_array(
1191
      render_type_designations($type_designations, 'div', 'div')
1192
    );
1193

    
1194
    $render_array['map'] = compose_type_designations_map($type_designations);
1195
  }
1196
  return $render_array;
1197
}
1198

    
1199

    
1200
/**
1201
 * Composes the TypedEntityReference to name type designations passed as associatve array.
1202
 *
1203
 * @param $type_entity_refs_by_status array
1204
 *   an associative array of name type type => TypedEntityReference for name type designations as
1205
 *   produced by the eu.etaxonomy.cdm.api.service.name.TypeDesignationSetManager
1206
 *
1207
 * @ingroup compose
1208
 */
1209
function compose_name_type_designations($type_entity_refs_by_status){
1210
  $render_array = array();
1211
  $preferredStableUri = '';
1212
  foreach($type_entity_refs_by_status as $type_status => $name_type_entityRefs){
1213
    foreach ($name_type_entityRefs as $name_type_entity_ref){
1214
      $type_designation = cdm_ws_get(CDM_WS_TYPEDESIGNATION, array($name_type_entity_ref->uuid, 'preferredUri'));
1215
      $footnote_keys = '';
1216

    
1217
      if(isset($type_designation->typeSpecimen->preferredStableUri) && $type_designation->typeSpecimen->preferredStableUri){
1218
        $preferredStableUri = $type_designation->typeSpecimen->preferredStableUri;
1219
      }
1220
      // annotations and sources for the $derived_unit_facade_dto
1221
      RenderHints::setAnnotationsAndSourceConfig(annotations_and_sources_config_typedesignations());
1222
      $annotations_and_sources = handle_annotations_and_sources($name_type_entity_ref);
1223

    
1224
      $render_array[] = markup_to_render_array('<div class="name_type_designation ' . html_class_attribute_ref($name_type_entity_ref)  .
1225
        '"><span class="type-status">'. ucfirst($type_status) . "</span>: "
1226
        . $name_type_entity_ref->label
1227
        . ($preferredStableUri ? " ". l($preferredStableUri,  $preferredStableUri) : '')
1228
        . $annotations_and_sources->footNoteKeysMarkup()
1229
        . '</div>');
1230
      }
1231
  }
1232
  return $render_array;
1233
}
1234

    
1235
/**
1236
 * Composes the specimen type designations with map from the the $type_entity_refs
1237
 *
1238
 * @param $type_entity_refs array
1239
 *   an associative array of specimen type type => TypedEntityReference for specimen type designations as
1240
 *   produced by the eu.etaxonomy.cdm.api.service.name.TypeDesignationSetManager
1241
 *
1242
 * @param $show_media_specimen
1243
 * @return array
1244
 *    A drupal render array with the following elements:
1245
 *    - 'type_designations'
1246
 *    - 'map'
1247
 *
1248
 * @ingroup compose
1249
 *
1250
 */
1251
function compose_specimen_type_designations($type_entity_refs, $show_media_specimen){
1252

    
1253
  $render_array = array();
1254

    
1255
  $type_designation_list = array();
1256
  uksort($type_entity_refs, "compare_type_designation_status_labels");
1257
  foreach($type_entity_refs as $type_status => $type_designation_entity_refs){
1258
    foreach($type_designation_entity_refs as $type_designation_entity_ref){
1259

    
1260
      $type_designation = cdm_ws_get(CDM_WS_PORTAL_TYPEDESIGNATION, array($type_designation_entity_ref->uuid));
1261
      $type_designation_list[] = $type_designation; // collect for the map
1262

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

    
1267

    
1268
      $preferredStableUri = '';
1269
      $citation_markup = '';
1270
      $media = '';
1271

    
1272
      // annotations and sources for the $derived_unit_facade_dto
1273
      RenderHints::setAnnotationsAndSourceConfig(annotations_and_sources_config_typedesignations());
1274
      $annotations_and_sources = handle_annotations_and_sources($derived_unit_facade_dto);
1275
      $source_citations = $annotations_and_sources->getSourceReferences();
1276
      $foot_note_keys = $annotations_and_sources->footNoteKeysMarkup();
1277

    
1278
      // preferredStableUri
1279
      if(isset($type_designation->typeSpecimen->preferredStableUri) && $type_designation->typeSpecimen->preferredStableUri){
1280
        $preferredStableUri = $type_designation->typeSpecimen->preferredStableUri;
1281
      }
1282

    
1283
      if($show_media_specimen && $mediaSpecimen){
1284
        // compose output
1285
        // mediaURI
1286
        if(isset($mediaSpecimen->representations[0])) {
1287
          $gallery_settings = getGallerySettings(CDM_DATAPORTAL_SPECIMEN_GALLERY_NAME);
1288
          $captionElements = array(
1289
            '#uri' => t('open media'),
1290
            'elements' => array('-none-'),
1291
            'sources_as_content' => true
1292
          );
1293
          $media = compose_cdm_media_gallerie(array(
1294
            'mediaList' => array($mediaSpecimen),
1295
            'galleryName' => CDM_DATAPORTAL_TYPE_SPECIMEN_GALLERY_NAME . '_' . $type_designation_entity_ref->uuid,
1296
            'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
1297
            'cols' => $gallery_settings['cdm_dataportal_media_cols'],
1298
            'captionElements' => $captionElements,
1299
          ));
1300
        }
1301
        // citation and detail for the media specimen
1302
        RenderHints::setAnnotationsAndSourceConfig(annotations_and_sources_config_typedesignations());
1303
        $annotations_and_sources = handle_annotations_and_sources($mediaSpecimen);
1304
        if($annotations_and_sources->hasSourceReferences()){
1305
          $source_citations = array_merge($source_citations, $annotations_and_sources->getSourceReferences());
1306
        }
1307
        if($annotations_and_sources->hasFootnoteKeys()){
1308
          $foot_note_keys .= ', ' . $annotations_and_sources->footNoteKeysMarkup();
1309
        }
1310
      }
1311

    
1312
      $citation_markup = join(', ', $source_citations);
1313

    
1314
      $specimen_markup = $derived_unit_facade_dto->titleCache;
1315
      if(isset($derived_unit_facade_dto->specimenLabel) && $derived_unit_facade_dto->specimenLabel){
1316
        $specimen_markup = str_replace(
1317
          $derived_unit_facade_dto->specimenLabel,
1318
          l($derived_unit_facade_dto->specimenLabel, path_to_specimen($type_designation->typeSpecimen->uuid)), $specimen_markup);
1319
      }
1320

    
1321
      $type_designation_render_array = markup_to_render_array(
1322
        '<div class="type_designation_entity_ref ' . html_class_attribute_ref($type_designation_entity_ref)  . '">
1323
          <span class="type-status">' . ucfirst($type_status) . "</span>: "
1324
        . $specimen_markup . $foot_note_keys
1325
        . ($citation_markup ? ' '. $citation_markup : '')
1326
        . ($preferredStableUri ? " ". l($preferredStableUri,  $preferredStableUri) : '')
1327
        . $media
1328
        . '</div>');
1329

    
1330
      $render_array['type_designations'][] = $type_designation_render_array;
1331
    }
1332
  }
1333
  if(count($type_designation_list) > 0 ){
1334
    $render_array['map'] = compose_type_designations_map($type_designation_list);
1335
  } else {
1336
    $render_array['map'] = array();
1337
  }
1338
  return $render_array;
1339
}
1340

    
1341
/**
1342
 * Creates the markup for the given name relationship.
1343
 *
1344
 * For the footnotes see handle_annotations_and_sources().
1345
 *
1346
 * @param $other_name
1347
 *      The other name from the NameRelationship see get_other_name()
1348
 * @param $current_taxon_uuid
1349
 * @param $show_name_cache_only
1350
 *    The nameCache will be shown instead of the titleCache if this parameter is true.
1351
 * @return null|string
1352
 *    The markup or null
1353
 *
1354
 * @see \get_other_name
1355
 */
1356
function name_relationship_markup($current_name_uuid, $name_rel, $current_taxon_uuid, $show_name_cache_only = false){
1357

    
1358
  $relationship_markup = null;
1359

    
1360
  $other_name = get_other_name($current_name_uuid, $name_rel);
1361

    
1362
  cdm_load_tagged_full_title($other_name);
1363

    
1364
  $highlited_synonym_uuid = isset ($other_name->taxonBases[0]->uuid) ? $other_name->taxonBases[0]->uuid : '';
1365
  if($show_name_cache_only){
1366
    $relationship_markup = l(
1367
      '<span class="' . html_class_attribute_ref($other_name) . '"">' . $other_name->nameCache . '</span>',
1368
      path_to_name($other_name->uuid, $current_taxon_uuid, $highlited_synonym_uuid, false),
1369
      array('html' => true)
1370
    );
1371
    $annotations_and_sources = handle_annotations_and_sources($other_name);
1372
    $relationship_markup .= $annotations_and_sources->footNoteKeysMarkup();
1373
  } else {
1374
    $relationship_markup = render_taxon_or_name($other_name,
1375
      url(path_to_name($other_name->uuid, $current_taxon_uuid, $highlited_synonym_uuid, false))
1376
    );
1377
  }
1378

    
1379
  return $relationship_markup;
1380
}
1381

    
1382
/**
1383
 * Determined the other name which is contained in the NameRelationship entity.
1384
 *
1385
 * @param $current_name_uuid
1386
 *   The uuid of this name.
1387
 * @param $name_rel
1388
 *   The cdm NameRelationship entity
1389
 *
1390
 * @return object
1391
 *   The other cdm Name entity.
1392
 */
1393
function get_other_name($current_name_uuid, $name_rel) {
1394
  $current_name_is_toName = $current_name_uuid == $name_rel->toName->uuid;
1395

    
1396
  if ($current_name_is_toName) {
1397
    $name = $name_rel->fromName;
1398
  }
1399
  else {
1400
    $name = $name_rel->toName;
1401
  }
1402
  return $name;
1403
}
1404

    
1405

    
1406
/**
1407
 * Composes an inline representation of selected name relationships
1408
 *
1409
 * The output of this function will be usually appended to taxon name representations.
1410
 * Only the following types are displayed: LATER_HOMONYM, TREATED_AS_LATER_HOMONYM, BLOCKING_NAME_FOR, ORTHOGRAPHIC_VARIANT
1411
 *
1412
 * LATER_HOMONYM, TREATED_AS_LATER_HOMONYM, BLOCKING_NAME_FOR are displayed as
1413
 * non {titleCache} nec {titleCache} nec {titleCache} whereas the related names
1414
 * are ordered alphabetically.
1415
 *
1416
 * ORTHOGRAPHIC_VARIANT is displayed as 'ort. var. {nameCache}'
1417
 *
1418
 * Related issues:
1419
 *   - https://dev.e-taxonomy.eu/redmine/issues/5697 "Show name conserved against as [non xxx]"
1420
 *   - https://dev.e-taxonomy.eu/redmine/issues/6678 "How to correctly show name relationship "orth. var." in dataportal"
1421
 *   - https://dev.e-taxonomy.eu/redmine/issues/5857
1422
 *   - https://dev.e-taxonomy.eu/redmine/issues/2001 "[Cichorieae Portal] Name Relationship -> blocking name are not shown"
1423
 *
1424
 * @param $name_relations
1425
 *    The list of CDM NameRelationsips
1426
 * @param $current_name_uuid
1427
 *    The Uuid of the name for which the relations are to be rendered, the current name will be hidden when
1428
 *    rendering the relation an only the other name is shown. Parameter is REQUIRED.
1429
 * @param $suppress_if_current_name_is_source
1430
 *    The display of the relation will be
1431
 *    suppressed is the current name is on the source of the relation edge.
1432
 *    That is if it is on the from side of the relation. Except for 'blocking name for' which is
1433
 *    an inverse relation. For this relation type the toName is taken in to account.
1434
 * @param $current_taxon_uuid
1435
 *    The taxon to be omitted from related taxa. This is only used to create links, see path_to_name()
1436
 * @return array
1437
 *    A drupal render array
1438
 *
1439
 * @ingroup Compose
1440
 */
1441
function compose_name_relationships_inline($name_relations, $current_name_uuid, $current_taxon_uuid, $suppress_if_current_name_is_source = true) {
1442

    
1443
  RenderHints::pushToRenderStack('homonym');
1444
  // the render stack element homonyms is being used in the default render templates !!!, see NameRenderConfiguration::CDM_NAME_RENDER_TEMPLATES_DEFAULT
1445

    
1446
  $selected_name_rel_uuids = variable_get(CDM_NAME_RELATIONSHIP_INLINE_TYPES, unserialize(CDM_NAME_RELATIONSHIP_INLINE_TYPES_DEFAULT));
1447
  $name_rel_type_filter = array('direct' => array(), 'inverse' => array());
1448
  foreach ($selected_name_rel_uuids as $uuid){
1449
    $name_rel_type_filter['direct'][$uuid] = $uuid;
1450
    if($uuid != UUID_NAMERELATIONSHIPTYPE_MISSPELLING){
1451
      $name_rel_type_filter['inverse'][$uuid] = $uuid;
1452
    }
1453
  }
1454

    
1455
  $list_prefix = '<span class="name_relationships">[';
1456
  $list_suffix = ']</span>';
1457
  $item_prefix = '<span class="item">';
1458
  $item_suffix = '</span> ';
1459
  $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);
1460

    
1461
  // remove the glue space from the last item element which has been added by the $item_suffix = '</span> '
1462
  $items_ctn = count($render_array['list']['items']);
1463
  if($items_ctn){
1464
    $render_array['list']['items'][$items_ctn - 1]['#suffix'] = '</span>';
1465
  }
1466

    
1467
  RenderHints::popFromRenderStack();
1468
  return $render_array;
1469
}
1470

    
1471
/**
1472
 * Composes an list representation of the name relationships.
1473
 *
1474
 * The output of this function will be usually appended to taxon name representations.
1475
 *
1476
 * Related issues:
1477
 *   - https://dev.e-taxonomy.eu/redmine/issues/5697 "Show name conserved against as [non xxx]"
1478
 *   - https://dev.e-taxonomy.eu/redmine/issues/6678 "How to correctly show name relationship "orth. var." in dataportal"
1479
 *   - https://dev.e-taxonomy.eu/redmine/issues/5857
1480
 *
1481
 * @param $name_relations
1482
 *    The list of CDM NameRelationsips
1483
 * @param $current_name_uuid
1484
 *    The Uuid of the name for which the relations are to be rendered, the current name will be hidden when
1485
 *    rendering the relation an only the other name is shown. Parameter is REQUIRED.
1486
 * @param $current_taxon_uuid
1487
 *    The taxon to be omitted from related taxa. This is only used to create links, see path_to_name()
1488
 * @return array
1489
 *    A drupal render array
1490
 *
1491
 * @ingroup Compose
1492
 */
1493
function compose_name_relationships_list($name_relations, $current_name_uuid, $current_taxon_uuid) {
1494

    
1495
  // $ordered_name_relation_type_uuids = array_keys(cdm_terms_by_type_as_option('NameRelationshipType', CDM_ORDER_BY_ORDER_INDEX_ASC));
1496

    
1497
  $key = 'name_relationships';
1498
  RenderHints::pushToRenderStack($key);
1499
  if(RenderHints::isUnsetFootnoteListKey()){
1500
    RenderHints::setFootnoteListKey($key);
1501
  }
1502
  // the render stack element homonyms is being used in the default render templates !!!, see CDM_NAME_RENDER_TEMPLATES_DEFAULT
1503

    
1504
  $selected_name_rel_uuids = variable_get(CDM_NAME_RELATIONSHIP_LIST_TYPES, cdm_vocabulary_as_defaults(UUID_NAME_RELATIONSHIP_TYPE));
1505
  $name_rel_type_filter = array('direct' => array(), 'inverse' => array());
1506
  foreach ($selected_name_rel_uuids as $uuid){
1507
    $name_rel_type_filter['direct'][$uuid] = $uuid;
1508
    $name_rel_type_filter['inverse'][$uuid] = $uuid;
1509
  }
1510

    
1511
  $list_prefix = '<div class="relationships_list name_relationships">';
1512
  $list_suffix = '</div>';
1513
  $item_prefix = '<div class="item">';
1514
  $item_suffix = '</div>';
1515

    
1516
  $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);
1517

    
1518
  RenderHints::popFromRenderStack();
1519
  if(RenderHints::getFootnoteListKey() == $key) {
1520
    $render_array['footnotes'] = markup_to_render_array(render_footnotes(RenderHints::getFootnoteListKey()));
1521
    RenderHints::clearFootnoteListKey();
1522
  }
1523
  return $render_array;
1524
}
1525

    
1526
/**
1527
 * @param $name_relations
1528
 * @param $name_rel_type_filter
1529
 *   Associative array with two keys:
1530
 *   - 'direct': the relationship type uuids for the direct direction of the relation edge to be included
1531
 *   - 'inverse': the relationship type uuids for the direct direction of the relation edge to be included
1532
 * @param $current_name_uuid
1533
 * @param $current_taxon_uuid
1534
 * @param $list_prefix
1535
 * @param $list_suffix
1536
 * @param $item_prefix
1537
 * @param $item_suffix
1538
 * @return array
1539
 *
1540
 * @ingroup Compose
1541
 */
1542
function compose_name_relationships($name_relations, $name_rel_type_filter, $current_name_uuid, $current_taxon_uuid,
1543
                                    $list_prefix, $list_suffix, $item_prefix, $item_suffix)
1544
{
1545
  $non_nec_name_reltype_uuids = array(UUID_NAMERELATIONSHIPTYPE_LATER_HOMONYM,
1546
    UUID_NAMERELATIONSHIPTYPE_TREATED_AS_LATER_HOMONYM,
1547
    UUID_NAMERELATIONSHIPTYPE_CONSERVED_AGAINST,
1548
    UUID_NAMERELATIONSHIPTYPE_MISSPELLING,
1549
    UUID_NAMERELATIONSHIPTYPE_BLOCKING_NAME_FOR);
1550

    
1551
  $render_array = array(
1552
    'list' => array(
1553
      '#prefix' => $list_prefix,
1554
      '#suffix' => $list_suffix,
1555
      'items' => array()
1556
    ),
1557
    'footnotes' => array()
1558
  );
1559

    
1560
  if ($name_relations) {
1561

    
1562
    // remove all relations which are not selected in the settings and
1563
    // separate all LATER_HOMONYM, TREATED_AS_LATER_HOMONYM, BLOCKING_NAME_FOR relations and ORTHOGRAPHIC_VARIANTs
1564
    // for special handling
1565
    $filtered_name_rels = array();
1566
    $non_nec_name_rels = array();
1567
    $orthographic_variants = array();
1568
    foreach ($name_relations as $name_rel) {
1569
      $rel_type_uuid = $name_rel->type->uuid;
1570
      $is_inverse_relation = $current_name_uuid == $name_rel->toName->uuid;
1571
      if ((!$is_inverse_relation && isset($name_rel_type_filter['direct'][$rel_type_uuid]) && $name_rel_type_filter['direct'][$rel_type_uuid])
1572
        ||($is_inverse_relation && isset($name_rel_type_filter['inverse'][$rel_type_uuid]) && $name_rel_type_filter['inverse'][$rel_type_uuid])) {
1573

    
1574
        if (array_search($rel_type_uuid, $non_nec_name_reltype_uuids) !== false && (
1575
            $current_name_uuid == $name_rel->fromName->uuid && $rel_type_uuid != UUID_NAMERELATIONSHIPTYPE_BLOCKING_NAME_FOR
1576
            || $current_name_uuid == $name_rel->toName->uuid && $rel_type_uuid == UUID_NAMERELATIONSHIPTYPE_BLOCKING_NAME_FOR
1577
          )
1578
        ){
1579
          $non_nec_name_rels[] = $name_rel;
1580
        } else if (UUID_NAMERELATIONSHIPTYPE_ORTHOGRAPHIC_VARIANT == $rel_type_uuid) {
1581
          $orthographic_variants[] = $name_rel;
1582
        } else {
1583

    
1584
          $filtered_name_rels[] = $name_rel;
1585
        }
1586
      }
1587
    }
1588
    $name_relations = $filtered_name_rels;
1589

    
1590
    usort($name_relations, 'compare_name_relations_by_term_order_index');
1591

    
1592
    // compose
1593
    $show_name_cache_only = FALSE;
1594
    foreach ($name_relations as $name_rel) {
1595

    
1596
      $is_inverse_relation = $current_name_uuid == $name_rel->toName->uuid;
1597
      $rel_footnote_key_markup = render_footnote_key(handle_name_relationship_as_footnote($name_rel),'');
1598
      $relationship_markup = name_relationship_markup($current_name_uuid, $name_rel, $current_taxon_uuid, $show_name_cache_only);
1599
      $label = cdm_relationship_type_term_abbreviated_label($name_rel->type, $is_inverse_relation);
1600

    
1601
      $symbol = cdm_relationship_type_term_symbol($name_rel->type, $is_inverse_relation);
1602
      $symbol_markup = '<span class="symbol" title="' . $label . '">' . $symbol . '</span>' . $rel_footnote_key_markup . ' ';
1603
      $relationship_markup = $symbol_markup . $relationship_markup;
1604
      if ($relationship_markup) {
1605
        $render_array['list']['items'][] = markup_to_render_array($relationship_markup,
1606
          null,
1607
          $item_prefix,
1608
          $item_suffix);
1609
      }
1610
    }
1611

    
1612
    // name relationships to be displayed as non nec
1613
    if (count($non_nec_name_rels) > 0) {
1614
      $non_nec_markup = '';
1615
      $show_name_cache_only = FALSE;
1616
      foreach ($non_nec_name_rels as $name_rel) {
1617

    
1618
        $is_inverse_relation = $current_name_uuid == $name_rel->toName->uuid;
1619
        $rel_footnote_key_markup = render_footnote_key(handle_name_relationship_as_footnote($name_rel),'');
1620
        $relationship_markup = name_relationship_markup($current_name_uuid, $name_rel, $current_taxon_uuid, $show_name_cache_only);
1621
        $label = cdm_relationship_type_term_abbreviated_label($name_rel->type, $is_inverse_relation);
1622

    
1623
        $symbol = $non_nec_markup ? ' nec ' : 'non';
1624
        $symbol_markup = '<span class="symbol" title="' . $label . '">' . $symbol . '</span>' . $rel_footnote_key_markup .  ' ';
1625
        $non_nec_markup .= $symbol_markup . $relationship_markup;
1626
      }
1627
      if ($non_nec_markup) {
1628
        $render_array['list']['items'][] = markup_to_render_array($non_nec_markup,
1629
          null,
1630
          $item_prefix,
1631
          $item_suffix);
1632
      }
1633
    }
1634

    
1635
    // orthographic variants
1636
    if (count($orthographic_variants) > 0) {
1637
      $show_name_cache_only = TRUE;
1638
      foreach ($orthographic_variants as $name_rel) {
1639

    
1640
        $is_inverse_relation = $current_name_uuid == $name_rel->toName->uuid;
1641
        $rel_footnote_key_markup = render_footnote_key(handle_name_relationship_as_footnote($name_rel),'');
1642
        $relationship_markup = name_relationship_markup($current_name_uuid, $name_rel, $current_taxon_uuid, $show_name_cache_only);
1643
        $label = cdm_relationship_type_term_abbreviated_label($name_rel->type, $is_inverse_relation);
1644
        $symbol = cdm_relationship_type_term_symbol($name_rel->type, $is_inverse_relation);
1645
        $symbol_markup = '<span class="symbol" title="' . $label . '">' . $symbol . '</span>' . $rel_footnote_key_markup .  ' ';
1646
        $relationship_markup = $symbol_markup . $relationship_markup;
1647
      }
1648
      if (isset($relationship_markup) && $relationship_markup) {
1649
        $render_array['list']['items'][] = markup_to_render_array($relationship_markup,
1650
          null,
1651
          $item_prefix,
1652
          $item_suffix);
1653
      }
1654
    }
1655
  }
1656
  return $render_array;
1657
}
1658

    
1659

    
1660

    
1661
/**
1662
 * @param $taxon
1663
 * @return array
1664
 */
1665
function cdm_name_relationships_for_taxon($taxon)
1666
{
1667
  $from_name_relations = cdm_ws_get(CDM_WS_PORTAL_TAXON_FROM_NAMERELATIONS, $taxon->uuid);
1668
  $to_name_relations = cdm_ws_get(CDM_WS_PORTAL_TAXON_TO_NAMERELATIONS, $taxon->uuid);
1669
  $name_relations = array_merge($from_name_relations, $to_name_relations);
1670
  return $name_relations;
1671
}
1672

    
1673

    
1674
/**
1675
 * Recursively searches the array for the $key and sets the given value.
1676
 *
1677
 * Expects the key to be used only once in nested array structures.
1678
 *
1679
 * @param mixed $key
1680
 *   Key to search for.
1681
 * @param mixed $value
1682
 *   Value to set.'
1683
 * @param array $array
1684
 *   Array to search in.
1685
 *
1686
 * @return array
1687
 *   The array the key has been found.
1688
 */
1689
function &array_setr($key, $value, array &$array) {
1690
  $res = NULL;
1691
  foreach ($array as $k => &$v) {
1692
    if ($key == $k) {
1693
      $v = $value;
1694
      return $array;
1695
    }
1696
    elseif (is_array($v)) {
1697
      $innerArray = array_setr($key, $value, $v);
1698
      if ($innerArray) {
1699
        return $array;
1700
      }
1701
    }
1702
  }
1703
  return $res;
1704
}
1705

    
1706
/**
1707
 * Recursively searches the array for the $key and sets is to $new_key
1708
 *
1709
 * Expects the key to be used only once in nested array structures.
1710
 *
1711
 * @param mixed $key
1712
 *   Key to search for.
1713
 * @param mixed $new_key
1714
 *   The new key to use
1715
 * @param array $array
1716
 *   Array to search in.
1717
 *
1718
 * @return bool
1719
 *   True if the key has been found.
1720
 */
1721
function array_replace_keyr($key, $new_key, array &$array) {
1722
  $res = NULL;
1723
  if(array_key_exists($key, $array)){
1724
    $value = $array[$key];
1725
    unset($array[$key]);
1726
    $array[$new_key] = $value;
1727
    return true;
1728
  } else {
1729
    // search in next level
1730
    foreach ($array as &$v) {
1731
        if (is_array($v)) {
1732
          array_replace_keyr($key, $new_key, $v);
1733
        }
1734
      }
1735
  }
1736

    
1737
  return false;
1738
}
1739

    
1740
/**
1741
 * @todo Please document this function.
1742
 * @see http://drupal.org/node/1354
1743
 */
1744
function &get_preceding_contentElement($contentElementKey, array &$renderTemplate) {
1745
  $res = NULL;
1746
  $precedingElement = NULL;
1747
  foreach ($renderTemplate as &$part) {
1748
    foreach ($part as $key => &$element) {
1749
      if ($key == $contentElementKey) {
1750
        return $precedingElement;
1751
      }
1752
      $precedingElement = $element;
1753
    }
1754
  }
1755
  return $res;
1756
}
1757

    
1758
/**
1759
 * @todo Please document this function.
1760
 * @see http://drupal.org/node/1354
1761
 */
1762
function &get_preceding_contentElementKey($contentElementKey, array &$renderTemplate) {
1763
  $res = NULL;
1764
  $precedingKey = NULL;
1765
  foreach ($renderTemplate as &$part) {
1766
    if (is_array($part)) {
1767
      foreach ($part as $key => &$element) {
1768
        if ($key == $contentElementKey) {
1769
          return $precedingKey;
1770
        }
1771
        if (!str_beginsWith($key, '#')) {
1772
          $precedingKey = $key;
1773
        }
1774
      }
1775
    }
1776
  }
1777
  return $res;
1778
}
1779

    
1780
function nameTypeToDTYPE($dtype){
1781
  static $nameTypeLabelMap = array(
1782
    "ICNB" => "BacterialName",
1783
    "ICNAFP" => "BotanicalName",
1784
    "ICNCP" => "CultivarPlantName",
1785
    "ICZN" => "ZoologicalName",
1786
    "ICVCN" => "ViralName",
1787
    "Any taxon name" => "TaxonName",
1788
    "NonViral" => "TaxonName",
1789
    "Fungus" => "BotanicalName",
1790
    "Plant" => "BotanicalName",
1791
    "Algae" => "BotanicalName",
1792
  );
1793
  return $nameTypeLabelMap[$dtype];
1794

    
1795
}
1796

    
1797

    
1798
function compare_name_relations_by_term_order_index($name_rel1, $name_rel2){
1799
  return compare_terms_by_order_index($name_rel1->type, $name_rel2->type);
1800
}
1801

    
1802
/**
1803
 * Provides an array with the different registration types covered by the passed registration.
1804
 *
1805
 * The labels in the returned array are translatable.
1806
 *
1807
 * See also https://dev.e-taxonomy.eu/redmine/issues/8016
1808
 *
1809
 * @param $registration_dto
1810
 * @return array
1811
 *    An array of the labels describing the different registration types covered by the passed registration.
1812
 */
1813
function registration_types($registration_dto){
1814
  $reg_type_labels = array();
1815
  if(isset($registration_dto->nameRef)){
1816
    $reg_type_labels["name"] = t("new name");
1817
    $reg_type_labels["taxon"] = t("new taxon");
1818
    $name_relations = cdm_ws_fetch_all(str_replace("$0", $registration_dto->nameRef->uuid, CDM_WS_PORTAL_NAME_NAME_RELATIONS));
1819
    $is_new_combination = true;
1820
    foreach($name_relations as $name_rel){
1821
      if(isset($name_rel->type->uuid)){
1822
        $name_is_from_name = $registration_dto->nameRef->uuid == $name_rel->fromName->uuid;
1823
        switch($name_rel->type->uuid) {
1824
          case UUID_NAMERELATIONSHIPTYPE_BASIONYM:
1825
            if(!$name_is_from_name){
1826
              $reg_type_labels["basionym"] = t("new combination");
1827
              $is_new_combination = true;
1828
            }
1829
            break;
1830
          case UUID_NAMERELATIONSHIPTYPE_REPLACED_SYNONYM:
1831
            if(!$name_is_from_name) {
1832
              $is_new_combination = true;
1833
            }
1834
            break;
1835
          case UUID_NAMERELATIONSHIPTYPE_VALIDATED_BY_NAME:
1836
            if(!$name_is_from_name) {
1837
              $reg_type_labels["validation"] = t("validation");
1838
            }
1839
            break;
1840
          case UUID_NAMERELATIONSHIPTYPE_ORTHOGRAPHIC_VARIANT:
1841
            if(!$name_is_from_name) {
1842
              $reg_type_labels["orth_var"] = t("orthographical correction");
1843
            }break;
1844
          default:
1845
            // NOTHING
1846
        }
1847
      }
1848
    }
1849
    if($is_new_combination){
1850
      unset($reg_type_labels["taxon"]);
1851
    }
1852
  }
1853
  if(isset($registration_dto->orderedTypeDesignationWorkingSets)){
1854
    $reg_type_labels[] = t("new nomenclatural type");
1855
  }
1856
  return $reg_type_labels;
1857
}
1858

    
1859
/**
1860
 * Collects and deduplicates the type designations associated with the passes synonyms.
1861
 *
1862
 * @param $synonymy_group
1863
 *    An array containing a homotypic or heterotypic group of names.
1864
 * @param $accepted_taxon_name_uuid
1865
 *    The uuid of the accepted taxon name. Optional parameter which is required when composing
1866
 *    the information for the homotypic group. In this case the accepted taxon is not included
1867
 *    in the $synonymy_group and must therefor passed in this second parameter.
1868
 *
1869
 * @return array
1870
 *    The CDM TypeDesignation entities
1871
 */
1872
function type_designations_for_synonymy_group($synonymy_group, $accepted_taxon_name_uuid = null)
1873
{
1874
  if (count($synonymy_group) > 0) {
1875
    $name_uuid = array_pop($synonymy_group)->name->uuid;
1876
  } else {
1877
    $name_uuid = $accepted_taxon_name_uuid;
1878
  }
1879
  if ($name_uuid) {
1880
   $type_designations = cdm_ws_get(CDM_WS_PORTAL_NAME_TYPEDESIGNATIONS_IN_HOMOTYPICAL_GROUP, $name_uuid);
1881
    if ($type_designations) {
1882
      return $type_designations;
1883
    }
1884
  }
1885

    
1886
  return array();
1887
}
1888

    
1889

    
1890
/**
1891
 * Compares two SpecimenTypeDesignations
1892
 *
1893
 * @param object $a
1894
 *   A SpecimenTypeDesignation.
1895
 * @param object $b
1896
 *   SpecimenTypeDesignation.
1897
 */
1898
function compare_specimen_type_designation($a, $b) {
1899

    
1900
  $cmp_by_status = compare_type_designations_by_status($a,$b);
1901
  if($cmp_by_status !== 0){
1902
    return $cmp_by_status;
1903
  }
1904

    
1905
  $aQuantifier = FALSE;
1906
  $bQuantifier = FALSE;
1907
  if ($aQuantifier == $bQuantifier) {
1908
    // Sort alphabetically.
1909
    $a_text =  isset($a->typeSpecimen->titleCache) ? preg_replace('/[\[\]\"]/', '', $a->typeSpecimen->titleCache) : '';
1910
    $b_text =  isset($b->typeSpecimen->titleCache) ? preg_replace('/[\[\]\"]/', '', $b->typeSpecimen->titleCache) : '';
1911
    return strcasecmp($a_text, $b_text);
1912
  }
1913
  return ($aQuantifier < $bQuantifier) ? -1 : (($aQuantifier > $bQuantifier) ? 1 : 0);
1914
}
1915

    
1916
/**
1917
 * Compares the status of two TypeDesignations
1918
 *
1919
 * @param object $a
1920
 *   A TypeDesignation
1921
 * @param object $b
1922
 *   TypeDesignation
1923
 */
1924
function compare_type_designations_by_status($a, $b) {
1925
  $status_a = isset($a->typeStatus) ? $a->typeStatus : null;
1926
  $status_b = isset($b->typeStatus) ? $b->typeStatus : null;
1927
  return compare_type_designation_status($status_a, $status_b);
1928
}
1929

    
1930
/**
1931
 * Compares two TypeDesignationStatusBase
1932
 *
1933
 * @param object $a
1934
 *   A TypeDesignationStatusBase.
1935
 * @param object $b
1936
 *   TypeDesignationStatusBase.
1937
 */
1938
function compare_type_designation_status($a, $b) {
1939
  $type_status_order = type_status_order();
1940
  $aQuantifier = FALSE;
1941
  $bQuantifier = FALSE;
1942
  if (isset($a->label) && isset($b->label)) {
1943
    $aQuantifier = array_search($a->label, $type_status_order);
1944
    $bQuantifier = array_search($b->label, $type_status_order);
1945
  }
1946
  return ($aQuantifier < $bQuantifier) ? -1 : (($aQuantifier > $bQuantifier) ? 1 : 0);
1947
}
1948

    
1949
/**
1950
 * Compares the two TextualTypeDesignations
1951
 *
1952
 * @param object $a
1953
 *   A TextualTypeDesignations.
1954
 * @param object $b
1955
 *   TextualTypeDesignations.
1956
 */
1957
function compare_textual_type_designation($a, $b) {
1958

    
1959
  $cmp_by_status = compare_type_designations_by_status($a,$b);
1960
  if($cmp_by_status !== 0){
1961
    return $cmp_by_status;
1962
  }
1963

    
1964
  $aQuantifier = FALSE;
1965
  $bQuantifier = FALSE;
1966
  if ($aQuantifier == $bQuantifier) {
1967
    // Sort alphabetically.
1968
    $a_text =  isset($a->text_L10n->text) ? $a->text_L10n->text : '';
1969
    $b_text =  isset($b->text_L10n->text) ? $b->text_L10n->text : '';
1970
    return strcasecmp($a_text, $b_text);
1971
  }
1972
  return ($aQuantifier < $bQuantifier) ? -1 : (($aQuantifier > $bQuantifier) ? 1 : 0);
1973
}
1974

    
1975

    
1976
/**
1977
 * Compares two SpecimenTypeDesignation status labels
1978
 *
1979
 * @param string $a
1980
 *   A TypeDesignationStatus label.
1981
 * @param string $b
1982
 *   A TypeDesignationStatus label.
1983
 */
1984
function compare_type_designation_status_labels($a, $b) {
1985

    
1986
  $type_status_order = type_status_order();
1987

    
1988
  $aQuantifier = FALSE;
1989
  $bQuantifier = FALSE;
1990
  if (isset($a) && isset($b)) {
1991
    $aQuantifier = array_search(ucfirst($a), $type_status_order);
1992
    $bQuantifier = array_search(ucfirst($b), $type_status_order);
1993
  }
1994
  return ($aQuantifier < $bQuantifier) ? -1 : 1;
1995
}
1996

    
1997
/**
1998
 * Preliminary implementation of a preset to define a sort order for
1999
 * type designation status.
2000
 *
2001
 * TODO this is only preliminary and may break in future,
2002
 *      see https://dev.e-taxonomy.eu/redmine/issues/8402?issue_count=96&issue_position=4&next_issue_id=8351&prev_issue_id=7966#note-4
2003
 * @return array
2004
 *   The array of orderd type labels
2005
 */
2006
function type_status_order()
2007
{
2008
  /*
2009
    This is the desired sort order as of now: Holotype Isotype Lectotype
2010
    Isolectotype Syntype.
2011
    TODO Basically, what we are trying to do is, we define
2012
    an ordered array of TypeDesignation-states and use the index of this array
2013
    for comparison. This array has to be filled with the cdm- TypeDesignation
2014
    states and the order should be parameterisable inside the dataportal.
2015
    */
2016
  static $type_status_order = array(
2017
    'Epitype',
2018
    'Holotype',
2019
    'Isotype',
2020
    'Lectotype',
2021
    'Isolectotype',
2022
    'Syntype',
2023
    'Paratype'
2024
  );
2025
  return $type_status_order;
2026
}
2027

    
2028
/**
2029
 * Return HTML for the lectotype citation with the correct layout.
2030
 *
2031
 * This function prints the lectotype citation with the correct layout.
2032
 * Lectotypes are renderized in the synonymy tab of a taxon if they exist.
2033
 *
2034
 * @param mixed $typeDesignation
2035
 *   Object containing the lectotype citation to print.
2036
 *
2037
 * @return string
2038
 *   Valid html string.
2039
 */
2040
function type_designation_citation_layout($typeDesignation, $footnote_separator = ',') {
2041
  $res = '';
2042
  $citation = $typeDesignation->source->citation;
2043
  $pages = $typeDesignation->source->citationMicroReference;
2044
  if(isset($typeDesignation->typeStatus->uuid) && isset($typeDesignation->typeStatus->representation_L10n)) {
2045
    if ( $typeDesignation->typeStatus->uuid == UUID_NTD_ORIGINAL_DESIGNATION || $typeDesignation->typeStatus->uuid == UUID_NTD_MONOTYPY) {
2046
      $res = ' (' . $typeDesignation->typeStatus->representation_L10n . ')';
2047
      return $res;
2048
    }
2049
  }
2050

    
2051
  if ($citation) {
2052
    // $type = $typeDesignation_citation->type;
2053
    $year = isset($citation->datePublished->start) ? substr($citation->datePublished->start, 0, 4) : '';
2054
    $author = isset($citation->authorship->titleCache) ? $citation->authorship->titleCache : '';
2055
    $res .= ' (designated by ';
2056
    $res .= $author;
2057
    $res .= ($year ? ' ' . $year : '');
2058
    $res .= ($pages ? ': ' . $pages : '');
2059
    // $res .= ')';
2060

    
2061
    // footnotes should be rendered in the parent element so we
2062
    // are relying on the FootnoteListKey set there
2063
    $fkey_typeDesignation = FootnoteManager::addNewFootnote(RenderHints::getFootnoteListKey(), $citation->titleCache);
2064
    $res .= render_footnote_key($fkey_typeDesignation, $footnote_separator,TRUE) . ')';
2065
  }
2066
  return $res;
2067
}
2068

    
2069
/**
2070
 * 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"
2071
 *
2072
 * @param $type_designation
2073
 * @return string
2074
 */
2075
function type_designation_status_label_markup($type_designation)
2076
{
2077
  return '<span class="type-status">'
2078
    . ((isset($type_designation->typeStatus->representation_L10n)) ? ucfirst($type_designation->typeStatus->representation_L10n) : t('Type')) . '</span>'
2079
    ;
2080
}
2081

    
2082
/**
2083
 * Creates markup for the status of a type designation DTO.
2084
 * In case the status or its representation is missing the label will be set to "Type"
2085
 *
2086
 * @param $type_designation
2087
 * @return string
2088
 */
2089
function type_designation_dto_status_label_markup($type_designation)
2090
{
2091
  return '<span class="type-status">'
2092
    . ((isset($type_designation->typeStatus_L10n)) ? ucfirst($type_designation->typeStatus_L10n) : t('Type')) . '</span>'
2093
    ;
2094
}
(7-7/14)