[BUGFIX] HTMLParser doesn't remove endtag when applying rmTagIfNoAttrib 81/22681/7
authorAlexander Stehlik <alexander.stehlik@gmail.com>
Thu, 16 Jul 2015 18:43:45 +0000 (20:43 +0200)
committerSusanne Moog <typo3@susannemoog.de>
Fri, 17 Jul 2015 09:46:29 +0000 (11:46 +0200)
Solution: Force nesting = 1 when rmTagIfNoAttrib is set on a tag.

Releases: master, 6.2
Resolves: #22871
Resolves: #50144
Change-Id: Ibdadf07d80591588c0698ed322905cc0be45fe7e
Reviewed-on: http://review.typo3.org/22681
Reviewed-by: Markus Sommer <markussom@posteo.de>
Tested-by: Markus Sommer <markussom@posteo.de>
Reviewed-by: Susanne Moog <typo3@susannemoog.de>
Tested-by: Susanne Moog <typo3@susannemoog.de>
typo3/sysext/core/Classes/Html/HtmlParser.php
typo3/sysext/core/Tests/Unit/Html/HtmlParserTest.php

index de8cada..09237af 100644 (file)
@@ -1356,6 +1356,9 @@ class HtmlParser {
                                        }
                                        unset($tagC['fixAttrib.']);
                                        unset($tagC['fixAttrib']);
+                                       if (isset($tagC['rmTagIfNoAttrib']) && $tagC['rmTagIfNoAttrib'] && empty($tagC['nesting'])) {
+                                               $tagC['nesting'] = 1;
+                                       }
                                        $keepTags[$key] = array_merge($keepTags[$key], $tagC);
                                }
                        }
@@ -1365,6 +1368,9 @@ class HtmlParser {
                        $lN = GeneralUtility::trimExplode(',', strtolower($TSconfig['localNesting']), TRUE);
                        foreach ($lN as $tn) {
                                if (isset($keepTags[$tn])) {
+                                       if (!is_array($keepTags[$tn])) {
+                                               $keepTags[$tn] = array();
+                                       }
                                        $keepTags[$tn]['nesting'] = 1;
                                }
                        }
@@ -1388,6 +1394,9 @@ class HtmlParser {
                                                $keepTags[$tn] = array();
                                        }
                                        $keepTags[$tn]['rmTagIfNoAttrib'] = 1;
+                                       if (empty($keepTags[$tn]['nesting'])) {
+                                               $keepTags[$tn]['nesting'] = 1;
+                                       }
                                }
                        }
                }
index dd38587..6417135 100644 (file)
@@ -266,6 +266,141 @@ Value 2.2
        }
 
        /**
+        * Data provider for spanTagCorrectlyRemovedWhenRmTagIfNoAttribIsConfigured
+        */
+       public static function spanTagCorrectlyRemovedWhenRmTagIfNoAttribIsConfiguredDataProvider() {
+               return array(
+                       'Span tag with no attrib' => array(
+                               '<span>text</span>',
+                               'text'
+                       ),
+                       'Span tag with allowed id attrib' => array(
+                               '<span id="id">text</span>',
+                               '<span id="id">text</span>'
+                       ),
+                       'Span tag with disallowed style attrib' => array(
+                               '<span style="line-height: 12px;">text</span>',
+                               'text'
+                       )
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider spanTagCorrectlyRemovedWhenRmTagIfNoAttribIsConfiguredDataProvider
+        */
+       public function tagCorrectlyRemovedWhenRmTagIfNoAttribIsConfigured($content, $expectedResult) {
+               $tsConfig = array(
+                       'allowTags' => 'span',
+                       'tags.' => array(
+                               'span.' => array(
+                                       'allowedAttribs' => 'id',
+                                       'rmTagIfNoAttrib' => 1
+                               )
+                       )
+               );
+               $this->assertEquals($expectedResult, $this->parseConfigAndCleanHtml($tsConfig, $content));
+       }
+
+       /**
+        * @test
+        */
+       public function rmTagIfNoAttribIsConfiguredDoesNotChangeNestingType() {
+               $tsConfig = array(
+                       'allowTags' => 'div,span',
+                       'rmTagIfNoAttrib' => 'span',
+                       'globalNesting' => 'div,span'
+               );
+               $content = '<span></span><span id="test"><div></span></div>';
+               $expectedResult = '<span id="test"></span>';
+               $this->assertEquals($expectedResult, $this->parseConfigAndCleanHtml($tsConfig, $content));
+       }
+
+       /**
+        * Data provider for localNestingCorrectlyRemovesInvalidTags
+        */
+       public static function localNestingCorrectlyRemovesInvalidTagsDataProvider() {
+               return array(
+                       'Valid nesting is untouched' => array(
+                               '<B><I></B></I>',
+                               '<B><I></B></I>'
+                       ),
+                       'Valid nesting with content is untouched' => array(
+                               'testa<B>test1<I>test2</B>test3</I>testb',
+                               'testa<B>test1<I>test2</B>test3</I>testb'
+                       ),
+                       'Superflous tags are removed' => array(
+                               '</B><B><I></B></I></B>',
+                               '<B><I></B></I>'
+                       ),
+                       'Superflous tags with content are removed' => array(
+                               'test1</B>test2<B>test3<I>test4</B>test5</I>test6</B>test7',
+                               'test1test2<B>test3<I>test4</B>test5</I>test6test7'
+                       ),
+                       'Another valid nesting test' => array(
+                               '<span><div></span></div>',
+                               '<span><div></span></div>',
+                       ),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider localNestingCorrectlyRemovesInvalidTagsDataProvider
+        * @param string $content
+        * @param string $expectedResult
+        */
+       public function localNestingCorrectlyRemovesInvalidTags($content, $expectedResult) {
+               $tsConfig = array(
+                       'allowTags' => 'div,span,b,i',
+                       'localNesting' => 'div,span,b,i',
+               );
+               $this->assertEquals($expectedResult, $this->parseConfigAndCleanHtml($tsConfig, $content));
+       }
+
+       /**
+        * Data provider for globalNestingCorrectlyRemovesInvalidTags
+        */
+       public static function globalNestingCorrectlyRemovesInvalidTagsDataProvider() {
+               return array(
+                       'Valid nesting is untouched' => array(
+                               '<B><I></I></B>',
+                               '<B><I></I></B>'
+                       ),
+                       'Valid nesting with content is untouched' => array(
+                               'testa<B>test1<I>test2</I>test3</B>testb',
+                               'testa<B>test1<I>test2</I>test3</B>testb'
+                       ),
+                       'Invalid nesting is cleaned' => array(
+                               '</B><B><I></B></I></B>',
+                               '<B></B>'
+                       ),
+                       'Invalid nesting with content is cleaned' => array(
+                               'test1</B>test2<B>test3<I>test4</B>test5</I>test6</B>test7',
+                               'test1test2<B>test3test4</B>test5test6test7'
+                       ),
+                       'Another invalid nesting test' => array(
+                               '<span><div></span></div>',
+                               '<span></span>',
+                       ),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider globalNestingCorrectlyRemovesInvalidTagsDataProvider
+        * @param string $content
+        * @param string $expectedResult
+        */
+       public function globalNestingCorrectlyRemovesInvalidTags($content, $expectedResult) {
+               $tsConfig = array(
+                       'allowTags' => 'span,div,b,i',
+                       'globalNesting' => 'span,div,b,i',
+               );
+               $this->assertEquals($expectedResult, $this->parseConfigAndCleanHtml($tsConfig, $content));
+       }
+
+       /**
         * @return array
         */
        public function emptyTagsDataProvider() {
@@ -308,8 +443,20 @@ Value 2.2
                                'treatNonBreakingSpaceAsEmpty' => $treatNonBreakingSpaceAsEmpty
                        ),
                );
-               $config = $this->subject->HTMLparserConfig($tsConfig);
-               $result = $this->subject->HTMLcleaner($content, $config[0], $config[1], $config[2], $config[3]);
+
+               $result = $this->parseConfigAndCleanHtml($tsConfig, $content);
                $this->assertEquals($expectedResult, $result);
        }
+
+       /**
+        * Calls HTMLparserConfig() and passes the generated config to the HTMLcleaner() method on the current subject.
+        *
+        * @param array $tsConfig The TypoScript that should be used to generate the HTML parser config.
+        * @param string $content The content that should be parsed by the HTMLcleaner.
+        * @return string The parsed content.
+        */
+       protected function parseConfigAndCleanHtml(array $tsConfig, $content) {
+               $config = $this->subject->HTMLparserConfig($tsConfig);
+               return $this->subject->HTMLcleaner($content, $config[0], $config[1], $config[2], $config[3]);
+       }
 }