Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4074375
SearchFormWidget.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
SearchFormWidget.php
View Options
<?php
namespace
MediaWiki\Search\SearchWidgets
;
use
MediaWiki\Config\ServiceOptions
;
use
MediaWiki\HookContainer\HookContainer
;
use
MediaWiki\HookContainer\HookRunner
;
use
MediaWiki\Html\Html
;
use
MediaWiki\Language\ILanguageConverter
;
use
MediaWiki\MainConfigNames
;
use
MediaWiki\Specials\SpecialSearch
;
use
MediaWiki\Title\NamespaceInfo
;
use
MediaWiki\Widget\SearchInputWidget
;
use
OOUI\ActionFieldLayout
;
use
OOUI\ButtonInputWidget
;
use
OOUI\CheckboxInputWidget
;
use
OOUI\FieldLayout
;
use
SearchEngineConfig
;
class
SearchFormWidget
{
/** @internal For use by SpecialSearch only */
public
const
CONSTRUCTOR_OPTIONS
=
[
MainConfigNames
::
CapitalLinks
,
];
private
ServiceOptions
$options
;
protected
SpecialSearch
$specialSearch
;
protected
SearchEngineConfig
$searchConfig
;
private
HookContainer
$hookContainer
;
private
HookRunner
$hookRunner
;
private
ILanguageConverter
$languageConverter
;
private
NamespaceInfo
$namespaceInfo
;
protected
array
$profiles
;
public
function
__construct
(
ServiceOptions
$options
,
SpecialSearch
$specialSearch
,
SearchEngineConfig
$searchConfig
,
HookContainer
$hookContainer
,
ILanguageConverter
$languageConverter
,
NamespaceInfo
$namespaceInfo
,
array
$profiles
)
{
$options
->
assertRequiredOptions
(
self
::
CONSTRUCTOR_OPTIONS
);
$this
->
options
=
$options
;
$this
->
specialSearch
=
$specialSearch
;
$this
->
searchConfig
=
$searchConfig
;
$this
->
hookContainer
=
$hookContainer
;
$this
->
hookRunner
=
new
HookRunner
(
$hookContainer
);
$this
->
languageConverter
=
$languageConverter
;
$this
->
namespaceInfo
=
$namespaceInfo
;
$this
->
profiles
=
$profiles
;
}
/**
* @param string $profile The current search profile
* @param string $term The current search term
* @param int $numResults The number of results shown
* @param int $totalResults The total estimated results found
* @param bool $approximateTotalResults Whether $totalResults is approximate or not
* @param int $offset Current offset in search results
* @param bool $isPowerSearch Is the 'advanced' section open?
* @param array $options Widget options
* @return string HTML
*/
public
function
render
(
$profile
,
$term
,
$numResults
,
$totalResults
,
$approximateTotalResults
,
$offset
,
$isPowerSearch
,
array
$options
=
[]
)
{
$user
=
$this
->
specialSearch
->
getUser
();
$form
=
Html
::
openElement
(
'form'
,
[
'id'
=>
$isPowerSearch
?
'powersearch'
:
'search'
,
// T151903: default to POST in case JS is disabled
'method'
=>
(
$isPowerSearch
&&
$user
->
isRegistered
()
)
?
'post'
:
'get'
,
'action'
=>
wfScript
(),
]
)
.
Html
::
rawElement
(
'div'
,
[
'id'
=>
'mw-search-top-table'
],
$this
->
shortDialogHtml
(
$profile
,
$term
,
$numResults
,
$totalResults
,
$approximateTotalResults
,
$offset
,
$options
)
)
.
Html
::
rawElement
(
'div'
,
[
'class'
=>
'mw-search-visualclear'
]
)
.
Html
::
rawElement
(
'div'
,
[
'class'
=>
'mw-search-profile-tabs'
],
$this
->
profileTabsHtml
(
$profile
,
$term
)
.
Html
::
rawElement
(
'div'
,
[
'style'
=>
'clear:both'
]
)
)
.
$this
->
optionsHtml
(
$term
,
$isPowerSearch
,
$profile
)
.
Html
::
closeElement
(
'form'
);
return
Html
::
rawElement
(
'div'
,
[
'class'
=>
'mw-search-form-wrapper'
],
$form
);
}
/**
* @param string $profile The current search profile
* @param string $term The current search term
* @param int $numResults The number of results shown
* @param int $totalResults The total estimated results found
* @param bool $approximateTotalResults Whether $totalResults is approximate or not
* @param int $offset Current offset in search results
* @param array $options Widget options
* @return string HTML
*/
protected
function
shortDialogHtml
(
$profile
,
$term
,
$numResults
,
$totalResults
,
$approximateTotalResults
,
$offset
,
array
$options
=
[]
)
{
$autoCapHint
=
$this
->
options
->
get
(
MainConfigNames
::
CapitalLinks
);
$searchWidget
=
new
SearchInputWidget
(
$options
+
[
'id'
=>
'searchText'
,
'name'
=>
'search'
,
'autofocus'
=>
trim
(
$term
)
===
''
,
'title'
=>
$this
->
specialSearch
->
msg
(
'searchsuggest-search'
)->
text
(),
'value'
=>
$term
,
'dataLocation'
=>
'content'
,
'infusable'
=>
true
,
'autocapitalize'
=>
$autoCapHint
?
'sentences'
:
'none'
,
]
);
$html
=
new
ActionFieldLayout
(
$searchWidget
,
new
ButtonInputWidget
(
[
'type'
=>
'submit'
,
'label'
=>
$this
->
specialSearch
->
msg
(
'searchbutton'
)->
text
(),
'flags'
=>
[
'progressive'
,
'primary'
],
]
),
[
'align'
=>
'top'
,
]
);
if
(
$this
->
specialSearch
->
getPrefix
()
!==
''
)
{
$html
.=
Html
::
hidden
(
'prefix'
,
$this
->
specialSearch
->
getPrefix
()
);
}
if
(
$totalResults
>
0
&&
$offset
<
$totalResults
)
{
$html
.=
Html
::
rawElement
(
'div'
,
[
'class'
=>
'results-info'
,
'data-mw-num-results-offset'
=>
$offset
,
'data-mw-num-results-total'
=>
$totalResults
,
'data-mw-num-results-approximate-total'
=>
$approximateTotalResults
?
"true"
:
"false"
],
$this
->
specialSearch
->
msg
(
$approximateTotalResults
?
'search-showingresults-approximate'
:
'search-showingresults'
)
->
numParams
(
$offset
+
1
,
$offset
+
$numResults
,
$totalResults
)
->
numParams
(
$numResults
)
->
parse
()
);
}
$html
.=
Html
::
hidden
(
'title'
,
$this
->
specialSearch
->
getPageTitle
()->
getPrefixedText
()
)
.
Html
::
hidden
(
'profile'
,
$profile
)
.
Html
::
hidden
(
'fulltext'
,
'1'
);
return
$html
;
}
/**
* Generates HTML for the list of available search profiles.
*
* @param string $profile The currently selected profile
* @param string $term The user provided search terms
* @return string HTML
*/
protected
function
profileTabsHtml
(
$profile
,
$term
)
{
$bareterm
=
$this
->
startsWithImage
(
$term
)
?
substr
(
$term
,
strpos
(
$term
,
':'
)
+
1
)
:
$term
;
$lang
=
$this
->
specialSearch
->
getLanguage
();
$items
=
[];
foreach
(
$this
->
profiles
as
$id
=>
$profileConfig
)
{
$profileConfig
[
'parameters'
][
'profile'
]
=
$id
;
$tooltipParam
=
isset
(
$profileConfig
[
'namespace-messages'
]
)
?
$lang
->
commaList
(
$profileConfig
[
'namespace-messages'
]
)
:
null
;
$items
[]
=
Html
::
rawElement
(
'li'
,
[
'class'
=>
$profile
===
$id
?
'current'
:
'normal'
],
$this
->
makeSearchLink
(
$bareterm
,
$this
->
specialSearch
->
msg
(
$profileConfig
[
'message'
]
)->
text
(),
$this
->
specialSearch
->
msg
(
$profileConfig
[
'tooltip'
],
$tooltipParam
)->
text
(),
$profileConfig
[
'parameters'
]
)
);
}
return
Html
::
rawElement
(
'div'
,
[
'class'
=>
'search-types'
],
Html
::
rawElement
(
'ul'
,
[],
implode
(
''
,
$items
)
)
);
}
/**
* Check if query starts with image: prefix
*
* @param string $term The string to check
* @return bool
*/
protected
function
startsWithImage
(
$term
)
{
$parts
=
explode
(
':'
,
$term
);
return
count
(
$parts
)
>
1
&&
$this
->
specialSearch
->
getContentLanguage
()->
getNsIndex
(
$parts
[
0
]
)
===
NS_FILE
;
}
/**
* Make a search link with some target namespaces
*
* @param string $term The term to search for
* @param string $label Link's text
* @param string $tooltip Link's tooltip
* @param array $params Query string parameters
* @return string HTML fragment
*/
protected
function
makeSearchLink
(
$term
,
$label
,
$tooltip
,
array
$params
=
[]
)
{
$params
+=
[
'search'
=>
$term
,
'fulltext'
=>
1
,
];
return
Html
::
element
(
'a'
,
[
'href'
=>
$this
->
specialSearch
->
getPageTitle
()->
getLocalURL
(
$params
),
'title'
=>
$tooltip
,
],
$label
);
}
/**
* Generates HTML for advanced options available with the currently
* selected search profile.
*
* @param string $term User provided search term
* @param bool $isPowerSearch Is the advanced search profile enabled?
* @param string $profile The current search profile
* @return string HTML
*/
protected
function
optionsHtml
(
$term
,
$isPowerSearch
,
$profile
)
{
if
(
$isPowerSearch
)
{
$html
=
$this
->
powerSearchBox
(
$term
,
[]
);
}
else
{
$html
=
''
;
$this
->
getHookRunner
()->
onSpecialSearchProfileForm
(
$this
->
specialSearch
,
$html
,
$profile
,
$term
,
[]
);
}
return
$html
;
}
/**
* @param string $term The current search term
* @param array $opts Additional key/value pairs that will be submitted
* with the generated form.
* @return string HTML
*/
protected
function
powerSearchBox
(
$term
,
array
$opts
)
{
$namespaceTables
=
[
'namespaceTables'
=>
$this
->
createCheckboxesForEverySearchableNamespace
()
];
$this
->
getHookRunner
()->
onSpecialSearchPowerBox
(
$namespaceTables
,
$term
,
$opts
);
$outputHtml
=
''
;
$outputHtml
.=
$this
->
createSearchBoxHeadHtml
();
$outputHtml
.=
$this
->
searchFilterSeparatorHtml
();
$outputHtml
.=
implode
(
$this
->
searchFilterSeparatorHtml
(),
$namespaceTables
);
$outputHtml
.=
$this
->
createHiddenOptsHtml
(
$opts
);
// Stuff to feed SpecialSearch::saveNamespaces()
if
(
$this
->
specialSearch
->
getUser
()->
isRegistered
()
)
{
$outputHtml
.=
$this
->
searchFilterSeparatorHtml
();
$outputHtml
.=
$this
->
createPowerSearchRememberCheckBoxHtml
();
}
return
Html
::
rawElement
(
'fieldset'
,
[
'id'
=>
'mw-searchoptions'
],
$outputHtml
);
}
/**
* @return HookContainer
* @since 1.35
*/
protected
function
getHookContainer
()
{
return
$this
->
hookContainer
;
}
/**
* @return HookRunner
* @since 1.35
* @internal This is for use by core only. Hook interfaces may be removed
* without notice.
*/
protected
function
getHookRunner
()
{
return
$this
->
hookRunner
;
}
private
function
searchFilterSeparatorHtml
():
string
{
return
Html
::
rawElement
(
'div'
,
[
'class'
=>
'divider'
],
''
);
}
private
function
createPowerSearchRememberCheckBoxHtml
():
string
{
return
new
FieldLayout
(
new
CheckboxInputWidget
(
[
'name'
=>
'nsRemember'
,
'selected'
=>
false
,
'inputId'
=>
'mw-search-powersearch-remember'
,
// The token goes here rather than in a hidden field so it
// is only sent when necessary (not every form submission)
'value'
=>
$this
->
specialSearch
->
getContext
()->
getCsrfTokenSet
()
->
getToken
(
'searchnamespace'
)
]
),
[
'label'
=>
$this
->
specialSearch
->
msg
(
'powersearch-remember'
)->
text
(),
'align'
=>
'inline'
]
);
}
private
function
createNamespaceToggleBoxHtml
():
string
{
$toggleBoxContents
=
""
;
$toggleBoxContents
.=
Html
::
rawElement
(
'label'
,
[],
$this
->
specialSearch
->
msg
(
'powersearch-togglelabel'
)->
escaped
()
);
$toggleBoxContents
.=
Html
::
rawElement
(
'input'
,
[
'type'
=>
'button'
,
'id'
=>
'mw-search-toggleall'
,
'value'
=>
$this
->
specialSearch
->
msg
(
'powersearch-toggleall'
)->
text
(),
]
);
$toggleBoxContents
.=
Html
::
rawElement
(
'input'
,
[
'type'
=>
'button'
,
'id'
=>
'mw-search-togglenone'
,
'value'
=>
$this
->
specialSearch
->
msg
(
'powersearch-togglenone'
)->
text
(),
]
);
// Handled by JavaScript if available
return
Html
::
rawElement
(
'div'
,
[
'id'
=>
'mw-search-togglebox'
],
$toggleBoxContents
);
}
private
function
createSearchBoxHeadHtml
():
string
{
return
Html
::
rawElement
(
'legend'
,
[],
$this
->
specialSearch
->
msg
(
'powersearch-legend'
)->
escaped
()
)
.
Html
::
rawElement
(
'h4'
,
[],
$this
->
specialSearch
->
msg
(
'powersearch-ns'
)->
parse
()
)
.
$this
->
createNamespaceToggleBoxHtml
();
}
private
function
createNamespaceCheckbox
(
int
$namespace
,
array
$activeNamespaces
):
string
{
$namespaceDisplayName
=
$this
->
getNamespaceDisplayName
(
$namespace
);
return
new
FieldLayout
(
new
CheckboxInputWidget
(
[
'name'
=>
"ns{$namespace}"
,
'selected'
=>
in_array
(
$namespace
,
$activeNamespaces
),
'inputId'
=>
"mw-search-ns{$namespace}"
,
'value'
=>
'1'
]
),
[
'label'
=>
$namespaceDisplayName
,
'align'
=>
'inline'
]
);
}
private
function
getNamespaceDisplayName
(
int
$namespace
):
string
{
$name
=
$this
->
languageConverter
->
convertNamespace
(
$namespace
);
if
(
$name
===
''
)
{
$name
=
$this
->
specialSearch
->
msg
(
'blanknamespace'
)->
text
();
}
return
$name
;
}
private
function
createCheckboxesForEverySearchableNamespace
():
string
{
$rows
=
[];
$activeNamespaces
=
$this
->
specialSearch
->
getNamespaces
();
foreach
(
$this
->
searchConfig
->
searchableNamespaces
()
as
$namespace
=>
$name
)
{
$subject
=
$this
->
namespaceInfo
->
getSubject
(
$namespace
);
if
(
!
isset
(
$rows
[
$subject
]
)
)
{
$rows
[
$subject
]
=
""
;
}
$rows
[
$subject
]
.=
$this
->
createNamespaceCheckbox
(
$namespace
,
$activeNamespaces
);
}
return
'<div class="checkbox-wrapper"><div>'
.
implode
(
'</div><div>'
,
$rows
)
.
'</div></div>'
;
}
private
function
createHiddenOptsHtml
(
array
$opts
):
string
{
$hidden
=
''
;
foreach
(
$opts
as
$key
=>
$value
)
{
$hidden
.=
Html
::
hidden
(
$key
,
$value
);
}
return
$hidden
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Aug 17 2026, 15:28 (10 w, 3 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
dd/3d/bec07ecc1e4f2c94fb566de86f18
Default Alt Text
SearchFormWidget.php (12 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment