43 private $thumbnailPlaceholderHtml;
55 $this->hookRunner =
new HookRunner( $hookContainer );
56 $this->repoGroup = $repoGroup;
57 $this->thumbnailProvider = $thumbnailProvider;
58 $this->userOptionsManager = $userOptionsManager;
79 if ( !$this->specialPage->getAuthority()->definitelyCan(
'read', $result->
getTitle() ) ) {
80 return Html::rawElement(
'li', [], $link );
86 $date = htmlspecialchars(
87 $this->specialPage->getLanguage()->userTimeAndDate(
89 $this->specialPage->getUser()
95 $snippetWithEllipsis = $snippet . $this->specialPage->msg(
'ellipsis' );
96 $extract = Html::rawElement(
'div', [
'class' =>
'searchresult' ], $snippetWithEllipsis );
113 $terms = $result instanceof \MediaWiki\Search\SqlSearchResult ? $result->getTermMatches() : [];
114 if ( !$this->hookRunner->onShowSearchHit( $this->specialPage, $result,
115 $terms, $link, $redirect, $section, $extract, $score,
117 $desc, $date, $related, $html )
124 $joined =
"{$link} {$redirect} {$category} {$section} {$file}";
125 $meta = $this->
buildMeta( $desc, $date );
128 $html = Html::rawElement(
130 [
'class' =>
'mw-search-result-heading' ],
133 $html .= $extract .
' ' . $meta;
137 $gridCells = Html::rawElement(
139 [
'class' =>
'searchResultImage-thumbnail' ],
141 ) . Html::rawElement(
143 [
'class' =>
'searchResultImage-text' ],
146 $html = Html::rawElement(
148 [
'class' =>
'searchResultImage' ],
153 return Html::rawElement(
155 [
'class' => [
'mw-search-result',
'mw-search-result-ns-' . $result->
getTitle()->getNamespace() ] ],
172 if ( $snippet ===
'' ) {
182 $attributes = [
'data-serp-pos' => $position ];
183 $this->hookRunner->onShowSearchHitTitle( $title, $snippet, $result,
188 $link = $this->linkRenderer->makeLink(
209 $inner = $title ===
null
211 : $this->linkRenderer->makeLink( $title, $text ?
new HtmlArmor( $text ) : null );
213 return "<span class='searchalttitle'>" .
214 $this->specialPage->msg( $msgKey )->rawParams( $inner )->parse()
224 return $title ===
null
235 return $title ===
null
258 $cat = Category::newFromTitle( $title );
259 return $this->specialPage->msg(
'search-result-category-size' )
260 ->numParams( $cat->getMemberCount(), $cat->getSubcatCount(), $cat->getFileCount() )
264 return $this->specialPage->msg(
'search-result-size' )
282 if ( $title ===
null || !$title->canExist() ) {
283 return [
'',
null, null ];
289 [
'class' =>
'searchalttitle' ],
290 $this->specialPage->msg(
'search-file-match' )->text()
294 $allowExtraThumbsFromRequest = $this->specialPage->getRequest()->getVal(
'search-thumbnail-extra-namespaces' );
295 $allowExtraThumbsFromPreference = $this->userOptionsManager->getOption(
296 $this->specialPage->getUser(),
297 'search-thumbnail-extra-namespaces'
299 $allowExtraThumbs = (bool)( $allowExtraThumbsFromRequest ?? $allowExtraThumbsFromPreference );
300 if ( !$allowExtraThumbs && $title->getNamespace() !==
NS_FILE ) {
301 return [ $html,
null, null ];
304 $thumbnail = $this->getThumbnail( $result, self::THUMBNAIL_SIZE );
305 $thumbnailName = $thumbnail ? $thumbnail->getName() :
null;
306 if ( !$thumbnailName ) {
307 return [ $html,
null, $this->generateThumbnailHtml( $result ) ];
310 $img = $this->repoGroup->findFile( $thumbnailName );
312 return [ $html,
null, $this->generateThumbnailHtml( $result ) ];
317 $unsafeShortDesc = $img->getShortDesc();
318 $shortDesc = Sanitizer::removeSomeTags( $unsafeShortDesc );
322 $this->specialPage->msg(
'parentheses' )->rawParams( $shortDesc )->escaped(),
323 $this->generateThumbnailHtml( $result, $thumbnail )
333 $title = $result->getTitle();
335 if ( $title ===
null || !$title->canExist() ) {
339 $thumbnails = $this->thumbnailProvider->getThumbnails( [ $title->getArticleID() => $title ], $size );
341 return $thumbnails[ $title->getArticleID() ] ??
null;
349 private function generateThumbnailHtml( SearchResult $result, ?SearchResultThumbnail $thumbnail =
null ): ?string {
350 $title = $result->getTitle();
352 if ( $title ===
null || !$title->canExist() ) {
357 $showThumbnail = in_array( $title->getNamespace(), $namespacesWithThumbnails );
358 if ( !$showThumbnail ) {
362 $thumbnailName = $thumbnail ? $thumbnail->getName() :
null;
363 if ( !$thumbnail || !$thumbnailName ) {
364 return $this->generateThumbnailPlaceholderHtml();
367 $img = $this->repoGroup->findFile( $thumbnailName );
369 return $this->generateThumbnailPlaceholderHtml();
372 $thumb = $this->transformThumbnail( $img, $thumbnail );
374 if ( $title->getNamespace() ===
NS_FILE ) {
377 return $thumb->toHtml( [
380 'alt' => $this->specialPage->msg(
'search-thumbnail-alt', $title->getPrefixedText() ),
386 return $thumb->toHtml( [
388 'custom-title-link' => $title,
390 'alt' => $this->specialPage->msg(
'search-thumbnail-alt', $title->getPrefixedText() ),
394 return $this->generateThumbnailPlaceholderHtml();
402 private function transformThumbnail( File $img, SearchResultThumbnail $thumbnail ) {
403 $optimalThumbnailWidth = $thumbnail->getWidth();
410 $thumbnailMaxDimension = max( $thumbnail->getWidth(), $thumbnail->getHeight() );
411 $thumbnailMinDimension = min( $thumbnail->getWidth(), $thumbnail->getHeight() );
412 $rescaleCoefficient = $thumbnailMinDimension
413 ? $thumbnailMaxDimension / $thumbnailMinDimension : 1;
418 $rescaledWidth = (int)round( $rescaleCoefficient * $thumbnail->getWidth() );
427 if ( $rescaledWidth !== $thumbnail->getWidth() ) {
428 $thumbLimits = $this->specialPage->getConfig()->get( MainConfigNames::ThumbLimits );
429 $largerThumbLimits = array_filter(
431 static function ( $limit ) use ( $rescaledWidth ) {
432 return $limit >= $rescaledWidth;
435 $optimalThumbnailWidth = $largerThumbLimits ? min( $largerThumbLimits ) : max( $thumbLimits );
438 return $img->transform( [
'width' => $optimalThumbnailWidth ] );
441 private function generateThumbnailPlaceholderHtml(): string {
442 if ( $this->thumbnailPlaceholderHtml ) {
443 return $this->thumbnailPlaceholderHtml;
446 $path = MW_INSTALL_PATH .
'/resources/lib/ooui/themes/wikimediaui/images/icons/imageLayoutFrameless.svg';
447 $this->thumbnailPlaceholderHtml = Html::rawElement(
450 'class' =>
'searchResultImage-thumbnail-placeholder',
451 'aria-hidden' =>
'true',
453 file_get_contents(
$path )
455 return $this->thumbnailPlaceholderHtml;
466 if ( $desc && $date ) {
467 $meta =
"{$desc} - {$date}";
476 return "<div class='mw-search-result-data'>{$meta}</div>";