[BUGFIX] Empty class selector crashes Update image
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / Classes / SelectImage.php
1 <?php
2 namespace TYPO3\CMS\Rtehtmlarea;
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\Utility\GeneralUtility;
18 use TYPO3\CMS\Core\Resource;
19
20 /**
21 * Script Class
22 *
23 * @author Kasper Skårhøj <kasper@typo3.com>
24 */
25 class SelectImage extends \TYPO3\CMS\Recordlist\Browser\ElementBrowser {
26
27 /**
28 * These file extensions are allowed in the "plain" image selection mode.
29 *
30 * @const
31 */
32 const PLAIN_MODE_IMAGE_FILE_EXTENSIONS = 'jpg,jpeg,gif,png';
33
34 /**
35 * @todo Define visibility
36 */
37 public $extKey = 'rtehtmlarea';
38
39 /**
40 * @todo Define visibility
41 */
42 public $content;
43
44 public $allowedItems;
45
46 public $allowedFileTypes = array();
47
48 protected $defaultClass;
49
50 /**
51 * Relevant for RTE mode "plain": the maximum width an image must have to be selectable.
52 *
53 * @var int
54 */
55 protected $plainMaxWidth;
56
57 /**
58 * Relevant for RTE mode "plain": the maximum height an image must have to be selectable.
59 *
60 * @var int
61 */
62 protected $plainMaxHeight;
63
64 protected $imgPath;
65
66 public $editorNo;
67
68 public $sys_language_content;
69
70 public $thisConfig;
71
72 public $buttonConfig;
73
74 protected $imgObj;
75
76 /**
77 * Initialisation
78 *
79 * @return void
80 */
81 public function init() {
82 $this->initVariables();
83 $this->initConfiguration();
84 $this->initHookObjects('ext/rtehtmlarea/mod4/class.tx_rtehtmlarea_select_image.php');
85
86 $this->allowedItems = $this->getAllowedItems('magic,plain,image');
87 $this->insertImage();
88
89 $this->initDocumentTemplate();
90 }
91
92 /**
93 * Initialize class variables
94 *
95 * @return void
96 */
97 public function initVariables() {
98 parent::initVariables();
99 // Get "act"
100 $this->act = GeneralUtility::_GP('act');
101 if (!$this->act) {
102 $this->act = FALSE;
103 }
104 // Process bparams
105 $pArr = explode('|', $this->bparams);
106 $pRteArr = explode(':', $pArr[1]);
107 $this->editorNo = $pRteArr[0];
108 $this->sys_language_content = $pRteArr[1];
109 $this->RTEtsConfigParams = $pArr[2];
110 if (!$this->editorNo) {
111 $this->editorNo = GeneralUtility::_GP('editorNo');
112 $this->sys_language_content = GeneralUtility::_GP('sys_language_content');
113 $this->RTEtsConfigParams = GeneralUtility::_GP('RTEtsConfigParams');
114 }
115 $pArr[1] = implode(':', array($this->editorNo, $this->sys_language_content));
116 $pArr[2] = $this->RTEtsConfigParams;
117 if ($this->act == 'dragdrop' || $this->act == 'plain') {
118 $this->allowedFileTypes = explode(',', self::PLAIN_MODE_IMAGE_FILE_EXTENSIONS);
119 }
120 $pArr[3] = implode(',', $this->allowedFileTypes);
121 $this->bparams = implode('|', $pArr);
122 }
123
124 /**
125 * Initialize document template object
126 *
127 * @return void
128 */
129 protected function initDocumentTemplate() {
130 parent::initDocumentTemplate();
131
132 $this->doc->bodyTagId = 'typo3-browse-links-php';
133 $this->doc->bodyTagAdditions = $this->getBodyTagAdditions();
134
135 $this->doc->JScode .= $this->doc->wrapScriptTags('
136 Tree.ajaxID = "SC_alt_file_navframe::expandCollapse";
137 ');
138 $this->doc->getPageRenderer()->addCssFile($this->doc->backPath . \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('t3skin') . 'rtehtmlarea/htmlarea.css');
139 $this->doc->getContextMenuCode();
140 }
141
142 /**
143 * Provide the additional parameters to be included in the template body tag
144 *
145 * @return string the body tag additions
146 */
147 public function getBodyTagAdditions() {
148 return 'onload="initEventListeners();"';
149 }
150
151 /**
152 * Get the path to the folder where RTE images are stored
153 *
154 * @return string the path to the folder where RTE images are stored
155 * @deprecated since 6.2, will be removed in two versions
156 */
157 protected function getRTEImageStorageDir() {
158 GeneralUtility::logDeprecatedFunction();
159 return $this->imgPath ?: $GLOBALS['TYPO3_CONF_VARS']['BE']['RTE_imageStorageDir'];
160 }
161
162 /**
163 * Insert the image in the editing area
164 *
165 * @return void
166 */
167 protected function insertImage() {
168 $table = htmlspecialchars(GeneralUtility::_GP('table'));
169 $uid = (int) GeneralUtility::_GP('uid');
170 if (GeneralUtility::_GP('insertImage') && $uid) {
171 /** @var $fileObject Resource\File */
172 $fileObject = Resource\ResourceFactory::getInstance()->getFileObject($uid);
173 // Get default values for alt and title attributes from file properties
174 $altText = $fileObject->getProperty('alternative');
175 $titleText = $fileObject->getProperty('title');
176 switch ($this->act) {
177 case 'magic':
178 $this->insertMagicImage($fileObject, $altText, $titleText, 'data-htmlarea-file-uid="' . $uid . '" data-htmlarea-file-table="' . $table . '"');
179 die;
180 break;
181 case 'plain':
182 $this->insertPlainImage($fileObject, $altText, $titleText, 'data-htmlarea-file-uid="' . $uid . '" data-htmlarea-file-table="' . $table . '"');
183 die;
184 break;
185 default:
186 // Call hook
187 foreach ($this->hookObjects as $hookObject) {
188 if (method_exists($hookObject, 'insertElement')) {
189 $hookObject->insertElement($this->act);
190 }
191 }
192 }
193 }
194 }
195
196 /**
197 * Insert a magic image
198 *
199 * @param Resource\File $fileObject: the image file
200 * @param string $altText: text for the alt attribute of the image
201 * @param string $titleText: text for the title attribute of the image
202 * @param string $additionalParams: text representing more HTML attributes to be added on the img tag
203 * @return void
204 */
205 public function insertMagicImage(Resource\File $fileObject, $altText = '', $titleText = '', $additionalParams = '') {
206 // Create the magic image service
207 /** @var $magicImageService Resource\Service\MagicImageService */
208 $magicImageService = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Service\\MagicImageService');
209 $magicImageService->setMagicImageMaximumDimensions($this->thisConfig);
210 // Create the magic image
211 $imageConfiguration = array(
212 'width' => GeneralUtility::_GP('cWidth'),
213 'height' => GeneralUtility::_GP('cHeight')
214 );
215 $magicImage = $magicImageService->createMagicImage($fileObject, $imageConfiguration);
216 $imageUrl = $magicImage->getPublicUrl();
217 // If file is local, make the url absolute
218 if (substr($imageUrl, 0, 4) !== 'http') {
219 $imageUrl = $this->siteURL . $imageUrl;
220 }
221 // Insert the magic image
222 $this->imageInsertJS($imageUrl, $magicImage->getProperty('width'), $magicImage->getProperty('height'), $altText, $titleText, $additionalParams);
223 }
224
225 /**
226 * Insert a plain image
227 *
228 * @param \TYPO3\CMS\Core\Resource\File $fileObject: the image file
229 * @param string $altText: text for the alt attribute of the image
230 * @param string $titleText: text for the title attribute of the image
231 * @param string $additionalParams: text representing more HTML attributes to be added on the img tag
232 * @return void
233 */
234 public function insertPlainImage(Resource\File $fileObject, $altText = '', $titleText = '', $additionalParams = '') {
235 $width = $fileObject->getProperty('width');
236 $height = $fileObject->getProperty('height');
237 if (!$width || !$height) {
238 $filePath = $fileObject->getForLocalProcessing(FALSE);
239 $imageInfo = @getimagesize($filePath);
240 $width = $imageInfo[0];
241 $height = $imageInfo[1];
242 }
243 $imageUrl = $fileObject->getPublicUrl();
244 // If file is local, make the url absolute
245 if (substr($imageUrl, 0, 4) !== 'http') {
246 $imageUrl = $this->siteURL . $imageUrl;
247 }
248 $this->imageInsertJS($imageUrl, $width, $height, $altText, $titleText, $additionalParams);
249 }
250
251 /**
252 * Echo the HTML page and JS that will insert the image
253 *
254 * @param string $url: the url of the image
255 * @param integer $width: the width of the image
256 * @param integer $height: the height of the image
257 * @param string $altText: text for the alt attribute of the image
258 * @param string $titleText: text for the title attribute of the image
259 * @param string $additionalParams: text representing more html attributes to be added on the img tag
260 * @return void
261 */
262 protected function imageInsertJS($url, $width, $height, $altText = '', $titleText = '', $additionalParams = '') {
263 echo '
264 <!DOCTYPE html>
265 <html>
266 <head>
267 <title>Untitled</title>
268 <script type="text/javascript">
269 /*<![CDATA[*/
270 var plugin = window.parent.RTEarea["' . $this->editorNo . '"].editor.getPlugin("TYPO3Image");
271 function insertImage(file,width,height,alt,title,additionalParams) {
272 plugin.insertImage(\'<img src="\'+file+\'" width="\'+parseInt(width)+\'" height="\'+parseInt(height)+\'"\'' . ($this->defaultClass ? '+\' class="' . $this->defaultClass . '"\'' : '') . '+(alt?\' alt="\'+alt+\'"\':\'\')+(title?\' title="\'+title+\'"\':\'\')+(additionalParams?\' \'+additionalParams:\'\')+\' />\');
273 }
274 /*]]>*/
275 </script>
276 </head>
277 <body>
278 <script type="text/javascript">
279 /*<![CDATA[*/
280 insertImage(' . GeneralUtility::quoteJSvalue($url, 1) . ',' . $width . ',' . $height . ',' . GeneralUtility::quoteJSvalue($altText, 1) . ',' . GeneralUtility::quoteJSvalue($titleText, 1) . ',' . GeneralUtility::quoteJSvalue($additionalParams, 1) . ');
281 /*]]>*/
282 </script>
283 </body>
284 </html>';
285 }
286
287 /**
288 * Generate JS code to be used on the image insert/modify dialogue
289 *
290 * @param string $act: the action to be performed
291 * @param string $editorNo: the number of the RTE instance on the page
292 * @param string $sys_language_content: the language of the content element
293 * @return string the generated JS code
294 * @todo Define visibility
295 */
296 public function getJSCode($act, $editorNo, $sys_language_content) {
297 $removedProperties = array();
298 if (is_array($this->buttonConfig['properties.'])) {
299 if ($this->buttonConfig['properties.']['removeItems']) {
300 $removedProperties = GeneralUtility::trimExplode(',', $this->buttonConfig['properties.']['removeItems'], TRUE);
301 }
302 }
303 if ($this->buttonConfig['properties.']['class.']['allowedClasses']) {
304 $classesImageArray = GeneralUtility::trimExplode(',', $this->buttonConfig['properties.']['class.']['allowedClasses'], TRUE);
305 $classesImageJSOptions = '<option value=""></option>';
306 foreach ($classesImageArray as $class) {
307 $classesImageJSOptions .= '<option value="' . $class . '">' . $class . '</option>';
308 }
309 }
310 $lockPlainWidth = 'false';
311 $lockPlainHeight = 'false';
312 if (is_array($this->thisConfig['proc.']) && $this->thisConfig['proc.']['plainImageMode']) {
313 $plainImageMode = $this->thisConfig['proc.']['plainImageMode'];
314 $lockPlainWidth = $plainImageMode == 'lockDimensions' ? 'true' : 'false';
315 $lockPlainHeight = $lockPlainWidth || $plainImageMode == 'lockRatio' || $plainImageMode == 'lockRatioWhenSmaller' ? 'true' : 'false';
316 }
317 $JScode = '
318 var plugin = window.parent.RTEarea["' . $editorNo . '"].editor.getPlugin("TYPO3Image");
319 var HTMLArea = window.parent.HTMLArea;
320
321 HTMLArea.TYPO3Image.insertElement = function (table, uid, type, filename, filePath, fileExt, fileIcon) {
322 return jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript() . 'editorNo=') . ' + \'' . $editorNo . '\' + \'&insertImage=\' + filePath + \'&table=\' + table + \'&uid=\' + uid + \'&type=\' + type + \'bparams=\' + \'' . $this->bparams . '\');
323 }
324 function insertElement(table, uid, type, fileName, filePath, fileExt, fileIcon, action, close) {
325 return jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript() . 'editorNo=') . ' + \'' . $editorNo . '\' + \'&insertImage=\' + filePath + \'&table=\' + table + \'&uid=\' + uid + \'&type=\' + type + \'bparams=\' + \'' . $this->bparams . '\');
326 }
327 function initEventListeners() {
328 if (Ext.isWebKit) {
329 Ext.EventManager.addListener(window.document.body, "dragend", plugin.onDrop, plugin, { single: true });
330 }
331 }
332 function jumpToUrl(URL,anchor) {
333 var add_act = URL.indexOf("act=")==-1 ? "&act=' . $act . '" : "";
334 var add_editorNo = URL.indexOf("editorNo=")==-1 ? "&editorNo=' . $editorNo . '" : "";
335 var add_sys_language_content = URL.indexOf("sys_language_content=")==-1 ? "&sys_language_content=' . $sys_language_content . '" : "";
336 var RTEtsConfigParams = "&RTEtsConfigParams=' . rawurlencode($this->RTEtsConfigParams) . '";
337
338 var cur_width = selectedImageRef ? "&cWidth="+selectedImageRef.style.width : "";
339 var cur_height = selectedImageRef ? "&cHeight="+selectedImageRef.style.height : "";
340
341 var theLocation = URL+add_act+add_editorNo+add_sys_language_content+RTEtsConfigParams+cur_width+cur_height+(typeof(anchor)=="string"?anchor:"");
342 window.location.href = theLocation;
343 return false;
344 }
345 function insertImage(file,width,height) {
346 plugin.insertImage(\'<img src="\'+file+\'"' . ($this->defaultClass ? ' class="' . $this->defaultClass . '"' : '') . ' width="\'+parseInt(width)+\'" height="\'+parseInt(height)+\'" />\');
347 }
348 function launchView(url) {
349 var thePreviewWindow="";
350 thePreviewWindow = window.open("' . GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir . 'show_item.php?table="+url,"ShowItem","height=300,width=410,status=0,menubar=0,resizable=0,location=0,directories=0,scrollbars=1,toolbar=0");
351 if (thePreviewWindow && thePreviewWindow.focus) {
352 thePreviewWindow.focus();
353 }
354 }
355 function getCurrentImageRef() {
356 if (plugin.image) {
357 return plugin.image;
358 } else {
359 return null;
360 }
361 }
362 function printCurrentImageOptions() {
363 var classesImage = ' . ($this->buttonConfig['properties.']['class.']['allowedClasses'] || $this->thisConfig['classesImage'] ? 'true' : 'false') . ';
364 if (classesImage) var styleSelector=\'<select id="iClass" name="iClass" style="width:140px;">' . $classesImageJSOptions . '</select>\';
365 var floatSelector=\'<select id="iFloat" name="iFloat"><option value="">' . $GLOBALS['LANG']->getLL('notSet') . '</option><option value="none">' . $GLOBALS['LANG']->getLL('nonFloating') . '</option><option value="left">' . $GLOBALS['LANG']->getLL('left') . '</option><option value="right">' . $GLOBALS['LANG']->getLL('right') . '</option></select>\';
366 if (plugin.getButton("Language")) {
367 var languageSelector = \'<select id="iLang" name="iLang">\';
368 plugin.getButton("Language").getStore().each(function (record) {
369 languageSelector +=\'<option value="\' + record.get("value") + \'">\' + record.get("text") + \'</option>\';
370 });
371 languageSelector += \'</select>\';
372 }
373 var sz="";
374 sz+=\'<form action="" name="imageData"><table class="htmlarea-window-table">\';
375 ' . (in_array('class', $removedProperties) ? '' : '
376 if(classesImage) {
377 sz+=\'<tr><td><label for="iClass">' . $GLOBALS['LANG']->getLL('class') . ': </label></td><td>\'+styleSelector+\'</td></tr>\';
378 }') . (in_array('width', $removedProperties) ? '' : '
379 if (!(selectedImageRef && selectedImageRef.src.indexOf("RTEmagic") == -1 && ' . $lockPlainWidth . ')) {
380 sz+=\'<tr><td><label for="iWidth">' . $GLOBALS['LANG']->getLL('width') . ': </label></td><td><input type="text" id="iWidth" name="iWidth" value=""' . $GLOBALS['TBE_TEMPLATE']->formWidth(4) . ' /></td></tr>\';
381 }') . (in_array('height', $removedProperties) ? '' : '
382 if (!(selectedImageRef && selectedImageRef.src.indexOf("RTEmagic") == -1 && ' . $lockPlainHeight . ')) {
383 sz+=\'<tr><td><label for="iHeight">' . $GLOBALS['LANG']->getLL('height') . ': </label></td><td><input type="text" id="iHeight" name="iHeight" value=""' . $GLOBALS['TBE_TEMPLATE']->formWidth(4) . ' /></td></tr>\';
384 }') . (in_array('border', $removedProperties) ? '' : '
385 sz+=\'<tr><td><label for="iBorder">' . $GLOBALS['LANG']->getLL('border') . ': </label></td><td><input type="checkbox" id="iBorder" name="iBorder" value="1" /></td></tr>\';') . (in_array('float', $removedProperties) ? '' : '
386 sz+=\'<tr><td><label for="iFloat">' . $GLOBALS['LANG']->getLL('float') . ': </label></td><td>\'+floatSelector+\'</td></tr>\';') . (in_array('paddingTop', $removedProperties) ? '' : '
387 sz+=\'<tr><td><label for="iPaddingTop">' . $GLOBALS['LANG']->getLL('padding_top') . ': </label></td><td><input type="text" id="iPaddingTop" name="iPaddingTop" value=""' . $GLOBALS['TBE_TEMPLATE']->formWidth(4) . '></td></tr>\';') . (in_array('paddingRight', $removedProperties) ? '' : '
388 sz+=\'<tr><td><label for="iPaddingRight">' . $GLOBALS['LANG']->getLL('padding_right') . ': </label></td><td><input type="text" id="iPaddingRight" name="iPaddingRight" value=""' . $GLOBALS['TBE_TEMPLATE']->formWidth(4) . ' /></td></tr>\';') . (in_array('paddingBottom', $removedProperties) ? '' : '
389 sz+=\'<tr><td><label for="iPaddingBottom">' . $GLOBALS['LANG']->getLL('padding_bottom') . ': </label></td><td><input type="text" id="iPaddingBottom" name="iPaddingBottom" value=""' . $GLOBALS['TBE_TEMPLATE']->formWidth(4) . ' /></td></tr>\';') . (in_array('paddingLeft', $removedProperties) ? '' : '
390 sz+=\'<tr><td><label for="iPaddingLeft">' . $GLOBALS['LANG']->getLL('padding_left') . ': </label></td><td><input type="text" id="iPaddingLeft" name="iPaddingLeft" value=""' . $GLOBALS['TBE_TEMPLATE']->formWidth(4) . ' /></td></tr>\';') . (in_array('title', $removedProperties) ? '' : '
391 sz+=\'<tr><td><label for="iTitle">' . $GLOBALS['LANG']->getLL('title') . ': </label></td><td><input type="text" id="iTitle" name="iTitle"' . $GLOBALS['TBE_TEMPLATE']->formWidth(20) . ' /></td></tr>\';') . (in_array('alt', $removedProperties) ? '' : '
392 sz+=\'<tr><td><label for="iAlt">' . $GLOBALS['LANG']->getLL('alt') . ': </label></td><td><input type="text" id="iAlt" name="iAlt"' . $GLOBALS['TBE_TEMPLATE']->formWidth(20) . ' /></td></tr>\';') . (in_array('lang', $removedProperties) ? '' : '
393 if (plugin.getButton("Language")) {
394 sz+=\'<tr><td><label for="iLang">\' + plugin.editor.getPlugin("Language").localize(\'Language-Tooltip\') + \': </label></td><td>\' + languageSelector + \'</td></tr>\';
395 }') . (in_array('clickenlarge', $removedProperties) || in_array('data-htmlarea-clickenlarge', $removedProperties) ? '' : '
396 sz+=\'<tr><td><label for="iClickEnlarge">' . $GLOBALS['LANG']->sL('LLL:EXT:cms/locallang_ttc.xlf:image_zoom', TRUE) . ' </label></td><td><input type="checkbox" name="iClickEnlarge" id="iClickEnlarge" value="0" /></td></tr>\';') . '
397 sz+=\'<tr><td></td><td><input type="submit" value="' . $GLOBALS['LANG']->getLL('update') . '" onClick="return setImageProperties();"></td></tr>\';
398 sz+=\'</table></form>\';
399 return sz;
400 }
401 function setImageProperties() {
402 var classesImage = ' . ($this->buttonConfig['properties.']['class.']['allowedClasses'] || $this->thisConfig['classesImage'] ? 'true' : 'false') . ';
403 if (selectedImageRef) {
404 if (document.imageData.iWidth) {
405 if (document.imageData.iWidth.value && parseInt(document.imageData.iWidth.value)) {
406 selectedImageRef.style.width = "";
407 selectedImageRef.width = parseInt(document.imageData.iWidth.value);
408 }
409 }
410 if (document.imageData.iHeight) {
411 if (document.imageData.iHeight.value && parseInt(document.imageData.iHeight.value)) {
412 selectedImageRef.style.height = "";
413 selectedImageRef.height = parseInt(document.imageData.iHeight.value);
414 }
415 }
416 if (document.imageData.iPaddingTop) {
417 if (document.imageData.iPaddingTop.value != "" && !isNaN(parseInt(document.imageData.iPaddingTop.value))) {
418 selectedImageRef.style.paddingTop = parseInt(document.imageData.iPaddingTop.value) + "px";
419 } else {
420 selectedImageRef.style.paddingTop = "";
421 }
422 }
423 if (document.imageData.iPaddingRight) {
424 if (document.imageData.iPaddingRight.value != "" && !isNaN(parseInt(document.imageData.iPaddingRight.value))) {
425 selectedImageRef.style.paddingRight = parseInt(document.imageData.iPaddingRight.value) + "px";
426 } else {
427 selectedImageRef.style.paddingRight = "";
428 }
429 }
430 if (document.imageData.iPaddingBottom) {
431 if (document.imageData.iPaddingBottom.value != "" && !isNaN(parseInt(document.imageData.iPaddingBottom.value))) {
432 selectedImageRef.style.paddingBottom = parseInt(document.imageData.iPaddingBottom.value) + "px";
433 } else {
434 selectedImageRef.style.paddingBottom = "";
435 }
436 }
437 if (document.imageData.iPaddingLeft) {
438 if (document.imageData.iPaddingLeft.value != "" && !isNaN(parseInt(document.imageData.iPaddingLeft.value))) {
439 selectedImageRef.style.paddingLeft = parseInt(document.imageData.iPaddingLeft.value) + "px";
440 } else {
441 selectedImageRef.style.paddingLeft = "";
442 }
443 }
444 if (document.imageData.iTitle) {
445 selectedImageRef.title=document.imageData.iTitle.value;
446 }
447 if (document.imageData.iAlt) {
448 selectedImageRef.alt=document.imageData.iAlt.value;
449 }
450 if (document.imageData.iBorder) {
451 selectedImageRef.style.borderStyle = "";
452 selectedImageRef.style.borderWidth = "";
453 selectedImageRef.style.border = ""; // this statement ignored by Mozilla 1.3.1
454 selectedImageRef.style.borderTopStyle = "";
455 selectedImageRef.style.borderRightStyle = "";
456 selectedImageRef.style.borderBottomStyle = "";
457 selectedImageRef.style.borderLeftStyle = "";
458 selectedImageRef.style.borderTopWidth = "";
459 selectedImageRef.style.borderRightWidth = "";
460 selectedImageRef.style.borderBottomWidth = "";
461 selectedImageRef.style.borderLeftWidth = "";
462 if(document.imageData.iBorder.checked) {
463 selectedImageRef.style.borderStyle = "solid";
464 selectedImageRef.style.borderWidth = "thin";
465 }
466 selectedImageRef.removeAttribute("border");
467 }
468 if (document.imageData.iFloat) {
469 var iFloat = document.imageData.iFloat.options[document.imageData.iFloat.selectedIndex].value;
470 if (document.all) {
471 selectedImageRef.style.styleFloat = iFloat ? iFloat : "";
472 } else {
473 selectedImageRef.style.cssFloat = iFloat ? iFloat : "";
474 }
475 }
476 if (classesImage && document.imageData.iClass) {
477 var iClass;
478 if (document.imageData.iClass.options.length > 0) {
479 iClass = document.imageData.iClass.options[document.imageData.iClass.selectedIndex].value;
480 }
481 if (iClass || (selectedImageRef.attributes["class"] && selectedImageRef.attributes["class"].value)) {
482 selectedImageRef.className = iClass;
483 } else {
484 selectedImageRef.className = "";
485 }
486 }
487 if (document.imageData.iLang) {
488 var iLang = document.imageData.iLang.options[document.imageData.iLang.selectedIndex].value;
489 var languageObject = plugin.editor.getPlugin("Language");
490 if (iLang || languageObject.getLanguageAttribute(selectedImageRef)) {
491 languageObject.setLanguageAttributes(selectedImageRef, iLang);
492 } else {
493 languageObject.setLanguageAttributes(selectedImageRef, "none");
494 }
495 }
496 if (document.imageData.iClickEnlarge) {
497 if (document.imageData.iClickEnlarge.checked) {
498 selectedImageRef.setAttribute("data-htmlarea-clickenlarge","1");
499 } else {
500 selectedImageRef.removeAttribute("data-htmlarea-clickenlarge");
501 selectedImageRef.removeAttribute("clickenlarge");
502 }
503 }
504 plugin.close();
505 }
506 return false;
507 }
508 function insertImagePropertiesInForm() {
509 var classesImage = ' . ($this->buttonConfig['properties.']['class.']['allowedClasses'] || $this->thisConfig['classesImage'] ? 'true' : 'false') . ';
510 if (selectedImageRef) {
511 var styleWidth, styleHeight, padding;
512 if (document.imageData.iWidth) {
513 styleWidth = selectedImageRef.style.width ? selectedImageRef.style.width : selectedImageRef.width;
514 styleWidth = parseInt(styleWidth);
515 if (!(isNaN(styleWidth) || styleWidth == 0)) {
516 document.imageData.iWidth.value = styleWidth;
517 }
518 }
519 if (document.imageData.iHeight) {
520 styleHeight = selectedImageRef.style.height ? selectedImageRef.style.height : selectedImageRef.height;
521 styleHeight = parseInt(styleHeight);
522 if (!(isNaN(styleHeight) || styleHeight == 0)) {
523 document.imageData.iHeight.value = styleHeight;
524 }
525 }
526 if (document.imageData.iPaddingTop) {
527 var padding = selectedImageRef.style.paddingTop ? selectedImageRef.style.paddingTop : selectedImageRef.vspace;
528 var padding = parseInt(padding);
529 if (isNaN(padding) || padding <= 0) { padding = ""; }
530 document.imageData.iPaddingTop.value = padding;
531 }
532 if (document.imageData.iPaddingRight) {
533 padding = selectedImageRef.style.paddingRight ? selectedImageRef.style.paddingRight : selectedImageRef.hspace;
534 var padding = parseInt(padding);
535 if (isNaN(padding) || padding <= 0) { padding = ""; }
536 document.imageData.iPaddingRight.value = padding;
537 }
538 if (document.imageData.iPaddingBottom) {
539 var padding = selectedImageRef.style.paddingBottom ? selectedImageRef.style.paddingBottom : selectedImageRef.vspace;
540 var padding = parseInt(padding);
541 if (isNaN(padding) || padding <= 0) { padding = ""; }
542 document.imageData.iPaddingBottom.value = padding;
543 }
544 if (document.imageData.iPaddingLeft) {
545 var padding = selectedImageRef.style.paddingLeft ? selectedImageRef.style.paddingLeft : selectedImageRef.hspace;
546 var padding = parseInt(padding);
547 if (isNaN(padding) || padding <= 0) { padding = ""; }
548 document.imageData.iPaddingLeft.value = padding;
549 }
550 if (document.imageData.iTitle) {
551 document.imageData.iTitle.value = selectedImageRef.title;
552 }
553 if (document.imageData.iAlt) {
554 document.imageData.iAlt.value = selectedImageRef.alt;
555 }
556 if (document.imageData.iBorder) {
557 if((selectedImageRef.style.borderStyle && selectedImageRef.style.borderStyle != "none" && selectedImageRef.style.borderStyle != "none none none none") || selectedImageRef.border) {
558 document.imageData.iBorder.checked = 1;
559 }
560 }
561 if (document.imageData.iFloat) {
562 var fObj=document.imageData.iFloat;
563 var value = (selectedImageRef.style.cssFloat ? selectedImageRef.style.cssFloat : selectedImageRef.style.styleFloat);
564 var l=fObj.length;
565 for (var a=0;a<l;a++) {
566 if (fObj.options[a].value == value) {
567 fObj.selectedIndex = a;
568 }
569 }
570 }
571 if (classesImage && document.imageData.iClass) {
572 var fObj=document.imageData.iClass;
573 var value=selectedImageRef.className;
574 var l=fObj.length;
575 for (var a=0;a < l; a++) {
576 if (fObj.options[a].value == value) {
577 fObj.selectedIndex = a;
578 }
579 }
580 }
581 if (document.imageData.iLang) {
582 var fObj=document.imageData.iLang;
583 var value=plugin.editor.getPlugin("Language").getLanguageAttribute(selectedImageRef);
584 for (var i = 0, n = fObj.length; i < n; i++) {
585 if (fObj.options[i].value == value) {
586 fObj.selectedIndex = i;
587 if (i) {
588 fObj.options[0].text = plugin.editor.getPlugin("Language").localize("Remove language mark");
589 }
590 }
591 }
592 }
593 if (document.imageData.iClickEnlarge) {
594 if (selectedImageRef.getAttribute("data-htmlarea-clickenlarge") == "1" || selectedImageRef.getAttribute("clickenlarge") == "1") {
595 document.imageData.iClickEnlarge.checked = 1;
596 } else {
597 document.imageData.iClickEnlarge.checked = 0;
598 }
599 }
600 return false;
601 }
602 }
603
604 var selectedImageRef = getCurrentImageRef();';
605 // Setting this to a reference to the image object.
606 return $JScode;
607 }
608
609 /**
610 * Session data for this class can be set from outside with this method.
611 * Call after init()
612 *
613 * @param array $data Session data array
614 * @return array Session data and boolean which indicates that data needs to be stored in session because it's changed
615 * @todo Define visibility
616 */
617 public function processSessionData($data) {
618 $store = FALSE;
619 if ($this->act != 'image') {
620 if (isset($this->act)) {
621 $data['act'] = $this->act;
622 $store = TRUE;
623 } else {
624 $this->act = $data['act'];
625 }
626 }
627 if (isset($this->expandFolder)) {
628 $data['expandFolder'] = $this->expandFolder;
629 $store = TRUE;
630 } else {
631 $this->expandFolder = $data['expandFolder'];
632 }
633 return array($data, $store);
634 }
635
636 /**
637 * Rich Text Editor (RTE) image selector
638 *
639 * @param boolean $wiz Not used here, kept for method signature compatibility with parent class
640 * @return string Modified content variable.
641 * @return string
642 */
643 public function main_rte($wiz = FALSE) {
644 // Starting content:
645 $this->content = $this->doc->startPage($GLOBALS['LANG']->getLL('Insert Image', TRUE));
646
647 $this->content .= $this->doc->getTabMenuRaw($this->buildMenuArray($wiz, $this->allowedItems));
648 switch ($this->act) {
649 case 'image':
650 $JScode = '
651 document.write(printCurrentImageOptions());
652 insertImagePropertiesInForm();';
653 $this->content .= '<br />' . $this->doc->wrapScriptTags($JScode);
654 break;
655 case 'plain':
656
657 case 'magic':
658 // Create folder tree:
659 $foldertree = GeneralUtility::makeInstance('TYPO3\\CMS\\Rtehtmlarea\\FolderTree');
660 $foldertree->thisScript = $this->thisScript;
661 $tree = $foldertree->getBrowsableTree();
662 // Get currently selected folder
663 if (!$this->curUrlInfo['value'] || $this->curUrlInfo['act'] != $this->act) {
664 $cmpPath = '';
665 } else {
666 $cmpPath = $this->curUrlInfo['value'];
667 if (!isset($this->expandFolder)) {
668 $this->expandFolder = $cmpPath;
669 }
670 }
671 // Get the selected folder
672 $selectedFolder = FALSE;
673 if ($this->expandFolder) {
674 $fileOrFolderObject = NULL;
675 try {
676 $fileOrFolderObject = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->retrieveFileOrFolderObject($this->expandFolder);
677 } catch (\Exception $e) {
678 // No path is selected
679 }
680 if ($fileOrFolderObject instanceof \TYPO3\CMS\Core\Resource\Folder) {
681 // it's a folder
682 $selectedFolder = $fileOrFolderObject;
683 } elseif ($fileOrFolderObject instanceof \TYPO3\CMS\Core\Resource\FileInterface) {
684 // it's a file
685 $selectedFolder = $fileOrFolderObject->getParentFolder();
686 }
687 }
688 // If no folder is selected, get the user's default upload folder
689 if (!$selectedFolder) {
690 try {
691 $selectedFolder = $GLOBALS['BE_USER']->getDefaultUploadFolder();
692 } catch (\Exception $e) {
693 // The configured default user folder does not exist
694 }
695 }
696 // Build the file upload and folder creation form
697 $uploadForm = '';
698 $createFolder = '';
699 if ($selectedFolder) {
700 $uploadForm = $this->uploadForm($selectedFolder);
701 $createFolder = $this->createFolder($selectedFolder);
702 }
703 // Insert the upload form on top, if so configured
704 if ($GLOBALS['BE_USER']->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
705 $this->content .= $uploadForm;
706 }
707 // Render the filelist if there is a folder selected
708 $files = '';
709 if ($selectedFolder) {
710 $files = $this->TBE_expandFolder($selectedFolder, $this->act === 'plain' ? self::PLAIN_MODE_IMAGE_FILE_EXTENSIONS : $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $GLOBALS['BE_USER']->getTSConfigVal('options.noThumbsInRTEimageSelect'));
711 }
712 // Setup filelist indexed elements:
713 $this->doc->JScode .= $this->doc->wrapScriptTags('BrowseLinks.addElements(' . json_encode($this->elements) . ');');
714 // Wrap tree
715 $this->content .= '
716
717 <!--
718 Wrapper table for folder tree / file/folder list:
719 -->
720 <table border="0" cellpadding="0" cellspacing="0" id="typo3-linkFiles">
721 <tr>
722 <td class="c-wCell" valign="top">' . $this->barheader(($GLOBALS['LANG']->getLL('folderTree') . ':')) . $tree . '</td>
723 <td class="c-wCell" valign="top">' . $files . '</td>
724 </tr>
725 </table>
726 ';
727 // Add help message
728 $helpMessage = $this->getHelpMessage($this->act);
729 if ($helpMessage) {
730 $this->content .= $this->getMsgBox($helpMessage);
731 }
732 // Adding create folder + upload form if applicable
733 if (!$GLOBALS['BE_USER']->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
734 $this->content .= $uploadForm;
735 }
736 $this->content .= $createFolder;
737 $this->content .= '<br />';
738 break;
739 case 'dragdrop':
740 $foldertree = GeneralUtility::makeInstance('TBE_FolderTree');
741 $foldertree->thisScript = $this->thisScript;
742 $foldertree->ext_noTempRecyclerDirs = TRUE;
743 $tree = $foldertree->getBrowsableTree();
744 // Get currently selected folder
745 if (!$this->curUrlInfo['value'] || $this->curUrlInfo['act'] != $this->act) {
746 $cmpPath = '';
747 } else {
748 $cmpPath = $this->curUrlInfo['value'];
749 if (!isset($this->expandFolder)) {
750 $this->expandFolder = $cmpPath;
751 }
752 }
753 $selectedFolder = FALSE;
754 if ($this->expandFolder) {
755 try {
756 $selectedFolder = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier($this->expandFolder);
757 } catch (\Exception $e) {
758 }
759 }
760 // Render the filelist if there is a folder selected
761 $files = '';
762 if ($selectedFolder) {
763 $files = $this->TBE_dragNDrop($selectedFolder, implode(',', $this->allowedFileTypes));
764 }
765 // Wrap tree
766 $this->content .= '<table border="0" cellpadding="0" cellspacing="0">
767 <tr>
768 <td style="vertical-align: top;">' . $this->barheader(($GLOBALS['LANG']->getLL('folderTree') . ':')) . $tree . '</td>
769 <td>&nbsp;</td>
770 <td style="vertical-align: top;">' . $files . '</td>
771 </tr>
772 </table>';
773 break;
774 default:
775 // Call hook
776 foreach ($this->hookObjects as $hookObject) {
777 $this->content .= $hookObject->getTab($this->act);
778 }
779 }
780 $this->content .= $this->doc->endPage();
781
782 // unset the default jumpToUrl() function
783 unset($this->doc->JScodeArray['jumpToUrl']);
784
785 $this->doc->JScodeArray['rtehtmlarea'] = $this->getJSCode($this->act, $this->editorNo, $this->sys_language_content);
786 $this->content = $this->doc->insertStylesAndJS($this->content);
787 return $this->content;
788 }
789
790 /**
791 * Returns an array definition of the top menu
792 *
793 * @param $wiz
794 * @param $allowedItems
795 * @return array
796 */
797 protected function buildMenuArray($wiz, $allowedItems) {
798 $menuDef = array();
799 if (in_array('image', $this->allowedItems) && ($this->act === 'image' || GeneralUtility::_GP('cWidth'))) {
800 $menuDef['image']['isActive'] = FALSE;
801 $menuDef['image']['label'] = $GLOBALS['LANG']->getLL('currentImage', TRUE);
802 $menuDef['image']['url'] = '#';
803 $menuDef['image']['addParams'] = 'onClick="jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript() . 'act=image&bparams=' . $this->bparams) . ');return false;"';
804 }
805 if (in_array('magic', $this->allowedItems)) {
806 $menuDef['magic']['isActive'] = FALSE;
807 $menuDef['magic']['label'] = $GLOBALS['LANG']->getLL('magicImage', TRUE);
808 $menuDef['magic']['url'] = '#';
809 $menuDef['magic']['addParams'] = 'onClick="jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript() . 'act=magic&bparams=' . $this->bparams) . ');return false;"';
810 }
811 if (in_array('plain', $this->allowedItems)) {
812 $menuDef['plain']['isActive'] = FALSE;
813 $menuDef['plain']['label'] = $GLOBALS['LANG']->getLL('plainImage', TRUE);
814 $menuDef['plain']['url'] = '#';
815 $menuDef['plain']['addParams'] = 'onClick="jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript() . 'act=plain&bparams=' . $this->bparams) . ');return false;"';
816 }
817 if (in_array('dragdrop', $this->allowedItems)) {
818 $menuDef['dragdrop']['isActive'] = FALSE;
819 $menuDef['dragdrop']['label'] = $GLOBALS['LANG']->getLL('dragDropImage', TRUE);
820 $menuDef['dragdrop']['url'] = '#';
821 $menuDef['dragdrop']['addParams'] = 'onClick="jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript() . 'act=dragdrop&bparams=' . $this->bparams) . ');return false;"';
822 }
823 // Call hook for extra options
824 foreach ($this->hookObjects as $hookObject) {
825 $menuDef = $hookObject->modifyMenuDefinition($menuDef);
826 }
827 // Order the menu items as specified in Page TSconfig
828 $menuDef = $this->orderMenuDefinition($menuDef);
829 // Set active menu item
830 reset($menuDef);
831 if ($this->act === FALSE || !in_array($this->act, $this->allowedItems)) {
832 $this->act = key($menuDef);
833 }
834 $menuDef[$this->act]['isActive'] = TRUE;
835 return $menuDef;
836 }
837
838 /**
839 * Initializes the configuration variables
840 *
841 * @return void
842 */
843 public function initConfiguration() {
844 $this->thisConfig = $this->getRTEConfig();
845 $this->buttonConfig = $this->getButtonConfig();
846 $this->imgPath = $this->getImgPath();
847 $this->defaultClass = $this->getDefaultClass();
848 $this->setMaximumPlainImageDimensions();
849 }
850
851 /**
852 * Get the path of the image to be inserted or modified
853 *
854 * @return string path to the image
855 */
856 protected function getImgPath() {
857 $RTEtsConfigParts = explode(':', $this->RTEtsConfigParams);
858 return $RTEtsConfigParts[6];
859 }
860
861 /**
862 * Get the configuration of the image button
863 *
864 * @return array the configuration array of the image button
865 */
866 protected function getButtonConfig() {
867 return is_array($this->thisConfig['buttons.']) && is_array($this->thisConfig['buttons.']['image.']) ? $this->thisConfig['buttons.']['image.'] : array();
868 }
869
870 /**
871 * Get the allowed items or tabs
872 *
873 * @param string $items: initial list of possible items
874 * @return array the allowed items
875 */
876 public function getAllowedItems($items) {
877 $allowedItems = explode(',', $items);
878 $clientInfo = GeneralUtility::clientInfo();
879 if ($clientInfo['BROWSER'] !== 'opera') {
880 $allowedItems[] = 'dragdrop';
881 }
882 // Call hook for extra options
883 foreach ($this->hookObjects as $hookObject) {
884 $allowedItems = $hookObject->addAllowedItems($allowedItems);
885 }
886 // Remove tab "image" if there is no current image
887 if ($this->act !== 'image') {
888 $allowedItems = array_diff($allowedItems, array('image'));
889 }
890 // Remove options according to RTE configuration
891 if (is_array($this->buttonConfig['options.']) && $this->buttonConfig['options.']['removeItems']) {
892 $allowedItems = array_diff($allowedItems, GeneralUtility::trimExplode(',', $this->buttonConfig['options.']['removeItems'], TRUE));
893 }
894 return $allowedItems;
895 }
896
897 /**
898 * Order the definition of menu items according to configured order
899 *
900 * @param array $menuDefinition: definition of menu items
901 * @return array ordered menu definition
902 */
903 public function orderMenuDefinition($menuDefinition) {
904 $orderedMenuDefinition = array();
905 if (is_array($this->buttonConfig['options.']) && $this->buttonConfig['options.']['orderItems']) {
906 $orderItems = GeneralUtility::trimExplode(',', $this->buttonConfig['options.']['orderItems'], TRUE);
907 $orderItems = array_intersect($orderItems, $this->allowedItems);
908 foreach ($orderItems as $item) {
909 $orderedMenuDefinition[$item] = $menuDefinition[$item];
910 }
911 } else {
912 $orderedMenuDefinition = $menuDefinition;
913 }
914 return $orderedMenuDefinition;
915 }
916
917 /**
918 * Get the default image class
919 *
920 * @return string the default class, if any
921 */
922 protected function getDefaultClass() {
923 $defaultClass = '';
924 if (is_array($this->buttonConfig['properties.'])) {
925 if (is_array($this->buttonConfig['properties.']['class.']) && trim($this->buttonConfig['properties.']['class.']['default'])) {
926 $defaultClass = trim($this->buttonConfig['properties.']['class.']['default']);
927 }
928 }
929 return $defaultClass;
930 }
931
932 /**
933 * Set variables for maximum plain image dimensions
934 *
935 * @return void
936 */
937 protected function setMaximumPlainImageDimensions() {
938 if (is_array($this->buttonConfig['options.']) && is_array($this->buttonConfig['options.']['plain.'])) {
939 if ($this->buttonConfig['options.']['plain.']['maxWidth']) {
940 $this->plainMaxWidth = $this->buttonConfig['options.']['plain.']['maxWidth'];
941 }
942 if ($this->buttonConfig['options.']['plain.']['maxHeight']) {
943 $this->plainMaxHeight = $this->buttonConfig['options.']['plain.']['maxHeight'];
944 }
945 }
946 if (!$this->plainMaxWidth) {
947 $this->plainMaxWidth = 640;
948 }
949 if (!$this->plainMaxHeight) {
950 $this->plainMaxHeight = 680;
951 }
952 }
953
954 /**
955 * Get the help message to be displayed on a given tab
956 *
957 * @param string $act: the identifier of the tab
958 * @return string the text of the message
959 */
960 public function getHelpMessage($act) {
961 switch ($act) {
962 case 'plain':
963 return sprintf($GLOBALS['LANG']->getLL('plainImage_msg'), $this->plainMaxWidth, $this->plainMaxHeight);
964 break;
965 case 'magic':
966 return sprintf($GLOBALS['LANG']->getLL('magicImage_msg'));
967 break;
968 default:
969 return '';
970 }
971 }
972
973 /**
974 * Checks if the given file is selectable in the file list.
975 *
976 * In "plain" RTE mode only image files with a maximum width and height are selectable.
977 *
978 * @param \TYPO3\CMS\Core\Resource\FileInterface $file
979 * @param array $imgInfo Image dimensions from \TYPO3\CMS\Core\Imaging\GraphicalFunctions::getImageDimensions()
980 * @return bool TRUE if file is selectable.
981 */
982 protected function fileIsSelectableInFileList(\TYPO3\CMS\Core\Resource\FileInterface $file, array $imgInfo) {
983 return (
984 $this->act !== 'plain'
985 || (
986 GeneralUtility::inList(self::PLAIN_MODE_IMAGE_FILE_EXTENSIONS, strtolower($file->getExtension()))
987 && $imgInfo[0] <= $this->plainMaxWidth
988 && $imgInfo[1] <= $this->plainMaxHeight
989 )
990 );
991 }
992 }