[TASK] Use ImageInfo class instead of direct PHP GD call
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / ContentObject / Menu / ImageMenuContentObject.php
1 <?php
2 namespace TYPO3\CMS\Frontend\ContentObject\Menu;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Type\File\ImageInfo;
18 use TYPO3\CMS\Core\Utility\ArrayUtility;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
21 use TYPO3\CMS\Frontend\Imaging\GifBuilder;
22
23 /**
24 * ImageMap based menus
25 */
26 class ImageMenuContentObject extends AbstractMenuContentObject
27 {
28 /**
29 * Calls procesItemStates() so that the common configuration for the menu items are resolved into individual configuration per item.
30 * Calls makeImageMap() to generate the image map image-file
31 *
32 * @see AbstractMenuContentObject::procesItemStates(), makeImageMap()
33 */
34 public function generate()
35 {
36 $NOconf = [];
37 $splitCount = count($this->menuArr);
38 if ($splitCount) {
39 list($NOconf) = $this->procesItemStates($splitCount);
40 }
41 if (!empty($this->mconf['debugItemConf'])) {
42 echo '<h3>$NOconf:</h3>';
43 debug($NOconf);
44 }
45 $this->makeImageMap($NOconf);
46 }
47
48 /**
49 * Will traverse input array with configuration per-item and create corresponding GIF files for the menu.
50 * The data of the files are stored in $this->result
51 *
52 * @param array $conf Array with configuration for each item.
53 * @access private
54 * @see generate()
55 */
56 public function makeImageMap($conf)
57 {
58 if (!is_array($conf)) {
59 $conf = [];
60 }
61 if (is_array($this->mconf['main.'])) {
62 $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
63 $gifCreator->init();
64 $itemsConf = $conf;
65 $conf = $this->mconf['main.'];
66 if (is_array($conf)) {
67 $sKeyArray = ArrayUtility::filterAndSortByNumericKeys($conf);
68 $gifObjCount = (int)end($sKeyArray);
69 // Now we add graphical objects to the gifbuilder-setup
70 $waArr = [];
71 foreach ($itemsConf as $key => $val) {
72 if (is_array($val)) {
73 $gifObjCount++;
74 $waArr[$key]['free'] = $gifObjCount;
75 $sKeyArray = ArrayUtility::filterAndSortByNumericKeys($val);
76 foreach ($sKeyArray as $theKey) {
77 $theValue = $val[$theKey];
78 if ((int)$theKey && ($theValArr = $val[$theKey . '.'])) {
79 $cObjData = $this->menuArr[$key] ?: [];
80 $gifObjCount++;
81 if ($theValue === 'TEXT') {
82 $waArr[$key]['textNum'] = $gifObjCount;
83 $gifCreator->data = $cObjData;
84 $theValArr = $gifCreator->checkTextObj($theValArr);
85 // if this is not done it seems that imageMaps will be rendered wrong!!
86 unset($theValArr['text.']);
87 // check links
88 $LD = $this->menuTypoLink($this->menuArr[$key], $this->mconf['target'], '', '', [], '', $this->mconf['forceTypeValue']);
89 // If access restricted pages should be shown in menus, change the link of such pages to link to a redirection page:
90 $this->changeLinksForAccessRestrictedPages($LD, $this->menuArr[$key], $this->mconf['target'], $this->mconf['forceTypeValue']);
91 // Overriding URL / Target if set to do so:
92 if ($this->menuArr[$key]['_OVERRIDE_HREF']) {
93 $LD['totalURL'] = $this->menuArr[$key]['_OVERRIDE_HREF'];
94 if ($this->menuArr[$key]['_OVERRIDE_TARGET']) {
95 $LD['target'] = $this->menuArr[$key]['_OVERRIDE_TARGET'];
96 }
97 }
98 // Setting target/url for Image Map:
99 if ($theValArr['imgMap.']['url'] === '') {
100 $theValArr['imgMap.']['url'] = $LD['totalURL'];
101 }
102 if ($theValArr['imgMap.']['target'] === '') {
103 $theValArr['imgMap.']['target'] = $LD['target'];
104 }
105 if (is_array($theValArr['imgMap.']['altText.'])) {
106 $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
107 $cObj->start($cObjData, 'pages');
108 if (isset($theValArr['imgMap.']['altText.'])) {
109 $theValArr['imgMap.']['altText'] = $cObj->stdWrap($theValArr['imgMap.']['altText'], $theValArr['imgMap.']['altText.']);
110 }
111 unset($theValArr['imgMap.']['altText.']);
112 }
113 if (is_array($theValArr['imgMap.']['titleText.'])) {
114 $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
115 $cObj->start($cObjData, 'pages');
116 if (isset($theValArr['imgMap.']['titleText.'])) {
117 $theValArr['imgMap.']['titleText'] = $cObj->stdWrap($theValArr['imgMap.']['titleText'], $theValArr['imgMap.']['titleText.']);
118 }
119 unset($theValArr['imgMap.']['titleText.']);
120 }
121 }
122 // This code goes one level in if the object is an image. If 'file' and/or 'mask' appears to be GIFBUILDER-objects, they are both searched for TEXT objects, and if a textobj is found, it's checked with the currently loaded record!!
123 if ($theValue === 'IMAGE') {
124 if ($theValArr['file'] === 'GIFBUILDER') {
125 $temp_sKeyArray = ArrayUtility::filterAndSortByNumericKeys($theValArr['file.']);
126 foreach ($temp_sKeyArray as $temp_theKey) {
127 if ($theValArr['mask.'][$temp_theKey] === 'TEXT') {
128 $gifCreator->data = $this->menuArr[$key] ?: [];
129 $theValArr['mask.'][$temp_theKey . '.'] = $gifCreator->checkTextObj($theValArr['mask.'][$temp_theKey . '.']);
130 // If this is not done it seems that imageMaps will be rendered wrong!!
131 unset($theValArr['mask.'][$temp_theKey . '.']['text.']);
132 }
133 }
134 }
135 if ($theValArr['mask'] === 'GIFBUILDER') {
136 $temp_sKeyArray = ArrayUtility::filterAndSortByNumericKeys($theValArr['mask.']);
137 foreach ($temp_sKeyArray as $temp_theKey) {
138 if ($theValArr['mask.'][$temp_theKey] === 'TEXT') {
139 $gifCreator->data = $this->menuArr[$key] ?: [];
140 $theValArr['mask.'][$temp_theKey . '.'] = $gifCreator->checkTextObj($theValArr['mask.'][$temp_theKey . '.']);
141 // if this is not done it seems that imageMaps will be rendered wrong!!
142 unset($theValArr['mask.'][$temp_theKey . '.']['text.']);
143 }
144 }
145 }
146 }
147 // Checks if disabled is set...
148 $setObjFlag = 1;
149 if ($theValArr['if.']) {
150 /** @var ContentObjectRenderer $cObj */
151 $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
152 $cObj->start($cObjData, 'pages');
153 if (!empty($theValArr['if.']) && !$cObj->checkIf($theValArr['if.'])) {
154 $setObjFlag = 0;
155 }
156 unset($theValArr['if.']);
157 }
158 // Set the object!
159 if ($setObjFlag) {
160 $conf[$gifObjCount] = $theValue;
161 $conf[$gifObjCount . '.'] = $theValArr;
162 }
163 }
164 }
165 }
166 }
167 $gifCreator->start($conf, $this->getTypoScriptFrontendController()->page);
168 // calculations
169 $dConf = [];
170 foreach ($waArr as $key => $val) {
171 if ($dConf[$key] = $itemsConf[$key]['distrib']) {
172 $textBB = $gifCreator->objBB[$val['textNum']];
173 $dConf[$key] = str_replace(
174 ['textX', 'textY'],
175 [$textBB[0], $textBB[1]],
176 $dConf[$key]
177 );
178 $dConf[$key] = GeneralUtility::intExplode(',', $gifCreator->calcOffset($dConf[$key]));
179 }
180 }
181 $workArea = GeneralUtility::intExplode(',', $gifCreator->calcOffset($this->mconf['dWorkArea']));
182 foreach ($waArr as $key => $val) {
183 $index = $val['free'];
184 $gifCreator->setup[$index] = 'WORKAREA';
185 $workArea[2] = $dConf[$key][2] ?: $dConf[$key][0];
186 $workArea[3] = $dConf[$key][3] ?: $dConf[$key][1];
187 $gifCreator->setup[$index . '.']['set'] = implode(',', $workArea);
188 $workArea[0] += $dConf[$key][0];
189 $workArea[1] += $dConf[$key][1];
190 }
191 if ($this->mconf['debugRenumberedObject']) {
192 echo '<h3>Renumbered GIFBUILDER object:</h3>';
193 debug($gifCreator->setup);
194 }
195 GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/assets/menu/');
196 $gifFileName = $gifCreator->fileName('assets/menu/');
197 // Gets the ImageMap from the cache...
198 $cache = $this->getCache();
199 $imgHash = md5($gifFileName);
200 $imgMap = $cache->get($imgHash);
201 // File exists
202 if ($imgMap && file_exists($gifFileName)) {
203 $imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $gifFileName);
204 $w = $imageInfo->getWidth();
205 $h = $imageInfo->getHeight();
206 } else {
207 // file is generated
208 $gifCreator->make();
209 $w = $gifCreator->w;
210 $h = $gifCreator->h;
211 $gifCreator->output($gifFileName);
212 $gifCreator->destroy();
213 $imgMap = $gifCreator->map;
214 $cache->set($imgHash, $imgMap, ['ident_MENUIMAGEMAP'], 0);
215 }
216 $imgMap .= $this->mconf['imgMapExtras'];
217 $this->result = ['output_file' => $gifFileName, 'output_w' => $w, 'output_h' => $h, 'imgMap' => $imgMap];
218 }
219 }
220 }
221
222 /**
223 * Returns the HTML for the image map menu.
224 * If ->result is TRUE it will create the HTML for the image map menu.
225 *
226 * @return string The HTML for the menu
227 */
228 public function writeMenu()
229 {
230 if ($this->result) {
231 $res = $this->result;
232 // shortMD5 260900
233 $menuName = 'menu_' . GeneralUtility::shortMD5($res['imgMap']);
234 $result = '<img src="' . $this->getTypoScriptFrontendController()->absRefPrefix . $res['output_file'] . '" width="' . $res['output_w'] . '" height="' . $res['output_h'] . '" usemap="#' . $menuName . '" border="0" ' . $this->mconf['params'];
235 // Adding alt attribute if not set.
236 if (!strstr($result, 'alt="')) {
237 $result .= ' alt="Menu Image Map"';
238 }
239 $result .= ' /><map name="' . $menuName . '" id="' . $menuName . '">' . $res['imgMap'] . '</map>';
240 $this->getTypoScriptFrontendController()->imagesOnPage[] = $res['output_file'];
241 return $this->WMcObj->wrap($result, $this->mconf['wrap']);
242 }
243 return '';
244 }
245 }