Project

General

Profile

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

    
3
/**
4
 * @file
5
 * Functions for handling CDM TaggedText arrays
6
 *
7
 *
8
 * @copyright
9
 *   (C) 2007-2018 EDIT
10
 *   European Distributed Institute of Taxonomy
11
 *   http://www.e-taxonomy.eu
12
 *
13
 *   The contents of this module are subject to the Mozilla
14
 *   Public License Version 1.1.
15
 * @see http://www.mozilla.org/MPL/MPL-1.1.html
16
 *
17
 * @author
18
 *   - Andreas Kohlbecker <a.kohlbecker@BGBM.org>
19
 */
20

    
21
function tagged_text_new($tag_type, $text = null){
22
  $tt = new stdClass();
23
  $tt->type = $tag_type;
24
  $tt->text = $text;
25
  return $tt;
26
}
27

    
28
/**
29
 * Adds an array of render options to specific tagged text elements.
30
 *
31
 * The attributes are added as $tagged_text_item['attributes']. Existing 'attributes' are
32
 * preserved by merging new ones  to the existing array.
33
 *
34
 * @param array $taggedtxt
35
 * @param array $attributes_map array
36
 *   An array of arrays with following elements in each element array:
37
 *    - 'filter-type': the tagged text type to which the attributes are applicable
38
 *    - 'filter-uuid': (optional) and optional filter to only match a specific uuid.
39
 *    - 'attributes': array of attributes as accepted by drupal_attributes()
40
 *    - 'prefix': like the drupal_render() '#prefix' option
41
 *    - 'suffix': like the drupal_render() '#prefix' option
42
 */
43
function cdm_tagged_text_add_options(array &$taggedtxt, array $attributes_map){
44

    
45
  foreach ($attributes_map as $attribute_data){
46
    foreach ($taggedtxt as &$tt){
47
      if($tt->type == $attribute_data['filter-type']){
48
        if(isset($attribute_data['filter-uuid'])){
49
          if($tt->uuid !== $attribute_data['filter-uuid']){
50
            // ignore
51
            continue;
52
          }
53
        }
54
        // $tt matched type and filter-uuid if set, apply options
55
        foreach (array('attributes', 'prefix', 'suffix') as $key ){
56
          if(isset($attribute_data[$key])){
57
            $tt->$key = $attribute_data[$key];
58
          }
59
        }
60
      }
61
    }
62
  }
63
}
64

    
65
/**
66
 * Walks the passed TaggedText array to find all elements which have a
67
 * TaggedText->entityReference for CDM an entity. For each of these the
68
 * taggedTexts is loaded from the webservice and the original entry in
69
 * the TaggedText array will be replaced by the newly loaded array.
70
 *
71
 * Existing 'attributes' (@see cdm_tagged_text_add_options()) fields will be preserved by
72
 * copying them to each of the replacement tagged text items. 'prefix' will be added to
73
 * the first of the replaced items and 'suffix' to the last one.
74
 *
75
 *
76
 * @param array $taggedtxt
77
 *    The original TaggedText array
78
 * @param array $skiptags
79
 *    Optional list of tag names to skip
80
 * @return array
81
 *    The new tagged text with all TaggedText->entityReference objects expanded
82
 */
83
function cdm_tagged_text_expand_entity_references(array $taggedtxt, $skiptags = array()) {
84
  $tagged_text_expanded = array();
85
  foreach ($taggedtxt as $tt) {
86
    if (isset($tt->entityReference) && !in_array($tt->type, $skiptags)) {
87
      $referenced_tt = null;
88
      if(cdm_type_has_tagged_text($tt->entityReference->type)) {
89
        $base_uri = cdm_ws_base_uri($tt->entityReference->type);
90
        if($base_uri){
91
          if(str_endsWith($base_uri, '$0')){
92
            // sanitize placeholders at the end
93
            $base_uri = substr($base_uri, 0, strlen($base_uri) - 2);
94
          }
95
            $tagged_text_method = "/taggedText";
96
            if($base_uri == CDM_WS_NAME){
97
              $tagged_text_method = "/taggedName";
98
            }
99
            if(!str_endsWith($base_uri, '/')){
100
              $base_uri .= '/';
101
            }
102
            $referenced_tt = cdm_ws_get($base_uri . $tt->entityReference->uuid . $tagged_text_method);
103
        }
104
        if($referenced_tt){
105
          if(isset($tt->attributes)){
106
            foreach($referenced_tt as $reftt){
107
              $reftt->attributes = $tt->attributes;
108
            }
109
          }
110
          if(isset($tt->prefix)){
111
            $referenced_tt[0]->prefix = $tt->prefix;
112
          }
113
          if(isset($tt->suffix)){
114
            $referenced_tt[count($referenced_tt)-1]->suffix = $tt->suffix;
115
          }
116
          $tagged_text_expanded = array_merge($tagged_text_expanded, $referenced_tt);
117
          continue;
118
        }
119
      }
120
    }
121
    // default case
122
    $tagged_text_expanded[] = $tt;
123
  }
124
  return $tagged_text_expanded;
125
}
126

    
127
/**
128
 * Converts an array of TaggedText items into corresponding html tags.
129
 *
130
 * Each item is provided with a class attribute which is set to the key of the
131
 * TaggedText item.
132
 *
133
 * Tagged text where the type starts with 'PLACEHOLDER_' will be added to the markup as plain text whereas the
134
 * taggedText->type wrapped in curly brackets: '{'. $tt->text . '}' is used as text.
135
 * see tagged_text_extract_reference_and_detail()
136
 *
137
 * In addition to the tagged text element fields as defined in the cdm this method also recognizes:
138
 *    - 'attributes': array of attributes as accepted by drupal_attributes()
139
 *    - 'prefix': like the drupal_render() '#prefix' option
140
 *    - 'suffix': like the drupal_render() '#prefix' option
141
 * See also cdm_tagged_text_add_options()
142
 *
143
 * The algorithm of this functions is basically the same as for
144
 * eu.etaxonomy.cdm.strategy.cache.TaggedCacheHelper.createString(List<TaggedText> tags, HTMLTagRules htmlRules)
145
 *
146
 * @param array $taggedtxt
147
 *   Array with text items to convert.
148
 * @param array $skiptags
149
 *   Array of tag names to skip
150
 * @param $tag
151
 * @param $options
152
 *     - 'html' (default FALSE): Whether $text is HTML or just plain-text. NOTE! in this case the text is not
153
 *              translated by t()
154
 *
155
 * @return string
156
 *   The markup.
157
 */
158
function cdm_tagged_text_to_markup(array $taggedtxt, $skiptags = array(), $tag = 'span', $options = array()) {
159

    
160
  $out = '';
161
  $was_separator = false;
162
  $last_type = null;
163
  $last_suffix = '';
164
  $i = 0;
165
  foreach ($taggedtxt as $tt) {
166
    if (!in_array($tt->type, $skiptags) && $tt->text) {
167

    
168
      $is_first = $i == 0;
169
      $is_separator = is_tagged_text_sepatator_type($tt->type);
170
      if(str_beginsWith($tt->type, 'PLACEHOLDER_')){
171
        $out .=  '{'. $tt->type . '}';
172
      } else {
173

    
174
        // attributes
175
        $attributes = array();
176
        if(isset($tt->attributes)){
177
          $attributes = $tt->attributes;
178
        }
179
        if(!isset($attributes['class'])){
180
          $attributes['class'] = array();
181
        }
182
        $attributes['class'][] = $tt->type;
183
        if(isset($tt->entityReference)){
184
          $attributes['class'][] = html_class_attribute_ref($tt->entityReference);
185
        }
186

    
187
        // prefix and suffix
188
        $prefix = '';
189
        $suffix = '';
190
        if(isset($tt->prefix)){
191
          $prefix = $tt->prefix;
192
        }
193
        if(isset($tt->suffix)){
194
          $suffix = $tt->suffix;
195
        }
196

    
197
        if(($last_suffix || $last_type && $last_type != $tt->type) && $tag) {
198
          $out .= '</' . $tag . '>' . $last_suffix;
199
        }
200
        if(($prefix || !$last_type || $last_type != $tt->type) && $tag){
201
          $out .= $prefix . '<' . $tag . drupal_attributes($attributes) . '>';
202
        }
203
        if(!$is_separator && !$was_separator && !$is_first){
204
          $out .= " ";
205
        }else if ($is_separator){
206
          $out = rtrim($out);
207
        }
208
        if(isset($options['html']) &&  $options['html']){
209
          $out .= $tt->text;
210
        } else {
211
          $out .= t('@text', array('@text' => $tt->text));
212
        }
213
        $was_separator = $is_separator;
214
        $last_type = $tt->type;
215
        $last_suffix = $suffix;
216
      }
217
    }
218
    $i++;
219
  }
220
  if($tag) {
221
    $out .= '</' . $tag . '>';
222
  }
223
  return $out;
224
}
225

    
226
/**
227
 * Converts an array of TaggedText items into corresponding plain text string
228
 *
229
 * Each item is provided with a class attribute which is set to the key of the
230
 * TaggedText item.
231
 *
232
 * The algorithm of this functions is basically the same as for
233
 * eu.etaxonomy.cdm.strategy.cache.TaggedCacheHelper.createString(List<TaggedText> tags)
234
 *
235
 * @param array $taggedtxt
236
 *   Array with text items to convert.
237
 * @param array $skiptags
238
 *   Array of tag names to skip
239
 *
240
 * @return string
241
 *   The plain text
242
 */
243
function cdm_tagged_text_to_string(array $taggedtxt, $skiptags = array()) {
244

    
245
  return cdm_tagged_text_to_markup($taggedtxt, $skiptags, null);
246
}
247

    
248
/**
249
 * See cdmlib: boolean eu.etaxonomy.cdm.strategy.cache.TagEnum.isSeparator();
250
 *
251
 * @return bool
252
 */
253
function is_tagged_text_sepatator_type($tagged_text_type){
254
  static $separator_names = array('separator', 'postSeparator', 'secNameInSourceSeparator');
255
  $result = array_search($tagged_text_type, $separator_names) !== false;
256
  return $result;
257
}
258

    
259

    
260
/**
261
 * Finds the text tagged with $tag_type in an array of taggedText instances.
262
 *
263
 *
264
 * @param array $taggedtxt
265
 *   Array with text items.
266
 * @param array $include_tag_types
267
 *   Array of the tag types for which to find text items in the $taggedtxt array, or NULL
268
 *   to return all texts.
269
 *
270
 * @return array
271
 *   An array with the texts mapped by $tag_type.
272
 */
273
function cdm_tagged_text_values(array $taggedtxt, $include_tag_types = NULL) {
274
  $tokens = array();
275
  if (!empty($taggedtxt)) {
276
    foreach ($taggedtxt as $tagtxt) {
277
      if ($include_tag_types === NULL || array_search($tagtxt->type, $include_tag_types) !== false) {
278
        $tokens[] = $tagtxt->text;
279
      }
280
    }
281
  }
282
  return $tokens;
283
}
284

    
285
/**
286
 * Preprocess the taggedTitle arrays.
287
 *
288
 * Step 1: Turns 'newly' introduces tag types ("hybridSign")
289
 * into tag type "name"
290
 *
291
 * Step 2: Two taggedTexts which have the same type and which have
292
 * a separator between them are merged together.
293
 *
294
 * @param array $taggedTextList
295
 *    An array of TaggedText objects
296
 */
297
function normalize_tagged_text(&$taggedTextList) {
298

    
299
  if (is_array($taggedTextList)) {
300

    
301
    // First pass: rename.
302
    for ($i = 0; $i < count($taggedTextList); $i++) {
303

    
304
      if ($taggedTextList[$i]->type == "hybridSign") {
305
        $taggedTextList[$i]->type = "name";
306
      }
307
    }
308

    
309
    // Second pass: resolve separators.
310
    $taggedNameListNew = array();
311
    for ($i = 0; $i < count($taggedTextList); $i++) {
312

    
313
      // elements of the same type concatenated by a separator should be merged together
314
      if (isset($taggedTextList[$i + 2]) && $taggedTextList[$i + 1]->type == "separator" && $taggedTextList[$i]->type == $taggedTextList[$i + 2]->type) {
315
        $taggedName = clone $taggedTextList[$i];
316
        $taggedName->text = $taggedName->text . $taggedTextList[$i + 1]->text . $taggedTextList[$i + 2]->text;
317
        $taggedNameListNew[] = $taggedName;
318
        ++$i;
319
        ++$i;
320
        continue;
321
      }
322
      // no special handling
323
      $taggedNameListNew[] = $taggedTextList[$i];
324

    
325
    }
326
    $taggedTextList = $taggedNameListNew;
327
  }
328
}
329

    
330
/**
331
 * Extracts from a tagged text array all tagged text for references which is concatenated with citation
332
 * detail separator and citation detail into one single element.
333
 *
334
 * @param $tagged_text
335
 *    The tagged text to operate on
336
 * @param string $ref_tag_type
337
 *    The tag type for a reference is "reference", this is uses as default.
338
 * @param bool $replace_with_placeholder
339
 *    Indicates the method to add a empty placeholder tagged text alement as relpacement for the extrated tagged text
340
 *    elements.
341
 * @return array
342
 */
343
function tagged_text_extract_reference(&$tagged_text, $ref_tag_type = "reference", $replace_with_placeholder = false) {
344

    
345
  $extracted_tt = array();
346
  if (is_array($tagged_text)) {
347
    $extract_pos = null;
348
    $tt_legth = count($tagged_text);
349
    for ($i = 0; $i < $tt_legth; $i++) {
350
      if ($tagged_text[$i]->type == $ref_tag_type){
351
        if ($i > 0 && $tagged_text[$i - 1]->type == 'separator') {
352
          // the reference may be preceeded by a separator in case it is not a in-reference
353
          $extracted_tt[] = $tagged_text[$i - 1];
354
          unset($tagged_text[$i - 1]);
355
        } else {
356
          // need to add a separator since the reference tagged text will become the first element
357
          // ant thus will not be preceded by a separator
358
          $extracted_tt[] = tagged_text_new('separator', ' ');
359
        }
360
        $extracted_tt[] = $tagged_text[$i];
361
        if ($replace_with_placeholder) {
362
          // text must not be null, see cdm_tagged_text_to_markup()
363
          $tagged_text[$i] = tagged_text_new("PLACEHOLDER_" . $ref_tag_type, "PLACEHOLDER_" . $ref_tag_type);
364
        } else {
365
          unset($tagged_text[$i]);
366
        }
367
      }
368
    }
369
  }
370
  $tagged_text = array_values($tagged_text); // re-index array to make it continuous again
371
  return $extracted_tt;
372
}
373

    
374
/**
375
 * Extracts the tagged text for sec references with separator and citation detail from a tagged text array.
376
 * @param $tagged_text
377
 *    The tagged text to operate on
378
 * @param string $ref_tag_type
379
 *    The tagtype for a secreference is "secReference", but "relSecReference" is also used in case of relationships.
380
 * @param bool $replace_with_placeholder
381
 *    Indicates the method to add a empty placeholder tagged text alement as relpacement for the extrated tagged text
382
 *    elements.
383
 * @param string $ref_detail_tag_type
384
 * @return array
385
 */
386
function tagged_text_extract_reference_and_detail(&$tagged_text, $ref_tag_type = "secReference", $replace_with_placeholder = false) {
387

    
388
  $extracted_tt = array();
389
  if (is_array($tagged_text)) {
390
    $extract_pos = null;
391
    $tt_length = count($tagged_text);
392
    for ($i = 0; $i < $tt_length - 1; $i++) {
393
      if ($tagged_text[$i + 1]->type == $ref_tag_type && $tagged_text[$i]->type == "separator"){
394
        $extracted_tt[0] = $tagged_text[$i];
395
        $extracted_tt[1] = $tagged_text[$i + 1];
396

    
397
        if($replace_with_placeholder){
398
          // text must not be null, see cdm_tagged_text_to_markup()
399
          $tagged_text[$i] = tagged_text_new("PLACEHOLDER_" . $ref_tag_type, "PLACEHOLDER_" . $ref_tag_type);
400
        } else {
401
          unset($tagged_text[$i]);
402
        }
403
        unset($tagged_text[$i + 1]);
404
        // also get the microreference which could be in $tagged_text[$i + 3]
405
        if(isset($tagged_text[$i + 3])  && $tagged_text[$i + 2]->type == "separator" && $tagged_text[$i + 3]->type == "secMicroReference"){
406
          $extracted_tt[2] = $tagged_text[$i + 2];
407
          $extracted_tt[3] = $tagged_text[$i + 3];
408
          unset($tagged_text[$i + 2]);
409
          unset($tagged_text[$i + 3]);
410
        }
411
        break;
412
      }
413
    }
414
  }
415
  $tagged_text = array_values($tagged_text); // re-index array to make it continuous again
416
  return $extracted_tt;
417
}
418

    
419
/**
420
 * Extracts the tagged text for sec references name in source with separator from a tagged text array.
421
 * @param $tagged_text
422
 *    The tagged text to operate on
423
 * @param bool $replace_with_placeholder
424
 *    Indicates the method to add a empty placeholder tagged text alement as relpacement for the extrated tagged text
425
 *    elements.
426
 *
427
 * @return array
428
 */
429
function tagged_text_extract_secReference_name_in_source(&$tagged_text) {
430

    
431
  $extracted_tt = array();
432
  if (is_array($tagged_text)) {
433

    
434
    $tt_length = count($tagged_text);
435
    $index = -1;
436
    for ($i = 0; $i < $tt_length; $i++) {
437
      if ($i < $tt_length -1 && isset($tagged_text[$i]) && $tagged_text[$i+1]->type == 'name' && $tagged_text[$i]->type == "secNameInSourceSeparator") {
438
        $extracted_tt[++$index] = $tagged_text[$i];
439
        $extracted_tt[++$index] = $tagged_text[$i + 1];
440

    
441
        unset($tagged_text[$i]);
442
        unset($tagged_text[$i + 1]);
443
        continue;
444
        //search for all elements with type secNameInSource and the last separator
445
      }
446
      if (isset($tagged_text[$i]) && $tagged_text[$i]->type == 'secNameInSourceSeparator') {
447
        $extracted_tt[++$index] = $tagged_text[$i];
448

    
449
        unset($tagged_text[$i]);
450
        break;
451
        //search for all elements with type secNameInSource and the last separator
452
      }
453
      if(isset($tagged_text[$i])  && $index >0){
454
          $extracted_tt[++$index] = $tagged_text[$i];
455
          unset($tagged_text[$i]);
456
      }
457

    
458

    
459
    }
460
  }
461
  $tagged_text = array_values($tagged_text); // re-index array to make it continuous again
462
  return $extracted_tt;
463
}
464

    
465
function tagged_text_extract_nomstatus(&$tagged_text) {
466

    
467
  $extracted_tt = [];
468
  if (is_array($tagged_text)) {
469
    $itemcnt = count($tagged_text); // preserve the count since the array may shrink while processing
470
    for ($i = 0; $i < $itemcnt; $i++) {
471
      if(isset($tagged_text[$i])){ // prevent from accessing removed indices
472
        if ($tagged_text[$i]->type == "nomStatus"){
473
          $extracted_status_items = [];
474
          $extracted_status_items[] = $tagged_text[$i];
475
          if(isset($tagged_text[$i + 1]) && $tagged_text[$i + 1]->type == "postSeparator"){
476
            $extracted_status_items[] = $tagged_text[$i + 1];
477
            unset($tagged_text[$i + 1]);
478
          }
479
          if ($tagged_text[$i - 1]->type == "separator"){
480
            array_unshift($extracted_status_items, $tagged_text[$i - 1]);
481
            unset($tagged_text[$i - 1]);
482
          }
483
          unset($tagged_text[$i]);
484
          $extracted_tt = array_merge($extracted_tt, $extracted_status_items);
485
        }
486
      }
487
    }
488
  }
489
  $tagged_text = array_values($tagged_text); // re-index array to make it continuous again
490
  return $extracted_tt;
491
}
492

    
493
/**
494
 * Tries to find a tagged text elements that matches the $type and $regex_pattern
495
 * and returns all preceding elements as new array.
496
 *
497
 * @param $tagged_text
498
 *  The tagged text to crop
499
 * @param $type
500
 *  The tagged text type to match
501
 * @param $regex_pattern
502
 *  A PREG regex pattern
503
 *
504
 * @return array
505
 *  A new tagged text array
506
 */
507
function tagged_text_crop_at(&$tagged_text, $type, $regex_pattern) {
508
  $cropped_tagged_text = array();
509
  if (is_array($tagged_text)) {
510
    for ($i = 0; $i < count($tagged_text); $i++) {
511
      if ($tagged_text[$i]->type == $type && preg_match($regex_pattern, $tagged_text[$i]->text)) {
512
        break;
513
      }
514
      $cropped_tagged_text[] = $tagged_text[$i];
515
    }
516
  }
517
  return $cropped_tagged_text;
518
}
519

    
520
function tagged_text_extract(&$tagged_text, $type, $replace_with_placeholder = false) {
521
  $matching_elements = array();
522
  if (is_array($tagged_text)) {
523
    for ($i = 0; $i < count($tagged_text); $i++) {
524
      if($tagged_text[$i]->type == $type){
525
        $matching_elements[] = $tagged_text[$i];
526
        if($replace_with_placeholder){
527
          // text must not be null, see cdm_tagged_text_to_markup()
528
          $tagged_text[$i] = tagged_text_new("PLACEHOLDER_" . $type, "PLACEHOLDER_" . $type);
529
        } else {
530
          unset($tagged_text[$i]);
531
        }
532
      }
533
    }
534
  }
535
  $tagged_text = array_values($tagged_text); // re-index array to make it continuous again
536
  return $matching_elements;
537
}
538

    
539
function find_tagged_text_elements(&$tagged_text, $type){
540
  $matching_elements = array();
541
  if (is_array($tagged_text)) {
542
    for ($i = 0; $i < count($tagged_text) - 1; $i++) {
543
      if($tagged_text[$i]->type == $type){
544
        $matching_elements[] = $tagged_text[$i];
545
      }
546
    }
547
  }
548
  return $matching_elements;
549
}
(9-9/12)