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