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