‪TYPO3CMS  ‪main
ImageContentObject.php
Go to the documentation of this file.
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 
17 
24 
29 {
30  public function ‪__construct(
31  protected readonly ‪MarkerBasedTemplateService $markerTemplateService,
32  ) {
33  }
34 
41  public function ‪render($conf = [])
42  {
43  if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
44  return '';
45  }
46 
47  $theValue = $this->‪cImage($conf['file'] ?? '', $conf);
48  if (isset($conf['stdWrap.'])) {
49  $theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
50  }
51  return $theValue;
52  }
53 
62  protected function ‪cImage($file, $conf)
63  {
64  $tsfe = $this->‪getTypoScriptFrontendController();
65  $info = $this->cObj->getImgResource($file, $conf['file.'] ?? []);
66  if (!is_array($info)) {
67  return '';
68  }
69  // $info['originalFile'] will be set, when the file is processed by FAL.
70  // In that case the URL is final and we must not add a prefix
71  if (!isset($info['originalFile']) && is_file($info['3'])) {
72  $source = $tsfe->absRefPrefix . str_replace('%2F', '/', rawurlencode(‪PathUtility::stripPathSitePrefix($info['3'])));
73  } else {
74  $source = ‪PathUtility::stripPathSitePrefix($info[3]);
75  }
76  // Remove file objects for AssetCollector, as it only allows to store scalar values
77  $infoOriginalFile = $info['originalFile'] ?? null;
78  unset($info['originalFile'], $info['processedFile']);
79  GeneralUtility::makeInstance(AssetCollector::class)->addMedia(
80  $source,
81  $info
82  );
83 
84  $layoutKey = (string)$this->cObj->stdWrapValue('layoutKey', $conf ?? []);
85  $imageTagTemplate = $this->‪getImageTagTemplate($layoutKey, $conf);
86  $sourceCollection = $this->‪getImageSourceCollection($layoutKey, $conf, $file);
87 
88  $altParam = $this->‪getAltParam($conf);
89  $params = $this->cObj->stdWrapValue('params', $conf ?? []);
90  if ($params !== '' && $params[0] !== ' ') {
91  $params = ' ' . $params;
92  }
93 
94  $imageTagValues = [
95  'width' => (int)$info[0],
96  'height' => (int)$info[1],
97  'src' => htmlspecialchars($source),
98  'params' => $params,
99  'altParams' => $altParam,
100  'sourceCollection' => $sourceCollection,
101  'selfClosingTagSlash' => $this->‪getPageRenderer()->getDocType()->isXmlCompliant() ? ' /' : '',
102  ];
103 
104  $theValue = $this->markerTemplateService->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
105 
106  $linkWrap = (string)$this->cObj->stdWrapValue('linkWrap', $conf ?? []);
107  if ($linkWrap !== '') {
108  $theValue = $this->‪linkWrap((string)$theValue, $linkWrap);
109  } elseif ($conf['imageLinkWrap'] ?? false) {
110  $originalFile = !empty($infoOriginalFile) ? $infoOriginalFile : urldecode($info['origFile']);
111  $theValue = $this->cObj->imageLinkWrap($theValue, $originalFile, $conf['imageLinkWrap.']);
112  }
113  $wrap = $this->cObj->stdWrapValue('wrap', $conf ?? []);
114  if ((string)$wrap !== '') {
115  $theValue = $this->cObj->wrap($theValue, $conf['wrap']);
116  }
117  return $theValue;
118  }
119 
127  protected function ‪getImageTagTemplate($layoutKey, $conf): string
128  {
129  if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) {
130  return $this->cObj->stdWrapValue('element', $conf['layout.'][$layoutKey . '.']);
131  }
132  return '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###SELFCLOSINGTAGSLASH###>';
133  }
134 
144  protected function ‪getImageSourceCollection($layoutKey, $conf, $file)
145  {
146  $sourceCollection = '';
147  if ($layoutKey
148  && isset($conf['sourceCollection.']) && $conf['sourceCollection.']
149  && (
150  isset($conf['layout.'][$layoutKey . '.']['source']) && $conf['layout.'][$layoutKey . '.']['source']
151  || isset($conf['layout.'][$layoutKey . '.']['source.']) && $conf['layout.'][$layoutKey . '.']['source.']
152  )
153  ) {
154  // find active sourceCollection
155  $activeSourceCollections = [];
156  foreach ($conf['sourceCollection.'] as $sourceCollectionKey => $sourceCollectionConfiguration) {
157  if (substr($sourceCollectionKey, -1) === '.') {
158  if (empty($sourceCollectionConfiguration['if.']) || $this->cObj->checkIf($sourceCollectionConfiguration['if.'])) {
159  $activeSourceCollections[] = $sourceCollectionConfiguration;
160  }
161  }
162  }
163 
164  // apply option split to configurations
165  $tsfe = $this->‪getTypoScriptFrontendController();
166  $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
167  $srcLayoutOptionSplitted = $typoScriptService->explodeConfigurationForOptionSplit((array)$conf['layout.'][$layoutKey . '.'], count($activeSourceCollections));
168 
169  // render sources
170  foreach ($activeSourceCollections as $key => $sourceConfiguration) {
171  $sourceLayout = $this->cObj->stdWrapValue('source', $srcLayoutOptionSplitted[$key] ?? []);
172 
173  $sourceRenderConfiguration = [
174  'file' => $file,
175  'file.' => $conf['file.'] ?? null,
176  ];
177 
178  $imageQuality = $this->cObj->stdWrapValue('quality', $sourceConfiguration ?? []);
179  if ($imageQuality) {
180  $sourceRenderConfiguration['file.']['params'] = '-quality ' . (int)$imageQuality;
181  }
182 
183  $pixelDensity = (int)$this->cObj->stdWrapValue('pixelDensity', $sourceConfiguration, 1);
184  $dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH', 'maxWidth', 'maxHeight', 'XY'];
185  foreach ($dimensionKeys as $dimensionKey) {
186  $dimension = (string)$this->cObj->stdWrapValue($dimensionKey, $sourceConfiguration);
187  if ($dimension === '') {
188  $dimension = (string)$this->cObj->stdWrapValue($dimensionKey, $conf['file.'] ?? []);
189  }
190  if ($dimension !== '') {
191  if (str_contains($dimension, 'c') && ($dimensionKey === 'width' || $dimensionKey === 'height')) {
192  $dimensionParts = explode('c', $dimension, 2);
193  $dimension = ((int)$dimensionParts[0] * $pixelDensity) . 'c';
194  if ($dimensionParts[1]) {
195  $dimension .= $dimensionParts[1];
196  }
197  } elseif ($dimensionKey === 'XY') {
198  $dimensionParts = GeneralUtility::intExplode(',', $dimension);
199  $dimension = $dimensionParts[0] * $pixelDensity;
200  if ($dimensionParts[1]) {
201  $dimension .= ',' . $dimensionParts[1] * $pixelDensity;
202  }
203  } else {
204  $dimension = (int)$dimension * $pixelDensity;
205  }
206  $sourceRenderConfiguration['file.'][$dimensionKey] = $dimension;
207  // Remove the stdWrap properties for dimension as they have been processed already above.
208  unset($sourceRenderConfiguration['file.'][$dimensionKey . '.']);
209  }
210  }
211  $sourceInfo = $this->cObj->getImgResource($sourceRenderConfiguration['file'], $sourceRenderConfiguration['file.']);
212  if ($sourceInfo) {
213  $sourceConfiguration['width'] = $sourceInfo[0];
214  $sourceConfiguration['height'] = $sourceInfo[1];
215 
216  $urlPrefix = '';
217  ‪$publicUrl = str_starts_with($sourceInfo[3], ‪Environment::getPublicPath())
218  ? ‪PathUtility::stripPathSitePrefix($sourceInfo[3])
219  : $sourceInfo[3];
220 
221  // Prepend 'absRefPrefix' to file path only if file was not processed
222  // by FAL, e.g. GIFBUILDER
223  if (!isset($sourceInfo['originalFile']) && is_file(‪Environment::getPublicPath() . '/' . ‪$publicUrl)) {
224  $urlPrefix = $tsfe->absRefPrefix;
225  }
226 
227  $sourceConfiguration['src'] = htmlspecialchars($urlPrefix . ‪$publicUrl);
228  $sourceConfiguration['selfClosingTagSlash'] = $this->‪getPageRenderer()->getDocType()->isXmlCompliant() ? ' /' : '';
229 
230  $oneSourceCollection = $this->markerTemplateService->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
231 
232  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'] ?? [] as $className) {
233  $hookObject = GeneralUtility::makeInstance($className);
234  if (!$hookObject instanceof ‪ContentObjectOneSourceCollectionHookInterface) {
235  throw new \UnexpectedValueException(
236  '$hookObject must implement interface ' . ContentObjectOneSourceCollectionHookInterface::class,
237  1380017853
238  );
239  }
240  $oneSourceCollection = $hookObject->getOneSourceCollection((array)$sourceRenderConfiguration, (array)$sourceConfiguration, $oneSourceCollection, $this->cObj);
241  }
242 
243  $sourceCollection .= $oneSourceCollection;
244  }
245  }
246  }
247  return $sourceCollection;
248  }
249 
260  protected function ‪linkWrap(string $content, string $wrap): string
261  {
262  $wrapArr = explode('|', $wrap);
263  if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
264  ‪$uid = $this->‪getTypoScriptFrontendController()->config['rootLine'][$reg[1]]['uid'] ?? null;
265  if (‪$uid) {
266  $wrapArr[0] = str_replace($reg[0], ‪$uid, $wrapArr[0]);
267  }
268  }
269  return trim($wrapArr[0] ?? '') . $content . trim($wrapArr[1] ?? '');
270  }
271 
279  protected function ‪getAltParam(array $conf): string
280  {
281  $altText = trim((string)$this->cObj->stdWrapValue('altText', $conf ?? []));
282  $titleText = trim((string)$this->cObj->stdWrapValue('titleText', $conf ?? []));
283 
284  // "alt":
285  $altParam = ' alt="' . htmlspecialchars($altText) . '"';
286  // "title":
287  $emptyTitleHandling = $this->cObj->stdWrapValue('emptyTitleHandling', $conf ?? []);
288  // Choices: 'keepEmpty' | 'useAlt' | 'removeAttr'
289  if ($titleText || $emptyTitleHandling === 'keepEmpty') {
290  $altParam .= ' title="' . htmlspecialchars($titleText) . '"';
291  } elseif (!$titleText && $emptyTitleHandling === 'useAlt') {
292  $altParam .= ' title="' . htmlspecialchars($altText) . '"';
293  }
294  return $altParam;
295  }
296 }
‪TYPO3\CMS\Core\Page\AssetCollector
Definition: AssetCollector.php:42
‪TYPO3\CMS\Core\Utility\PathUtility\stripPathSitePrefix
‪static stripPathSitePrefix(string $path)
Definition: PathUtility.php:428
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\linkWrap
‪linkWrap(string $content, string $wrap)
Definition: ImageContentObject.php:260
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\getImageTagTemplate
‪getImageTagTemplate($layoutKey, $conf)
Definition: ImageContentObject.php:127
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject
Definition: ImageContentObject.php:29
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\cImage
‪string cImage($file, $conf)
Definition: ImageContentObject.php:62
‪TYPO3\CMS\Frontend\ContentObject\AbstractContentObject\getPageRenderer
‪getPageRenderer()
Definition: AbstractContentObject.php:101
‪TYPO3\CMS\Frontend\ContentObject
Definition: AbstractContentObject.php:18
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static getPublicPath()
Definition: Environment.php:187
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\render
‪string render($conf=[])
Definition: ImageContentObject.php:41
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\__construct
‪__construct(protected readonly MarkerBasedTemplateService $markerTemplateService,)
Definition: ImageContentObject.php:30
‪TYPO3\CMS\Webhooks\Message\$publicUrl
‪identifier readonly string readonly string $publicUrl
Definition: FileUpdatedMessage.php:36
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\getImageSourceCollection
‪string getImageSourceCollection($layoutKey, $conf, $file)
Definition: ImageContentObject.php:144
‪TYPO3\CMS\Frontend\ContentObject\AbstractContentObject
Definition: AbstractContentObject.php:31
‪TYPO3\CMS\Core\TypoScript\TypoScriptService
Definition: TypoScriptService.php:27
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Frontend\ContentObject\AbstractContentObject\getTypoScriptFrontendController
‪getTypoScriptFrontendController()
Definition: AbstractContentObject.php:77
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectOneSourceCollectionHookInterface
Definition: ContentObjectOneSourceCollectionHookInterface.php:22
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\getAltParam
‪string getAltParam(array $conf)
Definition: ImageContentObject.php:279
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService
Definition: MarkerBasedTemplateService.php:27
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51