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