Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/changelog/2595-from-description
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

Preserve newlines inside pre elements when federating content.
21 changes: 21 additions & 0 deletions includes/class-sanitize.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,25 @@ public static function content( $content ) {

return $content;
}

/**
* Strip newlines, carriage returns, and tabs from content while preserving them inside pre elements.
*
* Code blocks require whitespace to be preserved for proper formatting.
*
* @param string $content The content to process.
*
* @return string The trimmed content with whitespace stripped except inside pre elements.
*/
public static function strip_whitespace( $content ) {
return \trim(
\preg_replace_callback(
'/(<pre[^>]*>.*?<\/pre>)|[\n\r\t]+/is',
static function ( $matches ) {
return $matches[1] ?? '';
},
$content
)
);
}
}
2 changes: 1 addition & 1 deletion includes/class-shortcodes.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public static function content( $attributes, $content, $tag ) {
// Replace script and style elements.
$content = \preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $content );
$content = \strip_shortcodes( $content );
$content = \trim( \preg_replace( '/[\n\r\t]/', '', $content ) );
$content = Sanitize::strip_whitespace( $content );

add_shortcode( 'ap_content', array( 'Activitypub\Shortcodes', 'content' ) );

Expand Down
4 changes: 2 additions & 2 deletions includes/transformer/class-post.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Activitypub\Collection\Interactions;
use Activitypub\Collection\Replies;
use Activitypub\Model\Blog;
use Activitypub\Sanitize;
use Activitypub\Shortcodes;

use function Activitypub\esc_hashtag;
Expand Down Expand Up @@ -604,8 +605,7 @@ protected function get_content() {
\wp_reset_postdata();

$content = \wpautop( $content );
$content = \preg_replace( '/[\n\r\t]/', '', $content );
$content = \trim( $content );
$content = Sanitize::strip_whitespace( $content );

// Don't need these anymore, should never appear in a post.
Shortcodes::unregister();
Expand Down
48 changes: 48 additions & 0 deletions tests/phpunit/tests/includes/transformer/class-test-post.php
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,54 @@ public function test_get_post_content_template_with_scenarios( $post_data, $expe
$this->assertSame( $expected_template, $template, $description );
}

/**
* Test that code blocks preserve newlines when content is transformed.
*
* @covers ::get_content
*/
public function test_code_blocks_preserve_newlines() {
$code_content = "function test() {\n\treturn true;\n}";
$post = self::factory()->post->create_and_get(
array(
'post_title' => 'Code Example',
'post_content' => '<pre>' . $code_content . '</pre>',
'post_status' => 'publish',
)
);

$object = Post::transform( $post )->to_object();
$content = $object->get_content();

// The pre block content should preserve newlines and tabs.
$this->assertStringContainsString( '<pre>', $content, 'Content should contain pre tag' );
$this->assertStringContainsString( "\n", $content, 'Content should preserve newlines inside pre blocks' );
$this->assertStringContainsString( "\t", $content, 'Content should preserve tabs inside pre blocks' );
}

/**
* Test that regular content has whitespace stripped while code blocks are preserved.
*
* @covers ::get_content
*/
public function test_mixed_content_preserves_code_blocks() {
$post = self::factory()->post->create_and_get(
array(
'post_title' => 'Mixed Content',
'post_content' => "<p>Regular paragraph</p>\n\n<pre class=\"code\">line1\nline2\nline3</pre>\n\n<p>Another paragraph</p>",
'post_status' => 'publish',
)
);

$object = Post::transform( $post )->to_object();
$content = $object->get_content();

// Pre block should preserve newlines.
$this->assertStringContainsString( "line1\nline2\nline3", $content, 'Pre block should preserve newlines' );

// Regular paragraphs should be joined without extra newlines.
$this->assertStringNotContainsString( "</p>\n\n<p>", $content, 'Paragraphs should not have newlines between them' );
}

/**
* Data provider for get_post_content_template tests with various scenarios.
*
Expand Down