Project

General

Profile

Download (17.8 KB) Statistics
| Branch: | Tag: | Revision:
1 2bdf5f11 Andreas Kohlbecker
<?php
2 f19f47fa Andreas Kohlbecker
/**
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 2bdf5f11 Andreas Kohlbecker
 */
18
19 5b26b91a Andreas Kohlbecker
/**
20
 * @defgroup compose Compose functions
21
 * @{
22
 * Functions which are composing Drupal render arays
23
 *
24 31765b7b Andreas Kohlbecker
 * The cdm_dataportal module needs to compose rather complex render arrays from
25 ce29c528 Andreas Kohlbecker
 * the data returned by the CDM REST service. The compose functions are
26
 * responsible for creating the render arrays.
27 5b26b91a Andreas Kohlbecker
 *
28 ce29c528 Andreas Kohlbecker
 * All these functions are also implementations of the compose_hook()
29
 * which is used in the proxy_content() function.
30 5b26b91a Andreas Kohlbecker
 * @}
31
 */
32
33 2bdf5f11 Andreas Kohlbecker
/**
34
 * Compose an render array from a CDM Marker object.
35
 *
36
 * compose_hook() implementation
37
 *
38
 * @param object $marker
39
 *   CDM instance of type Marker
40
 * @return array
41
 *   A drupal render array
42 5b26b91a Andreas Kohlbecker
 *
43
 * @ingroup compose
44 2bdf5f11 Andreas Kohlbecker
 */
45
function compose_cdm_marker($marker) {
46
47
  $render_array = array(
48
      // ---- generic
49
      //  these entries should be common to all cdm enitiy render arrays
50
      '#theme' => 'cdm_marker', // TODO   add alternative theme funcitons: 'cdm_marker_' . marker.type.label
51 f19f47fa Andreas Kohlbecker
      '#attributes' => array('class' => html_class_attribute_ref($marker)),
52 2bdf5f11 Andreas Kohlbecker
53
      // ---- individual
54
      '#label' => $marker->markerType->representation_L10n . ': ' . (($marker->flag !== TRUE ? t('yes') : t('no'))),
55
  );
56
57
  return $render_array;
58
}
59 19e097a3 Andreas Kohlbecker
60
/**
61
 * Checks if the given $cdm_entitiy has a marker the type references by the
62
 * $marker_type_uuid and returns TRUE if a matching marker has been found.
63
 *
64 ce29c528 Andreas Kohlbecker
 * @param object $cdm_entitiy A CDM Entity
65 19e097a3 Andreas Kohlbecker
 * @param string $marker_type_uuid
66
 */
67
function cdm_entity_has_marker($cdm_entitiy, $marker_type_uuid) {
68
  if(isset($cdm_entitiy->markers[0]) && !is_uuid($marker_type_uuid)){
69
    foreach ($cdm_entitiy->markers as $marker) {
70
      if(isset($marker->markerType) && $marker->markerType->uuid == $marker_type_uuid){
71
        return TRUE;
72
      }
73
    }
74
  }
75
  return FALSE;
76 c2486c6b Andreas Kohlbecker
}
77
78
/**
79 c7c53f4e Andreas Kohlbecker
 * Sorts an array of CDM IdentifiableSource instances by 1. by the
80 ce29c528 Andreas Kohlbecker
 * author teams family names and 2. by the publication date.
81 c2486c6b Andreas Kohlbecker
 *
82 ce29c528 Andreas Kohlbecker
 * @param array $sources
83 c2486c6b Andreas Kohlbecker
 *    The array of CDM IdentifiableSource instances
84 ce29c528 Andreas Kohlbecker
 * @param bool $do_theme if set TRUE the sources will be themed
85
 *        by theme_cdm_OriginalSource
86 c2486c6b Andreas Kohlbecker
 * @return multitype:
87
 */
88 ce29c528 Andreas Kohlbecker
function oder_sources($sources, $do_theme = false){
89 c2486c6b Andreas Kohlbecker
    $sort_array = array();
90
    foreach ($sources as $source) {
91 c7c53f4e Andreas Kohlbecker
92
      $order_key = '';
93
94
      // find the familynames
95 1ce9afb7 Patric Plitzner
      if(isset($source->citation->uuid) && !isset($source->citation->authorship)){
96 c7c53f4e Andreas Kohlbecker
        $authorteam = cdm_ws_get(CDM_WS_REFERENCE_AUTHORTEAM, $source->citation->uuid);
97
98
        $persons = array();
99
        if($authorteam->class == 'Team'){
100
          if(isset($authorteam->teamMembers)){
101
            $persons = $authorteam->teamMembers;
102
          }
103
        } else {
104
          $persons[] = $authorteam;
105
        }
106
107
        foreach($persons as $person){
108
          if(!empty($person->lastname)){
109
            $order_key .= $person->lastname;
110
          } else {
111
            $order_key .= $person->titleCache;
112
          }
113
        }
114
        if(empty($order_key)){
115
          $order_key = $authorteam->titleCache;
116
        }
117
118 c2486c6b Andreas Kohlbecker
      }
119 c7c53f4e Andreas Kohlbecker
      $order_key = str_pad($order_key, 50);
120
121
      // add publication date to the key
122
      if(isset($source->citation->datePublished)){
123
        $order_key .= '_' . timePeriodAsOrderKey($source->citation->datePublished);
124
      } else {
125
        $order_key .= '_' . "0000";
126 c2486c6b Andreas Kohlbecker
      }
127 c7c53f4e Andreas Kohlbecker
128
      // padd key until unique
129 c2486c6b Andreas Kohlbecker
      while(array_key_exists($order_key, $sort_array)){
130
        $order_key .= "_";
131
      }
132 c7c53f4e Andreas Kohlbecker
133
134 c2486c6b Andreas Kohlbecker
      if($do_theme) {
135 2416e2eb Andreas Kohlbecker
        $sort_array[$order_key] = theme('cdm_OriginalSource', array('source' => $source));
136 c2486c6b Andreas Kohlbecker
      } else {
137
        $sort_array[$order_key] = $source;
138
      }
139
    }
140
    ksort($sort_array);
141
    return array_values ($sort_array);
142 ce29c528 Andreas Kohlbecker
}
143
144 9e2aa1ff Andreas Kohlbecker
/**
145
 * Compare callback to be used in usort to sort image sources of CDM OriginalSource instances.
146
 *
147 0c2b9b9d Andreas Kohlbecker
 * TODO the compare strategy implemented in oder_sources() is probably better but is not taking the
148 9e2aa1ff Andreas Kohlbecker
 * originalName into account.
149
 *
150
 * @param $a
151
 * @param $b
152
 */
153
function compare_original_sources($a, $b){
154
155 7c0c59d8 Andreas Kohlbecker
  $a_string = '';
156
  if(isset($a->citation->titleCache)) {
157
    $a_string = $a->citation->titleCache;
158
  }
159 9e2aa1ff Andreas Kohlbecker
  if((isset($a->nameUsedInSource))){
160
    $a_string .= $a->nameUsedInSource->titleCache;
161
  } elseif (isset($a->originalNameString)){
162
    $a_string .= $a->originalNameString;
163
  }
164
165 7c0c59d8 Andreas Kohlbecker
  $b_string = '';
166
  if(isset($b->citation->titleCache)) {
167
    $b_string = $b->citation->titleCache;
168
  };
169 9e2aa1ff Andreas Kohlbecker
  if((isset($b->nameUsedInSource))){
170
    $b_string .= $b->nameUsedInSource->titleCache;
171
  } elseif (isset($b->originalNameString)){
172
    $b_string .= $b->originalNameString;
173
  }
174
175
  if ($a_string == $b_string) {
176
    return 0;
177
  }
178
  return ($a_string < $b_string) ? -1 : 1;
179
}
180
181
/**
182
 * Compare callback to be used in usort to sort image sources of CDM Media instances.
183
 *
184
 * @param $a
185
 * @param $b
186
 */
187 50a67433 Andreas Kohlbecker
function compare_text_data($a, $b) {
188 9e2aa1ff Andreas Kohlbecker
189
  if ($a->multilanguageText_L10n->text == $b->multilanguageText_L10n->text) {
190
    return 0;
191
  }
192
  return ($a->multilanguageText_L10n->text < $b->multilanguageText_L10n->text) ? -1 : 1;
193
}
194
195 ce29c528 Andreas Kohlbecker
  /**
196
   * Compare two different footnotes objects.
197
   *
198
   * The comparison is based on the footnote key. The one which is
199
   * displayed as footnote number.
200
   *
201
   * @param mixed $a
202
   *   Footnote object $a.
203
   * @param mixed $b
204
   *   Footnote object $b.
205
   */
206
  function footnotes_key_compare($a, $b) {
207
    $res = 0;
208
    if (empty($a) || empty($b)) {
209
      return $res;
210
    }
211
    if ($a->keyStr < $b->keyStr) {
212
      $res = -1;
213
    }
214
    elseif ($a->keyStr > $b->keyStr) {
215
      $res = 1;
216
    }
217
    return $res;
218
  }
219 74ee6b54 Andreas Kohlbecker
220
221
/**
222
 * Creates an array suitable to be used in min_max_markup()
223
 *
224
 * @return array
225
 */
226
function min_max_array()
227
{
228
// FIXME use UUIDs instead? how about idInVocab?
229
  $min_max = array(
230
      'Extreme Min' => NULL,
231
      'Min' => NULL,
232
      'Average' => NULL,
233
      'Max' => NULL,
234
      'Extreme Max' => NULL,
235
  );
236
  return $min_max;
237
}
238
239
/**
240 5a079dbf Andreas Kohlbecker
 * Creates markup from a min max array.
241
 *
242 74ee6b54 Andreas Kohlbecker
 * NOTE: use  min_max_array() to create an appropriate array
243
 *
244
 * @param $min_max
245 5a079dbf Andreas Kohlbecker
 *  the min-max array
246
 * @param $unit
247
 *  Defaults to no unit
248 74ee6b54 Andreas Kohlbecker
 * @return string
249
 */
250 5a079dbf Andreas Kohlbecker
function min_max_markup($min_max, $unit = '') {
251 74ee6b54 Andreas Kohlbecker
252
  $min_max_markup = '';
253
  // create min-max string
254
  if ($min_max['Min'] !== NULL && $min_max['Max'] !== NULL && $min_max['Min']->_value == $min_max['Max']->_value) {
255 5a079dbf Andreas Kohlbecker
    // min and max are identical
256 74ee6b54 Andreas Kohlbecker
    $min_max['Average'] = $min_max['Min'];
257
    $min_max['Min'] = NULL;
258
    $min_max['Max'] = NULL;
259
  }
260
261
  // check for inconsistent cases, eg. only Max and average given
262
  if ($min_max['Min'] === NULL && $min_max['Max'] !== NULL) {
263 5a079dbf Andreas Kohlbecker
    // min missing
264 74ee6b54 Andreas Kohlbecker
    $min_max['Min'] = '?';
265
  }
266
  if ($min_max['Min'] !== NULL && $min_max['Max'] === NULL) {
267 5a079dbf Andreas Kohlbecker
    // max missing
268 74ee6b54 Andreas Kohlbecker
    $min_max['Max'] = '?';
269
  }
270
271
272
  foreach ($min_max as $key => $statistical_val) {
273
    if ($statistical_val !== NULL) {
274
275
      if ($statistical_val == '?') {
276
        $val_markup = $statistical_val;
277
      } else {
278
        $val_markup = '<span class="'
279
            . html_class_attribute_ref($statistical_val) . ' '
280
            . (isset($statistical_val->type) ? $statistical_val->type->termType : '') . ' ">'
281
            . $statistical_val->_value . '</span>';
282
      }
283
284
      if (strlen($min_max_markup)) {
285
        $min_max_markup .= '–';
286
      }
287
      if (str_beginsWith($key, 'Extreme')) {
288
        $val_markup = "($val_markup)";
289
      }
290
      $min_max_markup .= $val_markup;
291
    }
292
  }
293 5a079dbf Andreas Kohlbecker
  return $min_max_markup . ' ' . $unit;
294
}
295
296
/**
297
 * Creates min max markup to represent a min-average-max measure optionally with an error value.
298
 *
299
 * The fields that are taken into account are:
300
 * - field_base_name = min
301
 * - field_base_nameMax = max
302
 * - field_base_nameText = free text
303
 * - field_base_nameError = error value
304
 *
305
 * @param $object
306
 *    The object having min max measurement fields
307
 * @param string $field_base_name
308
 *    The base name for all measurement fields. This name is at the same time the full name of the
309
 *    min value.
310 dbc2bfa9 Andreas Kohlbecker
 * @return string
311
 *   The markup for the min max
312 5a079dbf Andreas Kohlbecker
 */
313
function min_max_measure($object, $field_base_name)
314
{
315
  static $default_unit = 'm';
316
317
  $field_name = $field_base_name . 'Text';
318 0820cdc0 Andreas Kohlbecker
  if (@is_string($object->$field_name)) {
319 dbc2bfa9 Andreas Kohlbecker
    // Freetext overrides all other data
320
    $min_max_markup = ' ' . $object->$field_name;
321
  } else {
322
    // create markup for the atomized min max data
323
    $min_max_array = min_max_array();
324
    if (@is_numeric($object->$field_base_name)) {
325
      $min_max_array['Min'] = new stdClass();
326
      $min_max_array['Min']->_value = $object->$field_base_name;
327
    }
328
    $field_name = $field_base_name . 'Max';
329
    if (@is_numeric($object->$field_name)) {
330
      $min_max_array['Max'] = new stdClass();
331
      $min_max_array['Max']->_value = $object->$field_name;
332
    }
333
    $min_max_markup = min_max_markup($min_max_array, $default_unit);
334 5a079dbf Andreas Kohlbecker
  }
335 dbc2bfa9 Andreas Kohlbecker
336 74ee6b54 Andreas Kohlbecker
  return $min_max_markup;
337
}
338 d9763fd3 Andreas Kohlbecker
339
// TODO  move below code into new file: agent.inc
340
341
/*
342
 * Compose an render array from a CDM TaxonNodeAgentRelation object as Taxon Expert.
343
 *
344
 * compose_hook() implementation
345
 *
346
 * @param object $taxon_node_agent_relation
347
 *   CDM instance of type TaxonNodeAgentRelation
348
 * @return array
349
 *   A drupal render array
350
 *
351
 * @ingroup compose
352
 */
353
function compose_cdm_taxon_expert($taxon_node_agent_relation) {
354
355
  $label_suffix = ':';
356
357
  if($taxon_node_agent_relation->class == 'DefaultPagerImpl'){
358
    // oops this is a pager
359
    // this situation will occur when this compose is executed
360
    // through the proxy_content() method
361
    $taxon_node_agent_relation = $taxon_node_agent_relation->records[0];
362
363
  }
364
365
  if(is_object($taxon_node_agent_relation->agent)) {
366
    $agent_details = compose_cdm_team_or_person_base($taxon_node_agent_relation->agent);
367
    // all data will be added to the groups of the agent_details render array
368
    $groups = &$agent_details[0]['#groups'];
369
370 bf90783c Andreas Kohlbecker
    @_description_list_group_add($groups, t('Role'). $label_suffix, $taxon_node_agent_relation->type->representation_L10n);
371 d9763fd3 Andreas Kohlbecker
372
    $family_tnars = cdm_ws_fetch_all(CDM_WS_PORTAL_AGENT . '/' . $taxon_node_agent_relation->agent->uuid . '/taxonNodeAgentRelations', array("rank"=>"Familia"));
373
374
    $taxa_markup = array(
375
      '#theme_wrappers' => array('container'),
376
      '#attributes' => array('class' => array('managed_taxa')),
377
      '#wrapper_attributes' => array('class' => 'sublist-container')
378
      );
379
    foreach($family_tnars as $tnar){
380
      if(is_object($tnar->taxonNode->taxon)){
381 bf2387cc Andreas Kohlbecker
        $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))));
382 d9763fd3 Andreas Kohlbecker
      }
383
    }
384
    ksort($taxa_markup);
385
386
    @_description_list_group_add($groups, t('Families'). $label_suffix, array($taxa_markup));
387
388
  }
389
390
  return $agent_details;
391
}
392
393
394
/*
395
 * Compose an render array from a CDM TeamOrPersonBase object.
396
 *
397
 * compose_hook() implementation
398
 *
399
 * TODO: currently mainly implemented for Agent, add Team details
400
 *
401
 * @param object $team_or_person
402
 *   CDM instance of type TeamOrPersonBase
403
 * @return array
404
 *   A drupal render array
405
 *
406
 * @ingroup compose
407
 */
408
function compose_cdm_team_or_person_base($team_or_person, $data = array()) {
409
410
  $groups = array();
411
412
  $label_suffix = ':';
413
414
  // $weight = 0;
415
  if($team_or_person){
416
417
    if(is_object($team_or_person->lifespan)){
418
      // ToDo render as date* - date† ?
419
      @_description_list_group_add($groups, t('Lifespan'). $label_suffix, timePeriodToString($team_or_person->lifespan) /*, '' , $weight++ */);
420
    }
421
422
    // nomenclaturalTitle
423
    @_description_list_group_add($groups, "Nomenclatural Title". $label_suffix, $team_or_person->nomenclaturalTitle);
424
    // collectorTitle
425
    @_description_list_group_add($groups, "Collector Title". $label_suffix, $team_or_person->collectorTitle);
426
427
    // institutionalMemberships
428
    if(is_array($team_or_person->institutionalMemberships)){
429
430
      $institutes_ra =  array();
431
      foreach($team_or_person->institutionalMemberships as $membership) {
432
        $membership_groups = array();
433
        @_description_list_group_add($membership_groups, t('Department'). $label_suffix, $membership->department);
434
        @_description_list_group_add($membership_groups, t('Role'). $label_suffix, $membership->role);
435
        if(is_object($membership->period)){
436
          @_description_list_group_add($membership_groups, t('Period'). $label_suffix, timePeriodToString($membership->period));
437
        }
438
        if(is_object($membership->institute->contact)){
439
          $institute_contact_details = compose_cdm_contact($membership->institute->contact, $membership->institute->titleCache);
440
          if(is_array($institute_contact_details[0]['#groups'])){
441
            $membership_groups = array_merge($membership_groups, $institute_contact_details[0]['#groups']);
442
          }
443
        }
444
        if(count($membership_groups) > 0){
445
          $institutes_ra[]  = array(
446
            '#title' => $membership->institute->titleCache,
447
            '#theme' => 'description_list',
448
            '#groups' => $membership_groups,
449
            '#attributes' => array('class' => html_class_attribute_ref($membership)),
450
            '#wrapper_attributes' => array('class' => 'sublist-container')
451
          );
452
        } else {
453
          // no further details for the membership, display the title
454
          $institutes_ra[] = markup_to_render_array('<h3>' . $membership->institute->titleCache . '</h3>');
455
        }
456
457
      }
458
459
      $label = count($institutes_ra) > 1 ? t('Institutes'):  t('Institute');
460
      @_description_list_group_add($groups, $label. $label_suffix, $institutes_ra /*, '' , $weight++ */);
461
    }
462
463
464
    // Contact
465
    $agent_contact_details = compose_cdm_contact($team_or_person->contact, $team_or_person->titleCache);
466
    if(is_array($agent_contact_details[0]['#groups'])){
467
      $groups = array_merge($groups, $agent_contact_details[0]['#groups']);
468
    }
469
470
    // additional data
471
    foreach($data as $key=>$value){
472 7cc085da Andreas Kohlbecker
      @_description_list_group_add($sub_dl_groups, t('@key', array('@key' => $key)), $value /*, '' , $weight++ */);
473 d9763fd3 Andreas Kohlbecker
    }
474
475
  }
476
477
  $team_or_person_details = array(
478
    '#title' => $team_or_person->titleCache,
479
    '#theme' => 'description_list',
480
    '#groups' => $groups,
481
    '#attributes' => array('class' => html_class_attribute_ref($team_or_person)),
482
  );
483
  return array($team_or_person_details);
484
}
485
486
487
/*
488
 * Compose an render array from a CDM Contact object.
489
 *
490
 * compose_hook() implementation
491
 *
492
 * TODO: currently mainly implemented for Agent, add Team details
493
 *
494
 * @param object $contact
495
 *   CDM instance of type Contact
496
 * @param $title
497
 *   The title for the description list header
498
 * @param $weight
499
 *   Optional weight for the description list entries
500
 * @return array
501
 *   A drupal render array
502
 *
503
 * @ingroup compose
504
 */
505
function compose_cdm_contact($contact, $title, $weight = 0)
506
{
507
508
  $groups = array();
509
510 0820cdc0 Andreas Kohlbecker
  $contact_details = null;
511
512 d9763fd3 Andreas Kohlbecker
  $label_suffix = ':';
513
514
  $contact_field_names_map = array(
515
    'emailAddresses' => t('Email'),
516
    'urls' => t('Urls'),
517
    'phoneNumbers' => t('Phone'),
518
    'faxNumbers' => t('Fax'),
519
  );
520
521
  // Contact
522
  if(is_object($contact)){
523
    if(isset($contact->addresses)){
524
      // TODO ....
525
      // $sub_groups = array();
526
      // foreach($contact->addresses as $address){
527
      //   @_description_list_group_add($sub_groups, $label, $contact->$fieldName, '', $weight++);
528
      // }
529
    }
530
    foreach($contact_field_names_map as $fieldName => $label){
531
      if(is_array($contact->$fieldName)){
532
        @_description_list_group_add($groups, $label . $label_suffix, $contact->$fieldName, '', $weight++);
533
      }
534
    }
535
    $contact_details = array(
536
      '#title' => $title,
537
      '#theme' => 'description_list',
538
      '#groups' => $groups
539
    );
540
541
542
  } else if(is_string($title)) {
543
    // if the contact entity is empty but the title is given anyway
544
    // we are only adding the title, using the description_list
545
    // structure is not possible since it would be empty due to
546
    // missing group data
547
    $contact_details = array('#markup' => '<h3>' . $title . '</h3>');
548
  }
549
550
  return array($contact_details);
551
552
}
553 0820cdc0 Andreas Kohlbecker
554
/**
555
 * Compose an render array from a CDM Extension objects.
556
 *
557
 * @param $extensions
558
 *    An array of CDM Extension objects
559
 * @return array
560
 *   A render array containing the fields of the supplied $sequence
561
 *
562
 * @ingroup compose
563
 */
564
function compose_extensions($extensions)
565
{
566
  $extensions_render_array= null;
567
  $extensions_by_type = array();
568
  foreach ($extensions as $extension) {
569
    if (@is_string($extension->value)) {
570
      if (!isset($extensions_by_type[$extension->type->representation_L10n])) {
571
        $extensions_by_type[$extension->type->representation_L10n] = array();
572
      }
573
      $extensions_by_type[$extension->type->representation_L10n][] = markup_to_render_array($extension->value);
574
    }
575
  }
576
577
  if (count($extensions_by_type)) {
578
    $sub_dl_groups = array();
579
    foreach ($extensions_by_type as $type_label => $text_list) {
580
      @_description_list_group_add($sub_dl_groups, $type_label . ':', $text_list);
581
    }
582
    $extensions_render_array = array(
583
      array('#theme' => 'description_list', '#groups' => $sub_dl_groups)
584
    );
585
    return $extensions_render_array;
586
  }
587
  return $extensions_render_array;
588
}
589 eaff53f7 Andreas Kohlbecker
590 6eaec849 Katja Luther
function formatParams($params) {
591
    if (is_array($params)){
592
        $keys =array_keys($params);
593
        $paramString = '';
594
        foreach ($keys as $k ){
595
            if ($k != 'pageNumber' && $k != 'pageSize'){
596
                $paramString .= ' -'.$k.'='.urlencode($params[$k]);
597
            }
598
        }
599
    }
600
    return $paramString;
601
}
602 eaff53f7 Andreas Kohlbecker
603 72d57201 Katja Luther
function formatWSParams($params) {
604
    if (is_array($params)){
605
        $keys =array_keys($params);
606
        $paramString = '';
607
        foreach ($keys as $k ){
608
            if ($k != 'pageNumber' && $k != 'pageSize'){
609
                $paramString .= '&'.$k.'='.urlencode($params[$k]);
610
            }
611
        }
612
    }
613
    return $paramString;
614
}