Points
28
Solutions
0
When a user types a URL into the publisher and presses the spacebar, a literal </p> ends up showing
in the rendered post
Files to modify
Two locations contain the same buggy regex — both must be fixed:
- includes/traits/publisher.php:742 — used when a post is freshly published via publisher().
- includes/traits/posts.php:452 — used when an existing post is fetched via get_post().
Change
Replace the single-line regex at both sites with a small block that:
1. Collapses empty Quill paragraphs (<p></p> and <p><br></p>) into a newline.
2. Converts paragraph breaks (</p><p>) into a newline.
3. Strips any remaining <p> / </p> tags.
4. Trims trailing whitespace.
Newlines are preserved deliberately — parse() (via includes/traits/publisher.php:986) calls
nl2br($text) near the end (functions.php), so multi-paragraph posts continue to render with <br>
line breaks.
includes/traits/publisher.php:741-742
includes/traits/posts.php:451-452
With the same block as above (note this site is one indent level deeper because it sits inside the
if ($post['can_get_details']) branch).
Why this approach
- The fix stays in the read path (already where stripping happens), so existing posts saved with the
orphan tag are auto-cleaned next render — no migration needed.
- It does not touch the JS submit path, Quill config, or secure() / DB encoding — the surface area
is minimal.
- Newlines flow through the existing nl2br() step in parse(), so multi-paragraph posts keep working
without any new logic downstream.
in the rendered post
Files to modify
Two locations contain the same buggy regex — both must be fixed:
- includes/traits/publisher.php:742 — used when a post is freshly published via publisher().
- includes/traits/posts.php:452 — used when an existing post is fetched via get_post().
Change
Replace the single-line regex at both sites with a small block that:
1. Collapses empty Quill paragraphs (<p></p> and <p><br></p>) into a newline.
2. Converts paragraph breaks (</p><p>) into a newline.
3. Strips any remaining <p> / </p> tags.
4. Trims trailing whitespace.
Newlines are preserved deliberately — parse() (via includes/traits/publisher.php:986) calls
nl2br($text) near the end (functions.php), so multi-paragraph posts continue to render with <br>
line breaks.
includes/traits/publisher.php:741-742
PHP:
Replace:
$post['text_html'] = ($post['text_raw']) ? htmlspecialchars_decode($post['text_raw'], ENT_QUOTES) :
$post['text_raw'];
$post['text_html'] = ($post['text_html']) ? preg_replace('/^<p>|<\/p>$/', '', $post['text_html']) :
$post['text_html'];
With:
/* parse text */
$post['text_raw'] = $post['text'];
$post['text_html'] = ($post['text_raw']) ? htmlspecialchars_decode($post['text_raw'], ENT_QUOTES) :
$post['text_raw'];
if ($post['text_html']) {
// collapse Quill empty paragraphs and paragraph breaks; paragraph borders become \n\n so nl2br() renders a paragraph break, not a single line break
$post['text_html'] = preg_replace('/<p[^>]*>\s*(<br\s*\/?>)?\s*<\/p>/i', "\n",
$post['text_html']);
$post['text_html'] = preg_replace('/<\/p>\s*<p[^>]*>/i', "\n\n", $post['text_html']);
$post['text_html'] = preg_replace('/<\/?p[^>]*>/i', '', $post['text_html']);
$post['text_html'] = trim($post['text_html']);
}
includes/traits/posts.php:451-452
PHP:
Replace:
$post['text_html'] = ($post['text_raw']) ? htmlspecialchars_decode($post['text_raw'], ENT_QUOTES) :
$post['text_raw'];
$post['text_html'] = ($post['text_html']) ? preg_replace('/^<p>|<\/p>$/', '', $post['text_html']) :
$post['text_html'];
With:
/* parse text */
$post['text_raw'] = $post['text'];
$post['text_html'] = ($post['text_raw']) ? htmlspecialchars_decode($post['text_raw'], ENT_QUOTES) : $post['text_raw'];
if ($post['text_html']) {
// collapse Quill empty paragraphs and paragraph breaks; paragraph borders become \n\n so nl2br() renders a paragraph break, not a single line break
$post['text_html'] = preg_replace('/<p[^>]*>\s*(<br\s*\/?>)?\s*<\/p>/i', "\n", $post['text_html']);
$post['text_html'] = preg_replace('/<\/p>\s*<p[^>]*>/i', "\n\n", $post['text_html']);
$post['text_html'] = preg_replace('/<\/?p[^>]*>/i', '', $post['text_html']);
$post['text_html'] = trim($post['text_html']);
}
With the same block as above (note this site is one indent level deeper because it sits inside the
if ($post['can_get_details']) branch).
Why this approach
- The fix stays in the read path (already where stripping happens), so existing posts saved with the
orphan tag are auto-cleaned next render — no migration needed.
- It does not touch the JS submit path, Quill config, or secure() / DB encoding — the surface area
is minimal.
- Newlines flow through the existing nl2br() step in parse(), so multi-paragraph posts keep working
without any new logic downstream.