* Made special page names case-insensitive and localisable. Care has been taken to...
authorTim Starling <tstarling@users.mediawiki.org>
Mon, 30 Oct 2006 06:25:31 +0000 (06:25 +0000)
committerTim Starling <tstarling@users.mediawiki.org>
Mon, 30 Oct 2006 06:25:31 +0000 (06:25 +0000)
* Used special page subpages in a few more places, instead of query parameters

51 files changed:
RELEASE-NOTES
includes/Article.php
includes/ChangesList.php
includes/EditPage.php
includes/ImagePage.php
includes/Linker.php
includes/LogPage.php
includes/OutputPage.php
includes/PageHistory.php
includes/Parser.php
includes/ProxyTools.php
includes/QueryPage.php
includes/SearchEngine.php
includes/Skin.php
includes/SkinTemplate.php
includes/SpecialAllpages.php
includes/SpecialBlockip.php
includes/SpecialConfirmemail.php
includes/SpecialContributions.php
includes/SpecialEmailuser.php
includes/SpecialExport.php
includes/SpecialIpblocklist.php
includes/SpecialLockdb.php
includes/SpecialLog.php
includes/SpecialMostlinked.php
includes/SpecialMovepage.php
includes/SpecialNewimages.php
includes/SpecialNewpages.php
includes/SpecialPage.php
includes/SpecialPreferences.php
includes/SpecialRecentchanges.php
includes/SpecialRevisiondelete.php
includes/SpecialSearch.php
includes/SpecialUndelete.php
includes/SpecialUnlockdb.php
includes/SpecialUnusedtemplates.php
includes/SpecialUpload.php
includes/SpecialUserlogin.php
includes/SpecialUserrights.php
includes/SpecialWantedpages.php
includes/SpecialWatchlist.php
includes/Title.php
includes/User.php
includes/UserMailer.php
includes/Wiki.php
includes/api/ApiFeedWatchlist.php
languages/Language.php
languages/messages/MessagesEn.php
maintenance/dumpHTML.inc
opensearch_desc.php
skins/CologneBlue.php

index c592aa2..aa86281 100644 (file)
@@ -107,6 +107,9 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
   required.
 * (bug 5051) Accesskeys no longer require JavaScript.
 * Correctly detect browser accesskey prefix for all buttons, not just most.
+* Made special page names case-insensitive and localisable. Care has been taken 
+  to maintain backwards compatibility.
+* Used special page subpages in a few more places, instead of query parameters.
 
 == Languages updated ==
 
index c422cc1..6bc38c5 100644 (file)
@@ -85,7 +85,7 @@ class Article {
                                        //
                                        // This can be hard to reverse, so they may be disabled.
                                        
-                                       if( $rt->getNamespace() == NS_SPECIAL && $rt->getText() == 'Userlogout' ) {
+                                       if( $rt->isSpecial( 'Userlogout' ) ) {
                                                // rolleyes
                                        } else {
                                                return $rt->getFullURL();
@@ -1473,7 +1473,7 @@ class Article {
                                $wgOut->setPagetitle( wfMsg( 'markedaspatrolled' ) );
                                $wgOut->addWikiText( wfMsg( 'markedaspatrolledtext' ) );
                        }
-                       $rcTitle = Title::makeTitle( NS_SPECIAL, 'Recentchanges' );
+                       $rcTitle = SpecialPage::getTitleFor( 'Recentchanges' );
                        $wgOut->returnToMain( false, $rcTitle->getPrefixedText() );
                }
                else {
index 6797bb4..fe3ec1b 100644 (file)
@@ -246,8 +246,13 @@ class OldChangesList extends ChangesList {
                if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
                        $this->insertMove( $s, $rc );
                // log entries
-               } elseif( $rc_namespace == NS_SPECIAL && preg_match( '!^Log/(.*)$!', $rc_title, $matches ) ) {
-                       $this->insertLog($s, $rc->getTitle(), $matches[1]);
+               } elseif ( $rc_namespace == NS_SPECIAL ) {
+                       list( $specialName, $specialSubpage ) = SpecialPage::resolveAliasWithSubpage( $rc_title );
+                       if ( $specialName == 'Log' ) {
+                               $this->insertLog( $s, $rc->getTitle(), $specialSubpage );
+                       } else {
+                               wfDebug( "Unexpected special page in recentchanges\n" );
+                       }
                // all other stuff
                } else {
                        wfProfileIn($fname.'-page');
@@ -321,11 +326,16 @@ class EnhancedChangesList extends ChangesList {
                        $msg = ( $rc_type == RC_MOVE ) ? "1movedto2" : "1movedto2_redir";
                        $clink = wfMsg( $msg, $this->skin->makeKnownLinkObj( $rc->getTitle(), '', 'redirect=no' ),
                          $this->skin->makeKnownLinkObj( $rc->getMovedToTitle(), '' ) );
-               } elseif( $rc_namespace == NS_SPECIAL && preg_match( '!^Log/(.*)$!', $rc_title, $matches ) ) {
-                       # Log updates, etc
-                       $logtype = $matches[1];
-                       $logname = LogPage::logName( $logtype );
-                       $clink = '(' . $this->skin->makeKnownLinkObj( $rc->getTitle(), $logname ) . ')';
+               } elseif( $rc_namespace == NS_SPECIAL ) {
+                       list( $specialName, $logtype ) = SpecialPage::resolveAliasWithSubpage( $rc_title );
+                       if ( $specialName == 'Log' ) {
+                               # Log updates, etc
+                               $logname = LogPage::logName( $logtype );
+                               $clink = '(' . $this->skin->makeKnownLinkObj( $rc->getTitle(), $logname ) . ')';
+                       } else {
+                               wfDebug( "Unexpected special page in recentchanges\n" );
+                               $clink = '';
+                       }
                } elseif( $rc->unpatrolled && $rc_type == RC_NEW ) {
                        # Unpatrolled new page, give rc_id in query
                        $clink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '', "rcid={$rc_id}" );
index ef60aee..27bcb0e 100644 (file)
@@ -1436,7 +1436,7 @@ END
                global $wgUser, $wgOut;
                $skin = $wgUser->getSkin();
                
-               $loginTitle = Title::makeTitle( NS_SPECIAL, 'Userlogin' );
+               $loginTitle = SpecialPage::getTitleFor( 'Userlogin' );
                $loginLink = $skin->makeKnownLinkObj( $loginTitle, wfMsgHtml( 'loginreqlink' ), 'returnto=' . $this->mTitle->getPrefixedUrl() );
        
                $wgOut->setPageTitle( wfMsg( 'whitelistedittitle' ) );
index 02c4c2e..86a1500 100644 (file)
@@ -333,7 +333,7 @@ END
                } else {
                        # Image does not exist
 
-                       $title = Title::makeTitle( NS_SPECIAL, 'Upload' );
+                       $title = SpecialPage::getTitleFor( 'Upload' );
                        $link = $sk->makeKnownLinkObj($title, wfMsgHtml('noimage-linktext'),
                                'wpDestFile=' . urlencode( $this->img->getName() ) );
                        $wgOut->addHTML( wfMsgWikiHtml( 'noimage', $link ) );
@@ -348,7 +348,7 @@ END
                if ($wgRepositoryBaseUrl && !$wgFetchCommonsDescriptions) {
 
                        $sk = $wgUser->getSkin();
-                       $title = Title::makeTitle( NS_SPECIAL, 'Upload' );
+                       $title = SpecialPage::getTitleFor( 'Upload' );
                        $link = $sk->makeKnownLinkObj($title, wfMsgHtml('shareduploadwiki-linktext'),
                        array( 'wpDestFile' => urlencode( $this->img->getName() )));
                        $sharedtext .= " " . wfMsgWikiHtml('shareduploadwiki', $link);
@@ -365,7 +365,7 @@ END
 
        function getUploadUrl() {
                global $wgServer;
-               $uploadTitle = Title::makeTitle( NS_SPECIAL, 'Upload' );
+               $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
                return $wgServer . $uploadTitle->getLocalUrl( 'wpDestFile=' . urlencode( $this->img->getName() ) );
        }
 
index 53deb39..f4a59d6 100644 (file)
@@ -673,7 +673,7 @@ class Linker {
                if ( '' != $query ) {
                        $q .= "&$query";
                }
-               $uploadTitle = Title::makeTitle( NS_SPECIAL, 'Upload' );
+               $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
                $url = $uploadTitle->escapeLocalURL( $q );
 
                if ( '' == $text ) {
@@ -716,7 +716,7 @@ class Linker {
                                $url  = $img->getURL();
                                $class = 'internal';
                        } else {
-                               $upload = Title::makeTitle( NS_SPECIAL, 'Upload' );
+                               $upload = SpecialPage::getTitleFor( 'Upload' );
                                $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $img->getName() ) );
                                $class = 'new';
                        }
@@ -763,7 +763,7 @@ class Linker {
        function userLink( $userId, $userText ) {
                $encName = htmlspecialchars( $userText );
                if( $userId == 0 ) {
-                       $contribsPage = Title::makeTitle( NS_SPECIAL, 'Contributions' );
+                       $contribsPage = SpecialPage::getTitleFor( 'Contributions' );
                        return $this->makeKnownLinkObj( $contribsPage,
                                $encName, 'target=' . urlencode( $userText ) );
                } else {
@@ -788,7 +788,7 @@ class Linker {
                        $items[] = $this->userTalkLink( $userId, $userText );
                }
                if( $userId ) {
-                       $contribsPage = Title::makeTitle( NS_SPECIAL, 'Contributions' );
+                       $contribsPage = SpecialPage::getTitleFor( 'Contributions' );
                        $items[] = $this->makeKnownLinkObj( $contribsPage,
                                wfMsgHtml( 'contribslink' ), 'target=' . urlencode( $userText ) );
                }
@@ -825,7 +825,7 @@ class Linker {
         * @private
         */
        function blockLink( $userId, $userText ) {
-               $blockPage = Title::makeTitle( NS_SPECIAL, 'Blockip' );
+               $blockPage = SpecialPage::getTitleFor( 'Blockip' );
                $blockLink = $this->makeKnownLinkObj( $blockPage,
                        wfMsgHtml( 'blocklink' ), 'ip=' . urlencode( $userText ) );
                return $blockLink;
index 954b178..d25b8aa 100644 (file)
@@ -74,7 +74,7 @@ class LogPage {
 
                # And update recentchanges
                if ( $this->updateRecentChanges ) {
-                       $titleObj = Title::makeTitle( NS_SPECIAL, 'Log/' . $this->type );
+                       $titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
                        $rcComment = $this->actionText;
                        if( '' != $this->comment ) {
                                if ($rcComment == '')
@@ -150,7 +150,7 @@ class LogPage {
                                                                $titleLink = $title->getText();
                                                        } else {
                                                                $titleLink = $skin->makeLinkObj( $title, $title->getText() );
-                                                               $titleLink .= ' (' . $skin->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Contributions/' . $title->getDBkey() ), wfMsg( 'contribslink' ) ) . ')';
+                                                               $titleLink .= ' (' . $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Contributions', $title->getDBkey() ), wfMsg( 'contribslink' ) ) . ')';
                                                        }
                                                        break;
                                                case 'rights':
index f560397..7ed4919 100644 (file)
@@ -783,7 +783,7 @@ class OutputPage {
                $this->setRobotPolicy( 'noindex,nofollow' );
                $this->setArticleFlag( false );
                
-               $loginTitle = Title::makeTitle( NS_SPECIAL, 'Userlogin' );
+               $loginTitle = SpecialPage::getTitleFor( 'Userlogin' );
                $loginLink = $skin->makeKnownLinkObj( $loginTitle, wfMsgHtml( 'loginreqlink' ), 'returnto=' . $wgTitle->getPrefixedUrl() );
                $this->addHtml( wfMsgWikiHtml( 'loginreqpagetext', $loginLink ) );
                $this->addHtml( "\n<!--" . $wgTitle->getPrefixedUrl() . "-->" );
index 0f39a99..0476931 100644 (file)
@@ -70,7 +70,7 @@ class PageHistory {
                $wgOut->setRobotpolicy( 'noindex,nofollow' );
                $wgOut->setSyndicated( true );
 
-               $logPage = Title::makeTitle( NS_SPECIAL, 'Log' );
+               $logPage = SpecialPage::getTitleFor( 'Log' );
                $logLink = $this->mSkin->makeKnownLinkObj( $logPage, wfMsgHtml( 'viewpagelogs' ), 'page=' . $this->mTitle->getPrefixedUrl() );
 
                $subtitle = wfMsgHtml( 'revhistory' ) . '<br />' . $logLink;
@@ -184,7 +184,7 @@ class PageHistory {
                $s .= "($curlink) ($lastlink) $arbitrary";
                
                if( $wgUser->isAllowed( 'deleterevision' ) ) {
-                       $revdel = Title::makeTitle( NS_SPECIAL, 'Revisiondelete' );
+                       $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
                        if( $firstInList ) {
                                // We don't currently handle well changing the top revision's settings
                                $del = wfMsgHtml( 'rev-delundel' );
index 72d24b3..cfe1d70 100644 (file)
@@ -1018,7 +1018,7 @@ class Parser
                                ' ' => '',
                                'x' => 'X',
                        ));
-                       $titleObj = Title::makeTitle( NS_SPECIAL, 'Booksources' );
+                       $titleObj = SpecialPage::getTitleFor( 'Booksources' );
                        $text = '<a href="' .
                                $titleObj->escapeLocalUrl( "isbn=$num" ) .
                                "\" class=\"internal\">ISBN $isbn</a>";
index 7974c88..fde8965 100644 (file)
@@ -96,7 +96,7 @@ function wfProxyCheck() {
 
        # Fork the processes
        if ( !$skip ) {
-               $title = Title::makeTitle( NS_SPECIAL, 'Blockme' );
+               $title = SpecialPage::getTitleFor( 'Blockme' );
                $iphash = md5( $ip . $wgProxyKey );
                $url = $title->getFullURL( 'ip='.$iphash );
 
index 7d6dc90..3839aae 100644 (file)
@@ -92,7 +92,7 @@ class QueryPage {
         * @return Title
         */
        function getTitle() {
-               return Title::makeTitle( NS_SPECIAL, $this->getName() );
+               return SpecialPage::getTitleFor( $this->getName() );
        }
 
        /**
@@ -459,7 +459,7 @@ class QueryPage {
        }
 
        function feedUrl() {
-               $title = Title::MakeTitle( NS_SPECIAL, $this->getName() );
+               $title = SpecialPage::getTitleFor( $this->getName() );
                return $title->getFullURL();
        }
 }
index 5e59888..32725a5 100644 (file)
@@ -116,7 +116,7 @@ class SearchEngine {
                # Entering an IP address goes to the contributions page
                if ( ( $title->getNamespace() == NS_USER && User::isIP($title->getText() ) )
                        || User::isIP( trim( $searchterm ) ) ) {
-                       return Title::makeTitle( NS_SPECIAL, "Contributions/" . $title->getDbkey() );
+                       return SpecialPage::getTitleFor( 'Contributions', $title->getDbkey() );
                }
 
 
index 9df74b4..d1d853a 100644 (file)
@@ -579,7 +579,7 @@ END;
                $t = $embed . implode ( "{$pop} {$sep} {$embed}" , $wgOut->mCategoryLinks ) . $pop;
 
                $msg = wfMsgExt( 'pagecategories', array( 'parsemag', 'escape' ), count( $wgOut->mCategoryLinks ) );
-               $s = $this->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Categories' ),
+               $s = $this->makeKnownLinkObj( SpecialPage::getTitleFor( 'Categories' ),
                        $msg, 'article=' . urlencode( $wgTitle->getPrefixedDBkey() ) )
                        . ': ' . $t;
 
@@ -836,7 +836,7 @@ END;
                        } else { $q = "returnto={$rt}"; }
 
                        $s .= "\n<br />" . $this->makeKnownLinkObj(
-                               Title::makeTitle( NS_SPECIAL, 'Userlogin' ),
+                               SpecialPage::getTitleFor( 'Userlogin' ),
                                wfMsg( 'login' ), $q );
                } else {
                        $n = $wgUser->getName();
@@ -848,7 +848,7 @@ END;
 
                        $s .= $this->makeKnownLinkObj( $wgUser->getUserPage(),
                          $n ) . "{$tl}<br />" .
-                         $this->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Userlogout' ), wfMsg( 'logout' ),
+                         $this->makeKnownLinkObj( SpecialPage::getTitleFor( 'Userlogout' ), wfMsg( 'logout' ),
                          "returnto={$rt}" ) . ' | ' .
                          $this->specialLink( 'preferences' );
                }
@@ -859,7 +859,7 @@ END;
        }
 
        function getSearchLink() {
-               $searchPage =& Title::makeTitle( NS_SPECIAL, 'Search' );
+               $searchPage =& SpecialPage::getTitleFor( 'Search' );
                return $searchPage->getLocalURL();
        }
 
@@ -1253,7 +1253,7 @@ END;
                global $wgTitle;
 
                if ( $wgTitle->userCanMove() ) {
-                       return $this->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Movepage' ),
+                       return $this->makeKnownLinkObj( SpecialPage::getTitleFor( 'Movepage' ),
                          wfMsg( 'movethispage' ), 'target=' . $wgTitle->getPrefixedURL() );
                } else {
                        // no message if page is protected - would be redundant
@@ -1271,15 +1271,17 @@ END;
        function whatLinksHere() {
                global $wgTitle;
 
-               return $this->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Whatlinkshere' ),
-                 wfMsg( 'whatlinkshere' ), 'target=' . $wgTitle->getPrefixedURL() );
+               return $this->makeKnownLinkObj( 
+                       SpecialPage::getTitleFor( 'Whatlinkshere', $wgTitle->getPrefixedDBkey() ), 
+                       wfMsg( 'whatlinkshere' ) );
        }
 
        function userContribsLink() {
                global $wgTitle;
 
-               return $this->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Contributions' ),
-                 wfMsg( 'contributions' ), 'target=' . $wgTitle->getPartialURL() );
+               return $this->makeKnownLinkObj( 
+                       SpecialPage::getTitleFor( 'Contributions', $wgTitle->getDBkey() ),
+                       wfMsg( 'contributions' ) );
        }
 
        function showEmailUser( $id ) {
@@ -1296,8 +1298,9 @@ END;
        function emailUserLink() {
                global $wgTitle;
 
-               return $this->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Emailuser' ),
-                 wfMsg( 'emailuser' ), 'target=' . $wgTitle->getPartialURL() );
+               return $this->makeKnownLinkObj( 
+                       SpecialPage::getTitleFor( 'Emailuser', $wgTitle->getDBkey() ),
+                       wfMsg( 'emailuser' ) );
        }
 
        function watchPageLinksLink() {
@@ -1306,9 +1309,9 @@ END;
                if ( ! $wgOut->isArticleRelated() ) {
                        return '(' . wfMsg( 'notanarticle' ) . ')';
                } else {
-                       return $this->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL,
-                         'Recentchangeslinked' ), wfMsg( 'recentchangeslinked' ),
-                         'target=' . $wgTitle->getPrefixedURL() );
+                       return $this->makeKnownLinkObj( 
+                               SpecialPage::getTitleFor( 'Recentchangeslinked', $wgTitle->getPrefixedDBkey() ), 
+                               wfMsg( 'recentchangeslinked' ) );
                }
        }
 
@@ -1450,7 +1453,7 @@ END;
 
        /* these are used extensively in SkinTemplate, but also some other places */
        static function makeSpecialUrl( $name, $urlaction = '' ) {
-               $title = Title::makeTitle( NS_SPECIAL, $name );
+               $title = SpecialPage::getTitleFor( $name );
                return $title->getLocalURL( $urlaction );
        }
 
@@ -1559,7 +1562,15 @@ END;
                                                $text = $line[1];
                                        if (wfEmptyMsg($line[0], $link))
                                                $link = $line[0];
-                                       $href = self::makeInternalOrExternalUrl( $link );
+
+                                       if ( preg_match( '/^(?:' . wfUrlProtocols() . ')/', $link ) ) {
+                                               $href = $link;
+                                       } else {
+                                               $title = Title::newFromText( $link );
+                                               $title = $title->fixSpecialName();
+                                               $href = $title->getLocalURL();
+                                       }
+
                                        $bar[$heading][] = array(
                                                'text' => $text,
                                                'href' => $href,
index 0503684..a4238cc 100644 (file)
@@ -518,7 +518,7 @@ class SkinTemplate extends Skin {
                        $personal_urls['logout'] = array(
                                'text' => wfMsg( 'userlogout' ),
                                'href' => self::makeSpecialUrl( 'Userlogout',
-                                       $wgTitle->getNamespace() === NS_SPECIAL && $wgTitle->getText() === 'Preferences' ? '' : "returnto={$this->thisurl}"
+                                       $wgTitle->isSpecial( 'Preferences' ) ? '' : "returnto={$this->thisurl}"
                                )
                        );
                } else {
@@ -541,14 +541,14 @@ class SkinTemplate extends Skin {
                                $personal_urls['anonlogin'] = array(
                                        'text' => wfMsg('userlogin'),
                                        'href' => self::makeSpecialUrl( 'Userlogin', 'returnto=' . $this->thisurl ),
-                                       'active' => ( NS_SPECIAL == $wgTitle->getNamespace() && 'Userlogin' == $wgTitle->getDBkey() )
+                                       'active' => $wgTitle->isSpecial( 'Userlogin' ) 
                                );
                        } else {
 
                                $personal_urls['login'] = array(
                                        'text' => wfMsg('userlogin'),
                                        'href' => self::makeSpecialUrl( 'Userlogin', 'returnto=' . $this->thisurl ),
-                                       'active' => ( NS_SPECIAL == $wgTitle->getNamespace() && 'Userlogin' == $wgTitle->getDBkey() )
+                                       'active' => $wgTitle->isSpecial( 'Userlogin' )
                                );
                        }
                }
@@ -703,18 +703,18 @@ class SkinTemplate extends Skin {
                                        );
                                }
                                if ( $this->mTitle->userCanMove()) {
-                                       $moveTitle = Title::makeTitle( NS_SPECIAL, 'Movepage' );
+                                       $moveTitle = SpecialPage::getTitleFor( 'Movepage', $this->thispage );
                                        $content_actions['move'] = array(
-                                               'class' => ($this->mTitle->getDbKey() == 'Movepage' and $this->mTitle->getNamespace == NS_SPECIAL) ? 'selected' : false,
+                                               'class' => $this->mTitle->isSpecial( 'Movepage' ) ? 'selected' : false,
                                                'text' => wfMsg('move'),
-                                               'href' => $moveTitle->getLocalUrl( 'target=' . urlencode( $this->thispage ) )
+                                               'href' => $moveTitle->getLocalUrl()
                                        );
                                }
                        } else {
                                //article doesn't exist or is deleted
                                if( $wgUser->isAllowed( 'delete' ) ) {
                                        if( $n = $this->mTitle->isDeleted() ) {
-                                               $undelTitle = Title::makeTitle( NS_SPECIAL, 'Undelete' );
+                                               $undelTitle = SpecialPage::getTitleFor( 'Undelete' );
                                                $content_actions['undelete'] = array(
                                                        'class' => false,
                                                        'text' => wfMsgExt( 'undelete_short', array( 'parsemag' ), $n ),
@@ -849,14 +849,14 @@ class SkinTemplate extends Skin {
                }
 
                if( $this->mTitle->getNamespace() != NS_SPECIAL ) {
-                       $wlhTitle = Title::makeTitle( NS_SPECIAL, 'Whatlinkshere' );
+                       $wlhTitle = SpecialPage::getTitleFor( 'Whatlinkshere', $this->thispage );
                        $nav_urls['whatlinkshere'] = array(
-                               'href' => $wlhTitle->getLocalUrl( 'target=' . urlencode( $this->thispage ) )
+                               'href' => $wlhTitle->getLocalUrl()
                        );
                        if( $this->mTitle->getArticleId() ) {
-                               $rclTitle = Title::makeTitle( NS_SPECIAL, 'Recentchangeslinked' );
+                               $rclTitle = SpecialPage::getTitleFor( 'Recentchangeslinked', $this->thispage );
                                $nav_urls['recentchangeslinked'] = array(
-                                       'href' => $rclTitle->getLocalUrl( 'target=' . urlencode( $this->thispage ) )
+                                       'href' => $rclTitle->getLocalUrl()
                                );
                        }
                        if ($wgUseTrackbacks)
index 8d41bb3..e868ec7 100644 (file)
@@ -51,7 +51,7 @@ class SpecialAllpages {
  */
 function namespaceForm ( $namespace = NS_MAIN, $from = '' ) {
        global $wgScript;
-       $t = Title::makeTitle( NS_SPECIAL, $this->name );
+       $t = SpecialPage::getTitleFor( $this->name );
 
        $namespaceselect = HTMLnamespaceselector($namespace, null);
 
@@ -192,7 +192,7 @@ function showline( $inpoint, $outpoint, $namespace = NS_MAIN ) {
        $inpointf = htmlspecialchars( str_replace( '_', ' ', $inpoint ) );
        $outpointf = htmlspecialchars( str_replace( '_', ' ', $outpoint ) );
        $queryparams = ($namespace ? "namespace=$namespace" : '');
-       $special = Title::makeTitle( NS_SPECIAL, $this->name . '/' . $inpoint );
+       $special = SpecialPage::getTitleFor( $this->name, $inpoint );
        $link = $special->escapeLocalUrl( $queryparams );
 
        $out = wfMsgHtml(
@@ -276,7 +276,7 @@ function showChunk( $namespace = NS_MAIN, $from, $including = false ) {
                                $sk->makeKnownLink( $wgContLang->specialPage( "Allpages" ),
                                        wfMsgHtml ( 'allpages' ) );
                if ( isset($dbr) && $dbr && ($n == $this->maxPerPage) && ($s = $dbr->fetchObject( $res )) ) {
-                       $self = Title::makeTitle( NS_SPECIAL, 'Allpages' );
+                       $self = SpecialPage::getTitleFor( 'Allpages' );
                        $q = 'from=' . $t->getPartialUrl() . ( $namespace ? '&namespace=' . $namespace : '' );
                        $out2 .= ' | ' . $sk->makeKnownLinkObj( $self, wfMsgHtml( 'nextpage', $t->getText() ), $q );
                }
index 4eb4957..567fd82 100644 (file)
@@ -73,7 +73,7 @@ class IPBlockForm {
                $mIpbothertime = wfMsgHtml( 'ipbotheroption' );
                $mIpbreason = wfMsgHtml( 'ipbreason' );
                $mIpbsubmit = wfMsgHtml( 'ipbsubmit' );
-               $titleObj = Title::makeTitle( NS_SPECIAL, 'Blockip' );
+               $titleObj = SpecialPage::getTitleFor( 'Blockip' );
                $action = $titleObj->escapeLocalURL( "action=submit" );
 
                if ( "" != $err ) {
@@ -260,7 +260,7 @@ class IPBlockForm {
                          $this->BlockReason, $expirestr );
 
                        # Report to the user
-                       $titleObj = Title::makeTitle( NS_SPECIAL, 'Blockip' );
+                       $titleObj = SpecialPage::getTitleFor( 'Blockip' );
                        $wgOut->redirect( $titleObj->getFullURL( 'action=success&ip=' .
                                urlencode( $this->BlockAddress ) ) );
                }
index 94104f3..d109eee 100644 (file)
@@ -36,8 +36,8 @@ class EmailConfirmation extends SpecialPage {
                                        $wgOut->addWikiText( wfMsg( 'confirmemail_noemail' ) );
                                }
                        } else {
-                               $title = Title::makeTitle( NS_SPECIAL, 'Userlogin' );
-                               $self = Title::makeTitle( NS_SPECIAL, 'Confirmemail' );
+                               $title = SpecialPage::getTitleFor( 'Userlogin' );
+                               $self = SpecialPage::getTitleFor( 'Confirmemail' );
                                $skin = $wgUser->getSkin();
                                $llink = $skin->makeKnownLinkObj( $title, wfMsgHtml( 'loginreqlink' ), 'returnto=' . $self->getPrefixedUrl() );
                                $wgOut->addHtml( wfMsgWikiHtml( 'confirmemail_needlogin', $llink ) );
@@ -65,7 +65,7 @@ class EmailConfirmation extends SpecialPage {
                                $wgOut->addWikiText( wfMsg( 'emailauthenticated', $time ) );
                        }
                        $wgOut->addWikiText( wfMsg( 'confirmemail_text' ) );
-                       $self = Title::makeTitle( NS_SPECIAL, 'Confirmemail' );         
+                       $self = SpecialPage::getTitleFor( 'Confirmemail' );             
                        $form  = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $self->getLocalUrl() ) );
                        $form .= wfHidden( 'token', $wgUser->editToken() );
                        $form .= wfSubmitButton( wfMsgHtml( 'confirmemail_send' ) );
@@ -88,7 +88,7 @@ class EmailConfirmation extends SpecialPage {
                                $message = $wgUser->isLoggedIn() ? 'confirmemail_loggedin' : 'confirmemail_success';
                                $wgOut->addWikiText( wfMsg( $message ) );
                                if( !$wgUser->isLoggedIn() ) {
-                                       $title = Title::makeTitle( NS_SPECIAL, 'Userlogin' );
+                                       $title = SpecialPage::getTitleFor( 'Userlogin' );
                                        $wgOut->returnToMain( true, $title->getPrefixedText() );
                                }
                        } else {
index 8477b6b..29198bb 100644 (file)
@@ -190,7 +190,7 @@ function wfSpecialContributions( $par = null ) {
        if ( !strlen( $options['offset'] ) || !preg_match( '/^[0-9]+$/', $options['offset'] ) )
                $options['offset'] = '';
 
-       $title = Title::makeTitle( NS_SPECIAL, 'Contributions' );
+       $title = SpecialPage::getTitleFor( 'Contributions' );
        $options['target'] = $target;
 
        $nt =& Title::makeTitle( NS_USER, $nt->getDBkey() );
@@ -321,12 +321,12 @@ function contributionsSub( $nt ) {
                if( ( $id != 0 && $wgSysopUserBans ) || ( $id == 0 && User::isIP( $nt->getText() ) ) ) {
                        # Block link
                        if( $wgUser->isAllowed( 'block' ) )
-                               $tools[] = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Blockip/' . $nt->getDBkey() ), wfMsgHtml( 'blocklink' ) );
+                               $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Blockip', $nt->getDBkey() ), wfMsgHtml( 'blocklink' ) );
                        # Block log link
-                       $tools[] = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Log' ), htmlspecialchars( LogPage::logName( 'block' ) ), 'type=block&page=' . $nt->getPrefixedUrl() );
+                       $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), htmlspecialchars( LogPage::logName( 'block' ) ), 'type=block&page=' . $nt->getPrefixedUrl() );
                }
                # Other logs link
-               $tools[] = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Log' ), wfMsgHtml( 'log' ), 'user=' . $nt->getPartialUrl() );
+               $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), wfMsgHtml( 'log' ), 'user=' . $nt->getPartialUrl() );
                $ul .= ' (' . implode( ' | ', $tools ) . ')';
        }
        return $ul;
index d711947..caf24ff 100644 (file)
@@ -97,7 +97,7 @@ class EmailUserForm {
                $ems = wfMsg( "emailsend" );
                $encSubject = htmlspecialchars( $this->subject );
 
-               $titleObj = Title::makeTitle( NS_SPECIAL, "Emailuser" );
+               $titleObj = SpecialPage::getTitleFor( "Emailuser" );
                $action = $titleObj->escapeLocalURL( "target=" .
                        urlencode( $this->target->getName() ) . "&action=submit" );
                $token = $wgUser->editToken();
@@ -140,7 +140,7 @@ class EmailUserForm {
                        if( WikiError::isError( $mailResult ) ) {
                                $wgOut->addHTML( wfMsg( "usermailererror" ) . $mailResult);
                        } else {
-                               $titleObj = Title::makeTitle( NS_SPECIAL, "Emailuser" );
+                               $titleObj = SpecialPage::getTitleFor( "Emailuser" );
                                $encTarget = wfUrlencode( $this->target->getName() );
                                $wgOut->redirect( $titleObj->getFullURL( "target={$encTarget}&action=success" ) );
                                wfRunHooks( 'EmailUserComplete', array( $to, $from, $subject, $this->text ) );
index dc52e00..d387f3d 100644 (file)
@@ -123,7 +123,7 @@ function wfSpecialExport( $page = '' ) {
        }
 
        $wgOut->addWikiText( wfMsg( "exporttext" ) );
-       $titleObj = Title::makeTitle( NS_SPECIAL, "Export" );
+       $titleObj = SpecialPage::getTitleFor( "Export" );
        
        $form = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalUrl() ) );
        $form .= wfOpenElement( 'textarea', array( 'name' => 'pages', 'cols' => 40, 'rows' => 10 ) ) . '</textarea><br />';
index 437fac7..8d0ef10 100644 (file)
@@ -58,7 +58,7 @@ class IPUnblockForm {
                $ipa = wfMsgHtml( $wgSysopUserBans ? 'ipadressorusername' : 'ipaddress' );
                $ipr = wfMsgHtml( 'ipbreason' );
                $ipus = wfMsgHtml( 'ipusubmit' );
-               $titleObj = Title::makeTitle( NS_SPECIAL, "Ipblocklist" );
+               $titleObj = SpecialPage::getTitleFor( "Ipblocklist" );
                $action = $titleObj->escapeLocalURL( "action=submit" );
 
                if ( "" != $err ) {
@@ -142,7 +142,7 @@ class IPUnblockForm {
 
                if ( $success ) {
                        # Report to the user
-                       $titleObj = Title::makeTitle( NS_SPECIAL, "Ipblocklist" );
+                       $titleObj = SpecialPage::getTitleFor( "Ipblocklist" );
                        $success = $titleObj->getFullURL( "action=success&successip=" . urlencode( $this->ip ) );
                        $wgOut->redirect( $success );
                } else {
@@ -259,7 +259,7 @@ class IPUnblockForm {
                        $target = $block->getRedactedName(); # Hide the IP addresses of auto-blocks; privacy
                } else {
                        $target = $sk->makeLinkObj( Title::makeTitle( NS_USER, $block->mAddress ), $block->mAddress );
-                       $target .= ' (' . $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Contributions' ), $msg['contribslink'], 'target=' . urlencode( $block->mAddress ) ) . ')';
+                       $target .= ' (' . $sk->makeKnownLinkObj( SpecialPage::getSafeTitleFor( 'Contributions', $block->mAddress ), $msg['contribslink'] ) . ')';
                }
                
                $formattedTime = $wgLang->timeanddate( $block->mTimestamp, true );
@@ -284,7 +284,7 @@ class IPUnblockForm {
                $s = "<li>{$line}";
 
                if ( $wgUser->isAllowed('block') ) {
-                       $titleObj = Title::makeTitle( NS_SPECIAL, "Ipblocklist" );
+                       $titleObj = SpecialPage::getTitleFor( "Ipblocklist" );
                        $s .= ' (' . $sk->makeKnownLinkObj($titleObj, $msg['unblocklink'], 'action=unblock&id=' . urlencode( $block->mId ) ) . ')';
                }
                $s .= $sk->commentBlock( $block->mReason );
index 72172e2..f0142e5 100644 (file)
@@ -62,7 +62,7 @@ class DBLockForm {
                $lc = htmlspecialchars( wfMsg( 'lockconfirm' ) );
                $lb = htmlspecialchars( wfMsg( 'lockbtn' ) );
                $elr = htmlspecialchars( wfMsg( 'enterlockreason' ) );
-               $titleObj = Title::makeTitle( NS_SPECIAL, 'Lockdb' );
+               $titleObj = SpecialPage::getTitleFor( 'Lockdb' );
                $action = $titleObj->escapeLocalURL( 'action=submit' );
                $reason = htmlspecialchars( $this->reason );
                $token = htmlspecialchars( $wgUser->editToken() );
@@ -114,7 +114,7 @@ END
                  $wgLang->timeanddate( wfTimestampNow() ) . ")\n" );
                fclose( $fp );
 
-               $titleObj = Title::makeTitle( NS_SPECIAL, 'Lockdb' );
+               $titleObj = SpecialPage::getTitleFor( 'Lockdb' );
                $wgOut->redirect( $titleObj->getFullURL( 'action=success' ) );
        }
 
index e32d224..81188aa 100644 (file)
@@ -321,7 +321,7 @@ class LogViewer {
                $paramArray = LogPage::extractParams( $s->log_params );
                $revert = '';
                if ( $s->log_type == 'move' && isset( $paramArray[0] ) ) {
-                       $specialTitle = Title::makeTitle( NS_SPECIAL, 'Movepage' );
+                       $specialTitle = SpecialPage::getTitleFor( 'Movepage' );
                        $destTitle = Title::newFromText( $paramArray[0] );
                        if ( $destTitle ) {
                                $revert = '(' . $this->skin->makeKnownLinkObj( $specialTitle, wfMsg( 'revertmove' ),
@@ -356,7 +356,7 @@ class LogViewer {
        function showOptions( &$out ) {
                global $wgScript;
                $action = htmlspecialchars( $wgScript );
-               $title = Title::makeTitle( NS_SPECIAL, 'Log' );
+               $title = SpecialPage::getTitleFor( 'Log' );
                $special = htmlspecialchars( $title->getPrefixedDBkey() );
                $out->addHTML( "<form action=\"$action\" method=\"get\">\n" .
                        "<input type='hidden' name='title' value=\"$special\" />\n" .
index 1791228..6f5f30d 100644 (file)
@@ -62,8 +62,8 @@ class MostlinkedPage extends QueryPage {
         * @return string
         */
        function makeWlhLink( &$title, $caption, &$skin ) {
-               $wlh = Title::makeTitle( NS_SPECIAL, 'Whatlinkshere' );
-               return $skin->makeKnownLinkObj( $wlh, $caption, 'target=' . $title->getPrefixedUrl() );
+               $wlh = SpecialPage::getTitleFor( 'Whatlinkshere', $title->getPrefixedDBkey() );
+               return $skin->makeKnownLinkObj( $wlh, $caption );
        }
 
        /**
index c1d6833..1b9e7b2 100644 (file)
@@ -130,7 +130,7 @@ class MovePageForm {
                $movetalk = wfMsgHtml( 'movetalk' );
                $movereason = wfMsgHtml( 'movereason' );
 
-               $titleObj = Title::makeTitle( NS_SPECIAL, 'Movepage' );
+               $titleObj = SpecialPage::getTitleFor( 'Movepage' );
                $action = $titleObj->escapeLocalURL( 'action=submit' );
                $token = htmlspecialchars( $wgUser->editToken() );
 
@@ -245,7 +245,7 @@ class MovePageForm {
                }
 
                # Give back result to user.
-               $titleObj = Title::makeTitle( NS_SPECIAL, 'Movepage' );
+               $titleObj = SpecialPage::getTitleFor( 'Movepage' );
                $success = $titleObj->getFullURL(
                  'action=success&oldtitle=' . wfUrlencode( $ot->getPrefixedText() ) .
                  '&newtitle=' . wfUrlencode( $nt->getPrefixedText() ) .
index 95c90e4..66cf426 100644 (file)
@@ -154,7 +154,7 @@ function wfSpecialNewimages( $par, $specialPage ) {
        }
 
        $sub = wfMsg( 'ilsubmit' );
-       $titleObj = Title::makeTitle( NS_SPECIAL, 'Newimages' );
+       $titleObj = SpecialPage::getTitleFor( 'Newimages' );
        $action = $titleObj->escapeLocalURL( $hidebots ? '' : 'hidebots=0' );
        if ($shownav) {
                $wgOut->addHTML( "<form id=\"imagesearch\" method=\"post\" action=\"" .
index 3fd0eba..a9a028b 100644 (file)
@@ -132,7 +132,7 @@ class NewPagesPage extends QueryPage {
         * @return string
         */     
        function getPageHeader() {
-               $self = Title::makeTitle( NS_SPECIAL, $this->getName() );
+               $self = SpecialPage::getTitleFor( $this->getName() );
                $form = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $self->getLocalUrl() ) );
                $form .= '<table><tr><td align="right">' . wfMsgHtml( 'namespace' ) . '</td>';
                $form .= '<td>' . HtmlNamespaceSelector( $this->namespace ) . '</td><tr>';
index 294c05e..73b6233 100644 (file)
@@ -36,10 +36,14 @@ class SpecialPage
         * @access private
         */
        /**
-        * The name of the class, used in the URL.
+        * The canonical name of this special page
         * Also used for the default <h1> heading, @see getDescription()
         */
        var $mName;
+       /**
+        * The local name of this special page
+        */
+       var $mLocalName;
        /**
         * Minimum user level required to access this page, or "" for anyone.
         * Also used to categorise the pages in Special:Specialpages
@@ -65,72 +69,82 @@ class SpecialPage
         * Whether the special page can be included in an article
         */
        var $mIncludable;
+       /**
+        * Query parameters that can be passed through redirects
+        */
+       var $mAllowedRedirectParams = array();
 
        static public $mList = array(
-               'DoubleRedirects'       => array( 'SpecialPage', 'DoubleRedirects' ),
-               'BrokenRedirects'       => array( 'SpecialPage', 'BrokenRedirects' ),
-               'Disambiguations'       => array( 'SpecialPage', 'Disambiguations' ),
-
-               'Userlogin'         => array( 'SpecialPage', 'Userlogin' ),
-               'Userlogout'        => array( 'UnlistedSpecialPage', 'Userlogout' ),
-               'Preferences'       => array( 'SpecialPage', 'Preferences' ),
-               'Watchlist'         => array( 'SpecialPage', 'Watchlist' ),
-
-               'Recentchanges'     => array( 'IncludableSpecialPage', 'Recentchanges' ),
-               'Upload'            => array( 'SpecialPage', 'Upload' ),
-               'Imagelist'         => array( 'SpecialPage', 'Imagelist' ),
-               'Newimages'         => array( 'IncludableSpecialPage', 'Newimages' ),
-               'Listusers'         => array( 'SpecialPage', 'Listusers' ),
-               'Statistics'        => array( 'SpecialPage', 'Statistics' ),
-               'Random'            => array( 'SpecialPage', 'Randompage' ),
-               'Lonelypages'       => array( 'SpecialPage', 'Lonelypages' ),
-               'Uncategorizedpages'=> array( 'SpecialPage', 'Uncategorizedpages' ),
-               'Uncategorizedcategories'=> array( 'SpecialPage', 'Uncategorizedcategories' ),
-               'Uncategorizedimages' => array( 'SpecialPage', 'Uncategorizedimages' ),
-               'Unusedcategories'      => array( 'SpecialPage', 'Unusedcategories' ),
-               'Unusedimages'      => array( 'SpecialPage', 'Unusedimages' ),
-               'Wantedpages'       => array( 'IncludableSpecialPage', 'Wantedpages' ),
-               'Wantedcategories'  => array( 'SpecialPage', 'Wantedcategories' ),
-               'Mostlinked'        => array( 'SpecialPage', 'Mostlinked' ),
-               'Mostlinkedcategories' => array( 'SpecialPage', 'Mostlinkedcategories' ),
-               'Mostcategories'    => array( 'SpecialPage', 'Mostcategories' ),
-               'Mostimages'        => array( 'SpecialPage', 'Mostimages' ),
-               'Mostrevisions'     => array( 'SpecialPage', 'Mostrevisions' ),
-               'Shortpages'        => array( 'SpecialPage', 'Shortpages' ),
-               'Longpages'                 => array( 'SpecialPage', 'Longpages' ),
-               'Newpages'                  => array( 'IncludableSpecialPage', 'Newpages' ),
-               'Ancientpages'      => array( 'SpecialPage', 'Ancientpages' ),
-               'Deadendpages'      => array( 'SpecialPage', 'Deadendpages' ),
-               'Allpages'                  => array( 'IncludableSpecialPage', 'Allpages' ),
-               'Prefixindex'       => array( 'IncludableSpecialPage', 'Prefixindex' ) ,
-               'Ipblocklist'       => array( 'SpecialPage', 'Ipblocklist' ),
-               'Specialpages'      => array( 'UnlistedSpecialPage', 'Specialpages' ),
-               'Contributions'     => array( 'UnlistedSpecialPage', 'Contributions' ),
-               'Emailuser'                 => array( 'UnlistedSpecialPage', 'Emailuser' ),
-               'Whatlinkshere'     => array( 'UnlistedSpecialPage', 'Whatlinkshere' ),
-               'Recentchangeslinked' => array( 'UnlistedSpecialPage', 'Recentchangeslinked' ),
-               'Movepage'                  => array( 'UnlistedSpecialPage', 'Movepage' ),
-               'Blockme'           => array( 'UnlistedSpecialPage', 'Blockme' ),
-               'Booksources'       => array( 'SpecialPage', 'Booksources' ),
-               'Categories'        => array( 'SpecialPage', 'Categories' ),
-               'Export'                    => array( 'SpecialPage', 'Export' ),
-               'Version'                   => array( 'SpecialPage', 'Version' ),
-               'Allmessages'       => array( 'SpecialPage', 'Allmessages' ),
-               'Log'               => array( 'SpecialPage', 'Log' ),
-               'Blockip'                   => array( 'SpecialPage', 'Blockip', 'block' ),
-               'Undelete'                  => array( 'SpecialPage', 'Undelete', 'deletedhistory' ),
-               "Import"                    => array( 'SpecialPage', "Import", 'import' ),
-               'Lockdb'                    => array( 'SpecialPage', 'Lockdb', 'siteadmin' ),
-               'Unlockdb'                  => array( 'SpecialPage', 'Unlockdb', 'siteadmin' ),
-               'Userrights'        => array( 'SpecialPage', 'Userrights', 'userrights' ),
-               'MIMEsearch'        => array( 'SpecialPage', 'MIMEsearch' ),
-               'Unwatchedpages'    => array( 'SpecialPage', 'Unwatchedpages', 'unwatchedpages' ),
-               'Listredirects'     => array( 'SpecialPage', 'Listredirects' ),
-               'Revisiondelete'    => array( 'SpecialPage', 'Revisiondelete', 'deleterevision' ),
-               'Unusedtemplates'   => array( 'SpecialPage', 'Unusedtemplates' ),
-               'Randomredirect'    => array( 'SpecialPage', 'Randomredirect' ),
+               'DoubleRedirects'           => array( 'SpecialPage', 'DoubleRedirects' ),
+               'BrokenRedirects'           => array( 'SpecialPage', 'BrokenRedirects' ),
+               'Disambiguations'           => array( 'SpecialPage', 'Disambiguations' ),
+
+               'Userlogin'                 => array( 'SpecialPage', 'Userlogin' ),
+               'Userlogout'                => array( 'UnlistedSpecialPage', 'Userlogout' ),
+               'Preferences'               => array( 'SpecialPage', 'Preferences' ),
+               'Watchlist'                 => array( 'SpecialPage', 'Watchlist' ),
+
+               'Recentchanges'             => array( 'IncludableSpecialPage', 'Recentchanges' ),
+               'Upload'                    => array( 'SpecialPage', 'Upload' ),
+               'Imagelist'                 => array( 'SpecialPage', 'Imagelist' ),
+               'Newimages'                 => array( 'IncludableSpecialPage', 'Newimages' ),
+               'Listusers'                 => array( 'SpecialPage', 'Listusers' ),
+               'Statistics'                => array( 'SpecialPage', 'Statistics' ),
+               'Randompage'                => array( 'SpecialPage', 'Randompage' ),
+               'Lonelypages'               => array( 'SpecialPage', 'Lonelypages' ),
+               'Uncategorizedpages'        => array( 'SpecialPage', 'Uncategorizedpages' ),
+               'Uncategorizedcategories'   => array( 'SpecialPage', 'Uncategorizedcategories' ),
+               'Uncategorizedimages'       => array( 'SpecialPage', 'Uncategorizedimages' ),
+               'Unusedcategories'          => array( 'SpecialPage', 'Unusedcategories' ),
+               'Unusedimages'              => array( 'SpecialPage', 'Unusedimages' ),
+               'Wantedpages'               => array( 'IncludableSpecialPage', 'Wantedpages' ),
+               'Wantedcategories'          => array( 'SpecialPage', 'Wantedcategories' ),
+               'Mostlinked'                => array( 'SpecialPage', 'Mostlinked' ),
+               'Mostlinkedcategories'      => array( 'SpecialPage', 'Mostlinkedcategories' ),
+               'Mostcategories'            => array( 'SpecialPage', 'Mostcategories' ),
+               'Mostimages'                => array( 'SpecialPage', 'Mostimages' ),
+               'Mostrevisions'             => array( 'SpecialPage', 'Mostrevisions' ),
+               'Shortpages'                => array( 'SpecialPage', 'Shortpages' ),
+               'Longpages'                 => array( 'SpecialPage', 'Longpages' ),
+               'Newpages'                  => array( 'IncludableSpecialPage', 'Newpages' ),
+               'Ancientpages'              => array( 'SpecialPage', 'Ancientpages' ),
+               'Deadendpages'              => array( 'SpecialPage', 'Deadendpages' ),
+               'Allpages'                  => array( 'IncludableSpecialPage', 'Allpages' ),
+               'Prefixindex'               => array( 'IncludableSpecialPage', 'Prefixindex' ) ,
+               'Ipblocklist'               => array( 'SpecialPage', 'Ipblocklist' ),
+               'Specialpages'              => array( 'UnlistedSpecialPage', 'Specialpages' ),
+               'Contributions'             => array( 'UnlistedSpecialPage', 'Contributions' ),
+               'Emailuser'                 => array( 'UnlistedSpecialPage', 'Emailuser' ),
+               'Whatlinkshere'             => array( 'UnlistedSpecialPage', 'Whatlinkshere' ),
+               'Recentchangeslinked'       => array( 'UnlistedSpecialPage', 'Recentchangeslinked' ),
+               'Movepage'                  => array( 'UnlistedSpecialPage', 'Movepage' ),
+               'Blockme'                   => array( 'UnlistedSpecialPage', 'Blockme' ),
+               'Booksources'               => array( 'SpecialPage', 'Booksources' ),
+               'Categories'                => array( 'SpecialPage', 'Categories' ),
+               'Export'                    => array( 'SpecialPage', 'Export' ),
+               'Version'                   => array( 'SpecialPage', 'Version' ),
+               'Allmessages'               => array( 'SpecialPage', 'Allmessages' ),
+               'Log'                       => array( 'SpecialPage', 'Log' ),
+               'Blockip'                   => array( 'SpecialPage', 'Blockip', 'block' ),
+               'Undelete'                  => array( 'SpecialPage', 'Undelete', 'deletedhistory' ),
+               'Import'                    => array( 'SpecialPage', "Import", 'import' ),
+               'Lockdb'                    => array( 'SpecialPage', 'Lockdb', 'siteadmin' ),
+               'Unlockdb'                  => array( 'SpecialPage', 'Unlockdb', 'siteadmin' ),
+               'Userrights'                => array( 'SpecialPage', 'Userrights', 'userrights' ),
+               'MIMEsearch'                => array( 'SpecialPage', 'MIMEsearch' ),
+               'Unwatchedpages'            => array( 'SpecialPage', 'Unwatchedpages', 'unwatchedpages' ),
+               'Listredirects'             => array( 'SpecialPage', 'Listredirects' ),
+               'Revisiondelete'            => array( 'SpecialPage', 'Revisiondelete', 'deleterevision' ),
+               'Unusedtemplates'           => array( 'SpecialPage', 'Unusedtemplates' ),
+               'Randomredirect'            => array( 'SpecialPage', 'Randomredirect' ),
+
+               'Mypage'                    => array( 'SpecialMypage' ),
+               'Mytalk'                    => array( 'SpecialMytalk' ),
+               'Mycontributions'           => array( 'SpecialMycontributions' ),
+               'Listadmins'                => array( 'SpecialRedirectToSpecial', 'Listadmins', 'Listusers', 'sysop' ),
        );
 
+       static public $mAliases;
        static public $mListInitialised = false;
 
        /**#@-*/
@@ -148,6 +162,9 @@ class SpecialPage
                }
                wfProfileIn( __METHOD__ );
                
+               # Better to set this now, to avoid infinite recursion in carelessly written hooks
+               self::$mListInitialised = true;
+
                if( !$wgDisableCounters ) {
                        self::$mList['Popularpages'] = array( 'SpecialPage', 'Popularpages' );
                }
@@ -163,15 +180,65 @@ class SpecialPage
                # Add extension special pages
                self::$mList = array_merge( self::$mList, $wgSpecialPages );
 
-               # Better to set this now, to avoid infinite recursion in carelessly written hooks
-               self::$mListInitialised = true;
-
                # Run hooks
                # This hook can be used to remove undesired built-in special pages
                wfRunHooks( 'SpecialPage_initList', array( &self::$mList ) );
                wfProfileOut( __METHOD__ );
        }
 
+       static function initAliasList() {
+               if ( !is_null( self::$mAliases ) ) {
+                       return;
+               }
+
+               global $wgContLang;
+               $aliases = $wgContLang->getSpecialPageAliases();
+               $missingPages = self::$mList;
+               self::$mAliases = array();
+               foreach ( $aliases as $realName => $aliasList ) {
+                       foreach ( $aliasList as $alias ) {
+                               self::$mAliases[$wgContLang->caseFold( $alias )] = $realName;
+                       }
+                       unset( $missingPages[$realName] );
+               }
+               foreach ( $missingPages as $name => $stuff ) {
+                       self::$mAliases[$wgContLang->caseFold( $name )] = $name;
+               }
+       }
+
+       /**
+        * Given a special page alias, return the special page name.
+        * Returns false if there is no such alias.
+        */
+       static function resolveAlias( $alias ) {
+               global $wgContLang;
+
+               if ( !self::$mListInitialised ) self::initList();
+               if ( is_null( self::$mAliases ) ) self::initAliasList();
+               $caseFoldedAlias = $wgContLang->caseFold( $alias );
+               if ( isset( self::$mAliases[$caseFoldedAlias] ) ) {
+                       return self::$mAliases[$caseFoldedAlias];
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Given a special page name with a possible subpage, return an array 
+        * where the first element is the special page name and the second is the
+        * subpage.
+        */
+       static function resolveAliasWithSubpage( $alias ) {
+               $bits = explode( '/', $alias, 2 );
+               $name = self::resolveAlias( $bits[0] );
+               if( !isset( $bits[1] ) ) { // bug 2087
+                       $par = NULL;
+               } else {
+                       $par = $bits[1];
+               }
+               return array( $name, $par );
+       }
+
        /**
         * Add a page to the list of valid special pages. This used to be the preferred 
         * method for adding special pages in extensions. It's now suggested that you add 
@@ -228,55 +295,18 @@ class SpecialPage
                }
        }
 
-
        /**
-        * @static
-        * @param string $name
-        * @return mixed Title object if the redirect exists, otherwise NULL
+        * Get a special page with a given localised name, or NULL if there
+        * is no such special page.
         */
-       static function getRedirect( $name ) {
-               global $wgUser;
-
-               $redirects = array(
-                       'Mypage' => Title::makeTitle( NS_USER, $wgUser->getName() ),
-                       'Mytalk' => Title::makeTitle( NS_USER_TALK, $wgUser->getName() ),
-                       'Mycontributions' => Title::makeTitle( NS_SPECIAL, 'Contributions/' . $wgUser->getName() ),
-                       'Listadmins' => Title::makeTitle( NS_SPECIAL, 'Listusers/sysop' ), # @bug 2832
-                       'Logs' => Title::makeTitle( NS_SPECIAL, 'Log' ),
-                       'Randompage' => Title::makeTitle( NS_SPECIAL, 'Random' ),
-                       'Userlist' => Title::makeTitle( NS_SPECIAL, 'Listusers' )
-               );
-               wfRunHooks( 'SpecialPageGetRedirect', array( &$redirects ) );
-
-               return isset( $redirects[$name] ) ? $redirects[$name] : null;
-       }
-
-       /**
-        * Return part of the request string for a special redirect page
-        * This allows passing, e.g. action=history to Special:Mypage, etc.
-        *
-        * @param $name Name of the redirect page
-        * @return string
-        */
-       function getRedirectParams( $name ) {
-               global $wgRequest;
-               
-               $args = array();
-               switch( $name ) {
-                       case 'Mypage':
-                       case 'Mytalk':
-                       case 'Randompage':
-                               $args = array( 'action' );
-               }
-               
-               $params = array();
-               foreach( $args as $arg ) {
-                       if( $val = $wgRequest->getVal( $arg, false ) )
-                               $params[] = $arg . '=' . $val;
+       static function getPageByAlias( $alias ) {
+               $realName = self::resolveAlias( $alias );
+               if ( $realName ) {
+                       return self::getPage( $realName );
+               } else {
+                       return NULL;
                }
-               
-               return count( $params ) ? implode( '&', $params ) : false;
-       }       
+       }
 
        /**
         * Return categorised listable special pages for all users
@@ -333,67 +363,74 @@ class SpecialPage
         * @param $including      output is being captured for use in {{special:whatever}}
         */
        static function executePath( &$title, $including = false ) {
-               global $wgOut, $wgTitle;
-               $fname = 'SpecialPage::executePath';
-               wfProfileIn( $fname );
+               global $wgOut, $wgTitle, $wgRequest;
+               wfProfileIn( __METHOD__ );
 
-               $bits = split( "/", $title->getDBkey(), 2 );
+               # FIXME: redirects broken due to this call
+               $bits = explode( '/', $title->getDBkey(), 2 );
                $name = $bits[0];
                if( !isset( $bits[1] ) ) { // bug 2087
                        $par = NULL;
                } else {
                        $par = $bits[1];
                }
-
-               $page = SpecialPage::getPage( $name );
-               if ( is_null( $page ) ) {
-                       if ( $including ) {
-                               wfProfileOut( $fname );
-                               return false;
-                       } else {
-                               $redir = SpecialPage::getRedirect( $name );
-                               if ( isset( $redir ) ) {
-                                       if( $par )
-                                               $redir = Title::makeTitle( $redir->getNamespace(), $redir->getText() . '/' . $par );
-                                       $params = SpecialPage::getRedirectParams( $name );
-                                       if( $params ) {
-                                               $url = $redir->getFullUrl( $params );
-                                       } else {
-                                               $url = $redir->getFullUrl();
-                                       }
-                                       $wgOut->redirect( $url );
-                                       $retVal = $redir;
-                                       $wgOut->redirect( $url );
-                                       $retVal = $redir;
-                               } else {
-                                       $wgOut->setArticleRelated( false );
-                                       $wgOut->setRobotpolicy( 'noindex,nofollow' );
-                                       $wgOut->setStatusCode( 404 );
-                                       $wgOut->showErrorPage( 'nosuchspecialpage', 'nospecialpagetext' );
-                                       $retVal = false;
-                               }
+               $page = SpecialPage::getPageByAlias( $name );
+
+               # Nonexistent?
+               if ( !$page ) {
+                       if ( !$including ) {
+                               $wgOut->setArticleRelated( false );
+                               $wgOut->setRobotpolicy( 'noindex,nofollow' );
+                               $wgOut->setStatusCode( 404 );
+                               $wgOut->showErrorPage( 'nosuchspecialpage', 'nospecialpagetext' );
                        }
-               } else {
-                       if ( $including && !$page->includable() ) {
-                               wfProfileOut( $fname );
-                               return false;
-                       } elseif ( !$including ) {
-                               if($par !== NULL) {
-                                       $wgTitle = Title::makeTitle( NS_SPECIAL, $name );
-                               } else {
-                                       $wgTitle = $title;
-                               }
+                       wfProfileOut( __METHOD__ );
+                       return false;
+               }
+
+               # Check for redirect
+               if ( !$including ) {
+                       $redirect = $page->getRedirect( $par );
+                       if ( $redirect ) {
+                               $query = $page->getRedirectQuery();
+                               $url = $redirect->getFullUrl( $query );
+                               $wgOut->redirect( $url );
+                               wfProfileOut( __METHOD__ );
+                               return $redirect;
                        }
-                       $page->including( $including );
+               }
 
-                       $profName = 'Special:' . $page->getName();
-                       wfProfileIn( $profName );
-                       $page->execute( $par );
-                       wfProfileOut( $profName );
-                       $retVal = true;
+               # Redirect to canonical alias for GET commands
+               # Not for POST, we'd lose the post data, so it's best to just distribute 
+               # the request. Such POST requests are possible for old extensions that 
+               # generate self-links without being aware that their default name has 
+               # changed.
+               if ( !$including && $name != $page->getLocalName() && !$wgRequest->wasPosted() ) {
+                       $query = $_GET;
+                       unset( $query['title'] );
+                       $query = wfArrayToCGI( $query );
+                       $title = $page->getTitle();
+                       $url = $title->getFullUrl( $query );
+                       $wgOut->redirect( $url );
+                       wfProfileOut( __METHOD__ );
+                       return $redirect;
                }
-               wfProfileOut( $fname );
-               return $retVal;
+
+               if ( $including && !$page->includable() ) {
+                       wfProfileOut( __METHOD__ );
+                       return false;
+               } elseif ( !$including ) {
+                       $wgTitle = $page->getTitle();
+               }
+               $page->including( $including );
+
+               // Execute special page
+               $profName = 'Special:' . $page->getName();
+               wfProfileIn( $profName );
+               $page->execute( $par );
+               wfProfileOut( $profName );
+               wfProfileOut( __METHOD__ );
+               return true;
        }
 
        /**
@@ -418,6 +455,45 @@ class SpecialPage
                return $ret;
        }
 
+       /**
+        * Get the local name for a specified canonical name
+        */
+       static function getLocalNameFor( $name, $subpage = false ) {
+               global $wgContLang;
+               $aliases = $wgContLang->getSpecialPageAliases();
+               if ( isset( $aliases[$name][0] ) ) {
+                       $name = $aliases[$name][0];
+               }
+               if ( $subpage !== false && !is_null( $subpage ) ) {
+                       $name = "$name/$subpage";
+               }
+               return $name;
+       }
+
+       /**
+        * Get a localised Title object for a specified special page name
+        */
+       static function getTitleFor( $name, $subpage = false ) {
+               $name = self::getLocalNameFor( $name, $subpage );
+               return Title::makeTitle( NS_SPECIAL, $name );
+       }
+
+       /**
+        * Get a localised Title object for a page name with a possibly unvalidated subpage
+        */
+       static function getSafeTitleFor( $name, $subpage = false ) {
+               $name = self::getLocalNameFor( $name, $subpage );
+               return Title::makeTitleSafe( NS_SPECIAL, $name );
+       }
+
+       /**
+        * Get a title for a given alias
+        */
+       static function getTitleForAlias( $alias ) {
+               $name = self::resolveAlias( $alias );
+               return self::getTitleFor( $name );
+       }
+
        /**
         * Default constructor for special pages
         * Derivative classes should call this from their constructor
@@ -474,6 +550,16 @@ class SpecialPage
        function including( $x = NULL ) { return wfSetVar( $this->mIncluding, $x ); }
        /**#@-*/
 
+       /**
+        * Get the localised name of the special page
+        */
+       function getLocalName() {
+               if ( !isset( $this->mLocalName ) ) {
+                       $this->mLocalName = self::getLocalNameFor( $this->mName );
+               }
+               return $this->mLocalName;
+       }
+
        /**
         * Checks if the given user (identified by an object) can execute this
         * special page (as defined by $mRestriction)
@@ -503,6 +589,8 @@ class SpecialPage
        /**
         * Default execute method
         * Checks user permissions, calls the function given in mFunction
+        *
+        * This may be overridden by subclasses. 
         */
        function execute( $par ) {
                global $wgUser;
@@ -515,6 +603,7 @@ class SpecialPage
                        if(!function_exists($func) and $this->mFile) {
                                require_once( $this->mFile );
                        }
+                       # FIXME: these hooks are broken for extensions and anything else that subclasses SpecialPage. 
                        if ( wfRunHooks( 'SpecialPageExecuteBeforeHeader', array( &$this, &$par, &$func ) ) )
                                $this->outputHeader();
                        if ( ! wfRunHooks( 'SpecialPageExecuteBeforePage', array( &$this, &$par, &$func ) ) )
@@ -549,8 +638,8 @@ class SpecialPage
        /**
         * Get a self-referential title object
         */
-       function getTitle() {
-               return Title::makeTitle( NS_SPECIAL, $this->mName );
+       function getTitle( $subpage = false) {
+               return self::getTitleFor( $this->mName, $subpage );
        }
 
        /**
@@ -560,6 +649,30 @@ class SpecialPage
                return wfSetVar( $this->mListed, $listed );
        }
 
+       /**
+        * If the special page is a redirect, then get the Title object it redirects to. 
+        * False otherwise.
+        */
+       function getRedirect( $subpage = false ) {
+               return false;
+       }
+
+       /**
+        * Return part of the request string for a special redirect page
+        * This allows passing, e.g. action=history to Special:Mypage, etc.
+        *
+        * @return string
+        */
+       function getRedirectQuery() {
+               global $wgRequest;
+               $params = array();
+               foreach( $this->mAllowedRedirectParams as $arg ) {
+                       if( $val = $wgRequest->getVal( $arg, false ) )
+                               $params[] = $arg . '=' . $val;
+               }
+               
+               return count( $params ) ? implode( '&', $params ) : false;
+       }
 }
 
 /**
@@ -583,4 +696,67 @@ class IncludableSpecialPage extends SpecialPage
                SpecialPage::SpecialPage( $name, $restriction, $listed, $function, $file, true );
        }
 }
+
+class SpecialRedirectToSpecial extends UnlistedSpecialPage {
+       var $redirName, $redirSubpage;
+
+       function __construct( $name, $redirName, $redirSubpage = false, $redirectParams = array() ) {
+               parent::__construct( $name );
+               $this->redirName = $redirName;
+               $this->redirSubpage = $redirSubpage;
+               $this->mAllowedRedirectParams = $redirectParams;
+       }
+
+       function getRedirect( $subpage ) {
+               if ( $this->redirSubpage === false ) {
+                       return SpecialPage::getTitleFor( $this->redirName, $subpage );
+               } else {
+                       return SpecialPage::getTitleFor( $this->redirName, $this->redirSubpage );
+               }
+       }
+}
+
+class SpecialMypage extends UnlistedSpecialPage {
+       function __construct() {
+               parent::__construct( 'Mypage' );
+               $this->mAllowedRedirectParams = array( 'action' );
+       }
+
+       function getRedirect( $subpage ) {
+               global $wgUser;
+               if ( strval( $subpage ) !== '' ) {
+                       return Title::makeTitle( NS_USER, $wgUser->getName() . '/' . $subpage );
+               } else {
+                       return Title::makeTitle( NS_USER, $wgUser->getName() );
+               }
+       }
+}
+
+class SpecialMytalk extends UnlistedSpecialPage {
+       function __construct() {
+               parent::__construct( 'Mytalk' );
+               $this->mAllowedRedirectParams = array( 'action' );
+       }
+
+       function getRedirect( $subpage ) {
+               global $wgUser;
+               if ( strval( $subpage ) !== '' ) {
+                       return Title::makeTitle( NS_USER_TALK, $wgUser->getName() . '/' . $subpage );
+               } else {
+                       return Title::makeTitle( NS_USER_TALK, $wgUser->getName() );
+               }
+       }
+}
+
+class SpecialMycontributions extends UnlistedSpecialPage {
+       function __construct() {
+               parent::__construct(  'Mycontributions' );
+       }
+
+       function getRedirect( $subpage ) {
+               global $wgUser;
+               return SpecialPage::getTitleFor( 'Contributions', $wgUser->getName() );
+       }
+}
+
 ?>
index 2484b32..497c732 100644 (file)
@@ -328,7 +328,7 @@ class PreferencesForm {
                }
 
                if( $needRedirect && $error === false ) {
-                       $title =& Title::makeTitle( NS_SPECIAL, "Preferences" );
+                       $title =& SpecialPage::getTitleFor( "Preferences" );
                        $wgOut->redirect($title->getFullURL('success'));
                        return;
                }
@@ -478,7 +478,7 @@ class PreferencesForm {
                $dateopts = $wgLang->getDatePreferences();
                $togs = User::getToggles();
 
-               $titleObj = Title::makeTitle( NS_SPECIAL, 'Preferences' );
+               $titleObj = SpecialPage::getTitleFor( 'Preferences' );
                $action = $titleObj->escapeLocalURL();
 
                # Pre-expire some toggles so they won't show if disabled
@@ -508,7 +508,7 @@ class PreferencesForm {
                                $disableEmailPrefs = true;
                                $skin = $wgUser->getSkin();
                                $emailauthenticated = wfMsg('emailnotauthenticated').'<br />' .
-                                       $skin->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Confirmemail' ),
+                                       $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Confirmemail' ),
                                                wfMsg( 'emailconfirmlink' ) );
                        }
                } else {
index 8dfb68a..35f0d29 100644 (file)
@@ -571,7 +571,7 @@ function rcOptionsPanel( $defaults, $nondefaults ) {
  */
 function rcNamespaceForm( $namespace, $invert, $nondefaults, $categories_any ) {
        global $wgScript, $wgAllowCategorizedRecentChanges, $wgRequest;
-       $t = Title::makeTitle( NS_SPECIAL, 'Recentchanges' );
+       $t = SpecialPage::getTitleFor( 'Recentchanges' );
 
        $namespaceselect = HTMLnamespaceselector($namespace, '');
        $submitbutton = '<input type="submit" value="' . wfMsgHtml( 'allpagessubmit' ) . "\" />\n";
index afbb589..1ab505d 100644 (file)
@@ -89,7 +89,7 @@ class RevisionDeleteForm {
                        $hidden[] = wfHidden( 'oldid[]', $revid );
                }
                
-               $special = Title::makeTitle( NS_SPECIAL, 'Revisiondelete' );
+               $special = SpecialPage::getTitleFor( 'Revisiondelete' );
                $wgOut->addHtml( wfElement( 'form', array(
                        'method' => 'post',
                        'action' => $special->getLocalUrl( 'action=submit' ) ),
index 057b487..8c2f8aa 100644 (file)
@@ -177,7 +177,7 @@ class SpecialSearch {
 
                if( $num || $this->offset ) {
                        $prevnext = wfViewPrevNext( $this->offset, $this->limit,
-                               'Special:Search',
+                               SpecialPage::getTitleFor( 'Search' ),
                                wfArrayToCGI(
                                        $this->powerSearchOptions(),
                                        array( 'search' => $term ) ) );
@@ -404,7 +404,7 @@ class SpecialSearch {
                        '', '', '', '', '', # Dummy placeholders
                        $searchButton );
 
-               $title = Title::makeTitle( NS_SPECIAL, 'Search' );
+               $title = SpecialPage::getTitleFor( 'Search' );
                $action = $title->escapeLocalURL();
                return "<br /><br />\n<form id=\"powersearch\" method=\"get\" " .
                  "action=\"$action\">\n{$ret}\n</form>\n";
index e07fa95..299cb23 100644 (file)
@@ -495,7 +495,7 @@ class UndeleteForm {
                $wgOut->addWikiText( wfMsg( "undeletepagetext" ) );
 
                $sk = $wgUser->getSkin();
-               $undelete =& Title::makeTitle( NS_SPECIAL, 'Undelete' );
+               $undelete =& SpecialPage::getTitleFor( 'Undelete' );
                $wgOut->addHTML( "<ul>\n" );
                while( $row = $result->fetchObject() ) {
                        $title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title );
@@ -533,7 +533,7 @@ class UndeleteForm {
                        $wgOut->addWikiText( $rev->getText() );
                }
                
-               $self = Title::makeTitle( NS_SPECIAL, "Undelete" );
+               $self = SpecialPage::getTitleFor( "Undelete" );
                
                $wgOut->addHtml(
                        wfElement( 'textarea', array(
@@ -631,7 +631,7 @@ class UndeleteForm {
                }
 
                if ( $this->mAllowed ) {
-                       $titleObj = Title::makeTitle( NS_SPECIAL, "Undelete" );
+                       $titleObj = SpecialPage::getTitleFor( "Undelete" );
                        $action = $titleObj->getLocalURL( "action=submit" );
                        # Start the form here
                        $top = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'undelete' ) );
index 6627f75..1f24d13 100644 (file)
@@ -54,7 +54,7 @@ class DBUnlockForm {
                }
                $lc = htmlspecialchars( wfMsg( "unlockconfirm" ) );
                $lb = htmlspecialchars( wfMsg( "unlockbtn" ) );
-               $titleObj = Title::makeTitle( NS_SPECIAL, "Unlockdb" );
+               $titleObj = SpecialPage::getTitleFor( "Unlockdb" );
                $action = $titleObj->escapeLocalURL( "action=submit" );
                $token = htmlspecialchars( $wgUser->editToken() );
 
@@ -94,7 +94,7 @@ END
                        $wgOut->showFileDeleteError( $wgReadOnlyFile );
                        return;
                }
-               $titleObj = Title::makeTitle( NS_SPECIAL, "Unlockdb" );
+               $titleObj = SpecialPage::getTitleFor( "Unlockdb" );
                $success = $titleObj->getFullURL( "action=success" );
                $wgOut->redirect( $success );
        }
index b33a24d..d232f02 100644 (file)
@@ -37,7 +37,7 @@ class UnusedtemplatesPage extends QueryPage {
                $title = Title::makeTitle( NS_TEMPLATE, $result->title );
                $pageLink = $skin->makeKnownLinkObj( $title, '', 'redirect=no' );
                $wlhLink = $skin->makeKnownLinkObj(
-                       Title::makeTitle( NS_SPECIAL, 'Whatlinkshere' ),
+                       SpecialPage::getTitleFor( 'Whatlinkshere' ),
                        wfMsgHtml( 'unusedtemplateswlh' ),
                        'target=' . $title->getPrefixedUrl() );
                return wfSpecialList( $pageLink, $wlhLink );
index 29d6f3b..5873dfe 100644 (file)
@@ -398,7 +398,7 @@ class UploadForm {
                                $image = new Image( $nt );
                                if( $image->wasDeleted() ) {
                                        $skin = $wgUser->getSkin();
-                                       $ltitle = Title::makeTitle( NS_SPECIAL, 'Log' );
+                                       $ltitle = SpecialPage::getTitleFor( 'Log' );
                                        $llink = $skin->makeKnownLinkObj( $ltitle, wfMsgHtml( 'deletionlog' ), 'type=delete&page=' . $nt->getPrefixedUrl() );
                                        $warning .= wfOpenElement( 'li' ) . wfMsgWikiHtml( 'filewasdeleted', $llink ) . wfCloseElement( 'li' );
                                }
@@ -632,7 +632,7 @@ class UploadForm {
                $reupload = wfMsgHtml( 'reupload' );
                $iw = wfMsgWikiHtml( 'ignorewarning' );
                $reup = wfMsgWikiHtml( 'reuploaddesc' );
-               $titleObj = Title::makeTitle( NS_SPECIAL, 'Upload' );
+               $titleObj = SpecialPage::getTitleFor( 'Upload' );
                $action = $titleObj->escapeLocalURL( 'action=submit' );
 
                if ( $wgUseCopyrightUpload )
@@ -712,7 +712,7 @@ class UploadForm {
                $ulb = wfMsgHtml( 'uploadbtn' );
 
 
-               $titleObj = Title::makeTitle( NS_SPECIAL, 'Upload' );
+               $titleObj = SpecialPage::getTitleFor( 'Upload' );
                $action = $titleObj->escapeLocalURL();
 
                $encDestFile = htmlspecialchars( $this->mDestFile );
index 2cd1468..719ab37 100644 (file)
@@ -178,7 +178,7 @@ class LoginForm {
                        # Confirm that the account was created
                        global $wgOut;
                        $skin = $wgUser->getSkin();
-                       $self = Title::makeTitle( NS_SPECIAL, 'Userlogin' );
+                       $self = SpecialPage::getTitleFor( 'Userlogin' );
                        $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
                        $wgOut->setArticleRelated( false );
                        $wgOut->setRobotPolicy( 'noindex,nofollow' );
@@ -564,7 +564,7 @@ class LoginForm {
                        }
                }
 
-               $titleObj = Title::makeTitle( NS_SPECIAL, 'Userlogin' );
+               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
 
                if ( $this->mType == 'signup' ) {
                        $template = new UsercreateTemplate();
@@ -662,7 +662,7 @@ class LoginForm {
        function cookieRedirectCheck( $type ) {
                global $wgOut;
 
-               $titleObj = Title::makeTitle( NS_SPECIAL, 'Userlogin' );
+               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
                $check = $titleObj->getFullURL( 'wpCookieCheck='.$type );
 
                return $wgOut->redirect( $check );
@@ -728,7 +728,7 @@ class LoginForm {
         */
        function makeLanguageSelectorLink( $text, $lang ) {
                global $wgUser;
-               $self = Title::makeTitle( NS_SPECIAL, 'Userlogin' );
+               $self = SpecialPage::getTitleFor( 'Userlogin' );
                $attr[] = 'uselang=' . $lang;
                if( $this->mType == 'signup' )
                        $attr[] = 'type=signup';
index b17cc4a..4edbf12 100644 (file)
@@ -34,7 +34,7 @@ class UserrightsForm extends HTMLForm {
                $this->mRequest =& $request;
                $this->mName = 'userrights';
 
-               $titleObj = Title::makeTitle( NS_SPECIAL, 'Userrights' );
+               $titleObj = SpecialPage::getTitleFor( 'Userrights' );
                $this->action = $titleObj->escapeLocalURL();
        }
 
index 7b07060..8e5cee3 100644 (file)
@@ -103,7 +103,7 @@ class WantedPagesPage extends QueryPage {
         * @return string
         */
        function makeWlhLink( &$title, &$skin, $text ) {
-               $wlhTitle = Title::makeTitle( NS_SPECIAL, 'Whatlinkshere' );
+               $wlhTitle = SpecialPage::getTitleFor( 'Whatlinkshere' );
                return $skin->makeKnownLinkObj( $wlhTitle, $text, 'target=' . $title->getPrefixedUrl() );
        }
        
index 87c925a..0ce03b8 100644 (file)
@@ -23,13 +23,13 @@ function wfSpecialWatchlist( $par ) {
        $fname = 'wfSpecialWatchlist';
 
        $skin =& $wgUser->getSkin();
-       $specialTitle = Title::makeTitle( NS_SPECIAL, 'Watchlist' );
+       $specialTitle = SpecialPage::getTitleFor( 'Watchlist' );
        $wgOut->setRobotPolicy( 'noindex,nofollow' );
 
        # Anons don't get a watchlist
        if( $wgUser->isAnon() ) {
                $wgOut->setPageTitle( wfMsg( 'watchnologin' ) );
-               $llink = $skin->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Userlogin' ), wfMsgHtml( 'loginreqlink' ), 'returnto=' . $specialTitle->getPrefixedUrl() );
+               $llink = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Userlogin' ), wfMsgHtml( 'loginreqlink' ), 'returnto=' . $specialTitle->getPrefixedUrl() );
                $wgOut->addHtml( wfMsgWikiHtml( 'watchlistanontext', $llink ) );
                return;
        } else {
@@ -325,7 +325,7 @@ function wfSpecialWatchlist( $par ) {
        $wgOut->addHTML( "\n" . wlCutoffLinks( $days, 'Watchlist', $nondefaults ) . "<br />\n" );
 
        # Spit out some control panel links
-       $thisTitle = Title::makeTitle( NS_SPECIAL, 'Watchlist' );
+       $thisTitle = SpecialPage::getTitleFor( 'Watchlist' );
        $skin = $wgUser->getSkin();
        $linkElements = array( 'hideOwn' => 'wlhideshowown', 'hideBots' => 'wlhideshowbots' );
        
@@ -493,7 +493,7 @@ function wlHandleClear( &$out, &$request, $par ) {
                                $out->returnToMain();
                        } else {
                                # Confirming, so show a form
-                               $wlTitle = Title::makeTitle( NS_SPECIAL, 'Watchlist' );
+                               $wlTitle = SpecialPage::getTitleFor( 'Watchlist' );
                                $out->addHTML( wfElement( 'form', array( 'method' => 'post', 'action' => $wlTitle->getLocalUrl( 'action=clear' ) ), NULL ) );
                                $out->addWikiText( wfMsgExt( 'watchlistcount', array( 'parsemag', 'escape'), $wgLang->formatNum( $count ) ) );
                                $out->addWikiText( wfMsg( 'watchlistcleartext' ) );
index acc4b89..b81b631 100644 (file)
@@ -299,7 +299,7 @@ class Title {
 
                                $rt = Title::newFromText( $m[1] );
                                # Disallow redirects to Special:Userlogout
-                               if ( !is_null($rt) && $rt->getNamespace() == NS_SPECIAL && preg_match( '/^Userlogout/i', $rt->getText() ) ) {
+                               if ( !is_null($rt) && $rt->isSpecial( 'Userlogout' ) ) {
                                        $rt = NULL;
                                }
                        }
@@ -2365,5 +2365,37 @@ class Title {
                                return 'nstab-' . $wgContLang->lc( $this->getSubjectNsText() );
                }
        }
+
+       /**
+        * Returns true if this title resolves to the named special page
+        * @param string $name The special page name
+        * @access public
+        */
+       function isSpecial( $name ) {
+               if ( $this->getNamespace() == NS_SPECIAL ) {
+                       list( $thisName, $subpage ) = SpecialPage::resolveAliasWithSubpage( $this->getDBkey() );
+                       if ( $name == $thisName ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * If the Title refers to a special page alias which is not the local default, 
+        * returns a new Title which points to the local default. Otherwise, returns $this.
+        */
+       function fixSpecialName() {
+               if ( $this->getNamespace() == NS_SPECIAL ) {
+                       $canonicalName = SpecialPage::resolveAlias( $this->mDbkeyform );
+                       if ( $canonicalName ) {
+                               $localName = SpecialPage::getLocalNameFor( $canonicalName );
+                               if ( $localName != $this->mDbkeyform ) {
+                                       return Title::makeTitle( NS_SPECIAL, $localName );
+                               }
+                       }
+               }
+               return $this;
+       }
 }
 ?>
index 5d169e5..6bda68e 100644 (file)
@@ -2234,7 +2234,7 @@ class User {
         */
        function confirmationTokenUrl( &$expiration ) {
                $token = $this->confirmationToken( $expiration );
-               $title = Title::makeTitle( NS_SPECIAL, 'Confirmemail/' . $token );
+               $title = SpecialPage::getTitleFor( 'Confirmemail', $token );
                return $title->getFullUrl();
        }
 
index 9428761..8953d7a 100644 (file)
@@ -382,7 +382,7 @@ class EmailNotification {
                } else {
                        $subject = str_replace('$PAGEEDITOR', $name, $subject);
                        $keys['$PAGEEDITOR']          = $name;
-                       $emailPage = Title::makeTitle( NS_SPECIAL, 'Emailuser/' . $name );
+                       $emailPage = SpecialPage::getSafeTitleFor( 'Emailuser', $name );
                        $keys['$PAGEEDITOR_EMAIL'] = $emailPage->getFullUrl();
                }
                $userPage = $wgUser->getUserPage();
index 401756b..ea89eef 100644 (file)
@@ -99,7 +99,7 @@ class MediaWiki {
                if( !is_null( $search ) && $search !== '' ) {
                        // Compatibility with old search URLs which didn't use Special:Search
                        // Do this above the read whitelist check for security...
-                       $title = Title::makeTitle( NS_SPECIAL, 'Search' );
+                       $title = SpecialPage::getTitleFor( 'Search' );
                }
                $this->setVal( 'Search', $search );
 
@@ -125,10 +125,10 @@ class MediaWiki {
                $action = $this->getVal('Action');
                if( !$this->getVal('DisableInternalSearch') && !is_null( $search ) && $search !== '' ) {
                        require_once( 'includes/SpecialSearch.php' );
-                       $title = Title::makeTitle( NS_SPECIAL, 'Search' );
+                       $title = SpecialPage::getTitleFor( 'Search' );
                        wfSpecialSearch();
                } else if( !$title or $title->getDBkey() == '' ) {
-                       $title = Title::makeTitle( NS_SPECIAL, 'Badtitle' );
+                       $title = SpecialPage::getTitleFor( 'Badtitle' );
                        # Die now before we mess up $wgArticle and the skin stops working
                        throw new ErrorPageError( 'badtitle', 'badtitletext' );
                } else if ( $title->getInterwiki() != '' ) {
@@ -141,7 +141,7 @@ class MediaWiki {
                        if ( !preg_match( '/^' . preg_quote( $this->getVal('Server'), '/' ) . '/', $url ) && $title->isLocal() ) {
                                $output->redirect( $url );
                        } else {
-                               $title = Title::makeTitle( NS_SPECIAL, 'Badtitle' );
+                               $title = SpecialPage::getTitleFor( 'Badtitle' );
                                throw new ErrorPageError( 'badtitle', 'badtitletext' );
                        }
                } else if ( ( $action == 'view' ) &&
index b5003a2..a2c75b9 100644 (file)
@@ -71,7 +71,7 @@ class ApiFeedWatchlist extends ApiBase {
 
                global $wgFeedClasses, $wgSitename, $wgContLanguageCode;
                $feedTitle = $wgSitename . ' - ' . wfMsgForContent('watchlist') . ' [' . $wgContLanguageCode . ']';
-               $feedUrl = Title :: makeTitle(NS_SPECIAL, 'Watchlist')->getFullUrl();
+               $feedUrl = SpecialPage::getTitleFor( 'Watchlist' )->getFullUrl();
 
                $feed = new $wgFeedClasses[$feedformat] ($feedTitle, htmlspecialchars(wfMsgForContent('watchlist')), $feedUrl);
 
@@ -124,4 +124,4 @@ class ApiFeedWatchlist extends ApiBase {
                return __CLASS__ . ': $Id$';
        }
 }
-?>
\ No newline at end of file
+?>
index 454d60e..877ebd6 100644 (file)
@@ -67,13 +67,15 @@ class Language {
                'separatorTransformTable', 'fallback8bitEncoding', 'linkPrefixExtension',
                'defaultUserOptionOverrides', 'linkTrail', 'namespaceAliases', 
                'dateFormats', 'datePreferences', 'datePreferenceMigrationMap', 
-               'defaultDateFormat', 'extraUserToggles' );
+               'defaultDateFormat', 'extraUserToggles', 'specialPageAliases' );
 
        static public $mMergeableMapKeys = array( 'messages', 'namespaceNames', 'mathNames', 
                'dateFormats', 'defaultUserOptionOverrides', 'magicWords' );
 
        static public $mMergeableListKeys = array( 'extraUserToggles' );
 
+       static public $mMergeableAliasListKeys = array( 'specialPageAliases' );
+
        static public $mLocalisationCache = array();
 
        static public $mWeekdayMsgs = array(
@@ -248,6 +250,10 @@ class Language {
        }
 
        function specialPage( $name ) {
+               $aliases = $this->getSpecialPageAliases();
+               if ( isset( $aliases[$name][0] ) ) {
+                       $name = $aliases[$name][0];
+               }
                return $this->getNsText(NS_SPECIAL) . ':' . $name;
        }
 
@@ -908,6 +914,21 @@ class Language {
                        $str );
        }
 
+       /**
+        * Return a case-folded representation of $s
+        *
+        * This is a representation such that caseFold($s1)==caseFold($s2) if $s1 
+        * and $s2 are the same except for the case of their characters. It is not
+        * necessary for the value returned to make sense when displayed.
+        *
+        * Do *not* perform any other normalisation in this function. If a caller
+        * uses this function when it should be using a more general normalisation
+        * function, then fix the caller.
+        */
+       function caseFold( $s ) {
+               return $this->uc( $s );
+       }
+
        function checkTitleEncoding( $s ) {
                if( is_array( $s ) ) {
                        wfDebugDieBacktrace( 'Given array to checkTitleEncoding.' );
@@ -1083,6 +1104,20 @@ class Language {
                $mw->mSynonyms = array_slice( $rawEntry, 1 );
        }
 
+       /**
+        * Get special page names, as an associative array
+        *   case folded alias => real name
+        */
+       function getSpecialPageAliases() {
+               $this->load();
+               if ( !isset( $this->mExtendedSpecialPageAliases ) ) {
+                       $this->mExtendedSpecialPageAliases = $this->specialPageAliases;
+                       wfRunHooks( 'LangugeGetSpecialPageAliases', 
+                               array( &$this->mExtendedSpecialPageAliases, $this->getCode() ) );
+               }
+               return $this->mExtendedSpecialPageAliases;
+       }
+
        /**
         * Italic is unsuitable for some languages
         *
@@ -1550,6 +1585,8 @@ class Language {
                                                        $cache[$key] = $cache[$key] + $secondary[$key];
                                                } elseif ( in_array( $key, self::$mMergeableListKeys ) ) {
                                                        $cache[$key] = array_merge( $secondary[$key], $cache[$key] );
+                                               } elseif ( in_array( $key, self::$mMergeableAliasListKeys ) ) {
+                                                       $cache[$key] = array_merge_recursive( $cache[$key], $secondary[$key] );
                                                }
                                        }
                                } else {
index 8d35dea..7d2de2c 100644 (file)
@@ -206,6 +206,8 @@ $bookstoreList = array(
  * Note to translators:
  *   Please include the English words as synonyms.  This allows people
  *   from other wikis to contribute more easily.
+ *
+ * This array can be modified at runtime with the LanguageGetMagic hook
  */
 $magicWords = array(
 #   ID                                 CASE  SYNONYMS
@@ -318,6 +320,81 @@ $magicWords = array(
        'padright'               => array( 0,    'PADRIGHT'               ),
 );
 
+/**
+ * Alternate names of special pages. All names are case-insensitive. The first
+ * listed alias will be used as the default. Aliases from the fallback 
+ * localisation (usually English) will be included by default. 
+ * 
+ * This array may be altered at runtime using the LangugeGetSpecialPageAliases 
+ * hook. 
+ */
+$specialPageAliases = array(
+       'DoubleRedirects'           => array( 'DoubleRedirects' ),
+       'BrokenRedirects'           => array( 'BrokenRedirects' ),
+       'Disambiguations'           => array( 'Disambiguations' ),
+       'Userlogin'                 => array( 'Userlogin' ),
+       'Userlogout'                => array( 'Userlogout' ),
+       'Preferences'               => array( 'Preferences' ),
+       'Watchlist'                 => array( 'Watchlist' ),
+       'Recentchanges'             => array( 'Recentchanges' ),
+       'Upload'                    => array( 'Upload' ),
+       'Imagelist'                 => array( 'Imagelist' ),
+       'Newimages'                 => array( 'Newimages' ),
+       'Listusers'                 => array( 'Listusers', 'Userlist' ),
+       'Statistics'                => array( 'Statistics' ),
+       'Randompage'                => array( 'Random', 'Randompage' ),
+       'Lonelypages'               => array( 'Lonelypages' ),
+       'Uncategorizedpages'        => array( 'Uncategorizedpages' ),
+       'Uncategorizedcategories'   => array( 'Uncategorizedcategories' ),
+       'Uncategorizedimages'       => array( 'Uncategorizedimages' ),
+       'Unusedcategories'          => array( 'Unusedcategories' ),
+       'Unusedimages'              => array( 'Unusedimages' ),
+       'Wantedpages'               => array( 'Wantedpages' ),
+       'Wantedcategories'          => array( 'Wantedcategories' ),
+       'Mostlinked'                => array( 'Mostlinked' ),
+       'Mostlinkedcategories'      => array( 'Mostlinkedcategories' ),
+       'Mostcategories'            => array( 'Mostcategories' ),
+       'Mostimages'                => array( 'Mostimages' ),
+       'Mostrevisions'             => array( 'Mostrevisions' ),
+       'Shortpages'                => array( 'Shortpages' ),
+       'Longpages'                 => array( 'Longpages' ),
+       'Newpages'                  => array( 'Newpages' ),
+       'Ancientpages'              => array( 'Ancientpages' ),
+       'Deadendpages'              => array( 'Deadendpages' ),
+       'Allpages'                  => array( 'Allpages' ),
+       'Prefixindex'               => array( 'Prefixindex' ) ,
+       'Ipblocklist'               => array( 'Ipblocklist' ),
+       'Specialpages'              => array( 'Specialpages' ),
+       'Contributions'             => array( 'Contributions' ),
+       'Emailuser'                 => array( 'Emailuser' ),
+       'Whatlinkshere'             => array( 'Whatlinkshere' ),
+       'Recentchangeslinked'       => array( 'Recentchangeslinked' ),
+       'Movepage'                  => array( 'Movepage' ),
+       'Blockme'                   => array( 'Blockme' ),
+       'Booksources'               => array( 'Booksources' ),
+       'Categories'                => array( 'Categories' ),
+       'Export'                    => array( 'Export' ),
+       'Version'                   => array( 'Version' ),
+       'Allmessages'               => array( 'Allmessages' ),
+       'Log'                       => array( 'Log', 'Logs' ),
+       'Blockip'                   => array( 'Blockip' ),
+       'Undelete'                  => array( 'Undelete' ),
+       'Import'                    => array( 'Import' ),
+       'Lockdb'                    => array( 'Lockdb' ),
+       'Unlockdb'                  => array( 'Unlockdb' ),
+       'Userrights'                => array( 'Userrights' ),
+       'MIMEsearch'                => array( 'MIMEsearch' ),
+       'Unwatchedpages'            => array( 'Unwatchedpages' ),
+       'Listredirects'             => array( 'Listredirects' ),
+       'Revisiondelete'            => array( 'Revisiondelete' ),
+       'Unusedtemplates'           => array( 'Unusedtemplates' ),
+       'Randomredirect'            => array( 'Randomredirect' ),
+       'Mypage'                    => array( 'Mypage' ),
+       'Mytalk'                    => array( 'Mytalk' ),
+       'Mycontributions'           => array( 'Mycontributions' ),
+       'Listadmins'                => array( 'Listadmins' ),
+);
+
 /**
  * Regular expression matching the "link trail", e.g. "ed" in [[Toast]]ed, as 
  * the first group, and the remainder of the string as the second group.
index ca2a62d..0f6e62c 100644 (file)
@@ -193,7 +193,7 @@ class DumpHTML {
 
                $this->setupGlobals();
                print "Special:Categories...";
-               $this->doArticle( Title::makeTitle( NS_SPECIAL, 'Categories' ) );
+               $this->doArticle( SpecialPage::getTitleFor( 'Categories' ) );
                print "\n";
        }
 
index 90bfe7d..b06b7fe 100644 (file)
@@ -16,7 +16,7 @@ if ( !preg_match( '/^https?:/', $wgFavicon ) ) {
        $favicon = htmlspecialchars( $wgFavicon );
 }
 
-$title = Title::makeTitle( NS_SPECIAL, 'Search' );
+$title = SpecialPage::getTitleFor( 'Search' );
 $template = $title->escapeFullURL( 'search={searchTerms}' );
 
 $suggest = htmlspecialchars($wgServer . $wgScriptPath . '/api.php?action=opensearch&search={searchTerms}');
index 63fe963..6ad8914 100644 (file)
@@ -252,8 +252,8 @@ class SkinCologneBlue extends Skin {
                                wfMsg( "mypage" ) )
                          . $sep . $tl
                          . $sep . $this->specialLink( "watchlist" )
-                         . $sep . $this->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, "Contributions" ),
-                               wfMsg( "mycontris" ), "target=" . wfUrlencode($wgUser->getName() ) )
+                         . $sep . $this->makeKnownLinkObj( SpecialPage::getSafeTitleFor( "Contributions", $wgUser->getName() ),
+                               wfMsg( "mycontris" ) )
                          . $sep . $this->specialLink( "preferences" )
                          . $sep . $this->specialLink( "userlogout" );
                } else {
@@ -275,7 +275,7 @@ class SkinCologneBlue extends Skin {
                }
 
                $s .= $sep . $this->makeKnownLinkObj(
-                       Title::makeTitle( NS_SPECIAL, 'Specialpages' ),
+                       SpecialPage::getTitleFor( 'Specialpages' ),
                        wfMsg( 'moredotdotdot' ) );
 
                $s .= $sep . "\n</div>\n";