‪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 
18 use Psr\EventDispatcher\EventDispatcherInterface;
26 
31 {
32  public function ‪__construct(
33  protected readonly ‪MarkerBasedTemplateService $markerTemplateService,
34  ) {}
35 
42  public function ‪render($conf = [])
43  {
44  if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
45  return '';
46  }
47 
48  $theValue = $this->‪cImage($conf['file'] ?? '', is_array($conf) ? $conf : []);
49  if (isset($conf['stdWrap.'])) {
50  $theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
51  }
52  return $theValue;
53  }
54 
63  protected function ‪cImage($file, array $conf): string
64  {
65  $tsfe = $this->‪getTypoScriptFrontendController();
66  $imageResource = $this->cObj->getImgResource($file, $conf['file.'] ?? []);
67  if ($imageResource === null) {
68  return '';
69  }
70  // $info['originalFile'] will be set, when the file is processed by FAL.
71  // In that case the URL is final and we must not add a prefix
72  if ($imageResource->getOriginalFile() === null && is_file($imageResource->getFullPath())) {
73  $source = $tsfe->absRefPrefix . str_replace('%2F', '/', rawurlencode($imageResource->getPublicUrl()));
74  } else {
75  $source = $imageResource->getPublicUrl();
76  }
77  GeneralUtility::makeInstance(AssetCollector::class)->addMedia(
78  $source,
79  $imageResource->getLegacyImageResourceInformation()
80  );
81 
82  $layoutKey = (string)$this->cObj->stdWrapValue('layoutKey', $conf);
83  $imageTagTemplate = $this->‪getImageTagTemplate($layoutKey, $conf);
84  $sourceCollection = $this->‪getImageSourceCollection($layoutKey, $conf, $file);
85 
86  $altParam = $this->‪getAltParam($conf);
87  $params = $this->cObj->stdWrapValue('params', $conf);
88  if ($params !== '' && $params[0] !== ' ') {
89  $params = ' ' . $params;
90  }
91 
92  $imageTagValues = [
93  'width' => $imageResource->getWidth(),
94  'height' => $imageResource->getHeight(),
95  'src' => htmlspecialchars($source),
96  'params' => $params,
97  'altParams' => $altParam,
98  'sourceCollection' => $sourceCollection,
99  'selfClosingTagSlash' => $this->‪getPageRenderer()->getDocType()->isXmlCompliant() ? ' /' : '',
100  ];
101 
102  $theValue = $this->markerTemplateService->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
103 
104  $linkWrap = (string)$this->cObj->stdWrapValue('linkWrap', $conf);
105  if ($linkWrap !== '') {
106  $theValue = $this->‪linkWrap($theValue, $linkWrap);
107  } elseif ($conf['imageLinkWrap'] ?? false) {
108  $originalFile = urldecode($imageResource->getFullPath());
109  $theValue = $this->cObj->imageLinkWrap($theValue, $originalFile, $conf['imageLinkWrap.']);
110  }
111  $wrap = $this->cObj->stdWrapValue('wrap', $conf);
112  if ((string)$wrap !== '') {
113  $theValue = $this->cObj->wrap($theValue, $conf['wrap']);
114  }
115  return $theValue;
116  }
117 
125  protected function ‪getImageTagTemplate($layoutKey, $conf): string
126  {
127  if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) {
128  return $this->cObj->stdWrapValue('element', $conf['layout.'][$layoutKey . '.']);
129  }
130  return '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###SELFCLOSINGTAGSLASH###>';
131  }
132 
141  protected function ‪getImageSourceCollection(string $layoutKey, array $conf, $file)
142  {
143  $sourceCollection = '';
144  if ($layoutKey
145  && isset($conf['sourceCollection.']) && $conf['sourceCollection.']
146  && (
147  isset($conf['layout.'][$layoutKey . '.']['source']) && $conf['layout.'][$layoutKey . '.']['source']
148  || isset($conf['layout.'][$layoutKey . '.']['source.']) && $conf['layout.'][$layoutKey . '.']['source.']
149  )
150  ) {
151  // find active sourceCollection
152  $activeSourceCollections = [];
153  foreach ($conf['sourceCollection.'] as $sourceCollectionKey => $sourceCollectionConfiguration) {
154  if (str_ends_with($sourceCollectionKey, '.')) {
155  if (empty($sourceCollectionConfiguration['if.']) || $this->cObj->checkIf($sourceCollectionConfiguration['if.'])) {
156  $activeSourceCollections[] = $sourceCollectionConfiguration;
157  }
158  }
159  }
160 
161  // apply option split to configurations
162  $tsfe = $this->‪getTypoScriptFrontendController();
163  $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
164  $srcLayoutOptionSplitted = $typoScriptService->explodeConfigurationForOptionSplit((array)$conf['layout.'][$layoutKey . '.'], count($activeSourceCollections));
165  $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
166 
167  // render sources
168  foreach ($activeSourceCollections as $key => $sourceConfiguration) {
169  $sourceLayout = $this->cObj->stdWrapValue('source', $srcLayoutOptionSplitted[$key] ?? []);
170 
171  $sourceRenderConfiguration = [
172  'file' => $file,
173  'file.' => $conf['file.'] ?? null,
174  ];
175 
176  $imageQuality = $this->cObj->stdWrapValue('quality', $sourceConfiguration ?? []);
177  if ($imageQuality) {
178  $sourceRenderConfiguration['file.']['params'] = '-quality ' . (int)$imageQuality;
179  }
180 
181  $pixelDensity = (int)$this->cObj->stdWrapValue('pixelDensity', $sourceConfiguration, 1);
182  $dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH', 'maxWidth', 'maxHeight', 'XY'];
183  foreach ($dimensionKeys as $dimensionKey) {
184  $dimension = (string)$this->cObj->stdWrapValue($dimensionKey, $sourceConfiguration);
185  if ($dimension === '') {
186  $dimension = (string)$this->cObj->stdWrapValue($dimensionKey, $conf['file.'] ?? []);
187  }
188  if ($dimension !== '') {
189  if (str_contains($dimension, 'c') && ($dimensionKey === 'width' || $dimensionKey === 'height')) {
190  $dimensionParts = explode('c', $dimension, 2);
191  $dimension = ((int)$dimensionParts[0] * $pixelDensity) . 'c';
192  if ($dimensionParts[1]) {
193  $dimension .= $dimensionParts[1];
194  }
195  } elseif ($dimensionKey === 'XY') {
196  $dimensionParts = ‪GeneralUtility::intExplode(',', $dimension);
197  $dimension = $dimensionParts[0] * $pixelDensity;
198  if ($dimensionParts[1]) {
199  $dimension .= ',' . $dimensionParts[1] * $pixelDensity;
200  }
201  } else {
202  $dimension = (int)$dimension * $pixelDensity;
203  }
204  $sourceRenderConfiguration['file.'][$dimensionKey] = $dimension;
205  // Remove the stdWrap properties for dimension as they have been processed already above.
206  unset($sourceRenderConfiguration['file.'][$dimensionKey . '.']);
207  }
208  }
209  $imageResource = $this->cObj->getImgResource($sourceRenderConfiguration['file'], $sourceRenderConfiguration['file.']);
210  if ($imageResource !== null) {
211  $sourceConfiguration['width'] = $imageResource->getWidth();
212  $sourceConfiguration['height'] = $imageResource->getHeight();
213 
214  $urlPrefix = '';
215  // Prepend 'absRefPrefix' to file path only if file was not processed
216  // by FAL, e.g. GIFBUILDER
217  if ($imageResource->getOriginalFile() === null && is_file($imageResource->getFullPath())) {
218  $urlPrefix = $tsfe->absRefPrefix;
219  }
220 
221  $sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $imageResource->getPublicUrl());
222  $sourceConfiguration['selfClosingTagSlash'] = $this->‪getPageRenderer()->getDocType()->isXmlCompliant() ? ' /' : '';
223 
224  $oneSourceCollection = $this->markerTemplateService->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
225 
226  $sourceCollection .= $eventDispatcher->dispatch(
227  new ‪ModifyImageSourceCollectionEvent($oneSourceCollection, $sourceCollection, (array)$sourceConfiguration, $sourceRenderConfiguration, $this->cObj)
228  )->getSourceCollection();
229  }
230  }
231  }
232  return $sourceCollection;
233  }
234 
245  protected function ‪linkWrap(string $content, string $wrap): string
246  {
247  $wrapArr = explode('|', $wrap);
248  if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
249  $localRootLine = $this->request->getAttribute('frontend.page.information')->getLocalRootLine();
250  ‪$uid = $localRootLine[$reg[1]]['uid'] ?? null;
251  if (‪$uid) {
252  $wrapArr[0] = str_replace($reg[0], ‪$uid, $wrapArr[0]);
253  }
254  }
255  return trim($wrapArr[0] ?? '') . $content . trim($wrapArr[1] ?? '');
256  }
257 
265  protected function ‪getAltParam(array $conf): string
266  {
267  $altText = trim((string)$this->cObj->stdWrapValue('altText', $conf));
268  $titleText = trim((string)$this->cObj->stdWrapValue('titleText', $conf));
269 
270  // "alt":
271  $altParam = ' alt="' . htmlspecialchars($altText) . '"';
272  // "title":
273  $emptyTitleHandling = $this->cObj->stdWrapValue('emptyTitleHandling', $conf);
274  // Choices: 'keepEmpty' | 'useAlt' | 'removeAttr'
275  if ($titleText || $emptyTitleHandling === 'keepEmpty') {
276  $altParam .= ' title="' . htmlspecialchars($titleText) . '"';
277  } elseif (!$titleText && $emptyTitleHandling === 'useAlt') {
278  $altParam .= ' title="' . htmlspecialchars($altText) . '"';
279  }
280  return $altParam;
281  }
282 }
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\getImageSourceCollection
‪string getImageSourceCollection(string $layoutKey, array $conf, $file)
Definition: ImageContentObject.php:141
‪TYPO3\CMS\Core\Page\AssetCollector
Definition: AssetCollector.php:42
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\linkWrap
‪linkWrap(string $content, string $wrap)
Definition: ImageContentObject.php:245
‪TYPO3\CMS\Frontend\ContentObject\Event\ModifyImageSourceCollectionEvent
Definition: ModifyImageSourceCollectionEvent.php:26
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\getImageTagTemplate
‪getImageTagTemplate($layoutKey, $conf)
Definition: ImageContentObject.php:125
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject
Definition: ImageContentObject.php:31
‪TYPO3\CMS\Frontend\ContentObject\AbstractContentObject\getPageRenderer
‪getPageRenderer()
Definition: AbstractContentObject.php:93
‪TYPO3\CMS\Frontend\ContentObject
Definition: AbstractContentObject.php:18
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\render
‪string render($conf=[])
Definition: ImageContentObject.php:42
‪TYPO3\CMS\Core\Resource\FileReference
Definition: FileReference.php:37
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\__construct
‪__construct(protected readonly MarkerBasedTemplateService $markerTemplateService,)
Definition: ImageContentObject.php:32
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:26
‪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
‪TYPO3\CMS\Frontend\ContentObject\AbstractContentObject\getTypoScriptFrontendController
‪getTypoScriptFrontendController()
Definition: AbstractContentObject.php:79
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\cImage
‪string cImage($file, array $conf)
Definition: ImageContentObject.php:63
‪TYPO3\CMS\Frontend\ContentObject\ImageContentObject\getAltParam
‪string getAltParam(array $conf)
Definition: ImageContentObject.php:265
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService
Definition: MarkerBasedTemplateService.php:27
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static list< int > intExplode(string $delimiter, string $string, bool $removeEmptyValues=false)
Definition: GeneralUtility.php:756