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\Plugin; |
14
|
|
|
|
15
|
|
|
use Kitodo\Dlf\Common\Helper; |
16
|
|
|
use Kitodo\Dlf\Common\IiifManifest; |
17
|
|
|
use Ubl\Iiif\Presentation\Common\Model\Resources\ManifestInterface; |
18
|
|
|
use Ubl\Iiif\Presentation\Common\Vocabulary\Motivation; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Plugin 'Page View' for the 'dlf' extension |
22
|
|
|
* |
23
|
|
|
* @author Sebastian Meyer <[email protected]> |
24
|
|
|
* @package TYPO3 |
25
|
|
|
* @subpackage dlf |
26
|
|
|
* @access public |
27
|
|
|
*/ |
28
|
|
|
class PageView extends \Kitodo\Dlf\Common\AbstractPlugin |
29
|
|
|
{ |
30
|
|
|
public $scriptRelPath = 'Classes/Plugin/PageView.php'; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Holds the controls to add to the map |
34
|
|
|
* |
35
|
|
|
* @var array |
36
|
|
|
* @access protected |
37
|
|
|
*/ |
38
|
|
|
protected $controls = []; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Holds the current images' URLs and MIME types |
42
|
|
|
* |
43
|
|
|
* @var array |
44
|
|
|
* @access protected |
45
|
|
|
*/ |
46
|
|
|
protected $images = []; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Holds the current fulltexts' URLs |
50
|
|
|
* |
51
|
|
|
* @var array |
52
|
|
|
* @access protected |
53
|
|
|
*/ |
54
|
|
|
protected $fulltexts = []; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* Holds the current AnnotationLists / AnnotationPages |
58
|
|
|
* |
59
|
|
|
* @var array |
60
|
|
|
* @access protected |
61
|
|
|
*/ |
62
|
|
|
protected $annotationContainers = []; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Adds Viewer javascript |
66
|
|
|
* |
67
|
|
|
* @access protected |
68
|
|
|
* |
69
|
|
|
* @return string The output string for the ###JAVASCRIPT### template marker |
70
|
|
|
*/ |
71
|
|
|
protected function addViewerJS() |
72
|
|
|
{ |
73
|
|
|
$markerArray = ''; |
74
|
|
|
// CSS files. |
75
|
|
|
$cssFiles = [ |
76
|
|
|
'Resources/Public/Javascript/OpenLayers/ol3.css' |
77
|
|
|
]; |
78
|
|
|
// Javascript files. |
79
|
|
|
$jsFiles = [ |
80
|
|
|
// OpenLayers |
81
|
|
|
'Resources/Public/Javascript/OpenLayers/glif.min.js', |
82
|
|
|
'Resources/Public/Javascript/OpenLayers/ol3-dlf.js', |
83
|
|
|
// Viewer |
84
|
|
|
'Resources/Public/Javascript/PageView/Utility.js', |
85
|
|
|
'Resources/Public/Javascript/PageView/OL3.js', |
86
|
|
|
'Resources/Public/Javascript/PageView/OL3Styles.js', |
87
|
|
|
'Resources/Public/Javascript/PageView/OL3Sources.js', |
88
|
|
|
'Resources/Public/Javascript/PageView/AltoParser.js', |
89
|
|
|
'Resources/Public/Javascript/PageView/AnnotationParser.js', |
90
|
|
|
'Resources/Public/Javascript/PageView/AnnotationControl.js', |
91
|
|
|
'Resources/Public/Javascript/PageView/ImageManipulationControl.js', |
92
|
|
|
'Resources/Public/Javascript/PageView/FulltextDownloadControl.js', |
93
|
|
|
'Resources/Public/Javascript/PageView/FulltextControl.js', |
94
|
|
|
'Resources/Public/Javascript/PageView/FullTextUtility.js', |
95
|
|
|
'Resources/Public/Javascript/PageView/PageView.js' |
96
|
|
|
]; |
97
|
|
|
// Viewer configuration. |
98
|
|
|
$viewerConfiguration = ' |
99
|
|
|
$(document).ready(function() { |
100
|
|
|
if (dlfUtils.exists(dlfViewer)) { |
101
|
|
|
tx_dlf_viewer = new dlfViewer({ |
102
|
|
|
controls: ["' . implode('", "', $this->controls) . '"], |
103
|
|
|
div: "' . $this->conf['elementId'] . '", |
104
|
|
|
images: ' . json_encode($this->images) . ', |
105
|
|
|
fulltexts: ' . json_encode($this->fulltexts) . ', |
106
|
|
|
annotationContainers: ' . json_encode($this->annotationContainers) . ', |
107
|
|
|
useInternalProxy: ' . ($this->conf['useInternalProxy'] ? 1 : 0) . ' |
108
|
|
|
}); |
109
|
|
|
} |
110
|
|
|
}); |
111
|
|
|
'; |
112
|
|
|
// Add Javascript to page footer if not configured otherwise. |
113
|
|
|
if (empty($this->conf['addJStoBody'])) { |
114
|
|
|
$pageRenderer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Page\PageRenderer::class); |
115
|
|
|
foreach ($cssFiles as $cssFile) { |
116
|
|
|
$pageRenderer->addCssFile(\TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($this->extKey)) . $cssFile); |
117
|
|
|
} |
118
|
|
|
foreach ($jsFiles as $jsFile) { |
119
|
|
|
$pageRenderer->addJsFooterFile(\TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($this->extKey)) . $jsFile); |
120
|
|
|
} |
121
|
|
|
$pageRenderer->addJsFooterInlineCode('kitodo-pageview-configuration', $viewerConfiguration); |
122
|
|
|
} else { |
123
|
|
|
foreach ($jsFiles as $jsFile) { |
124
|
|
|
$markerArray .= '<script type="text/javascript" src="' . \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($this->extKey)) . $jsFile . '"></script>' . "\n"; |
125
|
|
|
} |
126
|
|
|
$markerArray .= ' |
127
|
|
|
<script type="text/javascript"> |
128
|
|
|
/*<![CDATA[*/ |
129
|
|
|
/*kitodo-pageview-configuration*/ |
130
|
|
|
' . $viewerConfiguration . ' |
131
|
|
|
/*]]>*/ |
132
|
|
|
</script>'; |
133
|
|
|
} |
134
|
|
|
return $markerArray; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Adds pageview interaction (crop, magnifier and rotation) |
139
|
|
|
* |
140
|
|
|
* @access protected |
141
|
|
|
* |
142
|
|
|
* @return array Marker array |
143
|
|
|
*/ |
144
|
|
|
protected function addInteraction() |
145
|
|
|
{ |
146
|
|
|
$markerArray = []; |
147
|
|
|
if ($this->piVars['id']) { |
148
|
|
|
if ($this->conf['crop']) { |
149
|
|
|
$markerArray['###EDITBUTTON###'] = '<a href="javascript: tx_dlf_viewer.activateSelection();" title="' . htmlspecialchars($this->pi_getLL('editMode', '')) . '">' . htmlspecialchars($this->pi_getLL('editMode', '')) . '</a>'; |
150
|
|
|
$markerArray['###EDITREMOVE###'] = '<a href="javascript: tx_dlf_viewer.resetCropSelection();" title="' . htmlspecialchars($this->pi_getLL('editRemove', '')) . '">' . htmlspecialchars($this->pi_getLL('editRemove', '')) . '</a>'; |
151
|
|
|
} else { |
152
|
|
|
$markerArray['###EDITBUTTON###'] = ''; |
153
|
|
|
$markerArray['###EDITREMOVE###'] = ''; |
154
|
|
|
} |
155
|
|
|
if ($this->conf['magnifier']) { |
156
|
|
|
$markerArray['###MAGNIFIER###'] = '<a href="javascript: tx_dlf_viewer.activateMagnifier();" title="' . htmlspecialchars($this->pi_getLL('magnifier', '')) . '">' . htmlspecialchars($this->pi_getLL('magnifier', '')) . '</a>'; |
157
|
|
|
} else { |
158
|
|
|
$markerArray['###MAGNIFIER###'] = ''; |
159
|
|
|
} |
160
|
|
|
} |
161
|
|
|
return $markerArray; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Adds form to save cropping data to basket |
166
|
|
|
* |
167
|
|
|
* @access protected |
168
|
|
|
* |
169
|
|
|
* @return array Marker array |
170
|
|
|
*/ |
171
|
|
|
protected function addBasketForm() |
172
|
|
|
{ |
173
|
|
|
$markerArray = []; |
174
|
|
|
// Add basket button |
175
|
|
|
if ($this->conf['basketButton'] && $this->conf['targetBasket'] && $this->piVars['id']) { |
176
|
|
|
$label = htmlspecialchars($this->pi_getLL('addBasket', '')); |
177
|
|
|
$params = [ |
178
|
|
|
'id' => $this->piVars['id'], |
179
|
|
|
'addToBasket' => true |
180
|
|
|
]; |
181
|
|
|
if (empty($this->piVars['page'])) { |
182
|
|
|
$params['page'] = 1; |
183
|
|
|
} |
184
|
|
|
$basketConf = [ |
185
|
|
|
'parameter' => $this->conf['targetBasket'], |
186
|
|
|
'forceAbsoluteUrl' => !empty($this->conf['forceAbsoluteUrl']) ? 1 : 0, |
187
|
|
|
'forceAbsoluteUrl.' => ['scheme' => !empty($this->conf['forceAbsoluteUrl']) && !empty($this->conf['forceAbsoluteUrlHttps']) ? 'https' : 'http'], |
188
|
|
|
'additionalParams' => \TYPO3\CMS\Core\Utility\GeneralUtility::implodeArrayForUrl($this->prefixId, $params, '', true, false), |
189
|
|
|
'title' => $label |
190
|
|
|
]; |
191
|
|
|
$output = '<form id="addToBasketForm" action="' . $this->cObj->typoLink_URL($basketConf) . '" method="post">'; |
192
|
|
|
$output .= '<input type="hidden" name="tx_dlf[startpage]" id="startpage" value="' . htmlspecialchars($this->piVars['page']) . '">'; |
193
|
|
|
$output .= '<input type="hidden" name="tx_dlf[endpage]" id="endpage" value="' . htmlspecialchars($this->piVars['page']) . '">'; |
194
|
|
|
$output .= '<input type="hidden" name="tx_dlf[startX]" id="startX">'; |
195
|
|
|
$output .= '<input type="hidden" name="tx_dlf[startY]" id="startY">'; |
196
|
|
|
$output .= '<input type="hidden" name="tx_dlf[endX]" id="endX">'; |
197
|
|
|
$output .= '<input type="hidden" name="tx_dlf[endY]" id="endY">'; |
198
|
|
|
$output .= '<input type="hidden" name="tx_dlf[rotation]" id="rotation">'; |
199
|
|
|
$output .= '<button id="submitBasketForm" onclick="this.form.submit()">' . $label . '</button>'; |
200
|
|
|
$output .= '</form>'; |
201
|
|
|
$output .= '<script>'; |
202
|
|
|
$output .= '$(document).ready(function() { $("#submitBasketForm").click(function() { $("#addToBasketForm").submit(); }); });'; |
203
|
|
|
$output .= '</script>'; |
204
|
|
|
$markerArray['###BASKETBUTTON###'] = $output; |
205
|
|
|
} else { |
206
|
|
|
$markerArray['###BASKETBUTTON###'] = ''; |
207
|
|
|
} |
208
|
|
|
return $markerArray; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Get image's URL and MIME type |
213
|
|
|
* |
214
|
|
|
* @access protected |
215
|
|
|
* |
216
|
|
|
* @param int $page: Page number |
217
|
|
|
* |
218
|
|
|
* @return array URL and MIME type of image file |
219
|
|
|
*/ |
220
|
|
|
protected function getImage($page) |
221
|
|
|
{ |
222
|
|
|
$image = []; |
223
|
|
|
// Get @USE value of METS fileGrp. |
224
|
|
|
$fileGrps = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->conf['fileGrps']); |
225
|
|
|
while ($fileGrp = @array_pop($fileGrps)) { |
226
|
|
|
// Get image link. |
227
|
|
|
if (!empty($this->doc->physicalStructureInfo[$this->doc->physicalStructure[$page]]['files'][$fileGrp])) { |
228
|
|
|
$image['url'] = $this->doc->getFileLocation($this->doc->physicalStructureInfo[$this->doc->physicalStructure[$page]]['files'][$fileGrp]); |
229
|
|
|
if ($this->conf['useInternalProxy']) { |
230
|
|
|
// Configure @action URL for form. |
231
|
|
|
$linkConf = [ |
232
|
|
|
'parameter' => $GLOBALS['TSFE']->id, |
233
|
|
|
'forceAbsoluteUrl' => !empty($this->conf['forceAbsoluteUrl']) ? 1 : 0, |
234
|
|
|
'forceAbsoluteUrl.' => ['scheme' => !empty($this->conf['forceAbsoluteUrl']) && !empty($this->conf['forceAbsoluteUrlHttps']) ? 'https' : 'http'], |
235
|
|
|
'additionalParams' => '&eID=tx_dlf_pageview_proxy&url=' . urlencode($image['url']), |
236
|
|
|
]; |
237
|
|
|
$image['url'] = $this->cObj->typoLink_URL($linkConf); |
238
|
|
|
} |
239
|
|
|
$image['mimetype'] = $this->doc->getFileMimeType($this->doc->physicalStructureInfo[$this->doc->physicalStructure[$page]]['files'][$fileGrp]); |
240
|
|
|
break; |
241
|
|
|
} else { |
242
|
|
|
Helper::devLog('File not found in fileGrp "' . $fileGrp . '"', DEVLOG_SEVERITY_WARNING); |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
|
return $image; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Get fulltext URL and MIME type |
250
|
|
|
* |
251
|
|
|
* @access protected |
252
|
|
|
* |
253
|
|
|
* @param int $page: Page number |
254
|
|
|
* |
255
|
|
|
* @return array URL and MIME type of fulltext file |
256
|
|
|
*/ |
257
|
|
|
protected function getFulltext($page) |
258
|
|
|
{ |
259
|
|
|
$fulltext = []; |
260
|
|
|
// Get fulltext link. |
261
|
|
|
if (!empty($this->doc->physicalStructureInfo[$this->doc->physicalStructure[$page]]['files'][$this->conf['fileGrpFulltext']])) { |
262
|
|
|
$fulltext['url'] = $this->doc->getFileLocation($this->doc->physicalStructureInfo[$this->doc->physicalStructure[$page]]['files'][$this->conf['fileGrpFulltext']]); |
263
|
|
|
if ($this->conf['useInternalProxy']) { |
264
|
|
|
// Configure @action URL for form. |
265
|
|
|
$linkConf = [ |
266
|
|
|
'parameter' => $GLOBALS['TSFE']->id, |
267
|
|
|
'forceAbsoluteUrl' => !empty($this->conf['forceAbsoluteUrl']) ? 1 : 0, |
268
|
|
|
'forceAbsoluteUrl.' => ['scheme' => !empty($this->conf['forceAbsoluteUrl']) && !empty($this->conf['forceAbsoluteUrlHttps']) ? 'https' : 'http'], |
269
|
|
|
'additionalParams' => '&eID=tx_dlf_pageview_proxy&url=' . urlencode($fulltext['url']), |
270
|
|
|
]; |
271
|
|
|
$fulltext['url'] = $this->cObj->typoLink_URL($linkConf); |
272
|
|
|
} |
273
|
|
|
$fulltext['mimetype'] = $this->doc->getFileMimeType($this->doc->physicalStructureInfo[$this->doc->physicalStructure[$page]]['files'][$this->conf['fileGrpFulltext']]); |
274
|
|
|
} else { |
275
|
|
|
Helper::devLog('File not found in fileGrp "' . $this->conf['fileGrpFulltext'] . '"', DEVLOG_SEVERITY_WARNING); |
276
|
|
|
} |
277
|
|
|
return $fulltext; |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Get all AnnotationPages / AnnotationLists that contain text Annotations with motivation "painting" |
282
|
|
|
* |
283
|
|
|
* @access protected |
284
|
|
|
* |
285
|
|
|
* @param int $page: Page number |
286
|
|
|
* @return array An array containing the IRIs of the AnnotationLists / AnnotationPages as well as |
287
|
|
|
* some information about the canvas. |
288
|
|
|
*/ |
289
|
|
|
protected function getAnnotationContainers($page) |
290
|
|
|
{ |
291
|
|
|
if ($this->doc instanceof IiifManifest) { |
292
|
|
|
$canvasId = $this->doc->physicalStructure[$page]; |
293
|
|
|
$iiif = $this->doc->getIiif(); |
294
|
|
|
if ($iiif instanceof ManifestInterface) { |
|
|
|
|
295
|
|
|
$canvas = $iiif->getContainedResourceById($canvasId); |
296
|
|
|
/* @var $canvas \Ubl\Iiif\Presentation\Common\Model\Resources\CanvasInterface */ |
297
|
|
|
if ($canvas != null && !empty($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING))) { |
298
|
|
|
$annotationContainers = []; |
299
|
|
|
/* |
300
|
|
|
* TODO Analyzing the annotations on the server side requires loading the annotation lists / pages |
301
|
|
|
* just to determine wether they contain text annotations for painting. This will take time and lead to a bad user experience. |
302
|
|
|
* It would be better to link every annotation and analyze the data on the client side. |
303
|
|
|
* |
304
|
|
|
* On the other hand, server connections are potentially better than client connections. Downloading annotation lists |
305
|
|
|
*/ |
306
|
|
|
foreach ($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING) as $annotationContainer) { |
307
|
|
|
if (($textAnnotations = $annotationContainer->getTextAnnotations(Motivation::PAINTING)) != null) { |
308
|
|
|
foreach ($textAnnotations as $annotation) { |
309
|
|
|
if ( |
310
|
|
|
$annotation->getBody()->getFormat() == 'text/plain' |
311
|
|
|
&& $annotation->getBody()->getChars() != null |
312
|
|
|
) { |
313
|
|
|
$annotationListData = []; |
314
|
|
|
$annotationListData['uri'] = $annotationContainer->getId(); |
315
|
|
|
$annotationListData['label'] = $annotationContainer->getLabelForDisplay(); |
316
|
|
|
$annotationContainers[] = $annotationListData; |
317
|
|
|
break; |
318
|
|
|
} |
319
|
|
|
} |
320
|
|
|
} |
321
|
|
|
} |
322
|
|
|
$result = [ |
323
|
|
|
'canvas' => [ |
324
|
|
|
'id' => $canvas->getId(), |
325
|
|
|
'width' => $canvas->getWidth(), |
326
|
|
|
'height' => $canvas->getHeight(), |
327
|
|
|
], |
328
|
|
|
'annotationContainers' => $annotationContainers |
329
|
|
|
]; |
330
|
|
|
return $result; |
331
|
|
|
} |
332
|
|
|
} |
333
|
|
|
} |
334
|
|
|
return []; |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* The main method of the PlugIn |
339
|
|
|
* |
340
|
|
|
* @access public |
341
|
|
|
* |
342
|
|
|
* @param string $content: The PlugIn content |
343
|
|
|
* @param array $conf: The PlugIn configuration |
344
|
|
|
* |
345
|
|
|
* @return string The content that is displayed on the website |
346
|
|
|
*/ |
347
|
|
|
public function main($content, $conf) |
348
|
|
|
{ |
349
|
|
|
$this->init($conf); |
350
|
|
|
// Load current document. |
351
|
|
|
$this->loadDocument(); |
352
|
|
|
if ( |
353
|
|
|
$this->doc === null |
354
|
|
|
|| $this->doc->numPages < 1 |
355
|
|
|
) { |
356
|
|
|
// Quit without doing anything if required variables are not set. |
357
|
|
|
return $content; |
358
|
|
|
} else { |
359
|
|
|
if (!empty($this->piVars['logicalPage'])) { |
360
|
|
|
$this->piVars['page'] = $this->doc->getPhysicalPage($this->piVars['logicalPage']); |
|
|
|
|
361
|
|
|
// The logical page parameter should not appear again |
362
|
|
|
unset($this->piVars['logicalPage']); |
363
|
|
|
} |
364
|
|
|
// Set default values if not set. |
365
|
|
|
// $this->piVars['page'] may be integer or string (physical structure @ID) |
366
|
|
|
if ((int) $this->piVars['page'] > 0 || empty($this->piVars['page'])) { |
367
|
|
|
$this->piVars['page'] = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange((int) $this->piVars['page'], 1, $this->doc->numPages, 1); |
368
|
|
|
} else { |
369
|
|
|
$this->piVars['page'] = array_search($this->piVars['page'], $this->doc->physicalStructure); |
370
|
|
|
} |
371
|
|
|
$this->piVars['double'] = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($this->piVars['double'], 0, 1, 0); |
372
|
|
|
} |
373
|
|
|
// Load template file. |
374
|
|
|
$this->getTemplate(); |
375
|
|
|
// Get image data. |
376
|
|
|
$this->images[0] = $this->getImage($this->piVars['page']); |
377
|
|
|
$this->fulltexts[0] = $this->getFulltext($this->piVars['page']); |
378
|
|
|
$this->annotationContainers[0] = $this->getAnnotationContainers($this->piVars['page']); |
379
|
|
|
if ($this->piVars['double'] && $this->piVars['page'] < $this->doc->numPages) { |
380
|
|
|
$this->images[1] = $this->getImage($this->piVars['page'] + 1); |
381
|
|
|
$this->fulltexts[1] = $this->getFulltext($this->piVars['page'] + 1); |
382
|
|
|
$this->annotationContainers[1] = $this->getAnnotationContainers($this->piVars['page'] + 1); |
383
|
|
|
} |
384
|
|
|
// Get the controls for the map. |
385
|
|
|
$this->controls = explode(',', $this->conf['features']); |
386
|
|
|
// Fill in the template markers. |
387
|
|
|
$markerArray = array_merge($this->addInteraction(), $this->addBasketForm()); |
388
|
|
|
$markerArray['###JAVASCRIPT###'] = $this->addViewerJS(); |
389
|
|
|
$content .= $this->templateService->substituteMarkerArray($this->template, $markerArray); |
390
|
|
|
return $this->pi_wrapInBaseClass($content); |
391
|
|
|
} |
392
|
|
|
} |
393
|
|
|
|