MediaWiki  master
FullSearchResultWidget.php
Go to the documentation of this file.
1 <?php
2 
4 
5 use Category;
6 use HtmlArmor;
11 use SearchResult;
12 use SpecialSearch;
13 use Title;
14 
24  protected $specialPage;
26  protected $linkRenderer;
28  private $hookRunner;
29 
31  HookContainer $hookContainer
32  ) {
33  $this->specialPage = $specialPage;
34  $this->linkRenderer = $linkRenderer;
35  $this->hookRunner = new HookRunner( $hookContainer );
36  }
37 
43  public function render( SearchResult $result, $position ) {
44  // If the page doesn't *exist*... our search index is out of date.
45  // The least confusing at this point is to drop the result.
46  // You may get less results, but... on well. :P
47  if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
48  return '';
49  }
50 
51  $link = $this->generateMainLinkHtml( $result, $position );
52  // If page content is not readable, just return ths title.
53  // This is not quite safe, but better than showing excerpts from
54  // non-readable pages. Note that hiding the entry entirely would
55  // screw up paging (really?).
56  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
57  if ( !$permissionManager->userCan(
58  'read', $this->specialPage->getUser(), $result->getTitle()
59  ) ) {
60  return "<li>{$link}</li>";
61  }
62 
63  $redirect = $this->generateRedirectHtml( $result );
64  $section = $this->generateSectionHtml( $result );
65  $category = $this->generateCategoryHtml( $result );
66  $date = $this->specialPage->getLanguage()->userTimeAndDate(
67  $result->getTimestamp(),
68  $this->specialPage->getUser()
69  );
70  list( $file, $desc, $thumb ) = $this->generateFileHtml( $result );
71  $snippet = $result->getTextSnippet();
72  if ( $snippet ) {
73  $extract = "<div class='searchresult'>$snippet</div>";
74  } else {
75  $extract = '';
76  }
77 
78  if ( $thumb === null ) {
79  // If no thumb, then the description is about size
80  $desc = $this->generateSizeHtml( $result );
81 
82  // Let hooks do their own final construction if desired.
83  // FIXME: Not sure why this is only for results without thumbnails,
84  // but keeping it as-is for now to prevent breaking hook consumers.
85  $html = null;
86  $score = '';
87  $related = '';
88  // TODO: remove this instanceof and always pass [], let implementors do the cast if
89  // they want to be SearchDatabase specific
90  $terms = $result instanceof \SqlSearchResult ? $result->getTermMatches() : [];
91  if ( !$this->hookRunner->onShowSearchHit( $this->specialPage, $result,
92  $terms, $link, $redirect, $section, $extract, $score,
93  $desc, $date, $related, $html )
94  ) {
95  return $html;
96  }
97  }
98 
99  // All the pieces have been collected. Now generate the final HTML
100  $joined = "{$link} {$redirect} {$category} {$section} {$file}";
101  $meta = $this->buildMeta( $desc, $date );
102 
103  if ( $thumb === null ) {
104  $html =
105  "<div class='mw-search-result-heading'>{$joined}</div>" .
106  "{$extract} {$meta}";
107  } else {
108  $html =
109  "<table class='searchResultImage'>" .
110  "<tr>" .
111  "<td style='width: 120px; text-align: center; vertical-align: top'>" .
112  $thumb .
113  "</td>" .
114  "<td style='vertical-align: top'>" .
115  "{$joined} {$extract} {$meta}" .
116  "</td>" .
117  "</tr>" .
118  "</table>";
119  }
120 
121  return "<li class='mw-search-result'>{$html}</li>";
122  }
123 
134  protected function generateMainLinkHtml( SearchResult $result, $position ) {
135  $snippet = $result->getTitleSnippet();
136  if ( $snippet === '' ) {
137  $snippet = null;
138  } else {
139  $snippet = new HtmlArmor( $snippet );
140  }
141 
142  // clone to prevent hook from changing the title stored inside $result
143  $title = clone $result->getTitle();
144  $query = [];
145 
146  $attributes = [ 'data-serp-pos' => $position ];
147  $this->hookRunner->onShowSearchHitTitle( $title, $snippet, $result,
148  $result instanceof \SqlSearchResult ? $result->getTermMatches() : [],
149  $this->specialPage, $query, $attributes );
150 
151  $link = $this->linkRenderer->makeLink(
152  $title,
153  $snippet,
154  $attributes,
155  $query
156  );
157 
158  return $link;
159  }
160 
171  protected function generateAltTitleHtml( $msgKey, ?Title $title, $text ) {
172  $inner = $title === null
173  ? $text
174  : $this->linkRenderer->makeLink( $title, $text ? new HtmlArmor( $text ) : null );
175 
176  return "<span class='searchalttitle'>" .
177  $this->specialPage->msg( $msgKey )->rawParams( $inner )->parse()
178  . "</span>";
179  }
180 
185  protected function generateRedirectHtml( SearchResult $result ) {
186  $title = $result->getRedirectTitle();
187  return $title === null
188  ? ''
189  : $this->generateAltTitleHtml( 'search-redirect', $title, $result->getRedirectSnippet() );
190  }
191 
196  protected function generateSectionHtml( SearchResult $result ) {
197  $title = $result->getSectionTitle();
198  return $title === null
199  ? ''
200  : $this->generateAltTitleHtml( 'search-section', $title, $result->getSectionSnippet() );
201  }
202 
207  protected function generateCategoryHtml( SearchResult $result ) {
208  $snippet = $result->getCategorySnippet();
209  return $snippet
210  ? $this->generateAltTitleHtml( 'search-category', null, $snippet )
211  : '';
212  }
213 
218  protected function generateSizeHtml( SearchResult $result ) {
219  $title = $result->getTitle();
220  if ( $title->getNamespace() === NS_CATEGORY ) {
221  $cat = Category::newFromTitle( $title );
222  return $this->specialPage->msg( 'search-result-category-size' )
223  ->numParams( $cat->getPageCount(), $cat->getSubcatCount(), $cat->getFileCount() )
224  ->escaped();
225  // TODO: This is a bit odd...but requires changing the i18n message to fix
226  } elseif ( $result->getByteSize() !== null || $result->getWordCount() > 0 ) {
227  $lang = $this->specialPage->getLanguage();
228  $bytes = $lang->formatSize( $result->getByteSize() );
229  $words = $result->getWordCount();
230 
231  return $this->specialPage->msg( 'search-result-size', $bytes )
232  ->numParams( $words )
233  ->escaped();
234  }
235 
236  return '';
237  }
238 
245  protected function generateFileHtml( SearchResult $result ) {
246  $title = $result->getTitle();
247  if ( $title->getNamespace() !== NS_FILE ) {
248  return [ '', null, null ];
249  }
250 
251  if ( $result->isFileMatch() ) {
252  $html = "<span class='searchalttitle'>" .
253  $this->specialPage->msg( 'search-file-match' )->escaped() .
254  "</span>";
255  } else {
256  $html = '';
257  }
258 
259  $descHtml = null;
260  $thumbHtml = null;
261 
262  $img = $result->getFile() ?: MediaWikiServices::getInstance()->getRepoGroup()
263  ->findFile( $title );
264  if ( $img ) {
265  $thumb = $img->transform( [ 'width' => 120, 'height' => 120 ] );
266  if ( $thumb ) {
267  $descHtml = $this->specialPage->msg( 'parentheses' )
268  ->rawParams( $img->getShortDesc() )
269  ->escaped();
270  $thumbHtml = $thumb->toHtml( [ 'desc-link' => true ] );
271  }
272  }
273 
274  return [ $html, $descHtml, $thumbHtml ];
275  }
276 
284  protected function buildMeta( $desc, $date ) {
285  if ( $desc && $date ) {
286  $meta = "{$desc} - {$date}";
287  } elseif ( $desc ) {
288  $meta = $desc;
289  } elseif ( $date ) {
290  $meta = $date;
291  } else {
292  return '';
293  }
294 
295  return "<div class='mw-search-result-data'>{$meta}</div>";
296  }
297 }
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\$specialPage
SpecialSearch $specialPage
Definition: FullSearchResultWidget.php:24
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\$hookRunner
HookRunner $hookRunner
Definition: FullSearchResultWidget.php:28
HtmlArmor
Marks HTML that shouldn't be escaped.
Definition: HtmlArmor.php:30
SqlSearchResult
Definition: SqlSearchResult.php:25
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:160
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:41
Category
Category objects are immutable, strictly speaking.
Definition: Category.php:32
NS_FILE
const NS_FILE
Definition: Defines.php:75
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateCategoryHtml
generateCategoryHtml(SearchResult $result)
Definition: FullSearchResultWidget.php:207
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\render
render(SearchResult $result, $position)
Definition: FullSearchResultWidget.php:43
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateFileHtml
generateFileHtml(SearchResult $result)
Definition: FullSearchResultWidget.php:245
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:192
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateSectionHtml
generateSectionHtml(SearchResult $result)
Definition: FullSearchResultWidget.php:196
SearchResult
NOTE: this class is being refactored into an abstract base class.
Definition: SearchResult.php:38
$title
$title
Definition: testCompression.php:38
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\__construct
__construct(SpecialSearch $specialPage, LinkRenderer $linkRenderer, HookContainer $hookContainer)
Definition: FullSearchResultWidget.php:30
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:83
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateAltTitleHtml
generateAltTitleHtml( $msgKey, ?Title $title, $text)
Generates an alternate title link, such as (redirect from Foo).
Definition: FullSearchResultWidget.php:171
Category\newFromTitle
static newFromTitle( $title)
Factory function.
Definition: Category.php:153
SpecialSearch
implements Special:Search - Run text & title search and display the output
Definition: SpecialSearch.php:38
MediaWiki\Search\SearchWidgets\SearchResultWidget
Renders a single search result to HTML.
Definition: SearchResultWidget.php:10
Title
Represents a title within MediaWiki.
Definition: Title.php:41
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateMainLinkHtml
generateMainLinkHtml(SearchResult $result, $position)
Generates HTML for the primary call to action.
Definition: FullSearchResultWidget.php:134
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateRedirectHtml
generateRedirectHtml(SearchResult $result)
Definition: FullSearchResultWidget.php:185
MediaWiki\Search\SearchWidgets
Definition: BasicSearchResultSetWidget.php:3
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\$linkRenderer
LinkRenderer $linkRenderer
Definition: FullSearchResultWidget.php:26
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:45
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateSizeHtml
generateSizeHtml(SearchResult $result)
Definition: FullSearchResultWidget.php:218
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:562
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\buildMeta
buildMeta( $desc, $date)
Definition: FullSearchResultWidget.php:284
MediaWiki\Search\SearchWidgets\FullSearchResultWidget
Renders a 'full' multi-line search result with metadata.
Definition: FullSearchResultWidget.php:22