Project

General

Profile

Bug #92301

Updated by Tobias Ulber over 3 years ago

Let's assume we have a CKEditor plugin/widget that generates the following code 

 <pre><code class="html"> 
 <div> 
 <time datetime="2001-05-15T22:00">Tuesday, 15. May 2001, 10:00 Uhr</time> 
 </div> 
 </code></pre> 

 After saving, this code gets correctly inserted into the database. But after that, 
 Typo3 loads the following code inside the RTE, even if the time tag is allowed outside of a paragraph via tsconfig 

 <pre><code class="html"> 
 <div> 
 <p><time datetime="2001-05-15T22:00">Tuesday, 15. May 2001, 10:00 Uhr</time></p> 
 </div> 
 </code></pre> 

 After saving the record again, the code is also wrong in the database. 

 -I I managed to fix this issue after hours of php debugging.  
 The problem is located in the RteHtmlParser(typo3/sysext/core/Classes/Html/RteHtmlParser.php). 
 The allowedTagsOutsideOfParagraphs property of the RteHtmlParser class, is not getting queried before the call of the setDivTags method inside the TS_transform_rte method. 
 I created a Typo3 fork, where I fixed the problem.- 

 Edit: problem. 
 I created a better patch. 

 For anyone who stumbles across this issue, I managed to create a workaround. 
 I've just created a custom transformation class that inherits from the RteHtmlParser class. 
 <pre><code class="php"> 
 <?php 

 namespace Vendor\Sitepackage\Service; 

 use Psr\EventDispatcher\EventDispatcherInterface; 
 use TYPO3\CMS\Core\Html\RteHtmlParser; 

 class RteTransformation extends RteHtmlParser 
 { 
     /** 
      * NOTE: must be public as it is accessed by \TYPO3\CMS\Core\Html\RteHtmlParser without API 
      * 
      * @var \TYPO3\CMS\Core\Html\RteHtmlParser 
      */ 
     public $pObj; 

     /** 
      * NOTE: must be public as it is accessed by \TYPO3\CMS\Core\Html\RteHtmlParser without API 
      * 
      * @var string 
      */ 
     public $transformationKey = 'tx_sitepackage_transformation'; 

     /** 
      * @var array 
      */ 
     protected $configuration; 

     public function __construct(EventDispatcherInterface $eventDispatcher = null) 
     { 
         $this->eventDispatcher = $eventDispatcher; 
     } 

     protected function loadConfiguration() 
     { 
         $this->configuration = $this->pObj->procOptions['usertrans.'][$this->transformationKey . '.']; 
         $this->setProcessingConfiguration($this->pObj->procOptions); 
     } 

     /** 
      * Transforms RTE content prior to database storage 
      * 
      * @param string $value RTE HTML to clean for database storage 
      * @return string 
      */ 
     public function transform_db($value) 
     { 
         $this->loadConfiguration(); 
         // Transform empty paragraphs into spacing paragraphs 
         $value = str_replace('<p></p>', '<p>&nbsp;</p>', $value); 
         // Double any trailing spacing paragraph so that it does not get removed by divideIntoLines() 
         $value = preg_replace('/<p>&nbsp;<\/p>$/', '<p>&nbsp;</p><p>&nbsp;</p>', $value); 
         return $this->TS_transform_db($value); 
     } 

     /** 
      * Transforms database content for RTE display 
      * 
      * @param string $value Database content to transform into RTE-ready HTML 
      * @return string 
      */ 
     public function transform_rte($value) 
     { 
         $this->loadConfiguration(); 
         return $this->TS_transform_rte($value); 
     } 

     /** 
      * OVERRIDES DEFAULT METHOD IN RteHtmlParser.php!!! 
      * Converts all lines into <p></p>-sections (unless the line has a p - tag already) 
      * For processing of content going FROM database TO RTE. 
      * 
      * @param string $value Value to convert 
      * @return string Processed value. 
      * @see divideIntoLines() 
      */ 
     protected function setDivTags($value) 
     { 
         // First, setting configuration for the HTMLcleaner function. This will process each line between the <div>/<p> section on their way to the RTE 
         $keepTags = $this->getKeepTags('rte'); 
         // Divide the content into lines 
         $parts = explode(LF, $value); 
         foreach ($parts as $k => $v) { 
             // Processing of line content: 
             // If the line is blank, set it to &nbsp; 
             if (trim($parts[$k]) === '') { 
                 $parts[$k] = '&nbsp;'; 
             } else { 
                 // Clean the line content, keeping unknown tags (as they can be removed in the entryHTMLparser) 
                 $parts[$k] = $this->HTMLcleaner($parts[$k], $keepTags, 'protect'); 
                 // convert double-encoded &nbsp; into regular &nbsp; however this could also be reversed via the exitHTMLparser 
                 // This was previously an option to disable called "dontConvAmpInNBSP_rte" 
                 $parts[$k] = str_replace('&amp;nbsp;', '&nbsp;', $parts[$k]); 
             } 
             $partFTN = strtolower($this->getFirstTagName($parts[$k] ?? '')); 
             // Filter out the hr 
             $allowedTagsOutsideOfParagraphs = array_filter($this->allowedTagsOutsideOfParagraphs, function ($tag) { 
                 return $tag != 'hr'; 
             }); 
             // Wrapping the line in <p> tags if not already wrapped and does not contain an hr tag and is not allowed outside of Paragraphs 
             if (!preg_match('/<(hr)(\\s[^>\\/]*)?[[:space:]]*\\/?>/i', $parts[$k]) && !in_array($partFTN, $allowedTagsOutsideOfParagraphs)) { 
                 $testStr = strtolower(trim($parts[$k])); 
                 if (strpos($testStr, '<div') !== 0 || substr($testStr, -6) !== '</div>') { 
                     if (strpos($testStr, '<p') !== 0 || substr($testStr, -4) !== '</p>') { 
                         // Only set p-tags if there is not already div or p tags: 
                         $parts[$k] = '<p>' . $parts[$k] . '</p>'; 
                     } 
                 } 
             } 
         } 
         // Implode result: 
         return implode(LF, $parts); 
     } 
 } 

 </code></pre> 
 Notice that I have overrode the default setDivTags method with my changes. 

 Don't forget to add this line to your ext_localconf.php 
 <pre><code class="php"> 
 /*************** 
  * Register Custom Transformation 
  */ 
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['transformation']['tx_sitepackage_transformation'] = Vendor\Sitepackage\Service\RteTransformation::class; 
 </code></pre> 

 This is how you can use the custom transformation in the RTE.tsconfig 
 RTE.config.tt_content.bodytext.proc.HTMLparser_db.overruleMode = tx_sitepackage_transformation,detectbrokenlinks,ts_links 

 Have a look at https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/Rte/Transformations/CustomApi.html if you need more informations on how to use custom transformations.  
 Here are a few more infomations on how to use custom transformations https://docs.typo3.org/m/typo3/reference-coreapi/7.6/en-us/_sources/Rte/Transformations/CustomApi/Index.rst.txt?refid=transformations-custom&line=25 
 https://github.com/kosimas/TYPO3.CMS/commit/10c8ca4f9ad6fa6f51da09faf12079bca8ed0640

Back