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 (#650)
by
unknown
02:45
created

MetsDocument::getLogicalStructureInfo()   F

Complexity

Conditions 24
Paths 9216

Size

Total Lines 93
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 57
dl 0
loc 93
rs 0
c 0
b 0
f 0
cc 24
nc 9216
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Document;
14
15
use Kitodo\Dlf\Common\Helper;
16
use Kitodo\Dlf\Common\IiifUrlReader;
17
use Kitodo\Dlf\Common\MetadataInterface;
18
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
19
use TYPO3\CMS\Core\Database\ConnectionPool;
20
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
21
use TYPO3\CMS\Core\Utility\GeneralUtility;
22
use TYPO3\CMS\Core\Utility\MathUtility;
23
use Ubl\Iiif\Tools\IiifHelper;
24
use Ubl\Iiif\Services\AbstractImageService;
25
26
/**
27
 * MetsDocument class for the 'dlf' extension.
28
 *
29
 * @author Sebastian Meyer <[email protected]>
30
 * @author Henrik Lochmann <[email protected]>
31
 * @package TYPO3
32
 * @subpackage dlf
33
 * @access public
34
 * @property int $cPid This holds the PID for the configuration
35
 * @property-read array $dmdSec This holds the XML file's dmdSec parts with their IDs as array key
36
 * @property-read array $fileGrps This holds the file ID -> USE concordance
37
 * @property-read string $location This holds the documents location
38
 * @property-read array $metadataArray This holds the documents' parsed metadata array
39
 * @property-read \SimpleXMLElement $mets This holds the XML file's METS part as \SimpleXMLElement object
40
 * @property-read int $numPages The holds the total number of pages
41
 * @property-read int $parentId This holds the UID of the parent document or zero if not multi-volumed
42
 * @property-read array $physicalStructure This holds the physical structure
43
 * @property-read array $physicalStructureInfo This holds the physical structure metadata
44
 * @property-read int $pid This holds the PID of the document or zero if not in database
45
 * @property-read bool $ready Is the document instantiated successfully?
46
 * @property-read string $recordId The METS file's / IIIF manifest's record identifier
47
 * @property-read int $rootId This holds the UID of the root document or zero if not multi-volumed
48
 * @property-read array $smLinks This holds the smLinks between logical and physical structMap
49
 * @property-read array $tableOfContents This holds the logical structure
50
 * @property-read string $thumbnail This holds the document's thumbnail location
51
 * @property-read string $toplevelId This holds the toplevel structure's @ID (METS) or the manifest's @id (IIIF)
52
 * @property-read mixed $uid This holds the UID or the URL of the document
53
 */
54
final class MetsDocument extends FullTextDocument
55
{
56
    /**
57
     * This holds the whole XML file as string for serialization purposes
58
     * @see __sleep() / __wakeup()
59
     *
60
     * @var string
61
     * @access protected
62
     */
63
    protected $asXML = '';
64
65
    /**
66
     * This holds the XML file's dmdSec parts with their IDs as array key
67
     *
68
     * @var array
69
     * @access protected
70
     */
71
    protected $dmdSec = [];
72
73
    /**
74
     * Are the METS file's dmdSecs loaded?
75
     * @see $dmdSec
76
     *
77
     * @var bool
78
     * @access protected
79
     */
80
    protected $dmdSecLoaded = false;
81
82
    /**
83
     * The extension key
84
     *
85
     * @var	string
86
     * @access public
87
     */
88
    public static $extKey = 'dlf';
89
90
    /**
91
     * This holds the file ID -> USE concordance
92
     * @see _getFileGrps()
93
     *
94
     * @var array
95
     * @access protected
96
     */
97
    protected $fileGrps = [];
98
99
    /**
100
     * Are the image file groups loaded?
101
     * @see $fileGrps
102
     *
103
     * @var bool
104
     * @access protected
105
     */
106
    protected $fileGrpsLoaded = false;
107
108
    /**
109
     * This holds the XML file's METS part as \SimpleXMLElement object
110
     *
111
     * @var \SimpleXMLElement
112
     * @access protected
113
     */
114
    protected $mets;
115
116
    /**
117
     * This adds metadata from METS structural map to metadata array.
118
     *
119
     * @access	public
120
     *
121
     * @param	array	&$metadata: The metadata array to extend
122
     * @param	string	$id: The @ID attribute of the logical structure node
123
     *
124
     * @return  void
125
     */
126
    public function addMetadataFromMets(&$metadata, $id)
127
    {
128
        $details = $this->getLogicalStructure($id);
129
        if (!empty($details)) {
130
            $metadata['mets_order'][0] = $details['order'];
131
            $metadata['mets_label'][0] = $details['label'];
132
            $metadata['mets_orderlabel'][0] = $details['orderlabel'];
133
        }
134
    }
135
136
    /**
137
     *
138
     * {@inheritDoc}
139
     * @see Document::establishRecordId()
140
     */
141
    protected function establishRecordId($pid)
142
    {
143
        // Check for METS object @ID.
144
        if (!empty($this->mets['OBJID'])) {
145
            $this->recordId = (string) $this->mets['OBJID'];
0 ignored issues
show
Bug introduced by
The property recordId is declared read-only in Kitodo\Dlf\Common\Document\MetsDocument.
Loading history...
146
        }
147
        // Get hook objects.
148
        $hookObjects = Helper::getHookObjects('Classes/Common/Document/MetsDocument.php');
149
        // Apply hooks.
150
        foreach ($hookObjects as $hookObj) {
151
            if (method_exists($hookObj, 'construct_postProcessRecordId')) {
152
                $hookObj->construct_postProcessRecordId($this->xml, $this->recordId);
153
            }
154
        }
155
    }
156
157
    /**
158
     *
159
     * {@inheritDoc}
160
     * @see Document::getDownloadLocation()
161
     */
162
    public function getDownloadLocation($id)
163
    {
164
        $fileMimeType = $this->getFileMimeType($id);
165
        $fileLocation = $this->getFileLocation($id);
166
        if ($fileMimeType === 'application/vnd.kitodo.iiif') {
167
            $fileLocation = (strrpos($fileLocation, 'info.json') === strlen($fileLocation) - 9) ? $fileLocation : (strrpos($fileLocation, '/') === strlen($fileLocation) ? $fileLocation . 'info.json' : $fileLocation . '/info.json');
168
            $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
169
            IiifHelper::setUrlReader(IiifUrlReader::getInstance());
170
            IiifHelper::setMaxThumbnailHeight($conf['iiifThumbnailHeight']);
171
            IiifHelper::setMaxThumbnailWidth($conf['iiifThumbnailWidth']);
172
            $service = IiifHelper::loadIiifResource($fileLocation);
173
            if ($service !== null && $service instanceof AbstractImageService) {
174
                return $service->getImageUrl();
175
            }
176
        } elseif ($fileMimeType === 'application/vnd.netfpx') {
177
            $baseURL = $fileLocation . (strpos($fileLocation, '?') === false ? '?' : '');
178
            // TODO CVT is an optional IIP server capability; in theory, capabilities should be determined in the object request with '&obj=IIP-server'
179
            return $baseURL . '&CVT=jpeg';
180
        }
181
        return $fileLocation;
182
    }
183
184
    /**
185
     * {@inheritDoc}
186
     * @see Document::getFileLocation()
187
     */
188
    public function getFileLocation($id)
189
    {
190
        $location = $this->mets->xpath('./mets:fileSec/mets:fileGrp/mets:file[@ID="' . $id . '"]/mets:FLocat[@LOCTYPE="URL"]');
191
        if (
192
            !empty($id)
193
            && !empty($location)
194
        ) {
195
            return (string) $location[0]->attributes('http://www.w3.org/1999/xlink')->href;
196
        } else {
197
            $this->logger->warning('There is no file node with @ID "' . $id . '"');
1 ignored issue
show
Bug introduced by
The method warning() does not exist on TYPO3\CMS\Core\Log\LogManager. ( Ignorable by Annotation )

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

197
            $this->logger->/** @scrutinizer ignore-call */ 
198
                           warning('There is no file node with @ID "' . $id . '"');

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...
198
            return '';
199
        }
200
    }
201
202
    /**
203
     * {@inheritDoc}
204
     * @see Document::getFileMimeType()
205
     */
206
    public function getFileMimeType($id)
207
    {
208
        $mimetype = $this->mets->xpath('./mets:fileSec/mets:fileGrp/mets:file[@ID="' . $id . '"]/@MIMETYPE');
209
        if (
210
            !empty($id)
211
            && !empty($mimetype)
212
        ) {
213
            return (string) $mimetype[0];
214
        } else {
215
            $this->logger->warning('There is no file node with @ID "' . $id . '" or no MIME type specified');
216
            return '';
217
        }
218
    }
219
220
    /**
221
     * {@inheritDoc}
222
     * @see Document::getLogicalStructure()
223
     */
224
    public function getLogicalStructure($id, $recursive = false)
225
    {
226
        $details = [];
227
        // Is the requested logical unit already loaded?
228
        if (
229
            !$recursive
230
            && !empty($this->logicalUnits[$id])
231
        ) {
232
            // Yes. Return it.
233
            return $this->logicalUnits[$id];
234
        } elseif (!empty($id)) {
235
            // Get specified logical unit.
236
            $divs = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $id . '"]');
237
        } else {
238
            // Get all logical units at top level.
239
            $divs = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]/mets:div');
240
        }
241
        if (!empty($divs)) {
242
            if (!$recursive) {
243
                // Get the details for the first xpath hit.
244
                $details = $this->getLogicalStructureInfo($divs[0]);
245
            } else {
246
                // Walk the logical structure recursively and fill the whole table of contents.
247
                foreach ($divs as $div) {
248
                    $this->tableOfContents[] = $this->getLogicalStructureInfo($div, $recursive);
0 ignored issues
show
Bug introduced by
The property tableOfContents is declared read-only in Kitodo\Dlf\Common\Document\MetsDocument.
Loading history...
249
                }
250
            }
251
        }
252
        return $details;
253
    }
254
255
    /**
256
     * This gets details about a logical structure element
257
     *
258
     * @access protected
259
     *
260
     * @param \SimpleXMLElement $structure: The logical structure node
261
     * @param bool $recursive: Whether to include the child elements
262
     *
263
     * @return array Array of the element's id, label, type and physical page indexes/mptr link
264
     */
265
    protected function getLogicalStructureInfo(\SimpleXMLElement $structure, $recursive = false)
266
    {
267
        // Get attributes.
268
        foreach ($structure->attributes() as $attribute => $value) {
269
            $attributes[$attribute] = (string) $value;
270
        }
271
        // Load plugin configuration.
272
        $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
273
        // Extract identity information.
274
        $details = [];
275
        $details['id'] = $attributes['ID'];
276
        $details['dmdId'] = (isset($attributes['DMDID']) ? $attributes['DMDID'] : '');
277
        $details['order'] = (isset($attributes['ORDER']) ? $attributes['ORDER'] : '');
278
        $details['label'] = (isset($attributes['LABEL']) ? $attributes['LABEL'] : '');
279
        $details['orderlabel'] = (isset($attributes['ORDERLABEL']) ? $attributes['ORDERLABEL'] : '');
280
        $details['contentIds'] = (isset($attributes['CONTENTIDS']) ? $attributes['CONTENTIDS'] : '');
281
        $details['volume'] = '';
282
        // Set volume information only if no label is set and this is the toplevel structure element.
283
        if (
284
            empty($details['label'])
285
            && $details['id'] == $this->_getToplevelId()
286
        ) {
287
            $metadata = $this->getMetadata($details['id']);
288
            if (!empty($metadata['volume'][0])) {
289
                $details['volume'] = $metadata['volume'][0];
290
            }
291
        }
292
        $details['pagination'] = '';
293
        $details['type'] = $attributes['TYPE'];
294
        $details['thumbnailId'] = '';
295
        // Load smLinks.
296
        $this->_getSmLinks();
297
        // Load physical structure.
298
        $this->_getPhysicalStructure();
299
        // Get the physical page or external file this structure element is pointing at.
300
        $details['points'] = '';
301
        // Is there a mptr node?
302
        if (count($structure->children('http://www.loc.gov/METS/')->mptr)) {
303
            // Yes. Get the file reference.
304
            $details['points'] = (string) $structure->children('http://www.loc.gov/METS/')->mptr[0]->attributes('http://www.w3.org/1999/xlink')->href;
305
        } elseif (
306
            !empty($this->physicalStructure)
307
            && array_key_exists($details['id'], $this->smLinks['l2p'])
308
        ) {
309
            // Link logical structure to the first corresponding physical page/track.
310
            $details['points'] = max(intval(array_search($this->smLinks['l2p'][$details['id']][0], $this->physicalStructure, true)), 1);
311
            $fileGrpsThumb = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']);
312
            while ($fileGrpThumb = array_shift($fileGrpsThumb)) {
313
                if (!empty($this->physicalStructureInfo[$this->smLinks['l2p'][$details['id']][0]]['files'][$fileGrpThumb])) {
314
                    $details['thumbnailId'] = $this->physicalStructureInfo[$this->smLinks['l2p'][$details['id']][0]]['files'][$fileGrpThumb];
315
                    break;
316
                }
317
            }
318
            // Get page/track number of the first page/track related to this structure element.
319
            $details['pagination'] = $this->physicalStructureInfo[$this->smLinks['l2p'][$details['id']][0]]['orderlabel'];
320
        } elseif ($details['id'] == $this->_getToplevelId()) {
321
            // Point to self if this is the toplevel structure.
322
            $details['points'] = 1;
323
            $fileGrpsThumb = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']);
324
            while ($fileGrpThumb = array_shift($fileGrpsThumb)) {
325
                if (
326
                    !empty($this->physicalStructure)
327
                    && !empty($this->physicalStructureInfo[$this->physicalStructure[1]]['files'][$fileGrpThumb])
328
                ) {
329
                    $details['thumbnailId'] = $this->physicalStructureInfo[$this->physicalStructure[1]]['files'][$fileGrpThumb];
330
                    break;
331
                }
332
            }
333
        }
334
        // Get the files this structure element is pointing at.
335
        $details['files'] = [];
336
        $fileUse = $this->_getFileGrps();
337
        // Get the file representations from fileSec node.
338
        foreach ($structure->children('http://www.loc.gov/METS/')->fptr as $fptr) {
339
            // Check if file has valid @USE attribute.
340
            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

340
            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...
341
                $details['files'][$fileUse[(string) $fptr->attributes()->FILEID]] = (string) $fptr->attributes()->FILEID;
342
            }
343
        }
344
        // Keep for later usage.
345
        $this->logicalUnits[$details['id']] = $details;
346
        // Walk the structure recursively? And are there any children of the current element?
347
        if (
348
            $recursive
349
            && count($structure->children('http://www.loc.gov/METS/')->div)
350
        ) {
351
            $details['children'] = [];
352
            foreach ($structure->children('http://www.loc.gov/METS/')->div as $child) {
353
                // Repeat for all children.
354
                $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\Docume...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

354
                $details['children'][] = $this->getLogicalStructureInfo(/** @scrutinizer ignore-type */ $child, true);
Loading history...
355
            }
356
        }
357
        return $details;
358
    }
359
360
    /**
361
     * {@inheritDoc}
362
     * @see Document::getMetadata()
363
     */
364
    public function getMetadata($id, $cPid = 0)
365
    {
366
        // Make sure $cPid is a non-negative integer.
367
        $cPid = max(intval($cPid), 0);
368
        // If $cPid is not given, try to get it elsewhere.
369
        if (
370
            !$cPid
371
            && ($this->cPid || $this->pid)
372
        ) {
373
            // Retain current PID.
374
            $cPid = ($this->cPid ? $this->cPid : $this->pid);
375
        } elseif (!$cPid) {
376
            $this->logger->warning('Invalid PID ' . $cPid . ' for metadata definitions');
377
            return [];
378
        }
379
        // Get metadata from parsed metadata array if available.
380
        if (
381
            !empty($this->metadataArray[$id])
382
            && $this->metadataArray[0] == $cPid
383
        ) {
384
            return $this->metadataArray[$id];
385
        }
386
        // Initialize metadata array with empty values.
387
        $metadata = [
388
            'title' => [],
389
            'title_sorting' => [],
390
            'author' => [],
391
            'place' => [],
392
            'year' => [],
393
            'prod_id' => [],
394
            'record_id' => [],
395
            'opac_id' => [],
396
            'union_id' => [],
397
            'urn' => [],
398
            'purl' => [],
399
            'type' => [],
400
            'volume' => [],
401
            'volume_sorting' => [],
402
            'license' => [],
403
            'terms' => [],
404
            'restrictions' => [],
405
            'out_of_print' => [],
406
            'rights_info' => [],
407
            'collection' => [],
408
            'owner' => [],
409
            'mets_label' => [],
410
            'mets_orderlabel' => [],
411
            'document_format' => ['METS'],
412
        ];
413
        // Get the logical structure node's @DMDID.
414
        if (!empty($this->logicalUnits[$id])) {
415
            $dmdIds = $this->logicalUnits[$id]['dmdId'];
416
        } else {
417
            $dmdIds = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $id . '"]/@DMDID');
418
            $dmdIds = (string) $dmdIds[0];
419
        }
420
        if (!empty($dmdIds)) {
421
            // Handle multiple DMDIDs separately.
422
            $dmdIds = explode(' ', $dmdIds);
423
            $hasSupportedMetadata = false;
424
        } else {
425
            // There is no dmdSec for this structure node.
426
            return [];
427
        }
428
        // Load available metadata formats and dmdSecs.
429
        $this->loadFormats();
430
        $this->_getDmdSec();
431
        foreach ($dmdIds as $dmdId) {
432
            // Is this metadata format supported?
433
            if (!empty($this->formats[$this->dmdSec[$dmdId]['type']])) {
434
                if (!empty($this->formats[$this->dmdSec[$dmdId]['type']]['class'])) {
435
                    $class = $this->formats[$this->dmdSec[$dmdId]['type']]['class'];
436
                    // Get the metadata from class.
437
                    if (
438
                        class_exists($class)
439
                        && ($obj = GeneralUtility::makeInstance($class)) instanceof MetadataInterface
440
                    ) {
441
                        $obj->extractMetadata($this->dmdSec[$dmdId]['xml'], $metadata);
442
                    } else {
443
                        $this->logger->warning('Invalid class/method "' . $class . '->extractMetadata()" for metadata format "' . $this->dmdSec[$dmdId]['type'] . '"');
444
                    }
445
                }
446
            } else {
447
                $this->logger->notice('Unsupported metadata format "' . $this->dmdSec[$dmdId]['type'] . '" in dmdSec with @ID "' . $dmdId . '"');
1 ignored issue
show
Bug introduced by
The method notice() does not exist on TYPO3\CMS\Core\Log\LogManager. ( Ignorable by Annotation )

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

447
                $this->logger->/** @scrutinizer ignore-call */ 
448
                               notice('Unsupported metadata format "' . $this->dmdSec[$dmdId]['type'] . '" in dmdSec with @ID "' . $dmdId . '"');

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...
448
                // Continue searching for supported metadata with next @DMDID.
449
                continue;
450
            }
451
            // Get the structure's type.
452
            if (!empty($this->logicalUnits[$id])) {
453
                $metadata['type'] = [$this->logicalUnits[$id]['type']];
454
            } else {
455
                $struct = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $id . '"]/@TYPE');
456
                if (!empty($struct)) {
457
                    $metadata['type'] = [(string) $struct[0]];
458
                }
459
            }
460
            // Get the additional metadata from database.
461
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
462
                ->getQueryBuilderForTable('tx_dlf_metadata');
463
            // Get hidden records, too.
464
            $queryBuilder
465
                ->getRestrictions()
466
                ->removeByType(HiddenRestriction::class);
467
            // Get all metadata with configured xpath and applicable format first.
468
            $resultWithFormat = $queryBuilder
469
                ->select(
470
                    'tx_dlf_metadata.index_name AS index_name',
471
                    'tx_dlf_metadataformat_joins.xpath AS xpath',
472
                    'tx_dlf_metadataformat_joins.xpath_sorting AS xpath_sorting',
473
                    'tx_dlf_metadata.is_sortable AS is_sortable',
474
                    'tx_dlf_metadata.default_value AS default_value',
475
                    'tx_dlf_metadata.format AS format'
476
                )
477
                ->from('tx_dlf_metadata')
478
                ->innerJoin(
479
                    'tx_dlf_metadata',
480
                    'tx_dlf_metadataformat',
481
                    'tx_dlf_metadataformat_joins',
482
                    $queryBuilder->expr()->eq(
483
                        'tx_dlf_metadataformat_joins.parent_id',
484
                        'tx_dlf_metadata.uid'
485
                    )
486
                )
487
                ->innerJoin(
488
                    'tx_dlf_metadataformat_joins',
489
                    'tx_dlf_formats',
490
                    'tx_dlf_formats_joins',
491
                    $queryBuilder->expr()->eq(
492
                        'tx_dlf_formats_joins.uid',
493
                        'tx_dlf_metadataformat_joins.encoded'
494
                    )
495
                )
496
                ->where(
497
                    $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($cPid)),
498
                    $queryBuilder->expr()->eq('tx_dlf_metadata.l18n_parent', 0),
499
                    $queryBuilder->expr()->eq('tx_dlf_metadataformat_joins.pid', intval($cPid)),
500
                    $queryBuilder->expr()->eq('tx_dlf_formats_joins.type', $queryBuilder->createNamedParameter($this->dmdSec[$dmdId]['type']))
501
                )
502
                ->execute();
503
            // Get all metadata without a format, but with a default value next.
504
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
505
                ->getQueryBuilderForTable('tx_dlf_metadata');
506
            // Get hidden records, too.
507
            $queryBuilder
508
                ->getRestrictions()
509
                ->removeByType(HiddenRestriction::class);
510
            $resultWithoutFormat = $queryBuilder
511
                ->select(
512
                    'tx_dlf_metadata.index_name AS index_name',
513
                    'tx_dlf_metadata.is_sortable AS is_sortable',
514
                    'tx_dlf_metadata.default_value AS default_value',
515
                    'tx_dlf_metadata.format AS format'
516
                )
517
                ->from('tx_dlf_metadata')
518
                ->where(
519
                    $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($cPid)),
520
                    $queryBuilder->expr()->eq('tx_dlf_metadata.l18n_parent', 0),
521
                    $queryBuilder->expr()->eq('tx_dlf_metadata.format', 0),
522
                    $queryBuilder->expr()->neq('tx_dlf_metadata.default_value', $queryBuilder->createNamedParameter(''))
523
                )
524
                ->execute();
525
            // Merge both result sets.
526
            $allResults = array_merge($resultWithFormat->fetchAll(), $resultWithoutFormat->fetchAll());
527
            // We need a \DOMDocument here, because SimpleXML doesn't support XPath functions properly.
528
            $domNode = dom_import_simplexml($this->dmdSec[$dmdId]['xml']);
529
            $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

529
            $domXPath = new \DOMXPath(/** @scrutinizer ignore-type */ $domNode->ownerDocument);
Loading history...
530
            $this->registerNamespaces($domXPath);
531
            // OK, now make the XPath queries.
532
            foreach ($allResults as $resArray) {
533
                // Set metadata field's value(s).
534
                if (
535
                    $resArray['format'] > 0
536
                    && !empty($resArray['xpath'])
537
                    && ($values = $domXPath->evaluate($resArray['xpath'], $domNode))
538
                ) {
539
                    if (
540
                        $values instanceof \DOMNodeList
541
                        && $values->length > 0
542
                    ) {
543
                        $metadata[$resArray['index_name']] = [];
544
                        foreach ($values as $value) {
545
                            $metadata[$resArray['index_name']][] = trim((string) $value->nodeValue);
546
                        }
547
                    } elseif (!($values instanceof \DOMNodeList)) {
548
                        $metadata[$resArray['index_name']] = [trim((string) $values)];
549
                    }
550
                }
551
                // Set default value if applicable.
552
                if (
553
                    empty($metadata[$resArray['index_name']][0])
554
                    && strlen($resArray['default_value']) > 0
555
                ) {
556
                    $metadata[$resArray['index_name']] = [$resArray['default_value']];
557
                }
558
                // Set sorting value if applicable.
559
                if (
560
                    !empty($metadata[$resArray['index_name']])
561
                    && $resArray['is_sortable']
562
                ) {
563
                    if (
564
                        $resArray['format'] > 0
565
                        && !empty($resArray['xpath_sorting'])
566
                        && ($values = $domXPath->evaluate($resArray['xpath_sorting'], $domNode))
567
                    ) {
568
                        if (
569
                            $values instanceof \DOMNodeList
570
                            && $values->length > 0
571
                        ) {
572
                            $metadata[$resArray['index_name'] . '_sorting'][0] = trim((string) $values->item(0)->nodeValue);
573
                        } elseif (!($values instanceof \DOMNodeList)) {
574
                            $metadata[$resArray['index_name'] . '_sorting'][0] = trim((string) $values);
575
                        }
576
                    }
577
                    if (empty($metadata[$resArray['index_name'] . '_sorting'][0])) {
578
                        $metadata[$resArray['index_name'] . '_sorting'][0] = $metadata[$resArray['index_name']][0];
579
                    }
580
                }
581
            }
582
            // Set title to empty string if not present.
583
            if (empty($metadata['title'][0])) {
584
                $metadata['title'][0] = '';
585
                $metadata['title_sorting'][0] = '';
586
            }
587
            // Add collections and owner from database to toplevel element if document is already saved.
588
            if (
589
                MathUtility::canBeInterpretedAsInteger($this->uid)
590
                && $id == $this->_getToplevelId()
591
            ) {
592
                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
593
                    ->getQueryBuilderForTable('tx_dlf_documents');
594
595
                $result = $queryBuilder
596
                    ->select(
597
                        'tx_dlf_collections_join.index_name AS index_name'
598
                    )
599
                    ->from('tx_dlf_documents')
600
                    ->innerJoin(
601
                        'tx_dlf_documents',
602
                        'tx_dlf_relations',
603
                        'tx_dlf_relations_joins',
604
                        $queryBuilder->expr()->eq(
605
                            'tx_dlf_relations_joins.uid_local',
606
                            'tx_dlf_documents.uid'
607
                        )
608
                    )
609
                    ->innerJoin(
610
                        'tx_dlf_relations_joins',
611
                        'tx_dlf_collections',
612
                        'tx_dlf_collections_join',
613
                        $queryBuilder->expr()->eq(
614
                            'tx_dlf_relations_joins.uid_foreign',
615
                            'tx_dlf_collections_join.uid'
616
                        )
617
                    )
618
                    ->where(
619
                        $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($cPid)),
620
                        $queryBuilder->expr()->eq('tx_dlf_documents.uid', intval($this->uid))
621
                    )
622
                    ->orderBy('tx_dlf_collections_join.index_name', 'ASC')
623
                    ->execute();
624
625
                $allResults = $result->fetchAll();
626
627
                foreach ($allResults as $resArray) {
628
                    if (!in_array($resArray['index_name'], $metadata['collection'])) {
629
                        $metadata['collection'][] = $resArray['index_name'];
630
                    }
631
                }
632
633
                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
634
                    ->getQueryBuilderForTable('tx_dlf_documents');
635
636
                $result = $queryBuilder
637
                    ->select(
638
                        'tx_dlf_documents.owner AS owner'
639
                    )
640
                    ->from('tx_dlf_documents')
641
                    ->where(
642
                        $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($cPid)),
643
                        $queryBuilder->expr()->eq('tx_dlf_documents.uid', intval($this->uid))
644
                    )
645
                    ->execute();
646
647
                $resArray = $result->fetch();
648
649
                $metadata['owner'][0] = $resArray['owner'];
650
            }
651
            // Extract metadata only from first supported dmdSec.
652
            $hasSupportedMetadata = true;
653
            break;
654
        }
655
        if ($hasSupportedMetadata) {
656
            return $metadata;
657
        } else {
658
            $this->logger->warning('No supported metadata found for logical structure with @ID "' . $id . '"');
659
            return [];
660
        }
661
    }
662
663
    /**
664
     * {@inheritDoc}
665
     * @see FullTextDocument::getFullText()
666
     */
667
    public function getFullText($id)
668
    {
669
        $fullText = '';
670
671
        // Load fileGrps and check for full text files.
672
        $this->_getFileGrps();
673
        if ($this->hasFullText) {
674
            $fullText = $this->getFullTextFromXml($id);
675
        }
676
        return $fullText;
677
    }
678
679
    /**
680
     * {@inheritDoc}
681
     * @see Document::getStructureDepth()
682
     */
683
    public function getStructureDepth($logId)
684
    {
685
        $ancestors = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $logId . '"]/ancestor::*');
686
        if (!empty($ancestors)) {
687
            return count($ancestors);
688
        } else {
689
            return 0;
690
        }
691
    }
692
693
    /**
694
     * {@inheritDoc}
695
     * @see Document::init()
696
     */
697
    protected function init()
698
    {
699
        // Get METS node from XML file.
700
        $this->registerNamespaces($this->xml);
701
        $mets = $this->xml->xpath('//mets:mets');
702
        if (!empty($mets)) {
703
            $this->mets = $mets[0];
0 ignored issues
show
Bug introduced by
The property mets is declared read-only in Kitodo\Dlf\Common\Document\MetsDocument.
Loading history...
704
            // Register namespaces.
705
            $this->registerNamespaces($this->mets);
706
        } else {
707
            $this->logger->error('No METS part found in document with UID ' . $this->uid);
1 ignored issue
show
Bug introduced by
The method error() does not exist on TYPO3\CMS\Core\Log\LogManager. ( Ignorable by Annotation )

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

707
            $this->logger->/** @scrutinizer ignore-call */ 
708
                           error('No METS part found in document with UID ' . $this->uid);

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...
708
        }
709
    }
710
711
    /**
712
     * {@inheritDoc}
713
     * @see Document::loadLocation()
714
     */
715
    protected function loadLocation($location)
716
    {
717
        $fileResource = GeneralUtility::getUrl($location);
718
        if ($fileResource !== false) {
719
            $xml = Helper::getXmlFileAsString($fileResource);
720
            // Set some basic properties.
721
            if ($xml !== false) {
722
                $this->xml = $xml;
723
                return true;
724
            }
725
        }
726
        $this->logger->error('Could not load XML file from "' . $location . '"');
727
        return false;
728
    }
729
730
    /**
731
     * {@inheritDoc}
732
     * @see FullTextDocument::ensureHasFullTextIsSet()
733
     */
734
    protected function ensureHasFullTextIsSet()
735
    {
736
        // Are the fileGrps already loaded?
737
        if (!$this->fileGrpsLoaded) {
738
            $this->_getFileGrps();
739
        }
740
    }
741
742
    /**
743
     * {@inheritDoc}
744
     * @see Document::getParentDocumentUid()
745
     */
746
    protected function getParentDocumentUidForSaving($pid, $core, $owner)
747
    {
748
        $partof = 0;
749
        // Get the closest ancestor of the current document which has a MPTR child.
750
        $parentMptr = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $this->_getToplevelId() . '"]/ancestor::mets:div[./mets:mptr][1]/mets:mptr');
751
        if (!empty($parentMptr)) {
752
            $parentLocation = (string) $parentMptr[0]->attributes('http://www.w3.org/1999/xlink')->href;
753
            if ($parentLocation != $this->location) {
754
                $parentDoc = self::getInstance($parentLocation, $pid);
755
                if ($parentDoc->ready) {
756
                    if ($parentDoc->pid != $pid) {
757
                        $parentDoc->save($pid, $core, $owner);
758
                    }
759
                    $partof = $parentDoc->uid;
760
                }
761
            }
762
        }
763
        return $partof;
764
    }
765
766
    /**
767
     * {@inheritDoc}
768
     * @see Document::setPreloadedDocument()
769
     */
770
    protected function setPreloadedDocument($preloadedDocument)
771
    {
772
773
        if ($preloadedDocument instanceof \SimpleXMLElement) {
774
            $this->xml = $preloadedDocument;
775
            return true;
776
        }
777
        return false;
778
    }
779
780
    /**
781
     * {@inheritDoc}
782
     * @see Document::getDocument()
783
     */
784
    protected function getDocument()
785
    {
786
        return $this->mets;
787
    }
788
789
    /**
790
     * This builds an array of the document's dmdSecs
791
     *
792
     * @access protected
793
     *
794
     * @return array Array of dmdSecs with their IDs as array key
795
     */
796
    protected function _getDmdSec()
797
    {
798
        if (!$this->dmdSecLoaded) {
799
            // Get available data formats.
800
            $this->loadFormats();
801
            // Get dmdSec nodes from METS.
802
            $dmdIds = $this->mets->xpath('./mets:dmdSec/@ID');
803
            if (!empty($dmdIds)) {
804
                foreach ($dmdIds as $dmdId) {
805
                    if ($type = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[not(@MDTYPE="OTHER")]/@MDTYPE')) {
806
                        if (!empty($this->formats[(string) $type[0]])) {
807
                            $type = (string) $type[0];
808
                            $xml = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[@MDTYPE="' . $type . '"]/mets:xmlData/' . strtolower($type) . ':' . $this->formats[$type]['rootElement']);
809
                        }
810
                    } elseif ($type = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[@MDTYPE="OTHER"]/@OTHERMDTYPE')) {
811
                        if (!empty($this->formats[(string) $type[0]])) {
812
                            $type = (string) $type[0];
813
                            $xml = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[@MDTYPE="OTHER"][@OTHERMDTYPE="' . $type . '"]/mets:xmlData/' . strtolower($type) . ':' . $this->formats[$type]['rootElement']);
814
                        }
815
                    }
816
                    if (!empty($xml)) {
817
                        $this->dmdSec[(string) $dmdId]['type'] = $type;
0 ignored issues
show
Bug introduced by
The property dmdSec is declared read-only in Kitodo\Dlf\Common\Document\MetsDocument.
Loading history...
818
                        $this->dmdSec[(string) $dmdId]['xml'] = $xml[0];
819
                        $this->registerNamespaces($this->dmdSec[(string) $dmdId]['xml']);
820
                    }
821
                }
822
            }
823
            $this->dmdSecLoaded = true;
824
        }
825
        return $this->dmdSec;
826
    }
827
828
    /**
829
     * This builds the file ID -> USE concordance
830
     *
831
     * @access protected
832
     *
833
     * @return array Array of file use groups with file IDs
834
     */
835
    protected function _getFileGrps()
836
    {
837
        if (!$this->fileGrpsLoaded) {
838
            // Get configured USE attributes.
839
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
840
            $useGrps = GeneralUtility::trimExplode(',', $extConf['fileGrpImages']);
841
            if (!empty($extConf['fileGrpThumbs'])) {
842
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']));
843
            }
844
            if (!empty($extConf['fileGrpDownload'])) {
845
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpDownload']));
846
            }
847
            if (!empty($extConf['fileGrpFulltext'])) {
848
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpFulltext']));
849
            }
850
            if (!empty($extConf['fileGrpAudio'])) {
851
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpAudio']));
852
            }
853
            // Get all file groups.
854
            $fileGrps = $this->mets->xpath('./mets:fileSec/mets:fileGrp');
855
            if (!empty($fileGrps)) {
856
                // Build concordance for configured USE attributes.
857
                foreach ($fileGrps as $fileGrp) {
858
                    if (in_array((string) $fileGrp['USE'], $useGrps)) {
859
                        foreach ($fileGrp->children('http://www.loc.gov/METS/')->file as $file) {
860
                            $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

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