MediaWiki  REL1_21
SpecialPage.php
Go to the documentation of this file.
00001 <?php
00029 class SpecialPage {
00030 
00031         // The canonical name of this special page
00032         // Also used for the default <h1> heading, @see getDescription()
00033         protected $mName;
00034 
00035         // The local name of this special page
00036         private $mLocalName;
00037 
00038         // Minimum user level required to access this page, or "" for anyone.
00039         // Also used to categorise the pages in Special:Specialpages
00040         private $mRestriction;
00041 
00042         // Listed in Special:Specialpages?
00043         private $mListed;
00044 
00045         // Function name called by the default execute()
00046         private $mFunction;
00047 
00048         // File which needs to be included before the function above can be called
00049         private $mFile;
00050 
00051         // Whether or not this special page is being included from an article
00052         protected $mIncluding;
00053 
00054         // Whether the special page can be included in an article
00055         protected $mIncludable;
00056 
00061         protected $mContext;
00062 
00068         static function initList() {
00069                 wfDeprecated( __METHOD__, '1.18' );
00070                 // Noop
00071         }
00072 
00076         static function initAliasList() {
00077                 wfDeprecated( __METHOD__, '1.18' );
00078                 // Noop
00079         }
00080 
00089         static function resolveAlias( $alias ) {
00090                 wfDeprecated( __METHOD__, '1.18' );
00091                 list( $name, /*...*/ ) = SpecialPageFactory::resolveAlias( $alias );
00092                 return $name;
00093         }
00094 
00104         static function resolveAliasWithSubpage( $alias ) {
00105                 return SpecialPageFactory::resolveAlias( $alias );
00106         }
00107 
00115         static function setGroup( $page, $group ) {
00116                 wfDeprecated( __METHOD__, '1.18' );
00117                 SpecialPageFactory::setGroup( $page, $group );
00118         }
00119 
00127         static function getGroup( &$page ) {
00128                 wfDeprecated( __METHOD__, '1.18' );
00129                 return SpecialPageFactory::getGroup( $page );
00130         }
00131 
00140         static function removePage( $name ) {
00141                 wfDeprecated( __METHOD__, '1.18' );
00142                 unset( SpecialPageFactory::getList()->$name );
00143         }
00144 
00152         static function exists( $name ) {
00153                 wfDeprecated( __METHOD__, '1.18' );
00154                 return SpecialPageFactory::exists( $name );
00155         }
00156 
00164         static function getPage( $name ) {
00165                 wfDeprecated( __METHOD__, '1.18' );
00166                 return SpecialPageFactory::getPage( $name );
00167         }
00168 
00177         static function getPageByAlias( $alias ) {
00178                 wfDeprecated( __METHOD__, '1.18' );
00179                 return SpecialPageFactory::getPage( $alias );
00180         }
00181 
00191         static function getUsablePages( User $user = null ) {
00192                 wfDeprecated( __METHOD__, '1.18' );
00193                 return SpecialPageFactory::getUsablePages( $user );
00194         }
00195 
00202         static function getRegularPages() {
00203                 wfDeprecated( __METHOD__, '1.18' );
00204                 return SpecialPageFactory::getRegularPages();
00205         }
00206 
00214         static function getRestrictedPages() {
00215                 wfDeprecated( __METHOD__, '1.18' );
00216                 return SpecialPageFactory::getRestrictedPages();
00217         }
00218 
00233         public static function executePath( &$title, IContextSource &$context, $including = false ) {
00234                 wfDeprecated( __METHOD__, '1.18' );
00235                 return SpecialPageFactory::executePath( $title, $context, $including );
00236         }
00237 
00247         static function getLocalNameFor( $name, $subpage = false ) {
00248                 wfDeprecated( __METHOD__, '1.18' );
00249                 return SpecialPageFactory::getLocalNameFor( $name, $subpage );
00250         }
00251 
00261         public static function getTitleFor( $name, $subpage = false, $fragment = '' ) {
00262                 $name = SpecialPageFactory::getLocalNameFor( $name, $subpage );
00263                 if ( $name ) {
00264                         return Title::makeTitle( NS_SPECIAL, $name, $fragment );
00265                 } else {
00266                         throw new MWException( "Invalid special page name \"$name\"" );
00267                 }
00268         }
00269 
00277         public static function getSafeTitleFor( $name, $subpage = false ) {
00278                 $name = SpecialPageFactory::getLocalNameFor( $name, $subpage );
00279                 if ( $name ) {
00280                         return Title::makeTitleSafe( NS_SPECIAL, $name );
00281                 } else {
00282                         return null;
00283                 }
00284         }
00285 
00293         static function getTitleForAlias( $alias ) {
00294                 wfDeprecated( __METHOD__, '1.18' );
00295                 return SpecialPageFactory::getTitleForAlias( $alias );
00296         }
00297 
00315         public function __construct(
00316                 $name = '', $restriction = '', $listed = true,
00317                 $function = false, $file = 'default', $includable = false
00318         ) {
00319                 $this->init( $name, $restriction, $listed, $function, $file, $includable );
00320         }
00321 
00332         private function init( $name, $restriction, $listed, $function, $file, $includable ) {
00333                 $this->mName = $name;
00334                 $this->mRestriction = $restriction;
00335                 $this->mListed = $listed;
00336                 $this->mIncludable = $includable;
00337                 if ( !$function ) {
00338                         $this->mFunction = 'wfSpecial' . $name;
00339                 } else {
00340                         $this->mFunction = $function;
00341                 }
00342                 if ( $file === 'default' ) {
00343                         $this->mFile = __DIR__ . "/specials/Special$name.php";
00344                 } else {
00345                         $this->mFile = $file;
00346                 }
00347         }
00348 
00358         public function __call( $fName, $a ) {
00359                 // Deprecated messages now, remove in 1.19 or 1.20?
00360                 wfDeprecated( __METHOD__, '1.17' );
00361 
00362                 // Sometimes $fName is SpecialPage, sometimes it's specialpage. <3 PHP
00363                 if ( strtolower( $fName ) == 'specialpage' ) {
00364                         $name = isset( $a[0] ) ? $a[0] : '';
00365                         $restriction = isset( $a[1] ) ? $a[1] : '';
00366                         $listed = isset( $a[2] ) ? $a[2] : true;
00367                         $function = isset( $a[3] ) ? $a[3] : false;
00368                         $file = isset( $a[4] ) ? $a[4] : 'default';
00369                         $includable = isset( $a[5] ) ? $a[5] : false;
00370                         $this->init( $name, $restriction, $listed, $function, $file, $includable );
00371                 } else {
00372                         $className = get_class( $this );
00373                         throw new MWException( "Call to undefined method $className::$fName" );
00374                 }
00375         }
00376 
00381         function getName() {
00382                 return $this->mName;
00383         }
00384 
00389         function getRestriction() {
00390                 return $this->mRestriction;
00391         }
00392 
00400         function getFile() {
00401                 wfDeprecated( __METHOD__, '1.18' );
00402                 return $this->mFile;
00403         }
00404 
00405         // @todo FIXME: Decide which syntax to use for this, and stick to it
00411         function isListed() {
00412                 return $this->mListed;
00413         }
00420         function setListed( $listed ) {
00421                 return wfSetVar( $this->mListed, $listed );
00422         }
00429         function listed( $x = null ) {
00430                 return wfSetVar( $this->mListed, $x );
00431         }
00432 
00437         public function isIncludable() {
00438                 return $this->mIncludable;
00439         }
00440 
00448         function name( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mName, $x ); }
00449 
00457         function restriction( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mRestriction, $x ); }
00458 
00466         function func( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mFunction, $x ); }
00467 
00475         function file( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mFile, $x ); }
00476 
00484         function includable( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mIncludable, $x ); }
00485 
00491         function including( $x = null ) {
00492                 return wfSetVar( $this->mIncluding, $x );
00493         }
00494 
00498         function getLocalName() {
00499                 if ( !isset( $this->mLocalName ) ) {
00500                         $this->mLocalName = SpecialPageFactory::getLocalNameFor( $this->mName );
00501                 }
00502                 return $this->mLocalName;
00503         }
00504 
00513         public function isExpensive() {
00514                 return false;
00515         }
00516 
00526         public function isCached() {
00527                 return false;
00528         }
00529 
00537         public function isRestricted() {
00538                 // DWIM: If all anons can do something, then it is not restricted
00539                 return $this->mRestriction != '' && !User::groupHasPermission( '*', $this->mRestriction );
00540         }
00541 
00550         public function userCanExecute( User $user ) {
00551                 return $user->isAllowed( $this->mRestriction );
00552         }
00553 
00557         function displayRestrictionError() {
00558                 throw new PermissionsError( $this->mRestriction );
00559         }
00560 
00566         public function checkPermissions() {
00567                 if ( !$this->userCanExecute( $this->getUser() ) ) {
00568                         $this->displayRestrictionError();
00569                 }
00570         }
00571 
00578         public function checkReadOnly() {
00579                 if ( wfReadOnly() ) {
00580                         throw new ReadOnlyError;
00581                 }
00582         }
00583 
00587         function setHeaders() {
00588                 $out = $this->getOutput();
00589                 $out->setArticleRelated( false );
00590                 $out->setRobotPolicy( "noindex,nofollow" );
00591                 $out->setPageTitle( $this->getDescription() );
00592         }
00593 
00601         final public function run( $subPage ) {
00610                 wfRunHooks( 'SpecialPageBeforeExecute', array( $this, $subPage ) );
00611 
00612                 $this->beforeExecute( $subPage );
00613                 $this->execute( $subPage );
00614                 $this->afterExecute( $subPage );
00615 
00624                 wfRunHooks( 'SpecialPageAfterExecute', array( $this, $subPage ) );
00625         }
00626 
00634         protected function beforeExecute( $subPage ) {
00635                 // No-op
00636         }
00637 
00645         protected function afterExecute( $subPage ) {
00646                 // No-op
00647         }
00648 
00657         public function execute( $subPage ) {
00658                 $this->setHeaders();
00659                 $this->checkPermissions();
00660 
00661                 $func = $this->mFunction;
00662                 // only load file if the function does not exist
00663                 if ( !is_callable( $func ) && $this->mFile ) {
00664                         require_once( $this->mFile );
00665                 }
00666                 $this->outputHeader();
00667                 call_user_func( $func, $subPage, $this );
00668         }
00669 
00678         function outputHeader( $summaryMessageKey = '' ) {
00679                 global $wgContLang;
00680 
00681                 if ( $summaryMessageKey == '' ) {
00682                         $msg = $wgContLang->lc( $this->getName() ) . '-summary';
00683                 } else {
00684                         $msg = $summaryMessageKey;
00685                 }
00686                 if ( !$this->msg( $msg )->isDisabled() && !$this->including() ) {
00687                         $this->getOutput()->wrapWikiMsg(
00688                                 "<div class='mw-specialpage-summary'>\n$1\n</div>", $msg );
00689                 }
00690 
00691         }
00692 
00703         function getDescription() {
00704                 return $this->msg( strtolower( $this->mName ) )->text();
00705         }
00706 
00713         function getTitle( $subpage = false ) {
00714                 return self::getTitleFor( $this->mName, $subpage );
00715         }
00716 
00723         public function setContext( $context ) {
00724                 $this->mContext = $context;
00725         }
00726 
00733         public function getContext() {
00734                 if ( $this->mContext instanceof IContextSource ) {
00735                         return $this->mContext;
00736                 } else {
00737                         wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" );
00738                         return RequestContext::getMain();
00739                 }
00740         }
00741 
00748         public function getRequest() {
00749                 return $this->getContext()->getRequest();
00750         }
00751 
00758         public function getOutput() {
00759                 return $this->getContext()->getOutput();
00760         }
00761 
00768         public function getUser() {
00769                 return $this->getContext()->getUser();
00770         }
00771 
00778         public function getSkin() {
00779                 return $this->getContext()->getSkin();
00780         }
00781 
00789         public function getLang() {
00790                 wfDeprecated( __METHOD__, '1.19' );
00791                 return $this->getLanguage();
00792         }
00793 
00800         public function getLanguage() {
00801                 return $this->getContext()->getLanguage();
00802         }
00803 
00810         public function getFullTitle() {
00811                 return $this->getContext()->getTitle();
00812         }
00813 
00820         public function msg( /* $args */ ) {
00821                 // Note: can't use func_get_args() directly as second or later item in
00822                 // a parameter list until PHP 5.3 or you get a fatal error.
00823                 // Works fine as the first parameter, which appears elsewhere in the
00824                 // code base. Sighhhh.
00825                 $args = func_get_args();
00826                 $message = call_user_func_array( array( $this->getContext(), 'msg' ), $args );
00827                 // RequestContext passes context to wfMessage, and the language is set from
00828                 // the context, but setting the language for Message class removes the
00829                 // interface message status, which breaks for example usernameless gender
00830                 // invocations. Restore the flag when not including special page in content.
00831                 if ( $this->including() ) {
00832                         $message->setInterfaceMessageFlag( false );
00833                 }
00834                 return $message;
00835         }
00836 
00842         protected function addFeedLinks( $params ) {
00843                 global $wgFeedClasses;
00844 
00845                 $feedTemplate = wfScript( 'api' ) . '?';
00846 
00847                 foreach ( $wgFeedClasses as $format => $class ) {
00848                         $theseParams = $params + array( 'feedformat' => $format );
00849                         $url = $feedTemplate . wfArrayToCgi( $theseParams );
00850                         $this->getOutput()->addFeedLink( $format, $url );
00851                 }
00852         }
00853 
00862         public function getFinalGroupName() {
00863                 global $wgSpecialPageGroups;
00864                 $name = $this->getName();
00865                 $group = '-';
00866 
00867                 // Allow overbidding the group from the wiki side
00868                 $msg = $this->msg( 'specialpages-specialpagegroup-' . strtolower( $name ) )->inContentLanguage();
00869                 if ( !$msg->isBlank() ) {
00870                         $group = $msg->text();
00871                 } else {
00872                         // Than use the group from this object
00873                         $group = $this->getGroupName();
00874 
00875                         // Group '-' is used as default to have the chance to determine,
00876                         // if the special pages overrides this method,
00877                         // if not overridden, $wgSpecialPageGroups is checked for b/c
00878                         if ( $group === '-' && isset( $wgSpecialPageGroups[$name] ) ) {
00879                                 $group = $wgSpecialPageGroups[$name];
00880                         }
00881                 }
00882 
00883                 // never give '-' back, change to 'other'
00884                 if ( $group === '-' ) {
00885                         $group = 'other';
00886                 }
00887 
00888                 return $group;
00889         }
00890 
00899         protected function getGroupName() {
00900                 // '-' used here to determine, if this group is overridden or has a hardcoded 'other'
00901                 // Needed for b/c in getFinalGroupName
00902                 return '-';
00903         }
00904 }
00905 
00911 abstract class FormSpecialPage extends SpecialPage {
00912 
00917         abstract protected function getFormFields();
00918 
00923         protected function preText() { return ''; }
00924         protected function postText() { return ''; }
00925 
00930         protected function alterForm( HTMLForm $form ) {}
00931 
00938         protected function getMessagePrefix() {
00939                 return strtolower( $this->getName() );
00940         }
00941 
00946         protected function getForm() {
00947                 $this->fields = $this->getFormFields();
00948 
00949                 $form = new HTMLForm( $this->fields, $this->getContext(), $this->getMessagePrefix() );
00950                 $form->setSubmitCallback( array( $this, 'onSubmit' ) );
00951                 $form->setWrapperLegend( $this->msg( $this->getMessagePrefix() . '-legend' ) );
00952                 $form->addHeaderText(
00953                         $this->msg( $this->getMessagePrefix() . '-text' )->parseAsBlock() );
00954 
00955                 // Retain query parameters (uselang etc)
00956                 $params = array_diff_key(
00957                         $this->getRequest()->getQueryValues(), array( 'title' => null ) );
00958                 $form->addHiddenField( 'redirectparams', wfArrayToCgi( $params ) );
00959 
00960                 $form->addPreText( $this->preText() );
00961                 $form->addPostText( $this->postText() );
00962                 $this->alterForm( $form );
00963 
00964                 // Give hooks a chance to alter the form, adding extra fields or text etc
00965                 wfRunHooks( "Special{$this->getName()}BeforeFormDisplay", array( &$form ) );
00966 
00967                 return $form;
00968         }
00969 
00975         abstract public function onSubmit( array $data );
00976 
00981         abstract public function onSuccess();
00982 
00988         public function execute( $par ) {
00989                 $this->setParameter( $par );
00990                 $this->setHeaders();
00991 
00992                 // This will throw exceptions if there's a problem
00993                 $this->checkExecutePermissions( $this->getUser() );
00994 
00995                 $form = $this->getForm();
00996                 if ( $form->show() ) {
00997                         $this->onSuccess();
00998                 }
00999         }
01000 
01005         protected function setParameter( $par ) {}
01006 
01014         protected function checkExecutePermissions( User $user ) {
01015                 $this->checkPermissions();
01016 
01017                 if ( $this->requiresUnblock() && $user->isBlocked() ) {
01018                         $block = $user->getBlock();
01019                         throw new UserBlockedError( $block );
01020                 }
01021 
01022                 if ( $this->requiresWrite() ) {
01023                         $this->checkReadOnly();
01024                 }
01025 
01026                 return true;
01027         }
01028 
01033         public function requiresWrite() {
01034                 return true;
01035         }
01036 
01041         public function requiresUnblock() {
01042                 return true;
01043         }
01044 }
01045 
01050 class UnlistedSpecialPage extends SpecialPage {
01051         function __construct( $name, $restriction = '', $function = false, $file = 'default' ) {
01052                 parent::__construct( $name, $restriction, false, $function, $file );
01053         }
01054 
01055         public function isListed() {
01056                 return false;
01057         }
01058 }
01059 
01064 class IncludableSpecialPage extends SpecialPage {
01065         function __construct(
01066                 $name, $restriction = '', $listed = true, $function = false, $file = 'default'
01067         ) {
01068                 parent::__construct( $name, $restriction, $listed, $function, $file, true );
01069         }
01070 
01071         public function isIncludable() {
01072                 return true;
01073         }
01074 }
01075 
01080 abstract class RedirectSpecialPage extends UnlistedSpecialPage {
01081 
01082         // Query parameters that can be passed through redirects
01083         protected $mAllowedRedirectParams = array();
01084 
01085         // Query parameters added by redirects
01086         protected $mAddedRedirectParams = array();
01087 
01088         public function execute( $par ) {
01089                 $redirect = $this->getRedirect( $par );
01090                 $query = $this->getRedirectQuery();
01091                 // Redirect to a page title with possible query parameters
01092                 if ( $redirect instanceof Title ) {
01093                         $url = $redirect->getFullUrl( $query );
01094                         $this->getOutput()->redirect( $url );
01095                         return $redirect;
01096                 // Redirect to index.php with query parameters
01097                 } elseif ( $redirect === true ) {
01098                         global $wgScript;
01099                         $url = $wgScript . '?' . wfArrayToCgi( $query );
01100                         $this->getOutput()->redirect( $url );
01101                         return $redirect;
01102                 } else {
01103                         $class = __CLASS__;
01104                         throw new MWException( "RedirectSpecialPage $class doesn't redirect!" );
01105                 }
01106         }
01107 
01115         abstract public function getRedirect( $par );
01116 
01123         public function getRedirectQuery() {
01124                 $params = array();
01125 
01126                 foreach ( $this->mAllowedRedirectParams as $arg ) {
01127                         if ( $this->getRequest()->getVal( $arg, null ) !== null ) {
01128                                 $params[$arg] = $this->getRequest()->getVal( $arg );
01129                         }
01130                 }
01131 
01132                 foreach ( $this->mAddedRedirectParams as $arg => $val ) {
01133                         $params[$arg] = $val;
01134                 }
01135 
01136                 return count( $params )
01137                         ? $params
01138                         : false;
01139         }
01140 }
01141 
01142 abstract class SpecialRedirectToSpecial extends RedirectSpecialPage {
01143         var $redirName, $redirSubpage;
01144 
01145         function __construct(
01146                 $name, $redirName, $redirSubpage = false,
01147                 $allowedRedirectParams = array(), $addedRedirectParams = array()
01148         ) {
01149                 parent::__construct( $name );
01150                 $this->redirName = $redirName;
01151                 $this->redirSubpage = $redirSubpage;
01152                 $this->mAllowedRedirectParams = $allowedRedirectParams;
01153                 $this->mAddedRedirectParams = $addedRedirectParams;
01154         }
01155 
01156         public function getRedirect( $subpage ) {
01157                 if ( $this->redirSubpage === false ) {
01158                         return SpecialPage::getTitleFor( $this->redirName, $subpage );
01159                 } else {
01160                         return SpecialPage::getTitleFor( $this->redirName, $this->redirSubpage );
01161                 }
01162         }
01163 }
01164 
01168 class SpecialListAdmins extends SpecialRedirectToSpecial {
01169         function __construct() {
01170                 parent::__construct( 'Listadmins', 'Listusers', 'sysop' );
01171         }
01172 }
01173 
01177 class SpecialListBots extends SpecialRedirectToSpecial {
01178         function __construct() {
01179                 parent::__construct( 'Listbots', 'Listusers', 'bot' );
01180         }
01181 }
01182 
01187 class SpecialCreateAccount extends SpecialRedirectToSpecial {
01188         function __construct() {
01189                 parent::__construct( 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) );
01190         }
01191 }
01266 abstract class RedirectSpecialArticle extends RedirectSpecialPage {
01267         function __construct( $name ) {
01268                 parent::__construct( $name );
01269                 $redirectParams = array(
01270                         'action',
01271                         'redirect', 'rdfrom',
01272                         # Options for preloaded edits
01273                         'preload', 'editintro', 'preloadtitle', 'summary',
01274                         # Options for overriding user settings
01275                         'preview', 'internaledit', 'externaledit', 'mode',
01276                         # Options for history/diffs
01277                         'section', 'oldid', 'diff', 'dir',
01278                         'limit', 'offset', 'feed',
01279                         # Misc options
01280                         'redlink', 'debug',
01281                         # Options for action=raw; missing ctype can break JS or CSS in some browsers
01282                         'ctype', 'maxage', 'smaxage',
01283                 );
01284 
01285                 wfRunHooks( "RedirectSpecialArticleRedirectParams", array( &$redirectParams ) );
01286                 $this->mAllowedRedirectParams = $redirectParams;
01287         }
01288 }
01289 
01294 class SpecialMypage extends RedirectSpecialArticle {
01295         function __construct() {
01296                 parent::__construct( 'Mypage' );
01297         }
01298 
01299         function getRedirect( $subpage ) {
01300                 if ( strval( $subpage ) !== '' ) {
01301                         return Title::makeTitle( NS_USER, $this->getUser()->getName() . '/' . $subpage );
01302                 } else {
01303                         return Title::makeTitle( NS_USER, $this->getUser()->getName() );
01304                 }
01305         }
01306 }
01307 
01312 class SpecialMytalk extends RedirectSpecialArticle {
01313         function __construct() {
01314                 parent::__construct( 'Mytalk' );
01315         }
01316 
01317         function getRedirect( $subpage ) {
01318                 if ( strval( $subpage ) !== '' ) {
01319                         return Title::makeTitle( NS_USER_TALK, $this->getUser()->getName() . '/' . $subpage );
01320                 } else {
01321                         return Title::makeTitle( NS_USER_TALK, $this->getUser()->getName() );
01322                 }
01323         }
01324 }
01325 
01330 class SpecialMycontributions extends RedirectSpecialPage {
01331         function __construct() {
01332                 parent::__construct( 'Mycontributions' );
01333                 $this->mAllowedRedirectParams = array( 'limit', 'namespace', 'tagfilter',
01334                         'offset', 'dir', 'year', 'month', 'feed' );
01335         }
01336 
01337         function getRedirect( $subpage ) {
01338                 return SpecialPage::getTitleFor( 'Contributions', $this->getUser()->getName() );
01339         }
01340 }
01341 
01345 class SpecialMyuploads extends RedirectSpecialPage {
01346         function __construct() {
01347                 parent::__construct( 'Myuploads' );
01348                 $this->mAllowedRedirectParams = array( 'limit' );
01349         }
01350 
01351         function getRedirect( $subpage ) {
01352                 return SpecialPage::getTitleFor( 'Listfiles', $this->getUser()->getName() );
01353         }
01354 }
01355 
01359 class SpecialPermanentLink extends RedirectSpecialPage {
01360         function __construct() {
01361                 parent::__construct( 'PermanentLink' );
01362                 $this->mAllowedRedirectParams = array();
01363         }
01364 
01365         function getRedirect( $subpage ) {
01366                 $subpage = intval( $subpage );
01367                 if ( $subpage === 0 ) {
01368                         # throw an error page when no subpage was given
01369                         throw new ErrorPageError( 'nopagetitle', 'nopagetext' );
01370                 }
01371                 $this->mAddedRedirectParams['oldid'] = $subpage;
01372                 return true;
01373         }
01374 }