summaryrefslogtreecommitdiff
path: root/includes/SpecialPage.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/SpecialPage.php')
-rw-r--r--includes/SpecialPage.php307
1 files changed, 220 insertions, 87 deletions
diff --git a/includes/SpecialPage.php b/includes/SpecialPage.php
index 2e5e02b0..a6195fc4 100644
--- a/includes/SpecialPage.php
+++ b/includes/SpecialPage.php
@@ -27,7 +27,6 @@
* @ingroup SpecialPage
*/
class SpecialPage {
-
// The canonical name of this special page
// Also used for the default <h1> heading, @see getDescription()
protected $mName;
@@ -106,19 +105,6 @@ class SpecialPage {
}
/**
- * Add a page to the list of valid special pages. This used to be the preferred
- * method for adding special pages in extensions. It's now suggested that you add
- * an associative record to $wgSpecialPages. This avoids autoloading SpecialPage.
- *
- * @param $page SpecialPage
- * @deprecated since 1.7, warnings in 1.17, might be removed in 1.20
- */
- static function addPage( &$page ) {
- wfDeprecated( __METHOD__, '1.7' );
- SpecialPageFactory::getList()->{$page->mName} = $page;
- }
-
- /**
* Add a page to a certain display group for Special:SpecialPages
*
* @param $page Mixed: SpecialPage or string
@@ -148,7 +134,7 @@ class SpecialPage {
* preferred method is now to add a SpecialPage_initList hook.
* @deprecated since 1.18
*
- * @param $name String the page to remove
+ * @param string $name the page to remove
*/
static function removePage( $name ) {
wfDeprecated( __METHOD__, '1.18' );
@@ -158,7 +144,7 @@ class SpecialPage {
/**
* Check if a given name exist as a special page or as a special page alias
*
- * @param $name String: name of a special page
+ * @param string $name name of a special page
* @return Boolean: true if a special page exists with this name
* @deprecated since 1.18 call SpecialPageFactory method directly
*/
@@ -266,13 +252,15 @@ class SpecialPage {
* Get a localised Title object for a specified special page name
*
* @param $name String
- * @param $subpage String|Bool subpage string, or false to not use a subpage
+ * @param string|Bool $subpage subpage string, or false to not use a subpage
+ * @param string $fragment the link fragment (after the "#")
+ * @throws MWException
* @return Title object
*/
- public static function getTitleFor( $name, $subpage = false ) {
+ public static function getTitleFor( $name, $subpage = false, $fragment = '' ) {
$name = SpecialPageFactory::getLocalNameFor( $name, $subpage );
if ( $name ) {
- return Title::makeTitle( NS_SPECIAL, $name );
+ return Title::makeTitle( NS_SPECIAL, $name, $fragment );
} else {
throw new MWException( "Invalid special page name \"$name\"" );
}
@@ -282,7 +270,7 @@ class SpecialPage {
* Get a localised Title object for a page name with a possibly unvalidated subpage
*
* @param $name String
- * @param $subpage String|Bool subpage string, or false to not use a subpage
+ * @param string|Bool $subpage subpage string, or false to not use a subpage
* @return Title object or null if the page doesn't exist
*/
public static function getSafeTitleFor( $name, $subpage = false ) {
@@ -313,15 +301,17 @@ class SpecialPage {
* be displayed by the default execute() method, without the global function ever
* being called.
*
- * If you override execute(), you can recover the default behaviour with userCanExecute()
+ * If you override execute(), you can recover the default behavior with userCanExecute()
* and displayRestrictionError()
*
- * @param $name String: name of the special page, as seen in links and URLs
- * @param $restriction String: user right required, e.g. "block" or "delete"
- * @param $listed Bool: whether the page is listed in Special:Specialpages
- * @param $function Callback|Bool: function called by execute(). By default it is constructed from $name
- * @param $file String: file which is included by execute(). It is also constructed from $name by default
- * @param $includable Bool: whether the page can be included in normal pages
+ * @param string $name Name of the special page, as seen in links and URLs
+ * @param string $restriction User right required, e.g. "block" or "delete"
+ * @param bool $listed Whether the page is listed in Special:Specialpages
+ * @param Callback|Bool $function Function called by execute(). By default
+ * it is constructed from $name
+ * @param string $file File which is included by execute(). It is also
+ * constructed from $name by default
+ * @param bool $includable Whether the page can be included in normal pages
*/
public function __construct(
$name = '', $restriction = '', $listed = true,
@@ -333,12 +323,14 @@ class SpecialPage {
/**
* Do the real work for the constructor, mainly so __call() can intercept
* calls to SpecialPage()
- * @param $name String: name of the special page, as seen in links and URLs
- * @param $restriction String: user right required, e.g. "block" or "delete"
- * @param $listed Bool: whether the page is listed in Special:Specialpages
- * @param $function Callback|Bool: function called by execute(). By default it is constructed from $name
- * @param $file String: file which is included by execute(). It is also constructed from $name by default
- * @param $includable Bool: whether the page can be included in normal pages
+ * @param string $name Name of the special page, as seen in links and URLs
+ * @param string $restriction User right required, e.g. "block" or "delete"
+ * @param bool $listed Whether the page is listed in Special:Specialpages
+ * @param Callback|Bool $function Function called by execute(). By default
+ * it is constructed from $name
+ * @param string $file File which is included by execute(). It is also
+ * constructed from $name by default
+ * @param bool $includable Whether the page can be included in normal pages
*/
private function init( $name, $restriction, $listed, $function, $file, $includable ) {
$this->mName = $name;
@@ -361,8 +353,9 @@ class SpecialPage {
* Use PHP's magic __call handler to get calls to the old PHP4 constructor
* because PHP E_STRICT yells at you for having __construct() and SpecialPage()
*
- * @param $fName String Name of called method
- * @param $a Array Arguments to the method
+ * @param string $fName Name of called method
+ * @param array $a Arguments to the method
+ * @throws MWException
* @deprecated since 1.17, call parent::__construct()
*/
public function __call( $fName, $a ) {
@@ -455,7 +448,10 @@ class SpecialPage {
* @return Mixed
* @deprecated since 1.18
*/
- function name( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mName, $x ); }
+ function name( $x = null ) {
+ wfDeprecated( __METHOD__, '1.18' );
+ return wfSetVar( $this->mName, $x );
+ }
/**
* These mutators are very evil, as the relevant variables should not mutate. So
@@ -464,7 +460,10 @@ class SpecialPage {
* @return Mixed
* @deprecated since 1.18
*/
- function restriction( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mRestriction, $x ); }
+ function restriction( $x = null ) {
+ wfDeprecated( __METHOD__, '1.18' );
+ return wfSetVar( $this->mRestriction, $x );
+ }
/**
* These mutators are very evil, as the relevant variables should not mutate. So
@@ -473,7 +472,10 @@ class SpecialPage {
* @return Mixed
* @deprecated since 1.18
*/
- function func( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mFunction, $x ); }
+ function func( $x = null ) {
+ wfDeprecated( __METHOD__, '1.18' );
+ return wfSetVar( $this->mFunction, $x );
+ }
/**
* These mutators are very evil, as the relevant variables should not mutate. So
@@ -482,7 +484,10 @@ class SpecialPage {
* @return Mixed
* @deprecated since 1.18
*/
- function file( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mFile, $x ); }
+ function file( $x = null ) {
+ wfDeprecated( __METHOD__, '1.18' );
+ return wfSetVar( $this->mFile, $x );
+ }
/**
* These mutators are very evil, as the relevant variables should not mutate. So
@@ -491,7 +496,10 @@ class SpecialPage {
* @return Mixed
* @deprecated since 1.18
*/
- function includable( $x = null ) { wfDeprecated( __METHOD__, '1.18' ); return wfSetVar( $this->mIncludable, $x ); }
+ function includable( $x = null ) {
+ wfDeprecated( __METHOD__, '1.18' );
+ return wfSetVar( $this->mIncludable, $x );
+ }
/**
* Whether the special page is being evaluated via transclusion
@@ -525,6 +533,19 @@ class SpecialPage {
}
/**
+ * Is this page cached?
+ * Expensive pages are cached or disabled in miser mode.
+ * Used by QueryPage and subclasses, moved here so that
+ * Special:SpecialPages can safely call it for all special pages.
+ *
+ * @return Boolean
+ * @since 1.21
+ */
+ public function isCached() {
+ return false;
+ }
+
+ /**
* Can be overridden by subclasses with more complicated permissions
* schemes.
*
@@ -532,9 +553,8 @@ class SpecialPage {
* pages?
*/
public function isRestricted() {
- global $wgGroupPermissions;
- // DWIM: If all anons can do something, then it is not restricted
- return $this->mRestriction != '' && empty( $wgGroupPermissions['*'][$this->mRestriction] );
+ // DWIM: If anons can do something, then it is not restricted
+ return $this->mRestriction != '' && !User::groupHasPermission( '*', $this->mRestriction );
}
/**
@@ -596,7 +616,7 @@ class SpecialPage {
*
* @param $subPage string|null
*/
- public final function run( $subPage ) {
+ final public function run( $subPage ) {
/**
* Gets called before @see SpecialPage::execute.
*
@@ -659,7 +679,7 @@ class SpecialPage {
$func = $this->mFunction;
// only load file if the function does not exist
if ( !is_callable( $func ) && $this->mFile ) {
- require_once( $this->mFile );
+ require_once $this->mFile;
}
$this->outputHeader();
call_user_func( $func, $subPage, $this );
@@ -668,10 +688,10 @@ class SpecialPage {
/**
* Outputs a summary message on top of special pages
* Per default the message key is the canonical name of the special page
- * May be overriden, i.e. by extensions to stick with the naming conventions
+ * May be overridden, i.e. by extensions to stick with the naming conventions
* for message keys: 'extensionname-xxx'
*
- * @param $summaryMessageKey String: message key of the summary
+ * @param string $summaryMessageKey message key of the summary
*/
function outputHeader( $summaryMessageKey = '' ) {
global $wgContLang;
@@ -693,7 +713,7 @@ class SpecialPage {
* also the name that will be listed in Special:Specialpages
*
* Derived classes can override this, but usually it is easier to keep the
- * default behaviour. Messages can be added at run-time, see
+ * default behavior. Messages can be added at run-time, see
* MessageCache.php.
*
* @return String
@@ -732,7 +752,8 @@ class SpecialPage {
if ( $this->mContext instanceof IContextSource ) {
return $this->mContext;
} else {
- wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" );
+ wfDebug( __METHOD__ . " called and \$mContext is null. " .
+ "Return RequestContext::getMain(); for sanity\n" );
return RequestContext::getMain();
}
}
@@ -780,7 +801,7 @@ class SpecialPage {
/**
* Shortcut to get user's language
*
- * @deprecated 1.19 Use getLanguage instead
+ * @deprecated since 1.19 Use getLanguage instead
* @return Language
* @since 1.18
*/
@@ -825,7 +846,7 @@ class SpecialPage {
// RequestContext passes context to wfMessage, and the language is set from
// the context, but setting the language for Message class removes the
// interface message status, which breaks for example usernameless gender
- // invokations. Restore the flag when not including special page in content.
+ // invocations. Restore the flag when not including special page in content.
if ( $this->including() ) {
$message->setInterfaceMessageFlag( false );
}
@@ -840,14 +861,65 @@ class SpecialPage {
protected function addFeedLinks( $params ) {
global $wgFeedClasses;
- $feedTemplate = wfScript( 'api' ) . '?';
+ $feedTemplate = wfScript( 'api' );
foreach ( $wgFeedClasses as $format => $class ) {
$theseParams = $params + array( 'feedformat' => $format );
- $url = $feedTemplate . wfArrayToCGI( $theseParams );
+ $url = wfAppendQuery( $feedTemplate, $theseParams );
$this->getOutput()->addFeedLink( $format, $url );
}
}
+
+ /**
+ * Get the group that the special page belongs in on Special:SpecialPage
+ * Use this method, instead of getGroupName to allow customization
+ * of the group name from the wiki side
+ *
+ * @return string Group of this special page
+ * @since 1.21
+ */
+ public function getFinalGroupName() {
+ global $wgSpecialPageGroups;
+ $name = $this->getName();
+ $group = '-';
+
+ // Allow overbidding the group from the wiki side
+ $msg = $this->msg( 'specialpages-specialpagegroup-' . strtolower( $name ) )->inContentLanguage();
+ if ( !$msg->isBlank() ) {
+ $group = $msg->text();
+ } else {
+ // Than use the group from this object
+ $group = $this->getGroupName();
+
+ // Group '-' is used as default to have the chance to determine,
+ // if the special pages overrides this method,
+ // if not overridden, $wgSpecialPageGroups is checked for b/c
+ if ( $group === '-' && isset( $wgSpecialPageGroups[$name] ) ) {
+ $group = $wgSpecialPageGroups[$name];
+ }
+ }
+
+ // never give '-' back, change to 'other'
+ if ( $group === '-' ) {
+ $group = 'other';
+ }
+
+ return $group;
+ }
+
+ /**
+ * Under which header this special page is listed in Special:SpecialPages
+ * See messages 'specialpages-group-*' for valid names
+ * This method defaults to group 'other'
+ *
+ * @return string
+ * @since 1.21
+ */
+ protected function getGroupName() {
+ // '-' used here to determine, if this group is overridden or has a hardcoded 'other'
+ // Needed for b/c in getFinalGroupName
+ return '-';
+ }
}
/**
@@ -856,43 +928,77 @@ class SpecialPage {
* a new structure for SpecialPages
*/
abstract class FormSpecialPage extends SpecialPage {
+ /**
+ * The sub-page of the special page.
+ * @var string
+ */
+ protected $par = null;
/**
* Get an HTMLForm descriptor array
* @return Array
*/
- protected abstract function getFormFields();
+ abstract protected function getFormFields();
/**
- * Add pre- or post-text to the form
+ * Add pre-text to the form
* @return String HTML which will be sent to $form->addPreText()
*/
- protected function preText() { return ''; }
- protected function postText() { return ''; }
+ protected function preText() {
+ return '';
+ }
+
+ /**
+ * Add post-text to the form
+ * @return String HTML which will be sent to $form->addPostText()
+ */
+ protected function postText() {
+ return '';
+ }
/**
* Play with the HTMLForm if you need to more substantially
* @param $form HTMLForm
*/
- protected function alterForm( HTMLForm $form ) {}
+ protected function alterForm( HTMLForm $form ) {
+ }
+
+ /**
+ * Get message prefix for HTMLForm
+ *
+ * @since 1.21
+ * @return string
+ */
+ protected function getMessagePrefix() {
+ return strtolower( $this->getName() );
+ }
/**
- * Get the HTMLForm to control behaviour
+ * Get the HTMLForm to control behavior
* @return HTMLForm|null
*/
protected function getForm() {
$this->fields = $this->getFormFields();
- $form = new HTMLForm( $this->fields, $this->getContext() );
+ $form = new HTMLForm( $this->fields, $this->getContext(), $this->getMessagePrefix() );
$form->setSubmitCallback( array( $this, 'onSubmit' ) );
- $form->setWrapperLegend( $this->msg( strtolower( $this->getName() ) . '-legend' ) );
- $form->addHeaderText(
- $this->msg( strtolower( $this->getName() ) . '-text' )->parseAsBlock() );
+ // If the form is a compact vertical form, then don't output this ugly
+ // fieldset surrounding it.
+ // XXX Special pages can setDisplayFormat to 'vform' in alterForm(), but that
+ // is called after this.
+ if ( !$form->isVForm() ) {
+ $form->setWrapperLegendMsg( $this->getMessagePrefix() . '-legend' );
+ }
+
+ $headerMsg = $this->msg( $this->getMessagePrefix() . '-text' );
+ if ( !$headerMsg->isDisabled() ) {
+ $form->addHeaderText( $headerMsg->parseAsBlock() );
+ }
// Retain query parameters (uselang etc)
$params = array_diff_key(
$this->getRequest()->getQueryValues(), array( 'title' => null ) );
- $form->addHiddenField( 'redirectparams', wfArrayToCGI( $params ) );
+ $form->addHiddenField( 'redirectparams', wfArrayToCgi( $params ) );
$form->addPreText( $this->preText() );
$form->addPostText( $this->postText() );
@@ -909,18 +1015,20 @@ abstract class FormSpecialPage extends SpecialPage {
* @param $data Array
* @return Bool|Array true for success, false for didn't-try, array of errors on failure
*/
- public abstract function onSubmit( array $data );
+ abstract public function onSubmit( array $data );
/**
* Do something exciting on successful processing of the form, most likely to show a
* confirmation message
+ * @since 1.22 Default is to do nothing
*/
- public abstract function onSuccess();
+ public function onSuccess() {
+ }
/**
* Basic SpecialPage workflow: get a form, send it to the user; get some data back,
*
- * @param $par String Subpage string if one was specified
+ * @param string $par Subpage string if one was specified
*/
public function execute( $par ) {
$this->setParameter( $par );
@@ -937,16 +1045,18 @@ abstract class FormSpecialPage extends SpecialPage {
/**
* Maybe do something interesting with the subpage parameter
- * @param $par String
+ * @param string $par
*/
- protected function setParameter( $par ) {}
+ protected function setParameter( $par ) {
+ $this->par = $par;
+ }
/**
* Called from execute() to check if the given user can perform this action.
* Failures here must throw subclasses of ErrorPageError.
* @param $user User
+ * @throws UserBlockedError
* @return Bool true
- * @throws ErrorPageError
*/
protected function checkExecutePermissions( User $user ) {
$this->checkPermissions();
@@ -1019,7 +1129,7 @@ abstract class RedirectSpecialPage extends UnlistedSpecialPage {
// Query parameters that can be passed through redirects
protected $mAllowedRedirectParams = array();
- // Query parameteres added by redirects
+ // Query parameters added by redirects
protected $mAddedRedirectParams = array();
public function execute( $par ) {
@@ -1027,19 +1137,16 @@ abstract class RedirectSpecialPage extends UnlistedSpecialPage {
$query = $this->getRedirectQuery();
// Redirect to a page title with possible query parameters
if ( $redirect instanceof Title ) {
- $url = $redirect->getFullUrl( $query );
+ $url = $redirect->getFullURL( $query );
$this->getOutput()->redirect( $url );
- wfProfileOut( __METHOD__ );
return $redirect;
- // Redirect to index.php with query parameters
} elseif ( $redirect === true ) {
- global $wgScript;
- $url = $wgScript . '?' . wfArrayToCGI( $query );
+ // Redirect to index.php with query parameters
+ $url = wfAppendQuery( wfScript( 'index' ), $query );
$this->getOutput()->redirect( $url );
- wfProfileOut( __METHOD__ );
return $redirect;
} else {
- $class = __CLASS__;
+ $class = get_class( $this );
throw new MWException( "RedirectSpecialPage $class doesn't redirect!" );
}
}
@@ -1048,7 +1155,7 @@ abstract class RedirectSpecialPage extends UnlistedSpecialPage {
* If the special page is a redirect, then get the Title object it redirects to.
* False otherwise.
*
- * @param $par String Subpage string
+ * @param string $par Subpage string
* @return Title|bool
*/
abstract public function getRedirect( $par );
@@ -1079,6 +1186,7 @@ abstract class RedirectSpecialPage extends UnlistedSpecialPage {
}
abstract class SpecialRedirectToSpecial extends RedirectSpecialPage {
+ // @todo FIXME: Visibility must be declared
var $redirName, $redirSubpage;
function __construct(
@@ -1125,12 +1233,21 @@ class SpecialListBots extends SpecialRedirectToSpecial {
*/
class SpecialCreateAccount extends SpecialRedirectToSpecial {
function __construct() {
- parent::__construct( 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) );
+ parent::__construct( 'CreateAccount', 'Userlogin', 'signup', array( 'returnto', 'returntoquery', 'uselang' ) );
+ }
+
+ // No reason to hide this link on Special:Specialpages
+ public function isListed() {
+ return true;
+ }
+
+ protected function getGroupName() {
+ return 'login';
}
}
/**
* SpecialMypage, SpecialMytalk and SpecialMycontributions special pages
- * are used to get user independant links pointing to the user page, talk
+ * are used to get user independent links pointing to the user page, talk
* page and list of contributions.
* This can let us cache a single copy of any generated content for all
* users.
@@ -1155,7 +1272,7 @@ class SpecialCreateAccount extends SpecialRedirectToSpecial {
* - limit, offset: Useful for linking to history of one's own user page or
* user talk page. For example, this would be a link to "the last edit to your
* user talk page in the year 2010":
- * http://en.wikipedia.org/w/index.php?title=Special:MyPage&offset=20110000000000&limit=1&action=history
+ * http://en.wikipedia.org/wiki/Special:MyPage?offset=20110000000000&limit=1&action=history
*
* - feed: would allow linking to the current user's RSS feed for their user
* talk page:
@@ -1209,9 +1326,9 @@ abstract class RedirectSpecialArticle extends RedirectSpecialPage {
'action',
'redirect', 'rdfrom',
# Options for preloaded edits
- 'preload', 'editintro', 'preloadtitle', 'summary',
+ 'preload', 'editintro', 'preloadtitle', 'summary', 'nosummary',
# Options for overriding user settings
- 'preview', 'internaledit', 'externaledit', 'mode',
+ 'preview', 'internaledit', 'externaledit', 'mode', 'minor', 'watchthis',
# Options for history/diffs
'section', 'oldid', 'diff', 'dir',
'limit', 'offset', 'feed',
@@ -1221,7 +1338,7 @@ abstract class RedirectSpecialArticle extends RedirectSpecialPage {
'ctype', 'maxage', 'smaxage',
);
- wfRunHooks( "RedirectSpecialArticleRedirectParams", array(&$redirectParams) );
+ wfRunHooks( "RedirectSpecialArticleRedirectParams", array( &$redirectParams ) );
$this->mAllowedRedirectParams = $redirectParams;
}
}
@@ -1268,7 +1385,7 @@ class SpecialMytalk extends RedirectSpecialArticle {
*/
class SpecialMycontributions extends RedirectSpecialPage {
function __construct() {
- parent::__construct( 'Mycontributions' );
+ parent::__construct( 'Mycontributions' );
$this->mAllowedRedirectParams = array( 'limit', 'namespace', 'tagfilter',
'offset', 'dir', 'year', 'month', 'feed' );
}
@@ -1284,7 +1401,7 @@ class SpecialMycontributions extends RedirectSpecialPage {
class SpecialMyuploads extends RedirectSpecialPage {
function __construct() {
parent::__construct( 'Myuploads' );
- $this->mAllowedRedirectParams = array( 'limit' );
+ $this->mAllowedRedirectParams = array( 'limit', 'ilshowall', 'ilsearch' );
}
function getRedirect( $subpage ) {
@@ -1293,6 +1410,22 @@ class SpecialMyuploads extends RedirectSpecialPage {
}
/**
+ * Redirect Special:Listfiles?user=$wgUser&ilshowall=true
+ */
+class SpecialAllMyUploads extends RedirectSpecialPage {
+ function __construct() {
+ parent::__construct( 'AllMyUploads' );
+ $this->mAllowedRedirectParams = array( 'limit', 'ilsearch' );
+ }
+
+ function getRedirect( $subpage ) {
+ $this->mAddedRedirectParams['ilshowall'] = 1;
+ return SpecialPage::getTitleFor( 'Listfiles', $this->getUser()->getName() );
+ }
+}
+
+
+/**
* Redirect from Special:PermanentLink/### to index.php?oldid=###
*/
class SpecialPermanentLink extends RedirectSpecialPage {