Project

General

Profile

Download (64.4 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2

    
3
/**
4
 * Returns the localized representations of the modifiers hold by the
5
 * supplied cdm instance concatenated into one string.
6
 *
7
 * @param object $iModifieable
8
 *   cdm instance of an class implementing the interface IModifieable:
9
 *   DescriptionElementBase, StateDate, State
10
 *
11
 * @return String
12
 *   localized representations of the modifiers hold by the
13
 *   supplied cdm instance concatenated into one string
14
 */
15
function cdm_modifers_representations($iModifieable, $glue = ', ') {
16
  $modifiers_strings = array();
17
  if (isset($iModifieable->modifiers)) {
18
    foreach ($iModifieable->modifiers as $modifier) {
19
      $modifiers_strings[] = cdm_term_representation($modifier);
20
    }
21
  }
22
  return implode(', ', $modifiers_strings);
23
}
24

    
25
/**
26
 * Filters the given set of description elements and prefers computed elements over others.
27
 *
28
 * Computed description elements
29
 * are identified by the MarkerType.COMPUTED()
30
 *
31
 * If the given set contains at least one computed element only
32
 * the computed elements are returned.
33
 *
34
 * @param array $description_elements
35
 *   An array of CDM DescriptionElementBase instances
36
 *
37
 * @return array
38
 *   only the computed description elements otherwise all others.
39
 *
40
 * @deprecated this is replaced by the cdmlib DistributionUtil class!!!
41
 */
42
function cdm_description_elements_prefer_computed($description_elements){
43

    
44
  $computed_elements = array();
45
  $other_elements = array();
46

    
47
  if (!empty($description_elements)) {
48
    foreach ($description_elements as $element) {
49
      if (cdm_entity_has_marker($element, UUID_MARKERTYPE_COMPUTED)) {
50
        $computed_elements[] = $element;
51
      }
52
      else {
53
        $other_elements[] = $element;
54
      }
55
    }
56
  }
57

    
58
  if (count($computed_elements) > 0) {
59
    return $computed_elements;
60
  }
61
  else {
62
    return $other_elements;
63
  }
64
}
65

    
66
/**
67
 * Creates a query parameter array based on the setting stored in the drupal variable CDM_DISTRIBUTION_FILTER.
68
 *
69
 * @return array
70
 *   An array with distribution filter query parameters
71
 */
72
function cdm_distribution_filter_query() {
73
  $cdm_distribution_filter = get_array_variable_merged(CDM_DISTRIBUTION_FILTER, CDM_DISTRIBUTION_FILTER_DEFAULT);
74
  $query = array();
75

    
76
  if ($cdm_distribution_filter['filter_rules']['statusOrderPreference']) {
77
    $query['statusOrderPreference'] = 1;
78
  }
79
  if ($cdm_distribution_filter['filter_rules']['subAreaPreference']) {
80
    $query['subAreaPreference'] = 1;
81
  }
82
  if (is_array($cdm_distribution_filter['hiddenAreaMarkerType']) && count($cdm_distribution_filter['hiddenAreaMarkerType']) > 0) {
83
    $query['hiddenAreaMarkerType'] = '';
84
    foreach ($cdm_distribution_filter['hiddenAreaMarkerType'] as $marker_type => $enabled) {
85
      if ($enabled) {
86
        $query['hiddenAreaMarkerType'] .= ($query['hiddenAreaMarkerType'] ? ',' : '') . $marker_type;
87
      }
88
    }
89
  }
90

    
91
  return $query;
92
}
93

    
94
/**
95
 * Merge the fields 'annotations', 'markers', 'sources', 'media' from the source CDM DescriptionElement into  the target.
96
 *
97
 * @param object $target
98
 *   The source CDM DescriptionElement
99
 * @param object $source
100
 *   The target CDM DescriptionElement
101
 */
102
function cdm_merge_description_elements(&$target, &$source) {
103
  static $fields_to_merge = array('annotations', 'markers', 'sources', 'media');
104

    
105
  foreach ($fields_to_merge as $field) {
106
    if (is_array($source->$field)) {
107
      if (!is_array($target->$field)) {
108
        $target->$field = $source->$field;
109
      }
110
      else {
111
        $target->$field = array_merge($target->$field, $source->$field);
112
      }
113
    }
114
  }
115
}
116

    
117
/**
118
 * Adds an entry to the end of the table of content items list
119
 *
120
 * The  table of content items are crated internally by calling
121
 * toc_list_item() the resulting item is added to the statically cached
122
 * list of toc elements
123
 *
124
 * @param string $label
125
 *   The label of toc entry
126
 * @param $class_attribute_suffix
127
 *   The suffix to be appended to the class attribute prefix: "feature-toc-item-"
128
 * @param string $fragment
129
 *   Optional parameter to define a url fragment different from the $label,
130
 *   if the $fragment is not defined the $label will be used
131
 */
132
function cdm_toc_list_add_item($label, $class_attribute_suffix, $fragment = NULL, $as_first_element = FALSE) {
133
  $toc_list_items = &cdm_toc_list();
134

    
135
  if (!$fragment) {
136
    $fragment = $label;
137
  }
138
  $fragment = generalizeString($fragment);
139

    
140
  $class_attributes = 'feature-toc-item-' . $class_attribute_suffix;
141

    
142
  $new_item = toc_list_item(
143
    theme(
144
      'cdm_feature_name',
145
      array('feature_name' => $label)),
146
      array('class' => $class_attributes),
147
      $fragment
148
    );
149

    
150
  if ($as_first_element) {
151
    array_unshift($toc_list_items, $new_item);
152
  }
153
  else {
154
    $toc_list_items[] = $new_item;
155
  }
156

    
157
}
158

    
159
/**
160
 * Returns the statically cached table of content items as render array.
161
 *
162
 * @see cdm_toc_list_add_item()
163
 *
164
 * @return array
165
 *   a render array of table of content items suitable for theme_item_list()
166
 */
167
function &cdm_toc_list(){
168
  $toc_list_items = &drupal_static('toc_list_items', array());
169

    
170
  return $toc_list_items;
171
}
172

    
173
/**
174
 * Prepares an empty Drupal block for displaying description elements of a specific CDM Feature.
175
 *
176
 * The block can also be used for pseudo Features like a bibliography. Pseudo features are
177
 * derived from other data on the fly and so not exist as such in the cdm data. In case
178
 * of pseudo features the $feature is left empty
179
 *
180
 * @param $feature_name
181
 *   A label describing the feature, usually the localized feature representation.
182
 * @param object $feature
183
 *   The CDM Feature for which the block is created. (optional)
184
 * @return object
185
 *   A Drupal block object
186
 */
187
function feature_block($feature_name, $feature = NULL) {
188
  $block = new stdclass(); // Empty object.
189
  $block->module = 'cdm_dataportal';
190
  $block->delta = generalizeString($feature_name);
191
  $block->region = NULL;
192
  $block->subject = '<a name="' . $block->delta . '"></a><span class="' . html_class_attribute_ref($feature) . '">'
193
    . theme('cdm_feature_name', array('feature_name' => $feature_name))
194
    . '</span>';
195
  $block->module = "cdm_dataportal-feature";
196
  $block->content = '';
197
  return $block;
198
}
199

    
200

    
201
/**
202
 * Returns a list of a specific type of IdentificationKeys.
203
 *
204
 * The list can be restricted by a taxon.
205
 *
206
 * @param string $type
207
 *   The simple name of the cdm class implementing the interface
208
 *   IdentificationKey, valid values are:
209
 *   PolytomousKey, MediaKey, MultiAccessKey.
210
 * @param string $taxonUuid
211
 *   If given this parameter restrict the listed keys to those which have
212
 *   the taxon identified be this uuid in scope.
213
 *
214
 * @return array
215
 *   List with identification keys.
216
 */
217
function _list_IdentificationKeys($type, $taxonUuid = NULL, $pageSize = NULL, $pageNumber = NULL) {
218
  if (!$type) {
219
    drupal_set_message(t('Type parameter is missing'), 'error');
220
    return;
221
  }
222
  $cdm_ws_pasepath = NULL;
223
  switch ($type) {
224
    case "PolytomousKey":
225
      $cdm_ws_pasepath = CDM_WS_POLYTOMOUSKEY;
226
      break;
227

    
228
    case "MediaKey":
229
      $cdm_ws_pasepath = CDM_WS_MEDIAKEY;
230
      break;
231

    
232
    case "MultiAccessKey":
233
      $cdm_ws_pasepath = CDM_WS_MULTIACCESSKEY;
234
      break;
235

    
236
  }
237

    
238
  if (!$cdm_ws_pasepath) {
239
    drupal_set_message(t('Type parameter is not valid: ') . $type, 'error');
240
  }
241

    
242
  $queryParameters = '';
243
  if (is_numeric($pageSize)) {
244
    $queryParameters = "pageSize=" . $pageSize;
245
  }
246
  else {
247
    $queryParameters = "pageSize=0";
248
  }
249

    
250
  if (is_numeric($pageNumber)) {
251
    $queryParameters = "pageNumber=" . $pageNumber;
252
  }
253
  else {
254
    $queryParameters = "pageNumber=0";
255
  }
256
  $queryParameters = NULL;
257
  if ($taxonUuid) {
258
    $queryParameters = "findByTaxonomicScope=$taxonUuid";
259
  }
260
  $pager = cdm_ws_get($cdm_ws_pasepath, NULL, $queryParameters);
261

    
262
  if (!$pager || $pager->count == 0) {
263
    return array();
264
  }
265
  return $pager->records;
266
}
267

    
268

    
269
/**
270
 * Creates a list item for a table of content, suitable as data element for a themed list
271
 *
272
 * @see theme_list()
273
 *
274
 * @param $label
275
 * @param $http_request_params
276
 * @param $attributes
277
 * @return array
278
 */
279
function toc_list_item($label, $attributes = array(), $fragment = null) {
280

    
281
  // we better cache here since drupal_get_query_parameters has no internal static cache variable
282
  $http_request_params = drupal_static('http_request_params', drupal_get_query_parameters());
283

    
284
  $item =  array(
285
    'data' => l(
286
      $label,
287
      $_GET['q'],
288
      array(
289
        'attributes' => array('class' => array('toc')),
290
        'fragment' => generalizeString($label),
291
        'query' => $http_request_params,
292
      )
293
    ),
294
  );
295
  $item['attributes'] = $attributes;
296
  return $item;
297
}
298

    
299
/**
300
 * Creates the footnotes for the given CDM DescriptionElement instance.
301
 *
302
 * Footnotes are created for annotations and original sources,
303
 * optionally the sources are put into a separate bibliography.
304
 *
305
 * @param $descriptionElement
306
 *   A CDM DescriptionElement instance
307
 * @param $separator
308
 *   Optional parameter. The separator string to concatenate the footnote ids, default is ','
309
 * @param $footnote_list_key_suggestion
310
 *   will be overridden for original sources if the bibliography block is enabled
311
 *
312
 * @return String
313
 *   The foot note keys
314
 */
315
function cdm_create_description_element_footnotes($description_element, $separator = ',',
316
          $footnote_list_key_suggestion = null, $do_link_to_reference = FALSE,
317
          $do_link_to_name_used_in_source = FALSE
318
    ){
319

    
320

    
321
  // Annotations as footnotes.
322
  $footNoteKeys = cdm_annotations_as_footnotekeys($description_element, $footnote_list_key_suggestion);
323

    
324
  // Source references as footnotes.
325
  $bibliography_settings = get_bibliography_settings();
326
  $original_source_footnote_tag = $bibliography_settings['enabled'] == 1 ? 'div' : null; // null will cause original_source_footnote_list_key to use the default
327

    
328
  usort($description_element->sources, 'compare_original_sources');
329
  foreach ($description_element->sources as $source) {
330
    if (_is_original_source_type($source)) {
331
      $fn_key = FootnoteManager::addNewFootnote(
332
        original_source_footnote_list_key($footnote_list_key_suggestion),
333
        theme('cdm_OriginalSource', array(
334
          'source' => $source,
335
          'doLink' => $do_link_to_reference,
336
          'do_link_to_name_used_in_source' => $do_link_to_name_used_in_source
337

    
338
        )),
339
        $original_source_footnote_tag
340
      );
341
      // Ensure uniqueness of the footnote keys.
342
      cdm_add_footnote_to_array($footNoteKeys, $fn_key);
343
    }
344
  }
345
  // Sort and render footnote keys.
346
  $footnoteKeyListStr = '';
347
  asort($footNoteKeys);
348
  foreach ($footNoteKeys as $footNoteKey) {
349
    $footnoteKeyListStr .= theme('cdm_footnote_key',
350
      array(
351
        'footnoteKey' => $footNoteKey,
352
        'separator' => ($footnoteKeyListStr ? $separator : '')));
353
  }
354
  return $footnoteKeyListStr;
355
}
356

    
357

    
358
/**
359
 * Compare callback to be used in usort() to sort render arrays produced by compose_description_element().
360
 *
361
 * @param $a
362
 * @param $b
363
 */
364
function compare_description_element_render_arrays($a, $b){
365
  if ($a['#value'].$a['#value-suffix'] == $b['#value'].$b['#value-suffix']) {
366
    return 0;
367
  }
368
  return ($a['#value'].$a['#value-suffix'] < $b['#value'].$b['#value-suffix']) ? -1 : 1;
369

    
370
}
371

    
372

    
373
/**
374
 * @param $render_array
375
 * @param $element
376
 * @param $feature_block_settings
377
 * @param $element_markup
378
 * @param $footnote_list_key_suggestion
379
 */
380
function compose_description_element($element, $feature_block_settings, $element_markup, $footnote_list_key_suggestion, $prepend_feature_label = FALSE)
381
{
382

    
383
  $render_array = array(
384
    '#type' => 'html_tag',
385
    '#tag' => cdm_feature_block_element_tag_name($feature_block_settings),
386

    
387
    '#attributes' => array(
388
      'class' => array(
389
        'DescriptionElement',
390
        'DescriptionElement-' . $element->class,
391
        html_class_attribute_ref($element)
392
      )
393
    ),
394

    
395
    '#value' => '',
396
    '#value_suffix' => NULL
397

    
398
  );
399

    
400
  $annotations_and_sources = handle_annotations_and_sources($element, $feature_block_settings, $element_markup, $footnote_list_key_suggestion);
401

    
402
  // handle the special case were the TextData is used as container for original source with name
403
  // used in source information without any text stored in it.
404
  $names_used_in_source_markup = '';
405
  if (!empty($annotations_and_sources['names_used_in_source']) && empty($element_markup)) {
406
    // $element_text ==  NULL  usually occurs only in the case of CITATIONS!!!
407
    $names_used_in_source_markup = join(', ', $annotations_and_sources['names_used_in_source']) . ': ';
408
  }
409

    
410
  $source_references_markup = '';
411
  if (!empty($annotations_and_sources['source_references'])) {
412
    $source_references_markup = '<span class="sources">' . join(' ', $annotations_and_sources['source_references']) . '<span>';
413
  }
414

    
415
  $feature_label = '';
416
  if ($prepend_feature_label) {
417
    $feature_label = '<span class="nested-feature-tree-feature-label">' . $element->feature->representation_L10n . ':</span> ';
418
  }
419
  $render_array['#value'] = $feature_label . $names_used_in_source_markup . $element_markup . $source_references_markup;
420
  $render_array['#value_suffix'] = $annotations_and_sources['foot_note_keys'];
421

    
422
  return $render_array;
423
}
424

    
425

    
426
  /**
427
   * @param $element
428
   * @param $feature_block_settings
429
   * @param $element_text
430
   *   used to decide if the source references should be enclosed in brackets or not
431
   * @param $footnote_list_key_suggestion
432
   * @return array
433
   *   an associative array with the following elements:
434
   *   - foot_note_keys: all footnote keys as markup
435
   *   - source_references: an array of the source references citations
436
   *   - names used in source: an associative array of the names in source,
437
   *        the name in source strings are de-duplicated
438
   *        !!!NOTE!!!!: this field will most probably be removed soon (TODO)
439
   *
440
   *
441
   */
442
  function handle_annotations_and_sources($element, $feature_block_settings, $element_text, $footnote_list_key_suggestion) {
443
    $annotations_and_sources = array(
444
      'foot_note_keys' => NULL,
445
      'source_references' => array(),
446
      'names_used_in_source' => array()
447
    );
448

    
449
    usort($element->sources, 'compare_original_sources');
450

    
451
    if ($feature_block_settings['sources_as_content'] == 1) {
452
      foreach ($element->sources as $source) {
453

    
454
        $referenceCitation = theme('cdm_OriginalSource',
455
          array(
456
            'source' => $source,
457
            'doLink' => $feature_block_settings['link_to_reference'] == 1,
458
            'do_link_to_name_used_in_source' => $feature_block_settings['link_to_name_used_in_source'] == 1,
459
          )
460
        );
461

    
462
        if ($referenceCitation) {
463
          if (empty($element_text)) {
464
            $annotations_and_sources['source_references'][] = $referenceCitation;
465
          }
466
          else {
467
            $annotations_and_sources['source_references'][] = ' (' . $referenceCitation . ')';
468
          }
469
        }
470

    
471
        $name_in_source_render_array = compose_name_in_source(
472
          $source,
473
          $feature_block_settings['link_to_name_used_in_source'] == 1
474
        );
475

    
476
        if(!empty($name_in_source_render_array)){
477
          $annotations_and_sources['names_used_in_source'][$name_in_source_render_array['#_plaintext']] = drupal_render($name_in_source_render_array);
478
        }
479
      } // END of loop over sources
480

    
481
      // annotations footnotes separate.
482
      $annotations_and_sources['foot_note_keys'] = theme('cdm_annotations_as_footnotekeys',
483
        array(
484
          'cdmBase_list' => $element,
485
          'footnote_list_key' => $footnote_list_key_suggestion,
486
        )
487
      );
488

    
489
    } // END of references inline
490

    
491
    // put sources into bibliography if requested ...
492
    if ($feature_block_settings['sources_as_content'] !== 1 || $feature_block_settings['sources_as_content_to_bibliography'] == 1) {
493
      $annotations_and_sources['foot_note_keys'] = cdm_create_description_element_footnotes(
494
        $element, ',',
495
        $footnote_list_key_suggestion,
496
        $feature_block_settings['link_to_reference'] == 1,
497
        $feature_block_settings['link_to_name_used_in_source'] == 1
498
      );
499
    }
500

    
501
    return $annotations_and_sources;
502
  }
503

    
504

    
505
  /**
506
   *
507
   *
508
   * @return string
509
   *  the footnote_list_key
510
   */
511
  function original_source_footnote_list_key($key_suggestion = null) {
512
    if(!$key_suggestion){
513
      $key_suggestion = RenderHints::getFootnoteListKey();
514
    }
515
    $bibliography_settings = get_bibliography_settings();
516
    $footnote_list_key = $bibliography_settings['enabled'] == 1 ? 'BIBLIOGRAPHY' : 'BIBLIOGRAPHY-' . $key_suggestion;
517
    return $footnote_list_key;
518
  }
519

    
520
  /**
521
   * Provides the according tag name for the description element markup which fits the  $feature_block_settings['as_list'] value
522
   *
523
   * @param $feature_block_settings
524
   *   A feature_block_settings array, for details, please see get_feature_block_settings($feature_uuid = 'DEFAULT')
525
   */
526
  function cdm_feature_block_element_tag_name($feature_block_settings){
527
    switch ($feature_block_settings['as_list']){
528
      case 'ul':
529
      case 'ol':
530
        return 'li';
531
      case 'div':
532
        if(isset($feature_block_settings['element_tag'])){
533
          return $feature_block_settings['element_tag'];
534
        }
535
        return 'span';
536
      case 'dl':
537
        return 'dd';
538
      default:
539
        return 'div'; // should never happen, throw error instead?
540
    }
541
  }
542

    
543

    
544
/* ==================== COMPOSE FUNCTIONS =============== */
545

    
546
  /**
547
   * Returns a set of feature blocks for a taxon profile from the $mergedFeatureNodes of a given $taxon.
548
   *
549
   * The taxon profile consists of drupal block elements, one for the description elements
550
   * of a specific feature. The structure is defined by specific FeatureTree.
551
   * The chosen FeatureTree is merged with the list of description elements prior to using this method.
552
   *
553
   * The merged nodes can be obtained by making use of the
554
   * function cdm_ws_descriptions_by_featuretree().
555
   *
556
   * @see cdm_ws_descriptions_by_featuretree()
557
   *
558
   * @param $mergedFeatureNodes
559
   *
560
   * @param $taxon
561
   *
562
   * @return array
563
   *  A Drupal render array containing feature blocks and the table of content
564
   *
565
   * @ingroup compose
566
   */
567
  function compose_feature_blocks($mergedFeatureNodes, $taxon) {
568

    
569
    $block_list = array();
570

    
571
    RenderHints::pushToRenderStack('feature_nodes');
572

    
573
    $gallery_settings = getGallerySettings(CDM_DATAPORTAL_DESCRIPTION_GALLERY_NAME);
574

    
575
    // Create a drupal block for each feature
576
    foreach ($mergedFeatureNodes as $node) {
577

    
578
      if ((isset($node->descriptionElements['#type']) ||
579
          has_feature_node_description_elements($node)) && $node->feature->uuid != UUID_IMAGE) { // skip empty or suppressed features
580

    
581
        $feature_name = cdm_term_representation($node->feature, 'Unnamed Feature');
582
        $feature_block_settings = get_feature_block_settings($node->feature->uuid);
583

    
584
        $block = feature_block($feature_name, $node->feature);
585
        $block->content = array();
586
        $block_content_is_empty = TRUE;
587

    
588
        /*
589
         * Content/DISTRIBUTION.
590
         */
591

    
592
        if ($node->feature->uuid == UUID_DISTRIBUTION) {
593
          $block = compose_feature_block_distribution($taxon, $node->descriptionElements, $node->feature);
594
          $block_content_is_empty = FALSE;
595
        }
596
        /*
597
         * Content/COMMON_NAME.
598
         */
599
        else if ($node->feature->uuid == UUID_COMMON_NAME) {
600
          $common_names_render_array = compose_feature_block_items_feature_common_name($node->descriptionElements, $node->feature);
601
          $block->content[] = $common_names_render_array;
602
          $block_content_is_empty = FALSE;
603
        }
604

    
605
        else if ($node->feature->uuid == UUID_USE_RECORD) {
606
          $block_uses_content_html = theme('cdm_block_Uses', array('taxonUuid' => $taxon->uuid));
607
          $block->content[] = markup_to_render_array($block_uses_content_html);
608
          $block_content_is_empty = FALSE;
609
        }
610

    
611
        /*
612
         * Content/ALL OTHER FEATURES.
613
         */
614
        else {
615

    
616
          $media_list = array();
617
          $elements_render_array = array();
618
          $child_elements_render_array = null;
619

    
620
          if (isset($node->descriptionElements[0])) {
621
            $elements_render_array = compose_feature_block_items_generic($node->descriptionElements, $node->feature);
622
          }
623

    
624
          // Content/ALL OTHER FEATURES/Subordinate Features
625
          // subordinate features are printed inline in one floating text,
626
          // it is expected hat subordinate features can "contain" TextData,
627
          // Qualitative- and Qualitative- DescriptionElements
628
          if (isset($node->childNodes[0])) {
629
            $child_elements_render_array = compose_feature_block_items_nested($node, $media_list, $feature_block_settings);
630
            $elements_render_array = array_merge($elements_render_array, $child_elements_render_array);
631
          }
632
          $block_content_is_empty = $block_content_is_empty && empty($media_list) && empty($elements_render_array);
633
          if(!$block_content_is_empty){
634
            $block->content[] = compose_feature_block_wrap_elements($elements_render_array, $node->feature);
635
            $block->content[] = compose_feature_media_gallery($node, $media_list, $gallery_settings);
636
            /*
637
             * Footnotes for the feature block
638
             */
639
            $block->content[] = markup_to_render_array(theme('cdm_footnotes', array('footnoteListKey' => 'BIBLIOGRAPHY-' . $node->feature->uuid)));
640
            $block->content[] = markup_to_render_array(theme('cdm_footnotes', array('footnoteListKey' => $node->feature->uuid)));
641
            $block->content[] = markup_to_render_array(theme('cdm_annotation_footnotes', array('footnoteListKey' => $node->feature->uuid)));
642
          }
643
        } // END all other features
644

    
645
        // allows modifying the block contents via a the hook_cdm_feature_node_block_content_alter
646
        drupal_alter('cdm_feature_node_block_content', $block->content, $node->feature, $node->descriptionElements);
647

    
648
        if(!$block_content_is_empty){ // skip empty block content
649
          $block_list[] = $block;
650
          cdm_toc_list_add_item(cdm_term_representation($node->feature), $node->feature->uuid);
651
        } // END: skip empty block content
652
      } // END: skip empty or suppressed features
653
    } // END: creating a block per feature
654

    
655
    drupal_alter('cdm_feature_node_blocks', $block_list, $taxon);
656

    
657
    RenderHints::popFromRenderStack();
658

    
659
    return _block_get_renderable_array($block_list);
660
  }
661

    
662
/**
663
 * Creates a render array of description elements  held by child nodes of the given feature node.
664
 *
665
 * This function is called recursively!
666
 *
667
 * @param $node
668
 *   The feature node.
669
 * @param array $media_list
670
 *   List of CDM Media entities. All media of subordinate elements should be passed to the main feature.ä
671
 * @param $feature_block_settings
672
 *   The feature block settings.
673
 * @param $main_feature
674
 *  Only used internally in recursive calls.
675
 *
676
 * @return array
677
 *  A Drupal render array
678
 *
679
 * @ingroup compose
680
 */
681
function compose_feature_block_items_nested($node, &$media_list, $feature_block_settings, $main_feature = NULL)
682
{
683

    
684
  if(!$main_feature){
685
    $main_feature = $node->feature;
686
  }
687
  /*
688
   * TODO should be configurable, options; YES, NO, AUTOMATIC
689
   * (automatic will only place the label if the first word of the description element text is not the same)
690
   */
691
  $prepend_feature_label = false;
692

    
693
  $render_arrays = array();
694
  foreach ($node->childNodes as $child_node) {
695
    if (isset($child_node->descriptionElements[0])) {
696
      foreach ($child_node->descriptionElements as $element) {
697

    
698
        if (isset($element->media[0])) {
699
          // Append media of subordinate elements to list of main
700
          // feature.
701
          $media_list = array_merge($media_list, $element->media);
702
        }
703

    
704
        $child_node_element = null;
705
        switch ($element->class) {
706
          case 'TextData':
707
            $child_node_element = compose_description_element_text_data($element, $element->feature->uuid, $feature_block_settings, $prepend_feature_label);
708
            break;
709
          case 'CategoricalData':
710
            $child_node_element = compose_description_element_categorical_data($element, $feature_block_settings, $prepend_feature_label);
711
            break;
712
          case 'QuantitativeData':
713
            $child_node_element = compose_description_element_quantitative_data($element, $feature_block_settings, $prepend_feature_label);
714

    
715
        }
716
        if (is_array($child_node_element)) {
717
          $render_arrays[] = $child_node_element;
718
        }
719
      }
720
    }
721

    
722
    if(isset($child_node->childNodes[0])){
723
      $render_arrays = array_merge($render_arrays, compose_feature_block_items_nested($child_node, $media_list, $feature_block_settings, $main_feature ));
724
    }
725
  }
726

    
727
  return $render_arrays;
728
}
729

    
730
  /**
731
   *
732
   * @param $node
733
   *  The merged feature three node which potentially contains media in its description elements.
734
   * @param $media_list
735
   *    Additional media to be merged witht the media contained in the nodes description elements
736
   * @param $gallery_settings
737
   * @return array
738
   *
739
   * @ingroup compose
740
   */
741
  function compose_feature_media_gallery($node, $media_list, $gallery_settings) {
742

    
743
    if (isset($node->descriptionElements)) {
744
      $media_list = array_merge($media_list, cdm_dataportal_media_from_descriptionElements($node->descriptionElements));
745
    }
746

    
747
    $captionElements = array('title', 'rights');
748

    
749
    if (isset($media_list[0]) && isset($gallery_settings['cdm_dataportal_media_maxextend']) && isset($gallery_settings['cdm_dataportal_media_cols'])) {
750
      $gallery = theme('cdm_media_gallerie', array(
751
        'mediaList' => $media_list,
752
        'galleryName' => CDM_DATAPORTAL_DESCRIPTION_GALLERY_NAME . '_' . $node->feature->uuid,
753
        'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
754
        'cols' => $gallery_settings['cdm_dataportal_media_cols'],
755
        'captionElements' => $captionElements,
756
      ));
757
      return markup_to_render_array($gallery);
758
    }
759

    
760
    return markup_to_render_array('');
761
  }
762

    
763
  /**
764
   * Composes the distribution feature block for a taxon
765
   *
766
   * @param $taxon
767
   * @param $descriptionElements
768
   *   an associative array with two elements:
769
   *   - '#type': must be 'DTO'
770
   *   - 'DistributionInfoDTO': a CDM DistributionInfoDTO object as returned by the DistributionInfo web service
771
   * @param $feature
772
   *
773
   * @return array
774
   *  A drupal render array
775
   *
776
   * @ingroup compose
777
   */
778
  function compose_feature_block_distribution($taxon, $descriptionElements, $feature) {
779
    $text_data_glue = '';
780
    $text_data_sortOutArray = FALSE;
781
    $text_data_enclosingTag = 'ul';
782
    $text_data_out_array = array();
783

    
784
    $distributionElements = NULL;
785
    $distribution_info_dto = NULL;
786
    $distribution_sortOutArray = FALSE;
787

    
788
    $feature_block_settings = get_feature_block_settings(UUID_DISTRIBUTION);
789

    
790
    // TODO use feature_block_settings instead of DISTRIBUTION_ORDER_MODE
791
    if (variable_get(DISTRIBUTION_ORDER_MODE, DISTRIBUTION_ORDER_MODE_DEFAULT) != 'TREE') {
792
      $distribution_glue = '';
793
      $distribution_enclosingTag = 'dl';
794
    } else {
795
      $distribution_glue = '';
796
      $distribution_enclosingTag = 'ul';
797
    }
798

    
799
    if (!isset($descriptionElements['#type']) || !$descriptionElements['#type'] == 'DTO') {
800
      // skip the DISTRIBUTION section if there is no DTO type element
801
      return array(); // FIXME is it ok to return an empty array?
802
    }
803

    
804
    $block = feature_block(
805
      cdm_term_representation($feature, 'Unnamed Feature'),
806
      $feature
807
    );
808

    
809
    // $$descriptionElements['TextData'] is added to to the feature node in merged_taxon_feature_tree()
810
    if (isset($descriptionElements['TextData'])) {
811
      // --- TextData
812
      foreach ($descriptionElements['TextData'] as $text_data_element) {
813
        $text_data_render_array = compose_description_element_text_data($text_data_element, $text_data_element->feature->uuid, $feature_block_settings);
814
        $repr = drupal_render($text_data_render_array);
815

    
816
        if (!array_search($repr, $text_data_out_array)) { // de-duplication !!
817
          $text_data_out_array[] = $repr;
818
          // TODO HINT: sorting in compose_feature_block_wrap_elements will
819
          // not work since this array contains html attributes with uuids
820
          // and what is about cases like the bibliography where
821
          // any content can be prefixed with some foot-note anchors?
822
          $text_data_sortOutArray = TRUE;
823
          $text_data_glue = '<br/> ';
824
          $text_data_enclosingTag = 'p';
825
        }
826
      }
827
    }
828

    
829

    
830
    if ($text_data_out_array && variable_get(DISTRIBUTION_TEXTDATA_DISPLAY_ON_TOP, 0)) {
831
      $block->content[] = compose_feature_block_wrap_elements(
832
        $text_data_out_array, $feature, $text_data_glue, $text_data_sortOutArray
833
      );
834
    }
835

    
836
    // --- Distribution map
837
    $distribution_map_query_parameters = NULL;
838
    if (isset($descriptionElements['DistributionInfoDTO'])) {
839
      $distribution_map_query_parameters = $descriptionElements['DistributionInfoDTO']->mapUriParams;
840
    }
841
    $map_render_element = compose_distribution_map($taxon, $distribution_map_query_parameters);
842
    $block->content[] = $map_render_element;
843

    
844
    $dto_out_array = array();
845

    
846
    // --- Condensed Distribution
847
    if(variable_get(DISTRIBUTION_CONDENSED) && isset($descriptionElements['DistributionInfoDTO']->condensedDistribution)){
848
      $condensed_distribution_markup = '<p class="condensed_distribution">';
849

    
850
      $isFirst = true;
851
      if(isset($descriptionElements['DistributionInfoDTO']->condensedDistribution->indigenous[0])){
852
        foreach($descriptionElements['DistributionInfoDTO']->condensedDistribution->indigenous as $cdItem){
853
          if(!$isFirst){
854
            $condensed_distribution_markup .= ' ';
855
          }
856
          $condensed_distribution_markup .= '<span class="status_' . $cdItem->status->idInVocabulary . '">'
857
          . $cdItem->areaStatusLabel . '</span>';
858
          $isFirst = false;
859
        }
860
      }
861

    
862
      if(isset($descriptionElements['DistributionInfoDTO']->condensedDistribution->foreign[0])) {
863
        if(!$isFirst){
864
          $condensed_distribution_markup .= ' ';
865
        }
866
        $isFirst = TRUE;
867
        $condensed_distribution_markup .= '[';
868
        foreach ($descriptionElements['DistributionInfoDTO']->condensedDistribution->foreign as $cdItem) {
869
          if (!$isFirst) {
870
            $condensed_distribution_markup .= ' ';
871
          }
872
          $condensed_distribution_markup .= '<span class="status_' . $cdItem->status->titleCache . '">'
873
            . $cdItem->areaStatusLabel . '</span>';
874
          $isFirst = false;
875
        }
876
        $condensed_distribution_markup .= ']';
877
      }
878

    
879
      $condensed_distribution_markup .= '&nbsp;' . l(
880
          font_awesome_icon_markup(
881
            'fa-info-circle',
882
            array(
883
              'alt'=>'help',
884
              'class' => array('superscript')
885
            )
886
          ),
887
          'cdm_dataportal/help/condensed_distribution',
888
          array('html' => TRUE));
889
      $condensed_distribution_markup .= '</p>';
890
      $dto_out_array[] = $condensed_distribution_markup;
891
    }
892

    
893
    // --- tree or list
894
    if (isset($descriptionElements['DistributionInfoDTO'])) {
895
      $distribution_info_dto = $descriptionElements['DistributionInfoDTO'];
896

    
897
      // --- tree
898
      if (is_object($distribution_info_dto->tree)) {
899
        $distribution_tree_render_array = compose_distribution_hierarchy($distribution_info_dto->tree, $feature_block_settings);
900
        $dto_out_array[] = $distribution_tree_render_array;
901
      }
902

    
903
      // --- sorted element list
904
      if (is_array($distribution_info_dto->elements) && count($distribution_info_dto->elements) > 0) {
905
        foreach ($distribution_info_dto->elements as $descriptionElement) {
906
          if (is_object($descriptionElement->area)) {
907
            $sortKey = $descriptionElement->area->representation_L10n;
908
            $distributionElements[$sortKey] = $descriptionElement;
909
          }
910
        }
911
        ksort($distributionElements);
912
        $distribution_element_render_array = compose_description_elements_distribution($distributionElements);
913
        $dto_out_array[] = $distribution_element_render_array;
914

    
915
      }
916
      //
917
      $block->content[] = compose_feature_block_wrap_elements(
918
        $dto_out_array, $feature, $distribution_glue, $distribution_sortOutArray
919
      );
920
    }
921

    
922
    // --- TextData at the bottom
923
    if ($text_data_out_array && !variable_get(DISTRIBUTION_TEXTDATA_DISPLAY_ON_TOP, 0)) {
924
      $block->content[] = compose_feature_block_wrap_elements(
925
        $text_data_out_array, $feature, $text_data_glue, $text_data_sortOutArray
926
      );
927
    }
928

    
929
    $block->content[] = markup_to_render_array(theme('cdm_footnotes', array('footnoteListKey' => 'BIBLIOGRAPHY-' . UUID_DISTRIBUTION)));
930
    $block->content[] = markup_to_render_array(theme('cdm_footnotes', array('footnoteListKey' => UUID_DISTRIBUTION)));
931
    $block->content[] = markup_to_render_array(theme('cdm_annotation_footnotes', array('footnoteListKey' => UUID_DISTRIBUTION)));
932

    
933
    return $block;
934
  }
935

    
936

    
937
  /**
938
   * Composes a drupal render array for single CDM TextData description element.
939
   *
940
   * @param $element
941
   *    The CDM TextData description element.
942
   *  @param $feature_uuid
943
   * @param bool $prepend_feature_label
944
   *   Used in nested feature trees to put the feature as label in front of the description element text representation.
945
   *
946
   * @return array
947
   *   A drupal render array with the following elements being used:
948
   *    - #tag: either 'div', 'li', ...
949
   *    ⁻ #attributes: class attributes
950
   *    - #value_prefix: (optionally) contains footnote anchors
951
   *    - #value: contains the textual content
952
   *    - #value_suffix: (optionally) contains footnote keys
953
   *
954
   * @ingroup compose
955
   */
956
  function compose_description_element_text_data($element, $feature_uuid, $feature_block_settings, $prepend_feature_label = FALSE) {
957

    
958
    $footnote_list_key_suggestion = $feature_uuid;
959

    
960
    $element_markup = '';
961
    if (isset($element->multilanguageText_L10n->text)) {
962
      // TODO replacement of \n by <br> should be configurable
963
      $element_markup = str_replace("\n", "<br/>", $element->multilanguageText_L10n->text);
964
    }
965

    
966
    $render_array = compose_description_element($element, $feature_block_settings, $element_markup, $footnote_list_key_suggestion, $prepend_feature_label);
967

    
968
    return $render_array;
969
  }
970

    
971

    
972
/**
973
 * Theme function to render CDM DescriptionElements of the type TaxonInteraction.
974
 *
975
 * @param $element
976
 *  The CDM TaxonInteraction entity
977
 *
978
 * @return
979
 *  A drupal render array
980
 *
981
 * @ingroup compose
982
 */
983
function compose_description_element_taxon_interaction($element, $feature_block_settings) {
984

    
985
  $out = '';
986
  $enclosing_tag = cdm_feature_block_element_tag_name($feature_block_settings);
987

    
988
  if (isset($element->description_L10n)) {
989
    $out .=  ' ' . $element->description_L10n;
990
  }
991

    
992
  if(isset($element->taxon2)){
993
    $out = render_taxon_or_name($element->taxon2, url(path_to_taxon($element->taxon2->uuid)));
994
  }
995

    
996
  $render_array = compose_description_element($element, $feature_block_settings, $out, $element->feature->uuid);
997

    
998
  return $render_array;
999
}
1000

    
1001

    
1002
/**
1003
 * Renders a single instance of the type IndividualsAssociations.
1004
 *
1005
 * @param $element
1006
 *   The CDM IndividualsAssociations entity.
1007
 * @param $feature_block_settings
1008
 *
1009
 * @return array
1010
 *   Drupal render array
1011
 *
1012
 * @ingroup compose
1013
 */
1014
function compose_description_element_individuals_association($element, $feature_block_settings) {
1015

    
1016
  $out = '';
1017
  $enclosing_tag = cdm_feature_block_element_tag_name($feature_block_settings);
1018

    
1019
  $render_array = compose_cdm_specimenOrObservation($element->associatedSpecimenOrObservation);
1020

    
1021
  if (isset($element->description_L10n)) {
1022
    $out .=  ' ' . $element->description_L10n;
1023
  }
1024

    
1025
  $out .= drupal_render($render_array);
1026

    
1027
  $render_array = compose_description_element($element, $feature_block_settings, $out, $element->feature->uuid);
1028

    
1029
  return $render_array;
1030
}
1031

    
1032
/**
1033
 * Renders a single instance of the type CategoricalData.
1034
 *
1035
 * @param $element
1036
 *  The CDM CategoricalData entity
1037
 *
1038
 * @param $feature_block_settings
1039
 *
1040
 * @param bool $prepend_feature_label
1041
 *   Used in nested feature trees to put the feature as label in front of the description element text representation.
1042
 *
1043
 * @return string
1044
 *   a html representation of the given CategoricalData element
1045
 *
1046
 * @ingroup compose
1047
 */
1048
function compose_description_element_categorical_data($element, $feature_block_settings, $prepend_feature_label = FALSE) {
1049

    
1050
  $enclosing_tag = cdm_feature_block_element_tag_name($feature_block_settings);
1051

    
1052
  $state_data_strings = array();
1053
  if (isset($element->stateData)) {
1054
    foreach ($element->stateData as $state_data) {
1055

    
1056
      $state  = NULL;
1057

    
1058
      if (isset($state_data->state)) {
1059
        $state = cdm_term_representation($state_data->state);
1060
      }
1061

    
1062
      if (isset($state_data->modifyingText_L10n)) {
1063
        $state = ' ' . $state_data->modifyingText_L10n;
1064
      }
1065

    
1066
      $modifiers_strings = cdm_modifers_representations($state_data);
1067

    
1068
      $state_data_strings[] = $state . ($modifiers_strings ? ' ' . $modifiers_strings : '');
1069

    
1070
    }
1071
  }
1072

    
1073
  $out = '<span class="' . html_class_attribute_ref($element) . '">' . implode(', ', $state_data_strings) . '</span>';
1074

    
1075
  $render_array = compose_description_element($element, $feature_block_settings, $out, $element->feature->uuid, $prepend_feature_label);
1076

    
1077
  return $render_array;
1078
}
1079

    
1080

    
1081
/**
1082
 * Theme function to render CDM DescriptionElements of the type QuantitativeData.
1083
 *
1084
 * The function renders the statisticalValues contained in the QuantitativeData
1085
 * entity according to the following scheme:
1086
 *
1087
 * (ExtremeMin)-Min-Average-Max-(ExtremeMax)
1088
 *
1089
 * All modifiers of these values are appended.
1090
 *
1091
 * If the QuantitativeData is containing more statisticalValues with further
1092
 * statisticalValue types, these additional measures will be appended to the
1093
 * above string separated by whitespace.
1094
 *
1095
 * Special cases;
1096
 * 1. Min==Max: this will be interpreted as Average
1097
 *
1098
 * @param $element
1099
 *  The CDM QuantitativeData entity
1100
 *
1101
 * @param $feature_block_settings
1102
 *
1103
 * @param bool $prepend_feature_label
1104
 *   Used in nested feature trees to put the feature as label in front of the description element text representation.
1105
 *
1106
 *
1107
 * @return string
1108
 *   a html representation of the given QuantitativeData element
1109
 *
1110
 * @ingroup themeable
1111
 */
1112
function compose_description_element_quantitative_data($element, $feature_block_settings, $prepend_feature_label = FALSE) {
1113
  /*
1114
   * - statisticalValues
1115
   *   - value
1116
   *   - modifiers
1117
   *   - type
1118
   * - unit->representation_L10n
1119
   * - modifyingText
1120
   * - modifiers
1121
   * - sources
1122
   */
1123

    
1124
  $out = '';
1125
  $type_representation = NULL;
1126
  $min_max = min_max_array();
1127

    
1128

    
1129
  $other_values = array();
1130

    
1131
  if (isset($element->statisticalValues)) {
1132
    $other_values_markup = array();
1133
    foreach ($element->statisticalValues as $statistical_val) {
1134

    
1135
      // compile the full value string which also may contain modifiers
1136
      if (isset($statistical_val->value)) {
1137
        $statistical_val->_value = $statistical_val->value;
1138
      }
1139
      $val_modifiers_strings = cdm_modifers_representations($statistical_val);
1140
      if ($val_modifiers_strings) {
1141
        $statistical_val->_value = ' ' . $val_modifiers_strings . ' ' . $statistical_val->_value;
1142
      }
1143

    
1144
      // either put into min max array or into $other_values
1145
      // for generic output to be appended to 'min-max' string
1146
      if (array_key_exists($statistical_val->type->titleCache, $min_max)) {
1147
        $min_max[$statistical_val->type->titleCache] = $statistical_val;
1148
      }
1149
      else {
1150
        $other_values[] = $statistical_val;
1151
      }
1152
    } // end of loop over statisticalValues
1153

    
1154
    // create markup
1155

    
1156
    $min_max_markup = min_max_markup($min_max);
1157

    
1158

    
1159
    foreach ($other_values as $statistical_val) {
1160
      $statistical_val_type_representation = NULL;
1161
      if (isset($statistical_val->type)) {
1162
        $statistical_val_type_representation = cdm_term_representation($statistical_val->type);
1163
        // $statistical_val->type->termType;
1164
        // $statistical_val->type->userFriendlyTypeName;
1165
      }
1166
      $value_markup = '<span class="' . html_class_attribute_ref($statistical_val) . ' ' . $statistical_val->type->termType . ' ">'
1167
        . $statistical_val->_value . '</span>';
1168
      $value_markup = $value_markup .
1169
        ($statistical_val_type_representation ? ' <span class="type">' . $statistical_val_type_representation . '</span>' : '');
1170
      $other_values_markup[] = $value_markup;
1171
    }
1172

    
1173

    
1174
    $out .= $min_max_markup . ' ' . implode($other_values_markup, ', ');
1175
  }
1176

    
1177
  if (isset($element->unit)) {
1178
    $out .= ' <span class="unit" title="'
1179
      . cdm_term_representation($element->unit) . '">'
1180
      . cdm_term_representation_abbreviated($element->unit)
1181
      . '</span>';
1182
  }
1183

    
1184
  // modifiers of the description element itself
1185
  $modifier_string = cdm_modifers_representations($element);
1186
  $out .= ($modifier_string ? ' ' . $modifier_string : '');
1187
  if (isset($element->modifyingText_L10n)) {
1188
    $out = $element->modifyingText_L10n . ' ' . $out;
1189
  }
1190

    
1191
  $render_array = compose_description_element($element, $feature_block_settings, $out, $element->feature->uuid, $prepend_feature_label);
1192

    
1193
  return $render_array;
1194
}
1195

    
1196

    
1197
/**
1198
 * Wraps the render array for the given feature into an enclosing html tag.
1199
 *
1200
 * Optionally the elements can be sorted and glued together by a separator string.
1201
 *
1202
 * @param array $description_element_render_arrays
1203
 *   An list of render arrays. Which are usually are produced by the compose_description_element()
1204
 *   function. The render arrays should be of #type=html_tag, so that they can understand the #attribute property.
1205
 * @param  $feature :
1206
 *  The feature to which the elements given in $elements are belonging to.
1207
 * @param string $glue :
1208
 *  Defaults to empty string.
1209
 * @param bool $sort
1210
 *   Boolean Whether to sort the $elements alphabetically, default is FALSE
1211
 *
1212
 * @return array
1213
 *    A Drupal render array
1214
 *
1215
 * @ingroup compose
1216
 */
1217
  function compose_feature_block_wrap_elements(array $description_element_render_arrays, $feature, $glue = '', $sort = FALSE)
1218
  {
1219

    
1220
    $feature_block_settings = get_feature_block_settings($feature->uuid);
1221
    $enclosing_tag = $feature_block_settings['as_list'];
1222

    
1223
    if ($sort) { // TODO remove parameter and replace by $feature_block_settings['sort_elements']
1224
      usort($description_element_render_arrays, 'compare_description_element_render_arrays');
1225
    }
1226

    
1227
    $is_first = true;
1228
    foreach($description_element_render_arrays as &$element_render_array){
1229
      if(!is_array($element_render_array)){
1230
        $element_render_array = markup_to_render_array($element_render_array);
1231
      }
1232
      $element_render_array['#attributes']['class'][] = "feature-block-element";
1233

    
1234
      // add the glue!
1235
      if(!$is_first) {
1236
        if (isset($element_render_array['#value'])) {
1237
          $element_render_array['#value'] = $glue . $element_render_array['#value'];
1238
        } elseif (isset($element_render_array['#markup'])) {
1239
          $element_render_array['#markup'] = $glue . $element_render_array['#markup'];
1240
        }
1241
      }
1242
      $is_first = false;
1243
    }
1244

    
1245
    $render_array['elements']['children'] = $description_element_render_arrays;
1246

    
1247
    $render_array['elements']['#prefix'] = '<' . $enclosing_tag . ' class="feature-block-elements" id="' . $feature->representation_L10n . '">';
1248
    $render_array['elements']['#suffix'] = '</' . $enclosing_tag . '>';
1249

    
1250
    return $render_array;
1251
  }
1252

    
1253

    
1254
  /* compose nameInSource or originalNameString as markup
1255
   *
1256
   * @param $source
1257
   * @param $do_link_to_name_used_in_source
1258
   * @param $suppress_for_shown_taxon
1259
   *    the nameInSource will be suppressed when it has the same name as the accepted taxon
1260
   *    for which the taxon page is being created, Defaults to TRUE
1261
   *
1262
   * @return array
1263
   *    A Drupal render array with an additional element, the render array is empty
1264
   *    if the source had no name in source information
1265
   *    - #_plaintext: contains the plaintext version of the name (custom element)
1266
   *
1267
   * @ingroup compose
1268
   */
1269
  function compose_name_in_source($source, $do_link_to_name_used_in_source, $suppress_for_shown_taxon = TRUE) {
1270

    
1271
    $plaintext = NULL;
1272
    $markup = NULL;
1273
    $name_in_source_render_array = array();
1274

    
1275
    static $taxon_page_accepted_name = '';
1276
    if($suppress_for_shown_taxon && arg(1) == 'taxon' && empty($taxon_page_accepted_name)){
1277

    
1278
      $current_taxon = cdm_ws_get(CDM_WS_PORTAL_TAXON, arg(2));
1279
      $taxon_page_accepted_name = $current_taxon->name->titleCache;
1280
    }
1281

    
1282
    if (isset($source->nameUsedInSource->uuid) && isset($source->nameUsedInSource->titleCache)) {
1283
      // it is a DescriptionElementSource !
1284
      $plaintext = $source->nameUsedInSource->titleCache;
1285
      if($suppress_for_shown_taxon && $taxon_page_accepted_name == $plaintext){
1286
        return $name_in_source_render_array; // SKIP this name
1287
      }
1288
      $markup = render_taxon_or_name($source->nameUsedInSource);
1289
      if ($do_link_to_name_used_in_source) {
1290
        $markup = l(
1291
          $markup,
1292
          path_to_name($source->nameUsedInSource->uuid),
1293
          array(
1294
            'attributes' => array(),
1295
            'absolute' => TRUE,
1296
            'html' => TRUE,
1297
          ));
1298
      }
1299
    }
1300
    else if (isset($source->originalNameString) && !empty($source->originalNameString)) {
1301
      // the name used in source can not be expressed as valid taxon name,
1302
      // so the editor has chosen to put the freetext name into ReferencedEntityBase.originalNameString
1303
      // field
1304
      // using the originalNameString as key to avoid duplicate entries
1305
      $plaintext = $source->originalNameString;
1306
      if($suppress_for_shown_taxon && $taxon_page_accepted_name == $plaintext){
1307
        return $name_in_source_render_array; // SKIP this name
1308
      }
1309
      $markup = $source->originalNameString;
1310
    }
1311

    
1312
    if ($plaintext) { // checks if we have any content
1313
      $name_in_source_render_array = markup_to_render_array($markup);
1314
      $name_in_source_render_array['#_plaintext'] = $plaintext;
1315
    }
1316

    
1317
    return $name_in_source_render_array;
1318
  }
1319

    
1320

    
1321

    
1322
  /**
1323
   * Return HTML for a list of description elements.
1324
   *
1325
   * Usually these are of a specific feature type.
1326
   *
1327
   * @param $description_elements
1328
   *   array of descriptionElements which belong to the same feature.
1329
   *   These descriptions elements of a Description must be ordered by the chosen feature tree by
1330
   *   calling the function _mergeFeatureTreeDescriptions().
1331
   *   @see _mergeFeatureTreeDescriptions()
1332
   *
1333
   * @param  $feature_uuid
1334
   *
1335
   * @return
1336
   *    A drupal render array for the $descriptionElements, may be an empty array if the textual content was empty.
1337
   *    Footnote key or anchors are not considered to be textual content.
1338
   *
1339
   * @ingroup compose
1340
   */
1341
  function compose_feature_block_items_generic($description_elements, $feature) {
1342

    
1343
    $elements_out_array = array();
1344
    $distribution_tree = null;
1345

    
1346
    /*
1347
     * $feature_block_has_content will be set true if at least one of the
1348
     * $descriptionElements contains some text which makes up some content
1349
     * for the feature block. Footnote keys are not considered
1350
     * to be content in this sense.
1351
     */
1352
    $feature_block_has_content = false;
1353

    
1354
    if (is_array($description_elements)) {
1355
      foreach ($description_elements as $description_element) {
1356
          /* decide based on the description element class
1357
           *
1358
           * Features handled here:
1359
           * all except DISTRIBUTION, COMMON_NAME, USES, IMAGES,
1360
           *
1361
           * TODO provide api_hook as extension point for this?
1362
           */
1363
        $feature_block_settings = get_feature_block_settings($description_element->feature->uuid);
1364
        switch ($description_element->class) {
1365
          case 'TextData':
1366
            $elements_out_array[] = compose_description_element_text_data($description_element, $description_element->feature->uuid, $feature_block_settings);
1367
            break;
1368
          case 'CategoricalData':
1369
            $elements_out_array[] = compose_description_element_categorical_data($description_element, $feature_block_settings);
1370
            break;
1371
          case 'QuantitativeData':
1372
            $elements_out_array[] = compose_description_element_quantitative_data($description_element, $feature_block_settings);
1373
            break;
1374
          case 'IndividualsAssociation':
1375
            $elements_out_array[] = compose_description_element_individuals_association($description_element, $feature_block_settings);
1376
            break;
1377
          case 'TaxonInteraction':
1378
            $elements_out_array[] = compose_description_element_taxon_interaction($description_element, $feature_block_settings);
1379
            break;
1380
          case 'Uses':
1381
            /* IGNORE Uses classes, these are handled completely in theme_cdm_UseDescription */
1382
            break;
1383
          default:
1384
            $feature_block_has_content = true;
1385
            $elements_out_array[] = markup_to_render_array('<li>No method for rendering unknown description class: ' . $description_element->class . '</li>');
1386
        }
1387
        $elements_out_array_last_item = $elements_out_array[count($elements_out_array) - 1];
1388
        // considering not empty as long as the last item added is a render array
1389
        $feature_block_has_content = $feature_block_has_content || !empty($elements_out_array_last_item['#type']);
1390
      }
1391

    
1392
      // If feature = CITATION sort the list of sources.
1393
      // This is ONLY for FLORA MALESIANA and FLORE d'AFRIQUE CENTRALE.
1394
      if (isset($description_element) && $description_element->feature->uuid == UUID_CITATION) {
1395
        sort($elements_out_array);
1396
      }
1397
    }
1398

    
1399
    return $elements_out_array;
1400
  }
1401

    
1402
/**
1403
 * Composes block of common names for the given DescriptionElements $elements which must be of the feature CommonName
1404
 *
1405
 * @parameter $elements
1406
 *  an array of CDM DescriptionElements either of type CommonName or TextData
1407
 * @parameter $feature
1408
 *  the common feature of all $elements, must be CommonName
1409
 *
1410
 * @return
1411
 *   A drupal render array
1412
 *
1413
 * @ingroup compose
1414
 */
1415
function compose_feature_block_items_feature_common_name($elements, $feature, $weight = FALSE) {
1416

    
1417
  $common_name_out = '';
1418
  $common_name_feature_elements = array();
1419
  $textData_commonNames = array();
1420

    
1421
  $footnote_key_suggestion = 'common-names-feature-block';
1422

    
1423
  $feature_block_settings = get_feature_block_settings(UUID_COMMON_NAME);
1424

    
1425
  $element_tag_name = cdm_feature_block_element_tag_name($feature_block_settings);
1426

    
1427
  if (is_array($elements)) {
1428
    foreach ($elements as $element) {
1429

    
1430
      if ($element->class == 'CommonTaxonName') {
1431

    
1432
        // common name without a language or area, should not happen but is possible
1433
        $language_area_key = '';
1434
        if (isset($element->language->representation_L10n)) {
1435
          $language_area_key .= '<b>' . $element->language->representation_L10n . '</b>';
1436
        }
1437
        if(isset($element->area->titleCache) && strlen($element->area->titleCache) > 0){
1438
          $language_area_key .= ($language_area_key ? ' '  : '') . '(' . $element->area->titleCache . ')';
1439
        }
1440

    
1441
        if(isset($common_names[$language_area_key][$element->name])) {
1442
          // same name already exists for language and area combination, se we merge the description elements
1443
          cdm_merge_description_elements($common_names[$language_area_key][$element->name], $element);
1444
        } else{
1445
          // otherwise add as new entry
1446
          $common_names[$language_area_key][$element->name] = $element;
1447
        }
1448

    
1449
      }
1450
      elseif ($element->class == 'TextData') {
1451
        $textData_commonNames[] = $element;
1452
      }
1453
    }
1454
  }
1455
  // Handling common names.
1456
  if (isset($common_names) && count($common_names) > 0) {
1457
    // Sorting the array based on the key (language, + area if set).
1458
    // Comment @WA there are common names without a language, so this sorting
1459
    // can give strange results.
1460
    ksort($common_names);
1461

    
1462
    // loop over set of elements per language area
1463
    foreach ($common_names as $language_area_key => $elements) {
1464
      ksort($elements); // sort names alphabetically
1465
      $per_language_area_out = array();
1466
      // loop over set of individual elements
1467
      foreach ($elements as $element) {
1468
        if ($element->name) {
1469
          $annotations_and_sources = handle_annotations_and_sources($element, $feature_block_settings, $element->name, $footnote_key_suggestion);
1470
          $source_references_markup = '';
1471
          if(!empty($annotations_and_sources['source_references'])){
1472
            $source_references_markup = ' ' . join(', ', $annotations_and_sources['source_references']);
1473
          }
1474
          $per_language_area_out[] = '<' . $element_tag_name. ' class="' . html_class_attribute_ref($element) . '">'
1475
            . $element->name . $source_references_markup . $annotations_and_sources['foot_note_keys'] . '</' . $element_tag_name. '>';
1476
        }
1477
      } // End of loop over set of individual elements
1478
      $common_name_feature_elements[] = ($language_area_key ? $language_area_key . ': ' : '' ) . join(', ', $per_language_area_out);
1479
    } // End of loop over set of elements per language area
1480

    
1481

    
1482
    $common_name_feature_elements_render_array = compose_feature_block_wrap_elements(
1483
      $common_name_feature_elements, $feature, '; ', FALSE
1484
    );
1485
    $common_name_out .= drupal_render($common_name_feature_elements_render_array); // FIXME should this be a render array instead?
1486

    
1487
  }
1488

    
1489
  // Handling commons names as text data.
1490
  $text_data_out = array();
1491

    
1492
  foreach ($textData_commonNames as $text_data_element) {
1493
    /* footnotes are not handled correctly in compose_description_element_text_data,
1494
       need to set 'common-names-feature-block' as $footnote_key_suggestion */
1495
    RenderHints::setFootnoteListKey($footnote_key_suggestion);
1496
    $text_data_render_array = compose_description_element_text_data($text_data_element, $text_data_element->feature->uuid, $feature_block_settings);
1497
    $text_data_out[] = drupal_render($text_data_render_array);
1498
  }
1499

    
1500
  $common_name_out_text_data = compose_feature_block_wrap_elements(
1501
    $text_data_out, $feature
1502
  );
1503

    
1504
  $footnotes = theme('cdm_footnotes', array('footnoteListKey' => 'BIBLIOGRAPHY-' . $footnote_key_suggestion));
1505
  $footnotes .= theme('cdm_footnotes', array('footnoteListKey' => $footnote_key_suggestion)); // FIXME is this needed at all?
1506
  $footnotes .= theme('cdm_annotation_footnotes', array('footnoteListKey' => $footnote_key_suggestion));
1507

    
1508
  return  markup_to_render_array(  // FIXME markup_to_render_array should no longer be needed
1509
    '<div class="common_names_as_common_names">' . $common_name_out . '</div>'
1510
    .'<div class="common_names_as_text_data">' . drupal_render($common_name_out_text_data) . '</div>'
1511
    .$footnotes,
1512
    $weight
1513
  );
1514
}
1515

    
1516
  /**
1517
   * Composes the render array for a CDM Distribution description element
1518
   *
1519
   * @param array $description_elements
1520
   *   Array of CDM Distribution instances
1521
   * @param $enclosingTag
1522
   *   The html tag to be use for the enclosing element
1523
   *
1524
   * @return array
1525
   *   A Drupal render array
1526
   *
1527
   * @ingroup compose
1528
   */
1529
  function compose_description_elements_distribution($description_elements){
1530

    
1531
    $out = '';
1532
    RenderHints::pushToRenderStack('descriptionElementDistribution');
1533
    RenderHints::setFootnoteListKey(UUID_DISTRIBUTION);
1534

    
1535
    $feature_block_settings = get_feature_block_settings(UUID_DISTRIBUTION);
1536
    $enclosingTag = cdm_feature_block_element_tag_name($feature_block_settings);
1537

    
1538
    foreach ($description_elements as $description_element) {
1539
      $annotations_and_sources = handle_annotations_and_sources(
1540
        $description_element,
1541
        $feature_block_settings,
1542
        $description_element->area->representation_L10n,
1543
        UUID_DISTRIBUTION
1544
      );
1545

    
1546

    
1547
      list($status_label, $status_markup) = distribution_status_label_and_markup($description_element);
1548

    
1549
      $out .= '<' . $enclosingTag . ' class="descriptionElement descriptionElement-' . $description_element->uuid
1550
        . ' " title="' . $status_label. '">'
1551
        . $description_element->area->representation_L10n
1552
        . $status_markup;
1553
      if(!empty($annotations_and_sources['source_references'])){
1554
        $out .= ' ' . join(' ', $annotations_and_sources['source_references'] );
1555
      }
1556
      $out .= $annotations_and_sources['foot_note_keys']   . ' </' . $enclosingTag . '>';
1557
    }
1558

    
1559
    RenderHints::popFromRenderStack();
1560
    return markup_to_render_array($out);
1561
  }
1562

    
1563
  /**
1564
   * @param $descriptionElement
1565
   * @return array
1566
   */
1567
  function distribution_status_label_and_markup($descriptionElement) {
1568
    $status_markup = '';
1569
    $status_label = '';
1570

    
1571
    if (isset($descriptionElement->status)) {
1572
      $status_label = $descriptionElement->status->representation_L10n;
1573
      $status_markup = '<span class="distributionStatus distributionStatus-' . $descriptionElement->status->idInVocabulary . '"> '
1574
        . $status_label . ' </span>';
1575

    
1576
    };
1577
    return array($status_label, $status_markup);
1578
  }
1579

    
1580

    
1581
  /**
1582
   * Provides the merged feature tree for a taxon profile page.
1583
   *
1584
   * The merging of the profile feature tree is actully done in
1585
   * _mergeFeatureTreeDescriptions(). See this method  for details
1586
   * on the structure of the merged tree.
1587
   *
1588
   * This method provides t hook which can be used to modify the
1589
   * merged feature tree after it has been created, see
1590
   * hook_merged_taxon_feature_tree_alter()
1591
   *
1592
   * @param $taxon
1593
   *   A CDM Taxon instance
1594
   *
1595
   * @return object
1596
   *  The merged feature tree
1597
   *
1598
   */
1599
  function merged_taxon_feature_tree($taxon) {
1600

    
1601
    // 1. fetch descriptions_by_featuretree but exclude the distribution feature
1602
    $merged_tree = cdm_ws_descriptions_by_featuretree(get_profile_feature_tree(), $taxon->uuid, array(UUID_DISTRIBUTION));
1603

    
1604

    
1605
    // 2. find the distribution feature node
1606
    $distribution_node =& cdm_feature_tree_find_node($merged_tree->root->childNodes, UUID_DISTRIBUTION);
1607

    
1608
    if ($distribution_node) {
1609
      // 3. get the distributionInfoDTO
1610
      $query_parameters = cdm_distribution_filter_query();
1611
      $query_parameters['part'] = array('mapUriParams');
1612
      if(variable_get(DISTRIBUTION_CONDENSED)){
1613
        $query_parameters['part'][] = 'condensedDistribution';
1614
      }
1615
      if (variable_get(DISTRIBUTION_ORDER_MODE, DISTRIBUTION_ORDER_MODE_DEFAULT) == 'TREE') {
1616
        $query_parameters['part'][] = 'tree';
1617
      }
1618
      else {
1619
        $query_parameters['part'][] = 'elements';
1620
      }
1621
      $query_parameters['omitLevels'] = array();
1622
      foreach(variable_get(DISTRIBUTION_TREE_OMIT_LEVELS, array()) as $uuid){
1623
        if(is_uuid($uuid)){
1624
          $query_parameters['omitLevels'][] = $uuid;
1625
        }
1626
      }
1627
      $customStatusColorsJson = variable_get(DISTRIBUTION_STATUS_COLORS, NULL);
1628
      if ($customStatusColorsJson) {
1629
        $query_parameters['statusColors'] = $customStatusColorsJson;
1630
      }
1631

    
1632
      $distribution_info_dto = cdm_ws_get(CDM_WS_PORTAL_DESCRIPTION_DISTRIBUTION_INFO_FOR, $taxon->uuid, queryString($query_parameters));
1633
      // 4. get distribution TextData is there are any
1634
      $distribution_text_data = cdm_ws_fetch_all(CDM_WS_DESCRIPTIONELEMENT_BY_TAXON,
1635
        array(
1636
          'taxon' => $taxon->uuid,
1637
          'type' => 'TextData',
1638
          'features' => UUID_DISTRIBUTION
1639
        )
1640
      );
1641

    
1642
      // 5. put all distribution data into the distribution feature node
1643
      if ($distribution_text_data //if text data exists
1644
        || ($distribution_info_dto && isset($distribution_info_dto->tree) && $distribution_info_dto->tree->rootElement->numberOfChildren > 0) // OR if tree element has distribution elements
1645
        || ($distribution_info_dto && !empty($distribution_info_dto->elements))
1646
      ) { // OR if DTO has distribution elements
1647
        $distribution_node->descriptionElements = array('#type' => 'DTO');
1648
        if ($distribution_text_data) {
1649
          $distribution_node->descriptionElements['TextData'] = $distribution_text_data;
1650
        }
1651
        if ($distribution_info_dto) {
1652
          $distribution_node->descriptionElements['DistributionInfoDTO'] = $distribution_info_dto;
1653
        }
1654
      }
1655
    }
1656

    
1657
    // allows modifying the merged tree via a the hook_cdm_feature_node_block_content_alter
1658
    drupal_alter('merged_taxon_feature_tree', $taxon, $merged_tree);
1659

    
1660
    return $merged_tree;
1661
  }
1662

    
1663

    
1664
  function compose_distribution_hierarchy($distribution_tree, $feature_block_settings){
1665

    
1666
    static $hierarchy_style;
1667
    // TODO expose $hierarchy_style to administration of provide a hook
1668
    if( !isset($hierarchy_style)){
1669
      $hierarchy_style = array(
1670
        // level 2
1671
        array(
1672
          'label_suffix' => '',
1673
          'element_glue' => ', ',
1674
          'element_set_pre' => '(',
1675
          'element_set_post' => ')'
1676
        ),
1677
        // level 1
1678
        array(
1679
          'label_suffix' => '',
1680
          'element_glue' => '; ',
1681
          'element_set_pre' => '',
1682
          'element_set_post' => ''
1683
        ),
1684
        // level 0
1685
        array(
1686
          'label_suffix' => ':',
1687
          'element_glue' => ' ',
1688
          'element_set_pre' => '',
1689
          'element_set_post' => ''
1690
        ),
1691
      );
1692
    }
1693

    
1694
    $render_array = array();
1695

    
1696
    RenderHints::pushToRenderStack('descriptionElementDistribution');
1697
    RenderHints::setFootnoteListKey(UUID_DISTRIBUTION);
1698

    
1699
    // Returning NULL if there are no description elements.
1700
    if ($distribution_tree == null) {
1701
      return $render_array;
1702
    }
1703
    // for now we are not using a render array internally to avoid performance problems
1704
    $markup = '';
1705
    if (isset($distribution_tree->rootElement->children)) {
1706
      $tree_nodes = $distribution_tree->rootElement->children;
1707
      _compose_distribution_hierarchy($tree_nodes, $feature_block_settings, $markup, $hierarchy_style);
1708
    }
1709

    
1710
    $render_array['distribution_hierarchy'] = markup_to_render_array(
1711
      $markup,
1712
      0,
1713
      '<div id="distribution_hierarchy" class="distribution_hierarchy">',
1714
      '</div>'
1715
    );
1716

    
1717
    return $render_array;
1718
  }
1719

    
1720
  /**
1721
   * this function should produce markup as the compose_description_elements_distribution()
1722
   * function.
1723
   *
1724
   * @see compose_description_elements_distribution()
1725
   *
1726
   * @param $distribution_tree
1727
   * @param $feature_block_settings
1728
   * @param $tree_nodes
1729
   * @param $markup
1730
   * @param $hierarchy_style
1731
   */
1732
  function _compose_distribution_hierarchy($tree_nodes, $feature_block_settings, &$markup, $hierarchy_style, $level_index = -1){
1733

    
1734
    $level_index++;
1735
    static $enclosingTag = "span";
1736

    
1737
    $level_style = array_pop($hierarchy_style);
1738
    if(count($hierarchy_style) == 0){
1739
      // lowest defined level style will be reused for all following levels
1740
      $hierarchy_style[] = $level_style;
1741
    }
1742

    
1743
    $node_index = -1;
1744
    $per_node_markup = array();
1745
    foreach ($tree_nodes as $node){
1746

    
1747
      $per_node_markup[++$node_index] = '';
1748

    
1749
      $label = $node->nodeId->representation_L10n;
1750

    
1751
      $distributions = $node->data;
1752
      $distribution_uuids = array();
1753
      $distribution_aggregate = NULL;
1754
        foreach($distributions as $distribution){
1755

    
1756
          $distribution_uuids[] = $distribution->uuid;
1757

    
1758
          // if there is more than one distribution we aggregate the sources and
1759
          // annotations into a synthetic distribution so that the footnote keys
1760
          // can be rendered consistently
1761
          if(!$distribution_aggregate) {
1762
            $distribution_aggregate = $distribution;
1763
            if(!isset($distribution_aggregate->sources[0])){
1764
              $distribution_aggregate->sources = array();
1765
            }
1766
            if(!isset($distribution_aggregate->annotations[0])){
1767
              $distribution_aggregate->annotations = array();
1768
            }
1769
          } else {
1770
            if(isset($distribution->sources[0])) {
1771
              $distribution_aggregate->sources = array_merge($distribution_aggregate->sources,
1772
                $distribution->sources);
1773
            }
1774
            if(isset($distribution->annotations[0])) {
1775
              $distribution_aggregate->annotations = array_merge($distribution_aggregate->annotations,
1776
                $distribution->annotations);
1777
            }
1778
          }
1779
        }
1780

    
1781
      $status_label= '';
1782
      $status_markup = '';
1783
      $annotations_and_sources =  null;
1784
      if($distribution_aggregate) {
1785
        $annotations_and_sources = handle_annotations_and_sources(
1786
          $distribution_aggregate,
1787
          $feature_block_settings,
1788
          $label,
1789
          UUID_DISTRIBUTION
1790
        );
1791

    
1792
        list($status_label, $status_markup) = distribution_status_label_and_markup($distribution);
1793
      }
1794

    
1795
      $per_node_markup[$node_index] .= '<' . $enclosingTag . ' class="descriptionElement'
1796
        . join(' descriptionElement-', $distribution_uuids)
1797
        . ' level_index_' . $level_index
1798
        . ' " title="' . $status_label . '">'
1799
        . '<span class="area_label">' . $label
1800
        . $level_style['label_suffix'] . ' </span>'
1801
        .  $status_markup
1802
      ;
1803

    
1804
      if(isset($annotations_and_sources)){
1805
        if(!empty($annotations_and_sources['source_references'])){
1806
          $per_node_markup[$node_index] .= ' ' . join(', ' , $annotations_and_sources['source_references']);
1807
        }
1808
        if($annotations_and_sources['foot_note_keys']) {
1809
          $per_node_markup[$node_index] .= $annotations_and_sources['foot_note_keys'];
1810
        }
1811
      }
1812

    
1813
      if(isset($node->children[0])){
1814
        _compose_distribution_hierarchy(
1815
          $node->children,
1816
          $feature_block_settings,
1817
          $per_node_markup[$node_index],
1818
          $hierarchy_style,
1819
          $level_index
1820
        );
1821
      }
1822

    
1823
      $per_node_markup[$node_index] .= '</' . $enclosingTag . '>';
1824
    }
1825
    $markup .= $level_style['element_set_pre']  . join( $level_style['element_glue'], $per_node_markup) . $level_style['element_set_post'];
1826
  }
1827

    
1828

    
(2-2/8)