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.
Passed
Pull Request — master (#835)
by
unknown
04:00
created

MetsDocument::getFileMimeType()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 11
rs 10
cc 3
nc 2
nop 1
1
<?php
2
3
/**
4
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
5
 *
6
 * This file is part of the Kitodo and TYPO3 projects.
7
 *
8
 * @license GNU General Public License version 3 or later.
9
 * For the full copyright and license information, please read the
10
 * LICENSE.txt file that was distributed with this source code.
11
 */
12
13
namespace Kitodo\Dlf\Common;
14
15
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
16
use TYPO3\CMS\Core\Database\ConnectionPool;
17
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
18
use TYPO3\CMS\Core\Utility\GeneralUtility;
19
use Ubl\Iiif\Tools\IiifHelper;
20
use Ubl\Iiif\Services\AbstractImageService;
21
use TYPO3\CMS\Core\Log\LogManager;
22
23
/**
24
 * MetsDocument class for the 'dlf' extension.
25
 *
26
 * @author Sebastian Meyer <[email protected]>
27
 * @author Henrik Lochmann <[email protected]>
28
 * @package TYPO3
29
 * @subpackage dlf
30
 * @access public
31
 * @property int $cPid This holds the PID for the configuration
32
 * @property-read array $dmdSec This holds the XML file's dmdSec parts with their IDs as array key
33
 * @property-read array $fileGrps This holds the file ID -> USE concordance
34
 * @property-read bool $hasFulltext Are there any fulltext files available?
35
 * @property-read array $metadataArray This holds the documents' parsed metadata array
36
 * @property-read \SimpleXMLElement $mets This holds the XML file's METS part as \SimpleXMLElement object
37
 * @property-read int $numPages The holds the total number of pages
38
 * @property-read int $parentId This holds the UID of the parent document or zero if not multi-volumed
39
 * @property-read array $physicalStructure This holds the physical structure
40
 * @property-read array $physicalStructureInfo This holds the physical structure metadata
41
 * @property-read int $pid This holds the PID of the document or zero if not in database
42
 * @property-read bool $ready Is the document instantiated successfully?
43
 * @property-read string $recordId The METS file's / IIIF manifest's record identifier
44
 * @property-read int $rootId This holds the UID of the root document or zero if not multi-volumed
45
 * @property-read array $smLinks This holds the smLinks between logical and physical structMap
46
 * @property-read array $tableOfContents This holds the logical structure
47
 * @property-read string $thumbnail This holds the document's thumbnail location
48
 * @property-read string $toplevelId This holds the toplevel structure's @ID (METS) or the manifest's @id (IIIF)
49
 */
50
final class MetsDocument extends Doc
51
{
52
    /**
53
     * This holds the whole XML file as string for serialization purposes
54
     * @see __sleep() / __wakeup()
55
     *
56
     * @var string
57
     * @access protected
58
     */
59
    protected $asXML = '';
60
61
    /**
62
     * This holds the XML file's dmdSec parts with their IDs as array key
63
     *
64
     * @var array
65
     * @access protected
66
     */
67
    protected $dmdSec = [];
68
69
    /**
70
     * Are the METS file's dmdSecs loaded?
71
     * @see $dmdSec
72
     *
73
     * @var bool
74
     * @access protected
75
     */
76
    protected $dmdSecLoaded = false;
77
78
    /**
79
     * The extension key
80
     *
81
     * @var	string
82
     * @access public
83
     */
84
    public static $extKey = 'dlf';
85
86
    /**
87
     * This holds the file ID -> USE concordance
88
     * @see _getFileGrps()
89
     *
90
     * @var array
91
     * @access protected
92
     */
93
    protected $fileGrps = [];
94
95
    /**
96
     * Are the image file groups loaded?
97
     * @see $fileGrps
98
     *
99
     * @var bool
100
     * @access protected
101
     */
102
    protected $fileGrpsLoaded = false;
103
104
    /**
105
     * This holds the XML file's METS part as \SimpleXMLElement object
106
     *
107
     * @var \SimpleXMLElement
108
     * @access protected
109
     */
110
    protected $mets;
111
112
    /**
113
     * This holds the whole XML file as \SimpleXMLElement object
114
     *
115
     * @var \SimpleXMLElement
116
     * @access protected
117
     */
118
    protected $xml;
119
120
    /**
121
     * This adds metadata from METS structural map to metadata array.
122
     *
123
     * @access	public
124
     *
125
     * @param	array	&$metadata: The metadata array to extend
126
     * @param	string	$id: The "@ID" attribute of the logical structure node
127
     *
128
     * @return  void
129
     */
130
    public function addMetadataFromMets(&$metadata, $id)
131
    {
132
        $details = $this->getLogicalStructure($id);
133
        if (!empty($details)) {
134
            $metadata['mets_order'][0] = $details['order'];
135
            $metadata['mets_label'][0] = $details['label'];
136
            $metadata['mets_orderlabel'][0] = $details['orderlabel'];
137
        }
138
    }
139
140
    /**
141
     *
142
     * {@inheritDoc}
143
     * @see \Kitodo\Dlf\Common\Doc::establishRecordId()
144
     */
145
    protected function establishRecordId($pid)
146
    {
147
        // Check for METS object @ID.
148
        if (!empty($this->mets['OBJID'])) {
149
            $this->recordId = (string) $this->mets['OBJID'];
150
        }
151
        // Get hook objects.
152
        $hookObjects = Helper::getHookObjects('Classes/Common/MetsDocument.php');
153
        // Apply hooks.
154
        foreach ($hookObjects as $hookObj) {
155
            if (method_exists($hookObj, 'construct_postProcessRecordId')) {
156
                $hookObj->construct_postProcessRecordId($this->xml, $this->recordId);
157
            }
158
        }
159
    }
160
161
    /**
162
     *
163
     * {@inheritDoc}
164
     * @see \Kitodo\Dlf\Common\Doc::getDownloadLocation()
165
     */
166
    public function getDownloadLocation($id)
167
    {
168
        $fileMimeType = $this->getFileMimeType($id);
169
        $fileLocation = $this->getFileLocation($id);
170
        if ($fileMimeType === 'application/vnd.kitodo.iiif') {
171
            $fileLocation = (strrpos($fileLocation, 'info.json') === strlen($fileLocation) - 9) ? $fileLocation : (strrpos($fileLocation, '/') === strlen($fileLocation) ? $fileLocation . 'info.json' : $fileLocation . '/info.json');
172
            $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
173
            IiifHelper::setUrlReader(IiifUrlReader::getInstance());
174
            IiifHelper::setMaxThumbnailHeight($conf['iiifThumbnailHeight']);
175
            IiifHelper::setMaxThumbnailWidth($conf['iiifThumbnailWidth']);
176
            $service = IiifHelper::loadIiifResource($fileLocation);
177
            if ($service !== null && $service instanceof AbstractImageService) {
178
                return $service->getImageUrl();
179
            }
180
        } elseif ($fileMimeType === 'application/vnd.netfpx') {
181
            $baseURL = $fileLocation . (strpos($fileLocation, '?') === false ? '?' : '');
182
            // TODO CVT is an optional IIP server capability; in theory, capabilities should be determined in the object request with '&obj=IIP-server'
183
            return $baseURL . '&CVT=jpeg';
184
        }
185
        return $fileLocation;
186
    }
187
188
    /**
189
     * {@inheritDoc}
190
     * @see \Kitodo\Dlf\Common\Doc::getFileLocation()
191
     */
192
    public function getFileLocation($id)
193
    {
194
        $location = $this->mets->xpath('./mets:fileSec/mets:fileGrp/mets:file[@ID="' . $id . '"]/mets:FLocat[@LOCTYPE="URL"]');
195
        if (
196
            !empty($id)
197
            && !empty($location)
198
        ) {
199
            return (string) $location[0]->attributes('http://www.w3.org/1999/xlink')->href;
200
        } else {
201
            $this->logger->warning('There is no file node with @ID "' . $id . '"');
202
            return '';
203
        }
204
    }
205
206
    /**
207
     * {@inheritDoc}
208
     * @see \Kitodo\Dlf\Common\Doc::getFileMimeType()
209
     */
210
    public function getFileMimeType($id)
211
    {
212
        $mimetype = $this->mets->xpath('./mets:fileSec/mets:fileGrp/mets:file[@ID="' . $id . '"]/@MIMETYPE');
213
        if (
214
            !empty($id)
215
            && !empty($mimetype)
216
        ) {
217
            return (string) $mimetype[0];
218
        } else {
219
            $this->logger->warning('There is no file node with @ID "' . $id . '" or no MIME type specified');
220
            return '';
221
        }
222
    }
223
224
    /**
225
     * {@inheritDoc}
226
     * @see \Kitodo\Dlf\Common\Doc::getLogicalStructure()
227
     */
228
    public function getLogicalStructure($id, $recursive = false)
229
    {
230
        $details = [];
231
        // Is the requested logical unit already loaded?
232
        if (
233
            !$recursive
234
            && !empty($this->logicalUnits[$id])
235
        ) {
236
            // Yes. Return it.
237
            return $this->logicalUnits[$id];
238
        } elseif (!empty($id)) {
239
            // Get specified logical unit.
240
            $divs = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $id . '"]');
241
        } else {
242
            // Get all logical units at top level.
243
            $divs = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]/mets:div');
244
        }
245
        if (!empty($divs)) {
246
            if (!$recursive) {
247
                // Get the details for the first xpath hit.
248
                $details = $this->getLogicalStructureInfo($divs[0]);
249
            } else {
250
                // Walk the logical structure recursively and fill the whole table of contents.
251
                foreach ($divs as $div) {
252
                    $this->tableOfContents[] = $this->getLogicalStructureInfo($div, $recursive);
253
                }
254
            }
255
        }
256
        return $details;
257
    }
258
259
    /**
260
     * This gets details about a logical structure element
261
     *
262
     * @access protected
263
     *
264
     * @param \SimpleXMLElement $structure: The logical structure node
265
     * @param bool $recursive: Whether to include the child elements
266
     *
267
     * @return array Array of the element's id, label, type and physical page indexes/mptr link
268
     */
269
    protected function getLogicalStructureInfo(\SimpleXMLElement $structure, $recursive = false)
270
    {
271
        // Get attributes.
272
        foreach ($structure->attributes() as $attribute => $value) {
273
            $attributes[$attribute] = (string) $value;
274
        }
275
        // Load plugin configuration.
276
        $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
277
        // Extract identity information.
278
        $details = [];
279
        $details['id'] = $attributes['ID'];
280
        $details['dmdId'] = (isset($attributes['DMDID']) ? $attributes['DMDID'] : '');
281
        $details['order'] = (isset($attributes['ORDER']) ? $attributes['ORDER'] : '');
282
        $details['label'] = (isset($attributes['LABEL']) ? $attributes['LABEL'] : '');
283
        $details['orderlabel'] = (isset($attributes['ORDERLABEL']) ? $attributes['ORDERLABEL'] : '');
284
        $details['contentIds'] = (isset($attributes['CONTENTIDS']) ? $attributes['CONTENTIDS'] : '');
285
        $details['volume'] = '';
286
        // Set volume information only if no label is set and this is the toplevel structure element.
287
        if (
288
            empty($details['label'])
289
            && $details['id'] == $this->_getToplevelId()
290
        ) {
291
            $metadata = $this->getMetadata($details['id']);
292
            if (!empty($metadata['volume'][0])) {
293
                $details['volume'] = $metadata['volume'][0];
294
            }
295
        }
296
        $details['pagination'] = '';
297
        $details['type'] = $attributes['TYPE'];
298
        $details['thumbnailId'] = '';
299
        // Load smLinks.
300
        $this->_getSmLinks();
301
        // Load physical structure.
302
        $this->_getPhysicalStructure();
303
        // Get the physical page or external file this structure element is pointing at.
304
        $details['points'] = '';
305
        // Is there a mptr node?
306
        if (count($structure->children('http://www.loc.gov/METS/')->mptr)) {
307
            // Yes. Get the file reference.
308
            $details['points'] = (string) $structure->children('http://www.loc.gov/METS/')->mptr[0]->attributes('http://www.w3.org/1999/xlink')->href;
309
        } elseif (
310
            !empty($this->physicalStructure)
311
            && array_key_exists($details['id'], $this->smLinks['l2p'])
312
        ) {
313
            // Link logical structure to the first corresponding physical page/track.
314
            $details['points'] = max(intval(array_search($this->smLinks['l2p'][$details['id']][0], $this->physicalStructure, true)), 1);
315
            $fileGrpsThumb = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']);
316
            while ($fileGrpThumb = array_shift($fileGrpsThumb)) {
317
                if (!empty($this->physicalStructureInfo[$this->smLinks['l2p'][$details['id']][0]]['files'][$fileGrpThumb])) {
318
                    $details['thumbnailId'] = $this->physicalStructureInfo[$this->smLinks['l2p'][$details['id']][0]]['files'][$fileGrpThumb];
319
                    break;
320
                }
321
            }
322
            // Get page/track number of the first page/track related to this structure element.
323
            $details['pagination'] = $this->physicalStructureInfo[$this->smLinks['l2p'][$details['id']][0]]['orderlabel'];
324
        } elseif ($details['id'] == $this->_getToplevelId()) {
325
            // Point to self if this is the toplevel structure.
326
            $details['points'] = 1;
327
            $fileGrpsThumb = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']);
328
            while ($fileGrpThumb = array_shift($fileGrpsThumb)) {
329
                if (
330
                    !empty($this->physicalStructure)
331
                    && !empty($this->physicalStructureInfo[$this->physicalStructure[1]]['files'][$fileGrpThumb])
332
                ) {
333
                    $details['thumbnailId'] = $this->physicalStructureInfo[$this->physicalStructure[1]]['files'][$fileGrpThumb];
334
                    break;
335
                }
336
            }
337
        }
338
        // Get the files this structure element is pointing at.
339
        $details['files'] = [];
340
        $fileUse = $this->_getFileGrps();
341
        // Get the file representations from fileSec node.
342
        foreach ($structure->children('http://www.loc.gov/METS/')->fptr as $fptr) {
343
            // Check if file has valid @USE attribute.
344
            if (!empty($fileUse[(string) $fptr->attributes()->FILEID])) {
0 ignored issues
show
Bug introduced by
The method attributes() 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

344
            if (!empty($fileUse[(string) $fptr->/** @scrutinizer ignore-call */ attributes()->FILEID])) {

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...
345
                $details['files'][$fileUse[(string) $fptr->attributes()->FILEID]] = (string) $fptr->attributes()->FILEID;
346
            }
347
        }
348
        // Keep for later usage.
349
        $this->logicalUnits[$details['id']] = $details;
350
        // Walk the structure recursively? And are there any children of the current element?
351
        if (
352
            $recursive
353
            && count($structure->children('http://www.loc.gov/METS/')->div)
354
        ) {
355
            $details['children'] = [];
356
            foreach ($structure->children('http://www.loc.gov/METS/')->div as $child) {
357
                // Repeat for all children.
358
                $details['children'][] = $this->getLogicalStructureInfo($child, true);
0 ignored issues
show
Bug introduced by
It seems like $child can also be of type null; however, parameter $structure of Kitodo\Dlf\Common\MetsDo...tLogicalStructureInfo() does only seem to accept SimpleXMLElement, maybe add an additional type check? ( Ignorable by Annotation )

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

358
                $details['children'][] = $this->getLogicalStructureInfo(/** @scrutinizer ignore-type */ $child, true);
Loading history...
359
            }
360
        }
361
        return $details;
362
    }
363
364
    /**
365
     * {@inheritDoc}
366
     * @see \Kitodo\Dlf\Common\Doc::getMetadata()
367
     */
368
    public function getMetadata($id, $cPid = 0)
369
    {
370
        // Make sure $cPid is a non-negative integer.
371
        $cPid = max(intval($cPid), 0);
372
        // If $cPid is not given, try to get it elsewhere.
373
        if (
374
            !$cPid
375
            && ($this->cPid || $this->pid)
376
        ) {
377
            // Retain current PID.
378
            $cPid = ($this->cPid ? $this->cPid : $this->pid);
379
        } elseif (!$cPid) {
380
            $this->logger->warning('Invalid PID ' . $cPid . ' for metadata definitions');
381
            return [];
382
        }
383
        // Get metadata from parsed metadata array if available.
384
        if (
385
            !empty($this->metadataArray[$id])
386
            && $this->metadataArray[0] == $cPid
387
        ) {
388
            return $this->metadataArray[$id];
389
        }
390
        // Initialize metadata array with empty values.
391
        $metadata = [
392
            'title' => [],
393
            'title_sorting' => [],
394
            'author' => [],
395
            'place' => [],
396
            'year' => [],
397
            'prod_id' => [],
398
            'record_id' => [],
399
            'opac_id' => [],
400
            'union_id' => [],
401
            'urn' => [],
402
            'purl' => [],
403
            'type' => [],
404
            'volume' => [],
405
            'volume_sorting' => [],
406
            'license' => [],
407
            'terms' => [],
408
            'restrictions' => [],
409
            'out_of_print' => [],
410
            'rights_info' => [],
411
            'collection' => [],
412
            'owner' => [],
413
            'mets_label' => [],
414
            'mets_orderlabel' => [],
415
            'document_format' => ['METS'],
416
        ];
417
        // Get the logical structure node's @DMDID.
418
        if (!empty($this->logicalUnits[$id])) {
419
            $dmdIds = $this->logicalUnits[$id]['dmdId'];
420
        } else {
421
            $dmdIds = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $id . '"]/@DMDID');
422
            $dmdIds = (string) $dmdIds[0];
423
        }
424
        if (!empty($dmdIds)) {
425
            // Handle multiple DMDIDs separately.
426
            $dmdIds = explode(' ', $dmdIds);
427
            $hasSupportedMetadata = false;
428
        } else {
429
            // There is no dmdSec for this structure node.
430
            return [];
431
        }
432
        // Load available metadata formats and dmdSecs.
433
        $this->loadFormats();
434
        $this->_getDmdSec();
435
        foreach ($dmdIds as $dmdId) {
436
            // Is this metadata format supported?
437
            if (!empty($this->formats[$this->dmdSec[$dmdId]['type']])) {
438
                if (!empty($this->formats[$this->dmdSec[$dmdId]['type']]['class'])) {
439
                    $class = $this->formats[$this->dmdSec[$dmdId]['type']]['class'];
440
                    // Get the metadata from class.
441
                    if (
442
                        class_exists($class)
443
                        && ($obj = GeneralUtility::makeInstance($class)) instanceof MetadataInterface
444
                    ) {
445
                        $obj->extractMetadata($this->dmdSec[$dmdId]['xml'], $metadata);
446
                    } else {
447
                        $this->logger->warning('Invalid class/method "' . $class . '->extractMetadata()" for metadata format "' . $this->dmdSec[$dmdId]['type'] . '"');
448
                    }
449
                }
450
            } else {
451
                $this->logger->notice('Unsupported metadata format "' . $this->dmdSec[$dmdId]['type'] . '" in dmdSec with @ID "' . $dmdId . '"');
452
                // Continue searching for supported metadata with next @DMDID.
453
                continue;
454
            }
455
            // Get the structure's type.
456
            if (!empty($this->logicalUnits[$id])) {
457
                $metadata['type'] = [$this->logicalUnits[$id]['type']];
458
            } else {
459
                $struct = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $id . '"]/@TYPE');
460
                if (!empty($struct)) {
461
                    $metadata['type'] = [(string) $struct[0]];
462
                }
463
            }
464
            // Get the additional metadata from database.
465
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
466
                ->getQueryBuilderForTable('tx_dlf_metadata');
467
            // Get hidden records, too.
468
            $queryBuilder
469
                ->getRestrictions()
470
                ->removeByType(HiddenRestriction::class);
471
            // Get all metadata with configured xpath and applicable format first.
472
            // Exclude metadata with subentries, we will fetch them later.
473
            $resultWithFormat = $queryBuilder
474
                ->select(
475
                    'tx_dlf_metadata.index_name AS index_name',
476
                    'tx_dlf_metadataformat_joins.xpath AS xpath',
477
                    'tx_dlf_metadataformat_joins.xpath_sorting AS xpath_sorting',
478
                    'tx_dlf_metadata.is_sortable AS is_sortable',
479
                    'tx_dlf_metadata.default_value AS default_value',
480
                    'tx_dlf_metadata.format AS format'
481
                )
482
                ->from('tx_dlf_metadata')
483
                ->innerJoin(
484
                    'tx_dlf_metadata',
485
                    'tx_dlf_metadataformat',
486
                    'tx_dlf_metadataformat_joins',
487
                    $queryBuilder->expr()->eq(
488
                        'tx_dlf_metadataformat_joins.parent_id',
489
                        'tx_dlf_metadata.uid'
490
                    )
491
                )
492
                ->innerJoin(
493
                    'tx_dlf_metadataformat_joins',
494
                    'tx_dlf_formats',
495
                    'tx_dlf_formats_joins',
496
                    $queryBuilder->expr()->eq(
497
                        'tx_dlf_formats_joins.uid',
498
                        'tx_dlf_metadataformat_joins.encoded'
499
                    )
500
                )
501
                ->where(
502
                    $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($cPid)),
503
                    $queryBuilder->expr()->eq('tx_dlf_metadata.l18n_parent', 0),
504
                    $queryBuilder->expr()->eq('tx_dlf_metadataformat_joins.pid', intval($cPid)),
505
                    //$queryBuilder->expr()->eq('tx_dlf_metadataformat_joins.subentries', 0),
506
                    $queryBuilder->expr()->eq('tx_dlf_formats_joins.type', $queryBuilder->createNamedParameter($this->dmdSec[$dmdId]['type']))
507
                )
508
                ->execute();
509
            // Get all metadata without a format, but with a default value next.
510
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
511
                ->getQueryBuilderForTable('tx_dlf_metadata');
512
            // Get hidden records, too.
513
            $queryBuilder
514
                ->getRestrictions()
515
                ->removeByType(HiddenRestriction::class);
516
            $resultWithoutFormat = $queryBuilder
517
                ->select(
518
                    'tx_dlf_metadata.index_name AS index_name',
519
                    'tx_dlf_metadata.is_sortable AS is_sortable',
520
                    'tx_dlf_metadata.default_value AS default_value',
521
                    'tx_dlf_metadata.format AS format'
522
                )
523
                ->from('tx_dlf_metadata')
524
                ->where(
525
                    $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($cPid)),
526
                    $queryBuilder->expr()->eq('tx_dlf_metadata.l18n_parent', 0),
527
                    $queryBuilder->expr()->eq('tx_dlf_metadata.format', 0),
528
                    $queryBuilder->expr()->neq('tx_dlf_metadata.default_value', $queryBuilder->createNamedParameter(''))
529
                )
530
                ->execute();
531
            // Merge both result sets.
532
            $allResults = array_merge($resultWithFormat->fetchAll(), $resultWithoutFormat->fetchAll());
533
            // Get subentries separately.
534
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
535
                ->getQueryBuilderForTable('tx_dlf_metadata');
536
            // Get hidden records, too.
537
            $queryBuilder
538
                ->getRestrictions()
539
                ->removeByType(HiddenRestriction::class);
540
            $subentries = $queryBuilder
541
                ->select(
542
                    'tx_dlf_subentries_joins.index_name AS index_name',
543
                    'tx_dlf_metadata.index_name AS parent_index_name',
544
                    'tx_dlf_subentries_joins.xpath AS xpath',
545
                    'tx_dlf_subentries_joins.default_value AS default_value'
546
                )
547
                ->from('tx_dlf_metadata')
548
                ->innerJoin(
549
                    'tx_dlf_metadata',
550
                    'tx_dlf_metadataformat',
551
                    'tx_dlf_metadataformat_joins',
552
                    $queryBuilder->expr()->eq(
553
                        'tx_dlf_metadataformat_joins.parent_id',
554
                        'tx_dlf_metadata.uid'
555
                    )
556
                )
557
                ->innerJoin(
558
                    'tx_dlf_metadataformat_joins',
559
                    'tx_dlf_metadatasubentries',
560
                    'tx_dlf_subentries_joins',
561
                    $queryBuilder->expr()->eq(
562
                        'tx_dlf_subentries_joins.parent_id',
563
                        'tx_dlf_metadataformat_joins.uid'
564
                    )
565
                )
566
                ->where(
567
                    $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($cPid)),
568
                    // $queryBuilder->expr()->eq('tx_dlf_metadata.l18n_parent', 0),
569
                    // $queryBuilder->expr()->eq('tx_dlf_metadataformat_joins.pid', intval($cPid)),
570
                    $queryBuilder->expr()->gt('tx_dlf_metadataformat_joins.subentries', 0),
571
                    $queryBuilder->expr()->eq('tx_dlf_subentries_joins.l18n_parent', 0),
572
                    $queryBuilder->expr()->eq('tx_dlf_subentries_joins.pid', intval($cPid))
573
                    // $queryBuilder->expr()->eq('tx_dlf_formats_joins.type', $queryBuilder->createNamedParameter($this->dmdSec[$dmdId]['type']))
574
                )
575
                ->execute();
576
            $subentriesResult = $subentries->fetchAll();
577
            $metadata = $this->getXPathQueries($dmdId, $allResults, $subentriesResult, $metadata);
578
            // Extract metadata only from first supported dmdSec.
579
            $hasSupportedMetadata = true;
580
            break;
581
        }
582
        if ($hasSupportedMetadata) {
583
            return $metadata;
584
        } else {
585
            $this->logger->warning('No supported metadata found for logical structure with @ID "' . $id . '"');
586
            return [];
587
        }
588
    }
589
590
    /**
591
     * @param array $allResults
592
     * @param array $allSubentries
593
     * @param array $metadata
594
     * @return array
595
     */
596
    private function getXPathQueries($dmdId, $allResults, $allSubentries, array $metadata): array
597
    {
598
        // We need a \DOMDocument here, because SimpleXML doesn't support XPath functions properly.
599
        $domNode = dom_import_simplexml($this->dmdSec[$dmdId]['xml']);
600
        $domXPath = new \DOMXPath($domNode->ownerDocument);
0 ignored issues
show
Bug introduced by
It seems like $domNode->ownerDocument can also be of type null; however, parameter $document of DOMXPath::__construct() does only seem to accept DOMDocument, maybe add an additional type check? ( Ignorable by Annotation )

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

600
        $domXPath = new \DOMXPath(/** @scrutinizer ignore-type */ $domNode->ownerDocument);
Loading history...
601
        $this->registerNamespaces($domXPath);
602
        // OK, now make the XPath queries.
603
        foreach ($allResults as $resArray) {
604
            // Set metadata field's value(s).
605
            if (
606
                $resArray['format'] > 0
607
                && !empty($resArray['xpath'])
608
                && ($values = $domXPath->evaluate($resArray['xpath'], $domNode))
609
            ) {
610
                if (
611
                    $values instanceof \DOMNodeList
612
                    && $values->length > 0
613
                ) {
614
                    $metadata[$resArray['index_name']] = [];
615
                    foreach ($values as $value) {
616
                        if ($subentries = $this->getSubentries($allSubentries, $resArray['index_name'], $value)) 
617
                        {
618
                            $metadata[$resArray['index_name']][] = $subentries;
619
                        } else {
620
                            $metadata[$resArray['index_name']][] = trim((string)$value->nodeValue);
621
                        }
622
                    }
623
                } elseif (!($values instanceof \DOMNodeList)) {
624
                    $metadata[$resArray['index_name']] = [trim((string)$values->nodeValue)];
625
                }
626
            }
627
            // Set default value if applicable.
628
            if (
629
                empty($metadata[$resArray['index_name']][0])
630
                && strlen($resArray['default_value']) > 0
631
            ) {
632
                $metadata[$resArray['index_name']] = [$resArray['default_value']];
633
            }
634
            // Set sorting value if applicable.
635
            if (
636
                !empty($metadata[$resArray['index_name']])
637
                && $resArray['is_sortable']
638
            ) {
639
                if (
640
                    $resArray['format'] > 0
641
                    && !empty($resArray['xpath_sorting']) // TODO: will fail, for subentries
642
                    && ($values = $domXPath->evaluate($resArray['xpath_sorting'], $domNode))
643
                ) {
644
                    if (
645
                        $values instanceof \DOMNodeList
646
                        && $values->length > 0
647
                    ) {
648
                        $metadata[$resArray['index_name'] . '_sorting'][0] = trim((string)$values->item(0)->nodeValue);
649
                    } elseif (!($values instanceof \DOMNodeList)) {
650
                        $metadata[$resArray['index_name'] . '_sorting'][0] = trim((string)$values);
651
                    }
652
                }
653
                if (empty($metadata[$resArray['index_name'] . '_sorting'][0])) {
654
                    $metadata[$resArray['index_name'] . '_sorting'][0] = $metadata[$resArray['index_name']][0];
655
                }
656
            }
657
        }
658
        // Set title to empty string if not present.
659
        if (empty($metadata['title'][0])) {
660
            $metadata['title'][0] = '';
661
            $metadata['title_sorting'][0] = '';
662
        }
663
        return $metadata;
664
    }
665
666
    /**
667
     * @param array $allSubentries
668
     * @param string $parentIndex
669
     * @param \DOMNode parentNode
0 ignored issues
show
Bug introduced by
The type Kitodo\Dlf\Common\parentNode 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...
670
     * @return array
671
     */
672
    private function getSubentries($allSubentries, string $parentIndex, \DOMNode $parentNode)
673
    {
674
        $domXPath = new \DOMXPath($parentNode->ownerDocument);
0 ignored issues
show
Bug introduced by
It seems like $parentNode->ownerDocument can also be of type null; however, parameter $document of DOMXPath::__construct() does only seem to accept DOMDocument, maybe add an additional type check? ( Ignorable by Annotation )

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

674
        $domXPath = new \DOMXPath(/** @scrutinizer ignore-type */ $parentNode->ownerDocument);
Loading history...
675
        $this->registerNamespaces($domXPath);
676
        $theseSubentries = [];
677
        foreach ($allSubentries as $subentry) 
678
        {
679
            if ($subentry['parent_index_name'] == $parentIndex) 
680
            {
681
                if (
682
                    !empty($subentry['xpath'])
683
                    && ($values = $domXPath->evaluate($subentry['xpath'], $parentNode))
684
                ) {
685
                    if (
686
                        $values instanceof \DOMNodeList
687
                        && $values->length > 0
688
                    ) {
689
                        $theseSubentries[$subentry['index_name']] = [];
690
                        foreach ($values as $value) {
691
                            $theseSubentries[$subentry['index_name']][] = trim((string)$value->nodeValue);
692
                        }
693
                    } elseif (!($values instanceof \DOMNodeList)) {
694
                        $theseSubentries[$subentry['index_name']] = [trim((string)$values->nodeValue)];
695
                    }
696
                }
697
                // Set default value if applicable.
698
                if (
699
                    empty($theseSubentries[$subentry['index_name']][0]) 
700
                    && strlen($subentry['default_value']) > 0
701
                ) {
702
                    $theseSubentries[$subentry['index_name']] = [$subentry['default_value']];
703
                }
704
            }
705
        }
706
        if (empty($theseSubentries)) 
707
        {
708
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
709
        }
710
        return $theseSubentries;
711
    }
712
713
    /**
714
     * {@inheritDoc}
715
     * @see \Kitodo\Dlf\Common\Doc::getFullText()
716
     */
717
    public function getFullText($id)
718
    {
719
        $fullText = '';
720
721
        // Load fileGrps and check for full text files.
722
        $this->_getFileGrps();
723
        if ($this->hasFulltext) {
724
            $fullText = $this->getFullTextFromXml($id);
725
        }
726
        return $fullText;
727
    }
728
729
    /**
730
     * {@inheritDoc}
731
     * @see Doc::getStructureDepth()
732
     */
733
    public function getStructureDepth($logId)
734
    {
735
        $ancestors = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $logId . '"]/ancestor::*');
736
        if (!empty($ancestors)) {
737
            return count($ancestors);
738
        } else {
739
            return 0;
740
        }
741
    }
742
743
    /**
744
     * {@inheritDoc}
745
     * @see \Kitodo\Dlf\Common\Doc::init()
746
     */
747
    protected function init($location)
748
    {
749
        $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(get_class($this));
750
        // Get METS node from XML file.
751
        $this->registerNamespaces($this->xml);
752
        $mets = $this->xml->xpath('//mets:mets');
753
        if (!empty($mets)) {
754
            $this->mets = $mets[0];
755
            // Register namespaces.
756
            $this->registerNamespaces($this->mets);
757
        } else {
758
            if (!empty($location)) {
759
                $this->logger->error('No METS part found in document with location "' . $location . '".');
760
            } else if (!empty($this->recordId)) {
761
                $this->logger->error('No METS part found in document with recordId "' . $this->recordId . '".');
762
            } else {
763
                $this->logger->error('No METS part found in current document.');
764
            }
765
        }
766
    }
767
768
    /**
769
     * {@inheritDoc}
770
     * @see \Kitodo\Dlf\Common\Doc::loadLocation()
771
     */
772
    protected function loadLocation($location)
773
    {
774
        $fileResource = Helper::getUrl($location);
775
        if ($fileResource !== false) {
776
            $xml = Helper::getXmlFileAsString($fileResource);
777
            // Set some basic properties.
778
            if ($xml !== false) {
779
                $this->xml = $xml;
780
                return true;
781
            }
782
        }
783
        $this->logger->error('Could not load XML file from "' . $location . '"');
784
        return false;
785
    }
786
787
    /**
788
     * {@inheritDoc}
789
     * @see \Kitodo\Dlf\Common\Doc::ensureHasFulltextIsSet()
790
     */
791
    protected function ensureHasFulltextIsSet()
792
    {
793
        // Are the fileGrps already loaded?
794
        if (!$this->fileGrpsLoaded) {
795
            $this->_getFileGrps();
796
        }
797
    }
798
799
    /**
800
     * {@inheritDoc}
801
     * @see Doc::setPreloadedDocument()
802
     */
803
    protected function setPreloadedDocument($preloadedDocument)
804
    {
805
806
        if ($preloadedDocument instanceof \SimpleXMLElement) {
807
            $this->xml = $preloadedDocument;
808
            return true;
809
        }
810
        return false;
811
    }
812
813
    /**
814
     * {@inheritDoc}
815
     * @see Doc::getDocument()
816
     */
817
    protected function getDocument()
818
    {
819
        return $this->mets;
820
    }
821
822
    /**
823
     * This builds an array of the document's dmdSecs
824
     *
825
     * @access protected
826
     *
827
     * @return array Array of dmdSecs with their IDs as array key
828
     */
829
    protected function _getDmdSec()
830
    {
831
        if (!$this->dmdSecLoaded) {
832
            // Get available data formats.
833
            $this->loadFormats();
834
            // Get dmdSec nodes from METS.
835
            $dmdIds = $this->mets->xpath('./mets:dmdSec/@ID');
836
            if (!empty($dmdIds)) {
837
                foreach ($dmdIds as $dmdId) {
838
                    if ($type = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[not(@MDTYPE="OTHER")]/@MDTYPE')) {
839
                        if (!empty($this->formats[(string) $type[0]])) {
840
                            $type = (string) $type[0];
841
                            $xml = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[@MDTYPE="' . $type . '"]/mets:xmlData/' . strtolower($type) . ':' . $this->formats[$type]['rootElement']);
842
                        }
843
                    } elseif ($type = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[@MDTYPE="OTHER"]/@OTHERMDTYPE')) {
844
                        if (!empty($this->formats[(string) $type[0]])) {
845
                            $type = (string) $type[0];
846
                            $xml = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[@MDTYPE="OTHER"][@OTHERMDTYPE="' . $type . '"]/mets:xmlData/' . strtolower($type) . ':' . $this->formats[$type]['rootElement']);
847
                        }
848
                    }
849
                    if (!empty($xml)) {
850
                        $this->dmdSec[(string) $dmdId]['type'] = $type;
851
                        $this->dmdSec[(string) $dmdId]['xml'] = $xml[0];
852
                        $this->registerNamespaces($this->dmdSec[(string) $dmdId]['xml']);
853
                    }
854
                }
855
            }
856
            $this->dmdSecLoaded = true;
857
        }
858
        return $this->dmdSec;
859
    }
860
861
    /**
862
     * This builds the file ID -> USE concordance
863
     *
864
     * @access protected
865
     *
866
     * @return array Array of file use groups with file IDs
867
     */
868
    protected function _getFileGrps()
869
    {
870
        if (!$this->fileGrpsLoaded) {
871
            // Get configured USE attributes.
872
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
873
            $useGrps = GeneralUtility::trimExplode(',', $extConf['fileGrpImages']);
874
            if (!empty($extConf['fileGrpThumbs'])) {
875
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']));
876
            }
877
            if (!empty($extConf['fileGrpDownload'])) {
878
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpDownload']));
879
            }
880
            if (!empty($extConf['fileGrpFulltext'])) {
881
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpFulltext']));
882
            }
883
            if (!empty($extConf['fileGrpAudio'])) {
884
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpAudio']));
885
            }
886
            // Get all file groups.
887
            $fileGrps = $this->mets->xpath('./mets:fileSec/mets:fileGrp');
888
            if (!empty($fileGrps)) {
889
                // Build concordance for configured USE attributes.
890
                foreach ($fileGrps as $fileGrp) {
891
                    if (in_array((string) $fileGrp['USE'], $useGrps)) {
892
                        foreach ($fileGrp->children('http://www.loc.gov/METS/')->file as $file) {
893
                            $this->fileGrps[(string) $file->attributes()->ID] = (string) $fileGrp['USE'];
0 ignored issues
show
Bug introduced by
The method attributes() 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

893
                            $this->fileGrps[(string) $file->/** @scrutinizer ignore-call */ attributes()->ID] = (string) $fileGrp['USE'];

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...
894
                        }
895
                    }
896
                }
897
            }
898
            // Are there any fulltext files available?
899
            if (
900
                !empty($extConf['fileGrpFulltext'])
901
                && array_intersect(GeneralUtility::trimExplode(',', $extConf['fileGrpFulltext']), $this->fileGrps) !== []
902
            ) {
903
                $this->hasFulltext = true;
904
            }
905
            $this->fileGrpsLoaded = true;
906
        }
907
        return $this->fileGrps;
908
    }
909
910
    /**
911
     * {@inheritDoc}
912
     * @see \Kitodo\Dlf\Common\Doc::prepareMetadataArray()
913
     */
914
    protected function prepareMetadataArray($cPid)
915
    {
916
        $ids = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@DMDID]/@ID');
917
        // Get all logical structure nodes with metadata.
918
        if (!empty($ids)) {
919
            foreach ($ids as $id) {
920
                $this->metadataArray[(string) $id] = $this->getMetadata((string) $id, $cPid);
921
            }
922
        }
923
        // Set current PID for metadata definitions.
924
    }
925
926
    /**
927
     * This returns $this->mets via __get()
928
     *
929
     * @access protected
930
     *
931
     * @return \SimpleXMLElement The XML's METS part as \SimpleXMLElement object
932
     */
933
    protected function _getMets()
934
    {
935
        return $this->mets;
936
    }
937
938
    /**
939
     * {@inheritDoc}
940
     * @see \Kitodo\Dlf\Common\Doc::_getPhysicalStructure()
941
     */
942
    protected function _getPhysicalStructure()
943
    {
944
        // Is there no physical structure array yet?
945
        if (!$this->physicalStructureLoaded) {
946
            // Does the document have a structMap node of type "PHYSICAL"?
947
            $elementNodes = $this->mets->xpath('./mets:structMap[@TYPE="PHYSICAL"]/mets:div[@TYPE="physSequence"]/mets:div');
948
            if (!empty($elementNodes)) {
949
                // Get file groups.
950
                $fileUse = $this->_getFileGrps();
951
                // Get the physical sequence's metadata.
952
                $physNode = $this->mets->xpath('./mets:structMap[@TYPE="PHYSICAL"]/mets:div[@TYPE="physSequence"]');
953
                $physSeq[0] = (string) $physNode[0]['ID'];
954
                $this->physicalStructureInfo[$physSeq[0]]['id'] = (string) $physNode[0]['ID'];
955
                $this->physicalStructureInfo[$physSeq[0]]['dmdId'] = (isset($physNode[0]['DMDID']) ? (string) $physNode[0]['DMDID'] : '');
956
                $this->physicalStructureInfo[$physSeq[0]]['order'] = (isset($physNode[0]['ORDER']) ? (string) $physNode[0]['ORDER'] : '');
957
                $this->physicalStructureInfo[$physSeq[0]]['label'] = (isset($physNode[0]['LABEL']) ? (string) $physNode[0]['LABEL'] : '');
958
                $this->physicalStructureInfo[$physSeq[0]]['orderlabel'] = (isset($physNode[0]['ORDERLABEL']) ? (string) $physNode[0]['ORDERLABEL'] : '');
959
                $this->physicalStructureInfo[$physSeq[0]]['type'] = (string) $physNode[0]['TYPE'];
960
                $this->physicalStructureInfo[$physSeq[0]]['contentIds'] = (isset($physNode[0]['CONTENTIDS']) ? (string) $physNode[0]['CONTENTIDS'] : '');
961
                // Get the file representations from fileSec node.
962
                foreach ($physNode[0]->children('http://www.loc.gov/METS/')->fptr as $fptr) {
963
                    // Check if file has valid @USE attribute.
964
                    if (!empty($fileUse[(string) $fptr->attributes()->FILEID])) {
965
                        $this->physicalStructureInfo[$physSeq[0]]['files'][$fileUse[(string) $fptr->attributes()->FILEID]] = (string) $fptr->attributes()->FILEID;
966
                    }
967
                }
968
                // Build the physical elements' array from the physical structMap node.
969
                foreach ($elementNodes as $elementNode) {
970
                    $elements[(int) $elementNode['ORDER']] = (string) $elementNode['ID'];
971
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['id'] = (string) $elementNode['ID'];
972
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['dmdId'] = (isset($elementNode['DMDID']) ? (string) $elementNode['DMDID'] : '');
973
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['order'] = (isset($elementNode['ORDER']) ? (string) $elementNode['ORDER'] : '');
974
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['label'] = (isset($elementNode['LABEL']) ? (string) $elementNode['LABEL'] : '');
975
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['orderlabel'] = (isset($elementNode['ORDERLABEL']) ? (string) $elementNode['ORDERLABEL'] : '');
976
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['type'] = (string) $elementNode['TYPE'];
977
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['contentIds'] = (isset($elementNode['CONTENTIDS']) ? (string) $elementNode['CONTENTIDS'] : '');
978
                    // Get the file representations from fileSec node.
979
                    foreach ($elementNode->children('http://www.loc.gov/METS/')->fptr as $fptr) {
980
                        // Check if file has valid @USE attribute.
981
                        if (!empty($fileUse[(string) $fptr->attributes()->FILEID])) {
982
                            $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['files'][$fileUse[(string) $fptr->attributes()->FILEID]] = (string) $fptr->attributes()->FILEID;
983
                        }
984
                    }
985
                }
986
                // Sort array by keys (= @ORDER).
987
                if (ksort($elements)) {
988
                    // Set total number of pages/tracks.
989
                    $this->numPages = count($elements);
990
                    // Merge and re-index the array to get nice numeric indexes.
991
                    $this->physicalStructure = array_merge($physSeq, $elements);
992
                }
993
            }
994
            $this->physicalStructureLoaded = true;
995
        }
996
        return $this->physicalStructure;
997
    }
998
999
    /**
1000
     * {@inheritDoc}
1001
     * @see \Kitodo\Dlf\Common\Doc::_getSmLinks()
1002
     */
1003
    protected function _getSmLinks()
1004
    {
1005
        if (!$this->smLinksLoaded) {
1006
            $smLinks = $this->mets->xpath('./mets:structLink/mets:smLink');
1007
            if (!empty($smLinks)) {
1008
                foreach ($smLinks as $smLink) {
1009
                    $this->smLinks['l2p'][(string) $smLink->attributes('http://www.w3.org/1999/xlink')->from][] = (string) $smLink->attributes('http://www.w3.org/1999/xlink')->to;
1010
                    $this->smLinks['p2l'][(string) $smLink->attributes('http://www.w3.org/1999/xlink')->to][] = (string) $smLink->attributes('http://www.w3.org/1999/xlink')->from;
1011
                }
1012
            }
1013
            $this->smLinksLoaded = true;
1014
        }
1015
        return $this->smLinks;
1016
    }
1017
1018
    /**
1019
     * {@inheritDoc}
1020
     * @see \Kitodo\Dlf\Common\Doc::_getThumbnail()
1021
     */
1022
    protected function _getThumbnail($forceReload = false)
1023
    {
1024
        if (
1025
            !$this->thumbnailLoaded
1026
            || $forceReload
1027
        ) {
1028
            // Retain current PID.
1029
            $cPid = ($this->cPid ? $this->cPid : $this->pid);
1030
            if (!$cPid) {
1031
                $this->logger->error('Invalid PID ' . $cPid . ' for structure definitions');
1032
                $this->thumbnailLoaded = true;
1033
                return $this->thumbnail;
1034
            }
1035
            // Load extension configuration.
1036
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
1037
            if (empty($extConf['fileGrpThumbs'])) {
1038
                $this->logger->warning('No fileGrp for thumbnails specified');
1039
                $this->thumbnailLoaded = true;
1040
                return $this->thumbnail;
1041
            }
1042
            $strctId = $this->_getToplevelId();
1043
            $metadata = $this->getTitledata($cPid);
1044
1045
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1046
                ->getQueryBuilderForTable('tx_dlf_structures');
1047
1048
            // Get structure element to get thumbnail from.
1049
            $result = $queryBuilder
1050
                ->select('tx_dlf_structures.thumbnail AS thumbnail')
1051
                ->from('tx_dlf_structures')
1052
                ->where(
1053
                    $queryBuilder->expr()->eq('tx_dlf_structures.pid', intval($cPid)),
1054
                    $queryBuilder->expr()->eq('tx_dlf_structures.index_name', $queryBuilder->expr()->literal($metadata['type'][0])),
1055
                    Helper::whereExpression('tx_dlf_structures')
1056
                )
1057
                ->setMaxResults(1)
1058
                ->execute();
1059
1060
            $allResults = $result->fetchAll();
1061
1062
            if (count($allResults) == 1) {
1063
                $resArray = $allResults[0];
1064
                // Get desired thumbnail structure if not the toplevel structure itself.
1065
                if (!empty($resArray['thumbnail'])) {
1066
                    $strctType = Helper::getIndexNameFromUid($resArray['thumbnail'], 'tx_dlf_structures', $cPid);
1067
                    // Check if this document has a structure element of the desired type.
1068
                    $strctIds = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@TYPE="' . $strctType . '"]/@ID');
1069
                    if (!empty($strctIds)) {
1070
                        $strctId = (string) $strctIds[0];
1071
                    }
1072
                }
1073
                // Load smLinks.
1074
                $this->_getSmLinks();
1075
                // Get thumbnail location.
1076
                $fileGrpsThumb = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']);
1077
                while ($fileGrpThumb = array_shift($fileGrpsThumb)) {
1078
                    if (
1079
                        $this->_getPhysicalStructure()
1080
                        && !empty($this->smLinks['l2p'][$strctId])
1081
                        && !empty($this->physicalStructureInfo[$this->smLinks['l2p'][$strctId][0]]['files'][$fileGrpThumb])
1082
                    ) {
1083
                        $this->thumbnail = $this->getFileLocation($this->physicalStructureInfo[$this->smLinks['l2p'][$strctId][0]]['files'][$fileGrpThumb]);
1084
                        break;
1085
                    } elseif (!empty($this->physicalStructureInfo[$this->physicalStructure[1]]['files'][$fileGrpThumb])) {
1086
                        $this->thumbnail = $this->getFileLocation($this->physicalStructureInfo[$this->physicalStructure[1]]['files'][$fileGrpThumb]);
1087
                        break;
1088
                    }
1089
                }
1090
            } else {
1091
                $this->logger->error('No structure of type "' . $metadata['type'][0] . '" found in database');
1092
            }
1093
            $this->thumbnailLoaded = true;
1094
        }
1095
        return $this->thumbnail;
1096
    }
1097
1098
    /**
1099
     * {@inheritDoc}
1100
     * @see \Kitodo\Dlf\Common\Doc::_getToplevelId()
1101
     */
1102
    protected function _getToplevelId()
1103
    {
1104
        if (empty($this->toplevelId)) {
1105
            // Get all logical structure nodes with metadata, but without associated METS-Pointers.
1106
            $divs = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@DMDID and not(./mets:mptr)]');
1107
            if (!empty($divs)) {
1108
                // Load smLinks.
1109
                $this->_getSmLinks();
1110
                foreach ($divs as $div) {
1111
                    $id = (string) $div['ID'];
1112
                    // Are there physical structure nodes for this logical structure?
1113
                    if (array_key_exists($id, $this->smLinks['l2p'])) {
1114
                        // Yes. That's what we're looking for.
1115
                        $this->toplevelId = $id;
1116
                        break;
1117
                    } elseif (empty($this->toplevelId)) {
1118
                        // No. Remember this anyway, but keep looking for a better one.
1119
                        $this->toplevelId = $id;
1120
                    }
1121
                }
1122
            }
1123
        }
1124
        return $this->toplevelId;
1125
    }
1126
1127
    /**
1128
     * This magic method is executed prior to any serialization of the object
1129
     * @see __wakeup()
1130
     *
1131
     * @access public
1132
     *
1133
     * @return array Properties to be serialized
1134
     */
1135
    public function __sleep()
1136
    {
1137
        // \SimpleXMLElement objects can't be serialized, thus save the XML as string for serialization
1138
        $this->asXML = $this->xml->asXML();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->xml->asXML() can also be of type true. However, the property $asXML is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1139
        return ['uid', 'pid', 'recordId', 'parentId', 'asXML'];
1140
    }
1141
1142
    /**
1143
     * This magic method is used for setting a string value for the object
1144
     *
1145
     * @access public
1146
     *
1147
     * @return string String representing the METS object
1148
     */
1149
    public function __toString()
1150
    {
1151
        $xml = new \DOMDocument('1.0', 'utf-8');
1152
        $xml->appendChild($xml->importNode(dom_import_simplexml($this->mets), true));
1153
        $xml->formatOutput = true;
1154
        return $xml->saveXML();
1155
    }
1156
1157
    /**
1158
     * This magic method is executed after the object is deserialized
1159
     * @see __sleep()
1160
     *
1161
     * @access public
1162
     *
1163
     * @return void
1164
     */
1165
    public function __wakeup()
1166
    {
1167
        $xml = Helper::getXmlFileAsString($this->asXML);
1168
        if ($xml !== false) {
1169
            $this->asXML = '';
1170
            $this->xml = $xml;
1171
            // Rebuild the unserializable properties.
1172
            $this->init('');
1173
        } else {
1174
            $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class);
1175
            $this->logger->error('Could not load XML after deserialization');
1176
        }
1177
    }
1178
}
1179