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: 3 additions & 2 deletions src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,10 @@ public function update_value( $meta, $object_id ) {
$args = $this->get_registered_fields()[ $meta_key ];

if ( $args['single'] ) {
$current = get_metadata( $this->get_meta_type(), $object_id, $meta_key, true );
// Only a stored value needs protecting, so a key with nothing stored is not validated.
$current = get_metadata_raw( $this->get_meta_type(), $object_id, $meta_key, true );

if ( is_wp_error( rest_validate_value_from_schema( $current, $args['schema'] ) ) ) {
if ( null !== $current && is_wp_error( rest_validate_value_from_schema( $current, $args['schema'] ) ) ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of null, get_metadata() falls back to get_metadata_default(). So this means that the fallback value doesn't need to be obtained here, since the null case is checked. So this seems right to me.

$error->add(
'rest_invalid_stored_value',
/* translators: %s: Custom field key. */
Expand Down
38 changes: 38 additions & 0 deletions tests/phpunit/tests/rest-api/rest-comments-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -4142,6 +4142,44 @@ public function test_cannot_create_empty_note_with_invalid_resolution_meta() {
$this->assertErrorResponse( 'rest_comment_content_invalid', $response, 400 );
}

/**
* Sending back the meta of a comment with no note status must not fail.
*
* @ticket 66377
*/
public function test_update_comment_with_meta_echoed_back_from_response() {
wp_set_current_user( self::$editor_id );
$comment_id = self::factory()->comment->create(
array(
'comment_post_ID' => self::factory()->post->create(),
)
);

$request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%d', $comment_id ) );
$request->set_param( 'context', 'edit' );
$meta = rest_get_server()->dispatch( $request )->get_data()['meta'];

$this->assertArrayHasKey( '_wp_note_status', $meta );
$this->assertNull( $meta['_wp_note_status'] );

$request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', $comment_id ) );
$request->add_header( 'Content-Type', 'application/json' );
$request->set_body(
wp_json_encode(
array(
'content' => 'Edited comment.',
'meta' => $meta,
)
)
);

$response = rest_get_server()->dispatch( $request );

$this->assertSame( 200, $response->get_status() );
$this->assertSame( 'Edited comment.', get_comment( $comment_id )->comment_content );
$this->assertFalse( metadata_exists( 'comment', $comment_id, '_wp_note_status' ) );
}

/**
* @ticket 64096
*/
Expand Down
100 changes: 100 additions & 0 deletions tests/phpunit/tests/rest-api/rest-post-meta-fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,106 @@ public function data_delete_does_not_trigger_error_if_no_meta_values() {
);
}

/**
* Resetting a single key with nothing stored should not be treated as an invalid stored value.
*
* @ticket 66377
* @dataProvider data_set_value_to_null_with_nothing_stored
*
* @param bool|array<string, mixed> $show_in_rest The show_in_rest argument for the meta key.
* @param non-falsy-string&lowercase-string $type The meta type.
*/
public function test_set_value_to_null_with_nothing_stored( $show_in_rest, string $type ) {
register_post_meta(
'post',
'my_meta_key',
array(
'show_in_rest' => $show_in_rest,
'single' => true,
'type' => $type,
)
);

$this->grant_write_permission();

$request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
$request->set_header( 'Content-Type', 'application/json' );
$request->set_body(
wp_json_encode(
array(
'meta' => array(
'my_meta_key' => null,
),
)
)
);

$response = rest_get_server()->dispatch( $request );

$this->assertSame( 200, $response->get_status() );
$this->assertFalse( metadata_exists( 'post', self::$post_id, 'my_meta_key' ) );
}

/**
* Data provider for test_set_value_to_null_with_nothing_stored().
*
* @return array<non-falsy-string, array{ bool|array<string, mixed>, non-falsy-string&lowercase-string}> Array of test parameters.
*/
public function data_set_value_to_null_with_nothing_stored(): array {
return array(
'boolean' => array( true, 'boolean' ),
'integer' => array( true, 'integer' ),
'number' => array( true, 'number' ),
'enum' => array(
array(
'schema' => array(
'type' => 'string',
'enum' => array( 'a', 'b' ),
),
),
'string',
),
);
}

/**
* A stored empty string that does not match the schema is still protected from being reset.
*
* @ticket 66377
*/
public function test_set_value_to_null_with_invalid_empty_string_stored() {
register_post_meta(
'post',
'my_meta_key',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'boolean',
)
);

add_post_meta( self::$post_id, 'my_meta_key', '' );

$this->grant_write_permission();

$request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
$request->set_header( 'Content-Type', 'application/json' );
$request->set_body(
wp_json_encode(
array(
'meta' => array(
'my_meta_key' => null,
),
)
)
);

$response = rest_get_server()->dispatch( $request );

$this->assertErrorResponse( 'rest_invalid_stored_value', $response, 500 );
$this->assertTrue( metadata_exists( 'post', self::$post_id, 'my_meta_key' ) );
}

public function test_remove_multi_value_db_error() {
add_post_meta( self::$post_id, 'test_multi', 'val1' );
$values = get_post_meta( self::$post_id, 'test_multi', false );
Expand Down
Loading