MediaWiki
master
|
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 00260 public static function getTitleFor( $name, $subpage = false ) { 00261 $name = SpecialPageFactory::getLocalNameFor( $name, $subpage ); 00262 if ( $name ) { 00263 return Title::makeTitle( NS_SPECIAL, $name ); 00264 } else { 00265 throw new MWException( "Invalid special page name \"$name\"" ); 00266 } 00267 } 00268 00276 public static function getSafeTitleFor( $name, $subpage = false ) { 00277 $name = SpecialPageFactory::getLocalNameFor( $name, $subpage ); 00278 if ( $name ) { 00279 return Title::makeTitleSafe( NS_SPECIAL, $name ); 00280 } else { 00281 return null; 00282 } 00283 } 00284 00292 static function getTitleForAlias( $alias ) { 00293 wfDeprecated( __METHOD__, '1.18' ); 00294 return SpecialPageFactory::getTitleForAlias( $alias ); 00295 } 00296 00314 public function __construct( 00315 $name = '', $restriction = '', $listed = true, 00316 $function = false, $file = 'default', $includable = false 00317 ) { 00318 $this->init( $name, $restriction, $listed, $function, $file, $includable ); 00319 } 00320 00331 private function init( $name, $restriction, $listed, $function, $file, $includable ) { 00332 $this->mName = $name; 00333 $this->mRestriction = $restriction; 00334 $this->mListed = $listed; 00335 $this->mIncludable = $includable; 00336 if ( !$function ) { 00337 $this->mFunction = 'wfSpecial' . $name; 00338 } else { 00339 $this->mFunction = $function; 00340 } 00341 if ( $file === 'default' ) { 00342 $this->mFile = __DIR__ . "/specials/Special$name.php"; 00343 } else { 00344 $this->mFile = $file; 00345 } 00346 } 00347 00357 public function __call( $fName, $a ) { 00358 // Deprecated messages now, remove in 1.19 or 1.20? 00359 wfDeprecated( __METHOD__, '1.17' ); 00360 00361 // Sometimes $fName is SpecialPage, sometimes it's specialpage. <3 PHP 00362 if ( strtolower( $fName ) == 'specialpage' ) { 00363 $name = isset( $a[0] ) ? $a[0] : ''; 00364 $restriction = isset( $a[1] ) ? $a[1] : ''; 00365 $listed = isset( $a[2] ) ? $a[2] : true; 00366 $function = isset( $a[3] ) ? $a[3] : false; 00367 $file = isset( $a[4] ) ? $a[4] : 'default'; 00368 $includable = isset( $a[5] ) ? $a[5] : false; 00369 $this->init( $name, $restriction, $listed, $function, $file, $includable ); 00370 } else { 00371 $className = get_class( $this ); 00372 throw new MWException( "Call to undefined method $className::$fName" ); 00373 } 00374 } 00375 00380 function getName() { 00381 return $this->mName; 00382 } 00383 00388 function getRestriction() { 00389 return $this->mRestriction; 00390 } 00391 00399 function getFile() { 00400 wfDeprecated( __METHOD__, '1.18' ); 00401 return $this->mFile; 00402 } 00403 00404 // @todo FIXME: Decide which syntax to use for this, and stick to it 00410 function isListed() { 00411 return $this->mListed; 00412 } 00419 function setListed( $listed ) { 00420 return wfSetVar( $this->mListed, $listed ); 00421 } 00428 function listed( $x = null ) { 00429 return wfSetVar( $this->mListed, $x ); 00430 } 00431 00436 public function isIncludable() { 00437 return $this->mIncludable; 00438 } 00439 00447 function name( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mName, $x ); } 00448 00456 function restriction( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mRestriction, $x ); } 00457 00465 function func( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mFunction, $x ); } 00466 00474 function file( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mFile, $x ); } 00475 00483 function includable( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mIncludable, $x ); } 00484 00490 function including( $x = null ) { 00491 return wfSetVar( $this->mIncluding, $x ); 00492 } 00493 00497 function getLocalName() { 00498 if ( !isset( $this->mLocalName ) ) { 00499 $this->mLocalName = SpecialPageFactory::getLocalNameFor( $this->mName ); 00500 } 00501 return $this->mLocalName; 00502 } 00503 00512 public function isExpensive() { 00513 return false; 00514 } 00515 00523 public function isRestricted() { 00524 // DWIM: If all anons can do something, then it is not restricted 00525 return $this->mRestriction != '' && !User::groupHasPermission( '*', $this->mRestriction ); 00526 } 00527 00536 public function userCanExecute( User $user ) { 00537 return $user->isAllowed( $this->mRestriction ); 00538 } 00539 00543 function displayRestrictionError() { 00544 throw new PermissionsError( $this->mRestriction ); 00545 } 00546 00552 public function checkPermissions() { 00553 if ( !$this->userCanExecute( $this->getUser() ) ) { 00554 $this->displayRestrictionError(); 00555 } 00556 } 00557 00564 public function checkReadOnly() { 00565 if ( wfReadOnly() ) { 00566 throw new ReadOnlyError; 00567 } 00568 } 00569 00573 function setHeaders() { 00574 $out = $this->getOutput(); 00575 $out->setArticleRelated( false ); 00576 $out->setRobotPolicy( "noindex,nofollow" ); 00577 $out->setPageTitle( $this->getDescription() ); 00578 } 00579 00587 public final function run( $subPage ) { 00596 wfRunHooks( 'SpecialPageBeforeExecute', array( $this, $subPage ) ); 00597 00598 $this->beforeExecute( $subPage ); 00599 $this->execute( $subPage ); 00600 $this->afterExecute( $subPage ); 00601 00610 wfRunHooks( 'SpecialPageAfterExecute', array( $this, $subPage ) ); 00611 } 00612 00620 protected function beforeExecute( $subPage ) { 00621 // No-op 00622 } 00623 00631 protected function afterExecute( $subPage ) { 00632 // No-op 00633 } 00634 00643 public function execute( $subPage ) { 00644 $this->setHeaders(); 00645 $this->checkPermissions(); 00646 00647 $func = $this->mFunction; 00648 // only load file if the function does not exist 00649 if ( !is_callable( $func ) && $this->mFile ) { 00650 require_once( $this->mFile ); 00651 } 00652 $this->outputHeader(); 00653 call_user_func( $func, $subPage, $this ); 00654 } 00655 00664 function outputHeader( $summaryMessageKey = '' ) { 00665 global $wgContLang; 00666 00667 if ( $summaryMessageKey == '' ) { 00668 $msg = $wgContLang->lc( $this->getName() ) . '-summary'; 00669 } else { 00670 $msg = $summaryMessageKey; 00671 } 00672 if ( !$this->msg( $msg )->isDisabled() && !$this->including() ) { 00673 $this->getOutput()->wrapWikiMsg( 00674 "<div class='mw-specialpage-summary'>\n$1\n</div>", $msg ); 00675 } 00676 00677 } 00678 00689 function getDescription() { 00690 return $this->msg( strtolower( $this->mName ) )->text(); 00691 } 00692 00699 function getTitle( $subpage = false ) { 00700 return self::getTitleFor( $this->mName, $subpage ); 00701 } 00702 00709 public function setContext( $context ) { 00710 $this->mContext = $context; 00711 } 00712 00719 public function getContext() { 00720 if ( $this->mContext instanceof IContextSource ) { 00721 return $this->mContext; 00722 } else { 00723 wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" ); 00724 return RequestContext::getMain(); 00725 } 00726 } 00727 00734 public function getRequest() { 00735 return $this->getContext()->getRequest(); 00736 } 00737 00744 public function getOutput() { 00745 return $this->getContext()->getOutput(); 00746 } 00747 00754 public function getUser() { 00755 return $this->getContext()->getUser(); 00756 } 00757 00764 public function getSkin() { 00765 return $this->getContext()->getSkin(); 00766 } 00767 00775 public function getLang() { 00776 wfDeprecated( __METHOD__, '1.19' ); 00777 return $this->getLanguage(); 00778 } 00779 00786 public function getLanguage() { 00787 return $this->getContext()->getLanguage(); 00788 } 00789 00796 public function getFullTitle() { 00797 return $this->getContext()->getTitle(); 00798 } 00799 00806 public function msg( /* $args */ ) { 00807 // Note: can't use func_get_args() directly as second or later item in 00808 // a parameter list until PHP 5.3 or you get a fatal error. 00809 // Works fine as the first parameter, which appears elsewhere in the 00810 // code base. Sighhhh. 00811 $args = func_get_args(); 00812 $message = call_user_func_array( array( $this->getContext(), 'msg' ), $args ); 00813 // RequestContext passes context to wfMessage, and the language is set from 00814 // the context, but setting the language for Message class removes the 00815 // interface message status, which breaks for example usernameless gender 00816 // invokations. Restore the flag when not including special page in content. 00817 if ( $this->including() ) { 00818 $message->setInterfaceMessageFlag( false ); 00819 } 00820 return $message; 00821 } 00822 00828 protected function addFeedLinks( $params ) { 00829 global $wgFeedClasses; 00830 00831 $feedTemplate = wfScript( 'api' ) . '?'; 00832 00833 foreach ( $wgFeedClasses as $format => $class ) { 00834 $theseParams = $params + array( 'feedformat' => $format ); 00835 $url = $feedTemplate . wfArrayToCGI( $theseParams ); 00836 $this->getOutput()->addFeedLink( $format, $url ); 00837 } 00838 } 00839 } 00840 00846 abstract class FormSpecialPage extends SpecialPage { 00847 00852 protected abstract function getFormFields(); 00853 00858 protected function preText() { return ''; } 00859 protected function postText() { return ''; } 00860 00865 protected function alterForm( HTMLForm $form ) {} 00866 00871 protected function getForm() { 00872 $this->fields = $this->getFormFields(); 00873 00874 $form = new HTMLForm( $this->fields, $this->getContext() ); 00875 $form->setSubmitCallback( array( $this, 'onSubmit' ) ); 00876 $form->setWrapperLegend( $this->msg( strtolower( $this->getName() ) . '-legend' ) ); 00877 $form->addHeaderText( 00878 $this->msg( strtolower( $this->getName() ) . '-text' )->parseAsBlock() ); 00879 00880 // Retain query parameters (uselang etc) 00881 $params = array_diff_key( 00882 $this->getRequest()->getQueryValues(), array( 'title' => null ) ); 00883 $form->addHiddenField( 'redirectparams', wfArrayToCGI( $params ) ); 00884 00885 $form->addPreText( $this->preText() ); 00886 $form->addPostText( $this->postText() ); 00887 $this->alterForm( $form ); 00888 00889 // Give hooks a chance to alter the form, adding extra fields or text etc 00890 wfRunHooks( "Special{$this->getName()}BeforeFormDisplay", array( &$form ) ); 00891 00892 return $form; 00893 } 00894 00900 public abstract function onSubmit( array $data ); 00901 00906 public abstract function onSuccess(); 00907 00913 public function execute( $par ) { 00914 $this->setParameter( $par ); 00915 $this->setHeaders(); 00916 00917 // This will throw exceptions if there's a problem 00918 $this->checkExecutePermissions( $this->getUser() ); 00919 00920 $form = $this->getForm(); 00921 if ( $form->show() ) { 00922 $this->onSuccess(); 00923 } 00924 } 00925 00930 protected function setParameter( $par ) {} 00931 00939 protected function checkExecutePermissions( User $user ) { 00940 $this->checkPermissions(); 00941 00942 if ( $this->requiresUnblock() && $user->isBlocked() ) { 00943 $block = $user->getBlock(); 00944 throw new UserBlockedError( $block ); 00945 } 00946 00947 if ( $this->requiresWrite() ) { 00948 $this->checkReadOnly(); 00949 } 00950 00951 return true; 00952 } 00953 00958 public function requiresWrite() { 00959 return true; 00960 } 00961 00966 public function requiresUnblock() { 00967 return true; 00968 } 00969 } 00970 00975 class UnlistedSpecialPage extends SpecialPage { 00976 function __construct( $name, $restriction = '', $function = false, $file = 'default' ) { 00977 parent::__construct( $name, $restriction, false, $function, $file ); 00978 } 00979 00980 public function isListed() { 00981 return false; 00982 } 00983 } 00984 00989 class IncludableSpecialPage extends SpecialPage { 00990 function __construct( 00991 $name, $restriction = '', $listed = true, $function = false, $file = 'default' 00992 ) { 00993 parent::__construct( $name, $restriction, $listed, $function, $file, true ); 00994 } 00995 00996 public function isIncludable() { 00997 return true; 00998 } 00999 } 01000 01005 abstract class RedirectSpecialPage extends UnlistedSpecialPage { 01006 01007 // Query parameters that can be passed through redirects 01008 protected $mAllowedRedirectParams = array(); 01009 01010 // Query parameteres added by redirects 01011 protected $mAddedRedirectParams = array(); 01012 01013 public function execute( $par ) { 01014 $redirect = $this->getRedirect( $par ); 01015 $query = $this->getRedirectQuery(); 01016 // Redirect to a page title with possible query parameters 01017 if ( $redirect instanceof Title ) { 01018 $url = $redirect->getFullUrl( $query ); 01019 $this->getOutput()->redirect( $url ); 01020 return $redirect; 01021 // Redirect to index.php with query parameters 01022 } elseif ( $redirect === true ) { 01023 global $wgScript; 01024 $url = $wgScript . '?' . wfArrayToCGI( $query ); 01025 $this->getOutput()->redirect( $url ); 01026 return $redirect; 01027 } else { 01028 $class = __CLASS__; 01029 throw new MWException( "RedirectSpecialPage $class doesn't redirect!" ); 01030 } 01031 } 01032 01040 abstract public function getRedirect( $par ); 01041 01048 public function getRedirectQuery() { 01049 $params = array(); 01050 01051 foreach ( $this->mAllowedRedirectParams as $arg ) { 01052 if ( $this->getRequest()->getVal( $arg, null ) !== null ) { 01053 $params[$arg] = $this->getRequest()->getVal( $arg ); 01054 } 01055 } 01056 01057 foreach ( $this->mAddedRedirectParams as $arg => $val ) { 01058 $params[$arg] = $val; 01059 } 01060 01061 return count( $params ) 01062 ? $params 01063 : false; 01064 } 01065 } 01066 01067 abstract class SpecialRedirectToSpecial extends RedirectSpecialPage { 01068 var $redirName, $redirSubpage; 01069 01070 function __construct( 01071 $name, $redirName, $redirSubpage = false, 01072 $allowedRedirectParams = array(), $addedRedirectParams = array() 01073 ) { 01074 parent::__construct( $name ); 01075 $this->redirName = $redirName; 01076 $this->redirSubpage = $redirSubpage; 01077 $this->mAllowedRedirectParams = $allowedRedirectParams; 01078 $this->mAddedRedirectParams = $addedRedirectParams; 01079 } 01080 01081 public function getRedirect( $subpage ) { 01082 if ( $this->redirSubpage === false ) { 01083 return SpecialPage::getTitleFor( $this->redirName, $subpage ); 01084 } else { 01085 return SpecialPage::getTitleFor( $this->redirName, $this->redirSubpage ); 01086 } 01087 } 01088 } 01089 01093 class SpecialListAdmins extends SpecialRedirectToSpecial { 01094 function __construct() { 01095 parent::__construct( 'Listadmins', 'Listusers', 'sysop' ); 01096 } 01097 } 01098 01102 class SpecialListBots extends SpecialRedirectToSpecial { 01103 function __construct() { 01104 parent::__construct( 'Listbots', 'Listusers', 'bot' ); 01105 } 01106 } 01107 01112 class SpecialCreateAccount extends SpecialRedirectToSpecial { 01113 function __construct() { 01114 parent::__construct( 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) ); 01115 } 01116 } 01191 abstract class RedirectSpecialArticle extends RedirectSpecialPage { 01192 function __construct( $name ) { 01193 parent::__construct( $name ); 01194 $redirectParams = array( 01195 'action', 01196 'redirect', 'rdfrom', 01197 # Options for preloaded edits 01198 'preload', 'editintro', 'preloadtitle', 'summary', 01199 # Options for overriding user settings 01200 'preview', 'internaledit', 'externaledit', 'mode', 01201 # Options for history/diffs 01202 'section', 'oldid', 'diff', 'dir', 01203 'limit', 'offset', 'feed', 01204 # Misc options 01205 'redlink', 'debug', 01206 # Options for action=raw; missing ctype can break JS or CSS in some browsers 01207 'ctype', 'maxage', 'smaxage', 01208 ); 01209 01210 wfRunHooks( "RedirectSpecialArticleRedirectParams", array( &$redirectParams ) ); 01211 $this->mAllowedRedirectParams = $redirectParams; 01212 } 01213 } 01214 01219 class SpecialMypage extends RedirectSpecialArticle { 01220 function __construct() { 01221 parent::__construct( 'Mypage' ); 01222 } 01223 01224 function getRedirect( $subpage ) { 01225 if ( strval( $subpage ) !== '' ) { 01226 return Title::makeTitle( NS_USER, $this->getUser()->getName() . '/' . $subpage ); 01227 } else { 01228 return Title::makeTitle( NS_USER, $this->getUser()->getName() ); 01229 } 01230 } 01231 } 01232 01237 class SpecialMytalk extends RedirectSpecialArticle { 01238 function __construct() { 01239 parent::__construct( 'Mytalk' ); 01240 } 01241 01242 function getRedirect( $subpage ) { 01243 if ( strval( $subpage ) !== '' ) { 01244 return Title::makeTitle( NS_USER_TALK, $this->getUser()->getName() . '/' . $subpage ); 01245 } else { 01246 return Title::makeTitle( NS_USER_TALK, $this->getUser()->getName() ); 01247 } 01248 } 01249 } 01250 01255 class SpecialMycontributions extends RedirectSpecialPage { 01256 function __construct() { 01257 parent::__construct( 'Mycontributions' ); 01258 $this->mAllowedRedirectParams = array( 'limit', 'namespace', 'tagfilter', 01259 'offset', 'dir', 'year', 'month', 'feed' ); 01260 } 01261 01262 function getRedirect( $subpage ) { 01263 return SpecialPage::getTitleFor( 'Contributions', $this->getUser()->getName() ); 01264 } 01265 } 01266 01270 class SpecialMyuploads extends RedirectSpecialPage { 01271 function __construct() { 01272 parent::__construct( 'Myuploads' ); 01273 $this->mAllowedRedirectParams = array( 'limit' ); 01274 } 01275 01276 function getRedirect( $subpage ) { 01277 return SpecialPage::getTitleFor( 'Listfiles', $this->getUser()->getName() ); 01278 } 01279 } 01280 01284 class SpecialPermanentLink extends RedirectSpecialPage { 01285 function __construct() { 01286 parent::__construct( 'PermanentLink' ); 01287 $this->mAllowedRedirectParams = array(); 01288 } 01289 01290 function getRedirect( $subpage ) { 01291 $subpage = intval( $subpage ); 01292 if ( $subpage === 0 ) { 01293 # throw an error page when no subpage was given 01294 throw new ErrorPageError( 'nopagetitle', 'nopagetext' ); 01295 } 01296 $this->mAddedRedirectParams['oldid'] = $subpage; 01297 return true; 01298 } 01299 }