diff --git a/_config/html.yml b/_config/html.yml index 83b30d3db..1beef739c 100644 --- a/_config/html.yml +++ b/_config/html.yml @@ -2,10 +2,6 @@ Name: corehtml --- SilverStripe\Core\Injector\Injector: - SilverStripe\View\Parsers\HTMLValue: - class: SilverStripe\View\Parsers\HTML4Value - # Shorthand - HTMLValue: '%$SilverStripe\View\Parsers\HTMLValue' SilverStripe\Forms\HTMLEditor\HTMLEditorConfig: class: SilverStripe\Forms\HTMLEditor\TinyMCEConfig SilverStripe\Forms\HTMLEditor\TinyMCEScriptGenerator: '%$SilverStripe\Forms\HTMLEditor\TinyMCECombinedGenerator' diff --git a/composer.json b/composer.json index 04433a977..5fec802cf 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "embed/embed": "^4.4.7", "league/csv": "^9.8.0", "m1/env": "^2.2.0", + "masterminds/html5": "^2.7", "monolog/monolog": "^3.2.0", "nikic/php-parser": "^4.15.0", "psr/container": "^2.0", diff --git a/src/View/HTML.php b/src/View/HTML.php index 703a90ed0..cd42f219d 100644 --- a/src/View/HTML.php +++ b/src/View/HTML.php @@ -82,7 +82,7 @@ class HTML if ($content) { throw new InvalidArgumentException("Void element \"{$tag}\" cannot have content"); } - return "<{$tag}{$preparedAttributes} />"; + return "<{$tag}{$preparedAttributes}>"; } // Closed tag type diff --git a/src/View/Parsers/HTML4Value.php b/src/View/Parsers/HTML4Value.php deleted file mode 100644 index 528802df9..000000000 --- a/src/View/Parsers/HTML4Value.php +++ /dev/null @@ -1,31 +0,0 @@ -isValid()) { - $this->setDocument(null); - } - - $errorState = libxml_use_internal_errors(true); - $result = $this->getDocument()->loadHTML( - '
' . "$content" - ); - libxml_clear_errors(); - libxml_use_internal_errors($errorState); - return $result; - } -} diff --git a/src/View/Parsers/HTMLValue.php b/src/View/Parsers/HTMLValue.php index 06ed6df34..ee44ec142 100644 --- a/src/View/Parsers/HTMLValue.php +++ b/src/View/Parsers/HTMLValue.php @@ -4,22 +4,20 @@ namespace SilverStripe\View\Parsers; use SilverStripe\Core\Convert; use SilverStripe\View\ViewableData; +use Masterminds\HTML5; use DOMNodeList; use DOMXPath; use DOMDocument; +use SilverStripe\View\HTML; /** * This class handles the converting of HTML fragments between a string and a DOMDocument based * representation. * - * It's designed to allow dependency injection to replace the standard HTML4 version with one that - * handles XHTML or HTML5 instead - * * @mixin DOMDocument */ -abstract class HTMLValue extends ViewableData +class HTMLValue extends ViewableData { - public function __construct($fragment = null) { if ($fragment) { @@ -28,7 +26,25 @@ abstract class HTMLValue extends ViewableData parent::__construct(); } - abstract public function setContent($fragment); + /** + * @param string $content + * @return bool + */ + public function setContent($content) + { + $content = preg_replace('#?(html|head|body)[^>]*>#si', '', $content); + $html5 = new HTML5(['disable_html_ns' => true]); + $document = $html5->loadHTML( + '' . + "$content" + ); + if ($document) { + $this->setDocument($document); + return true; + } + $this->valid = false; + return false; + } /** * @return string diff --git a/src/View/Parsers/ShortcodeParser.php b/src/View/Parsers/ShortcodeParser.php index 41caf4c52..ed1cb858d 100644 --- a/src/View/Parsers/ShortcodeParser.php +++ b/src/View/Parsers/ShortcodeParser.php @@ -658,8 +658,8 @@ class ShortcodeParser // use a proper DOM list($content, $tags) = $this->replaceElementTagsWithMarkers($content); - /** @var HTMLValue $htmlvalue */ - $htmlvalue = Injector::inst()->create('HTMLValue', $content); + /** @var HTMLValue $htmlvalue */ + $htmlvalue = Injector::inst()->create(HTMLValue::class, $content); // Now parse the result into a DOM if (!$htmlvalue->isValid()) { diff --git a/tests/php/View/HTMLTest.php b/tests/php/View/HTMLTest.php index 9801e831f..362ec417f 100644 --- a/tests/php/View/HTMLTest.php +++ b/tests/php/View/HTMLTest.php @@ -14,7 +14,7 @@ class HTMLTest extends SapphireTest 'name' => 'description', 'content' => 'test tag', ]); - $this->assertEquals('', $tag); + $this->assertEquals('', $tag); } public function testEmptyAttributes() @@ -27,7 +27,7 @@ class HTMLTest extends SapphireTest 'disabled' => false, 'readonly' => true, ]); - $this->assertEquals('', $tag); + $this->assertEquals('', $tag); } public function testNormalTag() @@ -52,7 +52,7 @@ class HTMLTest extends SapphireTest 'alt' => '', ]); - $this->assertEquals('', $tag); + $this->assertEquals('', $tag); } public function testVoidContentError() diff --git a/tests/php/View/Parsers/HTML4ValueTest.php b/tests/php/View/Parsers/HTML4ValueTest.php deleted file mode 100644 index 833b10047..000000000 --- a/tests/php/View/Parsers/HTML4ValueTest.php +++ /dev/null @@ -1,98 +0,0 @@ -Enclosed Value' - => 'Enclosed Value
', - '' - => '', - '' - => '', - 'ö ß ā い 家
'); - $this->assertEquals('ö ß ā い 家
', $value->getContent()); - } - - public function testInvalidHTMLTagNames() - { - $value = new HTML4Value(); - - $invalid = [ - '', - 'paragraph
\nparagraph
\nThis is valid
'); - $this->assertEquals('This is valid
', $value->getContent(), "Valid content is returned"); - - $value->setContent('valid is false - // for instance if a content editor saves something really weird in a LiteralField - // we can manually get to this state via ->setInvalid() - $value->setInvalid(); - $this->assertEquals('', $value->getContent(), "Blank string is returned when invalid"); - } -} diff --git a/tests/php/View/Parsers/HTMLValueTest.php b/tests/php/View/Parsers/HTMLValueTest.php new file mode 100644 index 000000000..7ce889dd7 --- /dev/null +++ b/tests/php/View/Parsers/HTMLValueTest.php @@ -0,0 +1,163 @@ +Enclosed Value
a' => '
Enclosed Value
a
', + '' => '', + '' => '
', + 'ö ß ā い 家
'); + $this->assertEquals('ö ß ā い 家
', $value->getContent()); + } + + public function testWhitespaceHandling() + { + $value = new HTMLValue(); + + $value->setContent(' '); + $this->assertEquals(' ', $value->getContent()); + } + + public function testInvalidHTMLTagNames() + { + $value = new HTMLValue(); + + $invalid = [ + '', + 'paragraph
\nparagraph
\nSome content with a [test_shortcode] and a
followed by an
Some content with a bit of test shortcode output and a
followed by an