Follow up on r49790. Fixed a bug on variant select.
[lhc/web/wiklou.git] / includes / SearchEngine.php
index 032865e..86822d8 100644 (file)
@@ -13,6 +13,7 @@
 class SearchEngine {
        var $limit = 10;
        var $offset = 0;
+       var $prefix = '';
        var $searchTerms = array();
        var $namespaces = array( NS_MAIN );
        var $showRedirects = false;
@@ -20,11 +21,10 @@ class SearchEngine {
        /**
         * Perform a full text search query and return a result set.
         * If title searches are not supported or disabled, return null.
+        * STUB
         *
-        * @param string $term - Raw search term
+        * @param $term String: raw search term
         * @return SearchResultSet
-        * @access public
-        * @abstract
         */
        function searchText( $term ) {
                return null;
@@ -33,11 +33,10 @@ class SearchEngine {
        /**
         * Perform a title-only search query and return a result set.
         * If title searches are not supported or disabled, return null.
+        * STUB
         *
-        * @param string $term - Raw search term
+        * @param $term String: raw search term
         * @return SearchResultSet
-        * @access public
-        * @abstract
         */
        function searchTitle( $term ) {
                return null;
@@ -48,11 +47,19 @@ class SearchEngine {
                return true;
        }
        
+       /**
+        * Transform search term in cases when parts of the query came as different GET params (when supported)
+        * e.g. for prefix queries: search=test&prefix=Main_Page/Archive -> test prefix:Main Page/Archive
+        */
+       function transformSearchTerm( $term ) {
+               return $term;
+       }
+       
        /**
         * If an exact title match can be find, or a very slightly close match,
         * return the title. If no match, returns NULL.
         *
-        * @param string $term
+        * @param $searchterm String
         * @return Title
         */
        public static function getNearMatch( $searchterm ) {
@@ -71,8 +78,13 @@ class SearchEngine {
                        if (is_null($title))
                                return NULL;
 
-                       if ( $title->getNamespace() == NS_SPECIAL || $title->isExternal() 
-                            || $title->exists() ) {
+                       if ( $title->getNamespace() == NS_SPECIAL || $title->isExternal() || $title->exists() ) {
+                               return $title;
+                       }
+                       
+                       # See if it still otherwise has content is some sane sense
+                       $article = MediaWiki::articleFromTitle( $title );
+                       if( $article->hasViewableContent() ) {
                                return $title;
                        }
 
@@ -127,7 +139,7 @@ class SearchEngine {
                # 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_IMAGE ) {
+               if( $title->getNamespace() == NS_FILE ) {
                        $image = wfFindFile( $title );
                        if( $image ) {
                                return $title;
@@ -150,16 +162,15 @@ class SearchEngine {
        }
 
        public static function legalSearchChars() {
-               return "A-Za-z_'0-9\\x80-\\xFF\\-";
+               return "A-Za-z_'.0-9\\x80-\\xFF\\-";
        }
 
        /**
         * Set the maximum number of results to return
         * and how many to skip before returning the first.
         *
-        * @param int $limit
-        * @param int $offset
-        * @access public
+        * @param $limit Integer
+        * @param $offset Integer
         */
        function setLimitOffset( $limit, $offset = 0 ) {
                $this->limit = intval( $limit );
@@ -170,8 +181,7 @@ class SearchEngine {
         * Set which namespaces the search should include.
         * Give an array of namespace index numbers.
         *
-        * @param array $namespaces
-        * @access public
+        * @param $namespaces Array
         */
        function setNamespaces( $namespaces ) {
                $this->namespaces = $namespaces;
@@ -181,7 +191,7 @@ class SearchEngine {
         * Parse some common prefixes: all (search everything)
         * or namespace names
         *
-        * @param string $query
+        * @param $query String
         */
        function replacePrefixes( $query ){
                global $wgContLang;
@@ -210,7 +220,7 @@ class SearchEngine {
 
        /**
         * Make a list of searchable namespaces and their canonical names.
-        * @return array
+        * @return Array
         */
        public static function searchableNamespaces() {
                global $wgContLang;
@@ -227,26 +237,23 @@ class SearchEngine {
         * Extract default namespaces to search from the given user's
         * settings, returning a list of index numbers.
         *
-        * @param User $user
-        * @return array
-        * @static 
+        * @param $user User
+        * @return Array
         */
-       public static function userNamespaces( &$user ) {
-               $arr = array();
-               foreach( SearchEngine::searchableNamespaces() as $ns => $name ) {
-                       if( $user->getOption( 'searchNs' . $ns ) ) {
-                               $arr[] = $ns;
-                       }
-               }
+       public static function userNamespaces( $user ) {
+               $arr = Preferences::loadOldSearchNs( $user );
+               $searchableNamespaces = SearchEngine::searchableNamespaces();
+               
+               $arr = array_intersect( $arr, array_keys($searchableNamespaces) ); // Filter
+               
                return $arr;
        }
        
        /**
         * Find snippet highlight settings for a given user
         *
-        * @param User $user
-        * @return array contextlines, contextchars 
-        * @static
+        * @param $user User
+        * @return Array contextlines, contextchars 
         */
        public static function userHighlightPrefs( &$user ){
                //$contextlines = $user->getOption( 'contextlines',  5 );
@@ -259,8 +266,7 @@ class SearchEngine {
        /**
         * An array of namespaces indexes to be searched by default
         * 
-        * @return array 
-        * @static
+        * @return Array 
         */
        public static function defaultNamespaces(){
                global $wgNamespacesToBeSearchedDefault;
@@ -272,7 +278,7 @@ class SearchEngine {
         * Get a list of namespace names useful for showing in tooltips
         * and preferences
         *
-        * @param unknown_type $namespaces
+        * @param $namespaces Array
         */
        public static function namespacesAsText( $namespaces ){
                global $wgContLang;
@@ -289,8 +295,7 @@ class SearchEngine {
         * An array of "project" namespaces indexes typically searched
         * by logged-in users
         * 
-        * @return array 
-        * @static
+        * @return Array 
         */
        public static function projectNamespaces() {
                global $wgNamespacesToBeSearchedDefault, $wgNamespacesToBeSearchedProject;
@@ -302,8 +307,7 @@ class SearchEngine {
         * An array of "project" namespaces indexes typically searched
         * by logged-in users in addition to the default namespaces
         * 
-        * @return array 
-        * @static
+        * @return Array
         */
        public static function defaultAndProjectNamespaces() {
                global $wgNamespacesToBeSearchedDefault, $wgNamespacesToBeSearchedProject;
@@ -315,8 +319,8 @@ class SearchEngine {
        /**
         * Return a 'cleaned up' search string
         *
-        * @return string
-        * @access public
+        * @param $text String
+        * @return String
         */
        function filter( $text ) {
                $lc = $this->legalSearchChars();
@@ -344,11 +348,11 @@ class SearchEngine {
        /**
         * Create or update the search index record for the given page.
         * Title and text should be pre-processed.
+        * STUB
         *
-        * @param int $id
-        * @param string $title
-        * @param string $text
-        * @abstract
+        * @param $id Integer
+        * @param $title String
+        * @param $text String
         */
        function update( $id, $title, $text ) {
                // no-op
@@ -357,10 +361,10 @@ class SearchEngine {
        /**
         * Update a search index record's title only.
         * Title should be pre-processed.
+        * STUB
         *
-        * @param int $id
-        * @param string $title
-        * @abstract
+        * @param $id Integer
+        * @param $title String
         */
        function updateTitle( $id, $title ) {
                // no-op
@@ -369,16 +373,15 @@ class SearchEngine {
        /**
         * Get OpenSearch suggestion template
         * 
-        * @return string
-        * @static 
+        * @return String
         */
        public static function getOpenSearchTemplate() {
                global $wgOpenSearchTemplate, $wgServer, $wgScriptPath;
-               if($wgOpenSearchTemplate)               
+               if( $wgOpenSearchTemplate )     {       
                        return $wgOpenSearchTemplate;
-               else
-                       $ns = implode(',',SearchEngine::defaultNamespaces());
-                       if(!$ns) $ns = "0";
+               } else 
+                       $ns = implode( '|', SearchEngine::defaultNamespaces() );
+                       if( !$ns ) $ns = "0";
                        return $wgServer . $wgScriptPath . '/api.php?action=opensearch&search={searchTerms}&namespace='.$ns;
                }
        }
@@ -386,15 +389,14 @@ class SearchEngine {
        /**
         * Get internal MediaWiki Suggest template 
         * 
-        * @return string
-        * @static
+        * @return String
         */
        public static function getMWSuggestTemplate() {
                global $wgMWSuggestTemplate, $wgServer, $wgScriptPath;
                if($wgMWSuggestTemplate)                
                        return $wgMWSuggestTemplate;
                else 
-                       return $wgServer . $wgScriptPath . '/api.php?action=opensearch&search={searchTerms}&namespace={namespaces}';
+                       return $wgServer . $wgScriptPath . '/api.php?action=opensearch&search={searchTerms}&namespace={namespaces}&suggest';
        }
 }
 
@@ -405,10 +407,9 @@ class SearchResultSet {
        /**
         * Fetch an array of regular expression fragments for matching
         * the search terms as parsed by this engine in a text extract.
+        * STUB
         *
-        * @return array
-        * @access public
-        * @abstract
+        * @return Array
         */
        function termMatches() {
                return array();
@@ -420,8 +421,9 @@ class SearchResultSet {
 
        /**
         * Return true if results are included in this result set.
-        * @return bool
-        * @abstract
+        * STUB
+        *
+        * @return Boolean
         */
        function hasResults() {
                return false;
@@ -435,8 +437,7 @@ class SearchResultSet {
         *
         * Return null if no total hits number is supported.
         *
-        * @return int
-        * @access public
+        * @return Integer
         */
        function getTotalHits() {
                return null;
@@ -446,22 +447,21 @@ class SearchResultSet {
         * Some search modes return a suggested alternate term if there are
         * no exact hits. Returns true if there is one on this set.
         *
-        * @return bool
-        * @access public
+        * @return Boolean
         */
        function hasSuggestion() {
                return false;
        }
 
        /**
-        * @return string suggested query, null if none
+        * @return String: suggested query, null if none
         */
        function getSuggestionQuery(){
                return null;
        }
 
        /**
-        * @return string highlighted suggested query, '' if none
+        * @return String: HTML highlighted suggested query, '' if none
         */
        function getSuggestionSnippet(){
                return '';
@@ -471,7 +471,7 @@ class SearchResultSet {
         * Return information about how and from where the results were fetched,
         * should be useful for diagnostics and debugging 
         *
-        * @return string
+        * @return String
         */
        function getInfo() {
                return null;
@@ -489,7 +489,7 @@ class SearchResultSet {
        /**
         * Check if there are results on other wikis
         *
-        * @return boolean
+        * @return Boolean
         */
        function hasInterwikiResults() {
                return $this->getInterwikiResults() != null;
@@ -498,9 +498,9 @@ class SearchResultSet {
 
        /**
         * Fetches next search result, or false.
+        * STUB
+        *
         * @return SearchResult
-        * @access public
-        * @abstract
         */
        function next() {
                return false;
@@ -508,7 +508,6 @@ class SearchResultSet {
 
        /**
         * Frees the result set, if applicable.
-        * @ access public
         */
        function free() {
                // ...
@@ -525,9 +524,9 @@ class SearchResultTooMany {
 
 
 /**
- * @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.
+ * @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
  */
 class SearchResult {
@@ -538,7 +537,7 @@ class SearchResult {
                $this->mTitle = Title::makeTitle( $row->page_namespace, $row->page_title );
                if( !is_null($this->mTitle) ){
                        $this->mRevision = Revision::newFromTitle( $this->mTitle );
-                       if( $this->mTitle->getNamespace() === NS_IMAGE )
+                       if( $this->mTitle->getNamespace() === NS_FILE )
                                $this->mImage = wfFindFile( $this->mTitle );
                }
        }
@@ -546,8 +545,7 @@ class SearchResult {
        /**
         * Check if this is result points to an invalid title
         *
-        * @return boolean
-        * @access public
+        * @return Boolean
         */
        function isBrokenTitle(){
                if( is_null($this->mTitle) )
@@ -558,8 +556,7 @@ class SearchResult {
        /**
         * Check if target page is missing, happens when index is out of date
         * 
-        * @return boolean
-        * @access public
+        * @return Boolean
         */
        function isMissingRevision(){
                return !$this->mRevision && !$this->mImage;
@@ -567,14 +564,13 @@ class SearchResult {
 
        /**
         * @return Title
-        * @access public
         */
        function getTitle() {
                return $this->mTitle;
        }
 
        /**
-        * @return double or null if not supported
+        * @return Double or null if not supported
         */
        function getScore() {
                return null;
@@ -594,8 +590,8 @@ class SearchResult {
        }
        
        /**
-        * @param array $terms terms to highlight
-        * @return string highlighted text snippet, null (and not '') if not supported 
+        * @param $terms Array: terms to highlight
+        * @return String: highlighted text snippet, null (and not '') if not supported 
         */
        function getTextSnippet($terms){
                global $wgUser, $wgAdvancedSearchHighlighting;
@@ -609,16 +605,16 @@ class SearchResult {
        }
        
        /**
-        * @param array $terms terms to highlight
-        * @return string highlighted title, '' if not supported
+        * @param $terms Array: terms to highlight
+        * @return String: highlighted title, '' if not supported
         */
        function getTitleSnippet($terms){
                return '';
        }
 
        /**
-        * @param array $terms terms to highlight
-        * @return string highlighted redirect name (redirect to this page), '' if none or not supported
+        * @param $terms Array: terms to highlight
+        * @return String: highlighted redirect name (redirect to this page), '' if none or not supported
         */
        function getRedirectSnippet($terms){
                return '';
@@ -646,7 +642,7 @@ class SearchResult {
        }
 
        /**
-        * @return string timestamp
+        * @return String: timestamp
         */
        function getTimestamp(){
                if( $this->mRevision )
@@ -657,7 +653,7 @@ class SearchResult {
        }
 
        /**
-        * @return int number of words
+        * @return Integer: number of words
         */
        function getWordCount(){
                $this->initText();
@@ -665,7 +661,7 @@ class SearchResult {
        }
 
        /**
-        * @return int size in bytes
+        * @return Integer: size in bytes
         */
        function getByteSize(){
                $this->initText();
@@ -673,14 +669,14 @@ class SearchResult {
        }
        
        /**
-        * @return boolean if hit has related articles
+        * @return Boolean if hit has related articles
         */
        function hasRelated(){
                return false;
        }
        
        /**
-        * @return interwiki prefix of the title (return iw even if title is broken)
+        * @return String: interwiki prefix of the title (return iw even if title is broken)
         */
        function getInterwikiPrefix(){
                return '';
@@ -702,11 +698,11 @@ class SearchHighlighter {
        /**
         * Default implementation of wikitext highlighting
         *
-        * @param string $text
-        * @param array $terms Terms to highlight (unescaped)
-        * @param int $contextlines
-        * @param int $contextchars
-        * @return string
+        * @param $text String
+        * @param $terms Array: terms to highlight (unescaped)
+        * @param $contextlines Integer
+        * @param $contextchars Integer
+        * @return String
         */
        public function highlightText( $text, $terms, $contextlines, $contextchars ) {
                global $wgLang, $wgContLang;
@@ -745,7 +741,7 @@ class SearchHighlighter {
                                                if($key == 2){
                                                        // see if this is an image link
                                                        $ns = substr($val[0],2,-1);
-                                                       if( $wgContLang->getNsIndex($ns) != NS_IMAGE )
+                                                       if( $wgContLang->getNsIndex($ns) != NS_FILE )
                                                                break;
                                                        
                                                }
@@ -944,9 +940,9 @@ class SearchHighlighter {
        /**
         * Split text into lines and add it to extracts array
         *
-        * @param array $extracts index -> $line
-        * @param int $count
-        * @param string $text
+        * @param $extracts Array: index -> $line
+        * @param $count Integer
+        * @param $text String
         */
        function splitAndAdd(&$extracts, &$count, $text){
                $split = explode( "\n", $this->mCleanWikitext? $this->removeWiki($text) : $text );
@@ -960,7 +956,7 @@ class SearchHighlighter {
        /**
         * Do manual case conversion for non-ascii chars
         *
-        * @param unknown_type $matches
+        * @param $matches Array
         */
        function caseCallback($matches){
                global $wgContLang;
@@ -973,12 +969,12 @@ class SearchHighlighter {
        /**
         * Extract part of the text from start to end, but by
         * not chopping up words
-        * @param string $text
-        * @param int $start
-        * @param int $end
-        * @param int $posStart (out) actual start position
-        * @param int $posEnd (out) actual end position
-        * @return string  
+        * @param $text String
+        * @param $start Integer
+        * @param $end Integer
+        * @param $posStart Integer: (out) actual start position
+        * @param $posEnd Integer: (out) actual end position
+        * @return String  
         */
        function extract($text, $start, $end, &$posStart = null, &$posEnd = null ){
                global $wgContLang;             
@@ -1004,10 +1000,10 @@ class SearchHighlighter {
        /**
         * Find a nonletter near a point (index) in the text
         *
-        * @param string $text
-        * @param int $point
-        * @param int $offset to found index
-        * @return int nearest nonletter index, or beginning of utf8 char if none
+        * @param $text String
+        * @param $point Integer
+        * @param $offset Integer: offset to found index
+        * @return Integer: nearest nonletter index, or beginning of utf8 char if none
         */
        function position($text, $point, $offset=0 ){
                $tolerance = 10;
@@ -1034,12 +1030,12 @@ class SearchHighlighter {
        /**
         * Search extracts for a pattern, and return snippets
         *
-        * @param string $pattern regexp for matching lines
-        * @param array $extracts extracts to search   
-        * @param int $linesleft number of extracts to make
-        * @param int $contextchars length of snippet
-        * @param array $out map for highlighted snippets
-        * @param array $offsets map of starting points of snippets
+        * @param $pattern String: regexp for matching lines
+        * @param $extracts Array: extracts to search   
+        * @param $linesleft Integer: number of extracts to make
+        * @param $contextchars Integer: length of snippet
+        * @param $out Array: map for highlighted snippets
+        * @param $offsets Array: map of starting points of snippets
         * @protected
         */
        function process( $pattern, $extracts, &$linesleft, &$contextchars, &$out, &$offsets ){
@@ -1106,7 +1102,7 @@ class SearchHighlighter {
         * callback to replace [[target|caption]] kind of links, if
         * the target is category or image, leave it
         *
-        * @param array $matches
+        * @param $matches Array
         */
        function linkReplace($matches){
                $colon = strpos( $matches[1], ':' ); 
@@ -1115,7 +1111,7 @@ class SearchHighlighter {
                global $wgContLang;
                $ns = substr( $matches[1], 0, $colon );
                $index = $wgContLang->getNsIndex($ns);
-               if( $index !== false && ($index == NS_IMAGE || $index == NS_CATEGORY) )
+               if( $index !== false && ($index == NS_FILE || $index == NS_CATEGORY) )
                        return $matches[0]; // return the whole thing 
                else
                        return $matches[2];
@@ -1126,11 +1122,11 @@ class SearchHighlighter {
      * Simple & fast snippet extraction, but gives completely unrelevant
      * snippets
      *
-     * @param string $text
-     * @param array $terms
-     * @param int $contextlines
-     * @param int $contextchars
-     * @return string
+     * @param $text String
+     * @param $terms Array
+     * @param $contextlines Integer
+     * @param $contextchars Integer
+     * @return String
      */
     public function highlightSimple( $text, $terms, $contextlines, $contextchars ) {
         global $wgLang, $wgContLang;
@@ -1156,12 +1152,12 @@ class SearchHighlighter {
                 continue;
             }
             --$contextlines;
-            $pre = $wgContLang->truncate( $m[1], -$contextchars, ' ... ' );
+            $pre = $wgContLang->truncate( $m[1], -$contextchars );
 
             if ( count( $m ) < 3 ) {
                 $post = '';
             } else {
-                $post = $wgContLang->truncate( $m[3], $contextchars, ' ... ' );
+                $post = $wgContLang->truncate( $m[3], $contextchars );
             }
 
             $found = $m[2];
@@ -1182,7 +1178,7 @@ class SearchHighlighter {
 
 /**
  * Dummy class to be used when non-supported Database engine is present.
- * @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
  */