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