2 namespace TYPO3\CMS\Rtehtmlarea\Form\Element
;
5 * This file is part of the TYPO3 CMS project.
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.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use TYPO3\CMS\Backend\Form\Element\AbstractFormElement
;
18 use TYPO3\CMS\Backend\Form\InlineStackProcessor
;
19 use TYPO3\CMS\Backend\Utility\BackendUtility
;
20 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication
;
21 use TYPO3\CMS\Core\Database\DatabaseConnection
;
22 use TYPO3\CMS\Core\FrontendEditing\FrontendEditingController
;
23 use TYPO3\CMS\Core\Html\RteHtmlParser
;
24 use TYPO3\CMS\Core\Localization\Locales
;
25 use TYPO3\CMS\Core\Localization\LocalizationFactory
;
26 use TYPO3\CMS\Core\Utility\ArrayUtility
;
27 use TYPO3\CMS\Core\Utility\ClientUtility
;
28 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility
;
29 use TYPO3\CMS\Core\Utility\GeneralUtility
;
30 use TYPO3\CMS\Lang\LanguageService
;
31 use TYPO3\CMS\Rtehtmlarea\RteHtmlAreaApi
;
34 * Render rich text editor in FormEngine
36 class RichTextElement
extends AbstractFormElement
39 * Main result array as defined in initializeResultArray() of AbstractNode
43 protected $resultArray;
46 * pid of page record the TSconfig is located at.
47 * This is pid of record if table is not pages, or uid if table is pages
51 protected $pidOfPageRecord;
54 * pid of fixed versioned record.
55 * This is the pid of the record in normal cases, but is changed to the pid
56 * of the "mother" record in case the handled record is a versioned overlay
57 * and "mother" is located at a different pid.
61 protected $pidOfVersionedMotherRecord;
64 * Native, not further processed TsConfig of RTE section for this record on given pid.
73 * 'properties' => array(
80 protected $vanillaRteTsConfig;
83 * Based on $vanillaRteTsConfig, this property contains "processed" configuration
84 * where table and type specific RTE setup is merged into 'default.' array.
88 protected $processedRteConfiguration;
91 * An unique identifier based on field name to have id attributes in HTML referenced in javascript.
95 protected $domIdentifier;
98 * Parsed "defaultExtras" TCA
102 protected $defaultExtras;
105 * Some client info containing "user agent", "browser", "version", "system"
119 * TYPO3 language code of the content language
123 protected $contentTypo3Language;
126 * ISO language code of the content language
130 protected $contentISOLanguage;
133 * Uid of chosen content language
137 protected $contentLanguageUid;
140 * The order of the toolbar: the name is the TYPO3-button name
144 protected $defaultToolbarOrder;
147 * Conversion array: TYPO3 button names to htmlArea button names
151 protected $convertToolbarForHtmlAreaArray = array(
153 'bar' => 'separator',
154 'linebreak' => 'linebreak'
158 * Final toolbar array
162 protected $toolbar = array();
165 * Save the buttons for the toolbar
169 protected $toolbarOrderArray = array();
176 protected $pluginButton = array();
183 protected $pluginLabel = array();
186 * Array of plugin id's enabled in the current RTE editing area
190 protected $pluginEnabledArray = array();
193 * Cumulative array of plugin id's enabled so far in any of the RTE editing areas of the form
197 protected $pluginEnabledCumulativeArray = array();
200 * Array of registered plugins indexed by their plugin Id's
204 protected $registeredPlugins = array();
207 * This will render a <textarea> OR RTE area form field,
208 * possibly with various control/validation features
210 * @return array As defined in initializeResultArray() of AbstractNode
212 public function render()
214 $table = $this->data
['tableName'];
215 $fieldName = $this->data
['fieldName'];
216 $row = $this->data
['databaseRow'];
217 $parameterArray = $this->data
['parameterArray'];
219 $backendUser = $this->getBackendUserAuthentication();
221 $this->resultArray
= $this->initializeResultArray();
222 $this->defaultExtras
= BackendUtility
::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
223 $this->pidOfPageRecord
= $this->data
['effectivePid'];
224 BackendUtility
::fixVersioningPid($table, $row);
225 $this->pidOfVersionedMotherRecord
= (int)$row['pid'];
226 $this->vanillaRteTsConfig
= $backendUser->getTSConfig('RTE', BackendUtility
::getPagesTSconfig($this->pidOfPageRecord
));
227 $this->processedRteConfiguration
= BackendUtility
::RTEsetup(
228 $this->vanillaRteTsConfig
['properties'],
231 $this->data
['recordTypeValue']
233 $this->client
= $this->clientInfo();
234 $this->domIdentifier
= preg_replace('/[^a-zA-Z0-9_:.-]/', '_', $parameterArray['itemFormElName']);
235 $this->domIdentifier
= htmlspecialchars(preg_replace('/^[^a-zA-Z]/', 'x', $this->domIdentifier
));
237 $this->initializeLanguageRelatedProperties();
239 // Get skin file name from Page TSConfig if any
240 $skinFilename = trim($this->processedRteConfiguration
['skin']) ?
: 'EXT:rtehtmlarea/Resources/Public/Css/Skin/htmlarea.css';
241 $skinFilename = $this->getFullFileName($skinFilename);
242 $skinDirectory = dirname($skinFilename);
244 // jQuery UI Resizable style sheet and main skin stylesheet
245 $this->resultArray
['stylesheetFiles'][] = $skinDirectory . '/jquery-ui-resizable.css';
246 $this->resultArray
['stylesheetFiles'][] = $skinFilename;
248 $this->enableRegisteredPlugins();
253 // Check if some plugins need to be disabled
256 // Merge the list of enabled plugins with the lists from the previous RTE editing areas on the same form
257 $this->pluginEnabledCumulativeArray
= $this->pluginEnabledArray
;
259 $this->addInstanceJavaScriptRegistration();
261 $this->addOnSubmitJavaScriptCode();
263 // Add RTE JavaScript
264 $this->loadRequireModulesForRTE();
266 // Create language labels
267 $this->createJavaScriptLanguageLabelsFromFiles();
269 // Get RTE init JS code
270 $this->resultArray
['additionalJavaScriptPost'][] = $this->getRteInitJsCode();
272 $html = $this->getMainHtml();
274 $this->resultArray
['html'] = $this->renderWizards(
276 $parameterArray['fieldConf']['config']['wizards'],
281 $parameterArray['itemFormElName'],
282 $this->defaultExtras
,
286 return $this->resultArray
;
290 * Create main HTML elements
292 * @return string Main RTE html
294 protected function getMainHtml()
296 $backendUser = $this->getBackendUserAuthentication();
298 if ($this->isInFullScreenMode()) {
301 $paddingRight = '0px';
302 $editorWrapWidth = '100%';
304 $options = $backendUser->userTS
['options.'];
305 $width = 530 +
(isset($options['RTELargeWidthIncrement']) ?
(int)$options['RTELargeWidthIncrement'] : 150);
306 /** @var InlineStackProcessor $inlineStackProcessor */
307 $inlineStackProcessor = GeneralUtility
::makeInstance(InlineStackProcessor
::class);
308 $inlineStackProcessor->initializeByGivenStructure($this->data
['inlineStructure']);
309 $inlineStructureDepth = $inlineStackProcessor->getStructureDepth();
310 $width -= $inlineStructureDepth > 0 ?
($inlineStructureDepth +
1) * 12 : 0;
311 $widthOverride = isset($backendUser->uc
['rteWidth']) && trim($backendUser->uc
['rteWidth']) ?
trim($backendUser->uc
['rteWidth']) : trim($this->processedRteConfiguration
['RTEWidthOverride']);
312 if ($widthOverride) {
313 if (strstr($widthOverride, '%')) {
314 if ($this->client
['browser'] !== 'msie') {
315 $width = (int)$widthOverride > 0 ?
(int)$widthOverride : '100%';
318 $width = (int)$widthOverride > 0 ?
(int)$widthOverride : $width;
321 $width = strstr($width, '%') ?
$width : $width . 'px';
322 $height = 380 +
(isset($options['RTELargeHeightIncrement']) ?
(int)$options['RTELargeHeightIncrement'] : 0);
323 $heightOverride = isset($backendUser->uc
['rteHeight']) && (int)$backendUser->uc
['rteHeight'] ?
(int)$backendUser->uc
['rteHeight'] : (int)$this->processedRteConfiguration
['RTEHeightOverride'];
324 $height = $heightOverride > 0 ?
$heightOverride . 'px' : $height . 'px';
326 $editorWrapWidth = '99%';
328 $rteDivStyle = 'position:relative; left:0px; top:0px; height:' . $height . '; width:' . $width . '; border: 1px solid black; padding: 2 ' . $paddingRight . ' 2 2;';
330 $itemFormElementName = $this->data
['parameterArray']['itemFormElName'];
332 // This seems to result in:
333 // _TRANSFORM_bodytext (the handled field name) in case the field is a direct DB field
334 // _TRANSFORM_vDEF (constant string) in case the RTE is within a flex form
335 $triggerFieldName = preg_replace('/\\[([^]]+)\\]$/', '[_TRANSFORM_\\1]', $itemFormElementName);
337 $value = $this->transformDatabaseContentToEditor($this->data
['parameterArray']['itemFormElValue']);
340 // The hidden field tells the DataHandler that processing should be done on this value.
341 $result[] = '<input type="hidden" name="' . htmlspecialchars($triggerFieldName) . '" value="RTE" />';
342 $result[] = '<div id="pleasewait' . $this->domIdentifier
. '" class="pleasewait" style="display: block;" >';
343 $result[] = $this->getLanguageService()->sL('LLL:EXT:rtehtmlarea/Resources/Private/Language/locallang.xlf:Please wait');
344 $result[] = '</div>';
345 $result[] = '<div id="editorWrap' . $this->domIdentifier
. '" class="editorWrap" style="visibility: hidden; width:' . $editorWrapWidth . '; height:100%;">';
346 $result[] = '<textarea ' . $this->getValidationDataAsDataAttribute($this->data
['parameterArray']['fieldConf']['config']) . ' id="RTEarea' . $this->domIdentifier
. '" name="' . htmlspecialchars($itemFormElementName) . '" rows="0" cols="0" style="' . htmlspecialchars($rteDivStyle) . '">';
347 $result[] = htmlspecialchars($value);
348 $result[] = '</textarea>';
349 $result[] = '</div>';
351 return implode(LF
, $result);
355 * Add registered plugins to the array of enabled plugins
359 protected function enableRegisteredPlugins()
361 // Traverse registered plugins
362 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rtehtmlarea']['plugins'])) {
363 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rtehtmlarea']['plugins'] as $pluginId => $pluginObjectConfiguration) {
364 if (is_array($pluginObjectConfiguration) && isset($pluginObjectConfiguration['objectReference'])) {
365 /** @var RteHtmlAreaApi $plugin */
366 $plugin = GeneralUtility
::makeInstance($pluginObjectConfiguration['objectReference']);
367 $configuration = array(
368 'language' => $this->language
,
369 'contentTypo3Language' => $this->contentTypo3Language
,
370 'contentISOLanguage' => $this->contentISOLanguage
,
371 'contentLanguageUid' => $this->contentLanguageUid
,
372 'RTEsetup' => $this->vanillaRteTsConfig
,
373 'client' => $this->client
,
374 'thisConfig' => $this->processedRteConfiguration
,
375 'specConf' => $this->defaultExtras
,
377 if ($plugin->main($configuration)) {
378 $this->registeredPlugins
[$pluginId] = $plugin;
379 // Override buttons from previously registered plugins
380 $pluginButtons = GeneralUtility
::trimExplode(',', $plugin->getPluginButtons(), true);
381 foreach ($this->pluginButton
as $previousPluginId => $buttonList) {
382 $this->pluginButton
[$previousPluginId] = implode(',', array_diff(GeneralUtility
::trimExplode(',', $this->pluginButton
[$previousPluginId], true), $pluginButtons));
384 $this->pluginButton
[$pluginId] = $plugin->getPluginButtons();
385 $pluginLabels = GeneralUtility
::trimExplode(',', $plugin->getPluginLabels(), true);
386 foreach ($this->pluginLabel
as $previousPluginId => $labelList) {
387 $this->pluginLabel
[$previousPluginId] = implode(',', array_diff(GeneralUtility
::trimExplode(',', $this->pluginLabel
[$previousPluginId], true), $pluginLabels));
389 $this->pluginLabel
[$pluginId] = $plugin->getPluginLabels();
390 $this->pluginEnabledArray
[] = $pluginId;
396 $hidePlugins = array();
397 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
398 /** @var RteHtmlAreaApi $plugin */
399 if ($plugin->addsButtons() && !$this->pluginButton
[$pluginId]) {
400 $hidePlugins[] = $pluginId;
403 $this->pluginEnabledArray
= array_unique(array_diff($this->pluginEnabledArray
, $hidePlugins));
407 * Set the toolbar config (only in this PHP-Object, not in JS):
411 protected function setToolbar()
413 $backendUser = $this->getBackendUserAuthentication();
415 if ($this->client
['browser'] === 'msie' ||
$this->client
['browser'] === 'opera') {
416 $this->processedRteConfiguration
['keepButtonGroupTogether'] = 0;
418 $this->defaultToolbarOrder
= 'bar, blockstylelabel, blockstyle, textstylelabel, textstyle, linebreak,
419 bar, formattext, bold, strong, italic, emphasis, big, small, insertedtext, deletedtext, citation, code,'
420 . 'definition, keyboard, monospaced, quotation, sample, variable, bidioverride, strikethrough, subscript, superscript, underline, span,
421 bar, fontstyle, fontsize, bar, formatblock, insertparagraphbefore, insertparagraphafter, blockquote, line,
422 bar, left, center, right, justifyfull,
423 bar, orderedlist, unorderedlist, definitionlist, definitionitem, outdent, indent,
424 bar, language, showlanguagemarks,lefttoright, righttoleft,
425 bar, textcolor, bgcolor, textindicator,
426 bar, editelement, showmicrodata,
427 bar, image, emoticon, insertcharacter, insertsofthyphen, abbreviation, user,
430 . ($this->processedRteConfiguration
['hideTableOperationsInToolbar']
431 && is_array($this->processedRteConfiguration
['buttons.'])
432 && is_array($this->processedRteConfiguration
['buttons.']['toggleborders.'])
433 && $this->processedRteConfiguration
['buttons.']['toggleborders.']['keepInToolbar'] ?
' toggleborders,' : '')
434 . 'bar, findreplace, spellcheck,
435 bar, chMode, inserttag, removeformat, bar, copy, cut, paste, pastetoggle, pastebehaviour, bar, undo, redo, bar, about, linebreak,'
436 . ($this->processedRteConfiguration
['hideTableOperationsInToolbar'] ?
'' : 'bar, toggleborders,')
437 . ' bar, tableproperties, tablerestyle, bar, rowproperties, rowinsertabove, rowinsertunder, rowdelete, rowsplit, bar,
438 columnproperties, columninsertbefore, columninsertafter, columndelete, columnsplit, bar,
439 cellproperties, cellinsertbefore, cellinsertafter, celldelete, cellsplit, cellmerge';
441 // Additional buttons from registered plugins
442 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
443 /** @var RteHtmlAreaApi $plugin */
444 if ($this->isPluginEnabled($pluginId)) {
445 $pluginButtons = $plugin->getPluginButtons();
446 //Add only buttons not yet in the default toolbar order
447 $addButtons = implode(
450 GeneralUtility
::trimExplode(',', $pluginButtons, true),
451 GeneralUtility
::trimExplode(',', $this->defaultToolbarOrder
, true)
454 $this->defaultToolbarOrder
= ($addButtons ?
'bar,' . $addButtons . ',linebreak,' : '') . $this->defaultToolbarOrder
;
457 $toolbarOrder = $this->processedRteConfiguration
['toolbarOrder'] ?
: $this->defaultToolbarOrder
;
458 // Getting rid of undefined buttons
459 $this->toolbarOrderArray
= array_intersect(GeneralUtility
::trimExplode(',', $toolbarOrder, true), GeneralUtility
::trimExplode(',', $this->defaultToolbarOrder
, true));
460 $toolbarOrder = array_unique(array_values($this->toolbarOrderArray
));
461 // Fetching specConf for field from backend
462 $pList = is_array($this->defaultExtras
['richtext']['parameters']) ?
implode(',', $this->defaultExtras
['richtext']['parameters']) : '';
463 if ($pList !== '*') {
465 $show = is_array($this->defaultExtras
['richtext']['parameters']) ?
$this->defaultExtras
['richtext']['parameters'] : array();
466 if ($this->processedRteConfiguration
['showButtons']) {
467 if (!GeneralUtility
::inList($this->processedRteConfiguration
['showButtons'], '*')) {
468 $show = array_unique(array_merge($show, GeneralUtility
::trimExplode(',', $this->processedRteConfiguration
['showButtons'], true)));
470 $show = array_unique(array_merge($show, $toolbarOrder));
473 if (is_array($this->processedRteConfiguration
['showButtons.'])) {
474 foreach ($this->processedRteConfiguration
['showButtons.'] as $buttonId => $value) {
479 $show = array_unique($show);
482 $show = $toolbarOrder;
484 $RTEkeyList = isset($backendUser->userTS
['options.']['RTEkeyList']) ?
$backendUser->userTS
['options.']['RTEkeyList'] : '*';
485 if ($RTEkeyList !== '*') {
487 $show = array_intersect($show, GeneralUtility
::trimExplode(',', $RTEkeyList, true));
489 // Hiding buttons of disabled plugins
490 $hideButtons = array('space', 'bar', 'linebreak');
491 foreach ($this->pluginButton
as $pluginId => $buttonList) {
492 if (!$this->isPluginEnabled($pluginId)) {
493 $buttonArray = GeneralUtility
::trimExplode(',', $buttonList, true);
494 foreach ($buttonArray as $button) {
495 $hideButtons[] = $button;
499 // Hiding labels of disabled plugins
500 foreach ($this->pluginLabel
as $pluginId => $label) {
501 if (!$this->isPluginEnabled($pluginId)) {
502 $hideButtons[] = $label;
506 $show = array_diff($show, GeneralUtility
::trimExplode(',', $this->processedRteConfiguration
['hideButtons'], true));
507 // Apply toolbar constraints from registered plugins
508 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
509 if ($this->isPluginEnabled($pluginId) && method_exists($plugin, 'applyToolbarConstraints')) {
510 $show = $plugin->applyToolbarConstraints($show);
513 // Getting rid of the buttons for which we have no position
514 $show = array_intersect($show, $toolbarOrder);
515 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
516 /** @var RteHtmlAreaApi $plugin */
517 $plugin->setToolbar($show);
519 $this->toolbar
= $show;
523 * Disable some plugins
527 protected function setPlugins()
529 // Disabling a plugin that adds buttons if none of its buttons is in the toolbar
530 $hidePlugins = array();
531 foreach ($this->pluginButton
as $pluginId => $buttonList) {
532 /** @var RteHtmlAreaApi $plugin */
533 $plugin = $this->registeredPlugins
[$pluginId];
534 if ($plugin->addsButtons()) {
536 $buttonArray = GeneralUtility
::trimExplode(',', $buttonList, true);
537 foreach ($buttonArray as $button) {
538 if (in_array($button, $this->toolbar
)) {
543 $hidePlugins[] = $pluginId;
547 $this->pluginEnabledArray
= array_diff($this->pluginEnabledArray
, $hidePlugins);
548 // Hiding labels of disabled plugins
549 $hideLabels = array();
550 foreach ($this->pluginLabel
as $pluginId => $label) {
551 if (!$this->isPluginEnabled($pluginId)) {
552 $hideLabels[] = $label;
555 $this->toolbar
= array_diff($this->toolbar
, $hideLabels);
556 // Adding plugins declared as prerequisites by enabled plugins
557 $requiredPlugins = array();
558 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
559 /** @var RteHtmlAreaApi $plugin */
560 if ($this->isPluginEnabled($pluginId)) {
561 $requiredPlugins = array_merge($requiredPlugins, GeneralUtility
::trimExplode(',', $plugin->getRequiredPlugins(), true));
564 $requiredPlugins = array_unique($requiredPlugins);
565 foreach ($requiredPlugins as $pluginId) {
566 if (is_object($this->registeredPlugins
[$pluginId]) && !$this->isPluginEnabled($pluginId)) {
567 $this->pluginEnabledArray
[] = $pluginId;
570 $this->pluginEnabledArray
= array_unique($this->pluginEnabledArray
);
571 // Completing the toolbar conversion array for htmlArea
572 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
573 /** @var RteHtmlAreaApi $plugin */
574 if ($this->isPluginEnabled($pluginId)) {
575 $this->convertToolbarForHtmlAreaArray
= array_unique(array_merge($this->convertToolbarForHtmlAreaArray
, $plugin->getConvertToolbarForHtmlAreaArray()));
581 * Add RTE main scripts and plugin scripts
585 protected function loadRequireModulesForRTE()
587 $this->resultArray
['requireJsModules'] = array();
588 $this->resultArray
['requireJsModules'][] = 'TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea';
589 foreach ($this->pluginEnabledCumulativeArray
as $pluginId) {
590 /** @var RteHtmlAreaApi $plugin */
591 $plugin = $this->registeredPlugins
[$pluginId];
592 $extensionKey = is_object($plugin) ?
$plugin->getExtensionKey() : 'rtehtmlarea';
593 $requirePath = 'TYPO3/CMS/' . GeneralUtility
::underscoredToUpperCamelCase($extensionKey);
594 $this->resultArray
['requireJsModules'][] = $requirePath . '/Plugins/' . $pluginId;
599 * Return RTE initialization inline JavaScript code
601 * @return string RTE initialization inline JavaScript code
603 protected function getRteInitJsCode()
605 $skinFilename = trim($this->processedRteConfiguration
['skin']) ?
: 'EXT:rtehtmlarea/Resources/Public/Css/Skin/htmlarea.css';
606 $skinFilename = $this->getFullFileName($skinFilename);
607 $skinDirectory = dirname($skinFilename);
608 // Editing area style sheet
609 $editedContentCSS = GeneralUtility
::createVersionNumberedFilename($skinDirectory . '/htmlarea-edited-content.css');
611 return 'require(["TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea"], function (HTMLArea) {
612 if (typeof RTEarea === "undefined") {
613 RTEarea = new Object();
614 RTEarea[0] = new Object();
615 RTEarea[0].version = "' . $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rtehtmlarea']['version'] . '";
616 RTEarea[0].editorUrl = "' . ExtensionManagementUtility
::extRelPath('rtehtmlarea') . '";
617 RTEarea[0].editorSkin = "' . $skinDirectory . '/";
618 RTEarea[0].editedContentCSS = "' . $editedContentCSS . '";
619 RTEarea.init = function() {
620 if (typeof HTMLArea === "undefined" || !Ext.isReady) {
621 window.setTimeout(function () {
628 RTEarea.initEditor = function(editorNumber) {
629 if (typeof HTMLArea === "undefined" || !HTMLArea.isReady) {
630 window.setTimeout(function () {
631 RTEarea.initEditor(editorNumber);
634 HTMLArea.initEditor(editorNumber);
643 * Return the Javascript code for configuring the RTE
647 protected function addInstanceJavaScriptRegistration()
649 $backendUser = $this->getBackendUserAuthentication();
652 $jsArray[] = 'if (typeof configureEditorInstance === "undefined") {';
653 $jsArray[] = ' configureEditorInstance = new Object();';
655 $jsArray[] = 'configureEditorInstance[' . GeneralUtility
::quoteJSvalue($this->domIdentifier
) . '] = function() {';
656 $jsArray[] = 'if (typeof RTEarea === "undefined" || typeof HTMLArea === "undefined") {';
657 $jsArray[] = ' window.setTimeout("configureEditorInstance[' . GeneralUtility
::quoteJSvalue($this->domIdentifier
) . ']();", 40);';
658 $jsArray[] = '} else {';
659 $jsArray[] = 'editornumber = ' . GeneralUtility
::quoteJSvalue($this->domIdentifier
) . ';';
660 $jsArray[] = 'RTEarea[editornumber] = new Object();';
661 $jsArray[] = 'RTEarea[editornumber].RTEtsConfigParams = "&RTEtsConfigParams=' . rawurlencode($this->RTEtsConfigParams()) . '";';
662 $jsArray[] = 'RTEarea[editornumber].number = editornumber;';
663 $jsArray[] = 'RTEarea[editornumber].deleted = false;';
664 $jsArray[] = 'RTEarea[editornumber].textAreaId = ' . GeneralUtility
::quoteJSvalue($this->domIdentifier
) . ';';
665 $jsArray[] = 'RTEarea[editornumber].id = "RTEarea" + editornumber;';
666 $jsArray[] = 'RTEarea[editornumber].RTEWidthOverride = "'
667 . (isset($backendUser->uc
['rteWidth']) && trim($backendUser->uc
['rteWidth'])
668 ?
trim($backendUser->uc
['rteWidth'])
669 : trim($this->processedRteConfiguration
['RTEWidthOverride'])) . '";';
670 $jsArray[] = 'RTEarea[editornumber].RTEHeightOverride = "'
671 . (isset($backendUser->uc
['rteHeight']) && (int)$backendUser->uc
['rteHeight']
672 ?
(int)$backendUser->uc
['rteHeight']
673 : (int)$this->processedRteConfiguration
['RTEHeightOverride']) . '";';
674 $jsArray[] = 'RTEarea[editornumber].resizable = '
675 . (isset($backendUser->uc
['rteResize']) && $backendUser->uc
['rteResize']
677 : (trim($this->processedRteConfiguration
['rteResize']) ?
'true;' : 'false;'));
678 $jsArray[] = 'RTEarea[editornumber].maxHeight = "'
679 . (isset($backendUser->uc
['rteMaxHeight']) && (int)$backendUser->uc
['rteMaxHeight']
680 ?
trim($backendUser->uc
['rteMaxHeight'])
681 : ((int)$this->processedRteConfiguration
['rteMaxHeight'] ?
: '2000')) . '";';
682 $jsArray[] = 'RTEarea[editornumber].fullScreen = ' . ($this->isInFullScreenMode() ?
'true;' : 'false;');
683 $jsArray[] = 'RTEarea[editornumber].showStatusBar = ' . (trim($this->processedRteConfiguration
['showStatusBar']) ?
'true;' : 'false;');
684 $jsArray[] = 'RTEarea[editornumber].enableWordClean = ' . (trim($this->processedRteConfiguration
['enableWordClean']) ?
'true;' : 'false;');
685 $jsArray[] = 'RTEarea[editornumber].htmlRemoveComments = ' . (trim($this->processedRteConfiguration
['removeComments']) ?
'true;' : 'false;');
686 $jsArray[] = 'RTEarea[editornumber].disableEnterParagraphs = ' . (trim($this->processedRteConfiguration
['disableEnterParagraphs']) ?
'true;' : 'false;');
687 $jsArray[] = 'RTEarea[editornumber].disableObjectResizing = ' . (trim($this->processedRteConfiguration
['disableObjectResizing']) ?
'true;' : 'false;');
688 $jsArray[] = 'RTEarea[editornumber].removeTrailingBR = ' . (trim($this->processedRteConfiguration
['removeTrailingBR']) ?
'true;' : 'false;');
689 $jsArray[] = 'RTEarea[editornumber].useCSS = ' . (trim($this->processedRteConfiguration
['useCSS']) ?
'true' : 'false') . ';';
690 $jsArray[] = 'RTEarea[editornumber].keepButtonGroupTogether = ' . (trim($this->processedRteConfiguration
['keepButtonGroupTogether']) ?
'true;' : 'false;');
691 $jsArray[] = 'RTEarea[editornumber].disablePCexamples = ' . (trim($this->processedRteConfiguration
['disablePCexamples']) ?
'true;' : 'false;');
692 $jsArray[] = 'RTEarea[editornumber].showTagFreeClasses = ' . (trim($this->processedRteConfiguration
['showTagFreeClasses']) ?
'true;' : 'false;');
693 $jsArray[] = 'RTEarea[editornumber].tceformsNested = ' . (!empty($this->data
) ?
json_encode($this->data
['tabAndInlineStack']) : '[]') . ';';
694 $jsArray[] = 'RTEarea[editornumber].dialogueWindows = new Object();';
695 if (isset($this->processedRteConfiguration
['dialogueWindows.']['defaultPositionFromTop'])) {
696 $jsArray[] = 'RTEarea[editornumber].dialogueWindows.positionFromTop = ' . (int)$this->processedRteConfiguration
['dialogueWindows.']['defaultPositionFromTop'] . ';';
698 if (isset($this->processedRteConfiguration
['dialogueWindows.']['defaultPositionFromLeft'])) {
699 $jsArray[] = 'RTEarea[editornumber].dialogueWindows.positionFromLeft = ' . (int)$this->processedRteConfiguration
['dialogueWindows.']['defaultPositionFromLeft'] . ';';
701 $jsArray[] = 'RTEarea[editornumber].sys_language_content = "' . $this->contentLanguageUid
. '";';
702 $jsArray[] = 'RTEarea[editornumber].typo3ContentLanguage = "' . $this->contentTypo3Language
. '";';
703 $jsArray[] = 'RTEarea[editornumber].userUid = "' . 'BE_' . $backendUser->user
['uid'] . '";';
705 // Setting the plugin flags
706 $jsArray[] = 'RTEarea[editornumber].plugin = new Object();';
707 foreach ($this->pluginEnabledArray
as $pluginId) {
708 $jsArray[] = 'RTEarea[editornumber].plugin.' . $pluginId . ' = true;';
711 // Setting the buttons configuration
712 $jsArray[] = 'RTEarea[editornumber].buttons = new Object();';
713 if (is_array($this->processedRteConfiguration
['buttons.'])) {
714 foreach ($this->processedRteConfiguration
['buttons.'] as $buttonIndex => $conf) {
715 $button = substr($buttonIndex, 0, -1);
716 if (is_array($conf)) {
717 $jsArray[] = 'RTEarea[editornumber].buttons.' . $button . ' = ' . $this->buildNestedJSArray($conf) . ';';
722 // Setting the list of tags to be removed if specified in the RTE config
723 if (trim($this->processedRteConfiguration
['removeTags'])) {
724 $jsArray[] = 'RTEarea[editornumber].htmlRemoveTags = /^(' . implode('|', GeneralUtility
::trimExplode(',', $this->processedRteConfiguration
['removeTags'], true)) . ')$/i;';
727 // Setting the list of tags to be removed with their contents if specified in the RTE config
728 if (trim($this->processedRteConfiguration
['removeTagsAndContents'])) {
729 $jsArray[] = 'RTEarea[editornumber].htmlRemoveTagsAndContents = /^(' . implode('|', GeneralUtility
::trimExplode(',', $this->processedRteConfiguration
['removeTagsAndContents'], true)) . ')$/i;';
732 // Setting array of custom tags if specified in the RTE config
733 if (!empty($this->processedRteConfiguration
['customTags'])) {
734 $customTags = GeneralUtility
::trimExplode(',', $this->processedRteConfiguration
['customTags'], true);
735 if (!empty($customTags)) {
736 $jsArray[] = 'RTEarea[editornumber].customTags= ' . json_encode($customTags) . ';';
740 // Setting array of content css files if specified in the RTE config
741 $versionNumberedFileNames = array();
742 $contentCssFileNames = $this->getContentCssFileNames();
743 foreach ($contentCssFileNames as $contentCssFileName) {
744 $versionNumberedFileNames[] = GeneralUtility
::createVersionNumberedFilename($contentCssFileName);
746 $jsArray[] = 'RTEarea[editornumber].pageStyle = ["' . implode('","', $versionNumberedFileNames) . '"];';
748 $jsArray[] = 'RTEarea[editornumber].classesUrl = "' . $this->writeTemporaryFile(('classes_' . $this->language
), 'js', $this->buildJSClassesArray()) . '";';
750 // Add Javascript configuration for registered plugins
751 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
752 /** @var RteHtmlAreaApi $plugin */
753 if ($this->isPluginEnabled($pluginId)) {
754 $jsPluginString = $plugin->buildJavascriptConfiguration();
755 if ($jsPluginString) {
756 $jsArray[] = $plugin->buildJavascriptConfiguration();
761 // Avoid premature reference to HTMLArea when being initially loaded by IRRE Ajax call
762 $jsArray[] = 'RTEarea[editornumber].toolbar = ' . $this->getJSToolbarArray() . ';';
763 $jsArray[] = 'RTEarea[editornumber].convertButtonId = ' . json_encode(array_flip($this->convertToolbarForHtmlAreaArray
)) . ';';
764 $jsArray[] = 'RTEarea.initEditor(editornumber);';
767 $jsArray[] = 'configureEditorInstance[' . GeneralUtility
::quoteJSvalue($this->domIdentifier
) . ']();';
769 $this->resultArray
['additionalJavaScriptPost'][] = implode(LF
, $jsArray);
773 * Get the name of the contentCSS files to use
775 * @return array An array of full file name of the content css files to use
777 protected function getContentCssFileNames()
779 $contentCss = is_array($this->processedRteConfiguration
['contentCSS.']) ?
$this->processedRteConfiguration
['contentCSS.'] : array();
780 if (isset($this->processedRteConfiguration
['contentCSS'])) {
781 $contentCss[] = trim($this->processedRteConfiguration
['contentCSS']);
783 $contentCssFiles = array();
784 if (!empty($contentCss)) {
785 foreach ($contentCss as $contentCssKey => $contentCssfile) {
786 $fileName = trim($contentCssfile);
787 $absolutePath = GeneralUtility
::getFileAbsFileName($fileName);
788 if (file_exists($absolutePath) && filesize($absolutePath)) {
789 $contentCssFiles[$contentCssKey] = $this->getFullFileName($fileName);
793 // Fallback to default content css file if none of the configured files exists and is not empty
794 $contentCssFiles['default'] = $this->getFullFileName('EXT:rtehtmlarea/Resources/Public/Css/ContentCss/Default.css');
796 return array_unique($contentCssFiles);
800 * Return TRUE, if the plugin can be loaded
802 * @param string $pluginId: The identification string of the plugin
803 * @return bool TRUE if the plugin can be loaded
805 protected function isPluginEnabled($pluginId)
807 return in_array($pluginId, $this->pluginEnabledArray
);
811 * Return JS arrays of classes configuration
813 * @return string JS classes arrays
815 protected function buildJSClassesArray()
817 $RTEProperties = $this->vanillaRteTsConfig
['properties'];
818 // Declare sub-arrays
819 $classesArray = array(
823 'alternating' => array(),
824 'counting' => array(),
825 'selectable' => array(),
826 'requires' => array(),
827 'requiredBy' => array(),
830 $JSClassesArray = '';
831 // Scanning the list of classes if specified in the RTE config
832 if (is_array($RTEProperties['classes.'])) {
833 foreach ($RTEProperties['classes.'] as $className => $conf) {
834 $className = rtrim($className, '.');
837 if (!empty($conf['name'])) {
838 $label = $this->getLanguageService()->sL(trim($conf['name']));
839 $label = str_replace('"', '\\"', str_replace('\\\'', '\'', $label));
841 $classesArray['labels'][$className] = $label;
842 $classesArray['values'][$className] = str_replace('\\\'', '\'', $conf['value']);
843 if (isset($conf['noShow'])) {
844 $classesArray['noShow'][$className] = $conf['noShow'];
846 if (is_array($conf['alternating.'])) {
847 $classesArray['alternating'][$className] = $conf['alternating.'];
849 if (is_array($conf['counting.'])) {
850 $classesArray['counting'][$className] = $conf['counting.'];
852 if (isset($conf['selectable'])) {
853 $classesArray['selectable'][$className] = $conf['selectable'];
855 if (isset($conf['requires'])) {
856 $classesArray['requires'][$className] = explode(',', GeneralUtility
::rmFromList($className, $this->cleanList($conf['requires'])));
859 // Remove circularities from classes dependencies
860 $requiringClasses = array_keys($classesArray['requires']);
861 foreach ($requiringClasses as $requiringClass) {
862 if ($this->hasCircularDependency($classesArray, $requiringClass, $requiringClass)) {
863 unset($classesArray['requires'][$requiringClass]);
866 // Reverse relationship for the dependency checks when removing styles
867 $requiringClasses = array_keys($classesArray['requires']);
868 foreach ($requiringClasses as $className) {
869 foreach ($classesArray['requires'][$className] as $requiredClass) {
870 if (!is_array($classesArray['requiredBy'][$requiredClass])) {
871 $classesArray['requiredBy'][$requiredClass] = array();
873 if (!in_array($className, $classesArray['requiredBy'][$requiredClass])) {
874 $classesArray['requiredBy'][$requiredClass][] = $className;
879 // Scanning the list of sets of mutually exclusives classes if specified in the RTE config
880 if (is_array($RTEProperties['mutuallyExclusiveClasses.'])) {
881 foreach ($RTEProperties['mutuallyExclusiveClasses.'] as $listName => $conf) {
882 $classSet = GeneralUtility
::trimExplode(',', $conf, true);
883 $classList = implode(',', $classSet);
884 foreach ($classSet as $className) {
885 $classesArray['XOR'][$className] = '/^(' . implode('|', GeneralUtility
::trimExplode(',', GeneralUtility
::rmFromList($className, $classList), true)) . ')$/';
889 foreach ($classesArray as $key => $subArray) {
890 $JSClassesArray .= 'HTMLArea.classes' . ucfirst($key) . ' = ' . $this->buildNestedJSArray($subArray) . ';' . LF
;
892 return $JSClassesArray;
896 * Check for possible circularity in classes dependencies
898 * @param array $classesArray: reference to the array of classes dependencies
899 * @param string $requiringClass: class requiring at some iteration level from the initial requiring class
900 * @param string $initialClass: initial class from which a circular relationship is being searched
901 * @param int $recursionLevel: depth of recursive call
902 * @return bool TRUE, if a circular relationship is found
904 protected function hasCircularDependency(&$classesArray, $requiringClass, $initialClass, $recursionLevel = 0)
906 if (is_array($classesArray['requires'][$requiringClass])) {
907 if (in_array($initialClass, $classesArray['requires'][$requiringClass])) {
910 if ($recursionLevel++
< 20) {
911 foreach ($classesArray['requires'][$requiringClass] as $requiringClass2) {
912 if ($this->hasCircularDependency($classesArray, $requiringClass2, $initialClass, $recursionLevel)) {
925 * Translate Page TS Config array in JS nested array definition
926 * Replace 0 values with false
927 * Unquote regular expression values
928 * Replace empty arrays with empty objects
930 * @param array $conf: Page TSConfig configuration array
931 * @return string nested JS array definition
933 protected function buildNestedJSArray($conf)
935 $convertedConf = GeneralUtility
::removeDotsFromTS($conf);
937 array(':"0"', ':"\\/^(', ')$\\/i"', ':"\\/^(', ')$\\/"', '[]'),
938 array(':false', ':/^(', ')$/i', ':/^(', ')$/', '{}'), json_encode($convertedConf)
943 * Writes contents in a file in typo3temp and returns the file name
945 * @param string $label: A label to insert at the beginning of the name of the file
946 * @param string $fileExtension: The file extension of the file, defaulting to 'js'
947 * @param string $contents: The contents to write into the file
948 * @return string The name of the file written to typo3temp
949 * @throws \RuntimeException If writing to file failed
951 protected function writeTemporaryFile($label, $fileExtension = 'js', $contents = '')
953 $relativeFilename = 'typo3temp/assets/js/rte_' . str_replace('-', '_', $label) . '_' . GeneralUtility
::shortMD5($contents, 20) . '.' . $fileExtension;
954 $destination = PATH_site
. $relativeFilename;
955 if (!file_exists($destination)) {
956 $minifiedJavaScript = '';
957 if ($fileExtension === 'js' && $contents !== '') {
958 $minifiedJavaScript = GeneralUtility
::minifyJavaScript($contents);
960 $failure = GeneralUtility
::writeFileToTypo3tempDir($destination, $minifiedJavaScript ?
$minifiedJavaScript : $contents);
962 throw new \
RuntimeException($failure, 1294585668);
965 if (isset($GLOBALS['TSFE'])) {
966 $fileName = $relativeFilename;
968 $fileName = '../' . $relativeFilename;
970 return GeneralUtility
::resolveBackPath($fileName);
974 * Both rte framework and rte plugins can have label files that are
975 * used in JS. The methods gathers those and creates a JS object from
980 protected function createJavaScriptLanguageLabelsFromFiles()
982 $labelArray = array();
983 // Load labels of 3 base files into JS
984 foreach (array('tooltips', 'msg', 'dialogs') as $identifier) {
985 $fileName = 'EXT:rtehtmlarea/Resources/Private/Language/locallang_' . $identifier . '.xlf';
986 $newLabels = $this->getMergedLabelsFromFile($fileName);
987 if (!empty($newLabels)) {
988 $labelArray[$identifier] = $newLabels;
991 // Load labels of plugins into JS
992 foreach ($this->pluginEnabledCumulativeArray
as $pluginId) {
993 /** @var RteHtmlAreaApi $plugin */
994 $plugin = $this->registeredPlugins
[$pluginId];
995 $extensionKey = is_object($plugin) ?
$plugin->getExtensionKey() : 'rtehtmlarea';
996 $fileName = 'EXT:' . $extensionKey . '/Resources/Private/Language/Plugins/' . $pluginId . '/locallang_js.xlf';
997 $newLabels = $this->getMergedLabelsFromFile($fileName);
998 if (!empty($newLabels)) {
999 $labelArray[$pluginId] = $newLabels;
1002 $javaScriptString = 'TYPO3.jQuery(function() {';
1003 $javaScriptString .= 'HTMLArea.I18N = new Object();' . LF
;
1004 $javaScriptString .= 'HTMLArea.I18N = ' . json_encode($labelArray);
1005 $javaScriptString .= '});';
1006 $this->resultArray
['additionalJavaScriptPost'][] = $javaScriptString;
1010 * Get all labels from a specific label file, merge default
1011 * labels and target language labels.
1013 * @param string $fileName The file to merge labels from
1014 * @return array Label keys and values
1016 protected function getMergedLabelsFromFile($fileName)
1018 /** @var $languageFactory LocalizationFactory */
1019 $languageFactory = GeneralUtility
::makeInstance(LocalizationFactory
::class);
1020 $localizationArray = $languageFactory->getParsedData($fileName, $this->language
, 'utf-8', 1);
1021 if (is_array($localizationArray) && !empty($localizationArray)) {
1022 if (!empty($localizationArray[$this->language
])) {
1023 $finalLocalLang = $localizationArray['default'];
1024 ArrayUtility
::mergeRecursiveWithOverrule($finalLocalLang, $localizationArray[$this->language
], true, false);
1025 $localizationArray[$this->language
] = $finalLocalLang;
1027 $localizationArray[$this->language
] = $localizationArray['default'];
1030 $localizationArray = array();
1032 return $localizationArray[$this->language
];
1036 * Return the JS code of the toolbar configuration for the HTMLArea editor
1038 * @return string The JS code as nested JS arrays
1040 protected function getJSToolbarArray()
1042 // The toolbar array
1044 // The current row; a "linebreak" ends the current row
1046 // The current group; each group is between "bar"s; a "linebreak" ends the current group
1048 // Process each toolbar item in the toolbar order list
1049 foreach ($this->toolbarOrderArray
as $item) {
1052 // Add row to toolbar if not empty
1053 if (!empty($group)) {
1063 // Add group to row if not empty
1064 if (!empty($group)) {
1070 if (end($group) != $this->convertToolbarForHtmlAreaArray
[$item]) {
1071 $group[] = $this->convertToolbarForHtmlAreaArray
[$item];
1075 if (in_array($item, $this->toolbar
)) {
1076 // Add the item to the group
1077 $convertedItem = $this->convertToolbarForHtmlAreaArray
[$item];
1078 if ($convertedItem) {
1079 $group[] = $convertedItem;
1084 // Add the last group and last line, if not empty
1085 if (!empty($group)) {
1091 return json_encode($toolbar);
1095 * Make a file name relative to the PATH_site or to the PATH_typo3
1097 * @param string $filename: a file name of the form EXT:.... or relative to the PATH_site
1098 * @return string the file name relative to the PATH_site if in frontend or relative to the PATH_typo3 if in backend
1100 protected function getFullFileName($filename)
1102 if (substr($filename, 0, 4) === 'EXT:') {
1104 list($extKey, $local) = explode('/', substr($filename, 4), 2);
1106 if ((string)$extKey !== '' && ExtensionManagementUtility
::isLoaded($extKey) && (string)$local !== '') {
1107 $newFilename = ($this->isFrontendEditActive()
1108 ? ExtensionManagementUtility
::siteRelPath($extKey)
1109 : ExtensionManagementUtility
::extRelPath($extKey))
1113 $path = ($this->isFrontendEditActive() ?
'' : '../');
1114 $newFilename = $path . ($filename[0] === '/' ?
substr($filename, 1) : $filename);
1116 return GeneralUtility
::resolveBackPath($newFilename);
1120 * Return the Javascript code for copying the HTML code from the editor into the hidden input field.
1124 protected function addOnSubmitJavaScriptCode()
1126 $onSubmitCode = array();
1127 $onSubmitCode[] = 'if (RTEarea[' . GeneralUtility
::quoteJSvalue($this->domIdentifier
) . ']) {';
1128 $onSubmitCode[] = 'document.editform[' . GeneralUtility
::quoteJSvalue($this->data
['parameterArray']['itemFormElName']) . '].value = RTEarea[' . GeneralUtility
::quoteJSvalue($this->domIdentifier
) . '].editor.getHTML();';
1129 $onSubmitCode[] = '} else {';
1130 $onSubmitCode[] = 'OK = 0;';
1131 $onSubmitCode[] = '};';
1132 $this->resultArray
['additionalJavaScriptSubmit'][] = implode(LF
, $onSubmitCode);
1136 * Checks if frontend editing is active.
1138 * @return bool TRUE if frontend editing is active
1140 protected function isFrontendEditActive()
1142 return is_object($GLOBALS['TSFE']) && $GLOBALS['TSFE']->beUserLogin
&& $GLOBALS['BE_USER']->frontendEdit
instanceof FrontendEditingController
;
1146 * Client Browser Information
1148 * @return array Contains keys "user agent", "browser", "version", "system"
1150 protected function clientInfo()
1152 $userAgent = GeneralUtility
::getIndpEnv('HTTP_USER_AGENT');
1153 $browserInfo = ClientUtility
::getBrowserInfo($userAgent);
1154 // Known engines: order is not irrelevant!
1155 $knownEngines = array('opera', 'msie', 'gecko', 'webkit');
1156 if (is_array($browserInfo['all'])) {
1157 foreach ($knownEngines as $engine) {
1158 if ($browserInfo['all'][$engine]) {
1159 $browserInfo['browser'] = $engine;
1160 $browserInfo['version'] = ClientUtility
::getVersion($browserInfo['all'][$engine]);
1165 return $browserInfo;
1169 * Initialize a couple of language related local properties
1173 public function initializeLanguageRelatedProperties()
1175 $database = $this->getDatabaseConnection();
1176 $this->language
= $GLOBALS['LANG']->lang
;
1177 if ($this->language
=== 'default' ||
!$this->language
) {
1178 $this->language
= 'en';
1180 $currentLanguageUid = $this->data
['databaseRow']['sys_language_uid'];
1181 if (is_array($currentLanguageUid)) {
1182 $currentLanguageUid = $currentLanguageUid[0];
1184 $this->contentLanguageUid
= (int)max($currentLanguageUid, 0);
1185 if ($this->contentLanguageUid
) {
1186 $this->contentISOLanguage
= $this->language
;
1187 if (ExtensionManagementUtility
::isLoaded('static_info_tables')) {
1188 $tableA = 'sys_language';
1189 $tableB = 'static_languages';
1190 $selectFields = $tableA . '.uid,' . $tableB . '.lg_iso_2,' . $tableB . '.lg_country_iso_2';
1191 $tableAB = $tableA . ' LEFT JOIN ' . $tableB . ' ON ' . $tableA . '.static_lang_isocode=' . $tableB . '.uid';
1192 $whereClause = $tableA . '.uid = ' . intval($this->contentLanguageUid
);
1193 $whereClause .= BackendUtility
::BEenableFields($tableA);
1194 $whereClause .= BackendUtility
::deleteClause($tableA);
1195 $res = $database->exec_SELECTquery($selectFields, $tableAB, $whereClause);
1196 while ($languageRow = $database->sql_fetch_assoc($res)) {
1197 $this->contentISOLanguage
= strtolower(trim($languageRow['lg_iso_2']) . (trim($languageRow['lg_country_iso_2']) ?
'_' . trim($languageRow['lg_country_iso_2']) : ''));
1199 $database->sql_free_result($res);
1202 $this->contentISOLanguage
= trim($this->processedRteConfiguration
['defaultContentLanguage']) ?
: 'en';
1203 $languageCodeParts = explode('_', $this->contentISOLanguage
);
1204 $this->contentISOLanguage
= strtolower($languageCodeParts[0]) . ($languageCodeParts[1] ?
'_' . strtoupper($languageCodeParts[1]) : '');
1205 // Find the configured language in the list of localization locales
1206 /** @var $locales Locales */
1207 $locales = GeneralUtility
::makeInstance(Locales
::class);
1208 // If not found, default to 'en'
1209 if (!in_array($this->contentISOLanguage
, $locales->getLocales())) {
1210 $this->contentISOLanguage
= 'en';
1213 $this->contentTypo3Language
= $this->contentISOLanguage
=== 'en' ?
'default' : $this->contentISOLanguage
;
1217 * Log usage of deprecated Page TS Config Property
1219 * @param string $deprecatedProperty: Name of deprecated property
1220 * @param string $useProperty: Name of property to use instead
1221 * @param string $version: Version of TYPO3 in which the property will be removed
1224 protected function logDeprecatedProperty($deprecatedProperty, $useProperty, $version)
1226 $backendUser = $this->getBackendUserAuthentication();
1227 if (!$this->processedRteConfiguration
['logDeprecatedProperties.']['disabled']) {
1229 '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.',
1230 $deprecatedProperty,
1233 $this->data
['databaseRow']['pid']
1235 GeneralUtility
::deprecationLog($message);
1236 if ($this->processedRteConfiguration
['logDeprecatedProperties.']['logAlsoToBELog']) {
1238 $this->getLanguageService()->sL('LLL:EXT:rtehtmlarea/Resources/Private/Language/locallang.xlf:deprecatedPropertyMessage'),
1239 $deprecatedProperty,
1242 $this->data
['databaseRow']['pid']
1244 $backendUser->simplelog($message, 'rtehtmlarea');
1250 * A list of parameters that is mostly given as GET/POST to other RTE controllers.
1254 protected function RTEtsConfigParams()
1256 $parameters = BackendUtility
::getSpecConfParametersFromArray($this->defaultExtras
['rte_transform']['parameters']);
1258 $this->data
['tableName'],
1259 $this->data
['databaseRow']['uid'],
1260 $this->data
['fieldName'],
1261 $this->pidOfVersionedMotherRecord
,
1262 $this->data
['recordTypeValue'],
1263 $this->pidOfPageRecord
,
1264 $parameters['imgpath'],
1266 return implode(':', $result);
1272 * @param string $str String to clean
1273 * @return string Cleaned string
1275 protected function cleanList($str)
1277 if (strstr($str, '*')) {
1280 $str = implode(',', array_unique(GeneralUtility
::trimExplode(',', $str, true)));
1286 * Performs transformation of content from database to richtext editor
1288 * @param string $value Value to transform.
1289 * @return string Transformed content
1291 protected function transformDatabaseContentToEditor($value)
1293 // change <strong> to <b>
1294 $value = preg_replace('/<(\\/?)strong/i', '<$1b', $value);
1295 // change <em> to <i>
1296 $value = preg_replace('/<(\\/?)em([^b>]*>)/i', '<$1i$2', $value);
1298 if ($this->defaultExtras
['rte_transform']) {
1299 /** @var RteHtmlParser $parseHTML */
1300 $parseHTML = GeneralUtility
::makeInstance(RteHtmlParser
::class);
1301 $parseHTML->init($this->data
['table'] . ':' . $this->data
['fieldName'], $this->pidOfVersionedMotherRecord
);
1302 $value = $parseHTML->RTE_transform($value, $this->defaultExtras
, 'rte', $this->processedRteConfiguration
);
1308 * True if RTE is in full screen mode / called via wizard controller
1312 protected function isInFullScreenMode()
1314 return GeneralUtility
::_GP('M') === 'wizard_rte';
1318 * @return LanguageService
1320 protected function getLanguageService()
1322 return $GLOBALS['LANG'];
1326 * @return BackendUserAuthentication
1328 protected function getBackendUserAuthentication()
1330 return $GLOBALS['BE_USER'];
1334 * @return DatabaseConnection
1336 protected function getDatabaseConnection()
1338 return $GLOBALS['TYPO3_DB'];