[BUGFIX] ColorPicker: Use absolute web path for exampleImg
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / Wizard / ColorpickerController.php
1 <?php
2 namespace TYPO3\CMS\Backend\Controller\Wizard;
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 Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Backend\Template\DocumentTemplate;
20 use TYPO3\CMS\Backend\Utility\BackendUtility;
21 use TYPO3\CMS\Core\Page\PageRenderer;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23 use TYPO3\CMS\Core\Utility\PathUtility;
24
25 /**
26 * Script Class for colorpicker wizard
27 */
28 class ColorpickerController extends AbstractWizardController
29 {
30 /**
31 * Wizard parameters, coming from FormEngine linking to the wizard.
32 *
33 * @var array
34 */
35 public $wizardParameters;
36
37 /**
38 * Value of the current color picked.
39 *
40 * @var string
41 */
42 public $colorValue;
43
44 /**
45 * Serialized functions for changing the field...
46 * Necessary to call when the value is transferred to the FormEngine since the form might
47 * need to do internal processing. Otherwise the value is simply not be saved.
48 *
49 * @var string
50 */
51 public $fieldChangeFunc;
52
53 /**
54 * @var string
55 */
56 protected $fieldChangeFuncHash;
57
58 /**
59 * Form name (from opener script)
60 *
61 * @var string
62 */
63 public $fieldName;
64
65 /**
66 * Field name (from opener script)
67 *
68 * @var string
69 */
70 public $formName;
71
72 /**
73 * ID of element in opener script for which to set color.
74 *
75 * @var string
76 */
77 public $md5ID;
78
79 /**
80 * Internal: If FALSE, a frameset is rendered, if TRUE the content of the picker script.
81 *
82 * @var int
83 */
84 public $showPicker;
85
86 /**
87 * @var string
88 */
89 public $HTMLcolorList = 'aqua,black,blue,fuchsia,gray,green,lime,maroon,navy,olive,purple,red,silver,teal,yellow,white';
90
91 /**
92 * @var string
93 */
94 public $pickerImage = '';
95
96 /**
97 * Error message if image not found.
98 *
99 * @var string
100 */
101 public $imageError = '';
102
103 /**
104 * Document template object
105 *
106 * @var DocumentTemplate
107 */
108 public $doc;
109
110 /**
111 * @var string
112 */
113 public $content;
114
115 /**
116 * @var string
117 */
118 protected $exampleImg;
119
120 /**
121 * Constructor
122 */
123 public function __construct()
124 {
125 parent::__construct();
126 $this->getLanguageService()->includeLLFile('EXT:lang/locallang_wizards.xlf');
127 $GLOBALS['SOBE'] = $this;
128
129 $this->init();
130 }
131
132 /**
133 * Initialises the Class
134 *
135 * @return void
136 */
137 protected function init()
138 {
139 // Setting GET vars (used in frameset script):
140 $this->wizardParameters = GeneralUtility::_GP('P');
141 // Setting GET vars (used in colorpicker script):
142 $this->colorValue = GeneralUtility::_GP('colorValue');
143 $this->fieldChangeFunc = GeneralUtility::_GP('fieldChangeFunc');
144 $this->fieldChangeFuncHash = GeneralUtility::_GP('fieldChangeFuncHash');
145 $this->fieldName = GeneralUtility::_GP('fieldName');
146 $this->formName = GeneralUtility::_GP('formName');
147 $this->md5ID = GeneralUtility::_GP('md5ID');
148 $this->exampleImg = GeneralUtility::_GP('exampleImg');
149 // Resolving image (checking existence etc.)
150 $this->imageError = '';
151 if ($this->exampleImg) {
152 $this->pickerImage = GeneralUtility::getFileAbsFileName($this->exampleImg);
153 if (!$this->pickerImage || !@is_file($this->pickerImage)) {
154 $this->imageError = 'ERROR: The image "' . $this->exampleImg . '" could not be found!';
155 }
156 }
157 $update = array();
158 if ($this->areFieldChangeFunctionsValid()) {
159 // Setting field-change functions:
160 $fieldChangeFuncArr = unserialize($this->fieldChangeFunc);
161 unset($fieldChangeFuncArr['alert']);
162 foreach ($fieldChangeFuncArr as $v) {
163 $update[] = 'parent.opener.' . $v;
164 }
165 }
166 // Initialize document object:
167 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
168 $this->getPageRenderer()->loadRequireJsModule(
169 'TYPO3/CMS/Backend/Wizard/Colorpicker',
170 'function(Colorpicker) {
171 Colorpicker.setFieldChangeFunctions({
172 fieldChangeFunctions: function() {'
173 . implode('', $update) .
174 '}
175 });
176 }'
177 );
178 // Start page:
179 $this->content .= $this->doc->startPage($this->getLanguageService()->getLL('colorpicker_title'));
180 }
181
182 /**
183 * Injects the request object for the current request or subrequest
184 * As this controller goes only through the main() method, it is rather simple for now
185 *
186 * @param ServerRequestInterface $request
187 * @param ResponseInterface $response
188 * @return ResponseInterface
189 */
190 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
191 {
192 $this->main();
193
194 $this->content .= $this->doc->endPage();
195 $this->content = $this->doc->insertStylesAndJS($this->content);
196
197 $response->getBody()->write($this->content);
198 return $response;
199 }
200
201 /**
202 * Main Method, rendering either colorpicker or frameset depending on ->showPicker
203 *
204 * @return void
205 */
206 public function main()
207 {
208 // Show frameset by default:
209 if (!GeneralUtility::_GP('showPicker')) {
210 $this->frameSet();
211 } else {
212 // Putting together the items into a form:
213 $content = '
214 <form name="colorform" method="post" action="' . htmlspecialchars(BackendUtility::getModuleUrl('wizard_colorpicker')) . '">
215 ' . $this->colorMatrix() . '
216 ' . $this->colorList() . '
217 ' . $this->colorImage() . '
218
219 <!-- Value box: -->
220 <p class="c-head">' . $this->getLanguageService()->getLL('colorpicker_colorValue', true) . '</p>
221 <table border="0" cellpadding="0" cellspacing="3">
222 <tr>
223 <td>
224 <input id="colorValue" type="text" ' . $this->doc->formWidth(7) . ' maxlength="10" name="colorValue" value="' . htmlspecialchars($this->colorValue) . '" />
225 </td>
226 <td style="background-color:' . htmlspecialchars($this->colorValue) . '; border: 1px solid black;">
227 <span style="color: black;">' . $this->getLanguageService()->getLL('colorpicker_black', true) . '</span>&nbsp;<span style="color: white;">' . $this->getLanguageService()->getLL('colorpicker_white', true) . '</span>
228 </td>
229 <td>
230 <input class="btn btn-default" type="submit" id="colorpicker-saveclose" value="' . $this->getLanguageService()->getLL('colorpicker_setClose', true) . '" />
231 </td>
232 </tr>
233 </table>
234
235 <!-- Hidden fields with values that has to be kept constant -->
236 <input type="hidden" name="showPicker" value="1" />
237 <input type="hidden" name="fieldChangeFunc" value="' . htmlspecialchars($this->fieldChangeFunc) . '" />
238 <input type="hidden" name="fieldChangeFuncHash" value="' . htmlspecialchars($this->fieldChangeFuncHash) . '" />
239 <input type="hidden" name="fieldName" value="' . htmlspecialchars($this->fieldName) . '" />
240 <input type="hidden" name="formName" value="' . htmlspecialchars($this->formName) . '" />
241 <input type="hidden" name="md5ID" value="' . htmlspecialchars($this->md5ID) . '" />
242 <input type="hidden" name="exampleImg" value="' . htmlspecialchars($this->exampleImg) . '" />
243 </form>';
244
245 $this->content .= '<h2>' . $this->getLanguageService()->getLL('colorpicker_title', true) . '</h2>';
246 $this->content .= $content;
247 }
248 }
249
250 /**
251 * Returns the sourcecode to the browser
252 *
253 * @return void
254 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use mainAction() instead
255 */
256 public function printContent()
257 {
258 GeneralUtility::logDeprecatedFunction();
259 $this->content .= $this->doc->endPage();
260 $this->content = $this->doc->insertStylesAndJS($this->content);
261 echo $this->content;
262 }
263
264 /**
265 * Returns a frameset so our JavaScript Reference isn't lost
266 * Took some brains to figure this one out ;-)
267 * If Peter wouldn't have been I would've gone insane...
268 *
269 * @return void
270 */
271 public function frameSet()
272 {
273 $this->getDocumentTemplate()->JScode = $this->getDocumentTemplate()->wrapScriptTags('
274 if (!window.opener) {
275 alert("ERROR: Sorry, no link to main window... Closing");
276 close();
277 }
278 ');
279 $this->getDocumentTemplate()->startPage($this->getLanguageService()->getLL('colorpicker_title'));
280
281 // URL for the inner main frame:
282 $url = BackendUtility::getModuleUrl(
283 'wizard_colorpicker',
284 array(
285 'showPicker' => 1,
286 'colorValue' => $this->wizardParameters['currentValue'],
287 'fieldName' => $this->wizardParameters['itemName'],
288 'formName' => $this->wizardParameters['formName'],
289 'exampleImg' => $this->wizardParameters['exampleImg'],
290 'md5ID' => $this->wizardParameters['md5ID'],
291 'fieldChangeFunc' => serialize($this->wizardParameters['fieldChangeFunc']),
292 'fieldChangeFuncHash' => $this->wizardParameters['fieldChangeFuncHash'],
293 )
294 );
295 $this->content = $this->getPageRenderer()->render(PageRenderer::PART_HEADER) . '
296 <frameset rows="*,1" framespacing="0" frameborder="0" border="0">
297 <frame name="content" src="' . htmlspecialchars($url) . '" marginwidth="0" marginheight="0" frameborder="0" scrolling="auto" noresize="noresize" />
298 <frame name="menu" src="' . htmlspecialchars(BackendUtility::getModuleUrl('dummy')) . '" marginwidth="0" marginheight="0" frameborder="0" scrolling="no" noresize="noresize" />
299 </frameset>
300 </html>';
301 }
302
303 /************************************
304 *
305 * Rendering of various color selectors
306 *
307 ************************************/
308 /**
309 * Creates a color matrix table
310 *
311 * @return string
312 */
313 public function colorMatrix()
314 {
315 $steps = 51;
316 // Get colors:
317 $color = array();
318 for ($rr = 0; $rr < 256; $rr += $steps) {
319 for ($gg = 0; $gg < 256; $gg += $steps) {
320 for ($bb = 0; $bb < 256; $bb += $steps) {
321 $color[] = '#' . substr(('0' . dechex($rr)), -2) . substr(('0' . dechex($gg)), -2) . substr(('0' . dechex($bb)), -2);
322 }
323 }
324 }
325 // Traverse colors:
326 $columns = 24;
327 $rows = 0;
328 $tRows = array();
329 while (isset($color[$columns * $rows])) {
330 $tCells = array();
331 for ($i = 0; $i < $columns; $i++) {
332 $tCells[] = '<td bgcolor="' . $color[$columns * $rows + $i] . '" class="t3js-colorpicker-value" data-color-value="' . htmlspecialchars($color[($columns * $rows + $i)]) . '" title="' . htmlspecialchars($color[($columns * $rows + $i)]) . '">&nbsp;&nbsp;</td>';
333 }
334 $tRows[] = '<tr>' . implode('', $tCells) . '</tr>';
335 $rows++;
336 }
337 return '<p class="c-head">' . $this->getLanguageService()->getLL('colorpicker_fromMatrix', true) . '</p>
338 <table style="width:100%; border: 1px solid black; cursor:crosshair;">' . implode('', $tRows) . '</table>';
339 }
340
341 /**
342 * Creates a selector box with all HTML color names.
343 *
344 * @return string
345 */
346 public function colorList()
347 {
348 // Initialize variables:
349 $colors = explode(',', $this->HTMLcolorList);
350 $currentValue = strtolower($this->colorValue);
351 $opt = array();
352 $opt[] = '<option value=""></option>';
353 // Traverse colors, making option tags for selector box.
354 foreach ($colors as $colorName) {
355 $opt[] = '<option style="background-color: ' . $colorName . ';" value="' . htmlspecialchars($colorName) . '"' . ($currentValue === $colorName ? ' selected="selected"' : '') . '>' . htmlspecialchars($colorName) . '</option>';
356 }
357 // Compile selector box and return result:
358 return '<p class="c-head">' . $this->getLanguageService()->getLL('colorpicker_fromList', true) . '</p>
359 <select class="t3js-colorpicker-selector">' . implode(LF, $opt) . '</select><br />';
360 }
361
362 /**
363 * Creates a color image selector
364 *
365 * @return string
366 */
367 public function colorImage()
368 {
369 // Handling color-picker image if any:
370 if (!$this->imageError) {
371 if ($this->pickerImage) {
372 if (GeneralUtility::_POST('coords_x')) {
373 /** @var $image \TYPO3\CMS\Core\Imaging\GraphicalFunctions */
374 $image = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\GraphicalFunctions::class);
375 $this->colorValue = '#' . $this->getIndex($image->imageCreateFromFile($this->pickerImage), GeneralUtility::_POST('coords_x'), GeneralUtility::_POST('coords_y'));
376 }
377 $pickerFormImage = '
378 <p class="c-head">' . $this->getLanguageService()->getLL('colorpicker_fromImage', true) . '</p>
379 <input type="image" src="' . PathUtility::getAbsoluteWebPath($this->pickerImage) . '" name="coords" style="cursor:crosshair;" /><br />';
380 } else {
381 $pickerFormImage = '';
382 }
383 } else {
384 $pickerFormImage = '
385 <p class="c-head">' . htmlspecialchars($this->imageError) . '</p>';
386 }
387 return $pickerFormImage;
388 }
389
390 /**
391 * Gets the HTML (Hex) Color Code for the selected pixel of an image
392 * This method handles the correct imageResource no matter what format
393 *
394 * @param resource $im Valid ImageResource returned by \TYPO3\CMS\Core\Imaging\GraphicalFunctions::imageCreateFromFile
395 * @param int $x X-Coordinate of the pixel that should be checked
396 * @param int $y Y-Coordinate of the pixel that should be checked
397 * @return string HEX RGB value for color
398 * @see colorImage()
399 */
400 public function getIndex($im, $x, $y)
401 {
402 $rgb = ImageColorAt($im, $x, $y);
403 $colorRgb = imagecolorsforindex($im, $rgb);
404 $index['r'] = dechex($colorRgb['red']);
405 $index['g'] = dechex($colorRgb['green']);
406 $index['b'] = dechex($colorRgb['blue']);
407 $hexValue = array();
408 foreach ($index as $value) {
409 if (strlen($value) === 1) {
410 $hexValue[] = strtoupper('0' . $value);
411 } else {
412 $hexValue[] = strtoupper($value);
413 }
414 }
415 $hex = implode('', $hexValue);
416 return $hex;
417 }
418
419 /**
420 * Determines whether submitted field change functions are valid
421 * and are coming from the system and not from an external abuse.
422 *
423 * @return bool Whether the submitted field change functions are valid
424 */
425 protected function areFieldChangeFunctionsValid()
426 {
427 return $this->fieldChangeFunc && $this->fieldChangeFuncHash && $this->fieldChangeFuncHash === GeneralUtility::hmac($this->fieldChangeFunc);
428 }
429
430 /**
431 * @return PageRenderer
432 */
433 protected function getPageRenderer()
434 {
435 return GeneralUtility::makeInstance(PageRenderer::class);
436 }
437 }