diff --git a/extension.json b/extension.json index 9248460..5ee22f5 100644 --- a/extension.json +++ b/extension.json @@ -42,7 +42,7 @@ }, "search": { "class": "PageImages\\Hooks\\SearchResultProvideThumbnailHookHandler", - "services": [ "PageProps", "RepoGroup" ] + "services": [ "SearchResultThumbnailProvider", "PageProps", "RepoGroup" ] } }, "JobClasses": { diff --git a/includes/Hooks/SearchResultProvideThumbnailHookHandler.php b/includes/Hooks/SearchResultProvideThumbnailHookHandler.php index c9dde8e..a551147 100644 --- a/includes/Hooks/SearchResultProvideThumbnailHookHandler.php +++ b/includes/Hooks/SearchResultProvideThumbnailHookHandler.php @@ -3,16 +3,16 @@ namespace PageImages\Hooks; use MediaWiki\Page\PageIdentity; -use MediaWiki\Search\Entity\SearchResultThumbnail; use MediaWiki\Search\Hook\SearchResultProvideThumbnailHook; +use MediaWiki\Search\SearchResultThumbnailProvider; use PageImages\PageImages; use PageProps; use RepoGroup; -use Title; class SearchResultProvideThumbnailHookHandler implements SearchResultProvideThumbnailHook { - public const THUMBNAIL_SIZE = 200; + /** @var SearchResultThumbnailProvider */ + private $thumbnailProvider; /** @var PageProps */ private $pageProps; @@ -21,23 +21,37 @@ class SearchResultProvideThumbnailHookHandler implements SearchResultProvideThum private $repoGroup; /** + * @param SearchResultThumbnailProvider $thumbnailProvider * @param PageProps $pageProps * @param RepoGroup $repoGroup */ - public function __construct( PageProps $pageProps, RepoGroup $repoGroup ) { + public function __construct( + SearchResultThumbnailProvider $thumbnailProvider, + PageProps $pageProps, + RepoGroup $repoGroup + ) { + $this->thumbnailProvider = $thumbnailProvider; $this->pageProps = $pageProps; $this->repoGroup = $repoGroup; } /** - * Returns a list fileNames associated with given pages + * Returns a list of fileNames for a given list of PageIdentity objects (outside of NS_FILE) * - * @param array $pagesByPageId key-value array where key is pageID and value is Title + * @param PageIdentity[] $identitiesByPageId key-value array of where key + * is pageId, value is PageIdentity * @return array */ - private function getFileNamesForPageTitles( $pagesByPageId ): array { + private function getFileNamesByPageId( array $identitiesByPageId ): array { + $nonFileIdentitiesByPageId = array_filter( + $identitiesByPageId, + static function ( PageIdentity $pageIdentity ) { + return $pageIdentity->getNamespace() !== NS_FILE; + } + ); + $propValues = $this->pageProps->getProperties( - $pagesByPageId, + $nonFileIdentitiesByPageId, PageImages::getPropNames( PageImages::LICENSE_ANY ) ); $fileNames = array_map( static function ( $prop ) { @@ -47,82 +61,27 @@ class SearchResultProvideThumbnailHookHandler implements SearchResultProvideThum }, $propValues ); return array_filter( $fileNames, static function ( $fileName ) { - return $fileName != null; + return $fileName !== null; } ); } /** - * Returns a list fileNames for with given LinkTarget, where title is NS_FILE - * - * @param array $linkFileTargetsByPageId key-value array of where key - * is pageId, value is LinkTarget - * @return array - */ - private function getFileNamesForFileTitles( $linkFileTargetsByPageId ): array { - return array_map( static function ( $linkFileTarget ) { - return $linkFileTarget->getDBkey(); - }, $linkFileTargetsByPageId ); - } - - /** - * Returns thumbnails for given list - * - * @param array $titlesByPageId a key value array where key is pageId and value is Title - * @param int $size size of thumbnail height and width in points - * @return SearchResultThumbnail[] - */ - private function getThumbnails( array $titlesByPageId, int $size ): array { - $pagesByPageId = array_filter( $titlesByPageId, static function ( $title ) { - return !$title->inNamespace( NS_FILE ); - } ); - $titleFilesByPageId = array_filter( $titlesByPageId, static function ( $title ) { - return $title->inNamespace( NS_FILE ); - } ); - - $files = $this->getFileNamesForPageTitles( $pagesByPageId ) - + $this->getFileNamesForFileTitles( $titleFilesByPageId ); - - $res = []; - foreach ( $files as $pageId => $fileName ) { - $file = $this->repoGroup->findFile( $fileName ); - if ( !$file ) { - continue; - } - $thumb = $file->transform( [ 'width' => $size , 'height' => $size ] ); - if ( !$thumb || $thumb->isError() ) { - continue; - } - - $localPath = $thumb->getLocalCopyPath(); - $thumbSize = $localPath && file_exists( $localPath ) ? filesize( $localPath ) : null; - - $res[$pageId] = new SearchResultThumbnail( - $thumb->getFile()->getMimeType(), - $thumbSize, - $thumb->getWidth(), - $thumb->getHeight(), - null, - wfExpandUrl( $thumb->getUrl(), PROTO_RELATIVE ), - $fileName - ); - } - - return $res; - } - - /** - * @param array $pageIdentities array that contain $pageId => SearchResultPageIdentity. + * @param array $pageIdentities array that contain $pageId => PageIdentity. * @param array &$results Placeholder for result. $pageId => SearchResultThumbnail * @param int|null $size size of thumbnail height and width in points */ public function onSearchResultProvideThumbnail( array $pageIdentities, &$results, int $size = null ): void { - $pageIdTitles = array_map( static function ( PageIdentity $identity ) { - return Title::makeTitle( $identity->getNamespace(), $identity->getDBkey() ); - }, $pageIdentities ); - - $data = $this->getThumbnails( $pageIdTitles, $size ?? self::THUMBNAIL_SIZE ); - foreach ( $data as $pageId => $thumbnail ) { - $results[ $pageId ] = $thumbnail; + $fileNamesByPageId = $this->getFileNamesByPageId( $pageIdentities ); + $results = $results ?? []; + foreach ( $fileNamesByPageId as $pageId => $fileName ) { + $file = $this->repoGroup->findFile( $fileName ); + if ( !$file ) { + continue; + } + $thumbnail = $this->thumbnailProvider->buildSearchResultThumbnailFromFile( $file, $size ); + if ( $thumbnail ) { + $results[$pageId] = $thumbnail; + } } } } diff --git a/tests/phpunit/Hooks/SearchResultProvideThumbnailHookHandlerTest.php b/tests/phpunit/Hooks/SearchResultProvideThumbnailHookHandlerTest.php index cf6a2c5..bbfc2d0 100644 --- a/tests/phpunit/Hooks/SearchResultProvideThumbnailHookHandlerTest.php +++ b/tests/phpunit/Hooks/SearchResultProvideThumbnailHookHandlerTest.php @@ -5,10 +5,12 @@ namespace PageImages\Tests\Hooks; use LocalFile; use MediaWiki\Page\PageIdentity; use MediaWiki\Page\PageIdentityValue; +use MediaWiki\Search\SearchResultThumbnailProvider; use MediaWikiIntegrationTestCase; use PageImages\Hooks\SearchResultProvideThumbnailHookHandler; use PageImages\PageImages; use PageProps; +use PHPUnit\Framework\MockObject\Stub\ReturnCallback; use RepoGroup; use ThumbnailImage; @@ -68,17 +70,23 @@ class SearchResultProvideThumbnailHookHandlerTest extends MediaWikiIntegrationTe * Creates mock object for LocalFile * @param int $size * @param string $thumbFilePath + * @param string $filename * @return LocalFile */ - private function getMockLocalFile( int $size, $thumbFilePath ): LocalFile { + private function getMockLocalFile( int $size, $thumbFilePath, $filename ): LocalFile { $file = $this->getMockBuilder( LocalFile::class ) ->disableOriginalConstructor() ->onlyMethods( [ + 'getName', 'transform', 'getMimeType' ] ) ->getMock(); + $file->expects( $this->once() ) + ->method( 'getName' ) + ->willReturn( $filename ); + $file->expects( $this->once() ) ->method( 'transform' ) ->with( [ 'width' => $size , 'height' => $size ] ) @@ -123,44 +131,42 @@ class SearchResultProvideThumbnailHookHandlerTest extends MediaWikiIntegrationTe ->onlyMethods( [ 'findFile' ] ) ->getMock(); - $repoGroup->expects( $this->exactly( 4 ) ) + $findFileCallback = function ( $filename ) { + return $this->getMockLocalFile( + SearchResultThumbnailProvider::THUMBNAIL_SIZE, + __FILE__, + $filename + ); + }; + $repoGroup->expects( $this->exactly( 2 ) ) ->method( 'findFile' ) - ->withConsecutive( [ 'File1.jpg' ], [ 'File2_any.jpg' ], [ 'dbKey3' ], [ 'dbKey4' ] ) + ->withConsecutive( [ 'File1.jpg' ], [ 'File2_any.jpg' ] ) ->willReturnOnConsecutiveCalls( - $this->getMockLocalFile( - SearchResultProvideThumbnailHookHandler::THUMBNAIL_SIZE, - __FILE__ - ), - null, - $this->getMockLocalFile( - SearchResultProvideThumbnailHookHandler::THUMBNAIL_SIZE, - false - ), + new ReturnCallback( $findFileCallback ), null ); - $handler = new SearchResultProvideThumbnailHookHandler( $pageProps, $repoGroup ); + $provider = new SearchResultThumbnailProvider( $repoGroup, $this->createHookContainer() ); + $handler = new SearchResultProvideThumbnailHookHandler( $provider, $pageProps, $repoGroup ); $results = [ 1 => null, 2 => null, 3 => null, 4 => null ]; $handler->onSearchResultProvideThumbnail( $pageIdentities, $results ); $this->assertNull( $results[ 2 ] ); + $this->assertNull( $results[ 3 ] ); $this->assertNull( $results[ 4 ] ); $this->assertNotNull( $results[ 1 ] ); $this->assertSame( 'File1.jpg', $results[ 1 ]->getName() ); $this->assertSame( - SearchResultProvideThumbnailHookHandler::THUMBNAIL_SIZE, + SearchResultThumbnailProvider::THUMBNAIL_SIZE, $results[ 1 ]->getWidth() ); $this->assertSame( - SearchResultProvideThumbnailHookHandler::THUMBNAIL_SIZE, + SearchResultThumbnailProvider::THUMBNAIL_SIZE, $results[ 1 ]->getHeight() ); $this->assertGreaterThan( 0, $results[ 1 ]->getSize() ); $this->assertSame( 'https://example.org/test.url', $results[ 1 ]->getUrl() ); - - $this->assertNotNull( $results[ 3 ] ); - $this->assertNull( $results[ 3 ]->getSize() ); } }