'SearchDatabase' => __DIR__ . '/includes/search/SearchDatabase.php',
'SearchDump' => __DIR__ . '/maintenance/dumpIterator.php',
'SearchEngine' => __DIR__ . '/includes/search/SearchEngine.php',
+ 'SearchEngineConfig' => __DIR__ . '/includes/search/SearchEngineConfig.php',
'SearchEngineDummy' => __DIR__ . '/includes/search/SearchEngine.php',
+ 'SearchEngineFactory' => __DIR__ . '/includes/search/SearchEngineFactory.php',
'SearchExactMatchRescorer' => __DIR__ . '/includes/search/SearchExactMatchRescorer.php',
'SearchHighlighter' => __DIR__ . '/includes/search/SearchHighlighter.php',
'SearchMssql' => __DIR__ . '/includes/search/SearchMssql.php',
'SearchMySQL' => __DIR__ . '/includes/search/SearchMySQL.php',
'SearchNearMatchResultSet' => __DIR__ . '/includes/search/SearchNearMatchResultSet.php',
+ 'SearchNearMatcher' => __DIR__ . '/includes/search/SearchNearMatcher.php',
'SearchOracle' => __DIR__ . '/includes/search/SearchOracle.php',
'SearchPostgres' => __DIR__ . '/includes/search/SearchPostgres.php',
'SearchResult' => __DIR__ . '/includes/search/SearchResult.php',
namespace MediaWiki;
use ConfigFactory;
+use EventRelayerGroup;
use GlobalVarConfig;
use Config;
use Hooks;
return $this->getService( 'EventRelayerGroup' );
}
+ /**
+ * @return SearchEngine
+ */
+ public function newSearchEngine() {
+ // New engine object every time, since they keep state
+ return $this->getService( 'SearchEngineFactory' )->create();
+ }
+
+ /**
+ * @return SearchEngineFactory
+ */
+ public function getSearchEngineFactory() {
+ return $this->getService( 'SearchEngineFactory' );
+ }
+
+ /**
+ * @return SearchEngineConfig
+ */
+ public function getSearchEngineConfig() {
+ return $this->getService( 'SearchEngineConfig' );
+ }
+
///////////////////////////////////////////////////////////////////////////
// NOTE: When adding a service getter here, don't forget to add a test
// case for it in MediaWikiServicesTest::provideGetters() and in
return new EventRelayerGroup( $services->getMainConfig()->get( 'EventRelayerConfig' ) );
},
+ 'SearchEngineFactory' => function( MediaWikiServices $services ) {
+ // Create search engine
+ return new SearchEngineFactory( $services->getService( 'SearchEngineConfig' ) );
+ },
+
+ 'SearchEngineConfig' => function( MediaWikiServices $services ) {
+ // Create a search engine config from main config.
+ $config = $services->getService( 'MainConfig' );
+ return new SearchEngineConfig( $config );
+ }
+
///////////////////////////////////////////////////////////////////////////
// NOTE: When adding a service here, don't forget to add a getter function
// in the MediaWikiServices class. The convenience getter should just call
$this->reuseConnection( $dbr );
foreach ( $res as $row ) {
- $timestamps[(int)$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp;
+ $timestamps[$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp;
}
return $timestamps;
* @file
*/
+use MediaWiki\MediaWikiServices;
+
/**
* @ingroup API
*/
* @param array &$results Put results here. Keys have to be integers.
*/
protected function search( $search, $limit, $namespaces, $resolveRedir, &$results ) {
-
- $searchEngine = SearchEngine::create();
+ $searchEngine = MediaWikiServices::getInstance()->newSearchEngine();
$searchEngine->setLimitOffset( $limit );
$searchEngine->setNamespaces( $namespaces );
$titles = $searchEngine->extractTitles( $searchEngine->completionSearchWithVariants( $search ) );
* @throws MWException
*/
public static function getOpenSearchTemplate( $type ) {
- global $wgOpenSearchTemplate, $wgCanonicalServer;
+ $config = MediaWikiServices::getInstance()->getSearchEngineConfig();
+ $template = $config->getConfig()->get( 'OpenSearchTemplate' );
- if ( $wgOpenSearchTemplate && $type === 'application/x-suggestions+json' ) {
- return $wgOpenSearchTemplate;
+ if ( $template && $type === 'application/x-suggestions+json' ) {
+ return $template;
}
- $ns = implode( '|', SearchEngine::defaultNamespaces() );
+ $ns = implode( '|', $config->defaultNamespaces() );
if ( !$ns ) {
$ns = '0';
}
switch ( $type ) {
case 'application/x-suggestions+json':
- return $wgCanonicalServer . wfScript( 'api' )
+ return $config->getConfig()->get( 'CanonicalServer' ) . wfScript( 'api' )
. '?action=opensearch&search={searchTerms}&namespace=' . $ns;
case 'application/x-suggestions+xml':
- return $wgCanonicalServer . wfScript( 'api' )
+ return $config->getConfig()->get( 'CanonicalServer' ) . wfScript( 'api' )
. '?action=opensearch&format=xml&search={searchTerms}&namespace=' . $ns;
default:
<?php
+use MediaWiki\MediaWikiServices;
+
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
$namespaces = $params['namespace'];
$offset = $params['offset'];
- $searchEngine = SearchEngine::create();
+ $searchEngine = MediaWikiServices::getInstance()->newSearchEngine();
$searchEngine->setLimitOffset( $limit + 1, $offset );
$searchEngine->setNamespaces( $namespaces );
$titles = $searchEngine->extractTitles( $searchEngine->completionSearchWithVariants( $search ) );
* @file
*/
+use MediaWiki\MediaWikiServices;
+
/**
* Query module to perform full text search within wiki titles and content
*
}
// Create search engine instance and set options
- $search = isset( $params['backend'] ) && $params['backend'] != self::BACKEND_NULL_PARAM ?
- SearchEngine::create( $params['backend'] ) : SearchEngine::create();
+ $type = isset( $params['backend'] ) && $params['backend'] != self::BACKEND_NULL_PARAM ?
+ $params['backend'] : null;
+ $search = MediaWikiServices::getInstance()->getSearchEngineFactory()->create( $type );
$search->setLimitOffset( $limit + 1, $params['offset'] );
$search->setNamespaces( $params['namespace'] );
$search->setFeatureData( 'rewrite', (bool)$params['enablerewrites'] );
} elseif ( $what == 'nearmatch' ) {
// near matches must receive the user input as provided, otherwise
// the near matches within namespaces are lost.
- $matches = SearchEngine::getNearMatchResultSet( $params['search'] );
+ $matches = $search->getNearMatcher( $this->getConfig() )
+ ->getNearMatchResultSet( $params['search'] );
} else {
// We default to title searches; this is a terrible legacy
// of the way we initially set up the MySQL fulltext-based
'enablerewrites' => false,
];
- $alternatives = SearchEngine::getSearchTypes();
+ $searchConfig = MediaWikiServices::getInstance()->getSearchEngineConfig();
+ $alternatives = $searchConfig->getSearchTypes();
if ( count( $alternatives ) > 1 ) {
if ( $alternatives[0] === null ) {
$alternatives[0] = self::BACKEND_NULL_PARAM;
}
$params['backend'] = [
- ApiBase::PARAM_DFLT => $this->getConfig()->get( 'SearchType' ),
+ ApiBase::PARAM_DFLT => $searchConfig->getSearchType(),
ApiBase::PARAM_TYPE => $alternatives,
];
}
$sql
);
+ // last check for invalid utf8
+ $sql = UtfNormal\Validator::cleanUp( $sql );
+
self::$query[] = [
'sql' => $sql,
'function' => $function,
* @ingroup Search
*/
+use MediaWiki\MediaWikiServices;
+
/**
* Database independant search index updater
*
* Perform actual update for the entry
*/
public function doUpdate() {
- global $wgDisableSearchUpdate;
+ $config = MediaWikiServices::getInstance()->getSearchEngineConfig();
- if ( $wgDisableSearchUpdate || !$this->id ) {
+ if ( $config->getConfig()->get( 'DisableSearchUpdate' ) || !$this->id ) {
return;
}
- foreach ( SearchEngine::getSearchTypes() as $type ) {
- $search = SearchEngine::create( $type );
+ $seFactory = MediaWikiServices::getInstance()->getSearchEngineFactory();
+ foreach ( $config->getSearchTypes() as $type ) {
+ $search = $seFactory->create( $type );
if ( !$search->supports( 'search-update' ) ) {
continue;
}
$text = $search->getTextFromContent( $this->title, $this->content );
if ( !$search->textAlreadyUpdatedForIndex() ) {
- $text = self::updateText( $text );
+ $text = $this->updateText( $text, $search );
}
# Perform the actual update
* If you're using a real search engine, you'll probably want to override
* this behavior and do something nicer with the original wikitext.
* @param string $text
+ * @param SearchEngine $se Search engine
* @return string
*/
- public static function updateText( $text ) {
+ public function updateText( $text, SearchEngine $se = null ) {
global $wgContLang;
# Language-specific strip/conversion
$text = $wgContLang->normalizeForSearch( $text );
- $lc = SearchEngine::legalSearchChars() . '&#;';
+ $se = $se ?: MediaWikiServices::getInstance()->newSearchEngine();
+ $lc = $se->legalSearchChars() . '&#;';
$text = preg_replace( "/<\\/?\\s*[A-Za-z][^>]*?>/",
' ', $wgContLang->lc( " " . $text . " " ) ); # Strip HTML markup
foreach ( $fields as $key => $value ) {
if ( $value instanceof HTMLFormField ) {
- $v = isset( $this->mFieldData[$key] )
+ $v = array_key_exists( $key, $this->mFieldData )
? $this->mFieldData[$key]
: $value->getDefault();
$tmp = $m[1];
}
if ( substr( $tmp, 0, 2 ) == 'wp' &&
- !isset( $alldata[$tmp] ) &&
- isset( $alldata[substr( $tmp, 2 )] )
+ !array_key_exists( $tmp, $alldata ) &&
+ array_key_exists( substr( $tmp, 2 ), $alldata )
) {
// Adjust for name mangling.
$tmp = substr( $tmp, 2 );
$data = $alldata;
while ( $keys ) {
$key = array_shift( $keys );
- if ( !is_array( $data ) || !isset( $data[$key] ) ) {
+ if ( !is_array( $data ) || !array_key_exists( $key, $data ) ) {
continue 2;
}
$data = $data[$key];
$fields = $this->createFieldsForKey( $key );
foreach ( $fields as $fieldname => $field ) {
- $v = isset( $values[$fieldname] )
+ $v = array_key_exists( $fieldname, $values )
? $values[$fieldname]
: $field->getDefault();
*
* return CatConfig::newFromRow( $dbr->selectRow( ... ) );
* },
- * array(
+ * [
* // Calling touchCheckKey() on this key invalidates the cache
- * 'checkKeys' => array( $cache->makeKey( 'site-cat-config' ) ),
+ * 'checkKeys' => [ $cache->makeKey( 'site-cat-config' ) ],
* // Try to only let one datacenter thread manage cache updates at a time
* 'lockTSE' => 30
- * )
+ * ]
* );
* @endcode
*
*
* return CatState::newFromResults( $dbr->select( ... ) );
* },
- * array(
+ * [
* // The "check" keys that represent things the value depends on;
* // Calling touchCheckKey() on any of them invalidates the cache
- * 'checkKeys' => array(
+ * 'checkKeys' => [
* $cache->makeKey( 'sustenance-bowls', $cat->getRoomId() ),
* $cache->makeKey( 'people-present', $cat->getHouseId() ),
* $cache->makeKey( 'cat-laws', $cat->getCityId() ),
- * )
- * )
+ * ]
+ * ]
* );
* @endcode
*
* $setOpts += Database::getCacheSetOptions( $dbr );
*
* // Start off with the last cached list
- * $list = $oldValue ?: array();
+ * $list = $oldValue ?: [];
* // Fetch the last 100 relevant rows in descending order;
* // only fetch rows newer than $list[0] to reduce scanning
* $rows = iterator_to_array( $dbr->select( ... ) );
* return array_slice( array_merge( $new, $list ), 0, 100 );
* },
* // Try to only let one datacenter thread manage cache updates at a time
- * array( 'lockTSE' => 30 )
+ * [ 'lockTSE' => 30 ]
* );
* @endcode
*
* @defgroup Search Search
*/
+use MediaWiki\MediaWikiServices;
+
/**
* Contain a class for special pages
* @ingroup Search
*/
-class SearchEngine {
+abstract class SearchEngine {
/** @var string */
public $prefix = '';
* @param string $term
* @return string
*/
- function transformSearchTerm( $term ) {
+ public function transformSearchTerm( $term ) {
return $term;
}
+ /**
+ * Get service class to finding near matches.
+ * @param Config $config Configuration to use for the matcher.
+ * @return SearchNearMatcher
+ */
+ public function getNearMatcher( Config $config ) {
+ return new SearchNearMatcher( $config );
+ }
+
+ /**
+ * Get near matcher for default SearchEngine.
+ * @return SearchNearMatcher
+ */
+ protected static function defaultNearMatcher() {
+ $config = MediaWikiServices::getInstance()->getMainConfig();
+ return MediaWikiServices::getInstance()->newSearchEngine()->getNearMatcher( $config );
+ }
+
/**
* If an exact title match can be found, or a very slightly close match,
* return the title. If no match, returns NULL.
- *
+ * @deprecated since 1.27; Use SearchEngine::getNearMatcher()
* @param string $searchterm
* @return Title
*/
public static function getNearMatch( $searchterm ) {
- $title = self::getNearMatchInternal( $searchterm );
-
- Hooks::run( 'SearchGetNearMatchComplete', [ $searchterm, &$title ] );
- return $title;
+ return static::defaultNearMatcher()->getNearMatch( $searchterm );
}
/**
* Do a near match (see SearchEngine::getNearMatch) and wrap it into a
* SearchResultSet.
- *
+ * @deprecated since 1.27; Use SearchEngine::getNearMatcher()
* @param string $searchterm
* @return SearchResultSet
*/
public static function getNearMatchResultSet( $searchterm ) {
- return new SearchNearMatchResultSet( self::getNearMatch( $searchterm ) );
+ return static::defaultNearMatcher()->getNearMatchResultSet( $searchterm );
}
/**
- * Really find the title match.
- * @param string $searchterm
- * @return null|Title
+ * Get chars legal for search.
+ * NOTE: usage as static is deprecated and preserved only as BC measure
+ * @return string
*/
- private static function getNearMatchInternal( $searchterm ) {
- global $wgContLang, $wgEnableSearchContributorsByIP;
-
- $allSearchTerms = [ $searchterm ];
-
- if ( $wgContLang->hasVariants() ) {
- $allSearchTerms = array_unique( array_merge(
- $allSearchTerms,
- $wgContLang->autoConvertToAllVariants( $searchterm )
- ) );
- }
-
- $titleResult = null;
- if ( !Hooks::run( 'SearchGetNearMatchBefore', [ $allSearchTerms, &$titleResult ] ) ) {
- return $titleResult;
- }
-
- foreach ( $allSearchTerms as $term ) {
-
- # Exact match? No need to look further.
- $title = Title::newFromText( $term );
- if ( is_null( $title ) ) {
- return null;
- }
-
- # Try files if searching in the Media: namespace
- if ( $title->getNamespace() == NS_MEDIA ) {
- $title = Title::makeTitle( NS_FILE, $title->getText() );
- }
-
- if ( $title->isSpecialPage() || $title->isExternal() || $title->exists() ) {
- return $title;
- }
-
- # See if it still otherwise has content is some sane sense
- $page = WikiPage::factory( $title );
- if ( $page->hasViewableContent() ) {
- return $title;
- }
-
- if ( !Hooks::run( 'SearchAfterNoDirectMatch', [ $term, &$title ] ) ) {
- return $title;
- }
-
- # Now try all lower case (i.e. first letter capitalized)
- $title = Title::newFromText( $wgContLang->lc( $term ) );
- if ( $title && $title->exists() ) {
- return $title;
- }
-
- # Now try capitalized string
- $title = Title::newFromText( $wgContLang->ucwords( $term ) );
- if ( $title && $title->exists() ) {
- return $title;
- }
-
- # Now try all upper case
- $title = Title::newFromText( $wgContLang->uc( $term ) );
- if ( $title && $title->exists() ) {
- return $title;
- }
-
- # Now try Word-Caps-Breaking-At-Word-Breaks, for hyphenated names etc
- $title = Title::newFromText( $wgContLang->ucwordbreaks( $term ) );
- if ( $title && $title->exists() ) {
- return $title;
- }
-
- // Give hooks a chance at better match variants
- $title = null;
- if ( !Hooks::run( 'SearchGetNearMatch', [ $term, &$title ] ) ) {
- return $title;
- }
- }
-
- $title = Title::newFromText( $searchterm );
-
- # Entering an IP address goes to the contributions page
- if ( $wgEnableSearchContributorsByIP ) {
- if ( ( $title->getNamespace() == NS_USER && User::isIP( $title->getText() ) )
- || User::isIP( trim( $searchterm ) ) ) {
- return SpecialPage::getTitleFor( 'Contributions', $title->getDBkey() );
- }
- }
-
- # Entering a user goes to the user page whether it's there or not
- if ( $title->getNamespace() == NS_USER ) {
- return $title;
- }
-
- # Go to images that exist even if there's no local page.
- # There may have been a funny upload, or it may be on a shared
- # file repository such as Wikimedia Commons.
- if ( $title->getNamespace() == NS_FILE ) {
- $image = wfFindFile( $title );
- if ( $image ) {
- return $title;
- }
- }
-
- # MediaWiki namespace? Page may be "implied" if not customized.
- # Just return it, with caps forced as the message system likes it.
- if ( $title->getNamespace() == NS_MEDIAWIKI ) {
- return Title::makeTitle( NS_MEDIAWIKI, $wgContLang->ucfirst( $title->getText() ) );
- }
-
- # Quoted term? Try without the quotes...
- $matches = [];
- if ( preg_match( '/^"([^"]+)"$/', $searchterm, $matches ) ) {
- return SearchEngine::getNearMatch( $matches[1] );
- }
-
- return null;
- }
-
public static function legalSearchChars() {
return "A-Za-z_'.0-9\\x80-\\xFF\\-";
}
return $parsed;
}
- /**
- * Make a list of searchable namespaces and their canonical names.
- * @return array
- */
- public static function searchableNamespaces() {
- global $wgContLang;
- $arr = [];
- foreach ( $wgContLang->getNamespaces() as $ns => $name ) {
- if ( $ns >= NS_MAIN ) {
- $arr[$ns] = $name;
- }
- }
-
- Hooks::run( 'SearchableNamespaces', [ &$arr ] );
- return $arr;
- }
-
- /**
- * Extract default namespaces to search from the given user's
- * settings, returning a list of index numbers.
- *
- * @param user $user
- * @return array
- */
- public static function userNamespaces( $user ) {
- $arr = [];
- foreach ( SearchEngine::searchableNamespaces() as $ns => $name ) {
- if ( $user->getOption( 'searchNs' . $ns ) ) {
- $arr[] = $ns;
- }
- }
-
- return $arr;
- }
-
/**
* Find snippet highlight settings for all users
- *
* @return array Contextlines, contextchars
*/
public static function userHighlightPrefs() {
return [ $contextlines, $contextchars ];
}
- /**
- * An array of namespaces indexes to be searched by default
- *
- * @return array
- */
- public static function defaultNamespaces() {
- global $wgNamespacesToBeSearchedDefault;
-
- return array_keys( $wgNamespacesToBeSearchedDefault, true );
- }
-
- /**
- * Get a list of namespace names useful for showing in tooltips
- * and preferences
- *
- * @param array $namespaces
- * @return array
- */
- public static function namespacesAsText( $namespaces ) {
- global $wgContLang;
-
- $formatted = array_map( [ $wgContLang, 'getFormattedNsText' ], $namespaces );
- foreach ( $formatted as $key => $ns ) {
- if ( empty( $ns ) ) {
- $formatted[$key] = wfMessage( 'blanknamespace' )->text();
- }
- }
- return $formatted;
- }
-
- /**
- * Load up the appropriate search engine class for the currently
- * active database backend, and return a configured instance.
- *
- * @param string $type Type of search backend, if not the default
- * @return SearchEngine
- */
- public static function create( $type = null ) {
- global $wgSearchType;
- $dbr = null;
-
- $alternatives = self::getSearchTypes();
-
- if ( $type && in_array( $type, $alternatives ) ) {
- $class = $type;
- } elseif ( $wgSearchType !== null ) {
- $class = $wgSearchType;
- } else {
- $dbr = wfGetDB( DB_SLAVE );
- $class = $dbr->getSearchEngine();
- }
-
- $search = new $class( $dbr );
- return $search;
- }
-
- /**
- * Return the search engines we support. If only $wgSearchType
- * is set, it'll be an array of just that one item.
- *
- * @return array
- */
- public static function getSearchTypes() {
- global $wgSearchType, $wgSearchTypeAlternatives;
-
- $alternatives = $wgSearchTypeAlternatives ?: [];
- array_unshift( $alternatives, $wgSearchType );
-
- return $alternatives;
- }
-
/**
* Create or update the search index record for the given page.
* Title and text should be pre-processed.
return $backend->defaultSearchBackend( $this->namespaces, $search, $this->limit, $this->offset );
}
+ /**
+ * Make a list of searchable namespaces and their canonical names.
+ * @deprecated since 1.27; use SearchEngineConfig::searchableNamespaces()
+ * @return array
+ */
+ public static function searchableNamespaces() {
+ return MediaWikiServices::getInstance()->getSearchEngineConfig()->searchableNamespaces();
+ }
+
+ /**
+ * Extract default namespaces to search from the given user's
+ * settings, returning a list of index numbers.
+ * @deprecated since 1.27; use SearchEngineConfig::userNamespaces()
+ * @param user $user
+ * @return array
+ */
+ public static function userNamespaces( $user ) {
+ return MediaWikiServices::getInstance()->getSearchEngineConfig()->userNamespaces( $user );
+ }
+
+ /**
+ * An array of namespaces indexes to be searched by default
+ * @deprecated since 1.27; use SearchEngineConfig::defaultNamespaces()
+ * @return array
+ */
+ public static function defaultNamespaces() {
+ return MediaWikiServices::getInstance()->getSearchEngineConfig()->defaultNamespaces();
+ }
+
+ /**
+ * Get a list of namespace names useful for showing in tooltips
+ * and preferences
+ * @deprecated since 1.27; use SearchEngineConfig::namespacesAsText()
+ * @param array $namespaces
+ * @return array
+ */
+ public static function namespacesAsText( $namespaces ) {
+ return MediaWikiServices::getInstance()->getSearchEngineConfig()->namespacesAsText();
+ }
+
+ /**
+ * Load up the appropriate search engine class for the currently
+ * active database backend, and return a configured instance.
+ * @deprecated since 1.27; Use SearchEngineFactory::create
+ * @param string $type Type of search backend, if not the default
+ * @return SearchEngine
+ */
+ public static function create( $type = null ) {
+ return MediaWikiServices::getInstance()->getSearchEngineFactory()->create( $type );
+ }
+
+ /**
+ * Return the search engines we support. If only $wgSearchType
+ * is set, it'll be an array of just that one item.
+ * @deprecated since 1.27; use SearchEngineConfig::getSearchTypes()
+ * @return array
+ */
+ public static function getSearchTypes() {
+ return MediaWikiServices::getInstance()->getSearchEngineConfig()->getSearchTypes();
+ }
+
}
/**
--- /dev/null
+<?php
+
+/**
+ * Configuration handling class for SearchEngine.
+ * Provides added service over plain configuration.
+ *
+ * @since 1.27
+ */
+class SearchEngineConfig {
+
+ /**
+ * Config object from which the settings will be derived.
+ * @var Config
+ */
+ private $config;
+
+ public function __construct( Config $config ) {
+ $this->config = $config;
+ }
+
+ /**
+ * Retrieve original config.
+ * @return Config
+ */
+ public function getConfig() {
+ return $this->config;
+ }
+
+ /**
+ * Make a list of searchable namespaces and their canonical names.
+ * @return array Namespace ID => name
+ */
+ public function searchableNamespaces() {
+ $arr = [];
+ foreach ( $this->config->get( 'ContLang' )->getNamespaces() as $ns => $name ) {
+ if ( $ns >= NS_MAIN ) {
+ $arr[$ns] = $name;
+ }
+ }
+
+ Hooks::run( 'SearchableNamespaces', [ &$arr ] );
+ return $arr;
+ }
+
+ /**
+ * Extract default namespaces to search from the given user's
+ * settings, returning a list of index numbers.
+ *
+ * @param user $user
+ * @return int[]
+ */
+ public function userNamespaces( $user ) {
+ $arr = [];
+ foreach ( $this->searchableNamespaces() as $ns => $name ) {
+ if ( $user->getOption( 'searchNs' . $ns ) ) {
+ $arr[] = $ns;
+ }
+ }
+
+ return $arr;
+ }
+
+ /**
+ * An array of namespaces indexes to be searched by default
+ *
+ * @return int[] Namespace IDs
+ */
+ public function defaultNamespaces() {
+ return array_keys( $this->config->get( 'NamespacesToBeSearchedDefault' ), true );
+ }
+
+ /**
+ * Return the search engines we support. If only $wgSearchType
+ * is set, it'll be an array of just that one item.
+ *
+ * @return array
+ */
+ public function getSearchTypes() {
+ $alternatives = $this->config->get( 'SearchTypeAlternatives' ) ?: [];
+ array_unshift( $alternatives, $this->config->get( 'SearchType' ) );
+
+ return $alternatives;
+ }
+
+ /**
+ * Return the search engine configured in $wgSearchType, etc.
+ *
+ * @return string|null
+ */
+ public function getSearchType() {
+ return $this->config->get( 'SearchType' );
+ }
+
+ /**
+ * Get a list of namespace names useful for showing in tooltips
+ * and preferences.
+ *
+ * @param int[] $namespaces
+ * @return string[] List of names
+ */
+ public function namespacesAsText( $namespaces ) {
+ $formatted = array_map( [ $this->config->get( 'ContLang' ), 'getFormattedNsText' ], $namespaces );
+ foreach ( $formatted as $key => $ns ) {
+ if ( empty( $ns ) ) {
+ $formatted[$key] = wfMessage( 'blanknamespace' )->text();
+ }
+ }
+ return $formatted;
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * Factory class for SearchEngine.
+ * Allows to create engine of the specific type.
+ */
+class SearchEngineFactory {
+
+ /**
+ * Configuration for SearchEngine classes.
+ * @var SearchEngineConfig
+ */
+ private $config;
+
+ public function __construct( SearchEngineConfig $config ) {
+ $this->config = $config;
+ }
+
+ /**
+ * Create SearchEngine of the given type.
+ * @param string $type
+ * @return SearchEngine
+ */
+ public function create( $type = null ) {
+ $dbr = null;
+
+ $configType = $this->config->getSearchType();
+ $alternatives = $this->config->getSearchTypes();
+
+ if ( $type && in_array( $type, $alternatives ) ) {
+ $class = $type;
+ } elseif ( $configType !== null ) {
+ $class = $configType;
+ } else {
+ $dbr = wfGetDB( DB_SLAVE );
+ $class = $dbr->getSearchEngine();
+ }
+
+ $search = new $class( $dbr );
+ return $search;
+ }
+}
<?php
/**
- * A SearchResultSet wrapper for SearchEngine::getNearMatch
+ * A SearchResultSet wrapper for SearchNearMatcher
*/
class SearchNearMatchResultSet extends SearchResultSet {
private $fetched = false;
--- /dev/null
+<?php
+
+/**
+ * Implementation of near match title search.
+ * TODO: split into service/implementation.
+ */
+class SearchNearMatcher {
+ /**
+ * Configuration object.
+ * @param Config $config
+ */
+ protected $config;
+
+ public function __construct( Config $config ) {
+
+ $this->config = $config;
+ }
+
+ /**
+ * If an exact title match can be found, or a very slightly close match,
+ * return the title. If no match, returns NULL.
+ *
+ * @param string $searchterm
+ * @return Title
+ */
+ public function getNearMatch( $searchterm ) {
+ $title = $this->getNearMatchInternal( $searchterm );
+
+ Hooks::run( 'SearchGetNearMatchComplete', [ $searchterm, &$title ] );
+ return $title;
+ }
+
+ /**
+ * Do a near match (see SearchEngine::getNearMatch) and wrap it into a
+ * SearchResultSet.
+ *
+ * @param string $searchterm
+ * @return SearchResultSet
+ */
+ public function getNearMatchResultSet( $searchterm ) {
+ return new SearchNearMatchResultSet( $this->getNearMatch( $searchterm ) );
+ }
+
+ /**
+ * Really find the title match.
+ * @param string $searchterm
+ * @return null|Title
+ */
+ protected function getNearMatchInternal( $searchterm ) {
+ $lang = $this->config->get( 'ContLang' );
+
+ $allSearchTerms = [ $searchterm ];
+
+ if ( $lang->hasVariants() ) {
+ $allSearchTerms = array_unique( array_merge(
+ $allSearchTerms,
+ $lang->autoConvertToAllVariants( $searchterm )
+ ) );
+ }
+
+ $titleResult = null;
+ if ( !Hooks::run( 'SearchGetNearMatchBefore', [ $allSearchTerms, &$titleResult ] ) ) {
+ return $titleResult;
+ }
+
+ foreach ( $allSearchTerms as $term ) {
+
+ # Exact match? No need to look further.
+ $title = Title::newFromText( $term );
+ if ( is_null( $title ) ) {
+ return null;
+ }
+
+ # Try files if searching in the Media: namespace
+ if ( $title->getNamespace() == NS_MEDIA ) {
+ $title = Title::makeTitle( NS_FILE, $title->getText() );
+ }
+
+ if ( $title->isSpecialPage() || $title->isExternal() || $title->exists() ) {
+ return $title;
+ }
+
+ # See if it still otherwise has content is some sane sense
+ $page = WikiPage::factory( $title );
+ if ( $page->hasViewableContent() ) {
+ return $title;
+ }
+
+ if ( !Hooks::run( 'SearchAfterNoDirectMatch', [ $term, &$title ] ) ) {
+ return $title;
+ }
+
+ # Now try all lower case (i.e. first letter capitalized)
+ $title = Title::newFromText( $lang->lc( $term ) );
+ if ( $title && $title->exists() ) {
+ return $title;
+ }
+
+ # Now try capitalized string
+ $title = Title::newFromText( $lang->ucwords( $term ) );
+ if ( $title && $title->exists() ) {
+ return $title;
+ }
+
+ # Now try all upper case
+ $title = Title::newFromText( $lang->uc( $term ) );
+ if ( $title && $title->exists() ) {
+ return $title;
+ }
+
+ # Now try Word-Caps-Breaking-At-Word-Breaks, for hyphenated names etc
+ $title = Title::newFromText( $lang->ucwordbreaks( $term ) );
+ if ( $title && $title->exists() ) {
+ return $title;
+ }
+
+ // Give hooks a chance at better match variants
+ $title = null;
+ if ( !Hooks::run( 'SearchGetNearMatch', [ $term, &$title ] ) ) {
+ return $title;
+ }
+ }
+
+ $title = Title::newFromText( $searchterm );
+
+ # Entering an IP address goes to the contributions page
+ if ( $this->config->get( 'EnableSearchContributorsByIP' ) ) {
+ if ( ( $title->getNamespace() == NS_USER && User::isIP( $title->getText() ) )
+ || User::isIP( trim( $searchterm ) ) ) {
+ return SpecialPage::getTitleFor( 'Contributions', $title->getDBkey() );
+ }
+ }
+
+ # Entering a user goes to the user page whether it's there or not
+ if ( $title->getNamespace() == NS_USER ) {
+ return $title;
+ }
+
+ # Go to images that exist even if there's no local page.
+ # There may have been a funny upload, or it may be on a shared
+ # file repository such as Wikimedia Commons.
+ if ( $title->getNamespace() == NS_FILE ) {
+ $image = wfFindFile( $title );
+ if ( $image ) {
+ return $title;
+ }
+ }
+
+ # MediaWiki namespace? Page may be "implied" if not customized.
+ # Just return it, with caps forced as the message system likes it.
+ if ( $title->getNamespace() == NS_MEDIAWIKI ) {
+ return Title::makeTitle( NS_MEDIAWIKI, $lang->ucfirst( $title->getText() ) );
+ }
+
+ # Quoted term? Try without the quotes...
+ $matches = [];
+ if ( preg_match( '/^"([^"]+)"$/', $searchterm, $matches ) ) {
+ return self::getNearMatch( $matches[1] );
+ }
+
+ return null;
+ }
+}
* @ingroup Search
*/
+use MediaWiki\MediaWikiServices;
+
/**
* @todo FIXME: This class is horribly factored. It would probably be better to
* have a useful base class to which you pass some standard information, then
*/
protected $mText;
+ /**
+ * @var SearchEngine
+ */
+ protected $searchEngine;
+
/**
* Return a new SearchResult and initializes it with a title.
*
* @return SearchResult
*/
public static function newFromTitle( $title ) {
- $result = new self();
+ $result = new static();
$result->initFromTitle( $title );
return $result;
}
$this->mImage = wfFindFile( $this->mTitle );
}
}
+ $this->searchEngine = MediaWikiServices::getInstance()->newSearchEngine();
}
/**
protected function initText() {
if ( !isset( $this->mText ) ) {
if ( $this->mRevision != null ) {
- $this->mText = SearchEngine::create()
- ->getTextFromContent( $this->mTitle, $this->mRevision->getContent() );
+ $this->mText = $this->searchEngine->getTextFromContent(
+ $this->mTitle, $this->mRevision->getContent() );
} else { // TODO: can we fetch raw wikitext for commons images?
$this->mText = '';
}
$this->initText();
// TODO: make highliter take a content object. Make ContentHandler a factory for SearchHighliter.
- list( $contextlines, $contextchars ) = SearchEngine::userHighlightPrefs();
+ list( $contextlines, $contextchars ) = $this->searchEngine->userHighlightPrefs();
$h = new SearchHighlighter();
if ( count( $terms ) > 0 ) {
<?php
+use MediaWiki\MediaWikiServices;
+
/**
* Parent class for all special pages.
*
/**
* Get a localised Title object for a specified special page name
*
+ * @since 1.9
+ * @since 1.21 $fragment parameter added
+ *
* @param string $name
* @param string|bool $subpage Subpage string, or false to not use a subpage
* @param string $fragment The link fragment (after the "#")
return [];
}
- $searchEngine = SearchEngine::create();
+ $searchEngine = MediaWikiServices::getInstance()->newSearchEngine();
$searchEngine->setLimitOffset( $limit, $offset );
$searchEngine->setNamespaces( [] );
$result = $searchEngine->defaultPrefixSearch( $search );
<?php
+use MediaWiki\MediaWikiServices;
+
/**
* Implements Special:FileDuplicateSearch
*
// No prefix suggestion outside of file namespace
return [];
}
- $searchEngine = SearchEngine::create();
+ $searchEngine = MediaWikiServices::getInstance()->newSearchEngine();
$searchEngine->setLimitOffset( $limit, $offset );
// Autocomplete subpage the same as a normal search, but just for files
$searchEngine->setNamespaces( [ NS_FILE ] );
* @ingroup SpecialPage
*/
+use MediaWiki\MediaWikiServices;
+
/**
* implements Special:Search - Run text & title search and display the output
* @ingroup SpecialPage
*/
protected $customCaptions;
+ /**
+ * Search engine configurations.
+ * @var SearchEngineConfig
+ */
+ protected $searchConfig;
+
const NAMESPACES_CURRENT = 'sense';
public function __construct() {
parent::__construct( 'Search' );
+ $this->searchConfig = MediaWikiServices::getInstance()->getSearchEngineConfig();
}
/**
$nslist = $this->powerSearch( $request );
if ( !count( $nslist ) ) {
# Fallback to user preference
- $nslist = SearchEngine::userNamespaces( $user );
+ $nslist = $this->searchConfig->userNamespaces( $user );
}
$profile = null;
return;
}
# If there's an exact or very near match, jump right there.
- $title = SearchEngine::getNearMatch( $term );
+ $title = $this->newSearchEngine()->
+ getNearMatcher( $this->getConfig() )->getNearMatch( $term );
if ( !is_null( $title ) &&
Hooks::run( 'SpecialSearchGoResult', [ $term, $title, &$url ] )
*/
protected function powerSearch( &$request ) {
$arr = [];
- foreach ( SearchEngine::searchableNamespaces() as $ns => $name ) {
+ foreach ( $this->searchConfig->searchableNamespaces() as $ns => $name ) {
if ( $request->getCheck( 'ns' . $ns ) ) {
$arr[] = $ns;
}
// Groups namespaces into rows according to subject
$rows = [];
- foreach ( SearchEngine::searchableNamespaces() as $namespace => $name ) {
+ foreach ( $this->searchConfig->searchableNamespaces() as $namespace => $name ) {
$subject = MWNamespace::getSubject( $namespace );
if ( !array_key_exists( $subject, $rows ) ) {
$rows[$subject] = "";
*/
protected function getSearchProfiles() {
// Builds list of Search Types (profiles)
- $nsAllSet = array_keys( SearchEngine::searchableNamespaces() );
-
+ $nsAllSet = array_keys( $this->searchConfig->searchableNamespaces() );
+ $defaultNs = $this->searchConfig->defaultNamespaces();
$profiles = [
'default' => [
'message' => 'searchprofile-articles',
'tooltip' => 'searchprofile-articles-tooltip',
- 'namespaces' => SearchEngine::defaultNamespaces(),
- 'namespace-messages' => SearchEngine::namespacesAsText(
- SearchEngine::defaultNamespaces()
+ 'namespaces' => $defaultNs,
+ 'namespace-messages' => $this->searchConfig->namespacesAsText(
+ $defaultNs
),
],
'images' => [
public function getSearchEngine() {
if ( $this->searchEngine === null ) {
$this->searchEngine = $this->searchEngineType ?
- SearchEngine::create( $this->searchEngineType ) : SearchEngine::create();
+ MediaWikiServices::getInstance()->getSearchEngineFactory()->create( $this->searchEngineType ) :
+ MediaWikiServices::getInstance()->newSearchEngine();
}
return $this->searchEngine;
* @file
*/
+use MediaWiki\MediaWikiServices;
use MediaWiki\Session\SessionManager;
use MediaWiki\Session\Token;
foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
$defOpt[$langCode == $wgContLang->getCode() ? 'variant' : "variant-$langCode"] = $langCode;
}
- foreach ( SearchEngine::searchableNamespaces() as $nsnum => $nsname ) {
+ $namespaces = MediaWikiServices::getInstance()->getSearchEngineConfig()->searchableNamespaces();
+ foreach ( $namespaces as $nsnum => $nsname ) {
$defOpt['searchNs' . $nsnum] = !empty( $wgNamespacesToBeSearchedDefault[$nsnum] );
}
$defOpt['skin'] = Skin::normalizeKey( $wgDefaultSkin );
"minoredit": "This is a minor edit",
"watchthis": "Watch this page",
"savearticle": "Save page",
+ "publishpage": "Publish page",
"preview": "Preview",
"showpreview": "Show preview",
"showdiff": "Show changes",
"userpage-userdoesnotexist": "User account \"$1\" is not registered.\nPlease check if you want to create/edit this page.",
"userpage-userdoesnotexist-view": "User account \"$1\" is not registered.",
"blocked-notice-logextract": "This user is currently blocked.\nThe latest block log entry is provided below for reference:",
- "clearyourcache": "<strong>Note:</strong> After saving, you may have to bypass your browser's cache to see the changes.\n* <strong>Firefox / Safari:</strong> Hold <em>Shift</em> while clicking <em>Reload</em>, or press either <em>Ctrl-F5</em> or <em>Ctrl-R</em> (<em>⌘-R</em> on a Mac)\n* <strong>Google Chrome:</strong> Press <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> on a Mac)\n* <strong>Internet Explorer:</strong> Hold <em>Ctrl</em> while clicking <em>Refresh</em>, or press <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Clear the cache in <em>Tools → Preferences</em>",
+ "clearyourcache": "<strong>Note:</strong> After saving, you may have to bypass your browser's cache to see the changes.\n* <strong>Firefox / Safari:</strong> Hold <em>Shift</em> while clicking <em>Reload</em>, or press either <em>Ctrl-F5</em> or <em>Ctrl-R</em> (<em>⌘-R</em> on a Mac)\n* <strong>Google Chrome:</strong> Press <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> on a Mac)\n* <strong>Internet Explorer:</strong> Hold <em>Ctrl</em> while clicking <em>Refresh</em>, or press <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Go to <em>Menu → Settings</em> (<em>Opera → Preferences</em> on a Mac) and then to <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
"usercssyoucanpreview": "<strong>Tip:</strong> Use the \"{{int:showpreview}}\" button to test your new CSS before saving.",
"userjsyoucanpreview": "<strong>Tip:</strong> Use the \"{{int:showpreview}}\" button to test your new JavaScript before saving.",
"usercsspreview": "<strong>Remember that you are only previewing your user CSS.\nIt has not yet been saved!</strong>",
"accesskey-ca-nstab-category": "c",
"accesskey-minoredit": "i",
"accesskey-save": "s",
+ "accesskey-publish": "s",
"accesskey-preview": "p",
"accesskey-diff": "v",
"accesskey-compareselectedversions": "v",
"tooltip-ca-nstab-category": "View the category page",
"tooltip-minoredit": "Mark this as a minor edit",
"tooltip-save": "Save your changes",
+ "tooltip-publish": "Publish your changes",
"tooltip-preview": "Preview your changes. Please use this before saving.",
"tooltip-diff": "Show which changes you made to the text",
"tooltip-compareselectedversions": "See the differences between the two selected revisions of this page",
"minoredit": "Text above Save page button in editor\n\nSee also:\n* {{msg-mw|Minoredit}}\n* {{msg-mw|Accesskey-minoredit}}\n* {{msg-mw|Tooltip-minoredit}}",
"watchthis": "Text of checkbox above {{msg-mw|Showpreview}} button in editor.\n\nSee also:\n* {{msg-mw|Watchthis}}\n* {{msg-mw|Accesskey-watch}}\n* {{msg-mw|Tooltip-watch}}\n{{Identical|Watch this page}}",
"savearticle": "Text on the Save page button. See also {{msg-mw|showpreview}} and {{msg-mw|showdiff}} for the other buttons.\n\nSee also:\n* {{msg-mw|Savearticle}}\n* {{msg-mw|Accesskey-save}}\n* {{msg-mw|Tooltip-save}}\n{{Identical|Save page}}",
+ "publishpage": "Text on the button to save the changes to the page. It should be an action which is short and makes clear that the effect is immediate and public.\n\nSee also {{msg-mw|showpreview}} and {{msg-mw|showdiff}} for the other buttons.\n\nNote: This i18n is being introduced in advance of usage to provide extra time for translators.\n\nSee also:\n* {{msg-mw|Accesskey-publish}}\n* {{msg-mw|Tooltip-publish}}\n{{Identical|Publish page}}",
"preview": "The title of the Preview page shown after clicking the \"Show preview\" button in the edit page. Since this is a heading, it should probably be translated as a noun and not as a verb.\n\n{{Identical|Preview}}",
"showpreview": "The text of the button to preview the page you are editing. See also {{msg-mw|showdiff}} and {{msg-mw|savearticle}} for the other buttons.\n\nSee also:\n* {{msg-mw|Showpreview}}\n* {{msg-mw|Accesskey-preview}}\n* {{msg-mw|Tooltip-preview}}\n{{Identical|Show preview}}",
"showdiff": "Button below the edit page. See also {{msg-mw|Showpreview}} and {{msg-mw|Savearticle}} for the other buttons.\n\nSee also:\n* {{msg-mw|Showdiff}}\n* {{msg-mw|Accesskey-diff}}\n* {{msg-mw|Tooltip-diff}}\n{{Identical|Show change}}",
"accesskey-ca-nstab-category": "{{doc-accesskey}}\nSee also:\n* {{msg-mw|Nstab-category}}\n* {{msg-mw|Accesskey-ca-nstab-category}}\n* {{msg-mw|Tooltip-ca-nstab-category}}",
"accesskey-minoredit": "{{doc-accesskey}}\nSee also:\n* {{msg-mw|Minoredit}}\n* {{msg-mw|Accesskey-minoredit}}\n* {{msg-mw|Tooltip-minoredit}}",
"accesskey-save": "{{doc-accesskey}}\nSee also:\n* {{msg-mw|Savearticle}}\n* {{msg-mw|Accesskey-save}}\n* {{msg-mw|Tooltip-save}}",
+ "accesskey-publish": "{{doc-accesskey}}\n\nNote: This i18n is being introduced in advance of usage to provide extra time for translators.\nSee also:\n* {{msg-mw|publishpage}}\n* {{msg-mw|Tooltip-publish}}",
"accesskey-preview": "{{doc-accesskey}}\nSee also:\n* {{msg-mw|Showpreview}}\n* {{msg-mw|Accesskey-preview}}\n* {{msg-mw|Tooltip-preview}}",
"accesskey-diff": "{{doc-accesskey}}\nSee also:\n* {{msg-mw|Showdiff}}\n* {{msg-mw|Accesskey-diff}}\n* {{msg-mw|Tooltip-diff}}",
"accesskey-compareselectedversions": "{{doc-accesskey}}\nSee also:\n* {{msg-mw|Compareselectedversions}}\n* {{msg-mw|Accesskey-compareselectedversions}}\n* {{msg-mw|Tooltip-compareselectedversions}}",
"tooltip-ca-nstab-category": "Tooltip shown when hovering over the {{msg-mw|Nstab-category}} tab.\n\nSee also:\n* {{msg-mw|Nstab-category}}\n* {{msg-mw|Accesskey-ca-nstab-category}}\n* {{msg-mw|Tooltip-ca-nstab-category}}",
"tooltip-minoredit": "Tooltip shown when hovering over the \"{{msg-mw|Minoredit}}\" link below the edit form.\n\nSee also:\n* {{msg-mw|Minoredit}}\n* {{msg-mw|Accesskey-minoredit}}\n* {{msg-mw|Tooltip-minoredit}}",
"tooltip-save": "This is the text that appears when you hover the mouse over {{msg-mw|Savearticle}} button on the edit page.\n\nSee also:\n* {{msg-mw|Savearticle}}\n* {{msg-mw|Accesskey-save}}\n* {{msg-mw|Tooltip-save}}",
+ "tooltip-publish": "This is the text that appears when you hover the mouse over {{msg-mw|publishpage}} button on the edit page.\n\nNote: This i18n is being introduced in advance of usage to provide extra time for translators.\n\nSee also:\n* {{msg-mw|publishpage}}\n* {{msg-mw|Accesskey-publish}}\n{{Identical|Publish page}}",
"tooltip-preview": "Tooltip shown when hovering over {{msg-mw|Showpreview}} button.\n\nIf the length of the translated message is over 60 characters (including spaces) then the end of the message will be cut off when using Firefox 2.0.0.7 browser, Linux operating system and the Monobook skin.\n\nSee also:\n* {{msg-mw|Showpreview}}\n* {{msg-mw|Accesskey-preview}}\n* {{msg-mw|Tooltip-preview}}",
"tooltip-diff": "This is the text (tooltip) that appears when you hover the mouse over {{msg-mw|Showdiff}} button on the edit page.\n\nSee also:\n* {{msg-mw|Showdiff}}\n* {{msg-mw|Accesskey-diff}}\n* {{msg-mw|Tooltip-diff}}",
"tooltip-compareselectedversions": "Tooltip of {{msg-mw|Compareselectedversions}} (which is used as button in history pages).\n\nSee also:\n* {{msg-mw|Compareselectedversions}}\n* {{msg-mw|Accesskey-compareselectedversions}}\n* {{msg-mw|Tooltip-compareselectedversions}}",
);
$this->addOption( 'force', 'Run on all rows, even if the collation is ' .
- 'supposed to be up-to-date.' );
+ 'supposed to be up-to-date.', false, false, 'f' );
$this->addOption( 'previous-collation', 'Set the previous value of ' .
'$wgCategoryCollation here to speed up this script, especially if your ' .
'categorylinks table is large. This will only update rows with that ' .
url = ( ajaxOptions && ajaxOptions.url ) || this.defaults.ajax.url;
origin = ( parameters && parameters.origin ) || this.defaults.parameters.origin;
url += ( url.indexOf( '?' ) !== -1 ? '&' : '?' ) +
- 'origin=' + encodeURIComponent( origin );
+ // Depending on server configuration, MediaWiki may forbid periods in URLs, due to an IE 6
+ // XSS bug. So let's escape them here. See WebRequest::checkUrlExtension() and T30235.
+ 'origin=' + encodeURIComponent( origin ).replace( /\./g, '%2E' );
newAjaxOptions = $.extend( {}, ajaxOptions, { url: url } );
} else {
newAjaxOptions = ajaxOptions;
// Prevent jQuery from overriding the Content-Type header
ajaxOptions.contentType = false;
} else {
- // Some deployed MediaWiki >= 1.17 forbid periods in URLs, due to an IE XSS bug
- // So let's escape them here. See bug #28235
// This works because jQuery accepts data as a query string or as an Object
- ajaxOptions.data = $.param( parameters ).replace( /\./g, '%2E' );
-
+ ajaxOptions.data = $.param( parameters );
// If we extracted a token parameter, add it back in.
if ( token ) {
ajaxOptions.data += '&token=' + encodeURIComponent( token );
}
+ // Depending on server configuration, MediaWiki may forbid periods in URLs, due to an IE 6
+ // XSS bug. So let's escape them here. See WebRequest::checkUrlExtension() and T30235.
+ ajaxOptions.data = ajaxOptions.data.replace( /\./g, '%2E' );
+
if ( ajaxOptions.contentType === 'multipart/form-data' ) {
// We were asked to emulate but can't, so drop the Content-Type header, otherwise
// it'll be wrong and the server will fail to decode the POST body
'SiteLookup' => [ 'getSiteLookup', SiteLookup::class ],
'StatsdDataFactory' => [ 'getStatsdDataFactory', StatsdDataFactory::class ],
'EventRelayerGroup' => [ 'getEventRelayerGroup', EventRelayerGroup::class ],
+ 'SearchEngine' => [ 'newSearchEngine', SearchEngine::class ],
+ 'SearchEngineFactory' => [ 'getSearchEngineFactory', SearchEngineFactory::class ],
+ 'SearchEngineConfig' => [ 'getSearchEngineConfig', SearchEngineConfig::class ],
];
}
'SiteLookup' => [ 'SiteLookup', SiteLookup::class ],
'StatsdDataFactory' => [ 'StatsdDataFactory', StatsdDataFactory::class ],
'EventRelayerGroup' => [ 'EventRelayerGroup', EventRelayerGroup::class ],
+ 'SearchEngineFactory' => [ 'SearchEngineFactory', SearchEngineFactory::class ],
+ 'SearchEngineConfig' => [ 'SearchEngineConfig', SearchEngineConfig::class ],
];
}
*/
class SearchUpdateTest extends MediaWikiTestCase {
+ /**
+ * @var SearchUpdate
+ */
+ private $su;
+
protected function setUp() {
parent::setUp();
$this->setMwGlobals( 'wgSearchType', 'MockSearch' );
+ $this->su = new SearchUpdate( 0, "" );
}
public function updateText( $text ) {
- return trim( SearchUpdate::updateText( $text ) );
+ return trim( $this->su->updateText( $text ) );
}
/**
<?php
+use MediaWiki\MediaWikiServices;
+
/**
* @group Search
* @group Database
// Avoid special pages from extensions interferring with the tests
$this->setMwGlobals( 'wgSpecialPages', [] );
- $this->search = SearchEngine::create();
+ $this->search = MediaWikiServices::getInstance()->newSearchEngine();
$this->search->setNamespaces( [] );
}
<?php
+use MediaWiki\MediaWikiServices;
+
/**
* Test class for SpecialSearch class
* Copyright © 2012, Antoine Musso
* @author Antoine Musso
* @group Database
*/
-
class SpecialSearchTest extends MediaWikiTestCase {
/**
}
public static function provideSearchOptionsTests() {
- $defaultNS = SearchEngine::defaultNamespaces();
+ $defaultNS = MediaWikiServices::getInstance()->getSearchEngineConfig()->defaultNamespaces();
$EMPTY_REQUEST = [];
$NO_USER_PREF = null;