Completed
Push — master ( 8e078a...edce3c )
by
unknown
15:28
created

ImageContentObject::linkWrap()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 2
dl 0
loc 10
rs 10
c 0
b 0
f 0
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 . '.']);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->cObj->stdW...t.'][$layoutKey . '.']) could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
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
        ) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
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