/**
* Basic search engine
*
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
* @ingroup Search
*/
/**
* If this search backend can list/unlist redirects
- * @deprecated Call supports( 'list-redirects' );
+ * @deprecated since 1.18 Call supports( 'list-redirects' );
+ * @return bool
*/
function acceptListRedirects() {
+ wfDeprecated( __METHOD__, '1.18' );
return $this->supports( 'list-redirects' );
}
* @since 1.18
* @param $feature String
* @param $data Mixed
- * @return Noolean
+ * @return bool
*/
public function setFeatureData( $feature, $data ) {
$this->features[$feature] = $data;
wfRunHooks( 'SearchGetNearMatchComplete', array( $searchterm, &$title ) );
return $title;
}
-
+
/**
- * Do a near match (see SearchEngine::getNearMatch) and wrap it into a
+ * Do a near match (see SearchEngine::getNearMatch) and wrap it into a
* SearchResultSet.
- *
+ *
* @param $searchterm string
* @return SearchResultSet
*/
/**
* Really find the title match.
+ * @return null|\Title
*/
private static function getNearMatchInternal( $searchterm ) {
- global $wgContLang;
+ global $wgContLang, $wgEnableSearchContributorsByIP;
$allSearchTerms = array( $searchterm );
return $titleResult;
}
- $context = new RequestContext;
-
foreach ( $allSearchTerms as $term ) {
# Exact match? No need to look further.
return null;
}
- if ( $title->getNamespace() == NS_SPECIAL || $title->isExternal() || $title->exists() ) {
+ if ( $title->isSpecialPage() || $title->isExternal() || $title->exists() ) {
return $title;
}
# See if it still otherwise has content is some sane sense
- $context->setTitle( $title );
- $article = MediaWiki::articleFromTitle( $title, $context );
- if ( $article->hasViewableContent() ) {
+ $page = WikiPage::factory( $title );
+ if ( $page->hasViewableContent() ) {
return $title;
}
$title = Title::newFromText( $searchterm );
+
# Entering an IP address goes to the contributions page
- if ( ( $title->getNamespace() == NS_USER && User::isIP( $title->getText() ) )
- || User::isIP( trim( $searchterm ) ) ) {
- return SpecialPage::getTitleFor( 'Contributions', $title->getDBkey() );
+ if ( $wgEnableSearchContributorsByIP ) {
+ if ( ( $title->getNamespace() == NS_USER && User::isIP( $title->getText() ) )
+ || User::isIP( trim( $searchterm ) ) ) {
+ return SpecialPage::getTitleFor( 'Contributions', $title->getDBkey() );
+ }
}
* or namespace names
*
* @param $query String
+ * @return string
*/
function replacePrefixes( $query ) {
global $wgContLang;
return $parsed;
}
- $allkeyword = wfMsgForContent( 'searchall' ) . ":";
+ $allkeyword = wfMessage( 'searchall' )->inContentLanguage()->text() . ":";
if ( strncmp( $query, $allkeyword, strlen( $allkeyword ) ) == 0 ) {
$this->namespaces = null;
$parsed = substr( $query, strlen( $allkeyword ) );
- } else if ( strpos( $query, ':' ) !== false ) {
+ } elseif ( strpos( $query, ':' ) !== false ) {
$prefix = substr( $query, 0, strpos( $query, ':' ) );
$index = $wgContLang->getNsIndex( $prefix );
if ( $index !== false ) {
public static function userNamespaces( $user ) {
global $wgSearchEverythingOnlyLoggedIn;
- // get search everything preference, that can be set to be read for logged-in users
- $searcheverything = false;
- if ( ( $wgSearchEverythingOnlyLoggedIn && $user->isLoggedIn() )
- || !$wgSearchEverythingOnlyLoggedIn )
- $searcheverything = $user->getOption( 'searcheverything' );
-
- // searcheverything overrides other options
- if ( $searcheverything )
- return array_keys( SearchEngine::searchableNamespaces() );
-
- $arr = Preferences::loadOldSearchNs( $user );
$searchableNamespaces = SearchEngine::searchableNamespaces();
- $arr = array_intersect( $arr, array_keys( $searchableNamespaces ) ); // Filter
+ // get search everything preference, that can be set to be read for logged-in users
+ // it overrides other options
+ if ( !$wgSearchEverythingOnlyLoggedIn || $user->isLoggedIn() ) {
+ if ( $user->getOption( 'searcheverything' ) ) {
+ return array_keys( $searchableNamespaces );
+ }
+ }
+
+ $arr = array();
+ foreach ( $searchableNamespaces as $ns => $name ) {
+ if ( $user->getOption( 'searchNs' . $ns ) ) {
+ $arr[] = $ns;
+ }
+ }
return $arr;
}
* and preferences
*
* @param $namespaces Array
+ * @return array
*/
public static function namespacesAsText( $namespaces ) {
global $wgContLang;
$formatted = array_map( array( $wgContLang, 'getFormattedNsText' ), $namespaces );
foreach ( $formatted as $key => $ns ) {
if ( empty( $ns ) )
- $formatted[$key] = wfMsg( 'blanknamespace' );
+ $formatted[$key] = wfMessage( 'blanknamespace' )->text();
}
return $formatted;
}
* @return String
*/
public static function getOpenSearchTemplate() {
- global $wgOpenSearchTemplate, $wgServer;
- if ( $wgOpenSearchTemplate ) {
+ global $wgOpenSearchTemplate, $wgCanonicalServer;
+ if ( $wgOpenSearchTemplate ) {
return $wgOpenSearchTemplate;
} else {
$ns = implode( '|', SearchEngine::defaultNamespaces() );
- if ( !$ns ) $ns = "0";
- return $wgServer . wfScript( 'api' ) . '?action=opensearch&search={searchTerms}&namespace=' . $ns;
+ if ( !$ns ) {
+ $ns = "0";
+ }
+ return $wgCanonicalServer . wfScript( 'api' ) . '?action=opensearch&search={searchTerms}&namespace=' . $ns;
}
}
$row = $this->mResultSet->fetchObject();
if ( $row === false )
return false;
-
+
return SearchResult::newFromRow( $row );
}
/**
- * @todo Fixme: This class is horribly factored. It would probably be better to
+ * @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
* let the fancy self-highlighters extend that.
* @ingroup Search
/**
* Return a new SearchResult and initializes it with a title.
- *
- * @param $title Title
+ *
+ * @param $title Title
* @return SearchResult
*/
public static function newFromTitle( $title ) {
}
/**
* Return a new SearchResult and initializes it with a row.
- *
+ *
* @param $row object
* @return SearchResult
*/
$result->initFromRow( $row );
return $result;
}
-
+
public function __construct( $row = null ) {
if ( !is_null( $row ) ) {
// Backwards compatibility with pre-1.17 callers
$this->initFromRow( $row );
}
}
-
+
/**
* Initialize from a database row. Makes a Title and passes that to
* initFromTitle.
- *
+ *
* @param $row object
*/
protected function initFromRow( $row ) {
$this->initFromTitle( Title::makeTitle( $row->page_namespace, $row->page_title ) );
}
-
+
/**
* Initialize from a Title and if possible initializes a corresponding
* Revision and File.
- *
+ *
* @param $title Title
*/
protected function initFromTitle( $title ) {
$this->mTitle = $title;
if ( !is_null( $this->mTitle ) ) {
- $this->mRevision = Revision::newFromTitle( $this->mTitle );
+ $this->mRevision = Revision::newFromTitle(
+ $this->mTitle, false, Revision::READ_NORMAL );
if ( $this->mTitle->getNamespace() === NS_FILE )
$this->mImage = wfFindFile( $this->mTitle );
}
}
/**
- * @return Double or null if not supported
+ * @return float|null if not supported
*/
function getScore() {
return null;
*/
protected function initText() {
if ( !isset( $this->mText ) ) {
- if ( $this->mRevision != null )
- $this->mText = $this->mRevision->getText();
- else // TODO: can we fetch raw wikitext for commons images?
+ if ( $this->mRevision != null ) {
+ //TODO: don't use the text, but the content object!
+ $content = $this->mRevision->getContent();
+ $this->mText = $content->getTextForSearchIndex();
+ } else { // TODO: can we fetch raw wikitext for commons images?
$this->mText = '';
-
+ }
}
}
function getTextSnippet( $terms ) {
global $wgUser, $wgAdvancedSearchHighlighting;
$this->initText();
+
+ // TODO: make highliter take a content object. Make ContentHandler a factory for SearchHighliter.
list( $contextlines, $contextchars ) = SearchEngine::userHighlightPrefs( $wgUser );
$h = new SearchHighlighter();
if ( $wgAdvancedSearchHighlighting )
function getTimestamp() {
if ( $this->mRevision )
return $this->mRevision->getTimestamp();
- else if ( $this->mImage )
+ elseif ( $this->mImage )
return $this->mImage->getTimestamp();
return '';
}
2 => '/(\[\[)|(\]\])/', // image
3 => "/(\n\\{\\|)|(\n\\|\\})/" ); // table
- // FIXME: this should prolly be a hook or something
+ // @todo FIXME: This should prolly be a hook or something
if ( function_exists( 'wfCite' ) ) {
$spat .= '|(<ref>)'; // references via cite extension
$endPatterns[4] = '/(<ref>)|(<\/ref>)/';
$anyterm = implode( '|', $terms );
$phrase = implode( "$wgSearchHighlightBoundaries+", $terms );
- // FIXME: a hack to scale contextchars, a correct solution
+ // @todo FIXME: A hack to scale contextchars, a correct solution
// would be to have contextchars actually be char and not byte
// length, and do proper utf-8 substrings and lengths everywhere,
// but PHP is making that very hard and unclean to implement :(
} else {
// if begin of the article contains the whole phrase, show only that !!
if ( array_key_exists( $first, $snippets ) && preg_match( $pat1, $snippets[$first] )
- && $offsets[$first] < $contextchars * 2 ) {
+ && $offsets[$first] < $contextchars * 2 ) {
$snippets = array ( $first => $snippets[$first] );
}
// add more lines
$add = $index + 1;
while ( $len < $targetchars - 20
- && array_key_exists( $add, $all )
- && !array_key_exists( $add, $snippets ) ) {
- $offsets[$add] = 0;
- $tt = "\n" . $this->extract( $all[$add], 0, $targetchars - $len, $offsets[$add] );
+ && array_key_exists( $add, $all )
+ && !array_key_exists( $add, $snippets ) ) {
+ $offsets[$add] = 0;
+ $tt = "\n" . $this->extract( $all[$add], 0, $targetchars - $len, $offsets[$add] );
$extended[$add] = $tt;
$len += strlen( $tt );
$add++;
if ( ! isset( $processed[$term] ) ) {
$pat3 = "/$patPre(" . $term . ")$patPost/ui"; // highlight word
$extract = preg_replace( $pat3,
- "\\1<span class='searchmatch'>\\2</span>\\3", $extract );
+ "\\1<span class='searchmatch'>\\2</span>\\3", $extract );
$processed[$term] = true;
}
}
* Do manual case conversion for non-ascii chars
*
* @param $matches Array
+ * @return string
*/
function caseCallback( $matches ) {
global $wgContLang;
if ( strlen( $matches[0] ) > 1 ) {
return '[' . $wgContLang->lc( $matches[0] ) . $wgContLang->uc( $matches[0] ) . ']';
- } else
+ } else {
return $matches[0];
+ }
}
/**
* @return String
*/
function extract( $text, $start, $end, &$posStart = null, &$posEnd = null ) {
- if ( $start != 0 )
+ if ( $start != 0 ) {
$start = $this->position( $text, $start, 1 );
- if ( $end >= strlen( $text ) )
+ }
+ if ( $end >= strlen( $text ) ) {
$end = strlen( $text );
- else
+ } else {
$end = $this->position( $text, $end );
+ }
- if ( !is_null( $posStart ) )
+ if ( !is_null( $posStart ) ) {
$posStart = $start;
- if ( !is_null( $posEnd ) )
+ }
+ if ( !is_null( $posEnd ) ) {
$posEnd = $end;
+ }
- if ( $end > $start )
+ if ( $end > $start ) {
return substr( $text, $start, $end - $start );
- else
+ } else {
return '';
+ }
}
/**
/**
* Basic wikitext removal
* @protected
+ * @return mixed
*/
function removeWiki( $text ) {
$fname = __METHOD__;
}
/**
- * Simple & fast snippet extraction, but gives completely unrelevant
- * snippets
- *
- * @param $text String
- * @param $terms Array
- * @param $contextlines Integer
- * @param $contextchars Integer
- * @return String
- */
- public function highlightSimple( $text, $terms, $contextlines, $contextchars ) {
- global $wgContLang;
- $fname = __METHOD__;
-
- $lines = explode( "\n", $text );
-
- $terms = implode( '|', $terms );
- $max = intval( $contextchars ) + 1;
- $pat1 = "/(.*)($terms)(.{0,$max})/i";
-
- $lineno = 0;
-
- $extract = "";
- wfProfileIn( "$fname-extract" );
- foreach ( $lines as $line ) {
- if ( 0 == $contextlines ) {
- break;
- }
- ++$lineno;
- $m = array();
- if ( ! preg_match( $pat1, $line, $m ) ) {
- continue;
- }
- --$contextlines;
- // truncate function changes ... to relevant i18n message.
- $pre = $wgContLang->truncate( $m[1], - $contextchars, '...', false );
-
- if ( count( $m ) < 3 ) {
- $post = '';
- } else {
- $post = $wgContLang->truncate( $m[3], $contextchars, '...', false );
- }
-
- $found = $m[2];
-
- $line = htmlspecialchars( $pre . $found . $post );
- $pat2 = '/(' . $terms . ")/i";
- $line = preg_replace( $pat2,
- "<span class='searchmatch'>\\1</span>", $line );
-
- $extract .= "${line}\n";
- }
- wfProfileOut( "$fname-extract" );
-
- return $extract;
- }
+ * Simple & fast snippet extraction, but gives completely unrelevant
+ * snippets
+ *
+ * @param $text String
+ * @param $terms Array
+ * @param $contextlines Integer
+ * @param $contextchars Integer
+ * @return String
+ */
+ public function highlightSimple( $text, $terms, $contextlines, $contextchars ) {
+ global $wgContLang;
+ $fname = __METHOD__;
+
+ $lines = explode( "\n", $text );
+
+ $terms = implode( '|', $terms );
+ $max = intval( $contextchars ) + 1;
+ $pat1 = "/(.*)($terms)(.{0,$max})/i";
+
+ $lineno = 0;
+
+ $extract = "";
+ wfProfileIn( "$fname-extract" );
+ foreach ( $lines as $line ) {
+ if ( 0 == $contextlines ) {
+ break;
+ }
+ ++$lineno;
+ $m = array();
+ if ( ! preg_match( $pat1, $line, $m ) ) {
+ continue;
+ }
+ --$contextlines;
+ // truncate function changes ... to relevant i18n message.
+ $pre = $wgContLang->truncate( $m[1], - $contextchars, '...', false );
+
+ if ( count( $m ) < 3 ) {
+ $post = '';
+ } else {
+ $post = $wgContLang->truncate( $m[3], $contextchars, '...', false );
+ }
+
+ $found = $m[2];
+
+ $line = htmlspecialchars( $pre . $found . $post );
+ $pat2 = '/(' . $terms . ")/i";
+ $line = preg_replace( $pat2,
+ "<span class='searchmatch'>\\1</span>", $line );
+
+ $extract .= "${line}\n";
+ }
+ wfProfileOut( "$fname-extract" );
+
+ return $extract;
+ }
}
/**
* Dummy class to be used when non-supported Database engine is present.
- * @todo Fixme: dummy class should probably try something at least mildly useful,
+ * @todo FIXME: Dummy class should probably try something at least mildly useful,
* such as a LIKE search through titles.
* @ingroup Search
*/