[BUGFIX] Remove php warning for RTE classes without name
[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 $absolutePath = GeneralUtility::getFileAbsFileName($fileName);
530 // Fallback to default content css file if configured file does not exists or is of zero size
531 if (!$fileName || !file_exists($absolutePath) || !filesize($absolutePath)) {
532 $fileName = 'EXT:' . $this->ID . '/res/contentcss/default.css';
533 }
534 $fileName = $this->getFullFileName($fileName);
535 return $fileName;
536 }
537
538 /**
539 * Add links to skin style sheet(s) to document header
540 *
541 * @return void
542 */
543 protected function addSkin() {
544 // Get skin file name from Page TSConfig if any
545 $skinFilename = trim($this->thisConfig['skin']) ?: 'EXT:' . $this->ID . '/htmlarea/skins/default/htmlarea.css';
546 $this->editorCSS = $this->getFullFileName($skinFilename);
547 $skinDir = dirname($this->editorCSS);
548 // Editing area style sheet
549 $this->editedContentCSS = $skinDir . '/htmlarea-edited-content.css';
550 $this->addStyleSheet('rtehtmlarea-editing-area-skin', $this->editedContentCSS);
551 // Main skin
552 $this->addStyleSheet('rtehtmlarea-skin', $this->editorCSS);
553 // Additional icons from registered plugins
554 foreach ($this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter] as $pluginId) {
555 if (is_object($this->registeredPlugins[$pluginId])) {
556 $pathToSkin = $this->registeredPlugins[$pluginId]->getPathToSkin();
557 if ($pathToSkin) {
558 $key = $this->registeredPlugins[$pluginId]->getExtensionKey();
559 $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);
560 }
561 }
562 }
563 }
564
565 /**
566 * Add style sheet file to document header
567 *
568 * @param string $key: some key identifying the style sheet
569 * @param string $href: uri to the style sheet file
570 * @param string $title: value for the title attribute of the link element
571 * @param string $relation: value for the rel attribute of the link element
572 * @return void
573 */
574 protected function addStyleSheet($key, $href, $title = '', $relation = 'stylesheet') {
575 // 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
576 if (is_object($this->TCEform->inline) && $this->TCEform->inline->isAjaxCall) {
577 $this->TCEform->additionalCode_pre[$key] = '<link rel="' . $relation . '" type="text/css" href="' . $href . '" title="' . $title . '" />';
578 } else {
579 $this->pageRenderer->addCssFile($href, $relation, 'screen', $title);
580 }
581 }
582
583 /**
584 * Initialize toolbar configuration and enable registered plugins
585 *
586 * @return void
587 */
588 protected function initializeToolbarConfiguration() {
589 // Enable registred plugins
590 $this->enableRegisteredPlugins();
591 // Configure toolbar
592 $this->setToolbar();
593 // Check if some plugins need to be disabled
594 $this->setPlugins();
595 // Merge the list of enabled plugins with the lists from the previous RTE editing areas on the same form
596 $this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter] = $this->pluginEnabledArray;
597 if ($this->TCEform->RTEcounter > 1 && isset($this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter - 1]) && is_array($this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter - 1])) {
598 $this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter] = array_unique(array_values(array_merge($this->pluginEnabledArray, $this->pluginEnabledCumulativeArray[$this->TCEform->RTEcounter - 1])));
599 }
600 }
601
602 /**
603 * Add registered plugins to the array of enabled plugins
604 *
605 * @todo Define visibility
606 */
607 public function enableRegisteredPlugins() {
608 global $TYPO3_CONF_VARS;
609 // Traverse registered plugins
610 if (is_array($TYPO3_CONF_VARS['EXTCONF'][$this->ID]['plugins'])) {
611 foreach ($TYPO3_CONF_VARS['EXTCONF'][$this->ID]['plugins'] as $pluginId => $pluginObjectConfiguration) {
612 $plugin = FALSE;
613 if (is_array($pluginObjectConfiguration) && count($pluginObjectConfiguration)) {
614 $plugin = GeneralUtility::getUserObj($pluginObjectConfiguration['objectReference']);
615 }
616 if (is_object($plugin)) {
617 if ($plugin->main($this)) {
618 $this->registeredPlugins[$pluginId] = $plugin;
619 // Override buttons from previously registered plugins
620 $pluginButtons = GeneralUtility::trimExplode(',', $plugin->getPluginButtons(), TRUE);
621 foreach ($this->pluginButton as $previousPluginId => $buttonList) {
622 $this->pluginButton[$previousPluginId] = implode(',', array_diff(GeneralUtility::trimExplode(',', $this->pluginButton[$previousPluginId], TRUE), $pluginButtons));
623 }
624 $this->pluginButton[$pluginId] = $plugin->getPluginButtons();
625 $pluginLabels = GeneralUtility::trimExplode(',', $plugin->getPluginLabels(), TRUE);
626 foreach ($this->pluginLabel as $previousPluginId => $labelList) {
627 $this->pluginLabel[$previousPluginId] = implode(',', array_diff(GeneralUtility::trimExplode(',', $this->pluginLabel[$previousPluginId], TRUE), $pluginLabels));
628 }
629 $this->pluginLabel[$pluginId] = $plugin->getPluginLabels();
630 $this->pluginEnabledArray[] = $pluginId;
631 }
632 }
633 }
634 }
635 // Process overrides
636 $hidePlugins = array();
637 foreach ($this->registeredPlugins as $pluginId => $plugin) {
638 if ($plugin->addsButtons() && !$this->pluginButton[$pluginId]) {
639 $hidePlugins[] = $pluginId;
640 }
641 }
642 $this->pluginEnabledArray = array_unique(array_diff($this->pluginEnabledArray, $hidePlugins));
643 }
644
645 /**
646 * Set the toolbar config (only in this PHP-Object, not in JS):
647 *
648 * @todo Define visibility
649 */
650 public function setToolbar() {
651 if ($this->client['browser'] == 'msie' || $this->client['browser'] == 'opera') {
652 $this->thisConfig['keepButtonGroupTogether'] = 0;
653 }
654 $this->defaultToolbarOrder = 'bar, blockstylelabel, blockstyle, space, textstylelabel, textstyle, linebreak,
655 bar, formattext, bold, strong, italic, emphasis, big, small, insertedtext, deletedtext, citation, code, definition, keyboard, monospaced, quotation, sample, variable, bidioverride, strikethrough, subscript, superscript, underline, span,
656 bar, fontstyle, space, fontsize, bar, formatblock, insertparagraphbefore, insertparagraphafter, blockquote, line,
657 bar, left, center, right, justifyfull,
658 bar, orderedlist, unorderedlist, definitionlist, definitionitem, outdent, indent, bar, lefttoright, righttoleft, language, showlanguagemarks,
659 bar, textcolor, bgcolor, textindicator,
660 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,
661 bar, chMode, inserttag, removeformat, bar, copy, cut, paste, pastetoggle, pastebehaviour, bar, undo, redo, bar, showhelp, about, linebreak,
662 ' . ($this->thisConfig['hideTableOperationsInToolbar'] ? '' : 'bar, toggleborders,') . ' bar, tableproperties, tablerestyle, bar, rowproperties, rowinsertabove, rowinsertunder, rowdelete, rowsplit, bar,
663 columnproperties, columninsertbefore, columninsertafter, columndelete, columnsplit, bar,
664 cellproperties, cellinsertbefore, cellinsertafter, celldelete, cellsplit, cellmerge';
665 // Additional buttons from registered plugins
666 foreach ($this->registeredPlugins as $pluginId => $plugin) {
667 if ($this->isPluginEnabled($pluginId)) {
668 $this->defaultToolbarOrder = $plugin->addButtonsToToolbar();
669 }
670 }
671 $toolbarOrder = $this->thisConfig['toolbarOrder'] ?: $this->defaultToolbarOrder;
672 // Getting rid of undefined buttons
673 $this->toolbarOrderArray = array_intersect(GeneralUtility::trimExplode(',', $toolbarOrder, TRUE), GeneralUtility::trimExplode(',', $this->defaultToolbarOrder, TRUE));
674 $toolbarOrder = array_unique(array_values($this->toolbarOrderArray));
675 // Fetching specConf for field from backend
676 $pList = is_array($this->specConf['richtext']['parameters']) ? implode(',', $this->specConf['richtext']['parameters']) : '';
677 if ($pList != '*') {
678 // If not all
679 $show = is_array($this->specConf['richtext']['parameters']) ? $this->specConf['richtext']['parameters'] : array();
680 if ($this->thisConfig['showButtons']) {
681 if (!GeneralUtility::inList($this->thisConfig['showButtons'], '*')) {
682 $show = array_unique(array_merge($show, GeneralUtility::trimExplode(',', $this->thisConfig['showButtons'], TRUE)));
683 } else {
684 $show = array_unique(array_merge($show, $toolbarOrder));
685 }
686 }
687 if (is_array($this->thisConfig['showButtons.'])) {
688 foreach ($this->thisConfig['showButtons.'] as $buttonId => $value) {
689 if ($value) {
690 $show[] = $buttonId;
691 }
692 }
693 $show = array_unique($show);
694 }
695 } else {
696 $show = $toolbarOrder;
697 }
698 // Resticting to RTEkeyList for backend user
699 if (is_object($GLOBALS['BE_USER'])) {
700 $RTEkeyList = isset($GLOBALS['BE_USER']->userTS['options.']['RTEkeyList']) ? $GLOBALS['BE_USER']->userTS['options.']['RTEkeyList'] : '*';
701 if ($RTEkeyList != '*') {
702 // If not all
703 $show = array_intersect($show, GeneralUtility::trimExplode(',', $RTEkeyList, TRUE));
704 }
705 }
706 // Hiding buttons of disabled plugins
707 $hideButtons = array('space', 'bar', 'linebreak');
708 foreach ($this->pluginButton as $pluginId => $buttonList) {
709 if (!$this->isPluginEnabled($pluginId)) {
710 $buttonArray = GeneralUtility::trimExplode(',', $buttonList, TRUE);
711 foreach ($buttonArray as $button) {
712 $hideButtons[] = $button;
713 }
714 }
715 }
716 // Hiding labels of disabled plugins
717 foreach ($this->pluginLabel as $pluginId => $label) {
718 if (!$this->isPluginEnabled($pluginId)) {
719 $hideButtons[] = $label;
720 }
721 }
722 // Hiding buttons
723 $show = array_diff($show, $this->conf_toolbar_hide, GeneralUtility::trimExplode(',', $this->thisConfig['hideButtons'], TRUE));
724 // Apply toolbar constraints from registered plugins
725 foreach ($this->registeredPlugins as $pluginId => $plugin) {
726 if ($this->isPluginEnabled($pluginId) && method_exists($plugin, 'applyToolbarConstraints')) {
727 $show = $plugin->applyToolbarConstraints($show);
728 }
729 }
730 // Getting rid of the buttons for which we have no position
731 $show = array_intersect($show, $toolbarOrder);
732 $this->toolbar = $show;
733 }
734
735 /**
736 * Disable some plugins
737 *
738 * @todo Define visibility
739 */
740 public function setPlugins() {
741 // Disabling a plugin that adds buttons if none of its buttons is in the toolbar
742 $hidePlugins = array();
743 foreach ($this->pluginButton as $pluginId => $buttonList) {
744 if ($this->registeredPlugins[$pluginId]->addsButtons()) {
745 $showPlugin = FALSE;
746 $buttonArray = GeneralUtility::trimExplode(',', $buttonList, TRUE);
747 foreach ($buttonArray as $button) {
748 if (in_array($button, $this->toolbar)) {
749 $showPlugin = TRUE;
750 }
751 }
752 if (!$showPlugin) {
753 $hidePlugins[] = $pluginId;
754 }
755 }
756 }
757 $this->pluginEnabledArray = array_diff($this->pluginEnabledArray, $hidePlugins);
758 // Hiding labels of disabled plugins
759 $hideLabels = array();
760 foreach ($this->pluginLabel as $pluginId => $label) {
761 if (!$this->isPluginEnabled($pluginId)) {
762 $hideLabels[] = $label;
763 }
764 }
765 $this->toolbar = array_diff($this->toolbar, $hideLabels);
766 // Adding plugins declared as prerequisites by enabled plugins
767 $requiredPlugins = array();
768 foreach ($this->registeredPlugins as $pluginId => $plugin) {
769 if ($this->isPluginEnabled($pluginId)) {
770 $requiredPlugins = array_merge($requiredPlugins, GeneralUtility::trimExplode(',', $plugin->getRequiredPlugins(), TRUE));
771 }
772 }
773 $requiredPlugins = array_unique($requiredPlugins);
774 foreach ($requiredPlugins as $pluginId) {
775 if (is_object($this->registeredPlugins[$pluginId]) && !$this->isPluginEnabled($pluginId)) {
776 $this->pluginEnabledArray[] = $pluginId;
777 }
778 }
779 $this->pluginEnabledArray = array_unique($this->pluginEnabledArray);
780 // Completing the toolbar conversion array for htmlArea
781 foreach ($this->registeredPlugins as $pluginId => $plugin) {
782 if ($this->isPluginEnabled($pluginId)) {
783 $this->convertToolbarForHtmlAreaArray = array_unique(array_merge($this->convertToolbarForHtmlAreaArray, $plugin->getConvertToolbarForHtmlAreaArray()));
784 }
785 }
786 }
787
788 /**
789 * Convert the TYPO3 names of buttons into the names for htmlArea RTE
790 *
791 * @param string buttonname (typo3-name)
792 * @return string buttonname (htmlarea-name)
793 * @todo Define visibility
794 */
795 public function convertToolbarForHTMLArea($button) {
796 return $this->convertToolbarForHtmlAreaArray[$button];
797 }
798
799 /**
800 * Add RTE main scripts and plugin scripts
801 *
802 * @param string $RTEcounter: The index number of the current RTE editing area within the form.
803 * @return void
804 */
805 protected function addRteJsFiles($RTEcounter) {
806 $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $this->ID . '/htmlarea/htmlarea.js'));
807 foreach ($this->pluginEnabledCumulativeArray[$RTEcounter] as $pluginId) {
808 $extensionKey = is_object($this->registeredPlugins[$pluginId]) ? $this->registeredPlugins[$pluginId]->getExtensionKey() : $this->ID;
809 $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $extensionKey . '/htmlarea/plugins/' . $pluginId . '/' . strtolower(preg_replace('/([a-z])([A-Z])([a-z])/', '$1-$2$3', $pluginId)) . '.js'));
810 }
811 }
812
813 /**
814 * Return RTE initialization inline JavaScript code
815 *
816 * @return string RTE initialization inline JavaScript code
817 */
818 protected function getRteInitJsCode() {
819 return '
820 if (typeof(RTEarea) == "undefined") {
821 RTEarea = new Object();
822 RTEarea[0] = new Object();
823 RTEarea[0].version = "' . $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->ID]['version'] . '";
824 RTEarea[0].editorUrl = "' . $this->extHttpPath . 'htmlarea/";
825 RTEarea[0].editorCSS = "' . GeneralUtility::createVersionNumberedFilename($this->editorCSS) . '";
826 RTEarea[0].editorSkin = "' . dirname($this->editorCSS) . '/";
827 RTEarea[0].editedContentCSS = "' . GeneralUtility::createVersionNumberedFilename($this->editedContentCSS) . '";
828 RTEarea[0].hostUrl = "' . $this->hostURL . '";
829 RTEarea.init = function() {
830 if (typeof(HTMLArea) == "undefined" || !Ext.isReady) {
831 window.setTimeout("RTEarea.init();", 10);
832 } else {
833 Ext.QuickTips.init();
834 HTMLArea.init();
835 }
836 };
837 RTEarea.initEditor = function(editorNumber) {
838 if (typeof(HTMLArea) == "undefined" || !HTMLArea.isReady) {
839 RTEarea.initEditor.defer(40, null, [editorNumber]);
840 } else {
841 HTMLArea.initEditor(editorNumber);
842 }
843 };
844 }
845 RTEarea.init();';
846 }
847
848 /**
849 * Return the Javascript code for configuring the RTE
850 *
851 * @param integer $RTEcounter: The index number of the current RTE editing area within the form.
852 * @param string $table: The table that includes this RTE (optional, necessary for IRRE).
853 * @param string $uid: The uid of that table that includes this RTE (optional, necessary for IRRE).
854 * @param string $field: The field of that record that includes this RTE (optional).
855 * @param string $textAreaId ID of the textarea, to have a unigue number for the editor
856 * @return string the Javascript code for configuring the RTE
857 * @todo Define visibility
858 */
859 public function registerRTEinJS($RTEcounter, $table = '', $uid = '', $field = '', $textAreaId = '') {
860 $configureRTEInJavascriptString = '
861 if (typeof(configureEditorInstance) == "undefined") {
862 configureEditorInstance = new Object();
863 }
864 configureEditorInstance["' . $textAreaId . '"] = function() {
865 if (typeof(RTEarea) == "undefined" || typeof(HTMLArea) == "undefined") {
866 window.setTimeout("configureEditorInstance[\'' . $textAreaId . '\']();", 40);
867 } else {
868 editornumber = "' . $textAreaId . '";
869 RTEarea[editornumber] = new Object();
870 RTEarea[editornumber].RTEtsConfigParams = "&RTEtsConfigParams=' . rawurlencode($this->RTEtsConfigParams()) . '";
871 RTEarea[editornumber].number = editornumber;
872 RTEarea[editornumber].deleted = false;
873 RTEarea[editornumber].textAreaId = "' . $textAreaId . '";
874 RTEarea[editornumber].id = "RTEarea" + editornumber;
875 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'])) . '";
876 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']) . '";
877 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')) . ';
878 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')) . '";
879 RTEarea[editornumber].fullScreen = ' . ($this->fullScreen ? 'true' : 'false') . ';
880 RTEarea[editornumber].showStatusBar = ' . (trim($this->thisConfig['showStatusBar']) ? 'true' : 'false') . ';
881 RTEarea[editornumber].enableWordClean = ' . (trim($this->thisConfig['enableWordClean']) ? 'true' : 'false') . ';
882 RTEarea[editornumber].htmlRemoveComments = ' . (trim($this->thisConfig['removeComments']) ? 'true' : 'false') . ';
883 RTEarea[editornumber].disableEnterParagraphs = ' . (trim($this->thisConfig['disableEnterParagraphs']) ? 'true' : 'false') . ';
884 RTEarea[editornumber].disableObjectResizing = ' . (trim($this->thisConfig['disableObjectResizing']) ? 'true' : 'false') . ';
885 RTEarea[editornumber].removeTrailingBR = ' . (trim($this->thisConfig['removeTrailingBR']) ? 'true' : 'false') . ';
886 RTEarea[editornumber].useCSS = ' . (trim($this->thisConfig['useCSS']) ? 'true' : 'false') . ';
887 RTEarea[editornumber].keepButtonGroupTogether = ' . (trim($this->thisConfig['keepButtonGroupTogether']) ? 'true' : 'false') . ';
888 RTEarea[editornumber].disablePCexamples = ' . (trim($this->thisConfig['disablePCexamples']) ? 'true' : 'false') . ';
889 RTEarea[editornumber].showTagFreeClasses = ' . (trim($this->thisConfig['showTagFreeClasses']) ? 'true' : 'false') . ';
890 RTEarea[editornumber].useHTTPS = ' . (trim(stristr($this->siteURL, 'https')) || $this->thisConfig['forceHTTPS'] ? 'true' : 'false') . ';
891 RTEarea[editornumber].tceformsNested = ' . (is_object($this->TCEform) && method_exists($this->TCEform, 'getDynNestedStack') ? $this->TCEform->getDynNestedStack(TRUE) : '[]') . ';
892 RTEarea[editornumber].dialogueWindows = new Object();';
893 if (isset($this->thisConfig['dialogueWindows.']['defaultPositionFromTop'])) {
894 $configureRTEInJavascriptString .= '
895 RTEarea[editornumber].dialogueWindows.positionFromTop = ' . (int)$this->thisConfig['dialogueWindows.']['defaultPositionFromTop'] . ';';
896 }
897 if (isset($this->thisConfig['dialogueWindows.']['defaultPositionFromLeft'])) {
898 $configureRTEInJavascriptString .= '
899 RTEarea[editornumber].dialogueWindows.positionFromLeft = ' . (int)$this->thisConfig['dialogueWindows.']['defaultPositionFromLeft'] . ';';
900 }
901 // The following properties apply only to the backend
902 if (!$this->is_FE()) {
903 $configureRTEInJavascriptString .= '
904 RTEarea[editornumber].sys_language_content = "' . $this->contentLanguageUid . '";
905 RTEarea[editornumber].typo3ContentLanguage = "' . $this->contentTypo3Language . '";
906 RTEarea[editornumber].typo3ContentCharset = "' . $this->contentCharset . '";
907 RTEarea[editornumber].userUid = "' . $this->userUid . '";';
908 }
909 // Setting the plugin flags
910 $configureRTEInJavascriptString .= '
911 RTEarea[editornumber].plugin = new Object();
912 RTEarea[editornumber].pathToPluginDirectory = new Object();';
913 foreach ($this->pluginEnabledArray as $pluginId) {
914 $configureRTEInJavascriptString .= '
915 RTEarea[editornumber].plugin.' . $pluginId . ' = true;';
916 if (is_object($this->registeredPlugins[$pluginId])) {
917 $pathToPluginDirectory = $this->registeredPlugins[$pluginId]->getPathToPluginDirectory();
918 if ($pathToPluginDirectory) {
919 $configureRTEInJavascriptString .= '
920 RTEarea[editornumber].pathToPluginDirectory.' . $pluginId . ' = "' . $pathToPluginDirectory . '";';
921 }
922 }
923 }
924 // Setting the buttons configuration
925 $configureRTEInJavascriptString .= '
926 RTEarea[editornumber].buttons = new Object();';
927 if (is_array($this->thisConfig['buttons.'])) {
928 foreach ($this->thisConfig['buttons.'] as $buttonIndex => $conf) {
929 $button = substr($buttonIndex, 0, -1);
930 if (is_array($conf)) {
931 $configureRTEInJavascriptString .= '
932 RTEarea[editornumber].buttons.' . $button . ' = ' . $this->buildNestedJSArray($conf) . ';';
933 }
934 }
935 }
936 // Setting the list of tags to be removed if specified in the RTE config
937 if (trim($this->thisConfig['removeTags'])) {
938 $configureRTEInJavascriptString .= '
939 RTEarea[editornumber].htmlRemoveTags = /^(' . implode('|', GeneralUtility::trimExplode(',', $this->thisConfig['removeTags'], TRUE)) . ')$/i;';
940 }
941 // Setting the list of tags to be removed with their contents if specified in the RTE config
942 if (trim($this->thisConfig['removeTagsAndContents'])) {
943 $configureRTEInJavascriptString .= '
944 RTEarea[editornumber].htmlRemoveTagsAndContents = /^(' . implode('|', GeneralUtility::trimExplode(',', $this->thisConfig['removeTagsAndContents'], TRUE)) . ')$/i;';
945 }
946 // Setting array of custom tags if specified in the RTE config
947 if (!empty($this->thisConfig['customTags'])) {
948 $customTags = GeneralUtility::trimExplode(',', $this->thisConfig['customTags'], TRUE);
949 if (!empty($customTags)) {
950 $configureRTEInJavascriptString .= '
951 RTEarea[editornumber].customTags= ' . json_encode($customTags) . ';';
952 }
953 }
954 // Setting the pageStyle
955 $configureRTEInJavascriptString .= '
956 RTEarea[editornumber].pageStyle = "' . GeneralUtility::createVersionNumberedFilename($this->getContentCssFileName()) . '";';
957 // Process classes configuration
958 $classesConfigurationRequired = FALSE;
959 foreach ($this->registeredPlugins as $pluginId => $plugin) {
960 if ($this->isPluginEnabled($pluginId)) {
961 $classesConfigurationRequired = $classesConfigurationRequired || $plugin->requiresClassesConfiguration();
962 }
963 }
964 if ($classesConfigurationRequired) {
965 $configureRTEInJavascriptString .= $this->buildJSClassesConfig($RTEcounter);
966 }
967 // Add Javascript configuration for registered plugins
968 foreach ($this->registeredPlugins as $pluginId => $plugin) {
969 if ($this->isPluginEnabled($pluginId)) {
970 $configureRTEInJavascriptString .= $plugin->buildJavascriptConfiguration('editornumber');
971 }
972 }
973 // Avoid premature reference to HTMLArea when being initially loaded by IRRE Ajax call
974 $configureRTEInJavascriptString .= '
975 RTEarea[editornumber].toolbar = ' . $this->getJSToolbarArray() . ';
976 RTEarea[editornumber].convertButtonId = ' . json_encode(array_flip($this->convertToolbarForHtmlAreaArray)) . ';
977 RTEarea.initEditor(editornumber);
978 }
979 };
980 configureEditorInstance["' . $textAreaId . '"]();';
981 return $configureRTEInJavascriptString;
982 }
983
984 /**
985 * Return TRUE, if the plugin can be loaded
986 *
987 * @param string $pluginId: The identification string of the plugin
988 * @return boolean TRUE if the plugin can be loaded
989 * @todo Define visibility
990 */
991 public function isPluginEnabled($pluginId) {
992 return in_array($pluginId, $this->pluginEnabledArray);
993 }
994
995 /**
996 * Build the default content style sheet
997 *
998 * @return string Style sheet
999 * @deprecated since TYPO3 6.0, will be removed in TYPO3 6.2
1000 * @todo Define visibility
1001 */
1002 public function buildStyleSheet() {
1003 $stylesheet = '/* mainStyleOverride and inlineStyle properties ignored. */';
1004 return $stylesheet;
1005 }
1006
1007 /**
1008 * Return Javascript configuration of classes
1009 *
1010 * @param integer $RTEcounter: The index number of the current RTE editing area within the form.
1011 * @return string Javascript configuration of classes
1012 * @todo Define visibility
1013 */
1014 public function buildJSClassesConfig($RTEcounter) {
1015 // Include JS arrays of configured classes
1016 $configureRTEInJavascriptString .= '
1017 RTEarea[editornumber].classesUrl = "' . ($this->is_FE() && $GLOBALS['TSFE']->absRefPrefix ? $GLOBALS['TSFE']->absRefPrefix : '') . $this->writeTemporaryFile('', ('classes_' . $this->language), 'js', $this->buildJSClassesArray(), TRUE) . '";';
1018 return $configureRTEInJavascriptString;
1019 }
1020
1021 /**
1022 * Return JS arrays of classes configuration
1023 *
1024 * @return string JS classes arrays
1025 * @todo Define visibility
1026 */
1027 public function buildJSClassesArray() {
1028 if ($this->is_FE()) {
1029 $RTEProperties = $this->RTEsetup;
1030 } else {
1031 $RTEProperties = $this->RTEsetup['properties'];
1032 }
1033 $classesArray = array('labels' => array(), 'values' => array(), 'noShow' => array(), 'alternating' => array(), 'counting' => array(), 'XOR' => array());
1034 $JSClassesArray = '';
1035 // Scanning the list of classes if specified in the RTE config
1036 if (is_array($RTEProperties['classes.'])) {
1037 foreach ($RTEProperties['classes.'] as $className => $conf) {
1038 $className = rtrim($className, '.');
1039 $label = '';
1040 if (!empty($conf['name'])) {
1041 $label = $this->getPageConfigLabel($conf['name'], FALSE);
1042 }
1043 $classesArray['labels'][$className] = $label;
1044 $classesArray['values'][$className] = str_replace('\\\'', '\'', $conf['value']);
1045 if (isset($conf['noShow'])) {
1046 $classesArray['noShow'][$className] = $conf['noShow'];
1047 }
1048 if (is_array($conf['alternating.'])) {
1049 $classesArray['alternating'][$className] = $conf['alternating.'];
1050 }
1051 if (is_array($conf['counting.'])) {
1052 $classesArray['counting'][$className] = $conf['counting.'];
1053 }
1054 }
1055 }
1056 // Scanning the list of sets of mutually exclusives classes if specified in the RTE config
1057 if (is_array($RTEProperties['mutuallyExclusiveClasses.'])) {
1058 foreach ($RTEProperties['mutuallyExclusiveClasses.'] as $listName => $conf) {
1059 $classSet = GeneralUtility::trimExplode(',', $conf, TRUE);
1060 $classList = implode(',', $classSet);
1061 foreach ($classSet as $className) {
1062 $classesArray['XOR'][$className] = '/^(' . implode('|', GeneralUtility::trimExplode(',', GeneralUtility::rmFromList($className, $classList), TRUE)) . ')$/';
1063 }
1064 }
1065 }
1066 foreach ($classesArray as $key => $subArray) {
1067 $JSClassesArray .= 'HTMLArea.classes' . ucfirst($key) . ' = ' . $this->buildNestedJSArray($subArray) . ';' . LF;
1068 }
1069 return $JSClassesArray;
1070 }
1071
1072 /**
1073 * Translate Page TS Config array in JS nested array definition
1074 * Replace 0 values with false
1075 * Unquote regular expression values
1076 * Replace empty arrays with empty objects
1077 *
1078 * @param array $conf: Page TSConfig configuration array
1079 * @return string nested JS array definition
1080 * @todo Define visibility
1081 */
1082 public function buildNestedJSArray($conf) {
1083 $convertedConf = GeneralUtility::removeDotsFromTS($conf);
1084 return str_replace(array(':"0"', ':"\\/^(', ')$\\/i"', ':"\\/^(', ')$\\/"', '[]'), array(':false', ':/^(', ')$/i', ':/^(', ')$/', '{}'), json_encode($convertedConf));
1085 }
1086
1087 /**
1088 * Return a Javascript localization array for htmlArea RTE
1089 *
1090 * @return string Javascript localization array
1091 * @todo Define visibility
1092 */
1093 public function buildJSMainLangArray() {
1094 $JSLanguageArray = 'HTMLArea.I18N = new Object();' . LF;
1095 $labelsArray = array('tooltips' => array(), 'msg' => array(), 'dialogs' => array());
1096 foreach ($labelsArray as $labels => $subArray) {
1097 $LOCAL_LANG = GeneralUtility::readLLfile('EXT:' . $this->ID . '/htmlarea/locallang_' . $labels . '.xlf', $this->language, 'utf-8');
1098 if (!empty($LOCAL_LANG[$this->language])) {
1099 $mergedLocalLang = $LOCAL_LANG['default'];
1100 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($mergedLocalLang, $LOCAL_LANG[$this->language], TRUE, FALSE);
1101 $LOCAL_LANG[$this->language] = $mergedLocalLang;
1102 } else {
1103 $LOCAL_LANG[$this->language] = $LOCAL_LANG['default'];
1104 }
1105 $labelsArray[$labels] = $LOCAL_LANG[$this->language];
1106 }
1107 $JSLanguageArray .= 'HTMLArea.I18N = ' . json_encode($labelsArray) . ';' . LF;
1108 return $JSLanguageArray;
1109 }
1110
1111 /**
1112 * Writes contents in a file in typo3temp/rtehtmlarea directory and returns the file name
1113 *
1114 * @param string $sourceFileName: The name of the file from which the contents should be extracted
1115 * @param string $label: A label to insert at the beginning of the name of the file
1116 * @param string $fileExtension: The file extension of the file, defaulting to 'js'
1117 * @param string $contents: The contents to write into the file if no $sourceFileName is provided
1118 * @param boolean $concatenate Not used anymore
1119 * @return string The name of the file writtten to typo3temp/rtehtmlarea
1120 */
1121 public function writeTemporaryFile($sourceFileName = '', $label, $fileExtension = 'js', $contents = '', $concatenate = FALSE) {
1122 if ($sourceFileName) {
1123 $output = '';
1124 $source = GeneralUtility::getFileAbsFileName($sourceFileName);
1125 $output = file_get_contents($source);
1126 } else {
1127 $output = $contents;
1128 }
1129 $relativeFilename = 'typo3temp/' . $this->ID . '_' . str_replace('-', '_', $label) . '_' . GeneralUtility::shortMD5((TYPO3_version . $TYPO3_CONF_VARS['EXTCONF'][$this->ID]['version'] . ($sourceFileName ? $sourceFileName : $output)), 20) . '.' . $fileExtension;
1130 $destination = PATH_site . $relativeFilename;
1131 if (!file_exists($destination)) {
1132 $minifiedJavaScript = '';
1133 if ($fileExtension == 'js' && $output != '') {
1134 $minifiedJavaScript = GeneralUtility::minifyJavaScript($output);
1135 }
1136 $failure = GeneralUtility::writeFileToTypo3tempDir($destination, $minifiedJavaScript ? $minifiedJavaScript : $output);
1137 if ($failure) {
1138 throw new \RuntimeException($failure, 1294585668);
1139 }
1140 }
1141 if ($this->is_FE()) {
1142 $filename = $relativeFilename;
1143 } else {
1144 $filename = ($this->isFrontendEditActive() ? '' : $this->backPath . '../') . $relativeFilename;
1145 }
1146 return GeneralUtility::resolveBackPath($filename);
1147 }
1148
1149 /**
1150 * Return a file name containing the main JS language array for HTMLArea
1151 *
1152 * @param integer $RTEcounter: The index number of the current RTE editing area within the form.
1153 * @return string filename
1154 * @todo Define visibility
1155 */
1156 public function buildJSMainLangFile($RTEcounter) {
1157 $contents = $this->buildJSMainLangArray() . LF;
1158 foreach ($this->pluginEnabledCumulativeArray[$RTEcounter] as $pluginId) {
1159 $contents .= $this->buildJSLangArray($pluginId) . LF;
1160 }
1161 return $this->writeTemporaryFile('', $this->language . '_' . $this->OutputCharset, 'js', $contents, TRUE);
1162 }
1163
1164 /**
1165 * Return a Javascript localization array for the plugin
1166 *
1167 * @param string $plugin: identification string of the plugin
1168 * @return string Javascript localization array
1169 * @todo Define visibility
1170 */
1171 public function buildJSLangArray($plugin) {
1172 $extensionKey = is_object($this->registeredPlugins[$plugin]) ? $this->registeredPlugins[$plugin]->getExtensionKey() : $this->ID;
1173 $LOCAL_LANG = GeneralUtility::readLLfile('EXT:' . $extensionKey . '/htmlarea/plugins/' . $plugin . '/locallang.xlf', $this->language, 'utf-8', 1);
1174 $JSLanguageArray = 'HTMLArea.I18N["' . $plugin . '"] = new Object();' . LF;
1175 if (is_array($LOCAL_LANG)) {
1176 if (!empty($LOCAL_LANG[$this->language])) {
1177 $defaultLocalLang = $LOCAL_LANG['default'];
1178 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($defaultLocalLang, $LOCAL_LANG[$this->language], TRUE, FALSE);
1179 $LOCAL_LANG[$this->language] = $defaultLocalLang;
1180 } else {
1181 $LOCAL_LANG[$this->language] = $LOCAL_LANG['default'];
1182 }
1183 $JSLanguageArray .= 'HTMLArea.I18N["' . $plugin . '"] = ' . json_encode($LOCAL_LANG[$this->language]) . ';' . LF;
1184 }
1185 return $JSLanguageArray;
1186 }
1187
1188 /**
1189 * Return the JS code of the toolbar configuration for the HTMLArea editor
1190 *
1191 * @return string the JS code as nested JS arrays
1192 */
1193 protected function getJSToolbarArray() {
1194 // The toolbar array
1195 $toolbar = array();
1196 // The current row; a "linebreak" ends the current row
1197 $row = array();
1198 // The current group; each group is between "bar"s; a "linebreak" ends the current group
1199 $group = array();
1200 // Process each toolbar item in the toolbar order list
1201 foreach ($this->toolbarOrderArray as $item) {
1202 switch ($item) {
1203 case 'linebreak':
1204 // Add row to toolbar if not empty
1205 if (!empty($group)) {
1206 $row[] = $group;
1207 $group = array();
1208 }
1209 if (!empty($row)) {
1210 $toolbar[] = $row;
1211 $row = array();
1212 }
1213 break;
1214 case 'bar':
1215 // Add group to row if not empty
1216 if (!empty($group)) {
1217 $row[] = $group;
1218 $group = array();
1219 }
1220 break;
1221 case 'space':
1222 if (end($group) != $this->convertToolbarForHTMLArea($item)) {
1223 $group[] = $this->convertToolbarForHTMLArea($item);
1224 }
1225 break;
1226 default:
1227 if (in_array($item, $this->toolbar)) {
1228 // Add the item to the group
1229 $convertedItem = $this->convertToolbarForHTMLArea($item);
1230 if ($convertedItem) {
1231 $group[] = $convertedItem;
1232 }
1233 }
1234 }
1235 }
1236 // Add the last group and last line, if not empty
1237 if (!empty($group)) {
1238 $row[] = $group;
1239 }
1240 if (!empty($row)) {
1241 $toolbar[] = $row;
1242 }
1243 return json_encode($toolbar);
1244 }
1245
1246 /**
1247 * Localize a string using the language of the content element rather than the language of the BE interface
1248 *
1249 * @param string string: the label to be localized
1250 * @return string Localized string.
1251 */
1252 public function getLLContent($string) {
1253 return GeneralUtility::quoteJSvalue($this->contentLanguageService->sL($string));
1254 }
1255
1256 public function getPageConfigLabel($string, $JScharCode = 1) {
1257 global $LANG, $TSFE, $TYPO3_CONF_VARS;
1258 if ($this->is_FE()) {
1259 if (substr($string, 0, 4) !== 'LLL:') {
1260 // A pure string coming from Page TSConfig must be in utf-8
1261 $label = $TSFE->csConvObj->conv($TSFE->sL(trim($string)), 'utf-8', $this->OutputCharset);
1262 } else {
1263 $label = $TSFE->csConvObj->conv($TSFE->sL(trim($string)), $this->charset, $this->OutputCharset);
1264 }
1265 $label = str_replace('"', '\\"', str_replace('\\\'', '\'', $label));
1266 $label = $JScharCode ? $this->feJScharCode($label) : $label;
1267 } else {
1268 if (substr($string, 0, 4) !== 'LLL:') {
1269 $label = $string;
1270 } else {
1271 $label = $LANG->sL(trim($string));
1272 }
1273 $label = str_replace('"', '\\"', str_replace('\\\'', '\'', $label));
1274 $label = $JScharCode ? GeneralUtility::quoteJSvalue($label) : $label;
1275 }
1276 return $label;
1277 }
1278
1279 /**
1280 * @todo Define visibility
1281 */
1282 public function feJScharCode($str) {
1283 // Convert string to UTF-8:
1284 if ($this->OutputCharset != 'utf-8') {
1285 $str = $GLOBALS['TSFE']->csConvObj->utf8_encode($str, $this->OutputCharset);
1286 }
1287 // Convert the UTF-8 string into a 'JavaScript-safe' encoded string:
1288 return GeneralUtility::quoteJSvalue($str);
1289 }
1290
1291 /**
1292 * Make a file name relative to the PATH_site or to the PATH_typo3
1293 *
1294 * @param string $filename: a file name of the form EXT:.... or relative to the PATH_site
1295 * @return string the file name relative to the PATH_site if in frontend or relative to the PATH_typo3 if in backend
1296 */
1297 public function getFullFileName($filename) {
1298 if (substr($filename, 0, 4) == 'EXT:') {
1299 // extension
1300 list($extKey, $local) = explode('/', substr($filename, 4), 2);
1301 $newFilename = '';
1302 if ((string)$extKey !== '' && \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded($extKey) && (string)$local !== '') {
1303 $newFilename = ($this->is_FE() || $this->isFrontendEditActive() ? \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::siteRelPath($extKey) : $this->backPath . \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath($extKey)) . $local;
1304 }
1305 } else {
1306 $path = ($this->is_FE() || $this->isFrontendEditActive() ? '' : $this->backPath . '../');
1307 $newFilename = $path . ($filename[0] === '/' ? substr($filename, 1) : $filename);
1308 }
1309 return GeneralUtility::resolveBackPath($newFilename);
1310 }
1311
1312 /**
1313 * Return the Javascript code for copying the HTML code from the editor into the hidden input field.
1314 * This is for submit function of the form.
1315 *
1316 * @param integer $RTEcounter: The index number of the current RTE editing area within the form.
1317 * @param string $formName: the name of the form
1318 * @param string $textareaId: the id of the textarea
1319 * @param string $textareaName: the name of the textarea
1320 * @return string Javascript code
1321 * @todo Define visibility
1322 */
1323 public function setSaveRTE($RTEcounter, $formName, $textareaId, $textareaName) {
1324 return 'if (RTEarea["' . $textareaId . '"]) { document.' . $formName . '["' . $textareaName . '"].value = RTEarea["' . $textareaId . '"].editor.getHTML(); } else { OK = 0; };';
1325 }
1326
1327 /**
1328 * Return the Javascript code for copying the HTML code from the editor into the hidden input field.
1329 * This is for submit function of the form.
1330 *
1331 * @param integer $RTEcounter: The index number of the current RTE editing area within the form.
1332 * @param string $formName: the name of the form
1333 * @param string $textareaId: the id of the textarea
1334 * @return string Javascript code
1335 * @todo Define visibility
1336 */
1337 public function setDeleteRTE($RTEcounter, $formName, $textareaId) {
1338 return 'if (RTEarea["' . $textareaId . '"]) { RTEarea["' . $textareaId . '"].deleted = true;}';
1339 }
1340
1341 /**
1342 * Return TRUE if we are in the FE, but not in the FE editing feature of BE.
1343 *
1344 * @return boolean
1345 * @todo Define visibility
1346 */
1347 public function is_FE() {
1348 return is_object($GLOBALS['TSFE']) && !$this->isFrontendEditActive() && TYPO3_MODE == 'FE';
1349 }
1350
1351 /**
1352 * Checks whether frontend editing is active.
1353 *
1354 * @return boolean
1355 */
1356 public function isFrontendEditActive() {
1357 return is_object($GLOBALS['TSFE']) && $GLOBALS['TSFE']->beUserLogin && $GLOBALS['BE_USER']->frontendEdit instanceof \TYPO3\CMS\Core\FrontendEditing\FrontendEditingController;
1358 }
1359
1360 /**
1361 * Client Browser Information
1362 *
1363 * @param string $userAgent: The useragent string, \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('HTTP_USER_AGENT')
1364 * @return array Contains keys "useragent", "browser", "version", "system
1365 * @todo Define visibility
1366 */
1367 public function clientInfo($userAgent = '') {
1368 if (!$userAgent) {
1369 $userAgent = GeneralUtility::getIndpEnv('HTTP_USER_AGENT');
1370 }
1371 $browserInfo = \TYPO3\CMS\Core\Utility\ClientUtility::getBrowserInfo($userAgent);
1372 // Known engines: order is not irrelevant!
1373 $knownEngines = array('opera', 'msie', 'gecko', 'webkit');
1374 if (is_array($browserInfo['all'])) {
1375 foreach ($knownEngines as $engine) {
1376 if ($browserInfo['all'][$engine]) {
1377 $browserInfo['browser'] = $engine;
1378 $browserInfo['version'] = \TYPO3\CMS\Core\Utility\ClientUtility::getVersion($browserInfo['all'][$engine]);
1379 break;
1380 }
1381 }
1382 }
1383 return $browserInfo;
1384 }
1385
1386 /**
1387 * Log usage of deprecated Page TS Config Property
1388 *
1389 * @param string $deprecatedProperty: Name of deprecated property
1390 * @param string $useProperty: Name of property to use instead
1391 * @param string $version: Version of TYPO3 in which the property will be removed
1392 * @return void
1393 */
1394 public function logDeprecatedProperty($deprecatedProperty, $useProperty, $version) {
1395 if (!$this->thisConfig['logDeprecatedProperties.']['disabled']) {
1396 $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);
1397 GeneralUtility::deprecationLog($message);
1398 if (is_object($GLOBALS['BE_USER']) && $this->thisConfig['logDeprecatedProperties.']['logAlsoToBELog']) {
1399 $message = sprintf($GLOBALS['LANG']->getLL('deprecatedPropertyMessage'), $deprecatedProperty, $useProperty, $version, $this->thePid);
1400 $GLOBALS['BE_USER']->simplelog($message, $this->ID);
1401 }
1402 }
1403 }
1404
1405 /***************************
1406 *
1407 * OTHER FUNCTIONS: (from Classic RTE)
1408 *
1409 ***************************/
1410 /**
1411 * @return [type] ...
1412 * @desc
1413 * @todo Define visibility
1414 */
1415 public function RTEtsConfigParams() {
1416 if ($this->is_FE()) {
1417 return '';
1418 } else {
1419 $p = BackendUtility::getSpecConfParametersFromArray($this->specConf['rte_transform']['parameters']);
1420 return $this->elementParts[0] . ':' . $this->elementParts[1] . ':' . $this->elementParts[2] . ':' . $this->thePid . ':' . $this->typeVal . ':' . $this->tscPID . ':' . $p['imgpath'];
1421 }
1422 }
1423
1424 public function cleanList($str) {
1425 if (strstr($str, '*')) {
1426 $str = '*';
1427 } else {
1428 $str = implode(',', array_unique(GeneralUtility::trimExplode(',', $str, TRUE)));
1429 }
1430 return $str;
1431 }
1432
1433 /**
1434 * @todo Define visibility
1435 */
1436 public function filterStyleEl($elValue, $matchList) {
1437 $matchParts = GeneralUtility::trimExplode(',', $matchList, TRUE);
1438 $styleParts = explode(';', $elValue);
1439 $nStyle = array();
1440 foreach ($styleParts as $k => $p) {
1441 $pp = GeneralUtility::trimExplode(':', $p);
1442 if ($pp[0] && $pp[1]) {
1443 foreach ($matchParts as $el) {
1444 $star = substr($el, -1) == '*';
1445 if ($pp[0] === (string)$el || $star && GeneralUtility::isFirstPartOfStr($pp[0], substr($el, 0, -1))) {
1446 $nStyle[] = $pp[0] . ':' . $pp[1];
1447 } else {
1448 unset($styleParts[$k]);
1449 }
1450 }
1451 } else {
1452 unset($styleParts[$k]);
1453 }
1454 }
1455 return implode('; ', $nStyle);
1456 }
1457
1458 // Hook on lorem_ipsum extension to insert text into the RTE in wysiwyg mode
1459 /**
1460 * @todo Define visibility
1461 * @deprecated since 6.2 - will be removed two versions later without replacement
1462 */
1463 public function loremIpsumInsert($params) {
1464 GeneralUtility::logDeprecatedFunction();
1465 return '
1466 if (typeof(lorem_ipsum) == \'function\' && ' . $params['element'] . '.tagName.toLowerCase() == \'textarea\' ) lorem_ipsum(' . $params['element'] . ', lipsum_temp_strings[lipsum_temp_pointer]);
1467 ';
1468 }
1469
1470 }