2 namespace TYPO3\CMS\Rtehtmlarea\Form\Element;
154 'bar' =>
'separator',
155 'linebreak' =>
'linebreak'
215 $table = $this->data[
'tableName'];
216 $fieldName = $this->data[
'fieldName'];
217 $row = $this->data[
'databaseRow'];
218 $parameterArray = $this->data[
'parameterArray'];
226 $this->pidOfVersionedMotherRecord = (int)$row[
'pid'];
229 $this->vanillaRteTsConfig[
'properties'],
232 $this->data[
'recordTypeValue']
235 $this->domIdentifier = preg_replace(
'/[^a-zA-Z0-9_:.-]/',
'_', $parameterArray[
'itemFormElName']);
236 $this->domIdentifier = htmlspecialchars(preg_replace(
'/^[^a-zA-Z]/',
'x', $this->domIdentifier));
238 $this->initializeLanguageRelatedProperties();
241 $skinFilename = trim($this->processedRteConfiguration[
'skin']) ?:
'EXT:rtehtmlarea/Resources/Public/Css/Skin/htmlarea.css';
243 $skinDirectory = dirname($skinFilename);
246 $this->resultArray[
'stylesheetFiles'][] = $skinDirectory .
'/jquery-ui-resizable.css';
247 $this->resultArray[
'stylesheetFiles'][] = $skinFilename;
249 $this->enableRegisteredPlugins();
260 $this->addInstanceJavaScriptRegistration();
265 $this->loadRequireModulesForRTE();
268 $this->createJavaScriptLanguageLabelsFromFiles();
271 $this->resultArray[
'additionalJavaScriptPost'][] = $this->
getRteInitJsCode();
273 $html = $this->getMainHtml();
275 $this->resultArray[
'html'] = $this->renderWizards(
277 $parameterArray[
'fieldConf'][
'config'][
'wizards'],
282 $parameterArray[
'itemFormElName'],
283 $this->defaultExtras,
295 protected function getMainHtml()
302 $paddingRight =
'0px';
303 $editorWrapWidth =
'100%';
305 $options = $backendUser->userTS[
'options.'];
306 $width = 530 + (isset($options[
'RTELargeWidthIncrement']) ? (int)$options[
'RTELargeWidthIncrement'] : 150);
309 $inlineStackProcessor->initializeByGivenStructure($this->data[
'inlineStructure']);
310 $inlineStructureDepth = $inlineStackProcessor->getStructureDepth();
311 $width -= $inlineStructureDepth > 0 ? ($inlineStructureDepth + 1) * 12 : 0;
312 $widthOverride = isset($backendUser->uc[
'rteWidth']) && trim($backendUser->uc[
'rteWidth']) ?: trim($this->processedRteConfiguration[
'RTEWidthOverride']);
313 if ($widthOverride) {
314 if (strstr($widthOverride,
'%')) {
315 if ($this->client[
'browser'] !==
'msie') {
316 $width = (int)$widthOverride > 0 ? (
int)$widthOverride :
'100%';
319 $width = (int)$widthOverride > 0 ? (
int)$widthOverride : $width;
322 $width = strstr($width,
'%') ? $width : $width .
'px';
323 $height = 380 + (isset($options[
'RTELargeHeightIncrement']) ? (int)$options[
'RTELargeHeightIncrement'] : 0);
324 $heightOverride = isset($backendUser->uc[
'rteHeight']) && (int)$backendUser->uc[
'rteHeight'] ?: (
int)$this->processedRteConfiguration[
'RTEHeightOverride'];
325 $height = $heightOverride > 0 ? $heightOverride .
'px' : $height .
'px';
327 $editorWrapWidth =
'99%';
329 $rteDivStyle =
'position:relative; left:0px; top:0px; height:' . $height .
'; width:' . $width .
'; border: 1px solid black; padding: 2 ' . $paddingRight .
' 2 2;';
331 $itemFormElementName = $this->data[
'parameterArray'][
'itemFormElName'];
336 $triggerFieldName = preg_replace(
'/\\[([^]]+)\\]$/',
'[_TRANSFORM_\\1]', $itemFormElementName);
338 $value = $this->transformDatabaseContentToEditor($this->data[
'parameterArray'][
'itemFormElValue']);
342 $result[] =
'<input type="hidden" name="' . htmlspecialchars($triggerFieldName) .
'" value="RTE" />';
343 $result[] =
'<div id="pleasewait' . $this->domIdentifier .
'" class="pleasewait" style="display: block;" >';
344 $result[] = $this->
getLanguageService()->sL(
'LLL:EXT:rtehtmlarea/Resources/Private/Language/locallang.xlf:Please wait');
345 $result[] =
'</div>';
346 $result[] =
'<div id="editorWrap' . $this->domIdentifier .
'" class="editorWrap" style="visibility: hidden; width:' . $editorWrapWidth .
'; height:100%;">';
347 $result[] =
'<textarea id="RTEarea' . $this->domIdentifier .
'" name="' . htmlspecialchars($itemFormElementName) .
'" rows="0" cols="0" style="' . htmlspecialchars($rteDivStyle) .
'">';
348 $result[] = htmlspecialchars($value);
349 $result[] =
'</textarea>';
350 $result[] =
'</div>';
352 return implode(LF, $result);
360 protected function enableRegisteredPlugins()
363 if (is_array(
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXTCONF'][
'rtehtmlarea'][
'plugins'])) {
364 foreach (
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXTCONF'][
'rtehtmlarea'][
'plugins'] as $pluginId => $pluginObjectConfiguration) {
365 if (is_array($pluginObjectConfiguration) && isset($pluginObjectConfiguration[
'objectReference'])) {
368 $configuration = array(
369 'language' => $this->language,
370 'contentTypo3Language' => $this->contentTypo3Language,
371 'contentISOLanguage' => $this->contentISOLanguage,
372 'contentLanguageUid' => $this->contentLanguageUid,
373 'RTEsetup' => $this->vanillaRteTsConfig,
374 'client' => $this->client,
375 'thisConfig' => $this->processedRteConfiguration,
376 'specConf' => $this->defaultExtras,
378 if ($plugin->main($configuration)) {
379 $this->registeredPlugins[$pluginId] = $plugin;
382 foreach ($this->pluginButton as $previousPluginId => $buttonList) {
383 $this->pluginButton[$previousPluginId] = implode(
',', array_diff(
GeneralUtility::trimExplode(
',', $this->pluginButton[$previousPluginId],
true), $pluginButtons));
385 $this->pluginButton[$pluginId] = $plugin->getPluginButtons();
387 foreach ($this->pluginLabel as $previousPluginId => $labelList) {
388 $this->pluginLabel[$previousPluginId] = implode(
',', array_diff(
GeneralUtility::trimExplode(
',', $this->pluginLabel[$previousPluginId],
true), $pluginLabels));
390 $this->pluginLabel[$pluginId] = $plugin->getPluginLabels();
391 $this->pluginEnabledArray[] = $pluginId;
397 $hidePlugins = array();
398 foreach ($this->registeredPlugins as $pluginId => $plugin) {
400 if ($plugin->addsButtons() && !$this->pluginButton[$pluginId]) {
401 $hidePlugins[] = $pluginId;
404 $this->pluginEnabledArray = array_unique(array_diff($this->pluginEnabledArray, $hidePlugins));
412 protected function setToolbar()
416 if ($this->client[
'browser'] ===
'msie' || $this->client[
'browser'] ===
'opera') {
417 $this->processedRteConfiguration[
'keepButtonGroupTogether'] = 0;
419 $this->defaultToolbarOrder =
'bar, blockstylelabel, blockstyle, textstylelabel, textstyle, linebreak,
420 bar, formattext, bold, strong, italic, emphasis, big, small, insertedtext, deletedtext, citation, code,'
421 .
'definition, keyboard, monospaced, quotation, sample, variable, bidioverride, strikethrough, subscript, superscript, underline, span,
422 bar, fontstyle, fontsize, bar, formatblock, insertparagraphbefore, insertparagraphafter, blockquote, line,
423 bar, left, center, right, justifyfull,
424 bar, orderedlist, unorderedlist, definitionlist, definitionitem, outdent, indent,
425 bar, language, showlanguagemarks,lefttoright, righttoleft,
426 bar, textcolor, bgcolor, textindicator,
427 bar, editelement, showmicrodata,
428 bar, image, emoticon, insertcharacter, insertsofthyphen, abbreviation, user,
431 . ($this->processedRteConfiguration[
'hideTableOperationsInToolbar']
432 && is_array($this->processedRteConfiguration[
'buttons.'])
433 && is_array($this->processedRteConfiguration[
'buttons.'][
'toggleborders.'])
434 && $this->processedRteConfiguration[
'buttons.'][
'toggleborders.'][
'keepInToolbar'] ?
' toggleborders,' :
'')
435 .
'bar, findreplace, spellcheck,
436 bar, chMode, inserttag, removeformat, bar, copy, cut, paste, pastetoggle, pastebehaviour, bar, undo, redo, bar, about, linebreak,'
437 . ($this->processedRteConfiguration[
'hideTableOperationsInToolbar'] ?
'' :
'bar, toggleborders,')
438 .
' bar, tableproperties, tablerestyle, bar, rowproperties, rowinsertabove, rowinsertunder, rowdelete, rowsplit, bar,
439 columnproperties, columninsertbefore, columninsertafter, columndelete, columnsplit, bar,
440 cellproperties, cellinsertbefore, cellinsertafter, celldelete, cellsplit, cellmerge';
443 foreach ($this->registeredPlugins as $pluginId => $plugin) {
446 $pluginButtons = $plugin->getPluginButtons();
448 $addButtons = implode(
455 $this->defaultToolbarOrder = ($addButtons ?
'bar,' . $addButtons .
',linebreak,' :
'') . $this->defaultToolbarOrder;
461 $toolbarOrder = array_unique(array_values($this->toolbarOrderArray));
463 $pList = is_array($this->defaultExtras[
'richtext'][
'parameters']) ? implode(
',', $this->defaultExtras[
'richtext'][
'parameters']) :
'';
464 if ($pList !==
'*') {
466 $show = is_array($this->defaultExtras[
'richtext'][
'parameters']) ? $this->defaultExtras[
'richtext'][
'parameters'] : array();
467 if ($this->processedRteConfiguration[
'showButtons']) {
471 $show = array_unique(array_merge($show, $toolbarOrder));
474 if (is_array($this->processedRteConfiguration[
'showButtons.'])) {
475 foreach ($this->processedRteConfiguration[
'showButtons.'] as $buttonId => $value) {
480 $show = array_unique($show);
483 $show = $toolbarOrder;
485 $RTEkeyList = isset($backendUser->userTS[
'options.'][
'RTEkeyList']) ? $backendUser->userTS[
'options.'][
'RTEkeyList'] :
'*';
486 if ($RTEkeyList !==
'*') {
491 $hideButtons = array(
'space',
'bar',
'linebreak');
492 foreach ($this->pluginButton as $pluginId => $buttonList) {
495 foreach ($buttonArray as $button) {
496 $hideButtons[] = $button;
501 foreach ($this->pluginLabel as $pluginId => $label) {
503 $hideButtons[] = $label;
509 foreach ($this->registeredPlugins as $pluginId => $plugin) {
510 if ($this->
isPluginEnabled($pluginId) && method_exists($plugin,
'applyToolbarConstraints')) {
511 $show = $plugin->applyToolbarConstraints($show);
515 $show = array_intersect($show, $toolbarOrder);
516 foreach ($this->registeredPlugins as $pluginId => $plugin) {
518 $plugin->setToolbar($show);
520 $this->toolbar = $show;
528 protected function setPlugins()
531 $hidePlugins = array();
532 foreach ($this->pluginButton as $pluginId => $buttonList) {
534 $plugin = $this->registeredPlugins[$pluginId];
535 if ($plugin->addsButtons()) {
538 foreach ($buttonArray as $button) {
539 if (in_array($button, $this->toolbar)) {
544 $hidePlugins[] = $pluginId;
548 $this->pluginEnabledArray = array_diff($this->pluginEnabledArray, $hidePlugins);
550 $hideLabels = array();
551 foreach ($this->pluginLabel as $pluginId => $label) {
553 $hideLabels[] = $label;
556 $this->toolbar = array_diff($this->toolbar, $hideLabels);
558 $requiredPlugins = array();
559 foreach ($this->registeredPlugins as $pluginId => $plugin) {
565 $requiredPlugins = array_unique($requiredPlugins);
566 foreach ($requiredPlugins as $pluginId) {
567 if (is_object($this->registeredPlugins[$pluginId]) && !$this->
isPluginEnabled($pluginId)) {
568 $this->pluginEnabledArray[] = $pluginId;
571 $this->pluginEnabledArray = array_unique($this->pluginEnabledArray);
573 foreach ($this->registeredPlugins as $pluginId => $plugin) {
576 $this->convertToolbarForHtmlAreaArray = array_unique(array_merge($this->convertToolbarForHtmlAreaArray, $plugin->getConvertToolbarForHtmlAreaArray()));
586 protected function loadRequireModulesForRTE()
588 $this->resultArray[
'requireJsModules'] = array();
589 $this->resultArray[
'requireJsModules'][] =
'TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea';
590 foreach ($this->pluginEnabledCumulativeArray as $pluginId) {
592 $plugin = $this->registeredPlugins[$pluginId];
593 $extensionKey = is_object($plugin) ? $plugin->getExtensionKey() :
'rtehtmlarea';
595 $this->resultArray[
'requireJsModules'][] = $requirePath .
'/Plugins/' . $pluginId;
606 $skinFilename = trim($this->processedRteConfiguration[
'skin']) ?:
'EXT:rtehtmlarea/Resources/Public/Css/Skin/htmlarea.css';
608 $skinDirectory = dirname($skinFilename);
612 return 'require(["TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea"], function (HTMLArea) {
613 if (typeof RTEarea === "undefined") {
614 RTEarea = new Object();
615 RTEarea[0] = new Object();
616 RTEarea[0].version = "' .
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXTCONF'][
'rtehtmlarea'][
'version'] .
'";
618 RTEarea[0].editorSkin = "' . $skinDirectory .
'/";
619 RTEarea[0].editedContentCSS = "' . $editedContentCSS .
'";
620 RTEarea.init = function() {
621 if (typeof HTMLArea === "undefined" || !Ext.isReady) {
622 window.setTimeout(function () {
629 RTEarea.initEditor = function(editorNumber) {
630 if (typeof HTMLArea === "undefined" || !HTMLArea.isReady) {
631 window.setTimeout(function () {
632 RTEarea.initEditor(editorNumber);
635 HTMLArea.initEditor(editorNumber);
648 protected function addInstanceJavaScriptRegistration()
653 $jsArray[] =
'if (typeof configureEditorInstance === "undefined") {';
654 $jsArray[] =
' configureEditorInstance = new Object();';
656 $jsArray[] =
'configureEditorInstance["' . $this->domIdentifier .
'"] = function() {';
657 $jsArray[] =
'if (typeof RTEarea === "undefined" || typeof HTMLArea === "undefined") {';
658 $jsArray[] =
' window.setTimeout("configureEditorInstance[\'' . $this->domIdentifier .
'\']();
", 40);';
659 $jsArray[] = '} else {';
660 $jsArray[] = 'editornumber = "' . $this->domIdentifier . '";';
661 $jsArray[] = 'RTEarea[editornumber] = new Object();';
662 $jsArray[] = 'RTEarea[editornumber].RTEtsConfigParams = "&
RTEtsConfigParams=
' . rawurlencode($this->RTEtsConfigParams()) . '";';
663 $jsArray[] = 'RTEarea[editornumber].number = editornumber;';
664 $jsArray[] = 'RTEarea[editornumber].deleted = false;';
665 $jsArray[] = 'RTEarea[editornumber].textAreaId = "' . $this->domIdentifier . '";';
666 $jsArray[] = 'RTEarea[editornumber].id = "RTEarea
" + editornumber;';
667 $jsArray[] = 'RTEarea[editornumber].RTEWidthOverride = "'
668 . (isset($backendUser->uc['rteWidth
']) && trim($backendUser->uc['rteWidth
'])
669 ? trim($backendUser->uc['rteWidth
'])
670 : trim($this->processedRteConfiguration['RTEWidthOverride
'])) . '";';
671 $jsArray[] = 'RTEarea[editornumber].RTEHeightOverride = "'
672 . (isset($backendUser->uc['rteHeight
']) && (int)$backendUser->uc['rteHeight
']
673 ? (int)$backendUser->uc['rteHeight
']
674 : (int)$this->processedRteConfiguration['RTEHeightOverride
']) . '";';
675 $jsArray[] = 'RTEarea[editornumber].resizable = '
676 . (isset($backendUser->uc['rteResize']) && $backendUser->uc['rteResize']
678 : (trim($this->processedRteConfiguration['rteResize']) ? 'true;' : 'false;'));
679 $jsArray[] = 'RTEarea[editornumber].maxHeight = "'
680 . (isset($backendUser->uc['rteMaxHeight
']) && (int)$backendUser->uc['rteMaxHeight
']
681 ? trim($backendUser->uc['rteMaxHeight
'])
682 : ((int)$this->processedRteConfiguration['rteMaxHeight
'] ?: '2000
')) . '";';
683 $jsArray[] = 'RTEarea[editornumber].fullScreen = ' . ($this->isInFullScreenMode() ? 'true;' : 'false;');
684 $jsArray[] = 'RTEarea[editornumber].showStatusBar = ' . (trim($this->processedRteConfiguration['showStatusBar']) ? 'true;' : 'false;');
685 $jsArray[] = 'RTEarea[editornumber].enableWordClean = ' . (trim($this->processedRteConfiguration['enableWordClean']) ? 'true;' : 'false;');
686 $jsArray[] = 'RTEarea[editornumber].htmlRemoveComments = ' . (trim($this->processedRteConfiguration['removeComments']) ? 'true;' : 'false;');
687 $jsArray[] = 'RTEarea[editornumber].disableEnterParagraphs = ' . (trim($this->processedRteConfiguration['disableEnterParagraphs']) ? 'true;' : 'false;');
688 $jsArray[] = 'RTEarea[editornumber].disableObjectResizing = ' . (trim($this->processedRteConfiguration['disableObjectResizing']) ? 'true;' : 'false;');
689 $jsArray[] = 'RTEarea[editornumber].removeTrailingBR = ' . (trim($this->processedRteConfiguration['removeTrailingBR']) ? 'true;' : 'false;');
690 $jsArray[] = 'RTEarea[editornumber].useCSS = ' . (trim($this->processedRteConfiguration['useCSS']) ? 'true' : 'false') . ';';
691 $jsArray[] = 'RTEarea[editornumber].keepButtonGroupTogether = ' . (trim($this->processedRteConfiguration['keepButtonGroupTogether']) ? 'true;' : 'false;');
692 $jsArray[] = 'RTEarea[editornumber].disablePCexamples = ' . (trim($this->processedRteConfiguration['disablePCexamples']) ? 'true;' : 'false;');
693 $jsArray[] = 'RTEarea[editornumber].showTagFreeClasses = ' . (trim($this->processedRteConfiguration['showTagFreeClasses']) ? 'true;' : 'false;');
694 $jsArray[] = 'RTEarea[editornumber].tceformsNested = ' . (!empty($this->data) ? json_encode($this->data['tabAndInlineStack']) : '[]') . ';';
695 $jsArray[] = 'RTEarea[editornumber].dialogueWindows = new Object();';
696 if (isset($this->processedRteConfiguration['dialogueWindows.']['defaultPositionFromTop'])) {
697 $jsArray[] = 'RTEarea[editornumber].dialogueWindows.positionFromTop = ' . (int)$this->processedRteConfiguration['dialogueWindows.']['defaultPositionFromTop'] . ';';
699 if (isset($this->processedRteConfiguration['dialogueWindows.']['defaultPositionFromLeft'])) {
700 $jsArray[] = 'RTEarea[editornumber].dialogueWindows.positionFromLeft = ' . (int)$this->processedRteConfiguration['dialogueWindows.']['defaultPositionFromLeft'] . ';';
702 $jsArray[] = 'RTEarea[editornumber].sys_language_content = "' . $this->contentLanguageUid . '";';
703 $jsArray[] = 'RTEarea[editornumber].typo3ContentLanguage = "' . $this->contentTypo3Language . '";';
704 $jsArray[] = 'RTEarea[editornumber].userUid = "' . 'BE_
' . $backendUser->user['uid
'] . '";';
706 // Setting the plugin flags
707 $jsArray[] = 'RTEarea[editornumber].plugin = new Object();';
708 foreach ($this->pluginEnabledArray as $pluginId) {
709 $jsArray[] = 'RTEarea[editornumber].plugin.' . $pluginId . ' = true;';
712 // Setting the buttons configuration
713 $jsArray[] = 'RTEarea[editornumber].buttons = new Object();';
714 if (is_array($this->processedRteConfiguration['buttons.'])) {
715 foreach ($this->processedRteConfiguration['buttons.'] as $buttonIndex => $conf) {
716 $button = substr($buttonIndex, 0, -1);
717 if (is_array($conf)) {
718 $jsArray[] = 'RTEarea[editornumber].buttons.' . $button . ' = ' . $this->buildNestedJSArray($conf) . ';';
723 // Setting the list of tags to be removed if specified in the RTE config
724 if (trim($this->processedRteConfiguration['removeTags'])) {
725 $jsArray[] = 'RTEarea[editornumber].htmlRemoveTags = /^(' . implode('|', GeneralUtility::trimExplode(',', $this->processedRteConfiguration['removeTags'], true)) . ')$/i;';
728 // Setting the list of tags to be removed with their contents if specified in the RTE config
729 if (trim($this->processedRteConfiguration['removeTagsAndContents'])) {
730 $jsArray[] = 'RTEarea[editornumber].htmlRemoveTagsAndContents = /^(' . implode('|', GeneralUtility::trimExplode(',', $this->processedRteConfiguration['removeTagsAndContents'], true)) . ')$/i;';
733 // Setting array of custom tags if specified in the RTE config
734 if (!empty($this->processedRteConfiguration['customTags'])) {
735 $customTags = GeneralUtility::trimExplode(',', $this->processedRteConfiguration['customTags'], true);
736 if (!empty($customTags)) {
737 $jsArray[] = 'RTEarea[editornumber].customTags= ' . json_encode($customTags) . ';';
741 // Setting array of content css files if specified in the RTE config
742 $versionNumberedFileNames = array();
743 $contentCssFileNames = $this->getContentCssFileNames();
744 foreach ($contentCssFileNames as $contentCssFileName) {
745 $versionNumberedFileNames[] = GeneralUtility::createVersionNumberedFilename($contentCssFileName);
747 $jsArray[] = 'RTEarea[editornumber].pageStyle = ["' . implode('","', $versionNumberedFileNames) . '"];';
749 $jsArray[] = 'RTEarea[editornumber].classesUrl = "' . $this->writeTemporaryFile(('classes_
' . $this->language), 'js
', $this->buildJSClassesArray()) . '";';
751 // Add Javascript configuration for registered plugins
752 foreach ($this->registeredPlugins as $pluginId => $plugin) {
754 if ($this->isPluginEnabled($pluginId)) {
755 $jsPluginString = $plugin->buildJavascriptConfiguration();
756 if ($jsPluginString) {
757 $jsArray[] = $plugin->buildJavascriptConfiguration();
762 // Avoid premature reference to HTMLArea when being initially loaded by IRRE Ajax call
763 $jsArray[] = 'RTEarea[editornumber].toolbar = ' . $this->getJSToolbarArray() . ';';
764 $jsArray[] = 'RTEarea[editornumber].convertButtonId = ' . json_encode(array_flip($this->convertToolbarForHtmlAreaArray)) . ';';
765 $jsArray[] = 'RTEarea.initEditor(editornumber);';
768 $jsArray[] = 'configureEditorInstance["' . $this->domIdentifier . '"]();';
770 $this->resultArray['additionalJavaScriptPost'][] = implode(LF, $jsArray);
778 protected function getContentCssFileNames()
780 $contentCss = is_array($this->processedRteConfiguration['contentCSS.']) ? $this->processedRteConfiguration['contentCSS.'] : array();
781 if (isset($this->processedRteConfiguration['contentCSS'])) {
782 $contentCss[] = trim($this->processedRteConfiguration['contentCSS']);
784 $contentCssFiles = array();
785 if (!empty($contentCss)) {
786 foreach ($contentCss as $contentCssKey => $contentCssfile) {
787 $fileName = trim($contentCssfile);
788 $absolutePath = GeneralUtility::getFileAbsFileName($fileName);
789 if (file_exists($absolutePath) && filesize($absolutePath)) {
790 $contentCssFiles[$contentCssKey] = $this->getFullFileName($fileName);
794 // Fallback to default content css file if none of the configured files exists and is not empty
795 $contentCssFiles['default'] = $this->getFullFileName('EXT:rtehtmlarea/Resources/Public/Css/ContentCss/Default.css');
797 return array_unique($contentCssFiles);
806 protected function isPluginEnabled($pluginId)
808 return in_array($pluginId, $this->pluginEnabledArray);
816 protected function buildJSClassesArray()
818 $RTEProperties = $this->vanillaRteTsConfig['properties'];
819 // Declare sub-arrays
820 $classesArray = array(
824 'alternating' => array(),
825 'counting' => array(),
826 'selectable' => array(),
827 'requires' => array(),
828 'requiredBy' => array(),
831 $JSClassesArray = '';
832 // Scanning the list of classes if specified in the RTE config
833 if (is_array($RTEProperties['classes.'])) {
834 foreach ($RTEProperties['classes.'] as $className => $conf) {
835 $className = rtrim($className, '.');
838 if (!empty($conf['name'])) {
839 $label = $this->getLanguageService()->sL(trim($conf['name']));
840 $label = str_replace('"', '\\
"', str_replace('\\\'', '\'', $label));
842 $classesArray['labels'][$className] = $label;
843 $classesArray['values'][$className] = str_replace('\\\'', '\'', $conf['value']);
844 if (isset($conf['noShow'])) {
845 $classesArray['noShow'][$className] = $conf['noShow'];
847 if (is_array($conf['alternating.'])) {
848 $classesArray['alternating'][$className] = $conf['alternating.'];
850 if (is_array($conf['counting.'])) {
851 $classesArray['counting'][$className] = $conf['counting.'];
853 if (isset($conf['selectable'])) {
854 $classesArray['selectable'][$className] = $conf['selectable'];
856 if (isset($conf['requires'])) {
857 $classesArray['requires'][$className] = explode(',', GeneralUtility::rmFromList($className, $this->cleanList($conf['requires'])));
860 // Remove circularities from classes dependencies
861 $requiringClasses = array_keys($classesArray['requires']);
862 foreach ($requiringClasses as $requiringClass) {
863 if ($this->hasCircularDependency($classesArray, $requiringClass, $requiringClass)) {
864 unset($classesArray['requires'][$requiringClass]);
867 // Reverse relationship for the dependency checks when removing styles
868 $requiringClasses = array_keys($classesArray['requires']);
869 foreach ($requiringClasses as $className) {
870 foreach ($classesArray['requires'][$className] as $requiredClass) {
871 if (!is_array($classesArray['requiredBy'][$requiredClass])) {
872 $classesArray['requiredBy'][$requiredClass] = array();
874 if (!in_array($className, $classesArray['requiredBy'][$requiredClass])) {
875 $classesArray['requiredBy'][$requiredClass][] = $className;
880 // Scanning the list of sets of mutually exclusives classes if specified in the RTE config
881 if (is_array($RTEProperties['mutuallyExclusiveClasses.'])) {
882 foreach ($RTEProperties['mutuallyExclusiveClasses.'] as $listName => $conf) {
883 $classSet = GeneralUtility::trimExplode(',', $conf, true);
884 $classList = implode(',', $classSet);
885 foreach ($classSet as $className) {
886 $classesArray['XOR'][$className] = '/^(' . implode('|', GeneralUtility::trimExplode(',', GeneralUtility::rmFromList($className, $classList), true)) . ')$/';
890 foreach ($classesArray as $key => $subArray) {
891 $JSClassesArray .= 'HTMLArea.classes' . ucfirst($key) . ' = ' . $this->buildNestedJSArray($subArray) . ';' . LF;
893 return $JSClassesArray;
905 protected function hasCircularDependency(&$classesArray, $requiringClass, $initialClass, $recursionLevel = 0)
907 if (is_array($classesArray['requires'][$requiringClass])) {
908 if (in_array($initialClass, $classesArray['requires'][$requiringClass])) {
911 if ($recursionLevel++ < 20) {
912 foreach ($classesArray['requires'][$requiringClass] as $requiringClass2) {
913 if ($this->hasCircularDependency($classesArray, $requiringClass2, $initialClass, $recursionLevel)) {
934 protected function buildNestedJSArray($conf)
936 $convertedConf = GeneralUtility::removeDotsFromTS($conf);
938 array(':"0
"', ':"\\/^(
', ')$\\/i
"', ':"\\/^(
', ')$\\/
"', '[]'),
939 array(':false', ':/^(', ')$/i', ':/^(', ')$/', '{}'), json_encode($convertedConf)
952 protected function writeTemporaryFile($label, $fileExtension = 'js', $contents = '')
954 $relativeFilename = 'typo3temp/RteHtmlArea/' . str_replace('-', '_', $label) . '_' . GeneralUtility::shortMD5($contents, 20) . '.' . $fileExtension;
955 $destination = PATH_site . $relativeFilename;
956 if (!file_exists($destination)) {
957 $minifiedJavaScript = '';
958 if ($fileExtension === 'js' && $contents !== '') {
959 $minifiedJavaScript = GeneralUtility::minifyJavaScript($contents);
961 $failure = GeneralUtility::writeFileToTypo3tempDir($destination, $minifiedJavaScript ? $minifiedJavaScript : $contents);
963 throw new \RuntimeException($failure, 1294585668);
966 if (isset($GLOBALS['TSFE'])) {
967 $fileName = $relativeFilename;
969 $fileName = '../' . $relativeFilename;
971 return GeneralUtility::resolveBackPath($fileName);
981 protected function createJavaScriptLanguageLabelsFromFiles()
983 $labelArray = array();
984 // Load labels of 3 base files into JS
985 foreach (array('tooltips', 'msg', 'dialogs') as $identifier) {
986 $fileName = 'EXT:rtehtmlarea/Resources/Private/Language/locallang_' . $identifier . '.xlf';
987 $newLabels = $this->getMergedLabelsFromFile($fileName);
988 if (!empty($newLabels)) {
989 $labelArray[$identifier] = $newLabels;
992 // Load labels of plugins into JS
993 foreach ($this->pluginEnabledCumulativeArray as $pluginId) {
995 $plugin = $this->registeredPlugins[$pluginId];
996 $extensionKey = is_object($plugin) ? $plugin->getExtensionKey() : 'rtehtmlarea';
997 $fileName = 'EXT:' . $extensionKey . '/Resources/Private/Language/Plugins/' . $pluginId . '/locallang_js.xlf';
998 $newLabels = $this->getMergedLabelsFromFile($fileName);
999 if (!empty($newLabels)) {
1000 $labelArray[$pluginId] = $newLabels;
1003 $javaScriptString = 'TYPO3.jQuery(function() {';
1004 $javaScriptString .= 'HTMLArea.I18N = new Object();' . LF;
1005 $javaScriptString .= 'HTMLArea.I18N = ' . json_encode($labelArray);
1006 $javaScriptString .= '});';
1007 $this->resultArray['additionalJavaScriptPost'][] = $javaScriptString;
1017 protected function getMergedLabelsFromFile($fileName)
1020 $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
1021 $localizationArray = $languageFactory->getParsedData($fileName, $this->language, 'utf-8', 1);
1022 if (is_array($localizationArray) && !empty($localizationArray)) {
1023 if (!empty($localizationArray[$this->language])) {
1024 $finalLocalLang = $localizationArray['default'];
1025 ArrayUtility::mergeRecursiveWithOverrule($finalLocalLang, $localizationArray[$this->language], true, false);
1026 $localizationArray[$this->language] = $finalLocalLang;
1028 $localizationArray[$this->language] = $localizationArray['default'];
1031 $localizationArray = array();
1033 return $localizationArray[$this->language];
1041 protected function getJSToolbarArray()
1043 // The toolbar array
1045 // The current row; a "linebreak
" ends the current row
1047 // The current group; each group is between "bar
"s; a "linebreak
" ends the current group
1049 // Process each toolbar item in the toolbar order list
1050 foreach ($this->toolbarOrderArray as $item) {
1053 // Add row to toolbar if not empty
1054 if (!empty($group)) {
1064 // Add group to row if not empty
1065 if (!empty($group)) {
1071 if (end($group) != $this->convertToolbarForHtmlAreaArray[$item]) {
1072 $group[] = $this->convertToolbarForHtmlAreaArray[$item];
1076 if (in_array($item, $this->toolbar)) {
1077 // Add the item to the group
1078 $convertedItem = $this->convertToolbarForHtmlAreaArray[$item];
1079 if ($convertedItem) {
1080 $group[] = $convertedItem;
1085 // Add the last group and last line, if not empty
1086 if (!empty($group)) {
1092 return json_encode($toolbar);
1101 protected function getFullFileName($filename)
1103 if (substr($filename, 0, 4) === 'EXT:') {
1105 list($extKey, $local) = explode('/', substr($filename, 4), 2);
1107 if ((string)$extKey !== '' && ExtensionManagementUtility::isLoaded($extKey) && (string)$local !== '') {
1108 $newFilename = ($this->isFrontendEditActive()
1109 ? ExtensionManagementUtility::siteRelPath($extKey)
1110 : ExtensionManagementUtility::extRelPath($extKey))
1114 $path = ($this->isFrontendEditActive() ? '' : '../');
1115 $newFilename = $path . ($filename[0] === '/' ? substr($filename, 1) : $filename);
1117 return GeneralUtility::resolveBackPath($newFilename);
1125 protected function addOnSubmitJavaScriptCode()
1127 $onSubmitCode = array();
1128 $onSubmitCode[] = 'if (RTEarea["' . $this->domIdentifier . '"]) {';
1129 $onSubmitCode[] = 'document.editform["' . $this->data['parameterArray
']['itemFormElName
'] . '"].value = RTEarea["' . $this->domIdentifier . '"].editor.getHTML();';
1130 $onSubmitCode[] = '} else {';
1131 $onSubmitCode[] = 'OK = 0;';
1132 $onSubmitCode[] = '};';
1133 $this->resultArray['additionalJavaScriptSubmit'][] = implode(LF, $onSubmitCode);
1141 protected function isFrontendEditActive()
1143 return is_object($GLOBALS['TSFE']) && $GLOBALS['TSFE']->beUserLogin && $GLOBALS['BE_USER']->frontendEdit instanceof FrontendEditingController;
1151 protected function clientInfo()
1153 $userAgent = GeneralUtility::getIndpEnv('HTTP_USER_AGENT');
1154 $browserInfo = ClientUtility::getBrowserInfo($userAgent);
1155 // Known engines: order is not irrelevant!
1156 $knownEngines = array('opera', 'msie', 'gecko', 'webkit');
1157 if (is_array($browserInfo['all'])) {
1158 foreach ($knownEngines as $engine) {
1159 if ($browserInfo['all'][$engine]) {
1160 $browserInfo['browser'] = $engine;
1161 $browserInfo['version'] = ClientUtility::getVersion($browserInfo['all'][$engine]);
1166 return $browserInfo;
1174 public function initializeLanguageRelatedProperties()
1176 $database = $this->getDatabaseConnection();
1177 $this->language = $GLOBALS['LANG']->lang;
1178 if ($this->language === 'default' || !$this->language) {
1179 $this->language = 'en';
1181 $currentLanguageUid = $this->data['databaseRow']['sys_language_uid'];
1182 if (is_array($currentLanguageUid)) {
1183 $currentLanguageUid = $currentLanguageUid[0];
1185 $this->contentLanguageUid = (int)max($currentLanguageUid, 0);
1186 if ($this->contentLanguageUid) {
1187 $this->contentISOLanguage = $this->language;
1188 if (ExtensionManagementUtility::isLoaded('static_info_tables')) {
1189 $tableA = 'sys_language';
1190 $tableB = 'static_languages';
1191 $selectFields = $tableA . '.uid,' . $tableB . '.lg_iso_2,' . $tableB . '.lg_country_iso_2';
1192 $tableAB = $tableA . ' LEFT JOIN ' . $tableB . ' ON ' . $tableA . '.static_lang_isocode=' . $tableB . '.uid';
1193 $whereClause = $tableA . '.uid = ' . intval($this->contentLanguageUid);
1194 $whereClause .= BackendUtility::BEenableFields($tableA);
1195 $whereClause .= BackendUtility::deleteClause($tableA);
1196 $res = $database->exec_SELECTquery($selectFields, $tableAB, $whereClause);
1197 while ($languageRow = $database->sql_fetch_assoc($res)) {
1198 $this->contentISOLanguage = strtolower(trim($languageRow['lg_iso_2']) . (trim($languageRow['lg_country_iso_2']) ? '_' . trim($languageRow['lg_country_iso_2']) : ''));
1200 $database->sql_free_result($res);
1203 $this->contentISOLanguage = trim($this->processedRteConfiguration['defaultContentLanguage']) ?: 'en';
1204 $languageCodeParts = explode('_', $this->contentISOLanguage);
1205 $this->contentISOLanguage = strtolower($languageCodeParts[0]) . ($languageCodeParts[1] ? '_' . strtoupper($languageCodeParts[1]) : '');
1206 // Find the configured language in the list of localization locales
1208 $locales = GeneralUtility::makeInstance(Locales::class);
1209 // If not found, default to 'en'
1210 if (!in_array($this->contentISOLanguage, $locales->getLocales())) {
1211 $this->contentISOLanguage = 'en';
1214 $this->contentTypo3Language = $this->contentISOLanguage === 'en' ? 'default' : $this->contentISOLanguage;
1225 protected function logDeprecatedProperty($deprecatedProperty, $useProperty, $version)
1227 $backendUser = $this->getBackendUserAuthentication();
1228 if (!$this->processedRteConfiguration['logDeprecatedProperties.']['disabled']) {
1230 '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.',
1231 $deprecatedProperty,
1234 $this->data['databaseRow']['pid']
1236 GeneralUtility::deprecationLog($message);
1237 if ($this->processedRteConfiguration['logDeprecatedProperties.']['logAlsoToBELog']) {
1239 $this->getLanguageService()->sL('LLL:EXT:rtehtmlarea/Resources/Private/Language/locallang.xlf:deprecatedPropertyMessage'),
1240 $deprecatedProperty,
1243 $this->data['databaseRow']['pid']
1245 $backendUser->simplelog($message, 'rtehtmlarea');
1255 protected function RTEtsConfigParams()
1257 $parameters = BackendUtility::getSpecConfParametersFromArray($this->defaultExtras['rte_transform']['parameters']);
1259 $this->data['tableName'],
1260 $this->data['databaseRow']['uid'],
1261 $this->data['fieldName'],
1262 $this->pidOfVersionedMotherRecord,
1263 $this->data['recordTypeValue'],
1264 $this->pidOfPageRecord,
1265 $parameters['imgpath'],
1267 return implode(':', $result);
1276 protected function cleanList($str)
1278 if (strstr($str, '*')) {
1281 $str = implode(',', array_unique(GeneralUtility::trimExplode(',', $str, true)));
1292 protected function transformDatabaseContentToEditor($value)
1294 // change <strong> to <b>
1295 $value = preg_replace('/<(\\/?)strong/i', '<$1b', $value);
1296 // change <em> to <i>
1297 $value = preg_replace('/<(\\/?)em([^b>]*>)/i', '<$1i$2', $value);
1299 if ($this->defaultExtras['rte_transform']) {
1300 $parameters = BackendUtility::getSpecConfParametersFromArray($this->defaultExtras['rte_transform']['parameters']);
1301 // There must be a mode set for transformation
1302 if ($parameters['mode']) {
1304 $parseHTML = GeneralUtility::makeInstance(RteHtmlParser::class);
1305 $parseHTML->init($this->data['table'] . ':' . $this->data['fieldName'], $this->pidOfVersionedMotherRecord);
1306 $parseHTML->setRelPath('');
1307 $value = $parseHTML->RTE_transform($value, $this->defaultExtras, 'rte', $this->processedRteConfiguration);
1318 protected function isInFullScreenMode()
1320 return GeneralUtility::_GP('M') === 'wizard_rte';
1326 protected function getLanguageService()
1328 return $GLOBALS['LANG'];
1334 protected function getBackendUserAuthentication()
1336 return $GLOBALS['BE_USER'];
1342 protected function getDatabaseConnection()
1344 return $GLOBALS['TYPO3_DB'];