2 namespace TYPO3\CMS\Rtehtmlarea
;
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\FormEngine
;
18 use TYPO3\CMS\Backend\Utility\BackendUtility
;
19 use TYPO3\CMS\Core\Utility\GeneralUtility
;
22 * A RTE using the htmlArea editor
24 * @author Philipp Borgmann <philipp.borgmann@gmx.de>
25 * @author Stanislas Rolland <typo3(arobas)sjbr.ca>
27 class RteHtmlAreaBase
extends \TYPO3\CMS\Backend\Rte\AbstractRte
{
29 // Configuration of supported browsers
33 public $conf_supported_browser = array(
58 'disallowed' => array(
69 'disallowed' => array(
78 // Always hide these toolbar buttons (TYPO3 button name)
82 public $conf_toolbar_hide = array(
86 // The order of the toolbar: the name is the TYPO3-button name
90 public $defaultToolbarOrder;
92 // Conversion array: TYPO3 button names to htmlArea button names
96 public $convertToolbarForHtmlAreaArray = array(
97 'showhelp' => 'ShowHelp',
100 'linebreak' => 'linebreak'
106 public $pluginButton = array();
111 public $pluginLabel = array();
113 // Alternative style for RTE <div> tag.
116 // Relative path to this extension. It ends with "/"
119 public $backPath = '';
130 // Identifies the RTE as being the one from the "rtehtmlarea" extension if any external code needs to know
134 public $ID = 'rtehtmlarea';
136 // If set, the content goes into a regular TEXT area field - for developing testing of transformations.
140 public $debugMode = FALSE;
149 * Reference to parent object, which is an instance of the TCEforms
151 * @var \TYPO3\CMS\Backend\Form\FormEngine
163 public $elementParts;
192 * TYPO3 language code of the content language
194 public $contentTypo3Language;
196 * ISO language code of the content language
198 public $contentISOLanguage;
200 * Language service object for localization to the content language
202 protected $contentLanguageService;
203 public $charset = 'utf-8';
205 public $contentCharset = 'utf-8';
207 public $OutputCharset = 'utf-8';
222 public $toolbar = array();
224 // Save the buttons for the toolbar
228 public $toolbarOrderArray = array();
230 protected $pluginEnabledArray = array();
232 // Array of plugin id's enabled in the current RTE editing area
233 protected $pluginEnabledCumulativeArray = array();
235 // Cumulative array of plugin id's enabled so far in any of the RTE editing areas of the form
236 public $registeredPlugins = array();
238 // Array of registered plugins indexed by their plugin Id's
239 protected $fullScreen = FALSE;
240 // Page renderer object
241 protected $pageRenderer;
244 * Returns TRUE if the RTE is available. Here you check if the browser requirements are met.
245 * If there are reasons why the RTE cannot be displayed you simply enter them as text in ->errorLog
247 * @return bool TRUE if this RTE object offers an RTE in the current browser environment
249 public function isAvailable() {
250 $this->client
= $this->clientInfo();
251 $this->errorLog
= array();
252 if (!$this->debugMode
) {
253 // If debug-mode, let any browser through
254 $rteIsAvailable = FALSE;
255 $rteConfBrowser = $this->conf_supported_browser
;
256 if (is_array($rteConfBrowser)) {
257 foreach ($rteConfBrowser as $browser => $browserConf) {
258 if ($browser == $this->client
['browser']) {
259 // Config for Browser found, check it:
260 if (is_array($browserConf)) {
261 foreach ($browserConf as $browserConfSub) {
262 if ($browserConfSub['version'] <= $this->client
['version'] ||
empty($browserConfSub['version'])) {
263 // Version is supported
264 if (is_array($browserConfSub['system'])) {
265 // Check against allowed systems
266 if (is_array($browserConfSub['system']['allowed'])) {
267 foreach ($browserConfSub['system']['allowed'] as $system) {
268 if (in_array($system, $this->client
['all_systems'])) {
269 $rteIsAvailable = TRUE;
275 $rteIsAvailable = TRUE;
277 // Check against disallowed systems
278 if (is_array($browserConfSub['system']['disallowed'])) {
279 foreach ($browserConfSub['system']['disallowed'] as $system) {
280 if (in_array($system, $this->client
['all_systems'])) {
281 $rteIsAvailable = FALSE;
287 // No system config: system is supported
288 $rteIsAvailable = TRUE;
294 // no config for this browser found, so all versions or system with this browsers are allow
295 $rteIsAvailable = TRUE;
303 if (!$rteIsAvailable) {
304 $this->errorLog
[] = 'RTE: Browser not supported.';
306 if (\TYPO3\CMS\Core\Utility\VersionNumberUtility
::convertVersionNumberToInteger(TYPO3_version
) < 4000000) {
307 $rteIsAvailable = FALSE;
308 $this->errorLog
[] = 'rte: This version of htmlArea RTE cannot run under this version of TYPO3.';
311 return $rteIsAvailable;
315 * Draws the RTE as an iframe
317 * @param FormEngine $parentObject Reference to parent object, which is an instance of the TCEforms.
318 * @param string $table The table name
319 * @param string $field The field name
320 * @param array $row The current row from which field is being rendered
321 * @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.
322 * @param array $specConf "special" configuration - what is found at position 4 in the types configuration of a field from record, parsed into an array.
323 * @param array $thisConfig Configuration for RTEs; A mix between TSconfig and otherwise. Contains configuration for display, which buttons are enabled, additional transformation information etc.
324 * @param string $RTEtypeVal Record "type" field value.
325 * @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!
326 * @param int $thePidValue PID value of record (true parent page id)
327 * @return string HTML code for RTE!
329 public function drawRTE($parentObject, $table, $field, $row, $PA, $specConf, $thisConfig, $RTEtypeVal, $RTErelPath, $thePidValue) {
330 global $LANG, $TYPO3_DB;
331 $this->TCEform
= $parentObject;
332 $inline = $this->TCEform
->inline
;
333 $LANG->includeLLFile('EXT:' . $this->ID
. '/locallang.xml');
334 $this->client
= $this->clientInfo();
335 $this->typoVersion
= \TYPO3\CMS\Core\Utility\VersionNumberUtility
::convertVersionNumberToInteger(TYPO3_version
);
336 $this->userUid
= 'BE_' . $GLOBALS['BE_USER']->user
['uid'];
337 // Draw form element:
338 if ($this->debugMode
) {
339 // Draws regular text area (debug mode)
340 $item = parent
::drawRTE($this->TCEform
, $table, $field, $row, $PA, $specConf, $thisConfig, $RTEtypeVal, $RTErelPath, $thePidValue);
343 /* =======================================
344 * INIT THE EDITOR-SETTINGS
345 * =======================================
348 $this->backPath
= $this->TCEform
->backPath
;
349 // Get the path to this extension:
350 $this->extHttpPath
= $this->backPath
. \TYPO3\CMS\Core\Utility\ExtensionManagementUtility
::extRelPath($this->ID
);
352 $this->siteURL
= GeneralUtility
::getIndpEnv('TYPO3_SITE_URL');
354 $this->hostURL
= $this->siteURL
. TYPO3_mainDir
;
356 $this->elementId
= $PA['itemFormElName'];
358 $this->elementParts
= explode('][', preg_replace('/\\]$/', '', preg_replace('/^(TSFE_EDIT\\[data\\]\\[|data\\[)/', '', $this->elementId
)));
359 // Find the page PIDs:
360 list($this->tscPID
, $this->thePid
) = BackendUtility
::getTSCpid(trim($this->elementParts
[0]), trim($this->elementParts
[1]), $thePidValue);
361 // Record "types" field value:
362 $this->typeVal
= $RTEtypeVal;
363 // TCA "types" value for record
364 // Find "thisConfig" for record/editor:
365 unset($this->RTEsetup
);
366 $this->RTEsetup
= $GLOBALS['BE_USER']->getTSConfig('RTE', BackendUtility
::getPagesTSconfig($this->tscPID
));
367 $this->thisConfig
= $thisConfig;
368 // Special configuration and default extras:
369 $this->specConf
= $specConf;
370 if ($this->thisConfig
['forceHTTPS']) {
371 $this->extHttpPath
= preg_replace('/^(http|https)/', 'https', $this->extHttpPath
);
372 $this->siteURL
= preg_replace('/^(http|https)/', 'https', $this->siteURL
);
373 $this->hostURL
= preg_replace('/^(http|https)/', 'https', $this->hostURL
);
375 // Register RTE windows
376 $this->TCEform
->RTEwindows
[] = $PA['itemFormElName'];
377 $textAreaId = preg_replace('/[^a-zA-Z0-9_:.-]/', '_', $PA['itemFormElName']);
378 $textAreaId = htmlspecialchars(preg_replace('/^[^a-zA-Z]/', 'x', $textAreaId));
379 /* =======================================
380 * LANGUAGES & CHARACTER SETS
381 * =======================================
383 // Languages: interface and content
384 $this->language
= $GLOBALS['LANG']->lang
;
385 if ($this->language
=== 'default' ||
!$this->language
) {
386 $this->language
= 'en';
388 $this->contentLanguageUid
= max($row['sys_language_uid'], 0);
389 if ($this->contentLanguageUid
) {
390 $this->contentISOLanguage
= $this->language
;
391 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility
::isLoaded('static_info_tables')) {
392 $tableA = 'sys_language';
393 $tableB = 'static_languages';
394 $selectFields = $tableA . '.uid,' . $tableB . '.lg_iso_2,' . $tableB . '.lg_country_iso_2';
395 $tableAB = $tableA . ' LEFT JOIN ' . $tableB . ' ON ' . $tableA . '.static_lang_isocode=' . $tableB . '.uid';
396 $whereClause = $tableA . '.uid = ' . intval($this->contentLanguageUid
);
397 $whereClause .= BackendUtility
::BEenableFields($tableA);
398 $whereClause .= BackendUtility
::deleteClause($tableA);
399 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($selectFields, $tableAB, $whereClause);
400 while ($languageRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
401 $this->contentISOLanguage
= strtolower(trim($languageRow['lg_iso_2']) . (trim($languageRow['lg_country_iso_2']) ?
'_' . trim($languageRow['lg_country_iso_2']) : ''));
405 $this->contentISOLanguage
= trim($this->thisConfig
['defaultContentLanguage']) ?
: 'en';
406 $languageCodeParts = explode('_', $this->contentISOLanguage
);
407 $this->contentISOLanguage
= strtolower($languageCodeParts[0]) . ($languageCodeParts[1] ?
'_' . strtoupper($languageCodeParts[1]) : '');
408 // Find the configured language in the list of localization locales
409 /** @var $locales \TYPO3\CMS\Core\Localization\Locales */
410 $locales = GeneralUtility
::makeInstance('TYPO3\\CMS\\Core\\Localization\\Locales');
411 // If not found, default to 'en'
412 if (!in_array($this->contentISOLanguage
, $locales->getLocales())) {
413 $this->contentISOLanguage
= 'en';
416 // Create content laguage service
417 $this->contentLanguageService
= GeneralUtility
::makeInstance('TYPO3\\CMS\\Lang\\LanguageService');
418 $this->contentTypo3Language
= $this->contentISOLanguage
=== 'en' ?
'default' : $this->contentISOLanguage
;
419 $this->contentLanguageService
->init($this->contentTypo3Language
);
420 /* =======================================
421 * TOOLBAR CONFIGURATION
422 * =======================================
424 $this->initializeToolbarConfiguration();
425 /* =======================================
427 * =======================================
429 // Check if wizard_rte called this for fullscreen edtition
430 if (GeneralUtility
::_GP('M') === 'wizard_rte') {
431 $this->fullScreen
= TRUE;
434 $RTEPaddingRight = '0';
435 $editorWrapWidth = '100%';
437 $options = $GLOBALS['BE_USER']->userTS
['options.'];
438 $RTEWidth = 530 +
(isset($options['RTELargeWidthIncrement']) ?
(int)$options['RTELargeWidthIncrement'] : 150);
439 $RTEWidth -= $inline->getStructureDepth() > 0 ?
($inline->getStructureDepth() +
1) * $inline->getLevelMargin() : 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%';
447 $RTEWidth = (int)$RTEWidthOverride > 0 ?
(int)$RTEWidthOverride : $RTEWidth;
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%';
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 * =======================================
463 $this->pageRenderer
= $GLOBALS['SOBE']->doc
->getPageRenderer();
464 // Preloading the pageStyle and including RTE skin stylesheets
465 $this->addPageStyle();
467 // Register RTE in JS
468 $this->TCEform
->additionalJS_post
[] = $this->registerRTEinJS($this->TCEform
->RTEcounter
, $table, $row['uid'], $field, $textAreaId);
469 // Set the save option for the RTE
470 $this->TCEform
->additionalJS_submit
[] = $this->setSaveRTE($this->TCEform
->RTEcounter
, $this->TCEform
->formName
, $textAreaId, $PA['itemFormElName']);
471 $this->TCEform
->additionalJS_delete
[] = $this->setDeleteRTE($this->TCEform
->RTEcounter
, $this->TCEform
->formName
, $textAreaId);
472 // Loading ExtJs inline code
473 $this->pageRenderer
->enableExtJSQuickTips();
474 // Add TYPO3 notifications JavaScript
475 $this->pageRenderer
->addJsFile('sysext/backend/Resources/Public/JavaScript/notifications.js');
476 // Add RTE JavaScript
477 $this->addRteJsFiles($this->TCEform
->RTEcounter
);
478 $this->pageRenderer
->addJsFile($this->buildJSMainLangFile($this->TCEform
->RTEcounter
));
479 $this->pageRenderer
->addJsInlineCode('HTMLArea-init', $this->getRteInitJsCode(), TRUE);
480 /* =======================================
482 * =======================================
485 $value = $this->transformContent('rte', $PA['itemFormElValue'], $table, $field, $row, $specConf, $thisConfig, $RTErelPath, $thePidValue);
486 // Further content transformation by registered plugins
487 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
488 if ($this->isPluginEnabled($pluginId) && method_exists($plugin, 'transformContent')) {
489 $value = $plugin->transformContent($value);
493 $visibility = 'hidden';
494 $item = $this->triggerField($PA['itemFormElName']) . '
495 <div id="pleasewait' . $textAreaId . '" class="pleasewait" style="display: block;" >' . $LANG->getLL('Please wait') . '</div>
496 <div id="editorWrap' . $textAreaId . '" class="editorWrap" style="visibility: hidden; width:' . $editorWrapWidth . '; height:' . $editorWrapHeight . ';">
497 <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>
505 * Add links to content style sheets to document header
509 protected function addPageStyle() {
510 $contentCssFileNames = $this->getContentCssFileNames();
511 foreach ($contentCssFileNames as $contentCssKey => $contentCssFile) {
512 $this->addStyleSheet('rtehtmlarea-content-' . $contentCssKey, $contentCssFile, 'htmlArea RTE Content CSS', 'alternate stylesheet');
517 * Get the name of the contentCSS files to use
519 * @return array An array of full file name of the content css files to use
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']);
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);
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
. '/res/contentcss/default.css');
540 return array_unique($contentCssFiles);
544 * Add links to skin style sheet(s) to document header
548 protected function addSkin() {
549 // Get skin file name from Page TSConfig if any
550 $skinFilename = trim($this->thisConfig
['skin']) ?
: 'EXT:' . $this->ID
. '/htmlarea/skins/default/htmlarea.css';
551 $this->editorCSS
= $this->getFullFileName($skinFilename);
552 $skinDir = dirname($this->editorCSS
);
553 // Editing area style sheet
554 $this->editedContentCSS
= $skinDir . '/htmlarea-edited-content.css';
555 $this->addStyleSheet('rtehtmlarea-editing-area-skin', $this->editedContentCSS
);
557 $this->addStyleSheet('rtehtmlarea-skin', $this->editorCSS
);
558 // Additional icons from registered plugins
559 foreach ($this->pluginEnabledCumulativeArray
[$this->TCEform
->RTEcounter
] as $pluginId) {
560 if (is_object($this->registeredPlugins
[$pluginId])) {
561 $pathToSkin = $this->registeredPlugins
[$pluginId]->getPathToSkin();
563 $key = $this->registeredPlugins
[$pluginId]->getExtensionKey();
564 $this->addStyleSheet('rtehtmlarea-plugin-' . $pluginId . '-skin', ($this->is_FE() ? \TYPO3\CMS\Core\Utility\ExtensionManagementUtility
::siteRelPath($key) : $this->backPath
. \TYPO3\CMS\Core\Utility\ExtensionManagementUtility
::extRelPath($key)) . $pathToSkin);
571 * Add style sheet file to document header
573 * @param string $key: some key identifying the style sheet
574 * @param string $href: uri to the style sheet file
575 * @param string $title: value for the title attribute of the link element
576 * @param string $relation: value for the rel attribute of the link element
579 protected function addStyleSheet($key, $href, $title = '', $relation = 'stylesheet') {
580 // If it was not known that an RTE-enabled CE would be created when the page was first created, the css would not be added to head
581 if (is_object($this->TCEform
->inline
) && $this->TCEform
->inline
->isAjaxCall
) {
582 $this->TCEform
->additionalCode_pre
[$key] = '<link rel="' . $relation . '" type="text/css" href="' . $href . '" title="' . $title . '" />';
584 $this->pageRenderer
->addCssFile($href, $relation, 'screen', $title);
589 * Initialize toolbar configuration and enable registered plugins
593 protected function initializeToolbarConfiguration() {
594 // Enable registred plugins
595 $this->enableRegisteredPlugins();
598 // Check if some plugins need to be disabled
600 // Merge the list of enabled plugins with the lists from the previous RTE editing areas on the same form
601 $this->pluginEnabledCumulativeArray
[$this->TCEform
->RTEcounter
] = $this->pluginEnabledArray
;
602 if ($this->TCEform
->RTEcounter
> 1 && isset($this->pluginEnabledCumulativeArray
[$this->TCEform
->RTEcounter
- 1]) && is_array($this->pluginEnabledCumulativeArray
[$this->TCEform
->RTEcounter
- 1])) {
603 $this->pluginEnabledCumulativeArray
[$this->TCEform
->RTEcounter
] = array_unique(array_values(array_merge($this->pluginEnabledArray
, $this->pluginEnabledCumulativeArray
[$this->TCEform
->RTEcounter
- 1])));
608 * Add registered plugins to the array of enabled plugins
610 public function enableRegisteredPlugins() {
611 global $TYPO3_CONF_VARS;
612 // Traverse registered plugins
613 if (is_array($TYPO3_CONF_VARS['EXTCONF'][$this->ID
]['plugins'])) {
614 foreach ($TYPO3_CONF_VARS['EXTCONF'][$this->ID
]['plugins'] as $pluginId => $pluginObjectConfiguration) {
616 if (is_array($pluginObjectConfiguration) && count($pluginObjectConfiguration)) {
617 $plugin = GeneralUtility
::getUserObj($pluginObjectConfiguration['objectReference']);
619 if (is_object($plugin)) {
620 if ($plugin->main($this)) {
621 $this->registeredPlugins
[$pluginId] = $plugin;
622 // Override buttons from previously registered plugins
623 $pluginButtons = GeneralUtility
::trimExplode(',', $plugin->getPluginButtons(), TRUE);
624 foreach ($this->pluginButton
as $previousPluginId => $buttonList) {
625 $this->pluginButton
[$previousPluginId] = implode(',', array_diff(GeneralUtility
::trimExplode(',', $this->pluginButton
[$previousPluginId], TRUE), $pluginButtons));
627 $this->pluginButton
[$pluginId] = $plugin->getPluginButtons();
628 $pluginLabels = GeneralUtility
::trimExplode(',', $plugin->getPluginLabels(), TRUE);
629 foreach ($this->pluginLabel
as $previousPluginId => $labelList) {
630 $this->pluginLabel
[$previousPluginId] = implode(',', array_diff(GeneralUtility
::trimExplode(',', $this->pluginLabel
[$previousPluginId], TRUE), $pluginLabels));
632 $this->pluginLabel
[$pluginId] = $plugin->getPluginLabels();
633 $this->pluginEnabledArray
[] = $pluginId;
639 $hidePlugins = array();
640 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
641 if ($plugin->addsButtons() && !$this->pluginButton
[$pluginId]) {
642 $hidePlugins[] = $pluginId;
645 $this->pluginEnabledArray
= array_unique(array_diff($this->pluginEnabledArray
, $hidePlugins));
649 * Set the toolbar config (only in this PHP-Object, not in JS):
651 public function setToolbar() {
652 if ($this->client
['browser'] == 'msie' ||
$this->client
['browser'] == 'opera') {
653 $this->thisConfig
['keepButtonGroupTogether'] = 0;
655 $this->defaultToolbarOrder
= 'bar, blockstylelabel, blockstyle, space, textstylelabel, textstyle, linebreak,
656 bar, formattext, bold, strong, italic, emphasis, big, small, insertedtext, deletedtext, citation, code, definition, keyboard, monospaced, quotation, sample, variable, bidioverride, strikethrough, subscript, superscript, underline, span,
657 bar, fontstyle, space, fontsize, bar, formatblock, insertparagraphbefore, insertparagraphafter, blockquote, line,
658 bar, left, center, right, justifyfull,
659 bar, orderedlist, unorderedlist, definitionlist, definitionitem, outdent, indent, bar, lefttoright, righttoleft, language, showlanguagemarks,
660 bar, textcolor, bgcolor, textindicator,
661 bar, editelement, showmicrodata, emoticon, insertcharacter, insertsofthyphen, link, unlink, image, table,' . ($this->thisConfig
['hideTableOperationsInToolbar'] && is_array($this->thisConfig
['buttons.']) && is_array($this->thisConfig
['buttons.']['toggleborders.']) && $this->thisConfig
['buttons.']['toggleborders.']['keepInToolbar'] ?
' toggleborders,' : '') . ' user, acronym, bar, findreplace, spellcheck,
662 bar, chMode, inserttag, removeformat, bar, copy, cut, paste, pastetoggle, pastebehaviour, bar, undo, redo, bar, showhelp, about, linebreak,
663 ' . ($this->thisConfig
['hideTableOperationsInToolbar'] ?
'' : 'bar, toggleborders,') . ' bar, tableproperties, tablerestyle, bar, rowproperties, rowinsertabove, rowinsertunder, rowdelete, rowsplit, bar,
664 columnproperties, columninsertbefore, columninsertafter, columndelete, columnsplit, bar,
665 cellproperties, cellinsertbefore, cellinsertafter, celldelete, cellsplit, cellmerge';
666 // Additional buttons from registered plugins
667 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
668 if ($this->isPluginEnabled($pluginId)) {
669 $this->defaultToolbarOrder
= $plugin->addButtonsToToolbar();
672 $toolbarOrder = $this->thisConfig
['toolbarOrder'] ?
: $this->defaultToolbarOrder
;
673 // Getting rid of undefined buttons
674 $this->toolbarOrderArray
= array_intersect(GeneralUtility
::trimExplode(',', $toolbarOrder, TRUE), GeneralUtility
::trimExplode(',', $this->defaultToolbarOrder
, TRUE));
675 $toolbarOrder = array_unique(array_values($this->toolbarOrderArray
));
676 // Fetching specConf for field from backend
677 $pList = is_array($this->specConf
['richtext']['parameters']) ?
implode(',', $this->specConf
['richtext']['parameters']) : '';
680 $show = is_array($this->specConf
['richtext']['parameters']) ?
$this->specConf
['richtext']['parameters'] : array();
681 if ($this->thisConfig
['showButtons']) {
682 if (!GeneralUtility
::inList($this->thisConfig
['showButtons'], '*')) {
683 $show = array_unique(array_merge($show, GeneralUtility
::trimExplode(',', $this->thisConfig
['showButtons'], TRUE)));
685 $show = array_unique(array_merge($show, $toolbarOrder));
688 if (is_array($this->thisConfig
['showButtons.'])) {
689 foreach ($this->thisConfig
['showButtons.'] as $buttonId => $value) {
694 $show = array_unique($show);
697 $show = $toolbarOrder;
699 // Resticting to RTEkeyList for backend user
700 if (is_object($GLOBALS['BE_USER'])) {
701 $RTEkeyList = isset($GLOBALS['BE_USER']->userTS
['options.']['RTEkeyList']) ?
$GLOBALS['BE_USER']->userTS
['options.']['RTEkeyList'] : '*';
702 if ($RTEkeyList != '*') {
704 $show = array_intersect($show, GeneralUtility
::trimExplode(',', $RTEkeyList, TRUE));
707 // Hiding buttons of disabled plugins
708 $hideButtons = array('space', 'bar', 'linebreak');
709 foreach ($this->pluginButton
as $pluginId => $buttonList) {
710 if (!$this->isPluginEnabled($pluginId)) {
711 $buttonArray = GeneralUtility
::trimExplode(',', $buttonList, TRUE);
712 foreach ($buttonArray as $button) {
713 $hideButtons[] = $button;
717 // Hiding labels of disabled plugins
718 foreach ($this->pluginLabel
as $pluginId => $label) {
719 if (!$this->isPluginEnabled($pluginId)) {
720 $hideButtons[] = $label;
724 $show = array_diff($show, $this->conf_toolbar_hide
, GeneralUtility
::trimExplode(',', $this->thisConfig
['hideButtons'], TRUE));
725 // Apply toolbar constraints from registered plugins
726 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
727 if ($this->isPluginEnabled($pluginId) && method_exists($plugin, 'applyToolbarConstraints')) {
728 $show = $plugin->applyToolbarConstraints($show);
731 // Getting rid of the buttons for which we have no position
732 $show = array_intersect($show, $toolbarOrder);
733 $this->toolbar
= $show;
737 * Disable some plugins
739 public function setPlugins() {
740 // Disabling a plugin that adds buttons if none of its buttons is in the toolbar
741 $hidePlugins = array();
742 foreach ($this->pluginButton
as $pluginId => $buttonList) {
743 if ($this->registeredPlugins
[$pluginId]->addsButtons()) {
745 $buttonArray = GeneralUtility
::trimExplode(',', $buttonList, TRUE);
746 foreach ($buttonArray as $button) {
747 if (in_array($button, $this->toolbar
)) {
752 $hidePlugins[] = $pluginId;
756 $this->pluginEnabledArray
= array_diff($this->pluginEnabledArray
, $hidePlugins);
757 // Hiding labels of disabled plugins
758 $hideLabels = array();
759 foreach ($this->pluginLabel
as $pluginId => $label) {
760 if (!$this->isPluginEnabled($pluginId)) {
761 $hideLabels[] = $label;
764 $this->toolbar
= array_diff($this->toolbar
, $hideLabels);
765 // Adding plugins declared as prerequisites by enabled plugins
766 $requiredPlugins = array();
767 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
768 if ($this->isPluginEnabled($pluginId)) {
769 $requiredPlugins = array_merge($requiredPlugins, GeneralUtility
::trimExplode(',', $plugin->getRequiredPlugins(), TRUE));
772 $requiredPlugins = array_unique($requiredPlugins);
773 foreach ($requiredPlugins as $pluginId) {
774 if (is_object($this->registeredPlugins
[$pluginId]) && !$this->isPluginEnabled($pluginId)) {
775 $this->pluginEnabledArray
[] = $pluginId;
778 $this->pluginEnabledArray
= array_unique($this->pluginEnabledArray
);
779 // Completing the toolbar conversion array for htmlArea
780 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
781 if ($this->isPluginEnabled($pluginId)) {
782 $this->convertToolbarForHtmlAreaArray
= array_unique(array_merge($this->convertToolbarForHtmlAreaArray
, $plugin->getConvertToolbarForHtmlAreaArray()));
788 * Convert the TYPO3 names of buttons into the names for htmlArea RTE
790 * @param string buttonname (typo3-name)
791 * @return string buttonname (htmlarea-name)
793 public function convertToolbarForHTMLArea($button) {
794 return $this->convertToolbarForHtmlAreaArray
[$button];
798 * Add RTE main scripts and plugin scripts
800 * @param string $RTEcounter: The index number of the current RTE editing area within the form.
803 protected function addRteJsFiles($RTEcounter) {
804 // Component files. Order is important.
807 'NameSpace/NameSpace',
808 'UserAgent/UserAgent',
810 'Configuration/HTMLArea.Config',
811 'Extjs/ux/Ext.ux.HTMLAreaButton',
812 'Extjs/ux/Ext.ux.Toolbar.HTMLAreaToolbarText',
813 'Extjs/ux/Ext.ux.form.HTMLAreaCombo',
814 'Editor/HTMLArea.Toolbar',
815 'Editor/HTMLArea.Iframe',
816 'Editor/HTMLArea.StatusBar',
817 'Editor/HTMLArea.Framework',
818 'Editor/HTMLArea.Editor',
819 'Ajax/HTMLArea.Ajax',
820 'Util/HTMLArea.util.TYPO3',
821 'Util/HTMLArea.util',
823 'DOM/HTMLArea.DOM.Walker',
824 'DOM/HTMLArea.DOM.Selection',
825 'DOM/HTMLArea.DOM.BookMark',
826 'DOM/HTMLArea.DOM.Node',
827 'CSS/HTMLArea.CSS.Parser',
828 'Util/HTMLArea.util.Tips',
829 'Util/HTMLArea.util.Color',
830 'Extjs/Ext.ColorPalette',
831 'Extjs/ux/Ext.ux.menu.HTMLAreaColorMenu',
832 'Extjs/ux/Ext.ux.form.ColorPaletteField',
836 foreach ($components as $component) {
837 $this->pageRenderer
->addJsFile($this->getFullFileName('EXT:' . $this->ID
. '/htmlarea/' . $component . '.js'));
839 foreach ($this->pluginEnabledCumulativeArray
[$RTEcounter] as $pluginId) {
840 $extensionKey = is_object($this->registeredPlugins
[$pluginId]) ?
$this->registeredPlugins
[$pluginId]->getExtensionKey() : $this->ID
;
841 $this->pageRenderer
->addJsFile($this->getFullFileName('EXT:' . $extensionKey . '/htmlarea/plugins/' . $pluginId . '/' . strtolower(preg_replace('/([a-z])([A-Z])([a-z])/', '$1-$2$3', $pluginId)) . '.js'));
843 $this->pageRenderer
->addJsFile($this->getFullFileName('EXT:' . $this->ID
. '/htmlarea/Util/Wrap.close.js'));
847 * Return RTE initialization inline JavaScript code
849 * @return string RTE initialization inline JavaScript code
851 protected function getRteInitJsCode() {
853 if (typeof RTEarea === "undefined") {
854 RTEarea = new Object();
855 RTEarea[0] = new Object();
856 RTEarea[0].version = "' . $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->ID
]['version'] . '";
857 RTEarea[0].editorUrl = "' . $this->extHttpPath
. 'htmlarea/";
858 RTEarea[0].editorCSS = "' . GeneralUtility
::createVersionNumberedFilename($this->editorCSS
) . '";
859 RTEarea[0].editorSkin = "' . dirname($this->editorCSS
) . '/";
860 RTEarea[0].editedContentCSS = "' . GeneralUtility
::createVersionNumberedFilename($this->editedContentCSS
) . '";
861 RTEarea[0].hostUrl = "' . $this->hostURL
. '";
862 RTEarea.init = function() {
863 if (typeof HTMLArea === "undefined" || !Ext.isReady) {
864 window.setTimeout("RTEarea.init();", 10);
866 Ext.QuickTips.init();
870 RTEarea.initEditor = function(editorNumber) {
871 if (typeof HTMLArea === "undefined" || !HTMLArea.isReady) {
872 RTEarea.initEditor.defer(40, null, [editorNumber]);
874 HTMLArea.initEditor(editorNumber);
882 * Return the Javascript code for configuring the RTE
884 * @param int $RTEcounter: The index number of the current RTE editing area within the form.
885 * @param string $table: The table that includes this RTE (optional, necessary for IRRE).
886 * @param string $uid: The uid of that table that includes this RTE (optional, necessary for IRRE).
887 * @param string $field: The field of that record that includes this RTE (optional).
888 * @param string $textAreaId ID of the textarea, to have a unigue number for the editor
889 * @return string the Javascript code for configuring the RTE
891 public function registerRTEinJS($RTEcounter, $table = '', $uid = '', $field = '', $textAreaId = '') {
892 $configureRTEInJavascriptString = '
893 if (typeof configureEditorInstance === "undefined") {
894 configureEditorInstance = new Object();
896 configureEditorInstance["' . $textAreaId . '"] = function() {
897 if (typeof RTEarea === "undefined" || typeof HTMLArea === "undefined") {
898 window.setTimeout("configureEditorInstance[\'' . $textAreaId . '\']();", 40);
900 editornumber = "' . $textAreaId . '";
901 RTEarea[editornumber] = new Object();
902 RTEarea[editornumber].RTEtsConfigParams = "&RTEtsConfigParams=' . rawurlencode($this->RTEtsConfigParams()) . '";
903 RTEarea[editornumber].number = editornumber;
904 RTEarea[editornumber].deleted = false;
905 RTEarea[editornumber].textAreaId = "' . $textAreaId . '";
906 RTEarea[editornumber].id = "RTEarea" + editornumber;
907 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'])) . '";
908 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']) . '";
909 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')) . ';
910 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')) . '";
911 RTEarea[editornumber].fullScreen = ' . ($this->fullScreen ?
'true' : 'false') . ';
912 RTEarea[editornumber].showStatusBar = ' . (trim($this->thisConfig
['showStatusBar']) ?
'true' : 'false') . ';
913 RTEarea[editornumber].enableWordClean = ' . (trim($this->thisConfig
['enableWordClean']) ?
'true' : 'false') . ';
914 RTEarea[editornumber].htmlRemoveComments = ' . (trim($this->thisConfig
['removeComments']) ?
'true' : 'false') . ';
915 RTEarea[editornumber].disableEnterParagraphs = ' . (trim($this->thisConfig
['disableEnterParagraphs']) ?
'true' : 'false') . ';
916 RTEarea[editornumber].disableObjectResizing = ' . (trim($this->thisConfig
['disableObjectResizing']) ?
'true' : 'false') . ';
917 RTEarea[editornumber].removeTrailingBR = ' . (trim($this->thisConfig
['removeTrailingBR']) ?
'true' : 'false') . ';
918 RTEarea[editornumber].useCSS = ' . (trim($this->thisConfig
['useCSS']) ?
'true' : 'false') . ';
919 RTEarea[editornumber].keepButtonGroupTogether = ' . (trim($this->thisConfig
['keepButtonGroupTogether']) ?
'true' : 'false') . ';
920 RTEarea[editornumber].disablePCexamples = ' . (trim($this->thisConfig
['disablePCexamples']) ?
'true' : 'false') . ';
921 RTEarea[editornumber].showTagFreeClasses = ' . (trim($this->thisConfig
['showTagFreeClasses']) ?
'true' : 'false') . ';
922 RTEarea[editornumber].useHTTPS = ' . (trim(stristr($this->siteURL
, 'https')) ||
$this->thisConfig
['forceHTTPS'] ?
'true' : 'false') . ';
923 RTEarea[editornumber].tceformsNested = ' . (is_object($this->TCEform
) && method_exists($this->TCEform
, 'getDynNestedStack') ?
$this->TCEform
->getDynNestedStack(TRUE) : '[]') . ';
924 RTEarea[editornumber].dialogueWindows = new Object();';
925 if (isset($this->thisConfig
['dialogueWindows.']['defaultPositionFromTop'])) {
926 $configureRTEInJavascriptString .= '
927 RTEarea[editornumber].dialogueWindows.positionFromTop = ' . (int)$this->thisConfig
['dialogueWindows.']['defaultPositionFromTop'] . ';';
929 if (isset($this->thisConfig
['dialogueWindows.']['defaultPositionFromLeft'])) {
930 $configureRTEInJavascriptString .= '
931 RTEarea[editornumber].dialogueWindows.positionFromLeft = ' . (int)$this->thisConfig
['dialogueWindows.']['defaultPositionFromLeft'] . ';';
933 // The following properties apply only to the backend
934 if (!$this->is_FE()) {
935 $configureRTEInJavascriptString .= '
936 RTEarea[editornumber].sys_language_content = "' . $this->contentLanguageUid
. '";
937 RTEarea[editornumber].typo3ContentLanguage = "' . $this->contentTypo3Language
. '";
938 RTEarea[editornumber].typo3ContentCharset = "' . $this->contentCharset
. '";
939 RTEarea[editornumber].userUid = "' . $this->userUid
. '";';
941 // Setting the plugin flags
942 $configureRTEInJavascriptString .= '
943 RTEarea[editornumber].plugin = new Object();
944 RTEarea[editornumber].pathToPluginDirectory = new Object();';
945 foreach ($this->pluginEnabledArray
as $pluginId) {
946 $configureRTEInJavascriptString .= '
947 RTEarea[editornumber].plugin.' . $pluginId . ' = true;';
948 if (is_object($this->registeredPlugins
[$pluginId])) {
949 $pathToPluginDirectory = $this->registeredPlugins
[$pluginId]->getPathToPluginDirectory();
950 if ($pathToPluginDirectory) {
951 $configureRTEInJavascriptString .= '
952 RTEarea[editornumber].pathToPluginDirectory.' . $pluginId . ' = "' . $pathToPluginDirectory . '";';
956 // Setting the buttons configuration
957 $configureRTEInJavascriptString .= '
958 RTEarea[editornumber].buttons = new Object();';
959 if (is_array($this->thisConfig
['buttons.'])) {
960 foreach ($this->thisConfig
['buttons.'] as $buttonIndex => $conf) {
961 $button = substr($buttonIndex, 0, -1);
962 if (is_array($conf)) {
963 $configureRTEInJavascriptString .= '
964 RTEarea[editornumber].buttons.' . $button . ' = ' . $this->buildNestedJSArray($conf) . ';';
968 // Setting the list of tags to be removed if specified in the RTE config
969 if (trim($this->thisConfig
['removeTags'])) {
970 $configureRTEInJavascriptString .= '
971 RTEarea[editornumber].htmlRemoveTags = /^(' . implode('|', GeneralUtility
::trimExplode(',', $this->thisConfig
['removeTags'], TRUE)) . ')$/i;';
973 // Setting the list of tags to be removed with their contents if specified in the RTE config
974 if (trim($this->thisConfig
['removeTagsAndContents'])) {
975 $configureRTEInJavascriptString .= '
976 RTEarea[editornumber].htmlRemoveTagsAndContents = /^(' . implode('|', GeneralUtility
::trimExplode(',', $this->thisConfig
['removeTagsAndContents'], TRUE)) . ')$/i;';
978 // Setting array of custom tags if specified in the RTE config
979 if (!empty($this->thisConfig
['customTags'])) {
980 $customTags = GeneralUtility
::trimExplode(',', $this->thisConfig
['customTags'], TRUE);
981 if (!empty($customTags)) {
982 $configureRTEInJavascriptString .= '
983 RTEarea[editornumber].customTags= ' . json_encode($customTags) . ';';
986 // Setting array of content css files if specified in the RTE config
987 $versionNumberedFileNames = array();
988 $contentCssFileNames = $this->getContentCssFileNames();
989 foreach ($contentCssFileNames as $contentCssFileName) {
990 $versionNumberedFileNames[] = GeneralUtility
::createVersionNumberedFilename($contentCssFileName);
992 $configureRTEInJavascriptString .= '
993 RTEarea[editornumber].pageStyle = ["' . implode('","', $versionNumberedFileNames) . '"];';
994 // Process classes configuration
995 $classesConfigurationRequired = FALSE;
996 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
997 if ($this->isPluginEnabled($pluginId)) {
998 $classesConfigurationRequired = $classesConfigurationRequired ||
$plugin->requiresClassesConfiguration();
1001 if ($classesConfigurationRequired) {
1002 $configureRTEInJavascriptString .= $this->buildJSClassesConfig($RTEcounter);
1004 // Add Javascript configuration for registered plugins
1005 foreach ($this->registeredPlugins
as $pluginId => $plugin) {
1006 if ($this->isPluginEnabled($pluginId)) {
1007 $configureRTEInJavascriptString .= $plugin->buildJavascriptConfiguration('editornumber');
1010 // Avoid premature reference to HTMLArea when being initially loaded by IRRE Ajax call
1011 $configureRTEInJavascriptString .= '
1012 RTEarea[editornumber].toolbar = ' . $this->getJSToolbarArray() . ';
1013 RTEarea[editornumber].convertButtonId = ' . json_encode(array_flip($this->convertToolbarForHtmlAreaArray
)) . ';
1014 RTEarea.initEditor(editornumber);
1017 configureEditorInstance["' . $textAreaId . '"]();';
1018 return $configureRTEInJavascriptString;
1022 * Return TRUE, if the plugin can be loaded
1024 * @param string $pluginId: The identification string of the plugin
1025 * @return bool TRUE if the plugin can be loaded
1027 public function isPluginEnabled($pluginId) {
1028 return in_array($pluginId, $this->pluginEnabledArray
);
1032 * Build the default content style sheet
1034 * @return string Style sheet
1035 * @deprecated since TYPO3 6.0, will be removed in TYPO3 6.2
1037 public function buildStyleSheet() {
1038 $stylesheet = '/* mainStyleOverride and inlineStyle properties ignored. */';
1043 * Return Javascript configuration of classes
1045 * @param int $RTEcounter: The index number of the current RTE editing area within the form.
1046 * @return string Javascript configuration of classes
1048 public function buildJSClassesConfig($RTEcounter) {
1049 // Include JS arrays of configured classes
1050 $configureRTEInJavascriptString .= '
1051 RTEarea[editornumber].classesUrl = "' . ($this->is_FE() && $GLOBALS['TSFE']->absRefPrefix ?
$GLOBALS['TSFE']->absRefPrefix
: '') . $this->writeTemporaryFile('', ('classes_' . $this->language
), 'js', $this->buildJSClassesArray(), TRUE) . '";';
1052 return $configureRTEInJavascriptString;
1056 * Return JS arrays of classes configuration
1058 * @return string JS classes arrays
1060 public function buildJSClassesArray() {
1061 if ($this->is_FE()) {
1062 $RTEProperties = $this->RTEsetup
;
1064 $RTEProperties = $this->RTEsetup
['properties'];
1066 $classesArray = array('labels' => array(), 'values' => array(), 'noShow' => array(), 'alternating' => array(), 'counting' => array(), 'selectable' => array(), 'XOR' => array());
1067 $JSClassesArray = '';
1068 // Scanning the list of classes if specified in the RTE config
1069 if (is_array($RTEProperties['classes.'])) {
1070 foreach ($RTEProperties['classes.'] as $className => $conf) {
1071 $className = rtrim($className, '.');
1072 $classesArray['labels'][$className] = $this->getPageConfigLabel($conf['name'], FALSE);
1073 $classesArray['values'][$className] = str_replace('\\\'', '\'', $conf['value']);
1074 if (isset($conf['noShow'])) {
1075 $classesArray['noShow'][$className] = $conf['noShow'];
1077 if (is_array($conf['alternating.'])) {
1078 $classesArray['alternating'][$className] = $conf['alternating.'];
1080 if (is_array($conf['counting.'])) {
1081 $classesArray['counting'][$className] = $conf['counting.'];
1083 if (isset($conf['selectable'])) {
1084 $classesArray['selectable'][$className] = $conf['selectable'];
1088 // Scanning the list of sets of mutually exclusives classes if specified in the RTE config
1089 if (is_array($RTEProperties['mutuallyExclusiveClasses.'])) {
1090 foreach ($RTEProperties['mutuallyExclusiveClasses.'] as $listName => $conf) {
1091 $classSet = GeneralUtility
::trimExplode(',', $conf, TRUE);
1092 $classList = implode(',', $classSet);
1093 foreach ($classSet as $className) {
1094 $classesArray['XOR'][$className] = '/^(' . implode('|', GeneralUtility
::trimExplode(',', GeneralUtility
::rmFromList($className, $classList), TRUE)) . ')$/';
1098 foreach ($classesArray as $key => $subArray) {
1099 $JSClassesArray .= 'HTMLArea.classes' . ucfirst($key) . ' = ' . $this->buildNestedJSArray($subArray) . ';' . LF
;
1101 return $JSClassesArray;
1105 * Translate Page TS Config array in JS nested array definition
1106 * Replace 0 values with false
1107 * Unquote regular expression values
1108 * Replace empty arrays with empty objects
1110 * @param array $conf: Page TSConfig configuration array
1111 * @return string nested JS array definition
1113 public function buildNestedJSArray($conf) {
1114 $convertedConf = GeneralUtility
::removeDotsFromTS($conf);
1115 return str_replace(array(':"0"', ':"\\/^(', ')$\\/i"', ':"\\/^(', ')$\\/"', '[]'), array(':false', ':/^(', ')$/i', ':/^(', ')$/', '{}'), json_encode($convertedConf));
1119 * Return a Javascript localization array for htmlArea RTE
1121 * @return string Javascript localization array
1123 public function buildJSMainLangArray() {
1124 $JSLanguageArray = 'HTMLArea.I18N = new Object();' . LF
;
1125 $labelsArray = array('tooltips' => array(), 'msg' => array(), 'dialogs' => array());
1126 foreach ($labelsArray as $labels => $subArray) {
1127 $LOCAL_LANG = GeneralUtility
::readLLfile('EXT:' . $this->ID
. '/htmlarea/locallang_' . $labels . '.xlf', $this->language
, 'utf-8');
1128 if (!empty($LOCAL_LANG[$this->language
])) {
1129 $mergedLocalLang = $LOCAL_LANG['default'];
1130 \TYPO3\CMS\Core\Utility\ArrayUtility
::mergeRecursiveWithOverrule($mergedLocalLang, $LOCAL_LANG[$this->language
], TRUE, FALSE);
1131 $LOCAL_LANG[$this->language
] = $mergedLocalLang;
1133 $LOCAL_LANG[$this->language
] = $LOCAL_LANG['default'];
1135 $labelsArray[$labels] = $LOCAL_LANG[$this->language
];
1137 $JSLanguageArray .= 'HTMLArea.I18N = ' . json_encode($labelsArray) . ';' . LF
;
1138 return $JSLanguageArray;
1142 * Writes contents in a file in typo3temp/rtehtmlarea directory and returns the file name
1144 * @param string $sourceFileName: The name of the file from which the contents should be extracted
1145 * @param string $label: A label to insert at the beginning of the name of the file
1146 * @param string $fileExtension: The file extension of the file, defaulting to 'js'
1147 * @param string $contents: The contents to write into the file if no $sourceFileName is provided
1148 * @param bool $concatenate Not used anymore
1149 * @return string The name of the file writtten to typo3temp/rtehtmlarea
1151 public function writeTemporaryFile($sourceFileName = '', $label, $fileExtension = 'js', $contents = '', $concatenate = FALSE) {
1152 if ($sourceFileName) {
1154 $source = GeneralUtility
::getFileAbsFileName($sourceFileName);
1155 $output = file_get_contents($source);
1157 $output = $contents;
1159 $relativeFilename = 'typo3temp/' . $this->ID
. '_' . str_replace('-', '_', $label) . '_' . GeneralUtility
::shortMD5((TYPO3_version
. $TYPO3_CONF_VARS['EXTCONF'][$this->ID
]['version'] . ($sourceFileName ?
$sourceFileName : $output)), 20) . '.' . $fileExtension;
1160 $destination = PATH_site
. $relativeFilename;
1161 if (!file_exists($destination)) {
1162 $minifiedJavaScript = '';
1163 if ($fileExtension == 'js' && $output != '') {
1164 $minifiedJavaScript = GeneralUtility
::minifyJavaScript($output);
1166 $failure = GeneralUtility
::writeFileToTypo3tempDir($destination, $minifiedJavaScript ?
$minifiedJavaScript : $output);
1168 throw new \
RuntimeException($failure, 1294585668);
1171 if ($this->is_FE()) {
1172 $filename = $relativeFilename;
1174 $filename = ($this->isFrontendEditActive() ?
'' : $this->backPath
. '../') . $relativeFilename;
1176 return GeneralUtility
::resolveBackPath($filename);
1180 * Return a file name containing the main JS language array for HTMLArea
1182 * @param int $RTEcounter: The index number of the current RTE editing area within the form.
1183 * @return string filename
1185 public function buildJSMainLangFile($RTEcounter) {
1186 $contents = $this->buildJSMainLangArray() . LF
;
1187 foreach ($this->pluginEnabledCumulativeArray
[$RTEcounter] as $pluginId) {
1188 $contents .= $this->buildJSLangArray($pluginId) . LF
;
1190 return $this->writeTemporaryFile('', $this->language
. '_' . $this->OutputCharset
, 'js', $contents, TRUE);
1194 * Return a Javascript localization array for the plugin
1196 * @param string $plugin: identification string of the plugin
1197 * @return string Javascript localization array
1199 public function buildJSLangArray($plugin) {
1200 $extensionKey = is_object($this->registeredPlugins
[$plugin]) ?
$this->registeredPlugins
[$plugin]->getExtensionKey() : $this->ID
;
1201 $LOCAL_LANG = GeneralUtility
::readLLfile('EXT:' . $extensionKey . '/htmlarea/plugins/' . $plugin . '/locallang.xlf', $this->language
, 'utf-8', 1);
1202 $JSLanguageArray = 'HTMLArea.I18N["' . $plugin . '"] = new Object();' . LF
;
1203 if (is_array($LOCAL_LANG)) {
1204 if (!empty($LOCAL_LANG[$this->language
])) {
1205 $defaultLocalLang = $LOCAL_LANG['default'];
1206 \TYPO3\CMS\Core\Utility\ArrayUtility
::mergeRecursiveWithOverrule($defaultLocalLang, $LOCAL_LANG[$this->language
], TRUE, FALSE);
1207 $LOCAL_LANG[$this->language
] = $defaultLocalLang;
1209 $LOCAL_LANG[$this->language
] = $LOCAL_LANG['default'];
1211 $JSLanguageArray .= 'HTMLArea.I18N["' . $plugin . '"] = ' . json_encode($LOCAL_LANG[$this->language
]) . ';' . LF
;
1213 return $JSLanguageArray;
1217 * Return the JS code of the toolbar configuration for the HTMLArea editor
1219 * @return string the JS code as nested JS arrays
1221 protected function getJSToolbarArray() {
1222 // The toolbar array
1224 // The current row; a "linebreak" ends the current row
1226 // The current group; each group is between "bar"s; a "linebreak" ends the current group
1228 // Process each toolbar item in the toolbar order list
1229 foreach ($this->toolbarOrderArray
as $item) {
1232 // Add row to toolbar if not empty
1233 if (!empty($group)) {
1243 // Add group to row if not empty
1244 if (!empty($group)) {
1250 if (end($group) != $this->convertToolbarForHTMLArea($item)) {
1251 $group[] = $this->convertToolbarForHTMLArea($item);
1255 if (in_array($item, $this->toolbar
)) {
1256 // Add the item to the group
1257 $convertedItem = $this->convertToolbarForHTMLArea($item);
1258 if ($convertedItem) {
1259 $group[] = $convertedItem;
1264 // Add the last group and last line, if not empty
1265 if (!empty($group)) {
1271 return json_encode($toolbar);
1275 * Localize a string using the language of the content element rather than the language of the BE interface
1277 * @param string string: the label to be localized
1278 * @return string Localized string.
1280 public function getLLContent($string) {
1281 return GeneralUtility
::quoteJSvalue($this->contentLanguageService
->sL($string));
1284 public function getPageConfigLabel($string, $JScharCode = 1) {
1285 global $LANG, $TSFE, $TYPO3_CONF_VARS;
1286 if ($this->is_FE()) {
1287 if (substr($string, 0, 4) !== 'LLL:') {
1288 // A pure string coming from Page TSConfig must be in utf-8
1289 $label = $TSFE->csConvObj
->conv($TSFE->sL(trim($string)), 'utf-8', $this->OutputCharset
);
1291 $label = $TSFE->csConvObj
->conv($TSFE->sL(trim($string)), $this->charset
, $this->OutputCharset
);
1293 $label = str_replace('"', '\\"', str_replace('\\\'', '\'', $label));
1294 $label = $JScharCode ?
$this->feJScharCode($label) : $label;
1296 if (substr($string, 0, 4) !== 'LLL:') {
1299 $label = $LANG->sL(trim($string));
1301 $label = str_replace('"', '\\"', str_replace('\\\'', '\'', $label));
1302 $label = $JScharCode ? GeneralUtility
::quoteJSvalue($label) : $label;
1308 * JavaScript char code
1310 * @param string $str
1313 public function feJScharCode($str) {
1314 // Convert string to UTF-8:
1315 if ($this->OutputCharset
!= 'utf-8') {
1316 $str = $GLOBALS['TSFE']->csConvObj
->utf8_encode($str, $this->OutputCharset
);
1318 // Convert the UTF-8 string into a 'JavaScript-safe' encoded string:
1319 return GeneralUtility
::quoteJSvalue($str);
1323 * Make a file name relative to the PATH_site or to the PATH_typo3
1325 * @param string $filename: a file name of the form EXT:.... or relative to the PATH_site
1326 * @return string the file name relative to the PATH_site if in frontend or relative to the PATH_typo3 if in backend
1328 public function getFullFileName($filename) {
1329 if (substr($filename, 0, 4) == 'EXT:') {
1331 list($extKey, $local) = explode('/', substr($filename, 4), 2);
1333 if ((string)$extKey !== '' && \TYPO3\CMS\Core\Utility\ExtensionManagementUtility
::isLoaded($extKey) && (string)$local !== '') {
1334 $newFilename = ($this->is_FE() ||
$this->isFrontendEditActive() ? \TYPO3\CMS\Core\Utility\ExtensionManagementUtility
::siteRelPath($extKey) : $this->backPath
. \TYPO3\CMS\Core\Utility\ExtensionManagementUtility
::extRelPath($extKey)) . $local;
1337 $path = ($this->is_FE() ||
$this->isFrontendEditActive() ?
'' : $this->backPath
. '../');
1338 $newFilename = $path . ($filename[0] === '/' ?
substr($filename, 1) : $filename);
1340 return GeneralUtility
::resolveBackPath($newFilename);
1344 * Return the Javascript code for copying the HTML code from the editor into the hidden input field.
1345 * This is for submit function of the form.
1347 * @param int $RTEcounter: The index number of the current RTE editing area within the form.
1348 * @param string $formName: the name of the form
1349 * @param string $textareaId: the id of the textarea
1350 * @param string $textareaName: the name of the textarea
1351 * @return string Javascript code
1353 public function setSaveRTE($RTEcounter, $formName, $textareaId, $textareaName) {
1354 return 'if (RTEarea["' . $textareaId . '"]) { document.' . $formName . '["' . $textareaName . '"].value = RTEarea["' . $textareaId . '"].editor.getHTML(); } else { OK = 0; };';
1358 * Return the Javascript code for copying the HTML code from the editor into the hidden input field.
1359 * This is for submit function of the form.
1361 * @param int $RTEcounter: The index number of the current RTE editing area within the form.
1362 * @param string $formName: the name of the form
1363 * @param string $textareaId: the id of the textarea
1364 * @return string Javascript code
1366 public function setDeleteRTE($RTEcounter, $formName, $textareaId) {
1367 return 'if (RTEarea["' . $textareaId . '"]) { RTEarea["' . $textareaId . '"].deleted = true;}';
1371 * Return TRUE if we are in the FE, but not in the FE editing feature of BE.
1375 public function is_FE() {
1376 return is_object($GLOBALS['TSFE']) && !$this->isFrontendEditActive() && TYPO3_MODE
== 'FE';
1380 * Checks whether frontend editing is active.
1384 public function isFrontendEditActive() {
1385 return is_object($GLOBALS['TSFE']) && $GLOBALS['TSFE']->beUserLogin
&& $GLOBALS['BE_USER']->frontendEdit
instanceof \TYPO3\CMS\Core\FrontendEditing\FrontendEditingController
;
1389 * Client Browser Information
1391 * @param string $userAgent: The useragent string, \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('HTTP_USER_AGENT')
1392 * @return array Contains keys "useragent", "browser", "version", "system
1394 public function clientInfo($userAgent = '') {
1396 $userAgent = GeneralUtility
::getIndpEnv('HTTP_USER_AGENT');
1398 $browserInfo = \TYPO3\CMS\Core\Utility\ClientUtility
::getBrowserInfo($userAgent);
1399 // Known engines: order is not irrelevant!
1400 $knownEngines = array('opera', 'msie', 'gecko', 'webkit');
1401 if (is_array($browserInfo['all'])) {
1402 foreach ($knownEngines as $engine) {
1403 if ($browserInfo['all'][$engine]) {
1404 $browserInfo['browser'] = $engine;
1405 $browserInfo['version'] = \TYPO3\CMS\Core\Utility\ClientUtility
::getVersion($browserInfo['all'][$engine]);
1410 return $browserInfo;
1414 * Log usage of deprecated Page TS Config Property
1416 * @param string $deprecatedProperty: Name of deprecated property
1417 * @param string $useProperty: Name of property to use instead
1418 * @param string $version: Version of TYPO3 in which the property will be removed
1421 public function logDeprecatedProperty($deprecatedProperty, $useProperty, $version) {
1422 if (!$this->thisConfig
['logDeprecatedProperties.']['disabled']) {
1423 $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
);
1424 GeneralUtility
::deprecationLog($message);
1425 if (is_object($GLOBALS['BE_USER']) && $this->thisConfig
['logDeprecatedProperties.']['logAlsoToBELog']) {
1426 $message = sprintf($GLOBALS['LANG']->getLL('deprecatedPropertyMessage'), $deprecatedProperty, $useProperty, $version, $this->thePid
);
1427 $GLOBALS['BE_USER']->simplelog($message, $this->ID
);
1432 /***************************
1434 * OTHER FUNCTIONS: (from Classic RTE)
1436 ***************************/
1438 * @return [type] ...
1441 public function RTEtsConfigParams() {
1442 if ($this->is_FE()) {
1445 $p = BackendUtility
::getSpecConfParametersFromArray($this->specConf
['rte_transform']['parameters']);
1446 return $this->elementParts
[0] . ':' . $this->elementParts
[1] . ':' . $this->elementParts
[2] . ':' . $this->thePid
. ':' . $this->typeVal
. ':' . $this->tscPID
. ':' . $p['imgpath'];
1450 public function cleanList($str) {
1451 if (strstr($str, '*')) {
1454 $str = implode(',', array_unique(GeneralUtility
::trimExplode(',', $str, TRUE)));
1460 * Filter style element
1462 * @param string $elValue
1463 * @param string $matchList
1466 public function filterStyleEl($elValue, $matchList) {
1467 $matchParts = GeneralUtility
::trimExplode(',', $matchList, TRUE);
1468 $styleParts = explode(';', $elValue);
1470 foreach ($styleParts as $k => $p) {
1471 $pp = GeneralUtility
::trimExplode(':', $p);
1472 if ($pp[0] && $pp[1]) {
1473 foreach ($matchParts as $el) {
1474 $star = substr($el, -1) == '*';
1475 if ($pp[0] === (string)$el ||
$star && GeneralUtility
::isFirstPartOfStr($pp[0], substr($el, 0, -1))) {
1476 $nStyle[] = $pp[0] . ':' . $pp[1];
1478 unset($styleParts[$k]);
1482 unset($styleParts[$k]);
1485 return implode('; ', $nStyle);
1488 // Hook on lorem_ipsum extension to insert text into the RTE in wysiwyg mode
1490 * @deprecated since 6.2 - will be removed two versions later without replacement
1492 public function loremIpsumInsert($params) {
1493 GeneralUtility
::logDeprecatedFunction();
1495 if (typeof(lorem_ipsum) == \'function\' && ' . $params['element'] . '.tagName.toLowerCase() == \'textarea\' ) lorem_ipsum(' . $params['element'] . ', lipsum_temp_strings[lipsum_temp_pointer]);