31 public const THUMBNAIL_SIZE = 90;
41 private $thumbnailProvider;
43 private $thumbnailPlaceholderHtml;
45 private $userOptionsManager;
57 $this->hookRunner =
new HookRunner( $hookContainer );
58 $this->repoGroup = $repoGroup;
59 $this->thumbnailProvider = $thumbnailProvider;
60 $this->userOptionsManager = $userOptionsManager;
72 if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
81 if ( !$this->specialPage->getAuthority()->definitelyCan(
'read', $result->getTitle() ) ) {
88 $date = htmlspecialchars(
89 $this->specialPage->getLanguage()->userTimeAndDate(
90 $result->getTimestamp(),
91 $this->specialPage->getUser()
95 $snippet = $result->getTextSnippet();
97 $snippetWithEllipsis = $snippet . $this->specialPage->msg(
'ellipsis' );
98 $extract =
Html::rawElement(
'div', [
'class' =>
'searchresult' ], $snippetWithEllipsis );
103 if ( $result->getTitle() && $result->getTitle()->getNamespace() !==
NS_FILE ) {
115 $terms = $result instanceof \SqlSearchResult ? $result->getTermMatches() : [];
116 if ( !$this->hookRunner->onShowSearchHit( $this->specialPage, $result,
117 $terms, $link, $redirect, $section, $extract, $score,
119 $desc, $date, $related, $html )
126 $joined =
"{$link} {$redirect} {$category} {$section} {$file}";
127 $meta = $this->
buildMeta( $desc, $date );
132 [
'class' =>
'mw-search-result-heading' ],
135 $html .= $extract .
' ' . $meta;
141 [
'class' =>
'searchResultImage-thumbnail' ],
145 [
'class' =>
'searchResultImage-text' ],
150 [
'class' =>
'searchResultImage' ],
161 [
'class' => [
'mw-search-result',
'mw-search-result-ns-' . $result->getTitle()->getNamespace() ] ],
177 $snippet = $result->getTitleSnippet();
178 if ( $snippet ===
'' ) {
185 $title = clone $result->getTitle();
188 $attributes = [
'data-serp-pos' => $position ];
189 $this->hookRunner->onShowSearchHitTitle(
$title, $snippet, $result,
192 $this->specialPage, $query, $attributes );
194 $link = $this->linkRenderer->makeLink(
217 : $this->linkRenderer->makeLink(
$title, $text ?
new HtmlArmor( $text ) :
null );
219 return "<span class='searchalttitle'>" .
220 $this->specialPage->msg( $msgKey )->rawParams( $inner )->parse()
229 $title = $result->getRedirectTitle();
240 $title = $result->getSectionTitle();
251 $snippet = $result->getCategorySnippet();
262 $title = $result->getTitle();
265 return $this->specialPage->msg(
'search-result-category-size' )
266 ->numParams( $cat->getMemberCount(), $cat->getSubcatCount(), $cat->getFileCount() )
269 } elseif ( $result->getByteSize() !==
null || $result->getWordCount() > 0 ) {
270 return $this->specialPage->msg(
'search-result-size' )
271 ->sizeParams( $result->getByteSize() )
272 ->numParams( $result->getWordCount() )
286 $title = $result->getTitle();
289 return [
'',
null, null ];
293 if ( $result->isFileMatch() ) {
296 [
'class' =>
'searchalttitle' ],
297 $this->specialPage->msg(
'search-file-match' )->escaped()
301 $allowExtraThumbsFromRequest = $this->specialPage->getRequest()->getVal(
'search-thumbnail-extra-namespaces' );
302 $allowExtraThumbsFromPreference = $this->userOptionsManager->getOption(
303 $this->specialPage->getUser(),
304 'search-thumbnail-extra-namespaces'
306 $allowExtraThumbs = (bool)( $allowExtraThumbsFromRequest ?? $allowExtraThumbsFromPreference );
307 if ( !$allowExtraThumbs &&
$title->getNamespace() !==
NS_FILE ) {
308 return [ $html,
null, null ];
311 $thumbnail = $this->getThumbnail( $result, self::THUMBNAIL_SIZE );
312 $thumbnailName = $thumbnail ? $thumbnail->getName() :
null;
313 if ( !$thumbnailName ) {
314 return [ $html,
null, $this->generateThumbnailHtml( $result ) ];
317 $img = $this->repoGroup->findFile( $thumbnailName );
319 return [ $html,
null, $this->generateThumbnailHtml( $result ) ];
324 $this->specialPage->msg(
'parentheses' )->rawParams( $img->
getShortDesc() )->escaped(),
325 $this->generateThumbnailHtml( $result, $thumbnail )
341 $thumbnails = $this->thumbnailProvider->getThumbnails( [
$title->getArticleID() =>
$title ], $size );
343 return $thumbnails[
$title->getArticleID() ] ??
null;
351 private function generateThumbnailHtml(
SearchResult $result, SearchResultThumbnail $thumbnail =
null ): ?string {
358 $namespacesWithThumbnails = $this->specialPage->getConfig()->get(
'ThumbnailNamespaces' );
359 $showThumbnail = in_array(
$title->getNamespace(), $namespacesWithThumbnails );
360 if ( !$showThumbnail ) {
364 $thumbnailName = $thumbnail ? $thumbnail->getName() :
null;
365 if ( !$thumbnail || !$thumbnailName ) {
366 return $this->generateThumbnailPlaceholderHtml();
369 $img = $this->repoGroup->findFile( $thumbnailName );
371 return $this->generateThumbnailPlaceholderHtml();
374 $thumb = $this->transformThumbnail( $img, $thumbnail );
378 return $thumb->toHtml( [
381 'alt' => $this->specialPage->msg(
'search-thumbnail-alt' )->params(
$title ),
386 return $thumb->toHtml( [
388 'custom-title-link' =>
$title,
390 'alt' => $this->specialPage->msg(
'search-thumbnail-alt' )->params(
$title ),
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(
'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 ] );
444 private function generateThumbnailPlaceholderHtml(): string {
445 if ( $this->thumbnailPlaceholderHtml ) {
446 return $this->thumbnailPlaceholderHtml;
449 $path = MW_INSTALL_PATH .
'/resources/lib/ooui/themes/wikimediaui/images/icons/imageLayoutFrameless.svg';
450 $this->thumbnailPlaceholderHtml = Html::rawElement(
453 'class' =>
'searchResultImage-thumbnail-placeholder',
454 'aria-hidden' =>
'true',
456 file_get_contents(
$path )
458 return $this->thumbnailPlaceholderHtml;
469 if ( $desc && $date ) {
470 $meta =
"{$desc} - {$date}";
479 return "<div class='mw-search-result-data'>{$meta}</div>";
if(!defined('MW_SETUP_CALLBACK'))
Implements some public methods and some protected utility functions which are required by multiple ch...
transform( $params, $flags=0)
Transform a media file.
Marks HTML that shouldn't be escaped.
Prioritized list of file repositories.
NOTE: this class is being refactored into an abstract base class.
implements Special:Search - Run text & title search and display the output
Media transform output for images.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.