Skip to content
Open
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
5 changes: 5 additions & 0 deletions src/wp-admin/includes/post.php
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,11 @@ function edit_post( $post_data = null ) {
}
}

// Infer the password visibility when the caller did not send one of its own.
if ( ! isset( $post_data['visibility'] ) && ! empty( $post_data['post_password'] ) ) {
$post_data['visibility'] = 'password';
}

if ( isset( $post_data['visibility'] ) ) {
switch ( $post_data['visibility'] ) {
case 'public':
Expand Down
197 changes: 197 additions & 0 deletions tests/phpunit/tests/admin/includesPost.php
Original file line number Diff line number Diff line change
Expand Up @@ -1371,4 +1371,201 @@ public function test_user_get_refreshed_metabox_nonce() {
$this->assertNotEmpty( $response['wp-refresh-metabox-loader-nonces']['replace']['_wpnonce'] );
$this->assertNotEmpty( $response['wp-refresh-metabox-loader-nonces']['replace']['metabox_loader_nonce'] );
}

/**
* Ensures that giving a sticky post a password through edit_post() unsticks it,
* even when the caller never sends an explicit `visibility` value.
*
* The block editor sends `visibility`, so `edit_post()` reaches the
* `case 'password'` branch and drops the `sticky` flag. Quick Edit never sends
* it, so a post could end up both sticky and password protected, a combination
* the editor itself forbids.
*
* @ticket 64810
*
* @covers ::edit_post
*/
public function test_edit_post_unsticks_a_post_when_a_password_is_set_without_visibility() {
wp_set_current_user( self::$admin_id );

$post_id = self::factory()->post->create(
array(
'post_status' => 'publish',
'post_author' => self::$admin_id,
)
);

stick_post( $post_id );
$this->assertTrue( is_sticky( $post_id ), 'The post was not sticky to begin with: check test setup.' );

// Mirrors Quick Edit, which posts `sticky` but no `visibility`.
edit_post(
array(
'post_ID' => $post_id,
'post_title' => 'Protected and sticky',
'post_status' => 'publish',
'post_password' => 'secret',
'sticky' => 'sticky',
)
);

$this->assertSame(
'secret',
get_post( $post_id )->post_password,
'The password was not saved: check test setup.'
);
$this->assertFalse(
is_sticky( $post_id ),
'A password protected post was left sticky when no explicit visibility was sent.'
);
}

/**
* Ensures that an explicit `public` visibility clears any existing post password.
*
* @ticket 64810
*
* @covers ::edit_post
*/
public function test_edit_post_clears_the_password_when_visibility_is_public() {
wp_set_current_user( self::$admin_id );

$post_id = self::factory()->post->create(
array(
'post_status' => 'publish',
'post_author' => self::$admin_id,
'post_password' => 'secret',
)
);

edit_post(
array(
'post_ID' => $post_id,
'post_title' => 'Now public',
'post_status' => 'publish',
'post_password' => '',
'visibility' => 'public',
)
);

$this->assertSame( '', get_post( $post_id )->post_password );
}

/**
* Ensures that an explicit `password` visibility unsticks the post.
*
* The companion test above covers the same branch reached through an
* inferred visibility; this one covers a caller that sends it outright.
*
* @ticket 64810
*
* @covers ::edit_post
*/
public function test_edit_post_unsticks_the_post_when_visibility_is_password() {
wp_set_current_user( self::$admin_id );

$post_id = self::factory()->post->create(
array(
'post_status' => 'publish',
'post_author' => self::$admin_id,
)
);

stick_post( $post_id );

edit_post(
array(
'post_ID' => $post_id,
'post_title' => 'Protected',
'post_status' => 'publish',
'post_password' => 'secret',
'sticky' => 'sticky',
'visibility' => 'password',
)
);

$this->assertSame( 'secret', get_post( $post_id )->post_password, 'The password was not saved: check test setup.' );
$this->assertFalse( is_sticky( $post_id ), 'A password protected post was left sticky.' );
}

/**
* Ensures that an explicit `private` visibility privatises the post, drops any
* password and unsticks it.
*
* The password is sent alongside on purpose. Quick Edit posts
* `visibility => 'private'` whenever "Private" is ticked, so this pair is
* reachable from the UI, and inferring `password` from the password instead
* would leave the post public with its password intact.
*
* @ticket 64810
*
* @covers ::edit_post
*/
public function test_edit_post_privatises_and_unsticks_the_post_when_visibility_is_private() {
wp_set_current_user( self::$admin_id );

$post_id = self::factory()->post->create(
array(
'post_status' => 'publish',
'post_author' => self::$admin_id,
)
);

stick_post( $post_id );

edit_post(
array(
'post_ID' => $post_id,
'post_title' => 'Private',
'post_status' => 'publish',
'post_password' => 'secret',
'sticky' => 'sticky',
'visibility' => 'private',
)
);

$post = get_post( $post_id );

$this->assertSame( 'private', $post->post_status, 'The post was not made private.' );
$this->assertSame( '', $post->post_password, 'A private post kept a password.' );
$this->assertFalse( is_sticky( $post_id ), 'A private post was left sticky.' );
}

/**
* Ensures that a visibility sent by the caller is never overridden by an inferred one.
*
* The inference only fills a gap. A caller that sends `public` alongside a
* non-empty password still reaches `case 'public'` and still has the password
* dropped, exactly as before this change.
*
* @ticket 64810
*
* @covers ::edit_post
*/
public function test_edit_post_keeps_an_explicit_visibility_over_an_inferred_one() {
wp_set_current_user( self::$admin_id );

$post_id = self::factory()->post->create(
array(
'post_status' => 'publish',
'post_author' => self::$admin_id,
)
);

edit_post(
array(
'post_ID' => $post_id,
'post_title' => 'Contradictory',
'post_status' => 'publish',
'post_password' => 'secret',
'visibility' => 'public',
)
);

$this->assertSame(
'',
get_post( $post_id )->post_password,
'An explicit public visibility was overridden by the inferred one.'
);
}
}
Loading