Project

General

Profile

Download (19.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/**
3
 * @file
4
 * Functions for dealing with CDM entities of type SpecimenOrOccurrences
5
 * This file contains new functions which are to replace functions in
6
 *   occurrences.inc and should be merged with the latter one.
7
 *
8
 * @see http://www.mozilla.org/MPL/MPL-1.1.html
9
 *
10
 * @copyright
11
 *   (C) 2007-2021 EDIT
12
 *   European Distributed Institute of Taxonomy
13
 *   http://www.e-taxonomy.eu
14
 *
15
 *   The contents of this module are subject to the Mozilla
16
 *   Public License Version 1.1.
17
 * @author
18
 *   - Andreas Kohlbecker <a.kohlbecker@BGBM.org>
19
 */
20

    
21

    
22
/**
23
 * Composes the view on specimens and occurrences as derivate tree
24
 * starting from the field unit including all derivatives.
25
 *
26
 * @param array $root_unit_dtos
27
 *   list of SpecimenOrObservationDTOs
28
 *
29
 * @return array
30
 *   The Drupal render array
31
 *
32
 * @ingroup compose
33
 * @see CDM_SPECIMEN_LIST_VIEW_MODE_OPTION_DERIVATE_TREE
34
 *
35
 */
36
function compose_specimen_table_top_down_new(array $root_unit_dtos) {
37
  // add icons
38
  $expand_icon = font_awesome_icon_markup(
39
    'fa-plus-square-o',
40
    [
41
      'alt' => 'Show details',
42
      'class' => ['expand_icon'],
43
    ]
44
  );
45
  $collapse_icon = font_awesome_icon_markup(
46
    'fa-minus-square-o',
47
    [
48
      'alt' => 'Show details',
49
      'class' => ['collapse_icon'],
50
    ]
51
  );
52

    
53
  $derivation_tree = derived_units_tree($root_unit_dtos);
54

    
55
  $render_array = [];
56
  $render_array['derived-unit-tree'] = $derivation_tree;
57

    
58
  _add_js_derivation_tree('.derived-unit-tree');
59

    
60
  return $render_array;
61
}
62

    
63
/**
64
 * Creates the root levels and trees for all subordinate derivatives.
65
 *
66
 * See derived_units_sub_tree()
67
 *
68
 * @param array $root_unit_dtos
69
 *     list of SpecimenOrObservationDTOs
70
 *
71
 * @return array
72
 *    An array which can be used in render arrays to be passed to the
73
 * theme_table() and theme_list().
74
 */
75
function derived_units_tree(array $root_unit_dtos) {
76

    
77
  $root_items = [];
78
  //we need one more item to contain the items of one level (fieldunit, derivate data etc.)
79
  foreach ($root_unit_dtos as &$sob_dto) {
80
    $field_unit_dto_render_array = compose_cdm_specimen_or_observation_dto($sob_dto);
81
    $root_item = [
82
      '#prefix' => '<div class="derived-unit-tree">',
83
      '#suffix' => '</div>',
84
      '#type' => 'container',
85
      '#attributes' => [
86
        'class' => [
87
          'derived-unit-item derived-unit-tree-root',
88
          html_class_attribute_ref($sob_dto),
89
        ],
90
      ],
91
      'div-container' => [
92
        'root-item-and-sub-tree' => [
93
          markup_to_render_array('<div class="unit-header"><div class="unit-label">' . $sob_dto->label . '</div></div>'
94
            . '<div class="unit-content-wrapper">' // allows to apply the borders between .derived-unit-tree-root and .unit-content
95
            . '<div class="unit-content">' . drupal_render($field_unit_dto_render_array) . '</div>'
96
            . '</div>'),
97
        ],
98
      ],
99

    
100
    ];
101
    if (isset($sob_dto->derivatives) && sizeof($sob_dto->derivatives) > 0) {
102
      usort($sob_dto->derivatives, 'compare_specimen_or_observation_dtos');
103
      // children are displayed in a nested list.
104
      $root_item['div-container']['root-item-and-sub-tree'][] = derived_units_sub_tree($sob_dto->derivatives);
105
    }
106
    $root_items[] = $root_item;
107
  }
108

    
109
  return $root_items;
110
}
111

    
112
/**
113
 * @param array $unit_dtos
114
 *
115
 * @return array
116
 */
117
function derived_units_sub_tree(array $unit_dtos) {
118

    
119
  $list_items = derived_units_as_list_items($unit_dtos);
120

    
121
  $derivation_tree = [
122
    '#theme' => 'item_list',
123
    '#type' => 'ul',
124
    '#attributes' => [
125
      'class' => CDM_SPECIMEN_LIST_VIEW_MODE_OPTION_DERIVATE_TREE . ' derived-unit-sub-tree',
126
    ],
127
    '#items' => $list_items,
128
  ];
129
  return $derivation_tree;
130
}
131

    
132
/**
133
 * Creates render array items for FieldUnitDTO or DerivedUnitDTO.
134
 *
135
 * @param array $root_unit_dtos
136
 *     list of SpecimenOrObservationDTOs
137
 *
138
 * @return array
139
 *    An array which can be used in render arrays to be passed to the
140
 * theme_table() and theme_list().
141
 */
142
function derived_units_as_list_items(array $root_unit_dtos) {
143

    
144
  $list_items = [];
145
  //we need one more item to contain the items of one level (fieldunit, derivate data etc.)
146
  foreach ($root_unit_dtos as &$sob_dto) {
147
    $item = [];
148
    $item['class'] = ['derived-unit-item ', html_class_attribute_ref($sob_dto)];
149
    // data" element of the array is used as the contents of the list item
150
    $item['data'] = [];
151
    $units_render_array = compose_cdm_specimen_or_observation_dto($sob_dto);
152
    $item['data'] = '<div class="unit-header"><div class="unit-label">' . $sob_dto->label . '</div></div>'
153
      . '<div class="unit-content">' . drupal_render($units_render_array) . '</div>';
154
    if (isset($sob_dto->derivatives) && sizeof($sob_dto->derivatives) > 0) {
155
      usort($sob_dto->derivatives, 'compare_specimen_or_observation_dtos');
156
      // children are displayed in a nested list.
157
      $item['children'] = derived_units_as_list_items($sob_dto->derivatives);
158
    }
159
    $list_items[] = $item;
160
  }
161

    
162
  return $list_items;
163
}
164

    
165
/**
166
 * Compose an render array from a CDM SpecimenOrObservationDTO, without
167
 * subordinate derivatives.
168
 *
169
 * The resulting render array will contain separate DOM block level elements
170
 * with header line for the various kind of information like, location,
171
 * gathering, specimen/observation, DNA, etc.
172
 *
173
 * For showing the derivation hierarchy see methods like
174
 * derived_units_sub_tree()
175
 *
176
 * compose_hook() implementation
177
 *
178
 * @param object $sob_dto
179
 *   the CDM FieldUnitDTO or DerivedUnitDTO to compose
180
 *   the render array for.
181
 * @param bool $compact_mode
182
 *   Currently unused, but added for compatibility with
183
 *   compose_cdm_specimen_or_observation($specimen_or_observation,
184
 *   $isSpecimen_page = false, &$derivatives = null)
185
 * @param array $derivatives
186
 *   the render array which contains the compositions of the derivatives
187
 *   of the supplied $specimenOrObservation
188
 *
189
 * @return array
190
 *  The render array for the SpecimenOrObservationDTO
191
 *
192
 * @ingroup compose
193
 */
194
function compose_cdm_specimen_or_observation_dto($sob_dto, $compact_mode = FALSE, &$derivatives = NULL) {
195
  //TODO !!!! add derivation event information like method, etc
196
  $render_array = [];
197
  if (!$sob_dto) {
198
    return $render_array;
199
  }
200
  if ($sob_dto->type == 'FieldUnit') {
201
    $render_array['field-unit'] = compose_cdm_field_unit_dto_details($sob_dto, $compact_mode);
202
    if (isset($sob_dto->gatheringEvent)) {
203
      $render_array['gathering'] = compose_cdm_gathering_dto_details($sob_dto->gatheringEvent, $compact_mode);
204
    }
205
  } else {
206
    $render_array['storage'] = compose_cdm_derived_unit_dto_storage_details($sob_dto, $compact_mode);
207
    $render_array['type-designations'] = compose_cdm_unit_dto_type_designations($sob_dto, $compact_mode);
208
    if($sob_dto->type == 'DnaSample'){
209
      // FIXME:
210
      $render_array['dna-sample'] = compose_cdm_derived_unit_dto_sequences($sob_dto, $compact_mode);
211
    } else if($sob_dto->type == 'MediaSpecimen' && isset_not_empty($sob_dto->mediaSpecimen)) {
212
      $render_array['media-specimens'] = cdm_sob_dto_media_table($sob_dto->mediaSpecimen, $sob_dto, 'Media Specimens');
213
    }
214
  }
215
  if(isset_not_empty($sob_dto->determinedNames)){
216
    $render_array['determinations'] = compose_cdm_sob_dto_determinations($sob_dto, $compact_mode);
217
  }
218
  if(isset_not_empty($sob_dto->listOfMedia)){
219
    $render_array['media'] = cdm_sob_dto_media_table($sob_dto->listOfMedia, $sob_dto, 'Media');;
220
  }
221

    
222
  return $render_array;
223
}
224

    
225
/**
226
 * Compose an render array from a CDM FieldUnitDTO, without subordinate
227
 * derivatives.
228
 *
229
 * The resulting DOM block level element will have a header line and details.
230
 *
231
 * For showing the derivation hierarchy see methods like
232
 * derived_units_sub_tree()
233
 *
234
 * compose_hook() implementation
235
 *
236
 * @param object $fu_dto
237
 *   the CDM FieldUnitDTO
238
 *   the render array for.
239
 * @param bool $compact_mode
240
 *   Currently unused,
241
 *
242
 * @return array
243
 *  The render array for the SpecimenOrObservationDTO
244
 *
245
 * @ingroup compose
246
 */
247
function compose_cdm_field_unit_dto_details($fu_dto, $compact_mode = FALSE) {
248

    
249
  $table_row_data = [];
250
  if (isset_not_empty($fu_dto->sex)) {
251
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('sex'), cdm_term_representation($fu_dto->sex));
252
  }
253
  if (isset_not_empty($fu_dto->lifeStage)) {
254
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('lifeStage'), cdm_term_representation($fu_dto->lifeStage));
255
  }
256
  if (isset_not_empty($fu_dto->kindOfUnit)) {
257
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('kindOfUnit'), cdm_term_representation($fu_dto->kindOfUnit));
258
  }
259
  if (isset_not_empty($fu_dto->individualCount)) {
260
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('individualCount'), $fu_dto->individualCount);
261
  }
262
  if (isset_not_empty($fu_dto->definition)) {
263
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('definition'), $fu_dto->definition);
264
  }
265
  if (isset_not_empty($fu_dto->fieldNumber)) {
266
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('fieldNumber'), $fu_dto->fieldNumber);
267
  }
268
  if (isset_not_empty($fu_dto->primaryCollector)) {
269
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('primaryCollector'), $fu_dto->primaryCollector);
270
  }
271
  if (isset_not_empty($fu_dto->fieldNotes)) {
272
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('fieldNotes'), $fu_dto->fieldNotes);
273
  }
274

    
275
  return cdm_sob_dto_table(t("Field unit"), $table_row_data, $fu_dto, 2);
276
}
277

    
278
/**
279
 * Compose an render array from a CDM GatheringDTO.
280
 *
281
 * The resulting DOM block level element will have a header line and details.
282
 *
283
 * compose_hook() implementation
284
 *
285
 * @param object $gathering_dto
286
 *   the CDM GatheringDTO object
287
 * @param bool $compact_mode
288
 *   Currently unused,
289
 *
290
 * @return array
291
 *  The render array for the GatheringDTO
292
 *
293
 * @ingroup compose
294
 */
295
function compose_cdm_gathering_dto_details($gathering_dto, $compact_mode = FALSE) {
296

    
297
  $table_row_data = [];
298
  if (isset_not_empty($gathering_dto->date)) {
299
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('date'),
300
      partialToDate($gathering_dto->date));
301
  }
302
  if (isset_not_empty($gathering_dto->collector)) {
303
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('collector'),
304
      $gathering_dto->collector);
305
  }
306
  if (isset_not_empty($gathering_dto->description)) {
307
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('description'),
308
      $gathering_dto->description);
309
  }
310
  if (isset_not_empty($gathering_dto->locality)) {
311
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('locality'),
312
      $gathering_dto->locality);
313
  }
314
  if (isset_not_empty($gathering_dto->country)) {
315
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('country'),
316
      $gathering_dto->country);
317
  }
318
  if (isset_not_empty($gathering_dto->collectingMethod)) {
319
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('collectingMethod'),
320
      $gathering_dto->collectingMethod);
321
  }
322
  if (isset($gathering_dto->exactLocation)) {
323
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('exactLocation'),
324
      render_point($gathering_dto->exactLocation));
325
  }
326
  if (isset($gathering_dto->absoluteElevation)) {
327
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('absoluteElevation'),
328
      statistical_values_from_gathering_event($gathering_dto, 'absoluteElevation'));
329
  }
330
  if (isset($gathering_dto->distanceToGround) && $gathering_dto->distanceToGround > 0) {
331
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('distanceToGround'),
332
      statistical_values_from_gathering_event($gathering_dto, 'distanceToGround'));
333
  }
334
  if (isset($gathering_dto->distanceToWaterSurface) && $gathering_dto->distanceToWaterSurface > 0) {
335
    $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('distanceToWaterSurface'),
336
      statistical_values_from_gathering_event($gathering_dto, 'distanceToWaterSurface'));
337
  }
338
  if (isset_not_empty($gathering_dto->collectingAreas)) {
339
    $area_representations = [];
340
    foreach ($gathering_dto->collectingAreas as $area) {
341
      // $area_representations[] = l($area->representation_L10n, path_to_named_area($area->uuid));
342
      $area_representations[] = $area;
343
    }
344
    if (!empty($area_representations)) {
345
      $table_row_data[] = cdm_sob_dto_table_row(cdm_occurrence_field_name_label('collectingAreas'),
346
        implode(', ', $area_representations)
347
      );
348
    }
349
  }
350

    
351
  return cdm_sob_dto_table(t("Gathering & Location"), $table_row_data, $gathering_dto, 1);
352
}
353

    
354
/**
355
 * Compose an render array from the SpecimenOrObservationDTO.determinedNames.
356
 *
357
 * The resulting DOM block level element will have a header line and details.
358
 *
359
 * compose_hook() implementation
360
 *
361
 * @param object $sob_dto
362
 *   the CDM SpecimenOrObservationDTO
363
 * @param bool $compact_mode
364
 *   Currently unused,
365
 *
366
 * @return array
367
 *  The render array for the SpecimenOrObservationDTO.determinedNames
368
 *
369
 * @ingroup compose
370
 */
371
function compose_cdm_sob_dto_determinations($sob_dto, $compact_mode = FALSE) {
372

    
373
  $table_row_data = [];
374

    
375
  foreach ($sob_dto->determinedNames as $name) {
376
    $taxon_name = cdm_ws_get(CDM_WS_PORTAL_NAME, $name->uuid);
377
    $table_row_data[] = cdm_sob_dto_table_row(NULL, render_taxon_or_name($taxon_name, url(path_to_name($taxon_name->uuid))));
378
  }
379

    
380
  $label = count($table_row_data) > 1 ? t("Identifications") : t("Identification");
381
  return cdm_sob_dto_table($label, $table_row_data, $sob_dto, 1);
382
}
383

    
384
/**
385
 * Compose an render array from the DerivedUnitDTO.specimenTypeDesignations.
386
 *
387
 * The resulting DOM block level element will have a header line and details.
388
 *
389
 * compose_hook() implementation
390
 *
391
 * @param object $unit_dto
392
 *   the CDM DerivedUnitDTO
393
 * @param bool $compact_mode
394
 *   Currently unused,
395
 *
396
 * @return array
397
 *  The render array for the SpecimenOrObservationDTO.determinedNames
398
 *
399
 * @ingroup compose
400
 */
401
function compose_cdm_unit_dto_type_designations($unit_dto, $compact_mode = FALSE) {
402

    
403
  $table_row_data = [];
404

    
405
  if (isset_not_empty($unit_dto->specimenTypeDesignations)) {
406
    $table_row_data[] = cdm_sob_dto_table_row(
407
      NULL,
408
      render_specimen_typedesignation_dto($unit_dto->specimenTypeDesignations));
409
  }
410

    
411
  return cdm_sob_dto_table(t('Type designations'), $table_row_data, $unit_dto, 5, 2);
412
}
413

    
414
/**
415
 * Compose an render array from the SpecimenOrObservationDTO.determinedNames.
416
 *
417
 * The resulting DOM block level element will have a header line and details.
418
 *
419
 * compose_hook() implementation
420
 *
421
 * @param object $sob_dto
422
 *   the CDM SpecimenOrObservationDTO
423
 * @param bool $compact_mode
424
 *   Currently unused,
425
 *
426
 * @return array
427
 *  The render array for the SpecimenOrObservationDTO.determinedNames
428
 *
429
 * @ingroup compose
430
 */
431
function compose_cdm_derived_unit_dto_storage_details($sob_dto, $compact_mode = FALSE) {
432

    
433
  $table_row_data = [];
434

    
435
  if (isset($sob_dto->collection)) {
436
    $table_row_data[] = cdm_sob_dto_table_row(
437
      cdm_occurrence_field_name_label('collection'),
438
      render_collection_dto($sob_dto->collection));
439
  }
440
  if (isset($sob_dto->storedUnder)) {
441
    $taxon_name = cdm_ws_get(CDM_WS_PORTAL_NAME, array($sob_dto->storedUnder->uuid));
442
    $table_row_data[] = cdm_sob_dto_table_row(
443
      cdm_occurrence_field_name_label('storedUnder'),
444
      render_taxon_or_name($taxon_name, path_to_name($taxon_name->uuid)));
445
  }
446
  if (isset($sob_dto->storedUnder)) {
447
    $taxon_name = cdm_ws_get(CDM_WS_PORTAL_NAME, array($sob_dto->storedUnder->uuid));
448
    $table_row_data[] = cdm_sob_dto_table_row(
449
      cdm_occurrence_field_name_label('storedUnder'),
450
      render_taxon_or_name($taxon_name, path_to_name($taxon_name->uuid)));
451
  }
452
  return cdm_sob_dto_table(t('Storage'), $table_row_data, $sob_dto, 1);
453
}
454

    
455
/**
456
 * @param $listOfMedia
457
 * @param $sob_dto
458
 * @param $heading
459
 *
460
 * @return array|null
461
 */
462
function cdm_sob_dto_media_table(array $listOfMedia, $sob_dto, $heading) {
463
  $table_row_data = [];
464
  $gallery_settings = getGallerySettings(CDM_DATAPORTAL_SPECIMEN_GALLERY_NAME);
465
  $captionElements = array(
466
    'title',
467
    '#uri' => t('open media'),
468
  );
469
  $gallery_markup = compose_cdm_media_gallerie(array(
470
    'mediaList' => $listOfMedia,
471
    'galleryName' => $sob_dto->uuid,
472
    'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
473
    'cols' => $gallery_settings['cdm_dataportal_media_cols'],
474
    'maxRows' => isset($gallery_settings['cdm_dataportal_media_maxRows']) ? isset($gallery_settings['cdm_dataportal_media_maxRows']) : null,
475
    'captionElements' => $captionElements,
476
    'mediaLinkType' => 'LIGHTBOX',
477
    'alternativeMediaUri' => NULL,
478
    'galleryLinkUri' => NULL,
479
    'showCaption' => true
480
  ));
481
  $table_row_data[] = cdm_sob_dto_table_row(NULL, $gallery_markup);
482
  $grid_col_span = count($listOfMedia) > 1 ? 2 : NULL;
483
  return cdm_sob_dto_table(t($heading), $table_row_data, $sob_dto, 20, $grid_col_span);
484
}
485

    
486
/**
487
 * Compose an render array from the DerivedUnitDTO.sequences.
488
 *
489
 * The resulting DOM block level element will have a header line and details.
490
 *
491
 * compose_hook() implementation
492
 *
493
 * @param object $unit_dto
494
 *   the CDM DerivedUnitDTO
495
 * @param bool $compact_mode
496
 *   Currently unused,
497
 *
498
 * @return array
499
 *  The render array for the DerivedUnitDTO.sequences.
500
 *
501
 * @ingroup compose
502
 *
503
 * TODO see  #3347 (services and REST service controller for molecular classes implemented)
504
 */
505
function compose_cdm_derived_unit_dto_sequences($unit_dto, $compact_mode = FALSE) {
506

    
507
  $table_row_data = [];
508
  if(isset_not_empty($unit_dto->sequences)){
509
    foreach ($unit_dto->sequences as $sequence) {
510
      if (isset($sequence->geneticAccessionNumber)) {
511
        $table_row_data[] = cdm_sob_dto_table_row(
512
          cdm_occurrence_field_name_label('geneticAccessionNumber'),
513
          $sequence->geneticAccessionNumber);
514
      }
515
      // TODO ....
516
    }
517
  }
518

    
519
  return cdm_sob_dto_table(t('Storage'), $table_row_data, $unit_dto, 1);
520
}
521

    
522
/**
523
 * Creates a form array for showing details of SpecimenOrObservationDTO in a
524
 * tabular form with heading.
525
 *
526
 * @param $table_heading
527
 * @param array $table_row_data
528
 * @param $sob_dto
529
 * @param $weight
530
 *  The weight determining the order of the drupal render element
531
 * @param $grid_col_span
532
 *  The number of grid colums the table should
533
 *
534
 * @return array|null
535
 */
536
function cdm_sob_dto_table($table_heading, array $table_row_data, $sob_dto, $weight = NULL, $grid_col_span = 1) {
537
  if (count($table_row_data) > 0) {
538
    $wrapper_style = '';
539
    if($grid_col_span > 1){
540
      $wrapper_style = ' style="grid-column-start: span ' . $grid_col_span. ';"';
541
    }
542
    $sob_table = [
543
      '#theme' => 'table',
544
      '#prefix' => '<div class="table-wrapper"' . $wrapper_style . '>',
545
      '#suffix' => '</div>',
546
      '#header' => [
547
        [
548
          'data' => $table_heading,
549
          'colspan' => 2,
550
        ],
551
      ],
552
      "#rows" => $table_row_data,
553
      "#attributes" => [
554
        "class" => [
555
          'specimen-or-observation-details',
556
          html_class_attribute_ref($sob_dto)
557
        ],
558
      ],
559
    ];
560
    if ($weight) {
561
      $sob_table['#weight'] = $weight;
562
    }
563
    return $sob_table;
564
  }
565
  return NULL;
566
}
567

    
568
function cdm_sob_dto_table_row($label, $value) {
569
  if ($value) {
570
    if($label) {
571
      return [
572
        'data' => [
573
          [
574
            'data' => str_replace(':', '', $label),
575
            'class' => [
576
              'label',
577
            ],
578
          ],
579
          $value,
580
        ],
581
        'no_striping' => TRUE,
582
      ];
583
    } else {
584
      // value spanning two columns
585
      return [
586
        'data' => [
587
          [
588
            'data' => $value,
589
            'colspan' => 2,
590
          ]
591
        ],
592
        'no_striping' => TRUE,
593
      ];
594
    }
595
  }
596
  return NULL;
597
}
(9-9/15)