Passed
Pull Request — master (#1198)
by
unknown
03:34
created

Concept::removeNotationFromProperties()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 6
rs 10
1
<?php
2
3
/**
4
 * Dataobject for a single concept.
5
 */
6
7
class Concept extends VocabularyDataObject implements Modifiable
8
{
9
    /**
10
     * Stores a label string if the concept has been found through
11
     * a altLabel/label in a another language than the ui.
12
     */
13
    private $foundby;
14
    /** Type of foundby match: 'alt', 'hidden' or 'lang' */
15
    private $foundbytype;
16
    /** the EasyRdf\Graph object of the concept */
17
    private $graph;
18
    private $clang;
19
20
    /** concept properties that should not be shown to users */
21
    private $DELETED_PROPERTIES = array(
22
        'skosext:broaderGeneric', # these are remnants of bad modeling
23
        'skosext:broaderPartitive', #
24
25
        'skos:hiddenLabel', # because it's supposed to be hidden
26
        'skos:prefLabel', # handled separately by getLabel
27
        'rdfs:label', # handled separately by getLabel
28
29
        'skos:topConceptOf', # because it's too technical, not relevant for users
30
        'skos:inScheme', # should be evident in any case
31
        'skos:member', # this shouldn't be shown on the group page
32
        'dc:created', # handled separately
33
        'dc:modified', # handled separately
34
    );
35
36
    /** related concepts that should be shown to users in the appendix */
37
    private $MAPPING_PROPERTIES = array(
38
        'skos:exactMatch',
39
        'skos:narrowMatch',
40
        'skos:broadMatch',
41
        'skos:closeMatch',
42
        'skos:relatedMatch',
43
        'rdfs:seeAlso',
44
        'owl:sameAs',
45
    );
46
47
    /** default external properties we are interested in saving/displaying from mapped external objects */
48
    private $DEFAULT_EXT_PROPERTIES = array(
49
        "dc11:title",
50
        "dcterms:title",
51
        "skos:prefLabel",
52
        "skos:exactMatch",
53
        "skos:closeMatch",
54
        "skos:inScheme",
55
        "rdfs:label",
56
        "rdfs:isDefinedBy",
57
        "owl:sameAs",
58
        "rdf:type",
59
        "void:inDataset",
60
        "void:sparqlEndpoint",
61
        "void:uriLookupEndpoint",
62
        "schema:about",
63
        "schema:description",
64
        "schema:inLanguage",
65
        "schema:name",
66
        "schema:isPartOf",
67
        "wdt:P31",
68
        "wdt:P625"
69
    );
70
71
    /**
72
     * Initializing the concept object requires the following parameters.
73
     * @param Model $model
74
     * @param Vocabulary $vocab
75
     * @param EasyRdf\Resource $resource
76
     * @param EasyRdf\Graph $graph
77
     */
78
    public function __construct($model, $vocab, $resource, $graph, $clang)
79
    {
80
        parent::__construct($model, $vocab, $resource);
81
        if ($vocab !== null) {
82
            $this->order = $vocab->getConfig()->getPropertyOrder();
83
        } else {
84
            $this->order = VocabularyConfig::DEFAULT_PROPERTY_ORDER;
85
        }
86
        $this->graph = $graph;
87
        $this->clang = $clang;
88
    }
89
90
    /**
91
     * Returns the concept uri.
92
     * @return string
93
     */
94
    public function getUri()
95
    {
96
        return $this->resource->getUri();
97
    }
98
99
    public function getType()
100
    {
101
        return $this->resource->types();
102
    }
103
104
105
    /**
106
     * Returns a boolean value indicating whether the resource is a group defined in the vocab config as skosmos:groupClass.
107
     * @return boolean
108
     */
109
    public function isGroup() {
110
        $groupClass = $this->getVocab()->getConfig()->getGroupClassURI();
111
        if ($groupClass) {
112
            $groupClass = EasyRdf\RdfNamespace::shorten($groupClass) !== null ? EasyRdf\RdfNamespace::shorten($groupClass) : $groupClass;
0 ignored issues
show
introduced by
The condition EasyRdf\RdfNamespace::sh...n($groupClass) !== null is always true.
Loading history...
113
            return in_array($groupClass, $this->getType());
114
        }
115
        return false;
116
    }
117
118
    /**
119
     * Returns a boolean value indicating if the concept has been deprecated.
120
     * @return boolean
121
     */
122
    public function getDeprecated()
123
    {
124
        $deprecatedValue = $this->resource->getLiteral('owl:deprecated');
125
        return ($deprecatedValue !== null && filter_var($deprecatedValue->getValue(), FILTER_VALIDATE_BOOLEAN));
126
    }
127
128
    /**
129
     * Returns a label for the concept in the content language or if not possible in any language.
130
     * @return string
131
     */
132
    public function getLabel()
133
    {
134
        foreach ($this->vocab->getConfig()->getLanguageOrder($this->clang) as $fallback) {
135
            if ($this->resource->label($fallback) !== null) {
136
                return $this->resource->label($fallback);
137
            }
138
            // We need to check all the labels in case one of them matches a subtag of the current language
139
            foreach($this->resource->allLiterals('skos:prefLabel') as $label) {
140
                // the label lang code is a subtag of the UI lang eg. en-GB - create a new literal with the main language
141
                if ($label !== null && strpos($label->getLang(), $fallback . '-') === 0) {
142
                    return EasyRdf\Literal::create($label, $fallback);
143
                }
144
            }
145
        }
146
147
        // Last resort: label in any language, including literal with empty language tag
148
        $label = $this->resource->label();
149
        if ($label !== null) {
150
            if (!$label->getLang()) {
151
                return $label->getValue();
152
            }
153
            return EasyRdf\Literal::create($label->getValue(), $label->getLang());
154
        }
155
156
        // empty
157
        return "";
158
    }
159
160
    public function hasXlLabel($prop = 'prefLabel')
161
    {
162
        if ($this->resource->hasProperty('skosxl:' . $prop)) {
163
            return true;
164
        }
165
        return false;
166
    }
167
168
    public function getXlLabel()
169
    {
170
        $labels = $this->resource->allResources('skosxl:prefLabel');
171
        foreach($labels as $labres) {
172
            $label = $labres->getLiteral('skosxl:literalForm');
173
            if ($label !== null && $label->getLang() == $this->clang) {
174
                return new LabelSkosXL($this->model, $labres);
175
            }
176
        }
177
        return null;
178
    }
179
180
    /**
181
     * Returns a notation for the concept or null if it has not been defined.
182
     * @return string eg. '999'
183
     */
184
    public function getNotation()
185
    {
186
        $notation = $this->resource->get('skos:notation');
187
        if ($this->vocab->getConfig()->showNotation() && $notation !== null) {
188
            return $notation->getValue();
189
        }
190
191
        return null;
192
    }
193
194
    /**
195
     * Returns the Vocabulary object or undefined if that is not available.
196
     * @return Vocabulary
197
     */
198
    public function getVocab()
199
    {
200
        return $this->vocab;
201
    }
202
203
    /**
204
     * Returns the vocabulary shortname string or id if that is not available.
205
     * @return string
206
     */
207
    public function getShortName()
208
    {
209
        return $this->vocab ? $this->vocab->getShortName() : null;
210
    }
211
212
    /**
213
     * Returns the vocabulary shortname string or id if that is not available.
214
     * @return string
215
     */
216
    public function getVocabTitle()
217
    {
218
        return $this->vocab ? $this->vocab->getTitle() : null;
219
    }
220
221
    /**
222
     * Setter for the $clang property.
223
     * @param string $clang language code eg. 'en'
224
     */
225
    public function setContentLang($clang)
226
    {
227
        $this->clang = $clang;
228
    }
229
230
    public function getContentLang()
231
    {
232
        return $this->clang;
233
    }
234
235
    /**
236
     * Setter for the $foundby property.
237
     * @param string $label label that was matched
238
     * @param string $type type of match: 'alt', 'hidden', or 'lang'
239
     */
240
    public function setFoundBy($label, $type)
241
    {
242
        $this->foundby = $label;
243
        $this->foundbytype = $type;
244
    }
245
246
    /**
247
     * Getter for the $foundby property.
248
     * @return string
249
     */
250
    public function getFoundBy()
251
    {
252
        return $this->foundby;
253
    }
254
255
    /**
256
     * Getter for the $foundbytype property.
257
     * @return string
258
     */
259
    public function getFoundByType()
260
    {
261
        return $this->foundbytype;
262
    }
263
264
    /**
265
     * Processes a single external resource i.e., adds the properties from
266
     * 1) $this->$DEFAULT_EXT_PROPERTIES
267
     * 2) VocabConfig external properties
268
     * 3) Possible plugin defined external properties
269
     * to $this->graph
270
     * @param EasyRdf\Resource $res
271
     */
272
    public function processExternalResource($res)
273
    {
274
        $exGraph = $res->getGraph();
275
        // catch external subjects that have $res as object
276
        $extSubjects = $exGraph->resourcesMatching("schema:about", $res);
277
278
        $propList =  array_unique(array_merge(
279
            $this->DEFAULT_EXT_PROPERTIES,
280
            $this->getVocab()->getConfig()->getExtProperties(),
281
            $this->getVocab()->getConfig()->getPlugins()->getExtProperties()
282
        ));
283
284
        $seen = array();
285
        $this->addExternalTriplesToGraph($res, $seen, $propList);
286
        foreach ($extSubjects as $extSubject) {
287
            if ($extSubject->isBNode() && array_key_exists($extSubject->getUri(), $seen)) {
288
                // already processed, skip
289
                continue;
290
            }
291
            $seen[$extSubject->getUri()] = 1;
292
            $this->addExternalTriplesToGraph($extSubject, $seen, $propList);
293
        }
294
295
    }
296
297
    /**
298
     * Adds resource properties to $this->graph
299
     * @param EasyRdf\Resource $res
300
     * @param string[] $seen Processed resources so far
301
     * @param string[] $props (optional) limit to these property URIs
302
     */
303
    private function addExternalTriplesToGraph($res, &$seen, $props=null)
304
    {
305
        if (array_key_exists($res->getUri(), $seen) && $seen[$res->getUri()] === 0) {
0 ignored issues
show
introduced by
The condition $seen[$res->getUri()] === 0 is always false.
Loading history...
306
            return;
307
        }
308
        $seen[$res->getUri()] = 0;
309
310
        if ($res->isBNode() || is_null($props)) {
311
            foreach ($res->propertyUris() as $prop) {
312
                $this->addPropertyValues($res, $prop, $seen);
313
            }
314
        }
315
        else {
316
            foreach ($props as $prop) {
317
                if ($res->hasProperty($prop)) {
318
                    $this->addPropertyValues($res, $prop, $seen);
319
                }
320
            }
321
        }
322
    }
323
324
    /**
325
     * Adds values of a single single property of a resource to $this->graph
326
     * implements Concise Bounded Description definition
327
     * @param EasyRdf\Resource $res
328
     * @param string $prop
329
     * @param string[] $seen Processed resources so far
330
     */
331
    private function addPropertyValues($res, $prop, &$seen)
332
    {
333
        $resList = $res->allResources('<' . $prop . '>');
334
335
        foreach ($resList as $res2) {
336
            if ($res2->isBNode()) {
337
                $this->addExternalTriplesToGraph($res2, $seen);
338
            }
339
            $this->graph->addResource($res, $prop, $res2);
340
            $this->addResourceReifications($res, $prop, $res2, $seen);
341
        }
342
343
        $litList = $res->allLiterals('<' . $prop . '>');
344
345
        foreach ($litList as $lit) {
346
            $this->graph->addLiteral($res, $prop, $lit);
347
            $this->addLiteralReifications($res, $prop, $lit, $seen);
348
        }
349
    }
350
351
    /**
352
     * Adds reifications of a triple having a literal object to $this->graph
353
     * @param EasyRdf\Resource $sub
354
     * @param string $pred
355
     * @param EasyRdf\Literal $obj
356
     * @param string[] $seen Processed resources so far
357
     */
358
    private function addLiteralReifications($sub, $pred, $obj, &$seen)
359
    {
360
        $pos_reifs = $sub->getGraph()->resourcesMatching("rdf:subject", $sub);
361
        foreach ($pos_reifs as $pos_reif) {
362
            $lit = $pos_reif->getLiteral("rdf:object", $obj->getLang());
363
364
            if (!is_null($lit) && $lit->getValue() === $obj->getValue() &&
365
                $pos_reif->isA("rdf:Statement") &&
366
                $pos_reif->hasProperty("rdf:predicate", new EasyRdf\Resource($pred, $sub->getGraph())))
367
            {
368
                $this->addExternalTriplesToGraph($pos_reif, $seen);
369
            }
370
        }
371
    }
372
373
    /**
374
     * Adds reifications of a triple having a resource object to $this->graph
375
     * @param EasyRdf\Resource $sub
376
     * @param string $pred
377
     * @param EasyRdf\Resource $obj
378
     * @param string[] $seen Processed resources so far
379
     */
380
    private function addResourceReifications($sub, $pred, $obj, &$seen)
381
    {
382
        $pos_reifs = $sub->getGraph()->resourcesMatching("rdf:subject", $sub);
383
        foreach ($pos_reifs as $pos_reif) {
384
            if ($pos_reif->isA("rdf:Statement") &&
385
                $pos_reif->hasProperty("rdf:object", $obj) &&
386
                $pos_reif->hasProperty("rdf:predicate", new EasyRdf\Resource($pred, $sub->getGraph())))
387
            {
388
                $this->addExternalTriplesToGraph($pos_reif, $seen);
389
            }
390
        }
391
    }
392
393
    /**
394
     * @return ConceptProperty[]
395
     */
396
    public function getMappingProperties(array $whitelist = null)
397
    {
398
        $ret = array();
399
400
        $longUris = $this->resource->propertyUris();
401
        foreach ($longUris as &$prop) {
402
            if (EasyRdf\RdfNamespace::shorten($prop) !== null) {
403
                // shortening property labels if possible
404
                $prop = $sprop = EasyRdf\RdfNamespace::shorten($prop);
405
            } else {
406
                // EasyRdf requires full URIs to be in angle brackets
407
                $sprop = "<$prop>";
408
            }
409
            if ($whitelist && !in_array($prop, $whitelist)) {
410
                // whitelist in use and this is not a whitelisted property, skipping
411
                continue;
412
            }
413
414
            if (in_array($prop, $this->MAPPING_PROPERTIES) && !in_array($prop, $this->DELETED_PROPERTIES)) {
415
                $propres = new EasyRdf\Resource($prop, $this->graph);
416
                $proplabel = $propres->label($this->getEnvLang()) ? $propres->label($this->getEnvLang()) : $propres->label(); // current language
417
                $propobj = new ConceptProperty($prop, $proplabel);
418
                if ($propobj->getLabel() !== null) {
419
                    // only display properties for which we have a label
420
                    $ret[$prop] = $propobj;
421
                }
422
423
                // Iterating through every resource and adding these to the data object.
424
                foreach ($this->resource->allResources($sprop) as $val) {
425
                    if (isset($ret[$prop])) {
426
                        // checking if the target vocabulary can be found at the skosmos endpoint
427
                        $exuri = $val->getUri();
428
                        // if multiple vocabularies are found, the following method will return in priority the current vocabulary of the concept
429
                        $exvoc = $this->model->guessVocabularyFromURI($exuri, $this->vocab->getId());
430
                        // if not querying the uri itself
431
                        if (!$exvoc) {
432
                            $response = null;
433
                            // if told to do so in the vocabulary configuration
434
                            if ($this->vocab->getConfig()->getExternalResourcesLoading()) {
435
                                $response = $this->model->getResourceFromUri($exuri);
436
                            }
437
438
                            if ($response) {
439
                                $ret[$prop]->addValue(new ConceptMappingPropertyValue($this->model, $this->vocab, $response, $this->resource, $prop));
440
441
                                $this->processExternalResource($response);
442
443
                                continue;
444
                            }
445
                        }
446
                        $ret[$prop]->addValue(new ConceptMappingPropertyValue($this->model, $this->vocab, $val, $this->resource, $prop, $this->clang));
447
                    }
448
                }
449
            }
450
        }
451
452
        // sorting the properties to a order preferred in the Skosmos concept page.
453
        return $this->arbitrarySort($ret);
454
    }
455
456
    /**
457
     * Iterates over all the properties of the concept and returns those in an array.
458
     * @return array
459
     */
460
    public function getProperties()
461
    {
462
        $properties = array();
463
        $narrowersByUri = array();
464
        $inCollection = array();
465
        $membersArray = array();
466
        $longUris = $this->resource->propertyUris();
467
        $duplicates = array();
468
        $ret = array();
469
470
        // looking for collections and linking those with their narrower concepts
471
        if ($this->vocab->getConfig()->getArrayClassURI() !== null) {
0 ignored issues
show
introduced by
The condition $this->vocab->getConfig(...rrayClassURI() !== null is always true.
Loading history...
472
            $collections = $this->graph->allOfType($this->vocab->getConfig()->getArrayClassURI());
473
            if (sizeof($collections) > 0) {
474
                // indexing the narrowers once to avoid iterating all of them with every collection
475
                foreach ($this->resource->allResources('skos:narrower') as $narrower) {
476
                    $narrowersByUri[$narrower->getUri()] = $narrower;
477
                }
478
479
                foreach ($collections as $coll) {
480
                    $currCollMembers = $this->getCollectionMembers($coll, $narrowersByUri);
481
                    foreach ($currCollMembers as $collection) {
482
                        if ($collection->getSubMembers()) {
483
                            $submembers = $collection->getSubMembers();
484
                            foreach ($submembers as $member) {
485
                                $inCollection[$member->getUri()] = true;
486
                            }
487
488
                        }
489
                    }
490
491
                    if (isset($collection) && $collection->getSubMembers()) {
492
                        $membersArray = array_merge($currCollMembers, $membersArray);
493
                    }
494
495
                }
496
                $properties['skos:narrower'] = $membersArray;
497
            }
498
        }
499
500
        foreach ($longUris as &$prop) {
501
            // storing full URI without brackets in a separate variable
502
            $longUri = $prop;
503
            if (EasyRdf\RdfNamespace::shorten($prop) !== null) {
504
                // shortening property labels if possible
505
                $prop = $sprop = EasyRdf\RdfNamespace::shorten($prop);
506
            } else {
507
                // EasyRdf requires full URIs to be in angle brackets
508
                $sprop = "<$prop>";
509
            }
510
511
            if (!in_array($prop, $this->DELETED_PROPERTIES) || ($this->isGroup() === false && $prop === 'skos:member')) {
512
                // retrieve property label and super properties from the current vocabulary first
513
                $propres = new EasyRdf\Resource($prop, $this->graph);
514
                $proplabel = $propres->label($this->getEnvLang()) ? $propres->label($this->getEnvLang()) : $propres->label();
515
516
                $prophelp = $propres->getLiteral('rdfs:comment|skos:definition', $this->getEnvLang());
517
                if ($prophelp === null) {
518
                    // try again without language restriction
519
                    $prophelp = $propres->getLiteral('rdfs:comment|skos:definition');
520
                }
521
522
                // check if the property is one of the well-known properties for which we have a gettext translation
523
                // if it is then we can skip the additional lookups in the default graph
524
                $propkey = (substr($prop, 0, 5) == 'dc11:') ?
525
                    str_replace('dc11:', 'dc:', $prop) : $prop;
526
                $is_well_known = (gettext($propkey) != $propkey);
527
528
                // if not found in current vocabulary, look up in the default graph to be able
529
                // to read an ontology loaded in a separate graph
530
                // note that this imply that the property has an rdf:type declared for the query to work
531
                if(!$is_well_known && !$proplabel) {
532
                    $envLangLabels = $this->model->getDefaultSparql()->queryLabel($longUri, $this->getEnvLang());
533
                    
534
                    $defaultPropLabel = $this->model->getDefaultSparql()->queryLabel($longUri, '');
535
536
					if($envLangLabels) {
537
						$proplabel = $envLangLabels[$this->getEnvLang()];
538
                    } else {
539
						if($defaultPropLabel) {
540
							$proplabel = $defaultPropLabel[''];
541
						}
542
					}
543
                }
544
545
                // look for superproperties in the current graph
546
                $superprops = array();
547
                foreach ($this->graph->allResources($prop, 'rdfs:subPropertyOf') as $subi) {
548
                    $superprops[] = $subi->getUri();
549
                }
550
551
                // also look up superprops in the default graph if not found in current vocabulary
552
                if(!$is_well_known && (!$superprops || empty($superprops))) {
553
                    $superprops = $this->model->getDefaultSparql()->querySuperProperties($longUri);
554
                }
555
556
                // we're reading only one super property, even if there are multiple ones
557
                $superprop = ($superprops)?$superprops[0]:null;
558
                if ($superprop) {
559
                    $superprop = EasyRdf\RdfNamespace::shorten($superprop) ? EasyRdf\RdfNamespace::shorten($superprop) : $superprop;
560
                }
561
                $sort_by_notation = $this->vocab->getConfig()->sortByNotation();
562
                $propobj = new ConceptProperty($prop, $proplabel, $prophelp, $superprop, $sort_by_notation);
563
564
                if ($propobj->getLabel() !== null) {
565
                    // only display properties for which we have a label
566
                    $ret[$prop] = $propobj;
567
                }
568
569
                // searching for subproperties of literals too
570
                if($superprops) {
571
                    foreach ($superprops as $subi) {
572
                        $suburi = EasyRdf\RdfNamespace::shorten($subi) ? EasyRdf\RdfNamespace::shorten($subi) : $subi;
573
                        $duplicates[$suburi] = $prop;
574
                    }
575
                }
576
577
                // Iterating through every literal and adding these to the data object.
578
                foreach ($this->resource->allLiterals($sprop) as $val) {
579
                    $literal = new ConceptPropertyValueLiteral($this->model, $this->vocab, $this->resource, $val, $prop);
580
                    // only add literals when they match the content/hit language or have no language defined OR when they are literals of a multilingual property
581
                    if (isset($ret[$prop]) && ($literal->getLang() === $this->clang || $literal->getLang() === null) || $this->vocab->getConfig()->hasMultiLingualProperty($prop)) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (IssetNode && $literal->...iLingualProperty($prop), Probably Intended Meaning: IssetNode && ($literal->...LingualProperty($prop))
Loading history...
582
                        $ret[$prop]->addValue($literal);
583
                    }
584
585
                }
586
587
                // Iterating through every resource and adding these to the data object.
588
                foreach ($this->resource->allResources($sprop) as $val) {
589
                    // skipping narrower concepts which are already shown in a collection
590
                    if ($sprop === 'skos:narrower' && array_key_exists($val->getUri(), $inCollection)) {
591
                        continue;
592
                    }
593
594
                    // hiding rdf:type property if it's just skos:Concept
595
                    if ($sprop === 'rdf:type' && $val->shorten() === 'skos:Concept') {
596
                        continue;
597
                    }
598
599
                    // handled by getMappingProperties()
600
                    if (in_array($sprop, $this->MAPPING_PROPERTIES)) {
601
                        continue;
602
                    }
603
604
                    if (isset($ret[$prop])) {
605
                        // create a ConceptPropertyValue first, assuming the resource exists in current vocabulary
606
                        $value = new ConceptPropertyValue($this->model, $this->vocab, $val, $prop, $this->clang);
607
                        $ret[$prop]->addValue($value);
608
                    }
609
610
                }
611
            }
612
        }
613
        // adding narrowers part of a collection
614
        foreach ($properties as $prop => $values) {
615
            foreach ($values as $value) {
616
                $ret[$prop]->addValue($value);
617
            }
618
        }
619
620
        $groupPropObj = new ConceptProperty('skosmos:memberOf', null);
621
        foreach ($this->getGroupProperties() as $propVals) {
622
            foreach ($propVals as $propVal) {
623
                $groupPropObj->addValue($propVal);
624
            }
625
        }
626
        $ret['skosmos:memberOf'] = $groupPropObj;
627
628
        $arrayPropObj = new ConceptProperty('skosmos:memberOfArray', null);
629
        foreach ($this->getArrayProperties() as $propVals) {
630
            foreach ($propVals as $propVal) {
631
                $arrayPropObj->addValue($propVal);
632
            }
633
        }
634
635
        $ret['skosmos:memberOfArray'] = $arrayPropObj;
636
637
        foreach ($ret as $key => $prop) {
638
            if (sizeof($prop->getValues()) === 0) {
639
                unset($ret[$key]);
640
            }
641
        }
642
643
        $ret = $this->removeDuplicatePropertyValues($ret, $duplicates);
644
645
        $ret = $this->vocab->getConfig()->getShowNotationAsProperty() !== null ? $this->removeNotationFromProperties($ret, $this->vocab->getConfig()->getShowNotationAsProperty()) : $ret ;
0 ignored issues
show
introduced by
The condition $this->vocab->getConfig(...onAsProperty() !== null is always true.
Loading history...
646
647
        // sorting the properties to the order preferred in the Skosmos concept page.
648
        return $this->arbitrarySort($ret);
649
    }
650
651
    /**
652
     * Removes properties that have duplicate values.
653
     * @param array $ret the array of properties generated by getProperties
654
     * @param array $duplicates array of properties found are a subProperty of a another property
655
     * @return array of ConceptProperties
656
     */
657
    public function removeDuplicatePropertyValues($ret, $duplicates)
658
    {
659
        $propertyValues = array();
660
661
        foreach ($ret as $prop) {
662
            foreach ($prop->getValues() as $value) {
663
                $label = $value->getLabel();
664
                $propertyValues[(method_exists($label, 'getValue')) ? $label->getValue() : $label][] = $value->getType();
665
            }
666
        }
667
668
        foreach ($propertyValues as $value => $propnames) {
669
            // if there are multiple properties with the same string value.
670
            if (count($propnames) > 1) {
671
                foreach ($propnames as $property) {
672
                    // if there is a more accurate property delete the more generic one.
673
                    if (isset($duplicates[$property])) {
674
                        unset($ret[$property]);
675
                    }
676
                }
677
678
            }
679
        }
680
        // handled separately: remove duplicate skos:prefLabel value (#854)
681
        if (isset($duplicates["skos:prefLabel"])) {
682
            unset($ret[$duplicates["skos:prefLabel"]]);
683
        }
684
        return $ret;
685
    }
686
687
    public function removeNotationFromProperties($ret, $isShowNotationAsPropertySet = true)
688
    {
689
        if (!$isShowNotationAsPropertySet) {
690
            unset($ret["skos:notation"]);
691
        }
692
        return $ret;
693
    }
694
695
    /**
696
     * @param $lang UI language
0 ignored issues
show
Bug introduced by
The type UI was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
697
     * @return String|null the translated label of skos:prefLabel subproperty, or null if not available
698
     */
699
    public function getPreferredSubpropertyLabelTranslation($lang) {
700
        $prefLabelProp = $this->graph->resource("skos:prefLabel");
701
        $subPrefLabelProps = $this->graph->resourcesMatching('rdfs:subPropertyOf', $prefLabelProp);
702
        foreach ($subPrefLabelProps as $subPrefLabelProp) {
703
            // return the first available translation
704
            if ($subPrefLabelProp->label($lang)) return $subPrefLabelProp->label($lang);
705
        }
706
        return null;
707
    }
708
709
    /**
710
     * @return DateTime|null the modified date, or null if not available
711
     */
712
    public function getModifiedDate()
713
    {
714
        // finding the modified properties
715
        /** @var \EasyRdf\Resource|\EasyRdf\Literal|null $modifiedResource */
716
        $modifiedResource = $this->resource->get('dc:modified');
717
        if ($modifiedResource && $modifiedResource instanceof \EasyRdf\Literal\Date) {
718
            return $modifiedResource->getValue();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $modifiedResource->getValue() returns the type string which is incompatible with the documented return type DateTime|null.
Loading history...
719
        }
720
721
        // if the concept does not have a modified date, we look for it in its
722
        // vocabulary
723
        return $this->getVocab()->getModifiedDate();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getVocab()->getModifiedDate() also could return the type string which is incompatible with the documented return type DateTime|null.
Loading history...
724
    }
725
726
    /**
727
     * Gets the creation date and modification date if available.
728
     * @return String containing the date information in a human readable format.
729
     */
730
    public function getDate()
731
    {
732
        $ret = '';
733
        $created = '';
734
        try {
735
            // finding the created properties
736
            if ($this->resource->get('dc:created')) {
737
                $created = $this->resource->get('dc:created')->getValue();
738
            }
739
740
            $modified = $this->getModifiedDate();
741
742
            // making a human readable string from the timestamps
743
            if ($created != '') {
744
                $ret = gettext('skosmos:created') . ' ' . (Punic\Calendar::formatDate($created, 'short', $this->getEnvLang()));
745
            }
746
747
            if ($modified != '') {
748
                if ($created != '') {
749
                    $ret .= ', ' . gettext('skosmos:modified') . ' ' . (Punic\Calendar::formatDate($modified, 'short', $this->getEnvLang()));
750
                } else {
751
                    $ret .= ' ' . ucfirst(gettext('skosmos:modified')) . ' ' . (Punic\Calendar::formatDate($modified, 'short', $this->getEnvLang()));
752
                }
753
754
            }
755
        } catch (Exception $e) {
756
            trigger_error($e->getMessage(), E_USER_WARNING);
757
            $ret = '';
758
            if ($this->resource->get('dc:modified')) {
759
                $modified = (string) $this->resource->get('dc:modified');
760
                $ret = gettext('skosmos:modified') . ' ' . $modified;
761
            }
762
            if ($this->resource->get('dc:created')) {
763
                $created .= (string) $this->resource->get('dc:created');
764
                $ret .= ' ' . gettext('skosmos:created') . ' ' . $created;
765
            }
766
        }
767
        return $ret;
768
    }
769
770
    /**
771
     * Gets the members of a specific collection.
772
     * @param $coll
773
     * @param array containing all narrowers as EasyRdf\Resource
0 ignored issues
show
Bug introduced by
The type containing was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
774
     * @return array containing ConceptPropertyValue objects
775
     */
776
    private function getCollectionMembers($coll, $narrowers)
777
    {
778
        $membersArray = array();
779
        if ($coll->label()) {
780
            $collLabel = $coll->label()->getValue($this->clang) ? $coll->label($this->clang) : $coll->label();
781
            if ($collLabel) {
782
                $collLabel = $collLabel->getValue();
783
            }
784
        } else {
785
            $collLabel = $coll->getUri();
786
        }
787
        $membersArray[$collLabel] = new ConceptPropertyValue($this->model, $this->vocab, $coll, 'skos:narrower', $this->clang);
788
        foreach ($coll->allResources('skos:member') as $member) {
789
            if (array_key_exists($member->getUri(), $narrowers)) {
790
                $narrower = $narrowers[$member->getUri()];
791
                if (isset($narrower)) {
792
                    $membersArray[$collLabel]->addSubMember(new ConceptPropertyValue($this->model, $this->vocab, $narrower, 'skos:member', $this->clang), $this->clang);
793
                }
794
795
            }
796
        }
797
798
        return $membersArray;
799
    }
800
801
    /**
802
     * Gets the groups the concept belongs to.
803
     */
804
    public function getGroupProperties()
805
    {
806
        return $this->getCollections(false);
807
    }
808
809
    /**
810
     * Gets the groups/arrays the concept belongs to.
811
     */
812
    private function getCollections($includeArrays) {
813
        $groups = array();
814
        $collections = $this->graph->resourcesMatching('skos:member', $this->resource);
815
        if (isset($collections)) {
816
            $arrayClassURI = $this->vocab !== null ? $this->vocab->getConfig()->getArrayClassURI() : null;
817
            $arrayClass = $arrayClassURI !== null ? EasyRdf\RdfNamespace::shorten($arrayClassURI) : null;
818
            $superGroups = $this->resource->all('isothes:superGroup');
819
            $superGroupUris = array_map(function($obj) { return $obj->getUri(); }, $superGroups);
820
            foreach ($collections as $collection) {
821
                if (in_array($arrayClass, $collection->types()) === $includeArrays) {
822
                    // not adding the memberOf if the reverse resource is already covered by isothes:superGroup see issue #433
823
                    if (in_array($collection->getUri(), $superGroupUris)) {
824
                        continue;
825
                    }
826
                    $property = in_array($arrayClass, $collection->types()) ? "skosmos:memberOfArray" : "skosmos:memberOf";
827
                    $collLabel = $collection->label($this->clang) ? $collection->label($this->clang) : $collection->label();
828
                    if ($collLabel) {
829
                        $collLabel = $collLabel->getValue();
830
                    }
831
832
                    $group = new ConceptPropertyValue($this->model, $this->vocab, $collection, $property, $this->clang);
833
                    $groups[$collLabel] = array($group);
834
835
                    $res = $collection;
836
                    while($super = $this->graph->resourcesMatching('skos:member', $res)) {
837
                        foreach ($super as $res) {
838
                            $superprop = new ConceptPropertyValue($this->model, $this->vocab, $res, 'skosmos:memberOfSuper', $this->clang);
839
                            array_unshift($groups[$collLabel], $superprop);
840
                        }
841
                    }
842
                }
843
            }
844
        }
845
        uksort($groups, 'strcoll');
846
        return $groups;
847
    }
848
849
    public function getArrayProperties() {
850
        return $this->getCollections(true);
851
    }
852
853
    /**
854
     * Given a language code, gets its name in UI language via Punic or alternatively
855
     * tries to search for a gettext translation.
856
     * @param string $langCode
857
     * @return string e.g. 'English'
858
     */
859
    private function langToString($langCode) {
860
        // using empty string as the language name when there is no langcode set
861
        $langName = '';
862
        if (!empty($langCode)) {
863
            $langName = Punic\Language::getName($langCode, $this->getEnvLang()) !== $langCode ? Punic\Language::getName($langCode, $this->getEnvLang()) : gettext($langCode);
864
        }
865
        return $langName;
866
    }
867
868
    /**
869
     * Gets the values of a property in all other languages than in the current language
870
     * and places them in a [langCode][userDefinedKey] array.
871
     * @param string $prop The property for which the values are looked upon to
872
     * @param string $key User-defined key for accessing the values
873
     * @return array LangCode-based multidimensional array ([string][string][ConceptPropertyValueLiteral]) or empty array if no values
874
     */
875
    private function getForeignLabelList($prop, $key) {
876
        $ret = array();
877
        $labels = $this->resource->allLiterals($prop);
878
879
        foreach ($labels as $lit) {
880
            // filtering away subsets of the current language eg. en vs en-GB
881
            if ($lit->getLang() != $this->clang && strpos($lit->getLang(), $this->getEnvLang() . '-') !== 0) {
882
                $langCode = $lit->getLang() ? $lit->getLang() : '';
883
                $ret[$langCode][$key][] = new ConceptPropertyValueLiteral($this->model, $this->vocab, $this->resource, $lit, $prop);
884
            }
885
        }
886
        return $ret;
887
    }
888
889
    /**
890
     * Gets the values of skos:prefLabel and skos:altLabel in all other languages than in the current language.
891
     * @return array Language-based multidimensional sorted array ([string][string][ConceptPropertyValueLiteral]) or empty array if no values
892
     */
893
    public function getForeignLabels()
894
    {
895
        $prefLabels = $this->getForeignLabelList('skos:prefLabel', 'prefLabel');
896
        $altLabels = $this->getForeignLabelList('skos:altLabel', 'altLabel');
897
        $ret = array_merge_recursive($prefLabels, $altLabels);
898
899
        $langArray = array_keys($ret);
900
        foreach ($langArray as $lang) {
901
            $coll = collator_create($lang);
902
            if (isset($ret[$lang]['prefLabel']))
903
            {
904
                $coll->sort($ret[$lang]['prefLabel'], Collator::SORT_STRING);
905
            }
906
            if (isset($ret[$lang]['altLabel']))
907
            {
908
                $coll->sort($ret[$lang]['altLabel'], Collator::SORT_STRING);
909
            }
910
            if ($lang !== '') {
911
                $ret[$this->langToString($lang)] = $ret[$lang];
912
                unset($ret[$lang]);
913
            }
914
        }
915
        uksort($ret, 'strcoll');
916
        return $ret;
917
    }
918
919
    /**
920
     * Gets the values for the property in question in all other languages than the ui language.
921
     * @param string $property
922
     * @return array array of labels for the values of the given property
923
     */
924
    public function getAllLabels($property)
925
    {
926
        $labels = array();
927
        // shortening property labels if possible, EasyRdf requires full URIs to be in angle brackets
928
        $property = (EasyRdf\RdfNamespace::shorten($property) !== null) ? EasyRdf\RdfNamespace::shorten($property) : "<$property>";
0 ignored issues
show
introduced by
The condition EasyRdf\RdfNamespace::shorten($property) !== null is always true.
Loading history...
929
        foreach ($this->resource->allLiterals($property) as $lit) {
930
            $labels[Punic\Language::getName($lit->getLang(), $this->getEnvLang())][] = new ConceptPropertyValueLiteral($this->model, $this->vocab, $this->resource, $lit, $property);
931
        }
932
        ksort($labels);
933
        return $labels;
934
    }
935
936
    /**
937
     * Dump concept graph as JSON-LD.
938
     */
939
    public function dumpJsonLd() {
940
        $context = array(
941
            'skos' => EasyRdf\RdfNamespace::get("skos"),
942
            'isothes' => EasyRdf\RdfNamespace::get("isothes"),
943
            'rdfs' => EasyRdf\RdfNamespace::get("rdfs"),
944
            'owl' =>EasyRdf\RdfNamespace::get("owl"),
945
            'dct' => EasyRdf\RdfNamespace::get("dcterms"),
946
            'dc11' => EasyRdf\RdfNamespace::get("dc11"),
947
            'uri' => '@id',
948
            'type' => '@type',
949
            'lang' => '@language',
950
            'value' => '@value',
951
            'graph' => '@graph',
952
            'label' => 'rdfs:label',
953
            'prefLabel' => 'skos:prefLabel',
954
            'altLabel' => 'skos:altLabel',
955
            'hiddenLabel' => 'skos:hiddenLabel',
956
            'broader' => 'skos:broader',
957
            'narrower' => 'skos:narrower',
958
            'related' => 'skos:related',
959
            'inScheme' => 'skos:inScheme',
960
            'schema' => EasyRdf\RdfNamespace::get("schema"),
961
            'wd' => EasyRdf\RdfNamespace::get("wd"),
962
            'wdt' => EasyRdf\RdfNamespace::get("wdt"),
963
        );
964
        $vocabPrefix = preg_replace('/\W+/', '', $this->vocab->getId()); // strip non-word characters
965
        $vocabUriSpace = $this->vocab->getUriSpace();
966
967
        if (!in_array($vocabUriSpace, $context, true)) {
968
            if (!isset($context[$vocabPrefix])) {
969
                $context[$vocabPrefix] = $vocabUriSpace;
970
            }
971
            else if ($context[$vocabPrefix] !== $vocabUriSpace) {
972
                $i = 2;
973
                while (isset($context[$vocabPrefix . $i]) && $context[$vocabPrefix . $i] !== $vocabUriSpace) {
974
                    $i += 1;
975
                }
976
                $context[$vocabPrefix . $i] = $vocabUriSpace;
977
            }
978
        }
979
        $compactJsonLD = \ML\JsonLD\JsonLD::compact($this->graph->serialise('jsonld'), json_encode($context));
980
        return \ML\JsonLD\JsonLD::toString($compactJsonLD);
981
    }
982
983
    public function isUseModifiedDate()
984
    {
985
        return $this->getVocab()->isUseModifiedDate();
986
    }
987
}
988