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