[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/actions/ -> Action.php (source)

   1  <?php
   2  /**
   3   * Base classes for actions done on 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
  16   * along with this program; if not, write to the Free Software
  17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  18   *
  19   * @file
  20   */
  21  
  22  /**
  23   * @defgroup Actions Action done on pages
  24   */
  25  
  26  /**
  27   * Actions are things which can be done to pages (edit, delete, rollback, etc).  They
  28   * are distinct from Special Pages because an action must apply to exactly one page.
  29   *
  30   * To add an action in an extension, create a subclass of Action, and add the key to
  31   * $wgActions.  There is also the deprecated UnknownAction hook
  32   *
  33   * Actions generally fall into two groups: the show-a-form-then-do-something-with-the-input
  34   * format (protect, delete, move, etc), and the just-do-something format (watch, rollback,
  35   * patrol, etc). The FormAction and FormlessAction classes represent these two groups.
  36   */
  37  abstract class Action {
  38  
  39      /**
  40       * Page on which we're performing the action
  41       * @since 1.17
  42       * @var WikiPage|Article|ImagePage|CategoryPage|Page $page
  43       */
  44      protected $page;
  45  
  46      /**
  47       * IContextSource if specified; otherwise we'll use the Context from the Page
  48       * @since 1.17
  49       * @var IContextSource $context
  50       */
  51      protected $context;
  52  
  53      /**
  54       * The fields used to create the HTMLForm
  55       * @since 1.17
  56       * @var array $fields
  57       */
  58      protected $fields;
  59  
  60      /**
  61       * Get the Action subclass which should be used to handle this action, false if
  62       * the action is disabled, or null if it's not recognised
  63       * @param string $action
  64       * @param array $overrides
  65       * @return bool|null|string|callable
  66       */
  67  	final private static function getClass( $action, array $overrides ) {
  68          global $wgActions;
  69          $action = strtolower( $action );
  70  
  71          if ( !isset( $wgActions[$action] ) ) {
  72              return null;
  73          }
  74  
  75          if ( $wgActions[$action] === false ) {
  76              return false;
  77          } elseif ( $wgActions[$action] === true && isset( $overrides[$action] ) ) {
  78              return $overrides[$action];
  79          } elseif ( $wgActions[$action] === true ) {
  80              return ucfirst( $action ) . 'Action';
  81          } else {
  82              return $wgActions[$action];
  83          }
  84      }
  85  
  86      /**
  87       * Get an appropriate Action subclass for the given action
  88       * @since 1.17
  89       * @param string $action
  90       * @param Page $page
  91       * @param IContextSource $context
  92       * @return Action|bool|null False if the action is disabled, null
  93       *     if it is not recognised
  94       */
  95  	final public static function factory( $action, Page $page, IContextSource $context = null ) {
  96          $classOrCallable = self::getClass( $action, $page->getActionOverrides() );
  97  
  98          if ( is_string( $classOrCallable ) ) {
  99              $obj = new $classOrCallable( $page, $context );
 100              return $obj;
 101          }
 102  
 103          if ( is_callable( $classOrCallable ) ) {
 104              return call_user_func_array( $classOrCallable, array( $page, $context ) );
 105          }
 106  
 107          return $classOrCallable;
 108      }
 109  
 110      /**
 111       * Get the action that will be executed, not necessarily the one passed
 112       * passed through the "action" request parameter. Actions disabled in
 113       * $wgActions will be replaced by "nosuchaction".
 114       *
 115       * @since 1.19
 116       * @param IContextSource $context
 117       * @return string Action name
 118       */
 119  	final public static function getActionName( IContextSource $context ) {
 120          global $wgActions;
 121  
 122          $request = $context->getRequest();
 123          $actionName = $request->getVal( 'action', 'view' );
 124  
 125          // Check for disabled actions
 126          if ( isset( $wgActions[$actionName] ) && $wgActions[$actionName] === false ) {
 127              $actionName = 'nosuchaction';
 128          }
 129  
 130          // Workaround for bug #20966: inability of IE to provide an action dependent
 131          // on which submit button is clicked.
 132          if ( $actionName === 'historysubmit' ) {
 133              if ( $request->getBool( 'revisiondelete' ) ) {
 134                  $actionName = 'revisiondelete';
 135              } else {
 136                  $actionName = 'view';
 137              }
 138          } elseif ( $actionName == 'editredlink' ) {
 139              $actionName = 'edit';
 140          }
 141  
 142          // Trying to get a WikiPage for NS_SPECIAL etc. will result
 143          // in WikiPage::factory throwing "Invalid or virtual namespace -1 given."
 144          // For SpecialPages et al, default to action=view.
 145          if ( !$context->canUseWikiPage() ) {
 146              return 'view';
 147          }
 148  
 149          $action = Action::factory( $actionName, $context->getWikiPage(), $context );
 150          if ( $action instanceof Action ) {
 151              return $action->getName();
 152          }
 153  
 154          return 'nosuchaction';
 155      }
 156  
 157      /**
 158       * Check if a given action is recognised, even if it's disabled
 159       * @since 1.17
 160       *
 161       * @param string $name Name of an action
 162       * @return bool
 163       */
 164  	final public static function exists( $name ) {
 165          return self::getClass( $name, array() ) !== null;
 166      }
 167  
 168      /**
 169       * Get the IContextSource in use here
 170       * @since 1.17
 171       * @return IContextSource
 172       */
 173  	final public function getContext() {
 174          if ( $this->context instanceof IContextSource ) {
 175              return $this->context;
 176          } elseif ( $this->page instanceof Article ) {
 177              // NOTE: $this->page can be a WikiPage, which does not have a context.
 178              wfDebug( __METHOD__ . ": no context known, falling back to Article's context.\n" );
 179              return $this->page->getContext();
 180          }
 181  
 182          wfWarn( __METHOD__ . ': no context known, falling back to RequestContext::getMain().' );
 183          return RequestContext::getMain();
 184      }
 185  
 186      /**
 187       * Get the WebRequest being used for this instance
 188       * @since 1.17
 189       *
 190       * @return WebRequest
 191       */
 192  	final public function getRequest() {
 193          return $this->getContext()->getRequest();
 194      }
 195  
 196      /**
 197       * Get the OutputPage being used for this instance
 198       * @since 1.17
 199       *
 200       * @return OutputPage
 201       */
 202  	final public function getOutput() {
 203          return $this->getContext()->getOutput();
 204      }
 205  
 206      /**
 207       * Shortcut to get the User being used for this instance
 208       * @since 1.17
 209       *
 210       * @return User
 211       */
 212  	final public function getUser() {
 213          return $this->getContext()->getUser();
 214      }
 215  
 216      /**
 217       * Shortcut to get the Skin being used for this instance
 218       * @since 1.17
 219       *
 220       * @return Skin
 221       */
 222  	final public function getSkin() {
 223          return $this->getContext()->getSkin();
 224      }
 225  
 226      /**
 227       * Shortcut to get the user Language being used for this instance
 228       *
 229       * @return Language
 230       */
 231  	final public function getLanguage() {
 232          return $this->getContext()->getLanguage();
 233      }
 234  
 235      /**
 236       * Shortcut to get the Title object from the page
 237       * @since 1.17
 238       *
 239       * @return Title
 240       */
 241  	final public function getTitle() {
 242          return $this->page->getTitle();
 243      }
 244  
 245      /**
 246       * Get a Message object with context set
 247       * Parameters are the same as wfMessage()
 248       *
 249       * @return Message
 250       */
 251  	final public function msg() {
 252          $params = func_get_args();
 253          return call_user_func_array( array( $this->getContext(), 'msg' ), $params );
 254      }
 255  
 256      /**
 257       * Constructor.
 258       *
 259       * Only public since 1.21
 260       *
 261       * @param Page $page
 262       * @param IContextSource $context
 263       */
 264  	public function __construct( Page $page, IContextSource $context = null ) {
 265          if ( $context === null ) {
 266              wfWarn( __METHOD__ . ' called without providing a Context object.' );
 267              // NOTE: We could try to initialize $context using $page->getContext(),
 268              //      if $page is an Article. That however seems to not work seamlessly.
 269          }
 270  
 271          $this->page = $page;
 272          $this->context = $context;
 273      }
 274  
 275      /**
 276       * Return the name of the action this object responds to
 277       * @since 1.17
 278       *
 279       * @return string Lowercase name
 280       */
 281      abstract public function getName();
 282  
 283      /**
 284       * Get the permission required to perform this action.  Often, but not always,
 285       * the same as the action name
 286       * @since 1.17
 287       *
 288       * @return string|null
 289       */
 290  	public function getRestriction() {
 291          return null;
 292      }
 293  
 294      /**
 295       * Checks if the given user (identified by an object) can perform this action.  Can be
 296       * overridden by sub-classes with more complicated permissions schemes.  Failures here
 297       * must throw subclasses of ErrorPageError
 298       * @since 1.17
 299       *
 300       * @param User $user The user to check, or null to use the context user
 301       * @throws UserBlockedError|ReadOnlyError|PermissionsError
 302       */
 303  	protected function checkCanExecute( User $user ) {
 304          $right = $this->getRestriction();
 305          if ( $right !== null ) {
 306              $errors = $this->getTitle()->getUserPermissionsErrors( $right, $user );
 307              if ( count( $errors ) ) {
 308                  throw new PermissionsError( $right, $errors );
 309              }
 310          }
 311  
 312          if ( $this->requiresUnblock() && $user->isBlocked() ) {
 313              $block = $user->getBlock();
 314              throw new UserBlockedError( $block );
 315          }
 316  
 317          // This should be checked at the end so that the user won't think the
 318          // error is only temporary when he also don't have the rights to execute
 319          // this action
 320          if ( $this->requiresWrite() && wfReadOnly() ) {
 321              throw new ReadOnlyError();
 322          }
 323      }
 324  
 325      /**
 326       * Whether this action requires the wiki not to be locked
 327       * @since 1.17
 328       *
 329       * @return bool
 330       */
 331  	public function requiresWrite() {
 332          return true;
 333      }
 334  
 335      /**
 336       * Whether this action can still be executed by a blocked user
 337       * @since 1.17
 338       *
 339       * @return bool
 340       */
 341  	public function requiresUnblock() {
 342          return true;
 343      }
 344  
 345      /**
 346       * Set output headers for noindexing etc.  This function will not be called through
 347       * the execute() entry point, so only put UI-related stuff in here.
 348       * @since 1.17
 349       */
 350  	protected function setHeaders() {
 351          $out = $this->getOutput();
 352          $out->setRobotPolicy( "noindex,nofollow" );
 353          $out->setPageTitle( $this->getPageTitle() );
 354          $out->setSubtitle( $this->getDescription() );
 355          $out->setArticleRelated( true );
 356      }
 357  
 358      /**
 359       * Returns the name that goes in the \<h1\> page title
 360       *
 361       * @return string
 362       */
 363  	protected function getPageTitle() {
 364          return $this->getTitle()->getPrefixedText();
 365      }
 366  
 367      /**
 368       * Returns the description that goes below the \<h1\> tag
 369       * @since 1.17
 370       *
 371       * @return string
 372       */
 373  	protected function getDescription() {
 374          return $this->msg( strtolower( $this->getName() ) )->escaped();
 375      }
 376  
 377      /**
 378       * The main action entry point.  Do all output for display and send it to the context
 379       * output.  Do not use globals $wgOut, $wgRequest, etc, in implementations; use
 380       * $this->getOutput(), etc.
 381       * @since 1.17
 382       *
 383       * @throws ErrorPageError
 384       */
 385      abstract public function show();
 386  }


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