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