Project

General

Profile

Download (25.9 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/**
3
 * @file
4
 * Functions for dealing with CDM entities from the package model.common
5
 *
6
 * @copyright
7
 *   (C) 2007-2012 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
 * Composes an HTML element class attribute value composed of
36
 * the short-name of the cdm class and the uuid of the entity.
37
 * This class attribute should be used wherever an cdm-entity is rendered.
38
 *
39
 * In case of Taxon entities or TaxonNodeDTOs the secReference is also added
40
 * to the class attributes as 'sec_uuid:<uuid>'. In case of TaxonNodeDTOs the
41
 * Taxon uuid is added also as taxon_uuid:<uuid>
42
 *
43
 * These according class selectors in css must be escaped, eg:
44
 *    .cdm\:TextData
45
 *
46
 * @param $cdm_entity
47
 *    A CDM entity, TaxonNodeDTO or TypedEntityReference
48
 */
49
function html_class_attribute_ref($cdm_entity) {
50

    
51
  $attributes = '';
52
  if (is_cdm_entity($cdm_entity)) {
53
    $attributes =  "cdm:" . $cdm_entity->class . " uuid:" . $cdm_entity->uuid;
54
  } elseif (isset($cdm_entity->class)) {
55
    switch ($cdm_entity->class){
56
      case 'TypedEntityReference':
57
        $attributes =  "cdm:" . $cdm_entity->type . " uuid:" . $cdm_entity->uuid;
58
      break;
59
      case 'TaxonNodeDto':
60
        $attributes .= " taxon_uuid:"  . $cdm_entity->taxonUuid . " sec_uuid:"  . $cdm_entity->secUuid;;
61
      break;
62
      case 'Taxon':
63
        if(isset($cdm_entity->sec->uuid)){
64
        $attributes .= " sec_uuid:"  . $cdm_entity->sec->uuid;
65
      }
66
      break;
67
    }
68
  }
69

    
70
  return $attributes;
71
}
72

    
73

    
74
/**
75
 * Compose an render array from a CDM Marker object.
76
 *
77
 * compose_hook() implementation
78
 *
79
 * @param object $marker
80
 *   CDM instance of type Marker
81
 * @return array
82
 *   A drupal render array
83
 *
84
 * @ingroup compose
85
 */
86
function compose_cdm_marker($marker) {
87

    
88
  $render_array = array(
89
      // ---- generic
90
      //  these entries should be common to all cdm enitiy render arrays
91
      '#theme' => 'cdm_marker', // TODO   add alternative theme funcitons: 'cdm_marker_' . marker.type.label
92
      '#attributes' => array('class' => html_class_attribute_ref($marker)),
93

    
94
      // ---- individual
95
      '#label' => $marker->markerType->representation_L10n . ': ' . (($marker->flag !== TRUE ? t('yes') : t('no'))),
96
  );
97

    
98
  return $render_array;
99
}
100

    
101
/**
102
 * Checks if the given $cdm_entitiy has a marker the type references by the
103
 * $marker_type_uuid and returns TRUE if a matching marker has been found.
104
 *
105
 * @param object $cdm_entitiy A CDM Entity
106
 * @param string $marker_type_uuid
107
 */
108
function cdm_entity_has_marker($cdm_entitiy, $marker_type_uuid) {
109
  if(isset($cdm_entitiy->markers[0]) && !is_uuid($marker_type_uuid)){
110
    foreach ($cdm_entitiy->markers as $marker) {
111
      if(isset($marker->markerType) && $marker->markerType->uuid == $marker_type_uuid){
112
        return TRUE;
113
      }
114
    }
115
  }
116
  return FALSE;
117
}
118

    
119
/**
120
 * Sorts an array of CDM IdentifiableSource instances by 1. by the
121
 * author teams family names and 2. by the publication date.
122
 *
123
 * @param array $sources
124
 *    The array of CDM IdentifiableSource instances
125
 * @return array
126
 *  An array of drupal render arrays
127
 */
128
function oder_and_render_original_sources($sources){
129
    $sort_array = array();
130
    foreach ($sources as $source) {
131

    
132
      $order_key = '';
133

    
134
      // find the familynames
135
      if(isset($source->citation->uuid) && !isset($source->citation->authorship)){
136
        $authorteam = cdm_ws_get(CDM_WS_REFERENCE_AUTHORTEAM, $source->citation->uuid);
137

    
138
        $persons = array();
139
        if($authorteam->class == 'Team'){
140
          if(isset($authorteam->teamMembers)){
141
            $persons = $authorteam->teamMembers;
142
          }
143
        } else {
144
          $persons[] = $authorteam;
145
        }
146

    
147
        foreach($persons as $person){
148
          if(!empty($person->lastname)){
149
            $order_key .= $person->lastname;
150
          } else {
151
            $order_key .= $person->titleCache;
152
          }
153
        }
154
        if(empty($order_key)){
155
          $order_key = $authorteam->titleCache;
156
        }
157

    
158
      }
159
      $order_key = str_pad($order_key, 50);
160

    
161
      // add publication date to the key
162
      if(isset($source->citation->datePublished)){
163
        $order_key .= '_' . timePeriodAsOrderKey($source->citation->datePublished);
164
      } else {
165
        $order_key .= '_' . "0000";
166
      }
167

    
168
      // padd key until unique
169
      while(array_key_exists($order_key, $sort_array)){
170
        $order_key .= "_";
171
      }
172

    
173
      $sort_array[$order_key] = render_original_source($source);
174
    }
175
    ksort($sort_array);
176
    return array_values ($sort_array);
177
}
178

    
179
/**
180
 * Compare callback to be used in usort to sort image sources of CDM OriginalSource instances.
181
 *
182
 * TODO the compare strategy implemented in oder_and_render_original_sources() is probably better but is not taking the
183
 * originalName into account.
184
 *
185
 * @param $a
186
 * @param $b
187
 */
188
function compare_original_sources($a, $b){
189

    
190
  $a_string = '';
191
  if(isset($a->citation->titleCache)) {
192
    $a_string = $a->citation->titleCache;
193
  }
194
  if((isset($a->nameUsedInSource))){
195
    $a_string .= $a->nameUsedInSource->titleCache;
196
  } elseif (isset($a->originalNameString)){
197
    $a_string .= $a->originalNameString;
198
  }
199

    
200
  $b_string = '';
201
  if(isset($b->citation->titleCache)) {
202
    $b_string = $b->citation->titleCache;
203
  };
204
  if((isset($b->nameUsedInSource))){
205
    $b_string .= $b->nameUsedInSource->titleCache;
206
  } elseif (isset($b->originalNameString)){
207
    $b_string .= $b->originalNameString;
208
  }
209

    
210
  if ($a_string == $b_string) {
211
    return 0;
212
  }
213
  return ($a_string < $b_string) ? -1 : 1;
214
}
215

    
216
/**
217
 * Compare callback to be used in usort to sort image sources of CDM Media instances.
218
 *
219
 * @param $a
220
 * @param $b
221
 */
222
function compare_text_data($a, $b) {
223

    
224
  if ($a->multilanguageText_L10n->text == $b->multilanguageText_L10n->text) {
225
    return 0;
226
  }
227
  return ($a->multilanguageText_L10n->text < $b->multilanguageText_L10n->text) ? -1 : 1;
228
}
229

    
230
  /**
231
   * Compare two different footnotes objects.
232
   *
233
   * The comparison is based on the footnote key. The one which is
234
   * displayed as footnote number.
235
   *
236
   * @param mixed $a
237
   *   Footnote object $a.
238
   * @param mixed $b
239
   *   Footnote object $b.
240
   */
241
  function footnotes_key_compare($a, $b) {
242
    $res = 0;
243
    if (empty($a) || empty($b)) {
244
      return $res;
245
    }
246
    if ($a->keyStr < $b->keyStr) {
247
      $res = -1;
248
    }
249
    elseif ($a->keyStr > $b->keyStr) {
250
      $res = 1;
251
    }
252
    return $res;
253
  }
254

    
255
/**
256
 * Provides an explanatory text on the statistical values representation as generated by statistical_values()
257
 *
258
 * @return string
259
 *     the text
260
 */
261
  function statistical_values_explanation(){
262
    return t("A single or the first number in square brackets denotes sample size");
263
  }
264

    
265
/**
266
 * Creates an array suitable to be used in statistical_values()
267
 * The min max structure is suitable for being used in the context
268
 * of GatheringEvents and StatisticalMeasures (see render_quantitative_statistics()).
269
 *
270
 * The order of the items is important for the display and must not be changed.
271
 *
272
 * @return array
273
 */
274
function statistical_values_array() {
275

    
276
  $min_max = [
277
    'ExactValue' => NULL,
278
    'Min' => NULL,
279
    'TypicalLowerBoundary' => NULL,
280
    'TypicalUpperBoundary' => NULL,
281
    'Max' => NULL,
282
    'SampleSize' => NULL,
283
    'Average' => NULL,
284
    'Variance' => NULL,
285
    'StandardDeviation' => NULL
286
  ];
287
  return $min_max;
288
}
289

    
290
/**
291
 * Creates markup from a min max array.
292
 *
293
 * NOTE: use  statistical_values_array() to create an appropriate array
294
 *
295
 * Internally Min will be translated to TypicalLowerBoundary if no such value is present.
296
 * The same also accounts for Max and TypicalUpperBoundary.
297
 *
298
 * For further details see #3742, #8766
299
 *
300
 * @param $stat_vals_arr
301
 *  the statistical values array as produced by statistical_values_array()
302
 * @param $unit
303
 *  Defaults to no unit
304
 * @return string
305
 */
306
function statistical_values($stat_vals_arr, $unit = '') {
307

    
308
  static $xbar_equals = 'x&#772;='; // x&#772; is x-bar (http://www.personal.psu.edu/ejp10/blogs/gotunicode/2010/03/dealing-with-x-bar-x-and-p-hat.html)
309

    
310
  $exact_markup = '';
311
  $min_max_markup = '';
312
  $other_vals_array = [];
313

    
314
  // --- sanitize values
315
  if(statistical_values_num_equals($stat_vals_arr, 'Min', 'TypicalLowerBoundary')){
316
    $stat_vals_arr['Min'] = NULL;
317
  }
318

    
319
  if(statistical_values_num_equals($stat_vals_arr, 'Max', 'TypicalUpperBoundary')){
320
    $stat_vals_arr['Max'] = NULL;
321
  }
322

    
323
  if($stat_vals_arr['TypicalLowerBoundary'] === null && $stat_vals_arr['Min'] !== null){
324
    $stat_vals_arr['TypicalLowerBoundary'] = $stat_vals_arr['Min'];
325
    $stat_vals_arr['Min'] = NULL;
326
  }
327

    
328
  if($stat_vals_arr['TypicalUpperBoundary'] === null && $stat_vals_arr['Max']  !== null){
329
    $stat_vals_arr['TypicalUpperBoundary'] = $stat_vals_arr['Max'];
330
    $stat_vals_arr['Max'] = NULL;
331
  }
332

    
333
  if (statistical_values_num_equals($stat_vals_arr, 'TypicalUpperBoundary', 'TypicalLowerBoundary')) {
334
    $stat_vals_arr['Average'] = $stat_vals_arr['TypicalUpperBoundary'];
335
    $stat_vals_arr['TypicalLowerBoundary'] = NULL;
336
    $stat_vals_arr['TypicalUpperBoundary'] = NULL;
337
  }
338

    
339
  // --- check for inconsistent cases, eg. only Max and average given
340
  if ($stat_vals_arr['TypicalLowerBoundary'] === NULL && $stat_vals_arr['TypicalUpperBoundary']  !== null) {
341
    // min missing
342
    $stat_vals_arr['TypicalLowerBoundary'] = '?';
343
  }
344
  if ($stat_vals_arr['TypicalLowerBoundary'] !== null && $stat_vals_arr['TypicalUpperBoundary'] === NULL) {
345
    // max missing
346
    $stat_vals_arr['TypicalUpperBoundary'] = '?';
347
  }
348

    
349
  if(statistical_values_is_numeric($stat_vals_arr['Average']) && statistical_values_is_numeric($stat_vals_arr['TypicalUpperBoundary']) && statistical_values_is_numeric($stat_vals_arr['TypicalLowerBoundary'])) {
350
    statistical_values_adjust_significant_figures($stat_vals_arr['Average'], $stat_vals_arr['TypicalLowerBoundary'], $stat_vals_arr['TypicalUpperBoundary']);
351
  }
352

    
353
  foreach ($stat_vals_arr as $key => $statistical_val) {
354

    
355
    if ($statistical_val !== NULL) {
356
      if ($statistical_val == '?') {
357
        $val_markup = $statistical_val;
358
      }
359
      else {
360
        if (is_numeric($statistical_val->_value)) {
361
          $val_markup = '<span class="'
362
            . html_class_attribute_ref($statistical_val) . ' '
363
            . (isset($statistical_val->type) ? $statistical_val->type->termType : '') . ' ' . $key . '" title="' . $key . '">'
364
            . $statistical_val->_value . '</span>';
365

    
366
        }
367
        else {
368
          $val_markup = NULL;
369
        }
370
      }
371

    
372
      if ($val_markup) {
373
        switch ($key) {
374
          case 'ExactValue':
375
            $exact_markup = $val_markup;
376
            break;
377
          // ---- min_max_element
378
          case 'Min':
379
            $min_max_markup .= "($val_markup&ndash;)";
380
            break;
381
          case 'Max':
382
            $min_max_markup .= "(&ndash;$val_markup)";
383
            break;
384
          case 'TypicalLowerBoundary':
385
            $min_max_markup .= "$val_markup";
386
            break;
387
          case 'TypicalUpperBoundary':
388
            $min_max_markup .= "&ndash;$val_markup";
389
            break;
390
          // ---- other values
391
          case 'SampleSize':
392
            $other_vals_array[$key] = $val_markup;
393
            break;
394
          case 'Average':
395
            $other_vals_array[$key] = $xbar_equals . $val_markup;
396
            break;
397
          case 'Variance':
398
            $other_vals_array[$key] = 'σ²=' . $val_markup;
399
            break;
400
          case 'StandardDeviation':
401
            $other_vals_array[$key] = 'σ=' . $val_markup;
402
            break;
403
        }
404
      }
405
    }
406
  }
407

    
408
  $full_markup = $exact_markup . ($exact_markup ? ' ' : '') . $min_max_markup;
409

    
410
  if(!$full_markup && !empty($other_vals_array['Average'])){
411
    // this could be the case in which we only have one value for Average
412
    // this trivial case needs to be displayed a simpler way
413
    $full_markup = str_replace($xbar_equals, '' , $other_vals_array['Average']);
414
    unset($other_vals_array['Average']);
415
  }
416
  if($unit){
417
    $full_markup .= ' ' . $unit;
418
  }
419
  if(count($other_vals_array)){
420
    $full_markup .= ' [' . join(';', $other_vals_array) . ']';
421
  }
422

    
423
  return $full_markup;
424
}
425

    
426
/**
427
 * Calculates the required precision for the target value to be significantly different from min and may and rounds it.
428
 *
429
 * @param $target
430
 *    The statistical value to be rounded to the least significant precision
431
 * @param $min
432
 *    The lower bound to calculate the least significant precision
433
 * @param $max
434
 *    The upper bound to calculate the least significant precision
435
 * @param int $threshold
436
 *    Per default 1, but can be set to any other value > 0 and < 1 to define
437
 *    another threshold for the transition to the next precision level.
438
 *    E.g. A value of 0.5 will cause values > 50 and <= 500 to be shown with
439
 *    a precision of 2, whereas with a threshold of 1 the values > 10 and <= 100
440
 *    will be shown with a precision of 2 figures
441
 */
442
function statistical_values_adjust_significant_figures(&$target, $min,  $max, $threshold = 1){
443

    
444
  $precision = 1;
445
  if($min->_value !== $max->_value){
446
    $precision = floor(log10(abs($max->_value - $min->_value) * (1 / $threshold)));
447
    // increase precision by one
448
    $precision += $precision < 0 ? - 1 : 1;
449
  }
450

    
451
  $target->_value = sigFig($target->_value, $precision);
452
}
453

    
454
/**
455
 * based on an idea taken from https://stackoverflow.com/questions/37618679/format-number-to-n-significant-digits-in-php#answer-48283297
456
 *
457
 * @param $value
458
 * @param $digits
459
 *
460
 * @return float|string
461
 */
462
function sigFig($value, $digits, $round_only = true)
463
{
464
  if ($value == 0) {
465
    $decimalPlaces = $digits - 1;
466
  } elseif ($value < 0) {
467
    $decimalPlaces = $digits - floor(log10($value * -1)) - 1;
468
  } else {
469
    $decimalPlaces = $digits - floor(log10($value)) - 1;
470
  }
471

    
472
  $answer = ($decimalPlaces > 0) && !$round_only ?
473
    number_format($value, $decimalPlaces) : round($value, abs($decimalPlaces));
474
  return $answer;
475
}
476

    
477

    
478

    
479
/**
480
 * Used internally in statistical_values() do determine numerically equality of stat_vals_arr values
481
 *
482
 * @param $stat_vals_arr
483
 * @param $key1
484
 * @param $key2
485
 *
486
 * @return bool
487
 */
488
function statistical_values_num_equals($stat_vals_arr, $key1, $key2){
489

    
490
  return $stat_vals_arr[$key1] !== NULL && $stat_vals_arr[$key2] !== NULL && $stat_vals_arr[$key1]->_value == $stat_vals_arr[$key2]->_value && is_numeric($stat_vals_arr[$key1]->_value);
491
}
492

    
493
function statistical_values_is_numeric($stat_val){
494
  return $stat_val !== null && is_numeric($stat_val->_value);
495
}
496

    
497
/**
498
 * Creates min max markup to represent a min-average-max measure optionally with an error value.
499
 *
500
 * The fields that are taken into account are:
501
 * - field_base_name = min
502
 * - field_base_nameMax = max
503
 * - field_base_nameText = free text
504
 * - field_base_nameError = error value
505
 *
506
 * @param $object
507
 *    The object having min max measurement fields e.g.: GatheringEvent
508
 * @param string $field_base_name
509
 *    The base name for all measurement fields. This name is at the same time the full name of the
510
 *    min value.
511
 * @return string
512
 *   The markup for the min max
513
 *
514
 * @see statistical_values()
515
 */
516
function statistical_values_from_gathering_event($object, $field_base_name)
517
{
518
  static $default_unit = 'm';
519

    
520
  $field_name = $field_base_name . 'Text';
521
  if (@is_string($object->$field_name)) {
522
    // Freetext overrides all other data
523
    $min_max_markup = ' ' . $object->$field_name;
524
  } else {
525
    // create markup for the atomized min max data
526
    $min_max_array = statistical_values_array();
527
    if (@is_numeric($object->$field_base_name)) {
528
      $min_max_array['Min'] = new stdClass();
529
      $min_max_array['Min']->_value = $object->$field_base_name;
530
    }
531
    $field_name = $field_base_name . 'Max';
532
    if (@is_numeric($object->$field_name)) {
533
      $min_max_array['Max'] = new stdClass();
534
      $min_max_array['Max']->_value = $object->$field_name;
535
    }
536
    $min_max_markup = statistical_values($min_max_array, $default_unit);
537
  }
538

    
539
  return $min_max_markup;
540
}
541

    
542
// TODO  move below code into new file: agent.inc
543

    
544
/*
545
 * Compose an render array from a CDM TaxonNodeAgentRelation object as Taxon Expert.
546
 *
547
 * compose_hook() implementation
548
 *
549
 * @param object $taxon_node_agent_relation
550
 *   CDM instance of type TaxonNodeAgentRelation
551
 * @return array
552
 *   A drupal render array
553
 *
554
 * @ingroup compose
555
 */
556
function compose_cdm_taxon_expert($taxon_node_agent_relation) {
557

    
558
  $agent_details = null;
559

    
560
  $label_suffix = ':';
561

    
562
  if($taxon_node_agent_relation->class == 'DefaultPagerImpl'){
563
    // oops this is a pager
564
    // this situation will occur when this compose is executed
565
    // through the proxy_content() method
566
    $taxon_node_agent_relation = $taxon_node_agent_relation->records[0];
567

    
568
  }
569

    
570
  if(is_object($taxon_node_agent_relation->agent)) {
571
    $agent_details = compose_cdm_team_or_person_base($taxon_node_agent_relation->agent);
572
    // all data will be added to the groups of the agent_details render array
573
    $groups = &$agent_details[0]['#groups'];
574

    
575
    @_description_list_group_add($groups, t('Role'). $label_suffix, $taxon_node_agent_relation->type->representation_L10n);
576

    
577
    $family_tnars = cdm_ws_fetch_all(CDM_WS_PORTAL_AGENT . '/' . $taxon_node_agent_relation->agent->uuid . '/taxonNodeAgentRelations', array("rank"=>"Familia"));
578

    
579
    $taxa_markup = array(
580
      '#theme_wrappers' => array('container'),
581
      '#attributes' => array('class' => array('managed_taxa')),
582
      '#wrapper_attributes' => array('class' => 'sublist-container')
583
      );
584
    foreach($family_tnars as $tnar){
585
      if(is_object($tnar->taxonNode->taxon)){
586
        $taxa_markup[$tnar->taxonNode->taxon->titleCache] = markup_to_render_array(render_taxon_or_name($tnar->taxonNode->taxon, url(path_to_taxon($tnar->taxonNode->taxon->uuid))));
587
      }
588
    }
589
    ksort($taxa_markup);
590

    
591
    @_description_list_group_add($groups, t('Families'). $label_suffix, array($taxa_markup));
592

    
593
  }
594

    
595
  return $agent_details;
596
}
597

    
598

    
599
/*
600
 * Compose an render array from a CDM TeamOrPersonBase object.
601
 *
602
 * compose_hook() implementation
603
 *
604
 * TODO: currently mainly implemented for Agent, add Team details
605
 *
606
 * @param object $team_or_person
607
 *   CDM instance of type TeamOrPersonBase
608
 * @return array
609
 *   A drupal render array
610
 *
611
 * @ingroup compose
612
 */
613
function compose_cdm_team_or_person_base($team_or_person, $data = array()) {
614

    
615
  $groups = array();
616

    
617
  $label_suffix = ':';
618

    
619
  // $weight = 0;
620
  if($team_or_person){
621

    
622
    if(is_object($team_or_person->lifespan)){
623
      // ToDo render as date* - date† ?
624
      @_description_list_group_add($groups, t('Lifespan'). $label_suffix, timePeriodToString($team_or_person->lifespan) /*, '' , $weight++ */);
625
    }
626

    
627
    // nomenclaturalTitle
628
    @_description_list_group_add($groups, "Nomenclatural Title". $label_suffix, $team_or_person->nomenclaturalTitle);
629
    // collectorTitle
630
    @_description_list_group_add($groups, "Collector Title". $label_suffix, $team_or_person->collectorTitle);
631

    
632
    // institutionalMemberships
633
    if(is_array($team_or_person->institutionalMemberships)){
634

    
635
      $institutes_ra =  array();
636
      foreach($team_or_person->institutionalMemberships as $membership) {
637
        $membership_groups = array();
638
        @_description_list_group_add($membership_groups, t('Department'). $label_suffix, $membership->department);
639
        @_description_list_group_add($membership_groups, t('Role'). $label_suffix, $membership->role);
640
        if(is_object($membership->period)){
641
          @_description_list_group_add($membership_groups, t('Period'). $label_suffix, timePeriodToString($membership->period));
642
        }
643
        if(is_object($membership->institute->contact)){
644
          $institute_contact_details = compose_cdm_contact($membership->institute->contact, $membership->institute->titleCache);
645
          if(is_array($institute_contact_details[0]['#groups'])){
646
            $membership_groups = array_merge($membership_groups, $institute_contact_details[0]['#groups']);
647
          }
648
        }
649
        if(count($membership_groups) > 0){
650
          $institutes_ra[]  = array(
651
            '#title' => $membership->institute->titleCache,
652
            '#theme' => 'description_list',
653
            '#groups' => $membership_groups,
654
            '#attributes' => array('class' => html_class_attribute_ref($membership)),
655
            '#wrapper_attributes' => array('class' => 'sublist-container')
656
          );
657
        } else {
658
          // no further details for the membership, display the title
659
          $institutes_ra[] = markup_to_render_array('<h3>' . $membership->institute->titleCache . '</h3>');
660
        }
661

    
662
      }
663

    
664
      $label = count($institutes_ra) > 1 ? t('Institutes'):  t('Institute');
665
      @_description_list_group_add($groups, $label. $label_suffix, $institutes_ra /*, '' , $weight++ */);
666
    }
667

    
668

    
669
    // Contact
670
    $agent_contact_details = compose_cdm_contact($team_or_person->contact, $team_or_person->titleCache);
671
    if(is_array($agent_contact_details[0]['#groups'])){
672
      $groups = array_merge($groups, $agent_contact_details[0]['#groups']);
673
    }
674

    
675
    // additional data
676
    foreach($data as $key=>$value){
677
      @_description_list_group_add($sub_dl_groups, t('@key', array('@key' => $key)), $value /*, '' , $weight++ */);
678
    }
679

    
680
  }
681

    
682
  $team_or_person_details = array(
683
    '#title' => $team_or_person->titleCache,
684
    '#theme' => 'description_list',
685
    '#groups' => $groups,
686
    '#attributes' => array('class' => html_class_attribute_ref($team_or_person)),
687
  );
688
  return array($team_or_person_details);
689
}
690

    
691

    
692
/*
693
 * Compose an render array from a CDM Contact object.
694
 *
695
 * compose_hook() implementation
696
 *
697
 * TODO: currently mainly implemented for Agent, add Team details
698
 *
699
 * @param object $contact
700
 *   CDM instance of type Contact
701
 * @param $title
702
 *   The title for the description list header
703
 * @param $weight
704
 *   Optional weight for the description list entries
705
 * @return array
706
 *   A drupal render array
707
 *
708
 * @ingroup compose
709
 */
710
function compose_cdm_contact($contact, $title, $weight = 0)
711
{
712

    
713
  $groups = array();
714

    
715
  $contact_details = null;
716

    
717
  $label_suffix = ':';
718

    
719
  $contact_field_names_map = array(
720
    'emailAddresses' => t('Email'),
721
    'urls' => t('Urls'),
722
    'phoneNumbers' => t('Phone'),
723
    'faxNumbers' => t('Fax'),
724
  );
725

    
726
  // Contact
727
  if(is_object($contact)){
728
    if(isset($contact->addresses)){
729
      // TODO ....
730
      // $sub_groups = array();
731
      // foreach($contact->addresses as $address){
732
      //   @_description_list_group_add($sub_groups, $label, $contact->$fieldName, '', $weight++);
733
      // }
734
    }
735
    foreach($contact_field_names_map as $fieldName => $label){
736
      if(is_array($contact->$fieldName)){
737
        @_description_list_group_add($groups, $label . $label_suffix, $contact->$fieldName, '', $weight++);
738
      }
739
    }
740
    $contact_details = array(
741
      '#title' => $title,
742
      '#theme' => 'description_list',
743
      '#groups' => $groups
744
    );
745

    
746

    
747
  } else if(is_string($title)) {
748
    // if the contact entity is empty but the title is given anyway
749
    // we are only adding the title, using the description_list
750
    // structure is not possible since it would be empty due to
751
    // missing group data
752
    $contact_details = array('#markup' => '<h3>' . $title . '</h3>');
753
  }
754

    
755
  return array($contact_details);
756

    
757
}
758

    
759
/**
760
 * Compose an render array from a CDM Extension objects.
761
 *
762
 * @param $extensions
763
 *    An array of CDM Extension objects
764
 * @return array
765
 *   A render array containing the fields of the supplied $sequence
766
 *
767
 * @ingroup compose
768
 */
769
function compose_extensions($extensions)
770
{
771
  $extensions_render_array= null;
772
  $extensions_by_type = array();
773
  foreach ($extensions as $extension) {
774
    if (@is_string($extension->value)) {
775
      if (!isset($extensions_by_type[$extension->type->representation_L10n])) {
776
        $extensions_by_type[$extension->type->representation_L10n] = array();
777
      }
778
      $extensions_by_type[$extension->type->representation_L10n][] = markup_to_render_array($extension->value);
779
    }
780
  }
781

    
782
  if (count($extensions_by_type)) {
783
    $sub_dl_groups = array();
784
    foreach ($extensions_by_type as $type_label => $text_list) {
785
      @_description_list_group_add($sub_dl_groups, $type_label . ':', $text_list);
786
    }
787
    $extensions_render_array = array(
788
      array('#theme' => 'description_list', '#groups' => $sub_dl_groups)
789
    );
790
    return $extensions_render_array;
791
  }
792
  return $extensions_render_array;
793
}
794

    
795
function formatParams($params) {
796
    $paramString = null;
797
    if (is_array($params)){
798
        $keys =array_keys($params);
799
        $paramString = '';
800
        foreach ($keys as $k ){
801
            if ($k != 'pageNumber' && $k != 'pageSize'){
802
                $paramString .= ' -'.$k.'='.urlencode($params[$k]);
803
            }
804
        }
805
    }
806
    return $paramString;
807
}
808

    
809
function formatWSParams($params) {
810
  $paramString = null;
811
    if (is_array($params)){
812
        $keys =array_keys($params);
813
        $paramString = '';
814
        foreach ($keys as $k ){
815
            if ($k != 'pageNumber' && $k != 'pageSize'){
816
                $paramString .= '&'.$k.'='.urlencode($params[$k]);
817
            }
818
        }
819
    }
820
    return $paramString;
821
}
822

    
823
/**
824
 *
825
 * @param $cdm_entity
826
 *
827
 * @return string the markup
828
 */
829
function render_cdm_entity_link($cdm_entity) {
830

    
831
  switch ($cdm_entity->class) {
832
    case 'TaxonDescription':
833
    case 'NameDescription':
834
    case 'SpecimenDescription':
835
      $link =  '<span class="' . html_class_attribute_ref($cdm_entity) . '">' . $cdm_entity->titleCache . '</span> ' . icon_link(path_to_description($cdm_entity->uuid));
836
      break;
837
    default:
838
      $link = '<span class="error">UNSUPPORTED CDM ENTITY TYPE</span>';
839
  }
840
  return $link;
841
}
842

    
843
/**
844
 * Creates an icon which links to the given path
845
 * @param $path
846
 *
847
 * @return string
848
 */
849
function icon_link($path, $fragment = '') {
850
  $iconlink = l(custom_icon_font_markup('icon-interal-link-alt-solid', ['class' => ['superscript']]), $path, ['html' => TRUE, 'fragment' => $fragment] );
851
  return $iconlink;
852
}
(1-1/11)