MediaWiki  REL1_19
SpecialPage.php
Go to the documentation of this file.
00001 <?php
00030 class SpecialPage {
00031 
00032         // The canonical name of this special page
00033         // Also used for the default <h1> heading, @see getDescription()
00034         protected $mName;
00035 
00036         // The local name of this special page
00037         private $mLocalName;
00038 
00039         // Minimum user level required to access this page, or "" for anyone.
00040         // Also used to categorise the pages in Special:Specialpages
00041         private $mRestriction;
00042 
00043         // Listed in Special:Specialpages?
00044         private $mListed;
00045 
00046         // Function name called by the default execute()
00047         private $mFunction;
00048 
00049         // File which needs to be included before the function above can be called
00050         private $mFile;
00051 
00052         // Whether or not this special page is being included from an article
00053         protected $mIncluding;
00054 
00055         // Whether the special page can be included in an article
00056         protected $mIncludable;
00057 
00062         protected $mContext;
00063 
00069         static function initList() {
00070                 wfDeprecated( __METHOD__, '1.18' );
00071                 // Noop
00072         }
00073 
00077         static function initAliasList() {
00078                 wfDeprecated( __METHOD__, '1.18' );
00079                 // Noop
00080         }
00081 
00090         static function resolveAlias( $alias ) {
00091                 wfDeprecated( __METHOD__, '1.18' );
00092                 list( $name, /*...*/ ) = SpecialPageFactory::resolveAlias( $alias );
00093                 return $name;
00094         }
00095 
00105         static function resolveAliasWithSubpage( $alias ) {
00106                 return SpecialPageFactory::resolveAlias( $alias );
00107         }
00108 
00117         static function addPage( &$page ) {
00118                 wfDeprecated( __METHOD__, '1.7' );
00119                 SpecialPageFactory::getList()->{$page->mName} = $page;
00120         }
00121 
00130         static function setGroup( $page, $group ) {
00131                 wfDeprecated( __METHOD__, '1.18' );
00132                 return SpecialPageFactory::setGroup( $page, $group );
00133         }
00134 
00142         static function getGroup( &$page ) {
00143                 wfDeprecated( __METHOD__, '1.18' );
00144                 return SpecialPageFactory::getGroup( $page );
00145         }
00146 
00155         static function removePage( $name ) {
00156                 wfDeprecated( __METHOD__, '1.18' );
00157                 unset( SpecialPageFactory::getList()->$name );
00158         }
00159 
00167         static function exists( $name ) {
00168                 wfDeprecated( __METHOD__, '1.18' );
00169                 return SpecialPageFactory::exists( $name );
00170         }
00171 
00179         static function getPage( $name ) {
00180                 wfDeprecated( __METHOD__, '1.18' );
00181                 return SpecialPageFactory::getPage( $name );
00182         }
00183 
00192         static function getPageByAlias( $alias ) {
00193                 wfDeprecated( __METHOD__, '1.18' );
00194                 return SpecialPageFactory::getPage( $alias );
00195         }
00196 
00206         static function getUsablePages( User $user = null ) {
00207                 wfDeprecated( __METHOD__, '1.18' );
00208                 return SpecialPageFactory::getUsablePages( $user );
00209         }
00210 
00217         static function getRegularPages() {
00218                 wfDeprecated( __METHOD__, '1.18' );
00219                 return SpecialPageFactory::getRegularPages();
00220         }
00221 
00229         static function getRestrictedPages() {
00230                 wfDeprecated( __METHOD__, '1.18' );
00231                 return SpecialPageFactory::getRestrictedPages();
00232         }
00233 
00248         public static function executePath( &$title, IContextSource &$context, $including = false ) {
00249                 wfDeprecated( __METHOD__, '1.18' );
00250                 return SpecialPageFactory::executePath( $title, $context, $including );
00251         }
00252 
00262         static function getLocalNameFor( $name, $subpage = false ) {
00263                 wfDeprecated( __METHOD__, '1.18' );
00264                 return SpecialPageFactory::getLocalNameFor( $name, $subpage );
00265         }
00266 
00274         public static function getTitleFor( $name, $subpage = false ) {
00275                 $name = SpecialPageFactory::getLocalNameFor( $name, $subpage );
00276                 if ( $name ) {
00277                         return Title::makeTitle( NS_SPECIAL, $name );
00278                 } else {
00279                         throw new MWException( "Invalid special page name \"$name\"" );
00280                 }
00281         }
00282 
00290         public static function getSafeTitleFor( $name, $subpage = false ) {
00291                 $name = SpecialPageFactory::getLocalNameFor( $name, $subpage );
00292                 if ( $name ) {
00293                         return Title::makeTitleSafe( NS_SPECIAL, $name );
00294                 } else {
00295                         return null;
00296                 }
00297         }
00298 
00306         static function getTitleForAlias( $alias ) {
00307                 wfDeprecated( __METHOD__, '1.18' );
00308                 return SpecialPageFactory::getTitleForAlias( $alias );
00309         }
00310 
00328         public function __construct(
00329                 $name = '', $restriction = '', $listed = true,
00330                 $function = false, $file = 'default', $includable = false
00331         ) {
00332                 $this->init( $name, $restriction, $listed, $function, $file, $includable );
00333         }
00334 
00345         private function init( $name, $restriction, $listed, $function, $file, $includable ) {
00346                 $this->mName = $name;
00347                 $this->mRestriction = $restriction;
00348                 $this->mListed = $listed;
00349                 $this->mIncludable = $includable;
00350                 if ( !$function ) {
00351                         $this->mFunction = 'wfSpecial' . $name;
00352                 } else {
00353                         $this->mFunction = $function;
00354                 }
00355                 if ( $file === 'default' ) {
00356                         $this->mFile = dirname( __FILE__ ) . "/specials/Special$name.php";
00357                 } else {
00358                         $this->mFile = $file;
00359                 }
00360         }
00361 
00370         public function __call( $fName, $a ) {
00371                 // Deprecated messages now, remove in 1.19 or 1.20?
00372                 wfDeprecated( __METHOD__, '1.17' );
00373 
00374                 // Sometimes $fName is SpecialPage, sometimes it's specialpage. <3 PHP
00375                 if ( strtolower( $fName ) == 'specialpage' ) {
00376                         $name = isset( $a[0] ) ? $a[0] : '';
00377                         $restriction = isset( $a[1] ) ? $a[1] : '';
00378                         $listed = isset( $a[2] ) ? $a[2] : true;
00379                         $function = isset( $a[3] ) ? $a[3] : false;
00380                         $file = isset( $a[4] ) ? $a[4] : 'default';
00381                         $includable = isset( $a[5] ) ? $a[5] : false;
00382                         $this->init( $name, $restriction, $listed, $function, $file, $includable );
00383                 } else {
00384                         $className = get_class( $this );
00385                         throw new MWException( "Call to undefined method $className::$fName" );
00386                 }
00387         }
00388 
00393         function getName() {
00394                 return $this->mName;
00395         }
00396 
00401         function getRestriction() {
00402                 return $this->mRestriction;
00403         }
00404 
00412         function getFile() {
00413                 wfDeprecated( __METHOD__, '1.18' );
00414                 return $this->mFile;
00415         }
00416 
00417         // @todo FIXME: Decide which syntax to use for this, and stick to it
00423         function isListed() {
00424                 return $this->mListed;
00425         }
00432         function setListed( $listed ) {
00433                 return wfSetVar( $this->mListed, $listed );
00434         }
00441         function listed( $x = null ) {
00442                 return wfSetVar( $this->mListed, $x );
00443         }
00444 
00449         public function isIncludable() {
00450                 return $this->mIncludable;
00451         }
00452 
00460         function name( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mName, $x ); }
00461 
00469         function restriction( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mRestriction, $x ); }
00470 
00478         function func( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mFunction, $x ); }
00479 
00487         function file( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mFile, $x ); }
00488 
00496         function includable( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mIncludable, $x ); }
00497 
00503         function including( $x = null ) {
00504                 return wfSetVar( $this->mIncluding, $x );
00505         }
00506 
00510         function getLocalName() {
00511                 if ( !isset( $this->mLocalName ) ) {
00512                         $this->mLocalName = SpecialPageFactory::getLocalNameFor( $this->mName );
00513                 }
00514                 return $this->mLocalName;
00515         }
00516 
00525         public function isExpensive() {
00526                 return false;
00527         }
00528 
00536         public function isRestricted() {
00537                 global $wgGroupPermissions;
00538                 // DWIM: If all anons can do something, then it is not restricted
00539                 return $this->mRestriction != '' && empty( $wgGroupPermissions['*'][$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 
00602         function execute( $par ) {
00603                 $this->setHeaders();
00604                 $this->checkPermissions();
00605 
00606                 $func = $this->mFunction;
00607                 // only load file if the function does not exist
00608                 if ( !is_callable( $func ) && $this->mFile ) {
00609                         require_once( $this->mFile );
00610                 }
00611                 $this->outputHeader();
00612                 call_user_func( $func, $par, $this );
00613         }
00614 
00623         function outputHeader( $summaryMessageKey = '' ) {
00624                 global $wgContLang;
00625 
00626                 if ( $summaryMessageKey == '' ) {
00627                         $msg = $wgContLang->lc( $this->getName() ) . '-summary';
00628                 } else {
00629                         $msg = $summaryMessageKey;
00630                 }
00631                 if ( !$this->msg( $msg )->isBlank() && !$this->including() ) {
00632                         $this->getOutput()->wrapWikiMsg(
00633                                 "<div class='mw-specialpage-summary'>\n$1\n</div>", $msg );
00634                 }
00635 
00636         }
00637 
00648         function getDescription() {
00649                 return $this->msg( strtolower( $this->mName ) )->text();
00650         }
00651 
00658         function getTitle( $subpage = false ) {
00659                 return self::getTitleFor( $this->mName, $subpage );
00660         }
00661 
00668         public function setContext( $context ) {
00669                 $this->mContext = $context;
00670         }
00671 
00678         public function getContext() {
00679                 if ( $this->mContext instanceof IContextSource ) {
00680                         return $this->mContext;
00681                 } else {
00682                         wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" );
00683                         return RequestContext::getMain();
00684                 }
00685         }
00686 
00693         public function getRequest() {
00694                 return $this->getContext()->getRequest();
00695         }
00696 
00703         public function getOutput() {
00704                 return $this->getContext()->getOutput();
00705         }
00706 
00713         public function getUser() {
00714                 return $this->getContext()->getUser();
00715         }
00716 
00723         public function getSkin() {
00724                 return $this->getContext()->getSkin();
00725         }
00726 
00734         public function getLang() {
00735                 wfDeprecated( __METHOD__, '1.19' );
00736                 return $this->getLanguage();
00737         }
00738 
00745         public function getLanguage() {
00746                 return $this->getContext()->getLanguage();
00747         }
00748 
00755         public function getFullTitle() {
00756                 return $this->getContext()->getTitle();
00757         }
00758 
00765         public function msg( /* $args */ ) {
00766                 // Note: can't use func_get_args() directly as second or later item in
00767                 // a parameter list until PHP 5.3 or you get a fatal error.
00768                 // Works fine as the first parameter, which appears elsewhere in the
00769                 // code base. Sighhhh.
00770                 $args = func_get_args();
00771                 return call_user_func_array( array( $this->getContext(), 'msg' ), $args );
00772         }
00773 
00779         protected function addFeedLinks( $params ) {
00780                 global $wgFeedClasses;
00781 
00782                 $feedTemplate = wfScript( 'api' ) . '?';
00783 
00784                 foreach ( $wgFeedClasses as $format => $class ) {
00785                         $theseParams = $params + array( 'feedformat' => $format );
00786                         $url = $feedTemplate . wfArrayToCGI( $theseParams );
00787                         $this->getOutput()->addFeedLink( $format, $url );
00788                 }
00789         }
00790 }
00791 
00797 abstract class FormSpecialPage extends SpecialPage {
00798 
00803         protected abstract function getFormFields();
00804 
00809         protected function preText() { return ''; }
00810         protected function postText() { return ''; }
00811 
00816         protected function alterForm( HTMLForm $form ) {}
00817 
00822         protected function getForm() {
00823                 $this->fields = $this->getFormFields();
00824 
00825                 $form = new HTMLForm( $this->fields, $this->getContext() );
00826                 $form->setSubmitCallback( array( $this, 'onSubmit' ) );
00827                 $form->setWrapperLegend( $this->msg( strtolower( $this->getName() ) . '-legend' ) );
00828                 $form->addHeaderText(
00829                         $this->msg( strtolower( $this->getName() ) . '-text' )->parseAsBlock() );
00830 
00831                 // Retain query parameters (uselang etc)
00832                 $params = array_diff_key(
00833                         $this->getRequest()->getQueryValues(), array( 'title' => null ) );
00834                 $form->addHiddenField( 'redirectparams', wfArrayToCGI( $params ) );
00835 
00836                 $form->addPreText( $this->preText() );
00837                 $form->addPostText( $this->postText() );
00838                 $this->alterForm( $form );
00839 
00840                 // Give hooks a chance to alter the form, adding extra fields or text etc
00841                 wfRunHooks( "Special{$this->getName()}BeforeFormDisplay", array( &$form ) );
00842 
00843                 return $form;
00844         }
00845 
00851         public abstract function onSubmit( array $data );
00852 
00857         public abstract function onSuccess();
00858 
00864         public function execute( $par ) {
00865                 $this->setParameter( $par );
00866                 $this->setHeaders();
00867 
00868                 // This will throw exceptions if there's a problem
00869                 $this->checkExecutePermissions( $this->getUser() );
00870 
00871                 $form = $this->getForm();
00872                 if ( $form->show() ) {
00873                         $this->onSuccess();
00874                 }
00875         }
00876 
00881         protected function setParameter( $par ) {}
00882 
00890         protected function checkExecutePermissions( User $user ) {
00891                 $this->checkPermissions();
00892 
00893                 if ( $this->requiresUnblock() && $user->isBlocked() ) {
00894                         $block = $user->mBlock;
00895                         throw new UserBlockedError( $block );
00896                 }
00897 
00898                 if ( $this->requiresWrite() ) {
00899                         $this->checkReadOnly();
00900                 }
00901 
00902                 return true;
00903         }
00904 
00909         public function requiresWrite() {
00910                 return true;
00911         }
00912 
00917         public function requiresUnblock() {
00918                 return true;
00919         }
00920 }
00921 
00926 class UnlistedSpecialPage extends SpecialPage {
00927         function __construct( $name, $restriction = '', $function = false, $file = 'default' ) {
00928                 parent::__construct( $name, $restriction, false, $function, $file );
00929         }
00930 
00931         public function isListed() {
00932                 return false;
00933         }
00934 }
00935 
00940 class IncludableSpecialPage extends SpecialPage {
00941         function __construct(
00942                 $name, $restriction = '', $listed = true, $function = false, $file = 'default'
00943         ) {
00944                 parent::__construct( $name, $restriction, $listed, $function, $file, true );
00945         }
00946 
00947         public function isIncludable() {
00948                 return true;
00949         }
00950 }
00951 
00956 abstract class RedirectSpecialPage extends UnlistedSpecialPage {
00957 
00958         // Query parameters that can be passed through redirects
00959         protected $mAllowedRedirectParams = array();
00960 
00961         // Query parameteres added by redirects
00962         protected $mAddedRedirectParams = array();
00963 
00964         public function execute( $par ) {
00965                 $redirect = $this->getRedirect( $par );
00966                 $query = $this->getRedirectQuery();
00967                 // Redirect to a page title with possible query parameters
00968                 if ( $redirect instanceof Title ) {
00969                         $url = $redirect->getFullUrl( $query );
00970                         $this->getOutput()->redirect( $url );
00971                         wfProfileOut( __METHOD__ );
00972                         return $redirect;
00973                 // Redirect to index.php with query parameters
00974                 } elseif ( $redirect === true ) {
00975                         global $wgScript;
00976                         $url = $wgScript . '?' . wfArrayToCGI( $query );
00977                         $this->getOutput()->redirect( $url );
00978                         wfProfileOut( __METHOD__ );
00979                         return $redirect;
00980                 } else {
00981                         $class = __CLASS__;
00982                         throw new MWException( "RedirectSpecialPage $class doesn't redirect!" );
00983                 }
00984         }
00985 
00993         abstract public function getRedirect( $par );
00994 
01001         public function getRedirectQuery() {
01002                 $params = array();
01003 
01004                 foreach ( $this->mAllowedRedirectParams as $arg ) {
01005                         if ( $this->getRequest()->getVal( $arg, null ) !== null ) {
01006                                 $params[$arg] = $this->getRequest()->getVal( $arg );
01007                         }
01008                 }
01009 
01010                 foreach ( $this->mAddedRedirectParams as $arg => $val ) {
01011                         $params[$arg] = $val;
01012                 }
01013 
01014                 return count( $params )
01015                         ? $params
01016                         : false;
01017         }
01018 }
01019 
01020 abstract class SpecialRedirectToSpecial extends RedirectSpecialPage {
01021         var $redirName, $redirSubpage;
01022 
01023         function __construct(
01024                 $name, $redirName, $redirSubpage = false,
01025                 $allowedRedirectParams = array(), $addedRedirectParams = array()
01026         ) {
01027                 parent::__construct( $name );
01028                 $this->redirName = $redirName;
01029                 $this->redirSubpage = $redirSubpage;
01030                 $this->mAllowedRedirectParams = $allowedRedirectParams;
01031                 $this->mAddedRedirectParams = $addedRedirectParams;
01032         }
01033 
01034         public function getRedirect( $subpage ) {
01035                 if ( $this->redirSubpage === false ) {
01036                         return SpecialPage::getTitleFor( $this->redirName, $subpage );
01037                 } else {
01038                         return SpecialPage::getTitleFor( $this->redirName, $this->redirSubpage );
01039                 }
01040         }
01041 }
01042 
01046 class SpecialListAdmins extends SpecialRedirectToSpecial {
01047         function __construct() {
01048                 parent::__construct( 'Listadmins', 'Listusers', 'sysop' );
01049         }
01050 }
01051 
01055 class SpecialListBots extends SpecialRedirectToSpecial {
01056         function __construct() {
01057                 parent::__construct( 'Listbots', 'Listusers', 'bot' );
01058         }
01059 }
01060 
01065 class SpecialCreateAccount extends SpecialRedirectToSpecial {
01066         function __construct() {
01067                 parent::__construct( 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) );
01068         }
01069 }
01082 class SpecialMypage extends RedirectSpecialPage {
01083         function __construct() {
01084                 parent::__construct( 'Mypage' );
01085                 $this->mAllowedRedirectParams = array( 'action' , 'preload' , 'editintro',
01086                         'section', 'oldid', 'diff', 'dir',
01087                         // Options for action=raw; missing ctype can break JS or CSS in some browsers
01088                         'ctype', 'maxage', 'smaxage' );
01089         }
01090 
01091         function getRedirect( $subpage ) {
01092                 if ( strval( $subpage ) !== '' ) {
01093                         return Title::makeTitle( NS_USER, $this->getUser()->getName() . '/' . $subpage );
01094                 } else {
01095                         return Title::makeTitle( NS_USER, $this->getUser()->getName() );
01096                 }
01097         }
01098 }
01099 
01104 class SpecialMytalk extends RedirectSpecialPage {
01105         function __construct() {
01106                 parent::__construct( 'Mytalk' );
01107                 $this->mAllowedRedirectParams = array( 'action' , 'preload' , 'editintro',
01108                         'section', 'oldid', 'diff', 'dir' );
01109         }
01110 
01111         function getRedirect( $subpage ) {
01112                 if ( strval( $subpage ) !== '' ) {
01113                         return Title::makeTitle( NS_USER_TALK, $this->getUser()->getName() . '/' . $subpage );
01114                 } else {
01115                         return Title::makeTitle( NS_USER_TALK, $this->getUser()->getName() );
01116                 }
01117         }
01118 }
01119 
01124 class SpecialMycontributions extends RedirectSpecialPage {
01125         function __construct() {
01126                 parent::__construct(  'Mycontributions' );
01127                 $this->mAllowedRedirectParams = array( 'limit', 'namespace', 'tagfilter',
01128                         'offset', 'dir', 'year', 'month', 'feed' );
01129         }
01130 
01131         function getRedirect( $subpage ) {
01132                 return SpecialPage::getTitleFor( 'Contributions', $this->getUser()->getName() );
01133         }
01134 }
01135 
01139 class SpecialMyuploads extends RedirectSpecialPage {
01140         function __construct() {
01141                 parent::__construct( 'Myuploads' );
01142                 $this->mAllowedRedirectParams = array( 'limit' );
01143         }
01144 
01145         function getRedirect( $subpage ) {
01146                 return SpecialPage::getTitleFor( 'Listfiles', $this->getUser()->getName() );
01147         }
01148 }
01149 
01153 class SpecialPermanentLink extends RedirectSpecialPage {
01154         function __construct() {
01155                 parent::__construct( 'PermanentLink' );
01156                 $this->mAllowedRedirectParams = array();
01157         }
01158 
01159         function getRedirect( $subpage ) {
01160                 $subpage = intval( $subpage );
01161                 if ( $subpage === 0 ) {
01162                         # throw an error page when no subpage was given
01163                         throw new ErrorPageError( 'nopagetitle', 'nopagetext' );
01164                 }
01165                 $this->mAddedRedirectParams['oldid'] = $subpage;
01166                 return true;
01167         }
01168 }