Passed
Pull Request — master (#104)
by Alexander
04:02
created

IiifManifest::establishRecordId()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 48
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 31
c 1
b 0
f 0
nc 9
nop 1
dl 0
loc 48
rs 8.8017
1
<?php
2
3
/**
4
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
5
 *
6
 * This file is part of the Kitodo and TYPO3 projects.
7
 *
8
 * @license GNU General Public License version 3 or later.
9
 * For the full copyright and license information, please read the
10
 * LICENSE.txt file that was distributed with this source code.
11
 */
12
13
namespace Kitodo\Dlf\Common;
14
15
use Flow\JSONPath\JSONPath;
16
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
17
use TYPO3\CMS\Core\Database\ConnectionPool;
18
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
19
use TYPO3\CMS\Core\Log\LogManager;
20
use TYPO3\CMS\Core\Utility\GeneralUtility;
21
use Ubl\Iiif\Presentation\Common\Model\Resources\AnnotationContainerInterface;
22
use Ubl\Iiif\Presentation\Common\Model\Resources\AnnotationInterface;
23
use Ubl\Iiif\Presentation\Common\Model\Resources\CanvasInterface;
24
use Ubl\Iiif\Presentation\Common\Model\Resources\ContentResourceInterface;
25
use Ubl\Iiif\Presentation\Common\Model\Resources\IiifResourceInterface;
26
use Ubl\Iiif\Presentation\Common\Model\Resources\ManifestInterface;
27
use Ubl\Iiif\Presentation\Common\Model\Resources\RangeInterface;
28
use Ubl\Iiif\Presentation\Common\Vocabulary\Motivation;
29
use Ubl\Iiif\Presentation\V1\Model\Resources\AbstractIiifResource1;
30
use Ubl\Iiif\Presentation\V2\Model\Resources\AbstractIiifResource2;
31
use Ubl\Iiif\Presentation\V3\Model\Resources\AbstractIiifResource3;
32
use Ubl\Iiif\Services\AbstractImageService;
33
use Ubl\Iiif\Services\Service;
34
use Ubl\Iiif\Tools\IiifHelper;
35
36
/**
37
 * IiifManifest class for the 'dlf' extension.
38
 *
39
 * @author Lutz Helm <[email protected]>
40
 * @package TYPO3
41
 * @subpackage dlf
42
 * @access public
43
 * @property int $cPid This holds the PID for the configuration
44
 * @property-read bool $hasFulltext Are there any fulltext files available?
45
 * @property-read array $metadataArray This holds the documents' parsed metadata array
46
 * @property-read int $numPages The holds the total number of pages
47
 * @property-read int $parentId This holds the UID of the parent document or zero if not multi-volumed
48
 * @property-read array $physicalStructure This holds the physical structure
49
 * @property-read array $physicalStructureInfo This holds the physical structure metadata
50
 * @property-read int $pid This holds the PID of the document or zero if not in database
51
 * @property-read bool $ready Is the document instantiated successfully?
52
 * @property-read string $recordId The IIIF manifest's record identifier
53
 * @property-read int $rootId This holds the UID of the root document or zero if not multi-volumed
54
 * @property-read array $smLinks This holds the connections between resources and canvases
55
 * @property-read array $tableOfContents This holds the logical structure
56
 * @property-read string $thumbnail This holds the document's thumbnail location
57
 * @property-read string $toplevelId This holds the toplevel manifest's @id
58
 */
59
final class IiifManifest extends Doc
60
{
61
    /**
62
     * This holds the manifest file as string for serialization purposes
63
     * @see __sleep() / __wakeup()
64
     *
65
     * @var string
66
     * @access protected
67
     */
68
    protected $asJson = '';
69
70
    /**
71
     * A PHP object representation of a IIIF manifest.
72
     * @var ManifestInterface
73
     * @access protected
74
     */
75
    protected $iiif;
76
77
    /**
78
     * 'IIIF1', 'IIIF2' or 'IIIF3', depending on the API $this->iiif confrms to:
79
     * IIIF Metadata API 1, IIIF Presentation API 2 or 3
80
     * @var string
81
     * @access protected
82
     */
83
    protected $iiifVersion;
84
85
    /**
86
     * Document has already been analyzed if it contains fulltext for the Solr index
87
     * @var bool
88
     * @access protected
89
     */
90
    protected $hasFulltextSet = false;
91
92
    /**
93
     * This holds the original manifest's parsed metadata array with their corresponding
94
     * resource (Manifest / Sequence / Range) ID as array key
95
     *
96
     * @var array
97
     * @access protected
98
     */
99
    protected $originalMetadataArray = [];
100
101
    /**
102
     * Holds the mime types of linked resources in the manifest (extreacted during parsing) for later use.
103
     * @var array
104
     * @access protected
105
     */
106
    protected $mimeTypes = [];
107
108
    /**
109
     * The extension key
110
     *
111
     * @var string
112
     * @static
113
     * @access public
114
     */
115
    public static $extKey = 'dlf';
116
117
    /**
118
     * {@inheritDoc}
119
     * @see Doc::establishRecordId()
120
     */
121
    protected function establishRecordId($pid)
122
    {
123
        if ($this->iiif !== null) {
124
            /*
125
             *  FIXME This will not consistently work because we can not be sure to have the pid at hand. It may miss
126
             *  if the plugin that actually loads the manifest allows content from other pages.
127
             *  Up until now the cPid is only set after the document has been initialized. We need it before to
128
             *  check the configuration.
129
             *  TODO Saving / indexing should still work - check!
130
             */
131
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
132
                ->getQueryBuilderForTable('tx_dlf_metadata');
133
            // Get hidden records, too.
134
            $queryBuilder
135
                ->getRestrictions()
136
                ->removeByType(HiddenRestriction::class);
137
            $result = $queryBuilder
138
                ->select('tx_dlf_metadataformat.xpath AS querypath')
139
                ->from('tx_dlf_metadata')
140
                ->from('tx_dlf_metadataformat')
141
                ->from('tx_dlf_formats')
142
                ->where(
143
                    $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($pid)),
144
                    $queryBuilder->expr()->eq('tx_dlf_metadataformat.pid', intval($pid)),
145
                    $queryBuilder->expr()->orX(
146
                        $queryBuilder->expr()->andX(
147
                            $queryBuilder->expr()->eq('tx_dlf_metadata.uid', 'tx_dlf_metadataformat.parent_id'),
148
                            $queryBuilder->expr()->eq('tx_dlf_metadataformat.encoded', 'tx_dlf_formats.uid'),
149
                            $queryBuilder->expr()->eq('tx_dlf_metadata.index_name', $queryBuilder->createNamedParameter('record_id')),
150
                            $queryBuilder->expr()->eq('tx_dlf_formats.type', $queryBuilder->createNamedParameter($this->getIiifVersion()))
151
                        ),
152
                        $queryBuilder->expr()->eq('tx_dlf_metadata.format', 0)
153
                    )
154
                )
155
                ->execute();
156
            while ($resArray = $result->fetch()) {
157
                $recordIdPath = $resArray['querypath'];
158
                if (!empty($recordIdPath)) {
159
                    try {
160
                        $this->recordId = $this->iiif->jsonPath($recordIdPath);
0 ignored issues
show
Bug introduced by
The property recordId is declared read-only in Kitodo\Dlf\Common\IiifManifest.
Loading history...
Documentation Bug introduced by
It seems like $this->iiif->jsonPath($recordIdPath) can also be of type Ubl\Iiif\Presentation\Co...urces\ManifestInterface or Ubl\Iiif\Presentation\Co...s\AbstractIiifResource1 or Ubl\Iiif\Presentation\Co...s\AbstractIiifResource2 or Ubl\Iiif\Presentation\Co...s\AbstractIiifResource3. However, the property $recordId 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...
161
                    } catch (\Exception $e) {
162
                        $this->logger->warning('Could not evaluate JSONPath to get IIIF record ID');
0 ignored issues
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

162
                        $this->logger->/** @scrutinizer ignore-call */ 
163
                                       warning('Could not evaluate JSONPath to get IIIF record 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...
163
                    }
164
                }
165
            }
166
            // For now, it's a hardcoded ID, not only as a fallback
167
            if (!isset($this->recordId)) {
168
                $this->recordId = $this->iiif->getId();
169
            }
170
        }
171
    }
172
173
    /**
174
     * {@inheritDoc}
175
     * @see Doc::getDocument()
176
     */
177
    protected function getDocument()
178
    {
179
        return $this->iiif;
180
    }
181
182
    /**
183
     * Returns a string representing the Metadata / Presentation API version which the IIIF resource
184
     * conforms to. This is used for example to extract metadata according to configured patterns.
185
     *
186
     * @access public
187
     *
188
     * @return string 'IIIF1' if the resource is a Metadata API 1 resource, 'IIIF2' / 'IIIF3' if
189
     * the resource is a Presentation API 2 / 3 resource
190
     */
191
    public function getIiifVersion()
192
    {
193
        if (!isset($this->iiifVersion)) {
194
            if ($this->iiif instanceof AbstractIiifResource1) {
195
                $this->iiifVersion = 'IIIF1';
196
            } elseif ($this->iiif instanceof AbstractIiifResource2) {
197
                $this->iiifVersion = 'IIIF2';
198
            } elseif ($this->iiif instanceof AbstractIiifResource3) {
199
                $this->iiifVersion = 'IIIF3';
200
            }
201
        }
202
        return $this->iiifVersion;
203
    }
204
205
    /**
206
     * True if getUseGroups() has been called and $this->useGrps is loaded
207
     *
208
     * @var bool
209
     * @access protected
210
     */
211
    protected $useGrpsLoaded = false;
212
213
    /**
214
     * Holds the configured useGrps as array.
215
     *
216
     * @var array
217
     * @access protected
218
     */
219
    protected $useGrps = [];
220
221
    /**
222
     * IiifManifest also populates the physical stucture array entries for matching
223
     * 'fileGrp's. To do that, the configuration has to be loaded; afterwards configured
224
     * 'fileGrp's for thumbnails, downloads, audio, fulltext and the 'fileGrp's for images
225
     * can be requested with this method.
226
     *
227
     * @access protected
228
     *
229
     * @param string $use
230
     *
231
     * @return array|string
232
     */
233
    protected function getUseGroups($use)
234
    {
235
        if (!$this->useGrpsLoaded) {
236
            // Get configured USE attributes.
237
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
238
            if (!empty($extConf['fileGrpImages'])) {
239
                $this->useGrps['fileGrpImages'] = GeneralUtility::trimExplode(',', $extConf['fileGrpImages']);
240
            }
241
            if (!empty($extConf['fileGrpThumbs'])) {
242
                $this->useGrps['fileGrpThumbs'] = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']);
243
            }
244
            if (!empty($extConf['fileGrpDownload'])) {
245
                $this->useGrps['fileGrpDownload'] = GeneralUtility::trimExplode(',', $extConf['fileGrpDownload']);
246
            }
247
            if (!empty($extConf['fileGrpFulltext'])) {
248
                $this->useGrps['fileGrpFulltext'] = GeneralUtility::trimExplode(',', $extConf['fileGrpFulltext']);
249
            }
250
            if (!empty($extConf['fileGrpAudio'])) {
251
                $this->useGrps['fileGrpAudio'] = GeneralUtility::trimExplode(',', $extConf['fileGrpAudio']);
252
            }
253
            $this->useGrpsLoaded = true;
254
        }
255
        return array_key_exists($use, $this->useGrps) ? $this->useGrps[$use] : [];
256
    }
257
258
    /**
259
     * {@inheritDoc}
260
     * @see Doc::_getPhysicalStructure()
261
     */
262
    protected function _getPhysicalStructure()
263
    {
264
        // Is there no physical structure array yet?
265
        if (!$this->physicalStructureLoaded) {
266
            if ($this->iiif == null || !($this->iiif instanceof ManifestInterface)) {
267
                return null;
268
            }
269
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
270
            $iiifId = $this->iiif->getId();
271
            $physSeq[0] = $iiifId;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$physSeq was never initialized. Although not strictly required by PHP, it is generally a good practice to add $physSeq = array(); before regardless.
Loading history...
272
            $this->physicalStructureInfo[$physSeq[0]]['id'] = $iiifId;
0 ignored issues
show
Bug introduced by
The property physicalStructureInfo is declared read-only in Kitodo\Dlf\Common\IiifManifest.
Loading history...
273
            $this->physicalStructureInfo[$physSeq[0]]['dmdId'] = $iiifId;
274
            $this->physicalStructureInfo[$physSeq[0]]['label'] = $this->iiif->getLabelForDisplay();
275
            $this->physicalStructureInfo[$physSeq[0]]['orderlabel'] = $this->iiif->getLabelForDisplay();
276
            $this->physicalStructureInfo[$physSeq[0]]['type'] = 'physSequence';
277
            $this->physicalStructureInfo[$physSeq[0]]['contentIds'] = null;
278
            $fileUseDownload = $this->getUseGroups('fileGrpDownload');
279
            $fileUseFulltext = $this->getUseGroups('fileGrpFulltext');
280
            $fileUseThumbs = $this->getUseGroups('fileGrpThumbs');
281
            $fileUses = $this->getUseGroups('fileGrpImages');
282
            if (!empty($fileUseDownload)) {
283
                $docPdfRendering = $this->iiif->getRenderingUrlsForFormat('application/pdf');
284
                if (!empty($docPdfRendering)) {
285
                    $this->physicalStructureInfo[$physSeq[0]]['files'][$fileUseDownload[0]] = $docPdfRendering[0];
286
                }
287
            }
288
            if (!empty($fileUseFulltext)) {
289
                $iiifAlto = $this->iiif->getSeeAlsoUrlsForFormat('application/alto+xml');
290
                if (empty($iiifAlto)) {
291
                    $iiifAlto = $this->iiif->getSeeAlsoUrlsForProfile('http://www.loc.gov/standards/alto/', true);
292
                }
293
                if (!empty($iiifAlto)) {
294
                    $this->mimeTypes[$iiifAlto[0]] = 'application/alto+xml';
295
                    $this->physicalStructureInfo[$physSeq[0]]['files'][$fileUseFulltext[0]] = $iiifAlto[0];
296
                    $this->hasFulltext = true;
0 ignored issues
show
Bug introduced by
The property hasFulltext is declared read-only in Kitodo\Dlf\Common\IiifManifest.
Loading history...
297
                    $this->hasFulltextSet = true;
298
                }
299
            }
300
            if (!empty($this->iiif->getDefaultCanvases())) {
301
                // canvases have not order property, but the context defines canveses as @list with a specific order, so we can provide an alternative
302
                $canvasOrder = 0;
303
                foreach ($this->iiif->getDefaultCanvases() as $canvas) {
304
                    $canvasOrder++;
305
                    $thumbnailUrl = $canvas->getThumbnailUrl();
306
                    // put thumbnails in thumbnail filegroup
307
                    if (
308
                        !empty($thumbnailUrl)
309
                        && empty($this->physicalStructureInfo[$physSeq[0]]['files'][$fileUseThumbs[0]])
310
                    ) {
311
                        $this->physicalStructureInfo[$physSeq[0]]['files'][$fileUseThumbs[0]] = $thumbnailUrl;
312
                    }
313
                    // populate structural metadata info
314
                    $elements[$canvasOrder] = $canvas->getId();
315
                    $this->physicalStructureInfo[$elements[$canvasOrder]]['id'] = $canvas->getId();
316
                    $this->physicalStructureInfo[$elements[$canvasOrder]]['dmdId'] = null;
317
                    $this->physicalStructureInfo[$elements[$canvasOrder]]['label'] = $canvas->getLabelForDisplay();
318
                    $this->physicalStructureInfo[$elements[$canvasOrder]]['orderlabel'] = $canvas->getLabelForDisplay();
319
                    // assume that a canvas always represents a page
320
                    $this->physicalStructureInfo[$elements[$canvasOrder]]['type'] = 'page';
321
                    $this->physicalStructureInfo[$elements[$canvasOrder]]['contentIds'] = null;
322
                    $this->physicalStructureInfo[$elements[$canvasOrder]]['annotationContainers'] = null;
323
                    if (!empty($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING))) {
324
                        $this->physicalStructureInfo[$elements[$canvasOrder]]['annotationContainers'] = [];
325
                        foreach ($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING) as $annotationContainer) {
326
                            $this->physicalStructureInfo[$elements[$canvasOrder]]['annotationContainers'][] = $annotationContainer->getId();
327
                            if ($extConf['indexAnnotations']) {
328
                                $this->hasFulltext = true;
329
                                $this->hasFulltextSet = true;
330
                            }
331
                        }
332
                    }
333
                    if (!empty($fileUseFulltext)) {
334
                        $alto = $canvas->getSeeAlsoUrlsForFormat('application/alto+xml');
335
                        if (empty($alto)) {
336
                            $alto = $canvas->getSeeAlsoUrlsForProfile('http://www.loc.gov/standards/alto/', true);
337
                        }
338
                        if (!empty($alto)) {
339
                            $this->mimeTypes[$alto[0]] = 'application/alto+xml';
340
                            $this->physicalStructureInfo[$elements[$canvasOrder]]['files'][$fileUseFulltext[0]] = $alto[0];
341
                            $this->hasFulltext = true;
342
                            $this->hasFulltextSet = true;
343
                        }
344
                    }
345
                    if (!empty($fileUses)) {
346
                        $image = $canvas->getImageAnnotations()[0];
347
                        foreach ($fileUses as $fileUse) {
348
                            if ($image->getBody() !== null && $image->getBody() instanceof ContentResourceInterface) {
349
                                $this->physicalStructureInfo[$elements[$canvasOrder]]['files'][$fileUse] = $image->getBody()->getId();
350
                            }
351
                        }
352
                    }
353
                    if (!empty($thumbnailUrl)) {
354
                        $this->physicalStructureInfo[$elements[$canvasOrder]]['files'][$fileUseThumbs] = $thumbnailUrl;
355
                    }
356
                    if (!empty($fileUseDownload)) {
357
                        $pdfRenderingUrls = $canvas->getRenderingUrlsForFormat('application/pdf');
358
                        if (!empty($pdfRenderingUrls)) {
359
                            $this->physicalStructureInfo[$elements[$canvasOrder]]['files'][$fileUseDownload[0]] = $pdfRenderingUrls[0];
360
                        }
361
                    }
362
                }
363
                $this->numPages = $canvasOrder;
0 ignored issues
show
Bug introduced by
The property numPages is declared read-only in Kitodo\Dlf\Common\IiifManifest.
Loading history...
364
                // Merge and re-index the array to get nice numeric indexes.
365
                $this->physicalStructure = array_merge($physSeq, $elements);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $elements seems to be defined by a foreach iteration on line 303. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
Bug introduced by
The property physicalStructure is declared read-only in Kitodo\Dlf\Common\IiifManifest.
Loading history...
366
            }
367
            $this->physicalStructureLoaded = true;
368
        }
369
        return $this->physicalStructure;
370
    }
371
372
    /**
373
     * {@inheritDoc}
374
     * @see Doc::getDownloadLocation()
375
     */
376
    public function getDownloadLocation($id)
377
    {
378
        $fileLocation = $this->getFileLocation($id);
379
        $resource = $this->iiif->getContainedResourceById($fileLocation);
380
        if ($resource instanceof AbstractImageService) {
381
            return $resource->getImageUrl();
382
        }
383
        return $fileLocation;
384
    }
385
386
    /**
387
     * {@inheritDoc}
388
     * @see Doc::getFileLocation()
389
     */
390
    public function getFileLocation($id)
391
    {
392
        if ($id == null) {
393
            return null;
394
        }
395
        $resource = $this->iiif->getContainedResourceById($id);
396
        if (isset($resource)) {
397
            if ($resource instanceof CanvasInterface) {
398
                return (!empty($resource->getImageAnnotations()) && $resource->getImageAnnotations()->getSingleService() != null) ? $resource->getImageAnnotations()[0]->getSingleService()->getId() : $id;
399
            } elseif ($resource instanceof ContentResourceInterface) {
400
                return $resource->getSingleService() != null && $resource->getSingleService() instanceof Service ? $resource->getSingleService()->getId() : $id;
401
            } elseif ($resource instanceof AbstractImageService) {
402
                return $resource->getId();
403
            } elseif ($resource instanceof AnnotationContainerInterface) {
404
                return $id;
405
            }
406
        } else {
407
            return $id;
408
        }
409
    }
410
411
    /**
412
     * {@inheritDoc}
413
     * @see Doc::getFileMimeType()
414
     */
415
    public function getFileMimeType($id)
416
    {
417
        $fileResource = $this->iiif->getContainedResourceById($id);
418
        if ($fileResource instanceof CanvasInterface) {
419
            $format = "application/vnd.kitodo.iiif";
420
        } elseif ($fileResource instanceof AnnotationInterface) {
421
            $format = "application/vnd.kitodo.iiif";
422
        } elseif ($fileResource instanceof ContentResourceInterface) {
423
            if ($fileResource->isText() || $fileResource->isImage() && ($fileResource->getSingleService() == null || !($fileResource->getSingleService() instanceof AbstractImageService))) {
424
                // Support static images without an image service
425
                return $fileResource->getFormat();
426
            }
427
            $format = "application/vnd.kitodo.iiif";
428
        } elseif ($fileResource instanceof AbstractImageService) {
429
            $format = "application/vnd.kitodo.iiif";
430
        } else {
431
            // Assumptions: this can only be the thumbnail and the thumbnail is a jpeg - TODO determine mimetype
432
            $format = "image/jpeg";
433
        }
434
        return $format;
435
    }
436
437
    /**
438
     * {@inheritDoc}
439
     * @see Doc::getLogicalStructure()
440
     */
441
    public function getLogicalStructure($id, $recursive = false)
442
    {
443
        $details = [];
444
        if (!$recursive && !empty($this->logicalUnits[$id])) {
445
            return $this->logicalUnits[$id];
446
        } elseif (!empty($id)) {
447
            $logUnits[] = $this->iiif->getContainedResourceById($id);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$logUnits was never initialized. Although not strictly required by PHP, it is generally a good practice to add $logUnits = array(); before regardless.
Loading history...
448
        } else {
449
            $logUnits[] = $this->iiif;
450
        }
451
        if (!empty($logUnits)) {
452
            if (!$recursive) {
453
                $details = $this->getLogicalStructureInfo($logUnits[0]);
454
            } else {
455
                // cache the ranges - they might occure multiple times in the structures "tree" - with full data as well as referenced as id
456
                $processedStructures = [];
457
                foreach ($logUnits as $logUnit) {
458
                    if (array_search($logUnit->getId(), $processedStructures) == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing array_search($logUnit->g..., $processedStructures) of type integer|string against false; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
459
                        $this->tableOfContents[] = $this->getLogicalStructureInfo($logUnit, true, $processedStructures);
0 ignored issues
show
Bug introduced by
The property tableOfContents is declared read-only in Kitodo\Dlf\Common\IiifManifest.
Loading history...
460
                    }
461
                }
462
            }
463
        }
464
        return $details;
465
    }
466
467
    /**
468
     * Get the details about a IIIF resource (manifest or range) in the logical structure
469
     *
470
     * @access protected
471
     *
472
     * @param IiifResourceInterface $resource: IIIF resource, either a manifest or range.
473
     * @param bool $recursive: Whether to include the child elements
474
     * @param array $processedStructures: IIIF resources that already have been processed
475
     * @return array Logical structure array
476
     */
477
    protected function getLogicalStructureInfo(IiifResourceInterface $resource, $recursive = false, &$processedStructures = [])
478
    {
479
        $details = [];
480
        $details['id'] = $resource->getId();
481
        $details['dmdId'] = '';
482
        $details['label'] = $resource->getLabelForDisplay() !== null ? $resource->getLabelForDisplay() : '';
0 ignored issues
show
introduced by
The condition $resource->getLabelForDisplay() !== null is always true.
Loading history...
483
        $details['orderlabel'] = $resource->getLabelForDisplay() !== null ? $resource->getLabelForDisplay() : '';
0 ignored issues
show
introduced by
The condition $resource->getLabelForDisplay() !== null is always true.
Loading history...
484
        $details['contentIds'] = '';
485
        $details['volume'] = '';
486
        $details['pagination'] = '';
487
        $cPid = ($this->cPid ? $this->cPid : $this->pid);
488
        if ($details['id'] == $this->_getToplevelId()) {
489
            $metadata = $this->getMetadata($details['id'], $cPid);
490
            if (!empty($metadata['type'][0])) {
491
                $details['type'] = $metadata['type'][0];
492
            }
493
        }
494
        $details['thumbnailId'] = $resource->getThumbnailUrl();
495
        $details['points'] = '';
496
        // Load strucural mapping
497
        $this->_getSmLinks();
498
        // Load physical structure.
499
        $this->_getPhysicalStructure();
500
        $canvases = [];
501
        if ($resource instanceof ManifestInterface) {
502
            $startCanvas = $resource->getStartCanvasOrFirstCanvas();
503
            $canvases = $resource->getDefaultCanvases();
0 ignored issues
show
Unused Code introduced by
The assignment to $canvases is dead and can be removed.
Loading history...
504
        } elseif ($resource instanceof RangeInterface) {
505
            $startCanvas = $resource->getStartCanvasOrFirstCanvas();
506
            $canvases = $resource->getAllCanvases();
507
        }
508
        if ($startCanvas != null) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $startCanvas does not seem to be defined for all execution paths leading up to this point.
Loading history...
509
            $details['pagination'] = $startCanvas->getLabel();
510
            $startCanvasIndex = array_search($startCanvas, $this->iiif->getDefaultCanvases());
511
            if ($startCanvasIndex !== false) {
512
                $details['points'] = $startCanvasIndex + 1;
513
            }
514
        }
515
        $useGroups = $this->getUseGroups('fileGrpImages');
516
        if (is_string($useGroups)) {
517
            $useGroups = [$useGroups];
0 ignored issues
show
Unused Code introduced by
The assignment to $useGroups is dead and can be removed.
Loading history...
518
        }
519
        // Keep for later usage.
520
        $this->logicalUnits[$details['id']] = $details;
521
        // Walk the structure recursively? And are there any children of the current element?
522
        if ($recursive) {
523
            $processedStructures[] = $resource->getId();
524
            $details['children'] = [];
525
            if ($resource instanceof ManifestInterface && $resource->getRootRanges() != null) {
526
                $rangesToAdd = [];
527
                $rootRanges = [];
528
                if (sizeof($this->iiif->getRootRanges()) == 1 && $this->iiif->getRootRanges()[0]->isTopRange()) {
529
                    $rangesToAdd = $this->iiif->getRootRanges()[0]->getMemberRangesAndRanges();
0 ignored issues
show
Bug introduced by
The method getMemberRangesAndRanges() does not exist on Ubl\Iiif\Presentation\Co...esources\RangeInterface. It seems like you code against a sub-type of Ubl\Iiif\Presentation\Co...esources\RangeInterface such as Ubl\Iiif\Presentation\V2\Model\Resources\Range2. ( Ignorable by Annotation )

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

529
                    /** @scrutinizer ignore-call */ 
530
                    $rangesToAdd = $this->iiif->getRootRanges()[0]->getMemberRangesAndRanges();
Loading history...
530
                } else {
531
                    $rangesToAdd = $this->iiif->getRootRanges();
532
                }
533
                foreach ($rangesToAdd as $range) {
534
                    $rootRanges[] = $range;
535
                }
536
                foreach ($rootRanges as $range) {
537
                    if ((array_search($range->getId(), $processedStructures) == false)) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing array_search($range->get..., $processedStructures) of type integer|string against false; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
538
                        $details['children'][] = $this->getLogicalStructureInfo($range, true, $processedStructures);
539
                    }
540
                }
541
            } elseif ($resource instanceof RangeInterface) {
542
                if (!empty($resource->getAllRanges())) {
543
                    foreach ($resource->getAllRanges() as $range) {
544
                        if ((array_search($range->getId(), $processedStructures) == false)) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing array_search($range->get..., $processedStructures) of type integer|string against false; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
545
                            $details['children'][] = $this->getLogicalStructureInfo($range, true, $processedStructures);
546
                        }
547
                    }
548
                }
549
            }
550
        }
551
        return $details;
552
    }
553
554
    /**
555
     * Returns metadata for IIIF resources with the ID $id in there original form in
556
     * the manifest, but prepared for display to the user.
557
     *
558
     * @access public
559
     *
560
     * @param string $id: the ID of the IIIF resource
561
     * @param int $cPid: the configuration folder's id
562
     * @param bool $withDescription: add description / summary to the return value
563
     * @param bool $withRights: add attribution and license / rights and requiredStatement to the return value
564
     * @param bool $withRelated: add related links / homepage to the return value
565
     *
566
     * @return array
567
     *
568
     * @todo This method is still in experimental; the method signature may change.
569
     */
570
    public function getManifestMetadata($id, $cPid = 0, $withDescription = true, $withRights = true, $withRelated = true)
0 ignored issues
show
Unused Code introduced by
The parameter $cPid is not used and could be removed. ( Ignorable by Annotation )

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

570
    public function getManifestMetadata($id, /** @scrutinizer ignore-unused */ $cPid = 0, $withDescription = true, $withRights = true, $withRelated = true)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
571
    {
572
        if (!empty($this->originalMetadataArray[$id])) {
573
            return $this->originalMetadataArray[$id];
574
        }
575
        $iiifResource = $this->iiif->getContainedResourceById($id);
576
        $result = [];
577
        if ($iiifResource != null) {
578
            if ($iiifResource->getLabel() != null && $iiifResource->getLabel() != "") {
579
                $result['label'] = $iiifResource->getLabel();
580
            }
581
            if (!empty($iiifResource->getMetadata())) {
582
                $result['metadata'] = [];
583
                foreach ($iiifResource->getMetadataForDisplay() as $metadata) {
584
                    $result['metadata'][$metadata['label']] = $metadata['value'];
585
                }
586
            }
587
            if ($withDescription && !empty($iiifResource->getSummary())) {
588
                $result["description"] = $iiifResource->getSummaryForDisplay();
589
            }
590
            if ($withRights) {
591
                if (!empty($iiifResource->getRights())) {
592
                    $result["rights"] = $iiifResource->getRights();
593
                }
594
                if (!empty($iiifResource->getRequiredStatement())) {
595
                    $result["requiredStatement"] = $iiifResource->getRequiredStatementForDisplay();
596
                }
597
            }
598
            if ($withRelated && !empty($iiifResource->getWeblinksForDisplay())) {
599
                $result["weblinks"] = [];
600
                foreach ($iiifResource->getWeblinksForDisplay() as $link) {
601
                    $key = array_key_exists("label", $link) ? $link["label"] : $link["@id"];
602
                    $result["weblinks"][$key] = $link["@id"];
603
                }
604
            }
605
        }
606
        return $result;
607
    }
608
609
    /**
610
     * {@inheritDoc}
611
     * @see Doc::getMetadata()
612
     */
613
    public function getMetadata($id, $cPid = 0)
614
    {
615
        if (!empty($this->metadataArray[$id]) && $this->metadataArray[0] == $cPid) {
616
            return $this->metadataArray[$id];
617
        }
618
        // Initialize metadata array with empty values.
619
        // TODO initialize metadata in abstract class
620
        $metadata = [
621
            'title' => [],
622
            'title_sorting' => [],
623
            'author' => [],
624
            'place' => [],
625
            'year' => [],
626
            'prod_id' => [],
627
            'record_id' => [],
628
            'opac_id' => [],
629
            'union_id' => [],
630
            'urn' => [],
631
            'purl' => [],
632
            'type' => [],
633
            'volume' => [],
634
            'volume_sorting' => [],
635
            'license' => [],
636
            'terms' => [],
637
            'restrictions' => [],
638
            'out_of_print' => [],
639
            'rights_info' => [],
640
            'collection' => [],
641
            'owner' => [],
642
            'mets_label' => [],
643
            'mets_orderlabel' => [],
644
            'document_format' => ['IIIF'],
645
        ];
646
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
647
            ->getQueryBuilderForTable('tx_dlf_metadata');
648
        // Get hidden records, too.
649
        $queryBuilder
650
            ->getRestrictions()
651
            ->removeByType(HiddenRestriction::class);
652
        $result = $queryBuilder
653
            ->select(
654
                'tx_dlf_metadata.index_name AS index_name',
655
                'tx_dlf_metadataformat.xpath AS xpath',
656
                'tx_dlf_metadataformat.xpath_sorting AS xpath_sorting',
657
                'tx_dlf_metadata.is_sortable AS is_sortable',
658
                'tx_dlf_metadata.default_value AS default_value',
659
                'tx_dlf_metadata.format AS format'
660
            )
661
            ->from('tx_dlf_metadata')
662
            ->from('tx_dlf_metadataformat')
663
            ->from('tx_dlf_formats')
664
            ->where(
665
                $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($cPid)),
666
                $queryBuilder->expr()->eq('tx_dlf_metadataformat.pid', intval($cPid)),
667
                $queryBuilder->expr()->orX(
668
                    $queryBuilder->expr()->andX(
669
                        $queryBuilder->expr()->eq('tx_dlf_metadata.uid', 'tx_dlf_metadataformat.parent_id'),
670
                        $queryBuilder->expr()->eq('tx_dlf_metadataformat.encoded', 'tx_dlf_formats.uid'),
671
                        $queryBuilder->expr()->eq('tx_dlf_formats.type', $queryBuilder->createNamedParameter($this->getIiifVersion()))
672
                    ),
673
                    $queryBuilder->expr()->eq('tx_dlf_metadata.format', 0)
674
                )
675
            )
676
            ->execute();
677
        $iiifResource = $this->iiif->getContainedResourceById($id);
678
        while ($resArray = $result->fetch()) {
679
            // Set metadata field's value(s).
680
            if ($resArray['format'] > 0 && !empty($resArray['xpath']) && ($values = $iiifResource->jsonPath($resArray['xpath'])) != null) {
681
                if (is_string($values)) {
682
                    $metadata[$resArray['index_name']] = [trim((string) $values)];
683
                } elseif ($values instanceof JSONPath && is_array($values->data()) && count($values->data()) > 1) {
684
                    $metadata[$resArray['index_name']] = [];
685
                    foreach ($values->data() as $value) {
686
                        $metadata[$resArray['index_name']][] = trim((string) $value);
687
                    }
688
                }
689
            }
690
            // Set default value if applicable.
691
            if (empty($metadata[$resArray['index_name']][0]) && strlen($resArray['default_value']) > 0) {
692
                $metadata[$resArray['index_name']] = [$resArray['default_value']];
693
            }
694
            // Set sorting value if applicable.
695
            if (!empty($metadata[$resArray['index_name']]) && $resArray['is_sortable']) {
696
                if (
697
                    $resArray['format'] > 0 && !empty($resArray['xpath_sorting'])
698
                    && ($values = $iiifResource->jsonPath($resArray['xpath_sorting']) != null)
699
                ) {
700
                    if (is_string($values)) {
701
                        $metadata[$resArray['index_name'] . '_sorting'][0] = [trim((string) $values)];
702
                    } elseif ($values instanceof JSONPath && is_array($values->data()) && count($values->data()) > 1) {
703
                        $metadata[$resArray['index_name']] = [];
704
                        foreach ($values->data() as $value) {
705
                            $metadata[$resArray['index_name'] . '_sorting'][0] = trim((string) $value);
706
                        }
707
                    }
708
                }
709
                if (empty($metadata[$resArray['index_name'] . '_sorting'][0])) {
710
                    $metadata[$resArray['index_name'] . '_sorting'][0] = $metadata[$resArray['index_name']][0];
711
                }
712
            }
713
        }
714
        return $metadata;
715
    }
716
717
    /**
718
     * {@inheritDoc}
719
     * @see Doc::_getSmLinks()
720
     */
721
    protected function _getSmLinks()
722
    {
723
        if (!$this->smLinksLoaded && isset($this->iiif) && $this->iiif instanceof ManifestInterface) {
724
            if (!empty($this->iiif->getDefaultCanvases())) {
725
                foreach ($this->iiif->getDefaultCanvases() as $canvas) {
726
                    $this->smLinkCanvasToResource($canvas, $this->iiif);
727
                }
728
            }
729
            if (!empty($this->iiif->getStructures())) {
730
                foreach ($this->iiif->getStructures() as $range) {
731
                    $this->smLinkRangeCanvasesRecursively($range);
732
                }
733
            }
734
            $this->smLinksLoaded = true;
735
        }
736
        return $this->smLinks;
737
    }
738
739
    /**
740
     * Construct a link between a range and it's sub ranges and all contained canvases.
741
     *
742
     * @access private
743
     *
744
     * @param RangeInterface $range: Current range whose canvases shall be linked
745
     */
746
    private function smLinkRangeCanvasesRecursively(RangeInterface $range)
747
    {
748
        // map range's canvases including all child ranges' canvases
749
        if (!$range->isTopRange()) {
750
            foreach ($range->getAllCanvasesRecursively() as $canvas) {
751
                $this->smLinkCanvasToResource($canvas, $range);
752
            }
753
        }
754
        // recursive call for all ranges
755
        if (!empty($range->getAllRanges())) {
756
            foreach ($range->getAllRanges() as $childRange) {
757
                $this->smLinkRangeCanvasesRecursively($childRange);
758
            }
759
        }
760
    }
761
762
    /**
763
     * Link a single canvas to a containing range
764
     *
765
     * @access private
766
     *
767
     * @param CanvasInterface $canvas
768
     * @param IiifResourceInterface $resource
769
     */
770
    private function smLinkCanvasToResource(CanvasInterface $canvas, IiifResourceInterface $resource)
771
    {
772
        $this->smLinks['l2p'][$resource->getId()][] = $canvas->getId();
0 ignored issues
show
Bug introduced by
The property smLinks is declared read-only in Kitodo\Dlf\Common\IiifManifest.
Loading history...
773
        if (!is_array($this->smLinks['p2l'][$canvas->getId()]) || !in_array($resource->getId(), $this->smLinks['p2l'][$canvas->getId()])) {
774
            $this->smLinks['p2l'][$canvas->getId()][] = $resource->getId();
775
        }
776
    }
777
778
    /**
779
     * {@inheritDoc}
780
     * @see Doc::getFullText()
781
     */
782
    //TODO: rewrite it to get full OCR
783
    public function getFullText($id)
784
    {
785
        $rawText = '';
786
        // Get text from raw text array if available.
787
        if (!empty($this->rawTextArray[$id])) {
788
            return $this->rawTextArray[$id];
789
        }
790
        $this->ensureHasFulltextIsSet();
791
        if ($this->hasFulltext) {
792
            // Load physical structure ...
793
            $this->_getPhysicalStructure();
794
            // ... and extension configuration.
795
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
796
            $fileGrpsFulltext = GeneralUtility::trimExplode(',', $extConf['fileGrpFulltext']);
797
            if (!empty($this->physicalStructureInfo[$id])) {
798
                while ($fileGrpFulltext = array_shift($fileGrpsFulltext)) {
799
                    if (!empty($this->physicalStructureInfo[$id]['files'][$fileGrpFulltext])) {
800
                        $rawText = parent::getFullTextFromXml($id);
801
                        break;
802
                    }
803
                }
804
                if ($extConf['indexAnnotations'] == 1) {
805
                    $iiifResource = $this->iiif->getContainedResourceById($id);
806
                    // Get annotation containers
807
                    $annotationContainerIds = $this->physicalStructureInfo[$id]['annotationContainers'];
808
                    if (!empty($annotationContainerIds)) {
809
                        $annotationTexts = [];
810
                        foreach ($annotationContainerIds as $annotationListId) {
811
                            $annotationContainer = $this->iiif->getContainedResourceById($annotationListId);
812
                            /* @var $annotationContainer \Ubl\Iiif\Presentation\Common\Model\Resources\AnnotationContainerInterface */
813
                            foreach ($annotationContainer->getTextAnnotations(Motivation::PAINTING) as $annotation) {
0 ignored issues
show
Bug introduced by
The method getTextAnnotations() does not exist on Ubl\Iiif\Presentation\Co...s\IiifResourceInterface. It seems like you code against a sub-type of Ubl\Iiif\Presentation\Co...s\IiifResourceInterface such as Ubl\Iiif\Presentation\Co...ationContainerInterface or Ubl\Iiif\Presentation\V2...sources\AnnotationList2 or Ubl\Iiif\Presentation\V3...sources\AnnotationPage3 or Ubl\Iiif\Presentation\V1...sources\AnnotationList1 or Ubl\Iiif\Presentation\V2...sources\AnnotationList2 or Ubl\Iiif\Presentation\V3...sources\AnnotationPage3 or Ubl\Iiif\Presentation\V1...sources\AnnotationList1. ( Ignorable by Annotation )

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

813
                            foreach ($annotationContainer->/** @scrutinizer ignore-call */ getTextAnnotations(Motivation::PAINTING) as $annotation) {
Loading history...
814
                                if (
815
                                    $annotation->getTargetResourceId() == $iiifResource->getId() &&
816
                                    $annotation->getBody() != null && $annotation->getBody()->getChars() != null
817
                                ) {
818
                                    $annotationTexts[] = $annotation->getBody()->getChars();
819
                                }
820
                            }
821
                        }
822
                        $rawText .= implode(' ', $annotationTexts);
823
                    }
824
                }
825
            } else {
826
                $this->logger->warning('Invalid structure resource @id "' . $id . '"');
827
                return $rawText;
828
            }
829
            $this->rawTextArray[$id] = $rawText;
830
        }
831
        return $rawText;
832
    }
833
834
    /**
835
     * Returns the underlying IiifResourceInterface.
836
     *
837
     * @access public
838
     *
839
     * @return IiifResourceInterface
840
     */
841
    public function getIiif()
842
    {
843
        return $this->iiif;
844
    }
845
846
    /**
847
     * {@inheritDoc}
848
     * @see Doc::init()
849
     */
850
    protected function init($location)
851
    {
852
        $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class);
853
    }
854
855
    /**
856
     * {@inheritDoc}
857
     * @see Doc::loadLocation()
858
     */
859
    protected function loadLocation($location)
860
    {
861
        $fileResource = GeneralUtility::getUrl($location);
862
        if ($fileResource !== false) {
863
            $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
864
            IiifHelper::setUrlReader(IiifUrlReader::getInstance());
865
            IiifHelper::setMaxThumbnailHeight($conf['iiifThumbnailHeight']);
866
            IiifHelper::setMaxThumbnailWidth($conf['iiifThumbnailWidth']);
867
            $resource = IiifHelper::loadIiifResource($fileResource);
868
            if ($resource != null) {
869
                if ($resource instanceof ManifestInterface) {
870
                    $this->iiif = $resource;
871
                    return true;
872
                }
873
            }
874
        }
875
        $this->logger->error('Could not load IIIF manifest from "' . $location . '"');
0 ignored issues
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

875
        $this->logger->/** @scrutinizer ignore-call */ 
876
                       error('Could not load IIIF manifest from "' . $location . '"');

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...
876
        return false;
877
    }
878
879
    /**
880
     * {@inheritDoc}
881
     * @see \Kitodo\Dlf\Common\Doc::prepareMetadataArray()
882
     */
883
    protected function prepareMetadataArray($cPid)
884
    {
885
        $id = $this->iiif->getId();
886
        $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\IiifManifest.
Loading history...
887
    }
888
889
    /**
890
     * {@inheritDoc}
891
     * @see Doc::setPreloadedDocument()
892
     */
893
    protected function setPreloadedDocument($preloadedDocument)
894
    {
895
        if ($preloadedDocument instanceof ManifestInterface) {
896
            $this->iiif = $preloadedDocument;
897
            return true;
898
        }
899
        return false;
900
    }
901
902
    /**
903
     * {@inheritDoc}
904
     * @see Docu::ensureHasFulltextIsSet()
905
     */
906
    protected function ensureHasFulltextIsSet()
907
    {
908
        /*
909
         *  TODO Check annotations and annotation lists of canvas for ALTO documents.
910
         *  Example:
911
         *  https://digi.ub.uni-heidelberg.de/diglit/iiif/hirsch_hamburg1933_04_25/manifest.json links
912
         *  https://digi.ub.uni-heidelberg.de/diglit/iiif/hirsch_hamburg1933_04_25/list/0001.json
913
         */
914
        if (!$this->hasFulltextSet && $this->iiif instanceof ManifestInterface) {
915
            $manifest = $this->iiif;
916
            $canvases = $manifest->getDefaultCanvases();
917
            foreach ($canvases as $canvas) {
918
                if (
919
                    !empty($canvas->getSeeAlsoUrlsForFormat("application/alto+xml")) ||
920
                    !empty($canvas->getSeeAlsoUrlsForProfile("http://www.loc.gov/standards/alto/"))
921
                ) {
922
                    $this->hasFulltextSet = true;
923
                    $this->hasFulltext = true;
0 ignored issues
show
Bug introduced by
The property hasFulltext is declared read-only in Kitodo\Dlf\Common\IiifManifest.
Loading history...
924
                    return;
925
                }
926
                $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
927
                if ($extConf['indexAnnotations'] == 1 && !empty($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING))) {
928
                    foreach ($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING) as $annotationContainer) {
929
                        if (($textAnnotations = $annotationContainer->getTextAnnotations(Motivation::PAINTING)) != null) {
930
                            foreach ($textAnnotations as $annotation) {
931
                                if (
932
                                    $annotation->getBody() != null &&
933
                                    $annotation->getBody()->getFormat() == "text/plain" &&
934
                                    $annotation->getBody()->getChars() != null
935
                                ) {
936
                                    $this->hasFulltextSet = true;
937
                                    $this->hasFulltext = true;
938
                                    return;
939
                                }
940
                            }
941
                        }
942
                    }
943
                }
944
            }
945
            $this->hasFulltextSet = true;
946
        }
947
    }
948
949
    /**
950
     * {@inheritDoc}
951
     * @see \Kitodo\Dlf\Common\Doc::_getThumbnail()
952
     */
953
    protected function _getThumbnail($forceReload = false)
954
    {
955
        return $this->iiif->getThumbnailUrl();
956
    }
957
958
    /**
959
     * {@inheritDoc}
960
     * @see \Kitodo\Dlf\Common\Doc::_getToplevelId()
961
     */
962
    protected function _getToplevelId()
963
    {
964
        if (empty($this->toplevelId)) {
965
            if (isset($this->iiif)) {
966
                $this->toplevelId = $this->iiif->getId();
0 ignored issues
show
Bug introduced by
The property toplevelId is declared read-only in Kitodo\Dlf\Common\IiifManifest.
Loading history...
967
            }
968
        }
969
        return $this->toplevelId;
970
    }
971
972
    /**
973
     * This magic method is executed after the object is deserialized
974
     * @see __sleep()
975
     *
976
     * @access public
977
     *
978
     * @return void
979
     */
980
    public function __wakeup()
981
    {
982
        $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
983
        IiifHelper::setUrlReader(IiifUrlReader::getInstance());
984
        IiifHelper::setMaxThumbnailHeight($conf['iiifThumbnailHeight']);
985
        IiifHelper::setMaxThumbnailWidth($conf['iiifThumbnailWidth']);
986
        $resource = IiifHelper::loadIiifResource($this->asJson);
987
        if ($resource != null && $resource instanceof ManifestInterface) {
988
            $this->asJson = '';
989
            $this->iiif = $resource;
990
            $this->init('');
991
        } else {
992
            $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class);
993
            $this->logger->error('Could not load IIIF after deserialization');
994
        }
995
    }
996
997
    /**
998
     *
999
     * @return string[]
1000
     */
1001
    public function __sleep()
1002
    {
1003
        // TODO implement serializiation in IIIF library
1004
        $jsonArray = $this->iiif->getOriginalJsonArray();
1005
        $this->asJson = json_encode($jsonArray);
1006
        return ['uid', 'pid', 'recordId', 'parentId', 'asJson'];
1007
    }
1008
}
1009