Skip to content

Commit

Permalink
Merge branch 'trunk' into fix/62019
Browse files Browse the repository at this point in the history
* trunk:
  Build Tools: Allow easier customization of the .env file.
  Coding Standards: Avoid using confusing `!` condition in Media Library selection check.
  REST API: Only check password value in query parameters while checking post permissions.
  Media: Add Ctrl/Command + Enter shortcut to insert selected Media Library items.
  REST API: Support exact search in the REST API posts endpoint.
  Script Loader: Remove unused array_merge.
  REST API: Automatically populate targetHints for the Allow header.
  • Loading branch information
circlecube committed Sep 17, 2024
2 parents 18b6243 + 3cd3a00 commit 7ddbc19
Show file tree
Hide file tree
Showing 13 changed files with 530 additions and 17 deletions.
File renamed without changes.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# gitignore file for WordPress Core

# Configuration files with possibly sensitive information
.env
wp-config.php
wp-tests-config.php
.htaccess
Expand Down
5 changes: 5 additions & 0 deletions src/js/media/views/attachment.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ Attachment = View.extend(/** @lends wp.media.view.Attachment.prototype */{
method = 'toggle';
}

// Avoid toggles when the command or control key is pressed with the enter key to prevent deselecting the last selected attachment.
if ( ( event.metaKey || event.ctrlKey ) && ( 13 === event.keyCode || 10 === event.keyCode ) ) {
return;
}

this.toggleSelection({
method: method
});
Expand Down
30 changes: 30 additions & 0 deletions src/js/media/views/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,29 @@ Modal = wp.media.View.extend(/** @lends wp.media.view.Modal.prototype */{
this.escape();
},

/**
* Handles the selection of attachments when the command or control key is pressed with the enter key.
*
* @since 6.7
*
* @param {Object} event The keydown event object.
*/
selectHandler: function( event ) {
var selection = this.controller.state().get( 'selection' );

if ( selection.length <= 0 ) {
return;
}

if ( 'insert' === this.controller.options.state ) {
this.controller.trigger( 'insert', selection );
} else {
this.controller.trigger( 'select', selection );
event.preventDefault();
this.escape();
}
},

/**
* @param {Array|Object} content Views to register to '.media-modal-content'
* @return {wp.media.view.Modal} Returns itself to allow chaining.
Expand Down Expand Up @@ -214,6 +237,13 @@ Modal = wp.media.View.extend(/** @lends wp.media.view.Modal.prototype */{
this.escape();
event.stopImmediatePropagation();
}

// Select the attachment when command or control and enter are pressed.
if ( ( 13 === event.which || 10 === event.which ) && ( event.metaKey || event.ctrlKey ) ) {
this.selectHandler( event );
event.stopImmediatePropagation();
}

}
});

Expand Down
64 changes: 63 additions & 1 deletion src/wp-includes/rest-api/class-wp-rest-server.php
Original file line number Diff line number Diff line change
Expand Up @@ -636,13 +636,75 @@ public static function get_response_links( $response ) {
foreach ( $items as $item ) {
$attributes = $item['attributes'];
$attributes['href'] = $item['href'];
$data[ $rel ][] = $attributes;

if ( 'self' !== $rel ) {
$data[ $rel ][] = $attributes;
continue;
}

$target_hints = self::get_target_hints_for_link( $attributes );
if ( $target_hints ) {
$attributes['targetHints'] = $target_hints;
}

$data[ $rel ][] = $attributes;
}
}

return $data;
}

/**
* Gets the target links for a REST API Link.
*
* @since 6.7.0
*
* @param array $link
*
* @return array|null
*/
protected static function get_target_hints_for_link( $link ) {
// Prefer targetHints that were specifically designated by the developer.
if ( isset( $link['targetHints']['allow'] ) ) {
return null;
}

$request = WP_REST_Request::from_url( $link['href'] );
if ( ! $request ) {
return null;
}

$server = rest_get_server();
$match = $server->match_request_to_handler( $request );

if ( is_wp_error( $match ) ) {
return null;
}

if ( is_wp_error( $request->has_valid_params() ) ) {
return null;
}

if ( is_wp_error( $request->sanitize_params() ) ) {
return null;
}

$target_hints = array();

$response = new WP_REST_Response();
$response->set_matched_route( $match[0] );
$response->set_matched_handler( $match[1] );
$headers = rest_send_allow_header( $response, $server, $request )->get_headers();

foreach ( $headers as $name => $value ) {
$name = WP_REST_Request::canonicalize_header_name( $name );

$target_hints[ $name ] = array_map( 'trim', explode( ',', $value ) );
}

return $target_hints;
}

/**
* Retrieves the CURIEs (compact URIs) used for relations.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,13 @@ public function get_items( $request ) {
}
}

if (
isset( $registered['search_semantics'], $request['search_semantics'] )
&& 'exact' === $request['search_semantics']
) {
$args['exact'] = true;
}

$args = $this->prepare_tax_query( $args, $request );

// Force the post_type argument, since it's not a user input variable.
Expand Down Expand Up @@ -497,9 +504,9 @@ public function get_item_permissions_check( $request ) {
);
}

if ( $post && ! empty( $request['password'] ) ) {
if ( $post && ! empty( $request->get_query_params()['password'] ) ) {
// Check post password, and return error if invalid.
if ( ! hash_equals( $post->post_password, $request['password'] ) ) {
if ( ! hash_equals( $post->post_password, $request->get_query_params()['password'] ) ) {
return new WP_Error(
'rest_post_incorrect_password',
__( 'Incorrect post password.' ),
Expand Down Expand Up @@ -2886,6 +2893,12 @@ public function get_collection_params() {
);
}

$query_params['search_semantics'] = array(
'description' => __( 'How to interpret the search input.' ),
'type' => 'string',
'enum' => array( 'exact' ),
);

$query_params['offset'] = array(
'description' => __( 'Offset the result set by a specific number of items.' ),
'type' => 'integer',
Expand Down
4 changes: 0 additions & 4 deletions src/wp-includes/script-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -609,10 +609,6 @@ function wp_tinymce_inline_scripts() {
$tinymce_settings['wpeditimage_disable_captions'] = true;
}

if ( ! empty( $editor_settings['tinymce'] ) && is_array( $editor_settings['tinymce'] ) ) {
array_merge( $tinymce_settings, $editor_settings['tinymce'] );
}

/** This filter is documented in wp-includes/class-wp-editor.php */
$tinymce_settings = apply_filters( 'tiny_mce_before_init', $tinymce_settings, 'classic-block' );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ public function test_registered_query_params() {
'per_page',
'search',
'search_columns',
'search_semantics',
'slug',
'status',
),
Expand Down
1 change: 1 addition & 0 deletions tests/phpunit/tests/rest-api/rest-pages-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public function test_registered_query_params() {
'per_page',
'search',
'search_columns',
'search_semantics',
'slug',
'status',
),
Expand Down
104 changes: 104 additions & 0 deletions tests/phpunit/tests/rest-api/rest-posts-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ public function test_registered_query_params() {
'per_page',
'search',
'search_columns',
'search_semantics',
'slug',
'status',
'sticky',
Expand Down Expand Up @@ -765,6 +766,64 @@ public function test_get_items_status_without_permissions() {
}
}

/**
* @ticket 56350
*
* @dataProvider data_get_items_exact_search
*
* @param string $search_term The search term.
* @param bool $exact_search Whether the search is an exact or general search.
* @param int $expected The expected number of matching posts.
*/
public function test_get_items_exact_search( $search_term, $exact_search, $expected ) {
self::factory()->post->create(
array(
'post_title' => 'Rye',
'post_content' => 'This is a post about Rye Bread',
)
);

self::factory()->post->create(
array(
'post_title' => 'Types of Bread',
'post_content' => 'Types of bread are White and Rye Bread',
)
);

$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
$request['search'] = $search_term;
if ( $exact_search ) {
$request['search_semantics'] = 'exact';
}
$response = rest_get_server()->dispatch( $request );
$this->assertCount( $expected, $response->get_data() );
}

/**
* Data provider for test_get_items_exact_search().
*
* @return array[]
*/
public function data_get_items_exact_search() {
return array(
'general search, one exact match and one partial match' => array(
'search_term' => 'Rye',
'exact_search' => false,
'expected' => 2,
),
'exact search, one exact match and one partial match' => array(
'search_term' => 'Rye',
'exact_search' => true,
'expected' => 1,
),
'exact search, no match and one partial match' => array(
'search_term' => 'Rye Bread',
'exact_search' => true,
'expected' => 0,
),
);
}

public function test_get_items_order_and_orderby() {
self::factory()->post->create(
array(
Expand Down Expand Up @@ -2173,6 +2232,51 @@ public function test_get_post_with_password_without_permission() {
$this->assertTrue( $data['excerpt']['protected'] );
}

/**
* @ticket 61837
*/
public function test_get_item_permissions_check_while_updating_password() {
$endpoint = new WP_REST_Posts_Controller( 'post' );

$request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
$request->set_url_params( array( 'id' => self::$post_id ) );
$request->set_body_params(
$this->set_post_data(
array(
'id' => self::$post_id,
'password' => '123',
)
)
);
$permission = $endpoint->get_item_permissions_check( $request );

// Password provided in POST data, should not be used as authentication.
$this->assertNotWPError( $permission, 'Password in post body should be ignored by permissions check.' );
$this->assertTrue( $permission );
}

/**
* @ticket 61837
*/
public function test_get_item_permissions_check_while_updating_password_with_invalid_type() {
$endpoint = new WP_REST_Posts_Controller( 'post' );

$request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
$request->set_url_params( array( 'id' => self::$post_id ) );
$request->set_body_params(
$this->set_post_data(
array(
'id' => self::$post_id,
'password' => 123,
)
)
);
$permission = $endpoint->get_item_permissions_check( $request );

$this->assertNotWPError( $permission, 'Password in post body should be ignored by permissions check even when it is an invalid type.' );
$this->assertTrue( $permission );
}

/**
* The post response should not have `block_version` when in view context.
*
Expand Down
Loading

0 comments on commit 7ddbc19

Please sign in to comment.