From: Brion Vibber Date: Wed, 30 Jan 2008 01:07:49 +0000 (+0000) Subject: * Merged backends for OpenSearch suggestions and AJAX search. X-Git-Tag: 1.31.0-rc.0~49705 X-Git-Url: https://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/exercices/journal.php?a=commitdiff_plain;h=7a3227596e71b60f586d08bbb303fdca3fbc1bd3;p=lhc%2Fweb%2Fwiklou.git * Merged backends for OpenSearch suggestions and AJAX search. Both now accept namespace prefixes, handle 'Media:' and 'Special:' pages, and reject interwiki prefixes. PrefixSearch class centralizes this code, and the backend part can be overridden by the PrefixSearchBackend hook. --- diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 308fe087c5..9798b77c71 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -143,6 +143,11 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN * (bug 11897) Add alias [[Special:CreateAccount]] & [[Special:Userlogin/signup]] for Special:Userlogin?type=signup * (bug 12214) Add a predefined list of delete reasons to the file deletion form +* Merged backends for OpenSearch suggestions and AJAX search. + Both now accept namespace prefixes, handle 'Media:' and 'Special:' pages, + and reject interwiki prefixes. PrefixSearch class centralizes this code, + and the backend part can be overridden by the PrefixSearchBackend hook. + === Bug fixes in 1.12 === diff --git a/docs/hooks.txt b/docs/hooks.txt index b059299ef5..bca4439da9 100644 --- a/docs/hooks.txt +++ b/docs/hooks.txt @@ -800,6 +800,13 @@ the built-in rate limiting checks are used, if enabled. $form : PreferencesForm object &$html : HTML to append to +'PrefixSearchBackend': Override the title prefix search used for OpenSearch and +AJAX search suggestions. Put results into &$results outparam and return false. +$ns : int namespace key to search in +$search : search term (not guaranteed to be conveniently normalized) +$limit : maximum number of results to return +&$results : out param: array of page names (strings) + 'PrefsEmailAudit': called when user changes his email address $user: User (object) changing his email address $oldaddr: old email address (string) diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php index 50182d8aac..ffd3168a8e 100644 --- a/includes/AjaxFunctions.php +++ b/includes/AjaxFunctions.php @@ -73,7 +73,7 @@ function code2utf($num){ return ''; } -define( 'AJAX_SEARCH_VERSION', 1 ); //AJAX search cache version +define( 'AJAX_SEARCH_VERSION', 2 ); //AJAX search cache version function wfSajaxSearch( $term ) { global $wgContLang, $wgOut, $wgUser, $wgCapitalLinks, $wgMemc; @@ -81,12 +81,6 @@ function wfSajaxSearch( $term ) { $sk = $wgUser->getSkin(); $output = ''; - if( !wfRunHooks( 'SajaxSearch', array( $term, &$output ) ) ) { - $response = new AjaxResponse( $output ); - $response->setCacheDuration( 30*60 ); - return $response; - } - $term = trim( $term ); $term = $wgContLang->checkTitleEncoding( $wgContLang->recodeInput( js_unescape( $term ) ) ); if ( $wgCapitalLinks ) @@ -103,41 +97,33 @@ function wfSajaxSearch( $term ) { $r = $more = ''; $canSearch = true; - if( $term_title && $term_title->getNamespace() != NS_SPECIAL ) { - $db = wfGetDB( DB_SLAVE ); - $res = $db->select( 'page', array( 'page_title', 'page_namespace' ), - array( 'page_namespace' => $term_title->getNamespace(), - "page_title LIKE '". $db->strencode( $term_title->getDBkey() ) ."%'" ), - "wfSajaxSearch", - array( 'LIMIT' => $limit+1 ) - ); - - $i = 0; - while ( ( $row = $db->fetchObject( $res ) ) && ( ++$i <= $limit ) ) { - $nt = Title::makeTitle( $row->page_namespace, $row->page_title ); - $r .= '
  • ' . $sk->makeKnownLinkObj( $nt ) . "
  • \n"; - } - if ( $i > $limit ) { - $more = '' . $sk->makeKnownLink( $wgContLang->specialPage( "Allpages" ), - wfMsg('moredotdotdot'), - "namespace=0&from=" . wfUrlEncode ( $term ) ) . - ''; - } - } else if( $term_title && $term_title->getNamespace() == NS_SPECIAL ) { - SpecialPage::initList(); - SpecialPage::initAliasList(); - $specialPages = array_merge( - array_keys( SpecialPage::$mList ), - array_keys( SpecialPage::$mAliases ) - ); - - foreach( $specialPages as $page ) { - if( $wgContLang->uc( $page ) != $page && strpos( $page, $term_title->getText() ) === 0 ) { - $r .= '
  • ' . $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, $page ) ) . '
  • '; + + $results = PrefixSearch::titleSearch( $term, $limit + 1 ); + foreach( array_slice( $results, 0, $limit ) as $titleText ) { + $r .= '
  • ' . $sk->makeKnownLink( $titleText ) . "
  • \n"; + } + + // Hack to check for specials + if( $results ) { + $t = Title::newFromText( $results[0] ); + if( $t && $t->getNamespace() == NS_SPECIAL ) { + $canSearch = false; + if( count( $results ) > $limit ) { + $more = '' . + $sk->makeKnownLinkObj( + SpecialPage::getTitleFor( 'Specialpages' ), + wfMsgHtml( 'moredotdotdot' ) ) . + ''; + } + } else { + if( count( $results ) > $limit ) { + $more = '' . + $sk->makeKnownLinkObj( + SpecialPage::getTitleFor( "Allpages", $term ), + wfMsgHtml( 'moredotdotdot' ) ) . + ''; } } - - $canSearch = false; } $valid = (bool) $term_title; diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 3d34909bce..6faa5c6aaf 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -144,6 +144,7 @@ function __autoload($className) { 'ParserOptions' => 'includes/ParserOptions.php', 'PatrolLog' => 'includes/PatrolLog.php', 'Preprocessor' => 'includes/Preprocessor.php', + 'PrefixSearch' => 'includes/PrefixSearch.php', 'PPFrame' => 'includes/Preprocessor.php', 'PPNode' => 'includes/Preprocessor.php', 'Preprocessor_DOM' => 'includes/Preprocessor_DOM.php', diff --git a/includes/PrefixSearch.php b/includes/PrefixSearch.php new file mode 100644 index 0000000000..bddfb9f1e1 --- /dev/null +++ b/includes/PrefixSearch.php @@ -0,0 +1,135 @@ +getInterwiki() == '' ) { + $ns = $title->getNamespace(); + return self::searchBackend( + $title->getNamespace(), $title->getText(), $limit ); + } + + // Is this a namespace prefix? + $title = Title::newFromText( $search . 'Dummy' ); + if( $title && $title->getText() == 'Dummy' + && $title->getNamespace() != NS_MAIN + && $title->getInterwiki() == '' ) { + return self::searchBackend( + $title->getNamespace(), '', $limit ); + } + + return self::searchBackend( 0, $search, $limit ); + } + + + /** + * Do a prefix search of titles and return a list of matching page names. + * @param string $search + * @param int $limit + * @return array of strings + */ + protected static function searchBackend( $ns, $search, $limit ) { + if( $ns == NS_MEDIA ) { + $ns = NS_IMAGE; + } elseif( $ns == NS_SPECIAL ) { + return self::specialSearch( $search, $limit ); + } + + $srchres = array(); + if( wfRunHooks( 'PrefixSearchBackend', array( $ns, $search, $limit, &$srchres ) ) ) { + return self::defaultSearchBackend( $ns, $search, $limit ); + } + return $srchres; + } + + /** + * Prefix search special-case for Special: namespace. + */ + protected static function specialSearch( $search, $limit ) { + global $wgContLang; + $searchKey = $wgContLang->caseFold( $search ); + + // Unlike SpecialPage itself, we want the canonical forms of both + // canonical and alias title forms... + SpecialPage::initList(); + SpecialPage::initAliasList(); + $keys = array(); + foreach( array_keys( SpecialPage::$mList ) as $page ) { + $keys[$wgContLang->caseFold( $page )] = $page; + } + foreach( $wgContLang->getSpecialPageAliases() as $page => $aliases ) { + foreach( $aliases as $alias ) { + $keys[$wgContLang->caseFold( $alias )] = $alias; + } + } + ksort( $keys ); + + $srchres = array(); + foreach( $keys as $pageKey => $page ) { + if( $searchKey === '' || strpos( $pageKey, $searchKey ) === 0 ) { + $srchres[] = Title::makeTitle( NS_SPECIAL, $page )->getPrefixedText(); + } + if( count( $srchres ) >= $limit ) { + break; + } + } + return $srchres; + } + + /** + * Unless overridden by PrefixSearchBackend hook... + * This is case-sensitive except the first letter (per $wgCapitalLinks) + * + * @param int $ns Namespace to search in + * @param string $search term + * @param int $limit max number of items to return + * @return array of title strings + */ + protected static function defaultSearchBackend( $ns, $search, $limit ) { + global $wgCapitalLinks, $wgContLang; + + if( $wgCapitalLinks ) { + $search = $wgContLang->ucfirst( $search ); + } + + // Prepare nested request + $req = new FauxRequest(array ( + 'action' => 'query', + 'list' => 'allpages', + 'apnamespace' => $ns, + 'aplimit' => $limit, + 'apprefix' => $search + )); + + // Execute + $module = new ApiMain($req); + $module->execute(); + + // Get resulting data + $data = $module->getResultData(); + + // Reformat useful data for future printing by JSON engine + $srchres = array (); + foreach ($data['query']['allpages'] as & $pageinfo) { + // Note: this data will no be printable by the xml engine + // because it does not support lists of unnamed items + $srchres[] = $pageinfo['title']; + } + + return $srchres; + } + +} + +?> \ No newline at end of file diff --git a/includes/api/ApiOpenSearch.php b/includes/api/ApiOpenSearch.php index bfeb2a5b32..c3987ec799 100644 --- a/includes/api/ApiOpenSearch.php +++ b/includes/api/ApiOpenSearch.php @@ -44,37 +44,12 @@ class ApiOpenSearch extends ApiBase { public function execute() { $params = $this->extractRequestParams(); $search = $params['search']; + $limit = $params['limit']; // Open search results may be stored for a very long time $this->getMain()->setCacheMaxAge(1200); - - $title = Title :: newFromText($search); - if(!$title) - return; // Return empty result - - // Prepare nested request - $req = new FauxRequest(array ( - 'action' => 'query', - 'list' => 'allpages', - 'apnamespace' => $title->getNamespace(), - 'aplimit' => $params['limit'], - 'apprefix' => $title->getDBkey() - )); - - // Execute - $module = new ApiMain($req); - $module->execute(); - - // Get resulting data - $data = $module->getResultData(); - - // Reformat useful data for future printing by JSON engine - $srchres = array (); - foreach ($data['query']['allpages'] as & $pageinfo) { - // Note: this data will no be printable by the xml engine - // because it does not support lists of unnamed items - $srchres[] = $pageinfo['title']; - } + + $srchres = PrefixSearch::titleSearch( $search, $limit ); // Set top level elements $result = $this->getResult();