* (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 ===
$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)
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;
$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 )
$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 .= '<li>' . $sk->makeKnownLinkObj( $nt ) . "</li>\n";
- }
- if ( $i > $limit ) {
- $more = '<i>' . $sk->makeKnownLink( $wgContLang->specialPage( "Allpages" ),
- wfMsg('moredotdotdot'),
- "namespace=0&from=" . wfUrlEncode ( $term ) ) .
- '</i>';
- }
- } 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 .= '<li>' . $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, $page ) ) . '</li>';
+
+ $results = PrefixSearch::titleSearch( $term, $limit + 1 );
+ foreach( array_slice( $results, 0, $limit ) as $titleText ) {
+ $r .= '<li>' . $sk->makeKnownLink( $titleText ) . "</li>\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 = '<i>' .
+ $sk->makeKnownLinkObj(
+ SpecialPage::getTitleFor( 'Specialpages' ),
+ wfMsgHtml( 'moredotdotdot' ) ) .
+ '</i>';
+ }
+ } else {
+ if( count( $results ) > $limit ) {
+ $more = '<i>' .
+ $sk->makeKnownLinkObj(
+ SpecialPage::getTitleFor( "Allpages", $term ),
+ wfMsgHtml( 'moredotdotdot' ) ) .
+ '</i>';
}
}
-
- $canSearch = false;
}
$valid = (bool) $term_title;
'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',
--- /dev/null
+<?php
+
+class PrefixSearch {
+ /**
+ * Do a prefix search of titles and return a list of matching page names.
+ * @param string $search
+ * @param int $limit
+ * @return array of strings
+ */
+ public static function titleSearch( $search, $limit ) {
+ $search = trim( $search );
+ if( $search == '' ) {
+ return array(); // Return empty result
+ }
+
+ $title = Title::newFromText( $search );
+ if( $title && $title->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
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();