Sometimes, you want to code the <p>...</p> yourself, because you need to add a class or another attribute to the <p>.

Consider the following valid HTML:

<blockquote class="quote"><p>“The remedy for what ails our democracy is not simply better education (as important as that is) or civic education (as important as that can be), but the reestablishment of a genuine democratic discourse in which individuals can participate in a meaningful way – a conversation of democracy in which meritorious ideas and opinions from individuals do, in fact, evoke a meaningful response.”</p>
<p class="author">Al Gore, The Assault on Reason</p>
</blockquote>

The Drupal filter will add a superfluous </p> just before the <blockquote>:

<blockquote class="quote"><p>“The remedy for what ails our democracy is not simply better education (as important as that is) or civic education (as important as that can be), but the reestablishment of a genuine democratic discourse in which individuals can participate in a meaningful way – a conversation of democracy in which meritorious ideas and opinions from individuals do, in fact, evoke a meaningful response.”</p>
<p class="author">Al Gore, The Assault on Reason</p>
</p></blockquote>

The same happens if we put the <blockquote> on the same line as the last </p>.

The temporary fix is NOT to write the last </p> since Drupal will add one.
But
1) it forces the user to enter invalid html
2) when this bug gets fixed, all such nodes would have to be updated again to fix this.

Tested on latest HEAD code (Drupal 6).

CommentFileSizeAuthor
#2 177139.one_.p.too_.many_.blockquote.patch964 bytesbeginner

Comments

beginner’s picture

Version: 6.0-beta1 » 6.x-dev

The problem is in function _filter_autop($text).
When I comment out this line, the problem disappears:

$chunk = str_replace('</blockquote></p>', '</p></blockquote>', $chunk);

To reproduce:

The code in question is unchanged between D5 and D6, so you can test with either.
Create a node, select the 'php' input format, and insert the following code in the node:


$text = "<blockquote class=\"quote\"><p>Something</p>
<p class=\"author\">Al Gore, The Assault on Reason</p>
</blockquote>";

$text2 = str_replace('<', '&lt;' ,$text);
$text2 = str_replace('>', '&gt;', $text2);
echo "Before:<br/><pre>";
echo $text2;
echo "</pre>";

$text = _filter_autop($text);
$text2 = str_replace('<', '&lt;' ,$text);
$text2 = str_replace('>', '&gt;', $text2);
echo "After:<br/><pre>";
echo $text2;
echo "</pre>";

comment and uncomment the said php line, and see the difference.

beginner’s picture

Status: Active » Needs review
StatusFileSize
new964 bytes

See this chunck of code:

      $chunk = preg_replace('|\n*$|', '', $chunk) ."\n\n"; // just to make things a little easier, pad the end
      $chunk = preg_replace('|<br />\s*<br />|', "\n\n", $chunk);
      $chunk = preg_replace('!(<'. $block .'[^>]*>)!', "\n$1", $chunk); // Space things out a little
      $chunk = preg_replace('!(</'. $block .'>)!', "$1\n\n", $chunk); // Space things out a little
      $chunk = preg_replace("/\n\n+/", "\n\n", $chunk); // take care of duplicates
      $chunk = preg_replace('/\n?(.+?)(?:\n\s*\n|\z)/s', "<p>$1</p>\n", $chunk); // make paragraphs, including one at the end
      $chunk = preg_replace('|<p>\s*</p>\n|', '', $chunk); // under certain strange conditions it could create a P of entirely whitespace
      $chunk = preg_replace("|<p>(<li.+?)</p>|", "$1", $chunk); // problem with nested lists
      $chunk = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $chunk);
// CHECK THE NEXT TWO LINES
      $chunk = str_replace('</blockquote></p>', '</p></blockquote>', $chunk); // ------------------------------> This line is the troublemaker.
      $chunk = str_replace('<p></p>', '', $chunk); // --------------------------------------------------------------------> ADDED THIS LINE. 
      $chunk = preg_replace('!<p>\s*(</?'. $block .'[^>]*>)!', "$1", $chunk);
      $chunk = preg_replace('!(</?'. $block .'[^>]*>)\s*</p>!', "$1", $chunk);
      $chunk = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $chunk); // make line breaks
      $chunk = preg_replace('!(</?'. $block .'[^>]*>)\s*<br />!', "$1", $chunk);
      $chunk = preg_replace('!<br />(\s*</?(?:p|li|div|th|pre|td|ul|ol)>)!', '$1', $chunk);
      $chunk = preg_replace('/&([^#])(?![A-Za-z0-9]{1,8};)/', '&amp;$1', $chunk);

We start with the following text:

<blockquote class="quote"><p>Something</p>
<p class="author">Al Gore, The Assault on Reason</p>
</blockquote>

The closing blockquote can be on a separate line or at the end of the previous line. It doesn't seem to matter.

Before the troublemaking line is executed, we text has become thus:

<blockquote class="quote"><p>
<p>Something</p></p>
<p><p class="author">Al Gore, The Assault on Reason</p></p>
<p></blockquote></p>

I.e. plenty of <p>'s and </p>'s have been added.

The troublemaker changes the text to:

<blockquote class="quote"><p>
<p>Something</p></p>
<p><p class="author">Al Gore, The Assault on Reason</p></p>
<p></p></blockquote>

I.e. it prepares the ground for the rest of the of the code to remove the redundant p's.

The problem is that in this situation, the current code misses one /p, thus:

<blockquote class="quote"><p>Something</p>
<p class="author">Al Gore, The Assault on Reason</p>
</p></blockquote>

Rather than removing the troublemaking line which must be there for a reason, we add a str_replace() to remove code that is bound to be useless in every circumstances.

The added line removes the empty pair of <p></p> and solves the problem.

<blockquote class="quote"><p>Something</p>
<p class="author">Al Gore, The Assault on Reason</p>
</blockquote>
dries’s picture

Removing the blockquote line doesn't seem to be an option; it would render the generated HTML code invalid as the paragraph and blockquote tags would be unbalanced.

The line that you added in #2 already exists higher up (only slightly more generic):
$chunk = preg_replace('|<p>\s*</p>\n|', '', $chunk); // under certain strange conditions it could create a P of entirely whitespace
Would it be sane to move that line down?

Either way, even if you move that line down, the generated HTML code is sub-optimal -- or am I wrong?

It's not quite clear why blockquote needs special treatment. I'm left wondering if we could remove both:

      $chunk = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $chunk);
      $chunk = str_replace('</blockquote></p>', '</p></blockquote>', $chunk); //
beginner’s picture

I tried moving the more generic line down to where I added the new line as you suggested, but it doesn't work. I still end up with the extraneous /p.

I don't know what you mean by the generated HTML code being sub-optimal . Without the patch I have invalid html but with it the html validates.

The blockquote lines must be there for a reason. It would be foolhardy to remove them and expect no to break anything. Actually, they seem to be here to ensure that p's are nested within blockquote and not the other way around. <p><blockquote></blockquote></p> is not valid.

Having tried some more, I can state:
1) the patch fixes a validation bug.
2) the patch is so simple (removing an empty paragraph) that it cannot break anything else.

The only question in my mind is: is it worth to add a comment, with a reference to this issue?

beginner’s picture

Status: Needs review » Reviewed & tested by the community

- the patch is trivial
- it can't possibly break anything
- it fixes a bug
- alternative methods suggested have more chances to break something. Anyway: they have been tried but they failed.

Ergo: RTBC.

gábor hojtsy’s picture

Status: Reviewed & tested by the community » Needs review

Please do not RTBC your own patches. Get this reviewed and tested by a few people. Automated filter correction is not a trivial thing for sure, so this needs to be tested in some use cases.

beginner’s picture

It is said in many places and by many people that in case of a very trivial patch, it is ok to RTBC one's own patch.
The rest of your comment also strongly suggests that you didn't even have a look at the patch :-/

gábor hojtsy’s picture

I did look at the patch, but reading what Dries had to say, it does not seem to be right to RTBC this without a wider review in the community. As I said, automated HTML filtering is a tricky thing.

beginner’s picture

Version: 6.x-dev » 7.x-dev

1) I replied to Dries's concerns.
2) how tricky can removing an empty paragraph (<p></p>) be??

chx’s picture

beginner, are you sure that removing

is harmless? I do not know, for example, can't the user enter that for CSS purposes?

beginner’s picture

The patch only removed a completely empty <p></p>.
If someone uses an empty P for CSS purposes, then adding a space would do the trick: <p> </p>.

Does this mean that for this one liner to get committed, we need to create an upgrade function system_update_7009(), searching the DB for <p></p> and replacing all instances with <p> </p>?

robin monks’s picture

I know I have added empty paragraphs to my posts before. -1 for this solution.

Robin

catch’s picture

Status: Needs review » Closed (won't fix)

I think this is won't fix. Or at least it should come with a test.

beginner’s picture

Status: Closed (won't fix) » Needs review

I don't see why a real bug should be won't fixed.

catch’s picture

Status: Needs review » Needs work

Sorry, I should've read further up the issue, the extra </p> is indeed an issue. However I'd rather see if we can't get rid of the unclosed tag, rather than changing the behaviour of the filter as in the current patch. Another option would be for the htmlcorrector filter to handle extraneous closing tags more generally (afaik it only cleans unclosed tags).

dries’s picture

It looks like this might be a duplicate of #212236: Automatic line breaking sometimes results in an unpaired end of paragraph tag which I'm about to commit to CVS HEAD. Please confirm, and update the status accordingly. Thanks.

catch’s picture

Status: Needs work » Postponed (maintainer needs more info)

I'm pretty sure this bug was fixed today by #212236: Automatic line breaking sometimes results in an unpaired end of paragraph tag but if someone could confirm this would be great before marking it as duplicate.

catch’s picture

Missed Dries' post which says the same thing. Leaving status as is though.

wrwrwr’s picture

Tested the example from the description and it validates for me with Drupal 5, 6 & 7 now (with #212236 patches applied).

damien tournoud’s picture

Status: Postponed (maintainer needs more info) » Closed (duplicate)