diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 7bbf11779530ed0ac17eb2af0c7c7b23d215ea3a..6fb8e01105fc509dcf03f9d95a06fe1c1cb64629 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -73,6 +73,8 @@ Drupal 7.0, xxxx-xx-xx (development version) * If your site is being upgraded from Drupal 6 and you do not have the contributed date or event modules installed, user time zone settings will fallback to the system time zone and will have to be reconfigured by each user. +- Filter system: + * Refactored the HTML corrector to take advantage of PHP 5 features. - Removed ping module: * Contributed modules with similar functionality are available. - Refactored the "access rules" component of user module: diff --git a/modules/field/modules/text/text.test b/modules/field/modules/text/text.test index ae8a0a06ad68fb9b410d6d8bf04d14a784c6bbb9..db7c96c7cf125ea7e704432fe0b1ff026151f2bb 100644 --- a/modules/field/modules/text/text.test +++ b/modules/field/modules/text/text.test @@ -286,12 +286,12 @@ class TextSummaryTestCase extends DrupalWebTestCase { // And using a text format WITH the line-break and htmlcorrector filters. $expected_lb = array( "

\nHi\n

\n

\nfolks\n
\n!\n

", - "<", - "

", - "

", - "

", - "

", + "", + "

", + "

", + "

", + "

", + "

", "

\nHi

", "

\nHi

", "

\nHi

", diff --git a/modules/filter/filter.module b/modules/filter/filter.module index 00c4b55684621a2a221031737c97a48157968725..6b578a0a05ba885d383bcfee7b7993f1155252a1 100644 --- a/modules/filter/filter.module +++ b/modules/filter/filter.module @@ -757,74 +757,21 @@ function _filter_url($text, $format) { * Scan input and make sure that all HTML tags are properly closed and nested. */ function _filter_htmlcorrector($text) { - // Prepare tag lists. - static $no_nesting, $single_use; - if (!isset($no_nesting)) { - // Tags which cannot be nested but are typically left unclosed. - $no_nesting = drupal_map_assoc(array('li', 'p')); - - // Single use tags in HTML4 - $single_use = drupal_map_assoc(array('base', 'meta', 'link', 'hr', 'br', 'param', 'img', 'area', 'input', 'col', 'frame')); + // Ignore warnings during HTML soup loading. + $htmlDom = @DOMDocument::loadHTML('' . $text . ''); + + // The result of DOMDocument->saveXML($bodyNode) is a partial (X)HTML document. + // We only need what is inside the body tag. + $bodyNode = $htmlDom->getElementsByTagName('body')->item(0); + if (preg_match("|^]*>(.*)$|s", $htmlDom->saveXML($bodyNode), $matches)) { + $body_content = $matches[1]; + // The XHTML guidelines recommend to include a space before the trailing / + // and > of empty elements for better rendering on HTML user agents. + return preg_replace('|<([^>]*)/>|i', '<$1 />', $body_content); } - - // Properly entify angles. - $text = preg_replace('!<([^a-zA-Z/])!', '<\1', $text); - - // Split tags from text. - $split = preg_split('/<([^>]+?)>/', $text, -1, PREG_SPLIT_DELIM_CAPTURE); - // Note: PHP ensures the array consists of alternating delimiters and literals - // and begins and ends with a literal (inserting $null as required). - - $tag = FALSE; // Odd/even counter. Tag or no tag. - $stack = array(); - $output = ''; - foreach ($split as $value) { - // Process HTML tags. - if ($tag) { - list($tagname) = explode(' ', strtolower($value), 2); - // Closing tag - if ($tagname{0} == '/') { - $tagname = substr($tagname, 1); - // Discard XHTML closing tags for single use tags. - if (!isset($single_use[$tagname])) { - // See if we possibly have a matching opening tag on the stack. - if (in_array($tagname, $stack)) { - // Close other tags lingering first. - do { - $output .= ''; - } while (array_shift($stack) != $tagname); - } - // Otherwise, discard it. - } - } - // Opening tag - else { - // See if we have an identical 'no nesting' tag already open and close it if found. - if (count($stack) && ($stack[0] == $tagname) && isset($no_nesting[$stack[0]])) { - $output .= ''; - } - // Push non-single-use tags onto the stack - if (!isset($single_use[$tagname])) { - array_unshift($stack, $tagname); - } - // Add trailing slash to single-use tags as per X(HT)ML. - else { - $value = rtrim($value, ' /') . ' /'; - } - $output .= '<' . $value . '>'; - } - } - else { - // Passthrough all text. - $output .= $value; - } - $tag = !$tag; - } - // Close remaining tags. - while (count($stack) > 0) { - $output .= ''; + else { + return ''; } - return $output; } /** diff --git a/modules/filter/filter.test b/modules/filter/filter.test index 98834d6e472dd760dd21e1ff9705e86844a5d2af..90ee5e7e12995210644222840e4a128d99f1776f 100644 --- a/modules/filter/filter.test +++ b/modules/filter/filter.test @@ -182,24 +182,20 @@ class FilterAdminTestCase extends DrupalWebTestCase { } } -class FilterTestCase extends DrupalWebTestCase { +/** + * Unit tests for core filters. + */ +class FilterUnitTest extends DrupalWebTestCase { protected $format; public static function getInfo() { return array( 'name' => 'Core filters', - 'description' => 'Filter each filter individually: Convert URLs into links, Convert line breaks, Correct broken HTML, Escape all HTML, Limit allowed HTML tags.', + 'description' => 'Filter each filter individually: convert line breaks, correct broken HTML.', 'group' => 'Filter', ); } - function setUp() { - parent::setUp(); - - $admin_user = $this->drupalCreateUser(array('administer filters', 'create page content')); - $this->drupalLogin($admin_user); - } - /** * Test the line break filter. */ @@ -594,17 +590,17 @@ class FilterTestCase extends DrupalWebTestCase { } /** - * Test the HTML corrector. + * Test the HTML corrector filter. * * @todo This test could really use some validity checking function. */ - function testHtmlCorrector() { + function testHtmlCorrectorFilter() { // Tag closing. $f = _filter_htmlcorrector('

text'); $this->assertEqual($f, '

text

', t('HTML corrector -- tag closing at the end of input.')); $f = _filter_htmlcorrector('

text

text'); - $this->assertEqual($f, '

text

text

', t('HTML corrector -- tag closing.')); + $this->assertEqual($f, '

text

text

', t('HTML corrector -- tag closing.')); $f = _filter_htmlcorrector("