Skip to content
Snippets Groups Projects
Commit d90cb884 authored by Marco Huber's avatar Marco Huber
Browse files

New RTE API in TYPO3 7.4 part 2

parent ab8fb846
No related branches found
No related tags found
1 merge request!3TYPO3 7.4 compatibility
<?php
namespace SGalinski\Tinymce4Rte\Form\Element;
namespace SGalinski\Tinymce4Rte\Editors;
/***************************************************************
* Copyright notice
*
* (c) sgalinski Internet Services (http://www.sgalinski.de)
*
* All rights reserved
/*
* This file is part of the TYPO3 CMS project.
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
* The TYPO3 project - inspiring people to share!
*/
use SGalinski\Tinymce\Loader;
use TYPO3\CMS\Backend\Form\FormEngine;
use TYPO3\CMS\Backend\Rte\AbstractRte;
use TYPO3\CMS\Backend\Form\Element\AbstractFormElement;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Database\DatabaseConnection;
use TYPO3\CMS\Core\Html\RteHtmlParser;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use SGalinski\Tinymce\Loader;
/**
* A RTE using TinyMCE
* Render rich text editor in FormEngine
*/
class RteBase extends AbstractRte {
class RichTextElement extends AbstractFormElement {
/**
* Main result array as defined in initializeResultArray() of AbstractNode
*
* @var array
*/
protected $resultArray;
/**
* pid of page record the TSconfig is located at.
* This is pid of record if table is not pages, or uid if table is pages
*
* @var int
*/
protected $pidOfPageRecord;
/**
* pid of fixed versioned record.
* This is the pid of the record in normal cases, but is changed to the pid
* of the "mother" record in case the handled record is a versioned overlay
* and "mother" is located at a different pid.
*
* @var int
*/
protected $pidOfVersionedMotherRecord;
/**
* Native, not further processed TsConfig of RTE section for this record on given pid.
*
* Example:
*
* RTE = foo
* RTE.bar = xy
*
* array(
* 'value' => 'foo',
* 'properties' => array(
* 'bar' => 'xy',
* ),
* );
*
* @var array
*/
protected $vanillaRteTsConfig;
/**
* Based on $vanillaRteTsConfig, this property contains "processed" configuration
* where table and type specific RTE setup is merged into 'default.' array.
*
* @var array
*/
protected $processedRteConfiguration;
/**
* Parsed "defaultExtras" TCA
*
* @var array
*/
protected $defaultExtras;
/**
* Indicates that the tinymce is loaded
*
......@@ -46,52 +96,72 @@ class RteBase extends AbstractRte {
static protected $coreLoaded = FALSE;
/**
* Draws the RTE as a form field or whatever is needed (inserts JavaApplet, creates iframe, renders ....)
* Default is to output the transformed content in a plain textarea field. This mode is great for debugging transformations!
* This will render a <textarea> OR RTE area form field,
* possibly with various control/validation features
*
* @return array As defined in initializeResultArray() of AbstractNode
*/
public function render() {
$table = $this->globalOptions['table'];
$fieldName = $this->globalOptions['fieldName'];
$row = $this->globalOptions['databaseRow'];
$parameterArray = $this->globalOptions['parameterArray'];
$backendUser = $this->getBackendUserAuthentication();
$this->resultArray = $this->initializeResultArray();
$this->defaultExtras = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
$this->pidOfPageRecord = $table === 'pages' && MathUtility::canBeInterpretedAsInteger($row['uid']) ? (int)$row['uid'] : (int)$row['pid'];
BackendUtility::fixVersioningPid($table, $row);
$this->pidOfVersionedMotherRecord = (int)$row['pid'];
$this->vanillaRteTsConfig = $backendUser->getTSConfig('RTE', BackendUtility::getPagesTSconfig($this->pidOfPageRecord));
$this->processedRteConfiguration = BackendUtility::RTEsetup(
$this->vanillaRteTsConfig['properties'],
$table,
$fieldName,
$this->globalOptions['recordTypeValue']
);
$this->resultArray['html'] = $this->getMainHtml($parameterArray, $row);
return $this->resultArray;
}
/**
* Create main HTML elements
*
* @param FormEngine $parentObject Reference to parent object, which is an instance of the TCEforms.
* @param string $table The table name
* @param string $field The field name
* @param array $row The current row from which field is being rendered
* @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.
* @param array $specConf "special" configuration - what is found at position 4 in the types configuration of a field from record, parsed into an array.
* @param array $thisConfig Configuration for RTEs; A mix between TSconfig and otherwise. Contains configuration for display, which buttons are enabled, additional transformation information etc.
* @param string $RTEtypeVal Record "type" field value.
* @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!
* @param integer $thePidValue PID value of record (true parent page id)
* @return string HTML code for RTE!
* @return string Main RTE html
*/
public function drawRTE(
FormEngine $parentObject, $table, $field, $row, $PA, $specConf,
$thisConfig, $RTEtypeVal, $RTErelPath, $thePidValue
) {
protected function getMainHtml($parameterArray, $row) {
/** @var PageRenderer $pageRenderer */
$pageRenderer = $GLOBALS['SOBE']->doc->getPageRenderer();
// render the tinymce textarea
$value = $this->transformContent(
'rte', $PA['itemFormElValue'], $table, $field, $row, $specConf, $thisConfig, $RTErelPath, $thePidValue
);
$value = $this->transformDatabaseContentToEditor($parameterArray['itemFormElValue']);
// This seems to result in:
// _TRANSFORM_bodytext (the handled field name) in case the field is a direct DB field
// _TRANSFORM_vDEF (constant string) in case the RTE is within a flex form
$triggerFieldName = preg_replace('/\\[([^]]+)\\]$/', '[_TRANSFORM_\\1]', $parameterArray['itemFormElName']);
// render RTE field
$editorId = uniqid();
$width = (GeneralUtility::_GP('M') === 'wizard_rte' ? '100%' : '650px');
$code = $this->triggerField($PA['itemFormElName']);
$code = '<input type="hidden" name="' . htmlspecialchars($triggerFieldName) . '" value="RTE" />';
$code .= '<div style="width: ' . $width . '"><textarea id="editor' . $editorId . '" class="tinymce4_rte"
name="' . htmlspecialchars($PA['itemFormElName']) . '"
name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"
rows="20" cols="100">' . GeneralUtility::formatForTextarea($value) . '</textarea></div>';
// add the tinymce code and it's configuration
if (!self::$coreLoaded) {
self::$coreLoaded = TRUE;
$userOrPageProperties = BackendUtility::getModTSconfig($thePidValue, 'RTE');
/** @var Loader $tinyMCE */
$tinyMCE = GeneralUtility::makeInstance('SGalinski\\Tinymce\\Loader');
$tinyMCE->loadConfiguration($userOrPageProperties['properties']['default.']['tinymceConfiguration']);
if ($userOrPageProperties['properties']['default.']['contentCSS'] !== '') {
$tinyMCE->loadConfiguration($this->processedRteConfiguration['tinymceConfiguration']);
if ($this->processedRteConfiguration['contentCSS'] !== '') {
$contentCssFile = GeneralUtility::getFileAbsFileName(
$userOrPageProperties['properties']['default.']['contentCSS']
$this->processedRteConfiguration['contentCSS']
);
if (is_file($contentCssFile)) {
......@@ -122,17 +192,6 @@ class RteBase extends AbstractRte {
);
}
// calculate the dedicated RTE configuration
$elementParts = preg_replace('/^(TSFE_EDIT\\[data\\]\\[|data\\[)/', '', $PA['itemFormElName']);
$elementParts = preg_replace('/\\]$/', '', $elementParts);
$elementParts = explode('][', $elementParts);
list($typoscriptConfigurationPid, $pid) = BackendUtility::getTSCpid(
trim($elementParts[0]), trim($elementParts[1]), $thePidValue
);
$rteConfiguration = rawurlencode(
$this->getRteConfiguration($specConf, $RTEtypeVal, $pid, $typoscriptConfigurationPid, $elementParts)
);
// add RTE specific configuration data
$languageId = max($row['sys_language_uid'], 0);
$language = ($GLOBALS['LANG']->lang === '' ? 'default' : $GLOBALS['LANG']->lang);
......@@ -140,7 +199,7 @@ class RteBase extends AbstractRte {
'RTE' . $editorId,
'window.RTE = window.RTE || {};
window.RTE["editor' . $editorId . '"] = {};
window.RTE["editor' . $editorId . '"].rteConfiguration = "' . $rteConfiguration . '";
window.RTE["editor' . $editorId . '"].rteConfiguration = "' . rawurlencode($this->RTEtsConfigParams()) . '";
window.RTE["editor' . $editorId . '"].typo3ContentLanguage = "' . $language . '";
window.RTE["editor' . $editorId . '"].sys_language_content = parseInt(' . $languageId . ');
'
......@@ -150,21 +209,55 @@ class RteBase extends AbstractRte {
}
/**
* @param array $configuration
* @param string $typeValue
* @param int $pid
* @param int $typoscriptConfigurationPid
* @param array $elementParts
* A list of parameters that is mostly given as GET/POST to other RTE controllers.
*
* @return string
*/
public function getRteConfiguration(
array $configuration, $typeValue, $pid, $typoscriptConfigurationPid, array $elementParts
) {
$parameters = BackendUtility::getSpecConfParametersFromArray($configuration['rte_transform']['parameters']);
return $elementParts[0] . ':' . $elementParts[1] . ':' . $elementParts[2] . ':' . (int) $pid . ':' .
$typeValue . ':' . (int) $typoscriptConfigurationPid . ':' . $parameters['imgpath'];
protected function RTEtsConfigParams() {
$parameters = BackendUtility::getSpecConfParametersFromArray($this->defaultExtras['rte_transform']['parameters']);
$result = array(
$this->globalOptions['table'],
$this->globalOptions['databaseRow']['uid'],
$this->globalOptions['fieldName'],
$this->pidOfVersionedMotherRecord,
$this->globalOptions['recordTypeValue'],
$this->pidOfPageRecord,
$parameters['imgpath'],
);
return implode(':', $result);
}
}
/**
* Performs transformation of content from database to richtext editor
*
* @param string $value Value to transform.
* @return string Transformed content
*/
protected function transformDatabaseContentToEditor($value) {
// change <strong> to <b>
$value = preg_replace('/<(\\/?)strong/i', '<$1b', $value);
// change <em> to <i>
$value = preg_replace('/<(\\/?)em([^b>]*>)/i', '<$1i$2', $value);
if ($this->defaultExtras['rte_transform']) {
$parameters = BackendUtility::getSpecConfParametersFromArray($this->defaultExtras['rte_transform']['parameters']);
// There must be a mode set for transformation
if ($parameters['mode']) {
/** @var RteHtmlParser $parseHTML */
$parseHTML = GeneralUtility::makeInstance(RteHtmlParser::class);
$parseHTML->init($this->globalOptions['table'] . ':' . $this->globalOptions['fieldName'], $this->pidOfVersionedMotherRecord);
$parseHTML->setRelPath('');
$value = $parseHTML->RTE_transform($value, $this->defaultExtras, 'rte', $this->processedRteConfiguration);
}
}
return $value;
}
/**
* @return BackendUserAuthentication
*/
protected function getBackendUserAuthentication() {
return $GLOBALS['BE_USER'];
}
?>
\ No newline at end of file
}
<?php
namespace SGalinski\Tinymce4Rte\Form\Resolver;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\Form\NodeResolverInterface;
use SGalinski\Tinymce4Rte\Form\Element\RichTextElement;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Backend\Utility\BackendUtility;
/**
* This resolver will return the RichTextElement render class of ext:rtehtmlarea if RTE is enabled for this field.
*/
class RichTextNodeResolver implements NodeResolverInterface {
/**
* Global options from NodeFactory
*
* @var array
*/
protected $globalOptions;
/**
* Set global options from parent instance
*
* @param array $globalOptions Global options like 'readonly' for all elements
* @return $this
*/
public function setGlobalOptions(array $globalOptions) {
$this->globalOptions = $globalOptions;
return $this;
}
/**
* Returns RichTextElement as class name if RTE widget should be rendered.
*
* @return string|void New class name or void if this resolver does not change current class name.
*/
public function resolve() {
$table = $this->globalOptions['table'];
$fieldName = $this->globalOptions['fieldName'];
$row = $this->globalOptions['databaseRow'];
$parameterArray = $this->globalOptions['parameterArray'];
$backendUser = $this->getBackendUserAuthentication();
if (
// Whole thing is not read only
empty($this->globalOptions['renderReadonly'])
// This field is not read only
&& !$parameterArray['fieldConf']['config']['readOnly']
// If RTE is generally enabled by user settings and RTE object registry can return something valid
&& $backendUser->isRTE()
) {
$specialConfiguration = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
// If "richtext" is within defaultExtras
if (isset($specialConfiguration['richtext'])) {
// Operates by reference on $row! 'pid' is changed ...
BackendUtility::fixVersioningPid($table, $row);
list($recordPid, $tsConfigPid) = BackendUtility::getTSCpidCached($table, $row['uid'], $row['pid']);
// If the pid-value is not negative (that is, a pid could NOT be fetched)
if ($tsConfigPid >= 0) {
// Fetch page ts config and do some magic with it to find out if RTE is disabled on TS level.
$rteSetup = $backendUser->getTSConfig('RTE', BackendUtility::getPagesTSconfig($recordPid));
$rteTcaTypeValue = BackendUtility::getTCAtypeValue($table, $row);
$rteSetupConfiguration = BackendUtility::RTEsetup($rteSetup['properties'], $table, $fieldName, $rteTcaTypeValue);
if (!$rteSetupConfiguration['disabled']) {
// Finally, we're sure the editor should really be rendered ...
return RichtextElement::class;
}
}
}
}
return NULL;
}
/**
* @return BackendUserAuthentication
*/
protected function getBackendUserAuthentication() {
return $GLOBALS['BE_USER'];
}
}
......@@ -9,9 +9,11 @@ if (!$GLOBALS['TYPO3_CONF_VARS']['BE']['RTEenabled']) {
$GLOBALS['TYPO3_CONF_VARS']['BE']['RTEenabled'] = 1;
}
// RTE registration
$GLOBALS['TYPO3_CONF_VARS']['BE']['RTE_reg']['tinymce'] = array(
'objRef' => 'SGalinski\Tinymce4Rte\Editors\RteBase'
// Register FormEngine node type resolver hook to render RTE in FormEngine if enabled
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeResolver'][1442500255] = array(
'nodeName' => 'text',
'priority' => 40,
'class' => \SGalinski\Tinymce4Rte\Form\Resolver\RichTextNodeResolver::class,
);
// load default PageTS config
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment