From 9634ca740d408e92f42aa9342b38e61841d85b21 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Sat, 26 Sep 2026 05:05:10 +0200 Subject: [PATCH] fix: media search also matches file names --- src/Integrations/Media/MediaSearchAbility.php | 22 ++++++++++-- .../Media/MediaSearchAbilityTest.php | 34 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/Integrations/Media/MediaSearchAbility.php b/src/Integrations/Media/MediaSearchAbility.php index ce1c7f3..c3117cf 100644 --- a/src/Integrations/Media/MediaSearchAbility.php +++ b/src/Integrations/Media/MediaSearchAbility.php @@ -208,7 +208,7 @@ final class MediaSearchAbility implements Ability */ private function searchPage(array $args, int $limit, int $offset): array { - $rows = get_posts(array_merge($args, ['posts_per_page' => $limit + 1, 'offset' => $offset])); + $rows = self::query(array_merge($args, ['posts_per_page' => $limit + 1, 'offset' => $offset])); $page = Cursor::page(array_values($rows), $limit, $offset); return [ @@ -230,7 +230,7 @@ final class MediaSearchAbility implements Ability */ private function unusedPage(array $args, int $limit, int $offset): array { - $candidates = array_values(get_posts(array_merge($args, [ + $candidates = array_values(self::query(array_merge($args, [ 'posts_per_page' => self::MAX_SCAN + 1, 'offset' => $offset, ]))); @@ -282,4 +282,22 @@ final class MediaSearchAbility implements Ability { return current_user_can('edit_post', (int) $attachment->ID); } + + /** + * get_posts() with file-name matching for a search term, as in the media library screen: + * WordPress only searches _wp_attached_file when wp_allow_query_attachment_by_filename is + * true. WP_Query reads that filter once per query and then calls remove_all_filters() on it + * itself, so it applies to this query only. + * + * @param array $args + * @return array + */ + private static function query(array $args): array + { + if (isset($args['s'])) { + add_filter('wp_allow_query_attachment_by_filename', '__return_true'); + } + + return get_posts($args); + } } diff --git a/tests/Unit/Integrations/Media/MediaSearchAbilityTest.php b/tests/Unit/Integrations/Media/MediaSearchAbilityTest.php index 13651f7..a99cff8 100644 --- a/tests/Unit/Integrations/Media/MediaSearchAbilityTest.php +++ b/tests/Unit/Integrations/Media/MediaSearchAbilityTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Media; +use Brain\Monkey\Filters; use Brain\Monkey\Functions; use LIT\MCP\Abilities\Cursor; use LIT\MCP\Integrations\Media\MediaPresenter; @@ -149,6 +150,39 @@ final class MediaSearchAbilityTest extends TestCase (new MediaSearchAbility($this->presenter(), Mockery::mock(MediaUsageFinder::class)))->execute([]); } + public function testASearchTermAlsoMatchesFileNames(): void + { + $this->unrestrictedPostType(); + Functions\when('current_user_can')->justReturn(true); + Filters\expectAdded('wp_allow_query_attachment_by_filename')->once()->with('__return_true'); + Functions\expect('get_posts')->once()->with( + Mockery::on(static fn (array $args): bool => ($args['s'] ?? null) === 'logo') + )->andReturn([]); + + (new MediaSearchAbility($this->presenter(), Mockery::mock(MediaUsageFinder::class)))->execute(['search' => 'logo']); + } + + public function testTheUnusedScanAlsoMatchesFileNames(): void + { + $this->unrestrictedPostType(); + Functions\when('current_user_can')->justReturn(true); + Filters\expectAdded('wp_allow_query_attachment_by_filename')->once()->with('__return_true'); + Functions\when('get_posts')->justReturn([]); + + (new MediaSearchAbility($this->presenter(), Mockery::mock(MediaUsageFinder::class))) + ->execute(['search' => 'logo', 'unused' => true]); + } + + public function testNoFileNameFilterWithoutASearchTerm(): void + { + $this->unrestrictedPostType(); + Functions\when('current_user_can')->justReturn(true); + Filters\expectAdded('wp_allow_query_attachment_by_filename')->never(); + Functions\when('get_posts')->justReturn([]); + + (new MediaSearchAbility($this->presenter(), Mockery::mock(MediaUsageFinder::class)))->execute([]); + } + public function testUnusedFiltersThroughTheUsageFinder(): void { Functions\when('get_posts')->justReturn($this->attachments(1, 2, 3));