MediaWiki
REL1_20
|
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 00116 static function addPage( &$page ) { 00117 wfDeprecated( __METHOD__, '1.7' ); 00118 SpecialPageFactory::getList()->{$page->mName} = $page; 00119 } 00120 00128 static function setGroup( $page, $group ) { 00129 wfDeprecated( __METHOD__, '1.18' ); 00130 SpecialPageFactory::setGroup( $page, $group ); 00131 } 00132 00140 static function getGroup( &$page ) { 00141 wfDeprecated( __METHOD__, '1.18' ); 00142 return SpecialPageFactory::getGroup( $page ); 00143 } 00144 00153 static function removePage( $name ) { 00154 wfDeprecated( __METHOD__, '1.18' ); 00155 unset( SpecialPageFactory::getList()->$name ); 00156 } 00157 00165 static function exists( $name ) { 00166 wfDeprecated( __METHOD__, '1.18' ); 00167 return SpecialPageFactory::exists( $name ); 00168 } 00169 00177 static function getPage( $name ) { 00178 wfDeprecated( __METHOD__, '1.18' ); 00179 return SpecialPageFactory::getPage( $name ); 00180 } 00181 00190 static function getPageByAlias( $alias ) { 00191 wfDeprecated( __METHOD__, '1.18' ); 00192 return SpecialPageFactory::getPage( $alias ); 00193 } 00194 00204 static function getUsablePages( User $user = null ) { 00205 wfDeprecated( __METHOD__, '1.18' ); 00206 return SpecialPageFactory::getUsablePages( $user ); 00207 } 00208 00215 static function getRegularPages() { 00216 wfDeprecated( __METHOD__, '1.18' ); 00217 return SpecialPageFactory::getRegularPages(); 00218 } 00219 00227 static function getRestrictedPages() { 00228 wfDeprecated( __METHOD__, '1.18' ); 00229 return SpecialPageFactory::getRestrictedPages(); 00230 } 00231 00246 public static function executePath( &$title, IContextSource &$context, $including = false ) { 00247 wfDeprecated( __METHOD__, '1.18' ); 00248 return SpecialPageFactory::executePath( $title, $context, $including ); 00249 } 00250 00260 static function getLocalNameFor( $name, $subpage = false ) { 00261 wfDeprecated( __METHOD__, '1.18' ); 00262 return SpecialPageFactory::getLocalNameFor( $name, $subpage ); 00263 } 00264 00272 public static function getTitleFor( $name, $subpage = false ) { 00273 $name = SpecialPageFactory::getLocalNameFor( $name, $subpage ); 00274 if ( $name ) { 00275 return Title::makeTitle( NS_SPECIAL, $name ); 00276 } else { 00277 throw new MWException( "Invalid special page name \"$name\"" ); 00278 } 00279 } 00280 00288 public static function getSafeTitleFor( $name, $subpage = false ) { 00289 $name = SpecialPageFactory::getLocalNameFor( $name, $subpage ); 00290 if ( $name ) { 00291 return Title::makeTitleSafe( NS_SPECIAL, $name ); 00292 } else { 00293 return null; 00294 } 00295 } 00296 00304 static function getTitleForAlias( $alias ) { 00305 wfDeprecated( __METHOD__, '1.18' ); 00306 return SpecialPageFactory::getTitleForAlias( $alias ); 00307 } 00308 00326 public function __construct( 00327 $name = '', $restriction = '', $listed = true, 00328 $function = false, $file = 'default', $includable = false 00329 ) { 00330 $this->init( $name, $restriction, $listed, $function, $file, $includable ); 00331 } 00332 00343 private function init( $name, $restriction, $listed, $function, $file, $includable ) { 00344 $this->mName = $name; 00345 $this->mRestriction = $restriction; 00346 $this->mListed = $listed; 00347 $this->mIncludable = $includable; 00348 if ( !$function ) { 00349 $this->mFunction = 'wfSpecial' . $name; 00350 } else { 00351 $this->mFunction = $function; 00352 } 00353 if ( $file === 'default' ) { 00354 $this->mFile = __DIR__ . "/specials/Special$name.php"; 00355 } else { 00356 $this->mFile = $file; 00357 } 00358 } 00359 00368 public function __call( $fName, $a ) { 00369 // Deprecated messages now, remove in 1.19 or 1.20? 00370 wfDeprecated( __METHOD__, '1.17' ); 00371 00372 // Sometimes $fName is SpecialPage, sometimes it's specialpage. <3 PHP 00373 if ( strtolower( $fName ) == 'specialpage' ) { 00374 $name = isset( $a[0] ) ? $a[0] : ''; 00375 $restriction = isset( $a[1] ) ? $a[1] : ''; 00376 $listed = isset( $a[2] ) ? $a[2] : true; 00377 $function = isset( $a[3] ) ? $a[3] : false; 00378 $file = isset( $a[4] ) ? $a[4] : 'default'; 00379 $includable = isset( $a[5] ) ? $a[5] : false; 00380 $this->init( $name, $restriction, $listed, $function, $file, $includable ); 00381 } else { 00382 $className = get_class( $this ); 00383 throw new MWException( "Call to undefined method $className::$fName" ); 00384 } 00385 } 00386 00391 function getName() { 00392 return $this->mName; 00393 } 00394 00399 function getRestriction() { 00400 return $this->mRestriction; 00401 } 00402 00410 function getFile() { 00411 wfDeprecated( __METHOD__, '1.18' ); 00412 return $this->mFile; 00413 } 00414 00415 // @todo FIXME: Decide which syntax to use for this, and stick to it 00421 function isListed() { 00422 return $this->mListed; 00423 } 00430 function setListed( $listed ) { 00431 return wfSetVar( $this->mListed, $listed ); 00432 } 00439 function listed( $x = null ) { 00440 return wfSetVar( $this->mListed, $x ); 00441 } 00442 00447 public function isIncludable() { 00448 return $this->mIncludable; 00449 } 00450 00458 function name( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mName, $x ); } 00459 00467 function restriction( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mRestriction, $x ); } 00468 00476 function func( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mFunction, $x ); } 00477 00485 function file( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mFile, $x ); } 00486 00494 function includable( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mIncludable, $x ); } 00495 00501 function including( $x = null ) { 00502 return wfSetVar( $this->mIncluding, $x ); 00503 } 00504 00508 function getLocalName() { 00509 if ( !isset( $this->mLocalName ) ) { 00510 $this->mLocalName = SpecialPageFactory::getLocalNameFor( $this->mName ); 00511 } 00512 return $this->mLocalName; 00513 } 00514 00523 public function isExpensive() { 00524 return false; 00525 } 00526 00534 public function isRestricted() { 00535 global $wgGroupPermissions; 00536 // DWIM: If all anons can do something, then it is not restricted 00537 return $this->mRestriction != '' && empty( $wgGroupPermissions['*'][$this->mRestriction] ); 00538 } 00539 00548 public function userCanExecute( User $user ) { 00549 return $user->isAllowed( $this->mRestriction ); 00550 } 00551 00555 function displayRestrictionError() { 00556 throw new PermissionsError( $this->mRestriction ); 00557 } 00558 00564 public function checkPermissions() { 00565 if ( !$this->userCanExecute( $this->getUser() ) ) { 00566 $this->displayRestrictionError(); 00567 } 00568 } 00569 00576 public function checkReadOnly() { 00577 if ( wfReadOnly() ) { 00578 throw new ReadOnlyError; 00579 } 00580 } 00581 00585 function setHeaders() { 00586 $out = $this->getOutput(); 00587 $out->setArticleRelated( false ); 00588 $out->setRobotPolicy( "noindex,nofollow" ); 00589 $out->setPageTitle( $this->getDescription() ); 00590 } 00591 00599 public final function run( $subPage ) { 00608 wfRunHooks( 'SpecialPageBeforeExecute', array( $this, $subPage ) ); 00609 00610 $this->beforeExecute( $subPage ); 00611 $this->execute( $subPage ); 00612 $this->afterExecute( $subPage ); 00613 00622 wfRunHooks( 'SpecialPageAfterExecute', array( $this, $subPage ) ); 00623 } 00624 00632 protected function beforeExecute( $subPage ) { 00633 // No-op 00634 } 00635 00643 protected function afterExecute( $subPage ) { 00644 // No-op 00645 } 00646 00655 public function execute( $subPage ) { 00656 $this->setHeaders(); 00657 $this->checkPermissions(); 00658 00659 $func = $this->mFunction; 00660 // only load file if the function does not exist 00661 if ( !is_callable( $func ) && $this->mFile ) { 00662 require_once( $this->mFile ); 00663 } 00664 $this->outputHeader(); 00665 call_user_func( $func, $subPage, $this ); 00666 } 00667 00676 function outputHeader( $summaryMessageKey = '' ) { 00677 global $wgContLang; 00678 00679 if ( $summaryMessageKey == '' ) { 00680 $msg = $wgContLang->lc( $this->getName() ) . '-summary'; 00681 } else { 00682 $msg = $summaryMessageKey; 00683 } 00684 if ( !$this->msg( $msg )->isDisabled() && !$this->including() ) { 00685 $this->getOutput()->wrapWikiMsg( 00686 "<div class='mw-specialpage-summary'>\n$1\n</div>", $msg ); 00687 } 00688 00689 } 00690 00701 function getDescription() { 00702 return $this->msg( strtolower( $this->mName ) )->text(); 00703 } 00704 00711 function getTitle( $subpage = false ) { 00712 return self::getTitleFor( $this->mName, $subpage ); 00713 } 00714 00721 public function setContext( $context ) { 00722 $this->mContext = $context; 00723 } 00724 00731 public function getContext() { 00732 if ( $this->mContext instanceof IContextSource ) { 00733 return $this->mContext; 00734 } else { 00735 wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" ); 00736 return RequestContext::getMain(); 00737 } 00738 } 00739 00746 public function getRequest() { 00747 return $this->getContext()->getRequest(); 00748 } 00749 00756 public function getOutput() { 00757 return $this->getContext()->getOutput(); 00758 } 00759 00766 public function getUser() { 00767 return $this->getContext()->getUser(); 00768 } 00769 00776 public function getSkin() { 00777 return $this->getContext()->getSkin(); 00778 } 00779 00787 public function getLang() { 00788 wfDeprecated( __METHOD__, '1.19' ); 00789 return $this->getLanguage(); 00790 } 00791 00798 public function getLanguage() { 00799 return $this->getContext()->getLanguage(); 00800 } 00801 00808 public function getFullTitle() { 00809 return $this->getContext()->getTitle(); 00810 } 00811 00818 public function msg( /* $args */ ) { 00819 // Note: can't use func_get_args() directly as second or later item in 00820 // a parameter list until PHP 5.3 or you get a fatal error. 00821 // Works fine as the first parameter, which appears elsewhere in the 00822 // code base. Sighhhh. 00823 $args = func_get_args(); 00824 $message = call_user_func_array( array( $this->getContext(), 'msg' ), $args ); 00825 // RequestContext passes context to wfMessage, and the language is set from 00826 // the context, but setting the language for Message class removes the 00827 // interface message status, which breaks for example usernameless gender 00828 // invokations. Restore the flag when not including special page in content. 00829 if ( $this->including() ) { 00830 $message->setInterfaceMessageFlag( false ); 00831 } 00832 return $message; 00833 } 00834 00840 protected function addFeedLinks( $params ) { 00841 global $wgFeedClasses; 00842 00843 $feedTemplate = wfScript( 'api' ) . '?'; 00844 00845 foreach ( $wgFeedClasses as $format => $class ) { 00846 $theseParams = $params + array( 'feedformat' => $format ); 00847 $url = $feedTemplate . wfArrayToCGI( $theseParams ); 00848 $this->getOutput()->addFeedLink( $format, $url ); 00849 } 00850 } 00851 } 00852 00858 abstract class FormSpecialPage extends SpecialPage { 00859 00864 protected abstract function getFormFields(); 00865 00870 protected function preText() { return ''; } 00871 protected function postText() { return ''; } 00872 00877 protected function alterForm( HTMLForm $form ) {} 00878 00883 protected function getForm() { 00884 $this->fields = $this->getFormFields(); 00885 00886 $form = new HTMLForm( $this->fields, $this->getContext() ); 00887 $form->setSubmitCallback( array( $this, 'onSubmit' ) ); 00888 $form->setWrapperLegend( $this->msg( strtolower( $this->getName() ) . '-legend' ) ); 00889 $form->addHeaderText( 00890 $this->msg( strtolower( $this->getName() ) . '-text' )->parseAsBlock() ); 00891 00892 // Retain query parameters (uselang etc) 00893 $params = array_diff_key( 00894 $this->getRequest()->getQueryValues(), array( 'title' => null ) ); 00895 $form->addHiddenField( 'redirectparams', wfArrayToCGI( $params ) ); 00896 00897 $form->addPreText( $this->preText() ); 00898 $form->addPostText( $this->postText() ); 00899 $this->alterForm( $form ); 00900 00901 // Give hooks a chance to alter the form, adding extra fields or text etc 00902 wfRunHooks( "Special{$this->getName()}BeforeFormDisplay", array( &$form ) ); 00903 00904 return $form; 00905 } 00906 00912 public abstract function onSubmit( array $data ); 00913 00918 public abstract function onSuccess(); 00919 00925 public function execute( $par ) { 00926 $this->setParameter( $par ); 00927 $this->setHeaders(); 00928 00929 // This will throw exceptions if there's a problem 00930 $this->checkExecutePermissions( $this->getUser() ); 00931 00932 $form = $this->getForm(); 00933 if ( $form->show() ) { 00934 $this->onSuccess(); 00935 } 00936 } 00937 00942 protected function setParameter( $par ) {} 00943 00951 protected function checkExecutePermissions( User $user ) { 00952 $this->checkPermissions(); 00953 00954 if ( $this->requiresUnblock() && $user->isBlocked() ) { 00955 $block = $user->getBlock(); 00956 throw new UserBlockedError( $block ); 00957 } 00958 00959 if ( $this->requiresWrite() ) { 00960 $this->checkReadOnly(); 00961 } 00962 00963 return true; 00964 } 00965 00970 public function requiresWrite() { 00971 return true; 00972 } 00973 00978 public function requiresUnblock() { 00979 return true; 00980 } 00981 } 00982 00987 class UnlistedSpecialPage extends SpecialPage { 00988 function __construct( $name, $restriction = '', $function = false, $file = 'default' ) { 00989 parent::__construct( $name, $restriction, false, $function, $file ); 00990 } 00991 00992 public function isListed() { 00993 return false; 00994 } 00995 } 00996 01001 class IncludableSpecialPage extends SpecialPage { 01002 function __construct( 01003 $name, $restriction = '', $listed = true, $function = false, $file = 'default' 01004 ) { 01005 parent::__construct( $name, $restriction, $listed, $function, $file, true ); 01006 } 01007 01008 public function isIncludable() { 01009 return true; 01010 } 01011 } 01012 01017 abstract class RedirectSpecialPage extends UnlistedSpecialPage { 01018 01019 // Query parameters that can be passed through redirects 01020 protected $mAllowedRedirectParams = array(); 01021 01022 // Query parameteres added by redirects 01023 protected $mAddedRedirectParams = array(); 01024 01025 public function execute( $par ) { 01026 $redirect = $this->getRedirect( $par ); 01027 $query = $this->getRedirectQuery(); 01028 // Redirect to a page title with possible query parameters 01029 if ( $redirect instanceof Title ) { 01030 $url = $redirect->getFullUrl( $query ); 01031 $this->getOutput()->redirect( $url ); 01032 wfProfileOut( __METHOD__ ); 01033 return $redirect; 01034 // Redirect to index.php with query parameters 01035 } elseif ( $redirect === true ) { 01036 global $wgScript; 01037 $url = $wgScript . '?' . wfArrayToCGI( $query ); 01038 $this->getOutput()->redirect( $url ); 01039 wfProfileOut( __METHOD__ ); 01040 return $redirect; 01041 } else { 01042 $class = __CLASS__; 01043 throw new MWException( "RedirectSpecialPage $class doesn't redirect!" ); 01044 } 01045 } 01046 01054 abstract public function getRedirect( $par ); 01055 01062 public function getRedirectQuery() { 01063 $params = array(); 01064 01065 foreach ( $this->mAllowedRedirectParams as $arg ) { 01066 if ( $this->getRequest()->getVal( $arg, null ) !== null ) { 01067 $params[$arg] = $this->getRequest()->getVal( $arg ); 01068 } 01069 } 01070 01071 foreach ( $this->mAddedRedirectParams as $arg => $val ) { 01072 $params[$arg] = $val; 01073 } 01074 01075 return count( $params ) 01076 ? $params 01077 : false; 01078 } 01079 } 01080 01081 abstract class SpecialRedirectToSpecial extends RedirectSpecialPage { 01082 var $redirName, $redirSubpage; 01083 01084 function __construct( 01085 $name, $redirName, $redirSubpage = false, 01086 $allowedRedirectParams = array(), $addedRedirectParams = array() 01087 ) { 01088 parent::__construct( $name ); 01089 $this->redirName = $redirName; 01090 $this->redirSubpage = $redirSubpage; 01091 $this->mAllowedRedirectParams = $allowedRedirectParams; 01092 $this->mAddedRedirectParams = $addedRedirectParams; 01093 } 01094 01095 public function getRedirect( $subpage ) { 01096 if ( $this->redirSubpage === false ) { 01097 return SpecialPage::getTitleFor( $this->redirName, $subpage ); 01098 } else { 01099 return SpecialPage::getTitleFor( $this->redirName, $this->redirSubpage ); 01100 } 01101 } 01102 } 01103 01107 class SpecialListAdmins extends SpecialRedirectToSpecial { 01108 function __construct() { 01109 parent::__construct( 'Listadmins', 'Listusers', 'sysop' ); 01110 } 01111 } 01112 01116 class SpecialListBots extends SpecialRedirectToSpecial { 01117 function __construct() { 01118 parent::__construct( 'Listbots', 'Listusers', 'bot' ); 01119 } 01120 } 01121 01126 class SpecialCreateAccount extends SpecialRedirectToSpecial { 01127 function __construct() { 01128 parent::__construct( 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) ); 01129 } 01130 } 01205 abstract class RedirectSpecialArticle extends RedirectSpecialPage { 01206 function __construct( $name ) { 01207 parent::__construct( $name ); 01208 $redirectParams = array( 01209 'action', 01210 'redirect', 'rdfrom', 01211 # Options for preloaded edits 01212 'preload', 'editintro', 'preloadtitle', 'summary', 01213 # Options for overriding user settings 01214 'preview', 'internaledit', 'externaledit', 'mode', 01215 # Options for history/diffs 01216 'section', 'oldid', 'diff', 'dir', 01217 'limit', 'offset', 'feed', 01218 # Misc options 01219 'redlink', 'debug', 01220 # Options for action=raw; missing ctype can break JS or CSS in some browsers 01221 'ctype', 'maxage', 'smaxage', 01222 ); 01223 01224 wfRunHooks( "RedirectSpecialArticleRedirectParams", array(&$redirectParams) ); 01225 $this->mAllowedRedirectParams = $redirectParams; 01226 } 01227 } 01228 01233 class SpecialMypage extends RedirectSpecialArticle { 01234 function __construct() { 01235 parent::__construct( 'Mypage' ); 01236 } 01237 01238 function getRedirect( $subpage ) { 01239 if ( strval( $subpage ) !== '' ) { 01240 return Title::makeTitle( NS_USER, $this->getUser()->getName() . '/' . $subpage ); 01241 } else { 01242 return Title::makeTitle( NS_USER, $this->getUser()->getName() ); 01243 } 01244 } 01245 } 01246 01251 class SpecialMytalk extends RedirectSpecialArticle { 01252 function __construct() { 01253 parent::__construct( 'Mytalk' ); 01254 } 01255 01256 function getRedirect( $subpage ) { 01257 if ( strval( $subpage ) !== '' ) { 01258 return Title::makeTitle( NS_USER_TALK, $this->getUser()->getName() . '/' . $subpage ); 01259 } else { 01260 return Title::makeTitle( NS_USER_TALK, $this->getUser()->getName() ); 01261 } 01262 } 01263 } 01264 01269 class SpecialMycontributions extends RedirectSpecialPage { 01270 function __construct() { 01271 parent::__construct( 'Mycontributions' ); 01272 $this->mAllowedRedirectParams = array( 'limit', 'namespace', 'tagfilter', 01273 'offset', 'dir', 'year', 'month', 'feed' ); 01274 } 01275 01276 function getRedirect( $subpage ) { 01277 return SpecialPage::getTitleFor( 'Contributions', $this->getUser()->getName() ); 01278 } 01279 } 01280 01284 class SpecialMyuploads extends RedirectSpecialPage { 01285 function __construct() { 01286 parent::__construct( 'Myuploads' ); 01287 $this->mAllowedRedirectParams = array( 'limit' ); 01288 } 01289 01290 function getRedirect( $subpage ) { 01291 return SpecialPage::getTitleFor( 'Listfiles', $this->getUser()->getName() ); 01292 } 01293 } 01294 01298 class SpecialPermanentLink extends RedirectSpecialPage { 01299 function __construct() { 01300 parent::__construct( 'PermanentLink' ); 01301 $this->mAllowedRedirectParams = array(); 01302 } 01303 01304 function getRedirect( $subpage ) { 01305 $subpage = intval( $subpage ); 01306 if ( $subpage === 0 ) { 01307 # throw an error page when no subpage was given 01308 throw new ErrorPageError( 'nopagetitle', 'nopagetext' ); 01309 } 01310 $this->mAddedRedirectParams['oldid'] = $subpage; 01311 return true; 01312 } 01313 }