MediaWiki REL1_37
SearchFormWidget.php
Go to the documentation of this file.
1<?php
2
4
5use Html;
13use Xml;
14
17 protected $specialSearch;
19 protected $searchConfig;
21 protected $profiles;
25 private $hookRunner;
30
39 public function __construct(
45 array $profiles
46 ) {
47 $this->specialSearch = $specialSearch;
48 $this->searchConfig = $searchConfig;
49 $this->hookContainer = $hookContainer;
50 $this->hookRunner = new HookRunner( $hookContainer );
51 $this->languageConverter = $languageConverter;
52 $this->namespaceInfo = $namespaceInfo;
53 $this->profiles = $profiles;
54 }
55
66 public function render(
67 $profile,
68 $term,
69 $numResults,
70 $totalResults,
71 $offset,
72 $isPowerSearch,
73 array $options = []
74 ) {
75 $user = $this->specialSearch->getUser();
76
77 $form = Xml::openElement(
78 'form',
79 [
80 'id' => $isPowerSearch ? 'powersearch' : 'search',
81 // T151903: default to POST in case JS is disabled
82 'method' => ( $isPowerSearch && $user->isRegistered() ) ? 'post' : 'get',
83 'action' => wfScript(),
84 ]
85 ) .
86 Html::rawElement(
87 'div',
88 [ 'id' => 'mw-search-top-table' ],
89 $this->shortDialogHtml( $profile, $term, $numResults, $totalResults, $offset, $options )
90 ) .
91 Html::rawElement( 'div', [ 'class' => 'mw-search-visualclear' ] ) .
92 Html::rawElement(
93 'div',
94 [ 'class' => 'mw-search-profile-tabs' ],
95 $this->profileTabsHtml( $profile, $term ) .
96 Html::rawElement( 'div', [ 'style' => 'clear:both' ] )
97 ) .
98 $this->optionsHtml( $term, $isPowerSearch, $profile ) .
99 Xml::closeElement( 'form' );
100
101 return Html::rawElement( 'div', [ 'class' => 'mw-search-form-wrapper' ], $form );
102 }
103
113 protected function shortDialogHtml(
114 $profile,
115 $term,
116 $numResults,
117 $totalResults,
118 $offset,
119 array $options = []
120 ) {
121 $html = '';
122
123 $searchWidget = new SearchInputWidget( $options + [
124 'id' => 'searchText',
125 'name' => 'search',
126 'autofocus' => trim( $term ) === '',
127 'title' => $this->specialSearch->msg( 'searchsuggest-search' )->text(),
128 'value' => $term,
129 'dataLocation' => 'content',
130 'infusable' => true,
131 ] );
132
133 $layout = new \OOUI\ActionFieldLayout( $searchWidget, new \OOUI\ButtonInputWidget( [
134 'type' => 'submit',
135 'label' => $this->specialSearch->msg( 'searchbutton' )->text(),
136 'flags' => [ 'progressive', 'primary' ],
137 ] ), [
138 'align' => 'top',
139 ] );
140
141 $html .= $layout;
142
143 if ( $this->specialSearch->getPrefix() !== '' ) {
144 $html .= Html::hidden( 'prefix', $this->specialSearch->getPrefix() );
145 }
146
147 if ( $totalResults > 0 && $offset < $totalResults ) {
148 $html .= Xml::tags(
149 'div',
150 [
151 'class' => 'results-info',
152 'data-mw-num-results-offset' => $offset,
153 'data-mw-num-results-total' => $totalResults
154 ],
155 $this->specialSearch->msg( 'search-showingresults' )
156 ->numParams( $offset + 1, $offset + $numResults, $totalResults )
157 ->numParams( $numResults )
158 ->parse()
159 );
160 }
161
162 $html .=
163 Html::hidden( 'title', $this->specialSearch->getPageTitle()->getPrefixedText() ) .
164 Html::hidden( 'profile', $profile ) .
165 Html::hidden( 'fulltext', '1' );
166
167 return $html;
168 }
169
177 protected function profileTabsHtml( $profile, $term ) {
178 $bareterm = $this->startsWithImage( $term )
179 ? substr( $term, strpos( $term, ':' ) + 1 )
180 : $term;
181 $lang = $this->specialSearch->getLanguage();
182 $items = [];
183 foreach ( $this->profiles as $id => $profileConfig ) {
184 $profileConfig['parameters']['profile'] = $id;
185 $tooltipParam = isset( $profileConfig['namespace-messages'] )
186 ? $lang->commaList( $profileConfig['namespace-messages'] )
187 : null;
188 $items[] = Xml::tags(
189 'li',
190 [ 'class' => $profile === $id ? 'current' : 'normal' ],
191 $this->makeSearchLink(
192 $bareterm,
193 $this->specialSearch->msg( $profileConfig['message'] )->text(),
194 $this->specialSearch->msg( $profileConfig['tooltip'], $tooltipParam )->text(),
195 $profileConfig['parameters']
196 )
197 );
198 }
199
200 return Html::rawElement(
201 'div',
202 [ 'class' => 'search-types' ],
203 Html::rawElement( 'ul', [], implode( '', $items ) )
204 );
205 }
206
213 protected function startsWithImage( $term ) {
214 $parts = explode( ':', $term );
215 return count( $parts ) > 1
216 ? $this->specialSearch->getContentLanguage()->getNsIndex( $parts[0] ) ===
217 NS_FILE
218 : false;
219 }
220
230 protected function makeSearchLink( $term, $label, $tooltip, array $params = [] ) {
231 $params += [
232 'search' => $term,
233 'fulltext' => 1,
234 ];
235
236 return Xml::element(
237 'a',
238 [
239 'href' => $this->specialSearch->getPageTitle()->getLocalURL( $params ),
240 'title' => $tooltip,
241 ],
242 $label
243 );
244 }
245
255 protected function optionsHtml( $term, $isPowerSearch, $profile ) {
256 $html = '';
257
258 if ( $isPowerSearch ) {
259 $html .= $this->powerSearchBox( $term, [] );
260 } else {
261 $form = '';
262 $this->getHookRunner()->onSpecialSearchProfileForm(
263 $this->specialSearch, $form, $profile, $term, [] );
264 $html .= $form;
265 }
266
267 return $html;
268 }
269
276 protected function powerSearchBox( $term, array $opts ) {
277 $rows = [];
278 $activeNamespaces = $this->specialSearch->getNamespaces();
279 foreach ( $this->searchConfig->searchableNamespaces() as $namespace => $name ) {
280 $subject = $this->namespaceInfo->getSubject( $namespace );
281 if ( !isset( $rows[$subject] ) ) {
282 $rows[$subject] = "";
283 }
284
285 $name = $this->languageConverter->convertNamespace( $namespace );
286 if ( $name === '' ) {
287 $name = $this->specialSearch->msg( 'blanknamespace' )->text();
288 }
289
290 $rows[$subject] .= Html::rawElement(
291 'td',
292 [],
293 Xml::checkLabel(
294 $name,
295 "ns{$namespace}",
296 "mw-search-ns{$namespace}",
297 in_array( $namespace, $activeNamespaces )
298 )
299 );
300 }
301
302 // Lays out namespaces in multiple floating two-column tables so they'll
303 // be arranged nicely while still accomodating diferent screen widths
304 $tableRows = [];
305 foreach ( $rows as $row ) {
306 $tableRows[] = Html::rawElement( 'tr', [], $row );
307 }
308 $namespaceTables = [];
309 foreach ( array_chunk( $tableRows, 4 ) as $chunk ) {
310 $namespaceTables[] = implode( '', $chunk );
311 }
312
313 $showSections = [
314 'namespaceTables' => "<table>" . implode( '</table><table>', $namespaceTables ) . '</table>',
315 ];
316 $this->getHookRunner()->onSpecialSearchPowerBox( $showSections, $term, $opts );
317
318 $hidden = '';
319 foreach ( $opts as $key => $value ) {
320 $hidden .= Html::hidden( $key, $value );
321 }
322
323 $divider = Html::rawElement( 'div', [ 'class' => 'divider' ], '' );
324
325 // Stuff to feed SpecialSearch::saveNamespaces()
326 $user = $this->specialSearch->getUser();
327 $remember = '';
328 if ( $user->isRegistered() ) {
329 $remember = $divider . Xml::checkLabel(
330 $this->specialSearch->msg( 'powersearch-remember' )->text(),
331 'nsRemember',
332 'mw-search-powersearch-remember',
333 false,
334 // The token goes here rather than in a hidden field so it
335 // is only sent when necessary (not every form submission)
336 [ 'value' => $user->getEditToken(
337 'searchnamespace',
338 $this->specialSearch->getRequest()
339 ) ]
340 );
341 }
342
343 // Temporary variables to reduce nesting needed
344 $toggleBoxContents =
345 Html::rawElement( 'label', [], $this->specialSearch->msg( 'powersearch-togglelabel' )->escaped() ) .
346 Html::rawElement(
347 'input',
348 [
349 'type' => 'button',
350 'id' => 'mw-search-toggleall',
351 'value' => $this->specialSearch->msg( 'powersearch-toggleall' )->text(),
352 ]
353 ) .
354 Html::rawElement(
355 'input',
356 [
357 'type' => 'button',
358 'id' => 'mw-search-togglenone',
359 'value' => $this->specialSearch->msg( 'powersearch-togglenone' )->text(),
360 ]
361 );
362 $fieldSetContents =
363 Html::rawElement( 'legend', [], $this->specialSearch->msg( 'powersearch-legend' )->escaped() ) .
364 Html::rawElement( 'h4', [], $this->specialSearch->msg( 'powersearch-ns' )->parse() ) .
365 // Handled by JavaScript if available
366 Html::rawElement(
367 'div',
368 [ 'id' => 'mw-search-togglebox' ],
369 $toggleBoxContents
370 ) .
371 $divider . implode( $divider, $showSections ) . $hidden . $remember;
372
373 return Html::rawElement( 'fieldset', [ 'id' => 'mw-searchoptions' ], $fieldSetContents );
374 }
375
380 protected function getHookContainer() {
382 }
383
390 protected function getHookRunner() {
391 return $this->hookRunner;
392 }
393}
const NS_FILE
Definition Defines.php:70
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
This class is a collection of static functions that serve two purposes:
Definition Html.php:49
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
optionsHtml( $term, $isPowerSearch, $profile)
Generates HTML for advanced options available with the currently selected search profile.
startsWithImage( $term)
Check if query starts with image: prefix.
__construct(SpecialSearch $specialSearch, SearchEngineConfig $searchConfig, HookContainer $hookContainer, ILanguageConverter $languageConverter, NamespaceInfo $namespaceInfo, array $profiles)
shortDialogHtml( $profile, $term, $numResults, $totalResults, $offset, array $options=[])
profileTabsHtml( $profile, $term)
Generates HTML for the list of available search profiles.
render( $profile, $term, $numResults, $totalResults, $offset, $isPowerSearch, array $options=[])
makeSearchLink( $term, $label, $tooltip, array $params=[])
Make a search link with some target namespaces.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Configuration handling class for SearchEngine.
implements Special:Search - Run text & title search and display the output
Module of static functions for generating XML.
Definition Xml.php:28
The shared interface for all language converters.
if(!isset( $args[0])) $lang