Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

MetadataController::parseCollections()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
c 0
b 0
f 0
nc 3
nop 3
dl 0
loc 8
rs 10
1
<?php
2
/**
3
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
4
 *
5
 * This file is part of the Kitodo and TYPO3 projects.
6
 *
7
 * @license GNU General Public License version 3 or later.
8
 * For the full copyright and license information, please read the
9
 * LICENSE.txt file that was distributed with this source code.
10
 */
11
12
namespace Kitodo\Dlf\Controller;
13
14
use Kitodo\Dlf\Common\AbstractDocument;
15
use Kitodo\Dlf\Common\Helper;
16
use Kitodo\Dlf\Common\IiifManifest;
17
use Kitodo\Dlf\Domain\Repository\CollectionRepository;
18
use Kitodo\Dlf\Domain\Repository\MetadataRepository;
19
use Kitodo\Dlf\Domain\Repository\StructureRepository;
20
use Psr\Http\Message\ResponseInterface;
21
use Ubl\Iiif\Context\IRI;
22
23
/**
24
 * Controller class for the plugin 'Metadata'.
25
 *
26
 * @package TYPO3
27
 * @subpackage dlf
28
 *
29
 * @access public
30
 */
31
class MetadataController extends AbstractController
32
{
33
    /**
34
     * @access private
35
     * @var AbstractDocument
36
     */
37
    private $currentDocument;
38
39
    /**
40
     * @access private
41
     * @var bool
42
     */
43
    private $useOriginalIiifManifestMetadata;
44
45
    /**
46
     * @access protected
47
     * @var CollectionRepository
48
     */
49
    protected CollectionRepository $collectionRepository;
50
51
    /**
52
     * @access public
53
     *
54
     * @param CollectionRepository $collectionRepository
55
     *
56
     * @return void
57
     */
58
    public function injectCollectionRepository(CollectionRepository $collectionRepository): void
59
    {
60
        $this->collectionRepository = $collectionRepository;
61
    }
62
63
    /**
64
     * @access protected
65
     * @var MetadataRepository
66
     */
67
    protected MetadataRepository $metadataRepository;
68
69
    /**
70
     * @access public
71
     *
72
     * @param MetadataRepository $metadataRepository
73
     *
74
     * @return void
75
     */
76
    public function injectMetadataRepository(MetadataRepository $metadataRepository): void
77
    {
78
        $this->metadataRepository = $metadataRepository;
79
    }
80
81
    /**
82
     * @access protected
83
     * @var StructureRepository
84
     */
85
    protected StructureRepository $structureRepository;
86
87
    /**
88
     * @access public
89
     *
90
     * @param StructureRepository $structureRepository
91
     *
92
     * @return void
93
     */
94
    public function injectStructureRepository(StructureRepository $structureRepository): void
95
    {
96
        $this->structureRepository = $structureRepository;
97
    }
98
99
    /**
100
     * @access public
101
     *
102
     * @return ResponseInterface the response
103
     */
104
    public function mainAction(): ResponseInterface
105
    {
106
        // Load current document.
107
        $this->loadDocument();
108
        if ($this->isDocMissing()) {
109
            // Quit without doing anything if required variables are not set.
110
            return $this->htmlResponse();
111
        }
112
113
        $this->setPage();
114
115
        $this->currentDocument = $this->document->getCurrentDocument();
116
        $this->useOriginalIiifManifestMetadata = $this->settings['originalIiifMetadata'] == 1 && $this->currentDocument instanceof IiifManifest;
117
118
        $metadata = $this->getMetadata();
119
        $topLevelId = $this->currentDocument->toplevelId;
120
        // Get toplevel metadata?
121
        if (!$metadata || ($this->settings['rootline'] == 1 && $metadata[0]['_id'] != $topLevelId)) {
122
            $data = [];
123
            if ($this->useOriginalIiifManifestMetadata) {
124
                // @phpstan-ignore-next-line
125
                $data = $this->currentDocument->getManifestMetadata($topLevelId, $this->settings['storagePid']);
0 ignored issues
show
Bug introduced by
The method getManifestMetadata() does not exist on Kitodo\Dlf\Common\AbstractDocument. It seems like you code against a sub-type of Kitodo\Dlf\Common\AbstractDocument such as Kitodo\Dlf\Common\IiifManifest. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

125
                /** @scrutinizer ignore-call */ 
126
                $data = $this->currentDocument->getManifestMetadata($topLevelId, $this->settings['storagePid']);
Loading history...
126
            } else {
127
                $data = $this->currentDocument->getToplevelMetadata($this->settings['storagePid']);
128
            }
129
            $data['_id'] = $topLevelId;
130
            array_unshift($metadata, $data);
131
        }
132
133
        if (empty(array_filter($metadata))) {
134
            $this->logger->warning('No metadata found for document with UID ' . $this->document->getUid());
135
            return $this->htmlResponse();
136
        }
137
        ksort($metadata);
138
139
        $this->printMetadata($metadata);
140
141
        return $this->htmlResponse();
142
    }
143
144
    /**
145
     * Prepares the metadata array for output
146
     *
147
     * @access protected
148
     *
149
     * @param array $metadata The metadata array
150
     *
151
     * @return void
152
     */
153
    protected function printMetadata(array $metadata): void
154
    {
155
        if ($this->useOriginalIiifManifestMetadata) {
156
            $this->view->assign('useIiif', true);
157
            $this->view->assign('iiifData', $this->buildIiifData($metadata));
158
        } else {
159
            // findBySettings also sorts entries by the `sorting` field
160
            $metadataResult = $this->metadataRepository->findBySettings(
161
                [
162
                    'is_listed' => !$this->settings['showFull'],
163
                ]
164
            );
165
166
            foreach ($metadata as $i => $section) {
167
168
                foreach ($section as $name => $value) {
169
                    // NOTE: Labels are to be escaped in Fluid template
170
171
                    $metadata[$i][$name] = is_array($value)
172
                        ? $value
173
                        : explode($this->settings['separator'], $value);
174
175
                    // PHPStan error
176
                    // I don't understand what this code does, so I take it away until author can fix it
177
                    /*if ($metadata[$i][$name][0] === 'Array') {
178
                        $metadata[$i][$name] = [];
179
                        foreach ($value as $subKey => $subValue) {
180
                            $metadata[$i][$name][$subKey] = $subValue;
181
                        }
182
                    }*/
183
184
                    $this->parseMetadata($i, $name, $value, $metadata);
185
186
                    if (is_array($metadata[$i][$name])) {
187
                        $metadata[$i][$name] = array_values(array_filter($metadata[$i][$name], function ($metadataValue) {
188
                            return !empty($metadataValue);
189
                        }));
190
                    }
191
                }
192
            }
193
194
            $this->view->assign('buildUrl', $this->buildUrlFromMetadata($metadata));
195
            $this->view->assign('externalUrl', $this->buildExternalUrlFromMetadata($metadata));
196
            $this->view->assign('documentMetadataSections', $metadata);
197
            $this->view->assign('configMetadata', $metadataResult);
198
            $this->view->assign('separator', $this->settings['separator']);
199
            $this->view->assign('metaConfigObjectData', $this->buildMetaConfigObjectData($metadata));
200
        }
201
    }
202
203
    /**
204
     * Builds the IIIF data array from metadata array
205
     *
206
     * @access private
207
     *
208
     * @param array $metadata The metadata array
209
     *
210
     * @return array The IIIF data array ready for output
211
     */
212
    private function buildIiifData(array $metadata): array
213
    {
214
        $iiifData = [];
215
216
        foreach ($metadata as $row) {
217
            foreach ($row as $key => $group) {
218
                if ($key == '_id') {
219
                    continue;
220
                }
221
222
                if (!is_array($group)) {
223
                    $iiifData[$key] = $this->buildIiifDataGroup($key, $group);
224
                } else {
225
                    foreach ($group as $label => $value) {
226
                        if ($label == '_id') {
227
                            continue;
228
                        }
229
                        if (is_array($value)) {
230
                            $value = implode($this->settings['separator'], $value);
231
                        }
232
233
                        $iiifData[$key]['data'][] = $this->buildIiifDataGroup($label, $value);
234
                    }
235
                }
236
            }
237
        }
238
239
        return $iiifData;
240
    }
241
242
    /**
243
     * Builds the IIIF data array from label and value
244
     *
245
     * @access private
246
     *
247
     * @param string $label The label string
248
     * @param string $value The value string
249
     *
250
     * @return array The IIIF data array ready for output
251
     */
252
    private function buildIiifDataGroup(string $label, string $value): array
253
    {
254
        // NOTE: Labels are to be escaped in Fluid template
255
        $scheme = (new IRI($value))->getScheme();
256
        if (IRI::isAbsoluteIri($value) && ($scheme == 'http' || $scheme == 'https')) {
257
            //TODO: should really label be converted to empty string if equal to value?
258
            $label = $value == $label ? '' : $label;
259
            $buildUrl = true;
260
        } else {
261
            $buildUrl = false;
262
        }
263
264
        return [
265
            'label' => $label,
266
            'value' => $value,
267
            'buildUrl' => $buildUrl,
268
        ];
269
    }
270
271
    /**
272
     * Collects raw metadata into an array that will be passed as data to cObj.
273
     * This lets metadata wraps reference (own or foreign) values via TypoScript "field".
274
     *
275
     * @access private
276
     *
277
     * @param array $metadata The metadata array
278
     *
279
     * @return array The raw metadata array ready for output
280
     */
281
    private function buildMetaConfigObjectData(array $metadata): array
282
    {
283
        $metaConfigObjectData = [];
284
285
        foreach ($metadata as $i => $section) {
286
            $metaConfigObjectData[$i] = [];
287
288
            foreach ($section as $name => $value) {
289
                $metaConfigObjectData[$i][$name] = is_array($value)
290
                    ? implode($this->settings['separator'], $value)
291
                    : $value;
292
            }
293
        }
294
295
        return $metaConfigObjectData;
296
    }
297
298
    /**
299
     * Builds URLs array for given metadata array.
300
     *
301
     * @access private
302
     *
303
     * @param array $metadata The metadata array
304
     *
305
     * @return array URLs
306
     */
307
    private function buildUrlFromMetadata(array $metadata): array
308
    {
309
        $buildUrl = [];
310
311
        foreach ($metadata as $i => $section) {
312
            if ($this->settings['linkTitle'] && $section['_id'] && isset($section['title']) && !empty($section['title'])) {
313
                $details = $this->currentDocument->getLogicalStructure($section['_id'][0]);
314
                $buildUrl[$i]['title'] = [
315
                    'id' => $this->document->getUid(),
0 ignored issues
show
Bug introduced by
The method getUid() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

315
                    'id' => $this->document->/** @scrutinizer ignore-call */ getUid(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
316
                    'page' => (!empty($details['points']) ? (int) $details['points'] : 1),
317
                    'targetPid' => (!empty($this->settings['targetPid']) ? $this->settings['targetPid'] : 0),
318
                ];
319
            }
320
        }
321
322
        return $buildUrl;
323
    }
324
325
    /**
326
     * Builds external URLs array for given metadata array.
327
     *
328
     * @access private
329
     *
330
     * @param array $metadata The metadata array
331
     *
332
     * @return array external URLs
333
     */
334
    private function buildExternalUrlFromMetadata(array $metadata): array
335
    {
336
        $externalUrl = [];
337
338
        foreach ($metadata as $i => $section) {
339
            foreach ($section as $name => $value) {
340
                if (($name == 'author' || $name == 'holder') && !empty($value) && !empty($value[0]['url'])) {
341
                    $externalUrl[$i][$name]['externalUrl'] = $value[0];
342
                } elseif (($name == 'geonames' || $name == 'wikidata' || $name == 'wikipedia') && !empty($value)) {
343
                    $externalUrl[$i][$name]['externalUrl'] = [
344
                        'name' => $value[0],
345
                        'url' => $value[0]
346
                    ];
347
                }
348
            }
349
        }
350
351
        return $externalUrl;
352
    }
353
354
    /**
355
     * Parses metadata.
356
     *
357
     * @access private
358
     *
359
     * @param int $i The index of metadata array
360
     * @param string $name The name of section in metadata array
361
     * @param mixed $value The value of section in metadata array
362
     * @param array $metadata The metadata array passed as reference
363
     *
364
     * @return void
365
     */
366
    private function parseMetadata(int $i, string $name, $value, array &$metadata) : void
367
    {
368
        if ($name == 'title') {
369
            // Get title of parent document if needed.
370
            $this->parseParentTitle($i, $value, $metadata);
371
        } elseif ($name == 'owner' && empty($value)) {
372
            // no owner is found by metadata records --> take the one associated to the document
373
            $this->parseOwner($i, $metadata);
374
        } elseif ($name == 'type' && !empty($value)) {
375
            // Translate document type.
376
            $this->parseType($i, $metadata);
377
        } elseif ($name == 'collection' && !empty($value)) {
378
            // Check if collections isn't hidden.
379
            $this->parseCollections($i, $value, $metadata);
380
        } elseif ($name == 'language' && !empty($value)) {
381
            // Translate ISO 639 language code.
382
            foreach ($metadata[$i][$name] as &$langValue) {
383
                $langValue = Helper::getLanguageName($langValue);
384
            }
385
        }
386
    }
387
388
    /**
389
     * Parse title of parent document if needed.
390
     *
391
     * @access private
392
     *
393
     * @param int $i The index of metadata array
394
     * @param mixed $value The value of section in metadata array
395
     * @param array $metadata The metadata array passed as reference
396
     *
397
     * @return void
398
     */
399
    private function parseParentTitle(int $i, $value, array &$metadata) : void
400
    {
401
        if (empty(implode('', $value)) && $this->settings['getTitle'] && $this->document->getPartof()) {
402
            $superiorTitle = AbstractDocument::getTitle($this->document->getPartof(), true);
403
            if (!empty($superiorTitle)) {
404
                $metadata[$i]['title'] = ['[' . $superiorTitle . ']'];
405
            }
406
        }
407
    }
408
409
    /**
410
     * Parse owner if no owner is found by metadata records. Take the one associated to the document.
411
     *
412
     * @access private
413
     *
414
     * @param int $i The index of metadata array
415
     * @param array $metadata The metadata array passed as reference
416
     *
417
     * @return void
418
     */
419
    private function parseOwner(int $i, array &$metadata) : void
420
    {
421
        $library = $this->document->getOwner();
422
        if ($library) {
0 ignored issues
show
introduced by
$library is of type Kitodo\Dlf\Domain\Model\Library, thus it always evaluated to true.
Loading history...
423
            $metadata[$i]['owner'][0] = $library->getLabel();
424
        }
425
    }
426
427
    /**
428
     * Parse type - translate document type.
429
     *
430
     * @access private
431
     *
432
     * @param int $i The index of metadata array
433
     * @param array $metadata The metadata array passed as reference
434
     *
435
     * @return void
436
     */
437
    private function parseType(int $i, array &$metadata) : void
438
    {
439
        $structure = $this->structureRepository->findOneByIndexName($metadata[$i]['type'][0]);
0 ignored issues
show
Bug introduced by
The method findOneByIndexName() does not exist on Kitodo\Dlf\Domain\Repository\StructureRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

439
        /** @scrutinizer ignore-call */ 
440
        $structure = $this->structureRepository->findOneByIndexName($metadata[$i]['type'][0]);
Loading history...
440
        if ($structure) {
441
            $metadata[$i]['type'][0] = $structure->getLabel();
0 ignored issues
show
Bug introduced by
The method getLabel() does not exist on TYPO3\CMS\Extbase\Persistence\QueryResultInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

441
            /** @scrutinizer ignore-call */ 
442
            $metadata[$i]['type'][0] = $structure->getLabel();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
442
        }
443
    }
444
445
    /**
446
     * Parse collections - check if collections isn't hidden.
447
     *
448
     * @access private
449
     *
450
     * @param int $i The index of metadata array
451
     * @param mixed $value The value of section in metadata array
452
     * @param array $metadata The metadata array passed as reference
453
     *
454
     * @return void
455
     */
456
    private function parseCollections(int $i, $value, array &$metadata) : void
457
    {
458
        $j = 0;
459
        foreach ($value as $entry) {
460
            $collection = $this->collectionRepository->findOneByIndexName($entry);
0 ignored issues
show
Bug introduced by
The method findOneByIndexName() does not exist on Kitodo\Dlf\Domain\Repository\CollectionRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

460
            /** @scrutinizer ignore-call */ 
461
            $collection = $this->collectionRepository->findOneByIndexName($entry);
Loading history...
461
            if ($collection) {
462
                $metadata[$i]['collection'][$j] = $collection->getLabel() ? : '';
463
                $j++;
464
            }
465
        }
466
    }
467
468
    /**
469
     * Get metadata for given id array.
470
     *
471
     * @access private
472
     *
473
     * @return array metadata
474
     */
475
    private function getMetadata(): array
476
    {
477
        $metadata = [];
478
        if ($this->settings['rootline'] < 2) {
479
            // Get current structure's @ID.
480
            $ids = [];
481
            if (isset($this->requestData['page'])) {
482
                $page = $this->currentDocument->physicalStructure[$this->requestData['page']];
483
                if (!empty($page) && !empty($this->currentDocument->smLinks['p2l'][$page])) {
484
                    foreach ($this->currentDocument->smLinks['p2l'][$page] as $logId) {
485
                        $count = $this->currentDocument->getStructureDepth($logId);
486
                        $ids[$count][] = $logId;
487
                    }
488
                }
489
            }
490
            ksort($ids);
491
            reset($ids);
492
            // Check if we should display all metadata up to the root.
493
            if ($this->settings['rootline'] == 1) {
494
                foreach ($ids as $id) {
495
                    $metadata = $this->getMetadataForIds($id, $metadata);
496
                }
497
            } else {
498
                $id = array_pop($ids);
499
                if (is_array($id)) {
500
                    $metadata = $this->getMetadataForIds($id, $metadata);
501
                }
502
            }
503
        }
504
        return $metadata;
505
    }
506
507
    /**
508
     * Get metadata for given id array.
509
     *
510
     * @access private
511
     *
512
     * @param array $id An array with ids
513
     * @param array $metadata An array with metadata
514
     *
515
     * @return array metadata
516
     */
517
    private function getMetadataForIds(array $id, array $metadata): array
518
    {
519
        foreach ($id as $sid) {
520
            if ($this->useOriginalIiifManifestMetadata) {
521
                // @phpstan-ignore-next-line
522
                $data = $this->currentDocument->getManifestMetadata($sid, $this->settings['storagePid']);
523
            } else {
524
                $data = $this->currentDocument->getMetadata($sid, $this->settings['storagePid']);
525
            }
526
            if (!empty($data)) {
527
                $data['_id'] = $sid;
528
                $metadata[] = $data;
529
            }
530
        }
531
        return $metadata;
532
    }
533
}
534