return []; // Return empty result
}
- $hasNamespace = $this->extractNamespace( $search );
- if ( $hasNamespace ) {
- list( $namespace, $search ) = $hasNamespace;
- $namespaces = [ $namespace ];
- } else {
- $namespaces = $this->validateNamespaces( $namespaces );
- Hooks::run( 'PrefixSearchExtractNamespace', [ &$namespaces, &$search ] );
+ $hasNamespace = SearchEngine::parseNamespacePrefixes( $search, false, true );
+ if ( $hasNamespace !== false ) {
+ list( $search, $namespaces ) = $hasNamespace;
}
return $this->searchBackend( $namespaces, $search, $limit, $offset );
}
- /**
- * Figure out if given input contains an explicit namespace.
- *
- * @param string $input
- * @return false|array Array of namespace and remaining text, or false if no namespace given.
- */
- protected function extractNamespace( $input ) {
- if ( strpos( $input, ':' ) === false ) {
- return false;
- }
-
- // Namespace prefix only
- $title = Title::newFromText( $input . 'Dummy' );
- if (
- $title &&
- $title->getText() === 'Dummy' &&
- !$title->inNamespace( NS_MAIN ) &&
- !$title->isExternal()
- ) {
- return [ $title->getNamespace(), '' ];
- }
-
- // Namespace prefix with additional input
- $title = Title::newFromText( $input );
- if (
- $title &&
- !$title->inNamespace( NS_MAIN ) &&
- !$title->isExternal()
- ) {
- // getText provides correct capitalization
- return [ $title->getNamespace(), $title->getText() ];
- }
-
- return false;
- }
-
/**
* Do a prefix search for all possible variants of the prefix
* @param string $search
* or namespace names
*
* @param string $query
+ * @param bool $withAllKeyword activate support of the "all:" keyword and its
+ * translations to activate searching on all namespaces.
+ * @param bool $withPrefixSearchExtractNamespaceHook call the PrefixSearchExtractNamespace hook
+ * if classic namespace identification did not match.
* @return false|array false if no namespace was extracted, an array
* with the parsed query at index 0 and an array of namespaces at index
* 1 (or null for all namespaces).
- */
- public static function parseNamespacePrefixes( $query ) {
+ * @throws FatalError
+ * @throws MWException
+ */
+ public static function parseNamespacePrefixes(
+ $query,
+ $withAllKeyword = true,
+ $withPrefixSearchExtractNamespaceHook = false
+ ) {
global $wgContLang;
$parsed = $query;
return false;
}
$extractedNamespace = null;
- $allkeywords = [];
-
- $allkeywords[] = wfMessage( 'searchall' )->inContentLanguage()->text() . ":";
- // force all: so that we have a common syntax for all the wikis
- if ( !in_array( 'all:', $allkeywords ) ) {
- $allkeywords[] = 'all:';
- }
$allQuery = false;
- foreach ( $allkeywords as $kw ) {
- if ( strncmp( $query, $kw, strlen( $kw ) ) == 0 ) {
- $extractedNamespace = null;
- $parsed = substr( $query, strlen( $kw ) );
- $allQuery = true;
- break;
+ if ( $withAllKeyword ) {
+ $allkeywords = [];
+
+ $allkeywords[] = wfMessage( 'searchall' )->inContentLanguage()->text() . ":";
+ // force all: so that we have a common syntax for all the wikis
+ if ( !in_array( 'all:', $allkeywords ) ) {
+ $allkeywords[] = 'all:';
+ }
+
+ foreach ( $allkeywords as $kw ) {
+ if ( strncmp( $query, $kw, strlen( $kw ) ) == 0 ) {
+ $extractedNamespace = null;
+ $parsed = substr( $query, strlen( $kw ) );
+ $allQuery = true;
+ break;
+ }
}
}
if ( !$allQuery && strpos( $query, ':' ) !== false ) {
- // TODO: should we unify with PrefixSearch::extractNamespace ?
$prefix = str_replace( ' ', '_', substr( $query, 0, strpos( $query, ':' ) ) );
$index = $wgContLang->getNsIndex( $prefix );
if ( $index !== false ) {
$extractedNamespace = [ $index ];
$parsed = substr( $query, strlen( $prefix ) + 1 );
+ } elseif ( $withPrefixSearchExtractNamespaceHook ) {
+ $hookNamespaces = [ NS_MAIN ];
+ $hookQuery = $query;
+ Hooks::run( 'PrefixSearchExtractNamespace', [ &$hookNamespaces, &$hookQuery ] );
+ if ( $hookQuery !== $query ) {
+ $parsed = $hookQuery;
+ $extractedNamespace = $hookNamespaces;
+ } else {
+ return false;
+ }
} else {
return false;
}
}
- if ( trim( $parsed ) == '' ) {
- $parsed = $query; // prefix was the whole query
- }
-
return [ $parsed, $extractedNamespace ];
}
* @return string Simplified search string
*/
protected function normalizeNamespaces( $search ) {
- // Find a Title which is not an interwiki and is in NS_MAIN
- $title = Title::newFromText( $search );
- $ns = $this->namespaces;
- if ( $title && !$title->isExternal() ) {
- $ns = [ $title->getNamespace() ];
- $search = $title->getText();
- if ( $ns[0] == NS_MAIN ) {
- $ns = $this->namespaces; // no explicit prefix, use default namespaces
- Hooks::run( 'PrefixSearchExtractNamespace', [ &$ns, &$search ] );
- }
- } else {
- $title = Title::newFromText( $search . 'Dummy' );
- if ( $title && $title->getText() == 'Dummy'
- && $title->getNamespace() != NS_MAIN
- && !$title->isExternal()
- ) {
- $ns = [ $title->getNamespace() ];
- $search = '';
- } else {
- Hooks::run( 'PrefixSearchExtractNamespace', [ &$ns, &$search ] );
- }
+ $queryAndNs = self::parseNamespacePrefixes( $search, false, true );
+ if ( $queryAndNs !== false ) {
+ $this->setNamespaces( $queryAndNs[1] );
+ return $queryAndNs[0];
}
-
- $ns = array_map( function ( $space ) {
- return $space == NS_MEDIA ? NS_FILE : $space;
- }, $ns );
-
- $this->setNamespaces( $ns );
return $search;
}
EDIT_NEW | EDIT_SUPPRESS_RC
);
}
+
+ public function provideDataForParseNamespacePrefix() {
+ return [
+ 'noop' => [
+ [
+ 'query' => 'foo',
+ ],
+ false
+ ],
+ 'empty' => [
+ [
+ 'query' => '',
+ ],
+ false,
+ ],
+ 'namespace prefix' => [
+ [
+ 'query' => 'help:test',
+ ],
+ [ 'test', [ NS_HELP ] ],
+ ],
+ 'accented namespace prefix with hook' => [
+ [
+ 'query' => 'hélp:test',
+ 'withHook' => true,
+ ],
+ [ 'test', [ NS_HELP ] ],
+ ],
+ 'accented namespace prefix without hook' => [
+ [
+ 'query' => 'hélp:test',
+ 'withHook' => false,
+ ],
+ false,
+ ],
+ 'all with all keyword allowed' => [
+ [
+ 'query' => 'all:test',
+ 'withAll' => true,
+ ],
+ [ 'test', null ],
+ ],
+ 'all with all keyword disallowed' => [
+ [
+ 'query' => 'all:test',
+ 'withAll' => false,
+ ],
+ false
+ ],
+ 'ns only' => [
+ [
+ 'query' => 'help:',
+ ],
+ [ '', [ NS_HELP ] ]
+ ],
+ 'all only' => [
+ [
+ 'query' => 'all:',
+ 'withAll' => true,
+ ],
+ [ '', null ]
+ ],
+ 'all wins over namespace when first' => [
+ [
+ 'query' => 'all:help:test',
+ 'withAll' => true,
+ ],
+ [ 'help:test', null ]
+ ],
+ 'ns wins over all when first' => [
+ [
+ 'query' => 'help:all:test',
+ 'withAll' => true,
+ ],
+ [ 'all:test', [ NS_HELP ] ]
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideDataForParseNamespacePrefix
+ * @param array $params
+ * @param array|false $expected
+ * @throws FatalError
+ * @throws MWException
+ */
+ public function testParseNamespacePrefix( array $params, $expected ) {
+ $this->setTemporaryHook( 'PrefixSearchExtractNamespace', function ( &$namespaces, &$query ) {
+ if ( strpos( $query, 'hélp:' ) === 0 ) {
+ $namespaces = [ NS_HELP ];
+ $query = substr( $query, strlen( 'hélp:' ) );
+ }
+ return false;
+ } );
+ $testSet = [];
+ if ( isset( $params['withAll'] ) && isset( $params['withHook'] ) ) {
+ $testSet[] = $params;
+ } elseif ( isset( $params['withAll'] ) ) {
+ $testSet[] = $params + [ 'withHook' => true ];
+ $testSet[] = $params + [ 'withHook' => false ];
+ } elseif ( isset( $params['withHook'] ) ) {
+ $testSet[] = $params + [ 'withAll' => true ];
+ $testSet[] = $params + [ 'withAll' => false ];
+ } else {
+ $testSet[] = $params + [ 'withAll' => true, 'withHook' => true ];
+ $testSet[] = $params + [ 'withAll' => true, 'withHook' => false ];
+ $testSet[] = $params + [ 'withAll' => false, 'withHook' => false ];
+ $testSet[] = $params + [ 'withAll' => true, 'withHook' => false ];
+ }
+
+ foreach ( $testSet as $test ) {
+ $actual = SearchEngine::parseNamespacePrefixes( $test['query'],
+ $test['withAll'], $test['withHook'] );
+ $this->assertEquals( $expected, $actual, 'with params: ' . print_r( $test, true ) );
+ }
+ }
}
*/
public static function addMockResults( $query, array $result ) {
// Leading : ensures we don't treat another : as a namespace separator
- $normalized = Title::newFromText( ":$query" )->getText();
+ $normalized = mb_strtolower( Title::newFromText( ":$query" )->getText() );
self::$results[$normalized] = $result;
}
public function completionSearchBackend( $search ) {
+ $search = mb_strtolower( $search );
if ( !isset( self::$results[$search] ) ) {
return SearchSuggestionSet::emptySuggestionSet();
}