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 (#685)
by
unknown
04:31 queued 01:50
created

Document::save()   F

Complexity

Conditions 37
Paths > 20000

Size

Total Lines 306
Code Lines 218

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 218
dl 0
loc 306
rs 0
c 0
b 0
f 0
cc 37
nc 4534282
nop 3

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\Indexer;
17
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
18
use TYPO3\CMS\Core\Database\ConnectionPool;
19
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
20
use TYPO3\CMS\Core\Log\LogManager;
21
use TYPO3\CMS\Core\Utility\GeneralUtility;
22
use TYPO3\CMS\Core\Utility\MathUtility;
23
use Ubl\Iiif\Presentation\Common\Model\Resources\IiifResourceInterface;
24
use Ubl\Iiif\Tools\IiifHelper;
25
26
/**
27
 * Document 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 string $location This holds the documents location
36
 * @property-read array $metadataArray This holds the documents' parsed metadata array
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
 * @property-read mixed $uid This holds the UID or the URL of the document
50
 * @abstract
51
 */
52
abstract class Document
53
{
54
    use XMLDocument;
55
56
    /**
57
     * This holds the logger
58
     *
59
     * @var LogManager
60
     * @access protected
61
     */
62
    protected $logger;
63
64
    /**
65
     * This holds the PID for the configuration
66
     *
67
     * @var int
68
     * @access protected
69
     */
70
    protected $cPid = 0;
71
72
    /**
73
     * The extension key
74
     *
75
     * @var string
76
     * @access public
77
     */
78
    public static $extKey = 'dlf';
79
80
    /**
81
     * This holds the configuration for all supported metadata encodings
82
     * @see loadFormats()
83
     *
84
     * @var array
85
     * @access protected
86
     */
87
    protected $formats = [
88
        'OAI' => [
89
            'rootElement' => 'OAI-PMH',
90
            'namespaceURI' => 'http://www.openarchives.org/OAI/2.0/',
91
        ],
92
        'METS' => [
93
            'rootElement' => 'mets',
94
            'namespaceURI' => 'http://www.loc.gov/METS/',
95
        ],
96
        'XLINK' => [
97
            'rootElement' => 'xlink',
98
            'namespaceURI' => 'http://www.w3.org/1999/xlink',
99
        ]
100
    ];
101
102
    /**
103
     * Are the available metadata formats loaded?
104
     * @see $formats
105
     *
106
     * @var bool
107
     * @access protected
108
     */
109
    protected $formatsLoaded = false;
110
111
    /**
112
     * Last searched logical and physical page
113
     *
114
     * @var array
115
     * @access protected
116
     */
117
    protected $lastSearchedPhysicalPage = ['logicalPage' => null, 'physicalPage' => null];
118
119
    /**
120
     * This holds the documents location
121
     *
122
     * @var string
123
     * @access protected
124
     */
125
    protected $location = '';
126
127
    /**
128
     * This holds the logical units
129
     *
130
     * @var array
131
     * @access protected
132
     */
133
    protected $logicalUnits = [];
134
135
    /**
136
     * This holds the documents' parsed metadata array with their corresponding
137
     * structMap//div's ID (METS) or Range / Manifest / Sequence ID (IIIF) as array key
138
     *
139
     * @var array
140
     * @access protected
141
     */
142
    protected $metadataArray = [];
143
144
    /**
145
     * Is the metadata array loaded?
146
     * @see $metadataArray
147
     *
148
     * @var bool
149
     * @access protected
150
     */
151
    protected $metadataArrayLoaded = false;
152
153
    /**
154
     * The holds the total number of pages
155
     *
156
     * @var int
157
     * @access protected
158
     */
159
    protected $numPages = 0;
160
161
    /**
162
     * This holds the UID of the parent document or zero if not multi-volumed
163
     *
164
     * @var int
165
     * @access protected
166
     */
167
    protected $parentId = 0;
168
169
    /**
170
     * This holds the physical structure
171
     *
172
     * @var array
173
     * @access protected
174
     */
175
    protected $physicalStructure = [];
176
177
    /**
178
     * This holds the physical structure metadata
179
     *
180
     * @var array
181
     * @access protected
182
     */
183
    protected $physicalStructureInfo = [];
184
185
    /**
186
     * Is the physical structure loaded?
187
     * @see $physicalStructure
188
     *
189
     * @var bool
190
     * @access protected
191
     */
192
    protected $physicalStructureLoaded = false;
193
194
    /**
195
     * This holds the PID of the document or zero if not in database
196
     *
197
     * @var int
198
     * @access protected
199
     */
200
    protected $pid = 0;
201
202
    /**
203
     * Is the document instantiated successfully?
204
     *
205
     * @var bool
206
     * @access protected
207
     */
208
    protected $ready = false;
209
210
    /**
211
     * The METS file's / IIIF manifest's record identifier
212
     *
213
     * @var string
214
     * @access protected
215
     */
216
    protected $recordId;
217
218
    /**
219
     * This holds the singleton object of the document
220
     *
221
     * @var array (\Kitodo\Dlf\Common\Document\Document)
222
     * @static
223
     * @access protected
224
     */
225
    protected static $registry = [];
226
227
    /**
228
     * This holds the UID of the root document or zero if not multi-volumed
229
     *
230
     * @var int
231
     * @access protected
232
     */
233
    protected $rootId = 0;
234
235
    /**
236
     * Is the root id loaded?
237
     * @see $rootId
238
     *
239
     * @var bool
240
     * @access protected
241
     */
242
    protected $rootIdLoaded = false;
243
244
    /**
245
     * This holds the smLinks between logical and physical structMap
246
     *
247
     * @var array
248
     * @access protected
249
     */
250
    protected $smLinks = ['l2p' => [], 'p2l' => []];
251
252
    /**
253
     * Are the smLinks loaded?
254
     * @see $smLinks
255
     *
256
     * @var bool
257
     * @access protected
258
     */
259
    protected $smLinksLoaded = false;
260
261
    /**
262
     * This holds the logical structure
263
     *
264
     * @var array
265
     * @access protected
266
     */
267
    protected $tableOfContents = [];
268
269
    /**
270
     * Is the table of contents loaded?
271
     * @see $tableOfContents
272
     *
273
     * @var bool
274
     * @access protected
275
     */
276
    protected $tableOfContentsLoaded = false;
277
278
    /**
279
     * This holds the document's thumbnail location
280
     *
281
     * @var string
282
     * @access protected
283
     */
284
    protected $thumbnail = '';
285
286
    /**
287
     * Is the document's thumbnail location loaded?
288
     * @see $thumbnail
289
     *
290
     * @var bool
291
     * @access protected
292
     */
293
    protected $thumbnailLoaded = false;
294
295
    /**
296
     * This holds the toplevel structure's @ID (METS) or the manifest's @id (IIIF)
297
     *
298
     * @var string
299
     * @access protected
300
     */
301
    protected $toplevelId = '';
302
303
    /**
304
     * This holds the UID or the URL of the document
305
     *
306
     * @var mixed
307
     * @access protected
308
     */
309
    protected $uid = 0;
310
311
    /**
312
     * This holds the whole XML file as \SimpleXMLElement object
313
     *
314
     * @var \SimpleXMLElement
315
     * @access protected
316
     */
317
    protected $xml;
318
319
    /**
320
     * This clears the static registry to prevent memory exhaustion
321
     *
322
     * @access public
323
     *
324
     * @static
325
     *
326
     * @return void
327
     */
328
    public static function clearRegistry()
329
    {
330
        // Reset registry array.
331
        self::$registry = [];
332
    }
333
334
    /**
335
     * This is a singleton class, thus an instance must be created by this method
336
     *
337
     * @access public
338
     *
339
     * @static
340
     *
341
     * @param mixed $uid: The unique identifier of the document to parse, the URL of XML file or the IRI of the IIIF resource
342
     * @param int $pid: If > 0, then only document with this PID gets loaded
343
     * @param bool $forceReload: Force reloading the document instead of returning the cached instance
344
     *
345
     * @return \Kitodo\Dlf\Common\Document\Document Instance of this class, either MetsDocument or IiifManifest
346
     */
347
    public static function &getInstance($uid, $pid = 0, $forceReload = false)
348
    {
349
        // Sanitize input.
350
        $pid = max(intval($pid), 0);
351
        if (!$forceReload) {
352
            $regObj = Helper::digest($uid);
353
            if (
354
                is_object(self::$registry[$regObj])
355
                && self::$registry[$regObj] instanceof self
356
            ) {
357
                // Check if instance has given PID.
358
                if (
359
                    !$pid
360
                    || !self::$registry[$regObj]->pid
361
                    || $pid == self::$registry[$regObj]->pid
362
                ) {
363
                    // Return singleton instance if available.
364
                    return self::$registry[$regObj];
365
                }
366
            } else {
367
                // Check the user's session...
368
                $sessionData = Helper::loadFromSession(get_called_class());
369
                if (
370
                    is_object($sessionData[$regObj])
371
                    && $sessionData[$regObj] instanceof self
372
                ) {
373
                    // Check if instance has given PID.
374
                    if (
375
                        !$pid
376
                        || !$sessionData[$regObj]->pid
377
                        || $pid == $sessionData[$regObj]->pid
378
                    ) {
379
                        // ...and restore registry.
380
                        self::$registry[$regObj] = $sessionData[$regObj];
381
                        return self::$registry[$regObj];
382
                    }
383
                }
384
            }
385
        }
386
        // Create new instance depending on format (METS or IIIF) ...
387
        $instance = null;
388
        $documentFormat = null;
389
        $xml = null;
390
        $iiif = null;
391
        // Try to get document format from database
392
        if (MathUtility::canBeInterpretedAsInteger($uid)) {
393
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
394
                ->getQueryBuilderForTable('tx_dlf_documents');
395
396
            $queryBuilder
397
                ->select(
398
                    'tx_dlf_documents.location AS location',
399
                    'tx_dlf_documents.document_format AS document_format'
400
                )
401
                ->from('tx_dlf_documents');
402
403
            // Get UID of document with given record identifier.
404
            if ($pid) {
405
                $queryBuilder
406
                    ->where(
407
                        $queryBuilder->expr()->eq('tx_dlf_documents.uid', intval($uid)),
408
                        $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($pid)),
409
                        Helper::whereExpression('tx_dlf_documents')
410
                    );
411
            } else {
412
                $queryBuilder
413
                    ->where(
414
                        $queryBuilder->expr()->eq('tx_dlf_documents.uid', intval($uid)),
415
                        Helper::whereExpression('tx_dlf_documents')
416
                    );
417
            }
418
419
            $result = $queryBuilder
420
                ->setMaxResults(1)
421
                ->execute();
422
423
            if ($resArray = $result->fetch()) {
424
                $documentFormat = $resArray['document_format'];
425
            }
426
        } else {
427
            // Get document format from content of remote document
428
            // Cast to string for safety reasons.
429
            $location = (string) $uid;
430
            // Try to load a file from the url
431
            if (GeneralUtility::isValidUrl($location)) {
432
                // Load extension configuration
433
                $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
434
                // Set user-agent to identify self when fetching XML data.
435
                if (!empty($extConf['useragent'])) {
436
                    @ini_set('user_agent', $extConf['useragent']);
437
                }
438
                $content = GeneralUtility::getUrl($location);
439
                if ($content !== false) {
440
                    $xml = Helper::getXmlFileAsString($content);
441
                    if ($xml !== false) {
442
                        /* @var $xml \SimpleXMLElement */
443
                        $xml->registerXPathNamespace('mets', 'http://www.loc.gov/METS/');
444
                        $xpathResult = $xml->xpath('//mets:mets');
445
                        $documentFormat = !empty($xpathResult) ? 'METS' : null;
446
                    } else {
447
                        // Try to load file as IIIF resource instead.
448
                        $contentAsJsonArray = json_decode($content, true);
449
                        if ($contentAsJsonArray !== null) {
450
                            // Load plugin configuration.
451
                            $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
452
                            IiifHelper::setUrlReader(IiifUrlReader::getInstance());
0 ignored issues
show
Bug introduced by
The type Kitodo\Dlf\Common\Document\IiifUrlReader 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...
453
                            IiifHelper::setMaxThumbnailHeight($conf['iiifThumbnailHeight']);
454
                            IiifHelper::setMaxThumbnailWidth($conf['iiifThumbnailWidth']);
455
                            $iiif = IiifHelper::loadIiifResource($contentAsJsonArray);
456
                            if ($iiif instanceof IiifResourceInterface) {
457
                                $documentFormat = 'IIIF';
458
                            }
459
                        }
460
                    }
461
                }
462
            }
463
        }
464
        // Sanitize input.
465
        $pid = max(intval($pid), 0);
466
        if ($documentFormat == 'METS') {
467
            $instance = new MetsDocument($uid, $pid, $xml);
468
        } elseif ($documentFormat == 'IIIF') {
469
            $instance = new IiifManifest($uid, $pid, $iiif);
470
        }
471
        // Save instance to registry.
472
        if (
473
            $instance instanceof self
474
            && $instance->ready) {
475
            self::$registry[Helper::digest($instance->uid)] = $instance;
476
            if ($instance->uid != $instance->location) {
477
                self::$registry[Helper::digest($instance->location)] = $instance;
478
            }
479
            // Load extension configuration
480
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
481
            // Save registry to session if caching is enabled.
482
            if (!empty($extConf['caching'])) {
483
                Helper::saveToSession(self::$registry, get_class($instance));
484
            }
485
            $instance->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(get_class($instance));
486
        }
487
        // Return new instance.
488
        return $instance;
489
    }
490
491
    /**
492
     * Source document PHP object which is represented by a Document instance
493
     *
494
     * @access protected
495
     *
496
     * @abstract
497
     *
498
     * @return \SimpleXMLElement|IiifResourceInterface An PHP object representation of
499
     * the current document. SimpleXMLElement for METS, IiifResourceInterface for IIIF
500
     */
501
    protected abstract function getDocument();
502
503
    /**
504
     * This extracts all the metadata for a logical structure node
505
     *
506
     * @access public
507
     *
508
     * @abstract
509
     *
510
     * @param string $id: The @ID attribute of the logical structure node (METS) or the @id property
511
     * of the Manifest / Range (IIIF)
512
     * @param int $cPid: The PID for the metadata definitions
513
     *                       (defaults to $this->cPid or $this->pid)
514
     *
515
     * @return array The logical structure node's / the IIIF resource's parsed metadata array
516
     */
517
    public abstract function getMetadata($id, $cPid = 0);
518
519
    /**
520
     * This gets the location of a downloadable file for a physical page or track
521
     *
522
     * @access public
523
     *
524
     * @abstract
525
     *
526
     * @param string $id: The @ID attribute of the file node (METS) or the @id property of the IIIF resource
527
     *
528
     * @return string    The file's location as URL
529
     */
530
    public abstract function getDownloadLocation($id);
531
532
    /**
533
     * This gets the location of a file representing a physical page or track
534
     *
535
     * @access public
536
     *
537
     * @abstract
538
     *
539
     * @param string $id: The @ID attribute of the file node (METS) or the @id property of the IIIF resource
540
     *
541
     * @return string The file's location as URL
542
     */
543
    public abstract function getFileLocation($id);
544
545
    /**
546
     * This gets the MIME type of a file representing a physical page or track
547
     *
548
     * @access public
549
     *
550
     * @abstract
551
     *
552
     * @param string $id: The @ID attribute of the file node
553
     *
554
     * @return string The file's MIME type
555
     */
556
    public abstract function getFileMimeType($id);
557
558
    /**
559
     * This gets details about a logical structure element
560
     *
561
     * @access public
562
     *
563
     * @abstract
564
     *
565
     * @param string $id: The @ID attribute of the logical structure node (METS) or
566
     * the @id property of the Manifest / Range (IIIF)
567
     * @param bool $recursive: Whether to include the child elements / resources
568
     *
569
     * @return array Array of the element's id, label, type and physical page indexes/mptr link
570
     */
571
    public abstract function getLogicalStructure($id, $recursive = false);
572
573
    /**
574
     * This returns the first corresponding physical page number of a given logical page label
575
     *
576
     * @access public
577
     *
578
     * @param string $logicalPage: The label (or a part of the label) of the logical page
579
     *
580
     * @return int The physical page number
581
     */
582
    public function getPhysicalPage($logicalPage)
583
    {
584
        if (
585
            !empty($this->lastSearchedPhysicalPage['logicalPage'])
586
            && $this->lastSearchedPhysicalPage['logicalPage'] == $logicalPage
587
        ) {
588
            return $this->lastSearchedPhysicalPage['physicalPage'];
589
        } else {
590
            $physicalPage = 0;
591
            foreach ($this->physicalStructureInfo as $page) {
592
                if (strpos($page['orderlabel'], $logicalPage) !== false) {
593
                    $this->lastSearchedPhysicalPage['logicalPage'] = $logicalPage;
594
                    $this->lastSearchedPhysicalPage['physicalPage'] = $physicalPage;
595
                    return $physicalPage;
596
                }
597
                $physicalPage++;
598
            }
599
        }
600
        return 1;
601
    }
602
603
    /**
604
     * This determines a title for the given document
605
     *
606
     * @access public
607
     *
608
     * @static
609
     *
610
     * @param int $uid: The UID of the document
611
     * @param bool $recursive: Search superior documents for a title, too?
612
     *
613
     * @return string The title of the document itself or a parent document
614
     */
615
    public static function getTitle($uid, $recursive = false)
616
    {
617
        $title = '';
618
        // Sanitize input.
619
        $uid = max(intval($uid), 0);
620
        if ($uid) {
621
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
622
                ->getQueryBuilderForTable('tx_dlf_documents');
623
624
            $result = $queryBuilder
625
                ->select(
626
                    'tx_dlf_documents.title',
627
                    'tx_dlf_documents.partof'
628
                )
629
                ->from('tx_dlf_documents')
630
                ->where(
631
                    $queryBuilder->expr()->eq('tx_dlf_documents.uid', $uid),
632
                    Helper::whereExpression('tx_dlf_documents')
633
                )
634
                ->setMaxResults(1)
635
                ->execute();
636
637
            if ($resArray = $result->fetch()) {
638
                // Get title information.
639
                $title = $resArray['title'];
640
                $partof = $resArray['partof'];
641
                // Search parent documents recursively for a title?
642
                if (
643
                    $recursive
644
                    && empty($title)
645
                    && intval($partof)
646
                    && $partof != $uid
647
                ) {
648
                    $title = self::getTitle($partof, true);
649
                }
650
            } else {
651
                Helper::log('No document with UID ' . $uid . ' found or document not accessible', LOG_SEVERITY_WARNING);
652
            }
653
        } else {
654
            Helper::log('Invalid UID ' . $uid . ' for document', LOG_SEVERITY_ERROR);
655
        }
656
        return $title;
657
    }
658
659
    /**
660
     * This extracts all the metadata for the toplevel logical structure node / resource
661
     *
662
     * @access public
663
     *
664
     * @param int $cPid: The PID for the metadata definitions
665
     *
666
     * @return array The logical structure node's / resource's parsed metadata array
667
     */
668
    public function getTitleData($cPid = 0)
669
    {
670
        $titledata = $this->getMetadata($this->_getToplevelId(), $cPid);
671
        // Add information from METS structural map to titledata array.
672
        if ($this instanceof MetsDocument) {
673
            $this->addMetadataFromMets($titledata, $this->_getToplevelId());
674
        }
675
        // Set record identifier for METS file / IIIF manifest if not present.
676
        if (
677
            is_array($titledata)
678
            && array_key_exists('record_id', $titledata)
679
        ) {
680
            if (
681
                !empty($this->recordId)
682
                && !in_array($this->recordId, $titledata['record_id'])
683
            ) {
684
                array_unshift($titledata['record_id'], $this->recordId);
685
            }
686
        }
687
        return $titledata;
688
    }
689
690
    /**
691
     * Get the tree depth of a logical structure element within the table of content
692
     *
693
     * @access public
694
     *
695
     * @param string $logId: The id of the logical structure element whose depth is requested
696
     * @return int|bool tree depth as integer or false if no element with $logId exists within the TOC.
697
     */
698
    public function getStructureDepth($logId)
699
    {
700
        return $this->getTreeDepth($this->_getTableOfContents(), 1, $logId);
701
    }
702
703
    /**
704
     * This saves the document to the database and index
705
     *
706
     * @access public
707
     *
708
     * @param int $pid: The PID of the saved record
709
     * @param int $core: The UID of the Solr core for indexing
710
     * @param int|string $owner: UID or index_name of owner to set while indexing
711
     *
712
     * @return bool true on success or false on failure
713
     */
714
    public function save($pid = 0, $core = 0, $owner = null)
715
    {
716
        if (\TYPO3_MODE !== 'BE') {
717
            $this->logger->error('Saving a document is only allowed in the backend');
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

717
            $this->logger->/** @scrutinizer ignore-call */ 
718
                           error('Saving a document is only allowed in the backend');

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...
718
            return false;
719
        }
720
        // Make sure $pid is a non-negative integer.
721
        $pid = max(intval($pid), 0);
722
        // Make sure $core is a non-negative integer.
723
        $core = max(intval($core), 0);
724
        // If $pid is not given, try to get it elsewhere.
725
        if (
726
            !$pid
727
            && $this->pid
728
        ) {
729
            // Retain current PID.
730
            $pid = $this->pid;
731
        } elseif (!$pid) {
732
            $this->logger->error('Invalid PID ' . $pid . ' for document saving');
733
            return false;
734
        }
735
        // Set PID for metadata definitions.
736
        $this->cPid = $pid;
737
        // Set UID placeholder if not updating existing record.
738
        if ($pid != $this->pid) {
739
            $this->uid = uniqid('NEW');
0 ignored issues
show
Bug introduced by
The property uid is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
740
        }
741
        // Get metadata array.
742
        $metadata = $this->getTitleData($pid);
743
        // Check for record identifier.
744
        if (empty($metadata['record_id'][0])) {
745
            $this->logger->error('No record identifier found to avoid duplication');
746
            return false;
747
        }
748
        // Load plugin configuration.
749
        $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
750
751
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
752
            ->getQueryBuilderForTable('tx_dlf_structures');
753
754
        // Get UID for structure type.
755
        $result = $queryBuilder
756
            ->select('tx_dlf_structures.uid AS uid')
757
            ->from('tx_dlf_structures')
758
            ->where(
759
                $queryBuilder->expr()->eq('tx_dlf_structures.pid', intval($pid)),
760
                $queryBuilder->expr()->eq('tx_dlf_structures.index_name', $queryBuilder->expr()->literal($metadata['type'][0])),
761
                Helper::whereExpression('tx_dlf_structures')
762
            )
763
            ->setMaxResults(1)
764
            ->execute();
765
766
        if ($resArray = $result->fetch()) {
767
            $structure = $resArray['uid'];
768
        } else {
769
            $this->logger->error('Could not identify document/structure type "' . $queryBuilder->expr()->literal($metadata['type'][0]) . '"');
770
            return false;
771
        }
772
        $metadata['type'][0] = $structure;
773
774
        // Remove appended "valueURI" from authors' names for storing in database.
775
        foreach ($metadata['author'] as $i => $author) {
776
            $splitName = explode(chr(31), $author);
777
            $metadata['author'][$i] = $splitName[0];
778
        }
779
780
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
781
            ->getQueryBuilderForTable('tx_dlf_collections');
782
        // Get hidden records, too.
783
        $queryBuilder
784
            ->getRestrictions()
785
            ->removeByType(HiddenRestriction::class);
786
787
        // Get UIDs for collections.
788
        $result = $queryBuilder
789
            ->select(
790
                'tx_dlf_collections.index_name AS index_name',
791
                'tx_dlf_collections.uid AS uid'
792
            )
793
            ->from('tx_dlf_collections')
794
            ->where(
795
                $queryBuilder->expr()->eq('tx_dlf_collections.pid', intval($pid)),
796
                $queryBuilder->expr()->in('tx_dlf_collections.sys_language_uid', [-1, 0])
797
            )
798
            ->execute();
799
800
        $collUid = [];
801
        while ($resArray = $result->fetch()) {
802
            $collUid[$resArray['index_name']] = $resArray['uid'];
803
        }
804
        $collections = [];
805
        foreach ($metadata['collection'] as $collection) {
806
            if (!empty($collUid[$collection])) {
807
                // Add existing collection's UID.
808
                $collections[] = $collUid[$collection];
809
            } else {
810
                // Insert new collection.
811
                $collNewUid = uniqid('NEW');
812
                $collData['tx_dlf_collections'][$collNewUid] = [
813
                    'pid' => $pid,
814
                    'label' => $collection,
815
                    'index_name' => $collection,
816
                    'oai_name' => (!empty($conf['publishNewCollections']) ? Helper::getCleanString($collection) : ''),
817
                    'description' => '',
818
                    'documents' => 0,
819
                    'owner' => 0,
820
                    'status' => 0,
821
                ];
822
                $substUid = Helper::processDBasAdmin($collData);
823
                // Prevent double insertion.
824
                unset($collData);
825
                // Add new collection's UID.
826
                $collections[] = $substUid[$collNewUid];
827
                if (!(\TYPO3_REQUESTTYPE & \TYPO3_REQUESTTYPE_CLI)) {
828
                    Helper::addMessage(
829
                        htmlspecialchars(sprintf(Helper::getMessage('flash.newCollection'), $collection, $substUid[$collNewUid])),
830
                        Helper::getMessage('flash.attention', true),
831
                        \TYPO3\CMS\Core\Messaging\FlashMessage::INFO,
832
                        true
833
                    );
834
                }
835
            }
836
        }
837
        $metadata['collection'] = $collections;
838
839
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
840
            ->getQueryBuilderForTable('tx_dlf_libraries');
841
842
        // Get UID for owner.
843
        if (empty($owner)) {
844
            $owner = empty($metadata['owner'][0]) ? $metadata['owner'][0] : 'default';
845
        }
846
        if (!MathUtility::canBeInterpretedAsInteger($owner)) {
847
            $result = $queryBuilder
848
                ->select('tx_dlf_libraries.uid AS uid')
849
                ->from('tx_dlf_libraries')
850
                ->where(
851
                    $queryBuilder->expr()->eq('tx_dlf_libraries.pid', intval($pid)),
852
                    $queryBuilder->expr()->eq('tx_dlf_libraries.index_name', $queryBuilder->expr()->literal($owner)),
853
                    Helper::whereExpression('tx_dlf_libraries')
854
                )
855
                ->setMaxResults(1)
856
                ->execute();
857
858
            if ($resArray = $result->fetch()) {
859
                $ownerUid = $resArray['uid'];
860
            } else {
861
                // Insert new library.
862
                $libNewUid = uniqid('NEW');
863
                $libData['tx_dlf_libraries'][$libNewUid] = [
864
                    'pid' => $pid,
865
                    'label' => $owner,
866
                    'index_name' => $owner,
867
                    'website' => '',
868
                    'contact' => '',
869
                    'image' => '',
870
                    'oai_label' => '',
871
                    'oai_base' => '',
872
                    'opac_label' => '',
873
                    'opac_base' => '',
874
                    'union_label' => '',
875
                    'union_base' => '',
876
                ];
877
                $substUid = Helper::processDBasAdmin($libData);
878
                // Add new library's UID.
879
                $ownerUid = $substUid[$libNewUid];
880
                if (!(\TYPO3_REQUESTTYPE & \TYPO3_REQUESTTYPE_CLI)) {
881
                    Helper::addMessage(
882
                        htmlspecialchars(sprintf(Helper::getMessage('flash.newLibrary'), $owner, $ownerUid)),
883
                        Helper::getMessage('flash.attention', true),
884
                        \TYPO3\CMS\Core\Messaging\FlashMessage::INFO,
885
                        true
886
                    );
887
                }
888
            }
889
            $owner = $ownerUid;
890
        }
891
        $metadata['owner'][0] = $owner;
892
        // Get UID of parent document.
893
        $partof = $this->getParentDocumentUidForSaving($pid, $core, $owner);
894
        // Use the date of publication or title as alternative sorting metric for parts of multi-part works.
895
        if (!empty($partof)) {
896
            if (
897
                empty($metadata['volume'][0])
898
                && !empty($metadata['year'][0])
899
            ) {
900
                $metadata['volume'] = $metadata['year'];
901
            }
902
            if (empty($metadata['volume_sorting'][0])) {
903
                // If METS @ORDER is given it is preferred over year_sorting and year.
904
                if (!empty($metadata['mets_order'][0])) {
905
                    $metadata['volume_sorting'][0] = $metadata['mets_order'][0];
906
                } elseif (!empty($metadata['year_sorting'][0])) {
907
                    $metadata['volume_sorting'][0] = $metadata['year_sorting'][0];
908
                } elseif (!empty($metadata['year'][0])) {
909
                    $metadata['volume_sorting'][0] = $metadata['year'][0];
910
                }
911
            }
912
            // If volume_sorting is still empty, try to use title_sorting or METS @ORDERLABEL finally (workaround for newspapers)
913
            if (empty($metadata['volume_sorting'][0])) {
914
                if (!empty($metadata['title_sorting'][0])) {
915
                    $metadata['volume_sorting'][0] = $metadata['title_sorting'][0];
916
                } elseif (!empty($metadata['mets_orderlabel'][0])) {
917
                    $metadata['volume_sorting'][0] = $metadata['mets_orderlabel'][0];
918
                }
919
            }
920
        }
921
922
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
923
            ->getQueryBuilderForTable('tx_dlf_metadata');
924
925
        // Get metadata for lists and sorting.
926
        $result = $queryBuilder
927
            ->select(
928
                'tx_dlf_metadata.index_name AS index_name',
929
                'tx_dlf_metadata.is_listed AS is_listed',
930
                'tx_dlf_metadata.is_sortable AS is_sortable'
931
            )
932
            ->from('tx_dlf_metadata')
933
            ->where(
934
                $queryBuilder->expr()->orX(
935
                    $queryBuilder->expr()->eq('tx_dlf_metadata.is_listed', 1),
936
                    $queryBuilder->expr()->eq('tx_dlf_metadata.is_sortable', 1)
937
                ),
938
                $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($pid)),
939
                Helper::whereExpression('tx_dlf_metadata')
940
            )
941
            ->execute();
942
943
        $listed = [];
944
        $sortable = [];
945
946
        while ($resArray = $result->fetch()) {
947
            if (!empty($metadata[$resArray['index_name']])) {
948
                if ($resArray['is_listed']) {
949
                    $listed[$resArray['index_name']] = $metadata[$resArray['index_name']];
950
                }
951
                if ($resArray['is_sortable']) {
952
                    $sortable[$resArray['index_name']] = $metadata[$resArray['index_name']][0];
953
                }
954
            }
955
        }
956
        // Fill data array.
957
        $data['tx_dlf_documents'][$this->uid] = [
958
            'pid' => $pid,
959
            $GLOBALS['TCA']['tx_dlf_documents']['ctrl']['enablecolumns']['starttime'] => 0,
960
            $GLOBALS['TCA']['tx_dlf_documents']['ctrl']['enablecolumns']['endtime'] => 0,
961
            'prod_id' => $metadata['prod_id'][0],
962
            'location' => $this->location,
963
            'record_id' => $metadata['record_id'][0],
964
            'opac_id' => $metadata['opac_id'][0],
965
            'union_id' => $metadata['union_id'][0],
966
            'urn' => $metadata['urn'][0],
967
            'purl' => $metadata['purl'][0],
968
            'title' => $metadata['title'][0],
969
            'title_sorting' => $metadata['title_sorting'][0],
970
            'author' => implode('; ', $metadata['author']),
971
            'year' => implode('; ', $metadata['year']),
972
            'place' => implode('; ', $metadata['place']),
973
            'thumbnail' => $this->_getThumbnail(true),
974
            'metadata' => serialize($listed),
975
            'metadata_sorting' => serialize($sortable),
976
            'structure' => $metadata['type'][0],
977
            'partof' => $partof,
978
            'volume' => $metadata['volume'][0],
979
            'volume_sorting' => $metadata['volume_sorting'][0],
980
            'license' => $metadata['license'][0],
981
            'terms' => $metadata['terms'][0],
982
            'restrictions' => $metadata['restrictions'][0],
983
            'out_of_print' => $metadata['out_of_print'][0],
984
            'rights_info' => $metadata['rights_info'][0],
985
            'collections' => $metadata['collection'],
986
            'mets_label' => $metadata['mets_label'][0],
987
            'mets_orderlabel' => $metadata['mets_orderlabel'][0],
988
            'mets_order' => $metadata['mets_order'][0],
989
            'owner' => $metadata['owner'][0],
990
            'solrcore' => $core,
991
            'status' => 0,
992
            'document_format' => $metadata['document_format'][0],
993
        ];
994
        // Unhide hidden documents.
995
        if (!empty($conf['unhideOnIndex'])) {
996
            $data['tx_dlf_documents'][$this->uid][$GLOBALS['TCA']['tx_dlf_documents']['ctrl']['enablecolumns']['disabled']] = 0;
997
        }
998
        // Process data.
999
        $newIds = Helper::processDBasAdmin($data);
1000
        // Replace placeholder with actual UID.
1001
        if (strpos($this->uid, 'NEW') === 0) {
1002
            $this->uid = $newIds[$this->uid];
1003
            $this->pid = $pid;
0 ignored issues
show
Bug introduced by
The property pid is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
1004
            $this->parentId = $partof;
0 ignored issues
show
Bug introduced by
The property parentId is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
1005
        }
1006
        if (!(\TYPO3_REQUESTTYPE & \TYPO3_REQUESTTYPE_CLI)) {
1007
            Helper::addMessage(
1008
                htmlspecialchars(sprintf(Helper::getMessage('flash.documentSaved'), $metadata['title'][0], $this->uid)),
1009
                Helper::getMessage('flash.done', true),
1010
                \TYPO3\CMS\Core\Messaging\FlashMessage::OK,
1011
                true
1012
            );
1013
        }
1014
        // Add document to index.
1015
        if ($core) {
1016
            return Indexer::add($this, $core);
1017
        } else {
1018
            $this->logger->notice('Invalid UID "' . $core . '" for Solr core');
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

1018
            $this->logger->/** @scrutinizer ignore-call */ 
1019
                           notice('Invalid UID "' . $core . '" for Solr core');

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...
1019
            return false;
1020
        }
1021
    }
1022
1023
    /**
1024
     * This ensures that the recordId, if existent, is retrieved from the document
1025
     *
1026
     * @access protected
1027
     *
1028
     * @abstract
1029
     *
1030
     * @param int $pid: ID of the configuration page with the recordId config
1031
     *
1032
     */
1033
    protected abstract function establishRecordId($pid);
1034
1035
    /**
1036
     * Get the ID of the parent document if the current document has one. Also save a parent document
1037
     * to the database and the Solr index if their $pid and the current $pid differ.
1038
     * Currently only applies to METS documents.
1039
     *
1040
     * @access protected
1041
     *
1042
     * @abstract
1043
     *
1044
     * @return int The parent document's id.
1045
     */
1046
    protected abstract function getParentDocumentUidForSaving($pid, $core, $owner);
1047
1048
    /**
1049
     * This sets some basic class properties
1050
     *
1051
     * @access protected
1052
     *
1053
     * @abstract
1054
     *
1055
     * @return void
1056
     */
1057
    protected abstract function init();
1058
1059
    /**
1060
     * METS/IIIF specific part of loading a location
1061
     *
1062
     * @access protected
1063
     *
1064
     * @abstract
1065
     *
1066
     * @param string $location: The URL of the file to load
1067
     *
1068
     * @return bool true on success or false on failure
1069
     */
1070
    protected abstract function loadLocation($location);
1071
1072
    /**
1073
     * Reuse any document object that might have been already loaded to determine wether document is METS or IIIF
1074
     *
1075
     * @access protected
1076
     *
1077
     * @abstract
1078
     *
1079
     * @param \SimpleXMLElement|IiifResourceInterface $preloadedDocument: any instance that has already been loaded
1080
     *
1081
     * @return bool true if $preloadedDocument can actually be reused, false if it has to be loaded again
1082
     */
1083
    protected abstract function setPreloadedDocument($preloadedDocument);
1084
1085
    /**
1086
     * Load XML file / IIIF resource from URL
1087
     *
1088
     * @access protected
1089
     *
1090
     * @param string $location: The URL of the file to load
1091
     *
1092
     * @return bool true on success or false on failure
1093
     */
1094
    protected function load($location)
1095
    {
1096
        // Load XML / JSON-LD file.
1097
        if (GeneralUtility::isValidUrl($location)) {
1098
            // Load extension configuration
1099
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
1100
            // Set user-agent to identify self when fetching XML / JSON-LD data.
1101
            if (!empty($extConf['useragent'])) {
1102
                @ini_set('user_agent', $extConf['useragent']);
1103
            }
1104
            // the actual loading is format specific
1105
            return $this->loadLocation($location);
1106
        } else {
1107
            $this->logger->error('Invalid file location "' . $location . '" for document loading');
1108
        }
1109
        return false;
1110
    }
1111
1112
    /**
1113
     * Traverse a logical (sub-) structure tree to find the structure with the requested logical id and return it's depth.
1114
     *
1115
     * @access protected
1116
     *
1117
     * @param array $structure: logical structure array
1118
     * @param int $depth: current tree depth
1119
     * @param string $logId: ID of the logical structure whose depth is requested
1120
     *
1121
     * @return int|bool: false if structure with $logId is not a child of this substructure,
1122
     * or the actual depth.
1123
     */
1124
    protected function getTreeDepth($structure, $depth, $logId)
1125
    {
1126
        foreach ($structure as $element) {
1127
            if ($element['id'] == $logId) {
1128
                return $depth;
1129
            } elseif (array_key_exists('children', $element)) {
1130
                $foundInChildren = $this->getTreeDepth($element['children'], $depth + 1, $logId);
1131
                if ($foundInChildren !== false) {
1132
                    return $foundInChildren;
1133
                }
1134
            }
1135
        }
1136
        return false;
1137
    }
1138
1139
    /**
1140
     * This returns $this->cPid via __get()
1141
     *
1142
     * @access protected
1143
     *
1144
     * @return int The PID of the metadata definitions
1145
     */
1146
    protected function _getCPid()
1147
    {
1148
        return $this->cPid;
1149
    }
1150
1151
    /**
1152
     * This returns $this->location via __get()
1153
     *
1154
     * @access protected
1155
     *
1156
     * @return string The location of the document
1157
     */
1158
    protected function _getLocation()
1159
    {
1160
        return $this->location;
1161
    }
1162
1163
    /**
1164
     * Format specific part of building the document's metadata array
1165
     *
1166
     * @access protected
1167
     *
1168
     * @abstract
1169
     *
1170
     * @param int $cPid
1171
     */
1172
    protected abstract function prepareMetadataArray($cPid);
1173
1174
    /**
1175
     * This builds an array of the document's metadata
1176
     *
1177
     * @access protected
1178
     *
1179
     * @return array Array of metadata with their corresponding logical structure node ID as key
1180
     */
1181
    protected function _getMetadataArray()
1182
    {
1183
        // Set metadata definitions' PID.
1184
        $cPid = ($this->cPid ? $this->cPid : $this->pid);
1185
        if (!$cPid) {
1186
            $this->logger->error('Invalid PID ' . $cPid . ' for metadata definitions');
1187
            return [];
1188
        }
1189
        if (
1190
            !$this->metadataArrayLoaded
1191
            || $this->metadataArray[0] != $cPid
1192
        ) {
1193
            $this->prepareMetadataArray($cPid);
1194
            $this->metadataArray[0] = $cPid;
0 ignored issues
show
Bug introduced by
The property metadataArray is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
1195
            $this->metadataArrayLoaded = true;
1196
        }
1197
        return $this->metadataArray;
1198
    }
1199
1200
    /**
1201
     * This returns $this->numPages via __get()
1202
     *
1203
     * @access protected
1204
     *
1205
     * @return int The total number of pages and/or tracks
1206
     */
1207
    protected function _getNumPages()
1208
    {
1209
        $this->_getPhysicalStructure();
1210
        return $this->numPages;
1211
    }
1212
1213
    /**
1214
     * This returns $this->parentId via __get()
1215
     *
1216
     * @access protected
1217
     *
1218
     * @return int The UID of the parent document or zero if not applicable
1219
     */
1220
    protected function _getParentId()
1221
    {
1222
        return $this->parentId;
1223
    }
1224
1225
    /**
1226
     * This builds an array of the document's physical structure
1227
     *
1228
     * @access protected
1229
     *
1230
     * @abstract
1231
     *
1232
     * @return array Array of physical elements' id, type, label and file representations ordered
1233
     * by @ORDER attribute / IIIF Sequence's Canvases
1234
     */
1235
    protected abstract function _getPhysicalStructure();
1236
1237
    /**
1238
     * This gives an array of the document's physical structure metadata
1239
     *
1240
     * @access protected
1241
     *
1242
     * @return array Array of elements' type, label and file representations ordered by @ID attribute / Canvas order
1243
     */
1244
    protected function _getPhysicalStructureInfo()
1245
    {
1246
        // Is there no physical structure array yet?
1247
        if (!$this->physicalStructureLoaded) {
1248
            // Build physical structure array.
1249
            $this->_getPhysicalStructure();
1250
        }
1251
        return $this->physicalStructureInfo;
1252
    }
1253
1254
    /**
1255
     * This returns $this->pid via __get()
1256
     *
1257
     * @access protected
1258
     *
1259
     * @return int The PID of the document or zero if not in database
1260
     */
1261
    protected function _getPid()
1262
    {
1263
        return $this->pid;
1264
    }
1265
1266
    /**
1267
     * This returns $this->ready via __get()
1268
     *
1269
     * @access protected
1270
     *
1271
     * @return bool Is the document instantiated successfully?
1272
     */
1273
    protected function _getReady()
1274
    {
1275
        return $this->ready;
1276
    }
1277
1278
    /**
1279
     * This returns $this->recordId via __get()
1280
     *
1281
     * @access protected
1282
     *
1283
     * @return mixed The METS file's / IIIF manifest's record identifier
1284
     */
1285
    protected function _getRecordId()
1286
    {
1287
        return $this->recordId;
1288
    }
1289
1290
    /**
1291
     * This returns $this->rootId via __get()
1292
     *
1293
     * @access protected
1294
     *
1295
     * @return int The UID of the root document or zero if not applicable
1296
     */
1297
    protected function _getRootId()
1298
    {
1299
        if (!$this->rootIdLoaded) {
1300
            if ($this->parentId) {
1301
                $parent = self::getInstance($this->parentId, $this->pid);
1302
                $this->rootId = $parent->rootId;
0 ignored issues
show
Bug introduced by
The property rootId is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
1303
            }
1304
            $this->rootIdLoaded = true;
1305
        }
1306
        return $this->rootId;
1307
    }
1308
1309
    /**
1310
     * This returns the smLinks between logical and physical structMap (METS) and models the
1311
     * relation between IIIF Canvases and Manifests / Ranges in the same way
1312
     *
1313
     * @access protected
1314
     *
1315
     * @abstract
1316
     *
1317
     * @return array The links between logical and physical nodes / Range, Manifest and Canvas
1318
     */
1319
    protected abstract function _getSmLinks();
1320
1321
    /**
1322
     * This builds an array of the document's logical structure
1323
     *
1324
     * @access protected
1325
     *
1326
     * @return array Array of structure nodes' id, label, type and physical page indexes/mptr / Canvas link with original hierarchy preserved
1327
     */
1328
    protected function _getTableOfContents()
1329
    {
1330
        // Is there no logical structure array yet?
1331
        if (!$this->tableOfContentsLoaded) {
1332
            // Get all logical structures.
1333
            $this->getLogicalStructure('', true);
1334
            $this->tableOfContentsLoaded = true;
1335
        }
1336
        return $this->tableOfContents;
1337
    }
1338
1339
    /**
1340
     * This returns the document's thumbnail location
1341
     *
1342
     * @access protected
1343
     *
1344
     * @abstract
1345
     *
1346
     * @param bool $forceReload: Force reloading the thumbnail instead of returning the cached value
1347
     *
1348
     * @return string The document's thumbnail location
1349
     */
1350
    protected abstract function _getThumbnail($forceReload = false);
1351
1352
    /**
1353
     * This returns the ID of the toplevel logical structure node
1354
     *
1355
     * @access protected
1356
     *
1357
     * @abstract
1358
     *
1359
     * @return string The logical structure node's ID
1360
     */
1361
    protected abstract function _getToplevelId();
1362
1363
    /**
1364
     * This returns $this->uid via __get()
1365
     *
1366
     * @access protected
1367
     *
1368
     * @return mixed The UID or the URL of the document
1369
     */
1370
    protected function _getUid()
1371
    {
1372
        return $this->uid;
1373
    }
1374
1375
    /**
1376
     * This sets $this->cPid via __set()
1377
     *
1378
     * @access protected
1379
     *
1380
     * @param int $value: The new PID for the metadata definitions
1381
     *
1382
     * @return void
1383
     */
1384
    protected function _setCPid($value)
1385
    {
1386
        $this->cPid = max(intval($value), 0);
1387
    }
1388
1389
    /**
1390
     * This magic method is invoked each time a clone is called on the object variable
1391
     *
1392
     * @access protected
1393
     *
1394
     * @return void
1395
     */
1396
    protected function __clone()
1397
    {
1398
        // This method is defined as protected because singleton objects should not be cloned.
1399
    }
1400
1401
    /**
1402
     * This is a singleton class, thus the constructor should be private/protected
1403
     * (Get an instance of this class by calling \Kitodo\Dlf\Common\Document\Document::getInstance())
1404
     *
1405
     * @access protected
1406
     *
1407
     * @param int $uid: The UID of the document to parse or URL to XML file
1408
     * @param int $pid: If > 0, then only document with this PID gets loaded
1409
     * @param \SimpleXMLElement|IiifResourceInterface $preloadedDocument: Either null or the \SimpleXMLElement
1410
     * or IiifResourceInterface that has been loaded to determine the basic document format.
1411
     *
1412
     * @return void
1413
     */
1414
    protected function __construct($uid, $pid, $preloadedDocument)
1415
    {
1416
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1417
            ->getQueryBuilderForTable('tx_dlf_documents');
1418
        $location = '';
1419
        // Prepare to check database for the requested document.
1420
        if (MathUtility::canBeInterpretedAsInteger($uid)) {
1421
            $whereClause = $queryBuilder->expr()->andX(
1422
                $queryBuilder->expr()->eq('tx_dlf_documents.uid', intval($uid)),
1423
                Helper::whereExpression('tx_dlf_documents')
1424
            );
1425
        } else {
1426
            // Try to load METS file / IIIF manifest.
1427
            if ($this->setPreloadedDocument($preloadedDocument) || (GeneralUtility::isValidUrl($uid)
1428
                && $this->load($uid))) {
1429
                // Initialize core METS object.
1430
                $this->init();
1431
                if ($this->getDocument() !== null) {
1432
                    // Cast to string for safety reasons.
1433
                    $location = (string) $uid;
1434
                    $this->establishRecordId($pid);
1435
                } else {
1436
                    // No METS / IIIF part found.
1437
                    return;
1438
                }
1439
            } else {
1440
                // Loading failed.
1441
                return;
1442
            }
1443
            if (
1444
                !empty($location)
1445
                && !empty($this->recordId)
1446
            ) {
1447
                // Try to match record identifier or location (both should be unique).
1448
                $whereClause = $queryBuilder->expr()->andX(
1449
                    $queryBuilder->expr()->orX(
1450
                        $queryBuilder->expr()->eq('tx_dlf_documents.location', $queryBuilder->expr()->literal($location)),
1451
                        $queryBuilder->expr()->eq('tx_dlf_documents.record_id', $queryBuilder->expr()->literal($this->recordId))
1452
                    ),
1453
                    Helper::whereExpression('tx_dlf_documents')
1454
                );
1455
            } else {
1456
                // Can't persistently identify document, don't try to match at all.
1457
                $whereClause = '1=-1';
1458
            }
1459
        }
1460
        // Check for PID if needed.
1461
        if ($pid) {
1462
            $whereClause = $queryBuilder->expr()->andX(
1463
                $whereClause,
1464
                $queryBuilder->expr()->eq('tx_dlf_documents.pid', intval($pid))
1465
            );
1466
        }
1467
        // Get document PID and location from database.
1468
        $result = $queryBuilder
1469
            ->select(
1470
                'tx_dlf_documents.uid AS uid',
1471
                'tx_dlf_documents.pid AS pid',
1472
                'tx_dlf_documents.record_id AS record_id',
1473
                'tx_dlf_documents.partof AS partof',
1474
                'tx_dlf_documents.thumbnail AS thumbnail',
1475
                'tx_dlf_documents.location AS location'
1476
            )
1477
            ->from('tx_dlf_documents')
1478
            ->where($whereClause)
1479
            ->setMaxResults(1)
1480
            ->execute();
1481
1482
        if ($resArray = $result->fetch()) {
1483
            $this->uid = $resArray['uid'];
0 ignored issues
show
Bug introduced by
The property uid is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
1484
            $this->pid = $resArray['pid'];
0 ignored issues
show
Bug introduced by
The property pid is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
1485
            $this->recordId = $resArray['record_id'];
0 ignored issues
show
Bug introduced by
The property recordId is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
1486
            $this->parentId = $resArray['partof'];
0 ignored issues
show
Bug introduced by
The property parentId is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
1487
            $this->thumbnail = $resArray['thumbnail'];
0 ignored issues
show
Bug introduced by
The property thumbnail is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
1488
            $this->location = $resArray['location'];
0 ignored issues
show
Bug introduced by
The property location is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
1489
            $this->thumbnailLoaded = true;
1490
            // Load XML file if necessary...
1491
            if (
1492
                $this->getDocument() === null
1493
                && $this->load($this->location)
1494
            ) {
1495
                // ...and set some basic properties.
1496
                $this->init();
1497
            }
1498
            // Do we have a METS / IIIF object now?
1499
            if ($this->getDocument() !== null) {
1500
                // Set new location if necessary.
1501
                if (!empty($location)) {
1502
                    $this->location = $location;
1503
                }
1504
                // Document ready!
1505
                $this->ready = true;
0 ignored issues
show
Bug introduced by
The property ready is declared read-only in Kitodo\Dlf\Common\Document\Document.
Loading history...
1506
            }
1507
        } elseif ($this->getDocument() !== null) {
1508
            // Set location as UID for documents not in database.
1509
            $this->uid = $location;
1510
            $this->location = $location;
1511
            // Document ready!
1512
            $this->ready = true;
1513
        } else {
1514
            $this->logger->error('No document with UID ' . $uid . ' found or document not accessible');
1515
        }
1516
    }
1517
1518
    /**
1519
     * This magic method is called each time an invisible property is referenced from the object
1520
     *
1521
     * @access public
1522
     *
1523
     * @param string $var: Name of variable to get
1524
     *
1525
     * @return mixed Value of $this->$var
1526
     */
1527
    public function __get($var)
1528
    {
1529
        $method = '_get' . ucfirst($var);
1530
        if (
1531
            !property_exists($this, $var)
1532
            || !method_exists($this, $method)
1533
        ) {
1534
            $this->logger->warning('There is no getter function for property "' . $var . '"');
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

1534
            $this->logger->/** @scrutinizer ignore-call */ 
1535
                           warning('There is no getter function for property "' . $var . '"');

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...
1535
            return;
1536
        } else {
1537
            return $this->$method();
1538
        }
1539
    }
1540
1541
    /**
1542
     * This magic method is called each time an invisible property is checked for isset() or empty()
1543
     *
1544
     * @access public
1545
     *
1546
     * @param string $var: Name of variable to check
1547
     *
1548
     * @return bool true if variable is set and not empty, false otherwise
1549
     */
1550
    public function __isset($var)
1551
    {
1552
        return !empty($this->__get($var));
1553
    }
1554
1555
    /**
1556
     * This magic method is called each time an invisible property is referenced from the object
1557
     *
1558
     * @access public
1559
     *
1560
     * @param string $var: Name of variable to set
1561
     * @param mixed $value: New value of variable
1562
     *
1563
     * @return void
1564
     */
1565
    public function __set($var, $value)
1566
    {
1567
        $method = '_set' . ucfirst($var);
1568
        if (
1569
            !property_exists($this, $var)
1570
            || !method_exists($this, $method)
1571
        ) {
1572
            $this->logger->warning('There is no setter function for property "' . $var . '"');
1573
        } else {
1574
            $this->$method($value);
1575
        }
1576
    }
1577
}
1578