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