|
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 AbstractDocument |
|
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 AbstractDocument::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->fetchAssociative()) { |
|
157
|
|
|
$recordIdPath = $resArray['querypath']; |
|
158
|
|
|
if (!empty($recordIdPath)) { |
|
159
|
|
|
try { |
|
160
|
|
|
$this->recordId = $this->iiif->jsonPath($recordIdPath); |
|
|
|
|
|
|
161
|
|
|
} catch (\Exception $e) { |
|
162
|
|
|
$this->logger->warning('Could not evaluate JSONPath to get IIIF record ID'); |
|
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 AbstractDocument::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 structure 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 AbstractDocument::_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; |
|
272
|
|
|
$this->physicalStructureInfo[$physSeq[0]]['id'] = $iiifId; |
|
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; |
|
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; |
|
364
|
|
|
// Merge and re-index the array to get nice numeric indexes. |
|
365
|
|
|
$this->physicalStructure = array_merge($physSeq, $elements); |
|
|
|
|
|
|
366
|
|
|
} |
|
367
|
|
|
$this->physicalStructureLoaded = true; |
|
368
|
|
|
} |
|
369
|
|
|
return $this->physicalStructure; |
|
370
|
|
|
} |
|
371
|
|
|
|
|
372
|
|
|
/** |
|
373
|
|
|
* {@inheritDoc} |
|
374
|
|
|
* @see AbstractDocument::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 AbstractDocument::getFileInfo() |
|
389
|
|
|
*/ |
|
390
|
|
|
public function getFileInfo($id) |
|
391
|
|
|
{ |
|
392
|
|
|
if (empty($this->fileInfos[$id]['location'])) { |
|
393
|
|
|
$this->fileInfos[$id]['location'] = $this->getFileLocation($id); |
|
|
|
|
|
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
|
|
if (empty($this->fileInfos[$id]['mimeType'])) { |
|
397
|
|
|
$this->fileInfos[$id]['mimeType'] = $this->getFileMimeType($id); |
|
398
|
|
|
} |
|
399
|
|
|
|
|
400
|
|
|
return $this->fileInfos[$id]; |
|
401
|
|
|
} |
|
402
|
|
|
|
|
403
|
|
|
/** |
|
404
|
|
|
* {@inheritDoc} |
|
405
|
|
|
* @see AbstractDocument::getFileLocation() |
|
406
|
|
|
*/ |
|
407
|
|
|
public function getFileLocation($id) |
|
408
|
|
|
{ |
|
409
|
|
|
if ($id == null) { |
|
410
|
|
|
return null; |
|
411
|
|
|
} |
|
412
|
|
|
$resource = $this->iiif->getContainedResourceById($id); |
|
413
|
|
|
if (isset($resource)) { |
|
414
|
|
|
if ($resource instanceof CanvasInterface) { |
|
415
|
|
|
return (!empty($resource->getImageAnnotations()) && $resource->getImageAnnotations()->getSingleService() != null) ? $resource->getImageAnnotations()[0]->getSingleService()->getId() : $id; |
|
416
|
|
|
} elseif ($resource instanceof ContentResourceInterface) { |
|
417
|
|
|
return $resource->getSingleService() != null && $resource->getSingleService() instanceof Service ? $resource->getSingleService()->getId() : $id; |
|
418
|
|
|
} elseif ($resource instanceof AbstractImageService) { |
|
419
|
|
|
return $resource->getId(); |
|
420
|
|
|
} elseif ($resource instanceof AnnotationContainerInterface) { |
|
421
|
|
|
return $id; |
|
422
|
|
|
} |
|
423
|
|
|
} else { |
|
424
|
|
|
return $id; |
|
425
|
|
|
} |
|
426
|
|
|
} |
|
427
|
|
|
|
|
428
|
|
|
/** |
|
429
|
|
|
* {@inheritDoc} |
|
430
|
|
|
* @see AbstractDocument::getFileMimeType() |
|
431
|
|
|
*/ |
|
432
|
|
|
public function getFileMimeType($id) |
|
433
|
|
|
{ |
|
434
|
|
|
$fileResource = $this->iiif->getContainedResourceById($id); |
|
435
|
|
|
if ($fileResource instanceof CanvasInterface) { |
|
436
|
|
|
$format = "application/vnd.kitodo.iiif"; |
|
437
|
|
|
} elseif ($fileResource instanceof AnnotationInterface) { |
|
438
|
|
|
$format = "application/vnd.kitodo.iiif"; |
|
439
|
|
|
} elseif ($fileResource instanceof ContentResourceInterface) { |
|
440
|
|
|
if ($fileResource->isText() || $fileResource->isImage() && ($fileResource->getSingleService() == null || !($fileResource->getSingleService() instanceof AbstractImageService))) { |
|
441
|
|
|
// Support static images without an image service |
|
442
|
|
|
return $fileResource->getFormat(); |
|
443
|
|
|
} |
|
444
|
|
|
$format = "application/vnd.kitodo.iiif"; |
|
445
|
|
|
} elseif ($fileResource instanceof AbstractImageService) { |
|
446
|
|
|
$format = "application/vnd.kitodo.iiif"; |
|
447
|
|
|
} else { |
|
448
|
|
|
// Assumptions: this can only be the thumbnail and the thumbnail is a jpeg - TODO determine mimetype |
|
449
|
|
|
$format = "image/jpeg"; |
|
450
|
|
|
} |
|
451
|
|
|
return $format; |
|
452
|
|
|
} |
|
453
|
|
|
|
|
454
|
|
|
/** |
|
455
|
|
|
* {@inheritDoc} |
|
456
|
|
|
* @see AbstractDocument::getLogicalStructure() |
|
457
|
|
|
*/ |
|
458
|
|
|
public function getLogicalStructure($id, $recursive = false) |
|
459
|
|
|
{ |
|
460
|
|
|
$details = []; |
|
461
|
|
|
if (!$recursive && !empty($this->logicalUnits[$id])) { |
|
462
|
|
|
return $this->logicalUnits[$id]; |
|
463
|
|
|
} elseif (!empty($id)) { |
|
464
|
|
|
$logUnits[] = $this->iiif->getContainedResourceById($id); |
|
465
|
|
|
} else { |
|
466
|
|
|
$logUnits[] = $this->iiif; |
|
467
|
|
|
} |
|
468
|
|
|
if (!empty($logUnits)) { |
|
469
|
|
|
if (!$recursive) { |
|
470
|
|
|
$details = $this->getLogicalStructureInfo($logUnits[0]); |
|
471
|
|
|
} else { |
|
472
|
|
|
// cache the ranges - they might occur multiple times in the structures "tree" - with full data as well as referenced as id |
|
473
|
|
|
$processedStructures = []; |
|
474
|
|
|
foreach ($logUnits as $logUnit) { |
|
475
|
|
|
if (array_search($logUnit->getId(), $processedStructures) == false) { |
|
|
|
|
|
|
476
|
|
|
$this->tableOfContents[] = $this->getLogicalStructureInfo($logUnit, true, $processedStructures); |
|
477
|
|
|
} |
|
478
|
|
|
} |
|
479
|
|
|
} |
|
480
|
|
|
} |
|
481
|
|
|
return $details; |
|
482
|
|
|
} |
|
483
|
|
|
|
|
484
|
|
|
/** |
|
485
|
|
|
* Get the details about a IIIF resource (manifest or range) in the logical structure |
|
486
|
|
|
* |
|
487
|
|
|
* @access protected |
|
488
|
|
|
* |
|
489
|
|
|
* @param IiifResourceInterface $resource: IIIF resource, either a manifest or range. |
|
490
|
|
|
* @param bool $recursive: Whether to include the child elements |
|
491
|
|
|
* @param array $processedStructures: IIIF resources that already have been processed |
|
492
|
|
|
* @return array Logical structure array |
|
493
|
|
|
*/ |
|
494
|
|
|
protected function getLogicalStructureInfo(IiifResourceInterface $resource, $recursive = false, &$processedStructures = []) |
|
495
|
|
|
{ |
|
496
|
|
|
$details = []; |
|
497
|
|
|
$details['id'] = $resource->getId(); |
|
498
|
|
|
$details['dmdId'] = ''; |
|
499
|
|
|
$details['label'] = $resource->getLabelForDisplay() !== null ? $resource->getLabelForDisplay() : ''; |
|
|
|
|
|
|
500
|
|
|
$details['orderlabel'] = $resource->getLabelForDisplay() !== null ? $resource->getLabelForDisplay() : ''; |
|
|
|
|
|
|
501
|
|
|
$details['contentIds'] = ''; |
|
502
|
|
|
$details['volume'] = ''; |
|
503
|
|
|
$details['pagination'] = ''; |
|
504
|
|
|
$cPid = ($this->cPid ? $this->cPid : $this->pid); |
|
505
|
|
|
if ($details['id'] == $this->_getToplevelId()) { |
|
506
|
|
|
$metadata = $this->getMetadata($details['id'], $cPid); |
|
507
|
|
|
if (!empty($metadata['type'][0])) { |
|
508
|
|
|
$details['type'] = $metadata['type'][0]; |
|
509
|
|
|
} |
|
510
|
|
|
} |
|
511
|
|
|
$details['thumbnailId'] = $resource->getThumbnailUrl(); |
|
512
|
|
|
$details['points'] = ''; |
|
513
|
|
|
// Load strucural mapping |
|
514
|
|
|
$this->_getSmLinks(); |
|
515
|
|
|
// Load physical structure. |
|
516
|
|
|
$this->_getPhysicalStructure(); |
|
517
|
|
|
$canvases = []; |
|
518
|
|
|
if ($resource instanceof ManifestInterface) { |
|
519
|
|
|
$startCanvas = $resource->getStartCanvasOrFirstCanvas(); |
|
520
|
|
|
$canvases = $resource->getDefaultCanvases(); |
|
|
|
|
|
|
521
|
|
|
} elseif ($resource instanceof RangeInterface) { |
|
522
|
|
|
$startCanvas = $resource->getStartCanvasOrFirstCanvas(); |
|
523
|
|
|
$canvases = $resource->getAllCanvases(); |
|
524
|
|
|
} |
|
525
|
|
|
if ($startCanvas != null) { |
|
|
|
|
|
|
526
|
|
|
$details['pagination'] = $startCanvas->getLabel(); |
|
527
|
|
|
$startCanvasIndex = array_search($startCanvas, $this->iiif->getDefaultCanvases()); |
|
528
|
|
|
if ($startCanvasIndex !== false) { |
|
529
|
|
|
$details['points'] = $startCanvasIndex + 1; |
|
530
|
|
|
} |
|
531
|
|
|
} |
|
532
|
|
|
$useGroups = $this->getUseGroups('fileGrpImages'); |
|
533
|
|
|
if (is_string($useGroups)) { |
|
534
|
|
|
$useGroups = [$useGroups]; |
|
|
|
|
|
|
535
|
|
|
} |
|
536
|
|
|
// Keep for later usage. |
|
537
|
|
|
$this->logicalUnits[$details['id']] = $details; |
|
538
|
|
|
// Walk the structure recursively? And are there any children of the current element? |
|
539
|
|
|
if ($recursive) { |
|
540
|
|
|
$processedStructures[] = $resource->getId(); |
|
541
|
|
|
$details['children'] = []; |
|
542
|
|
|
if ($resource instanceof ManifestInterface && $resource->getRootRanges() != null) { |
|
543
|
|
|
$rangesToAdd = []; |
|
544
|
|
|
$rootRanges = []; |
|
545
|
|
|
if (sizeof($this->iiif->getRootRanges()) == 1 && $this->iiif->getRootRanges()[0]->isTopRange()) { |
|
546
|
|
|
$rangesToAdd = $this->iiif->getRootRanges()[0]->getMemberRangesAndRanges(); |
|
|
|
|
|
|
547
|
|
|
} else { |
|
548
|
|
|
$rangesToAdd = $this->iiif->getRootRanges(); |
|
549
|
|
|
} |
|
550
|
|
|
foreach ($rangesToAdd as $range) { |
|
551
|
|
|
$rootRanges[] = $range; |
|
552
|
|
|
} |
|
553
|
|
|
foreach ($rootRanges as $range) { |
|
554
|
|
|
if ((array_search($range->getId(), $processedStructures) == false)) { |
|
|
|
|
|
|
555
|
|
|
$details['children'][] = $this->getLogicalStructureInfo($range, true, $processedStructures); |
|
556
|
|
|
} |
|
557
|
|
|
} |
|
558
|
|
|
} elseif ($resource instanceof RangeInterface) { |
|
559
|
|
|
if (!empty($resource->getAllRanges())) { |
|
560
|
|
|
foreach ($resource->getAllRanges() as $range) { |
|
561
|
|
|
if ((array_search($range->getId(), $processedStructures) == false)) { |
|
|
|
|
|
|
562
|
|
|
$details['children'][] = $this->getLogicalStructureInfo($range, true, $processedStructures); |
|
563
|
|
|
} |
|
564
|
|
|
} |
|
565
|
|
|
} |
|
566
|
|
|
} |
|
567
|
|
|
} |
|
568
|
|
|
return $details; |
|
569
|
|
|
} |
|
570
|
|
|
|
|
571
|
|
|
/** |
|
572
|
|
|
* Returns metadata for IIIF resources with the ID $id in there original form in |
|
573
|
|
|
* the manifest, but prepared for display to the user. |
|
574
|
|
|
* |
|
575
|
|
|
* @access public |
|
576
|
|
|
* |
|
577
|
|
|
* @param string $id: the ID of the IIIF resource |
|
578
|
|
|
* @param int $cPid: the configuration folder's id |
|
579
|
|
|
* @param bool $withDescription: add description / summary to the return value |
|
580
|
|
|
* @param bool $withRights: add attribution and license / rights and requiredStatement to the return value |
|
581
|
|
|
* @param bool $withRelated: add related links / homepage to the return value |
|
582
|
|
|
* |
|
583
|
|
|
* @return array |
|
584
|
|
|
* |
|
585
|
|
|
* @todo This method is still in experimental; the method signature may change. |
|
586
|
|
|
*/ |
|
587
|
|
|
public function getManifestMetadata($id, $cPid = 0, $withDescription = true, $withRights = true, $withRelated = true) |
|
|
|
|
|
|
588
|
|
|
{ |
|
589
|
|
|
if (!empty($this->originalMetadataArray[$id])) { |
|
590
|
|
|
return $this->originalMetadataArray[$id]; |
|
591
|
|
|
} |
|
592
|
|
|
$iiifResource = $this->iiif->getContainedResourceById($id); |
|
593
|
|
|
$result = []; |
|
594
|
|
|
if ($iiifResource != null) { |
|
595
|
|
|
if ($iiifResource->getLabel() != null && $iiifResource->getLabel() != "") { |
|
596
|
|
|
$result['label'] = $iiifResource->getLabel(); |
|
597
|
|
|
} |
|
598
|
|
|
if (!empty($iiifResource->getMetadata())) { |
|
599
|
|
|
$result['metadata'] = []; |
|
600
|
|
|
foreach ($iiifResource->getMetadataForDisplay() as $metadata) { |
|
601
|
|
|
$result['metadata'][$metadata['label']] = $metadata['value']; |
|
602
|
|
|
} |
|
603
|
|
|
} |
|
604
|
|
|
if ($withDescription && !empty($iiifResource->getSummary())) { |
|
605
|
|
|
$result["description"] = $iiifResource->getSummaryForDisplay(); |
|
606
|
|
|
} |
|
607
|
|
|
if ($withRights) { |
|
608
|
|
|
if (!empty($iiifResource->getRights())) { |
|
609
|
|
|
$result["rights"] = $iiifResource->getRights(); |
|
610
|
|
|
} |
|
611
|
|
|
if (!empty($iiifResource->getRequiredStatement())) { |
|
612
|
|
|
$result["requiredStatement"] = $iiifResource->getRequiredStatementForDisplay(); |
|
613
|
|
|
} |
|
614
|
|
|
} |
|
615
|
|
|
if ($withRelated && !empty($iiifResource->getWeblinksForDisplay())) { |
|
616
|
|
|
$result["weblinks"] = []; |
|
617
|
|
|
foreach ($iiifResource->getWeblinksForDisplay() as $link) { |
|
618
|
|
|
$key = array_key_exists("label", $link) ? $link["label"] : $link["@id"]; |
|
619
|
|
|
$result["weblinks"][$key] = $link["@id"]; |
|
620
|
|
|
} |
|
621
|
|
|
} |
|
622
|
|
|
} |
|
623
|
|
|
return $result; |
|
624
|
|
|
} |
|
625
|
|
|
|
|
626
|
|
|
/** |
|
627
|
|
|
* {@inheritDoc} |
|
628
|
|
|
* @see AbstractDocument::getMetadata() |
|
629
|
|
|
*/ |
|
630
|
|
|
public function getMetadata($id, $cPid = 0) |
|
631
|
|
|
{ |
|
632
|
|
|
if (!empty($this->metadataArray[$id]) && $this->metadataArray[0] == $cPid) { |
|
633
|
|
|
return $this->metadataArray[$id]; |
|
634
|
|
|
} |
|
635
|
|
|
|
|
636
|
|
|
$metadata = $this->initializeMetadata('IIIF'); |
|
637
|
|
|
|
|
638
|
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) |
|
639
|
|
|
->getQueryBuilderForTable('tx_dlf_metadata'); |
|
640
|
|
|
// Get hidden records, too. |
|
641
|
|
|
$queryBuilder |
|
642
|
|
|
->getRestrictions() |
|
643
|
|
|
->removeByType(HiddenRestriction::class); |
|
644
|
|
|
$result = $queryBuilder |
|
645
|
|
|
->select( |
|
646
|
|
|
'tx_dlf_metadata.index_name AS index_name', |
|
647
|
|
|
'tx_dlf_metadataformat.xpath AS xpath', |
|
648
|
|
|
'tx_dlf_metadataformat.xpath_sorting AS xpath_sorting', |
|
649
|
|
|
'tx_dlf_metadata.is_sortable AS is_sortable', |
|
650
|
|
|
'tx_dlf_metadata.default_value AS default_value', |
|
651
|
|
|
'tx_dlf_metadata.format AS format' |
|
652
|
|
|
) |
|
653
|
|
|
->from('tx_dlf_metadata') |
|
654
|
|
|
->from('tx_dlf_metadataformat') |
|
655
|
|
|
->from('tx_dlf_formats') |
|
656
|
|
|
->where( |
|
657
|
|
|
$queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($cPid)), |
|
658
|
|
|
$queryBuilder->expr()->eq('tx_dlf_metadataformat.pid', intval($cPid)), |
|
659
|
|
|
$queryBuilder->expr()->orX( |
|
660
|
|
|
$queryBuilder->expr()->andX( |
|
661
|
|
|
$queryBuilder->expr()->eq('tx_dlf_metadata.uid', 'tx_dlf_metadataformat.parent_id'), |
|
662
|
|
|
$queryBuilder->expr()->eq('tx_dlf_metadataformat.encoded', 'tx_dlf_formats.uid'), |
|
663
|
|
|
$queryBuilder->expr()->eq('tx_dlf_formats.type', $queryBuilder->createNamedParameter($this->getIiifVersion())) |
|
664
|
|
|
), |
|
665
|
|
|
$queryBuilder->expr()->eq('tx_dlf_metadata.format', 0) |
|
666
|
|
|
) |
|
667
|
|
|
) |
|
668
|
|
|
->execute(); |
|
669
|
|
|
$iiifResource = $this->iiif->getContainedResourceById($id); |
|
670
|
|
|
while ($resArray = $result->fetchAssociative()) { |
|
671
|
|
|
// Set metadata field's value(s). |
|
672
|
|
|
if ($resArray['format'] > 0 && !empty($resArray['xpath']) && ($values = $iiifResource->jsonPath($resArray['xpath'])) != null) { |
|
673
|
|
|
if (is_string($values)) { |
|
674
|
|
|
$metadata[$resArray['index_name']] = [trim((string) $values)]; |
|
675
|
|
|
} elseif ($values instanceof JSONPath && is_array($values->data()) && count($values->data()) > 1) { |
|
676
|
|
|
$metadata[$resArray['index_name']] = []; |
|
677
|
|
|
foreach ($values->data() as $value) { |
|
678
|
|
|
$metadata[$resArray['index_name']][] = trim((string) $value); |
|
679
|
|
|
} |
|
680
|
|
|
} |
|
681
|
|
|
} |
|
682
|
|
|
// Set default value if applicable. |
|
683
|
|
|
if (empty($metadata[$resArray['index_name']][0]) && strlen($resArray['default_value']) > 0) { |
|
684
|
|
|
$metadata[$resArray['index_name']] = [$resArray['default_value']]; |
|
685
|
|
|
} |
|
686
|
|
|
// Set sorting value if applicable. |
|
687
|
|
|
if (!empty($metadata[$resArray['index_name']]) && $resArray['is_sortable']) { |
|
688
|
|
|
if ( |
|
689
|
|
|
$resArray['format'] > 0 && !empty($resArray['xpath_sorting']) |
|
690
|
|
|
&& ($values = $iiifResource->jsonPath($resArray['xpath_sorting']) != null) |
|
691
|
|
|
) { |
|
692
|
|
|
if (is_string($values)) { |
|
693
|
|
|
$metadata[$resArray['index_name'] . '_sorting'][0] = [trim((string) $values)]; |
|
694
|
|
|
} elseif ($values instanceof JSONPath && is_array($values->data()) && count($values->data()) > 1) { |
|
695
|
|
|
$metadata[$resArray['index_name']] = []; |
|
696
|
|
|
foreach ($values->data() as $value) { |
|
697
|
|
|
$metadata[$resArray['index_name'] . '_sorting'][0] = trim((string) $value); |
|
698
|
|
|
} |
|
699
|
|
|
} |
|
700
|
|
|
} |
|
701
|
|
|
if (empty($metadata[$resArray['index_name'] . '_sorting'][0])) { |
|
702
|
|
|
$metadata[$resArray['index_name'] . '_sorting'][0] = $metadata[$resArray['index_name']][0]; |
|
703
|
|
|
} |
|
704
|
|
|
} |
|
705
|
|
|
} |
|
706
|
|
|
// Set date to empty string if not present. |
|
707
|
|
|
if (empty($metadata['date'][0])) { |
|
708
|
|
|
$metadata['date'][0] = ''; |
|
709
|
|
|
} |
|
710
|
|
|
return $metadata; |
|
711
|
|
|
} |
|
712
|
|
|
|
|
713
|
|
|
/** |
|
714
|
|
|
* {@inheritDoc} |
|
715
|
|
|
* @see AbstractDocument::_getSmLinks() |
|
716
|
|
|
*/ |
|
717
|
|
|
protected function _getSmLinks() |
|
718
|
|
|
{ |
|
719
|
|
|
if (!$this->smLinksLoaded && isset($this->iiif) && $this->iiif instanceof ManifestInterface) { |
|
720
|
|
|
if (!empty($this->iiif->getDefaultCanvases())) { |
|
721
|
|
|
foreach ($this->iiif->getDefaultCanvases() as $canvas) { |
|
722
|
|
|
$this->smLinkCanvasToResource($canvas, $this->iiif); |
|
723
|
|
|
} |
|
724
|
|
|
} |
|
725
|
|
|
if (!empty($this->iiif->getStructures())) { |
|
726
|
|
|
foreach ($this->iiif->getStructures() as $range) { |
|
727
|
|
|
$this->smLinkRangeCanvasesRecursively($range); |
|
728
|
|
|
} |
|
729
|
|
|
} |
|
730
|
|
|
$this->smLinksLoaded = true; |
|
731
|
|
|
} |
|
732
|
|
|
return $this->smLinks; |
|
733
|
|
|
} |
|
734
|
|
|
|
|
735
|
|
|
/** |
|
736
|
|
|
* Construct a link between a range and it's sub ranges and all contained canvases. |
|
737
|
|
|
* |
|
738
|
|
|
* @access private |
|
739
|
|
|
* |
|
740
|
|
|
* @param RangeInterface $range: Current range whose canvases shall be linked |
|
741
|
|
|
*/ |
|
742
|
|
|
private function smLinkRangeCanvasesRecursively(RangeInterface $range) |
|
743
|
|
|
{ |
|
744
|
|
|
// map range's canvases including all child ranges' canvases |
|
745
|
|
|
if (!$range->isTopRange()) { |
|
746
|
|
|
foreach ($range->getAllCanvasesRecursively() as $canvas) { |
|
747
|
|
|
$this->smLinkCanvasToResource($canvas, $range); |
|
748
|
|
|
} |
|
749
|
|
|
} |
|
750
|
|
|
// recursive call for all ranges |
|
751
|
|
|
if (!empty($range->getAllRanges())) { |
|
752
|
|
|
foreach ($range->getAllRanges() as $childRange) { |
|
753
|
|
|
$this->smLinkRangeCanvasesRecursively($childRange); |
|
754
|
|
|
} |
|
755
|
|
|
} |
|
756
|
|
|
} |
|
757
|
|
|
|
|
758
|
|
|
/** |
|
759
|
|
|
* Link a single canvas to a containing range |
|
760
|
|
|
* |
|
761
|
|
|
* @access private |
|
762
|
|
|
* |
|
763
|
|
|
* @param CanvasInterface $canvas |
|
764
|
|
|
* @param IiifResourceInterface $resource |
|
765
|
|
|
*/ |
|
766
|
|
|
private function smLinkCanvasToResource(CanvasInterface $canvas, IiifResourceInterface $resource) |
|
767
|
|
|
{ |
|
768
|
|
|
$this->smLinks['l2p'][$resource->getId()][] = $canvas->getId(); |
|
769
|
|
|
if (!is_array($this->smLinks['p2l'][$canvas->getId()]) || !in_array($resource->getId(), $this->smLinks['p2l'][$canvas->getId()])) { |
|
770
|
|
|
$this->smLinks['p2l'][$canvas->getId()][] = $resource->getId(); |
|
771
|
|
|
} |
|
772
|
|
|
} |
|
773
|
|
|
|
|
774
|
|
|
/** |
|
775
|
|
|
* {@inheritDoc} |
|
776
|
|
|
* @see AbstractDocument::getFullText() |
|
777
|
|
|
*/ |
|
778
|
|
|
//TODO: rewrite it to get full OCR |
|
779
|
|
|
public function getFullText($id) |
|
780
|
|
|
{ |
|
781
|
|
|
$rawText = ''; |
|
782
|
|
|
// Get text from raw text array if available. |
|
783
|
|
|
if (!empty($this->rawTextArray[$id])) { |
|
784
|
|
|
return $this->rawTextArray[$id]; |
|
785
|
|
|
} |
|
786
|
|
|
$this->ensureHasFulltextIsSet(); |
|
787
|
|
|
if ($this->hasFulltext) { |
|
788
|
|
|
// Load physical structure ... |
|
789
|
|
|
$this->_getPhysicalStructure(); |
|
790
|
|
|
// ... and extension configuration. |
|
791
|
|
|
$extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey); |
|
792
|
|
|
$fileGrpsFulltext = GeneralUtility::trimExplode(',', $extConf['fileGrpFulltext']); |
|
793
|
|
|
if (!empty($this->physicalStructureInfo[$id])) { |
|
794
|
|
|
while ($fileGrpFulltext = array_shift($fileGrpsFulltext)) { |
|
795
|
|
|
if (!empty($this->physicalStructureInfo[$id]['files'][$fileGrpFulltext])) { |
|
796
|
|
|
$rawText = parent::getFullTextFromXml($id); |
|
797
|
|
|
break; |
|
798
|
|
|
} |
|
799
|
|
|
} |
|
800
|
|
|
if ($extConf['indexAnnotations'] == 1) { |
|
801
|
|
|
$iiifResource = $this->iiif->getContainedResourceById($id); |
|
802
|
|
|
// Get annotation containers |
|
803
|
|
|
$annotationContainerIds = $this->physicalStructureInfo[$id]['annotationContainers']; |
|
804
|
|
|
if (!empty($annotationContainerIds)) { |
|
805
|
|
|
$annotationTexts = []; |
|
806
|
|
|
foreach ($annotationContainerIds as $annotationListId) { |
|
807
|
|
|
$annotationContainer = $this->iiif->getContainedResourceById($annotationListId); |
|
808
|
|
|
/* @var $annotationContainer \Ubl\Iiif\Presentation\Common\Model\Resources\AnnotationContainerInterface */ |
|
809
|
|
|
foreach ($annotationContainer->getTextAnnotations(Motivation::PAINTING) as $annotation) { |
|
|
|
|
|
|
810
|
|
|
if ( |
|
811
|
|
|
$annotation->getTargetResourceId() == $iiifResource->getId() && |
|
812
|
|
|
$annotation->getBody() != null && $annotation->getBody()->getChars() != null |
|
813
|
|
|
) { |
|
814
|
|
|
$annotationTexts[] = $annotation->getBody()->getChars(); |
|
815
|
|
|
} |
|
816
|
|
|
} |
|
817
|
|
|
} |
|
818
|
|
|
$rawText .= implode(' ', $annotationTexts); |
|
819
|
|
|
} |
|
820
|
|
|
} |
|
821
|
|
|
} else { |
|
822
|
|
|
$this->logger->warning('Invalid structure resource @id "' . $id . '"'); |
|
823
|
|
|
return $rawText; |
|
824
|
|
|
} |
|
825
|
|
|
$this->rawTextArray[$id] = $rawText; |
|
826
|
|
|
} |
|
827
|
|
|
return $rawText; |
|
828
|
|
|
} |
|
829
|
|
|
|
|
830
|
|
|
/** |
|
831
|
|
|
* Returns the underlying IiifResourceInterface. |
|
832
|
|
|
* |
|
833
|
|
|
* @access public |
|
834
|
|
|
* |
|
835
|
|
|
* @return IiifResourceInterface |
|
836
|
|
|
*/ |
|
837
|
|
|
public function getIiif() |
|
838
|
|
|
{ |
|
839
|
|
|
return $this->iiif; |
|
840
|
|
|
} |
|
841
|
|
|
|
|
842
|
|
|
/** |
|
843
|
|
|
* {@inheritDoc} |
|
844
|
|
|
* @see AbstractDocument::init() |
|
845
|
|
|
*/ |
|
846
|
|
|
protected function init($location) |
|
847
|
|
|
{ |
|
848
|
|
|
$this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class); |
|
849
|
|
|
} |
|
850
|
|
|
|
|
851
|
|
|
/** |
|
852
|
|
|
* {@inheritDoc} |
|
853
|
|
|
* @see AbstractDocument::loadLocation() |
|
854
|
|
|
*/ |
|
855
|
|
|
protected function loadLocation($location) |
|
856
|
|
|
{ |
|
857
|
|
|
$fileResource = GeneralUtility::getUrl($location); |
|
858
|
|
|
if ($fileResource !== false) { |
|
859
|
|
|
$conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey); |
|
860
|
|
|
IiifHelper::setUrlReader(IiifUrlReader::getInstance()); |
|
861
|
|
|
IiifHelper::setMaxThumbnailHeight($conf['iiifThumbnailHeight']); |
|
862
|
|
|
IiifHelper::setMaxThumbnailWidth($conf['iiifThumbnailWidth']); |
|
863
|
|
|
$resource = IiifHelper::loadIiifResource($fileResource); |
|
864
|
|
|
if ($resource != null) { |
|
865
|
|
|
if ($resource instanceof ManifestInterface) { |
|
866
|
|
|
$this->iiif = $resource; |
|
867
|
|
|
return true; |
|
868
|
|
|
} |
|
869
|
|
|
} |
|
870
|
|
|
} |
|
871
|
|
|
$this->logger->error('Could not load IIIF manifest from "' . $location . '"'); |
|
872
|
|
|
return false; |
|
873
|
|
|
} |
|
874
|
|
|
|
|
875
|
|
|
/** |
|
876
|
|
|
* {@inheritDoc} |
|
877
|
|
|
* @see AbstractDocument::prepareMetadataArray() |
|
878
|
|
|
*/ |
|
879
|
|
|
protected function prepareMetadataArray($cPid) |
|
880
|
|
|
{ |
|
881
|
|
|
$id = $this->iiif->getId(); |
|
882
|
|
|
$this->metadataArray[(string) $id] = $this->getMetadata((string) $id, $cPid); |
|
883
|
|
|
} |
|
884
|
|
|
|
|
885
|
|
|
/** |
|
886
|
|
|
* {@inheritDoc} |
|
887
|
|
|
* @see AbstractDocument::setPreloadedDocument() |
|
888
|
|
|
*/ |
|
889
|
|
|
protected function setPreloadedDocument($preloadedDocument) |
|
890
|
|
|
{ |
|
891
|
|
|
if ($preloadedDocument instanceof ManifestInterface) { |
|
892
|
|
|
$this->iiif = $preloadedDocument; |
|
893
|
|
|
return true; |
|
894
|
|
|
} |
|
895
|
|
|
return false; |
|
896
|
|
|
} |
|
897
|
|
|
|
|
898
|
|
|
/** |
|
899
|
|
|
* {@inheritDoc} |
|
900
|
|
|
* @see AbstractDocument::ensureHasFulltextIsSet() |
|
901
|
|
|
*/ |
|
902
|
|
|
protected function ensureHasFulltextIsSet() |
|
903
|
|
|
{ |
|
904
|
|
|
/* |
|
905
|
|
|
* TODO Check annotations and annotation lists of canvas for ALTO documents. |
|
906
|
|
|
* Example: |
|
907
|
|
|
* https://digi.ub.uni-heidelberg.de/diglit/iiif/hirsch_hamburg1933_04_25/manifest.json links |
|
908
|
|
|
* https://digi.ub.uni-heidelberg.de/diglit/iiif/hirsch_hamburg1933_04_25/list/0001.json |
|
909
|
|
|
*/ |
|
910
|
|
|
if (!$this->hasFulltextSet && $this->iiif instanceof ManifestInterface) { |
|
911
|
|
|
$manifest = $this->iiif; |
|
912
|
|
|
$canvases = $manifest->getDefaultCanvases(); |
|
913
|
|
|
foreach ($canvases as $canvas) { |
|
914
|
|
|
if ( |
|
915
|
|
|
!empty($canvas->getSeeAlsoUrlsForFormat("application/alto+xml")) || |
|
916
|
|
|
!empty($canvas->getSeeAlsoUrlsForProfile("http://www.loc.gov/standards/alto/")) |
|
917
|
|
|
) { |
|
918
|
|
|
$this->hasFulltextSet = true; |
|
919
|
|
|
$this->hasFulltext = true; |
|
920
|
|
|
return; |
|
921
|
|
|
} |
|
922
|
|
|
$extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey); |
|
923
|
|
|
if ($extConf['indexAnnotations'] == 1 && !empty($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING))) { |
|
924
|
|
|
foreach ($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING) as $annotationContainer) { |
|
925
|
|
|
if (($textAnnotations = $annotationContainer->getTextAnnotations(Motivation::PAINTING)) != null) { |
|
926
|
|
|
foreach ($textAnnotations as $annotation) { |
|
927
|
|
|
if ( |
|
928
|
|
|
$annotation->getBody() != null && |
|
929
|
|
|
$annotation->getBody()->getFormat() == "text/plain" && |
|
930
|
|
|
$annotation->getBody()->getChars() != null |
|
931
|
|
|
) { |
|
932
|
|
|
$this->hasFulltextSet = true; |
|
933
|
|
|
$this->hasFulltext = true; |
|
934
|
|
|
return; |
|
935
|
|
|
} |
|
936
|
|
|
} |
|
937
|
|
|
} |
|
938
|
|
|
} |
|
939
|
|
|
} |
|
940
|
|
|
} |
|
941
|
|
|
$this->hasFulltextSet = true; |
|
942
|
|
|
} |
|
943
|
|
|
} |
|
944
|
|
|
|
|
945
|
|
|
/** |
|
946
|
|
|
* {@inheritDoc} |
|
947
|
|
|
* @see AbstractDocument::_getThumbnail() |
|
948
|
|
|
*/ |
|
949
|
|
|
protected function _getThumbnail($forceReload = false) |
|
950
|
|
|
{ |
|
951
|
|
|
return $this->iiif->getThumbnailUrl(); |
|
952
|
|
|
} |
|
953
|
|
|
|
|
954
|
|
|
/** |
|
955
|
|
|
* {@inheritDoc} |
|
956
|
|
|
* @see AbstractDocument::_getToplevelId() |
|
957
|
|
|
*/ |
|
958
|
|
|
protected function _getToplevelId() |
|
959
|
|
|
{ |
|
960
|
|
|
if (empty($this->toplevelId)) { |
|
961
|
|
|
if (isset($this->iiif)) { |
|
962
|
|
|
$this->toplevelId = $this->iiif->getId(); |
|
963
|
|
|
} |
|
964
|
|
|
} |
|
965
|
|
|
return $this->toplevelId; |
|
966
|
|
|
} |
|
967
|
|
|
|
|
968
|
|
|
/** |
|
969
|
|
|
* This magic method is executed after the object is deserialized |
|
970
|
|
|
* @see __sleep() |
|
971
|
|
|
* |
|
972
|
|
|
* @access public |
|
973
|
|
|
* |
|
974
|
|
|
* @return void |
|
975
|
|
|
*/ |
|
976
|
|
|
public function __wakeup() |
|
977
|
|
|
{ |
|
978
|
|
|
$conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey); |
|
979
|
|
|
IiifHelper::setUrlReader(IiifUrlReader::getInstance()); |
|
980
|
|
|
IiifHelper::setMaxThumbnailHeight($conf['iiifThumbnailHeight']); |
|
981
|
|
|
IiifHelper::setMaxThumbnailWidth($conf['iiifThumbnailWidth']); |
|
982
|
|
|
$resource = IiifHelper::loadIiifResource($this->asJson); |
|
983
|
|
|
if ($resource != null && $resource instanceof ManifestInterface) { |
|
984
|
|
|
$this->asJson = ''; |
|
985
|
|
|
$this->iiif = $resource; |
|
986
|
|
|
$this->init(''); |
|
987
|
|
|
} else { |
|
988
|
|
|
$this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class); |
|
989
|
|
|
$this->logger->error('Could not load IIIF after deserialization'); |
|
990
|
|
|
} |
|
991
|
|
|
} |
|
992
|
|
|
|
|
993
|
|
|
/** |
|
994
|
|
|
* |
|
995
|
|
|
* @return string[] |
|
996
|
|
|
*/ |
|
997
|
|
|
public function __sleep() |
|
998
|
|
|
{ |
|
999
|
|
|
// TODO implement serializiation in IIIF library |
|
1000
|
|
|
$jsonArray = $this->iiif->getOriginalJsonArray(); |
|
1001
|
|
|
$this->asJson = json_encode($jsonArray); |
|
1002
|
|
|
return ['uid', 'pid', 'recordId', 'parentId', 'asJson']; |
|
1003
|
|
|
} |
|
1004
|
|
|
} |
|
1005
|
|
|
|
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
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. 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.