[BUGFIX] HTMLParser doesn't remove endtag when applying rmTagIfNoAttrib 96/41596/2
authorAlexander Stehlik <alexander.stehlik@gmail.com>
Fri, 17 Jul 2015 19:39:43 +0000 (21:39 +0200)
committerSusanne Moog <typo3@susannemoog.de>
Tue, 21 Jul 2015 06:53:50 +0000 (08:53 +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/41596
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 6e5e39f..8e1a377 100644 (file)
@@ -1367,6 +1367,9 @@ class HtmlParser {
                                        }
                                        unset($tagC['fixAttrib.']);
                                        unset($tagC['fixAttrib']);
+                                       if (isset($tagC['rmTagIfNoAttrib']) && $tagC['rmTagIfNoAttrib'] && empty($tagC['nesting'])) {
+                                               $tagC['nesting'] = 1;
+                                       }
                                        // Candidate for \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge() if integer-keys will some day make trouble...
                                        $keepTags[$key] = array_merge($keepTags[$key], $tagC);
                                }
@@ -1377,6 +1380,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;
                                }
                        }
@@ -1400,6 +1406,9 @@ class HtmlParser {
                                                $keepTags[$tn] = array();
                                        }
                                        $keepTags[$tn]['rmTagIfNoAttrib'] = 1;
+                                       if (empty($keepTags[$tn]['nesting'])) {
+                                               $keepTags[$tn]['nesting'] = 1;
+                                       }
                                }
                        }
                }
index a02e333..45fff70 100644 (file)
@@ -267,4 +267,150 @@ Value 2.2
                $this->assertSame($expected, $result);
        }
 
+       /**
+        * 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));
+       }
+
+       /**
+        * 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->fixture->HTMLparserConfig($tsConfig);
+               return $this->fixture->HTMLcleaner($content, $config[0], $config[1], $config[2], $config[3]);
+       }
 }