TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
rtehtmlarea/Classes/Form/Element/RichTextElement.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Rtehtmlarea\Form\Element;
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 
32 
37 {
43  protected $resultArray;
44 
51  protected $pidOfPageRecord;
52 
62 
81 
89 
95  protected $domIdentifier;
96 
102  protected $defaultExtras;
103 
109  protected $client;
110 
116  protected $language;
117 
124 
131 
138 
145 
152  'space' => 'space',
153  'bar' => 'separator',
154  'linebreak' => 'linebreak'
155  ];
156 
162  protected $toolbar = [];
163 
169  protected $toolbarOrderArray = [];
170 
176  protected $pluginButton = [];
177 
183  protected $pluginLabel = [];
184 
190  protected $pluginEnabledArray = [];
191 
198 
204  protected $registeredPlugins = [];
205 
212  public function render()
213  {
214  $table = $this->data['tableName'];
215  $fieldName = $this->data['fieldName'];
216  $row = $this->data['databaseRow'];
217  $parameterArray = $this->data['parameterArray'];
218 
219  $backendUser = $this->getBackendUserAuthentication();
220 
221  $this->resultArray = $this->initializeResultArray();
222  $this->defaultExtras = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
223  $this->pidOfPageRecord = $this->data['effectivePid'];
224  BackendUtility::fixVersioningPid($table, $row);
225  $this->pidOfVersionedMotherRecord = (int)$row['pid'];
226  $this->vanillaRteTsConfig = $backendUser->getTSConfig('RTE', BackendUtility::getPagesTSconfig($this->pidOfPageRecord));
227  $this->processedRteConfiguration = BackendUtility::RTEsetup(
228  $this->vanillaRteTsConfig['properties'],
229  $table,
230  $fieldName,
231  $this->data['recordTypeValue']
232  );
233  $this->client = $this->clientInfo();
234  $this->domIdentifier = preg_replace('/[^a-zA-Z0-9_:.-]/', '_', $parameterArray['itemFormElName']);
235  $this->domIdentifier = htmlspecialchars(preg_replace('/^[^a-zA-Z]/', 'x', $this->domIdentifier));
236 
237  $this->initializeLanguageRelatedProperties();
238 
239  // Get skin file name from Page TSConfig if any
240  $skinFilename = trim($this->processedRteConfiguration['skin']) ?: 'EXT:rtehtmlarea/Resources/Public/Css/Skin/htmlarea.css';
242  $skinDirectory = dirname($skinFilename);
243 
244  // jQuery UI Resizable style sheet and main skin stylesheet
245  $this->resultArray['stylesheetFiles'][] = $skinDirectory . '/jquery-ui-resizable.css';
246  $this->resultArray['stylesheetFiles'][] = $skinFilename;
247 
248  $this->enableRegisteredPlugins();
249 
250  // Configure toolbar
251  $this->setToolbar();
252 
253  // Check if some plugins need to be disabled
254  $this->setPlugins();
255 
256  // Merge the list of enabled plugins with the lists from the previous RTE editing areas on the same form
257  $this->pluginEnabledCumulativeArray = $this->pluginEnabledArray;
258 
259  $this->addInstanceJavaScriptRegistration();
260 
261  $this->addOnSubmitJavaScriptCode();
262 
263  // Add RTE JavaScript
264  $this->loadRequireModulesForRTE();
265 
266  // Create language labels
267  $this->createJavaScriptLanguageLabelsFromFiles();
268 
269  // Get RTE init JS code
270  $this->resultArray['additionalJavaScriptPost'][] = $this->getRteInitJsCode();
271 
272  $html = $this->getMainHtml();
273 
274  $this->resultArray['html'] = $this->renderWizards(
275  [$html],
276  $parameterArray['fieldConf']['config']['wizards'],
277  $table,
278  $row,
279  $fieldName,
280  $parameterArray,
281  $parameterArray['itemFormElName'],
282  $this->defaultExtras,
283  true
284  );
285 
286  return $this->resultArray;
287  }
288 
294  protected function getMainHtml()
295  {
296  $backendUser = $this->getBackendUserAuthentication();
297 
298  if ($this->isInFullScreenMode()) {
299  $width = '100%';
300  $height = '100%';
301  $paddingRight = '0px';
302  $editorWrapWidth = '100%';
303  } else {
304  $options = $backendUser->userTS['options.'];
305  $width = 530 + (isset($options['RTELargeWidthIncrement']) ? (int)$options['RTELargeWidthIncrement'] : 150);
307  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
308  $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
309  $inlineStructureDepth = $inlineStackProcessor->getStructureDepth();
310  $width -= $inlineStructureDepth > 0 ? ($inlineStructureDepth + 1) * 12 : 0;
311  $widthOverride = isset($backendUser->uc['rteWidth']) && trim($backendUser->uc['rteWidth']) ? trim($backendUser->uc['rteWidth']) : trim($this->processedRteConfiguration['RTEWidthOverride']);
312  if ($widthOverride) {
313  if (strstr($widthOverride, '%')) {
314  if ($this->client['browser'] !== 'msie') {
315  $width = (int)$widthOverride > 0 ? (int)$widthOverride : '100%';
316  }
317  } else {
318  $width = (int)$widthOverride > 0 ? (int)$widthOverride : $width;
319  }
320  }
321  $width = strstr($width, '%') ? $width : $width . 'px';
322  $height = 380 + (isset($options['RTELargeHeightIncrement']) ? (int)$options['RTELargeHeightIncrement'] : 0);
323  $heightOverride = isset($backendUser->uc['rteHeight']) && (int)$backendUser->uc['rteHeight'] ? (int)$backendUser->uc['rteHeight'] : (int)$this->processedRteConfiguration['RTEHeightOverride'];
324  $height = $heightOverride > 0 ? $heightOverride . 'px' : $height . 'px';
325  $paddingRight = '2';
326  $editorWrapWidth = '99%';
327  }
328  $rteDivStyle = 'position:relative; left:0px; top:0px; height:' . $height . '; width:' . $width . '; border: 1px solid black; padding: 2 ' . $paddingRight . ' 2 2;';
329 
330  $itemFormElementName = $this->data['parameterArray']['itemFormElName'];
331 
332  // This seems to result in:
333  // _TRANSFORM_bodytext (the handled field name) in case the field is a direct DB field
334  // _TRANSFORM_vDEF (constant string) in case the RTE is within a flex form
335  $triggerFieldName = preg_replace('/\\[([^]]+)\\]$/', '[_TRANSFORM_\\1]', $itemFormElementName);
336 
337  $value = $this->transformDatabaseContentToEditor($this->data['parameterArray']['itemFormElValue']);
338 
339  $result = [];
340  // The hidden field tells the DataHandler that processing should be done on this value.
341  $result[] = '<input type="hidden" name="' . htmlspecialchars($triggerFieldName) . '" value="RTE" />';
342  $result[] = '<div id="pleasewait' . $this->domIdentifier . '" class="pleasewait" style="display: block;" >';
343  $result[] = $this->getLanguageService()->sL('LLL:EXT:rtehtmlarea/Resources/Private/Language/locallang.xlf:Please wait');
344  $result[] = '</div>';
345  $result[] = '<div id="editorWrap' . $this->domIdentifier . '" class="editorWrap" style="visibility: hidden; width:' . $editorWrapWidth . '; height:100%;">';
346  $result[] = '<textarea ' . $this->getValidationDataAsDataAttribute($this->data['parameterArray']['fieldConf']['config']) . ' id="RTEarea' . $this->domIdentifier . '" name="' . htmlspecialchars($itemFormElementName) . '" rows="0" cols="0" style="' . htmlspecialchars($rteDivStyle) . '">';
347  $result[] = htmlspecialchars($value);
348  $result[] = '</textarea>';
349  $result[] = '</div>';
350 
351  return implode(LF, $result);
352  }
353 
359  protected function enableRegisteredPlugins()
360  {
361  // Traverse registered plugins
362  if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rtehtmlarea']['plugins'])) {
363  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rtehtmlarea']['plugins'] as $pluginId => $pluginObjectConfiguration) {
364  if (is_array($pluginObjectConfiguration) && isset($pluginObjectConfiguration['objectReference'])) {
366  $plugin = GeneralUtility::makeInstance($pluginObjectConfiguration['objectReference']);
367  $configuration = [
368  'language' => $this->language,
369  'contentTypo3Language' => $this->contentTypo3Language,
370  'contentISOLanguage' => $this->contentISOLanguage,
371  'contentLanguageUid' => $this->contentLanguageUid,
372  'RTEsetup' => $this->vanillaRteTsConfig,
373  'client' => $this->client,
374  'thisConfig' => $this->processedRteConfiguration,
375  'specConf' => $this->defaultExtras,
376  ];
377  if ($plugin->main($configuration)) {
378  $this->registeredPlugins[$pluginId] = $plugin;
379  // Override buttons from previously registered plugins
380  $pluginButtons = GeneralUtility::trimExplode(',', $plugin->getPluginButtons(), true);
381  foreach ($this->pluginButton as $previousPluginId => $buttonList) {
382  $this->pluginButton[$previousPluginId] = implode(',', array_diff(GeneralUtility::trimExplode(',', $this->pluginButton[$previousPluginId], true), $pluginButtons));
383  }
384  $this->pluginButton[$pluginId] = $plugin->getPluginButtons();
385  $pluginLabels = GeneralUtility::trimExplode(',', $plugin->getPluginLabels(), true);
386  foreach ($this->pluginLabel as $previousPluginId => $labelList) {
387  $this->pluginLabel[$previousPluginId] = implode(',', array_diff(GeneralUtility::trimExplode(',', $this->pluginLabel[$previousPluginId], true), $pluginLabels));
388  }
389  $this->pluginLabel[$pluginId] = $plugin->getPluginLabels();
390  $this->pluginEnabledArray[] = $pluginId;
391  }
392  }
393  }
394  }
395  // Process overrides
396  $hidePlugins = [];
397  foreach ($this->registeredPlugins as $pluginId => $plugin) {
399  if ($plugin->addsButtons() && !$this->pluginButton[$pluginId]) {
400  $hidePlugins[] = $pluginId;
401  }
402  }
403  $this->pluginEnabledArray = array_unique(array_diff($this->pluginEnabledArray, $hidePlugins));
404  }
405 
411  protected function setToolbar()
412  {
413  $backendUser = $this->getBackendUserAuthentication();
414 
415  $this->defaultToolbarOrder = 'bar, blockstylelabel, blockstyle, textstylelabel, textstyle, linebreak,
416  bar, formattext, bold, strong, italic, emphasis, big, small, insertedtext, deletedtext, citation, code,'
417  . 'definition, keyboard, monospaced, quotation, sample, variable, bidioverride, strikethrough, subscript, superscript, underline, span,
418  bar, fontstyle, fontsize, bar, formatblock, insertparagraphbefore, insertparagraphafter, blockquote, line,
419  bar, left, center, right, justifyfull,
420  bar, orderedlist, unorderedlist, definitionlist, definitionitem, outdent, indent,
421  bar, language, showlanguagemarks,lefttoright, righttoleft,
422  bar, textcolor, bgcolor, textindicator,
423  bar, editelement, showmicrodata,
424  bar, image, emoticon, insertcharacter, insertsofthyphen, abbreviation, user,
425  bar, link, unlink,
426  bar, table,'
427  . ($this->processedRteConfiguration['hideTableOperationsInToolbar']
428  && is_array($this->processedRteConfiguration['buttons.'])
429  && is_array($this->processedRteConfiguration['buttons.']['toggleborders.'])
430  && $this->processedRteConfiguration['buttons.']['toggleborders.']['keepInToolbar'] ? ' toggleborders,' : '')
431  . 'bar, findreplace, spellcheck,
432  bar, chMode, inserttag, removeformat, bar, copy, cut, paste, pastetoggle, pastebehaviour, bar, undo, redo, bar, about, linebreak,'
433  . ($this->processedRteConfiguration['hideTableOperationsInToolbar'] ? '' : 'bar, toggleborders,')
434  . ' bar, tableproperties, tablerestyle, bar, rowproperties, rowinsertabove, rowinsertunder, rowdelete, rowsplit, bar,
435  columnproperties, columninsertbefore, columninsertafter, columndelete, columnsplit, bar,
436  cellproperties, cellinsertbefore, cellinsertafter, celldelete, cellsplit, cellmerge';
437 
438  // Additional buttons from registered plugins
439  foreach ($this->registeredPlugins as $pluginId => $plugin) {
441  if ($this->isPluginEnabled($pluginId)) {
442  $pluginButtons = $plugin->getPluginButtons();
443  //Add only buttons not yet in the default toolbar order
444  $addButtons = implode(
445  ',',
446  array_diff(
447  GeneralUtility::trimExplode(',', $pluginButtons, true),
448  GeneralUtility::trimExplode(',', $this->defaultToolbarOrder, true)
449  )
450  );
451  $this->defaultToolbarOrder = ($addButtons ? 'bar,' . $addButtons . ',linebreak,' : '') . $this->defaultToolbarOrder;
452  }
453  }
454  $toolbarOrder = $this->processedRteConfiguration['toolbarOrder'] ?: $this->defaultToolbarOrder;
455  // Getting rid of undefined buttons
456  $this->toolbarOrderArray = array_intersect(GeneralUtility::trimExplode(',', $toolbarOrder, true), GeneralUtility::trimExplode(',', $this->defaultToolbarOrder, true));
457  $toolbarOrder = array_unique(array_values($this->toolbarOrderArray));
458  // Fetching specConf for field from backend
459  $pList = is_array($this->defaultExtras['richtext']['parameters']) ? implode(',', $this->defaultExtras['richtext']['parameters']) : '';
460  if ($pList !== '*') {
461  // If not all
462  $show = is_array($this->defaultExtras['richtext']['parameters']) ? $this->defaultExtras['richtext']['parameters'] : [];
463  if ($this->processedRteConfiguration['showButtons']) {
464  if (!GeneralUtility::inList($this->processedRteConfiguration['showButtons'], '*')) {
465  $show = array_unique(array_merge($show, GeneralUtility::trimExplode(',', $this->processedRteConfiguration['showButtons'], true)));
466  } else {
467  $show = array_unique(array_merge($show, $toolbarOrder));
468  }
469  }
470  if (is_array($this->processedRteConfiguration['showButtons.'])) {
471  foreach ($this->processedRteConfiguration['showButtons.'] as $buttonId => $value) {
472  if ($value) {
473  $show[] = $buttonId;
474  }
475  }
476  $show = array_unique($show);
477  }
478  } else {
479  $show = $toolbarOrder;
480  }
481  $RTEkeyList = isset($backendUser->userTS['options.']['RTEkeyList']) ? $backendUser->userTS['options.']['RTEkeyList'] : '*';
482  if ($RTEkeyList !== '*') {
483  // If not all
484  $show = array_intersect($show, GeneralUtility::trimExplode(',', $RTEkeyList, true));
485  }
486  // Hiding buttons of disabled plugins
487  $hideButtons = ['space', 'bar', 'linebreak'];
488  foreach ($this->pluginButton as $pluginId => $buttonList) {
489  if (!$this->isPluginEnabled($pluginId)) {
490  $buttonArray = GeneralUtility::trimExplode(',', $buttonList, true);
491  foreach ($buttonArray as $button) {
492  $hideButtons[] = $button;
493  }
494  }
495  }
496  // Hiding labels of disabled plugins
497  foreach ($this->pluginLabel as $pluginId => $label) {
498  if (!$this->isPluginEnabled($pluginId)) {
499  $hideButtons[] = $label;
500  }
501  }
502  // Hiding buttons
503  $show = array_diff($show, GeneralUtility::trimExplode(',', $this->processedRteConfiguration['hideButtons'], true));
504  // Apply toolbar constraints from registered plugins
505  foreach ($this->registeredPlugins as $pluginId => $plugin) {
506  if ($this->isPluginEnabled($pluginId) && method_exists($plugin, 'applyToolbarConstraints')) {
507  $show = $plugin->applyToolbarConstraints($show);
508  }
509  }
510  // Getting rid of the buttons for which we have no position
511  $show = array_intersect($show, $toolbarOrder);
512  foreach ($this->registeredPlugins as $pluginId => $plugin) {
514  $plugin->setToolbar($show);
515  }
516  $this->toolbar = $show;
517  }
518 
524  protected function setPlugins()
525  {
526  // Disabling a plugin that adds buttons if none of its buttons is in the toolbar
527  $hidePlugins = [];
528  foreach ($this->pluginButton as $pluginId => $buttonList) {
530  $plugin = $this->registeredPlugins[$pluginId];
531  if ($plugin->addsButtons()) {
532  $showPlugin = false;
533  $buttonArray = GeneralUtility::trimExplode(',', $buttonList, true);
534  foreach ($buttonArray as $button) {
535  if (in_array($button, $this->toolbar)) {
536  $showPlugin = true;
537  }
538  }
539  if (!$showPlugin) {
540  $hidePlugins[] = $pluginId;
541  }
542  }
543  }
544  $this->pluginEnabledArray = array_diff($this->pluginEnabledArray, $hidePlugins);
545  // Hiding labels of disabled plugins
546  $hideLabels = [];
547  foreach ($this->pluginLabel as $pluginId => $label) {
548  if (!$this->isPluginEnabled($pluginId)) {
549  $hideLabels[] = $label;
550  }
551  }
552  $this->toolbar = array_diff($this->toolbar, $hideLabels);
553  // Adding plugins declared as prerequisites by enabled plugins
554  $requiredPlugins = [];
555  foreach ($this->registeredPlugins as $pluginId => $plugin) {
557  if ($this->isPluginEnabled($pluginId)) {
558  $requiredPlugins = array_merge($requiredPlugins, GeneralUtility::trimExplode(',', $plugin->getRequiredPlugins(), true));
559  }
560  }
561  $requiredPlugins = array_unique($requiredPlugins);
562  foreach ($requiredPlugins as $pluginId) {
563  if (is_object($this->registeredPlugins[$pluginId]) && !$this->isPluginEnabled($pluginId)) {
564  $this->pluginEnabledArray[] = $pluginId;
565  }
566  }
567  $this->pluginEnabledArray = array_unique($this->pluginEnabledArray);
568  // Completing the toolbar conversion array for htmlArea
569  foreach ($this->registeredPlugins as $pluginId => $plugin) {
571  if ($this->isPluginEnabled($pluginId)) {
572  $this->convertToolbarForHtmlAreaArray = array_unique(array_merge($this->convertToolbarForHtmlAreaArray, $plugin->getConvertToolbarForHtmlAreaArray()));
573  }
574  }
575  }
576 
582  protected function loadRequireModulesForRTE()
583  {
584  $this->resultArray['requireJsModules'] = [];
585  $this->resultArray['requireJsModules'][] = 'TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea';
586  foreach ($this->pluginEnabledCumulativeArray as $pluginId) {
588  $plugin = $this->registeredPlugins[$pluginId];
589  $extensionKey = is_object($plugin) ? $plugin->getExtensionKey() : 'rtehtmlarea';
590  $requirePath = 'TYPO3/CMS/' . GeneralUtility::underscoredToUpperCamelCase($extensionKey);
591  $this->resultArray['requireJsModules'][] = $requirePath . '/Plugins/' . $pluginId;
592  }
593  }
594 
600  protected function getRteInitJsCode()
601  {
602  $skinFilename = trim($this->processedRteConfiguration['skin']) ?: 'EXT:rtehtmlarea/Resources/Public/Css/Skin/htmlarea.css';
603  $skinFilename = GeneralUtility::getFileAbsFileName($skinFilename);
604  $skinDirectory = dirname($skinFilename);
605  // Editing area style sheet
606  $editedContentCSS = GeneralUtility::createVersionNumberedFilename($skinDirectory . '/htmlarea-edited-content.css');
607  $editorUrl = GeneralUtility::getFileAbsFileName('EXT:rtehtmlarea/Resources/');
608  $editorUrl = dirname($editorUrl) . '/';
609  return 'require(["TYPO3/CMS/Rtehtmlarea/HTMLArea/HTMLArea"], function (HTMLArea) {
610  if (typeof RTEarea === "undefined") {
611  RTEarea = new Object();
612  RTEarea[0] = new Object();
613  RTEarea[0].version = "' . TYPO3_version . '";
614  RTEarea[0].editorUrl = "' . PathUtility::getAbsoluteWebPath($editorUrl) . '";
615  RTEarea[0].editorSkin = "' . PathUtility::getAbsoluteWebPath($skinDirectory) . '/";
616  RTEarea[0].editedContentCSS = "' . PathUtility::getAbsoluteWebPath($editedContentCSS) . '";
617  RTEarea.init = function() {
618  HTMLArea.init();
619  };
620  RTEarea.initEditor = function(editorNumber) {
621  if (typeof HTMLArea === "undefined" || !HTMLArea.isReady) {
622  window.setTimeout(function () {
623  RTEarea.initEditor(editorNumber);
624  }, 40);
625  } else {
626  HTMLArea.initEditor(editorNumber);
627  }
628  };
629  }
630  RTEarea.init();
631  });';
632  }
633 
639  protected function addInstanceJavaScriptRegistration()
640  {
641  $backendUser = $this->getBackendUserAuthentication();
642 
643  $jsArray = [];
644  $jsArray[] = 'window.HTMLArea = window.HTMLArea || {};';
645  $jsArray[] = 'if (typeof configureEditorInstance === "undefined") {';
646  $jsArray[] = ' configureEditorInstance = new Object();';
647  $jsArray[] = '}';
648  $jsArray[] = 'configureEditorInstance[' . GeneralUtility::quoteJSvalue($this->domIdentifier) . '] = function() {';
649  $jsArray[] = 'if (typeof RTEarea === "undefined") {';
650  $jsArray[] = ' window.setTimeout("configureEditorInstance[' . GeneralUtility::quoteJSvalue($this->domIdentifier) . ']();", 40);';
651  $jsArray[] = '} else {';
652  $jsArray[] = 'editornumber = ' . GeneralUtility::quoteJSvalue($this->domIdentifier) . ';';
653  $jsArray[] = 'RTEarea[editornumber] = new Object();';
654  $jsArray[] = 'RTEarea[editornumber].RTEtsConfigParams = "&RTEtsConfigParams=' . rawurlencode($this->RTEtsConfigParams()) . '";';
655  $jsArray[] = 'RTEarea[editornumber].number = editornumber;';
656  $jsArray[] = 'RTEarea[editornumber].deleted = false;';
657  $jsArray[] = 'RTEarea[editornumber].textAreaId = ' . GeneralUtility::quoteJSvalue($this->domIdentifier) . ';';
658  $jsArray[] = 'RTEarea[editornumber].id = "RTEarea" + editornumber;';
659  $jsArray[] = 'RTEarea[editornumber].RTEWidthOverride = "'
660  . (isset($backendUser->uc['rteWidth']) && trim($backendUser->uc['rteWidth'])
661  ? trim($backendUser->uc['rteWidth'])
662  : trim($this->processedRteConfiguration['RTEWidthOverride'])) . '";';
663  $jsArray[] = 'RTEarea[editornumber].RTEHeightOverride = "'
664  . (isset($backendUser->uc['rteHeight']) && (int)$backendUser->uc['rteHeight']
665  ? (int)$backendUser->uc['rteHeight']
666  : (int)$this->processedRteConfiguration['RTEHeightOverride']) . '";';
667  $jsArray[] = 'RTEarea[editornumber].resizable = '
668  . (isset($backendUser->uc['rteResize']) && $backendUser->uc['rteResize']
669  ? 'true;'
670  : (trim($this->processedRteConfiguration['rteResize']) ? 'true;' : 'false;'));
671  $jsArray[] = 'RTEarea[editornumber].maxHeight = "'
672  . (isset($backendUser->uc['rteMaxHeight']) && (int)$backendUser->uc['rteMaxHeight']
673  ? trim($backendUser->uc['rteMaxHeight'])
674  : ((int)$this->processedRteConfiguration['rteMaxHeight'] ?: '2000')) . '";';
675  $jsArray[] = 'RTEarea[editornumber].fullScreen = ' . ($this->isInFullScreenMode() ? 'true;' : 'false;');
676  $jsArray[] = 'RTEarea[editornumber].showStatusBar = ' . (trim($this->processedRteConfiguration['showStatusBar']) ? 'true;' : 'false;');
677  $jsArray[] = 'RTEarea[editornumber].enableWordClean = ' . (trim($this->processedRteConfiguration['enableWordClean']) ? 'true;' : 'false;');
678  $jsArray[] = 'RTEarea[editornumber].htmlRemoveComments = ' . (trim($this->processedRteConfiguration['removeComments']) ? 'true;' : 'false;');
679  $jsArray[] = 'RTEarea[editornumber].disableEnterParagraphs = ' . (trim($this->processedRteConfiguration['disableEnterParagraphs']) ? 'true;' : 'false;');
680  $jsArray[] = 'RTEarea[editornumber].disableObjectResizing = ' . (trim($this->processedRteConfiguration['disableObjectResizing']) ? 'true;' : 'false;');
681  $jsArray[] = 'RTEarea[editornumber].removeTrailingBR = ' . (trim($this->processedRteConfiguration['removeTrailingBR']) ? 'true;' : 'false;');
682  $jsArray[] = 'RTEarea[editornumber].useCSS = ' . (trim($this->processedRteConfiguration['useCSS']) ? 'true' : 'false') . ';';
683  $jsArray[] = 'RTEarea[editornumber].disablePCexamples = ' . (trim($this->processedRteConfiguration['disablePCexamples']) ? 'true;' : 'false;');
684  $jsArray[] = 'RTEarea[editornumber].showTagFreeClasses = ' . (trim($this->processedRteConfiguration['showTagFreeClasses']) ? 'true;' : 'false;');
685  $jsArray[] = 'RTEarea[editornumber].tceformsNested = ' . (!empty($this->data) ? json_encode($this->data['tabAndInlineStack']) : '[]') . ';';
686  $jsArray[] = 'RTEarea[editornumber].dialogueWindows = new Object();';
687  if (isset($this->processedRteConfiguration['dialogueWindows.']['defaultPositionFromTop'])) {
688  $jsArray[] = 'RTEarea[editornumber].dialogueWindows.positionFromTop = ' . (int)$this->processedRteConfiguration['dialogueWindows.']['defaultPositionFromTop'] . ';';
689  }
690  if (isset($this->processedRteConfiguration['dialogueWindows.']['defaultPositionFromLeft'])) {
691  $jsArray[] = 'RTEarea[editornumber].dialogueWindows.positionFromLeft = ' . (int)$this->processedRteConfiguration['dialogueWindows.']['defaultPositionFromLeft'] . ';';
692  }
693  $jsArray[] = 'RTEarea[editornumber].sys_language_content = "' . $this->contentLanguageUid . '";';
694  $jsArray[] = 'RTEarea[editornumber].typo3ContentLanguage = "' . $this->contentTypo3Language . '";';
695  $jsArray[] = 'RTEarea[editornumber].userUid = "' . 'BE_' . $backendUser->user['uid'] . '";';
696 
697  // Setting the plugin flags
698  $jsArray[] = 'RTEarea[editornumber].plugin = new Object();';
699  foreach ($this->pluginEnabledArray as $pluginId) {
700  $jsArray[] = 'RTEarea[editornumber].plugin.' . $pluginId . ' = true;';
701  }
702 
703  // Setting the buttons configuration
704  $jsArray[] = 'RTEarea[editornumber].buttons = new Object();';
705  if (is_array($this->processedRteConfiguration['buttons.'])) {
706  foreach ($this->processedRteConfiguration['buttons.'] as $buttonIndex => $conf) {
707  $button = substr($buttonIndex, 0, -1);
708  if (is_array($conf)) {
709  $jsArray[] = 'RTEarea[editornumber].buttons.' . $button . ' = ' . $this->buildNestedJSArray($conf) . ';';
710  }
711  }
712  }
713 
714  // Setting the list of tags to be removed if specified in the RTE config
715  if (trim($this->processedRteConfiguration['removeTags'])) {
716  $jsArray[] = 'RTEarea[editornumber].htmlRemoveTags = /^(' . implode('|', GeneralUtility::trimExplode(',', $this->processedRteConfiguration['removeTags'], true)) . ')$/i;';
717  }
718 
719  // Setting the list of tags to be removed with their contents if specified in the RTE config
720  if (trim($this->processedRteConfiguration['removeTagsAndContents'])) {
721  $jsArray[] = 'RTEarea[editornumber].htmlRemoveTagsAndContents = /^(' . implode('|', GeneralUtility::trimExplode(',', $this->processedRteConfiguration['removeTagsAndContents'], true)) . ')$/i;';
722  }
723 
724  // Setting array of custom tags if specified in the RTE config
725  if (!empty($this->processedRteConfiguration['customTags'])) {
726  $customTags = GeneralUtility::trimExplode(',', $this->processedRteConfiguration['customTags'], true);
727  if (!empty($customTags)) {
728  $jsArray[] = 'RTEarea[editornumber].customTags= ' . json_encode($customTags) . ';';
729  }
730  }
731 
732  // Setting array of content css files if specified in the RTE config
733  $versionNumberedFileNames = [];
734  $contentCssFileNames = $this->getContentCssFileNames();
735  foreach ($contentCssFileNames as $contentCssFileName) {
736  $versionNumberedFileNames[] = GeneralUtility::createVersionNumberedFilename($contentCssFileName);
737  }
738  $jsArray[] = 'RTEarea[editornumber].pageStyle = ["' . implode('","', $versionNumberedFileNames) . '"];';
739 
740  $jsArray[] = 'RTEarea[editornumber].classesUrl = "' . $this->writeTemporaryFile(('classes_' . $this->language), 'js', $this->buildJSClassesArray()) . '";';
741 
742  // Add Javascript configuration for registered plugins
743  foreach ($this->registeredPlugins as $pluginId => $plugin) {
745  if ($this->isPluginEnabled($pluginId)) {
746  $jsPluginString = $plugin->buildJavascriptConfiguration();
747  if ($jsPluginString) {
748  $jsArray[] = $plugin->buildJavascriptConfiguration();
749  }
750  }
751  }
752 
753  // Avoid premature reference to HTMLArea when being initially loaded by IRRE Ajax call
754  $jsArray[] = 'RTEarea[editornumber].toolbar = ' . $this->getJSToolbarArray() . ';';
755  $jsArray[] = 'RTEarea[editornumber].convertButtonId = ' . json_encode(array_flip($this->convertToolbarForHtmlAreaArray)) . ';';
756  $jsArray[] = 'RTEarea.initEditor(editornumber);';
757  $jsArray[] = '}';
758  $jsArray[] = '}';
759  $jsArray[] = 'configureEditorInstance[' . GeneralUtility::quoteJSvalue($this->domIdentifier) . ']();';
760 
761  $this->resultArray['additionalJavaScriptPost'][] = implode(LF, $jsArray);
762  }
763 
769  protected function getContentCssFileNames()
770  {
771  $contentCss = is_array($this->processedRteConfiguration['contentCSS.']) ? $this->processedRteConfiguration['contentCSS.'] : [];
772  if (isset($this->processedRteConfiguration['contentCSS'])) {
773  $contentCss[] = trim($this->processedRteConfiguration['contentCSS']);
774  }
775  $contentCssFiles = [];
776  if (!empty($contentCss)) {
777  foreach ($contentCss as $contentCssKey => $contentCssfile) {
778  $fileName = trim($contentCssfile);
779  $absolutePath = GeneralUtility::getFileAbsFileName($fileName);
780  if (file_exists($absolutePath) && filesize($absolutePath)) {
781  $contentCssFiles[$contentCssKey] = PathUtility::getAbsoluteWebPath(GeneralUtility::getFileAbsFileName($fileName));
782  }
783  }
784  } else {
785  // Fallback to default content css file if none of the configured files exists and is not empty
786  $contentCssFiles['default'] = PathUtility::getAbsoluteWebPath(GeneralUtility::getFileAbsFileName('EXT:rtehtmlarea/Resources/Public/Css/ContentCss/Default.css'));
787  }
788  return array_unique($contentCssFiles);
789  }
790 
797  protected function isPluginEnabled($pluginId)
798  {
799  return in_array($pluginId, $this->pluginEnabledArray);
800  }
801 
807  protected function buildJSClassesArray()
808  {
809  $RTEProperties = $this->vanillaRteTsConfig['properties'];
810  // Declare sub-arrays
811  $classesArray = [
812  'labels' => [],
813  'values' => [],
814  'noShow' => [],
815  'alternating' => [],
816  'counting' => [],
817  'selectable' => [],
818  'requires' => [],
819  'requiredBy' => [],
820  'XOR' => []
821  ];
822  $JSClassesArray = '';
823  // Scanning the list of classes if specified in the RTE config
824  if (is_array($RTEProperties['classes.'])) {
825  foreach ($RTEProperties['classes.'] as $className => $conf) {
826  $className = rtrim($className, '.');
827 
828  $label = '';
829  if (!empty($conf['name'])) {
830  $label = $this->getLanguageService()->sL(trim($conf['name']));
831  $label = str_replace('"', '\\"', str_replace('\\\'', '\'', $label));
832  }
833  $classesArray['labels'][$className] = $label;
834  $classesArray['values'][$className] = str_replace('\\\'', '\'', $conf['value']);
835  if (isset($conf['noShow'])) {
836  $classesArray['noShow'][$className] = $conf['noShow'];
837  }
838  if (is_array($conf['alternating.'])) {
839  $classesArray['alternating'][$className] = $conf['alternating.'];
840  }
841  if (is_array($conf['counting.'])) {
842  $classesArray['counting'][$className] = $conf['counting.'];
843  }
844  if (isset($conf['selectable'])) {
845  $classesArray['selectable'][$className] = $conf['selectable'];
846  }
847  if (isset($conf['requires'])) {
848  $classesArray['requires'][$className] = explode(',', GeneralUtility::rmFromList($className, $this->cleanList($conf['requires'])));
849  }
850  }
851  // Remove circularities from classes dependencies
852  $requiringClasses = array_keys($classesArray['requires']);
853  foreach ($requiringClasses as $requiringClass) {
854  if ($this->hasCircularDependency($classesArray, $requiringClass, $requiringClass)) {
855  unset($classesArray['requires'][$requiringClass]);
856  }
857  }
858  // Reverse relationship for the dependency checks when removing styles
859  $requiringClasses = array_keys($classesArray['requires']);
860  foreach ($requiringClasses as $className) {
861  foreach ($classesArray['requires'][$className] as $requiredClass) {
862  if (!is_array($classesArray['requiredBy'][$requiredClass])) {
863  $classesArray['requiredBy'][$requiredClass] = [];
864  }
865  if (!in_array($className, $classesArray['requiredBy'][$requiredClass])) {
866  $classesArray['requiredBy'][$requiredClass][] = $className;
867  }
868  }
869  }
870  }
871  // Scanning the list of sets of mutually exclusives classes if specified in the RTE config
872  if (is_array($RTEProperties['mutuallyExclusiveClasses.'])) {
873  foreach ($RTEProperties['mutuallyExclusiveClasses.'] as $listName => $conf) {
874  $classSet = GeneralUtility::trimExplode(',', $conf, true);
875  $classList = implode(',', $classSet);
876  foreach ($classSet as $className) {
877  $classesArray['XOR'][$className] = '/^(' . implode('|', GeneralUtility::trimExplode(',', GeneralUtility::rmFromList($className, $classList), true)) . ')$/';
878  }
879  }
880  }
881  foreach ($classesArray as $key => $subArray) {
882  $JSClassesArray .= 'HTMLArea.classes' . ucfirst($key) . ' = ' . $this->buildNestedJSArray($subArray) . ';' . LF;
883  }
884  return $JSClassesArray;
885  }
886 
896  protected function hasCircularDependency(&$classesArray, $requiringClass, $initialClass, $recursionLevel = 0)
897  {
898  if (is_array($classesArray['requires'][$requiringClass])) {
899  if (in_array($initialClass, $classesArray['requires'][$requiringClass])) {
900  return true;
901  } else {
902  if ($recursionLevel++ < 20) {
903  foreach ($classesArray['requires'][$requiringClass] as $requiringClass2) {
904  if ($this->hasCircularDependency($classesArray, $requiringClass2, $initialClass, $recursionLevel)) {
905  return true;
906  }
907  }
908  }
909  return false;
910  }
911  } else {
912  return false;
913  }
914  }
915 
925  protected function buildNestedJSArray($conf)
926  {
927  $convertedConf = GeneralUtility::removeDotsFromTS($conf);
928  return str_replace(
929  [':"0"', ':"\\/^(', ')$\\/i"', ':"\\/^(', ')$\\/"', '[]'],
930  [':false', ':/^(', ')$/i', ':/^(', ')$/', '{}'], json_encode($convertedConf)
931  );
932  }
933 
943  protected function writeTemporaryFile($label, $fileExtension = 'js', $contents = '')
944  {
945  $relativeFilename = 'typo3temp/assets/js/rte_' . str_replace('-', '_', $label) . '_' . GeneralUtility::shortMD5($contents, 20) . '.' . $fileExtension;
946  $destination = PATH_site . $relativeFilename;
947  if (!file_exists($destination)) {
948  $minifiedJavaScript = '';
949  if ($fileExtension === 'js' && $contents !== '') {
950  $minifiedJavaScript = GeneralUtility::minifyJavaScript($contents);
951  }
952  $failure = GeneralUtility::writeFileToTypo3tempDir($destination, $minifiedJavaScript ? $minifiedJavaScript : $contents);
953  if ($failure) {
954  throw new \RuntimeException($failure, 1460975840);
955  }
956  }
957  if (isset($GLOBALS['TSFE'])) {
958  $fileName = $relativeFilename;
959  } else {
960  $fileName = '../' . $relativeFilename;
961  }
962  return GeneralUtility::resolveBackPath($fileName);
963  }
964 
972  protected function createJavaScriptLanguageLabelsFromFiles()
973  {
974  $labelArray = [];
975  // Load labels of 3 base files into JS
976  foreach (['tooltips', 'msg', 'dialogs'] as $identifier) {
977  $fileName = 'EXT:rtehtmlarea/Resources/Private/Language/locallang_' . $identifier . '.xlf';
978  $newLabels = $this->getMergedLabelsFromFile($fileName);
979  if (!empty($newLabels)) {
980  $labelArray[$identifier] = $newLabels;
981  }
982  }
983  // Load labels of plugins into JS
984  foreach ($this->pluginEnabledCumulativeArray as $pluginId) {
986  $plugin = $this->registeredPlugins[$pluginId];
987  $extensionKey = is_object($plugin) ? $plugin->getExtensionKey() : 'rtehtmlarea';
988  $fileName = 'EXT:' . $extensionKey . '/Resources/Private/Language/Plugins/' . $pluginId . '/locallang_js.xlf';
989  $newLabels = $this->getMergedLabelsFromFile($fileName);
990  if (!empty($newLabels)) {
991  $labelArray[$pluginId] = $newLabels;
992  }
993  }
994  $this->resultArray['additionalJavaScriptPost'][] = 'HTMLArea.I18N = ' . json_encode($labelArray);
995  }
996 
1004  protected function getMergedLabelsFromFile($fileName)
1005  {
1007  $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
1008  $localizationArray = $languageFactory->getParsedData($fileName, $this->language, 'utf-8', 1);
1009  if (is_array($localizationArray) && !empty($localizationArray)) {
1010  if (!empty($localizationArray[$this->language])) {
1011  $finalLocalLang = $localizationArray['default'];
1012  ArrayUtility::mergeRecursiveWithOverrule($finalLocalLang, $localizationArray[$this->language], true, false);
1013  $localizationArray[$this->language] = $finalLocalLang;
1014  } else {
1015  $localizationArray[$this->language] = $localizationArray['default'];
1016  }
1017  } else {
1018  $localizationArray = [];
1019  }
1020  return $localizationArray[$this->language];
1021  }
1022 
1028  protected function getJSToolbarArray()
1029  {
1030  // The toolbar array
1031  $toolbar = [];
1032  // The current row; a "linebreak" ends the current row
1033  $row = [];
1034  // The current group; each group is between "bar"s; a "linebreak" ends the current group
1035  $group = [];
1036  // Process each toolbar item in the toolbar order list
1037  foreach ($this->toolbarOrderArray as $item) {
1038  switch ($item) {
1039  case 'linebreak':
1040  // Add row to toolbar if not empty
1041  if (!empty($group)) {
1042  $row[] = $group;
1043  $group = [];
1044  }
1045  if (!empty($row)) {
1046  $toolbar[] = $row;
1047  $row = [];
1048  }
1049  break;
1050  case 'bar':
1051  // Add group to row if not empty
1052  if (!empty($group)) {
1053  $row[] = $group;
1054  $group = [];
1055  }
1056  break;
1057  case 'space':
1058  if (end($group) != $this->convertToolbarForHtmlAreaArray[$item]) {
1059  $group[] = $this->convertToolbarForHtmlAreaArray[$item];
1060  }
1061  break;
1062  default:
1063  if (in_array($item, $this->toolbar)) {
1064  // Add the item to the group
1065  $convertedItem = $this->convertToolbarForHtmlAreaArray[$item];
1066  if ($convertedItem) {
1067  $group[] = $convertedItem;
1068  }
1069  }
1070  }
1071  }
1072  // Add the last group and last line, if not empty
1073  if (!empty($group)) {
1074  $row[] = $group;
1075  }
1076  if (!empty($row)) {
1077  $toolbar[] = $row;
1078  }
1079  return json_encode($toolbar);
1080  }
1081 
1087  protected function addOnSubmitJavaScriptCode()
1088  {
1089  $onSubmitCode = [];
1090  $onSubmitCode[] = 'if (RTEarea[' . GeneralUtility::quoteJSvalue($this->domIdentifier) . ']) {';
1091  $onSubmitCode[] = 'document.editform[' . GeneralUtility::quoteJSvalue($this->data['parameterArray']['itemFormElName']) . '].value = RTEarea[' . GeneralUtility::quoteJSvalue($this->domIdentifier) . '].editor.getHTML();';
1092  $onSubmitCode[] = '} else {';
1093  $onSubmitCode[] = 'OK = 0;';
1094  $onSubmitCode[] = '};';
1095  $this->resultArray['additionalJavaScriptSubmit'][] = implode(LF, $onSubmitCode);
1096  }
1097 
1103  protected function clientInfo()
1104  {
1105  $userAgent = GeneralUtility::getIndpEnv('HTTP_USER_AGENT');
1106  $browserInfo = ClientUtility::getBrowserInfo($userAgent);
1107  // Known engines: order is not irrelevant!
1108  $knownEngines = ['opera', 'msie', 'gecko', 'webkit'];
1109  if (is_array($browserInfo['all'])) {
1110  foreach ($knownEngines as $engine) {
1111  if ($browserInfo['all'][$engine]) {
1112  $browserInfo['browser'] = $engine;
1113  $browserInfo['version'] = ClientUtility::getVersion($browserInfo['all'][$engine]);
1114  break;
1115  }
1116  }
1117  }
1118  return $browserInfo;
1119  }
1120 
1126  public function initializeLanguageRelatedProperties()
1127  {
1128  $this->language = $GLOBALS['LANG']->lang;
1129  if ($this->language === 'default' || !$this->language) {
1130  $this->language = 'en';
1131  }
1132  $currentLanguageUid = $this->data['databaseRow']['sys_language_uid'];
1133  if (is_array($currentLanguageUid)) {
1134  $currentLanguageUid = $currentLanguageUid[0];
1135  }
1136  $this->contentLanguageUid = (int)max($currentLanguageUid, 0);
1137  if ($this->contentLanguageUid) {
1138  $this->contentISOLanguage = $this->language;
1139  if (ExtensionManagementUtility::isLoaded('static_info_tables')) {
1140  $tableA = 'sys_language';
1141  $tableB = 'static_languages';
1142 
1143  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1144  ->getQueryBuilderForTable($tableA);
1145 
1146  $result = $queryBuilder
1147  ->select('a.uid', 'b.lg_iso_2', 'b.lg_country_iso_2')
1148  ->from($tableA, 'a')
1149  ->where('a.uid', (int)$this->contentLanguageUid)
1150  ->leftJoin(
1151  'a',
1152  $tableB,
1153  'b',
1154  $queryBuilder->expr()->eq('a.static_lang_isocode', $queryBuilder->quoteIdentifier('b.uid'))
1155  )
1156  ->execute();
1157 
1158  while ($languageRow = $result->fetch()) {
1159  $this->contentISOLanguage = strtolower(trim($languageRow['lg_iso_2']) . (trim($languageRow['lg_country_iso_2']) ? '_' . trim($languageRow['lg_country_iso_2']) : ''));
1160  }
1161  }
1162  } else {
1163  $this->contentISOLanguage = trim($this->processedRteConfiguration['defaultContentLanguage']) ?: 'en';
1164  $languageCodeParts = explode('_', $this->contentISOLanguage);
1165  $this->contentISOLanguage = strtolower($languageCodeParts[0]) . ($languageCodeParts[1] ? '_' . strtoupper($languageCodeParts[1]) : '');
1166  // Find the configured language in the list of localization locales
1168  $locales = GeneralUtility::makeInstance(Locales::class);
1169  // If not found, default to 'en'
1170  if (!in_array($this->contentISOLanguage, $locales->getLocales())) {
1171  $this->contentISOLanguage = 'en';
1172  }
1173  }
1174  $this->contentTypo3Language = $this->contentISOLanguage === 'en' ? 'default' : $this->contentISOLanguage;
1175  }
1176 
1185  protected function logDeprecatedProperty($deprecatedProperty, $useProperty, $version)
1186  {
1187  $backendUser = $this->getBackendUserAuthentication();
1188  if (!$this->processedRteConfiguration['logDeprecatedProperties.']['disabled']) {
1189  $message = sprintf(
1190  '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.',
1191  $deprecatedProperty,
1192  $useProperty,
1193  $version,
1194  $this->data['databaseRow']['pid']
1195  );
1197  if ($this->processedRteConfiguration['logDeprecatedProperties.']['logAlsoToBELog']) {
1198  $message = sprintf(
1199  $this->getLanguageService()->sL('LLL:EXT:rtehtmlarea/Resources/Private/Language/locallang.xlf:deprecatedPropertyMessage'),
1200  $deprecatedProperty,
1201  $useProperty,
1202  $version,
1203  $this->data['databaseRow']['pid']
1204  );
1205  $backendUser->simplelog($message, 'rtehtmlarea');
1206  }
1207  }
1208  }
1209 
1215  protected function RTEtsConfigParams()
1216  {
1217  $parameters = BackendUtility::getSpecConfParametersFromArray($this->defaultExtras['rte_transform']['parameters']);
1218  $result = [
1219  $this->data['tableName'],
1220  $this->data['databaseRow']['uid'],
1221  $this->data['fieldName'],
1223  $this->data['recordTypeValue'],
1225  $parameters['imgpath'],
1226  ];
1227  return implode(':', $result);
1228  }
1229 
1236  protected function cleanList($str)
1237  {
1238  if (strstr($str, '*')) {
1239  $str = '*';
1240  } else {
1241  $str = implode(',', array_unique(GeneralUtility::trimExplode(',', $str, true)));
1242  }
1243  return $str;
1244  }
1245 
1252  protected function transformDatabaseContentToEditor($value)
1253  {
1254  // change <strong> to <b>
1255  $value = preg_replace('/<(\\/?)strong/i', '<$1b', $value);
1256  // change <em> to <i>
1257  $value = preg_replace('/<(\\/?)em([^b>]*>)/i', '<$1i$2', $value);
1258 
1259  if ($this->defaultExtras['rte_transform']) {
1261  $parseHTML = GeneralUtility::makeInstance(RteHtmlParser::class);
1262  $parseHTML->init($this->data['table'] . ':' . $this->data['fieldName'], $this->pidOfVersionedMotherRecord);
1263  $value = $parseHTML->RTE_transform($value, $this->defaultExtras, 'rte', $this->processedRteConfiguration);
1264  }
1265  return $value;
1266  }
1267 
1273  protected function isInFullScreenMode()
1274  {
1275  return GeneralUtility::_GP('M') === 'wizard_rte';
1276  }
1277 
1281  protected function getLanguageService()
1282  {
1283  return $GLOBALS['LANG'];
1284  }
1285 
1289  protected function getBackendUserAuthentication()
1290  {
1291  return $GLOBALS['BE_USER'];
1292  }
1293 }
static minifyJavaScript($script, &$error= '')
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
hasCircularDependency(&$classesArray, $requiringClass, $initialClass, $recursionLevel=0)
static writeFileToTypo3tempDir($filepath, $content)
static getPagesTSconfig($id, $rootLine=null, $returnPartArray=false)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static RTEsetup($RTEprop, $table, $field, $type= '')
static makeInstance($className,...$constructorArguments)
static getFileAbsFileName($filename, $_=null, $_2=null)
static getAbsoluteWebPath($targetPath)
Definition: PathUtility.php:40
static fixVersioningPid($table, &$rr, $ignoreWorkspaceMatch=false)