[TASK] Make Backend Ajax URLs absolute
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / Classes / RteHtmlAreaBase.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\Backend\Form\FormEngine;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21
22 /**
23 * A RTE using the htmlArea editor
24 *
25 * @author Philipp Borgmann <philipp.borgmann@gmx.de>
26 * @author Stanislas Rolland <typo3(arobas)sjbr.ca>
27 */
28 class RteHtmlAreaBase extends \TYPO3\CMS\Backend\Rte\AbstractRte {
29
30 // Configuration of supported browsers
31 /**
32 * @var array
33 */
34 public $conf_supported_browser = array(
35 'msie' => array(
36 array(
37 'version' => 9.0,
38 'system' => array(
39 'allowed' => array(
40 'winNT',
41 'win98',
42 'win95'
43 )
44 )
45 )
46 ),
47 'gecko' => array(
48 array(
49 'version' => 1.8
50 )
51 ),
52 'webkit' => array(
53 array(
54 'version' => 534
55 ),
56 array(
57 'version' => 523,
58 'system' => array(
59 'disallowed' => array(
60 'iOS',
61 'android'
62 )
63 )
64 )
65 ),
66 'opera' => array(
67 array(
68 'version' => 9.62,
69 'system' => array(
70 'disallowed' => array(
71 'iOS',
72 'android'
73 )
74 )
75 )
76 )
77 );
78
79 // Always hide these toolbar buttons (TYPO3 button name)
80 /**
81 * @var array
82 */
83 public $conf_toolbar_hide = array(
84 'showhelp'
85 );
86
87 // The order of the toolbar: the name is the TYPO3-button name
88 /**
89 * @var string
90 */
91 public $defaultToolbarOrder;
92
93 // Conversion array: TYPO3 button names to htmlArea button names
94 /**
95 * @var array
96 */
97 public $convertToolbarForHtmlAreaArray = array(
98 'showhelp' => 'ShowHelp',
99 'space' => 'space',
100 'bar' => 'separator',
101 'linebreak' => 'linebreak'
102 );
103
104 /**
105 * @var array
106 */
107 public $pluginButton = array();
108
109 /**
110 * @var array
111 */
112 public $pluginLabel = array();
113
114 // Alternative style for RTE <div> tag.
115 public $RTEdivStyle;
116
117 // Relative path to this extension. It ends with "/"
118 public $extHttpPath;
119
120 public $backPath = '';
121
122 // TYPO3 site url
123 public $siteURL;
124
125 // TYPO3 host url
126 public $hostURL;
127
128 // Typo3 version
129 public $typoVersion;
130
131 // Identifies the RTE as being the one from the "rtehtmlarea" extension if any external code needs to know
132 /**
133 * @var string
134 */
135 public $ID = 'rtehtmlarea';
136
137 // If set, the content goes into a regular TEXT area field - for developing testing of transformations.
138 /**
139 * @var bool
140 */
141 public $debugMode = FALSE;
142
143 // For the editor
144 /**
145 * @var array
146 */
147 public $client;
148
149 /**
150 * Reference to parent object, which is an instance of the TCEforms
151 *
152 * @var \TYPO3\CMS\Backend\Form\FormEngine
153 */
154 public $TCEform;
155
156 /**
157 * @var string
158 */
159 public $elementId;
160
161 /**
162 * @var array
163 */
164 public $elementParts;
165
166 /**
167 * @var string
168 */
169 public $tscPID;
170
171 /**
172 * @var string
173 */
174 public $typeVal;
175
176 /**
177 * @var int
178 */
179 public $thePid;
180
181 /**
182 * @var array
183 */
184 public $RTEsetup;
185
186 /**
187 * @var array
188 */
189 public $thisConfig;
190
191 public $language;
192 /**
193 * TYPO3 language code of the content language
194 */
195 public $contentTypo3Language;
196 /**
197 * ISO language code of the content language
198 */
199 public $contentISOLanguage;
200 /**
201 * Language service object for localization to the content language
202 */
203 protected $contentLanguageService;
204 public $charset = 'utf-8';
205
206 public $contentCharset = 'utf-8';
207
208 public $OutputCharset = 'utf-8';
209
210 /**
211 * @var string
212 */
213 public $editorCSS;
214
215 /**
216 * @var array
217 */
218 public $specConf;
219
220 /**
221 * @var array
222 */
223 public $toolbar = array();
224
225 // Save the buttons for the toolbar
226 /**
227 * @var array
228 */
229 public $toolbarOrderArray = array();
230
231 protected $pluginEnabledArray = array();
232
233 // Array of plugin id's enabled in the current RTE editing area
234 protected $pluginEnabledCumulativeArray = array();
235
236 // Cumulative array of plugin id's enabled so far in any of the RTE editing areas of the form
237 public $registeredPlugins = array();
238
239 // Array of registered plugins indexed by their plugin Id's
240 protected $fullScreen = FALSE;
241 // Page renderer object
242 protected $pageRenderer;
243
244 /**
245 * Returns TRUE if the RTE is available. Here you check if the browser requirements are met.
246 * If there are reasons why the RTE cannot be displayed you simply enter them as text in ->errorLog
247 *
248 * @return bool TRUE if this RTE object offers an RTE in the current browser environment
249 */
250 public function isAvailable() {
251 $this->client = $this->clientInfo();
252 $this->errorLog = array();
253 if (!$this->debugMode) {
254 // If debug-mode, let any browser through
255 $rteIsAvailable = FALSE;
256 $rteConfBrowser = $this->conf_supported_browser;
257 if (is_array($rteConfBrowser)) {
258 foreach ($rteConfBrowser as $browser => $browserConf) {
259 if ($browser == $this->client['browser']) {
260 // Config for Browser found, check it:
261 if (is_array($browserConf)) {
262 foreach ($browserConf as $browserConfSub) {
263 if ($browserConfSub['version'] <= $this->client['version'] || empty($browserConfSub['version'])) {
264 // Version is supported
265 if (is_array($browserConfSub['system'])) {
266 // Check against allowed systems
267 if (is_array($browserConfSub['system']['allowed'])) {
268 foreach ($browserConfSub['system']['allowed'] as $system) {
269 if (in_array($system, $this->client['all_systems'])) {
270 $rteIsAvailable = TRUE;
271 break;
272 }
273 }
274 } else {
275 // All allowed
276 $rteIsAvailable = TRUE;
277 }
278 // Check against disallowed systems
279 if (is_array($browserConfSub['system']['disallowed'])) {
280 foreach ($browserConfSub['system']['disallowed'] as $system) {
281 if (in_array($system, $this->client['all_systems'])) {
282 $rteIsAvailable = FALSE;
283 break;
284 }
285 }
286 }
287 } else {
288 // No system config: system is supported
289 $rteIsAvailable = TRUE;
290 break;
291 }
292 }
293 }
294 } else {
295 // no config for this browser found, so all versions or system with this browsers are allow
296 $rteIsAvailable = TRUE;
297 break;
298 }
299 }
300 }
301 } else {
302
303 }
304 if (!$rteIsAvailable) {
305 $this->errorLog[] = 'RTE: Browser not supported.';
306 }
307 if (\TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) < 4000000) {
308 $rteIsAvailable = FALSE;
309 $this->errorLog[] = 'rte: This version of htmlArea RTE cannot run under this version of TYPO3.';
310 }
311 }
312 return $rteIsAvailable;
313 }
314
315 /**
316 * Draws the RTE as an iframe
317 *
318 * @param FormEngine $parentObject Reference to parent object, which is an instance of the TCEforms.
319 * @param string $table The table name
320 * @param string $field The field name
321 * @param array $row The current row from which field is being rendered
322 * @param array $PA Array of standard content for rendering form fields from TCEforms. See TCEforms for details on this. Includes for instance the value and the form field name, java script actions and more.
323 * @param array $specConf "special" configuration - what is found at position 4 in the types configuration of a field from record, parsed into an array.
324 * @param array $thisConfig Configuration for RTEs; A mix between TSconfig and otherwise. Contains configuration for display, which buttons are enabled, additional transformation information etc.
325 * @param string $RTEtypeVal Record "type" field value.
326 * @param string $RTErelPath Relative path for images/links in RTE; this is used when the RTE edits content from static files where the path of such media has to be transformed forth and back!
327 * @param int $thePidValue PID value of record (true parent page id)
328 * @return string HTML code for RTE!
329 */
330 public function drawRTE($parentObject, $table, $field, $row, $PA, $specConf, $thisConfig, $RTEtypeVal, $RTErelPath, $thePidValue) {
331 $this->TCEform = $parentObject;
332 $inline = $this->TCEform->inline;
333 $GLOBALS['LANG']->includeLLFile('EXT:' . $this->ID . '/locallang.xlf');
334 $this->client = $this->clientInfo();
335 $this->typoVersion = \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version);
336 $this->userUid = 'BE_' . $GLOBALS['BE_USER']->user['uid'];
337 // Draw form element:
338 if ($this->debugMode) {
339 // Draws regular text area (debug mode)
340 $item = parent::drawRTE($this->TCEform, $table, $field, $row, $PA, $specConf, $thisConfig, $RTEtypeVal, $RTErelPath, $thePidValue);
341 } else {
342 // Draw real RTE
343 /* =======================================
344 * INIT THE EDITOR-SETTINGS
345 * =======================================
346 */
347 // Get the path to this extension:
348 $this->extHttpPath = $this->backPath . ExtensionManagementUtility::extRelPath($this->ID);
349 // Get the site URL
350 $this->siteURL = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
351 // Get the host URL
352 $this->hostURL = $this->siteURL . TYPO3_mainDir;
353 // Element ID + pid
354 $this->elementId = $PA['itemFormElName'];
355 // Form element name
356 $this->elementParts = explode('][', preg_replace('/\\]$/', '', preg_replace('/^(TSFE_EDIT\\[data\\]\\[|data\\[)/', '', $this->elementId)));
357 // Find the page PIDs:
358 list($this->tscPID, $this->thePid) = BackendUtility::getTSCpid(trim($this->elementParts[0]), trim($this->elementParts[1]), $thePidValue);
359 // Record "types" field value:
360 $this->typeVal = $RTEtypeVal;
361 // TCA "types" value for record
362 // Find "thisConfig" for record/editor:
363 unset($this->RTEsetup);
364 $this->RTEsetup = $GLOBALS['BE_USER']->getTSConfig('RTE', BackendUtility::getPagesTSconfig($this->tscPID));
365 $this->thisConfig = $thisConfig;
366 // Special configuration and default extras:
367 $this->specConf = $specConf;
368 if ($this->thisConfig['forceHTTPS']) {
369 $this->extHttpPath = preg_replace('/^(http|https)/', 'https', $this->extHttpPath);
370 $this->siteURL = preg_replace('/^(http|https)/', 'https', $this->siteURL);
371 $this->hostURL = preg_replace('/^(http|https)/', 'https', $this->hostURL);
372 }
373 // Register RTE windows
374 $this->TCEform->RTEwindows[] = $PA['itemFormElName'];
375 $textAreaId = preg_replace('/[^a-zA-Z0-9_:.-]/', '_', $PA['itemFormElName']);
376 $textAreaId = htmlspecialchars(preg_replace('/^[^a-zA-Z]/', 'x', $textAreaId));
377 /* =======================================
378 * LANGUAGES & CHARACTER SETS
379 * =======================================
380 */
381 // Languages: interface and content
382 $this->language = $GLOBALS['LANG']->lang;
383 if ($this->language === 'default' || !$this->language) {
384 $this->language = 'en';
385 }
386 $this->contentLanguageUid = max($row['sys_language_uid'], 0);
387 if ($this->contentLanguageUid) {
388 $this->contentISOLanguage = $this->language;
389 if (ExtensionManagementUtility::isLoaded('static_info_tables')) {
390 $tableA = 'sys_language';
391 $tableB = 'static_languages';
392 $selectFields = $tableA . '.uid,' . $tableB . '.lg_iso_2,' . $tableB . '.lg_country_iso_2';
393 $tableAB = $tableA . ' LEFT JOIN ' . $tableB . ' ON ' . $tableA . '.static_lang_isocode=' . $tableB . '.uid';
394 $whereClause = $tableA . '.uid = ' . intval($this->contentLanguageUid);
395 $whereClause .= BackendUtility::BEenableFields($tableA);
396 $whereClause .= BackendUtility::deleteClause($tableA);
397 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($selectFields, $tableAB, $whereClause);
398 while ($languageRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
399 $this->contentISOLanguage = strtolower(trim($languageRow['lg_iso_2']) . (trim($languageRow['lg_country_iso_2']) ? '_' . trim($languageRow['lg_country_iso_2']) : ''));
400 }
401 }
402 } else {
403 $this->contentISOLanguage = trim($this->thisConfig['defaultContentLanguage']) ?: 'en';
404 $languageCodeParts = explode('_', $this->contentISOLanguage);
405 $this->contentISOLanguage = strtolower($languageCodeParts[0]) . ($languageCodeParts[1] ? '_' . strtoupper($languageCodeParts[1]) : '');
406 // Find the configured language in the list of localization locales
407 /** @var $locales \TYPO3\CMS\Core\Localization\Locales */
408 $locales = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\Locales::class);
409 // If not found, default to 'en'
410 if (!in_array($this->contentISOLanguage, $locales->getLocales())) {
411 $this->contentISOLanguage = 'en';
412 }
413 }
414 // Create content laguage service
415 $this->contentLanguageService = GeneralUtility::makeInstance(\TYPO3\CMS\Lang\LanguageService::class);
416 $this->contentTypo3Language = $this->contentISOLanguage === 'en' ? 'default' : $this->contentISOLanguage;
417 $this->contentLanguageService->init($this->contentTypo3Language);
418 /* =======================================
419 * TOOLBAR CONFIGURATION
420 * =======================================
421 */
422 $this->initializeToolbarConfiguration();
423 /* =======================================
424 * SET STYLES
425 * =======================================
426 */
427 // Check if wizard_rte called this for fullscreen edtition
428 if (GeneralUtility::_GP('M') === 'wizard_rte') {
429 $this->fullScreen = TRUE;
430 $RTEWidth = '100%';
431 $RTEHeight = '100%';
432 $RTEPaddingRight = '0';
433 $editorWrapWidth = '100%';
434 } else {
435 $options = $GLOBALS['BE_USER']->userTS['options.'];
436 $RTEWidth = 530 + (isset($options['RTELargeWidthIncrement']) ? (int)$options['RTELargeWidthIncrement'] : 150);
437 $RTEWidth -= $inline->getStructureDepth() > 0 ? ($inline->getStructureDepth() + 1) * $inline->getLevelMargin() : 0;
438 $RTEWidthOverride = is_object($GLOBALS['BE_USER']) && isset($GLOBALS['BE_USER']->uc['rteWidth']) && trim($GLOBALS['BE_USER']->uc['rteWidth']) ? trim($GLOBALS['BE_USER']->uc['rteWidth']) : trim($this->thisConfig['RTEWidthOverride']);
439 if ($RTEWidthOverride) {
440 if (strstr($RTEWidthOverride, '%')) {
441 if ($this->client['browser'] != 'msie') {
442 $RTEWidth = (int)$RTEWidthOverride > 0 ? $RTEWidthOverride : '100%';
443 }
444 } else {
445 $RTEWidth = (int)$RTEWidthOverride > 0 ? (int)$RTEWidthOverride : $RTEWidth;
446 }
447 }
448 $RTEWidth = strstr($RTEWidth, '%') ? $RTEWidth : $RTEWidth . 'px';
449 $RTEHeight = 380 + (isset($options['RTELargeHeightIncrement']) ? (int)$options['RTELargeHeightIncrement'] : 0);
450 $RTEHeightOverride = is_object($GLOBALS['BE_USER']) && isset($GLOBALS['BE_USER']->uc['rteHeight']) && (int)$GLOBALS['BE_USER']->uc['rteHeight'] ? (int)$GLOBALS['BE_USER']->uc['rteHeight'] : (int)$this->thisConfig['RTEHeightOverride'];
451 $RTEHeight = $RTEHeightOverride > 0 ? $RTEHeightOverride : $RTEHeight;
452 $RTEPaddingRight = '2px';
453 $editorWrapWidth = '99%';
454 }
455 $editorWrapHeight = '100%';
456 $this->RTEdivStyle = 'position:relative; left:0px; top:0px; height:' . $RTEHeight . 'px; width:' . $RTEWidth . '; border: 1px solid black; padding: 2px ' . $RTEPaddingRight . ' 2px 2px;';
457 /* =======================================
458 * LOAD CSS AND JAVASCRIPT
459 * =======================================
460 */
461 $this->pageRenderer = $GLOBALS['SOBE']->doc->getPageRenderer();
462 // Preloading the pageStyle and including RTE skin stylesheets
463 $this->addPageStyle();
464 $this->addSkin();
465 // Register RTE in JS
466 $this->TCEform->additionalJS_post[] = $this->registerRTEinJS($this->TCEform->RTEcounter, $table, $row['uid'], $field, $textAreaId);
467 // Set the save option for the RTE
468 $this->TCEform->additionalJS_submit[] = $this->setSaveRTE($this->TCEform->RTEcounter, 'editform', $textAreaId, $PA['itemFormElName']);
469 $this->TCEform->additionalJS_delete[] = $this->setDeleteRTE($this->TCEform->RTEcounter, 'editform', $textAreaId);
470 // Loading ExtJs inline code
471 $this->pageRenderer->enableExtJSQuickTips();
472 // Add TYPO3 notifications JavaScript
473 $this->pageRenderer->addJsFile('sysext/backend/Resources/Public/JavaScript/notifications.js');
474 // Add RTE JavaScript
475 $this->addRteJsFiles($this->TCEform->RTEcounter);
476 $this->pageRenderer->addJsFile($this->buildJSMainLangFile($this->TCEform->RTEcounter));
477 $this->pageRenderer->addJsInlineCode('HTMLArea-init', $this->getRteInitJsCode(), TRUE);
478 /* =======================================
479 * DRAW THE EDITOR
480 * =======================================
481 */
482 // Transform value:
483 $value = $this->transformContent('rte', $PA['itemFormElValue'], $table, $field, $row, $specConf, $thisConfig, $RTErelPath, $thePidValue);
484 // Further content transformation by registered plugins
485 foreach ($this->registeredPlugins as $pluginId => $plugin) {
486 if ($this->isPluginEnabled($pluginId) && method_exists($plugin, 'transformContent')) {
487 $value = $plugin->transformContent($value);
488 }
489 }
490 // Draw the textarea
491 $visibility = 'hidden';
492 $item = $this->triggerField($PA['itemFormElName']) . '
493 <div id="pleasewait' . $textAreaId . '" class="pleasewait" style="display: block;" >' . $GLOBALS['LANG']->getLL('Please wait') . '</div>
494 <div id="editorWrap' . $textAreaId . '" class="editorWrap" style="visibility: hidden; width:' . $editorWrapWidth . '; height:' . $editorWrapHeight . ';">
495 <textarea id="RTEarea' . $textAreaId . '" name="' . htmlspecialchars($PA['itemFormElName']) . '" rows="0" cols="0" style="' . htmlspecialchars($this->RTEdivStyle, ENT_COMPAT, 'UTF-8', FALSE) . '">' . GeneralUtility::formatForTextarea($value) . '</textarea>
496 </div>' . LF;
497 }
498 // Return form item:
499 return $item;
500 }
501
502 /**
503 * Add links to content style sheets to document header
504 *
505 * @return void
506 */
507 protected function addPageStyle() {
508 $contentCssFileNames = $this->getContentCssFileNames();
509 foreach ($contentCssFileNames as $contentCssKey => $contentCssFile) {
510 $this->addStyleSheet('rtehtmlarea-content-' . $contentCssKey, $contentCssFile, 'htmlArea RTE Content CSS', 'alternate stylesheet');
511 }
512 }
513
514 /**
515 * Get the name of the contentCSS files to use
516 *
517 * @return array An array of full file name of the content css files to use
518 */
519 protected function getContentCssFileNames() {
520 $contentCss = is_array($this->thisConfig['contentCSS.']) ? $this->thisConfig['contentCSS.'] : array();
521 if (isset($this->thisConfig['contentCSS'])) {
522 $contentCss[] = trim($this->thisConfig['contentCSS']);
523 }
524 $contentCssFiles = array();
525 if (count($contentCss)) {
526 foreach ($contentCss as $contentCssKey => $contentCssfile) {
527 $fileName = trim($contentCssfile);
528 $absolutePath = GeneralUtility::getFileAbsFileName($fileName);
529 if (file_exists($absolutePath) && filesize($absolutePath)) {
530 $contentCssFiles[$contentCssKey] = $this->getFullFileName($fileName);
531 }
532 }
533 }
534 // Fallback to default content css file if none of the configured files exists and is not empty
535 if (count($contentCssFiles) === 0) {
536 $contentCssFiles['default'] = $this->getFullFileName('EXT:' . $this->ID . '/Resources/Public/Css/ContentCss/Default.css');
537 }
538 return array_unique($contentCssFiles);
539 }
540
541 /**
542 * Add links to skin style sheet(s) to document header
543 *
544 * @return void
545 */
546 protected function addSkin() {
547 // Get skin file name from Page TSConfig if any
548 $skinFilename = trim($this->thisConfig['skin']) ?: 'EXT:' . $this->ID . '/Resources/Public/Css/Skin/htmlarea.css';
549 $this->editorCSS = $this->getFullFileName($skinFilename);
550 $skinDir = dirname($this->editorCSS);
551 // Editing area style sheet
552 $this->editedContentCSS = $skinDir . '/htmlarea-edited-content.css';
553 $this->addStyleSheet('rtehtmlarea-editing-area-skin', $this->editedContentCSS);
554 // jQuery UI Resizable style sheet
555 $this->addStyleSheet('jquery-ui-resizable', $skinDir . '/jquery-ui-resizable.css');
556 // Main skin
557 $this->addStyleSheet('rtehtmlarea-skin', $this->editorCSS);
558 // Additional icons from registered plugins
559 foreach ($this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter] as $pluginId) {
560 if (is_object($this->registeredPlugins[$pluginId])) {
561 $pathToSkin = $this->registeredPlugins[$pluginId]->getPathToSkin();
562 if ($pathToSkin) {
563 $key = $this->registeredPlugins[$pluginId]->getExtensionKey();
564 $this->addStyleSheet('rtehtmlarea-plugin-' . $pluginId . '-skin', ($this->is_FE() ? ExtensionManagementUtility::siteRelPath($key) : $this->backPath . ExtensionManagementUtility::extRelPath($key)) . $pathToSkin);
565 }
566 }
567 }
568 }
569
570 /**
571 * Add style sheet file to document header
572 *
573 * @param string $key: some key identifying the style sheet
574 * @param string $href: uri to the style sheet file
575 * @param string $title: value for the title attribute of the link element
576 * @param string $relation: value for the rel attribute of the link element
577 * @return void
578 */
579 protected function addStyleSheet($key, $href, $title = '', $relation = 'stylesheet') {
580 // If it was not known that an RTE-enabled CE would be created when the page was first created, the css would not be added to head
581 if (is_object($this->TCEform->inline) && $this->TCEform->inline->isAjaxCall) {
582 $this->TCEform->additionalCode_pre[$key] = '<link rel="' . $relation . '" type="text/css" href="' . $href . '" title="' . $title . '" />';
583 } else {
584 $this->pageRenderer->addCssFile($href, $relation, 'screen', $title);
585 }
586 }
587
588 /**
589 * Initialize toolbar configuration and enable registered plugins
590 *
591 * @return void
592 */
593 protected function initializeToolbarConfiguration() {
594 // Enable registred plugins
595 $this->enableRegisteredPlugins();
596 // Configure toolbar
597 $this->setToolbar();
598 // Check if some plugins need to be disabled
599 $this->setPlugins();
600 // Merge the list of enabled plugins with the lists from the previous RTE editing areas on the same form
601 $this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter] = $this->pluginEnabledArray;
602 if ($this->TCEform->RTEcounter > 1 && isset($this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter - 1]) && is_array($this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter - 1])) {
603 $this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter] = array_unique(array_values(array_merge($this->pluginEnabledArray, $this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter - 1])));
604 }
605 }
606
607 /**
608 * Add registered plugins to the array of enabled plugins
609 */
610 public function enableRegisteredPlugins() {
611 // Traverse registered plugins
612 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->ID]['plugins'])) {
613 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->ID]['plugins'] as $pluginId => $pluginObjectConfiguration) {
614 $plugin = FALSE;
615 if (is_array($pluginObjectConfiguration) && count($pluginObjectConfiguration)) {
616 $plugin = GeneralUtility::getUserObj($pluginObjectConfiguration['objectReference']);
617 }
618 if (is_object($plugin)) {
619 if ($plugin->main($this)) {
620 $this->registeredPlugins[$pluginId] = $plugin;
621 // Override buttons from previously registered plugins
622 $pluginButtons = GeneralUtility::trimExplode(',', $plugin->getPluginButtons(), TRUE);
623 foreach ($this->pluginButton as $previousPluginId => $buttonList) {
624 $this->pluginButton[$previousPluginId] = implode(',', array_diff(GeneralUtility::trimExplode(',', $this->pluginButton[$previousPluginId], TRUE), $pluginButtons));
625 }
626 $this->pluginButton[$pluginId] = $plugin->getPluginButtons();
627 $pluginLabels = GeneralUtility::trimExplode(',', $plugin->getPluginLabels(), TRUE);
628 foreach ($this->pluginLabel as $previousPluginId => $labelList) {
629 $this->pluginLabel[$previousPluginId] = implode(',', array_diff(GeneralUtility::trimExplode(',', $this->pluginLabel[$previousPluginId], TRUE), $pluginLabels));
630 }
631 $this->pluginLabel[$pluginId] = $plugin->getPluginLabels();
632 $this->pluginEnabledArray[] = $pluginId;
633 }
634 }
635 }
636 }
637 // Process overrides
638 $hidePlugins = array();
639 foreach ($this->registeredPlugins as $pluginId => $plugin) {
640 if ($plugin->addsButtons() && !$this->pluginButton[$pluginId]) {
641 $hidePlugins[] = $pluginId;
642 }
643 }
644 $this->pluginEnabledArray = array_unique(array_diff($this->pluginEnabledArray, $hidePlugins));
645 }
646
647 /**
648 * Set the toolbar config (only in this PHP-Object, not in JS):
649 */
650 public function setToolbar() {
651 if ($this->client['browser'] == 'msie' || $this->client['browser'] == 'opera') {
652 $this->thisConfig['keepButtonGroupTogether'] = 0;
653 }
654 $this->defaultToolbarOrder = 'bar, blockstylelabel, blockstyle, textstylelabel, textstyle, linebreak,
655 bar, formattext, bold, strong, italic, emphasis, big, small, insertedtext, deletedtext, citation, code, definition, keyboard, monospaced, quotation, sample, variable, bidioverride, strikethrough, subscript, superscript, underline, span,
656 bar, fontstyle, fontsize, bar, formatblock, insertparagraphbefore, insertparagraphafter, blockquote, line,
657 bar, left, center, right, justifyfull,
658 bar, orderedlist, unorderedlist, definitionlist, definitionitem, outdent, indent,
659 bar, language, showlanguagemarks,lefttoright, righttoleft,
660 bar, textcolor, bgcolor, textindicator,
661 bar, editelement, showmicrodata,
662 bar, image, emoticon, insertcharacter, insertsofthyphen, abbreviation, user,
663 bar, link, unlink,
664 bar, table,' . ($this->thisConfig['hideTableOperationsInToolbar'] && is_array($this->thisConfig['buttons.']) && is_array($this->thisConfig['buttons.']['toggleborders.']) && $this->thisConfig['buttons.']['toggleborders.']['keepInToolbar'] ? ' toggleborders,' : '') . '
665 bar, findreplace, spellcheck,
666 bar, chMode, inserttag, removeformat, bar, copy, cut, paste, pastetoggle, pastebehaviour, bar, undo, redo, bar, showhelp, about, linebreak,
667 ' . ($this->thisConfig['hideTableOperationsInToolbar'] ? '' : 'bar, toggleborders,') . ' bar, tableproperties, tablerestyle, bar, rowproperties, rowinsertabove, rowinsertunder, rowdelete, rowsplit, bar,
668 columnproperties, columninsertbefore, columninsertafter, columndelete, columnsplit, bar,
669 cellproperties, cellinsertbefore, cellinsertafter, celldelete, cellsplit, cellmerge';
670 // Additional buttons from registered plugins
671 foreach ($this->registeredPlugins as $pluginId => $plugin) {
672 if ($this->isPluginEnabled($pluginId)) {
673 $this->defaultToolbarOrder = $plugin->addButtonsToToolbar();
674 }
675 }
676 $toolbarOrder = $this->thisConfig['toolbarOrder'] ?: $this->defaultToolbarOrder;
677 // Getting rid of undefined buttons
678 $this->toolbarOrderArray = array_intersect(GeneralUtility::trimExplode(',', $toolbarOrder, TRUE), GeneralUtility::trimExplode(',', $this->defaultToolbarOrder, TRUE));
679 $toolbarOrder = array_unique(array_values($this->toolbarOrderArray));
680 // Fetching specConf for field from backend
681 $pList = is_array($this->specConf['richtext']['parameters']) ? implode(',', $this->specConf['richtext']['parameters']) : '';
682 if ($pList != '*') {
683 // If not all
684 $show = is_array($this->specConf['richtext']['parameters']) ? $this->specConf['richtext']['parameters'] : array();
685 if ($this->thisConfig['showButtons']) {
686 if (!GeneralUtility::inList($this->thisConfig['showButtons'], '*')) {
687 $show = array_unique(array_merge($show, GeneralUtility::trimExplode(',', $this->thisConfig['showButtons'], TRUE)));
688 } else {
689 $show = array_unique(array_merge($show, $toolbarOrder));
690 }
691 }
692 if (is_array($this->thisConfig['showButtons.'])) {
693 foreach ($this->thisConfig['showButtons.'] as $buttonId => $value) {
694 if ($value) {
695 $show[] = $buttonId;
696 }
697 }
698 $show = array_unique($show);
699 }
700 } else {
701 $show = $toolbarOrder;
702 }
703 // Resticting to RTEkeyList for backend user
704 if (is_object($GLOBALS['BE_USER'])) {
705 $RTEkeyList = isset($GLOBALS['BE_USER']->userTS['options.']['RTEkeyList']) ? $GLOBALS['BE_USER']->userTS['options.']['RTEkeyList'] : '*';
706 if ($RTEkeyList != '*') {
707 // If not all
708 $show = array_intersect($show, GeneralUtility::trimExplode(',', $RTEkeyList, TRUE));
709 }
710 }
711 // Hiding buttons of disabled plugins
712 $hideButtons = array('space', 'bar', 'linebreak');
713 foreach ($this->pluginButton as $pluginId => $buttonList) {
714 if (!$this->isPluginEnabled($pluginId)) {
715 $buttonArray = GeneralUtility::trimExplode(',', $buttonList, TRUE);
716 foreach ($buttonArray as $button) {
717 $hideButtons[] = $button;
718 }
719 }
720 }
721 // Hiding labels of disabled plugins
722 foreach ($this->pluginLabel as $pluginId => $label) {
723 if (!$this->isPluginEnabled($pluginId)) {
724 $hideButtons[] = $label;
725 }
726 }
727 // Hiding buttons
728 $show = array_diff($show, $this->conf_toolbar_hide, GeneralUtility::trimExplode(',', $this->thisConfig['hideButtons'], TRUE));
729 // Apply toolbar constraints from registered plugins
730 foreach ($this->registeredPlugins as $pluginId => $plugin) {
731 if ($this->isPluginEnabled($pluginId) && method_exists($plugin, 'applyToolbarConstraints')) {
732 $show = $plugin->applyToolbarConstraints($show);
733 }
734 }
735 // Getting rid of the buttons for which we have no position
736 $show = array_intersect($show, $toolbarOrder);
737 $this->toolbar = $show;
738 }
739
740 /**
741 * Disable some plugins
742 */
743 public function setPlugins() {
744 // Disabling a plugin that adds buttons if none of its buttons is in the toolbar
745 $hidePlugins = array();
746 foreach ($this->pluginButton as $pluginId => $buttonList) {
747 if ($this->registeredPlugins[$pluginId]->addsButtons()) {
748 $showPlugin = FALSE;
749 $buttonArray = GeneralUtility::trimExplode(',', $buttonList, TRUE);
750 foreach ($buttonArray as $button) {
751 if (in_array($button, $this->toolbar)) {
752 $showPlugin = TRUE;
753 }
754 }
755 if (!$showPlugin) {
756 $hidePlugins[] = $pluginId;
757 }
758 }
759 }
760 $this->pluginEnabledArray = array_diff($this->pluginEnabledArray, $hidePlugins);
761 // Hiding labels of disabled plugins
762 $hideLabels = array();
763 foreach ($this->pluginLabel as $pluginId => $label) {
764 if (!$this->isPluginEnabled($pluginId)) {
765 $hideLabels[] = $label;
766 }
767 }
768 $this->toolbar = array_diff($this->toolbar, $hideLabels);
769 // Adding plugins declared as prerequisites by enabled plugins
770 $requiredPlugins = array();
771 foreach ($this->registeredPlugins as $pluginId => $plugin) {
772 if ($this->isPluginEnabled($pluginId)) {
773 $requiredPlugins = array_merge($requiredPlugins, GeneralUtility::trimExplode(',', $plugin->getRequiredPlugins(), TRUE));
774 }
775 }
776 $requiredPlugins = array_unique($requiredPlugins);
777 foreach ($requiredPlugins as $pluginId) {
778 if (is_object($this->registeredPlugins[$pluginId]) && !$this->isPluginEnabled($pluginId)) {
779 $this->pluginEnabledArray[] = $pluginId;
780 }
781 }
782 $this->pluginEnabledArray = array_unique($this->pluginEnabledArray);
783 // Completing the toolbar conversion array for htmlArea
784 foreach ($this->registeredPlugins as $pluginId => $plugin) {
785 if ($this->isPluginEnabled($pluginId)) {
786 $this->convertToolbarForHtmlAreaArray = array_unique(array_merge($this->convertToolbarForHtmlAreaArray, $plugin->getConvertToolbarForHtmlAreaArray()));
787 }
788 }
789 }
790
791 /**
792 * Convert the TYPO3 names of buttons into the names for htmlArea RTE
793 *
794 * @param string buttonname (typo3-name)
795 * @return string buttonname (htmlarea-name)
796 */
797 public function convertToolbarForHTMLArea($button) {
798 return $this->convertToolbarForHtmlAreaArray[$button];
799 }
800
801 /**
802 * Add RTE main scripts and plugin scripts
803 *
804 * @param string $RTEcounter: The index number of the current RTE editing area within the form.
805 * @return void
806 */
807 protected function addRteJsFiles($RTEcounter) {
808 // Component files. Order is important.
809 $components = array(
810 'Util/Wrap.open',
811 'NameSpace/NameSpace',
812 'UserAgent/UserAgent',
813 'Util/Util',
814 'Util/Color',
815 'Util/Resizable',
816 'Util/String',
817 'Util/Tips',
818 'Util/TYPO3',
819 'Ajax/Ajax',
820 'DOM/DOM',
821 'Event/Event',
822 'Event/KeyMap',
823 'CSS/Parser',
824 'DOM/BookMark',
825 'DOM/Node',
826 'DOM/Selection',
827 'DOM/Walker',
828 'Configuration/Config',
829 'Toolbar/Button',
830 'Toolbar/ToolbarText',
831 'Toolbar/Select',
832 'Extjs/ColorPalette',
833 'Extjs/ux/ColorMenu',
834 'Extjs/ux/ColorPaletteField',
835 'LoremIpsum',
836 'Plugin/Plugin'
837 );
838 $components2 = array(
839 'Editor/Toolbar',
840 'Editor/Iframe',
841 'Editor/TextAreaContainer',
842 'Editor/StatusBar',
843 'Editor/Framework',
844 'Editor/Editor',
845 'HTMLArea'
846 );
847 foreach ($components as $component) {
848 $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $this->ID . '/Resources/Public/JavaScript/HTMLArea/' . $component . '.js'));
849 }
850 foreach ($this->pluginEnabledCumulativeArray[$RTEcounter] as $pluginId) {
851 $extensionKey = is_object($this->registeredPlugins[$pluginId]) ? $this->registeredPlugins[$pluginId]->getExtensionKey() : $this->ID;
852 $fileName = 'EXT:' . $extensionKey . '/Resources/Public/JavaScript/Plugins/' . $pluginId . '.js';
853 $absolutePath = GeneralUtility::getFileAbsFileName($fileName);
854 if (file_exists($absolutePath)) {
855 $this->pageRenderer->addJsFile($this->getFullFileName($fileName));
856 } else {
857 // Backward compatibility
858 $pluginName = strtolower(preg_replace('/([a-z])([A-Z])([a-z])/', '$1-$2$3', $pluginId));
859 $fileName = 'EXT:' . $extensionKey . '/htmlarea/plugins/' . $pluginId . '/' . $pluginName . '.js';
860 $absolutePath = GeneralUtility::getFileAbsFileName($fileName);
861 if (file_exists($absolutePath)) {
862 $this->pageRenderer->addJsFile($this->getFullFileName($fileName));
863 }
864 }
865 }
866 foreach ($components2 as $component) {
867 $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $this->ID . '/Resources/Public/JavaScript/HTMLArea/' . $component . '.js'));
868 }
869 $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $this->ID . '/Resources/Public/JavaScript/HTMLArea/Util/Wrap.close.js'));
870 }
871
872 /**
873 * Return RTE initialization inline JavaScript code
874 *
875 * @return string RTE initialization inline JavaScript code
876 */
877 protected function getRteInitJsCode() {
878 return 'require(["TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea"], function (HTMLArea) {
879 if (typeof RTEarea === "undefined") {
880 RTEarea = new Object();
881 RTEarea[0] = new Object();
882 RTEarea[0].version = "' . $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->ID]['version'] . '";
883 RTEarea[0].editorUrl = "' . $this->extHttpPath . '";
884 RTEarea[0].editorCSS = "' . GeneralUtility::createVersionNumberedFilename($this->editorCSS) . '";
885 RTEarea[0].editorSkin = "' . dirname($this->editorCSS) . '/";
886 RTEarea[0].editedContentCSS = "' . GeneralUtility::createVersionNumberedFilename($this->editedContentCSS) . '";
887 RTEarea[0].hostUrl = "' . $this->hostURL . '";
888 RTEarea.init = function() {
889 if (typeof HTMLArea === "undefined" || !Ext.isReady) {
890 window.setTimeout(function () {
891 RTEarea.init();
892 }, 10);
893 } else {
894 Ext.QuickTips.init();
895 HTMLArea.init();
896 }
897 };
898 RTEarea.initEditor = function(editorNumber) {
899 if (typeof HTMLArea === "undefined" || !HTMLArea.isReady) {
900 window.setTimeout(function () {
901 RTEarea.initEditor(editorNumber);
902 }, 40);
903 } else {
904 HTMLArea.initEditor(editorNumber);
905 }
906 };
907 }
908 RTEarea.init();
909 });';
910 }
911
912 /**
913 * Return the Javascript code for configuring the RTE
914 *
915 * @param int $RTEcounter: The index number of the current RTE editing area within the form.
916 * @param string $table: The table that includes this RTE (optional, necessary for IRRE).
917 * @param string $uid: The uid of that table that includes this RTE (optional, necessary for IRRE).
918 * @param string $field: The field of that record that includes this RTE (optional).
919 * @param string $textAreaId ID of the textarea, to have a unigue number for the editor
920 * @return string the Javascript code for configuring the RTE
921 */
922 public function registerRTEinJS($RTEcounter, $table = '', $uid = '', $field = '', $textAreaId = '') {
923 $configureRTEInJavascriptString = '
924 if (typeof configureEditorInstance === "undefined") {
925 configureEditorInstance = new Object();
926 }
927 configureEditorInstance["' . $textAreaId . '"] = function() {
928 if (typeof RTEarea === "undefined" || typeof HTMLArea === "undefined") {
929 window.setTimeout("configureEditorInstance[\'' . $textAreaId . '\']();", 40);
930 } else {
931 editornumber = "' . $textAreaId . '";
932 RTEarea[editornumber] = new Object();
933 RTEarea[editornumber].RTEtsConfigParams = "&RTEtsConfigParams=' . rawurlencode($this->RTEtsConfigParams()) . '";
934 RTEarea[editornumber].number = editornumber;
935 RTEarea[editornumber].deleted = false;
936 RTEarea[editornumber].textAreaId = "' . $textAreaId . '";
937 RTEarea[editornumber].id = "RTEarea" + editornumber;
938 RTEarea[editornumber].RTEWidthOverride = "' . (is_object($GLOBALS['BE_USER']) && isset($GLOBALS['BE_USER']->uc['rteWidth']) && trim($GLOBALS['BE_USER']->uc['rteWidth']) ? trim($GLOBALS['BE_USER']->uc['rteWidth']) : trim($this->thisConfig['RTEWidthOverride'])) . '";
939 RTEarea[editornumber].RTEHeightOverride = "' . (is_object($GLOBALS['BE_USER']) && isset($GLOBALS['BE_USER']->uc['rteHeight']) && (int)$GLOBALS['BE_USER']->uc['rteHeight'] ? (int)$GLOBALS['BE_USER']->uc['rteHeight'] : (int)$this->thisConfig['RTEHeightOverride']) . '";
940 RTEarea[editornumber].resizable = ' . (is_object($GLOBALS['BE_USER']) && isset($GLOBALS['BE_USER']->uc['rteResize']) && $GLOBALS['BE_USER']->uc['rteResize'] ? 'true' : (trim($this->thisConfig['rteResize']) ? 'true' : 'false')) . ';
941 RTEarea[editornumber].maxHeight = "' . (is_object($GLOBALS['BE_USER']) && isset($GLOBALS['BE_USER']->uc['rteMaxHeight']) && (int)$GLOBALS['BE_USER']->uc['rteMaxHeight'] ? trim($GLOBALS['BE_USER']->uc['rteMaxHeight']) : ((int)$this->thisConfig['rteMaxHeight'] ?: '2000')) . '";
942 RTEarea[editornumber].fullScreen = ' . ($this->fullScreen ? 'true' : 'false') . ';
943 RTEarea[editornumber].showStatusBar = ' . (trim($this->thisConfig['showStatusBar']) ? 'true' : 'false') . ';
944 RTEarea[editornumber].enableWordClean = ' . (trim($this->thisConfig['enableWordClean']) ? 'true' : 'false') . ';
945 RTEarea[editornumber].htmlRemoveComments = ' . (trim($this->thisConfig['removeComments']) ? 'true' : 'false') . ';
946 RTEarea[editornumber].disableEnterParagraphs = ' . (trim($this->thisConfig['disableEnterParagraphs']) ? 'true' : 'false') . ';
947 RTEarea[editornumber].disableObjectResizing = ' . (trim($this->thisConfig['disableObjectResizing']) ? 'true' : 'false') . ';
948 RTEarea[editornumber].removeTrailingBR = ' . (trim($this->thisConfig['removeTrailingBR']) ? 'true' : 'false') . ';
949 RTEarea[editornumber].useCSS = ' . (trim($this->thisConfig['useCSS']) ? 'true' : 'false') . ';
950 RTEarea[editornumber].keepButtonGroupTogether = ' . (trim($this->thisConfig['keepButtonGroupTogether']) ? 'true' : 'false') . ';
951 RTEarea[editornumber].disablePCexamples = ' . (trim($this->thisConfig['disablePCexamples']) ? 'true' : 'false') . ';
952 RTEarea[editornumber].showTagFreeClasses = ' . (trim($this->thisConfig['showTagFreeClasses']) ? 'true' : 'false') . ';
953 RTEarea[editornumber].useHTTPS = ' . (trim(stristr($this->siteURL, 'https')) || $this->thisConfig['forceHTTPS'] ? 'true' : 'false') . ';
954 RTEarea[editornumber].tceformsNested = ' . (is_object($this->TCEform) && method_exists($this->TCEform, 'getDynNestedStack') ? $this->TCEform->getDynNestedStack(TRUE) : '[]') . ';
955 RTEarea[editornumber].dialogueWindows = new Object();';
956 if (isset($this->thisConfig['dialogueWindows.']['defaultPositionFromTop'])) {
957 $configureRTEInJavascriptString .= '
958 RTEarea[editornumber].dialogueWindows.positionFromTop = ' . (int)$this->thisConfig['dialogueWindows.']['defaultPositionFromTop'] . ';';
959 }
960 if (isset($this->thisConfig['dialogueWindows.']['defaultPositionFromLeft'])) {
961 $configureRTEInJavascriptString .= '
962 RTEarea[editornumber].dialogueWindows.positionFromLeft = ' . (int)$this->thisConfig['dialogueWindows.']['defaultPositionFromLeft'] . ';';
963 }
964 // The following properties apply only to the backend
965 if (!$this->is_FE()) {
966 $configureRTEInJavascriptString .= '
967 RTEarea[editornumber].sys_language_content = "' . $this->contentLanguageUid . '";
968 RTEarea[editornumber].typo3ContentLanguage = "' . $this->contentTypo3Language . '";
969 RTEarea[editornumber].typo3ContentCharset = "' . $this->contentCharset . '";
970 RTEarea[editornumber].userUid = "' . $this->userUid . '";';
971 }
972 // Setting the plugin flags
973 $configureRTEInJavascriptString .= '
974 RTEarea[editornumber].plugin = new Object();
975 RTEarea[editornumber].pathToPluginDirectory = new Object();';
976 foreach ($this->pluginEnabledArray as $pluginId) {
977 $configureRTEInJavascriptString .= '
978 RTEarea[editornumber].plugin.' . $pluginId . ' = true;';
979 if (is_object($this->registeredPlugins[$pluginId])) {
980 $pathToPluginDirectory = $this->registeredPlugins[$pluginId]->getPathToPluginDirectory();
981 if ($pathToPluginDirectory) {
982 $configureRTEInJavascriptString .= '
983 RTEarea[editornumber].pathToPluginDirectory.' . $pluginId . ' = "' . $pathToPluginDirectory . '";';
984 }
985 }
986 }
987 // Setting the buttons configuration
988 $configureRTEInJavascriptString .= '
989 RTEarea[editornumber].buttons = new Object();';
990 if (is_array($this->thisConfig['buttons.'])) {
991 foreach ($this->thisConfig['buttons.'] as $buttonIndex => $conf) {
992 $button = substr($buttonIndex, 0, -1);
993 if (is_array($conf)) {
994 $configureRTEInJavascriptString .= '
995 RTEarea[editornumber].buttons.' . $button . ' = ' . $this->buildNestedJSArray($conf) . ';';
996 }
997 }
998 }
999 // Setting the list of tags to be removed if specified in the RTE config
1000 if (trim($this->thisConfig['removeTags'])) {
1001 $configureRTEInJavascriptString .= '
1002 RTEarea[editornumber].htmlRemoveTags = /^(' . implode('|', GeneralUtility::trimExplode(',', $this->thisConfig['removeTags'], TRUE)) . ')$/i;';
1003 }
1004 // Setting the list of tags to be removed with their contents if specified in the RTE config
1005 if (trim($this->thisConfig['removeTagsAndContents'])) {
1006 $configureRTEInJavascriptString .= '
1007 RTEarea[editornumber].htmlRemoveTagsAndContents = /^(' . implode('|', GeneralUtility::trimExplode(',', $this->thisConfig['removeTagsAndContents'], TRUE)) . ')$/i;';
1008 }
1009 // Setting array of custom tags if specified in the RTE config
1010 if (!empty($this->thisConfig['customTags'])) {
1011 $customTags = GeneralUtility::trimExplode(',', $this->thisConfig['customTags'], TRUE);
1012 if (!empty($customTags)) {
1013 $configureRTEInJavascriptString .= '
1014 RTEarea[editornumber].customTags= ' . json_encode($customTags) . ';';
1015 }
1016 }
1017 // Setting array of content css files if specified in the RTE config
1018 $versionNumberedFileNames = array();
1019 $contentCssFileNames = $this->getContentCssFileNames();
1020 foreach ($contentCssFileNames as $contentCssFileName) {
1021 $versionNumberedFileNames[] = GeneralUtility::createVersionNumberedFilename($contentCssFileName);
1022 }
1023 $configureRTEInJavascriptString .= '
1024 RTEarea[editornumber].pageStyle = ["' . implode('","', $versionNumberedFileNames) . '"];';
1025 // Process classes configuration
1026 $classesConfigurationRequired = FALSE;
1027 foreach ($this->registeredPlugins as $pluginId => $plugin) {
1028 if ($this->isPluginEnabled($pluginId)) {
1029 $classesConfigurationRequired = $classesConfigurationRequired || $plugin->requiresClassesConfiguration();
1030 }
1031 }
1032 if ($classesConfigurationRequired) {
1033 $configureRTEInJavascriptString .= $this->buildJSClassesConfig($RTEcounter);
1034 }
1035 // Add Javascript configuration for registered plugins
1036 foreach ($this->registeredPlugins as $pluginId => $plugin) {
1037 if ($this->isPluginEnabled($pluginId)) {
1038 $configureRTEInJavascriptString .= $plugin->buildJavascriptConfiguration('editornumber');
1039 }
1040 }
1041 // Avoid premature reference to HTMLArea when being initially loaded by IRRE Ajax call
1042 $configureRTEInJavascriptString .= '
1043 RTEarea[editornumber].toolbar = ' . $this->getJSToolbarArray() . ';
1044 RTEarea[editornumber].convertButtonId = ' . json_encode(array_flip($this->convertToolbarForHtmlAreaArray)) . ';
1045 RTEarea.initEditor(editornumber);
1046 }
1047 };
1048 configureEditorInstance["' . $textAreaId . '"]();';
1049 return $configureRTEInJavascriptString;
1050 }
1051
1052 /**
1053 * Return TRUE, if the plugin can be loaded
1054 *
1055 * @param string $pluginId: The identification string of the plugin
1056 * @return bool TRUE if the plugin can be loaded
1057 */
1058 public function isPluginEnabled($pluginId) {
1059 return in_array($pluginId, $this->pluginEnabledArray);
1060 }
1061
1062 /**
1063 * Return Javascript configuration of classes
1064 *
1065 * @param int $RTEcounter: The index number of the current RTE editing area within the form.
1066 * @return string Javascript configuration of classes
1067 */
1068 public function buildJSClassesConfig($RTEcounter) {
1069 // Include JS arrays of configured classes
1070 $configureRTEInJavascriptString = '
1071 RTEarea[editornumber].classesUrl = "' . ($this->is_FE() && $GLOBALS['TSFE']->absRefPrefix ? $GLOBALS['TSFE']->absRefPrefix : '') . $this->writeTemporaryFile('', ('classes_' . $this->language), 'js', $this->buildJSClassesArray(), TRUE) . '";';
1072 return $configureRTEInJavascriptString;
1073 }
1074
1075 /**
1076 * Return JS arrays of classes configuration
1077 *
1078 * @return string JS classes arrays
1079 */
1080 public function buildJSClassesArray() {
1081 if ($this->is_FE()) {
1082 $RTEProperties = $this->RTEsetup;
1083 } else {
1084 $RTEProperties = $this->RTEsetup['properties'];
1085 }
1086 // Declare sub-arrays
1087 $classesArray = array(
1088 'labels' => array(),
1089 'values' => array(),
1090 'noShow' => array(),
1091 'alternating' => array(),
1092 'counting' => array(),
1093 'selectable' => array(),
1094 'requires' => array(),
1095 'requiredBy' => array(),
1096 'XOR' => array()
1097 );
1098 $JSClassesArray = '';
1099 // Scanning the list of classes if specified in the RTE config
1100 if (is_array($RTEProperties['classes.'])) {
1101 foreach ($RTEProperties['classes.'] as $className => $conf) {
1102 $className = rtrim($className, '.');
1103 $classesArray['labels'][$className] = trim($conf['name']) ? $this->getPageConfigLabel($conf['name'], FALSE) : '';
1104 $classesArray['values'][$className] = str_replace('\\\'', '\'', $conf['value']);
1105 if (isset($conf['noShow'])) {
1106 $classesArray['noShow'][$className] = $conf['noShow'];
1107 }
1108 if (is_array($conf['alternating.'])) {
1109 $classesArray['alternating'][$className] = $conf['alternating.'];
1110 }
1111 if (is_array($conf['counting.'])) {
1112 $classesArray['counting'][$className] = $conf['counting.'];
1113 }
1114 if (isset($conf['selectable'])) {
1115 $classesArray['selectable'][$className] = $conf['selectable'];
1116 }
1117 if (isset($conf['requires'])) {
1118 $classesArray['requires'][$className] = explode(',', GeneralUtility::rmFromList($className, $this->cleanList($conf['requires'])));
1119 }
1120 }
1121 // Remove circularities from classes dependencies
1122 $requiringClasses = array_keys($classesArray['requires']);
1123 foreach ($requiringClasses as $requiringClass) {
1124 if ($this->hasCircularDependency($classesArray, $requiringClass, $requiringClass)) {
1125 unset($classesArray['requires'][$requiringClass]);
1126 }
1127 }
1128 // Reverse relationship for the dependency checks when removing styles
1129 $requiringClasses = array_keys($classesArray['requires']);
1130 foreach ($requiringClasses as $className) {
1131 foreach ($classesArray['requires'][$className] as $requiredClass) {
1132 if (!is_array($classesArray['requiredBy'][$requiredClass])) {
1133 $classesArray['requiredBy'][$requiredClass] = array();
1134 }
1135 if (!in_array($className, $classesArray['requiredBy'][$requiredClass])) {
1136 $classesArray['requiredBy'][$requiredClass][] = $className;
1137 }
1138 }
1139 }
1140 }
1141 // Scanning the list of sets of mutually exclusives classes if specified in the RTE config
1142 if (is_array($RTEProperties['mutuallyExclusiveClasses.'])) {
1143 foreach ($RTEProperties['mutuallyExclusiveClasses.'] as $listName => $conf) {
1144 $classSet = GeneralUtility::trimExplode(',', $conf, TRUE);
1145 $classList = implode(',', $classSet);
1146 foreach ($classSet as $className) {
1147 $classesArray['XOR'][$className] = '/^(' . implode('|', GeneralUtility::trimExplode(',', GeneralUtility::rmFromList($className, $classList), TRUE)) . ')$/';
1148 }
1149 }
1150 }
1151 foreach ($classesArray as $key => $subArray) {
1152 $JSClassesArray .= 'HTMLArea.classes' . ucfirst($key) . ' = ' . $this->buildNestedJSArray($subArray) . ';' . LF;
1153 }
1154 return $JSClassesArray;
1155 }
1156
1157 /**
1158 * Check for possible circularity in classes dependencies
1159 *
1160 * @param array $classesArray: reference to the array of classes dependencies
1161 * @param string $requiringClass: class requiring at some iteration level from the initial requiring class
1162 * @param string $initialClass: initial class from which a circular relationship is being searched
1163 * @param int $recursionLevel: depth of recursive call
1164 * @return boolean TRUE, if a circular relationship is found
1165 */
1166 protected function hasCircularDependency(&$classesArray, $requiringClass, $initialClass, $recursionLevel = 0) {
1167 if (is_array($classesArray['requires'][$requiringClass])) {
1168 if (in_array($initialClass, $classesArray['requires'][$requiringClass])) {
1169 return TRUE;
1170 } else {
1171 if ($recursionLevel++ < 20) {
1172 foreach ($classesArray['requires'][$requiringClass] as $requiringClass2) {
1173 if ($this->hasCircularDependency($classesArray, $requiringClass2, $initialClass, $recursionLevel)) {
1174 return TRUE;
1175 }
1176 }
1177 }
1178 return FALSE;
1179 }
1180 } else {
1181 return FALSE;
1182 }
1183 }
1184
1185 /**
1186 * Translate Page TS Config array in JS nested array definition
1187 * Replace 0 values with false
1188 * Unquote regular expression values
1189 * Replace empty arrays with empty objects
1190 *
1191 * @param array $conf: Page TSConfig configuration array
1192 * @return string nested JS array definition
1193 */
1194 public function buildNestedJSArray($conf) {
1195 $convertedConf = GeneralUtility::removeDotsFromTS($conf);
1196 return str_replace(array(':"0"', ':"\\/^(', ')$\\/i"', ':"\\/^(', ')$\\/"', '[]'), array(':false', ':/^(', ')$/i', ':/^(', ')$/', '{}'), json_encode($convertedConf));
1197 }
1198
1199 /**
1200 * Return a Javascript localization array for htmlArea RTE
1201 *
1202 * @return string Javascript localization array
1203 */
1204 public function buildJSMainLangArray() {
1205 $JSLanguageArray = 'HTMLArea.I18N = new Object();' . LF;
1206 $labelsArray = array('tooltips' => array(), 'msg' => array(), 'dialogs' => array());
1207 foreach ($labelsArray as $labels => $subArray) {
1208 $LOCAL_LANG = GeneralUtility::readLLfile('EXT:' . $this->ID . '/htmlarea/locallang_' . $labels . '.xlf', $this->language, 'utf-8');
1209 if (!empty($LOCAL_LANG[$this->language])) {
1210 $mergedLocalLang = $LOCAL_LANG['default'];
1211 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($mergedLocalLang, $LOCAL_LANG[$this->language], TRUE, FALSE);
1212 $LOCAL_LANG[$this->language] = $mergedLocalLang;
1213 } else {
1214 $LOCAL_LANG[$this->language] = $LOCAL_LANG['default'];
1215 }
1216 $labelsArray[$labels] = $LOCAL_LANG[$this->language];
1217 }
1218 $JSLanguageArray .= 'HTMLArea.I18N = ' . json_encode($labelsArray) . ';' . LF;
1219 return $JSLanguageArray;
1220 }
1221
1222 /**
1223 * Writes contents in a file in typo3temp/rtehtmlarea directory and returns the file name
1224 *
1225 * @param string $sourceFileName: The name of the file from which the contents should be extracted
1226 * @param string $label: A label to insert at the beginning of the name of the file
1227 * @param string $fileExtension: The file extension of the file, defaulting to 'js'
1228 * @param string $contents: The contents to write into the file if no $sourceFileName is provided
1229 * @param bool $concatenate Not used anymore
1230 * @return string The name of the file writtten to typo3temp/rtehtmlarea
1231 */
1232 public function writeTemporaryFile($sourceFileName = '', $label, $fileExtension = 'js', $contents = '', $concatenate = FALSE) {
1233 if ($sourceFileName) {
1234 $output = '';
1235 $source = GeneralUtility::getFileAbsFileName($sourceFileName);
1236 $output = file_get_contents($source);
1237 } else {
1238 $output = $contents;
1239 }
1240 $relativeFilename = 'typo3temp/' . $this->ID . '_' . str_replace('-', '_', $label) . '_' . GeneralUtility::shortMD5((TYPO3_version . $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->ID]['version'] . ($sourceFileName ? $sourceFileName : $output)), 20) . '.' . $fileExtension;
1241 $destination = PATH_site . $relativeFilename;
1242 if (!file_exists($destination)) {
1243 $minifiedJavaScript = '';
1244 if ($fileExtension == 'js' && $output != '') {
1245 $minifiedJavaScript = GeneralUtility::minifyJavaScript($output);
1246 }
1247 $failure = GeneralUtility::writeFileToTypo3tempDir($destination, $minifiedJavaScript ? $minifiedJavaScript : $output);
1248 if ($failure) {
1249 throw new \RuntimeException($failure, 1294585668);
1250 }
1251 }
1252 if ($this->is_FE()) {
1253 $filename = $relativeFilename;
1254 } else {
1255 $filename = ($this->isFrontendEditActive() ? '' : $this->backPath . '../') . $relativeFilename;
1256 }
1257 return GeneralUtility::resolveBackPath($filename);
1258 }
1259
1260 /**
1261 * Return a file name containing the main JS language array for HTMLArea
1262 *
1263 * @param int $RTEcounter: The index number of the current RTE editing area within the form.
1264 * @return string filename
1265 */
1266 public function buildJSMainLangFile($RTEcounter) {
1267 $contents = $this->buildJSMainLangArray() . LF;
1268 foreach ($this->pluginEnabledCumulativeArray[$RTEcounter] as $pluginId) {
1269 $contents .= $this->buildJSLangArray($pluginId) . LF;
1270 }
1271 return $this->writeTemporaryFile('', $this->language . '_' . $this->OutputCharset, 'js', $contents, TRUE);
1272 }
1273
1274 /**
1275 * Return a Javascript localization array for the plugin
1276 *
1277 * @param string $plugin: identification string of the plugin
1278 * @return string Javascript localization array
1279 */
1280 public function buildJSLangArray($plugin) {
1281 $extensionKey = is_object($this->registeredPlugins[$plugin]) ? $this->registeredPlugins[$plugin]->getExtensionKey() : $this->ID;
1282 $LOCAL_LANG = GeneralUtility::readLLfile('EXT:' . $extensionKey . '/htmlarea/plugins/' . $plugin . '/locallang.xlf', $this->language, 'utf-8', 1);
1283 $JSLanguageArray = 'HTMLArea.I18N["' . $plugin . '"] = new Object();' . LF;
1284 if (is_array($LOCAL_LANG)) {
1285 if (!empty($LOCAL_LANG[$this->language])) {
1286 $defaultLocalLang = $LOCAL_LANG['default'];
1287 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($defaultLocalLang, $LOCAL_LANG[$this->language], TRUE, FALSE);
1288 $LOCAL_LANG[$this->language] = $defaultLocalLang;
1289 } else {
1290 $LOCAL_LANG[$this->language] = $LOCAL_LANG['default'];
1291 }
1292 $JSLanguageArray .= 'HTMLArea.I18N["' . $plugin . '"] = ' . json_encode($LOCAL_LANG[$this->language]) . ';' . LF;
1293 }
1294 return $JSLanguageArray;
1295 }
1296
1297 /**
1298 * Return the JS code of the toolbar configuration for the HTMLArea editor
1299 *
1300 * @return string the JS code as nested JS arrays
1301 */
1302 protected function getJSToolbarArray() {
1303 // The toolbar array
1304 $toolbar = array();
1305 // The current row; a "linebreak" ends the current row
1306 $row = array();
1307 // The current group; each group is between "bar"s; a "linebreak" ends the current group
1308 $group = array();
1309 // Process each toolbar item in the toolbar order list
1310 foreach ($this->toolbarOrderArray as $item) {
1311 switch ($item) {
1312 case 'linebreak':
1313 // Add row to toolbar if not empty
1314 if (!empty($group)) {
1315 $row[] = $group;
1316 $group = array();
1317 }
1318 if (!empty($row)) {
1319 $toolbar[] = $row;
1320 $row = array();
1321 }
1322 break;
1323 case 'bar':
1324 // Add group to row if not empty
1325 if (!empty($group)) {
1326 $row[] = $group;
1327 $group = array();
1328 }
1329 break;
1330 case 'space':
1331 if (end($group) != $this->convertToolbarForHTMLArea($item)) {
1332 $group[] = $this->convertToolbarForHTMLArea($item);
1333 }
1334 break;
1335 default:
1336 if (in_array($item, $this->toolbar)) {
1337 // Add the item to the group
1338 $convertedItem = $this->convertToolbarForHTMLArea($item);
1339 if ($convertedItem) {
1340 $group[] = $convertedItem;
1341 }
1342 }
1343 }
1344 }
1345 // Add the last group and last line, if not empty
1346 if (!empty($group)) {
1347 $row[] = $group;
1348 }
1349 if (!empty($row)) {
1350 $toolbar[] = $row;
1351 }
1352 return json_encode($toolbar);
1353 }
1354
1355 /**
1356 * Localize a string using the language of the content element rather than the language of the BE interface
1357 *
1358 * @param string string: the label to be localized
1359 * @return string Localized string.
1360 */
1361 public function getLLContent($string) {
1362 return GeneralUtility::quoteJSvalue($this->contentLanguageService->sL($string));
1363 }
1364
1365 public function getPageConfigLabel($string, $JScharCode = 1) {
1366 if ($this->is_FE()) {
1367 if (substr($string, 0, 4) !== 'LLL:') {
1368 // A pure string coming from Page TSConfig must be in utf-8
1369 $label = $GLOBALS['TSFE']->csConvObj->conv($GLOBALS['TSFE']->sL(trim($string)), 'utf-8', $this->OutputCharset);
1370 } else {
1371 $label = $GLOBALS['TSFE']->csConvObj->conv($GLOBALS['TSFE']->sL(trim($string)), $this->charset, $this->OutputCharset);
1372 }
1373 $label = str_replace('"', '\\"', str_replace('\\\'', '\'', $label));
1374 $label = $JScharCode ? $this->feJScharCode($label) : $label;
1375 } else {
1376 if (substr($string, 0, 4) !== 'LLL:') {
1377 $label = $string;
1378 } else {
1379 $label = $GLOBALS['LANG']->sL(trim($string));
1380 }
1381 $label = str_replace('"', '\\"', str_replace('\\\'', '\'', $label));
1382 $label = $JScharCode ? GeneralUtility::quoteJSvalue($label) : $label;
1383 }
1384 return $label;
1385 }
1386
1387 /**
1388 * JavaScript char code
1389 *
1390 * @param string $str
1391 * @return string
1392 */
1393 public function feJScharCode($str) {
1394 // Convert string to UTF-8:
1395 if ($this->OutputCharset != 'utf-8') {
1396 $str = $GLOBALS['TSFE']->csConvObj->utf8_encode($str, $this->OutputCharset);
1397 }
1398 // Convert the UTF-8 string into a 'JavaScript-safe' encoded string:
1399 return GeneralUtility::quoteJSvalue($str);
1400 }
1401
1402 /**
1403 * Make a file name relative to the PATH_site or to the PATH_typo3
1404 *
1405 * @param string $filename: a file name of the form EXT:.... or relative to the PATH_site
1406 * @return string the file name relative to the PATH_site if in frontend or relative to the PATH_typo3 if in backend
1407 */
1408 public function getFullFileName($filename) {
1409 if (substr($filename, 0, 4) == 'EXT:') {
1410 // extension
1411 list($extKey, $local) = explode('/', substr($filename, 4), 2);
1412 $newFilename = '';
1413 if ((string)$extKey !== '' && ExtensionManagementUtility::isLoaded($extKey) && (string)$local !== '') {
1414 $newFilename = ($this->is_FE() || $this->isFrontendEditActive() ? ExtensionManagementUtility::siteRelPath($extKey) : $this->backPath . ExtensionManagementUtility::extRelPath($extKey)) . $local;
1415 }
1416 } else {
1417 $path = ($this->is_FE() || $this->isFrontendEditActive() ? '' : $this->backPath . '../');
1418 $newFilename = $path . ($filename[0] === '/' ? substr($filename, 1) : $filename);
1419 }
1420 return GeneralUtility::resolveBackPath($newFilename);
1421 }
1422
1423 /**
1424 * Return the Javascript code for copying the HTML code from the editor into the hidden input field.
1425 * This is for submit function of the form.
1426 *
1427 * @param int $RTEcounter: The index number of the current RTE editing area within the form.
1428 * @param string $formName: the name of the form
1429 * @param string $textareaId: the id of the textarea
1430 * @param string $textareaName: the name of the textarea
1431 * @return string Javascript code
1432 */
1433 public function setSaveRTE($RTEcounter, $formName, $textareaId, $textareaName) {
1434 return 'if (RTEarea["' . $textareaId . '"]) { document.' . $formName . '["' . $textareaName . '"].value = RTEarea["' . $textareaId . '"].editor.getHTML(); } else { OK = 0; };';
1435 }
1436
1437 /**
1438 * Return the Javascript code for copying the HTML code from the editor into the hidden input field.
1439 * This is for submit function of the form.
1440 *
1441 * @param int $RTEcounter: The index number of the current RTE editing area within the form.
1442 * @param string $formName: the name of the form
1443 * @param string $textareaId: the id of the textarea
1444 * @return string Javascript code
1445 */
1446 public function setDeleteRTE($RTEcounter, $formName, $textareaId) {
1447 return 'if (RTEarea["' . $textareaId . '"]) { RTEarea["' . $textareaId . '"].deleted = true;}';
1448 }
1449
1450 /**
1451 * Return TRUE if we are in the FE, but not in the FE editing feature of BE.
1452 *
1453 * @return bool
1454 */
1455 public function is_FE() {
1456 return is_object($GLOBALS['TSFE']) && !$this->isFrontendEditActive() && TYPO3_MODE == 'FE';
1457 }
1458
1459 /**
1460 * Checks whether frontend editing is active.
1461 *
1462 * @return boolean
1463 */
1464 public function isFrontendEditActive() {
1465 return is_object($GLOBALS['TSFE']) && $GLOBALS['TSFE']->beUserLogin && $GLOBALS['BE_USER']->frontendEdit instanceof \TYPO3\CMS\Core\FrontendEditing\FrontendEditingController;
1466 }
1467
1468 /**
1469 * Client Browser Information
1470 *
1471 * @param string $userAgent: The useragent string, \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('HTTP_USER_AGENT')
1472 * @return array Contains keys "useragent", "browser", "version", "system
1473 */
1474 public function clientInfo($userAgent = '') {
1475 if (!$userAgent) {
1476 $userAgent = GeneralUtility::getIndpEnv('HTTP_USER_AGENT');
1477 }
1478 $browserInfo = \TYPO3\CMS\Core\Utility\ClientUtility::getBrowserInfo($userAgent);
1479 // Known engines: order is not irrelevant!
1480 $knownEngines = array('opera', 'msie', 'gecko', 'webkit');
1481 if (is_array($browserInfo['all'])) {
1482 foreach ($knownEngines as $engine) {
1483 if ($browserInfo['all'][$engine]) {
1484 $browserInfo['browser'] = $engine;
1485 $browserInfo['version'] = \TYPO3\CMS\Core\Utility\ClientUtility::getVersion($browserInfo['all'][$engine]);
1486 break;
1487 }
1488 }
1489 }
1490 return $browserInfo;
1491 }
1492
1493 /**
1494 * Log usage of deprecated Page TS Config Property
1495 *
1496 * @param string $deprecatedProperty: Name of deprecated property
1497 * @param string $useProperty: Name of property to use instead
1498 * @param string $version: Version of TYPO3 in which the property will be removed
1499 * @return void
1500 */
1501 public function logDeprecatedProperty($deprecatedProperty, $useProperty, $version) {
1502 if (!$this->thisConfig['logDeprecatedProperties.']['disabled']) {
1503 $message = sprintf('RTE Page TSConfig property "%1$s" used on page id #%4$s is DEPRECATED and will be removed in TYPO3 %3$s. Use "%2$s" instead.', $deprecatedProperty, $useProperty, $version, $this->thePid);
1504 GeneralUtility::deprecationLog($message);
1505 if (is_object($GLOBALS['BE_USER']) && $this->thisConfig['logDeprecatedProperties.']['logAlsoToBELog']) {
1506 $message = sprintf($GLOBALS['LANG']->getLL('deprecatedPropertyMessage'), $deprecatedProperty, $useProperty, $version, $this->thePid);
1507 $GLOBALS['BE_USER']->simplelog($message, $this->ID);
1508 }
1509 }
1510 }
1511
1512 /***************************
1513 *
1514 * OTHER FUNCTIONS: (from Classic RTE)
1515 *
1516 ***************************/
1517 /**
1518 * @return string
1519 */
1520 public function RTEtsConfigParams() {
1521 if ($this->is_FE()) {
1522 return '';
1523 } else {
1524 $p = BackendUtility::getSpecConfParametersFromArray($this->specConf['rte_transform']['parameters']);
1525 return $this->elementParts[0] . ':' . $this->elementParts[1] . ':' . $this->elementParts[2] . ':' . $this->thePid . ':' . $this->typeVal . ':' . $this->tscPID . ':' . $p['imgpath'];
1526 }
1527 }
1528
1529 /**
1530 * Clean list
1531 *
1532 * @param string $str
1533 * @return string
1534 */
1535 public function cleanList($str) {
1536 if (strstr($str, '*')) {
1537 $str = '*';
1538 } else {
1539 $str = implode(',', array_unique(GeneralUtility::trimExplode(',', $str, TRUE)));
1540 }
1541 return $str;
1542 }
1543
1544 /**
1545 * Filter style element
1546 *
1547 * @param string $elValue
1548 * @param string $matchList
1549 * @return string
1550 */
1551 public function filterStyleEl($elValue, $matchList) {
1552 $matchParts = GeneralUtility::trimExplode(',', $matchList, TRUE);
1553 $styleParts = explode(';', $elValue);
1554 $nStyle = array();
1555 foreach ($styleParts as $k => $p) {
1556 $pp = GeneralUtility::trimExplode(':', $p);
1557 if ($pp[0] && $pp[1]) {
1558 foreach ($matchParts as $el) {
1559 $star = substr($el, -1) == '*';
1560 if ($pp[0] === (string)$el || $star && GeneralUtility::isFirstPartOfStr($pp[0], substr($el, 0, -1))) {
1561 $nStyle[] = $pp[0] . ':' . $pp[1];
1562 } else {
1563 unset($styleParts[$k]);
1564 }
1565 }
1566 } else {
1567 unset($styleParts[$k]);
1568 }
1569 }
1570 return implode('; ', $nStyle);
1571 }
1572 }