MediaWiki  master
FullSearchResultWidget.php
Go to the documentation of this file.
1 <?php
2 
4 
5 use Category;
6 use Html;
7 use HtmlArmor;
12 use SearchResult;
13 use SpecialSearch;
14 use Title;
15 
25  protected $specialPage;
27  protected $linkRenderer;
29  private $hookRunner;
30 
32  HookContainer $hookContainer
33  ) {
34  $this->specialPage = $specialPage;
35  $this->linkRenderer = $linkRenderer;
36  $this->hookRunner = new HookRunner( $hookContainer );
37  }
38 
44  public function render( SearchResult $result, $position ) {
45  // If the page doesn't *exist*... our search index is out of date.
46  // The least confusing at this point is to drop the result.
47  // You may get less results, but... on well. :P
48  if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
49  return '';
50  }
51 
52  $link = $this->generateMainLinkHtml( $result, $position );
53  // If page content is not readable, just return ths title.
54  // This is not quite safe, but better than showing excerpts from
55  // non-readable pages. Note that hiding the entry entirely would
56  // screw up paging (really?).
57  if ( !$this->specialPage->getAuthority()->definitelyCan( 'read', $result->getTitle() ) ) {
58  return Html::rawElement( 'li', [], $link );
59  }
60 
61  $redirect = $this->generateRedirectHtml( $result );
62  $section = $this->generateSectionHtml( $result );
63  $category = $this->generateCategoryHtml( $result );
64  $date = htmlspecialchars(
65  $this->specialPage->getLanguage()->userTimeAndDate(
66  $result->getTimestamp(),
67  $this->specialPage->getUser()
68  )
69  );
70  list( $file, $desc, $thumb ) = $this->generateFileHtml( $result );
71  $snippet = $result->getTextSnippet();
72  if ( $snippet ) {
73  $extract = Html::rawElement( 'div', [ 'class' => 'searchresult' ], $snippet );
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 = Html::rawElement(
105  'div',
106  [ 'class' => 'mw-search-result-heading' ],
107  $joined
108  );
109  $html .= $extract . ' ' . $meta;
110  } else {
111  $tableCells = Html::rawElement(
112  'td',
113  [ 'style' => 'width: 120px; text-align: center; vertical-align: top' ],
114  $thumb
115  ) . Html::rawElement(
116  'td',
117  [ 'style' => 'vertical-align: top' ],
118  "$joined $extract $meta"
119  );
120  $html = Html::rawElement(
121  'table',
122  [ 'class' => 'searchResultImage' ],
124  'tr',
125  [],
126  $tableCells
127  )
128  );
129  }
130 
131  return Html::rawElement( 'li', [ 'class' => 'mw-search-result' ], $html );
132  }
133 
144  protected function generateMainLinkHtml( SearchResult $result, $position ) {
145  $snippet = $result->getTitleSnippet();
146  if ( $snippet === '' ) {
147  $snippet = null;
148  } else {
149  $snippet = new HtmlArmor( $snippet );
150  }
151 
152  // clone to prevent hook from changing the title stored inside $result
153  $title = clone $result->getTitle();
154  $query = [];
155 
156  $attributes = [ 'data-serp-pos' => $position ];
157  $this->hookRunner->onShowSearchHitTitle( $title, $snippet, $result,
158  $result instanceof \SqlSearchResult ? $result->getTermMatches() : [],
159  $this->specialPage, $query, $attributes );
160 
161  $link = $this->linkRenderer->makeLink(
162  $title,
163  $snippet,
164  $attributes,
165  $query
166  );
167 
168  return $link;
169  }
170 
181  protected function generateAltTitleHtml( $msgKey, ?Title $title, $text ) {
182  $inner = $title === null
183  ? $text
184  : $this->linkRenderer->makeLink( $title, $text ? new HtmlArmor( $text ) : null );
185 
186  return "<span class='searchalttitle'>" .
187  $this->specialPage->msg( $msgKey )->rawParams( $inner )->parse()
188  . "</span>";
189  }
190 
195  protected function generateRedirectHtml( SearchResult $result ) {
196  $title = $result->getRedirectTitle();
197  return $title === null
198  ? ''
199  : $this->generateAltTitleHtml( 'search-redirect', $title, $result->getRedirectSnippet() );
200  }
201 
206  protected function generateSectionHtml( SearchResult $result ) {
207  $title = $result->getSectionTitle();
208  return $title === null
209  ? ''
210  : $this->generateAltTitleHtml( 'search-section', $title, $result->getSectionSnippet() );
211  }
212 
217  protected function generateCategoryHtml( SearchResult $result ) {
218  $snippet = $result->getCategorySnippet();
219  return $snippet
220  ? $this->generateAltTitleHtml( 'search-category', null, $snippet )
221  : '';
222  }
223 
228  protected function generateSizeHtml( SearchResult $result ) {
229  $title = $result->getTitle();
230  if ( $title->getNamespace() === NS_CATEGORY ) {
231  $cat = Category::newFromTitle( $title );
232  return $this->specialPage->msg( 'search-result-category-size' )
233  ->numParams( $cat->getPageCount(), $cat->getSubcatCount(), $cat->getFileCount() )
234  ->escaped();
235  // TODO: This is a bit odd...but requires changing the i18n message to fix
236  } elseif ( $result->getByteSize() !== null || $result->getWordCount() > 0 ) {
237  return $this->specialPage->msg( 'search-result-size' )
238  ->sizeParams( $result->getByteSize() )
239  ->numParams( $result->getWordCount() )
240  ->escaped();
241  }
242 
243  return '';
244  }
245 
252  protected function generateFileHtml( SearchResult $result ) {
253  $title = $result->getTitle();
254  if ( $title->getNamespace() !== NS_FILE ) {
255  return [ '', null, null ];
256  }
257 
258  if ( $result->isFileMatch() ) {
259  $html = Html::rawElement(
260  'span',
261  [ 'class' => 'searchalttitle' ],
262  $this->specialPage->msg( 'search-file-match' )->escaped()
263  );
264  } else {
265  $html = '';
266  }
267 
268  $descHtml = null;
269  $thumbHtml = null;
270 
271  $img = $result->getFile() ?: MediaWikiServices::getInstance()->getRepoGroup()
272  ->findFile( $title );
273  if ( $img ) {
274  $thumb = $img->transform( [ 'width' => 120, 'height' => 120 ] );
275  if ( $thumb ) {
276  $descHtml = $this->specialPage->msg( 'parentheses' )
277  ->rawParams( $img->getShortDesc() )
278  ->escaped();
279  $thumbHtml = $thumb->toHtml( [ 'desc-link' => true ] );
280  }
281  }
282 
283  return [ $html, $descHtml, $thumbHtml ];
284  }
285 
293  protected function buildMeta( $desc, $date ) {
294  if ( $desc && $date ) {
295  $meta = "{$desc} - {$date}";
296  } elseif ( $desc ) {
297  $meta = $desc;
298  } elseif ( $date ) {
299  $meta = $date;
300  } else {
301  return '';
302  }
303 
304  return "<div class='mw-search-result-data'>{$meta}</div>";
305  }
306 }
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\$specialPage
SpecialSearch $specialPage
Definition: FullSearchResultWidget.php:25
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\$hookRunner
HookRunner $hookRunner
Definition: FullSearchResultWidget.php:29
HtmlArmor
Marks HTML that shouldn't be escaped.
Definition: HtmlArmor.php:30
SqlSearchResult
Definition: SqlSearchResult.php:25
Category\newFromTitle
static newFromTitle(PageIdentity $page)
Factory function.
Definition: Category.php:159
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:202
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:43
Category
Category objects are immutable, strictly speaking.
Definition: Category.php:33
$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:217
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\render
render(SearchResult $result, $position)
Definition: FullSearchResultWidget.php:44
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateFileHtml
generateFileHtml(SearchResult $result)
Definition: FullSearchResultWidget.php:252
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:263
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateSectionHtml
generateSectionHtml(SearchResult $result)
Definition: FullSearchResultWidget.php:206
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:31
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateAltTitleHtml
generateAltTitleHtml( $msgKey, ?Title $title, $text)
Generates an alternate title link, such as (redirect from Foo).
Definition: FullSearchResultWidget.php:181
SpecialSearch
implements Special:Search - Run text & title search and display the output
Definition: SpecialSearch.php:41
MediaWiki\Search\SearchWidgets\SearchResultWidget
Renders a single search result to HTML.
Definition: SearchResultWidget.php:10
Title
Represents a title within MediaWiki.
Definition: Title.php:47
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateMainLinkHtml
generateMainLinkHtml(SearchResult $result, $position)
Generates HTML for the primary call to action.
Definition: FullSearchResultWidget.php:144
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateRedirectHtml
generateRedirectHtml(SearchResult $result)
Definition: FullSearchResultWidget.php:195
MediaWiki\Search\SearchWidgets
Definition: BasicSearchResultSetWidget.php:3
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:210
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:78
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\$linkRenderer
LinkRenderer $linkRenderer
Definition: FullSearchResultWidget.php:27
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:45
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\generateSizeHtml
generateSizeHtml(SearchResult $result)
Definition: FullSearchResultWidget.php:228
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:554
NS_FILE
const NS_FILE
Definition: Defines.php:70
MediaWiki\Search\SearchWidgets\FullSearchResultWidget\buildMeta
buildMeta( $desc, $date)
Definition: FullSearchResultWidget.php:293
MediaWiki\Search\SearchWidgets\FullSearchResultWidget
Renders a 'full' multi-line search result with metadata.
Definition: FullSearchResultWidget.php:23
Html
This class is a collection of static functions that serve two purposes:
Definition: Html.php:49