Bugfis, making ?uselang=qqq an invalid language code, see
[lhc/web/wiklou.git] / includes / SearchEngine.php
index 50231d3..3ea0341 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;
@@ -43,6 +44,19 @@ class SearchEngine {
                return null;
        }
        
+       /** If this search backend can list/unlist redirects */
+       function acceptListRedirects() {
+               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.
@@ -66,7 +80,8 @@ class SearchEngine {
                        if (is_null($title))
                                return NULL;
 
-                       if ( $title->getNamespace() == NS_SPECIAL || $title->exists() ) {
+                       if ( $title->getNamespace() == NS_SPECIAL || $title->isExternal() 
+                            || $title->exists() ) {
                                return $title;
                        }
 
@@ -97,19 +112,6 @@ class SearchEngine {
                                return $title;
                        }
 
-                       global $wgCapitalLinks, $wgContLang;
-                       if( !$wgCapitalLinks ) {
-                               // Catch differs-by-first-letter-case-only
-                               $title = Title::newFromText( $wgContLang->ucfirst( $term ) );
-                               if ( $title && $title->exists() ) {
-                                       return $title;
-                               }
-                               $title = Title::newFromText( $wgContLang->lcfirst( $term ) );
-                               if ( $title && $title->exists() ) {
-                                       return $title;
-                               }
-                       }
-
                        // Give hooks a chance at better match variants
                        $title = null;
                        if( !wfRunHooks( 'SearchGetNearMatch', array( $term, &$title ) ) ) {
@@ -134,7 +136,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;
@@ -157,7 +159,7 @@ class SearchEngine {
        }
 
        public static function legalSearchChars() {
-               return "A-Za-z_'0-9\\x80-\\xFF\\-";
+               return "A-Za-z_'.0-9\\x80-\\xFF\\-";
        }
 
        /**
@@ -274,7 +276,51 @@ class SearchEngine {
                
                return array_keys($wgNamespacesToBeSearchedDefault, true);
        }
-
+       
+       /**
+        * Get a list of namespace names useful for showing in tooltips
+        * and preferences
+        *
+        * @param unknown_type $namespaces
+        */
+       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' );
+               }
+               return $formatted;
+       }
+       
+       /**
+        * An array of "project" namespaces indexes typically searched
+        * by logged-in users
+        * 
+        * @return array 
+        * @static
+        */
+       public static function projectNamespaces() {
+               global $wgNamespacesToBeSearchedDefault, $wgNamespacesToBeSearchedProject;
+               
+               return array_keys( $wgNamespacesToBeSearchedProject, true );
+       }
+       
+       /**
+        * An array of "project" namespaces indexes typically searched
+        * by logged-in users in addition to the default namespaces
+        * 
+        * @return array 
+        * @static
+        */
+       public static function defaultAndProjectNamespaces() {
+               global $wgNamespacesToBeSearchedDefault, $wgNamespacesToBeSearchedProject;
+               
+               return array_keys( $wgNamespacesToBeSearchedDefault + 
+                       $wgNamespacesToBeSearchedProject, true);
+       }
+       
        /**
         * Return a 'cleaned up' search string
         *
@@ -292,19 +338,14 @@ class SearchEngine {
         * @return SearchEngine
         */
        public static function create() {
-               global $wgDBtype, $wgSearchType;
+               global $wgSearchType;
+               $dbr = wfGetDB( DB_SLAVE );
                if( $wgSearchType ) {
                        $class = $wgSearchType;
-               } elseif( $wgDBtype == 'mysql' ) {
-                       $class = 'SearchMySQL';
-               } else if ( $wgDBtype == 'postgres' ) {
-                       $class = 'SearchPostgres';
-               } else if ( $wgDBtype == 'oracle' ) {
-                       $class = 'SearchOracle';
                } else {
-                       $class = 'SearchEngineDummy';
+                       $class = $dbr->getSearchEngine();
                }
-               $search = new $class( wfGetDB( DB_SLAVE ) );
+               $search = new $class( $dbr );
                $search->setLimitOffset(0,0);
                return $search;
        }
@@ -342,11 +383,11 @@ class SearchEngine {
         */
        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;
                }
        }
@@ -429,7 +470,7 @@ class SearchResultSet {
        }
 
        /**
-        * @return string highlighted suggested query, '' if none
+        * @return string HTML highlighted suggested query, '' if none
         */
        function getSuggestionSnippet(){
                return '';
@@ -493,15 +534,22 @@ 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.
  * @ingroup Search
  */
 class SearchResult {
        var $mRevision = null;
+       var $mImage = null;
 
-       function SearchResult( $row ) {
+       function __construct( $row ) {
                $this->mTitle = Title::makeTitle( $row->page_namespace, $row->page_title );
-               if( !is_null($this->mTitle) )
+               if( !is_null($this->mTitle) ){
                        $this->mRevision = Revision::newFromTitle( $this->mTitle );
+                       if( $this->mTitle->getNamespace() === NS_FILE )
+                               $this->mImage = wfFindFile( $this->mTitle );
+               }
        }
        
        /**
@@ -523,9 +571,7 @@ class SearchResult {
         * @access public
         */
        function isMissingRevision(){
-               if( !$this->mRevision )
-                       return true;
-               return false;
+               return !$this->mRevision && !$this->mImage;
        }
 
        /**
@@ -548,7 +594,11 @@ class SearchResult {
         */
        protected function initText(){
                if( !isset($this->mText) ){
-                       $this->mText = $this->mRevision->getText();
+                       if($this->mRevision != null)
+                               $this->mText = $this->mRevision->getText();
+                       else // TODO: can we fetch raw wikitext for commons images?
+                               $this->mText = '';
+                       
                }
        }
        
@@ -608,7 +658,11 @@ class SearchResult {
         * @return string timestamp
         */
        function getTimestamp(){
-               return $this->mRevision->getTimestamp();
+               if( $this->mRevision )
+                       return $this->mRevision->getTimestamp();
+               else if( $this->mImage )
+                       return $this->mImage->getTimestamp();
+               return '';                      
        }
 
        /**
@@ -700,7 +754,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;
                                                        
                                                }
@@ -755,13 +809,12 @@ class SearchHighlighter {
                
                // prepare regexps
                foreach( $terms as $index => $term ) {
-                       $terms[$index] = preg_quote( $term, '/' );                      
                        // manually do upper/lowercase stuff for utf-8 since PHP won't do it
                        if(preg_match('/[\x80-\xff]/', $term) ){
                                $terms[$index] = preg_replace_callback('/./us',array($this,'caseCallback'),$terms[$index]);
+                       } else {
+                               $terms[$index] = $term;
                        }
-                       
-                       
                }
                $anyterm = implode( '|', $terms );
                $phrase = implode("$wgSearchHighlightBoundaries+", $terms );
@@ -1071,7 +1124,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];
@@ -1091,11 +1144,10 @@ class SearchHighlighter {
     public function highlightSimple( $text, $terms, $contextlines, $contextchars ) {
         global $wgLang, $wgContLang;
         $fname = __METHOD__;
-    
+
         $lines = explode( "\n", $text );
         
         $terms = implode( '|', $terms );
-        $terms = str_replace( '/', "\\/", $terms);
         $max = intval( $contextchars ) + 1;
         $pat1 = "/(.*)($terms)(.{0,$max})/i";
 
@@ -1138,16 +1190,11 @@ 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,
+ * such as a LIKE search through titles.
  * @ingroup Search
  */
-class SearchEngineDummy {
-       function search( $term ) {
-               return null;
-       }
-       function setLimitOffset($l, $o) {}
-       function legalSearchChars() {}
-       function update() {}
-       function setnamespaces() {}
-       function searchtitle() {}
-       function searchtext() {}
+class SearchEngineDummy extends SearchEngine {
+       // no-op
 }