MediaWiki
REL1_21
|
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 }