Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: bugfix

Copy Post: Fix issue where backslash characters were being stripped from post content when duplicating a post.
4 changes: 3 additions & 1 deletion projects/plugins/jetpack/modules/copy-post.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ 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 (like from $_POST) and internally calls wp_unslash,
// so we need to slash the data first to preserve backslash characters in the content.
return wp_update_post( wp_slash( $data ) );
}

/**
Expand Down
3 changes: 3 additions & 0 deletions projects/plugins/jetpack/phpunit.11.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
<testsuite name="comment-likes">
<directory suffix="Test.php">tests/php/modules/comment-likes</directory>
</testsuite>
<testsuite name="copy-post">
<directory suffix="Test.php">tests/php/modules/copy-post</directory>
</testsuite>
<testsuite name="likes">
<directory suffix="Test.php">tests/php/modules/likes</directory>
</testsuite>
Expand Down
3 changes: 3 additions & 0 deletions projects/plugins/jetpack/phpunit.9.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
<testsuite name="comment-likes">
<directory suffix="Test.php">tests/php/modules/comment-likes</directory>
</testsuite>
<testsuite name="copy-post">
<directory suffix="Test.php">tests/php/modules/copy-post</directory>
</testsuite>
<testsuite name="likes">
<directory suffix="Test.php">tests/php/modules/likes</directory>
</testsuite>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
<?php
/**
* Tests for the Jetpack_Copy_Post class.
*
* @package automattic/jetpack
*/

use PHPUnit\Framework\Attributes\DataProvider;

require_once JETPACK__PLUGIN_DIR . 'modules/copy-post.php';

/**
* Test class for Jetpack_Copy_Post.
*/
class Copy_Post_Test extends WP_UnitTestCase {
use \Automattic\Jetpack\PHPUnit\WP_UnitTestCase_Fix;

/**
* Test user ID.
*
* @var int
*/
private $user_id;

/**
* Set up.
*/
public function set_up() {
parent::set_up();

// Create an admin user for testing.
$this->user_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
wp_set_current_user( $this->user_id );
}

/**
* Data provider for testing content with backslashes.
*
* @return array
*/
public static function data_content_with_backslashes() {
return array(
'tab escape sequence' => array(
'<!-- wp:code --><pre class="wp-block-code"><code>\t is a tab</code></pre><!-- /wp:code -->',
),
'newline escape sequence' => array(
'<!-- wp:code --><pre class="wp-block-code"><code>\n is a newline</code></pre><!-- /wp:code -->',
),
'form feed escape sequence' => array(
'<!-- wp:code --><pre class="wp-block-code"><code>\f is a form feed</code></pre><!-- /wp:code -->',
),
'double backslash' => array(
'<!-- wp:code --><pre class="wp-block-code"><code>\\\\ becomes \\</code></pre><!-- /wp:code -->',
),
'backslash with number' => array(
'<!-- wp:code --><pre class="wp-block-code"><code>\\9 is backslash 9</code></pre><!-- /wp:code -->',
),
'backslash with space' => array(
'<!-- wp:code --><pre class="wp-block-code"><code>\\ followed by space</code></pre><!-- /wp:code -->',
),
'multiple backslashes' => array(
'<!-- wp:paragraph --><p>Text with \\t and \\n and \\f sequences</p><!-- /wp:paragraph -->',
),
'paragraph with backslash' => array(
'<!-- wp:paragraph --><p>Simple \\t text</p><!-- /wp:paragraph -->',
),
'mixed content with backslashes' => array(
"<!-- wp:paragraph --><p>Path: C:\\\\Users\\\\test</p><!-- /wp:paragraph -->\n<!-- wp:code --><pre class=\"wp-block-code\"><code>echo \"\\n\";</code></pre><!-- /wp:code -->",
),
);
}

/**
* Test that backslash characters are preserved when copying post content.
*
* @dataProvider data_content_with_backslashes
*
* @param string $content Post content to test.
*/
#[DataProvider( 'data_content_with_backslashes' )]
public function test_backslashes_preserved_in_content( $content ) {
// Create a source post with the content containing backslashes.
$source_post_id = $this->factory->post->create(
array(
'post_content' => $content,
'post_title' => 'Test Post with Backslashes',
'post_status' => 'draft',
)
);

$source_post = get_post( $source_post_id );

// Create a target post.
$target_post_id = $this->factory->post->create(
array(
'post_content' => '',
'post_title' => '',
'post_status' => 'auto-draft',
)
);

// Create an instance of Jetpack_Copy_Post and use reflection to call the protected update_content method.
$copy_post = new Jetpack_Copy_Post();

$reflection = new ReflectionClass( $copy_post );
$method = $reflection->getMethod( 'update_content' );
$method->setAccessible( true );

// Call update_content.
$result = $method->invoke( $copy_post, $source_post, $target_post_id );

// Verify the result is successful.
$this->assertGreaterThan( 0, $result, 'wp_update_post should return a post ID on success' );

// Get the updated target post.
$updated_post = get_post( $target_post_id );

// Assert that the backslashes are preserved in the copied content.
$this->assertEquals(
$content,
$updated_post->post_content,
'Backslash characters should be preserved in copied content'
);
}

/**
* Test that backslash characters are preserved in post titles.
*/
public function test_backslashes_preserved_in_title() {
$title = 'Title with \\t and \\n characters';

// Create a source post with a title containing backslashes.
$source_post_id = $this->factory->post->create(
array(
'post_content' => 'Some content',
'post_title' => $title,
'post_status' => 'draft',
)
);

$source_post = get_post( $source_post_id );

// Create a target post.
$target_post_id = $this->factory->post->create(
array(
'post_content' => '',
'post_title' => '',
'post_status' => 'auto-draft',
)
);

$copy_post = new Jetpack_Copy_Post();

$reflection = new ReflectionClass( $copy_post );
$method = $reflection->getMethod( 'update_content' );
$method->setAccessible( true );

$method->invoke( $copy_post, $source_post, $target_post_id );

$updated_post = get_post( $target_post_id );

$this->assertEquals(
$title,
$updated_post->post_title,
'Backslash characters should be preserved in copied title'
);
}

/**
* Test that backslash characters are preserved in post excerpts.
*/
public function test_backslashes_preserved_in_excerpt() {
$excerpt = 'Excerpt with \\t tab and \\n newline';

// Create a source post with an excerpt containing backslashes.
$source_post_id = $this->factory->post->create(
array(
'post_content' => 'Some content',
'post_title' => 'Test Title',
'post_excerpt' => $excerpt,
'post_status' => 'draft',
)
);

$source_post = get_post( $source_post_id );

// Create a target post.
$target_post_id = $this->factory->post->create(
array(
'post_content' => '',
'post_title' => '',
'post_excerpt' => '',
'post_status' => 'auto-draft',
)
);

$copy_post = new Jetpack_Copy_Post();

$reflection = new ReflectionClass( $copy_post );
$method = $reflection->getMethod( 'update_content' );
$method->setAccessible( true );

$method->invoke( $copy_post, $source_post, $target_post_id );

$updated_post = get_post( $target_post_id );

$this->assertEquals(
$excerpt,
$updated_post->post_excerpt,
'Backslash characters should be preserved in copied excerpt'
);
}
}