We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.
Total Complexity | 194 |
Total Lines | 949 |
Duplicated Lines | 0 % |
Changes | 4 | ||
Bugs | 0 | Features | 0 |
Complex classes like IiifManifest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use IiifManifest, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
78 | final class IiifManifest extends AbstractDocument |
||
79 | { |
||
80 | /** |
||
81 | * @access protected |
||
82 | * @var string This holds the manifest file as string for serialization purposes |
||
83 | * |
||
84 | * @see __sleep() / __wakeup() |
||
85 | */ |
||
86 | protected string $asJson = ''; |
||
87 | |||
88 | /** |
||
89 | * @access protected |
||
90 | * @var ManifestInterface|null A PHP object representation of a IIIF manifest |
||
91 | */ |
||
92 | protected ?ManifestInterface $iiif; |
||
93 | |||
94 | /** |
||
95 | * @access protected |
||
96 | * @var string 'IIIF1', 'IIIF2' or 'IIIF3', depending on the API $this->iiif conforms to: IIIF Metadata API 1, IIIF Presentation API 2 or 3 |
||
97 | */ |
||
98 | protected string $iiifVersion; |
||
99 | |||
100 | /** |
||
101 | * @access protected |
||
102 | * @var bool Document has already been analyzed if it contains fulltext for the Solr index |
||
103 | */ |
||
104 | protected bool $hasFulltextSet = false; |
||
105 | |||
106 | /** |
||
107 | * @access protected |
||
108 | * @var array This holds the original manifest's parsed metadata array with their corresponding resource (Manifest / Sequence / Range) ID as array key |
||
109 | */ |
||
110 | protected array $originalMetadataArray = []; |
||
111 | |||
112 | /** |
||
113 | * @access protected |
||
114 | * @var array Holds the mime types of linked resources in the manifest (extracted during parsing) for later use |
||
115 | */ |
||
116 | protected array $mimeTypes = []; |
||
117 | |||
118 | /** |
||
119 | * @see AbstractDocument::establishRecordId() |
||
120 | */ |
||
121 | protected function establishRecordId(int $pid): void |
||
169 | } |
||
170 | } |
||
171 | } |
||
172 | |||
173 | /** |
||
174 | * @see AbstractDocument::getDocument() |
||
175 | */ |
||
176 | protected function getDocument(): IiifResourceInterface |
||
177 | { |
||
178 | return $this->iiif; |
||
179 | } |
||
180 | |||
181 | /** |
||
182 | * Returns a string representing the Metadata / Presentation API version which the IIIF resource |
||
183 | * conforms to. This is used for example to extract metadata according to configured patterns. |
||
184 | * |
||
185 | * @access public |
||
186 | * |
||
187 | * @return string 'IIIF1' if the resource is a Metadata API 1 resource, 'IIIF2' / 'IIIF3' if |
||
188 | * the resource is a Presentation API 2 / 3 resource |
||
189 | */ |
||
190 | public function getIiifVersion(): string |
||
191 | { |
||
192 | if (!isset($this->iiifVersion)) { |
||
193 | if ($this->iiif instanceof AbstractIiifResource1) { |
||
194 | $this->iiifVersion = 'IIIF1'; |
||
195 | } elseif ($this->iiif instanceof AbstractIiifResource2) { |
||
196 | $this->iiifVersion = 'IIIF2'; |
||
197 | } elseif ($this->iiif instanceof AbstractIiifResource3) { |
||
198 | $this->iiifVersion = 'IIIF3'; |
||
199 | } |
||
200 | } |
||
201 | return $this->iiifVersion; |
||
202 | } |
||
203 | |||
204 | /** |
||
205 | * True if getUseGroups() has been called and $this->useGrps is loaded |
||
206 | * |
||
207 | * @var bool |
||
208 | * @access protected |
||
209 | */ |
||
210 | protected bool $useGrpsLoaded = false; |
||
211 | |||
212 | /** |
||
213 | * Holds the configured useGrps as array. |
||
214 | * |
||
215 | * @var array |
||
216 | * @access protected |
||
217 | */ |
||
218 | protected array $useGrps = []; |
||
219 | |||
220 | /** |
||
221 | * IiifManifest also populates the physical structure array entries for matching |
||
222 | * 'fileGrp's. To do that, the configuration has to be loaded; afterwards configured |
||
223 | * 'fileGrp's for thumbnails, downloads, audio, fulltext and the 'fileGrp's for images |
||
224 | * can be requested with this method. |
||
225 | * |
||
226 | * @access protected |
||
227 | * |
||
228 | * @param string $use |
||
229 | * |
||
230 | * @return array|string |
||
231 | */ |
||
232 | protected function getUseGroups(string $use) |
||
233 | { |
||
234 | if (!$this->useGrpsLoaded) { |
||
235 | // Get configured USE attributes. |
||
236 | $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'files'); |
||
237 | if (!empty($extConf['fileGrpImages'])) { |
||
238 | $this->useGrps['fileGrpImages'] = GeneralUtility::trimExplode(',', $extConf['fileGrpImages']); |
||
239 | } |
||
240 | if (!empty($extConf['fileGrpThumbs'])) { |
||
241 | $this->useGrps['fileGrpThumbs'] = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']); |
||
242 | } |
||
243 | if (!empty($extConf['fileGrpDownload'])) { |
||
244 | $this->useGrps['fileGrpDownload'] = GeneralUtility::trimExplode(',', $extConf['fileGrpDownload']); |
||
245 | } |
||
246 | if (!empty($extConf['fileGrpFulltext'])) { |
||
247 | $this->useGrps['fileGrpFulltext'] = GeneralUtility::trimExplode(',', $extConf['fileGrpFulltext']); |
||
248 | } |
||
249 | if (!empty($extConf['fileGrpAudio'])) { |
||
250 | $this->useGrps['fileGrpAudio'] = GeneralUtility::trimExplode(',', $extConf['fileGrpAudio']); |
||
251 | } |
||
252 | $this->useGrpsLoaded = true; |
||
253 | } |
||
254 | return array_key_exists($use, $this->useGrps) ? $this->useGrps[$use] : []; |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * @see AbstractDocument::magicGetPhysicalStructure() |
||
259 | */ |
||
260 | protected function magicGetPhysicalStructure(): array |
||
261 | { |
||
262 | // Is there no physical structure array yet? |
||
263 | if (!$this->physicalStructureLoaded) { |
||
264 | if ($this->iiif == null || !($this->iiif instanceof ManifestInterface)) { |
||
265 | return []; |
||
266 | } |
||
267 | $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'iiif'); |
||
268 | $iiifId = $this->iiif->getId(); |
||
269 | $this->physicalStructureInfo[$iiifId]['id'] = $iiifId; |
||
270 | $this->physicalStructureInfo[$iiifId]['dmdId'] = $iiifId; |
||
271 | $this->physicalStructureInfo[$iiifId]['label'] = $this->iiif->getLabelForDisplay(); |
||
272 | $this->physicalStructureInfo[$iiifId]['orderlabel'] = $this->iiif->getLabelForDisplay(); |
||
273 | $this->physicalStructureInfo[$iiifId]['type'] = 'physSequence'; |
||
274 | $this->physicalStructureInfo[$iiifId]['contentIds'] = null; |
||
275 | |||
276 | $this->setFileUseDownload($iiifId, $this->iiif); |
||
277 | $this->setFileUseFulltext($iiifId, $this->iiif); |
||
278 | |||
279 | $fileUseThumbs = $this->getUseGroups('fileGrpThumbs'); |
||
280 | $fileUses = $this->getUseGroups('fileGrpImages'); |
||
281 | |||
282 | if (!empty($this->iiif->getDefaultCanvases())) { |
||
283 | // canvases have not order property, but the context defines canveses as @list with a specific order, so we can provide an alternative |
||
284 | $elements = []; |
||
285 | $canvasOrder = 0; |
||
286 | foreach ($this->iiif->getDefaultCanvases() as $canvas) { |
||
287 | $canvasOrder++; |
||
288 | $thumbnailUrl = $canvas->getThumbnailUrl(); |
||
289 | // put thumbnails in thumbnail filegroup |
||
290 | if ( |
||
291 | !empty($thumbnailUrl) |
||
292 | && empty($this->physicalStructureInfo[$iiifId]['files'][$fileUseThumbs[0]]) |
||
293 | ) { |
||
294 | $this->physicalStructureInfo[$iiifId]['files'][$fileUseThumbs[0]] = $thumbnailUrl; |
||
295 | } |
||
296 | // populate structural metadata info |
||
297 | $elements[$canvasOrder] = $canvas->getId(); |
||
298 | $this->physicalStructureInfo[$elements[$canvasOrder]]['id'] = $canvas->getId(); |
||
299 | $this->physicalStructureInfo[$elements[$canvasOrder]]['dmdId'] = null; |
||
300 | $this->physicalStructureInfo[$elements[$canvasOrder]]['label'] = $canvas->getLabelForDisplay(); |
||
301 | $this->physicalStructureInfo[$elements[$canvasOrder]]['orderlabel'] = $canvas->getLabelForDisplay(); |
||
302 | // assume that a canvas always represents a page |
||
303 | $this->physicalStructureInfo[$elements[$canvasOrder]]['type'] = 'page'; |
||
304 | $this->physicalStructureInfo[$elements[$canvasOrder]]['contentIds'] = null; |
||
305 | $this->physicalStructureInfo[$elements[$canvasOrder]]['annotationContainers'] = null; |
||
306 | if (!empty($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING))) { |
||
307 | $this->physicalStructureInfo[$elements[$canvasOrder]]['annotationContainers'] = []; |
||
308 | foreach ($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING) as $annotationContainer) { |
||
309 | $this->physicalStructureInfo[$elements[$canvasOrder]]['annotationContainers'][] = $annotationContainer->getId(); |
||
310 | if ($extConf['indexAnnotations']) { |
||
311 | $this->hasFulltext = true; |
||
312 | $this->hasFulltextSet = true; |
||
313 | } |
||
314 | } |
||
315 | } |
||
316 | |||
317 | $this->setFileUseFulltext($elements[$canvasOrder], $canvas); |
||
318 | |||
319 | if (!empty($fileUses)) { |
||
320 | $image = $canvas->getImageAnnotations()[0]; |
||
321 | foreach ($fileUses as $fileUse) { |
||
322 | if ($image->getBody() !== null && $image->getBody() instanceof ContentResourceInterface) { |
||
323 | $this->physicalStructureInfo[$elements[$canvasOrder]]['files'][$fileUse] = $image->getBody()->getId(); |
||
324 | } |
||
325 | } |
||
326 | } |
||
327 | if (!empty($thumbnailUrl)) { |
||
328 | $this->physicalStructureInfo[$elements[$canvasOrder]]['files'][$fileUseThumbs] = $thumbnailUrl; |
||
329 | } |
||
330 | |||
331 | $this->setFileUseDownload($elements[$canvasOrder], $canvas); |
||
332 | } |
||
333 | $this->numPages = $canvasOrder; |
||
334 | // Merge and re-index the array to get nice numeric indexes. |
||
335 | array_unshift($elements, $iiifId); |
||
336 | $this->physicalStructure = $elements; |
||
337 | } |
||
338 | $this->physicalStructureLoaded = true; |
||
339 | } |
||
340 | return $this->physicalStructure; |
||
341 | } |
||
342 | |||
343 | /** |
||
344 | * @see AbstractDocument::getDownloadLocation() |
||
345 | */ |
||
346 | public function getDownloadLocation(string $id): string |
||
347 | { |
||
348 | $fileLocation = $this->getFileLocation($id); |
||
349 | $resource = $this->iiif->getContainedResourceById($fileLocation); |
||
350 | if ($resource instanceof AbstractImageService) { |
||
351 | return $resource->getImageUrl(); |
||
352 | } |
||
353 | return $fileLocation; |
||
354 | } |
||
355 | |||
356 | /** |
||
357 | * @see AbstractDocument::getFileInfo() |
||
358 | */ |
||
359 | public function getFileInfo($id): ?array |
||
360 | { |
||
361 | if (empty($this->fileInfos[$id]['location'])) { |
||
362 | $this->fileInfos[$id]['location'] = $this->getFileLocation($id); |
||
363 | } |
||
364 | |||
365 | if (empty($this->fileInfos[$id]['mimeType'])) { |
||
366 | $this->fileInfos[$id]['mimeType'] = $this->getFileMimeType($id); |
||
367 | } |
||
368 | |||
369 | return $this->fileInfos[$id] ?? null; |
||
370 | } |
||
371 | |||
372 | /** |
||
373 | * @see AbstractDocument::getFileLocation() |
||
374 | */ |
||
375 | public function getFileLocation(string $id): string |
||
376 | { |
||
377 | if ($id == null) { |
||
378 | return ''; |
||
379 | } |
||
380 | $resource = $this->iiif->getContainedResourceById($id); |
||
381 | if (isset($resource)) { |
||
382 | if ($resource instanceof CanvasInterface) { |
||
383 | // TODO: Cannot call method getSingleService() on array<Ubl\Iiif\Presentation\Common\Model\Resources\AnnotationInterface>. |
||
384 | // @phpstan-ignore-next-line |
||
385 | return (!empty($resource->getImageAnnotations()) && $resource->getImageAnnotations()->getSingleService() != null) ? $resource->getImageAnnotations()[0]->getSingleService()->getId() : $id; |
||
386 | } elseif ($resource instanceof ContentResourceInterface) { |
||
387 | return $resource->getSingleService() instanceof Service ? $resource->getSingleService()->getId() : $id; |
||
388 | } elseif ($resource instanceof AbstractImageService) { |
||
389 | return $resource->getId(); |
||
390 | } elseif ($resource instanceof AnnotationContainerInterface) { |
||
391 | return $id; |
||
392 | } |
||
393 | } |
||
394 | return $id; |
||
395 | } |
||
396 | |||
397 | /** |
||
398 | * @see AbstractDocument::getFileMimeType() |
||
399 | */ |
||
400 | public function getFileMimeType(string $id): string |
||
420 | } |
||
421 | |||
422 | /** |
||
423 | * @see AbstractDocument::getLogicalStructure() |
||
424 | */ |
||
425 | public function getLogicalStructure(string $id, bool $recursive = false): array |
||
426 | { |
||
427 | $details = []; |
||
428 | if (!$recursive && !empty($this->logicalUnits[$id])) { |
||
429 | return $this->logicalUnits[$id]; |
||
430 | } elseif (!empty($id)) { |
||
431 | $logUnits[] = $this->iiif->getContainedResourceById($id); |
||
432 | } else { |
||
433 | $logUnits[] = $this->iiif; |
||
434 | } |
||
435 | // TODO: Variable $logUnits in empty() always exists and is not falsy. |
||
436 | // @phpstan-ignore-next-line |
||
437 | if (!empty($logUnits)) { |
||
438 | if (!$recursive) { |
||
439 | $details = $this->getLogicalStructureInfo($logUnits[0]); |
||
440 | } else { |
||
441 | // cache the ranges - they might occur multiple times in the structures "tree" - with full data as well as referenced as id |
||
442 | $processedStructures = []; |
||
443 | foreach ($logUnits as $logUnit) { |
||
444 | if (array_search($logUnit->getId(), $processedStructures) == false) { |
||
445 | $this->tableOfContents[] = $this->getLogicalStructureInfo($logUnit, true, $processedStructures); |
||
446 | } |
||
447 | } |
||
448 | } |
||
449 | } |
||
450 | return $details; |
||
451 | } |
||
452 | |||
453 | /** |
||
454 | * Get the details about a IIIF resource (manifest or range) in the logical structure |
||
455 | * |
||
456 | * @access protected |
||
457 | * |
||
458 | * @param IiifResourceInterface $resource IIIF resource, either a manifest or range. |
||
459 | * @param bool $recursive Whether to include the child elements |
||
460 | * @param array $processedStructures IIIF resources that already have been processed |
||
461 | * |
||
462 | * @return array Logical structure array |
||
463 | */ |
||
464 | protected function getLogicalStructureInfo(IiifResourceInterface $resource, bool $recursive = false, array &$processedStructures = []): array |
||
465 | { |
||
466 | $details = []; |
||
467 | $details['id'] = $resource->getId(); |
||
468 | $details['dmdId'] = ''; |
||
469 | $details['label'] = $resource->getLabelForDisplay() ?? ''; |
||
470 | $details['orderlabel'] = $resource->getLabelForDisplay() ?? ''; |
||
471 | $details['contentIds'] = ''; |
||
472 | $details['volume'] = ''; |
||
473 | $details['pagination'] = ''; |
||
474 | $cPid = ($this->cPid ? $this->cPid : $this->pid); |
||
475 | if ($details['id'] == $this->magicGetToplevelId()) { |
||
476 | $metadata = $this->getMetadata($details['id'], $cPid); |
||
477 | if (!empty($metadata['type'][0])) { |
||
478 | $details['type'] = $metadata['type'][0]; |
||
479 | } |
||
480 | } |
||
481 | $details['thumbnailId'] = $resource->getThumbnailUrl(); |
||
482 | $details['points'] = ''; |
||
483 | // Load structural mapping |
||
484 | $this->magicGetSmLinks(); |
||
485 | // Load physical structure. |
||
486 | $this->magicGetPhysicalStructure(); |
||
487 | |||
488 | if ($resource instanceof ManifestInterface || $resource instanceof RangeInterface) { |
||
489 | $startCanvas = $resource->getStartCanvasOrFirstCanvas(); |
||
490 | } |
||
491 | if (isset($startCanvas)) { |
||
492 | $details['pagination'] = $startCanvas->getLabel(); |
||
493 | $startCanvasIndex = array_search($startCanvas, $this->iiif->getDefaultCanvases()); |
||
494 | if ($startCanvasIndex !== false) { |
||
495 | $details['points'] = $startCanvasIndex + 1; |
||
496 | } |
||
497 | } |
||
498 | $useGroups = $this->getUseGroups('fileGrpImages'); |
||
499 | if (is_string($useGroups)) { |
||
500 | $useGroups = [$useGroups]; |
||
501 | } |
||
502 | // Keep for later usage. |
||
503 | $this->logicalUnits[$details['id']] = $details; |
||
504 | // Walk the structure recursively? And are there any children of the current element? |
||
505 | if ($recursive) { |
||
506 | $processedStructures[] = $resource->getId(); |
||
507 | $details['children'] = []; |
||
508 | if ($resource instanceof ManifestInterface && $resource->getRootRanges() != null) { |
||
509 | $rangesToAdd = []; |
||
510 | $rootRanges = []; |
||
511 | if (count($this->iiif->getRootRanges()) == 1 && $this->iiif->getRootRanges()[0]->isTopRange()) { |
||
512 | $rangesToAdd = $this->iiif->getRootRanges()[0]->getMemberRangesAndRanges(); |
||
513 | } else { |
||
514 | $rangesToAdd = $this->iiif->getRootRanges(); |
||
515 | } |
||
516 | foreach ($rangesToAdd as $range) { |
||
517 | $rootRanges[] = $range; |
||
518 | } |
||
519 | foreach ($rootRanges as $range) { |
||
520 | if ((array_search($range->getId(), $processedStructures) == false)) { |
||
521 | $details['children'][] = $this->getLogicalStructureInfo($range, true, $processedStructures); |
||
522 | } |
||
523 | } |
||
524 | } elseif ($resource instanceof RangeInterface) { |
||
525 | if (!empty($resource->getAllRanges())) { |
||
526 | foreach ($resource->getAllRanges() as $range) { |
||
527 | if ((array_search($range->getId(), $processedStructures) == false)) { |
||
528 | $details['children'][] = $this->getLogicalStructureInfo($range, true, $processedStructures); |
||
529 | } |
||
530 | } |
||
531 | } |
||
532 | } |
||
533 | } |
||
534 | return $details; |
||
535 | } |
||
536 | |||
537 | /** |
||
538 | * Returns metadata for IIIF resources with the ID $id in there original form in |
||
539 | * the manifest, but prepared for display to the user. |
||
540 | * |
||
541 | * @access public |
||
542 | * |
||
543 | * @param string $id the ID of the IIIF resource |
||
544 | * @param bool $withDescription add description / summary to the return value |
||
545 | * @param bool $withRights add attribution and license / rights and requiredStatement to the return value |
||
546 | * @param bool $withRelated add related links / homepage to the return value |
||
547 | * |
||
548 | * @return array |
||
549 | * |
||
550 | * @todo This method is still in experimental; the method signature may change. |
||
551 | */ |
||
552 | public function getManifestMetadata(string $id, bool $withDescription = true, bool $withRights = true, bool $withRelated = true): array |
||
553 | { |
||
554 | if (!empty($this->originalMetadataArray[$id])) { |
||
555 | return $this->originalMetadataArray[$id]; |
||
556 | } |
||
557 | $iiifResource = $this->iiif->getContainedResourceById($id); |
||
558 | $result = []; |
||
559 | if ($iiifResource != null) { |
||
560 | if (!empty($iiifResource->getLabel())) { |
||
561 | $result['label'] = $iiifResource->getLabel(); |
||
562 | } |
||
563 | if (!empty($iiifResource->getMetadata())) { |
||
564 | $result['metadata'] = []; |
||
565 | foreach ($iiifResource->getMetadataForDisplay() as $metadata) { |
||
566 | $result['metadata'][$metadata['label']] = $metadata['value']; |
||
567 | } |
||
568 | } |
||
569 | if ($withDescription && !empty($iiifResource->getSummary())) { |
||
570 | $result["description"] = $iiifResource->getSummaryForDisplay(); |
||
571 | } |
||
572 | if ($withRights) { |
||
573 | if (!empty($iiifResource->getRights())) { |
||
574 | $result["rights"] = $iiifResource->getRights(); |
||
575 | } |
||
576 | if (!empty($iiifResource->getRequiredStatement())) { |
||
577 | $result["requiredStatement"] = $iiifResource->getRequiredStatementForDisplay(); |
||
578 | } |
||
579 | } |
||
580 | if ($withRelated && !empty($iiifResource->getWeblinksForDisplay())) { |
||
581 | $result["weblinks"] = []; |
||
582 | foreach ($iiifResource->getWeblinksForDisplay() as $link) { |
||
583 | $key = array_key_exists("label", $link) ? $link["label"] : $link["@id"]; |
||
584 | $result["weblinks"][$key] = $link["@id"]; |
||
585 | } |
||
586 | } |
||
587 | } |
||
588 | return $result; |
||
589 | } |
||
590 | |||
591 | /** |
||
592 | * @see AbstractDocument::getMetadata() |
||
593 | */ |
||
594 | public function getMetadata(string $id, int $cPid = 0): array |
||
595 | { |
||
596 | if (!empty($this->metadataArray[$id]) && $this->metadataArray[0] == $cPid) { |
||
597 | return $this->metadataArray[$id]; |
||
598 | } |
||
599 | |||
600 | $metadata = $this->initializeMetadata('IIIF'); |
||
601 | |||
602 | $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) |
||
603 | ->getQueryBuilderForTable('tx_dlf_metadata'); |
||
604 | // Get hidden records, too. |
||
605 | $queryBuilder |
||
606 | ->getRestrictions() |
||
607 | ->removeByType(HiddenRestriction::class); |
||
608 | $result = $queryBuilder |
||
609 | ->select( |
||
610 | 'tx_dlf_metadata.index_name AS index_name', |
||
611 | 'tx_dlf_metadataformat.xpath AS xpath', |
||
612 | 'tx_dlf_metadataformat.xpath_sorting AS xpath_sorting', |
||
613 | 'tx_dlf_metadata.is_sortable AS is_sortable', |
||
614 | 'tx_dlf_metadata.default_value AS default_value', |
||
615 | 'tx_dlf_metadata.format AS format' |
||
616 | ) |
||
617 | ->from('tx_dlf_metadata') |
||
618 | ->from('tx_dlf_metadataformat') |
||
619 | ->from('tx_dlf_formats') |
||
620 | ->where( |
||
621 | $queryBuilder->expr()->eq('tx_dlf_metadata.pid', (int) $cPid), |
||
622 | $queryBuilder->expr()->eq('tx_dlf_metadataformat.pid', (int) $cPid), |
||
623 | $queryBuilder->expr()->orX( |
||
624 | $queryBuilder->expr()->andX( |
||
625 | $queryBuilder->expr()->eq('tx_dlf_metadata.uid', 'tx_dlf_metadataformat.parent_id'), |
||
626 | $queryBuilder->expr()->eq('tx_dlf_metadataformat.encoded', 'tx_dlf_formats.uid'), |
||
627 | $queryBuilder->expr()->eq('tx_dlf_formats.type', $queryBuilder->createNamedParameter($this->getIiifVersion())) |
||
628 | ), |
||
629 | $queryBuilder->expr()->eq('tx_dlf_metadata.format', 0) |
||
630 | ) |
||
631 | ) |
||
632 | ->execute(); |
||
633 | $iiifResource = $this->iiif->getContainedResourceById($id); |
||
634 | while ($resArray = $result->fetchAssociative()) { |
||
635 | // Set metadata field's value(s). |
||
636 | if ($resArray['format'] > 0 && !empty($resArray['xpath'])) { |
||
637 | $values = $iiifResource->jsonPath($resArray['xpath']); |
||
638 | if (is_string($values)) { |
||
639 | $metadata[$resArray['index_name']] = [trim((string) $values)]; |
||
640 | } elseif ($values instanceof JSONPath && is_array($values->data()) && count($values->data()) > 1) { |
||
641 | $metadata[$resArray['index_name']] = []; |
||
642 | foreach ($values->data() as $value) { |
||
643 | $metadata[$resArray['index_name']][] = trim((string) $value); |
||
644 | } |
||
645 | } |
||
646 | } |
||
647 | // Set default value if applicable. |
||
648 | if (empty($metadata[$resArray['index_name']][0]) && strlen($resArray['default_value']) > 0) { |
||
649 | $metadata[$resArray['index_name']] = [$resArray['default_value']]; |
||
650 | } |
||
651 | // Set sorting value if applicable. |
||
652 | if (!empty($metadata[$resArray['index_name']]) && $resArray['is_sortable']) { |
||
653 | if ($resArray['format'] > 0 && !empty($resArray['xpath_sorting'])) { |
||
654 | $values = $iiifResource->jsonPath($resArray['xpath_sorting']); |
||
655 | if (is_string($values)) { |
||
656 | $metadata[$resArray['index_name'] . '_sorting'][0] = [trim((string) $values)]; |
||
657 | } elseif ($values instanceof JSONPath && is_array($values->data()) && count($values->data()) > 1) { |
||
658 | $metadata[$resArray['index_name']] = []; |
||
659 | foreach ($values->data() as $value) { |
||
660 | $metadata[$resArray['index_name'] . '_sorting'][0] = trim((string) $value); |
||
661 | } |
||
662 | } |
||
663 | } |
||
664 | if (empty($metadata[$resArray['index_name'] . '_sorting'][0])) { |
||
665 | $metadata[$resArray['index_name'] . '_sorting'][0] = $metadata[$resArray['index_name']][0]; |
||
666 | } |
||
667 | } |
||
668 | } |
||
669 | // Set date to empty string if not present. |
||
670 | if (empty($metadata['date'][0])) { |
||
671 | $metadata['date'][0] = ''; |
||
672 | } |
||
673 | return $metadata; |
||
674 | } |
||
675 | |||
676 | /** |
||
677 | * @see AbstractDocument::magicGetSmLinks() |
||
678 | */ |
||
679 | protected function magicGetSmLinks(): array |
||
680 | { |
||
681 | if (!$this->smLinksLoaded && isset($this->iiif) && $this->iiif instanceof ManifestInterface) { |
||
682 | if (!empty($this->iiif->getDefaultCanvases())) { |
||
683 | foreach ($this->iiif->getDefaultCanvases() as $canvas) { |
||
684 | $this->smLinkCanvasToResource($canvas, $this->iiif); |
||
685 | } |
||
686 | } |
||
687 | if (!empty($this->iiif->getStructures())) { |
||
688 | foreach ($this->iiif->getStructures() as $range) { |
||
689 | $this->smLinkRangeCanvasesRecursively($range); |
||
690 | } |
||
691 | } |
||
692 | $this->smLinksLoaded = true; |
||
693 | } |
||
694 | return $this->smLinks; |
||
695 | } |
||
696 | |||
697 | /** |
||
698 | * Construct a link between a range and it's sub ranges and all contained canvases. |
||
699 | * |
||
700 | * @access private |
||
701 | * |
||
702 | * @param RangeInterface $range Current range whose canvases shall be linked |
||
703 | * |
||
704 | * @return void |
||
705 | */ |
||
706 | private function smLinkRangeCanvasesRecursively(RangeInterface $range): void |
||
707 | { |
||
708 | // map range's canvases including all child ranges' canvases |
||
709 | if (!$range->isTopRange()) { |
||
710 | foreach ($range->getAllCanvasesRecursively() as $canvas) { |
||
711 | $this->smLinkCanvasToResource($canvas, $range); |
||
712 | } |
||
713 | } |
||
714 | // recursive call for all ranges |
||
715 | if (!empty($range->getAllRanges())) { |
||
716 | foreach ($range->getAllRanges() as $childRange) { |
||
717 | $this->smLinkRangeCanvasesRecursively($childRange); |
||
718 | } |
||
719 | } |
||
720 | } |
||
721 | |||
722 | /** |
||
723 | * Link a single canvas to a containing range |
||
724 | * |
||
725 | * @access private |
||
726 | * |
||
727 | * @param CanvasInterface $canvas |
||
728 | * @param IiifResourceInterface $resource |
||
729 | * |
||
730 | * @return void |
||
731 | */ |
||
732 | private function smLinkCanvasToResource(CanvasInterface $canvas, IiifResourceInterface $resource): void |
||
733 | { |
||
734 | $this->smLinks['l2p'][$resource->getId()][] = $canvas->getId(); |
||
735 | if (!is_array($this->smLinks['p2l'][$canvas->getId()]) || !in_array($resource->getId(), $this->smLinks['p2l'][$canvas->getId()])) { |
||
736 | $this->smLinks['p2l'][$canvas->getId()][] = $resource->getId(); |
||
737 | } |
||
738 | } |
||
739 | |||
740 | /** |
||
741 | * @see AbstractDocument::getFullText() |
||
742 | */ |
||
743 | //TODO: rewrite it to get full OCR |
||
744 | public function getFullText(string $id): string |
||
745 | { |
||
746 | $rawText = ''; |
||
747 | // Get text from raw text array if available. |
||
748 | if (!empty($this->rawTextArray[$id])) { |
||
749 | return $this->rawTextArray[$id]; |
||
750 | } |
||
751 | $this->ensureHasFulltextIsSet(); |
||
752 | if ($this->hasFulltext) { |
||
753 | // Load physical structure ... |
||
754 | $this->magicGetPhysicalStructure(); |
||
755 | // ... and extension configuration. |
||
756 | $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey); |
||
757 | $fileGrpsFulltext = GeneralUtility::trimExplode(',', $extConf['files']['fileGrpFulltext']); |
||
758 | if (!empty($this->physicalStructureInfo[$id])) { |
||
759 | while ($fileGrpFulltext = array_shift($fileGrpsFulltext)) { |
||
760 | if (!empty($this->physicalStructureInfo[$id]['files'][$fileGrpFulltext])) { |
||
761 | $rawText = parent::getFullTextFromXml($id); |
||
762 | break; |
||
763 | } |
||
764 | } |
||
765 | if ($extConf['iiif']['indexAnnotations'] == 1) { |
||
766 | $iiifResource = $this->iiif->getContainedResourceById($id); |
||
767 | // Get annotation containers |
||
768 | $annotationContainerIds = $this->physicalStructureInfo[$id]['annotationContainers']; |
||
769 | if (!empty($annotationContainerIds)) { |
||
770 | $annotationTexts = $this->getAnnotationTexts($annotationContainerIds, $iiifResource->getId()); |
||
771 | $rawText .= implode(' ', $annotationTexts); |
||
772 | } |
||
773 | } |
||
774 | } else { |
||
775 | $this->logger->warning('Invalid structure resource @id "' . $id . '"'); |
||
776 | return $rawText; |
||
777 | } |
||
778 | $this->rawTextArray[$id] = $rawText; |
||
779 | } |
||
780 | return $rawText; |
||
781 | } |
||
782 | |||
783 | /** |
||
784 | * Returns the underlying IiifResourceInterface. |
||
785 | * |
||
786 | * @access public |
||
787 | * |
||
788 | * @return IiifResourceInterface |
||
789 | */ |
||
790 | public function getIiif(): IiifResourceInterface |
||
791 | { |
||
792 | return $this->iiif; |
||
793 | } |
||
794 | |||
795 | /** |
||
796 | * @see AbstractDocument::init() |
||
797 | */ |
||
798 | protected function init(string $location, array $settings = []): void |
||
799 | { |
||
800 | $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class); |
||
801 | } |
||
802 | |||
803 | /** |
||
804 | * @see AbstractDocument::loadLocation() |
||
805 | */ |
||
806 | protected function loadLocation(string $location): bool |
||
807 | { |
||
808 | $fileResource = GeneralUtility::getUrl($location); |
||
809 | if ($fileResource !== false) { |
||
810 | $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'iiif'); |
||
811 | IiifHelper::setUrlReader(IiifUrlReader::getInstance()); |
||
812 | IiifHelper::setMaxThumbnailHeight($conf['thumbnailHeight']); |
||
813 | IiifHelper::setMaxThumbnailWidth($conf['thumbnailWidth']); |
||
814 | $resource = IiifHelper::loadIiifResource($fileResource); |
||
815 | if ($resource instanceof ManifestInterface) { |
||
816 | $this->iiif = $resource; |
||
817 | return true; |
||
818 | } |
||
819 | } |
||
820 | $this->logger->error('Could not load IIIF manifest from "' . $location . '"'); |
||
821 | return false; |
||
822 | } |
||
823 | |||
824 | /** |
||
825 | * @see AbstractDocument::prepareMetadataArray() |
||
826 | */ |
||
827 | protected function prepareMetadataArray(int $cPid): void |
||
831 | } |
||
832 | |||
833 | /** |
||
834 | * @see AbstractDocument::setPreloadedDocument() |
||
835 | */ |
||
836 | protected function setPreloadedDocument($preloadedDocument): bool |
||
837 | { |
||
838 | if ($preloadedDocument instanceof ManifestInterface) { |
||
839 | $this->iiif = $preloadedDocument; |
||
840 | return true; |
||
841 | } |
||
842 | return false; |
||
843 | } |
||
844 | |||
845 | /** |
||
846 | * @see AbstractDocument::ensureHasFulltextIsSet() |
||
847 | */ |
||
848 | protected function ensureHasFulltextIsSet(): void |
||
889 | } |
||
890 | } |
||
891 | |||
892 | /** |
||
893 | * @see AbstractDocument::magicGetThumbnail() |
||
894 | */ |
||
895 | protected function magicGetThumbnail(bool $forceReload = false): string |
||
896 | { |
||
897 | return $this->iiif->getThumbnailUrl(); |
||
898 | } |
||
899 | |||
900 | /** |
||
901 | * @see AbstractDocument::magicGetToplevelId() |
||
902 | */ |
||
903 | protected function magicGetToplevelId(): string |
||
904 | { |
||
905 | if (empty($this->toplevelId)) { |
||
906 | if (isset($this->iiif)) { |
||
907 | $this->toplevelId = $this->iiif->getId(); |
||
908 | } |
||
909 | } |
||
910 | return $this->toplevelId; |
||
911 | } |
||
912 | |||
913 | /** |
||
914 | * Get annotation texts. |
||
915 | * |
||
916 | * @access private |
||
917 | * |
||
918 | * @param array $annotationContainerIds |
||
919 | * @param string $iiifId |
||
920 | * |
||
921 | * @return array |
||
922 | */ |
||
923 | private function getAnnotationTexts($annotationContainerIds, $iiifId): array |
||
924 | { |
||
925 | $annotationTexts = []; |
||
926 | foreach ($annotationContainerIds as $annotationListId) { |
||
927 | $annotationContainer = $this->iiif->getContainedResourceById($annotationListId); |
||
928 | /* @var $annotationContainer \Ubl\Iiif\Presentation\Common\Model\Resources\AnnotationContainerInterface */ |
||
929 | foreach ($annotationContainer->getTextAnnotations(Motivation::PAINTING) as $annotation) { |
||
930 | if ( |
||
931 | $annotation->getTargetResourceId() == $iiifId && |
||
932 | $annotation->getBody() != null && $annotation->getBody()->getChars() != null |
||
933 | ) { |
||
934 | $annotationTexts[] = $annotation->getBody()->getChars(); |
||
935 | } |
||
936 | } |
||
937 | } |
||
938 | return $annotationTexts; |
||
939 | } |
||
940 | |||
941 | /** |
||
942 | * Set files used for download (PDF). |
||
943 | * |
||
944 | * @access private |
||
945 | * |
||
946 | * @param string $iiifId |
||
947 | * @param IiifResourceInterface $iiif |
||
948 | * |
||
949 | * @return void |
||
950 | */ |
||
951 | private function setFileUseDownload(string $iiifId, $iiif): void |
||
952 | { |
||
953 | $fileUseDownload = $this->getUseGroups('fileGrpDownload'); |
||
954 | |||
955 | if (!empty($fileUseDownload)) { |
||
956 | $docPdfRendering = $iiif->getRenderingUrlsForFormat('application/pdf'); |
||
957 | if (!empty($docPdfRendering)) { |
||
958 | $this->physicalStructureInfo[$iiifId]['files'][$fileUseDownload[0]] = $docPdfRendering[0]; |
||
959 | } |
||
960 | } |
||
961 | } |
||
962 | |||
963 | /** |
||
964 | * Set files used for full text (ALTO). |
||
965 | * |
||
966 | * @access private |
||
967 | * |
||
968 | * @param string $iiifId |
||
969 | * @param IiifResourceInterface $iiif |
||
970 | * |
||
971 | * @return void |
||
972 | */ |
||
973 | private function setFileUseFulltext(string $iiifId, $iiif): void |
||
987 | } |
||
988 | } |
||
989 | } |
||
990 | |||
991 | /** |
||
992 | * This magic method is executed after the object is deserialized |
||
993 | * @see __sleep() |
||
994 | * |
||
995 | * @access public |
||
996 | * |
||
997 | * @return void |
||
998 | */ |
||
999 | public function __wakeup(): void |
||
1000 | { |
||
1001 | $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'iiif'); |
||
1002 | IiifHelper::setUrlReader(IiifUrlReader::getInstance()); |
||
1003 | IiifHelper::setMaxThumbnailHeight($conf['thumbnailHeight']); |
||
1004 | IiifHelper::setMaxThumbnailWidth($conf['thumbnailWidth']); |
||
1005 | $resource = IiifHelper::loadIiifResource($this->asJson); |
||
1006 | if ($resource instanceof ManifestInterface) { |
||
1007 | $this->asJson = ''; |
||
1008 | $this->iiif = $resource; |
||
1009 | $this->init(''); |
||
1010 | } else { |
||
1011 | $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class); |
||
1012 | $this->logger->error('Could not load IIIF after deserialization'); |
||
1013 | } |
||
1014 | } |
||
1015 | |||
1016 | /** |
||
1017 | * @access public |
||
1018 | * |
||
1019 | * @return string[] |
||
1020 | */ |
||
1021 | public function __sleep(): array |
||
1027 | } |
||
1028 | } |
||
1029 |
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 theid
property of an instance of theAccount
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.