diff --git a/projects/plugins/jetpack/changelog/fix-copy-post-backslash-stripping b/projects/plugins/jetpack/changelog/fix-copy-post-backslash-stripping new file mode 100644 index 000000000000..2652ba74c846 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-copy-post-backslash-stripping @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Fix backslashes being stripped from post content when using the Copy Post (Duplicate) feature. diff --git a/projects/plugins/jetpack/modules/copy-post.php b/projects/plugins/jetpack/modules/copy-post.php index 7b36e130b74d..68cadd6c1ae3 100644 --- a/projects/plugins/jetpack/modules/copy-post.php +++ b/projects/plugins/jetpack/modules/copy-post.php @@ -171,7 +171,12 @@ protected function update_content( $source_post, $target_post_id ) { * @param int $target_post_id Target post ID. */ $data = apply_filters( 'jetpack_copy_post_data', $data, $source_post, $target_post_id ); - return wp_update_post( $data ); + + // wp_update_post() expects slashed data when passing an array. + // Since the data comes from get_post() (which returns unslashed data), + // we need to re-slash it to prevent wp_unslash() from stripping + // backslashes and other escape sequences from the content. + return wp_update_post( wp_slash( $data ) ); } /** diff --git a/projects/plugins/jetpack/tests/php/modules/copy-post/Copy_Post_Test.php b/projects/plugins/jetpack/tests/php/modules/copy-post/Copy_Post_Test.php index 7eca0443070a..30db61aa859f 100644 --- a/projects/plugins/jetpack/tests/php/modules/copy-post/Copy_Post_Test.php +++ b/projects/plugins/jetpack/tests/php/modules/copy-post/Copy_Post_Test.php @@ -15,9 +15,11 @@ * * @group copy-post * @covers Jetpack_Copy_Post::copy_footnotes + * @covers Jetpack_Copy_Post::update_content */ #[Group( 'copy-post' )] #[CoversMethod( Jetpack_Copy_Post::class, 'copy_footnotes' )] +#[CoversMethod( Jetpack_Copy_Post::class, 'update_content' )] class Copy_Post_Test extends WP_UnitTestCase { use \Automattic\Jetpack\PHPUnit\WP_UnitTestCase_Fix; @@ -257,4 +259,66 @@ public function test_copy_footnotes_preserves_content() { $this->assertIsArray( $target_footnotes ); $this->assertEquals( $footnote_content, $target_footnotes[0]['content'] ); } + + /** + * Test that backslashes are preserved when copying post content. + * + * @see https://github.com/Automattic/jetpack/issues/46020 + */ + public function test_update_content_preserves_backslashes() { + $copy_post = new Jetpack_Copy_Post(); + + $content_with_backslashes = ' +
\t is a tab
+\n is newline
+\\ is backslash
+';
+
+ $source_post_id = self::factory()->post->create(
+ array(
+ 'post_content' => $content_with_backslashes,
+ )
+ );
+ $source_post = get_post( $source_post_id );
+
+ $target_post_id = self::factory()->post->create();
+
+ // Call update_content which is the method under test.
+ $result = $copy_post->update_content( $source_post, $target_post_id );
+
+ // Assert wp_update_post succeeded.
+ $this->assertNotEquals( 0, $result );
+
+ // Fetch the target post and verify backslashes are preserved.
+ $target_post = get_post( $target_post_id );
+ $this->assertEquals( $content_with_backslashes, $target_post->post_content );
+ }
+
+ /**
+ * Test that update_content preserves content with special escape sequences.
+ *
+ * @see https://github.com/Automattic/jetpack/issues/46020
+ */
+ public function test_update_content_preserves_escape_sequences() {
+ $copy_post = new Jetpack_Copy_Post();
+
+ // Various backslash sequences that should be preserved.
+ $content = '\t \n \f \\ \9 \
'; + + $source_post_id = self::factory()->post->create( + array( + 'post_content' => $content, + ) + ); + $source_post = get_post( $source_post_id ); + + $target_post_id = self::factory()->post->create(); + + $result = $copy_post->update_content( $source_post, $target_post_id ); + + $this->assertNotEquals( 0, $result ); + + $target_post = get_post( $target_post_id ); + $this->assertEquals( $content, $target_post->post_content, 'Backslash escape sequences should be preserved when copying posts.' ); + } }