diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index b6bd7576ea..561c37b437 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -2397,6 +2397,9 @@ function wp_ajax_query_attachments() { if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) ) $query['post_status'] .= ',private'; + // Filter query clauses to include filenames. + add_filter( 'posts_clauses', '_filter_query_attachment_filenames' ); + /** * Filters the arguments passed to WP_Query during an Ajax * call for querying attachments. diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index dfcb3ec440..b12aa5d070 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -1144,9 +1144,37 @@ function wp_edit_attachments_query_vars( $q = false ) { $q['post_parent'] = 0; } + // Filter query clauses to include filenames. + add_filter( 'posts_clauses', '_filter_query_attachment_filenames' ); + return $q; } +/** + * Filter the SQL clauses of an attachment query to include filenames. + * + * @since 4.7.0 + * @access private + * + * @param array $clauses An array including WHERE, GROUP BY, JOIN, ORDER BY, + * DISTINCT, fields (SELECT), and LIMITS clauses. + * @return array The modified clauses. + */ +function _filter_query_attachment_filenames( $clauses ) { + global $wpdb; + remove_filter( 'posts_clauses', __FUNCTION__ ); + + $clauses['join'] = " INNER JOIN {$wpdb->postmeta} ON ( {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id )"; + $clauses['groupby'] = "{$wpdb->posts}.ID"; + + $clauses['where'] = preg_replace( + "/\({$wpdb->posts}.post_content (NOT LIKE|LIKE) (\'[^']+\')\)/", + "$0 OR ( {$wpdb->postmeta}.meta_key = '_wp_attached_file' AND {$wpdb->postmeta}.meta_value $1 $2 )", + $clauses['where'] ); + + return $clauses; +} + /** * Executes a query for attachments. An array of WP_Query arguments * can be passed in, which will override the arguments set by this function. diff --git a/tests/phpunit/tests/query/search.php b/tests/phpunit/tests/query/search.php index a228f91d78..d0a34a67cb 100644 --- a/tests/phpunit/tests/query/search.php +++ b/tests/phpunit/tests/query/search.php @@ -280,6 +280,110 @@ class Tests_Query_Search extends WP_UnitTestCase { $this->assertSame( array( $p1, $p3, $p2 ), $q->posts ); } + /** + * Unfiltered search queries for attachment post types should not inlcude + * filenames to ensure the postmeta JOINs don't happen on the front end. + * + * @ticket 22744 + */ + public function test_exclude_file_names_in_attachment_search_by_default() { + $attachment = self::factory()->post->create( array( + 'post_type' => 'attachment', + 'post_status' => 'publish', + 'post_title' => 'bar foo', + 'post_content' => 'foo bar', + 'post_excerpt' => 'This post has foo', + ) ); + + add_post_meta( $attachment, '_wp_attached_file', 'some-image2.png', true ); + + // Pass post_type an array value. + $q = new WP_Query( array( + 's' => 'image2', + 'fields' => 'ids', + 'post_type' => 'attachment', + 'post_status' => 'inherit', + ) ); + + $this->assertNotEquals( array( $attachment ), $q->posts ); + } + + /** + * @ticket 22744 + */ + public function test_include_file_names_in_attachment_search_as_string() { + $attachment = self::factory()->post->create( array( + 'post_type' => 'attachment', + 'post_status' => 'publish', + 'post_title' => 'bar foo', + 'post_content' => 'foo bar', + 'post_excerpt' => 'This post has foo', + ) ); + + add_post_meta( $attachment, '_wp_attached_file', 'some-image1.png', true ); + add_filter( 'posts_clauses', '_filter_query_attachment_filenames' ); + + // Pass post_type a string value. + $q = new WP_Query( array( + 's' => 'image1', + 'fields' => 'ids', + 'post_type' => 'attachment', + 'post_status' => 'inherit', + ) ); + + $this->assertSame( array( $attachment ), $q->posts ); + } + + /** + * @ticket 22744 + */ + public function test_include_file_names_in_attachment_search_as_array() { + $attachment = self::factory()->post->create( array( + 'post_type' => 'attachment', + 'post_status' => 'publish', + 'post_title' => 'bar foo', + 'post_content' => 'foo bar', + 'post_excerpt' => 'This post has foo', + ) ); + + add_post_meta( $attachment, '_wp_attached_file', 'some-image2.png', true ); + add_filter( 'posts_clauses', '_filter_query_attachment_filenames' ); + + // Pass post_type an array value. + $q = new WP_Query( array( + 's' => 'image2', + 'fields' => 'ids', + 'post_type' => array( 'attachment' ), + 'post_status' => 'inherit', + ) ); + + $this->assertSame( array( $attachment ), $q->posts ); + } + + /** + * @ticket 22744 + */ + public function test_exclude_attachment_file_names_in_general_searches() { + $attachment = self::factory()->post->create( array( + 'post_type' => 'attachment', + 'post_status' => 'publish', + 'post_title' => 'bar foo', + 'post_content' => 'foo bar', + 'post_excerpt' => 'This post has foo', + ) ); + + add_post_meta( $attachment, '_wp_attached_file', 'some-image3.png', true ); + + $q = new WP_Query( array( + 's' => 'image3', + 'fields' => 'ids', + 'post_type' => array( 'post', 'page', 'attachment' ), + 'post_status' => 'inherit', + ) ); + + $this->assertNotEquals( array( $attachment ), $q->posts ); + } + public function filter_posts_search( $sql ) { return $sql . ' /* posts_search */'; }