[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/specialpage/ -> SpecialPage.php (source)

   1  <?php
   2  /**
   3   * Parent class for all special pages.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   * @ingroup SpecialPage
  22   */
  23  
  24  /**
  25   * Parent class for all special pages.
  26   *
  27   * Includes some static functions for handling the special page list deprecated
  28   * in favor of SpecialPageFactory.
  29   *
  30   * @ingroup SpecialPage
  31   */
  32  class SpecialPage {
  33      // The canonical name of this special page
  34      // Also used for the default <h1> heading, @see getDescription()
  35      protected $mName;
  36  
  37      // The local name of this special page
  38      private $mLocalName;
  39  
  40      // Minimum user level required to access this page, or "" for anyone.
  41      // Also used to categorise the pages in Special:Specialpages
  42      protected $mRestriction;
  43  
  44      // Listed in Special:Specialpages?
  45      private $mListed;
  46  
  47      // Whether or not this special page is being included from an article
  48      protected $mIncluding;
  49  
  50      // Whether the special page can be included in an article
  51      protected $mIncludable;
  52  
  53      /**
  54       * Current request context
  55       * @var IContextSource
  56       */
  57      protected $mContext;
  58  
  59      /**
  60       * Get a localised Title object for a specified special page name
  61       *
  62       * @param string $name
  63       * @param string|bool $subpage Subpage string, or false to not use a subpage
  64       * @param string $fragment The link fragment (after the "#")
  65       * @return Title
  66       * @throws MWException
  67       */
  68  	public static function getTitleFor( $name, $subpage = false, $fragment = '' ) {
  69          $name = SpecialPageFactory::getLocalNameFor( $name, $subpage );
  70  
  71          return Title::makeTitle( NS_SPECIAL, $name, $fragment );
  72      }
  73  
  74      /**
  75       * Get a localised Title object for a page name with a possibly unvalidated subpage
  76       *
  77       * @param string $name
  78       * @param string|bool $subpage Subpage string, or false to not use a subpage
  79       * @return Title|null Title object or null if the page doesn't exist
  80       */
  81  	public static function getSafeTitleFor( $name, $subpage = false ) {
  82          $name = SpecialPageFactory::getLocalNameFor( $name, $subpage );
  83          if ( $name ) {
  84              return Title::makeTitleSafe( NS_SPECIAL, $name );
  85          } else {
  86              return null;
  87          }
  88      }
  89  
  90      /**
  91       * Default constructor for special pages
  92       * Derivative classes should call this from their constructor
  93       *     Note that if the user does not have the required level, an error message will
  94       *     be displayed by the default execute() method, without the global function ever
  95       *     being called.
  96       *
  97       *     If you override execute(), you can recover the default behavior with userCanExecute()
  98       *     and displayRestrictionError()
  99       *
 100       * @param string $name Name of the special page, as seen in links and URLs
 101       * @param string $restriction User right required, e.g. "block" or "delete"
 102       * @param bool $listed Whether the page is listed in Special:Specialpages
 103       * @param callable|bool $function Unused
 104       * @param string $file Unused
 105       * @param bool $includable Whether the page can be included in normal pages
 106       */
 107  	public function __construct(
 108          $name = '', $restriction = '', $listed = true,
 109          $function = false, $file = '', $includable = false
 110      ) {
 111          $this->mName = $name;
 112          $this->mRestriction = $restriction;
 113          $this->mListed = $listed;
 114          $this->mIncludable = $includable;
 115      }
 116  
 117      /**
 118       * Get the name of this Special Page.
 119       * @return string
 120       */
 121  	function getName() {
 122          return $this->mName;
 123      }
 124  
 125      /**
 126       * Get the permission that a user must have to execute this page
 127       * @return string
 128       */
 129  	function getRestriction() {
 130          return $this->mRestriction;
 131      }
 132  
 133      // @todo FIXME: Decide which syntax to use for this, and stick to it
 134      /**
 135       * Whether this special page is listed in Special:SpecialPages
 136       * @since 1.3 (r3583)
 137       * @return bool
 138       */
 139  	function isListed() {
 140          return $this->mListed;
 141      }
 142  
 143      /**
 144       * Set whether this page is listed in Special:Specialpages, at run-time
 145       * @since 1.3
 146       * @param bool $listed
 147       * @return bool
 148       */
 149  	function setListed( $listed ) {
 150          return wfSetVar( $this->mListed, $listed );
 151      }
 152  
 153      /**
 154       * Get or set whether this special page is listed in Special:SpecialPages
 155       * @since 1.6
 156       * @param bool $x
 157       * @return bool
 158       */
 159  	function listed( $x = null ) {
 160          return wfSetVar( $this->mListed, $x );
 161      }
 162  
 163      /**
 164       * Whether it's allowed to transclude the special page via {{Special:Foo/params}}
 165       * @return bool
 166       */
 167  	public function isIncludable() {
 168          return $this->mIncludable;
 169      }
 170  
 171      /**
 172       * Whether the special page is being evaluated via transclusion
 173       * @param bool $x
 174       * @return bool
 175       */
 176  	function including( $x = null ) {
 177          return wfSetVar( $this->mIncluding, $x );
 178      }
 179  
 180      /**
 181       * Get the localised name of the special page
 182       * @return string
 183       */
 184  	function getLocalName() {
 185          if ( !isset( $this->mLocalName ) ) {
 186              $this->mLocalName = SpecialPageFactory::getLocalNameFor( $this->mName );
 187          }
 188  
 189          return $this->mLocalName;
 190      }
 191  
 192      /**
 193       * Is this page expensive (for some definition of expensive)?
 194       * Expensive pages are disabled or cached in miser mode.  Originally used
 195       * (and still overridden) by QueryPage and subclasses, moved here so that
 196       * Special:SpecialPages can safely call it for all special pages.
 197       *
 198       * @return bool
 199       */
 200  	public function isExpensive() {
 201          return false;
 202      }
 203  
 204      /**
 205       * Is this page cached?
 206       * Expensive pages are cached or disabled in miser mode.
 207       * Used by QueryPage and subclasses, moved here so that
 208       * Special:SpecialPages can safely call it for all special pages.
 209       *
 210       * @return bool
 211       * @since 1.21
 212       */
 213  	public function isCached() {
 214          return false;
 215      }
 216  
 217      /**
 218       * Can be overridden by subclasses with more complicated permissions
 219       * schemes.
 220       *
 221       * @return bool Should the page be displayed with the restricted-access
 222       *   pages?
 223       */
 224  	public function isRestricted() {
 225          // DWIM: If anons can do something, then it is not restricted
 226          return $this->mRestriction != '' && !User::groupHasPermission( '*', $this->mRestriction );
 227      }
 228  
 229      /**
 230       * Checks if the given user (identified by an object) can execute this
 231       * special page (as defined by $mRestriction).  Can be overridden by sub-
 232       * classes with more complicated permissions schemes.
 233       *
 234       * @param User $user The user to check
 235       * @return bool Does the user have permission to view the page?
 236       */
 237  	public function userCanExecute( User $user ) {
 238          return $user->isAllowed( $this->mRestriction );
 239      }
 240  
 241      /**
 242       * Output an error message telling the user what access level they have to have
 243       * @throws PermissionsError
 244       */
 245  	function displayRestrictionError() {
 246          throw new PermissionsError( $this->mRestriction );
 247      }
 248  
 249      /**
 250       * Checks if userCanExecute, and if not throws a PermissionsError
 251       *
 252       * @since 1.19
 253       * @return void
 254       * @throws PermissionsError
 255       */
 256  	public function checkPermissions() {
 257          if ( !$this->userCanExecute( $this->getUser() ) ) {
 258              $this->displayRestrictionError();
 259          }
 260      }
 261  
 262      /**
 263       * If the wiki is currently in readonly mode, throws a ReadOnlyError
 264       *
 265       * @since 1.19
 266       * @return void
 267       * @throws ReadOnlyError
 268       */
 269  	public function checkReadOnly() {
 270          if ( wfReadOnly() ) {
 271              throw new ReadOnlyError;
 272          }
 273      }
 274  
 275      /**
 276       * If the user is not logged in, throws UserNotLoggedIn error
 277       *
 278       * The user will be redirected to Special:Userlogin with the given message as an error on
 279       * the form.
 280       *
 281       * @since 1.23
 282       * @param string $reasonMsg [optional] Message key to be displayed on login page
 283       * @param string $titleMsg [optional] Passed on to UserNotLoggedIn constructor
 284       * @throws UserNotLoggedIn
 285       */
 286  	public function requireLogin(
 287          $reasonMsg = 'exception-nologin-text', $titleMsg = 'exception-nologin'
 288      ) {
 289          if ( $this->getUser()->isAnon() ) {
 290              throw new UserNotLoggedIn( $reasonMsg, $titleMsg );
 291          }
 292      }
 293  
 294      /**
 295       * Return an array of subpages beginning with $search that this special page will accept.
 296       *
 297       * For example, if a page supports subpages "foo", "bar" and "baz" (as in Special:PageName/foo,
 298       * etc.):
 299       *
 300       *   - `prefixSearchSubpages( "ba" )` should return `array( "bar", "baz" )`
 301       *   - `prefixSearchSubpages( "f" )` should return `array( "foo" )`
 302       *   - `prefixSearchSubpages( "z" )` should return `array()`
 303       *   - `prefixSearchSubpages( "" )` should return `array( foo", "bar", "baz" )`
 304       *
 305       * @param string $search Prefix to search for
 306       * @param int $limit Maximum number of results to return
 307       * @return string[] Matching subpages
 308       */
 309  	public function prefixSearchSubpages( $search, $limit = 10 ) {
 310          return array();
 311      }
 312  
 313      /**
 314       * Helper function for implementations of prefixSearchSubpages() that
 315       * filter the values in memory (as oppposed to making a query).
 316       *
 317       * @since 1.24
 318       * @param string $search
 319       * @param int $limit
 320       * @param array $subpages
 321       * @return string[]
 322       */
 323  	protected static function prefixSearchArray( $search, $limit, array $subpages ) {
 324          $escaped = preg_quote( $search, '/' );
 325          return array_slice( preg_grep( "/^$escaped/i", $subpages ), 0, $limit );
 326      }
 327  
 328      /**
 329       * Sets headers - this should be called from the execute() method of all derived classes!
 330       */
 331  	function setHeaders() {
 332          $out = $this->getOutput();
 333          $out->setArticleRelated( false );
 334          $out->setRobotPolicy( $this->getRobotPolicy() );
 335          $out->setPageTitle( $this->getDescription() );
 336          if ( $this->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) {
 337              $out->addModuleStyles( array(
 338                  'mediawiki.ui.input',
 339                  'mediawiki.ui.checkbox',
 340              ) );
 341          }
 342      }
 343  
 344      /**
 345       * Entry point.
 346       *
 347       * @since 1.20
 348       *
 349       * @param string|null $subPage
 350       */
 351  	final public function run( $subPage ) {
 352          /**
 353           * Gets called before @see SpecialPage::execute.
 354           *
 355           * @since 1.20
 356           *
 357           * @param SpecialPage $this
 358           * @param string|null $subPage
 359           */
 360          wfRunHooks( 'SpecialPageBeforeExecute', array( $this, $subPage ) );
 361  
 362          $this->beforeExecute( $subPage );
 363          $this->execute( $subPage );
 364          $this->afterExecute( $subPage );
 365  
 366          /**
 367           * Gets called after @see SpecialPage::execute.
 368           *
 369           * @since 1.20
 370           *
 371           * @param SpecialPage $this
 372           * @param string|null $subPage
 373           */
 374          wfRunHooks( 'SpecialPageAfterExecute', array( $this, $subPage ) );
 375      }
 376  
 377      /**
 378       * Gets called before @see SpecialPage::execute.
 379       *
 380       * @since 1.20
 381       *
 382       * @param string|null $subPage
 383       */
 384  	protected function beforeExecute( $subPage ) {
 385          // No-op
 386      }
 387  
 388      /**
 389       * Gets called after @see SpecialPage::execute.
 390       *
 391       * @since 1.20
 392       *
 393       * @param string|null $subPage
 394       */
 395  	protected function afterExecute( $subPage ) {
 396          // No-op
 397      }
 398  
 399      /**
 400       * Default execute method
 401       * Checks user permissions
 402       *
 403       * This must be overridden by subclasses; it will be made abstract in a future version
 404       *
 405       * @param string|null $subPage
 406       */
 407  	public function execute( $subPage ) {
 408          $this->setHeaders();
 409          $this->checkPermissions();
 410          $this->outputHeader();
 411      }
 412  
 413      /**
 414       * Outputs a summary message on top of special pages
 415       * Per default the message key is the canonical name of the special page
 416       * May be overridden, i.e. by extensions to stick with the naming conventions
 417       * for message keys: 'extensionname-xxx'
 418       *
 419       * @param string $summaryMessageKey Message key of the summary
 420       */
 421  	function outputHeader( $summaryMessageKey = '' ) {
 422          global $wgContLang;
 423  
 424          if ( $summaryMessageKey == '' ) {
 425              $msg = $wgContLang->lc( $this->getName() ) . '-summary';
 426          } else {
 427              $msg = $summaryMessageKey;
 428          }
 429          if ( !$this->msg( $msg )->isDisabled() && !$this->including() ) {
 430              $this->getOutput()->wrapWikiMsg(
 431                  "<div class='mw-specialpage-summary'>\n$1\n</div>", $msg );
 432          }
 433      }
 434  
 435      /**
 436       * Returns the name that goes in the \<h1\> in the special page itself, and
 437       * also the name that will be listed in Special:Specialpages
 438       *
 439       * Derived classes can override this, but usually it is easier to keep the
 440       * default behavior.
 441       *
 442       * @return string
 443       */
 444  	function getDescription() {
 445          return $this->msg( strtolower( $this->mName ) )->text();
 446      }
 447  
 448      /**
 449       * Get a self-referential title object
 450       *
 451       * @param string|bool $subpage
 452       * @return Title
 453       * @deprecated since 1.23, use SpecialPage::getPageTitle
 454       */
 455  	function getTitle( $subpage = false ) {
 456          return $this->getPageTitle( $subpage );
 457      }
 458  
 459      /**
 460       * Get a self-referential title object
 461       *
 462       * @param string|bool $subpage
 463       * @return Title
 464       * @since 1.23
 465       */
 466  	function getPageTitle( $subpage = false ) {
 467          return self::getTitleFor( $this->mName, $subpage );
 468      }
 469  
 470      /**
 471       * Sets the context this SpecialPage is executed in
 472       *
 473       * @param IContextSource $context
 474       * @since 1.18
 475       */
 476  	public function setContext( $context ) {
 477          $this->mContext = $context;
 478      }
 479  
 480      /**
 481       * Gets the context this SpecialPage is executed in
 482       *
 483       * @return IContextSource|RequestContext
 484       * @since 1.18
 485       */
 486  	public function getContext() {
 487          if ( $this->mContext instanceof IContextSource ) {
 488              return $this->mContext;
 489          } else {
 490              wfDebug( __METHOD__ . " called and \$mContext is null. " .
 491                  "Return RequestContext::getMain(); for sanity\n" );
 492  
 493              return RequestContext::getMain();
 494          }
 495      }
 496  
 497      /**
 498       * Get the WebRequest being used for this instance
 499       *
 500       * @return WebRequest
 501       * @since 1.18
 502       */
 503  	public function getRequest() {
 504          return $this->getContext()->getRequest();
 505      }
 506  
 507      /**
 508       * Get the OutputPage being used for this instance
 509       *
 510       * @return OutputPage
 511       * @since 1.18
 512       */
 513  	public function getOutput() {
 514          return $this->getContext()->getOutput();
 515      }
 516  
 517      /**
 518       * Shortcut to get the User executing this instance
 519       *
 520       * @return User
 521       * @since 1.18
 522       */
 523  	public function getUser() {
 524          return $this->getContext()->getUser();
 525      }
 526  
 527      /**
 528       * Shortcut to get the skin being used for this instance
 529       *
 530       * @return Skin
 531       * @since 1.18
 532       */
 533  	public function getSkin() {
 534          return $this->getContext()->getSkin();
 535      }
 536  
 537      /**
 538       * Shortcut to get user's language
 539       *
 540       * @return Language
 541       * @since 1.19
 542       */
 543  	public function getLanguage() {
 544          return $this->getContext()->getLanguage();
 545      }
 546  
 547      /**
 548       * Shortcut to get main config object
 549       * @return Config
 550       * @since 1.24
 551       */
 552  	public function getConfig() {
 553          return $this->getContext()->getConfig();
 554      }
 555  
 556      /**
 557       * Return the full title, including $par
 558       *
 559       * @return Title
 560       * @since 1.18
 561       */
 562  	public function getFullTitle() {
 563          return $this->getContext()->getTitle();
 564      }
 565  
 566      /**
 567       * Return the robot policy. Derived classes that override this can change
 568       * the robot policy set by setHeaders() from the default 'noindex,nofollow'.
 569       *
 570       * @return string
 571       * @since 1.23
 572       */
 573  	protected function getRobotPolicy() {
 574          return 'noindex,nofollow';
 575      }
 576  
 577      /**
 578       * Wrapper around wfMessage that sets the current context.
 579       *
 580       * @return Message
 581       * @see wfMessage
 582       */
 583  	public function msg( /* $args */ ) {
 584          $message = call_user_func_array(
 585              array( $this->getContext(), 'msg' ),
 586              func_get_args()
 587          );
 588          // RequestContext passes context to wfMessage, and the language is set from
 589          // the context, but setting the language for Message class removes the
 590          // interface message status, which breaks for example usernameless gender
 591          // invocations. Restore the flag when not including special page in content.
 592          if ( $this->including() ) {
 593              $message->setInterfaceMessageFlag( false );
 594          }
 595  
 596          return $message;
 597      }
 598  
 599      /**
 600       * Adds RSS/atom links
 601       *
 602       * @param array $params
 603       */
 604  	protected function addFeedLinks( $params ) {
 605          $feedTemplate = wfScript( 'api' );
 606  
 607          foreach ( $this->getConfig()->get( 'FeedClasses' ) as $format => $class ) {
 608              $theseParams = $params + array( 'feedformat' => $format );
 609              $url = wfAppendQuery( $feedTemplate, $theseParams );
 610              $this->getOutput()->addFeedLink( $format, $url );
 611          }
 612      }
 613  
 614      /**
 615       * Get the group that the special page belongs in on Special:SpecialPage
 616       * Use this method, instead of getGroupName to allow customization
 617       * of the group name from the wiki side
 618       *
 619       * @return string Group of this special page
 620       * @since 1.21
 621       */
 622  	public function getFinalGroupName() {
 623          $name = $this->getName();
 624          $specialPageGroups = $this->getConfig()->get( 'SpecialPageGroups' );
 625  
 626          // Allow overbidding the group from the wiki side
 627          $msg = $this->msg( 'specialpages-specialpagegroup-' . strtolower( $name ) )->inContentLanguage();
 628          if ( !$msg->isBlank() ) {
 629              $group = $msg->text();
 630          } else {
 631              // Than use the group from this object
 632              $group = $this->getGroupName();
 633  
 634              // Group '-' is used as default to have the chance to determine,
 635              // if the special pages overrides this method,
 636              // if not overridden, $wgSpecialPageGroups is checked for b/c
 637              if ( $group === '-' && isset( $specialPageGroups[$name] ) ) {
 638                  $group = $specialPageGroups[$name];
 639              }
 640          }
 641  
 642          // never give '-' back, change to 'other'
 643          if ( $group === '-' ) {
 644              $group = 'other';
 645          }
 646  
 647          return $group;
 648      }
 649  
 650      /**
 651       * Under which header this special page is listed in Special:SpecialPages
 652       * See messages 'specialpages-group-*' for valid names
 653       * This method defaults to group 'other'
 654       *
 655       * @return string
 656       * @since 1.21
 657       */
 658  	protected function getGroupName() {
 659          // '-' used here to determine, if this group is overridden or has a hardcoded 'other'
 660          // Needed for b/c in getFinalGroupName
 661          return '-';
 662      }
 663  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1