|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* This file is part of the TYPO3 CMS project. |
|
5
|
|
|
* |
|
6
|
|
|
* It is free software; you can redistribute it and/or modify it under |
|
7
|
|
|
* the terms of the GNU General Public License, either version 2 |
|
8
|
|
|
* of the License, or any later version. |
|
9
|
|
|
* |
|
10
|
|
|
* For the full copyright and license information, please read the |
|
11
|
|
|
* LICENSE.txt file that was distributed with this source code. |
|
12
|
|
|
* |
|
13
|
|
|
* The TYPO3 project - inspiring people to share! |
|
14
|
|
|
*/ |
|
15
|
|
|
|
|
16
|
|
|
namespace TYPO3\CMS\Frontend\ContentObject; |
|
17
|
|
|
|
|
18
|
|
|
use TYPO3\CMS\Core\Core\Environment; |
|
19
|
|
|
use TYPO3\CMS\Core\Page\AssetCollector; |
|
20
|
|
|
use TYPO3\CMS\Core\Service\MarkerBasedTemplateService; |
|
21
|
|
|
use TYPO3\CMS\Core\TypoScript\TypoScriptService; |
|
22
|
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility; |
|
23
|
|
|
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; |
|
24
|
|
|
|
|
25
|
|
|
/** |
|
26
|
|
|
* Contains IMAGE class object. |
|
27
|
|
|
*/ |
|
28
|
|
|
class ImageContentObject extends AbstractContentObject |
|
29
|
|
|
{ |
|
30
|
|
|
/** |
|
31
|
|
|
* Rendering the cObject, IMAGE |
|
32
|
|
|
* |
|
33
|
|
|
* @param array $conf Array of TypoScript properties |
|
34
|
|
|
* @return string Output |
|
35
|
|
|
*/ |
|
36
|
|
|
public function render($conf = []) |
|
37
|
|
|
{ |
|
38
|
|
|
if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) { |
|
39
|
|
|
return ''; |
|
40
|
|
|
} |
|
41
|
|
|
|
|
42
|
|
|
$theValue = $this->cImage($conf['file'], $conf); |
|
43
|
|
|
if (isset($conf['stdWrap.'])) { |
|
44
|
|
|
$theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']); |
|
45
|
|
|
} |
|
46
|
|
|
return $theValue; |
|
47
|
|
|
} |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* Returns a <img> tag with the image file defined by $file and processed according to the properties in the TypoScript array. |
|
51
|
|
|
* Mostly this function is a sub-function to the IMAGE function which renders the IMAGE cObject in TypoScript. |
|
52
|
|
|
* |
|
53
|
|
|
* @param string $file File TypoScript resource |
|
54
|
|
|
* @param array $conf TypoScript configuration properties |
|
55
|
|
|
* @return string HTML <img> tag, (possibly wrapped in links and other HTML) if any image found. |
|
56
|
|
|
*/ |
|
57
|
|
|
protected function cImage($file, $conf) |
|
58
|
|
|
{ |
|
59
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
|
60
|
|
|
$info = $this->cObj->getImgResource($file, $conf['file.']); |
|
61
|
|
|
if (!is_array($info)) { |
|
62
|
|
|
return ''; |
|
63
|
|
|
} |
|
64
|
|
|
if (is_file(Environment::getPublicPath() . '/' . $info['3'])) { |
|
65
|
|
|
$source = $tsfe->absRefPrefix . str_replace('%2F', '/', rawurlencode($info['3'])); |
|
66
|
|
|
} else { |
|
67
|
|
|
$source = $info[3]; |
|
68
|
|
|
} |
|
69
|
|
|
// Remove file objects for AssetCollector, as it only allows to store scalar values |
|
70
|
|
|
unset($info['originalFile'], $info['processedFile']); |
|
71
|
|
|
GeneralUtility::makeInstance(AssetCollector::class)->addMedia( |
|
72
|
|
|
$source, |
|
73
|
|
|
$info |
|
74
|
|
|
); |
|
75
|
|
|
|
|
76
|
|
|
$layoutKey = $this->cObj->stdWrapValue('layoutKey', $conf); |
|
77
|
|
|
$imageTagTemplate = $this->getImageTagTemplate($layoutKey, $conf); |
|
78
|
|
|
$sourceCollection = $this->getImageSourceCollection($layoutKey, $conf, $file); |
|
79
|
|
|
|
|
80
|
|
|
$altParam = $this->getAltParam($conf); |
|
81
|
|
|
$params = $this->cObj->stdWrapValue('params', $conf); |
|
82
|
|
|
if ($params !== '' && $params[0] !== ' ') { |
|
83
|
|
|
$params = ' ' . $params; |
|
84
|
|
|
} |
|
85
|
|
|
|
|
86
|
|
|
$imageTagValues = [ |
|
87
|
|
|
'width' => (int)$info[0], |
|
88
|
|
|
'height' => (int)$info[1], |
|
89
|
|
|
'src' => htmlspecialchars($source), |
|
90
|
|
|
'params' => $params, |
|
91
|
|
|
'altParams' => $altParam, |
|
92
|
|
|
'border' => $this->getBorderAttr(' border="' . (int)$conf['border'] . '"'), |
|
93
|
|
|
'sourceCollection' => $sourceCollection, |
|
94
|
|
|
'selfClosingTagSlash' => !empty($tsfe->xhtmlDoctype) ? ' /' : '', |
|
95
|
|
|
]; |
|
96
|
|
|
|
|
97
|
|
|
$markerTemplateEngine = GeneralUtility::makeInstance(MarkerBasedTemplateService::class); |
|
98
|
|
|
$theValue = $markerTemplateEngine->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true); |
|
99
|
|
|
|
|
100
|
|
|
$linkWrap = $this->cObj->stdWrapValue('linkWrap', $conf); |
|
101
|
|
|
if ($linkWrap) { |
|
102
|
|
|
$theValue = $this->linkWrap($theValue, $linkWrap); |
|
103
|
|
|
} elseif ($conf['imageLinkWrap']) { |
|
104
|
|
|
$originalFile = !empty($info['originalFile']) ? $info['originalFile'] : $info['origFile']; |
|
105
|
|
|
$theValue = $this->cObj->imageLinkWrap($theValue, $originalFile, $conf['imageLinkWrap.']); |
|
106
|
|
|
} |
|
107
|
|
|
$wrap = $this->cObj->stdWrapValue('wrap', $conf); |
|
108
|
|
|
if ((string)$wrap !== '') { |
|
109
|
|
|
$theValue = $this->cObj->wrap($theValue, $conf['wrap']); |
|
110
|
|
|
} |
|
111
|
|
|
return $theValue; |
|
112
|
|
|
} |
|
113
|
|
|
|
|
114
|
|
|
/** |
|
115
|
|
|
* Returns the 'border' attribute for an <img> tag only if the doctype is not xhtml_strict, xhtml_11 or html5 |
|
116
|
|
|
* or if the config parameter 'disableImgBorderAttr' is not set. |
|
117
|
|
|
* |
|
118
|
|
|
* @param string $borderAttr The border attribute |
|
119
|
|
|
* @return string The border attribute |
|
120
|
|
|
*/ |
|
121
|
|
|
protected function getBorderAttr($borderAttr) |
|
122
|
|
|
{ |
|
123
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
|
124
|
|
|
$docType = $tsfe->xhtmlDoctype; |
|
125
|
|
|
if ( |
|
126
|
|
|
$docType !== 'xhtml_strict' && $docType !== 'xhtml_11' |
|
127
|
|
|
&& $tsfe->config['config']['doctype'] !== 'html5' |
|
128
|
|
|
&& !$tsfe->config['config']['disableImgBorderAttr'] |
|
129
|
|
|
) { |
|
130
|
|
|
return $borderAttr; |
|
131
|
|
|
} |
|
132
|
|
|
return ''; |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
/** |
|
136
|
|
|
* Returns the html-template for rendering the image-Tag if no template is defined via typoscript the |
|
137
|
|
|
* default <img> tag template is returned |
|
138
|
|
|
* |
|
139
|
|
|
* @param string $layoutKey rendering key |
|
140
|
|
|
* @param array $conf TypoScript configuration properties |
|
141
|
|
|
* @return string |
|
142
|
|
|
*/ |
|
143
|
|
|
protected function getImageTagTemplate($layoutKey, $conf): string |
|
144
|
|
|
{ |
|
145
|
|
|
if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) { |
|
146
|
|
|
return $this->cObj->stdWrapValue('element', $conf['layout.'][$layoutKey . '.']); |
|
|
|
|
|
|
147
|
|
|
} |
|
148
|
|
|
return '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>'; |
|
149
|
|
|
} |
|
150
|
|
|
|
|
151
|
|
|
/** |
|
152
|
|
|
* Render alternate sources for the image tag. If no source collection is given an empty string is returned. |
|
153
|
|
|
* |
|
154
|
|
|
* @param string $layoutKey rendering key |
|
155
|
|
|
* @param array $conf TypoScript configuration properties |
|
156
|
|
|
* @param string $file |
|
157
|
|
|
* @throws \UnexpectedValueException |
|
158
|
|
|
* @return string |
|
159
|
|
|
*/ |
|
160
|
|
|
protected function getImageSourceCollection($layoutKey, $conf, $file) |
|
161
|
|
|
{ |
|
162
|
|
|
$sourceCollection = ''; |
|
163
|
|
|
if ($layoutKey |
|
164
|
|
|
&& isset($conf['sourceCollection.']) && $conf['sourceCollection.'] |
|
165
|
|
|
&& ( |
|
166
|
|
|
isset($conf['layout.'][$layoutKey . '.']['source']) && $conf['layout.'][$layoutKey . '.']['source'] |
|
167
|
|
|
|| isset($conf['layout.'][$layoutKey . '.']['source.']) && $conf['layout.'][$layoutKey . '.']['source.'] |
|
168
|
|
|
) |
|
169
|
|
|
) { |
|
|
|
|
|
|
170
|
|
|
|
|
171
|
|
|
// find active sourceCollection |
|
172
|
|
|
$activeSourceCollections = []; |
|
173
|
|
|
foreach ($conf['sourceCollection.'] as $sourceCollectionKey => $sourceCollectionConfiguration) { |
|
174
|
|
|
if (substr($sourceCollectionKey, -1) === '.') { |
|
175
|
|
|
if (empty($sourceCollectionConfiguration['if.']) || $this->cObj->checkIf($sourceCollectionConfiguration['if.'])) { |
|
176
|
|
|
$activeSourceCollections[] = $sourceCollectionConfiguration; |
|
177
|
|
|
} |
|
178
|
|
|
} |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
// apply option split to configurations |
|
182
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
|
183
|
|
|
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class); |
|
184
|
|
|
$srcLayoutOptionSplitted = $typoScriptService->explodeConfigurationForOptionSplit((array)$conf['layout.'][$layoutKey . '.'], count($activeSourceCollections)); |
|
185
|
|
|
|
|
186
|
|
|
// render sources |
|
187
|
|
|
foreach ($activeSourceCollections as $key => $sourceConfiguration) { |
|
188
|
|
|
$sourceLayout = $this->cObj->stdWrapValue('source', $srcLayoutOptionSplitted[$key] ?? []); |
|
189
|
|
|
|
|
190
|
|
|
$sourceRenderConfiguration = [ |
|
191
|
|
|
'file' => $file, |
|
192
|
|
|
'file.' => $conf['file.'] ?? null |
|
193
|
|
|
]; |
|
194
|
|
|
|
|
195
|
|
|
$imageQuality = $this->cObj->stdWrapValue('quality', $sourceConfiguration); |
|
196
|
|
|
if ($imageQuality) { |
|
197
|
|
|
$sourceRenderConfiguration['file.']['params'] = '-quality ' . (int)$imageQuality; |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
$pixelDensity = (int)$this->cObj->stdWrapValue('pixelDensity', $sourceConfiguration, 1); |
|
201
|
|
|
$dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH', 'maxWidth', 'maxHeight', 'XY']; |
|
202
|
|
|
foreach ($dimensionKeys as $dimensionKey) { |
|
203
|
|
|
$dimension = $this->cObj->stdWrapValue($dimensionKey, $sourceConfiguration); |
|
204
|
|
|
if (!$dimension) { |
|
205
|
|
|
$dimension = $this->cObj->stdWrapValue($dimensionKey, $conf['file.'] ?? []); |
|
206
|
|
|
} |
|
207
|
|
|
if ($dimension) { |
|
208
|
|
|
if (strpos($dimension, 'c') !== false && ($dimensionKey === 'width' || $dimensionKey === 'height')) { |
|
209
|
|
|
$dimensionParts = explode('c', $dimension, 2); |
|
210
|
|
|
$dimension = ((int)$dimensionParts[0] * $pixelDensity) . 'c'; |
|
211
|
|
|
if ($dimensionParts[1]) { |
|
212
|
|
|
$dimension .= $dimensionParts[1]; |
|
213
|
|
|
} |
|
214
|
|
|
} elseif ($dimensionKey === 'XY') { |
|
215
|
|
|
$dimensionParts = GeneralUtility::intExplode(',', $dimension, false, 2); |
|
216
|
|
|
$dimension = $dimensionParts[0] * $pixelDensity; |
|
217
|
|
|
if ($dimensionParts[1]) { |
|
218
|
|
|
$dimension .= ',' . $dimensionParts[1] * $pixelDensity; |
|
219
|
|
|
} |
|
220
|
|
|
} else { |
|
221
|
|
|
$dimension = (int)$dimension * $pixelDensity; |
|
222
|
|
|
} |
|
223
|
|
|
$sourceRenderConfiguration['file.'][$dimensionKey] = $dimension; |
|
224
|
|
|
// Remove the stdWrap properties for dimension as they have been processed already above. |
|
225
|
|
|
unset($sourceRenderConfiguration['file.'][$dimensionKey . '.']); |
|
226
|
|
|
} |
|
227
|
|
|
} |
|
228
|
|
|
$sourceInfo = $this->cObj->getImgResource($sourceRenderConfiguration['file'], $sourceRenderConfiguration['file.']); |
|
229
|
|
|
if ($sourceInfo) { |
|
230
|
|
|
$sourceConfiguration['width'] = $sourceInfo[0]; |
|
231
|
|
|
$sourceConfiguration['height'] = $sourceInfo[1]; |
|
232
|
|
|
$urlPrefix = ''; |
|
233
|
|
|
if (parse_url($sourceInfo[3], PHP_URL_HOST) === null) { |
|
234
|
|
|
$urlPrefix = $tsfe->absRefPrefix; |
|
235
|
|
|
} |
|
236
|
|
|
$sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $sourceInfo[3]); |
|
237
|
|
|
$sourceConfiguration['selfClosingTagSlash'] = !empty($tsfe->xhtmlDoctype) ? ' /' : ''; |
|
238
|
|
|
|
|
239
|
|
|
$markerTemplateEngine = GeneralUtility::makeInstance(MarkerBasedTemplateService::class); |
|
240
|
|
|
$oneSourceCollection = $markerTemplateEngine->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true); |
|
241
|
|
|
|
|
242
|
|
|
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'] ?? [] as $className) { |
|
243
|
|
|
$hookObject = GeneralUtility::makeInstance($className); |
|
244
|
|
|
if (!$hookObject instanceof ContentObjectOneSourceCollectionHookInterface) { |
|
245
|
|
|
throw new \UnexpectedValueException( |
|
246
|
|
|
'$hookObject must implement interface ' . ContentObjectOneSourceCollectionHookInterface::class, |
|
247
|
|
|
1380017853 |
|
248
|
|
|
); |
|
249
|
|
|
} |
|
250
|
|
|
$oneSourceCollection = $hookObject->getOneSourceCollection((array)$sourceRenderConfiguration, (array)$sourceConfiguration, $oneSourceCollection, $this->cObj); |
|
251
|
|
|
} |
|
252
|
|
|
|
|
253
|
|
|
$sourceCollection .= $oneSourceCollection; |
|
254
|
|
|
} |
|
255
|
|
|
} |
|
256
|
|
|
} |
|
257
|
|
|
return $sourceCollection; |
|
258
|
|
|
} |
|
259
|
|
|
|
|
260
|
|
|
/** |
|
261
|
|
|
* Wraps the input string by the $wrap value and implements the "linkWrap" data type as well. |
|
262
|
|
|
* The "linkWrap" data type means that this function will find any integer encapsulated in {} (curly braces) in the first wrap part and substitute it with the corresponding page uid from the rootline where the found integer is pointing to the key in the rootline. See link below. |
|
263
|
|
|
* |
|
264
|
|
|
* @param string $content Input string |
|
265
|
|
|
* @param string $wrap A string where the first two parts separated by "|" (vertical line) will be wrapped around the input string |
|
266
|
|
|
* @return string Wrapped output string |
|
267
|
|
|
* @see wrap() |
|
268
|
|
|
* @see cImage() |
|
269
|
|
|
*/ |
|
270
|
|
|
protected function linkWrap($content, $wrap) |
|
271
|
|
|
{ |
|
272
|
|
|
$wrapArr = explode('|', $wrap); |
|
273
|
|
|
if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) { |
|
274
|
|
|
$uid = $this->getTypoScriptFrontendController()->tmpl->rootLine[$reg[1]]['uid'] ?? null; |
|
275
|
|
|
if ($uid) { |
|
276
|
|
|
$wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]); |
|
277
|
|
|
} |
|
278
|
|
|
} |
|
279
|
|
|
return trim($wrapArr[0] ?? '') . $content . trim($wrapArr[1] ?? ''); |
|
280
|
|
|
} |
|
281
|
|
|
|
|
282
|
|
|
/** |
|
283
|
|
|
* An abstraction method which creates an alt or title parameter for an HTML img, applet, area or input element and the FILE content element. |
|
284
|
|
|
* From the $conf array it implements the properties "altText", "titleText" and "longdescURL" |
|
285
|
|
|
* |
|
286
|
|
|
* @param array $conf TypoScript configuration properties |
|
287
|
|
|
* @param bool $longDesc If set, the longdesc attribute will be generated - must only be used for img elements! |
|
288
|
|
|
* @return string Parameter string containing alt and title parameters (if any) |
|
289
|
|
|
* @see cImage() |
|
290
|
|
|
*/ |
|
291
|
|
|
public function getAltParam($conf, $longDesc = true) |
|
292
|
|
|
{ |
|
293
|
|
|
$altText = trim($this->cObj->stdWrapValue('altText', $conf)); |
|
294
|
|
|
$titleText = trim($this->cObj->stdWrapValue('titleText', $conf)); |
|
295
|
|
|
if (isset($conf['longdescURL.']) && $this->getTypoScriptFrontendController()->config['config']['doctype'] !== 'html5') { |
|
296
|
|
|
$longDescUrl = $this->cObj->typoLink_URL($conf['longdescURL.']); |
|
297
|
|
|
} else { |
|
298
|
|
|
$longDescUrl = trim($conf['longdescURL']); |
|
299
|
|
|
} |
|
300
|
|
|
$longDescUrl = strip_tags($longDescUrl); |
|
301
|
|
|
|
|
302
|
|
|
// "alt": |
|
303
|
|
|
$altParam = ' alt="' . htmlspecialchars($altText) . '"'; |
|
304
|
|
|
// "title": |
|
305
|
|
|
$emptyTitleHandling = $this->cObj->stdWrapValue('emptyTitleHandling', $conf); |
|
306
|
|
|
// Choices: 'keepEmpty' | 'useAlt' | 'removeAttr' |
|
307
|
|
|
if ($titleText || $emptyTitleHandling === 'keepEmpty') { |
|
308
|
|
|
$altParam .= ' title="' . htmlspecialchars($titleText) . '"'; |
|
309
|
|
|
} elseif (!$titleText && $emptyTitleHandling === 'useAlt') { |
|
310
|
|
|
$altParam .= ' title="' . htmlspecialchars($altText) . '"'; |
|
311
|
|
|
} |
|
312
|
|
|
// "longDesc" URL |
|
313
|
|
|
if ($longDesc && !empty($longDescUrl)) { |
|
314
|
|
|
$altParam .= ' longdesc="' . htmlspecialchars($longDescUrl) . '"'; |
|
315
|
|
|
} |
|
316
|
|
|
return $altParam; |
|
317
|
|
|
} |
|
318
|
|
|
|
|
319
|
|
|
/** |
|
320
|
|
|
* @return TypoScriptFrontendController |
|
321
|
|
|
*/ |
|
322
|
|
|
protected function getTypoScriptFrontendController() |
|
323
|
|
|
{ |
|
324
|
|
|
return $GLOBALS['TSFE']; |
|
325
|
|
|
} |
|
326
|
|
|
} |
|
327
|
|
|
|